Audacity  2.2.2
ToneGen.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ToneGen.cpp
6 
7  Steve Jolly
8  James Crook (Adapted for 'Chirps')
9 
10  This class implements a tone generator effect.
11 
12 *******************************************************************//*******************************************************************/
20 
21 #include "../Audacity.h"
22 #include "ToneGen.h"
23 
24 #include <math.h>
25 #include <float.h>
26 
27 #include <wx/intl.h>
28 #include <wx/valgen.h>
29 
30 #include "../Project.h"
31 #include "../ShuttleGui.h"
32 #include "../widgets/NumericTextCtrl.h"
33 #include "../widgets/valnum.h"
34 
36 {
40 };
41 
43 {
44  // These are acceptable dual purpose internal/visible names
45  { XO("Linear") },
46  { XO("Logarithmic") }
47 };
48 
50 {
56 };
57 
59 {
60  { XO("Sine") },
61  { XO("Square") },
62  { XO("Sawtooth") },
63  { wxT("SquareNoAlias"), XO("Square, no alias") }
64 };
65 
66 // Define keys, defaults, minimums, and maximums for the effect parameters
67 //
68 // Name Type Key Def Min Max Scale
69 Param( StartFreq, double, wxT("StartFreq"), 440.0, 1.0, DBL_MAX, 1 );
70 Param( EndFreq, double, wxT("EndFreq"), 1320.0, 1.0, DBL_MAX, 1 );
71 Param( StartAmp, double, wxT("StartAmp"), 0.8, 0.0, 1.0, 1 );
72 Param( EndAmp, double, wxT("EndAmp"), 0.1, 0.0, 1.0, 1 );
73 Param( Frequency, double, wxT("Frequency"), 440.0, 1.0, DBL_MAX, 1 );
74 Param( Amplitude, double, wxT("Amplitude"), 0.8, 0.0, 1.0, 1 );
75 Param( Waveform, int, wxT("Waveform"), 0, 0, nWaveforms - 1, 1 );
76 Param( Interp, int, wxT("Interpolation"), 0, 0, nInterpolations - 1, 1 );
77 
78 //
79 // EffectToneGen
80 //
81 
82 BEGIN_EVENT_TABLE(EffectToneGen, wxEvtHandler)
83  EVT_TEXT(wxID_ANY, EffectToneGen::OnControlUpdate)
85 
87 {
88  wxASSERT(nWaveforms == WXSIZEOF(kWaveStrings));
89  wxASSERT(nInterpolations == WXSIZEOF(kInterStrings));
90 
91  mChirp = isChirp;
92 
93  mWaveform = DEF_Waveform;
94  mFrequency[0] = DEF_StartFreq;
95  mFrequency[1] = DEF_EndFreq;
96  mAmplitude[0] = DEF_StartAmp;
97  mAmplitude[1] = DEF_EndAmp;
98  mInterpolation = DEF_Interp;
99 
100  // Chirp varies over time so must use selected duration.
101  // TODO: When previewing, calculate only the first 'preview length'.
102  if (isChirp)
103  SetLinearEffectFlag(false);
104  else
105  SetLinearEffectFlag(true);
106 }
107 
109 {
110 }
111 
112 // IdentInterface implementation
113 
115 {
116  return mChirp
119 }
120 
122 {
123  return mChirp
124  ? _("Generates an ascending or descending tone of one of four types")
125  : _("Generates a constant frequency tone of one of four types");
126 }
127 
129 {
130  return mChirp
131  ? wxT("Chirp")
132  : wxT("Tone");
133 }
134 
135 // EffectDefinitionInterface implementation
136 
138 {
139  return EffectTypeGenerate;
140 }
141 
142 // EffectClientInterface implementation
143 
145 {
146  return 1;
147 }
148 
149 bool EffectToneGen::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
150 {
151  mPositionInCycles = 0.0;
152  mSample = 0;
153 
154  return true;
155 }
156 
157 size_t EffectToneGen::ProcessBlock(float **WXUNUSED(inBlock), float **outBlock, size_t blockLen)
158 {
159  float *buffer = outBlock[0];
160  double throwaway = 0; //passed to modf but never used
161  double f = 0.0;
162  double a, b;
163  int k;
164 
165  double frequencyQuantum;
166  double BlendedFrequency;
167  double BlendedAmplitude;
168  double BlendedLogFrequency = 0.0;
169 
170  // calculate delta, and reposition from where we left
171  auto doubleSampleCount = mSampleCnt.as_double();
172  auto doubleSample = mSample.as_double();
173  double amplitudeQuantum =
174  (mAmplitude[1] - mAmplitude[0]) / doubleSampleCount;
175  BlendedAmplitude = mAmplitude[0] +
176  amplitudeQuantum * doubleSample;
177 
178  // precalculations:
179  double pre2PI = 2.0 * M_PI;
180  double pre4divPI = 4.0 / M_PI;
181 
182  // initial setup should calculate deltas
184  {
185  // this for log interpolation
186  mLogFrequency[0] = log10(mFrequency[0]);
187  mLogFrequency[1] = log10(mFrequency[1]);
188  // calculate delta, and reposition from where we left
189  frequencyQuantum = (mLogFrequency[1] - mLogFrequency[0]) / doubleSampleCount;
190  BlendedLogFrequency = mLogFrequency[0] + frequencyQuantum * doubleSample;
191  BlendedFrequency = pow(10.0, BlendedLogFrequency);
192  }
193  else
194  {
195  // this for regular case, linear interpolation
196  frequencyQuantum = (mFrequency[1] - mFrequency[0]) / doubleSampleCount;
197  BlendedFrequency = mFrequency[0] + frequencyQuantum * doubleSample;
198  }
199 
200  // synth loop
201  for (decltype(blockLen) i = 0; i < blockLen; i++)
202  {
203  switch (mWaveform)
204  {
205  case kSine:
206  f = sin(pre2PI * mPositionInCycles / mSampleRate);
207  break;
208  case kSquare:
209  f = (modf(mPositionInCycles / mSampleRate, &throwaway) < 0.5) ? 1.0 : -1.0;
210  break;
211  case kSawtooth:
212  f = (2.0 * modf(mPositionInCycles / mSampleRate + 0.5, &throwaway)) - 1.0;
213  break;
214  case kSquareNoAlias: // Good down to 110Hz @ 44100Hz sampling.
215  //do fundamental (k=1) outside loop
216  b = (1.0 + cos((pre2PI * BlendedFrequency) / mSampleRate)) / pre4divPI; //scaling
217  f = pre4divPI * sin(pre2PI * mPositionInCycles / mSampleRate);
218  for (k = 3; (k < 200) && (k * BlendedFrequency < mSampleRate / 2.0); k += 2)
219  {
220  //Hanning Window in freq domain
221  a = 1.0 + cos((pre2PI * k * BlendedFrequency) / mSampleRate);
222  //calc harmonic, apply window, scale to amplitude of fundamental
223  f += a * sin(pre2PI * mPositionInCycles / mSampleRate * k) / (b * k);
224  }
225  }
226  // insert value in buffer
227  buffer[i] = (float) (BlendedAmplitude * f);
228  // update freq,amplitude
229  mPositionInCycles += BlendedFrequency;
230  BlendedAmplitude += amplitudeQuantum;
232  {
233  BlendedLogFrequency += frequencyQuantum;
234  BlendedFrequency = pow(10.0, BlendedLogFrequency);
235  }
236  else
237  {
238  BlendedFrequency += frequencyQuantum;
239  }
240  }
241 
242  // update external placeholder
243  mSample += blockLen;
244 
245  return blockLen;
246 }
247 
249  if( mChirp ){
250  S.SHUTTLE_PARAM( mFrequency[0], StartFreq );
251  S.SHUTTLE_PARAM( mFrequency[1], EndFreq );
252  S.SHUTTLE_PARAM( mAmplitude[0], StartAmp );
253  S.SHUTTLE_PARAM( mAmplitude[1], EndAmp );
254  } else {
255  S.SHUTTLE_PARAM( mFrequency[0], Frequency );
256  S.SHUTTLE_PARAM( mAmplitude[0], Amplitude );
257  // Slightly hacky way to set freq and ampl
258  // since we do this whatever query to params was made.
259  mFrequency[1] = mFrequency[0];
260  mAmplitude[1] = mAmplitude[0];
261  }
262  S.SHUTTLE_ENUM_PARAM( mWaveform, Waveform, kWaveStrings, nWaveforms );
263  S.SHUTTLE_ENUM_PARAM( mInterpolation, Interp, kInterStrings, nInterpolations );
264 
265 
266 // double freqMax = (GetActiveProject() ? GetActiveProject()->GetRate() : 44100.0) / 2.0;
267 // mFrequency[1] = TrapDouble(mFrequency[1], MIN_EndFreq, freqMax);
268 
269 
270  return true;
271 }
272 
274 {
275  if (mChirp)
276  {
277  parms.Write(KEY_StartFreq, mFrequency[0]);
278  parms.Write(KEY_EndFreq, mFrequency[1]);
279  parms.Write(KEY_StartAmp, mAmplitude[0]);
280  parms.Write(KEY_EndAmp, mAmplitude[1]);
281  }
282  else
283  {
284  parms.Write(KEY_Frequency, mFrequency[0]);
285  parms.Write(KEY_Amplitude, mAmplitude[0]);
286  }
287 
288  parms.Write(KEY_Waveform, kWaveStrings[mWaveform].Internal());
289  parms.Write(KEY_Interp, kInterStrings[mInterpolation].Internal());
290 
291  return true;
292 }
293 
295 {
296  ReadAndVerifyEnum(Waveform, kWaveStrings, nWaveforms);
297  ReadAndVerifyEnum(Interp, kInterStrings, nInterpolations);
298  if (mChirp)
299  {
300  ReadAndVerifyDouble(StartFreq);
301  ReadAndVerifyDouble(EndFreq);
302  ReadAndVerifyDouble(StartAmp);
303  ReadAndVerifyDouble(EndAmp);
304  mFrequency[0] = StartFreq;
305  mFrequency[1] = EndFreq;
306  mAmplitude[0] = StartAmp;
307  mAmplitude[1] = EndAmp;
308  }
309  else
310  {
311  ReadAndVerifyDouble(Frequency);
312  ReadAndVerifyDouble(Amplitude);
313  mFrequency[0] = Frequency;
314  mFrequency[1] = Frequency;
315  mAmplitude[0] = Amplitude;
316  mAmplitude[1] = Amplitude;
317  }
318 
319  mWaveform = Waveform;
320  mInterpolation = Interp;
321 
322  double freqMax = (GetActiveProject() ? GetActiveProject()->GetRate() : 44100.0) / 2.0;
323  mFrequency[1] = TrapDouble(mFrequency[1], MIN_EndFreq, freqMax);
324 
325  return true;
326 }
327 
328 // Effect implementation
329 
331 {
332  wxTextCtrl *t;
333 
334  S.StartMultiColumn(2, wxCENTER);
335  {
336  auto waveforms = LocalizedStrings(kWaveStrings, nWaveforms);
337  wxChoice *c = S.AddChoice(_("Waveform:"), wxT(""), &waveforms);
338  c->SetValidator(wxGenericValidator(&mWaveform));
339 
340  if (mChirp)
341  {
342  S.AddFixedText( {} );
343  S.StartHorizontalLay(wxEXPAND);
344  {
345  S.StartHorizontalLay(wxLEFT, 50);
346  {
347  S.AddTitle(_("Start"));
348  }
349  S.EndHorizontalLay();
350 
351  S.StartHorizontalLay(wxLEFT, 50);
352  {
353  S.AddTitle(_("End"));
354  }
355  S.EndHorizontalLay();
356  }
357  S.EndHorizontalLay();
358 
359  S.AddPrompt(_("Frequency (Hz):"));
360  S.StartHorizontalLay(wxEXPAND);
361  {
362  S.StartHorizontalLay(wxLEFT, 50);
363  {
364  FloatingPointValidator<double> vldStartFreq(6, &mFrequency[0], NumValidatorStyle::NO_TRAILING_ZEROES);
365  vldStartFreq.SetRange(MIN_StartFreq, GetActiveProject()->GetRate() / 2.0);
366  t = S.AddTextBox( {}, wxT(""), 12);
367  t->SetName(_("Frequency Hertz Start"));
368  t->SetValidator(vldStartFreq);
369  }
370  S.EndHorizontalLay();
371 
372  S.StartHorizontalLay(wxLEFT, 50);
373  {
374  FloatingPointValidator<double> vldEndFreq(6, &mFrequency[1], NumValidatorStyle::NO_TRAILING_ZEROES);
375  vldEndFreq.SetRange(MIN_EndFreq, GetActiveProject()->GetRate() / 2.0);
376  t = S.AddTextBox( {}, wxT(""), 12);
377  t->SetName(_("Frequency Hertz End"));
378  t->SetValidator(vldEndFreq);
379  }
380  S.EndHorizontalLay();
381  }
382  S.EndHorizontalLay();
383 
384  S.AddPrompt(_("Amplitude (0-1):"));
385  S.StartHorizontalLay(wxEXPAND);
386  {
387  S.StartHorizontalLay(wxLEFT, 50);
388  {
389  FloatingPointValidator<double> vldStartAmp(6, &mAmplitude[0], NumValidatorStyle::NO_TRAILING_ZEROES);
390  vldStartAmp.SetRange(MIN_StartAmp, MAX_StartAmp);
391  t = S.AddTextBox( {}, wxT(""), 12);
392  t->SetName(_("Amplitude Start"));
393  t->SetValidator(vldStartAmp);
394  }
395  S.EndHorizontalLay();
396 
397  S.StartHorizontalLay(wxLEFT, 50);
398  {
399  FloatingPointValidator<double> vldEndAmp(6, &mAmplitude[1], NumValidatorStyle::NO_TRAILING_ZEROES);
400  vldEndAmp.SetRange(MIN_EndAmp, MAX_EndAmp);
401  t = S.AddTextBox( {}, wxT(""), 12);
402  t->SetName(_("Amplitude End"));
403  t->SetValidator(vldEndAmp);
404  }
405  S.EndHorizontalLay();
406  }
407  S.EndHorizontalLay();
408 
409  auto interpolations = LocalizedStrings(kInterStrings, nInterpolations);
410  c = S.AddChoice(_("Interpolation:"), wxT(""), &interpolations);
411  c->SetValidator(wxGenericValidator(&mInterpolation));
412  }
413  else
414  {
415  FloatingPointValidator<double> vldFrequency(6, &mFrequency[0], NumValidatorStyle::NO_TRAILING_ZEROES);
416  vldFrequency.SetRange(MIN_Frequency, GetActiveProject()->GetRate() / 2.0);
417  t = S.AddTextBox(_("Frequency (Hz):"), wxT(""), 12);
418  t->SetValidator(vldFrequency);
419 
420  FloatingPointValidator<double> vldAmplitude(6, &mAmplitude[0], NumValidatorStyle::NO_TRAILING_ZEROES);
421  vldAmplitude.SetRange(MIN_Amplitude, MAX_Amplitude);
422  t = S.AddTextBox(_("Amplitude (0-1):"), wxT(""), 12);
423  t->SetValidator(vldAmplitude);
424  }
425 
426  S.AddPrompt(_("Duration:"));
428  NumericTextCtrl(S.GetParent(), wxID_ANY,
431  GetDuration(),
432  mProjectRate,
434  .AutoPos(true));
435  mToneDurationT->SetName(_("Duration"));
436  S.AddWindow(mToneDurationT, wxALIGN_LEFT | wxALL);
437  }
438  S.EndMultiColumn();
439 
440  return;
441 }
442 
444 {
445  if (!mUIParent->TransferDataToWindow())
446  {
447  return false;
448  }
449 
451 
452  return true;
453 }
454 
456 {
457  if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
458  {
459  return false;
460  }
461 
462  if (!mChirp)
463  {
464  mFrequency[1] = mFrequency[0];
465  mAmplitude[1] = mAmplitude[0];
466  }
467 
469 
470  return true;
471 }
472 
473 // EffectToneGen implementation
474 
475 void EffectToneGen::OnControlUpdate(wxCommandEvent & WXUNUSED(evt))
476 {
477  if (!EnableApply(mUIParent->TransferDataFromWindow()))
478  {
479  return;
480  }
481 }
int mInterpolation
Definition: ToneGen.h:75
double mLogFrequency[2]
Definition: ToneGen.h:78
static const IdentInterfaceSymbol kInterStrings[nInterpolations]
Definition: ToneGen.cpp:42
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
double GetDuration() override
Definition: Effect.cpp:722
void PopulateOrExchange(ShuttleGui &S) override
Definition: ToneGen.cpp:330
bool TransferDataToWindow() override
Definition: ToneGen.cpp:443
wxWindow * AddWindow(wxWindow *pWindow, int Flags=wxALIGN_CENTRE|wxALL)
Definition: ShuttleGui.cpp:288
double mFrequency[2]
Definition: ToneGen.h:76
wxArrayString LocalizedStrings(const IdentInterfaceSymbol strings[], size_t nStrings)
Definition: Internat.cpp:303
void EndMultiColumn()
wxString ManualPage() override
Definition: ToneGen.cpp:128
bool GetAutomationParameters(CommandParameters &parms) override
Definition: ToneGen.cpp:273
double as_double() const
Definition: Types.h:88
#define XO(s)
Definition: Internat.h:33
size_t ProcessBlock(float **inBlock, float **outBlock, size_t blockLen) override
Definition: ToneGen.cpp:157
NumericFormatId GetDurationFormat() override
Definition: Effect.cpp:732
#define TONE_PLUGIN_SYMBOL
Definition: ToneGen.h:26
Shuttle that deals with parameters. This is a base class with lots of virtual functions that do nothi...
Definition: Shuttle.h:60
double mSampleRate
Definition: Effect.h:455
double mPositionInCycles
Definition: ToneGen.h:70
unsigned GetAudioOutCount() override
Definition: ToneGen.cpp:144
#define safenew
Definition: Audacity.h:230
sampleCount mSample
Definition: ToneGen.h:69
void EndHorizontalLay()
void AddPrompt(const wxString &Prompt)
Right aligned text string.
Definition: ShuttleGui.cpp:239
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:540
#define ReadAndVerifyEnum(name, list, listSize)
Definition: Effect.h:786
double mAmplitude[2]
Definition: ToneGen.h:77
bool TransferDataFromWindow() override
Definition: ToneGen.cpp:455
wxWindow * GetParent()
Definition: ShuttleGui.h:294
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
wxString GetDescription() override
Definition: ToneGen.cpp:121
wxChoice * AddChoice(const wxString &Prompt, const wxString &Selected, const wxArrayString *pChoices)
Definition: ShuttleGui.cpp:371
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the Shuttle cla...
kInterpolations
#define CHIRP_PLUGIN_SYMBOL
Definition: ToneGen.h:25
#define ReadAndVerifyDouble(name)
Definition: Effect.h:798
void AddFixedText(const wxString &Str, bool bCenter=false)
Definition: ShuttleGui.cpp:397
void SetDuration(double duration) override
Definition: Effect.cpp:742
void AddTitle(const wxString &Prompt)
Centred text string.
Definition: ShuttleGui.cpp:274
IdentInterfaceSymbol pairs a persistent string identifier used internally with an optional...
enum ChannelName * ChannelNames
void SetValue(double newValue)
NumericTextCtrl * mToneDurationT
Definition: ToneGen.h:80
EffectType GetType() override
Definition: ToneGen.cpp:137
wxWindow * mUIParent
Definition: Effect.h:472
double TrapDouble(double x, double min, double max)
Definition: Effect.h:729
double mProjectRate
Definition: Effect.h:453
bool mChirp
Definition: ToneGen.h:65
bool SetAutomationParameters(CommandParameters &parms) override
Definition: ToneGen.cpp:294
void OnControlUpdate(wxCommandEvent &evt)
Definition: ToneGen.cpp:475
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
kWaveforms
Definition: ToneGen.cpp:49
EffectType
bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap=NULL) override
Definition: ToneGen.cpp:149
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
#define M_PI
Definition: Distortion.cpp:28
bool DefineParams(ShuttleParams &S) override
Definition: ToneGen.cpp:248
virtual ~EffectToneGen()
Definition: ToneGen.cpp:108
Param(StartFreq, double, wxT("StartFreq"), 440.0, 1.0, DBL_MAX, 1)
END_EVENT_TABLE()
sampleCount mSampleCnt
Definition: Effect.h:475
Options & AutoPos(bool value)
double GetRate() const
Definition: Project.h:199
static const IdentInterfaceSymbol kWaveStrings[nWaveforms]
Definition: ToneGen.cpp:58
int mWaveform
Definition: ToneGen.h:74
IdentInterfaceSymbol GetSymbol() override
Definition: ToneGen.cpp:114
virtual bool EnableApply(bool enable=true)
Definition: Effect.cpp:1886
An Effect that can generate a sine, square or sawtooth wave. An extended mode of EffectToneGen suppor...
Definition: ToneGen.h:28