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 "EffectEditor.h"
22#include "LoadEffects.h"
23
24#include <cmath>
25#include <algorithm>
26//#define _USE_MATH_DEFINES
27
28// Belt and braces
29#ifndef M_PI
30#define M_PI 3.1415926535897932384626433832795
31#endif
32#ifndef M_PI_2
33#define M_PI_2 1.57079632679489661923132169163975
34#endif
35
36#include <wx/checkbox.h>
37#include <wx/choice.h>
38#include <wx/valgen.h>
39#include <wx/log.h>
40#include <wx/slider.h>
41#include <wx/stattext.h>
42#include <wx/weakref.h>
43
44#include "Prefs.h"
45#include "ShuttleGui.h"
46#include "../widgets/valnum.h"
47
49{
50 { XO("Hard Clipping") },
51 { XO("Soft Clipping") },
52 { XO("Soft Overdrive") },
53 { XO("Medium Overdrive") },
54 { XO("Hard Overdrive") },
55 { XO("Cubic Curve (odd harmonics)") },
56 { XO("Even Harmonics") },
57 { XO("Expand and Compress") },
58 { XO("Leveller") },
59 { XO("Rectifier Distortion") },
60 { XO("Hard Limiter 1413") }
61};
62
64{
67 > parameters;
68 return parameters;
69}
70
71// How many samples are processed before recomputing the lookup table again
72#define skipsamples 1000
73
74static const struct
75{
78}
80{
81 // Table DCBlock threshold floor Param1 Param2 Repeats
82 // Defaults: 0 false -6.0 -70.0(off) 50.0 50.0 1
83 //
84 // xgettext:no-c-format
85 { XO("Hard clip -12dB, 80% make-up gain"), { 0, 0, -12.0, -70.0, 0.0, 80.0, 0 } },
86 // xgettext:no-c-format
87 { XO("Soft clip -12dB, 80% make-up gain"), { 1, 0, -12.0, -70.0, 50.0, 80.0, 0 } },
88 { XO("Fuzz Box"), { 1, 0, -30.0, -70.0, 80.0, 80.0, 0 } },
89 { XO("Walkie-talkie"), { 1, 0, -50.0, -70.0, 60.0, 80.0, 0 } },
90 { XO("Blues drive sustain"), { 2, 0, -6.0, -70.0, 30.0, 80.0, 0 } },
91 { XO("Light Crunch Overdrive"), { 3, 0, -6.0, -70.0, 20.0, 80.0, 0 } },
92 { XO("Heavy Overdrive"), { 4, 0, -6.0, -70.0, 90.0, 80.0, 0 } },
93 { XO("3rd Harmonic (Perfect Fifth)"), { 5, 0, -6.0, -70.0, 100.0, 60.0, 0 } },
94 { XO("Valve Overdrive"), { 6, 1, -6.0, -70.0, 30.0, 40.0, 0 } },
95 { XO("2nd Harmonic (Octave)"), { 6, 1, -6.0, -70.0, 50.0, 0.0, 0 } },
96 { XO("Gated Expansion Distortion"), { 7, 0, -6.0, -70.0, 30.0, 80.0, 0 } },
97 { XO("Leveller, Light, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 1 } },
98 { XO("Leveller, Moderate, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 2 } },
99 { XO("Leveller, Heavy, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 3 } },
100 { XO("Leveller, Heavier, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 4 } },
101 { XO("Leveller, Heaviest, -70dB noise floor"), { 8, 0, -6.0, -70.0, 0.0, 50.0, 5 } },
102 { XO("Half-wave Rectifier"), { 9, 0, -6.0, -70.0, 50.0, 50.0, 0 } },
103 { XO("Full-wave Rectifier"), { 9, 0, -6.0, -70.0, 100.0, 50.0, 0 } },
104 { XO("Full-wave Rectifier (DC blocked)"), { 9, 1, -6.0, -70.0, 100.0, 50.0, 0 } },
105 { XO("Percussion Limiter"), {10, 0, -12.0, -70.0, 100.0, 30.0, 0 } },
107
109{
110 static const TranslatableString names[] = {
111 XO("Upper Threshold"),
112 XO("Noise Floor"),
113 XO("Parameter 1"),
114 XO("Parameter 2"),
115 XO("Number of repeats"),
116 };
117
118 return names[ index ];
119}
120
121//
122// EffectDistortion
123//
124
126{ XO("Distortion") };
127
129
130
133{
134 Editor(const EffectUIServices& services,
137 ) : EffectEditor{ services, access }
138 , mInstance(instance)
140 {}
141 virtual ~Editor() = default;
142
143 bool ValidateUI() override;
144 bool UpdateUI() override;
145
147
148 // Control Handlers
149 void OnTypeChoice(wxCommandEvent& evt);
150 void OnDCBlockCheckbox(wxCommandEvent& evt);
151 void OnThresholdText(wxCommandEvent& evt);
152 void OnThresholdSlider(wxCommandEvent& evt);
153 void OnNoiseFloorText(wxCommandEvent& evt);
154 void OnNoiseFloorSlider(wxCommandEvent& evt);
155 void OnParam1Text(wxCommandEvent& evt);
156 void OnParam1Slider(wxCommandEvent& evt);
157 void OnParam2Text(wxCommandEvent& evt);
158 void OnParam2Slider(wxCommandEvent& evt);
159 void OnRepeatsText(wxCommandEvent& evt);
160 void OnRepeatsSlider(wxCommandEvent& evt);
161
163 wxTextCtrl* mThresholdT;
164 wxTextCtrl* mNoiseFloorT;
165 wxTextCtrl* mParam1T;
166 wxTextCtrl* mParam2T;
167 wxTextCtrl* mRepeatsT;
168
169 wxSlider* mThresholdS;
170 wxSlider* mNoiseFloorS;
171 wxSlider* mParam1S;
172 wxSlider* mParam2S;
173 wxSlider* mRepeatsS;
174
175 wxCheckBox* mDCBlockCheckBox;
176
177 wxStaticText* mThresholdTxt;
178 wxStaticText* mNoiseFloorTxt;
179 wxStaticText* mParam1Txt;
180 wxStaticText* mParam2Txt;
181 wxStaticText* mRepeatsTxt;
182
188
190
192
193 void UpdateControl(control id, bool enable, TranslatableString name);
194 void UpdateUIControls();
195 void UpdateControlText(wxTextCtrl* textCtrl, wxString& string, bool enabled);
196
197 wxWeakRef<wxWindow> mUIParent{};
199};
200
201
203{
204 {
205 // This section was copied from the original
206 // EffectDistortion::TransferDataFromWindow
207 //
208 // However, the call to ->Validate would bring up an error dialog
209 // saying "Empty value"
210
211 if ( !mUIParent->TransferDataFromWindow())
212 {
213 return false;
214 }
215 }
216
217
219 (
220 [this](EffectSettings& settings)
221 {
222 // pass back the modified settings to the MessageBuffer
223
225
226 return nullptr;
227 }
228 );
229
230 return true;
231}
232
233
237{
238 explicit Instance(const PerTrackEffect& effect)
239 : PerTrackEffect::Instance{ effect }
240 {}
241
243 ChannelNames chanMap) override;
244
246 const float* const* inBlock, float* const* outBlock, size_t blockLen) override;
247
248 bool RealtimeInitialize(EffectSettings& settings, double) override;
249
251 EffectOutputs* pOutputs, unsigned numChannels, float sampleRate) override;
252
253 bool RealtimeFinalize(EffectSettings& settings) noexcept override;
254
255 size_t RealtimeProcess(size_t group, EffectSettings& settings,
256 const float* const* inbuf, float* const* outbuf, size_t numSamples)
257 override;
258
259
261
263 const float* const* inBlock, float* const* outBlock, size_t blockLen);
264
266
268 const EffectDistortionSettings&); // hard clipping
269
271 const EffectDistortionSettings&); // soft clipping
272
273 void ExponentialTable (const EffectDistortionSettings&); // exponential mapping
274 void LogarithmicTable (const EffectDistortionSettings&); // logarithmic mapping
279 void Leveller (const EffectDistortionSettings&); // 'Leveller' wavetable is modeled on the legacy effect of the same name.
280 void Rectifier (const EffectDistortionSettings&); // 0% = Dry, 50% = half-wave rectified, 100% = full-wave rectified (abs value).
283 ); // Same effect as the LADSPA "hardLimiter 1413"
284
285 void CopyHalfTable(); // for symmetric tables
286
287 // Used by Soft Clipping but could be used for other tables.
288 // Log curve formula: y = T + (((e^(RT - Rx)) - 1) / -R)
289 // where R is the ratio, T is the threshold, and x is from T to 1.
290 inline float LogCurve(double threshold, float value, double ratio);
291
292 // Used by Cubic curve but could be used for other tables
293 // Cubic formula: y = x - (x^3 / 3.0)
294 inline double Cubic(const EffectDistortionSettings&, double x);
295
296 float WaveShaper(float sample, EffectDistortionSettings& ms);
297 float DCFilter(EffectDistortionState& data, float sample);
298
299 unsigned GetAudioInCount() const override;
300 unsigned GetAudioOutCount() const override;
301
303
305 std::vector<EffectDistortionState> mSlaves;
306};
307
308
309std::shared_ptr<EffectInstance>
311{
312 return std::make_shared<Instance>(*this);
313}
314
315
317{
318 return mInstance.mMaster;
319}
320
322{
323 wxASSERT(nTableTypes == WXSIZEOF(kTableTypeStrings));
324
325 SetLinearEffectFlag(false);
326}
327
329{
330}
331
332// ComponentInterface implementation
333
335{
336 return Symbol;
337}
338
340{
341 return XO("Waveshaping distortion effect");
342}
343
345{
346 return L"Distortion";
347}
348
349// EffectDefinitionInterface implementation
350
352{
353 return EffectTypeProcess;
354}
355
357{
359}
360
362{
363 return 1;
364}
365
367{
368 return 1;
369}
370
373{
374 InstanceInit(mMaster, settings, sampleRate);
375 return true;
376}
377
379 const float* const* inBlock, float* const* outBlock, size_t blockLen)
380{
381 return InstanceProcess(settings, mMaster, inBlock, outBlock, blockLen);
382}
383
385{
386 SetBlockSize(512);
387 mSlaves.clear();
388 return true;
389}
390
392 EffectSettings & settings, EffectOutputs *, unsigned, float sampleRate)
393{
395
396 InstanceInit(slave, settings, sampleRate);
397
398 mSlaves.push_back(slave);
399
400 return true;
401}
402
404{
405 mSlaves.clear();
406
407 return true;
408}
409
411 const float* const* inbuf, float* const* outbuf, size_t numSamples)
412{
413 if (group >= mSlaves.size())
414 return 0;
415 return InstanceProcess(settings, mSlaves[group], inbuf, outbuf, numSamples);
416}
417
419{
421
422 for (size_t i = 0; i < WXSIZEOF(FactoryPresets); i++)
423 {
424 names.push_back( FactoryPresets[i].name.Translation() );
425 }
426
427 return names;
428}
429
432{
433 // To do: externalize state so const_cast isn't needed
434 return const_cast<EffectDistortion*>(this)->DoLoadFactoryPreset(id, settings);
435}
436
438{
439 if (id < 0 || id >= (int) WXSIZEOF(FactoryPresets))
440 {
441 return {};
442 }
443
445
446 return { nullptr };
447}
448
449
450// Effect implementation
451
452std::unique_ptr<EffectEditor>
454 EffectSettingsAccess& access, const EffectOutputs* pOutputs) const
455{
456 auto& settings = access.Get();
457 auto& myEffSettings = GetSettings(settings);
458
459 auto result = std::make_unique<Editor>(*this, dynamic_cast<EffectDistortion::Instance&>(instance), access, myEffSettings);
460 result->PopulateOrExchange(S);
461 return result;
462}
463
464
466{
467 mUIParent = S.GetParent();
468 auto& ms = mSettings;
469
470 S.AddSpace(0, 5);
471 S.StartVerticalLay();
472 {
473 S.StartMultiColumn(4, wxCENTER);
474 {
475 mTypeChoiceCtrl = S
476 .MinSize( { -1, -1 } )
477 .Validator<wxGenericValidator>(&ms.mTableChoiceIndx)
478 .AddChoice(XXO("Distortion type:"),
480
481 BindTo(*mTypeChoiceCtrl, wxEVT_CHOICE, &Editor::OnTypeChoice);
482
483 mDCBlockCheckBox = S.AddCheckBox(XXO("DC blocking filter"),
484 DCBlock.def);
485
486 BindTo(*mDCBlockCheckBox, wxEVT_CHECKBOX, &Editor::OnDCBlockCheckbox);
487 }
488 S.EndMultiColumn();
489 S.AddSpace(0, 10);
490
491
492 S.StartStatic(XO("Threshold controls"));
493 {
494 S.StartMultiColumn(4, wxEXPAND);
495 S.SetStretchyCol(2);
496 {
497 // Allow space for first Column
498 S.AddSpace(250,0); S.AddSpace(0,0); S.AddSpace(0,0); S.AddSpace(0,0);
499
500 // Upper threshold control
501 mThresholdTxt = S.AddVariableText(defaultLabel(0),
502 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
503 mThresholdT = S
504 .Name(defaultLabel(0))
505 .Validator<FloatingPointValidator<double>>(
506 2, &ms.mThreshold_dB, NumValidatorStyle::DEFAULT,
508 .AddTextBox( {}, wxT(""), 10);
509
510 BindTo(*mThresholdT, wxEVT_TEXT, &Editor::OnThresholdText);
511
512 mThresholdS = S
513 .Name(defaultLabel(0))
514 .Style(wxSL_HORIZONTAL)
515 .AddSlider( {}, 0,
518 S.AddSpace(20, 0);
519
520 BindTo(*mThresholdS, wxEVT_SLIDER, &Editor::OnThresholdSlider);
521
522 // Noise floor control
523 mNoiseFloorTxt = S.AddVariableText(defaultLabel(1),
524 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
525 mNoiseFloorT = S
526 .Name(defaultLabel(1))
527 .Validator<FloatingPointValidator<double>>(
528 2, &ms.mNoiseFloor, NumValidatorStyle::DEFAULT,
530 )
531 .AddTextBox( {}, wxT(""), 10);
532
533 BindTo(*mNoiseFloorT, wxEVT_TEXT, &Editor::OnNoiseFloorText);
534
535 mNoiseFloorS = S
536 .Name(defaultLabel(1))
537 .Style(wxSL_HORIZONTAL)
538 .AddSlider( {}, 0, NoiseFloor.max, NoiseFloor.min);
539 S.AddSpace(20, 0);
540
541 BindTo(*mNoiseFloorS, wxEVT_SLIDER, &Editor::OnNoiseFloorSlider);
542 }
543 S.EndMultiColumn();
544 }
545 S.EndStatic();
546
547 S.StartStatic(XO("Parameter controls"));
548 {
549 S.StartMultiColumn(4, wxEXPAND);
550 S.SetStretchyCol(2);
551 {
552 // Allow space for first Column
553 S.AddSpace(250,0); S.AddSpace(0,0); S.AddSpace(0,0); S.AddSpace(0,0);
554
555 // Parameter1 control
556 mParam1Txt = S.AddVariableText(defaultLabel(2),
557 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
558 mParam1T = S
559 .Name(defaultLabel(2))
560 .Validator<FloatingPointValidator<double>>(
561 2, &ms.mParam1, NumValidatorStyle::DEFAULT,
563 )
564 .AddTextBox( {}, wxT(""), 10);
565
566 BindTo(*mParam1T, wxEVT_TEXT, &Editor::OnParam1Text);
567
568 mParam1S = S
569 .Name(defaultLabel(2))
570 .Style(wxSL_HORIZONTAL)
571 .AddSlider( {}, 0, Param1.max, Param1.min);
572 S.AddSpace(20, 0);
573
574 BindTo(*mParam1S, wxEVT_SLIDER, &Editor::OnParam1Slider);
575
576 // Parameter2 control
577 mParam2Txt = S.AddVariableText(defaultLabel(3),
578 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
579 mParam2T = S
580 .Name(defaultLabel(3))
581 .Validator<FloatingPointValidator<double>>(
582 2, &ms.mParam2, NumValidatorStyle::DEFAULT,
584 )
585 .AddTextBox( {}, wxT(""), 10);
586
587 BindTo(*mParam2T, wxEVT_TEXT, &Editor::OnParam2Text);
588
589 mParam2S = S
590 .Name(defaultLabel(3))
591 .Style(wxSL_HORIZONTAL)
592 .AddSlider( {}, 0, Param2.max, Param2.min);
593
594 BindTo(*mParam2S, wxEVT_SLIDER, &Editor::OnParam2Slider);
595
596 S.AddSpace(20, 0);
597
598 // Repeats control
599 mRepeatsTxt = S.AddVariableText(defaultLabel(4),
600 false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
601 mRepeatsT = S
602 .Name(defaultLabel(4))
603 .Validator<IntegerValidator<int>>(
604 &ms.mRepeats, NumValidatorStyle::DEFAULT,
606 )
607 .AddTextBox( {}, wxT(""), 10);
608
609 BindTo(*mRepeatsT, wxEVT_TEXT, &Editor::OnRepeatsText);
610
611 mRepeatsS = S
612 .Name(defaultLabel(4))
613 .Style(wxSL_HORIZONTAL)
614 .AddSlider( {}, Repeats.def, Repeats.max, Repeats.min);
615
616 BindTo(*mRepeatsS, wxEVT_SLIDER, &Editor::OnRepeatsSlider);
617
618 S.AddSpace(20, 0);
619 }
620 S.EndMultiColumn();
621 }
622 S.EndStatic();
623 }
624 S.EndVerticalLay();
625
626}
627
628
630{
631 const auto& ms = mSettings;
632
633 if (!mUIParent->TransferDataToWindow())
634 {
635 return false;
636 }
637
638 const double threshold = DB_TO_LINEAR(ms.mThreshold_dB);
639
640 mThresholdS-> SetValue((int) (threshold * Threshold_dB.scale + 0.5));
641 mDCBlockCheckBox->SetValue( ms.mDCBlock);
642 mNoiseFloorS-> SetValue((int)ms.mNoiseFloor + 0.5);
643 mParam1S-> SetValue((int)ms.mParam1 + 0.5);
644 mParam2S-> SetValue((int)ms.mParam2 + 0.5);
645 mRepeatsS-> SetValue( ms.mRepeats);
646
647 GetState().mbSavedFilterState = ms.mDCBlock;
648
649 UpdateUIControls();
650
651 return true;
652}
653
654
656{
657 auto& ms = GetSettings(settings);
658
659 data.samplerate = sampleRate;
660 data.skipcount = 0;
661 data.tablechoiceindx = ms.mTableChoiceIndx;
662 data.dcblock = ms.mDCBlock;
663 data.threshold = ms.mThreshold_dB;
664 data.noisefloor = ms.mNoiseFloor;
665 data.param1 = ms.mParam1;
666 data.param2 = ms.mParam2;
667 data.repeats = ms.mRepeats;
668
669 // DC block filter variables
670 data.queuetotal = 0.0;
671
672 //std::queue<float>().swap(data.queuesamples);
673 while (!data.queuesamples.empty())
674 data.queuesamples.pop();
675
676 MakeTable(data, ms);
677
678 return;
679}
680
683 const float *const *inBlock, float *const *outBlock, size_t blockLen)
684{
685 auto& ms = GetSettings(settings);
686
687 const float *ibuf = inBlock[0];
688 float *obuf = outBlock[0];
689
690 bool update = (ms.mTableChoiceIndx == data.tablechoiceindx &&
691 ms.mNoiseFloor == data.noisefloor &&
692 ms.mThreshold_dB == data.threshold &&
693 ms.mParam1 == data.param1 &&
694 ms.mParam2 == data.param2 &&
695 ms.mRepeats == data.repeats)? false : true;
696
697 double p1 = ms.mParam1 / 100.0;
698 double p2 = ms.mParam2 / 100.0;
699
700 data.tablechoiceindx = ms.mTableChoiceIndx;
701 data.threshold = ms.mThreshold_dB;
702 data.noisefloor = ms.mNoiseFloor;
703 data.param1 = ms.mParam1;
704 data.repeats = ms.mRepeats;
705
706 for (decltype(blockLen) i = 0; i < blockLen; i++) {
707 if (update && ((data.skipcount++) % skipsamples == 0)) {
708 MakeTable(data, ms);
709 }
710
711 switch (ms.mTableChoiceIndx)
712 {
713 case kHardClip:
714 // Param2 = make-up gain.
715 obuf[i] = WaveShaper(ibuf[i], ms) * ((1 - p2) + (data.mMakeupGain * p2));
716 break;
717 case kSoftClip:
718 // Param2 = make-up gain.
719 obuf[i] = WaveShaper(ibuf[i], ms) * ((1 - p2) + (data.mMakeupGain * p2));
720 break;
721 case kHalfSinCurve:
722 obuf[i] = WaveShaper(ibuf[i], ms) * p2;
723 break;
724 case kExpCurve:
725 obuf[i] = WaveShaper(ibuf[i], ms) * p2;
726 break;
727 case kLogCurve:
728 obuf[i] = WaveShaper(ibuf[i], ms) * p2;
729 break;
730 case kCubic:
731 obuf[i] = WaveShaper(ibuf[i], ms) * p2;
732 break;
733 case kEvenHarmonics:
734 obuf[i] = WaveShaper(ibuf[i], ms);
735 break;
736 case kSinCurve:
737 obuf[i] = WaveShaper(ibuf[i], ms) * p2;
738 break;
739 case kLeveller:
740 obuf[i] = WaveShaper(ibuf[i], ms);
741 break;
742 case kRectifier:
743 obuf[i] = WaveShaper(ibuf[i], ms);
744 break;
745 case kHardLimiter:
746 // Mix equivalent to LADSPA effect's "Wet / Residual" mix
747 obuf[i] = (WaveShaper(ibuf[i], ms) * (p1 - p2)) + (ibuf[i] * p2);
748 break;
749 default:
750 obuf[i] = WaveShaper(ibuf[i], ms);
751 }
752 if (ms.mDCBlock) {
753 obuf[i] = DCFilter(data, obuf[i]);
754 }
755 }
756
757 return blockLen;
758}
759
760void EffectDistortion::Editor::OnTypeChoice(wxCommandEvent& /*evt*/)
761{
762 mTypeChoiceCtrl->GetValidator()->TransferFromWindow();
763
764 UpdateUIControls();
765
766 ValidateUI();
767 Publish(EffectSettingChanged{});
768}
769
771{
772 auto& ms = mSettings;
773
774 ms.mDCBlock = mDCBlockCheckBox->GetValue();
775
776 GetState().mbSavedFilterState = ms.mDCBlock;
777
778 ValidateUI();
779 Publish(EffectSettingChanged{});
780}
781
782
783void EffectDistortion::Editor::OnThresholdText(wxCommandEvent& /*evt*/)
784{
785 const auto& ms = mSettings;
786
787 mThresholdT->GetValidator()->TransferFromWindow();
788 const double threshold = DB_TO_LINEAR(ms.mThreshold_dB);
789 mThresholdS->SetValue((int) (threshold * Threshold_dB.scale + 0.5));
790
791 ValidateUI();
792 Publish(EffectSettingChanged{});
793}
794
796{
797 auto& ms = mSettings;
798
799 static const double MIN_Threshold_Linear = DB_TO_LINEAR(Threshold_dB.min);
800
801 const double thresholdDB = (double)evt.GetInt() / Threshold_dB.scale;
802 ms.mThreshold_dB = wxMax(LINEAR_TO_DB(thresholdDB), Threshold_dB.min);
803
804 mThresholdT->GetValidator()->TransferToWindow();
805
806 ValidateUI();
807 Publish(EffectSettingChanged{});
808}
809
811{
812 const auto& ms = mSettings;
813
814 mNoiseFloorT->GetValidator()->TransferFromWindow();
815 mNoiseFloorS->SetValue((int) floor(ms.mNoiseFloor + 0.5));
816
817 ValidateUI();
818 Publish(EffectSettingChanged{});
819}
820
822{
823 auto& ms = mSettings;
824
825 ms.mNoiseFloor = (double) evt.GetInt();
826 mNoiseFloorT->GetValidator()->TransferToWindow();
827
828 ValidateUI();
829 Publish(EffectSettingChanged{});
830}
831
832
833void EffectDistortion::Editor::OnParam1Text(wxCommandEvent& /*evt*/)
834{
835 const auto& ms = mSettings;
836
837 mParam1T->GetValidator()->TransferFromWindow();
838 mParam1S->SetValue((int) floor(ms.mParam1 + 0.5));
839
840 ValidateUI();
841 Publish(EffectSettingChanged{});
842}
843
845{
846 auto& ms = mSettings;
847
848 ms.mParam1 = (double) evt.GetInt();
849 mParam1T->GetValidator()->TransferToWindow();
850
851 ValidateUI();
852 Publish(EffectSettingChanged{});
853}
854
855void EffectDistortion::Editor::OnParam2Text(wxCommandEvent& /*evt*/)
856{
857 const auto& ms = mSettings;
858
859 mParam2T->GetValidator()->TransferFromWindow();
860 mParam2S->SetValue((int) floor(ms.mParam2 + 0.5));
861
862 ValidateUI();
863 Publish(EffectSettingChanged{});
864}
865
867{
868 auto& ms = mSettings;
869
870 ms.mParam2 = (double) evt.GetInt();
871 mParam2T->GetValidator()->TransferToWindow();
872
873 ValidateUI();
874 Publish(EffectSettingChanged{});
875}
876
877void EffectDistortion::Editor::OnRepeatsText(wxCommandEvent& /*evt*/)
878{
879 const auto& ms = mSettings;
880
881 mRepeatsT->GetValidator()->TransferFromWindow();
882 mRepeatsS->SetValue(ms.mRepeats);
883
884 ValidateUI();
885 Publish(EffectSettingChanged{});
886}
887
889{
890 auto& ms = mSettings;
891
892 ms.mRepeats = evt.GetInt();
893 mRepeatsT->GetValidator()->TransferToWindow();
894
895 ValidateUI();
896 Publish(EffectSettingChanged{});
897}
898
900{
901 const auto& ms = mSettings;
902
903 // set control text and names to match distortion type
904 switch (ms.mTableChoiceIndx)
905 {
906 case kHardClip:
907 UpdateControlText(mThresholdT, mOldThresholdTxt, true);
908 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
909 UpdateControlText(mParam1T, mOldParam1Txt, true);
910 UpdateControlText(mParam2T, mOldParam2Txt, true);
911 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
912
913 UpdateControl(ID_Threshold, true, XO("Clipping level"));
914 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
915 UpdateControl(ID_Param1, true, XO("Drive"));
916 UpdateControl(ID_Param2, true, XO("Make-up Gain"));
917 UpdateControl(ID_Repeats, false, defaultLabel(4));
918 UpdateControl(ID_DCBlock, false, {});
919 break;
920 case kSoftClip:
921 UpdateControlText(mThresholdT, mOldThresholdTxt, true);
922 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
923 UpdateControlText(mParam1T, mOldParam1Txt, true);
924 UpdateControlText(mParam2T, mOldParam2Txt, true);
925 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
926
927 UpdateControl(ID_Threshold, true, XO("Clipping threshold"));
928 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
929 UpdateControl(ID_Param1, true, XO("Hardness"));
930 UpdateControl(ID_Param2, true, XO("Make-up Gain"));
931 UpdateControl(ID_Repeats, false, defaultLabel(4));
932 UpdateControl(ID_DCBlock, false, {});
933 break;
934 case kHalfSinCurve:
935 UpdateControlText(mThresholdT, mOldThresholdTxt, false);
936 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
937 UpdateControlText(mParam1T, mOldParam1Txt, true);
938 UpdateControlText(mParam2T, mOldParam2Txt, true);
939 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
940
941 UpdateControl(ID_Threshold, false, defaultLabel(0));
942 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
943 UpdateControl(ID_Param1, true, XO("Distortion amount"));
944 UpdateControl(ID_Param2, true, XO("Output level"));
945 UpdateControl(ID_Repeats, false, defaultLabel(4));
946 UpdateControl(ID_DCBlock, false, {});
947 break;
948 case kExpCurve:
949 UpdateControlText(mThresholdT, mOldThresholdTxt, false);
950 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
951 UpdateControlText(mParam1T, mOldParam1Txt, true);
952 UpdateControlText(mParam2T, mOldParam2Txt, true);
953 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
954
955 UpdateControl(ID_Threshold, false, defaultLabel(0));
956 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
957 UpdateControl(ID_Param1, true, XO("Distortion amount"));
958 UpdateControl(ID_Param2, true, XO("Output level"));
959 UpdateControl(ID_Repeats, false, defaultLabel(4));
960 UpdateControl(ID_DCBlock, false, {});
961 break;
962 case kLogCurve:
963 UpdateControlText(mThresholdT, mOldThresholdTxt, false);
964 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
965 UpdateControlText(mParam1T, mOldParam1Txt, true);
966 UpdateControlText(mParam2T, mOldParam2Txt, true);
967 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
968
969 UpdateControl(ID_Threshold, false, defaultLabel(0));
970 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
971 UpdateControl(ID_Param1, true, XO("Distortion amount"));
972 UpdateControl(ID_Param2, true, XO("Output level"));
973 UpdateControl(ID_Repeats, false, defaultLabel(4));
974 UpdateControl(ID_DCBlock, false, {});
975 break;
976 case kCubic:
977 UpdateControlText(mThresholdT, mOldThresholdTxt, false);
978 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
979 UpdateControlText(mParam1T, mOldParam1Txt, true);
980 UpdateControlText(mParam2T, mOldParam2Txt, true);
981 UpdateControlText(mRepeatsT, mOldRepeatsTxt, true);
982
983 UpdateControl(ID_Threshold, false, defaultLabel(0));
984 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
985 UpdateControl(ID_Param1, true, XO("Distortion amount"));
986 UpdateControl(ID_Param2, true, XO("Output level"));
987 UpdateControl(ID_Repeats, true, XO("Repeat processing"));
988 UpdateControl(ID_DCBlock, false, {});
989 break;
990 case kEvenHarmonics:
991 UpdateControlText(mThresholdT, mOldThresholdTxt, false);
992 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
993 UpdateControlText(mParam1T, mOldParam1Txt, true);
994 UpdateControlText(mParam2T, mOldParam2Txt, true);
995 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
996
997 UpdateControl(ID_Threshold, false, defaultLabel(0));
998 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
999 UpdateControl(ID_Param1, true, XO("Distortion amount"));
1000 UpdateControl(ID_Param2, true, XO("Harmonic brightness"));
1001 UpdateControl(ID_Repeats, false, defaultLabel(4));
1002 UpdateControl(ID_DCBlock, true, {});
1003 break;
1004 case kSinCurve:
1005 UpdateControlText(mThresholdT, mOldThresholdTxt, false);
1006 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
1007 UpdateControlText(mParam1T, mOldParam1Txt, true);
1008 UpdateControlText(mParam2T, mOldParam2Txt, true);
1009 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
1010
1011 UpdateControl(ID_Threshold, false, defaultLabel(0));
1012 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
1013 UpdateControl(ID_Param1, true, XO("Distortion amount"));
1014 UpdateControl(ID_Param2, true, XO("Output level"));
1015 UpdateControl(ID_Repeats, false, defaultLabel(4));
1016 UpdateControl(ID_DCBlock, false, {});
1017 break;
1018 case kLeveller:
1019 UpdateControlText(mThresholdT, mOldThresholdTxt, false);
1020 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, true);
1021 UpdateControlText(mParam1T, mOldParam1Txt, true);
1022 UpdateControlText(mParam2T, mOldParam2Txt, false);
1023 UpdateControlText(mRepeatsT, mOldRepeatsTxt, true);
1024
1025 UpdateControl(ID_Threshold, false, defaultLabel(0));
1026 UpdateControl(ID_NoiseFloor, true, defaultLabel(1));
1027 UpdateControl(ID_Param1, true, XO("Levelling fine adjustment"));
1028 UpdateControl(ID_Param2, false, defaultLabel(3));
1029 UpdateControl(ID_Repeats, true, XO("Degree of Levelling"));
1030 UpdateControl(ID_DCBlock, false, {});
1031 break;
1032 case kRectifier:
1033 UpdateControlText(mThresholdT, mOldThresholdTxt, false);
1034 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
1035 UpdateControlText(mParam1T, mOldParam1Txt, true);
1036 UpdateControlText(mParam2T, mOldParam2Txt, false);
1037 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
1038
1039 UpdateControl(ID_Threshold, false, defaultLabel(0));
1040 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
1041 UpdateControl(ID_Param1, true, XO("Distortion amount"));
1042 UpdateControl(ID_Param2, false, defaultLabel(3));
1043 UpdateControl(ID_Repeats, false, defaultLabel(4));
1044 UpdateControl(ID_DCBlock, true, {});
1045 break;
1046 case kHardLimiter:
1047 UpdateControlText(mThresholdT, mOldThresholdTxt, true);
1048 UpdateControlText(mNoiseFloorT, mOldmNoiseFloorTxt, false);
1049 UpdateControlText(mParam1T, mOldParam1Txt, true);
1050 UpdateControlText(mParam2T, mOldParam2Txt, true);
1051 UpdateControlText(mRepeatsT, mOldRepeatsTxt, false);
1052
1053 UpdateControl(ID_Threshold, true, XO("dB Limit"));
1054 UpdateControl(ID_NoiseFloor, false, defaultLabel(1));
1055 UpdateControl(ID_Param1, true, XO("Wet level"));
1056 UpdateControl(ID_Param2, true, XO("Residual level"));
1057 UpdateControl(ID_Repeats, false, defaultLabel(4));
1058 UpdateControl(ID_DCBlock, false, {});
1059 break;
1060 default:
1061 UpdateControl(ID_Threshold, true, defaultLabel(0));
1062 UpdateControl(ID_NoiseFloor, true, defaultLabel(1));
1063 UpdateControl(ID_Param1, true, defaultLabel(2));
1064 UpdateControl(ID_Param2, true, defaultLabel(3));
1065 UpdateControl(ID_Repeats, true, defaultLabel(4));
1066 UpdateControl(ID_DCBlock, false, {});
1067 }
1068}
1069
1070
1072 control id, bool enabled, TranslatableString name)
1073{
1074 auto& ms = mSettings;
1075
1076 auto suffix = XO("(Not Used):");
1077 switch (id)
1078 {
1079 case ID_Threshold: {
1080 /* i18n-hint: Control range. */
1081 if (enabled) suffix = XO("(-100 to 0 dB):");
1082 name.Join( suffix, wxT(" ") );
1083
1084 // Logarithmic slider is set indirectly
1085 const double threshold = DB_TO_LINEAR(ms.mThreshold_dB);
1086 mThresholdS->SetValue((int) (threshold * Threshold_dB.scale + 0.5));
1087
1088 auto translated = name.Translation();
1089 mThresholdTxt->SetLabel(translated);
1090 mThresholdS->SetName(translated);
1091 mThresholdT->SetName(translated);
1092 mThresholdS->Enable(enabled);
1093 mThresholdT->Enable(enabled);
1094 break;
1095 }
1096 case ID_NoiseFloor: {
1097 /* i18n-hint: Control range. */
1098 if (enabled) suffix = XO("(-80 to -20 dB):");
1099 name.Join( suffix, wxT(" ") );
1100
1101 auto translated = name.Translation();
1102 mNoiseFloorTxt->SetLabel(translated);
1103 mNoiseFloorS->SetName(translated);
1104 mNoiseFloorT->SetName(translated);
1105 mNoiseFloorS->Enable(enabled);
1106 mNoiseFloorT->Enable(enabled);
1107 break;
1108 }
1109 case ID_Param1: {
1110 /* i18n-hint: Control range. */
1111 if (enabled) suffix = XO("(0 to 100):");
1112 name.Join( suffix, wxT(" ") );
1113
1114 auto translated = name.Translation();
1115 mParam1Txt->SetLabel(translated);
1116 mParam1S->SetName(translated);
1117 mParam1T->SetName(translated);
1118 mParam1S->Enable(enabled);
1119 mParam1T->Enable(enabled);
1120 break;
1121 }
1122 case ID_Param2: {
1123 /* i18n-hint: Control range. */
1124 if (enabled) suffix = XO("(0 to 100):");
1125 name.Join( suffix, wxT(" ") );
1126
1127 auto translated = name.Translation();
1128 mParam2Txt->SetLabel(translated);
1129 mParam2S->SetName(translated);
1130 mParam2T->SetName(translated);
1131 mParam2S->Enable(enabled);
1132 mParam2T->Enable(enabled);
1133 break;
1134 }
1135 case ID_Repeats: {
1136 /* i18n-hint: Control range. */
1137 if (enabled) suffix = XO("(0 to 5):");
1138 name.Join( suffix, wxT(" ") );
1139
1140 auto translated = name.Translation();
1141 mRepeatsTxt->SetLabel(translated);
1142 mRepeatsS->SetName(translated);
1143 mRepeatsT->SetName(translated);
1144 mRepeatsS->Enable(enabled);
1145 mRepeatsT->Enable(enabled);
1146 break;
1147 }
1148 case ID_DCBlock: {
1149 if (enabled) {
1150 mDCBlockCheckBox->SetValue(GetState().mbSavedFilterState);
1151 ms.mDCBlock = GetState().mbSavedFilterState;
1152 }
1153 else {
1154 mDCBlockCheckBox->SetValue(false);
1155 ms.mDCBlock = false;
1156 }
1157
1158 mDCBlockCheckBox->Enable(enabled);
1159 break;
1160 }
1161 default:
1162 break;
1163 }
1164}
1165
1166void EffectDistortion::Editor::UpdateControlText(wxTextCtrl* textCtrl, wxString& string, bool enabled)
1167{
1168 if (enabled) {
1169 if (textCtrl->GetValue().empty())
1170 textCtrl->SetValue(string);
1171 else
1172 string = textCtrl->GetValue();
1173 }
1174 else {
1175 if (!textCtrl->GetValue().empty())
1176 string = textCtrl->GetValue();
1177 textCtrl->SetValue(wxT(""));
1178 }
1179}
1180
1182(
1183 EffectDistortionState& state,
1184 const EffectDistortionSettings& ms
1185)
1186{
1187 switch (ms.mTableChoiceIndx)
1188 {
1189 case kHardClip:
1190 HardClip(state, ms);
1191 break;
1192 case kSoftClip:
1193 SoftClip(state, ms);
1194 break;
1195 case kHalfSinCurve:
1196 HalfSinTable(ms);
1197 break;
1198 case kExpCurve:
1199 ExponentialTable(ms);
1200 break;
1201 case kLogCurve:
1202 LogarithmicTable(ms);
1203 break;
1204 case kCubic:
1205 CubicTable(ms);
1206 break;
1207 case kEvenHarmonics:
1208 EvenHarmonicTable(ms);
1209 break;
1210 case kSinCurve:
1211 SineTable(ms);
1212 break;
1213 case kLeveller:
1214 Leveller(ms);
1215 break;
1216 case kRectifier:
1217 Rectifier(ms);
1218 break;
1219 case kHardLimiter:
1220 HardLimiter(state, ms);
1221 break;
1222 }
1223}
1224
1225
1226//
1227// Preset tables for gain lookup
1228//
1229
1231(
1232 EffectDistortionState& state,
1233 const EffectDistortionSettings& ms
1234)
1235{
1236 const double threshold = DB_TO_LINEAR(ms.mThreshold_dB);
1237
1238 double lowThresh = 1 - threshold;
1239 double highThresh = 1 + threshold;
1240
1241 for (int n = 0; n < TABLESIZE; n++) {
1242 if (n < (STEPS * lowThresh))
1243 mTable[n] = - threshold;
1244 else if (n > (STEPS * highThresh))
1245 mTable[n] = threshold;
1246 else
1247 mTable[n] = n/(double)STEPS - 1;
1248
1249 state.mMakeupGain = 1.0 / threshold;
1250 }
1251}
1252
1254( EffectDistortionState& state,
1255 const EffectDistortionSettings& ms
1256)
1257{
1258 const double thresholdLinear = DB_TO_LINEAR(ms.mThreshold_dB);
1259
1260 double threshold = 1 + thresholdLinear;
1261 double amount = std::pow(2.0, 7.0 * ms.mParam1 / 100.0); // range 1 to 128
1262 double peak = LogCurve(thresholdLinear, 1.0, amount);
1263 state.mMakeupGain = 1.0 / peak;
1264 mTable[STEPS] = 0.0; // origin
1265
1266 // positive half of table
1267 for (int n = STEPS; n < TABLESIZE; n++) {
1268 if (n < (STEPS * threshold)) // origin to threshold
1269 mTable[n] = n/(float)STEPS - 1;
1270 else
1271 mTable[n] = LogCurve(thresholdLinear, n/(double)STEPS - 1, amount);
1272 }
1273 CopyHalfTable();
1274}
1275
1276float EffectDistortion::Instance::LogCurve(double threshold, float value, double ratio)
1277{
1278 return threshold + ((std::exp(ratio * (threshold - value)) - 1) / -ratio);
1279}
1280
1282{
1283 double amount = std::min(0.999, DB_TO_LINEAR(-1 * ms.mParam1)); // avoid divide by zero
1284
1285 for (int n = STEPS; n < TABLESIZE; n++) {
1286 double linVal = n/(float)STEPS;
1287 double scale = -1.0 / (1.0 - amount); // unity gain at 0dB
1288 double curve = std::exp((linVal - 1) * std::log(amount));
1289 mTable[n] = scale * (curve -1);
1290 }
1291 CopyHalfTable();
1292}
1293
1295{
1296 double amount = ms.mParam1;
1297 double stepsize = 1.0 / STEPS;
1298 double linVal = 0;
1299
1300 if (amount == 0){
1301 for (int n = STEPS; n < TABLESIZE; n++) {
1302 mTable[n] = linVal;
1303 linVal += stepsize;
1304 }
1305 }
1306 else {
1307 for (int n = STEPS; n < TABLESIZE; n++) {
1308 mTable[n] = std::log(1 + (amount * linVal)) / std::log(1 + amount);
1309 linVal += stepsize;
1310 }
1311 }
1312 CopyHalfTable();
1313}
1314
1316{
1317 int iter = std::floor(ms.mParam1 / 20.0);
1318 double fractionalpart = (ms.mParam1 / 20.0) - iter;
1319 double stepsize = 1.0 / STEPS;
1320 double linVal = 0;
1321
1322 for (int n = STEPS; n < TABLESIZE; n++) {
1323 mTable[n] = linVal;
1324 for (int i = 0; i < iter; i++) {
1325 mTable[n] = std::sin(mTable[n] * M_PI_2);
1326 }
1327 mTable[n] += ((std::sin(mTable[n] * M_PI_2) - mTable[n]) * fractionalpart);
1328 linVal += stepsize;
1329 }
1330 CopyHalfTable();
1331}
1332
1334{
1335 double amount = ms.mParam1 * std::sqrt(3.0) / 100.0;
1336 double gain = 1.0;
1337 if (amount != 0.0)
1338 gain = 1.0 / Cubic(ms, std::min(amount, 1.0));
1339
1340 double stepsize = amount / STEPS;
1341 double x = -amount;
1342
1343 if (amount == 0) {
1344 for (int i = 0; i < TABLESIZE; i++) {
1345 mTable[i] = (i / (double)STEPS) - 1.0;
1346 }
1347 }
1348 else {
1349 for (int i = 0; i < TABLESIZE; i++) {
1350 mTable[i] = gain * Cubic(ms, x);
1351 for (int j = 0; j < ms.mRepeats; j++) {
1352 mTable[i] = gain * Cubic(ms, mTable[i] * amount);
1353 }
1354 x += stepsize;
1355 }
1356 }
1357}
1358
1360{
1361 if (ms.mParam1 == 0.0)
1362 return x;
1363
1364 return x - (std::pow(x, 3.0) / 3.0);
1365}
1366
1367
1369{
1370 double amount = ms.mParam1 / -100.0;
1371 // double C = std::sin(std::max(0.001, mParams.mParam2) / 100.0) * 10.0;
1372 double C = std::max(0.001, ms.mParam2) / 10.0;
1373
1374 double step = 1.0 / STEPS;
1375 double xval = -1.0;
1376
1377 for (int i = 0; i < TABLESIZE; i++) {
1378 mTable[i] = ((1 + amount) * xval) -
1379 (xval * (amount / std::tanh(C)) * std::tanh(C * xval));
1380 xval += step;
1381 }
1382}
1383
1385{
1386 int iter = std::floor(ms.mParam1 / 20.0);
1387 double fractionalpart = (ms.mParam1 / 20.0) - iter;
1388 double stepsize = 1.0 / STEPS;
1389 double linVal = 0.0;
1390
1391 for (int n = STEPS; n < TABLESIZE; n++) {
1392 mTable[n] = linVal;
1393 for (int i = 0; i < iter; i++) {
1394 mTable[n] = (1.0 + std::sin((mTable[n] * M_PI) - M_PI_2)) / 2.0;
1395 }
1396 mTable[n] += (((1.0 + std::sin((mTable[n] * M_PI) - M_PI_2)) / 2.0) - mTable[n]) * fractionalpart;
1397 linVal += stepsize;
1398 }
1399 CopyHalfTable();
1400}
1401
1403{
1404 double noiseFloor = DB_TO_LINEAR(ms.mNoiseFloor);
1405 int numPasses = ms.mRepeats;
1406 double fractionalPass = ms.mParam1 / 100.0;
1407
1408 const int numPoints = 6;
1409 const double gainFactors[numPoints] = { 0.80, 1.00, 1.20, 1.20, 1.00, 0.80 };
1410 double gainLimits[numPoints] = { 0.0001, 0.0, 0.1, 0.3, 0.5, 1.0 };
1411 double addOnValues[numPoints];
1412
1413 gainLimits[1] = noiseFloor;
1414 /* In the original Leveller effect, behaviour was undefined for threshold > 20 dB.
1415 * If we want to support > 20 dB we need to scale the points to keep them non-decreasing.
1416 *
1417 * if (noiseFloor > gainLimits[2]) {
1418 * for (int i = 3; i < numPoints; i++) {
1419 * gainLimits[i] = noiseFloor + ((1 - noiseFloor)*((gainLimits[i] - 0.1) / 0.9));
1420 * }
1421 * gainLimits[2] = noiseFloor;
1422 * }
1423 */
1424
1425 // Calculate add-on values
1426 addOnValues[0] = 0.0;
1427 for (int i = 0; i < numPoints-1; i++) {
1428 addOnValues[i+1] = addOnValues[i] + (gainLimits[i] * (gainFactors[i] - gainFactors[1 + i]));
1429 }
1430
1431 // Positive half of table.
1432 // The original effect increased the 'strength' of the effect by
1433 // repeated passes over the audio data.
1434 // Here we model that more efficiently by repeated passes over a linear table.
1435 for (int n = STEPS; n < TABLESIZE; n++) {
1436 mTable[n] = ((double) (n - STEPS) / (double) STEPS);
1437 for (int j = 0; j < numPasses; j++) {
1438 // Find the highest index for gain adjustment
1439 int index = numPoints - 1;
1440 for (int i = index; i >= 0 && mTable[n] < gainLimits[i]; i--) {
1441 index = i;
1442 }
1443 // the whole number of 'repeats'
1444 mTable[n] = (mTable[n] * gainFactors[index]) + addOnValues[index];
1445 }
1446 // Extrapolate for fine adjustment.
1447 // tiny fractions are not worth the processing time
1448 if (fractionalPass > 0.001) {
1449 int index = numPoints - 1;
1450 for (int i = index; i >= 0 && mTable[n] < gainLimits[i]; i--) {
1451 index = i;
1452 }
1453 mTable[n] += fractionalPass * ((mTable[n] * (gainFactors[index] - 1)) + addOnValues[index]);
1454 }
1455 }
1456 CopyHalfTable();
1457}
1458
1460{
1461 double amount = (ms.mParam1 / 50.0) - 1;
1462 double stepsize = 1.0 / STEPS;
1463 int index = STEPS;
1464
1465 // positive half of waveform is passed unaltered.
1466 for (int n = 0; n <= STEPS; n++) {
1467 mTable[index] = n * stepsize;
1468 index += 1;
1469 }
1470
1471 // negative half of table
1472 index = STEPS - 1;
1473 for (int n = 1; n <= STEPS; n++) {
1474 mTable[index] = n * stepsize * amount;
1475 index--;
1476 }
1477}
1478
1480{
1481 // The LADSPA "hardLimiter 1413" is basically hard clipping,
1482 // but with a 'kind of' wet/dry mix:
1483 // out = ((wet-residual)*clipped) + (residual*in)
1484 HardClip(state, settings);
1485}
1486
1487
1488// Helper functions for lookup tables
1489
1491{
1492 // Copy negative half of table from positive half
1493 int count = TABLESIZE - 1;
1494 for (int n = 0; n < STEPS; n++) {
1495 mTable[n] = -mTable[count];
1496 count--;
1497 }
1498}
1499
1500
1502{
1503 float out;
1504 int index;
1505 double xOffset;
1506 double amount = 1;
1507
1508 switch (ms.mTableChoiceIndx)
1509 {
1510 // Do any pre-processing here
1511 case kHardClip:
1512 // Pre-gain
1513 amount = ms.mParam1 / 100.0;
1514 sample *= 1+amount;
1515 break;
1516 default: break;
1517 }
1518
1519 index = std::floor(sample * STEPS) + STEPS;
1520 index = wxMax<int>(wxMin<int>(index, 2 * STEPS - 1), 0);
1521 xOffset = ((1 + sample) * STEPS) - index;
1522 xOffset = wxMin<double>(wxMax<double>(xOffset, 0.0), 1.0); // Clip at 0dB
1523
1524 // linear interpolation: y = y0 + (y1-y0)*(x-x0)
1525 out = mTable[index] + (mTable[index + 1] - mTable[index]) * xOffset;
1526
1527 return out;
1528}
1529
1530
1532{
1533 // Rolling average gives less offset at the start than an IIR filter.
1534 const unsigned int queueLength = std::floor(data.samplerate / 20.0);
1535
1536 data.queuetotal += sample;
1537 data.queuesamples.push(sample);
1538
1539 if (data.queuesamples.size() > queueLength) {
1540 data.queuetotal -= data.queuesamples.front();
1541 data.queuesamples.pop();
1542 }
1543
1544 return sample - (data.queuetotal / data.queuesamples.size());
1545}
wxT("CloseDown"))
int min(int a, int b)
TranslatableString defaultLabel(int index)
Definition: Distortion.cpp:108
static const struct @18 FactoryPresets[]
EffectDistortionSettings params
Definition: Distortion.cpp:77
#define M_PI_2
Definition: Distortion.cpp:33
#define skipsamples
Definition: Distortion.cpp:72
const TranslatableString name
Definition: Distortion.cpp:76
#define M_PI
Definition: Distortion.cpp:30
#define TABLESIZE
Definition: Distortion.h:22
#define STEPS
Definition: Distortion.h:21
EffectType
@ EffectTypeProcess
ChannelName
std::optional< std::unique_ptr< EffectSettingsAccess::Message > > OptionalMessage
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
std::vector< RegistryPath > RegistryPaths
Definition: Identifier.h:219
#define LINEAR_TO_DB(x)
Definition: MemoryX.h:339
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:338
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Convenience function often useful when adding choice controls.
static TranslatableStrings names
Definition: TagsEditor.cpp:153
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:47
int id
bool ValidateUI(const EffectPlugin &context, EffectSettings &) const override
Generates EffectParameterMethods overrides from variadic template arguments.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
void SetLinearEffectFlag(bool linearEffectFlag)
Definition: EffectBase.cpp:210
RealtimeSince
In which versions of Audacity was an effect realtime capable?
A WaveShaper distortion effect.
Definition: Distortion.h:74
static constexpr EffectParameter Threshold_dB
Definition: Distortion.h:158
static constexpr EffectParameter Param2
Definition: Distortion.h:170
std::shared_ptr< EffectInstance > MakeInstance() const override
Make an object maintaining short-term state of an Effect.
Definition: Distortion.cpp:310
OptionalMessage DoLoadFactoryPreset(int id, EffectSettings &settings)
Definition: Distortion.cpp:437
static const ComponentInterfaceSymbol Symbol
Definition: Distortion.h:76
static constexpr EffectParameter Param1
Definition: Distortion.h:166
virtual ~EffectDistortion()
Definition: Distortion.cpp:328
static const EnumValueSymbol kTableTypeStrings[nTableTypes]
Definition: Distortion.h:146
OptionalMessage LoadFactoryPreset(int id, EffectSettings &settings) const override
Definition: Distortion.cpp:431
ComponentInterfaceSymbol GetSymbol() const override
Definition: Distortion.cpp:334
static constexpr EffectParameter Repeats
Definition: Distortion.h:173
static constexpr EffectParameter DCBlock
Definition: Distortion.h:154
static constexpr EnumParameter TableTypeIndx
Definition: Distortion.h:150
static constexpr EffectParameter NoiseFloor
Definition: Distortion.h:162
RealtimeSince RealtimeSupport() const override
Since which version of Audacity has the effect supported realtime?
Definition: Distortion.cpp:356
EffectType GetType() const override
Type determines how it behaves.
Definition: Distortion.cpp:351
TranslatableString GetDescription() const override
Definition: Distortion.cpp:339
const EffectParameterMethods & Parameters() const override
Definition: Distortion.cpp:63
std::unique_ptr< EffectEditor > MakeEditor(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) const override
Called only from PopulateUI, to add controls to effect panel.
Definition: Distortion.cpp:453
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
Definition: Distortion.cpp:344
RegistryPaths GetFactoryPresets() const override
Report names of factory presets.
Definition: Distortion.cpp:418
sampleCount skipcount
Definition: Distortion.h:30
std::queue< float > queuesamples
Definition: Distortion.h:40
EffectSettingsAccess & mAccess
Definition: EffectEditor.h:92
Performs effect computation.
Inherit to add a state variable to an EffectInstance subclass.
Hold values to send to effect output meters.
Interface for manipulations of an Effect's settings.
void ModifySettings(Function &&function)
Do a correct read-modify-write of settings.
virtual const EffectSettings & Get()=0
static EffectDistortionSettings & GetSettings(EffectSettings &settings)
Assume settings originated from MakeSettings() and copies thereof.
Definition: Effect.h:166
Base class for many of the effects in Audacity.
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
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:54
BuiltinEffectsModule::Registration< EffectDistortion > reg
Definition: Distortion.cpp:128
__finl float_x4 __vecc sqrt(const float_x4 &a)
wxStaticText * mRepeatsTxt
Definition: Distortion.cpp:181
virtual ~Editor()=default
void OnParam2Text(wxCommandEvent &evt)
Definition: Distortion.cpp:855
bool ValidateUI() override
Get settings data from the panel; may make error dialogs and return false.
Definition: Distortion.cpp:202
void UpdateControl(control id, bool enable, TranslatableString name)
Editor(const EffectUIServices &services, EffectDistortion::Instance &instance, EffectSettingsAccess &access, const EffectDistortionSettings &settings)
Definition: Distortion.cpp:134
void OnRepeatsText(wxCommandEvent &evt)
Definition: Distortion.cpp:877
void OnRepeatsSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:888
void OnTypeChoice(wxCommandEvent &evt)
Definition: Distortion.cpp:760
wxStaticText * mThresholdTxt
Definition: Distortion.cpp:177
void OnDCBlockCheckbox(wxCommandEvent &evt)
Definition: Distortion.cpp:770
wxStaticText * mParam2Txt
Definition: Distortion.cpp:180
EffectDistortionSettings mSettings
Definition: Distortion.cpp:189
EffectDistortion::Instance & mInstance
Definition: Distortion.cpp:198
bool UpdateUI() override
Update appearance of the panel for changes in settings.
Definition: Distortion.cpp:629
void UpdateControlText(wxTextCtrl *textCtrl, wxString &string, bool enabled)
wxCheckBox * mDCBlockCheckBox
Definition: Distortion.cpp:175
void OnNoiseFloorText(wxCommandEvent &evt)
Definition: Distortion.cpp:810
wxWeakRef< wxWindow > mUIParent
Definition: Distortion.cpp:197
void OnParam2Slider(wxCommandEvent &evt)
Definition: Distortion.cpp:866
void OnThresholdText(wxCommandEvent &evt)
Definition: Distortion.cpp:783
void OnParam1Slider(wxCommandEvent &evt)
Definition: Distortion.cpp:844
void OnParam1Text(wxCommandEvent &evt)
Definition: Distortion.cpp:833
wxStaticText * mParam1Txt
Definition: Distortion.cpp:179
void OnThresholdSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:795
void PopulateOrExchange(ShuttleGui &S)
Definition: Distortion.cpp:465
wxStaticText * mNoiseFloorTxt
Definition: Distortion.cpp:178
void OnNoiseFloorSlider(wxCommandEvent &evt)
Definition: Distortion.cpp:821
EffectDistortionState & GetState()
Definition: Distortion.cpp:316
void Leveller(const EffectDistortionSettings &)
void LogarithmicTable(const EffectDistortionSettings &)
void InstanceInit(EffectDistortionState &data, EffectSettings &settings, float sampleRate)
Definition: Distortion.cpp:655
void MakeTable(EffectDistortionState &state, const EffectDistortionSettings &ms)
size_t InstanceProcess(EffectSettings &settings, EffectDistortionState &data, const float *const *inBlock, float *const *outBlock, size_t blockLen)
Definition: Distortion.cpp:681
void CubicTable(const EffectDistortionSettings &)
void HalfSinTable(const EffectDistortionSettings &)
void EvenHarmonicTable(const EffectDistortionSettings &)
void SineTable(const EffectDistortionSettings &)
double Cubic(const EffectDistortionSettings &, double x)
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
Definition: Distortion.cpp:366
Instance(const PerTrackEffect &effect)
Definition: Distortion.cpp:238
EffectDistortionState mMaster
Definition: Distortion.cpp:304
float LogCurve(double threshold, float value, double ratio)
bool RealtimeAddProcessor(EffectSettings &settings, EffectOutputs *pOutputs, unsigned numChannels, float sampleRate) override
Definition: Distortion.cpp:391
float DCFilter(EffectDistortionState &data, float sample)
void Rectifier(const EffectDistortionSettings &)
unsigned GetAudioInCount() const override
How many input buffers to allocate at once.
Definition: Distortion.cpp:361
bool ProcessInitialize(EffectSettings &settings, double sampleRate, ChannelNames chanMap) override
Definition: Distortion.cpp:371
void SoftClip(EffectDistortionState &, const EffectDistortionSettings &)
bool RealtimeFinalize(EffectSettings &settings) noexcept override
Definition: Distortion.cpp:403
size_t RealtimeProcess(size_t group, EffectSettings &settings, const float *const *inbuf, float *const *outbuf, size_t numSamples) override
Definition: Distortion.cpp:410
std::vector< EffectDistortionState > mSlaves
Definition: Distortion.cpp:305
void ExponentialTable(const EffectDistortionSettings &)
size_t ProcessBlock(EffectSettings &settings, const float *const *inBlock, float *const *outBlock, size_t blockLen) override
Called for destructive effect computation.
Definition: Distortion.cpp:378
void HardClip(EffectDistortionState &, const EffectDistortionSettings &)
float WaveShaper(float sample, EffectDistortionSettings &ms)
bool RealtimeInitialize(EffectSettings &settings, double) override
Definition: Distortion.cpp:384
double mTable[TABLESIZE]
Definition: Distortion.cpp:302
void HardLimiter(EffectDistortionState &state, const EffectDistortionSettings &)
const Type scale
Scaling factor, for slider control.
const Type def
Default value.
const Type min
Minimum value.
const Type max
Maximum value.
Message sent by EffectEditor when a setting is changed by the user.
Definition: EffectEditor.h:26
Externalized state of a plug-in.