1
0
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:
Leland Lucius
2015-08-01 07:03:02 -05:00
parent 7414b000c2
commit f3ef8a27c7
6 changed files with 301 additions and 105 deletions

View File

@@ -150,6 +150,9 @@
// to enable.
#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 EXPERIMENTAL_EFFECTS_RACK

View File

@@ -421,6 +421,8 @@ bool Effect::RealtimeInitialize()
return mClient->RealtimeInitialize();
}
mBlockSize = 512;
return false;
}
@@ -455,9 +457,15 @@ bool Effect::RealtimeSuspend()
mRealtimeSuspendLock.Leave();
return true;
}
}
return false;
}
mRealtimeSuspendLock.Enter();
mRealtimeSuspendCount++;
mRealtimeSuspendLock.Leave();
return true;
}
bool Effect::RealtimeResume()
@@ -471,9 +479,15 @@ bool Effect::RealtimeResume()
mRealtimeSuspendLock.Leave();
return true;
}
}
return false;
}
mRealtimeSuspendLock.Enter();
mRealtimeSuspendCount--;
mRealtimeSuspendLock.Leave();
return true;
}
bool Effect::RealtimeProcessStart()
@@ -677,6 +691,7 @@ bool Effect::CloseUI()
mUIParent->RemoveEventHandler(this);
mUIParent = NULL;
mUIDialog = NULL;
return true;
}
@@ -2270,7 +2285,7 @@ bool Effect::RealtimeAddProcessor(int group, int chans, float rate)
}
// Add a new processor
mClient->RealtimeAddProcessor(gchans, rate);
RealtimeAddProcessor(gchans, rate);
// Bump to next processor
mCurrentProcessor++;
@@ -2377,7 +2392,7 @@ sampleCount Effect::RealtimeProcess(int group,
for (sampleCount block = 0; block < 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++)
{

View File

@@ -56,6 +56,9 @@ Param( Feedback, int, XO("Feedback"), 0, -100, 100, 1 );
// How many samples are processed before recomputing the lfo value again
#define lfoskipsamples 20
#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(EffectPhaserStateArray);
//
// EffectPhaser
//
@@ -110,6 +113,15 @@ EffectType EffectPhaser::GetType()
return EffectTypeProcess;
}
bool EffectPhaser::SupportsRealtime()
{
#if defined(EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS)
return true;
#else
return false;
#endif
}
// EffectClientInterface implementation
int EffectPhaser::GetAudioInCount()
@@ -124,20 +136,10 @@ int EffectPhaser::GetAudioOutCount()
bool EffectPhaser::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames chanMap)
{
for (int j = 0; j < mStages; j++)
{
old[j] = 0;
}
skipcount = 0;
gain = 0;
fbout = 0;
lfoskip = mFreq * 2 * M_PI / mSampleRate;
phase = mPhase * M_PI / 180;
InstanceInit(mMaster, mSampleRate);
if (chanMap[0] == ChannelNameFrontRight)
{
phase += M_PI;
mMaster.phase += M_PI;
}
return true;
@@ -145,40 +147,43 @@ bool EffectPhaser::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelName
sampleCount EffectPhaser::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
{
float *ibuf = inBlock[0];
float *obuf = outBlock[0];
return InstanceProcess(mMaster, inBlock, outBlock, blockLen);
}
for (sampleCount i = 0; i < blockLen; i++)
{
double in = ibuf[i];
bool EffectPhaser::RealtimeInitialize()
{
SetBlockSize(512);
double m = in + fbout * mFeedback / 101; // Feedback must be less than 100% to avoid infinite gain.
mSlaves.Clear();
if (((skipcount++) % lfoskipsamples) == 0)
{
//compute sine between 0 and 1
gain = (1.0 + cos(skipcount * lfoskip + phase)) / 2.0;
return true;
}
// change lfo shape
gain = expm1(gain * phaserlfoshape) / expm1(phaserlfoshape);
bool EffectPhaser::RealtimeAddProcessor(int WXUNUSED(numChannels), float sampleRate)
{
EffectPhaserState slave;
// attenuate the lfo
gain = 1.0 - gain / 255.0 * mDepth;
}
InstanceInit(slave, sampleRate);
// phasing routine
for (int j = 0; j < mStages; j++)
{
double tmp = old[j];
old[j] = gain * tmp + m;
m = tmp - gain * old[j];
}
fbout = m;
mSlaves.Add(slave);
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)
@@ -329,6 +334,70 @@ bool EffectPhaser::TransferDataFromWindow()
// 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)
{
mStages = (evt.GetInt() / SCL_Stages) & ~1; // must be even;

View File

@@ -29,6 +29,22 @@ class ShuttleGui;
#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
{
public:
@@ -43,6 +59,7 @@ public:
// EffectIdentInterface implementation
virtual EffectType GetType();
virtual bool SupportsRealtime();
// EffectClientInterface implementation
@@ -50,6 +67,13 @@ public:
virtual int GetAudioOutCount();
virtual bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL);
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 SetAutomationParameters(EffectAutomationParameters & parms);
@@ -62,6 +86,9 @@ public:
protected:
// EffectPhaser implementation
void InstanceInit(EffectPhaserState & data, float sampleRate);
sampleCount InstanceProcess(EffectPhaserState & data, float **inBlock, float **outBlock, sampleCount blockLen);
void OnStagesSlider(wxCommandEvent & evt);
void OnDryWetSlider(wxCommandEvent & evt);
void OnFeedbackSlider(wxCommandEvent & evt);
@@ -87,13 +114,8 @@ protected:
*/
private:
// state variables
sampleCount skipcount;
double old[NUM_STAGES]; // must be as large as MAX_STAGES
double gain;
double fbout;
double lfoskip;
double phase;
EffectPhaserState mMaster;
EffectPhaserStateArray mSlaves;
// parameters
int mStages;

View File

@@ -49,6 +49,9 @@ Param( FreqOfs, int, XO("Offset"), 30, 0, 100, 1 ); /
// How many samples are processed before recomputing the lfo value again
#define lfoskipsamples 30
#include <wx/arrimpl.cpp>
WX_DEFINE_OBJARRAY(EffectWahwahStateArray);
//
// EffectWahwah
//
@@ -100,6 +103,15 @@ EffectType EffectWahwah::GetType()
return EffectTypeProcess;
}
bool EffectWahwah::SupportsRealtime()
{
#if defined(EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS)
return true;
#else
return false;
#endif
}
// EffectClientInterface implementation
int EffectWahwah::GetAudioInCount()
@@ -114,26 +126,11 @@ int EffectWahwah::GetAudioOutCount()
bool EffectWahwah::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames chanMap)
{
lfoskip = mFreq * 2 * M_PI / mSampleRate;
skipcount = 0;
xn1 = 0;
xn2 = 0;
yn1 = 0;
yn2 = 0;
b0 = 0;
b1 = 0;
b2 = 0;
a0 = 0;
a1 = 0;
a2 = 0;
InstanceInit(mMaster, mSampleRate);
depth = mDepth / 100.0;
freqofs = mFreqOfs / 100.0;
phase = mPhase * M_PI / 180.0;
if (chanMap[0] == ChannelNameFrontRight)
{
phase += M_PI;
mMaster.phase += M_PI;
}
return true;
@@ -141,41 +138,43 @@ bool EffectWahwah::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelName
sampleCount EffectWahwah::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
{
float *ibuf = inBlock[0];
float *obuf = outBlock[0];
double frequency, omega, sn, cs, alpha;
double in, out;
return InstanceProcess(mMaster, inBlock, outBlock, blockLen);
}
for (int i = 0; i < blockLen; i++)
{
in = (double) ibuf[i];
bool EffectWahwah::RealtimeInitialize()
{
SetBlockSize(512);
if ((skipcount++) % lfoskipsamples == 0)
{
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;
mSlaves.Clear();
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)
@@ -299,6 +298,73 @@ bool EffectWahwah::TransferDataFromWindow()
// 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)
{
mFreq = (double) evt.GetInt() / SCL_Freq;

View File

@@ -27,6 +27,21 @@ class ShuttleGui;
#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
{
public:
@@ -41,6 +56,7 @@ public:
// EffectIdentInterface implementation
virtual EffectType GetType();
virtual bool SupportsRealtime();
// EffectClientInterface implementation
@@ -48,6 +64,13 @@ public:
virtual int GetAudioOutCount();
virtual bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL);
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 SetAutomationParameters(EffectAutomationParameters & parms);
@@ -60,6 +83,9 @@ public:
private:
// EffectWahwah implementation
void InstanceInit(EffectWahwahState & data, float sampleRate);
sampleCount InstanceProcess(EffectWahwahState & data, float **inBlock, float **outBlock, sampleCount blockLen);
void OnFreqSlider(wxCommandEvent & evt);
void OnPhaseSlider(wxCommandEvent & evt);
void OnDepthSlider(wxCommandEvent & evt);
@@ -73,15 +99,10 @@ private:
void OnFreqOffText(wxCommandEvent & evt);
private:
double depth;
double freqofs;
double phase;
double lfoskip;
unsigned long skipcount;
double xn1, xn2, yn1, yn2;
double b0, b1, b2, a0, a1, a2;
EffectWahwahState mMaster;
EffectWahwahStateArray mSlaves;
/* Parameters:
/* Parameters:
mFreq - LFO frequency
mPhase - LFO startphase in RADIANS - useful for stereo WahWah
mDepth - Wah depth