Audacity 3.2.0
DtmfGen.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 DtmfGen.cpp
6
7 Salvo Ventura - Dec 2006
8
9*******************************************************************//*******************************************************************/
15
16
17#include "DtmfGen.h"
18#include "LoadEffects.h"
19
20#include <wx/intl.h>
21#include <wx/slider.h>
22#include <wx/valgen.h>
23#include <wx/valtext.h>
24#include <wx/stattext.h>
25
26#include "Prefs.h"
27#include "../ShuttleGui.h"
28#include "../widgets/NumericTextCtrl.h"
29#include "../widgets/valnum.h"
30
31
32namespace {
33wxString AllSymbols();
34}
35
37{
40 > parameters{
41 [](EffectDtmf &, EffectSettings &es, DtmfSettings &s, bool updating){
42 if (updating) {
43 if (s.dtmfSequence.find_first_not_of(AllSymbols())
44 != wxString::npos)
45 return false;
46 s.Recalculate(es);
47 }
48 return true;
49 },
50 };
51 return parameters;
52}
53
54static const double kFadeInOut = 250.0; // used for fadein/out needed to remove clicking noise
55
56const static wxChar *kSymbols[] =
57{
58 wxT("0"), wxT("1"), wxT("2"), wxT("3"),
59 wxT("4"), wxT("5"), wxT("6"), wxT("7"),
60 wxT("8"), wxT("9"), wxT("*"), wxT("#"),
61 wxT("A"), wxT("B"), wxT("C"), wxT("D"),
62 wxT("a"), wxT("b"), wxT("c"), wxT("d"),
63 wxT("e"), wxT("f"), wxT("g"), wxT("h"),
64 wxT("i"), wxT("j"), wxT("k"), wxT("l"),
65 wxT("m"), wxT("n"), wxT("o"), wxT("p"),
66 wxT("q"), wxT("r"), wxT("s"), wxT("t"),
67 wxT("u"), wxT("v"), wxT("w"), wxT("x"),
68 wxT("y"), wxT("z")
69};
70
71namespace {
72wxString AllSymbols()
73{
74 wxString symbols;
75 for (unsigned int i = 0; i < WXSIZEOF(kSymbols); ++i)
76 symbols += kSymbols[i];
77 return symbols;
78}
79}
80
81//
82// EffectDtmf
83//
84
86{ XO("DTMF Tones") };
87
89
91{
92}
93
95{
96}
97
98// ComponentInterface implementation
99
101{
102 return Symbol;
103}
104
106{
107 return XO("Generates dual-tone multi-frequency (DTMF) tones like those produced by the keypad on telephones");
108}
109
111{
112 return L"DTMF_Tones";
113}
114
115// EffectDefinitionInterface implementation
116
118{
119 return EffectTypeGenerate;
120}
121
123{
124 return 1;
125}
126
131{
132 Instance(const PerTrackEffect &effect, double t0)
133 : PerTrackEffect::Instance{ effect }
134 , mT0{ t0 }
135 {}
136
137 bool ProcessInitialize(EffectSettings &settings, double sampleRate,
138 sampleCount totalLen, ChannelNames chanMap) override;
140 const float *const *inBlock, float *const *outBlock, size_t blockLen)
141 override;
142
143 const double mT0;
144 double mSampleRate{};
145
146 sampleCount numSamplesSequence; // total number of samples to generate
147 sampleCount numSamplesTone; // number of samples in a tone block
148 sampleCount numSamplesSilence; // number of samples in a silence block
149 sampleCount diff; // number of extra samples to redistribute
150 sampleCount numRemaining; // number of samples left to produce in the current block
151 sampleCount curTonePos; // position in tone to start the wave
152 bool isTone; // true if block is tone, otherwise silence
153 int curSeqPos; // index into dtmf tone string
154};
155
157 EffectSettings &settings, double sampleRate, sampleCount, ChannelNames)
158{
159 mSampleRate = sampleRate;
160
161 auto &dtmfSettings = GetSettings(settings);
162 if (dtmfSettings.dtmfNTones == 0) { // Bail if no DTFM sequence.
163 // TODO: don't use mProcessor for this
165 XO("DTMF sequence empty.\nCheck ALL settings for this effect."),
166 wxICON_ERROR );
167
168 return false;
169 }
170
171 double duration = settings.extra.GetDuration();
172
173 // all dtmf sequence durations in samples from seconds
174 // MJS: Note that mDuration is in seconds but will have been quantised to the units of the TTC.
175 // If this was 'samples' and the project rate was lower than the track rate,
176 // extra samples may get created as mDuration may now be > mT1 - mT0;
177 // However we are making our best efforts at creating what was asked for.
178
179 auto nT0 = (sampleCount)floor(mT0 * mSampleRate + 0.5);
180 auto nT1 = (sampleCount)floor((mT0 + duration) * mSampleRate + 0.5);
181 numSamplesSequence = nT1 - nT0; // needs to be exact number of samples selected
182
183 //make under-estimates if anything, and then redistribute the few remaining samples
184 numSamplesTone = sampleCount( floor(dtmfSettings.dtmfTone * mSampleRate) );
185 numSamplesSilence = sampleCount( floor(dtmfSettings.dtmfSilence * mSampleRate) );
186
187 // recalculate the sum, and spread the difference - due to approximations.
188 // Since diff should be in the order of "some" samples, a division (resulting in zero)
189 // is not sufficient, so we add the additional remaining samples in each tone/silence block,
190 // at least until available.
191 diff = numSamplesSequence - (dtmfSettings.dtmfNTones * numSamplesTone)
192 - (dtmfSettings.dtmfNTones - 1) * numSamplesSilence;
193 while (diff > 2 * dtmfSettings.dtmfNTones - 1) { // more than one per thingToBeGenerated
194 // in this case, both numSamplesTone and numSamplesSilence would change, so it makes sense
195 // to recalculate diff here, otherwise just keep the value we already have
196
197 // should always be the case that dtmfNTones>1, as if 0, we don't even start processing,
198 // and with 1 there is no difference to spread (no silence slot)...
199 wxASSERT(dtmfSettings.dtmfNTones > 1);
200 numSamplesTone += (diff / (dtmfSettings.dtmfNTones));
201 numSamplesSilence += (diff / (dtmfSettings.dtmfNTones - 1));
202 diff = numSamplesSequence - (dtmfSettings.dtmfNTones * numSamplesTone)
203 - (dtmfSettings.dtmfNTones - 1) * numSamplesSilence;
204 }
205 wxASSERT(diff >= 0); // should never be negative
206
207 curSeqPos = -1; // pointer to string in dtmfSequence
208 isTone = false;
209 numRemaining = 0;
210
211 return true;
212}
213
215 const float *const *, float *const *outbuf, size_t size)
216{
217 auto &dtmfSettings = GetSettings(settings);
218 float *buffer = outbuf[0];
219 decltype(size) processed = 0;
220
221 // for the whole dtmf sequence, we will be generating either tone or silence
222 // according to a bool value, and this might be done in small chunks of size
223 // 'block', as a single tone might sometimes be larger than the block
224 // tone and silence generally have different duration, thus two generation blocks
225 //
226 // Note: to overcome a 'clicking' noise introduced by the abrupt transition from/to
227 // silence, I added a fade in/out of 1/250th of a second (4ms). This can still be
228 // tweaked but gives excellent results at 44.1kHz: I haven't tried other freqs.
229 // A problem might be if the tone duration is very short (<10ms)... (?)
230 //
231 // One more problem is to deal with the approximations done when calculating the duration
232 // of both tone and silence: in some cases the final sum might not be same as the initial
233 // duration. So, to overcome this, we had a redistribution block up, and now we will spread
234 // the remaining samples in every bin in order to achieve the full duration: test case was
235 // to generate an 11 tone DTMF sequence, in 4 seconds, and with DutyCycle=75%: after generation
236 // you ended up with 3.999s or in other units: 3 seconds and 44097 samples.
237 //
238 while (size)
239 {
240 if (numRemaining == 0)
241 {
242 isTone = !isTone;
243
244 if (isTone)
245 {
246 curSeqPos++;
247 numRemaining = numSamplesTone;
248 curTonePos = 0;
249 }
250 else
251 {
252 numRemaining = numSamplesSilence;
253 }
254
255 // the statement takes care of extracting one sample from the diff bin and
256 // adding it into the current block until depletion
257 numRemaining += (diff-- > 0 ? 1 : 0);
258 }
259
260 const auto len = limitSampleBufferSize( size, numRemaining );
261
262 if (isTone)
263 {
264 // generate the tone and append
265 MakeDtmfTone(buffer, len, mSampleRate,
266 dtmfSettings.dtmfSequence[curSeqPos], curTonePos, numSamplesTone,
267 dtmfSettings.dtmfAmplitude);
268 curTonePos += len;
269 }
270 else
271 {
272 memset(buffer, 0, sizeof(float) * len);
273 }
274
275 numRemaining -= len;
276
277 buffer += len;
278 size -= len;
279 processed += len;
280 }
281
282 return processed;
283}
284
285// Event handler object
288{
291 : EffectUIValidator{effect, access}
292 // Copy settings
294 {}
295 virtual ~Validator() = default;
296
297 Effect &GetEffect() const { return static_cast<Effect&>(mEffect); }
298
299 bool ValidateUI() override;
300 bool UpdateUI() override;
301 void DoUpdateUI();
302
304 const EffectSettings &settings, double projectRate);
305 void OnSequence(wxCommandEvent & evt);
306 void OnDuration(wxCommandEvent & evt);
307 void OnDutyCycle(wxCommandEvent & evt);
308
309 // These settings exist for the lifetime of the validator
311
312 wxTextCtrl *mDtmfSequenceT;
315 wxStaticText *mDtmfToneT;
316 wxStaticText *mDtmfSilenceT;
317 wxStaticText *mDtmfDutyT;
318};
319
321 const EffectSettings &settings, double projectRate)
322{
323 // Reference to our copy of this effect's special settings
324 auto &dtmfSettings = mSettings;
325
326 // Do NOT hold a reference to EffectSettings, just use it to find initial
327 // duration values. (It came from EffectSettingsAccess so its stable address
328 // can't be relied on.)
329
330 // dialog will be passed values from effect
331 // Effect retrieves values from saved config
332 // Dialog will take care of using them to initialize controls
333 // If there is a selection, use that duration, otherwise use
334 // value from saved config: this is useful is user wants to
335 // replace selection with dtmf sequence
336
337 S.AddSpace(0, 5);
338 S.StartMultiColumn(2, wxCENTER);
339 {
340 mDtmfSequenceT =
341 S
342 .Validator([&dtmfSettings]{
343 wxTextValidator vldDtmf(
344 wxFILTER_INCLUDE_CHAR_LIST, &dtmfSettings.dtmfSequence);
345 vldDtmf.SetIncludes(wxArrayString(WXSIZEOF(kSymbols), kSymbols));
346 return vldDtmf;
347 })
348 .AddTextBox(XXO("DTMF &sequence:"), wxT(""), 10);
349 BindTo(*mDtmfSequenceT, wxEVT_TEXT, &Validator::OnSequence);
350
351 // A control with no event handler but the validator causes updates
352 // when TransferData functions are called
353 S
354 .Validator<FloatingPointValidator<double>>(
355 3, &dtmfSettings.dtmfAmplitude, NumValidatorStyle::NO_TRAILING_ZEROES,
357 .AddTextBox(XXO("&Amplitude (0-1):"), wxT(""), 10);
358
359 S.AddPrompt(XXO("&Duration:"));
360 auto &extra = settings.extra;
361 mDtmfDurationT = safenew
362 NumericTextCtrl(S.GetParent(), wxID_ANY,
364 extra.GetDurationFormat(),
365 extra.GetDuration(),
366 projectRate,
368 .AutoPos(true));
369 S.Name(XO("Duration"))
370 .AddWindow(mDtmfDurationT);
371 BindTo(*mDtmfDurationT, wxEVT_TEXT, &Validator::OnDuration);
372
373 S.AddFixedText(XO("&Tone/silence ratio:"), false);
374 mDtmfDutyCycleS =
375 S
376 .Style(wxSL_HORIZONTAL | wxEXPAND)
377 .MinSize( { -1, -1 } )
378 .AddSlider( {},
379 dtmfSettings.dtmfDutyCycle * DutyCycle.scale,
382 BindTo(*mDtmfDutyCycleS, wxEVT_SLIDER, &Validator::OnDutyCycle);
383 }
384 S.EndMultiColumn();
385
386 S.StartMultiColumn(2, wxCENTER);
387 {
388 S.AddFixedText(XO("Duty cycle:"), false);
389 mDtmfDutyT =
390 S.AddVariableText(XO("%.1f %%")
391 .Format( dtmfSettings.dtmfDutyCycle ), false);
392
393 S.AddFixedText(XO("Tone duration:"), false);
394 mDtmfSilenceT =
395 /* i18n-hint milliseconds */
396 S.AddVariableText(XO("%.0f ms")
397 .Format( dtmfSettings.dtmfTone * 1000.0 ), false);
398
399 S.AddFixedText(XO("Silence duration:"), false);
400 mDtmfToneT =
401 /* i18n-hint milliseconds */
402 S.AddVariableText(XO("%0.f ms")
403 .Format( dtmfSettings.dtmfSilence * 1000.0 ), false);
404 }
405 S.EndMultiColumn();
406}
407
408// Effect implementation
409
410std::unique_ptr<EffectUIValidator> EffectDtmf::PopulateOrExchange(
412{
413 auto &settings = access.Get();
414 auto &dtmfSettings = GetSettings(settings);
415 auto result = std::make_unique<Validator>(*this, access, dtmfSettings);
416 result->PopulateOrExchange(S, settings, mProjectRate);
417 return result;
418}
419
420std::shared_ptr<EffectInstance> EffectDtmf::MakeInstance() const
421{
422 // TODO: don't use Effect::mT0 and Effect::mSampleRate, but use an
423 // EffectContext (that class is not yet defined)
424 return std::make_shared<Instance>(*this, mT0);
425}
426
428{
429 const auto &settings = mAccess.Get();
430 auto &dtmfSettings = mSettings;
431
432 // Copy into our settings
433 mSettings = GetSettings(settings);
434
435 mDtmfDutyCycleS->SetValue(dtmfSettings.dtmfDutyCycle * DutyCycle.scale);
436
437 mDtmfDurationT->SetValue(settings.extra.GetDuration());
438
439 DoUpdateUI();
440
441 return true;
442}
443
445{
446 mAccess.ModifySettings([this](EffectSettings &settings){
447 auto &dtmfSettings = mSettings;
448 dtmfSettings.dtmfDutyCycle =
449 (double) mDtmfDutyCycleS->GetValue() / DutyCycle.scale;
450 settings.extra.SetDuration(mDtmfDurationT->GetValue());
451
452 // recalculate to make sure all values are up-to-date. This is especially
453 // important if the user did not change any values in the dialog
454 dtmfSettings.Recalculate(settings);
455 });
456
457 return true;
458}
459
460// EffectDtmf implementation
461
462// Updates dtmfNTones, dtmfTone, dtmfSilence, and sometimes duration
463// They depend on dtmfSequence, dtmfDutyCycle, and duration
465{
466 auto &extra = settings.extra;
467 // remember that dtmfDutyCycle is in range (0.0-100.0)
468
469 dtmfNTones = dtmfSequence.length();
470
471 if (dtmfNTones==0) {
472 // no tones, all zero: don't do anything
473 // this should take care of the case where user got an empty
474 // dtmf sequence into the generator: track won't be generated
475 extra.SetDuration(0.0);
476 dtmfTone = 0;
477 dtmfSilence = 0;
478 } else {
479 if (dtmfNTones==1) {
480 // single tone, as long as the sequence
481 dtmfTone = extra.GetDuration();
482 dtmfSilence = 0;
483 } else {
484 // Don't be fooled by the fact that you divide the sequence into dtmfNTones:
485 // the last slot will only contain a tone, not ending with silence.
486 // Given this, the right thing to do is to divide the sequence duration
487 // by dtmfNTones tones and (dtmfNTones-1) silences each sized according to the duty
488 // cycle: original division was:
489 // slot=mDuration / (dtmfNTones*(dtmfDutyCycle/DutyCycle.max)+(dtmfNTones-1)*(1.0-dtmfDutyCycle/DutyCycle.max))
490 // which can be simplified in the one below.
491 // Then just take the part that belongs to tone or silence.
492 //
493 double slot = extra.GetDuration()
494 / ((double)dtmfNTones + (dtmfDutyCycle / 100.0) - 1);
495 dtmfTone = slot * (dtmfDutyCycle / 100.0); // seconds
496 dtmfSilence = slot * (1.0 - (dtmfDutyCycle / 100.0)); // seconds
497
498 // Note that in the extremes we have:
499 // - dutyCycle=100%, this means no silence, so each tone will measure mDuration/dtmfNTones
500 // - dutyCycle=0%, this means no tones, so each silence slot will measure mDuration/(NTones-1)
501 // But we always count:
502 // - dtmfNTones tones
503 // - dtmfNTones-1 silences
504 }
505 }
506
507 // `this` is the settings copy in the validator
508 // Update the EffectSettings held by the dialog
510}
511
512bool EffectDtmf::MakeDtmfTone(float *buffer, size_t len, float fs, wxChar tone, sampleCount last, sampleCount total, float amplitude)
513{
514/*
515 --------------------------------------------
516 1209 Hz 1336 Hz 1477 Hz 1633 Hz
517
518 ABC DEF
519 697 Hz 1 2 3 A
520
521 GHI JKL MNO
522 770 Hz 4 5 6 B
523
524 PQRS TUV WXYZ
525 852 Hz 7 8 9 C
526
527 oper
528 941 Hz * 0 # D
529 --------------------------------------------
530 Essentially we need to generate two sin with
531 frequencies according to this table, and sum
532 them up.
533 sin wave is generated by:
534 s(n)=sin(2*pi*n*f/fs)
535
536 We will precalculate:
537 A= 2*pi*f1/fs
538 B= 2*pi*f2/fs
539
540 And use two switch statements to select the frequency
541
542 Note: added support for letters, like those on the keypad
543 This support is only for lowercase letters: uppercase
544 are still considered to be the 'military'/carrier extra
545 tones.
546*/
547
548 float f1, f2=0.0;
549 double A,B;
550
551 // select low tone: left column
552 switch (tone) {
553 case '1': case '2': case '3': case 'A':
554 case 'a': case 'b': case 'c':
555 case 'd': case 'e': case 'f':
556 f1=697;
557 break;
558 case '4': case '5': case '6': case 'B':
559 case 'g': case 'h': case 'i':
560 case 'j': case 'k': case 'l':
561 case 'm': case 'n': case 'o':
562 f1=770;
563 break;
564 case '7': case '8': case '9': case 'C':
565 case 'p': case 'q': case 'r': case 's':
566 case 't': case 'u': case 'v':
567 case 'w': case 'x': case 'y': case 'z':
568 f1=852;
569 break;
570 case '*': case '0': case '#': case 'D':
571 f1=941;
572 break;
573 default:
574 f1=0;
575 }
576
577 // select high tone: top row
578 switch (tone) {
579 case '1': case '4': case '7': case '*':
580 case 'g': case 'h': case 'i':
581 case 'p': case 'q': case 'r': case 's':
582 f2=1209;
583 break;
584 case '2': case '5': case '8': case '0':
585 case 'a': case 'b': case 'c':
586 case 'j': case 'k': case 'l':
587 case 't': case 'u': case 'v':
588 f2=1336;
589 break;
590 case '3': case '6': case '9': case '#':
591 case 'd': case 'e': case 'f':
592 case 'm': case 'n': case 'o':
593 case 'w': case 'x': case 'y': case 'z':
594 f2=1477;
595 break;
596 case 'A': case 'B': case 'C': case 'D':
597 f2=1633;
598 break;
599 default:
600 f2=0;
601 }
602
603 // precalculations
604 A=B=2*M_PI/fs;
605 A*=f1;
606 B*=f2;
607
608 // now generate the wave: 'last' is used to avoid phase errors
609 // when inside the inner for loop of the Process() function.
610 for(decltype(len) i = 0; i < len; i++) {
611 buffer[i] = amplitude * 0.5 *
612 (sin( A * (i + last).as_double() ) +
613 sin( B * (i + last).as_double() ));
614 }
615
616 // generate a fade-in of duration 1/250th of second
617 if (last == 0) {
618 A = wxMin(len, (fs / kFadeInOut));
619 for(size_t i = 0; i < A; i++) {
620 buffer[i] *= i/A;
621 }
622 }
623
624 // generate a fade-out of duration 1/250th of second
625 if (last >= total - len) {
626 // we are at the last buffer of 'len' size, so, offset is to
627 // backup 'A' samples, from 'len'
628 A = wxMin(len, (fs / kFadeInOut));
629 size_t offset = len - A;
630 wxASSERT(offset >= 0);
631 for(size_t i = 0; i < A; i++) {
632 buffer[i + offset] *= (1 - (i / A));
633 }
634 }
635 return true;
636}
637
639{
640 // Update some texts in response to controls
641 auto &dtmfSettings = mSettings;
642
643 mDtmfDutyT
644 ->SetLabel(wxString::Format(wxT("%.1f %%"), dtmfSettings.dtmfDutyCycle));
645 mDtmfDutyT->SetName(mDtmfDutyT->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
646
647 mDtmfSilenceT
648 ->SetLabel(wxString::Format(_("%.0f ms"), dtmfSettings.dtmfTone * 1000.0));
649 mDtmfSilenceT->SetName(mDtmfSilenceT->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
650
651 mDtmfToneT
652 ->SetLabel(wxString::Format(_("%.0f ms"), dtmfSettings.dtmfSilence * 1000.0));
653 mDtmfToneT->SetName(mDtmfToneT->GetLabel()); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
654}
655
656void EffectDtmf::Validator::OnSequence(wxCommandEvent & WXUNUSED(evt))
657{
658 mAccess.ModifySettings([this](EffectSettings &settings){
659 auto &dtmfSettings = mSettings;
660 dtmfSettings.dtmfSequence = mDtmfSequenceT->GetValue();
661 dtmfSettings.Recalculate(settings);
662 });
663 DoUpdateUI();
664}
665
666void EffectDtmf::Validator::OnDuration(wxCommandEvent & WXUNUSED(evt))
667{
668 mAccess.ModifySettings([this](EffectSettings &settings){
669 auto &dtmfSettings = mSettings;
670 auto &effect = GetEffect();
671 settings.extra.SetDuration(mDtmfDurationT->GetValue());
672 dtmfSettings.Recalculate(settings);
673 });
674 DoUpdateUI();
675}
676
677void EffectDtmf::Validator::OnDutyCycle(wxCommandEvent & evt)
678{
679 mAccess.ModifySettings([this, &evt](EffectSettings &settings){
680 auto &dtmfSettings = mSettings;
681 dtmfSettings.dtmfDutyCycle = (double) evt.GetInt() / DutyCycle.scale;
682 dtmfSettings.Recalculate(settings);
683 });
684 DoUpdateUI();
685}
#define M_PI
Definition: Distortion.cpp:29
static const wxChar * kSymbols[]
Definition: DtmfGen.cpp:56
static const double kFadeInOut
Definition: DtmfGen.cpp:54
EffectType
@ EffectTypeGenerate
enum ChannelName * ChannelNames
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
#define safenew
Definition: MemoryX.h:10
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:23
#define S(N)
Definition: ToChars.cpp:64
#define A(N)
Definition: ToChars.cpp:62
static Settings & settings()
Definition: TrackInfo.cpp:87
Generates EffectParameterMethods overrides from variadic template arguments.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
double mProjectRate
Definition: EffectBase.h:99
double mT0
Definition: EffectBase.h:106
An effect that generates DTMF tones.
Definition: DtmfGen.h:43
EffectDtmf()
Definition: DtmfGen.cpp:90
const EffectParameterMethods & Parameters() const override
Definition: DtmfGen.cpp:36
virtual ~EffectDtmf()
Definition: DtmfGen.cpp:94
TranslatableString GetDescription() const override
Definition: DtmfGen.cpp:105
static constexpr EffectParameter Amplitude
Definition: DtmfGen.h:88
static const ComponentInterfaceSymbol Symbol
Definition: DtmfGen.h:45
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
Definition: DtmfGen.cpp:122
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
Definition: DtmfGen.cpp:110
EffectType GetType() const override
Type determines how it behaves.
Definition: DtmfGen.cpp:117
std::shared_ptr< EffectInstance > MakeInstance() const override
Make an object maintaining short-term state of an Effect.
Definition: DtmfGen.cpp:420
std::unique_ptr< EffectUIValidator > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access) override
Add controls to effect panel; always succeeds.
Definition: DtmfGen.cpp:410
static bool MakeDtmfTone(float *buffer, size_t len, float fs, wxChar tone, sampleCount last, sampleCount total, float amplitude)
Definition: DtmfGen.cpp:512
static constexpr EffectParameter Sequence
Definition: DtmfGen.h:84
ComponentInterfaceSymbol GetSymbol() const override
Definition: DtmfGen.cpp:100
static constexpr EffectParameter DutyCycle
Definition: DtmfGen.h:86
Base class for many of the effects in Audacity.
Definition: Effect.h:29
int MessageBox(const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={}) const
Definition: Effect.cpp:871
Performs effect computation.
Inherit to add a state variable to an EffectInstance subclass.
Interface for manipulations of an Effect's settings.
virtual const EffectSettings & Get()=0
EffectUIClientInterface is an abstract base class to populate a UI and validate UI values....
Interface for transferring values from a panel of effect controls.
EffectUIClientInterface & mEffect
static DtmfSettings & GetSettings(EffectSettings &settings)
Assume settings originated from MakeSettings() and copies thereof.
Definition: Effect.h:296
Abstract base class used in importing a file.
const PerTrackEffect & mProcessor
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:628
Holds a msgid for the translation catalog; may also bind format arguments.
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
BuiltinEffectsModule::Registration< EffectDtmf > reg
Definition: DtmfGen.cpp:88
void Recalculate(EffectSettings &settings)
Definition: DtmfGen.cpp:464
Temporary state of the computation.
Definition: DtmfGen.cpp:131
sampleCount numSamplesTone
Definition: DtmfGen.cpp:147
sampleCount numSamplesSequence
Definition: DtmfGen.cpp:146
sampleCount curTonePos
Definition: DtmfGen.cpp:151
sampleCount diff
Definition: DtmfGen.cpp:149
sampleCount numSamplesSilence
Definition: DtmfGen.cpp:148
Instance(const PerTrackEffect &effect, double t0)
Definition: DtmfGen.cpp:132
sampleCount numRemaining
Definition: DtmfGen.cpp:150
const double mT0
Definition: DtmfGen.cpp:143
size_t ProcessBlock(EffectSettings &settings, const float *const *inBlock, float *const *outBlock, size_t blockLen) override
Called for destructive effect computation.
Definition: DtmfGen.cpp:214
bool ProcessInitialize(EffectSettings &settings, double sampleRate, sampleCount totalLen, ChannelNames chanMap) override
Definition: DtmfGen.cpp:156
wxStaticText * mDtmfToneT
Definition: DtmfGen.cpp:315
void PopulateOrExchange(ShuttleGui &S, const EffectSettings &settings, double projectRate)
Definition: DtmfGen.cpp:320
wxStaticText * mDtmfSilenceT
Definition: DtmfGen.cpp:316
Validator(EffectUIClientInterface &effect, EffectSettingsAccess &access, const DtmfSettings &settings)
Definition: DtmfGen.cpp:289
wxStaticText * mDtmfDutyT
Definition: DtmfGen.cpp:317
wxTextCtrl * mDtmfSequenceT
Definition: DtmfGen.cpp:312
NumericTextCtrl * mDtmfDurationT
Definition: DtmfGen.cpp:314
void OnSequence(wxCommandEvent &evt)
Definition: DtmfGen.cpp:656
bool ValidateUI() override
Get settings data from the panel; may make error dialogs and return false.
Definition: DtmfGen.cpp:444
virtual ~Validator()=default
wxSlider * mDtmfDutyCycleS
Definition: DtmfGen.cpp:313
bool UpdateUI() override
Update appearance of the panel for changes in settings.
Definition: DtmfGen.cpp:427
DtmfSettings mSettings
Definition: DtmfGen.cpp:310
Effect & GetEffect() const
Definition: DtmfGen.cpp:297
void OnDuration(wxCommandEvent &evt)
Definition: DtmfGen.cpp:666
void OnDutyCycle(wxCommandEvent &evt)
Definition: DtmfGen.cpp:677
const Type scale
Scaling factor, for slider control.
Definition: Shuttle.h:32
const Type min
Minimum value.
Definition: Shuttle.h:30
const Type max
Maximum value.
Definition: Shuttle.h:31
Externalized state of a plug-in.
Options & AutoPos(bool enable)