mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-21 14:02:57 +02:00
Experimental RTP for Phaser and Wahwah
This can be disabled by undefining EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS in Experimental.cpp
This commit is contained in:
@@ -150,6 +150,9 @@
|
|||||||
// to enable.
|
// to enable.
|
||||||
#define EXPERIMENTAL_MODULE_PREFS
|
#define EXPERIMENTAL_MODULE_PREFS
|
||||||
|
|
||||||
|
// Define to allow realtime processing in Audacity effects that have been converted.
|
||||||
|
#define EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS
|
||||||
|
|
||||||
// Define to include the effects rack (such as it is).
|
// Define to include the effects rack (such as it is).
|
||||||
//#define EXPERIMENTAL_EFFECTS_RACK
|
//#define EXPERIMENTAL_EFFECTS_RACK
|
||||||
|
|
||||||
|
@@ -421,6 +421,8 @@ bool Effect::RealtimeInitialize()
|
|||||||
return mClient->RealtimeInitialize();
|
return mClient->RealtimeInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mBlockSize = 512;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,9 +457,15 @@ bool Effect::RealtimeSuspend()
|
|||||||
mRealtimeSuspendLock.Leave();
|
mRealtimeSuspendLock.Leave();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
mRealtimeSuspendLock.Enter();
|
||||||
|
mRealtimeSuspendCount++;
|
||||||
|
mRealtimeSuspendLock.Leave();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Effect::RealtimeResume()
|
bool Effect::RealtimeResume()
|
||||||
@@ -471,9 +479,15 @@ bool Effect::RealtimeResume()
|
|||||||
mRealtimeSuspendLock.Leave();
|
mRealtimeSuspendLock.Leave();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
mRealtimeSuspendLock.Enter();
|
||||||
|
mRealtimeSuspendCount--;
|
||||||
|
mRealtimeSuspendLock.Leave();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Effect::RealtimeProcessStart()
|
bool Effect::RealtimeProcessStart()
|
||||||
@@ -677,6 +691,7 @@ bool Effect::CloseUI()
|
|||||||
mUIParent->RemoveEventHandler(this);
|
mUIParent->RemoveEventHandler(this);
|
||||||
|
|
||||||
mUIParent = NULL;
|
mUIParent = NULL;
|
||||||
|
mUIDialog = NULL;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2270,7 +2285,7 @@ bool Effect::RealtimeAddProcessor(int group, int chans, float rate)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add a new processor
|
// Add a new processor
|
||||||
mClient->RealtimeAddProcessor(gchans, rate);
|
RealtimeAddProcessor(gchans, rate);
|
||||||
|
|
||||||
// Bump to next processor
|
// Bump to next processor
|
||||||
mCurrentProcessor++;
|
mCurrentProcessor++;
|
||||||
@@ -2377,7 +2392,7 @@ sampleCount Effect::RealtimeProcess(int group,
|
|||||||
for (sampleCount block = 0; block < numSamples; block += mBlockSize)
|
for (sampleCount block = 0; block < numSamples; block += mBlockSize)
|
||||||
{
|
{
|
||||||
sampleCount cnt = (block + mBlockSize > numSamples ? numSamples - block : mBlockSize);
|
sampleCount cnt = (block + mBlockSize > numSamples ? numSamples - block : mBlockSize);
|
||||||
len += mClient->RealtimeProcess(processor, clientIn, clientOut, cnt);
|
len += RealtimeProcess(processor, clientIn, clientOut, cnt);
|
||||||
|
|
||||||
for (int i = 0 ; i < mNumAudioIn; i++)
|
for (int i = 0 ; i < mNumAudioIn; i++)
|
||||||
{
|
{
|
||||||
|
@@ -56,6 +56,9 @@ Param( Feedback, int, XO("Feedback"), 0, -100, 100, 1 );
|
|||||||
// How many samples are processed before recomputing the lfo value again
|
// How many samples are processed before recomputing the lfo value again
|
||||||
#define lfoskipsamples 20
|
#define lfoskipsamples 20
|
||||||
|
|
||||||
|
#include <wx/arrimpl.cpp>
|
||||||
|
WX_DEFINE_OBJARRAY(EffectPhaserStateArray);
|
||||||
|
|
||||||
//
|
//
|
||||||
// EffectPhaser
|
// EffectPhaser
|
||||||
//
|
//
|
||||||
@@ -110,6 +113,15 @@ EffectType EffectPhaser::GetType()
|
|||||||
return EffectTypeProcess;
|
return EffectTypeProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EffectPhaser::SupportsRealtime()
|
||||||
|
{
|
||||||
|
#if defined(EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// EffectClientInterface implementation
|
// EffectClientInterface implementation
|
||||||
|
|
||||||
int EffectPhaser::GetAudioInCount()
|
int EffectPhaser::GetAudioInCount()
|
||||||
@@ -124,20 +136,10 @@ int EffectPhaser::GetAudioOutCount()
|
|||||||
|
|
||||||
bool EffectPhaser::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames chanMap)
|
bool EffectPhaser::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames chanMap)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < mStages; j++)
|
InstanceInit(mMaster, mSampleRate);
|
||||||
{
|
|
||||||
old[j] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
skipcount = 0;
|
|
||||||
gain = 0;
|
|
||||||
fbout = 0;
|
|
||||||
lfoskip = mFreq * 2 * M_PI / mSampleRate;
|
|
||||||
|
|
||||||
phase = mPhase * M_PI / 180;
|
|
||||||
if (chanMap[0] == ChannelNameFrontRight)
|
if (chanMap[0] == ChannelNameFrontRight)
|
||||||
{
|
{
|
||||||
phase += M_PI;
|
mMaster.phase += M_PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -145,40 +147,43 @@ bool EffectPhaser::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelName
|
|||||||
|
|
||||||
sampleCount EffectPhaser::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
|
sampleCount EffectPhaser::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
|
||||||
{
|
{
|
||||||
float *ibuf = inBlock[0];
|
return InstanceProcess(mMaster, inBlock, outBlock, blockLen);
|
||||||
float *obuf = outBlock[0];
|
}
|
||||||
|
|
||||||
for (sampleCount i = 0; i < blockLen; i++)
|
bool EffectPhaser::RealtimeInitialize()
|
||||||
{
|
{
|
||||||
double in = ibuf[i];
|
SetBlockSize(512);
|
||||||
|
|
||||||
double m = in + fbout * mFeedback / 101; // Feedback must be less than 100% to avoid infinite gain.
|
mSlaves.Clear();
|
||||||
|
|
||||||
if (((skipcount++) % lfoskipsamples) == 0)
|
return true;
|
||||||
{
|
}
|
||||||
//compute sine between 0 and 1
|
|
||||||
gain = (1.0 + cos(skipcount * lfoskip + phase)) / 2.0;
|
|
||||||
|
|
||||||
// change lfo shape
|
bool EffectPhaser::RealtimeAddProcessor(int WXUNUSED(numChannels), float sampleRate)
|
||||||
gain = expm1(gain * phaserlfoshape) / expm1(phaserlfoshape);
|
{
|
||||||
|
EffectPhaserState slave;
|
||||||
|
|
||||||
// attenuate the lfo
|
InstanceInit(slave, sampleRate);
|
||||||
gain = 1.0 - gain / 255.0 * mDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// phasing routine
|
mSlaves.Add(slave);
|
||||||
for (int j = 0; j < mStages; j++)
|
|
||||||
{
|
|
||||||
double tmp = old[j];
|
|
||||||
old[j] = gain * tmp + m;
|
|
||||||
m = tmp - gain * old[j];
|
|
||||||
}
|
|
||||||
fbout = m;
|
|
||||||
|
|
||||||
obuf[i] = (float) ((m * mDryWet + in * (255 - mDryWet)) / 255);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return blockLen;
|
bool EffectPhaser::RealtimeFinalize()
|
||||||
|
{
|
||||||
|
mSlaves.Clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleCount EffectPhaser::RealtimeProcess(int group,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples)
|
||||||
|
{
|
||||||
|
|
||||||
|
return InstanceProcess(mSlaves[group], inbuf, outbuf, numSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EffectPhaser::GetAutomationParameters(EffectAutomationParameters & parms)
|
bool EffectPhaser::GetAutomationParameters(EffectAutomationParameters & parms)
|
||||||
@@ -329,6 +334,70 @@ bool EffectPhaser::TransferDataFromWindow()
|
|||||||
|
|
||||||
// EffectPhaser implementation
|
// EffectPhaser implementation
|
||||||
|
|
||||||
|
void EffectPhaser::InstanceInit(EffectPhaserState & data, float sampleRate)
|
||||||
|
{
|
||||||
|
data.samplerate = sampleRate;
|
||||||
|
|
||||||
|
for (int j = 0; j < mStages; j++)
|
||||||
|
{
|
||||||
|
data.old[j] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.skipcount = 0;
|
||||||
|
data.gain = 0;
|
||||||
|
data.fbout = 0;
|
||||||
|
data.laststages = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleCount EffectPhaser::InstanceProcess(EffectPhaserState & data, float **inBlock, float **outBlock, sampleCount blockLen)
|
||||||
|
{
|
||||||
|
float *ibuf = inBlock[0];
|
||||||
|
float *obuf = outBlock[0];
|
||||||
|
|
||||||
|
for (int j = data.laststages; j < mStages; j++)
|
||||||
|
{
|
||||||
|
data.old[j] = 0;
|
||||||
|
}
|
||||||
|
data.laststages = mStages;
|
||||||
|
|
||||||
|
data.lfoskip = mFreq * 2 * M_PI / data.samplerate;
|
||||||
|
data.phase = mPhase * M_PI / 180;
|
||||||
|
|
||||||
|
for (sampleCount i = 0; i < blockLen; i++)
|
||||||
|
{
|
||||||
|
double in = ibuf[i];
|
||||||
|
|
||||||
|
double m = in + data.fbout * mFeedback / 101; // Feedback must be less than 100% to avoid infinite gain.
|
||||||
|
|
||||||
|
if (((data.skipcount++) % lfoskipsamples) == 0)
|
||||||
|
{
|
||||||
|
//compute sine between 0 and 1
|
||||||
|
data.gain = (1.0 + cos(data.skipcount * data.lfoskip + data.phase)) / 2.0;
|
||||||
|
|
||||||
|
// change lfo shape
|
||||||
|
data.gain = expm1(data.gain * phaserlfoshape) / expm1(phaserlfoshape);
|
||||||
|
|
||||||
|
// attenuate the lfo
|
||||||
|
data.gain = 1.0 - data.gain / 255.0 * mDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
// phasing routine
|
||||||
|
for (int j = 0; j < mStages; j++)
|
||||||
|
{
|
||||||
|
double tmp = data.old[j];
|
||||||
|
data.old[j] = data.gain * tmp + m;
|
||||||
|
m = tmp - data.gain * data.old[j];
|
||||||
|
}
|
||||||
|
data.fbout = m;
|
||||||
|
|
||||||
|
obuf[i] = (float) ((m * mDryWet + in * (255 - mDryWet)) / 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockLen;
|
||||||
|
}
|
||||||
|
|
||||||
void EffectPhaser::OnStagesSlider(wxCommandEvent & evt)
|
void EffectPhaser::OnStagesSlider(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
mStages = (evt.GetInt() / SCL_Stages) & ~1; // must be even;
|
mStages = (evt.GetInt() / SCL_Stages) & ~1; // must be even;
|
||||||
|
@@ -29,6 +29,22 @@ class ShuttleGui;
|
|||||||
|
|
||||||
#define PHASER_PLUGIN_SYMBOL XO("Phaser")
|
#define PHASER_PLUGIN_SYMBOL XO("Phaser")
|
||||||
|
|
||||||
|
class EffectPhaserState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// state variables
|
||||||
|
float samplerate;
|
||||||
|
sampleCount skipcount;
|
||||||
|
double old[NUM_STAGES]; // must be as large as MAX_STAGES
|
||||||
|
double gain;
|
||||||
|
double fbout;
|
||||||
|
double lfoskip;
|
||||||
|
double phase;
|
||||||
|
int laststages;
|
||||||
|
};
|
||||||
|
|
||||||
|
WX_DECLARE_OBJARRAY(EffectPhaserState, EffectPhaserStateArray);
|
||||||
|
|
||||||
class EffectPhaser : public Effect
|
class EffectPhaser : public Effect
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -43,6 +59,7 @@ public:
|
|||||||
// EffectIdentInterface implementation
|
// EffectIdentInterface implementation
|
||||||
|
|
||||||
virtual EffectType GetType();
|
virtual EffectType GetType();
|
||||||
|
virtual bool SupportsRealtime();
|
||||||
|
|
||||||
// EffectClientInterface implementation
|
// EffectClientInterface implementation
|
||||||
|
|
||||||
@@ -50,6 +67,13 @@ public:
|
|||||||
virtual int GetAudioOutCount();
|
virtual int GetAudioOutCount();
|
||||||
virtual bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL);
|
virtual bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL);
|
||||||
virtual sampleCount ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen);
|
virtual sampleCount ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen);
|
||||||
|
virtual bool RealtimeInitialize();
|
||||||
|
virtual bool RealtimeAddProcessor(int numChannels, float sampleRate);
|
||||||
|
virtual bool RealtimeFinalize();
|
||||||
|
virtual sampleCount RealtimeProcess(int group,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples);
|
||||||
virtual bool GetAutomationParameters(EffectAutomationParameters & parms);
|
virtual bool GetAutomationParameters(EffectAutomationParameters & parms);
|
||||||
virtual bool SetAutomationParameters(EffectAutomationParameters & parms);
|
virtual bool SetAutomationParameters(EffectAutomationParameters & parms);
|
||||||
|
|
||||||
@@ -62,6 +86,9 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
// EffectPhaser implementation
|
// EffectPhaser implementation
|
||||||
|
|
||||||
|
void InstanceInit(EffectPhaserState & data, float sampleRate);
|
||||||
|
sampleCount InstanceProcess(EffectPhaserState & data, float **inBlock, float **outBlock, sampleCount blockLen);
|
||||||
|
|
||||||
void OnStagesSlider(wxCommandEvent & evt);
|
void OnStagesSlider(wxCommandEvent & evt);
|
||||||
void OnDryWetSlider(wxCommandEvent & evt);
|
void OnDryWetSlider(wxCommandEvent & evt);
|
||||||
void OnFeedbackSlider(wxCommandEvent & evt);
|
void OnFeedbackSlider(wxCommandEvent & evt);
|
||||||
@@ -87,13 +114,8 @@ protected:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// state variables
|
EffectPhaserState mMaster;
|
||||||
sampleCount skipcount;
|
EffectPhaserStateArray mSlaves;
|
||||||
double old[NUM_STAGES]; // must be as large as MAX_STAGES
|
|
||||||
double gain;
|
|
||||||
double fbout;
|
|
||||||
double lfoskip;
|
|
||||||
double phase;
|
|
||||||
|
|
||||||
// parameters
|
// parameters
|
||||||
int mStages;
|
int mStages;
|
||||||
|
@@ -49,6 +49,9 @@ Param( FreqOfs, int, XO("Offset"), 30, 0, 100, 1 ); /
|
|||||||
// How many samples are processed before recomputing the lfo value again
|
// How many samples are processed before recomputing the lfo value again
|
||||||
#define lfoskipsamples 30
|
#define lfoskipsamples 30
|
||||||
|
|
||||||
|
#include <wx/arrimpl.cpp>
|
||||||
|
WX_DEFINE_OBJARRAY(EffectWahwahStateArray);
|
||||||
|
|
||||||
//
|
//
|
||||||
// EffectWahwah
|
// EffectWahwah
|
||||||
//
|
//
|
||||||
@@ -100,6 +103,15 @@ EffectType EffectWahwah::GetType()
|
|||||||
return EffectTypeProcess;
|
return EffectTypeProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EffectWahwah::SupportsRealtime()
|
||||||
|
{
|
||||||
|
#if defined(EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// EffectClientInterface implementation
|
// EffectClientInterface implementation
|
||||||
|
|
||||||
int EffectWahwah::GetAudioInCount()
|
int EffectWahwah::GetAudioInCount()
|
||||||
@@ -114,26 +126,11 @@ int EffectWahwah::GetAudioOutCount()
|
|||||||
|
|
||||||
bool EffectWahwah::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames chanMap)
|
bool EffectWahwah::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames chanMap)
|
||||||
{
|
{
|
||||||
lfoskip = mFreq * 2 * M_PI / mSampleRate;
|
InstanceInit(mMaster, mSampleRate);
|
||||||
skipcount = 0;
|
|
||||||
xn1 = 0;
|
|
||||||
xn2 = 0;
|
|
||||||
yn1 = 0;
|
|
||||||
yn2 = 0;
|
|
||||||
b0 = 0;
|
|
||||||
b1 = 0;
|
|
||||||
b2 = 0;
|
|
||||||
a0 = 0;
|
|
||||||
a1 = 0;
|
|
||||||
a2 = 0;
|
|
||||||
|
|
||||||
depth = mDepth / 100.0;
|
|
||||||
freqofs = mFreqOfs / 100.0;
|
|
||||||
|
|
||||||
phase = mPhase * M_PI / 180.0;
|
|
||||||
if (chanMap[0] == ChannelNameFrontRight)
|
if (chanMap[0] == ChannelNameFrontRight)
|
||||||
{
|
{
|
||||||
phase += M_PI;
|
mMaster.phase += M_PI;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -141,41 +138,43 @@ bool EffectWahwah::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelName
|
|||||||
|
|
||||||
sampleCount EffectWahwah::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
|
sampleCount EffectWahwah::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
|
||||||
{
|
{
|
||||||
float *ibuf = inBlock[0];
|
return InstanceProcess(mMaster, inBlock, outBlock, blockLen);
|
||||||
float *obuf = outBlock[0];
|
}
|
||||||
double frequency, omega, sn, cs, alpha;
|
|
||||||
double in, out;
|
|
||||||
|
|
||||||
for (int i = 0; i < blockLen; i++)
|
bool EffectWahwah::RealtimeInitialize()
|
||||||
{
|
{
|
||||||
in = (double) ibuf[i];
|
SetBlockSize(512);
|
||||||
|
|
||||||
if ((skipcount++) % lfoskipsamples == 0)
|
mSlaves.Clear();
|
||||||
{
|
|
||||||
frequency = (1 + cos(skipcount * lfoskip + phase)) / 2;
|
|
||||||
frequency = frequency * depth * (1 - freqofs) + freqofs;
|
|
||||||
frequency = exp((frequency - 1) * 6);
|
|
||||||
omega = M_PI * frequency;
|
|
||||||
sn = sin(omega);
|
|
||||||
cs = cos(omega);
|
|
||||||
alpha = sn / (2 * mRes);
|
|
||||||
b0 = (1 - cs) / 2;
|
|
||||||
b1 = 1 - cs;
|
|
||||||
b2 = (1 - cs) / 2;
|
|
||||||
a0 = 1 + alpha;
|
|
||||||
a1 = -2 * cs;
|
|
||||||
a2 = 1 - alpha;
|
|
||||||
};
|
|
||||||
out = (b0 * in + b1 * xn1 + b2 * xn2 - a1 * yn1 - a2 * yn2) / a0;
|
|
||||||
xn2 = xn1;
|
|
||||||
xn1 = in;
|
|
||||||
yn2 = yn1;
|
|
||||||
yn1 = out;
|
|
||||||
|
|
||||||
obuf[i] = (float) out;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return blockLen;
|
bool EffectWahwah::RealtimeAddProcessor(int WXUNUSED(numChannels), float sampleRate)
|
||||||
|
{
|
||||||
|
EffectWahwahState slave;
|
||||||
|
|
||||||
|
InstanceInit(slave, sampleRate);
|
||||||
|
|
||||||
|
mSlaves.Add(slave);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EffectWahwah::RealtimeFinalize()
|
||||||
|
{
|
||||||
|
mSlaves.Clear();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleCount EffectWahwah::RealtimeProcess(int group,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples)
|
||||||
|
{
|
||||||
|
|
||||||
|
return InstanceProcess(mSlaves[group], inbuf, outbuf, numSamples);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EffectWahwah::GetAutomationParameters(EffectAutomationParameters & parms)
|
bool EffectWahwah::GetAutomationParameters(EffectAutomationParameters & parms)
|
||||||
@@ -299,6 +298,73 @@ bool EffectWahwah::TransferDataFromWindow()
|
|||||||
|
|
||||||
// EffectWahwah implementation
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
sampleCount EffectWahwah::InstanceProcess(EffectWahwahState & data, float **inBlock, float **outBlock, sampleCount 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;
|
||||||
|
|
||||||
|
for (int 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;
|
||||||
|
|
||||||
|
obuf[i] = (float) out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return blockLen;
|
||||||
|
}
|
||||||
|
|
||||||
void EffectWahwah::OnFreqSlider(wxCommandEvent & evt)
|
void EffectWahwah::OnFreqSlider(wxCommandEvent & evt)
|
||||||
{
|
{
|
||||||
mFreq = (double) evt.GetInt() / SCL_Freq;
|
mFreq = (double) evt.GetInt() / SCL_Freq;
|
||||||
|
@@ -27,6 +27,21 @@ class ShuttleGui;
|
|||||||
|
|
||||||
#define WAHWAH_PLUGIN_SYMBOL XO("Wahwah")
|
#define WAHWAH_PLUGIN_SYMBOL XO("Wahwah")
|
||||||
|
|
||||||
|
class EffectWahwahState
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
float samplerate;
|
||||||
|
double depth;
|
||||||
|
double freqofs;
|
||||||
|
double phase;
|
||||||
|
double lfoskip;
|
||||||
|
unsigned long skipcount;
|
||||||
|
double xn1, xn2, yn1, yn2;
|
||||||
|
double b0, b1, b2, a0, a1, a2;
|
||||||
|
};
|
||||||
|
|
||||||
|
WX_DECLARE_OBJARRAY(EffectWahwahState, EffectWahwahStateArray);
|
||||||
|
|
||||||
class EffectWahwah : public Effect
|
class EffectWahwah : public Effect
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -41,6 +56,7 @@ public:
|
|||||||
// EffectIdentInterface implementation
|
// EffectIdentInterface implementation
|
||||||
|
|
||||||
virtual EffectType GetType();
|
virtual EffectType GetType();
|
||||||
|
virtual bool SupportsRealtime();
|
||||||
|
|
||||||
// EffectClientInterface implementation
|
// EffectClientInterface implementation
|
||||||
|
|
||||||
@@ -48,6 +64,13 @@ public:
|
|||||||
virtual int GetAudioOutCount();
|
virtual int GetAudioOutCount();
|
||||||
virtual bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL);
|
virtual bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL);
|
||||||
virtual sampleCount ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen);
|
virtual sampleCount ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen);
|
||||||
|
virtual bool RealtimeInitialize();
|
||||||
|
virtual bool RealtimeAddProcessor(int numChannels, float sampleRate);
|
||||||
|
virtual bool RealtimeFinalize();
|
||||||
|
virtual sampleCount RealtimeProcess(int group,
|
||||||
|
float **inbuf,
|
||||||
|
float **outbuf,
|
||||||
|
sampleCount numSamples);
|
||||||
virtual bool GetAutomationParameters(EffectAutomationParameters & parms);
|
virtual bool GetAutomationParameters(EffectAutomationParameters & parms);
|
||||||
virtual bool SetAutomationParameters(EffectAutomationParameters & parms);
|
virtual bool SetAutomationParameters(EffectAutomationParameters & parms);
|
||||||
|
|
||||||
@@ -60,6 +83,9 @@ public:
|
|||||||
private:
|
private:
|
||||||
// EffectWahwah implementation
|
// EffectWahwah implementation
|
||||||
|
|
||||||
|
void InstanceInit(EffectWahwahState & data, float sampleRate);
|
||||||
|
sampleCount InstanceProcess(EffectWahwahState & data, float **inBlock, float **outBlock, sampleCount blockLen);
|
||||||
|
|
||||||
void OnFreqSlider(wxCommandEvent & evt);
|
void OnFreqSlider(wxCommandEvent & evt);
|
||||||
void OnPhaseSlider(wxCommandEvent & evt);
|
void OnPhaseSlider(wxCommandEvent & evt);
|
||||||
void OnDepthSlider(wxCommandEvent & evt);
|
void OnDepthSlider(wxCommandEvent & evt);
|
||||||
@@ -73,15 +99,10 @@ private:
|
|||||||
void OnFreqOffText(wxCommandEvent & evt);
|
void OnFreqOffText(wxCommandEvent & evt);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double depth;
|
EffectWahwahState mMaster;
|
||||||
double freqofs;
|
EffectWahwahStateArray mSlaves;
|
||||||
double phase;
|
|
||||||
double lfoskip;
|
|
||||||
unsigned long skipcount;
|
|
||||||
double xn1, xn2, yn1, yn2;
|
|
||||||
double b0, b1, b2, a0, a1, a2;
|
|
||||||
|
|
||||||
/* Parameters:
|
/* Parameters:
|
||||||
mFreq - LFO frequency
|
mFreq - LFO frequency
|
||||||
mPhase - LFO startphase in RADIANS - useful for stereo WahWah
|
mPhase - LFO startphase in RADIANS - useful for stereo WahWah
|
||||||
mDepth - Wah depth
|
mDepth - Wah depth
|
||||||
|
Reference in New Issue
Block a user