Audacity 3.2.0
Phaser.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Phaser.cpp
6
7 Effect programming:
8 Nasca Octavian Paul (Paul Nasca)
9
10 UI programming:
11 Dominic Mazzoni (with the help of wxDesigner)
12 Vaughan Johnson (Preview)
13
14*******************************************************************//*******************************************************************/
20
21
22
23#include "Phaser.h"
24#include "LoadEffects.h"
25
26#include <math.h>
27
28#include <wx/intl.h>
29#include <wx/slider.h>
30
31#include "../ShuttleGui.h"
32#include "../widgets/valnum.h"
33
34enum
35{
36 ID_Stages = 10000,
43};
44
46{
49 > parameters{
50 [](EffectPhaser &, EffectSettings &, EffectPhaser &e, bool updating){
51 if (updating)
52 e.mStages &= ~1; // must be even, but don't complain about it
53 return true;
54 },
55 };
56 return parameters;
57}
58
59//
60#define phaserlfoshape 4.0
61
62// How many samples are processed before recomputing the lfo value again
63#define lfoskipsamples 20
64
65//
66// EffectPhaser
67//
68
70{ XO("Phaser") };
71
73
74BEGIN_EVENT_TABLE(EffectPhaser, wxEvtHandler)
90
92{
93 Parameters().Reset(*this);
94 SetLinearEffectFlag(true);
95}
96
98{
99}
100
101// ComponentInterface implementation
102
104{
105 return Symbol;
106}
107
109{
110 return XO("Combines phase-shifted signals with the original signal");
111}
112
114{
115 return L"Phaser";
116}
117
118// EffectDefinitionInterface implementation
119
121{
122 return EffectTypeProcess;
123}
124
126{
127 // TODO reenable after achieving statelessness
128 return false;
129// return true;
130}
131
133{
134 return 1;
135}
136
138{
139 return 1;
140}
141
143 EffectSettings &, double sampleRate, sampleCount, ChannelNames chanMap)
144{
145 InstanceInit(mMaster, sampleRate);
146 if (chanMap[0] == ChannelNameFrontRight)
147 mMaster.phase += M_PI;
148 return true;
149}
150
152 const float *const *inBlock, float *const *outBlock, size_t blockLen)
153{
154 return InstanceProcess(settings, mMaster, inBlock, outBlock, blockLen);
155}
156
158{
159 SetBlockSize(512);
160 mSlaves.clear();
161 return true;
162}
163
165 EffectSettings &, unsigned, float sampleRate)
166{
167 EffectPhaserState slave;
168
169 InstanceInit(slave, sampleRate);
170
171 mSlaves.push_back(slave);
172
173 return true;
174}
175
177{
178 mSlaves.clear();
179
180 return true;
181}
182
184 const float *const *inbuf, float *const *outbuf, size_t numSamples)
185{
186 if (group >= mSlaves.size())
187 return 0;
188 return InstanceProcess(settings, mSlaves[group], inbuf, outbuf, numSamples);
189}
190
191// Effect implementation
192
193std::unique_ptr<EffectUIValidator> EffectPhaser::PopulateOrExchange(
195{
196 S.SetBorder(5);
197 S.AddSpace(0, 5);
198
199 S.StartMultiColumn(3, wxEXPAND);
200 {
201 S.SetStretchyCol(2);
202
203 mStagesT = S.Id(ID_Stages)
204 .Validator<IntegerValidator<int>>(
205 &mStages, NumValidatorStyle::DEFAULT, Stages.min, Stages.max)
206 .AddTextBox(XXO("&Stages:"), L"", 15);
207
208 mStagesS = S.Id(ID_Stages)
209 .Name(XO("Stages"))
210 .Style(wxSL_HORIZONTAL)
211 .MinSize( { 100, -1 } )
212 .AddSlider( {}, Stages.def * Stages.scale, Stages.max * Stages.scale, Stages.min * Stages.scale);
213 mStagesS->SetLineSize(2);
214
215 mDryWetT = S.Id(ID_DryWet)
216 .Validator<IntegerValidator<int>>(
217 &mDryWet, NumValidatorStyle::DEFAULT, DryWet.min, DryWet.max)
218 .AddTextBox(XXO("&Dry/Wet:"), L"", 15);
219
220 mDryWetS = S.Id(ID_DryWet)
221 .Name(XO("Dry Wet"))
222 .Style(wxSL_HORIZONTAL)
223 .MinSize( { 100, -1 } )
224 .AddSlider( {}, DryWet.def * DryWet.scale, DryWet.max * DryWet.scale, DryWet.min * DryWet.scale);
225
226 mFreqT = S.Id(ID_Freq)
227 .Validator<FloatingPointValidator<double>>(
228 5, &mFreq, NumValidatorStyle::ONE_TRAILING_ZERO, Freq.min, Freq.max)
229 .AddTextBox(XXO("LFO Freq&uency (Hz):"), L"", 15);
230
231 mFreqS = S.Id(ID_Freq)
232 .Name(XO("LFO frequency in hertz"))
233 .Style(wxSL_HORIZONTAL)
234 .MinSize( { 100, -1 } )
235 .AddSlider( {}, Freq.def * Freq.scale, Freq.max * Freq.scale, 0.0);
236
237 mPhaseT = S.Id(ID_Phase)
238 .Validator<FloatingPointValidator<double>>(
239 1, &mPhase, NumValidatorStyle::DEFAULT, Phase.min, Phase.max)
240 .AddTextBox(XXO("LFO Sta&rt Phase (deg.):"), L"", 15);
241
242 mPhaseS = S.Id(ID_Phase)
243 .Name(XO("LFO start phase in degrees"))
244 .Style(wxSL_HORIZONTAL)
245 .MinSize( { 100, -1 } )
246 .AddSlider( {}, Phase.def * Phase.scale, Phase.max * Phase.scale, Phase.min * Phase.scale);
247 mPhaseS->SetLineSize(10);
248
249 mDepthT = S.Id(ID_Depth)
250 .Validator<IntegerValidator<int>>(
251 &mDepth, NumValidatorStyle::DEFAULT, Depth.min, Depth.max)
252 .AddTextBox(XXO("Dept&h:"), L"", 15);
253
254 mDepthS = S.Id(ID_Depth)
255 .Name(XO("Depth in percent"))
256 .Style(wxSL_HORIZONTAL)
257 .MinSize( { 100, -1 } )
258 .AddSlider( {}, Depth.def * Depth.scale, Depth.max * Depth.scale, Depth.min * Depth.scale);
259
261 .Validator<IntegerValidator<int>>(
262 &mFeedback, NumValidatorStyle::DEFAULT, Feedback.min, Feedback.max)
263 .AddTextBox(XXO("Feedbac&k (%):"), L"", 15);
264
266 .Name(XO("Feedback in percent"))
267 .Style(wxSL_HORIZONTAL)
268 .MinSize( { 100, -1 } )
270 mFeedbackS->SetLineSize(10);
271
273 .Validator<FloatingPointValidator<double>>(
274 1, &mOutGain, NumValidatorStyle::DEFAULT, OutGain.min, OutGain.max)
275 .AddTextBox(XXO("&Output gain (dB):"), L"", 12);
276
278 .Name(XO("Output gain (dB)"))
279 .Style(wxSL_HORIZONTAL)
280 .MinSize( { 100, -1 } )
282 }
283 S.EndMultiColumn();
284 return nullptr;
285}
286
288{
289 mStagesS->SetValue((int) (mStages * Stages.scale));
290 mDryWetS->SetValue((int) (mDryWet * DryWet.scale));
291 mFreqS->SetValue((int) (mFreq * Freq.scale));
292 mPhaseS->SetValue((int) (mPhase * Phase.scale));
293 mDepthS->SetValue((int) (mDepth * Depth.scale));
294 mFeedbackS->SetValue((int) (mFeedback * Feedback.scale));
295 mOutGainS->SetValue((int) (mOutGain * OutGain.scale));
296
297 return true;
298}
299
301{
302 if (mStages & 1) // must be even
303 {
304 mStages &= ~1;
305 mStagesT->GetValidator()->TransferToWindow();
306 }
307
308 return true;
309}
310
311// EffectPhaser implementation
312
313void EffectPhaser::InstanceInit(EffectPhaserState & data, float sampleRate)
314{
315 data.samplerate = sampleRate;
316
317 for (int j = 0; j < mStages; j++)
318 {
319 data.old[j] = 0;
320 }
321
322 data.skipcount = 0;
323 data.gain = 0;
324 data.fbout = 0;
325 data.laststages = 0;
326 data.outgain = 0;
327
328 return;
329}
330
332 EffectPhaserState & data,
333 const float *const *inBlock, float *const *outBlock, size_t blockLen)
334{
335 const float *ibuf = inBlock[0];
336 float *obuf = outBlock[0];
337
338 for (int j = data.laststages; j < mStages; j++)
339 {
340 data.old[j] = 0;
341 }
342 data.laststages = mStages;
343
344 data.lfoskip = mFreq * 2 * M_PI / data.samplerate;
345 data.phase = mPhase * M_PI / 180;
347
348 for (decltype(blockLen) i = 0; i < blockLen; i++)
349 {
350 double in = ibuf[i];
351
352 double m = in + data.fbout * mFeedback / 101; // Feedback must be less than 100% to avoid infinite gain.
353
354 if (((data.skipcount++) % lfoskipsamples) == 0)
355 {
356 //compute sine between 0 and 1
357 data.gain =
358 (1.0 +
359 cos(data.skipcount.as_double() * data.lfoskip
360 + data.phase)) / 2.0;
361
362 // change lfo shape
363 data.gain = expm1(data.gain * phaserlfoshape) / expm1(phaserlfoshape);
364
365 // attenuate the lfo
366 data.gain = 1.0 - data.gain / 255.0 * mDepth;
367 }
368
369 // phasing routine
370 for (int j = 0; j < mStages; j++)
371 {
372 double tmp = data.old[j];
373 data.old[j] = data.gain * tmp + m;
374 m = tmp - data.gain * data.old[j];
375 }
376 data.fbout = m;
377
378 obuf[i] = (float) (data.outgain * (m * mDryWet + in * (255 - mDryWet)) / 255);
379 }
380
381 return blockLen;
382}
383
384void EffectPhaser::OnStagesSlider(wxCommandEvent & evt)
385{
386 mStages = (evt.GetInt() / Stages.scale) & ~1; // must be even;
387 mStagesT->GetValidator()->TransferToWindow();
388 EnableApply(mUIParent->Validate());
389}
390
391void EffectPhaser::OnDryWetSlider(wxCommandEvent & evt)
392{
393 mDryWet = evt.GetInt() / DryWet.scale;
394 mDryWetT->GetValidator()->TransferToWindow();
395 EnableApply(mUIParent->Validate());
396}
397
398void EffectPhaser::OnFreqSlider(wxCommandEvent & evt)
399{
400 mFreq = (double) evt.GetInt() / Freq.scale;
401 if (mFreq < Freq.min) mFreq = Freq.min;
402 mFreqT->GetValidator()->TransferToWindow();
403 EnableApply(mUIParent->Validate());
404}
405
406void EffectPhaser::OnPhaseSlider(wxCommandEvent & evt)
407{
408 int val = ((evt.GetInt() + 5) / 10) * 10; // round to nearest multiple of 10
409 val = val > Phase.max * Phase.scale ? Phase.max * Phase.scale : val;
410 mPhaseS->SetValue(val);
411 mPhase = (double) val / Phase.scale;
412 mPhaseT->GetValidator()->TransferToWindow();
413 EnableApply(mUIParent->Validate());
414}
415
416void EffectPhaser::OnDepthSlider(wxCommandEvent & evt)
417{
418 mDepth = evt.GetInt() / Depth.scale;
419 mDepthT->GetValidator()->TransferToWindow();
420 EnableApply(mUIParent->Validate());
421}
422
423void EffectPhaser::OnFeedbackSlider(wxCommandEvent & evt)
424{
425 int val = evt.GetInt();
426 val = ((val + (val > 0 ? 5 : -5)) / 10) * 10; // round to nearest multiple of 10
427 val = val > Feedback.max * Feedback.scale ? Feedback.max * Feedback.scale : val;
428 mFeedbackS->SetValue(val);
429 mFeedback = val / Feedback.scale;
430 mFeedbackT->GetValidator()->TransferToWindow();
431 EnableApply(mUIParent->Validate());
432}
433
434void EffectPhaser::OnGainSlider(wxCommandEvent & evt)
435{
436 mOutGain = evt.GetInt() / OutGain.scale;
437 mOutGainT->GetValidator()->TransferToWindow();
438 EnableApply(mUIParent->Validate());
439}
440
441void EffectPhaser::OnStagesText(wxCommandEvent & WXUNUSED(evt))
442{
443 if (!EnableApply(mUIParent->TransferDataFromWindow()))
444 {
445 return;
446 }
447
448 mStagesS->SetValue((int) (mStages * Stages.scale));
449}
450
451void EffectPhaser::OnDryWetText(wxCommandEvent & WXUNUSED(evt))
452{
453 if (!EnableApply(mUIParent->TransferDataFromWindow()))
454 {
455 return;
456 }
457
458 mDryWetS->SetValue((int) (mDryWet * DryWet.scale));
459}
460
461void EffectPhaser::OnFreqText(wxCommandEvent & WXUNUSED(evt))
462{
463 if (!EnableApply(mUIParent->TransferDataFromWindow()))
464 {
465 return;
466 }
467
468 mFreqS->SetValue((int) (mFreq * Freq.scale));
469}
470
471void EffectPhaser::OnPhaseText(wxCommandEvent & WXUNUSED(evt))
472{
473 if (!EnableApply(mUIParent->TransferDataFromWindow()))
474 {
475 return;
476 }
477
478 mPhaseS->SetValue((int) (mPhase * Phase.scale));
479}
480
481void EffectPhaser::OnDepthText(wxCommandEvent & WXUNUSED(evt))
482{
483 if (!EnableApply(mUIParent->TransferDataFromWindow()))
484 {
485 return;
486 }
487
488 mDepthS->SetValue((int) (mDepth * Depth.scale));
489}
490
491void EffectPhaser::OnFeedbackText(wxCommandEvent & WXUNUSED(evt))
492{
493 if (!EnableApply(mUIParent->TransferDataFromWindow()))
494 {
495 return;
496 }
497
498 mFeedbackS->SetValue((int) (mFeedback * Feedback.scale));
499}
500
501void EffectPhaser::OnGainText(wxCommandEvent & WXUNUSED(evt))
502{
503 if (!EnableApply(mUIParent->TransferDataFromWindow()))
504 {
505 return;
506 }
507
508 mOutGainS->SetValue((int) (mOutGain * OutGain.scale));
509}
END_EVENT_TABLE()
#define M_PI
Definition: Distortion.cpp:29
EffectType
@ EffectTypeProcess
enum ChannelName * ChannelNames
@ ChannelNameFrontRight
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:535
@ ID_Feedback
Definition: Phaser.cpp:41
@ ID_Freq
Definition: Phaser.cpp:38
@ ID_Depth
Definition: Phaser.cpp:40
@ ID_OutGain
Definition: Phaser.cpp:42
@ ID_Stages
Definition: Phaser.cpp:36
@ ID_DryWet
Definition: Phaser.cpp:37
@ ID_Phase
Definition: Phaser.cpp:39
#define phaserlfoshape
Definition: Phaser.cpp:60
#define lfoskipsamples
Definition: Phaser.cpp:63
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:87
Generates EffectParameterMethods overrides from variadic template arguments.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
bool EnableApply(bool enable=true)
Definition: Effect.cpp:613
wxWindow * mUIParent
Definition: Effect.h:270
Performs effect computation.
Interface for manipulations of an Effect's settings.
An Effect that changes frequencies in a time varying manner.
Definition: Phaser.h:44
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
Definition: Phaser.cpp:137
void OnPhaseText(wxCommandEvent &evt)
Definition: Phaser.cpp:471
wxTextCtrl * mDepthT
Definition: Phaser.h:138
bool ProcessInitialize(EffectSettings &settings, double sampleRate, sampleCount totalLen, ChannelNames chanMap) override
Definition: Phaser.cpp:142
static constexpr EffectParameter Feedback
Definition: Phaser.h:163
void OnStagesSlider(wxCommandEvent &evt)
Definition: Phaser.cpp:384
size_t RealtimeProcess(size_t group, EffectSettings &settings, const float *const *inbuf, float *const *outbuf, size_t numSamples) override
Definition: Phaser.cpp:183
static constexpr EffectParameter DryWet
Definition: Phaser.h:155
void OnFeedbackSlider(wxCommandEvent &evt)
Definition: Phaser.cpp:423
int mStages
Definition: Phaser.h:126
void OnStagesText(wxCommandEvent &evt)
Definition: Phaser.cpp:441
static constexpr EffectParameter Stages
Definition: Phaser.h:153
static constexpr EffectParameter Phase
Definition: Phaser.h:159
bool TransferDataFromWindow(EffectSettings &settings) override
Update the given settings from controls.
Definition: Phaser.cpp:300
bool SupportsRealtime() const override
Whether the effect supports realtime previewing (while audio is playing).
Definition: Phaser.cpp:125
double mPhase
Definition: Phaser.h:129
void OnPhaseSlider(wxCommandEvent &evt)
Definition: Phaser.cpp:406
void OnDryWetSlider(wxCommandEvent &evt)
Definition: Phaser.cpp:391
double mFreq
Definition: Phaser.h:128
wxTextCtrl * mDryWetT
Definition: Phaser.h:135
void InstanceInit(EffectPhaserState &data, float sampleRate)
Definition: Phaser.cpp:313
wxSlider * mOutGainS
Definition: Phaser.h:148
wxSlider * mFreqS
Definition: Phaser.h:144
void OnDryWetText(wxCommandEvent &evt)
Definition: Phaser.cpp:451
size_t ProcessBlock(EffectSettings &settings, const float *const *inBlock, float *const *outBlock, size_t blockLen) override
Called for destructive effect computation.
Definition: Phaser.cpp:151
void OnDepthSlider(wxCommandEvent &evt)
Definition: Phaser.cpp:416
int mDryWet
Definition: Phaser.h:127
static const ComponentInterfaceSymbol Symbol
Definition: Phaser.h:48
static constexpr EffectParameter Depth
Definition: Phaser.h:161
std::unique_ptr< EffectUIValidator > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access) override
Add controls to effect panel; always succeeds.
Definition: Phaser.cpp:193
wxTextCtrl * mPhaseT
Definition: Phaser.h:137
wxTextCtrl * mFreqT
Definition: Phaser.h:136
size_t InstanceProcess(EffectSettings &settings, EffectPhaserState &data, const float *const *inBlock, float *const *outBlock, size_t blockLen)
Definition: Phaser.cpp:331
void OnFreqText(wxCommandEvent &evt)
Definition: Phaser.cpp:461
bool RealtimeFinalize(EffectSettings &settings) noexcept override
Definition: Phaser.cpp:176
wxTextCtrl * mFeedbackT
Definition: Phaser.h:139
bool RealtimeAddProcessor(EffectSettings &settings, unsigned numChannels, float sampleRate) override
Definition: Phaser.cpp:164
void OnGainSlider(wxCommandEvent &evt)
Definition: Phaser.cpp:434
static constexpr EffectParameter Freq
Definition: Phaser.h:157
wxTextCtrl * mStagesT
Definition: Phaser.h:134
const EffectParameterMethods & Parameters() const override
Definition: Phaser.cpp:45
void OnFeedbackText(wxCommandEvent &evt)
Definition: Phaser.cpp:491
int mDepth
Definition: Phaser.h:130
virtual ~EffectPhaser()
Definition: Phaser.cpp:97
wxSlider * mStagesS
Definition: Phaser.h:142
TranslatableString GetDescription() const override
Definition: Phaser.cpp:108
ComponentInterfaceSymbol GetSymbol() const override
Definition: Phaser.cpp:103
void OnFreqSlider(wxCommandEvent &evt)
Definition: Phaser.cpp:398
wxTextCtrl * mOutGainT
Definition: Phaser.h:140
wxSlider * mPhaseS
Definition: Phaser.h:145
std::vector< EffectPhaserState > mSlaves
Definition: Phaser.h:123
wxSlider * mDepthS
Definition: Phaser.h:146
EffectType GetType() const override
Type determines how it behaves.
Definition: Phaser.cpp:120
wxSlider * mFeedbackS
Definition: Phaser.h:147
int mFeedback
Definition: Phaser.h:131
static constexpr EffectParameter OutGain
Definition: Phaser.h:165
unsigned GetAudioInCount() const override
How many input buffers to allocate at once.
Definition: Phaser.cpp:132
bool TransferDataToWindow(const EffectSettings &settings) override
Update controls for the settings.
Definition: Phaser.cpp:287
bool RealtimeInitialize(EffectSettings &settings, double sampleRate) override
Definition: Phaser.cpp:157
void OnDepthText(wxCommandEvent &evt)
Definition: Phaser.cpp:481
EffectPhaser()
Definition: Phaser.cpp:91
double mOutGain
Definition: Phaser.h:132
void OnGainText(wxCommandEvent &evt)
Definition: Phaser.cpp:501
EffectPhaserState mMaster
Definition: Phaser.h:122
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
Definition: Phaser.cpp:113
wxSlider * mDryWetS
Definition: Phaser.h:143
double outgain
Definition: Phaser.h:37
double lfoskip
Definition: Phaser.h:38
double fbout
Definition: Phaser.h:36
double gain
Definition: Phaser.h:35
double phase
Definition: Phaser.h:39
double old[NUM_STAGES]
Definition: Phaser.h:34
float samplerate
Definition: Phaser.h:32
sampleCount skipcount
Definition: Phaser.h:33
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
size_t SetBlockSize(size_t maxBlockSize) override
Holds a msgid for the translation catalog; may also bind format arguments.
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
double as_double() const
Definition: SampleCount.h:45
BuiltinEffectsModule::Registration< EffectPhaser > reg
Definition: Phaser.cpp:72
const Type scale
Scaling factor, for slider control.
Definition: Shuttle.h:32
const Type def
Default value.
Definition: Shuttle.h:29
const Type min
Minimum value.
Definition: Shuttle.h:30
const Type max
Maximum value.
Definition: Shuttle.h:31
Externalized state of a plug-in.