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() ) );