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, sampleCount, 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 &, 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
266{
267 // To do: externalize state so const_cast isn't needed
268 return const_cast<EffectDistortion*>(this)->DoLoadFactoryPreset(id);
269}
270
272{
273 if (id < 0 || id >= (int) WXSIZEOF(FactoryPresets))
274 {
275 return false;
276 }
277
278 mParams = FactoryPresets[id].params;
280
281 return true;
282}
283
284
285// Effect implementation
286
287std::unique_ptr<EffectUIValidator> EffectDistortion::PopulateOrExchange(
289{
290 S.AddSpace(0, 5);
291 S.StartVerticalLay();
292 {
293 S.StartMultiColumn(4, wxCENTER);
294 {
296 .MinSize( { -1, -1 } )
298 .AddChoice(XXO("Distortion type:"),
300
301 mDCBlockCheckBox = S.Id(ID_DCBlock).AddCheckBox(XXO("DC blocking filter"),
302 DCBlock.def);
303 }
304 S.EndMultiColumn();
305 S.AddSpace(0, 10);
306
307
308 S.StartStatic(XO("Threshold controls"));
309 {
310 S.StartMultiColumn(4, wxEXPAND);
311 S.SetStretchyCol(2);
312 {
313 // Allow space for first Column
314 S.AddSpace(250,0); S.AddSpace(0,0); S.AddSpace(0,0); S.AddSpace(0,0);
315
316 // Upper threshold control
317 mThresholdTxt = S.AddVariableText(defaultLabel(0),
318 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
320 .Name(defaultLabel(0))
321 .Validator<FloatingPointValidator<double>>(
322 2, &mParams.mThreshold_dB, NumValidatorStyle::DEFAULT,
324 .AddTextBox( {}, wxT(""), 10);
325
327 .Name(defaultLabel(0))
328 .Style(wxSL_HORIZONTAL)
329 .AddSlider( {}, 0,
332 S.AddSpace(20, 0);
333
334 // Noise floor control
335 mNoiseFloorTxt = S.AddVariableText(defaultLabel(1),
336 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
338 .Name(defaultLabel(1))
339 .Validator<FloatingPointValidator<double>>(
340 2, &mParams.mNoiseFloor, NumValidatorStyle::DEFAULT,
342 )
343 .AddTextBox( {}, wxT(""), 10);
344
346 .Name(defaultLabel(1))
347 .Style(wxSL_HORIZONTAL)
348 .AddSlider( {}, 0, NoiseFloor.max, NoiseFloor.min);
349 S.AddSpace(20, 0);
350 }
351 S.EndMultiColumn();
352 }
353 S.EndStatic();
354
355 S.StartStatic(XO("Parameter controls"));
356 {
357 S.StartMultiColumn(4, wxEXPAND);
358 S.SetStretchyCol(2);
359 {
360 // Allow space for first Column
361 S.AddSpace(250,0); S.AddSpace(0,0); S.AddSpace(0,0); S.AddSpace(0,0);
362
363 // Parameter1 control
364 mParam1Txt = S.AddVariableText(defaultLabel(2),
365 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
366 mParam1T = S.Id(ID_Param1)
367 .Name(defaultLabel(2))
368 .Validator<FloatingPointValidator<double>>(
369 2, &mParams.mParam1, NumValidatorStyle::DEFAULT,
371 )
372 .AddTextBox( {}, wxT(""), 10);
373
374 mParam1S = S.Id(ID_Param1)
375 .Name(defaultLabel(2))
376 .Style(wxSL_HORIZONTAL)
377 .AddSlider( {}, 0, Param1.max, Param1.min);
378 S.AddSpace(20, 0);
379
380 // Parameter2 control
381 mParam2Txt = S.AddVariableText(defaultLabel(3),
382 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
383 mParam2T = S.Id(ID_Param2)
384 .Name(defaultLabel(3))
385 .Validator<FloatingPointValidator<double>>(
386 2, &mParams.mParam2, NumValidatorStyle::DEFAULT,
388 )
389 .AddTextBox( {}, wxT(""), 10);
390
391 mParam2S = S.Id(ID_Param2)
392 .Name(defaultLabel(3))
393 .Style(wxSL_HORIZONTAL)
394 .AddSlider( {}, 0, Param2.max, Param2.min);
395 S.AddSpace(20, 0);
396
397 // Repeats control
398 mRepeatsTxt = S.AddVariableText(defaultLabel(4),
399 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
401 .Name(defaultLabel(4))
402 .Validator<IntegerValidator<int>>(
403 &mParams.mRepeats, NumValidatorStyle::DEFAULT,
405 )
406 .AddTextBox( {}, wxT(""), 10);
407
409 .Name(defaultLabel(4))
410 .Style(wxSL_HORIZONTAL)
411 .AddSlider( {}, Repeats.def, Repeats.max, Repeats.min);
412 S.AddSpace(20, 0);
413 }
414 S.EndMultiColumn();
415 }
416 S.EndStatic();
417 }
418 S.EndVerticalLay();
419
420 return nullptr;
421}
422
424{
425 mThresholdS->SetValue((int) (mThreshold * Threshold_dB.scale + 0.5));
427 mNoiseFloorS->SetValue((int) mParams.mNoiseFloor + 0.5);
428 mParam1S->SetValue((int) mParams.mParam1 + 0.5);
429 mParam2S->SetValue((int) mParams.mParam2 + 0.5);
430 mRepeatsS->SetValue(mParams.mRepeats);
431
433
434 UpdateUI();
435
436 return true;
437}
438
440{
442 return true;
443}
444
446{
447 data.samplerate = sampleRate;
448 data.skipcount = 0;
450 data.dcblock = mParams.mDCBlock;
453 data.param1 = mParams.mParam1;
454 data.param2 = mParams.mParam2;
455 data.repeats = mParams.mRepeats;
456
457 // DC block filter variables
458 data.queuetotal = 0.0;
459
460 //std::queue<float>().swap(data.queuesamples);
461 while (!data.queuesamples.empty())
462 data.queuesamples.pop();
463
464 MakeTable();
465
466 return;
467}
468
471 const float *const *inBlock, float *const *outBlock, size_t blockLen)
472{
473 const float *ibuf = inBlock[0];
474 float *obuf = outBlock[0];
475
476 bool update = (mParams.mTableChoiceIndx == data.tablechoiceindx &&
479 mParams.mParam1 == data.param1 &&
480 mParams.mParam2 == data.param2 &&
481 mParams.mRepeats == data.repeats)? false : true;
482
483 double p1 = mParams.mParam1 / 100.0;
484 double p2 = mParams.mParam2 / 100.0;
485
489 data.param1 = mParams.mParam1;
490 data.repeats = mParams.mRepeats;
491
492 for (decltype(blockLen) i = 0; i < blockLen; i++) {
493 if (update && ((data.skipcount++) % skipsamples == 0)) {
494 MakeTable();
495 }
496
498 {
499 case kHardClip:
500 // Param2 = make-up gain.
501 obuf[i] = WaveShaper(ibuf[i]) * ((1 - p2) + (mMakeupGain * p2));
502 break;
503 case kSoftClip:
504 // Param2 = make-up gain.
505 obuf[i] = WaveShaper(ibuf[i]) * ((1 - p2) + (mMakeupGain * p2));
506 break;
507 case kHalfSinCurve:
508 obuf[i] = WaveShaper(ibuf[i]) * p2;
509 break;
510 case kExpCurve:
511 obuf[i] = WaveShaper(ibuf[i]) * p2;
512 break;
513 case kLogCurve:
514 obuf[i] = WaveShaper(ibuf[i]) * p2;
515 break;
516 case kCubic:
517 obuf[i] = WaveShaper(ibuf[i]) * p2;
518 break;
519 case kEvenHarmonics:
520 obuf[i] = WaveShaper(ibuf[i]);
521 break;
522 case kSinCurve:
523 obuf[i] = WaveShaper(ibuf[i]) * p2;
524 break;
525 case kLeveller:
526 obuf[i] = WaveShaper(ibuf[i]);
527 break;
528 case kRectifier:
529 obuf[i] = WaveShaper(ibuf[i]);
530 break;
531 case kHardLimiter:
532 // Mix equivalent to LADSPA effect's "Wet / Residual" mix
533 obuf[i] = (WaveShaper(ibuf[i]) * (p1 - p2)) + (ibuf[i] * p2);
534 break;
535 default:
536 obuf[i] = WaveShaper(ibuf[i]);
537 }
538 if (mParams.mDCBlock) {
539 obuf[i] = DCFilter(data, obuf[i]);
540 }
541 }
542
543 return blockLen;
544}
545
546void EffectDistortion::OnTypeChoice(wxCommandEvent& /*evt*/)
547{
548 mTypeChoiceCtrl->GetValidator()->TransferFromWindow();
549
550 UpdateUI();
551}
552
553void EffectDistortion::OnDCBlockCheckbox(wxCommandEvent& /*evt*/)
554{
555 mParams.mDCBlock = mDCBlockCheckBox->GetValue();
557}
558
559
560void EffectDistortion::OnThresholdText(wxCommandEvent& /*evt*/)
561{
562 mThresholdT->GetValidator()->TransferFromWindow();
564 mThresholdS->SetValue((int) (mThreshold * Threshold_dB.scale + 0.5));
565}
566
567void EffectDistortion::OnThresholdSlider(wxCommandEvent& evt)
568{
569 static const double MIN_Threshold_Linear = DB_TO_LINEAR(Threshold_dB.min);
570
571 mThreshold = (double) evt.GetInt() / Threshold_dB.scale;
573 mThreshold = std::max(MIN_Threshold_Linear, mThreshold);
574 mThresholdT->GetValidator()->TransferToWindow();
575}
576
577void EffectDistortion::OnNoiseFloorText(wxCommandEvent& /*evt*/)
578{
579 mNoiseFloorT->GetValidator()->TransferFromWindow();
580 mNoiseFloorS->SetValue((int) floor(mParams.mNoiseFloor + 0.5));
581}
582
584{
585 mParams.mNoiseFloor = (double) evt.GetInt();
586 mNoiseFloorT->GetValidator()->TransferToWindow();
587}
588
589
590void EffectDistortion::OnParam1Text(wxCommandEvent& /*evt*/)
591{
592 mParam1T->GetValidator()->TransferFromWindow();
593 mParam1S->SetValue((int) floor(mParams.mParam1 + 0.5));
594}
595
596void EffectDistortion::OnParam1Slider(wxCommandEvent& evt)
597{
598 mParams.mParam1 = (double) evt.GetInt();
599 mParam1T->GetValidator()->TransferToWindow();
600}
601
602void EffectDistortion::OnParam2Text(wxCommandEvent& /*evt*/)
603{
604 mParam2T->GetValidator()->TransferFromWindow();
605 mParam2S->SetValue((int) floor(mParams.mParam2 + 0.5));
606}
607
608void EffectDistortion::OnParam2Slider(wxCommandEvent& evt)
609{
610 mParams.mParam2 = (double) evt.GetInt();
611 mParam2T->GetValidator()->TransferToWindow();
612}
613
614void EffectDistortion::OnRepeatsText(wxCommandEvent& /*evt*/)
615{
616 mRepeatsT->GetValidator()->TransferFromWindow();
617 mRepeatsS->SetValue(mParams.mRepeats);
618}
619
620void EffectDistortion::OnRepeatsSlider(wxCommandEvent& evt)
621{
622 mParams.mRepeats = evt.GetInt();
623 mRepeatsT->GetValidator()->TransferToWindow();
624
625}
626
628{
629 // set control text and names to match distortion type
631 {
632 case kHardClip:
638
639 UpdateControl(ID_Threshold, true, XO("Clipping level"));
641 UpdateControl(ID_Param1, true, XO("Drive"));
642 UpdateControl(ID_Param2, true, XO("Make-up Gain"));
644 UpdateControl(ID_DCBlock, false, {});
645 break;
646 case kSoftClip:
652
653 UpdateControl(ID_Threshold, true, XO("Clipping threshold"));
655 UpdateControl(ID_Param1, true, XO("Hardness"));
656 UpdateControl(ID_Param2, true, XO("Make-up Gain"));
658 UpdateControl(ID_DCBlock, false, {});
659 break;
660 case kHalfSinCurve:
666
669 UpdateControl(ID_Param1, true, XO("Distortion amount"));
670 UpdateControl(ID_Param2, true, XO("Output level"));
672 UpdateControl(ID_DCBlock, false, {});
673 break;
674 case kExpCurve:
680
683 UpdateControl(ID_Param1, true, XO("Distortion amount"));
684 UpdateControl(ID_Param2, true, XO("Output level"));
686 UpdateControl(ID_DCBlock, false, {});
687 break;
688 case kLogCurve:
694
697 UpdateControl(ID_Param1, true, XO("Distortion amount"));
698 UpdateControl(ID_Param2, true, XO("Output level"));
700 UpdateControl(ID_DCBlock, false, {});
701 break;
702 case kCubic:
708
711 UpdateControl(ID_Param1, true, XO("Distortion amount"));
712 UpdateControl(ID_Param2, true, XO("Output level"));
713 UpdateControl(ID_Repeats, true, XO("Repeat processing"));
714 UpdateControl(ID_DCBlock, false, {});
715 break;
716 case kEvenHarmonics:
722
725 UpdateControl(ID_Param1, true, XO("Distortion amount"));
726 UpdateControl(ID_Param2, true, XO("Harmonic brightness"));
728 UpdateControl(ID_DCBlock, true, {});
729 break;
730 case kSinCurve:
736
739 UpdateControl(ID_Param1, true, XO("Distortion amount"));
740 UpdateControl(ID_Param2, true, XO("Output level"));
742 UpdateControl(ID_DCBlock, false, {});
743 break;
744 case kLeveller:
750
753 UpdateControl(ID_Param1, true, XO("Levelling fine adjustment"));
755 UpdateControl(ID_Repeats, true, XO("Degree of Levelling"));
756 UpdateControl(ID_DCBlock, false, {});
757 break;
758 case kRectifier:
764
767 UpdateControl(ID_Param1, true, XO("Distortion amount"));
770 UpdateControl(ID_DCBlock, true, {});
771 break;
772 case kHardLimiter:
778
779 UpdateControl(ID_Threshold, true, XO("dB Limit"));
781 UpdateControl(ID_Param1, true, XO("Wet level"));
782 UpdateControl(ID_Param2, true, XO("Residual level"));
784 UpdateControl(ID_DCBlock, false, {});
785 break;
786 default:
792 UpdateControl(ID_DCBlock, false, {});
793 }
794}
795
797 control id, bool enabled, TranslatableString name)
798{
799 auto suffix = XO("(Not Used):");
800 switch (id)
801 {
802 case ID_Threshold: {
803 /* i18n-hint: Control range. */
804 if (enabled) suffix = XO("(-100 to 0 dB):");
805 name.Join( suffix, wxT(" ") );
806
807 // Logarithmic slider is set indirectly
809 mThresholdS->SetValue((int) (mThreshold * Threshold_dB.scale + 0.5));
810
811 auto translated = name.Translation();
812 mThresholdTxt->SetLabel(translated);
813 mThresholdS->SetName(translated);
814 mThresholdT->SetName(translated);
815 mThresholdS->Enable(enabled);
816 mThresholdT->Enable(enabled);
817 break;
818 }
819 case ID_NoiseFloor: {
820 /* i18n-hint: Control range. */
821 if (enabled) suffix = XO("(-80 to -20 dB):");
822 name.Join( suffix, wxT(" ") );
823
824 auto translated = name.Translation();
825 mNoiseFloorTxt->SetLabel(translated);
826 mNoiseFloorS->SetName(translated);
827 mNoiseFloorT->SetName(translated);
828 mNoiseFloorS->Enable(enabled);
829 mNoiseFloorT->Enable(enabled);
830 break;
831 }
832 case ID_Param1: {
833 /* i18n-hint: Control range. */
834 if (enabled) suffix = XO("(0 to 100):");
835 name.Join( suffix, wxT(" ") );
836
837 auto translated = name.Translation();
838 mParam1Txt->SetLabel(translated);
839 mParam1S->SetName(translated);
840 mParam1T->SetName(translated);
841 mParam1S->Enable(enabled);
842 mParam1T->Enable(enabled);
843 break;
844 }
845 case ID_Param2: {
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 mParam2Txt->SetLabel(translated);
852 mParam2S->SetName(translated);
853 mParam2T->SetName(translated);
854 mParam2S->Enable(enabled);
855 mParam2T->Enable(enabled);
856 break;
857 }
858 case ID_Repeats: {
859 /* i18n-hint: Control range. */
860 if (enabled) suffix = XO("(0 to 5):");
861 name.Join( suffix, wxT(" ") );
862
863 auto translated = name.Translation();
864 mRepeatsTxt->SetLabel(translated);
865 mRepeatsS->SetName(translated);
866 mRepeatsT->SetName(translated);
867 mRepeatsS->Enable(enabled);
868 mRepeatsT->Enable(enabled);
869 break;
870 }
871 case ID_DCBlock: {
872 if (enabled) {
875 }
876 else {
877 mDCBlockCheckBox->SetValue(false);
878 mParams.mDCBlock = false;
879 }
880
881 mDCBlockCheckBox->Enable(enabled);
882 break;
883 }
884 default:
885 break;
886 }
887}
888
889void EffectDistortion::UpdateControlText(wxTextCtrl* textCtrl, wxString& string, bool enabled)
890{
891 if (enabled) {
892 if (textCtrl->GetValue().empty())
893 textCtrl->SetValue(string);
894 else
895 string = textCtrl->GetValue();
896 }
897 else {
898 if (!textCtrl->GetValue().empty())
899 string = textCtrl->GetValue();
900 textCtrl->SetValue(wxT(""));
901 }
902}
903
905{
907 {
908 case kHardClip:
909 HardClip();
910 break;
911 case kSoftClip:
912 SoftClip();
913 break;
914 case kHalfSinCurve:
915 HalfSinTable();
916 break;
917 case kExpCurve:
919 break;
920 case kLogCurve:
922 break;
923 case kCubic:
924 CubicTable();
925 break;
926 case kEvenHarmonics:
928 break;
929 case kSinCurve:
930 SineTable();
931 break;
932 case kLeveller:
933 Leveller();
934 break;
935 case kRectifier:
936 Rectifier();
937 break;
938 case kHardLimiter:
939 HardLimiter();
940 break;
941 }
942}
943
944
945//
946// Preset tables for gain lookup
947//
948
950{
951 double lowThresh = 1 - mThreshold;
952 double highThresh = 1 + mThreshold;
953
954 for (int n = 0; n < TABLESIZE; n++) {
955 if (n < (STEPS * lowThresh))
956 mTable[n] = - mThreshold;
957 else if (n > (STEPS * highThresh))
958 mTable[n] = mThreshold;
959 else
960 mTable[n] = n/(double)STEPS - 1;
961
962 mMakeupGain = 1.0 / mThreshold;
963 }
964}
965
967{
968 double threshold = 1 + mThreshold;
969 double amount = std::pow(2.0, 7.0 * mParams.mParam1 / 100.0); // range 1 to 128
970 double peak = LogCurve(mThreshold, 1.0, amount);
971 mMakeupGain = 1.0 / peak;
972 mTable[STEPS] = 0.0; // origin
973
974 // positive half of table
975 for (int n = STEPS; n < TABLESIZE; n++) {
976 if (n < (STEPS * threshold)) // origin to threshold
977 mTable[n] = n/(float)STEPS - 1;
978 else
979 mTable[n] = LogCurve(mThreshold, n/(double)STEPS - 1, amount);
980 }
982}
983
984float EffectDistortion::LogCurve(double threshold, float value, double ratio)
985{
986 return threshold + ((std::exp(ratio * (threshold - value)) - 1) / -ratio);
987}
988
990{
991 double amount = std::min(0.999, DB_TO_LINEAR(-1 * mParams.mParam1)); // avoid divide by zero
992
993 for (int n = STEPS; n < TABLESIZE; n++) {
994 double linVal = n/(float)STEPS;
995 double scale = -1.0 / (1.0 - amount); // unity gain at 0dB
996 double curve = std::exp((linVal - 1) * std::log(amount));
997 mTable[n] = scale * (curve -1);
998 }
1000}
1001
1003{
1004 double amount = mParams.mParam1;
1005 double stepsize = 1.0 / STEPS;
1006 double linVal = 0;
1007
1008 if (amount == 0){
1009 for (int n = STEPS; n < TABLESIZE; n++) {
1010 mTable[n] = linVal;
1011 linVal += stepsize;
1012 }
1013 }
1014 else {
1015 for (int n = STEPS; n < TABLESIZE; n++) {
1016 mTable[n] = std::log(1 + (amount * linVal)) / std::log(1 + amount);
1017 linVal += stepsize;
1018 }
1019 }
1020 CopyHalfTable();
1021}
1022
1024{
1025 int iter = std::floor(mParams.mParam1 / 20.0);
1026 double fractionalpart = (mParams.mParam1 / 20.0) - iter;
1027 double stepsize = 1.0 / STEPS;
1028 double linVal = 0;
1029
1030 for (int n = STEPS; n < TABLESIZE; n++) {
1031 mTable[n] = linVal;
1032 for (int i = 0; i < iter; i++) {
1033 mTable[n] = std::sin(mTable[n] * M_PI_2);
1034 }
1035 mTable[n] += ((std::sin(mTable[n] * M_PI_2) - mTable[n]) * fractionalpart);
1036 linVal += stepsize;
1037 }
1038 CopyHalfTable();
1039}
1040
1042{
1043 double amount = mParams.mParam1 * std::sqrt(3.0) / 100.0;
1044 double gain = 1.0;
1045 if (amount != 0.0)
1046 gain = 1.0 / Cubic(std::min(amount, 1.0));
1047
1048 double stepsize = amount / STEPS;
1049 double x = -amount;
1050
1051 if (amount == 0) {
1052 for (int i = 0; i < TABLESIZE; i++) {
1053 mTable[i] = (i / (double)STEPS) - 1.0;
1054 }
1055 }
1056 else {
1057 for (int i = 0; i < TABLESIZE; i++) {
1058 mTable[i] = gain * Cubic(x);
1059 for (int j = 0; j < mParams.mRepeats; j++) {
1060 mTable[i] = gain * Cubic(mTable[i] * amount);
1061 }
1062 x += stepsize;
1063 }
1064 }
1065}
1066
1068{
1069 if (mParams.mParam1 == 0.0)
1070 return x;
1071
1072 return x - (std::pow(x, 3.0) / 3.0);
1073}
1074
1075
1077{
1078 double amount = mParams.mParam1 / -100.0;
1079 // double C = std::sin(std::max(0.001, mParams.mParam2) / 100.0) * 10.0;
1080 double C = std::max(0.001, mParams.mParam2) / 10.0;
1081
1082 double step = 1.0 / STEPS;
1083 double xval = -1.0;
1084
1085 for (int i = 0; i < TABLESIZE; i++) {
1086 mTable[i] = ((1 + amount) * xval) -
1087 (xval * (amount / std::tanh(C)) * std::tanh(C * xval));
1088 xval += step;
1089 }
1090}
1091
1093{
1094 int iter = std::floor(mParams.mParam1 / 20.0);
1095 double fractionalpart = (mParams.mParam1 / 20.0) - iter;
1096 double stepsize = 1.0 / STEPS;
1097 double linVal = 0.0;
1098
1099 for (int n = STEPS; n < TABLESIZE; n++) {
1100 mTable[n] = linVal;
1101 for (int i = 0; i < iter; i++) {
1102 mTable[n] = (1.0 + std::sin((mTable[n] * M_PI) - M_PI_2)) / 2.0;
1103 }
1104 mTable[n] += (((1.0 + std::sin((mTable[n] * M_PI) - M_PI_2)) / 2.0) - mTable[n]) * fractionalpart;
1105 linVal += stepsize;
1106 }
1107 CopyHalfTable();
1108}
1109
1111{
1112 double noiseFloor = DB_TO_LINEAR(mParams.mNoiseFloor);
1113 int numPasses = mParams.mRepeats;
1114 double fractionalPass = mParams.mParam1 / 100.0;
1115
1116 const int numPoints = 6;
1117 const double gainFactors[numPoints] = { 0.80, 1.00, 1.20, 1.20, 1.00, 0.80 };
1118 double gainLimits[numPoints] = { 0.0001, 0.0, 0.1, 0.3, 0.5, 1.0 };
1119 double addOnValues[numPoints];
1120
1121 gainLimits[1] = noiseFloor;
1122 /* In the original Leveller effect, behaviour was undefined for threshold > 20 dB.
1123 * If we want to support > 20 dB we need to scale the points to keep them non-decreasing.
1124 *
1125 * if (noiseFloor > gainLimits[2]) {
1126 * for (int i = 3; i < numPoints; i++) {
1127 * gainLimits[i] = noiseFloor + ((1 - noiseFloor)*((gainLimits[i] - 0.1) / 0.9));
1128 * }
1129 * gainLimits[2] = noiseFloor;
1130 * }
1131 */
1132
1133 // Calculate add-on values
1134 addOnValues[0] = 0.0;
1135 for (int i = 0; i < numPoints-1; i++) {
1136 addOnValues[i+1] = addOnValues[i] + (gainLimits[i] * (gainFactors[i] - gainFactors[1 + i]));
1137 }
1138
1139 // Positive half of table.
1140 // The original effect increased the 'strength' of the effect by
1141 // repeated passes over the audio data.
1142 // Here we model that more efficiently by repeated passes over a linear table.
1143 for (int n = STEPS; n < TABLESIZE; n++) {
1144 mTable[n] = ((double) (n - STEPS) / (double) STEPS);
1145 for (int j = 0; j < numPasses; j++) {
1146 // Find the highest index for gain adjustment
1147 int index = numPoints - 1;
1148 for (int i = index; i >= 0 && mTable[n] < gainLimits[i]; i--) {
1149 index = i;
1150 }
1151 // the whole number of 'repeats'
1152 mTable[n] = (mTable[n] * gainFactors[index]) + addOnValues[index];
1153 }
1154 // Extrapolate for fine adjustment.
1155 // tiny fractions are not worth the processing time
1156 if (fractionalPass > 0.001) {
1157 int index = numPoints - 1;
1158 for (int i = index; i >= 0 && mTable[n] < gainLimits[i]; i--) {
1159 index = i;
1160 }
1161 mTable[n] += fractionalPass * ((mTable[n] * (gainFactors[index] - 1)) + addOnValues[index]);
1162 }
1163 }
1164 CopyHalfTable();
1165}
1166
1168{
1169 double amount = (mParams.mParam1 / 50.0) - 1;
1170 double stepsize = 1.0 / STEPS;
1171 int index = STEPS;
1172
1173 // positive half of waveform is passed unaltered.
1174 for (int n = 0; n <= STEPS; n++) {
1175 mTable[index] = n * stepsize;
1176 index += 1;
1177 }
1178
1179 // negative half of table
1180 index = STEPS - 1;
1181 for (int n = 1; n <= STEPS; n++) {
1182 mTable[index] = n * stepsize * amount;
1183 index--;
1184 }
1185}
1186
1188{
1189 // The LADSPA "hardLimiter 1413" is basically hard clipping,
1190 // but with a 'kind of' wet/dry mix:
1191 // out = ((wet-residual)*clipped) + (residual*in)
1192 HardClip();
1193}
1194
1195
1196// Helper functions for lookup tables
1197
1199{
1200 // Copy negative half of table from positive half
1201 int count = TABLESIZE - 1;
1202 for (int n = 0; n < STEPS; n++) {
1203 mTable[n] = -mTable[count];
1204 count--;
1205 }
1206}
1207
1208
1210{
1211 float out;
1212 int index;
1213 double xOffset;
1214 double amount = 1;
1215
1216 switch (mParams.mTableChoiceIndx)
1217 {
1218 // Do any pre-processing here
1219 case kHardClip:
1220 // Pre-gain
1221 amount = mParams.mParam1 / 100.0;
1222 sample *= 1+amount;
1223 break;
1224 default: break;
1225 }
1226
1227 index = std::floor(sample * STEPS) + STEPS;
1228 index = wxMax<int>(wxMin<int>(index, 2 * STEPS - 1), 0);
1229 xOffset = ((1 + sample) * STEPS) - index;
1230 xOffset = wxMin<double>(wxMax<double>(xOffset, 0.0), 1.0); // Clip at 0dB
1231
1232 // linear interpolation: y = y0 + (y1-y0)*(x-x0)
1233 out = mTable[index] + (mTable[index + 1] - mTable[index]) * xOffset;
1234
1235 return out;
1236}
1237
1238
1240{
1241 // Rolling average gives less offset at the start than an IIR filter.
1242 const unsigned int queueLength = std::floor(data.samplerate / 20.0);
1243
1244 data.queuetotal += sample;
1245 data.queuesamples.push(sample);
1246
1247 if (data.queuesamples.size() > queueLength) {
1248 data.queuetotal -= data.queuesamples.front();
1249 data.queuesamples.pop();
1250 }
1251
1252 return sample - (data.queuetotal / data.queuesamples.size());
1253}
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
EffectDistortion::Params params
Definition: Distortion.cpp:83
static const struct @20 FactoryPresets[]
#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
EffectType
@ EffectTypeProcess
enum ChannelName * ChannelNames
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:536
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:535
@ 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:182
wxString mOldRepeatsTxt
Definition: Distortion.h:215
wxString mOldParam2Txt
Definition: Distortion.h:214
unsigned GetAudioInCount() const override
How many input buffers to allocate at once.
Definition: Distortion.cpp:196
static constexpr EffectParameter Threshold_dB
Definition: Distortion.h:245
void OnThresholdText(wxCommandEvent &evt)
Definition: Distortion.cpp:560
void OnParam2Text(wxCommandEvent &evt)
Definition: Distortion.cpp:602
static constexpr EffectParameter Param2
Definition: Distortion.h:251
void OnThresholdSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:567
void InstanceInit(EffectDistortionState &data, float sampleRate)
Definition: Distortion.cpp:445
wxStaticText * mRepeatsTxt
Definition: Distortion.h:209
wxStaticText * mParam1Txt
Definition: Distortion.h:207
wxSlider * mRepeatsS
Definition: Distortion.h:201
double Cubic(double x)
std::vector< EffectDistortionState > mSlaves
Definition: Distortion.h:178
size_t InstanceProcess(EffectSettings &settings, EffectDistortionState &data, const float *const *inBlock, float *const *outBlock, size_t blockLen)
Definition: Distortion.cpp:469
void ExponentialTable()
Definition: Distortion.cpp:989
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:208
double mMakeupGain
Definition: Distortion.h:186
static const ComponentInterfaceSymbol Symbol
Definition: Distortion.h:53
static constexpr EffectParameter Param1
Definition: Distortion.h:249
virtual ~EffectDistortion()
Definition: Distortion.cpp:161
void OnParam2Slider(wxCommandEvent &evt)
Definition: Distortion.cpp:608
wxTextCtrl * mParam1T
Definition: Distortion.h:193
static const EnumValueSymbol kTableTypeStrings[nTableTypes]
Definition: Distortion.h:238
void OnTypeChoice(wxCommandEvent &evt)
Definition: Distortion.cpp:546
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:197
wxTextCtrl * mThresholdT
Definition: Distortion.h:191
wxStaticText * mNoiseFloorTxt
Definition: Distortion.h:206
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
Definition: Distortion.cpp:201
void OnParam1Text(wxCommandEvent &evt)
Definition: Distortion.cpp:590
float LogCurve(double threshold, float value, double ratio)
Definition: Distortion.cpp:984
float WaveShaper(float sample)
void EvenHarmonicTable()
wxSlider * mParam1S
Definition: Distortion.h:199
bool DoLoadFactoryPreset(int id)
Definition: Distortion.cpp:271
wxSlider * mNoiseFloorS
Definition: Distortion.h:198
ComponentInterfaceSymbol GetSymbol() const override
Definition: Distortion.cpp:167
wxTextCtrl * mNoiseFloorT
Definition: Distortion.h:192
bool TransferDataToWindow(const EffectSettings &settings) override
Update controls for the settings.
Definition: Distortion.cpp:423
double mTable[TABLESIZE]
Definition: Distortion.h:180
wxTextCtrl * mRepeatsT
Definition: Distortion.h:195
static constexpr EffectParameter Repeats
Definition: Distortion.h:253
wxString mOldmNoiseFloorTxt
Definition: Distortion.h:212
wxString mOldThresholdTxt
Definition: Distortion.h:211
static constexpr EffectParameter DCBlock
Definition: Distortion.h:243
bool ProcessInitialize(EffectSettings &settings, double sampleRate, sampleCount totalLen, ChannelNames chanMap) override
Definition: Distortion.cpp:206
bool TransferDataFromWindow(EffectSettings &settings) override
Update the given settings from controls.
Definition: Distortion.cpp:439
static constexpr EnumParameter TableTypeIndx
Definition: Distortion.h:241
static constexpr EffectParameter NoiseFloor
Definition: Distortion.h:247
void OnNoiseFloorSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:583
RealtimeSince RealtimeSupport() const override
Since which version of Audacity has the effect supported realtime?
Definition: Distortion.cpp:189
wxSlider * mParam2S
Definition: Distortion.h:200
std::unique_ptr< EffectUIValidator > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access) override
Add controls to effect panel; always succeeds.
Definition: Distortion.cpp:287
wxString mOldParam1Txt
Definition: Distortion.h:213
float DCFilter(EffectDistortionState &data, float sample)
bool RealtimeFinalize(EffectSettings &settings) noexcept override
Definition: Distortion.cpp:238
void OnParam1Slider(wxCommandEvent &evt)
Definition: Distortion.cpp:596
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:553
const EffectParameterMethods & Parameters() const override
Definition: Distortion.cpp:62
EffectDistortionState mMaster
Definition: Distortion.h:177
wxStaticText * mThresholdTxt
Definition: Distortion.h:205
void UpdateControlText(wxTextCtrl *textCtrl, wxString &string, bool enabled)
Definition: Distortion.cpp:889
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:577
void UpdateControl(control id, bool enable, TranslatableString name)
Definition: Distortion.cpp:796
bool LoadFactoryPreset(int id, EffectSettings &settings) const override
Change settings to the preset whose name is GetFactoryPresets()[id]
Definition: Distortion.cpp:265
wxCheckBox * mDCBlockCheckBox
Definition: Distortion.h:203
bool RealtimeAddProcessor(EffectSettings &settings, unsigned numChannels, float sampleRate) override
Definition: Distortion.cpp:226
RegistryPaths GetFactoryPresets() const override
Report names of factory presets.
Definition: Distortion.cpp:253
wxTextCtrl * mParam2T
Definition: Distortion.h:194
wxChoice * mTypeChoiceCtrl
Definition: Distortion.h:190
void OnRepeatsSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:620
void OnRepeatsText(wxCommandEvent &evt)
Definition: Distortion.cpp:614
sampleCount skipcount
Definition: Distortion.h:33
std::queue< float > queuesamples
Definition: Distortion.h:43
Performs effect computation.
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
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
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.