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