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