1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00
audacity/src/effects/Wahwah.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

521 lines
13 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Wahwah.cpp
Effect programming:
Nasca Octavian Paul (Paul Nasca)
UI programming:
Dominic Mazzoni (with the help of wxDesigner)
Vaughan Johnson (Preview)
*******************************************************************//**
\class EffectWahwah
\brief An Effect that adds a 'spectral glide'.
*//*******************************************************************/
#include "../Audacity.h"
#include "Wahwah.h"
#include "LoadEffects.h"
#include "../Experimental.h"
#include <math.h>
#include <wx/intl.h>
#include <wx/slider.h>
#include "../Shuttle.h"
#include "../ShuttleGui.h"
#include "../widgets/valnum.h"
enum
{
ID_Freq = 10000,
ID_Phase,
ID_Depth,
ID_Res,
ID_FreqOfs,
ID_OutGain
};
// Define keys, defaults, minimums, and maximums for the effect parameters
//
// Name Type Key Def Min Max Scale
Param( Freq, double, wxT("Freq"), 1.5, 0.1, 4.0, 10 );
Param( Phase, double, wxT("Phase"), 0.0, 0.0, 360.0, 1 );
Param( Depth, int, wxT("Depth"), 70, 0, 100, 1 ); // scaled to 0-1 before processing
Param( Res, double, wxT("Resonance"), 2.5, 0.1, 10.0, 10 );
Param( FreqOfs, int, wxT("Offset"), 30, 0, 100, 1 ); // scaled to 0-1 before processing
Param( OutGain, double, wxT("Gain"), -6.0, -30.0, 30.0, 1 );
// How many samples are processed before recomputing the lfo value again
#define lfoskipsamples 30
//
// EffectWahwah
//
const ComponentInterfaceSymbol EffectWahwah::Symbol
{ XO("Wahwah") };
namespace{ BuiltinEffectsModule::Registration< EffectWahwah > reg; }
BEGIN_EVENT_TABLE(EffectWahwah, wxEvtHandler)
EVT_SLIDER(ID_Freq, EffectWahwah::OnFreqSlider)
EVT_SLIDER(ID_Phase, EffectWahwah::OnPhaseSlider)
EVT_SLIDER(ID_Depth, EffectWahwah::OnDepthSlider)
EVT_SLIDER(ID_Res, EffectWahwah::OnResonanceSlider)
EVT_SLIDER(ID_FreqOfs, EffectWahwah::OnFreqOffSlider)
EVT_SLIDER(ID_OutGain, EffectWahwah::OnGainSlider)
EVT_TEXT(ID_Freq, EffectWahwah::OnFreqText)
EVT_TEXT(ID_Phase, EffectWahwah::OnPhaseText)
EVT_TEXT(ID_Depth, EffectWahwah::OnDepthText)
EVT_TEXT(ID_Res, EffectWahwah::OnResonanceText)
EVT_TEXT(ID_FreqOfs, EffectWahwah::OnFreqOffText)
EVT_TEXT(ID_OutGain, EffectWahwah::OnGainText)
END_EVENT_TABLE();
EffectWahwah::EffectWahwah()
{
mFreq = DEF_Freq;
mPhase = DEF_Phase;
mDepth = DEF_Depth;
mRes = DEF_Res;
mFreqOfs = DEF_FreqOfs;
mOutGain = DEF_OutGain;
SetLinearEffectFlag(true);
}
EffectWahwah::~EffectWahwah()
{
}
// ComponentInterface implementation
ComponentInterfaceSymbol EffectWahwah::GetSymbol()
{
return Symbol;
}
TranslatableString EffectWahwah::GetDescription()
{
return XO("Rapid tone quality variations, like that guitar sound so popular in the 1970's");
}
wxString EffectWahwah::ManualPage()
{
return wxT("Wahwah");
}
// EffectDefinitionInterface implementation
EffectType EffectWahwah::GetType()
{
return EffectTypeProcess;
}
bool EffectWahwah::SupportsRealtime()
{
#if defined(EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS)
return true;
#else
return false;
#endif
}
// EffectClientInterface implementation
unsigned EffectWahwah::GetAudioInCount()
{
return 1;
}
unsigned EffectWahwah::GetAudioOutCount()
{
return 1;
}
bool EffectWahwah::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames chanMap)
{
InstanceInit(mMaster, mSampleRate);
if (chanMap[0] == ChannelNameFrontRight)
{
mMaster.phase += M_PI;
}
return true;
}
size_t EffectWahwah::ProcessBlock(float **inBlock, float **outBlock, size_t blockLen)
{
return InstanceProcess(mMaster, inBlock, outBlock, blockLen);
}
bool EffectWahwah::RealtimeInitialize()
{
SetBlockSize(512);
mSlaves.clear();
return true;
}
bool EffectWahwah::RealtimeAddProcessor(unsigned WXUNUSED(numChannels), float sampleRate)
{
EffectWahwahState slave;
InstanceInit(slave, sampleRate);
mSlaves.push_back(slave);
return true;
}
bool EffectWahwah::RealtimeFinalize()
{
mSlaves.clear();
return true;
}
size_t EffectWahwah::RealtimeProcess(int group,
float **inbuf,
float **outbuf,
size_t numSamples)
{
return InstanceProcess(mSlaves[group], inbuf, outbuf, numSamples);
}
bool EffectWahwah::DefineParams( ShuttleParams & S ){
S.SHUTTLE_PARAM( mFreq, Freq );
S.SHUTTLE_PARAM( mPhase, Phase );
S.SHUTTLE_PARAM( mDepth, Depth );
S.SHUTTLE_PARAM( mRes, Res );
S.SHUTTLE_PARAM( mFreqOfs, FreqOfs );
S.SHUTTLE_PARAM( mOutGain, OutGain );
return true;
}
bool EffectWahwah::GetAutomationParameters(CommandParameters & parms)
{
parms.Write(KEY_Freq, mFreq);
parms.Write(KEY_Phase, mPhase);
parms.Write(KEY_Depth, mDepth);
parms.Write(KEY_Res, mRes);
parms.Write(KEY_FreqOfs, mFreqOfs);
parms.Write(KEY_OutGain, mOutGain);
return true;
}
bool EffectWahwah::SetAutomationParameters(CommandParameters & parms)
{
ReadAndVerifyDouble(Freq);
ReadAndVerifyDouble(Phase);
ReadAndVerifyInt(Depth);
ReadAndVerifyDouble(Res);
ReadAndVerifyInt(FreqOfs);
ReadAndVerifyDouble(OutGain);
mFreq = Freq;
mPhase = Phase;
mDepth = Depth;
mRes = Res;
mFreqOfs = FreqOfs;
mOutGain = OutGain;
return true;
}
// Effect implementation
void EffectWahwah::PopulateOrExchange(ShuttleGui & S)
{
S.SetBorder(5);
S.AddSpace(0, 5);
S.StartMultiColumn(3, wxEXPAND);
{
S.SetStretchyCol(2);
mFreqT = S.Id(ID_Freq)
.Validator<FloatingPointValidator<double>>(
5, &mFreq, NumValidatorStyle::ONE_TRAILING_ZERO, MIN_Freq, MAX_Freq)
.AddTextBox(XXO("LFO Freq&uency (Hz):"), wxT(""), 12);
mFreqS = S.Id(ID_Freq)
.Name(XO("LFO frequency in hertz"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 100, -1 } )
.AddSlider( {}, DEF_Freq * SCL_Freq, MAX_Freq * SCL_Freq, MIN_Freq * SCL_Freq);
mPhaseT = S.Id(ID_Phase)
.Validator<FloatingPointValidator<double>>(
1, &mPhase, NumValidatorStyle::DEFAULT, MIN_Phase, MAX_Phase)
.AddTextBox(XXO("LFO Sta&rt Phase (deg.):"), wxT(""), 12);
mPhaseS = S.Id(ID_Phase)
.Name(XO("LFO start phase in degrees"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 100, -1 } )
.AddSlider( {}, DEF_Phase * SCL_Phase, MAX_Phase * SCL_Phase, MIN_Phase * SCL_Phase);
mPhaseS->SetLineSize(10);
mDepthT = S.Id(ID_Depth)
.Validator<IntegerValidator<int>>(
&mDepth, NumValidatorStyle::DEFAULT, MIN_Depth, MAX_Depth)
.AddTextBox(XXO("Dept&h (%):"), wxT(""), 12);
mDepthS = S.Id(ID_Depth)
.Name(XO("Depth in percent"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 100, -1 } )
.AddSlider( {}, DEF_Depth * SCL_Depth, MAX_Depth * SCL_Depth, MIN_Depth * SCL_Depth);
mResT = S.Id(ID_Res)
.Validator<FloatingPointValidator<double>>(
1, &mRes, NumValidatorStyle::DEFAULT, MIN_Res, MAX_Res)
.AddTextBox(XXO("Reso&nance:"), wxT(""), 12);
mResS = S.Id(ID_Res)
.Name(XO("Resonance"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 100, -1 } )
.AddSlider( {}, DEF_Res * SCL_Res, MAX_Res * SCL_Res, MIN_Res * SCL_Res);
mFreqOfsT = S.Id(ID_FreqOfs)
.Validator<IntegerValidator<int>>(
&mFreqOfs, NumValidatorStyle::DEFAULT, MIN_FreqOfs, MAX_FreqOfs)
.AddTextBox(XXO("Wah Frequency Offse&t (%):"), wxT(""), 12);
mFreqOfsS = S.Id(ID_FreqOfs)
.Name(XO("Wah frequency offset in percent"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 100, -1 } )
.AddSlider( {}, DEF_FreqOfs * SCL_FreqOfs, MAX_FreqOfs * SCL_FreqOfs, MIN_FreqOfs * SCL_FreqOfs);
mOutGainT = S.Id(ID_OutGain)
.Validator<FloatingPointValidator<double>>(
1, &mOutGain, NumValidatorStyle::DEFAULT, MIN_OutGain, MAX_OutGain)
.AddTextBox(XXO("&Output gain (dB):"), wxT(""), 12);
mOutGainS = S.Id(ID_OutGain)
.Name(XO("Output gain (dB)"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 100, -1 } )
.AddSlider( {}, DEF_OutGain * SCL_OutGain, MAX_OutGain * SCL_OutGain, MIN_OutGain * SCL_OutGain);
}
S.EndMultiColumn();
}
bool EffectWahwah::TransferDataToWindow()
{
if (!mUIParent->TransferDataToWindow())
{
return false;
}
mFreqS->SetValue((int) (mFreq * SCL_Freq));
mPhaseS->SetValue((int) (mPhase * SCL_Phase));
mDepthS->SetValue((int) (mDepth * SCL_Depth));
mResS->SetValue((int) (mRes * SCL_Res));
mFreqOfsS->SetValue((int) (mFreqOfs * SCL_FreqOfs));
mOutGainS->SetValue((int) (mOutGain * SCL_OutGain));
return true;
}
bool EffectWahwah::TransferDataFromWindow()
{
if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
{
return false;
}
return true;
}
// EffectWahwah implementation
void EffectWahwah::InstanceInit(EffectWahwahState & data, float sampleRate)
{
data.samplerate = sampleRate;
data.lfoskip = mFreq * 2 * M_PI / sampleRate;
data.skipcount = 0;
data.xn1 = 0;
data.xn2 = 0;
data.yn1 = 0;
data.yn2 = 0;
data.b0 = 0;
data.b1 = 0;
data.b2 = 0;
data.a0 = 0;
data.a1 = 0;
data.a2 = 0;
data.depth = mDepth / 100.0;
data.freqofs = mFreqOfs / 100.0;
data.phase = mPhase * M_PI / 180.0;
data.outgain = DB_TO_LINEAR(mOutGain);
}
size_t EffectWahwah::InstanceProcess(EffectWahwahState & data, float **inBlock, float **outBlock, size_t blockLen)
{
float *ibuf = inBlock[0];
float *obuf = outBlock[0];
double frequency, omega, sn, cs, alpha;
double in, out;
data.lfoskip = mFreq * 2 * M_PI / data.samplerate;
data.depth = mDepth / 100.0;
data.freqofs = mFreqOfs / 100.0;
data.phase = mPhase * M_PI / 180.0;
data.outgain = DB_TO_LINEAR(mOutGain);
for (decltype(blockLen) i = 0; i < blockLen; i++)
{
in = (double) ibuf[i];
if ((data.skipcount++) % lfoskipsamples == 0)
{
frequency = (1 + cos(data.skipcount * data.lfoskip + data.phase)) / 2;
frequency = frequency * data.depth * (1 - data.freqofs) + data.freqofs;
frequency = exp((frequency - 1) * 6);
omega = M_PI * frequency;
sn = sin(omega);
cs = cos(omega);
alpha = sn / (2 * mRes);
data.b0 = (1 - cs) / 2;
data.b1 = 1 - cs;
data.b2 = (1 - cs) / 2;
data.a0 = 1 + alpha;
data.a1 = -2 * cs;
data.a2 = 1 - alpha;
};
out = (data.b0 * in + data.b1 * data.xn1 + data.b2 * data.xn2 - data.a1 * data.yn1 - data.a2 * data.yn2) / data.a0;
data.xn2 = data.xn1;
data.xn1 = in;
data.yn2 = data.yn1;
data.yn1 = out;
out *= data.outgain;
obuf[i] = (float) out;
}
return blockLen;
}
void EffectWahwah::OnFreqSlider(wxCommandEvent & evt)
{
mFreq = (double) evt.GetInt() / SCL_Freq;
mFreqT->GetValidator()->TransferToWindow();
EnableApply(mUIParent->Validate());
}
void EffectWahwah::OnPhaseSlider(wxCommandEvent & evt)
{
int val = ((evt.GetInt() + 5) / 10) * 10; // round to nearest multiple of 10
val = val > MAX_Phase * SCL_Phase ? MAX_Phase * SCL_Phase : val;
mPhaseS->SetValue(val);
mPhase = (double) val / SCL_Phase;
mPhaseT->GetValidator()->TransferToWindow();
EnableApply(mUIParent->Validate());
}
void EffectWahwah::OnDepthSlider(wxCommandEvent & evt)
{
mDepth = evt.GetInt() / SCL_Depth;
mDepthT->GetValidator()->TransferToWindow();
EnableApply(mUIParent->Validate());
}
void EffectWahwah::OnResonanceSlider(wxCommandEvent & evt)
{
mRes = (double) evt.GetInt() / SCL_Res;
mResT->GetValidator()->TransferToWindow();
EnableApply(mUIParent->Validate());
}
void EffectWahwah::OnFreqOffSlider(wxCommandEvent & evt)
{
mFreqOfs = evt.GetInt() / SCL_FreqOfs;
mFreqOfsT->GetValidator()->TransferToWindow();
EnableApply(mUIParent->Validate());
}
void EffectWahwah::OnGainSlider(wxCommandEvent & evt)
{
mOutGain = evt.GetInt() / SCL_OutGain;
mOutGainT->GetValidator()->TransferToWindow();
EnableApply(mUIParent->Validate());
}
void EffectWahwah::OnFreqText(wxCommandEvent & WXUNUSED(evt))
{
if (!EnableApply(mUIParent->TransferDataFromWindow()))
{
return;
}
mFreqS->SetValue((int) (mFreq * SCL_Freq));
}
void EffectWahwah::OnPhaseText(wxCommandEvent & WXUNUSED(evt))
{
if (!EnableApply(mUIParent->TransferDataFromWindow()))
{
return;
}
mPhaseS->SetValue((int) (mPhase * SCL_Phase));
}
void EffectWahwah::OnDepthText(wxCommandEvent & WXUNUSED(evt))
{
if (!EnableApply(mUIParent->TransferDataFromWindow()))
{
return;
}
mDepthS->SetValue((int) (mDepth * SCL_Depth));
}
void EffectWahwah::OnResonanceText(wxCommandEvent & WXUNUSED(evt))
{
if (!EnableApply(mUIParent->TransferDataFromWindow()))
{
return;
}
mResS->SetValue((int) (mRes * SCL_Res));
}
void EffectWahwah::OnFreqOffText(wxCommandEvent & WXUNUSED(evt))
{
if (!EnableApply(mUIParent->TransferDataFromWindow()))
{
return;
}
mFreqOfsS->SetValue((int) (mFreqOfs * SCL_FreqOfs));
}
void EffectWahwah::OnGainText(wxCommandEvent & WXUNUSED(evt))
{
if (!EnableApply(mUIParent->TransferDataFromWindow()))
{
return;
}
mOutGainS->SetValue((int) (mOutGain * SCL_OutGain));
}