mirror of
https://github.com/cookiengineer/audacity
synced 2026-03-18 04:06:39 +01:00
It combines the old IdentInterface with the ParamsInterface, providing an identifier and parameters (if needed). The main purpose of the change is to make the class hierarchy (as viewed via doxygen) much easier to follow.
507 lines
13 KiB
C++
507 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 <math.h>
|
|
|
|
#include <wx/intl.h>
|
|
|
|
#include "../ShuttleGui.h"
|
|
#include "../widgets/valnum.h"
|
|
|
|
#include "../Experimental.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
|
|
//
|
|
|
|
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 WAHWAH_PLUGIN_SYMBOL;
|
|
}
|
|
|
|
wxString EffectWahwah::GetDescription()
|
|
{
|
|
return _("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);
|
|
|
|
FloatingPointValidator<double> vldfreq(5, &mFreq, NumValidatorStyle::ONE_TRAILING_ZERO);
|
|
vldfreq.SetRange(MIN_Freq, MAX_Freq);
|
|
mFreqT = S.Id(ID_Freq).AddTextBox(_("LFO Freq&uency (Hz):"), wxT(""), 12);
|
|
mFreqT->SetValidator(vldfreq);
|
|
|
|
S.SetStyle(wxSL_HORIZONTAL);
|
|
mFreqS = S.Id(ID_Freq).AddSlider( {}, DEF_Freq * SCL_Freq, MAX_Freq * SCL_Freq, MIN_Freq * SCL_Freq);
|
|
mFreqS->SetName(_("LFO frequency in hertz"));
|
|
mFreqS->SetMinSize(wxSize(100, -1));
|
|
|
|
FloatingPointValidator<double> vldphase(1, &mPhase);
|
|
vldphase.SetRange(MIN_Phase, MAX_Phase);
|
|
mPhaseT = S.Id(ID_Phase).AddTextBox(_("LFO Sta&rt Phase (deg.):"), wxT(""), 12);
|
|
mPhaseT->SetValidator(vldphase);
|
|
|
|
S.SetStyle(wxSL_HORIZONTAL);
|
|
mPhaseS = S.Id(ID_Phase).AddSlider( {}, DEF_Phase * SCL_Phase, MAX_Phase * SCL_Phase, MIN_Phase * SCL_Phase);
|
|
mPhaseS->SetName(_("LFO start phase in degrees"));
|
|
mPhaseS->SetLineSize(10);
|
|
mPhaseS->SetMinSize(wxSize(100, -1));
|
|
|
|
IntegerValidator<int> vlddepth(&mDepth);
|
|
vlddepth.SetRange(MIN_Depth, MAX_Depth);
|
|
mDepthT = S.Id(ID_Depth).AddTextBox(_("Dept&h (%):"), wxT(""), 12);
|
|
mDepthT->SetValidator(vlddepth);
|
|
|
|
S.SetStyle(wxSL_HORIZONTAL);
|
|
mDepthS = S.Id(ID_Depth).AddSlider( {}, DEF_Depth * SCL_Depth, MAX_Depth * SCL_Depth, MIN_Depth * SCL_Depth);
|
|
mDepthS->SetName(_("Depth in percent"));
|
|
mDepthS->SetMinSize(wxSize(100, -1));
|
|
|
|
FloatingPointValidator<double> vldres(1, &mRes);
|
|
vldres.SetRange(MIN_Res, MAX_Res);
|
|
mResT = S.Id(ID_Res).AddTextBox(_("Reso&nance:"), wxT(""), 12);
|
|
mResT->SetValidator(vldres);
|
|
|
|
S.SetStyle(wxSL_HORIZONTAL);
|
|
mResS = S.Id(ID_Res).AddSlider( {}, DEF_Res * SCL_Res, MAX_Res * SCL_Res, MIN_Res * SCL_Res);
|
|
mResS->SetName(_("Resonance"));
|
|
mResS->SetMinSize(wxSize(100, -1));
|
|
|
|
IntegerValidator<int> vldfreqoffset(&mFreqOfs);
|
|
vldfreqoffset.SetRange(MIN_FreqOfs, MAX_FreqOfs);
|
|
mFreqOfsT = S.Id(ID_FreqOfs).AddTextBox(_("Wah Frequency Offse&t (%):"), wxT(""), 12);
|
|
mFreqOfsT->SetValidator(vldfreqoffset);
|
|
|
|
S.SetStyle(wxSL_HORIZONTAL);
|
|
mFreqOfsS = S.Id(ID_FreqOfs).AddSlider( {}, DEF_FreqOfs * SCL_FreqOfs, MAX_FreqOfs * SCL_FreqOfs, MIN_FreqOfs * SCL_FreqOfs);
|
|
mFreqOfsT->SetName(_("Wah frequency offset in percent"));
|
|
mFreqOfsT->SetMinSize(wxSize(100, -1));
|
|
|
|
FloatingPointValidator<double> vldoutgain(1, &mOutGain);
|
|
vldoutgain.SetRange(MIN_OutGain, MAX_OutGain);
|
|
mOutGainT = S.Id(ID_OutGain).AddTextBox(_("&Output gain (dB):"), wxT(""), 12);
|
|
mOutGainT->SetValidator(vldoutgain);
|
|
|
|
S.SetStyle(wxSL_HORIZONTAL);
|
|
mOutGainS = S.Id(ID_OutGain).AddSlider( {}, DEF_OutGain * SCL_OutGain, MAX_OutGain * SCL_OutGain, MIN_OutGain * SCL_OutGain);
|
|
mOutGainS->SetName(_("Output gain (dB)"));
|
|
mOutGainS->SetMinSize(wxSize(100, -1));
|
|
}
|
|
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));
|
|
}
|