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. // 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

View File

@@ -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++)
{ {

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 // 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;

View File

@@ -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;

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 // 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;

View File

@@ -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