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