1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-17 00:20:06 +02:00
audacity/src/effects/Reverb.cpp
Paul Licameli 4d09705a73 Change XO to XXO in many more places, with no effects at all...
... because the two macros have the same expansion, and are both checked for
in the --keyword arguments passed to msgfmt by locale/update_po_files.sh.

This commit makes ONLY such changes, and comments in Internat.h.  It is big
but quite harmless.

The intention is to introduce a type distinction in a later release, by defining
XXO differently.  XXO is used where & characters in strings (for hotkeys of menu
items or control prompts) are permitted, XO where not.
2020-05-22 13:07:50 -04:00

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 "../Audacity.h"
#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\"");
}
wxString EffectReverb::ManualPage()
{
return wxT("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 )
);
}