diff --git a/.github/workflows/autobuild.yml b/.github/workflows/autobuild.yml index 122150979e..57709282e1 100644 --- a/.github/workflows/autobuild.yml +++ b/.github/workflows/autobuild.yml @@ -287,10 +287,20 @@ jobs: - name: Checkout code uses: actions/checkout@v7 with: - # only Android needs the oboe submodule, so don't fetch it for other builds - submodules: ${{ matrix.config.target_os == 'android' }} + submodules: false fetch-depth: ${{ matrix.config.checkout_fetch_depth || '1' }} + - name: Initialize Submodules + shell: bash + run: | + # clone mverb for all platforms + git submodule update --init libs/mverb + + # clone oboe only for android builds + if [[ "${{ matrix.config.target_os }}" == "android" ]]; then + git submodule update --init libs/oboe + fi + - name: Cache Mac dependencies if: matrix.config.target_os == 'macos' uses: actions/cache@v6 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 c6ac38b098..3314596e6c 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -17,7 +17,19 @@ First of all, you need to get the Jamulus source code. You can either download i - For .tar.gz [use this link](https://github.com/jamulussoftware/jamulus/archive/latest.tar.gz) to download the latest release - For .zip [use this link](https://github.com/jamulussoftware/jamulus/archive/latest.zip) - If you use `git`, [set it up](https://docs.github.com/en/get-started/quickstart/set-up-git) – preferably with SSH if you want to contribute. -Then run `git clone git@github.com:jamulussoftware/jamulus` in Terminal to get the bleeding edge version directly from GitHub. +Then run +``` +git clone git@github.com:jamulussoftware/jamulus +``` +in Terminal to get the bleeding edge version directly from GitHub. +- The reverberation is done by MVerb and you need to pull it in as a submodule. +``` +# Change into the jamulus/ directory +cd jamulus +# Initialize submodule MVerb +git submodule update --init libs/mverb +``` + ## Linux diff --git a/Jamulus.pro b/Jamulus.pro index fa956ef6d5..c77568fb58 100644 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -90,6 +90,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 @@ -403,7 +405,8 @@ HEADERS += src/plugins/audioreverb.h \ src/recorder/jamrecorder.h \ src/recorder/creaperproject.h \ src/recorder/cwavestream.h \ - src/signalhandler.h + src/signalhandler.h \ + libs/mverb/MVerb.h !contains(CONFIG, "serveronly") { HEADERS += src/client.h \ 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 fb9bab5478..cbc71e436b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1345,7 +1345,7 @@ void CClient::Init() Channel.SetAudioStreamProperties ( eAudioCompressionType, iCeltNumCodedBytes, iSndCrdFrameSizeFactor, iNumAudioChannels ); // init reverberation - AudioReverb.Init ( eAudioChannelConf, iStereoBlockSizeSam, SYSTEM_SAMPLE_RATE_HZ ); + AudioReverb.Init ( eAudioChannelConf, iStereoBlockSizeSam ); // init the sound card conversion buffers if ( bSndCrdConversionBufferRequired ) diff --git a/src/client.h b/src/client.h index fb77930723..fe60510814 100644 --- a/src/client.h +++ b/src/client.h @@ -189,14 +189,12 @@ class CClient : public QObject void SetAudioInFader ( const int iNV ) { iAudioInFader = iNV; } 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; } void SetDoAutoSockBufSize ( const bool bValue ); bool GetDoAutoSockBufSize() const { return Channel.GetDoAutoSockBufSize(); } diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index ad92a0f8e1..8b409f668e 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -380,6 +380,15 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet cbxInputBoost->setWhatsThis ( strInputBoost ); cbxInputBoost->setAccessibleName ( tr ( "Input Boost combo box" ) ); + // 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 " ); + lblInputBoost->setWhatsThis ( strReverbPreset ); + cbxInputBoost->setWhatsThis ( strReverbPreset ); + cbxInputBoost->setAccessibleName ( tr ( "Reverb Preset combo box" ) ); + // custom directories QString strCustomDirectories = "" + tr ( "Custom Directories" ) + ": " + tr ( "If you need to add additional directories to the Connect dialog Directory drop down, " @@ -555,6 +564,15 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet // factor is 1-based while index is 0-based: cbxInputBoost->setCurrentIndex ( pSettings->iInputBoost - 1 ); + // Rever Preset combo box + cbxReverbPreset->clear(); + cbxReverbPreset->addItem ( "Subtle" ); + cbxReverbPreset->addItem ( "Stadium" ); + cbxReverbPreset->addItem ( "Cupboard" ); + cbxReverbPreset->addItem ( "Dark" ); + cbxReverbPreset->addItem ( "Halves" ); + cbxReverbPreset->setCurrentIndex ( pClient->GetReverbPreset() ); + // init number of mixer rows spnMixerRows->setValue ( pSettings->iNumMixerPanelRows ); @@ -786,6 +804,11 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet this, &CClientSettingsDlg::OnInputBoostChanged ); + QObject::connect ( cbxReverbPreset, + static_cast ( &QComboBox::activated ), + this, + &CClientSettingsDlg::OnReverbPresetChanged ); + // buttons #if defined( _WIN32 ) && !defined( WITH_JACK ) // Driver Setup button is only available for Windows when JACK is not used @@ -1389,6 +1412,8 @@ void CClientSettingsDlg::OnInputBoostChanged() pClient->SetInputBoost ( pSettings->iInputBoost ); } +void CClientSettingsDlg::OnReverbPresetChanged() { pClient->SetReverbPreset ( cbxReverbPreset->currentIndex() ); } + void CClientSettingsDlg::OnAliasTextChanged ( const QString& strNewName ) { // check length diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 7844f64117..6564e21ca9 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -109,6 +109,7 @@ public slots: void OnCustomDirectoriesChanged ( bool bDelete ); void OnNewClientLevelEditingFinished() { pSettings->iNewClientFaderLevel = edtNewClientLevel->text().toInt(); } void OnInputBoostChanged(); + void OnReverbPresetChanged(); 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..63bb18d59a 100644 --- a/src/plugins/audioreverb.h +++ b/src/plugins/audioreverb.h @@ -2,56 +2,72 @@ * 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, SUBTLE, HALVES ) ) + { + 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 = 1, + CUPBOARD = 2, + DARK = 3, + HALVES = 4, + NUM_REV_PRESETS = 5 }; - EAudChanConf eAudioChannelConf; - int iStereoBlockSizeSam; - CFIFO allpassDelays[3]; - CFIFO combDelays[4]; - COnePole combFilters[4]; - CFIFO outLeftDelay; - CFIFO outRightDelay; - float allpassCoefficient; - float combCoefficient[4]; + constexpr static inline float const presets[NUM_REV_PRESETS][MVerb::NUM_PARAMS] = { { .5, .5, .5, .5, .5, 1., 1., .5, .75 }, + { 0., .5, 1., .5, 0., 1., 1., .35, .75 }, + { 0., .5, 1., .5, 0., 1., 1., 1., .75 }, + { .9, .5, .1, .5, 0., 1., 1., 1., .75 }, + { .5, .5, .5, .5, .5, 1., 1., .5, .75 } + + }; }; diff --git a/src/settings.cpp b/src/settings.cpp index bdc453913e..4d8fec898b 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -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 ); + } + // sound card selection const QString strError = pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); @@ -951,6 +957,9 @@ void CClientSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument, bool is // reverberation channel assignment SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", pClient->IsReverbOnLeftChan() ); + // reverb preset + SetNumericIniSet ( IniXMLDocument, "client", "revpreset", pClient->GetReverbPreset() ); + // sound card selection PutIniSetting ( IniXMLDocument, "client", "auddev_base64", ToBase64 ( pClient->GetSndCrdDev() ) );