Audacity 3.2.0
Distortion.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 Distortion.cpp
6
7 Steve Daulton
8
9 // TODO: Add a graph display of the waveshaper equation.
10 // TODO: Allow the user to draw the graph.
11
12******************************************************************//*******************************************************************/
18
19
20#include "Distortion.h"
21#include "LoadEffects.h"
22
23#include <cmath>
24#include <algorithm>
25#define _USE_MATH_DEFINES
26
27// Belt and braces
28#ifndef M_PI
29#define M_PI 3.1415926535897932384626433832795
30#endif
31#ifndef M_PI_2
32#define M_PI_2 1.57079632679489661923132169163975
33#endif
34
35#include <wx/checkbox.h>
36#include <wx/choice.h>
37#include <wx/intl.h>
38#include <wx/valgen.h>
39#include <wx/log.h>
40#include <wx/slider.h>
41#include <wx/stattext.h>
42
43#include "Prefs.h"
44#include "../ShuttleGui.h"
45#include "../widgets/valnum.h"
46
48{
49 { XO("Hard Clipping") },
50 { XO("Soft Clipping") },
51 { XO("Soft Overdrive") },
52 { XO("Medium Overdrive") },
53 { XO("Hard Overdrive") },
54 { XO("Cubic Curve (odd harmonics)") },
55 { XO("Even Harmonics") },
56 { XO("Expand and Compress") },
57 { XO("Leveller") },
58 { XO("Rectifier Distortion") },
59 { XO("Hard Limiter 1413") }
60};
61
63{
66 > parameters{
68 bool updating) {
69 if (!updating)
70 e.mThreshold = DB_TO_LINEAR(p.mThreshold_dB);
71 return true;
72 }
73 };
74 return parameters;
75}
76
77// How many samples are processed before recomputing the lookup table again
78#define skipsamples 1000
79
80static const struct
81{
84}
86{
87 // Table DCBlock threshold floor Param1 Param2 Repeats
88 // Defaults: 0 false -6.0 -70.0(off) 50.0 50.0 1
89 //
90 // xgettext:no-c-format
91 { XO("Hard clip -12dB, 80% make-up gain"), { 0, 0, -12.0, -70.0, 0.0, 80.0, 0 } },
92 // xgettext:no-c-format
93 { XO("Soft clip -12dB, 80% make-up gain"), { 1, 0, -12.0, -70.0, 50.0, 80.0, 0 } },
94 { XO("Fuzz Box"), { 1, 0, -30.0, -70.0, 80.0, 80.0, 0 } },
95 { XO("Walkie-talkie"), { 1, 0, -50.0, -70.0, 60.0, 80.0, 0 } },
96 { XO("Blues drive sustain"), { 2, 0, -6.0, -70.0, 30.0, 80.0, 0 } },
97 { XO("Light Crunch Overdrive"), { 3, 0, -6.0, -70.0, 20.0, 80.0, 0 } },
98 { XO("Heavy Overdrive"), { 4, 0, -6.0, -70.0, 90.0, 80.0, 0 } },
99 { XO("3rd Harmonic (Perfect Fifth)"), { 5, 0, -6.0, -70.0, 100.0, 60.0, 0 } },
100 { XO("Valve Overdrive"), { 6, 1, -6.0, -70.0, 30.0, 40.0, 0 } },
101 { XO("2nd Harmonic (Octave)"), { 6, 1, -6.0, -70.0, 50.0, 0.0, 0 } },
102 { XO("Gated Expansion Distortion"), { 7, 0, -6.0, -70.0, 30.0, 80.0, 0 } },
103 { XO("Leveller, Light, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 1 } },
104 { XO("Leveller, Moderate, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 2 } },
105 { XO("Leveller, Heavy, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 3 } },
106 { XO("Leveller, Heavier, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 4 } },
107 { XO("Leveller, Heaviest, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 5 } },
108 { XO("Half-wave Rectifier"), { 9, 0, -6.0, -70.0, 50.0, 50.0, 0 } },
109 { XO("Full-wave Rectifier"), { 9, 0, -6.0, -70.0, 100.0, 50.0, 0 } },
110 { XO("Full-wave Rectifier (DC blocked)"), { 9, 1, -6.0, -70.0, 100.0, 50.0, 0 } },
111 { XO("Percussion Limiter"), {10, 0, -12.0, -70.0, 100.0, 30.0, 0 } },
113
115{
116 static const TranslatableString names[] = {
117 XO("Upper Threshold"),
118 XO("Noise Floor"),
119 XO("Parameter 1"),
120 XO("Parameter 2"),
121 XO("Number of repeats"),
122 };
123
124 return names[ index ];
125}
126
127//
128// EffectDistortion
129//
130
132{ XO("Distortion") };
133
135
136BEGIN_EVENT_TABLE(EffectDistortion, wxEvtHandler)
138 EVT_CHECKBOX(ID_DCBlock, EffectDistortion::OnDCBlockCheckbox)
143 EVT_TEXT(ID_Param1, EffectDistortion::OnParam1Text)
144 EVT_SLIDER(ID_Param1, EffectDistortion::OnParam1Slider)
145 EVT_TEXT(ID_Param2, EffectDistortion::OnParam2Text)
146 EVT_SLIDER(ID_Param2, EffectDistortion::OnParam2Slider)
147 EVT_TEXT(ID_Repeats, EffectDistortion::OnRepeatsText)
148 EVT_SLIDER(ID_Repeats, EffectDistortion::OnRepeatsSlider)
150
152{
153 Parameters().Reset(*this);
154 wxASSERT(nTableTypes == WXSIZEOF(kTableTypeStrings));
155 mMakeupGain = 1.0;
156 mbSavedFilterState = DCBlock.def;
157
158 SetLinearEffectFlag(false);
159}
160
162{
163}
164
165// ComponentInterface implementation
166
168{
169 return Symbol;
170}
171
173{
174 return XO("Waveshaping distortion effect");
175}
176
178{
179 return L"Distortion";
180}
181
182// EffectDefinitionInterface implementation
183
185{
186 return EffectTypeProcess;
187}
188
190{
191 // TODO reenable after achieving statelessness
193// return RealtimeSince::Always;
194}
195
197{
198 return 1;
199}
200
202{
203 return 1;
204}
205
207 EffectSettings &, double sampleRate, ChannelNames chanMap)
208{
209 InstanceInit(mMaster, sampleRate);
210 return true;
211}
212
214 const float *const *inBlock, float *const *outBlock, size_t blockLen)
215{
216 return InstanceProcess(settings, mMaster, inBlock, outBlock, blockLen);
217}
218
220{
221 SetBlockSize(512);
222 mSlaves.clear();
223 return true;
224}
225
227 EffectSettings &, EffectOutputs *, unsigned, float sampleRate)
228{
230
231 InstanceInit(slave, sampleRate);
232
233 mSlaves.push_back(slave);
234
235 return true;
236}
237
239{
240 mSlaves.clear();
241
242 return true;
243}
244
246 const float *const *inbuf, float *const *outbuf, size_t numSamples)
247{
248 if (group >= mSlaves.size())
249 return 0;
250 return InstanceProcess(settings, mSlaves[group], inbuf, outbuf, numSamples);
251}
252
254{
256
257 for (size_t i = 0; i < WXSIZEOF(FactoryPresets); i++)
258 {
259 names.push_back( FactoryPresets[i].name.Translation() );
260 }
261
262 return names;
263}
264
267{
268 // To do: externalize state so const_cast isn't needed
269 return const_cast<EffectDistortion*>(this)->DoLoadFactoryPreset(id);
270}
271
273{
274 if (id < 0 || id >= (int) WXSIZEOF(FactoryPresets))
275 {
276 return {};
277 }
278
279 mParams = FactoryPresets[id].params;
281
282 return { nullptr };
283}
284
285
286// Effect implementation
287
288std::unique_ptr<EffectUIValidator> EffectDistortion::PopulateOrExchange(
290 const EffectOutputs *)
291{
292 S.AddSpace(0, 5);
293 S.StartVerticalLay();
294 {
295 S.StartMultiColumn(4, wxCENTER);
296 {
298 .MinSize( { -1, -1 } )
300 .AddChoice(XXO("Distortion type:"),
302
303 mDCBlockCheckBox = S.Id(ID_DCBlock).AddCheckBox(XXO("DC blocking filter"),
304 DCBlock.def);
305 }
306 S.EndMultiColumn();
307 S.AddSpace(0, 10);
308
309
310 S.StartStatic(XO("Threshold controls"));
311 {
312 S.StartMultiColumn(4, wxEXPAND);
313 S.SetStretchyCol(2);
314 {
315 // Allow space for first Column
316 S.AddSpace(250,0); S.AddSpace(0,0); S.AddSpace(0,0); S.AddSpace(0,0);
317
318 // Upper threshold control
319 mThresholdTxt = S.AddVariableText(defaultLabel(0),
320 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
322 .Name(defaultLabel(0))
323 .Validator<FloatingPointValidator<double>>(
324 2, &mParams.mThreshold_dB, NumValidatorStyle::DEFAULT,
326 .AddTextBox( {}, wxT(""), 10);
327
329 .Name(defaultLabel(0))
330 .Style(wxSL_HORIZONTAL)
331 .AddSlider( {}, 0,
334 S.AddSpace(20, 0);
335
336 // Noise floor control
337 mNoiseFloorTxt = S.AddVariableText(defaultLabel(1),
338 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
340 .Name(defaultLabel(1))
341 .Validator<FloatingPointValidator<double>>(
342 2, &mParams.mNoiseFloor, NumValidatorStyle::DEFAULT,
344 )
345 .AddTextBox( {}, wxT(""), 10);
346
348 .Name(defaultLabel(1))
349 .Style(wxSL_HORIZONTAL)
350 .AddSlider( {}, 0, NoiseFloor.max, NoiseFloor.min);
351 S.AddSpace(20, 0);
352 }
353 S.EndMultiColumn();
354 }
355 S.EndStatic();
356
357 S.StartStatic(XO("Parameter controls"));
358 {
359 S.StartMultiColumn(4, wxEXPAND);
360 S.SetStretchyCol(2);
361 {
362 // Allow space for first Column
363 S.AddSpace(250,0); S.AddSpace(0,0); S.AddSpace(0,0); S.AddSpace(0,0);
364
365 // Parameter1 control
366 mParam1Txt = S.AddVariableText(defaultLabel(2),
367 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
368 mParam1T = S.Id(ID_Param1)
369 .Name(defaultLabel(2))
370 .Validator<FloatingPointValidator<double>>(
371 2, &mParams.mParam1, NumValidatorStyle::DEFAULT,
373 )
374 .AddTextBox( {}, wxT(""), 10);
375
376 mParam1S = S.Id(ID_Param1)
377 .Name(defaultLabel(2))
378 .Style(wxSL_HORIZONTAL)
379 .AddSlider( {}, 0, Param1.max, Param1.min);
380 S.AddSpace(20, 0);
381
382 // Parameter2 control
383 mParam2Txt = S.AddVariableText(defaultLabel(3),
384 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
385 mParam2T = S.Id(ID_Param2)
386 .Name(defaultLabel(3))
387 .Validator<FloatingPointValidator<double>>(
388 2, &mParams.mParam2, NumValidatorStyle::DEFAULT,
390 )
391 .AddTextBox( {}, wxT(""), 10);
392
393 mParam2S = S.Id(ID_Param2)
394 .Name(defaultLabel(3))
395 .Style(wxSL_HORIZONTAL)
396 .AddSlider( {}, 0, Param2.max, Param2.min);
397 S.AddSpace(20, 0);
398
399 // Repeats control
400 mRepeatsTxt = S.AddVariableText(defaultLabel(4),
401 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
403 .Name(defaultLabel(4))
404 .Validator<IntegerValidator<int>>(
405 &mParams.mRepeats, NumValidatorStyle::DEFAULT,
407 )
408 .AddTextBox( {}, wxT(""), 10);
409
411 .Name(defaultLabel(4))
412 .Style(wxSL_HORIZONTAL)
413 .AddSlider( {}, Repeats.def, Repeats.max, Repeats.min);
414 S.AddSpace(20, 0);
415 }
416 S.EndMultiColumn();
417 }
418 S.EndStatic();
419 }
420 S.EndVerticalLay();
421
422 return nullptr;
423}
424
426{
427 if (!mUIParent->TransferDataToWindow())
428 {
429 return false;
430 }
431
432 mThresholdS->SetValue((int) (mThreshold * Threshold_dB.scale + 0.5));
434 mNoiseFloorS->SetValue((int) mParams.mNoiseFloor + 0.5);
435 mParam1S->SetValue((int) mParams.mParam1 + 0.5);
436 mParam2S->SetValue((int) mParams.mParam2 + 0.5);
437 mRepeatsS->SetValue(mParams.mRepeats);
438
440
441 UpdateUI();
442
443 return true;
444}
445
447{
448 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
449 {
450 return false;
451 }
452
454
455 return true;
456}
457
459{
460 data.samplerate = sampleRate;
461 data.skipcount = 0;
463 data.dcblock = mParams.mDCBlock;
466 data.param1 = mParams.mParam1;
467 data.param2 = mParams.mParam2;
468 data.repeats = mParams.mRepeats;
469
470 // DC block filter variables
471 data.queuetotal = 0.0;
472
473 //std::queue<float>().swap(data.queuesamples);
474 while (!data.queuesamples.empty())
475 data.queuesamples.pop();
476
477 MakeTable();
478
479 return;
480}
481
484 const float *const *inBlock, float *const *outBlock, size_t blockLen)
485{
486 const float *ibuf = inBlock[0];
487 float *obuf = outBlock[0];
488
489 bool update = (mParams.mTableChoiceIndx == data.tablechoiceindx &&
492 mParams.mParam1 == data.param1 &&
493 mParams.mParam2 == data.param2 &&
494 mParams.mRepeats == data.repeats)? false : true;
495
496 double p1 = mParams.mParam1 / 100.0;
497 double p2 = mParams.mParam2 / 100.0;
498
502 data.param1 = mParams.mParam1;
503 data.repeats = mParams.mRepeats;
504
505 for (decltype(blockLen) i = 0; i < blockLen; i++) {
506 if (update && ((data.skipcount++) % skipsamples == 0)) {
507 MakeTable();
508 }
509
511 {
512 case kHardClip:
513 // Param2 = make-up gain.
514 obuf[i] = WaveShaper(ibuf[i]) * ((1 - p2) + (mMakeupGain * p2));
515 break;
516 case kSoftClip:
517 // Param2 = make-up gain.
518 obuf[i] = WaveShaper(ibuf[i]) * ((1 - p2) + (mMakeupGain * p2));
519 break;
520 case kHalfSinCurve:
521 obuf[i] = WaveShaper(ibuf[i]) * p2;
522 break;
523 case kExpCurve:
524 obuf[i] = WaveShaper(ibuf[i]) * p2;
525 break;
526 case kLogCurve:
527 obuf[i] = WaveShaper(ibuf[i]) * p2;
528 break;
529 case kCubic:
530 obuf[i] = WaveShaper(ibuf[i]) * p2;
531 break;
532 case kEvenHarmonics:
533 obuf[i] = WaveShaper(ibuf[i]);
534 break;
535 case kSinCurve:
536 obuf[i] = WaveShaper(ibuf[i]) * p2;
537 break;
538 case kLeveller:
539 obuf[i] = WaveShaper(ibuf[i]);
540 break;
541 case kRectifier:
542 obuf[i] = WaveShaper(ibuf[i]);
543 break;
544 case kHardLimiter:
545 // Mix equivalent to LADSPA effect's "Wet / Residual" mix
546 obuf[i] = (WaveShaper(ibuf[i]) * (p1 - p2)) + (ibuf[i] * p2);
547 break;
548 default:
549 obuf[i] = WaveShaper(ibuf[i]);
550 }
551 if (mParams.mDCBlock) {
552 obuf[i] = DCFilter(data, obuf[i]);
553 }
554 }
555
556 return blockLen;
557}
558
559void EffectDistortion::OnTypeChoice(wxCommandEvent& /*evt*/)
560{
561 mTypeChoiceCtrl->GetValidator()->TransferFromWindow();
562
563 UpdateUI();
564}
565
566void EffectDistortion::OnDCBlockCheckbox(wxCommandEvent& /*evt*/)
567{
568 mParams.mDCBlock = mDCBlockCheckBox->GetValue();
570}
571
572
573void EffectDistortion::OnThresholdText(wxCommandEvent& /*evt*/)
574{
575 mThresholdT->GetValidator()->TransferFromWindow();
577 mThresholdS->SetValue((int) (mThreshold * Threshold_dB.scale + 0.5));
578}
579
580void EffectDistortion::OnThresholdSlider(wxCommandEvent& evt)
581{
582 static const double MIN_Threshold_Linear = DB_TO_LINEAR(Threshold_dB.min);
583
584 mThreshold = (double) evt.GetInt() / Threshold_dB.scale;
586 mThreshold = std::max(MIN_Threshold_Linear, mThreshold);
587 mThresholdT->GetValidator()->TransferToWindow();
588}
589
590void EffectDistortion::OnNoiseFloorText(wxCommandEvent& /*evt*/)
591{
592 mNoiseFloorT->GetValidator()->TransferFromWindow();
593 mNoiseFloorS->SetValue((int) floor(mParams.mNoiseFloor + 0.5));
594}
595
597{
598 mParams.mNoiseFloor = (double) evt.GetInt();
599 mNoiseFloorT->GetValidator()->TransferToWindow();
600}
601
602
603void EffectDistortion::OnParam1Text(wxCommandEvent& /*evt*/)
604{
605 mParam1T->GetValidator()->TransferFromWindow();
606 mParam1S->SetValue((int) floor(mParams.mParam1 + 0.5));
607}
608
609void EffectDistortion::OnParam1Slider(wxCommandEvent& evt)
610{
611 mParams.mParam1 = (double) evt.GetInt();
612 mParam1T->GetValidator()->TransferToWindow();
613}
614
615void EffectDistortion::OnParam2Text(wxCommandEvent& /*evt*/)
616{
617 mParam2T->GetValidator()->TransferFromWindow();
618 mParam2S->SetValue((int) floor(mParams.mParam2 + 0.5));
619}
620
621void EffectDistortion::OnParam2Slider(wxCommandEvent& evt)
622{
623 mParams.mParam2 = (double) evt.GetInt();
624 mParam2T->GetValidator()->TransferToWindow();
625}
626
627void EffectDistortion::OnRepeatsText(wxCommandEvent& /*evt*/)
628{
629 mRepeatsT->GetValidator()->TransferFromWindow();
630 mRepeatsS->SetValue(mParams.mRepeats);
631}
632
633void EffectDistortion::OnRepeatsSlider(wxCommandEvent& evt)
634{
635 mParams.mRepeats = evt.GetInt();
636 mRepeatsT->GetValidator()->TransferToWindow();
637
638}
639
641{
642 // set control text and names to match distortion type
644 {
645 case kHardClip:
651
652 UpdateControl(ID_Threshold, true, XO("Clipping level"));
654 UpdateControl(ID_Param1, true, XO("Drive"));
655 UpdateControl(ID_Param2, true, XO("Make-up Gain"));
657 UpdateControl(ID_DCBlock, false, {});
658 break;
659 case kSoftClip:
665
666 UpdateControl(ID_Threshold, true, XO("Clipping threshold"));
668 UpdateControl(ID_Param1, true, XO("Hardness"));
669 UpdateControl(ID_Param2, true, XO("Make-up Gain"));
671 UpdateControl(ID_DCBlock, false, {});
672 break;
673 case kHalfSinCurve:
679
682 UpdateControl(ID_Param1, true, XO("Distortion amount"));
683 UpdateControl(ID_Param2, true, XO("Output level"));
685 UpdateControl(ID_DCBlock, false, {});
686 break;
687 case kExpCurve:
693
696 UpdateControl(ID_Param1, true, XO("Distortion amount"));
697 UpdateControl(ID_Param2, true, XO("Output level"));
699 UpdateControl(ID_DCBlock, false, {});
700 break;
701 case kLogCurve:
707
710 UpdateControl(ID_Param1, true, XO("Distortion amount"));
711 UpdateControl(ID_Param2, true, XO("Output level"));
713 UpdateControl(ID_DCBlock, false, {});
714 break;
715 case kCubic:
721
724 UpdateControl(ID_Param1, true, XO("Distortion amount"));
725 UpdateControl(ID_Param2, true, XO("Output level"));
726 UpdateControl(ID_Repeats, true, XO("Repeat processing"));
727 UpdateControl(ID_DCBlock, false, {});
728 break;
729 case kEvenHarmonics:
735
738 UpdateControl(ID_Param1, true, XO("Distortion amount"));
739 UpdateControl(ID_Param2, true, XO("Harmonic brightness"));
741 UpdateControl(ID_DCBlock, true, {});
742 break;
743 case kSinCurve:
749
752 UpdateControl(ID_Param1, true, XO("Distortion amount"));
753 UpdateControl(ID_Param2, true, XO("Output level"));
755 UpdateControl(ID_DCBlock, false, {});
756 break;
757 case kLeveller:
763
766 UpdateControl(ID_Param1, true, XO("Levelling fine adjustment"));
768 UpdateControl(ID_Repeats, true, XO("Degree of Levelling"));
769 UpdateControl(ID_DCBlock, false, {});
770 break;
771 case kRectifier:
777
780 UpdateControl(ID_Param1, true, XO("Distortion amount"));
783 UpdateControl(ID_DCBlock, true, {});
784 break;
785 case kHardLimiter:
791
792 UpdateControl(ID_Threshold, true, XO("dB Limit"));
794 UpdateControl(ID_Param1, true, XO("Wet level"));
795 UpdateControl(ID_Param2, true, XO("Residual level"));
797 UpdateControl(ID_DCBlock, false, {});
798 break;
799 default:
805 UpdateControl(ID_DCBlock, false, {});
806 }
807}
808
810 control id, bool enabled, TranslatableString name)
811{
812 auto suffix = XO("(Not Used):");
813 switch (id)
814 {
815 case ID_Threshold: {
816 /* i18n-hint: Control range. */
817 if (enabled) suffix = XO("(-100 to 0 dB):");
818 name.Join( suffix, wxT(" ") );
819
820 // Logarithmic slider is set indirectly
822 mThresholdS->SetValue((int) (mThreshold * Threshold_dB.scale + 0.5));
823
824 auto translated = name.Translation();
825 mThresholdTxt->SetLabel(translated);
826 mThresholdS->SetName(translated);
827 mThresholdT->SetName(translated);
828 mThresholdS->Enable(enabled);
829 mThresholdT->Enable(enabled);
830 break;
831 }
832 case ID_NoiseFloor: {
833 /* i18n-hint: Control range. */
834 if (enabled) suffix = XO("(-80 to -20 dB):");
835 name.Join( suffix, wxT(" ") );
836
837 auto translated = name.Translation();
838 mNoiseFloorTxt->SetLabel(translated);
839 mNoiseFloorS->SetName(translated);
840 mNoiseFloorT->SetName(translated);
841 mNoiseFloorS->Enable(enabled);
842 mNoiseFloorT->Enable(enabled);
843 break;
844 }
845 case ID_Param1: {
846 /* i18n-hint: Control range. */
847 if (enabled) suffix = XO("(0 to 100):");
848 name.Join( suffix, wxT(" ") );
849
850 auto translated = name.Translation();
851 mParam1Txt->SetLabel(translated);
852 mParam1S->SetName(translated);
853 mParam1T->SetName(translated);
854 mParam1S->Enable(enabled);
855 mParam1T->Enable(enabled);
856 break;
857 }
858 case ID_Param2: {
859 /* i18n-hint: Control range. */
860 if (enabled) suffix = XO("(0 to 100):");
861 name.Join( suffix, wxT(" ") );
862
863 auto translated = name.Translation();
864 mParam2Txt->SetLabel(translated);
865 mParam2S->SetName(translated);
866 mParam2T->SetName(translated);
867 mParam2S->Enable(enabled);
868 mParam2T->Enable(enabled);
869 break;
870 }
871 case ID_Repeats: {
872 /* i18n-hint: Control range. */
873 if (enabled) suffix = XO("(0 to 5):");
874 name.Join( suffix, wxT(" ") );
875
876 auto translated = name.Translation();
877 mRepeatsTxt->SetLabel(translated);
878 mRepeatsS->SetName(translated);
879 mRepeatsT->SetName(translated);
880 mRepeatsS->Enable(enabled);
881 mRepeatsT->Enable(enabled);
882 break;
883 }
884 case ID_DCBlock: {
885 if (enabled) {
888 }
889 else {
890 mDCBlockCheckBox->SetValue(false);
891 mParams.mDCBlock = false;
892 }
893
894 mDCBlockCheckBox->Enable(enabled);
895 break;
896 }
897 default:
898 break;
899 }
900}
901
902void EffectDistortion::UpdateControlText(wxTextCtrl* textCtrl, wxString& string, bool enabled)
903{
904 if (enabled) {
905 if (textCtrl->GetValue().empty())
906 textCtrl->SetValue(string);
907 else
908 string = textCtrl->GetValue();
909 }
910 else {
911 if (!textCtrl->GetValue().empty())
912 string = textCtrl->GetValue();
913 textCtrl->SetValue(wxT(""));
914 }
915}
916
918{
920 {
921 case kHardClip:
922 HardClip();
923 break;
924 case kSoftClip:
925 SoftClip();
926 break;
927 case kHalfSinCurve:
928 HalfSinTable();
929 break;
930 case kExpCurve:
932 break;
933 case kLogCurve:
935 break;
936 case kCubic:
937 CubicTable();
938 break;
939 case kEvenHarmonics:
941 break;
942 case kSinCurve:
943 SineTable();
944 break;
945 case kLeveller:
946 Leveller();
947 break;
948 case kRectifier:
949 Rectifier();
950 break;
951 case kHardLimiter:
952 HardLimiter();
953 break;
954 }
955}
956
957
958//
959// Preset tables for gain lookup
960//
961
963{
964 double lowThresh = 1 - mThreshold;
965 double highThresh = 1 + mThreshold;
966
967 for (int n = 0; n < TABLESIZE; n++) {
968 if (n < (STEPS * lowThresh))
969 mTable[n] = - mThreshold;
970 else if (n > (STEPS * highThresh))
971 mTable[n] = mThreshold;
972 else
973 mTable[n] = n/(double)STEPS - 1;
974
975 mMakeupGain = 1.0 / mThreshold;
976 }
977}
978
980{
981 double threshold = 1 + mThreshold;
982 double amount = std::pow(2.0, 7.0 * mParams.mParam1 / 100.0); // range 1 to 128
983 double peak = LogCurve(mThreshold, 1.0, amount);
984 mMakeupGain = 1.0 / peak;
985 mTable[STEPS] = 0.0; // origin
986
987 // positive half of table
988 for (int n = STEPS; n < TABLESIZE; n++) {
989 if (n < (STEPS * threshold)) // origin to threshold
990 mTable[n] = n/(float)STEPS - 1;
991 else
992 mTable[n] = LogCurve(mThreshold, n/(double)STEPS - 1, amount);
993 }
995}
996
997float EffectDistortion::LogCurve(double threshold, float value, double ratio)
998{
999 return threshold + ((std::exp(ratio * (threshold - value)) - 1) / -ratio);
1000}
1001
1003{
1004 double amount = std::min(0.999, DB_TO_LINEAR(-1 * mParams.mParam1)); // avoid divide by zero
1005
1006 for (int n = STEPS; n < TABLESIZE; n++) {
1007 double linVal = n/(float)STEPS;
1008 double scale = -1.0 / (1.0 - amount); // unity gain at 0dB
1009 double curve = std::exp((linVal - 1) * std::log(amount));
1010 mTable[n] = scale * (curve -1);
1011 }
1012 CopyHalfTable();
1013}
1014
1016{
1017 double amount = mParams.mParam1;
1018 double stepsize = 1.0 / STEPS;
1019 double linVal = 0;
1020
1021 if (amount == 0){
1022 for (int n = STEPS; n < TABLESIZE; n++) {
1023 mTable[n] = linVal;
1024 linVal += stepsize;
1025 }
1026 }
1027 else {
1028 for (int n = STEPS; n < TABLESIZE; n++) {
1029 mTable[n] = std::log(1 + (amount * linVal)) / std::log(1 + amount);
1030 linVal += stepsize;
1031 }
1032 }
1033 CopyHalfTable();
1034}
1035
1037{
1038 int iter = std::floor(mParams.mParam1 / 20.0);
1039 double fractionalpart = (mParams.mParam1 / 20.0) - iter;
1040 double stepsize = 1.0 / STEPS;
1041 double linVal = 0;
1042
1043 for (int n = STEPS; n < TABLESIZE; n++) {
1044 mTable[n] = linVal;
1045 for (int i = 0; i < iter; i++) {
1046 mTable[n] = std::sin(mTable[n] * M_PI_2);
1047 }
1048 mTable[n] += ((std::sin(mTable[n] * M_PI_2) - mTable[n]) * fractionalpart);
1049 linVal += stepsize;
1050 }
1051 CopyHalfTable();
1052}
1053
1055{
1056 double amount = mParams.mParam1 * std::sqrt(3.0) / 100.0;
1057 double gain = 1.0;
1058 if (amount != 0.0)
1059 gain = 1.0 / Cubic(std::min(amount, 1.0));
1060
1061 double stepsize = amount / STEPS;
1062 double x = -amount;
1063
1064 if (amount == 0) {
1065 for (int i = 0; i < TABLESIZE; i++) {
1066 mTable[i] = (i / (double)STEPS) - 1.0;
1067 }
1068 }
1069 else {
1070 for (int i = 0; i < TABLESIZE; i++) {
1071 mTable[i] = gain * Cubic(x);
1072 for (int j = 0; j < mParams.mRepeats; j++) {
1073 mTable[i] = gain * Cubic(mTable[i] * amount);
1074 }
1075 x += stepsize;
1076 }
1077 }
1078}
1079
1081{
1082 if (mParams.mParam1 == 0.0)
1083 return x;
1084
1085 return x - (std::pow(x, 3.0) / 3.0);
1086}
1087
1088
1090{
1091 double amount = mParams.mParam1 / -100.0;
1092 // double C = std::sin(std::max(0.001, mParams.mParam2) / 100.0) * 10.0;
1093 double C = std::max(0.001, mParams.mParam2) / 10.0;
1094
1095 double step = 1.0 / STEPS;
1096 double xval = -1.0;
1097
1098 for (int i = 0; i < TABLESIZE; i++) {
1099 mTable[i] = ((1 + amount) * xval) -
1100 (xval * (amount / std::tanh(C)) * std::tanh(C * xval));
1101 xval += step;
1102 }
1103}
1104
1106{
1107 int iter = std::floor(mParams.mParam1 / 20.0);
1108 double fractionalpart = (mParams.mParam1 / 20.0) - iter;
1109 double stepsize = 1.0 / STEPS;
1110 double linVal = 0.0;
1111
1112 for (int n = STEPS; n < TABLESIZE; n++) {
1113 mTable[n] = linVal;
1114 for (int i = 0; i < iter; i++) {
1115 mTable[n] = (1.0 + std::sin((mTable[n] * M_PI) - M_PI_2)) / 2.0;
1116 }
1117 mTable[n] += (((1.0 + std::sin((mTable[n] * M_PI) - M_PI_2)) / 2.0) - mTable[n]) * fractionalpart;
1118 linVal += stepsize;
1119 }
1120 CopyHalfTable();
1121}
1122
1124{
1125 double noiseFloor = DB_TO_LINEAR(mParams.mNoiseFloor);
1126 int numPasses = mParams.mRepeats;
1127 double fractionalPass = mParams.mParam1 / 100.0;
1128
1129 const int numPoints = 6;
1130 const double gainFactors[numPoints] = { 0.80, 1.00, 1.20, 1.20, 1.00, 0.80 };
1131 double gainLimits[numPoints] = { 0.0001, 0.0, 0.1, 0.3, 0.5, 1.0 };
1132 double addOnValues[numPoints];
1133
1134 gainLimits[1] = noiseFloor;
1135 /* In the original Leveller effect, behaviour was undefined for threshold > 20 dB.
1136 * If we want to support > 20 dB we need to scale the points to keep them non-decreasing.
1137 *
1138 * if (noiseFloor > gainLimits[2]) {
1139 * for (int i = 3; i < numPoints; i++) {
1140 * gainLimits[i] = noiseFloor + ((1 - noiseFloor)*((gainLimits[i] - 0.1) / 0.9));
1141 * }
1142 * gainLimits[2] = noiseFloor;
1143 * }
1144 */
1145
1146 // Calculate add-on values
1147 addOnValues[0] = 0.0;
1148 for (int i = 0; i < numPoints-1; i++) {
1149 addOnValues[i+1] = addOnValues[i] + (gainLimits[i] * (gainFactors[i] - gainFactors[1 + i]));
1150 }
1151
1152 // Positive half of table.
1153 // The original effect increased the 'strength' of the effect by
1154 // repeated passes over the audio data.
1155 // Here we model that more efficiently by repeated passes over a linear table.
1156 for (int n = STEPS; n < TABLESIZE; n++) {
1157 mTable[n] = ((double) (n - STEPS) / (double) STEPS);
1158 for (int j = 0; j < numPasses; j++) {
1159 // Find the highest index for gain adjustment
1160 int index = numPoints - 1;
1161 for (int i = index; i >= 0 && mTable[n] < gainLimits[i]; i--) {
1162 index = i;
1163 }
1164 // the whole number of 'repeats'
1165 mTable[n] = (mTable[n] * gainFactors[index]) + addOnValues[index];
1166 }
1167 // Extrapolate for fine adjustment.
1168 // tiny fractions are not worth the processing time
1169 if (fractionalPass > 0.001) {
1170 int index = numPoints - 1;
1171 for (int i = index; i >= 0 && mTable[n] < gainLimits[i]; i--) {
1172 index = i;
1173 }
1174 mTable[n] += fractionalPass * ((mTable[n] * (gainFactors[index] - 1)) + addOnValues[index]);
1175 }
1176 }
1177 CopyHalfTable();
1178}
1179
1181{
1182 double amount = (mParams.mParam1 / 50.0) - 1;
1183 double stepsize = 1.0 / STEPS;
1184 int index = STEPS;
1185
1186 // positive half of waveform is passed unaltered.
1187 for (int n = 0; n <= STEPS; n++) {
1188 mTable[index] = n * stepsize;
1189 index += 1;
1190 }
1191
1192 // negative half of table
1193 index = STEPS - 1;
1194 for (int n = 1; n <= STEPS; n++) {
1195 mTable[index] = n * stepsize * amount;
1196 index--;
1197 }
1198}
1199
1201{
1202 // The LADSPA "hardLimiter 1413" is basically hard clipping,
1203 // but with a 'kind of' wet/dry mix:
1204 // out = ((wet-residual)*clipped) + (residual*in)
1205 HardClip();
1206}
1207
1208
1209// Helper functions for lookup tables
1210
1212{
1213 // Copy negative half of table from positive half
1214 int count = TABLESIZE - 1;
1215 for (int n = 0; n < STEPS; n++) {
1216 mTable[n] = -mTable[count];
1217 count--;
1218 }
1219}
1220
1221
1223{
1224 float out;
1225 int index;
1226 double xOffset;
1227 double amount = 1;
1228
1229 switch (mParams.mTableChoiceIndx)
1230 {
1231 // Do any pre-processing here
1232 case kHardClip:
1233 // Pre-gain
1234 amount = mParams.mParam1 / 100.0;
1235 sample *= 1+amount;
1236 break;
1237 default: break;
1238 }
1239
1240 index = std::floor(sample * STEPS) + STEPS;
1241 index = wxMax<int>(wxMin<int>(index, 2 * STEPS - 1), 0);
1242 xOffset = ((1 + sample) * STEPS) - index;
1243 xOffset = wxMin<double>(wxMax<double>(xOffset, 0.0), 1.0); // Clip at 0dB
1244
1245 // linear interpolation: y = y0 + (y1-y0)*(x-x0)
1246 out = mTable[index] + (mTable[index + 1] - mTable[index]) * xOffset;
1247
1248 return out;
1249}
1250
1251
1253{
1254 // Rolling average gives less offset at the start than an IIR filter.
1255 const unsigned int queueLength = std::floor(data.samplerate / 20.0);
1256
1257 data.queuetotal += sample;
1258 data.queuesamples.push(sample);
1259
1260 if (data.queuesamples.size() > queueLength) {
1261 data.queuetotal -= data.queuesamples.front();
1262 data.queuesamples.pop();
1263 }
1264
1265 return sample - (data.queuetotal / data.queuesamples.size());
1266}
wxT("CloseDown"))
END_EVENT_TABLE()
int min(int a, int b)
@ ID_Threshold
Definition: Compressor.cpp:52
@ ID_NoiseFloor
Definition: Compressor.cpp:53
TranslatableString defaultLabel(int index)
Definition: Distortion.cpp:114
static const struct @21 FactoryPresets[]
EffectDistortion::Params params
Definition: Distortion.cpp:83
#define M_PI_2
Definition: Distortion.cpp:32
#define skipsamples
Definition: Distortion.cpp:78
const TranslatableString name
Definition: Distortion.cpp:82
#define M_PI
Definition: Distortion.cpp:29
#define TABLESIZE
Definition: Distortion.h:27
#define STEPS
Definition: Distortion.h:26
std::optional< std::unique_ptr< EffectSettingsAccess::Message > > OptionalMessage
EffectType
@ EffectTypeProcess
ChannelName
std::vector< RegistryPath > RegistryPaths
Definition: Identifier.h:219
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define LINEAR_TO_DB(x)
Definition: MemoryX.h:544
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:543
@ ID_Type
Definition: ScienFilter.cpp:79
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Convenience function often useful when adding choice controls.
static TranslatableStrings names
Definition: TagsEditor.cpp:151
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:87
int id
Generates EffectParameterMethods overrides from variadic template arguments.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
RealtimeSince
In which versions of Audacity was an effect realtime capable?
A WaveShaper distortion effect.
Definition: Distortion.h:48
bool mbSavedFilterState
Definition: Distortion.h:183
wxString mOldRepeatsTxt
Definition: Distortion.h:216
wxString mOldParam2Txt
Definition: Distortion.h:215
unsigned GetAudioInCount() const override
How many input buffers to allocate at once.
Definition: Distortion.cpp:196
OptionalMessage DoLoadFactoryPreset(int id)
Definition: Distortion.cpp:272
static constexpr EffectParameter Threshold_dB
Definition: Distortion.h:246
void OnThresholdText(wxCommandEvent &evt)
Definition: Distortion.cpp:573
void OnParam2Text(wxCommandEvent &evt)
Definition: Distortion.cpp:615
static constexpr EffectParameter Param2
Definition: Distortion.h:252
void OnThresholdSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:580
void InstanceInit(EffectDistortionState &data, float sampleRate)
Definition: Distortion.cpp:458
wxStaticText * mRepeatsTxt
Definition: Distortion.h:210
bool ProcessInitialize(EffectSettings &settings, double sampleRate, ChannelNames chanMap) override
Definition: Distortion.cpp:206
wxStaticText * mParam1Txt
Definition: Distortion.h:208
wxSlider * mRepeatsS
Definition: Distortion.h:202
double Cubic(double x)
std::vector< EffectDistortionState > mSlaves
Definition: Distortion.h:179
size_t InstanceProcess(EffectSettings &settings, EffectDistortionState &data, const float *const *inBlock, float *const *outBlock, size_t blockLen)
Definition: Distortion.cpp:482
size_t RealtimeProcess(size_t group, EffectSettings &settings, const float *const *inbuf, float *const *outbuf, size_t numSamples) override
Definition: Distortion.cpp:245
wxStaticText * mParam2Txt
Definition: Distortion.h:209
double mMakeupGain
Definition: Distortion.h:187
static const ComponentInterfaceSymbol Symbol
Definition: Distortion.h:53
static constexpr EffectParameter Param1
Definition: Distortion.h:250
virtual ~EffectDistortion()
Definition: Distortion.cpp:161
void OnParam2Slider(wxCommandEvent &evt)
Definition: Distortion.cpp:621
wxTextCtrl * mParam1T
Definition: Distortion.h:194
static const EnumValueSymbol kTableTypeStrings[nTableTypes]
Definition: Distortion.h:239
void OnTypeChoice(wxCommandEvent &evt)
Definition: Distortion.cpp:559
size_t ProcessBlock(EffectSettings &settings, const float *const *inBlock, float *const *outBlock, size_t blockLen) override
Called for destructive effect computation.
Definition: Distortion.cpp:213
wxSlider * mThresholdS
Definition: Distortion.h:198
wxTextCtrl * mThresholdT
Definition: Distortion.h:192
wxStaticText * mNoiseFloorTxt
Definition: Distortion.h:207
std::unique_ptr< EffectUIValidator > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) override
Add controls to effect panel; always succeeds.
Definition: Distortion.cpp:288
OptionalMessage LoadFactoryPreset(int id, EffectSettings &settings) const override
Definition: Distortion.cpp:266
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
Definition: Distortion.cpp:201
void OnParam1Text(wxCommandEvent &evt)
Definition: Distortion.cpp:603
float LogCurve(double threshold, float value, double ratio)
Definition: Distortion.cpp:997
float WaveShaper(float sample)
void EvenHarmonicTable()
wxSlider * mParam1S
Definition: Distortion.h:200
wxSlider * mNoiseFloorS
Definition: Distortion.h:199
ComponentInterfaceSymbol GetSymbol() const override
Definition: Distortion.cpp:167
wxTextCtrl * mNoiseFloorT
Definition: Distortion.h:193
bool TransferDataToWindow(const EffectSettings &settings) override
Update controls for the settings.
Definition: Distortion.cpp:425
double mTable[TABLESIZE]
Definition: Distortion.h:181
wxTextCtrl * mRepeatsT
Definition: Distortion.h:196
static constexpr EffectParameter Repeats
Definition: Distortion.h:254
wxString mOldmNoiseFloorTxt
Definition: Distortion.h:213
wxString mOldThresholdTxt
Definition: Distortion.h:212
bool RealtimeAddProcessor(EffectSettings &settings, EffectOutputs *pOutputs, unsigned numChannels, float sampleRate) override
Definition: Distortion.cpp:226
static constexpr EffectParameter DCBlock
Definition: Distortion.h:244
bool TransferDataFromWindow(EffectSettings &settings) override
Update the given settings from controls.
Definition: Distortion.cpp:446
static constexpr EnumParameter TableTypeIndx
Definition: Distortion.h:242
static constexpr EffectParameter NoiseFloor
Definition: Distortion.h:248
void OnNoiseFloorSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:596
RealtimeSince RealtimeSupport() const override
Since which version of Audacity has the effect supported realtime?
Definition: Distortion.cpp:189
wxSlider * mParam2S
Definition: Distortion.h:201
wxString mOldParam1Txt
Definition: Distortion.h:214
float DCFilter(EffectDistortionState &data, float sample)
bool RealtimeFinalize(EffectSettings &settings) noexcept override
Definition: Distortion.cpp:238
void OnParam1Slider(wxCommandEvent &evt)
Definition: Distortion.cpp:609
bool RealtimeInitialize(EffectSettings &settings, double sampleRate) override
Definition: Distortion.cpp:219
EffectType GetType() const override
Type determines how it behaves.
Definition: Distortion.cpp:184
TranslatableString GetDescription() const override
Definition: Distortion.cpp:172
void OnDCBlockCheckbox(wxCommandEvent &evt)
Definition: Distortion.cpp:566
const EffectParameterMethods & Parameters() const override
Definition: Distortion.cpp:62
EffectDistortionState mMaster
Definition: Distortion.h:178
wxStaticText * mThresholdTxt
Definition: Distortion.h:206
void UpdateControlText(wxTextCtrl *textCtrl, wxString &string, bool enabled)
Definition: Distortion.cpp:902
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
Definition: Distortion.cpp:177
void OnNoiseFloorText(wxCommandEvent &evt)
Definition: Distortion.cpp:590
void UpdateControl(control id, bool enable, TranslatableString name)
Definition: Distortion.cpp:809
wxCheckBox * mDCBlockCheckBox
Definition: Distortion.h:204
RegistryPaths GetFactoryPresets() const override
Report names of factory presets.
Definition: Distortion.cpp:253
wxTextCtrl * mParam2T
Definition: Distortion.h:195
wxChoice * mTypeChoiceCtrl
Definition: Distortion.h:191
void OnRepeatsSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:633
void OnRepeatsText(wxCommandEvent &evt)
Definition: Distortion.cpp:627
sampleCount skipcount
Definition: Distortion.h:33
std::queue< float > queuesamples
Definition: Distortion.h:43
wxWindow * mUIParent
Definition: Effect.h:307
Performs effect computation.
Hold values to send to effect output meters.
Interface for manipulations of an Effect's settings.
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.
TranslatableString & Join(TranslatableString arg, const wxString &separator={}) &
Append another translatable string.
wxString Translation() const
A Validator is an object which checks whether a wxVariant satisfies a certain criterion....
Definition: Validators.h:53
BuiltinEffectsModule::Registration< EffectDistortion > reg
Definition: Distortion.cpp:134
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.