diff --git a/.gitmodules b/.gitmodules index 819ad73047..f61a791095 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "libs/oboe"] path = libs/oboe url = ../../google/oboe.git +[submodule "libs/mverb"] + path = libs/mverb + url = https://github.com/martineastwood/mverb.git diff --git a/COMPILING.md b/COMPILING.md index d4a10c3bb5..e9d9a0c94b 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -205,3 +205,4 @@ During compile time some CONFIG arguments can be given to enable or disable spec | `disable_version_check` | Skip checks for version updates | | `noupcasename` | Compile Jamulus binary as lower case "jamulus" instead of "Jamulus" | | `raspijamulus` | Use raspijamulus.sh specific enhancements for build on Raspberry Pi | +| `noreverb` | Don't build the internal reverb and disable all related GUI elements | diff --git a/Jamulus.pro b/Jamulus.pro index 575c329732..6817659296 100644 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -42,6 +42,21 @@ contains(CONFIG, "nosound") { warning("\"nosound\" is deprecated: please use \"serveronly\" for a server-only build.") } +!contains(CONFIG, "serveronly") { + !contains(CONFIG, "noreverb"):!exists($$PWD/libs/mverb/MVerb.h) { + !system("git -C $$PWD submodule update --init --force libs/mverb") { + error('MVerb not found and could not be cloned as a submodule.$$escape_expand(\n) \ + Try cloning MVerb manually with: \'git submodule update --init --force libs/mverb\'$$escape_expand(\n) \ + To disable the reverb plugin call QMake with \"CONFIG+=noreverb\"$$escape_expand(\\n)Exiting...') + } else { + message("building with MVerb") + } + } else { + message("building without reverb plugin") + DEFINES += NO_REVERB + } +} + contains(CONFIG, "headless") { message(Headless mode activated.) QT -= gui @@ -90,6 +105,8 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\" \ DEFINES += QT_NO_DEPRECATED_WARNINGS win32 { + # fixes error C7525: inline variables require at least '/std:c++17' + CONFIG += c++17 DEFINES -= UNICODE # fixes issue with ASIO SDK (asiolist.cpp is not unicode compatible) DEFINES += NOMINMAX # solves a compiler error in qdatetime.h (Qt5) RC_FILE = src/res/win-mainicon.rc @@ -387,8 +404,7 @@ FORMS_GUI = src/aboutdlgbase.ui \ src/connectdlgbase.ui } -HEADERS += src/plugins/audioreverb.h \ - src/buffer.h \ +HEADERS += src/buffer.h \ src/channel.h \ src/global.h \ src/protocol.h \ @@ -409,6 +425,10 @@ HEADERS += src/plugins/audioreverb.h \ HEADERS += src/client.h \ src/sound/soundbase.h \ src/testbench.h + !contains(CONFIG, noreverb) { + HEADERS += src/plugins/audioreverb.h \ + libs/mverb/MVerb.h + } } HEADERS_GUI = src/serverdlg.h @@ -495,8 +515,7 @@ HEADERS_OPUS_X86 = libs/opus/celt/x86/celt_lpc_sse.h \ libs/opus/celt/x86/x86cpu.h \ $$files(libs/opus/silk/x86/*.h) -SOURCES += src/plugins/audioreverb.cpp \ - src/buffer.cpp \ +SOURCES += src/buffer.cpp \ src/channel.cpp \ src/main.cpp \ src/protocol.cpp \ @@ -514,7 +533,10 @@ SOURCES += src/plugins/audioreverb.cpp \ !contains(CONFIG, "serveronly") { SOURCES += src/client.cpp \ - src/sound/soundbase.cpp \ + src/sound/soundbase.cpp + !contains(CONFIG, "noreverb") { + SOURCES += src/plugins/audioreverb.cpp + } } SOURCES_GUI = src/serverdlg.cpp diff --git a/libs/mverb b/libs/mverb new file mode 160000 index 0000000000..9f9512e9d9 --- /dev/null +++ b/libs/mverb @@ -0,0 +1 @@ +Subproject commit 9f9512e9d990fcf265e00fc7c2ee47ad20c8019f diff --git a/src/client.cpp b/src/client.cpp index 6ee048a863..bfe2c499f4 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -75,8 +75,10 @@ CClient::CClient ( const quint16 iPortNumber, Socket ( &Channel, iPortNumber, iQosNumber, "", "", bNDisableIPv6, bIPv6Available ), Sound ( AudioCallback, this, bNoAutoJackConnect, strNClientName ), iAudioInFader ( AUD_FADER_IN_MIDDLE ), +#ifndef NO_REVERB bReverbOnLeftChan ( false ), iReverbLevel ( 0 ), +#endif iInputBoost ( 1 ), iSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_DEFAULT ), iSndCrdFrameSizeFactor ( FRAME_SIZE_FACTOR_DEFAULT ), @@ -1367,10 +1369,10 @@ void CClient::Init() // set the channel network properties Channel.SetAudioStreamProperties ( eAudioCompressionType, iCeltNumCodedBytes, iSndCrdFrameSizeFactor, iNumAudioChannels ); - +#ifndef NO_REVERB // init reverberation - AudioReverb.Init ( eAudioChannelConf, iStereoBlockSizeSam, SYSTEM_SAMPLE_RATE_HZ ); - + AudioReverb.Init ( eAudioChannelConf, iStereoBlockSizeSam ); +#endif // init the sound card conversion buffers if ( bSndCrdConversionBufferRequired ) { @@ -1462,13 +1464,13 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) #ifndef HEADLESS SignalLevelMeter.Update ( vecsStereoSndCrd, iMonoBlockSizeSam, true ); #endif - +#ifndef NO_REVERB // add reverberation effect if activated if ( iReverbLevel != 0 ) { AudioReverb.Process ( vecsStereoSndCrd, bReverbOnLeftChan, static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4 ); } - +#endif // apply pan (audio fader) and mix mono signals if ( !( ( iAudioInFader == AUD_FADER_IN_MIDDLE ) && ( eAudioChannelConf == CC_STEREO ) ) ) { diff --git a/src/client.h b/src/client.h index b56e5c42d1..f566fabc62 100644 --- a/src/client.h +++ b/src/client.h @@ -61,7 +61,9 @@ #include "socket.h" #include "channel.h" #include "util.h" -#include "plugins/audioreverb.h" +#ifndef NO_REVERB +# include "plugins/audioreverb.h" +#endif #include "buffer.h" #include "signalhandler.h" @@ -188,17 +190,15 @@ class CClient : public QObject int GetAudioInFader() const { return iAudioInFader; } void SetAudioInFader ( const int iNV ) { iAudioInFader = iNV; } - +#ifndef NO_REVERB int GetReverbLevel() const { return iReverbLevel; } + int GetReverbPreset() const { return AudioReverb.getPreset(); } void SetReverbLevel ( const int iNL ) { iReverbLevel = iNL; } + void SetReverbPreset ( const int iNP ) { AudioReverb.setPreset ( iNP ); } bool IsReverbOnLeftChan() const { return bReverbOnLeftChan; } - void SetReverbOnLeftChan ( const bool bIL ) - { - bReverbOnLeftChan = bIL; - AudioReverb.Clear(); - } - + void SetReverbOnLeftChan ( const bool bIL ) { bReverbOnLeftChan = bIL; } +#endif void SetDoAutoSockBufSize ( const bool bValue ); bool GetDoAutoSockBufSize() const { return Channel.GetDoAutoSockBufSize(); } @@ -403,11 +403,13 @@ class CClient : public QObject CVector vecbyNetwData; - std::atomic iAudioInFader; - std::atomic bReverbOnLeftChan; - std::atomic iReverbLevel; - CAudioReverb AudioReverb; - std::atomic iInputBoost; + int iAudioInFader; +#ifndef NO_REVERB + bool bReverbOnLeftChan; + int iReverbLevel; + CAudioReverb AudioReverb; +#endif + int iInputBoost; int iSndCrdPrefFrameSizeFactor; int iSndCrdFrameSizeFactor; diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index d939b54aa2..7f24b2124b 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -117,7 +117,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP, "If you are connected, pressing this button will end the session." ) ); butConnect->setAccessibleName ( tr ( "Connect and disconnect toggle button" ) ); - +#ifndef NO_REVERB // reverberation level QString strAudReverb = "" + tr ( "Reverb effect" ) + ": " + tr ( "Reverb can be applied to one local mono audio channel or to both " @@ -143,7 +143,13 @@ CClientDlg::CClientDlg ( CClient* pNCliP, rbtReverbSelL->setAccessibleName ( tr ( "Left channel selection for reverb" ) ); rbtReverbSelR->setWhatsThis ( strRevChanSel ); rbtReverbSelR->setAccessibleName ( tr ( "Right channel selection for reverb" ) ); - +#endif +#ifdef NO_REVERB + lblAudioReverb->setVisible ( false ); + sldAudioReverb->setVisible ( false ); + rbtReverbSelL->setVisible ( false ); + rbtReverbSelR->setVisible ( false ); +#endif // delay LED QString strLEDDelay = "" + tr ( "Delay Status LED" ) + ": " + tr ( "Shows the current audio delay status:" ) + "
    " @@ -257,18 +263,18 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // init status LEDs ledBuffers->Reset(); ledDelay->Reset(); - +#ifndef NO_REVERB // init audio reverberation sldAudioReverb->setRange ( 0, AUD_REVERB_MAX ); const int iCurAudReverb = pClient->GetReverbLevel(); sldAudioReverb->setValue ( iCurAudReverb ); sldAudioReverb->setTickInterval ( AUD_REVERB_MAX / 5 ); - // init input boost - pClient->SetInputBoost ( pSettings->iInputBoost ); - // init reverb channel UpdateRevSelection(); +#endif + // init input boost + pClient->SetInputBoost ( pSettings->iInputBoost ); // init connect dialog ConnectDlg.SetShowAllMusicians ( pSettings->bConnectDlgShowAllMusicians ); @@ -496,14 +502,14 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QObject::connect ( &TimerCheckAudioDeviceOk, &QTimer::timeout, this, &CClientDlg::OnTimerCheckAudioDeviceOk ); QObject::connect ( &TimerDetectFeedback, &QTimer::timeout, this, &CClientDlg::OnTimerDetectFeedback ); - +#ifndef NO_REVERB QObject::connect ( sldAudioReverb, &QSlider::valueChanged, this, &CClientDlg::OnAudioReverbValueChanged ); // radio buttons QObject::connect ( rbtReverbSelL, &QRadioButton::clicked, this, &CClientDlg::OnReverbSelLClicked ); QObject::connect ( rbtReverbSelR, &QRadioButton::clicked, this, &CClientDlg::OnReverbSelRClicked ); - +#endif // other QObject::connect ( pClient, &CClient::ConClientListMesReceived, this, &CClientDlg::OnConClientListMesReceived ); @@ -690,7 +696,7 @@ void CClientDlg::ManageDragNDrop ( QDropEvent* Event, const bool bCheckAccept ) } } } - +#ifndef NO_REVERB void CClientDlg::UpdateRevSelection() { if ( pClient->GetAudioChannels() == CC_STEREO ) @@ -720,7 +726,7 @@ void CClientDlg::UpdateRevSelection() // update visibility of the pan controls in the audio mixer board (pan is not supported for mono) MainMixerBoard->SetDisplayPans ( pClient->GetAudioChannels() != CC_MONO ); } - +#endif void CClientDlg::OnConnectDlgAccepted() { // We had an issue that the accepted signal was emit twice if a list item was double @@ -1402,7 +1408,8 @@ void CClientDlg::SetGUIDesign ( const EGUIDesign eNewDesign ) " image: url(:/png/fader/res/ledbuttonpressed.png); }" "QCheckBox { color: rgb(220, 220, 220);" " font: bold; }" ); -#ifdef _WIN32 +#ifndef NO_REVERB +# ifdef _WIN32 // Workaround QT-Windows problem: This should not be necessary since in the // background frame the style sheet for QRadioButton was already set. But it // seems that it is only applied if the style was set to default and then back @@ -1411,6 +1418,7 @@ void CClientDlg::SetGUIDesign ( const EGUIDesign eNewDesign ) "font: bold;" ); rbtReverbSelR->setStyleSheet ( "color: rgb(220, 220, 220);" "font: bold;" ); +# endif #endif ledBuffers->SetType ( CMultiColorLED::MT_LED ); @@ -1421,10 +1429,12 @@ void CClientDlg::SetGUIDesign ( const EGUIDesign eNewDesign ) // reset style sheet and set original parameters backgroundFrame->setStyleSheet ( "" ); -#ifdef _WIN32 +#ifndef NO_REVERB +# ifdef _WIN32 // Workaround QT-Windows problem: See above description rbtReverbSelL->setStyleSheet ( "" ); rbtReverbSelR->setStyleSheet ( "" ); +# endif #endif ledBuffers->SetType ( CMultiColorLED::MT_INDICATOR ); diff --git a/src/clientdlg.h b/src/clientdlg.h index 6a34cc66f0..856d04a74d 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -115,7 +115,9 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase void ShowChatWindow ( const bool bForceRaise = true ); void ShowAnalyzerConsole(); void UpdateAudioFaderSlider(); +#ifndef NO_REVERB void UpdateRevSelection(); +#endif void Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ); void Disconnect(); void ManageDragNDrop ( QDropEvent* Event, const bool bCheckAccept ); @@ -201,13 +203,13 @@ public slots: void OnSettingsStateChanged ( int value ); void OnChatStateChanged ( int value ); void OnLocalMuteStateChanged ( int value ); - +#ifndef NO_REVERB void OnAudioReverbValueChanged ( int value ) { pClient->SetReverbLevel ( value ); } void OnReverbSelLClicked() { pClient->SetReverbOnLeftChan ( true ); } void OnReverbSelRClicked() { pClient->SetReverbOnLeftChan ( false ); } - +#endif void OnFeedbackDetectionChanged ( int state ) { ClientSettingsDlg.SetEnableFeedbackDetection ( state == Qt::Checked ); } void OnConClientListMesReceived ( CVector vecChanInfo ); @@ -259,7 +261,12 @@ public slots: void OnMeterStyleChanged(); void OnRecorderStateReceived ( ERecorderState eRecorderState ); void SetMixerBoardDeco ( const ERecorderState newRecorderState, const EGUIDesign eNewDesign ); - void OnAudioChannelsChanged() { UpdateRevSelection(); } + void OnAudioChannelsChanged() + { +#ifndef NO_REVERB + UpdateRevSelection(); +#endif + } void OnNumClientsChanged ( int iNewNumClients ); void accept() { close(); } // introduced by pljones diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index ad92a0f8e1..b0582362e3 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -379,7 +379,21 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet lblInputBoost->setWhatsThis ( strInputBoost ); cbxInputBoost->setWhatsThis ( strInputBoost ); cbxInputBoost->setAccessibleName ( tr ( "Input Boost combo box" ) ); - +#ifndef NO_REVERB + // reverb preset + QString strReverbPreset = "" + tr ( "Reverb Preset" ) + ": " + + tr ( "Jamulus uses MVerb by Martin Eastwood for reverberation. " + "MVerb comes with a set of presets you can select here. " + "Available Presets: Subtle, Stadium, Cupboard, Dark, Halves, Drum Room, Club " ); + lblReverbPreset->setWhatsThis ( strReverbPreset ); + cbxReverbPreset->setWhatsThis ( strReverbPreset ); + cbxReverbPreset->setAccessibleName ( tr ( "Reverb Preset combo box" ) ); +#endif +#ifdef NO_REVERB + lblReverbPreset->setVisible ( false ); + cbxReverbPreset->setVisible ( false ); + verticalSpacer_13->changeSize ( 0, 0 ); +#endif // custom directories QString strCustomDirectories = "" + tr ( "Custom Directories" ) + ": " + tr ( "If you need to add additional directories to the Connect dialog Directory drop down, " @@ -554,7 +568,18 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet } // factor is 1-based while index is 0-based: cbxInputBoost->setCurrentIndex ( pSettings->iInputBoost - 1 ); - +#ifndef NO_REVERB + // Rever Preset combo box + cbxReverbPreset->clear(); + cbxReverbPreset->addItem ( "Subtle" ); + cbxReverbPreset->addItem ( "Stadium" ); + cbxReverbPreset->addItem ( "Cupboard" ); + cbxReverbPreset->addItem ( "Dark" ); + cbxReverbPreset->addItem ( "Halves" ); + cbxReverbPreset->addItem ( "Drum Room" ); + cbxReverbPreset->addItem ( "Club" ); + cbxReverbPreset->setCurrentIndex ( pClient->GetReverbPreset() ); +#endif // init number of mixer rows spnMixerRows->setValue ( pSettings->iNumMixerPanelRows ); @@ -785,7 +810,12 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet static_cast ( &QComboBox::activated ), this, &CClientSettingsDlg::OnInputBoostChanged ); - +#ifndef NO_REVERB + QObject::connect ( cbxReverbPreset, + static_cast ( &QComboBox::activated ), + this, + &CClientSettingsDlg::OnReverbPresetChanged ); +#endif // buttons #if defined( _WIN32 ) && !defined( WITH_JACK ) // Driver Setup button is only available for Windows when JACK is not used @@ -1388,7 +1418,9 @@ void CClientSettingsDlg::OnInputBoostChanged() pSettings->iInputBoost = cbxInputBoost->currentIndex() + 1; pClient->SetInputBoost ( pSettings->iInputBoost ); } - +#ifndef NO_REVERB +void CClientSettingsDlg::OnReverbPresetChanged() { pClient->SetReverbPreset ( cbxReverbPreset->currentIndex() ); } +#endif void CClientSettingsDlg::OnAliasTextChanged ( const QString& strNewName ) { // check length diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 7844f64117..f6ade362b2 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -109,6 +109,9 @@ public slots: void OnCustomDirectoriesChanged ( bool bDelete ); void OnNewClientLevelEditingFinished() { pSettings->iNewClientFaderLevel = edtNewClientLevel->text().toInt(); } void OnInputBoostChanged(); +#ifndef NO_REVERB + void OnReverbPresetChanged(); +#endif void OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* button ); void OnSoundcardActivated ( int iSndDevIdx ); void OnLInChanActivated ( int iChanIdx ); diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index 842ab9220c..8e15b59077 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -7,7 +7,7 @@ 0 0 534 - 591 + 652 @@ -30,7 +30,7 @@ - 3 + 2 true @@ -45,7 +45,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -60,10 +60,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Preferred + QSizePolicy::Policy::Preferred @@ -209,7 +209,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -305,7 +305,7 @@ - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter 1 @@ -332,7 +332,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -347,7 +347,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -399,10 +399,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Expanding + QSizePolicy::Policy::Expanding @@ -431,7 +431,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -450,10 +450,10 @@ - QFrame::NoFrame + QFrame::Shape::NoFrame - QFrame::Plain + QFrame::Shadow::Plain @@ -600,10 +600,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Expanding + QSizePolicy::Policy::Expanding @@ -654,7 +654,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -717,10 +717,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::MinimumExpanding + QSizePolicy::Policy::MinimumExpanding @@ -759,7 +759,7 @@ Local - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter false @@ -772,7 +772,7 @@ Server - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter false @@ -789,7 +789,7 @@ Size - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter false @@ -802,7 +802,7 @@ Size - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter false @@ -816,10 +816,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Minimum + QSizePolicy::Policy::Minimum @@ -841,20 +841,20 @@ 1 - Qt::Vertical + Qt::Orientation::Vertical - QSlider::TicksBothSides + QSlider::TickPosition::TicksBothSides - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Minimum + QSizePolicy::Policy::Minimum @@ -876,10 +876,10 @@ 1 - Qt::Vertical + Qt::Orientation::Vertical - QSlider::TicksBothSides + QSlider::TickPosition::TicksBothSides @@ -915,7 +915,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -931,7 +931,7 @@ kbps - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + Qt::AlignmentFlag::AlignLeading|Qt::AlignmentFlag::AlignLeft|Qt::AlignmentFlag::AlignVCenter @@ -941,7 +941,7 @@ val - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter 2 @@ -951,7 +951,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -971,10 +971,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Minimum + QSizePolicy::Policy::Minimum @@ -1000,7 +1000,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1015,10 +1015,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::MinimumExpanding + QSizePolicy::Policy::MinimumExpanding @@ -1055,7 +1055,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1092,7 +1092,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1115,10 +1115,33 @@ + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + Reverb Preset + + + + + + - Qt::Vertical + Qt::Orientation::Vertical @@ -1144,7 +1167,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1159,7 +1182,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1176,10 +1199,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Preferred + QSizePolicy::Policy::Preferred @@ -1206,7 +1229,7 @@ Pan - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter sldAudioPan @@ -1218,10 +1241,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Minimum + QSizePolicy::Policy::Minimum @@ -1252,20 +1275,20 @@ 1 - Qt::Horizontal + Qt::Orientation::Horizontal - QSlider::TicksBothSides + QSlider::TickPosition::TicksBothSides - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Minimum + QSizePolicy::Policy::Minimum @@ -1289,7 +1312,7 @@ Center - Qt::AlignCenter + Qt::AlignmentFlag::AlignCenter false @@ -1304,10 +1327,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QSizePolicy::Preferred + QSizePolicy::Policy::Preferred @@ -1324,7 +1347,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1383,7 +1406,6 @@ - 50 false @@ -1408,7 +1430,6 @@ - 50 false @@ -1502,7 +1523,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1522,7 +1543,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1538,7 +1559,6 @@ - 50 false @@ -1561,7 +1581,6 @@ - 50 false @@ -1580,7 +1599,6 @@ - 50 false @@ -1596,7 +1614,6 @@ - 50 false @@ -1611,7 +1628,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1629,7 +1646,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1642,7 +1659,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1657,7 +1674,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1671,7 +1688,6 @@ - 50 false @@ -1694,7 +1710,6 @@ - 50 false @@ -1713,7 +1728,6 @@ - 50 false @@ -1726,7 +1740,6 @@ - 50 false @@ -1741,7 +1754,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1755,7 +1768,6 @@ - 50 false @@ -1774,7 +1786,6 @@ - 50 false @@ -1795,7 +1806,6 @@ - 50 false @@ -1818,7 +1828,6 @@ - 50 false @@ -1837,7 +1846,6 @@ - 50 false @@ -1850,7 +1858,6 @@ - 50 false @@ -1865,7 +1872,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -1879,7 +1886,6 @@ - 50 false @@ -1898,7 +1904,6 @@ - 50 false @@ -1918,7 +1923,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -1932,7 +1937,6 @@ - 50 false @@ -1955,7 +1959,6 @@ - 50 false @@ -1974,7 +1977,6 @@ - 50 false @@ -1987,7 +1989,6 @@ - 50 false @@ -2002,7 +2003,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2016,7 +2017,6 @@ - 50 false @@ -2035,7 +2035,6 @@ - 50 false @@ -2056,7 +2055,6 @@ - 50 false @@ -2079,7 +2077,6 @@ - 50 false @@ -2098,7 +2095,6 @@ - 50 false @@ -2111,7 +2107,6 @@ - 50 false @@ -2126,7 +2121,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2140,7 +2135,6 @@ - 50 false @@ -2159,7 +2153,6 @@ - 50 false @@ -2179,7 +2172,7 @@ - Qt::Vertical + Qt::Orientation::Vertical @@ -2213,7 +2206,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -2228,7 +2221,7 @@ - Qt::Vertical + Qt::Orientation::Vertical diff --git a/src/plugins/audioreverb.cpp b/src/plugins/audioreverb.cpp index a10c415bb8..2e0a9885bb 100644 --- a/src/plugins/audioreverb.cpp +++ b/src/plugins/audioreverb.cpp @@ -2,206 +2,69 @@ * Audio Reverberation * \******************************************************************************/ /* - The following code is based on "JCRev: John Chowning's reverberator class" - by Perry R. Cook and Gary P. Scavone, 1995 - 2004 - which is in "The Synthesis ToolKit in C++ (STK)" - http://ccrma.stanford.edu/software/stk - - Original description: - This class is derived from the CLM JCRev function, which is based on the use - of networks of simple allpass and comb delay filters. This class implements - three series allpass units, followed by four parallel comb filters, and two - decorrelation delay lines in parallel at the output. + The following code calls MVerb for reverberation. + MVerb was written by Martin Eastwood. + https://github.com/martineastwood/mverb */ #include "audioreverb.h" -void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam, const int iSampleRate, const float fT60 ) +void CAudioReverb::Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam ) { - // store parameters eAudioChannelConf = eNAudioChannelConf; iStereoBlockSizeSam = iNStereoBlockSizeSam; - // delay lengths for 44100 Hz sample rate - int lengths[9] = { 1116, 1356, 1422, 1617, 225, 341, 441, 211, 179 }; - const float scaler = static_cast ( iSampleRate ) / 44100.0f; - - if ( scaler != 1.0f ) - { - for ( int i = 0; i < 9; i++ ) - { - int delay = static_cast ( floorf ( scaler * lengths[i] ) ); + // Jamulus uses interleaved stereo, mverb operates on a 2-dimensional array instead + // Calculate the number of frames for each channel ( iStereoBlockSizeSam / 2 ) + numFrames = iStereoBlockSizeSam >> 1; - if ( ( delay & 1 ) == 0 ) - { - delay++; - } + // These buffers get filled with dry signal and are then passed to mverb + // They need to be vectors as the windows builds fail when arrays are used + bufL.resize ( numFrames ); + bufR.resize ( numFrames ); - while ( !isPrime ( delay ) ) - { - delay += 2; - } - - lengths[i] = delay; - } - } - - for ( int i = 0; i < 3; i++ ) - { - allpassDelays[i].Init ( lengths[i + 4] ); - } + mverb->setSampleRate ( static_cast ( SYSTEM_SAMPLE_RATE_HZ ) ); + loadPreset(); +} - for ( int i = 0; i < 4; i++ ) +void CAudioReverb::loadPreset() +{ + for ( int i = 0; i < MVerb::NUM_PARAMS; i++ ) { - combDelays[i].Init ( lengths[i] ); - combFilters[i].setPole ( 0.2f ); + mverb->setParameter ( i, presets[iPreset][i] ); } - - setT60 ( fT60, iSampleRate ); - outLeftDelay.Init ( lengths[7] ); - outRightDelay.Init ( lengths[8] ); - allpassCoefficient = 0.7f; - Clear(); } -bool CAudioReverb::isPrime ( const int number ) +void CAudioReverb::Process ( CVector& vecsStereoInOut, const bool bReverbOnLeftChan, const float fReverbGain ) { - /* - Returns true if argument value is prime. Taken from "class Effect" in - "STK abstract effects parent class". - */ - if ( number == 2 ) - { - return true; - } - if ( number & 1 ) + // One buffer to pass to mverb's process function + float* fInput[2] = { bufL.data(), bufR.data() }; + + if ( eAudioChannelConf == CC_STEREO ) { - for ( int i = 3; i < static_cast ( sqrtf ( static_cast ( number ) ) ) + 1; i += 2 ) + for ( int i = 0, j = 0; j < numFrames; i += 2, j++ ) { - if ( ( number % i ) == 0 ) - { - return false; - } + // True stereo reverb + bufL[j] = static_cast ( vecsStereoInOut[i] ) / fMaxShort; + bufR[j] = static_cast ( vecsStereoInOut[i + 1] ) / fMaxShort; } - - return true; // prime } else { - return false; // even + for ( int i = 0, j = 0; j < numFrames; i += 2, j++ ) + { + // For Mono and Mono-in/Stereo-out only one channel is selected and copied into both fInput channels + bufR[j] = bufL[j] = static_cast ( vecsStereoInOut[i + !bReverbOnLeftChan] ) / fMaxShort; + } } -} -void CAudioReverb::Clear() -{ - // reset and clear all internal state - allpassDelays[0].Reset ( 0 ); - allpassDelays[1].Reset ( 0 ); - allpassDelays[2].Reset ( 0 ); - combDelays[0].Reset ( 0 ); - combDelays[1].Reset ( 0 ); - combDelays[2].Reset ( 0 ); - combDelays[3].Reset ( 0 ); - combFilters[0].Reset(); - combFilters[1].Reset(); - combFilters[2].Reset(); - combFilters[3].Reset(); - outRightDelay.Reset ( 0 ); - outLeftDelay.Reset ( 0 ); -} + mverb->process ( fInput, fInput, numFrames ); -void CAudioReverb::setT60 ( const float fT60, const int iSampleRate ) -{ - // set the reverberation T60 decay time - for ( int i = 0; i < 4; i++ ) + for ( int i = 0, j = 0; j < numFrames; i += 2, j++ ) { - combCoefficient[i] = powf ( 10.0f, static_cast ( -3.0f * combDelays[i].Size() / ( fT60 * iSampleRate ) ) ); + // Mix wet and dry signal + vecsStereoInOut[i] = Float2Short ( vecsStereoInOut[i] + bufL[j] * fMaxShort * fReverbGain ); + vecsStereoInOut[i + 1] = Float2Short ( vecsStereoInOut[i + 1] + bufR[j] * fMaxShort * fReverbGain ); } } - -void CAudioReverb::COnePole::setPole ( const float fPole ) -{ - // calculate IIR filter coefficients based on the pole value - fA = -fPole; - fB = 1.0f - fPole; -} - -float CAudioReverb::COnePole::Calc ( const float fIn ) -{ - // calculate IIR filter - fLastSample = fB * fIn - fA * fLastSample; - - return fLastSample; -} - -void CAudioReverb::Process ( CVector& vecsStereoInOut, const bool bReverbOnLeftChan, const float fAttenuation ) -{ - float fMixedInput, temp, temp0, temp1, temp2; - - for ( int i = 0; i < iStereoBlockSizeSam; i += 2 ) - { - // we sum up the stereo input channels (in case mono input is used, a zero - // shall be input for the right channel) - if ( eAudioChannelConf == CC_STEREO ) - { - fMixedInput = 0.5f * ( vecsStereoInOut[i] + vecsStereoInOut[i + 1] ); - } - else - { - if ( bReverbOnLeftChan ) - { - fMixedInput = vecsStereoInOut[i]; - } - else - { - fMixedInput = vecsStereoInOut[i + 1]; - } - } - - temp = allpassDelays[0].Get(); - temp0 = allpassCoefficient * temp; - temp0 += fMixedInput; - allpassDelays[0].Add ( temp0 ); - temp0 = -( allpassCoefficient * temp0 ) + temp; - - temp = allpassDelays[1].Get(); - temp1 = allpassCoefficient * temp; - temp1 += temp0; - allpassDelays[1].Add ( temp1 ); - temp1 = -( allpassCoefficient * temp1 ) + temp; - - temp = allpassDelays[2].Get(); - temp2 = allpassCoefficient * temp; - temp2 += temp1; - allpassDelays[2].Add ( temp2 ); - temp2 = -( allpassCoefficient * temp2 ) + temp; - - const float temp3 = temp2 + combFilters[0].Calc ( combCoefficient[0] * combDelays[0].Get() ); - const float temp4 = temp2 + combFilters[1].Calc ( combCoefficient[1] * combDelays[1].Get() ); - const float temp5 = temp2 + combFilters[2].Calc ( combCoefficient[2] * combDelays[2].Get() ); - const float temp6 = temp2 + combFilters[3].Calc ( combCoefficient[3] * combDelays[3].Get() ); - - combDelays[0].Add ( temp3 ); - combDelays[1].Add ( temp4 ); - combDelays[2].Add ( temp5 ); - combDelays[3].Add ( temp6 ); - - const float filtout = temp3 + temp4 + temp5 + temp6; - - outLeftDelay.Add ( filtout ); - outRightDelay.Add ( filtout ); - - // inplace apply the attenuated reverb signal (for stereo always apply - // reverberation effect on both channels) - if ( ( eAudioChannelConf == CC_STEREO ) || bReverbOnLeftChan ) - { - vecsStereoInOut[i] = Float2Short ( ( 1.0f - fAttenuation ) * vecsStereoInOut[i] + 0.5f * fAttenuation * outLeftDelay.Get() ); - } - - if ( ( eAudioChannelConf == CC_STEREO ) || !bReverbOnLeftChan ) - { - vecsStereoInOut[i + 1] = Float2Short ( ( 1.0f - fAttenuation ) * vecsStereoInOut[i + 1] + 0.5f * fAttenuation * outRightDelay.Get() ); - } - } -} \ No newline at end of file diff --git a/src/plugins/audioreverb.h b/src/plugins/audioreverb.h index a5da6fac61..df5704e224 100644 --- a/src/plugins/audioreverb.h +++ b/src/plugins/audioreverb.h @@ -2,56 +2,76 @@ * Audio Reverberation * \******************************************************************************/ /* - The following code is based on "JCRev: John Chowning's reverberator class" - by Perry R. Cook and Gary P. Scavone, 1995 - 2004 - which is in "The Synthesis ToolKit in C++ (STK)" - http://ccrma.stanford.edu/software/stk - - Original description: - This class is derived from the CLM JCRev function, which is based on the use - of networks of simple allpass and comb delay filters. This class implements - three series allpass units, followed by four parallel comb filters, and two - decorrelation delay lines in parallel at the output. + The following code calls MVerb for reverberation. + MVerb was written by Martin Eastwood. + https://github.com/martineastwood/mverb */ #pragma once #include "util.h" +#include "libs/mverb/MVerb.h" class CAudioReverb { public: - CAudioReverb() {} + CAudioReverb() + { + fMaxShort = static_cast ( _MAXSHORT ); + iPreset = STADIUM; + + // Create MVerb on the heap + mverb = std::unique_ptr> ( new MVerb() ); + mverb->setSampleRate ( SYSTEM_SAMPLE_RATE_HZ ); + loadPreset(); + } - void Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam, const int iSampleRate, const float fT60 = 1.1f ); + void Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam ); void Clear(); - void Process ( CVector& vecsStereoInOut, const bool bReverbOnLeftChan, const float fAttenuation ); + void Process ( CVector& vecsStereoInOut, const bool bReverbOnLeftChan, const float fReverbGain ); + void setPreset ( const int iNPreset ) + { + // silently fail if preset doesn't exist + if ( MathUtils::InRange ( iNPreset, 0, NUM_REV_PRESETS ) ) + { + iPreset = iNPreset; + loadPreset(); + } + }; + int getPreset() const { return iPreset; }; protected: - void setT60 ( const float fT60, const int iSampleRate ); - bool isPrime ( const int number ); + std::unique_ptr> mverb; + + void loadPreset(); + EAudChanConf eAudioChannelConf; + int iStereoBlockSizeSam; + float fMaxShort; + int iPreset; + + int numFrames; + std::vector bufL; + std::vector bufR; - class COnePole + enum { - public: - COnePole() : fA ( 0 ), fB ( 0 ) { Reset(); } - void setPole ( const float fPole ); - float Calc ( const float fIn ); - void Reset() { fLastSample = 0; } - - protected: - float fA; - float fB; - float fLastSample; + SUBTLE = 0, + STADIUM, + CUPBOARD, + DARK, + HALVES, + DRUMROOM, + CLUB, + NUM_REV_PRESETS }; - EAudChanConf eAudioChannelConf; - int iStereoBlockSizeSam; - CFIFO allpassDelays[3]; - CFIFO combDelays[4]; - COnePole combFilters[4]; - CFIFO outLeftDelay; - CFIFO outRightDelay; - float allpassCoefficient; - float combCoefficient[4]; + // Parameters are set iteratively by enum. See MVerb.h for reference. + // NOTE: parameters "GAIN" and "MIX" must be "1." + constexpr static inline float const presets[NUM_REV_PRESETS][MVerb::NUM_PARAMS] = { { 0., .5, 1., .5, 0., .5, 1., 1., .75 }, + { 0., .5, 1., .5, 0., 1., 1., 1., .75 }, + { 0., .5, 1., .5, 0., .25, 1., 1., .75 }, + { .9, .5, .1, .5, 0., .5, 1., 1., .75 }, + { .5, .5, .5, .5, .5, .75, 1., 1., .5 }, + { .2, .4, .4, .6, .1, .05, 1., 1., .4 }, + { .4, .2, .3, .6, .2, .2, 1., 1., .5 } }; }; diff --git a/src/settings.cpp b/src/settings.cpp index bdc453913e..aaa643f3da 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -495,7 +495,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, { pClient->SetAudioInFader ( iValue ); } - +# ifndef NO_REVERB // reverberation level if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", 0, AUD_REVERB_MAX, iValue ) ) { @@ -508,6 +508,12 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, pClient->SetReverbOnLeftChan ( bValue ); } + // reverb preset + if ( GetNumericIniSet ( IniXMLDocument, "client", "revpreset", 0, 4, iValue ) ) + { + pClient->SetReverbPreset ( iValue ); + } +# endif // sound card selection const QString strError = pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); @@ -944,13 +950,16 @@ void CClientSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument, bool is // audio fader SetNumericIniSet ( IniXMLDocument, "client", "audfad", pClient->GetAudioInFader() ); - +# ifndef NO_REVERB // reverberation level SetNumericIniSet ( IniXMLDocument, "client", "revlev", pClient->GetReverbLevel() ); // reverberation channel assignment SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", pClient->IsReverbOnLeftChan() ); + // reverb preset + SetNumericIniSet ( IniXMLDocument, "client", "revpreset", pClient->GetReverbPreset() ); +# endif // sound card selection PutIniSetting ( IniXMLDocument, "client", "auddev_base64", ToBase64 ( pClient->GetSndCrdDev() ) );