audacia/src/effects/Reverb.cpp

558 lines
16 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Audacity(R) is copyright (c) 1999-2013 Audacity Team.
License: GPL v2. See License.txt.
Reverb.cpp
Rob Sykes, Vaughan Johnson
******************************************************************//**
\class EffectReverb
\brief A reverberation effect
*//*******************************************************************/
#include "Reverb.h"
#include "LoadEffects.h"
#include <wx/arrstr.h>
#include <wx/checkbox.h>
#include <wx/intl.h>
#include <wx/slider.h>
#include <wx/spinctrl.h>
#include "../Prefs.h"
#include "../Shuttle.h"
#include "../ShuttleGui.h"
#include "../widgets/valnum.h"
#include "Reverb_libSoX.h"
enum
{
ID_RoomSize = 10000,
ID_PreDelay,
ID_Reverberance,
ID_HfDamping,
ID_ToneLow,
ID_ToneHigh,
ID_WetGain,
ID_DryGain,
ID_StereoWidth,
ID_WetOnly
};
// Define keys, defaults, minimums, and maximums for the effect parameters
//
// Name Type Key Def Min Max Scale
Param( RoomSize, double, wxT("RoomSize"), 75, 0, 100, 1 );
Param( PreDelay, double, wxT("Delay"), 10, 0, 200, 1 );
Param( Reverberance, double, wxT("Reverberance"), 50, 0, 100, 1 );
Param( HfDamping, double, wxT("HfDamping"), 50, 0, 100, 1 );
Param( ToneLow, double, wxT("ToneLow"), 100, 0, 100, 1 );
Param( ToneHigh, double, wxT("ToneHigh"), 100, 0, 100, 1 );
Param( WetGain, double, wxT("WetGain"), -1, -20, 10, 1 );
Param( DryGain, double, wxT("DryGain"), -1, -20, 10, 1 );
Param( StereoWidth, double, wxT("StereoWidth"), 100, 0, 100, 1 );
Param( WetOnly, bool, wxT("WetOnly"), false, false, true, 1 );
static const struct
{
const TranslatableString name;
EffectReverb::Params params;
}
FactoryPresets[] =
{
// Room Pre Hf Tone Tone Wet Dry Stereo Wet
// Name Size, Delay, Reverb, Damping, Low, High, Gain, Gain, Width, Only
{ XO("Vocal I" ), { 70, 20, 40, 99, 100, 50, -12, 0, 70, false } },
{ XO("Vocal II"), { 50, 0, 50, 99, 50, 100, -1, -1, 70, false } },
{ XO("Bathroom"), { 16, 8, 80, 0, 0, 100, -6, 0, 100, false } },
{ XO("Small Room Bright"), { 30, 10, 50, 50, 50, 100, -1, -1, 100, false } },
{ XO("Small Room Dark"), { 30, 10, 50, 50, 100, 0, -1, -1, 100, false } },
{ XO("Medium Room"), { 75, 10, 40, 50, 100, 70, -1, -1, 70, false } },
{ XO("Large Room"), { 85, 10, 40, 50, 100, 80, 0, -6, 90, false } },
{ XO("Church Hall"), { 90, 32, 60, 50, 100, 50, 0, -12, 100, false } },
{ XO("Cathedral"), { 90, 16, 90, 50, 100, 0, 0, -20, 100, false } },
};
struct Reverb_priv_t
{
reverb_t reverb;
float *dry;
float *wet[2];
};
//
// EffectReverb
//
const ComponentInterfaceSymbol EffectReverb::Symbol
{ XO("Reverb") };
namespace{ BuiltinEffectsModule::Registration< EffectReverb > reg; }
BEGIN_EVENT_TABLE(EffectReverb, wxEvtHandler)
#define SpinSliderEvent(n) \
EVT_SLIDER(ID_ ## n, EffectReverb::On ## n ## Slider) \
EVT_TEXT(ID_ ## n, EffectReverb::On ## n ## Text)
SpinSliderEvent(RoomSize)
SpinSliderEvent(PreDelay)
SpinSliderEvent(Reverberance)
SpinSliderEvent(HfDamping)
SpinSliderEvent(ToneLow)
SpinSliderEvent(ToneHigh)
SpinSliderEvent(WetGain)
SpinSliderEvent(DryGain)
SpinSliderEvent(StereoWidth)
#undef SpinSliderEvent
END_EVENT_TABLE()
EffectReverb::EffectReverb()
{
mParams.mRoomSize = DEF_RoomSize;
mParams.mPreDelay = DEF_PreDelay;
mParams.mReverberance = DEF_Reverberance;
mParams.mHfDamping = DEF_HfDamping;
mParams.mToneLow = DEF_ToneLow;
mParams.mToneHigh = DEF_ToneHigh;
mParams.mWetGain = DEF_WetGain;
mParams.mDryGain = DEF_DryGain;
mParams.mStereoWidth = DEF_StereoWidth;
mParams.mWetOnly = DEF_WetOnly;
mProcessingEvent = false;
SetLinearEffectFlag(true);
}
EffectReverb::~EffectReverb()
{
}
// ComponentInterface implementation
ComponentInterfaceSymbol EffectReverb::GetSymbol()
{
return Symbol;
}
TranslatableString EffectReverb::GetDescription()
{
return XO("Adds ambience or a \"hall effect\"");
}
ManualPageID EffectReverb::ManualPage()
{
return L"Reverb";
}
// EffectDefinitionInterface implementation
EffectType EffectReverb::GetType()
{
return EffectTypeProcess;
}
// EffectClientInterface implementation
unsigned EffectReverb::GetAudioInCount()
{
return mParams.mStereoWidth ? 2 : 1;
}
unsigned EffectReverb::GetAudioOutCount()
{
return mParams.mStereoWidth ? 2 : 1;
}
static size_t BLOCK = 16384;
bool EffectReverb::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames chanMap)
{
bool isStereo = false;
mNumChans = 1;
if (chanMap && chanMap[0] != ChannelNameEOL && chanMap[1] == ChannelNameFrontRight)
{
isStereo = true;
mNumChans = 2;
}
mP = (Reverb_priv_t *) calloc(sizeof(*mP), mNumChans);
for (unsigned int i = 0; i < mNumChans; i++)
{
reverb_create(&mP[i].reverb,
mSampleRate,
mParams.mWetGain,
mParams.mRoomSize,
mParams.mReverberance,
mParams.mHfDamping,
mParams.mPreDelay,
mParams.mStereoWidth * (isStereo ? 1 : 0),
mParams.mToneLow,
mParams.mToneHigh,
BLOCK,
mP[i].wet);
}
return true;
}
bool EffectReverb::ProcessFinalize()
{
for (unsigned int i = 0; i < mNumChans; i++)
{
reverb_delete(&mP[i].reverb);
}
free(mP);
return true;
}
size_t EffectReverb::ProcessBlock(float **inBlock, float **outBlock, size_t blockLen)
{
float *ichans[2] = {NULL, NULL};
float *ochans[2] = {NULL, NULL};
for (unsigned int c = 0; c < mNumChans; c++)
{
ichans[c] = inBlock[c];
ochans[c] = outBlock[c];
}
float const dryMult = mParams.mWetOnly ? 0 : dB_to_linear(mParams.mDryGain);
auto remaining = blockLen;
while (remaining)
{
auto len = std::min(remaining, decltype(remaining)(BLOCK));
for (unsigned int c = 0; c < mNumChans; c++)
{
// Write the input samples to the reverb fifo. Returned value is the address of the
// fifo buffer which contains a copy of the input samples.
mP[c].dry = (float *) fifo_write(&mP[c].reverb.input_fifo, len, ichans[c]);
reverb_process(&mP[c].reverb, len);
}
if (mNumChans == 2)
{
for (decltype(len) i = 0; i < len; i++)
{
for (int w = 0; w < 2; w++)
{
ochans[w][i] = dryMult *
mP[w].dry[i] +
0.5 *
(mP[0].wet[w][i] + mP[1].wet[w][i]);
}
}
}
else
{
for (decltype(len) i = 0; i < len; i++)
{
ochans[0][i] = dryMult *
mP[0].dry[i] +
mP[0].wet[0][i];
}
}
remaining -= len;
for (unsigned int c = 0; c < mNumChans; c++)
{
ichans[c] += len;
ochans[c] += len;
}
}
return blockLen;
}
bool EffectReverb::DefineParams( ShuttleParams & S ){
S.SHUTTLE_PARAM( mParams.mRoomSize, RoomSize );
S.SHUTTLE_PARAM( mParams.mPreDelay, PreDelay );
S.SHUTTLE_PARAM( mParams.mReverberance, Reverberance );
S.SHUTTLE_PARAM( mParams.mHfDamping, HfDamping );
S.SHUTTLE_PARAM( mParams.mToneLow, ToneLow );
S.SHUTTLE_PARAM( mParams.mToneHigh, ToneHigh );
S.SHUTTLE_PARAM( mParams.mWetGain, WetGain );
S.SHUTTLE_PARAM( mParams.mDryGain, DryGain );
S.SHUTTLE_PARAM( mParams.mStereoWidth, StereoWidth );
S.SHUTTLE_PARAM( mParams.mWetOnly, WetOnly );
return true;
}
bool EffectReverb::GetAutomationParameters(CommandParameters & parms)
{
parms.Write(KEY_RoomSize, mParams.mRoomSize);
parms.Write(KEY_PreDelay, mParams.mPreDelay);
parms.Write(KEY_Reverberance, mParams.mReverberance);
parms.Write(KEY_HfDamping, mParams.mHfDamping);
parms.Write(KEY_ToneLow, mParams.mToneLow);
parms.Write(KEY_ToneHigh, mParams.mToneHigh);
parms.Write(KEY_WetGain, mParams.mWetGain);
parms.Write(KEY_DryGain, mParams.mDryGain);
parms.Write(KEY_StereoWidth, mParams.mStereoWidth);
parms.Write(KEY_WetOnly, mParams.mWetOnly);
return true;
}
bool EffectReverb::SetAutomationParameters(CommandParameters & parms)
{
ReadAndVerifyDouble(RoomSize);
ReadAndVerifyDouble(PreDelay);
ReadAndVerifyDouble(Reverberance);
ReadAndVerifyDouble(HfDamping);
ReadAndVerifyDouble(ToneLow);
ReadAndVerifyDouble(ToneHigh);
ReadAndVerifyDouble(WetGain);
ReadAndVerifyDouble(DryGain);
ReadAndVerifyDouble(StereoWidth);
ReadAndVerifyBool(WetOnly);
mParams.mRoomSize = RoomSize;
mParams.mPreDelay = PreDelay;
mParams.mReverberance = Reverberance;
mParams.mHfDamping = HfDamping;
mParams.mToneLow = ToneLow;
mParams.mToneHigh = ToneHigh;
mParams.mWetGain = WetGain;
mParams.mDryGain = DryGain;
mParams.mStereoWidth = StereoWidth;
mParams.mWetOnly = WetOnly;
return true;
}
RegistryPaths EffectReverb::GetFactoryPresets()
{
RegistryPaths names;
for (size_t i = 0; i < WXSIZEOF(FactoryPresets); i++)
{
names.push_back( FactoryPresets[i].name.Translation() );
}
return names;
}
bool EffectReverb::LoadFactoryPreset(int id)
{
if (id < 0 || id >= (int) WXSIZEOF(FactoryPresets))
{
return false;
}
mParams = FactoryPresets[id].params;
if (mUIDialog)
{
TransferDataToWindow();
}
return true;
}
// Effect implementation
bool EffectReverb::Startup()
{
wxString base = wxT("/Effects/Reverb/");
// Migrate settings from 2.1.0 or before
// Already migrated, so bail
if (gPrefs->Exists(base + wxT("Migrated")))
{
return true;
}
// Load the old "current" settings
if (gPrefs->Exists(base))
{
gPrefs->Read(base + wxT("RoomSize"), &mParams.mRoomSize, DEF_RoomSize);
gPrefs->Read(base + wxT("Delay"), &mParams.mPreDelay, DEF_PreDelay);
gPrefs->Read(base + wxT("Reverberance"), &mParams.mReverberance, DEF_Reverberance);
gPrefs->Read(base + wxT("HfDamping"), &mParams.mHfDamping, DEF_HfDamping);
gPrefs->Read(base + wxT("ToneLow"), &mParams.mToneLow, DEF_ToneLow);
gPrefs->Read(base + wxT("ToneHigh"), &mParams.mToneHigh, DEF_ToneHigh);
gPrefs->Read(base + wxT("WetGain"), &mParams.mWetGain, DEF_WetGain);
gPrefs->Read(base + wxT("DryGain"), &mParams.mDryGain, DEF_DryGain);
gPrefs->Read(base + wxT("StereoWidth"), &mParams.mStereoWidth, DEF_StereoWidth);
gPrefs->Read(base + wxT("WetOnly"), &mParams.mWetOnly, DEF_WetOnly);
SaveUserPreset(GetCurrentSettingsGroup());
// Do not migrate again
gPrefs->Write(base + wxT("Migrated"), true);
}
// Load the previous user presets
for (int i = 0; i < 10; i++)
{
wxString path = base + wxString::Format(wxT("%d/"), i);
if (gPrefs->Exists(path))
{
Params save = mParams;
wxString name;
gPrefs->Read(path + wxT("RoomSize"), &mParams.mRoomSize, DEF_RoomSize);
gPrefs->Read(path + wxT("Delay"), &mParams.mPreDelay, DEF_PreDelay);
gPrefs->Read(path + wxT("Reverberance"), &mParams.mReverberance, DEF_Reverberance);
gPrefs->Read(path + wxT("HfDamping"), &mParams.mHfDamping, DEF_HfDamping);
gPrefs->Read(path + wxT("ToneLow"), &mParams.mToneLow, DEF_ToneLow);
gPrefs->Read(path + wxT("ToneHigh"), &mParams.mToneHigh, DEF_ToneHigh);
gPrefs->Read(path + wxT("WetGain"), &mParams.mWetGain, DEF_WetGain);
gPrefs->Read(path + wxT("DryGain"), &mParams.mDryGain, DEF_DryGain);
gPrefs->Read(path + wxT("StereoWidth"), &mParams.mStereoWidth, DEF_StereoWidth);
gPrefs->Read(path + wxT("WetOnly"), &mParams.mWetOnly, DEF_WetOnly);
gPrefs->Read(path + wxT("name"), &name, wxEmptyString);
if (!name.empty())
{
name.Prepend(wxT(" - "));
}
name.Prepend(wxString::Format(wxT("Settings%d"), i));
SaveUserPreset(GetUserPresetsGroup(name));
mParams = save;
}
}
return true;
}
void EffectReverb::PopulateOrExchange(ShuttleGui & S)
{
S.AddSpace(0, 5);
S.StartMultiColumn(3, wxEXPAND);
{
S.SetStretchyCol(2);
#define SpinSlider(n, p) \
m ## n ## T = S.Id(ID_ ## n). \
AddSpinCtrl( p, DEF_ ## n, MAX_ ## n, MIN_ ## n); \
S; \
m ## n ## S = S.Id(ID_ ## n) \
.Style(wxSL_HORIZONTAL) \
.AddSlider( {}, DEF_ ## n, MAX_ ## n, MIN_ ## n);
SpinSlider(RoomSize, XXO("&Room Size (%):"))
SpinSlider(PreDelay, XXO("&Pre-delay (ms):"))
SpinSlider(Reverberance, XXO("Rever&berance (%):"))
SpinSlider(HfDamping, XXO("Da&mping (%):"))
SpinSlider(ToneLow, XXO("Tone &Low (%):"))
SpinSlider(ToneHigh, XXO("Tone &High (%):"))
SpinSlider(WetGain, XXO("Wet &Gain (dB):"))
SpinSlider(DryGain, XXO("Dr&y Gain (dB):"))
SpinSlider(StereoWidth, XXO("Stereo Wid&th (%):"))
#undef SpinSlider
}
S.EndMultiColumn();
S.StartHorizontalLay(wxCENTER, false);
{
mWetOnlyC = S.Id(ID_WetOnly).
AddCheckBox(XXO("Wet O&nly"), DEF_WetOnly);
}
S.EndHorizontalLay();
return;
}
bool EffectReverb::TransferDataToWindow()
{
#define SetSpinSlider(n) \
m ## n ## S->SetValue((int) mParams.m ## n); \
m ## n ## T->SetValue(wxString::Format(wxT("%d"), (int) mParams.m ## n));
SetSpinSlider(RoomSize);
SetSpinSlider(PreDelay);
SetSpinSlider(Reverberance);
SetSpinSlider(HfDamping);
SetSpinSlider(ToneLow);
SetSpinSlider(ToneHigh);
SetSpinSlider(WetGain);
SetSpinSlider(DryGain);
SetSpinSlider(StereoWidth);
#undef SetSpinSlider
mWetOnlyC->SetValue((int) mParams.mWetOnly);
return true;
}
bool EffectReverb::TransferDataFromWindow()
{
if (!mUIParent->Validate())
{
return false;
}
mParams.mRoomSize = mRoomSizeS->GetValue();
mParams.mPreDelay = mPreDelayS->GetValue();
mParams.mReverberance = mReverberanceS->GetValue();
mParams.mHfDamping = mHfDampingS->GetValue();
mParams.mToneLow = mToneLowS->GetValue();
mParams.mToneHigh = mToneHighS->GetValue();
mParams.mWetGain = mWetGainS->GetValue();
mParams.mDryGain = mDryGainS->GetValue();
mParams.mStereoWidth = mStereoWidthS->GetValue();
mParams.mWetOnly = mWetOnlyC->GetValue();
return true;
}
#define SpinSliderHandlers(n) \
void EffectReverb::On ## n ## Slider(wxCommandEvent & evt) \
{ \
if (mProcessingEvent) return; \
mProcessingEvent = true; \
m ## n ## T->SetValue(wxString::Format(wxT("%d"), evt.GetInt())); \
mProcessingEvent = false; \
} \
void EffectReverb::On ## n ## Text(wxCommandEvent & evt) \
{ \
if (mProcessingEvent) return; \
mProcessingEvent = true; \
m ## n ## S->SetValue(TrapLong(evt.GetInt(), MIN_ ## n, MAX_ ## n)); \
mProcessingEvent = false; \
}
SpinSliderHandlers(RoomSize)
SpinSliderHandlers(PreDelay)
SpinSliderHandlers(Reverberance)
SpinSliderHandlers(HfDamping)
SpinSliderHandlers(ToneLow)
SpinSliderHandlers(ToneHigh)
SpinSliderHandlers(WetGain)
SpinSliderHandlers(DryGain)
SpinSliderHandlers(StereoWidth)
#undef SpinSliderHandlers
void EffectReverb::SetTitle(const wxString & name)
{
mUIDialog->SetTitle(
name.empty()
? _("Reverb")
: wxString::Format( _("Reverb: %s"), name )
);
}