Audacity  2.2.2
Nyquist.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Nyquist.cpp
6 
7  Dominic Mazzoni
8 
9 ******************************************************************//****************************************************************//****************************************************************//*******************************************************************/
26 
27 #include "../../Audacity.h"
28 
29 #include <algorithm>
30 #include <cmath>
31 
32 #include <locale.h>
33 
34 #include <wx/checkbox.h>
35 #include <wx/choice.h>
36 #include <wx/datetime.h>
37 #include <wx/intl.h>
38 #include <wx/log.h>
39 #include <wx/sstream.h>
40 #include <wx/textdlg.h>
41 #include <wx/txtstrm.h>
42 #include <wx/valgen.h>
43 #include <wx/wfstream.h>
44 #include <wx/numformatter.h>
45 
46 #include "../../AudacityApp.h"
47 #include "../../FileException.h"
48 #include "../../FileNames.h"
49 #include "../../Internat.h"
50 #include "../../LabelTrack.h"
51 #include "../../prefs/SpectrogramSettings.h"
52 #include "../../Project.h"
53 #include "../../ShuttleGui.h"
54 #include "../../WaveClip.h"
55 #include "../../WaveTrack.h"
56 #include "../../widgets/valnum.h"
57 #include "../../widgets/ErrorDialog.h"
58 #include "../../Prefs.h"
59 #include "../../prefs/WaveformSettings.h"
60 
61 #include "FileDialog.h"
62 
63 #include "Nyquist.h"
64 
65 #ifndef nyx_returns_start_and_end_time
66 #error You need to update lib-src/libnyquist
67 #endif
68 
69 #include <locale.h>
70 #include <iostream>
71 #include <ostream>
72 #include <sstream>
73 #include <float.h>
74 
75 #include "../../Experimental.h"
76 
77 enum
78 {
79  ID_Editor = 10000,
83 
84  ID_Slider = 11000,
85  ID_Text = 12000,
86  ID_Choice = 13000
87 };
88 
89 // Protect Nyquist from selections greater than 2^31 samples (bug 439)
90 #define NYQ_MAX_LEN (std::numeric_limits<long>::max())
91 
92 #define UNINITIALIZED_CONTROL ((double)99999999.99)
93 
94 static const wxChar *KEY_Version = wxT("Version");
95 static const wxChar *KEY_Command = wxT("Command");
96 
98 //
99 // NyquistEffect
100 //
102 
103 #include <wx/arrimpl.cpp>
104 WX_DEFINE_OBJARRAY(NyqControlArray);
105 
106 BEGIN_EVENT_TABLE(NyquistEffect, wxEvtHandler)
109 
110  EVT_COMMAND_RANGE(ID_Slider, ID_Slider+99,
111  wxEVT_COMMAND_SLIDER_UPDATED, NyquistEffect::OnSlider)
114  EVT_COMMAND_RANGE(ID_Choice, ID_Choice + 99,
115  wxEVT_COMMAND_CHOICE_SELECTED, NyquistEffect::OnChoice)
117 
118 NyquistEffect::NyquistEffect(const wxString &fName)
119 {
120  mOutputTrack[0] = mOutputTrack[1] = nullptr;
121 
122  mAction = _("Applying Nyquist Effect...");
123  mInputCmd = wxEmptyString;
124  mCmd = wxEmptyString;
125  mIsPrompt = false;
126  mExternal = false;
127  mCompiler = false;
128  mTrace = false;
129  mRedirectOutput = false;
130  mDebug = false;
131  mIsSal = false;
132  mOK = false;
133  mAuthor = XO("n/a");
134  mCopyright = XO("n/a");
135 
136  // set clip/split handling when applying over clip boundary.
137  mRestoreSplits = true; // Default: Restore split lines.
138  mMergeClips = -1; // Default (auto): Merge if length remains unchanged.
139 
140  mVersion = 4;
141 
142  mStop = false;
143  mBreak = false;
144  mCont = false;
145 
146  mMaxLen = NYQ_MAX_LEN;
147 
148  // Interactive Nyquist
149  if (fName == NYQUIST_PROMPT_ID) {
150  /* i18n-hint: "Nyquist" is an embedded interpreted programming language in
151  Audacity, named in honor of the Swedish-American Harry Nyquist (or Nyqvist).
152  In the translations of this and other strings, you may transliterate the
153  name into another alphabet. */
154  mName = XO("Nyquist Prompt");
155  mType = EffectTypeProcess;
156  mOK = true;
157  mIsPrompt = true;
158 
159  return;
160  }
161 
162  if (fName == NYQUIST_WORKER_ID) {
163  // Effect spawned from Nyquist Prompt
164 /* i18n-hint: It is acceptable to translate this the same as for "Nyquist Prompt" */
165  mName = XO("Nyquist Worker");
166  return;
167  }
168 
169  mFileName = fName;
170  mName = mFileName.GetName();
171  mFileModified = mFileName.GetModificationTime();
172  ParseFile();
173 
174  if (!mOK && mInitError.empty())
175  mInitError = _("Ill-formed Nyquist plug-in header");
176 }
177 
179 {
180 }
181 
182 // IdentInterface implementation
183 
185 {
186  if (mIsPrompt)
187  {
188  return NYQUIST_PROMPT_ID;
189  }
190 
191  return mFileName.GetFullPath();
192 }
193 
195 {
196  if (mIsPrompt)
197  {
198  return XO("Nyquist Prompt");
199  }
200 
201  return mName;
202 }
203 
205 {
206  return GetSymbol();
207 }
208 
210 {
211  if (mIsPrompt)
212  {
213  return _("Audacity");
214  }
215 
216  return mAuthor;
217 }
218 
220 {
221  return XO("n/a");
222 }
223 
225 {
226  return mCopyright;
227 }
228 
230 {
231  return mIsPrompt
232  ? wxT("Nyquist_Prompt")
233  : mManPage;
234 }
235 
237 {
238  wxArrayString paths = NyquistEffect::GetNyquistSearchPath();
239  wxString fileName;
240 
241  for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++) {
242  fileName = wxFileName(paths[i] + wxT("/") + mHelpFile).GetFullPath();
243  if (wxFileExists(fileName)) {
244  mHelpFileExists = true;
245  return fileName;
246  }
247  }
248  return wxEmptyString;
249 }
250 
251 // EffectIdentInterface implementation
252 
254 {
255  return mType;
256 }
257 
259 {
260  return NYQUISTEFFECTS_FAMILY;
261 }
262 
264 {
265  if (mIsPrompt)
266  {
267  return true;
268  }
269 
270  return mControls.GetCount() != 0;
271 }
272 
274 {
275  return mIsPrompt;
276 }
277 
278 // EffectClientInterface implementation
279 
280 bool NyquistEffect::GetAutomationParameters(EffectAutomationParameters & parms)
281 {
282  if (mExternal)
283  {
284  return true;
285  }
286 
287  if (mIsPrompt)
288  {
289  parms.Write(KEY_Command, mInputCmd);
290  parms.Write(KEY_Version, mVersion);
291 
292  return true;
293  }
294 
295  for (size_t c = 0, cnt = mControls.GetCount(); c < cnt; c++)
296  {
297  NyqControl & ctrl = mControls[c];
298  double d = ctrl.val;
299 
300  if (d == UNINITIALIZED_CONTROL && ctrl.type != NYQ_CTRL_STRING)
301  {
302  d = GetCtrlValue(ctrl.valStr);
303  }
304 
305  if (ctrl.type == NYQ_CTRL_REAL || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
306  {
307  parms.Write(ctrl.var, d);
308  }
309  else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_INT_TEXT)
310  {
311  parms.Write(ctrl.var, (int) d);
312  }
313  else if (ctrl.type == NYQ_CTRL_CHOICE)
314  {
315  wxArrayString choices = ParseChoice(ctrl);
316  parms.WriteEnum(ctrl.var, (int) d, choices);
317  }
318  else if (ctrl.type == NYQ_CTRL_STRING)
319  {
320  parms.Write(ctrl.var, ctrl.valStr);
321  }
322  }
323 
324  return true;
325 }
326 
327 bool NyquistEffect::SetAutomationParameters(EffectAutomationParameters & parms)
328 {
329  if (mExternal)
330  {
331  return true;
332  }
333 
334  if (mIsPrompt)
335  {
336  parms.Read(KEY_Command, &mInputCmd, wxEmptyString);
337  parms.Read(KEY_Version, &mVersion, mVersion);
338 
339  return true;
340  }
341 
342  // First pass verifies values
343  for (size_t c = 0, cnt = mControls.GetCount(); c < cnt; c++)
344  {
345  NyqControl & ctrl = mControls[c];
346  bool good = false;
347 
348  if (ctrl.type == NYQ_CTRL_REAL || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
349  {
350  double val;
351  good = parms.Read(ctrl.var, &val) &&
352  val >= ctrl.low &&
353  val <= ctrl.high;
354  }
355  else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_INT_TEXT)
356  {
357  int val;
358  good = parms.Read(ctrl.var, &val) &&
359  val >= ctrl.low &&
360  val <= ctrl.high;
361  }
362  else if (ctrl.type == NYQ_CTRL_CHOICE)
363  {
364  int val;
365  wxArrayString choices = ParseChoice(ctrl);
366  good = parms.ReadEnum(ctrl.var, &val, choices) &&
367  val != wxNOT_FOUND;
368  }
369  else if (ctrl.type == NYQ_CTRL_STRING)
370  {
371  wxString val;
372  good = parms.Read(ctrl.var, &val);
373  }
374 
375  if (!good)
376  {
377  return false;
378  }
379  }
380 
381  // Second pass sets the variables
382  for (size_t c = 0, cnt = mControls.GetCount(); c < cnt; c++)
383  {
384  NyqControl & ctrl = mControls[c];
385 
386  double d = ctrl.val;
387  if (d == UNINITIALIZED_CONTROL && ctrl.type != NYQ_CTRL_STRING)
388  {
389  d = GetCtrlValue(ctrl.valStr);
390  }
391 
392  if (ctrl.type == NYQ_CTRL_REAL || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
393  {
394  parms.Read(ctrl.var, &ctrl.val);
395  }
396  else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_INT_TEXT)
397  {
398  int val;
399  parms.Read(ctrl.var, &val);
400  ctrl.val = (double) val;
401  }
402  else if (ctrl.type == NYQ_CTRL_CHOICE)
403  {
404  int val {0};
405  wxArrayString choices = ParseChoice(ctrl);
406  parms.ReadEnum(ctrl.var, &val, choices);
407  ctrl.val = (double) val;
408  }
409  else if (ctrl.type == NYQ_CTRL_STRING)
410  {
411  parms.Read(ctrl.var, &ctrl.valStr);
412  }
413  }
414 
415  return true;
416 }
417 
418 // Effect Implementation
419 
421 {
422  // TODO: Document: Init() is called each time the effect is called but
423  // AFTER the UI (if any) has been created, so headers that affect the UI
424  // are only initialised at the start of the session.
425 
426  // EffectType may not be defined in script, so
427  // reset each time we call the Nyquist Prompt.
428  if (mIsPrompt) {
429  mType = EffectTypeProcess;
430  mName = XO("Nyquist Prompt");
431  mDebugButton = true; // Debug button always enabled for Nyquist Prompt.
432  mEnablePreview = true; // Preview button always enabled for Nyquist Prompt.
433  }
434 
435  // As of Audacity 2.1.2 rc1, 'spectral' effects are allowed only if
436  // the selected track(s) are in a spectrogram view, and there is at
437  // least one frequency bound and Spectral Selection is enabled for the
438  // selected track(s) - (but don't apply to Nyquist Prompt).
439 
440  if (!mIsPrompt && mIsSpectral) {
441  AudacityProject *project = GetActiveProject();
442  bool bAllowSpectralEditing = true;
443 
445  for (WaveTrack *t = (WaveTrack *) sel.First(); t; t = (WaveTrack *) sel.Next()) {
446  if (t->GetDisplay() != WaveTrack::Spectrum ||
447  !(t->GetSpectrogramSettings().SpectralSelectionEnabled())) {
448  bAllowSpectralEditing = false;
449  break;
450  }
451  }
452 
453  if (!bAllowSpectralEditing || ((mF0 < 0.0) && (mF1 < 0.0))) {
454  Effect::MessageBox(_("To use 'Spectral effects', enable 'Spectral Selection'\n"
455  "in the track Spectrogram settings and select the\n"
456  "frequency range for the effect to act on."),
457  wxOK | wxICON_EXCLAMATION | wxCENTRE, _("Error"));
458 
459  return false;
460  }
461  }
462 
463  if (!mIsPrompt && !mExternal)
464  {
465  //TODO: If we want to auto-add parameters from spectral selection,
466  //we will need to modify this test.
467  //Note that removing it stops the caching of parameter values,
468  //(during this session).
469  if (mFileName.GetModificationTime().IsLaterThan(mFileModified))
470  {
472 
474  ParseFile();
475  mFileModified = mFileName.GetModificationTime();
476 
478  }
479  }
480 
481  return true;
482 }
483 
485 {
486  // If we're a prompt and we have controls, then we've already processed
487  // the audio, so skip further processing.
488  return (mIsPrompt && mControls.GetCount() > 0);
489 }
490 
492 {
493  bool success = true;
494  mProjectChanged = false;
496  em.SetSkipStateFlag(false);
497 
498  if (mExternal) {
499  mProgress->Hide();
500  }
501 
502  // We must copy all the tracks, because Paste needs label tracks to ensure
503  // correct sync-lock group behavior when the timeline is affected; then we just want
504  // to operate on the selected wave tracks
507  mCurTrack[0] = (WaveTrack *) iter.First();
508  mOutputTime = 0;
509  mCount = 0;
510  mProgressIn = 0;
511  mProgressOut = 0;
512  mProgressTot = 0;
513  mScale = (GetType() == EffectTypeProcess ? 0.5 : 1.0) / GetNumWaveGroups();
514 
515  mStop = false;
516  mBreak = false;
517  mCont = false;
518 
519  mTrackIndex = 0;
520 
523  for (WaveTrack *t = (WaveTrack *) sel.First(); t; t = (WaveTrack *) sel.Next()) {
525  if (mT1 >= mT0) {
526  if (t->GetLinked()) {
528  sel.Next();
529  }
530  }
531  }
532 
533  mDebugOutput.Clear();
534  if (!mHelpFile.IsEmpty() && !mHelpFileExists) {
535  mDebugOutput = wxString::Format(_("error: File \"%s\" specified in header but not found in plug-in path.\n"), mHelpFile);
536  }
537 
538  if (mVersion >= 4)
539  {
540  AudacityProject *project = GetActiveProject();
541 
542  mProps = wxEmptyString;
543 
544  mProps += wxString::Format(wxT("(putprop '*AUDACITY* (list %d %d %d) 'VERSION)\n"), AUDACITY_VERSION, AUDACITY_RELEASE, AUDACITY_REVISION);
545 
546  mProps += wxString::Format(wxT("(setf *DECIMAL-SEPARATOR* #\\%c)\n"), wxNumberFormatter::GetDecimalSeparator());
547 
548  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'BASE)\n"), EscapeString(FileNames::BaseDir()));
549  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'DATA)\n"), EscapeString(FileNames::DataDir()));
550  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'HELP)\n"), EscapeString(FileNames::HtmlHelpDir().RemoveLast()));
551  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'TEMP)\n"), EscapeString(FileNames::TempDir()));
552 
553  wxArrayString paths = NyquistEffect::GetNyquistSearchPath();
554  wxString list;
555  for (size_t i = 0, cnt = paths.GetCount(); i < cnt; i++)
556  {
557  list += wxT("\"") + EscapeString(paths[i]) + wxT("\" ");
558  }
559  list = list.RemoveLast();
560 
561  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* (list %s) 'PLUGIN)\n"), list);
562  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* (list %s) 'PLUG-IN)\n"), list);
563 
564 
565  // Date and time:
566  wxDateTime now = wxDateTime::Now();
567  int year = now.GetYear();
568  int doy = now.GetDayOfYear();
569  int dom = now.GetDay();
570  // enumerated constants
571  wxDateTime::Month month = now.GetMonth();
572  wxDateTime::WeekDay day = now.GetWeekDay();
573 
574  // Date/time as a list: year, day of year, hour, minute, seconds
575  mProps += wxString::Format(wxT("(setf *SYSTEM-TIME* (list %d %d %d %d %d))\n"),
576  year, doy, now.GetHour(), now.GetMinute(), now.GetSecond());
577 
578  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'DATE)\n"), now.FormatDate());
579  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'TIME)\n"), now.FormatTime());
580  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'ISO-DATE)\n"), now.FormatISODate());
581  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'ISO-TIME)\n"), now.FormatISOTime());
582  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* %d 'YEAR)\n"), year);
583  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* %d 'DAY)\n"), dom); // day of month
584  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* %d 'MONTH)\n"), month);
585  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'MONTH-NAME)\n"), now.GetMonthName(month));
586  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'DAY-NAME)\n"), now.GetWeekDayName(day));
587 
588  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'PROJECTS)\n"), (int) gAudacityProjects.size());
589  mProps += wxString::Format(wxT("(putprop '*PROJECT* \"%s\" 'NAME)\n"), project->GetName());
590 
591  TrackListIterator all(project->GetTracks());
592  Track *t;
593  int numTracks = 0;
594  int numWave = 0;
595  int numLabel = 0;
596  int numMidi = 0;
597  int numTime = 0;
598  wxString waveTrackList = wxT(""); // track positions of selected audio tracks.
599 
600  for (t = all.First(); t; t = all.Next())
601  {
602  switch (t->GetKind())
603  {
604  case Track::Wave:
605  numWave++;
606  if (t->GetSelected()) {
607  waveTrackList += wxString::Format(wxT("%d "), 1 + numTracks);
608  }
609  break;
610  case Track::Label: numLabel++; break;
611 #if defined(USE_MIDI)
612  case Track::Note: numMidi++; break;
613 #endif
614  case Track::Time: numTime++; break;
615  default: break;
616  }
617 
618  numTracks++;
619  if (t->GetLinked())
620  {
621  all.Next();
622  }
623  }
624 
625  // We use Internat::ToString() rather than "%g" here because we
626  // always have to use the dot as decimal separator when giving
627  // numbers to Nyquist, whereas using "%g" will use the user's
628  // decimal separator which may be a comma in some countries.
629  mProps += wxString::Format(wxT("(putprop '*PROJECT* (float %s) 'RATE)\n"),
630  Internat::ToString(project->GetRate()));
631  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'TRACKS)\n"), numTracks);
632  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'WAVETRACKS)\n"), numWave);
633  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'LABELTRACKS)\n"), numLabel);
634  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'MIDITRACKS)\n"), numMidi);
635  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'TIMETRACKS)\n"), numTime);
636 
637  double previewLen = 6.0;
638  gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLen);
639  mProps += wxString::Format(wxT("(putprop '*PROJECT* (float %s) 'PREVIEW-DURATION)\n"),
640  Internat::ToString(previewLen));
641 
642  // *PREVIEWP* is true when previewing (better than relying on track view).
643  wxString isPreviewing = (this->IsPreviewing())? wxT("T") : wxT("NIL");
644  mProps += wxString::Format(wxT("(setf *PREVIEWP* %s)\n"), isPreviewing);
645 
646  mProps += wxString::Format(wxT("(putprop '*SELECTION* (float %s) 'START)\n"),
648  mProps += wxString::Format(wxT("(putprop '*SELECTION* (float %s) 'END)\n"),
650  mProps += wxString::Format(wxT("(putprop '*SELECTION* (list %s) 'TRACKS)\n"), waveTrackList);
651  mProps += wxString::Format(wxT("(putprop '*SELECTION* %d 'CHANNELS)\n"), mNumSelectedChannels);
652  }
653 
654  // Keep track of whether the current track is first selected in its sync-lock group
655  // (we have no idea what the length of the returned audio will be, so we have
656  // to handle sync-lock group behavior the "old" way).
657  mFirstInGroup = true;
658  Track *gtLast = NULL;
659 
660  while (mCurTrack[0]) {
661  mCurNumChannels = 1;
662  if (mT1 >= mT0) {
663  if (mCurTrack[0]->GetLinked()) {
664  mCurNumChannels = 2;
665 
666  mCurTrack[1] = (WaveTrack *)iter.Next();
667  if (mCurTrack[1]->GetRate() != mCurTrack[0]->GetRate()) {
668  Effect::MessageBox(_("Sorry, cannot apply effect on stereo tracks where the tracks don't match."),
669  wxOK | wxCENTRE);
670  success = false;
671  goto finish;
672  }
673  mCurStart[1] = mCurTrack[1]->TimeToLongSamples(mT0);
674  }
675 
676  // Check whether we're in the same group as the last selected track
678  Track *gt = gIter.StartWith(mCurTrack[0]);
679  mFirstInGroup = !gtLast || (gtLast != gt);
680  gtLast = gt;
681 
682  mCurStart[0] = mCurTrack[0]->TimeToLongSamples(mT0);
683  auto end = mCurTrack[0]->TimeToLongSamples(mT1);
684  mCurLen = end - mCurStart[0];
685 
686  if (mCurLen > NYQ_MAX_LEN) {
687  float hours = (float)NYQ_MAX_LEN / (44100 * 60 * 60);
688  const auto message = wxString::Format(
689 _("Selection too long for Nyquist code.\nMaximum allowed selection is %ld samples\n(about %.1f hours at 44100 Hz sample rate)."),
690  (long)NYQ_MAX_LEN, hours
691  );
692  Effect::MessageBox(message, wxOK | wxCENTRE, _("Nyquist Error"));
693  if (!mProjectChanged)
694  em.SetSkipStateFlag(true);
695  return false;
696  }
697 
699 
700  mProgressIn = 0.0;
701  mProgressOut = 0.0;
702 
703  // libnyquist breaks except in LC_NUMERIC=="C".
704  //
705  // Note that we must set the locale to "C" even before calling
706  // nyx_init() because otherwise some effects will not work!
707  //
708  // MB: setlocale is not thread-safe. Should use uselocale()
709  // if available, or fix libnyquist to be locale-independent.
710  // See also http://bugzilla.audacityteam.org/show_bug.cgi?id=642#c9
711  // for further info about this thread safety question.
712  wxString prevlocale = wxSetlocale(LC_NUMERIC, NULL);
713  wxSetlocale(LC_NUMERIC, wxString(wxT("C")));
714 
715  nyx_init();
716  nyx_set_os_callback(StaticOSCallback, (void *)this);
717  nyx_capture_output(StaticOutputCallback, (void *)this);
718 
719  auto cleanup = finally( [&] {
720  nyx_capture_output(NULL, (void *)NULL);
721  nyx_set_os_callback(NULL, (void *)NULL);
722  nyx_cleanup();
723  } );
724 
725 
726  if (mVersion >= 4)
727  {
728  mPerTrackProps = wxEmptyString;
729  wxString lowHz = wxT("nil");
730  wxString highHz = wxT("nil");
731  wxString centerHz = wxT("nil");
732  wxString bandwidth = wxT("nil");
733 
734 #if defined(EXPERIMENTAL_SPECTRAL_EDITING)
735  if (mF0 >= 0.0) {
736  lowHz.Printf(wxT("(float %s)"), Internat::ToString(mF0));
737  }
738 
739  if (mF1 >= 0.0) {
740  highHz.Printf(wxT("(float %s)"), Internat::ToString(mF1));
741  }
742 
743  if ((mF0 >= 0.0) && (mF1 >= 0.0)) {
744  centerHz.Printf(wxT("(float %s)"), Internat::ToString(sqrt(mF0 * mF1)));
745  }
746 
747  if ((mF0 > 0.0) && (mF1 >= mF0)) {
748  // with very small values, bandwidth calculation may be inf.
749  // (Observed on Linux)
750  double bw = log(mF1 / mF0) / log(2.0);
751  if (!std::isinf(bw)) {
752  bandwidth.Printf(wxT("(float %s)"), Internat::ToString(bw));
753  }
754  }
755 
756 #endif
757  mPerTrackProps += wxString::Format(wxT("(putprop '*SELECTION* %s 'LOW-HZ)\n"), lowHz);
758  mPerTrackProps += wxString::Format(wxT("(putprop '*SELECTION* %s 'CENTER-HZ)\n"), centerHz);
759  mPerTrackProps += wxString::Format(wxT("(putprop '*SELECTION* %s 'HIGH-HZ)\n"), highHz);
760  mPerTrackProps += wxString::Format(wxT("(putprop '*SELECTION* %s 'BANDWIDTH)\n"), bandwidth);
761  }
762 
763  success = ProcessOne();
764 
765  // Reset previous locale
766  wxSetlocale(LC_NUMERIC, prevlocale);
767 
768  if (!success) {
769  goto finish;
770  }
772  }
773 
774  mCurTrack[0] = (WaveTrack *) iter.Next();
776  }
777 
778  if (mOutputTime > 0.0) {
779  mT1 = mT0 + mOutputTime;
780  }
781 
782  finish:
783 
784  // Show debug window if trace set in plug-in header and something to show.
785  mDebug = (mTrace && !mDebugOutput.IsEmpty())? true : mDebug;
786 
787  if (mDebug && !mRedirectOutput) {
789  mName,
790  _("Debug Output: "),
791  mDebugOutput);
792  dlog.CentreOnParent();
793  dlog.ShowModal();
794  }
795 
796  ReplaceProcessedTracks(success);
797 
798  if (!mProjectChanged)
799  em.SetSkipStateFlag(true);
800 
801  return success;
802 }
803 
804 bool NyquistEffect::ShowInterface(wxWindow *parent, bool forceModal)
805 {
806  // Show the normal (prompt or effect) interface
807  bool res = Effect::ShowInterface(parent, forceModal);
808 
809  // Remember if the user clicked debug
810  mDebug = (mUIResultID == eDebugID);
811 
812  // We're done if the user clicked "Close", we are not the Nyquist Prompt,
813  // or the program currently loaded into the prompt doesn't have a UI.
814  if (!res || !mIsPrompt || mControls.GetCount() == 0)
815  {
816  return res;
817  }
818 
820 
821  effect.SetCommand(mInputCmd);
822  effect.mDebug = (mUIResultID == eDebugID);
823 
824  SelectedRegion region(mT0, mT1);
825 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
826  region.setF0(mF0);
827  region.setF1(mF1);
828 #endif
829  return Delegate(effect, parent, &region, true);
830 }
831 
833 {
834  if (mIsPrompt)
835  {
837  }
838  else
839  {
841  }
842 
844 }
845 
847 {
848  mUIParent->TransferDataToWindow();
849 
850  bool success;
851  if (mIsPrompt)
852  {
853  success = TransferDataToPromptWindow();
854  }
855  else
856  {
857  success = TransferDataToEffectWindow();
858  }
859 
860  if (success)
861  {
863  }
864 
865  return success;
866 }
867 
869 {
870  if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
871  {
872  return false;
873  }
874 
875  if (mIsPrompt)
876  {
878  }
879 
881 }
882 
883 // NyquistEffect implementation
884 
886 {
887  mError = false;
888  mFailedFileName.Clear();
889 
890  nyx_rval rval;
891 
892  wxString cmd;
893 
894  // TODO: Document.
895  // Nyquist default latency is 300 ms, which is rather conservative and
896  // too long when playback set to ALSA (bug 570), so we'll use 100 ms like Audacity.
897  cmd += wxT("(snd-set-latency 0.1)");
898 
899  if (mVersion >= 4) {
900  nyx_set_audio_name("*TRACK*");
901  cmd += wxT("(setf S 0.25)\n");
902  }
903  else {
904  nyx_set_audio_name("S");
905  cmd += wxT("(setf *TRACK* '*unbound*)\n");
906  }
907 
908  if (mVersion >= 4) {
909  cmd += mProps;
910  cmd += mPerTrackProps;
911 
912  // Set the track TYPE and VIEW properties
913  wxString type;
914  wxString view;
915  wxString bitFormat;
916  wxString spectralEditp;
917 
918  switch (mCurTrack[0]->GetKind())
919  {
920  case Track::Wave:
921  type = wxT("wave");
922  spectralEditp = mCurTrack[0]->GetSpectrogramSettings().SpectralSelectionEnabled()? wxT("T") : wxT("NIL");
923  switch (((WaveTrack *) mCurTrack[0])->GetDisplay())
924  {
925  case WaveTrack::Waveform:
926  view = (mCurTrack[0]->GetWaveformSettings().scaleType == 0) ? wxT("\"Waveform\"") : wxT("\"Waveform (dB)\"");
927  break;
928  case WaveTrack::Spectrum:
929  view = wxT("\"Spectrogram\"");
930  break;
931  default: view = wxT("NIL"); break;
932  }
933  break;
934 #if defined(USE_MIDI)
935  case Track::Note:
936  type = wxT("midi");
937  view = wxT("\"Midi\"");
938  break;
939 #endif
940  case Track::Label:
941  type = wxT("label");
942  view = wxT("\"Label\"");
943  break;
944  case Track::Time:
945  type = wxT("time");
946  view = wxT("\"Time\"");
947  break;
948  }
949 
950  cmd += wxString::Format(wxT("(putprop '*TRACK* %d 'INDEX)\n"), ++mTrackIndex);
951  cmd += wxString::Format(wxT("(putprop '*TRACK* \"%s\" 'NAME)\n"), mCurTrack[0]->GetName());
952  cmd += wxString::Format(wxT("(putprop '*TRACK* \"%s\" 'TYPE)\n"), type);
953  // Note: "View" property may change when Audacity's choice of track views has stabilized.
954  cmd += wxString::Format(wxT("(putprop '*TRACK* %s 'VIEW)\n"), view);
955  cmd += wxString::Format(wxT("(putprop '*TRACK* %d 'CHANNELS)\n"), mCurNumChannels);
956 
957  //NOTE: Audacity 2.1.3 True if spectral selection is enabled regardless of track view.
958  cmd += wxString::Format(wxT("(putprop '*TRACK* %s 'SPECTRAL-EDIT-ENABLED)\n"), spectralEditp);
959 
960  double startTime = 0.0;
961  double endTime = 0.0;
962 
963  if (mCurTrack[0]->GetLinked()) {
964  startTime = std::min<double>(mCurTrack[0]->GetStartTime(), mCurTrack[0]->GetLink()->GetStartTime());
965  }
966  else {
967  startTime = mCurTrack[0]->GetStartTime();
968  }
969 
970  if (mCurTrack[0]->GetLinked()) {
971  endTime = std::max<double>(mCurTrack[0]->GetEndTime(), mCurTrack[0]->GetLink()->GetEndTime());
972  }
973  else {
974  endTime = mCurTrack[0]->GetEndTime();
975  }
976 
977  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'START-TIME)\n"),
978  Internat::ToString(startTime));
979  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'END-TIME)\n"),
980  Internat::ToString(endTime));
981  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'GAIN)\n"),
982  Internat::ToString(mCurTrack[0]->GetGain()));
983  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'PAN)\n"),
984  Internat::ToString(mCurTrack[0]->GetPan()));
985  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'RATE)\n"),
986  Internat::ToString(mCurTrack[0]->GetRate()));
987 
988  switch (mCurTrack[0]->GetSampleFormat())
989  {
990  case int16Sample:
991  bitFormat = wxT("16");
992  break;
993  case int24Sample:
994  bitFormat = wxT("24");
995  break;
996  case floatSample:
997  bitFormat = wxT("32.0");
998  break;
999  }
1000  cmd += wxString::Format(wxT("(putprop '*TRACK* %s 'FORMAT)\n"), bitFormat);
1001 
1002  float maxPeakLevel = 0.0; // Deprecated as of 2.1.3
1003  wxString clips, peakString, rmsString;
1004  for (size_t i = 0; i < mCurNumChannels; i++) {
1005  auto ca = mCurTrack[i]->SortedClipArray();
1006  float maxPeak = 0.0;
1007 
1008  // A list of clips for mono, or an array of lists for multi-channel.
1009  if (mCurNumChannels > 1) {
1010  clips += wxT("(list ");
1011  }
1012  // Each clip is a list (start-time, end-time)
1013  for (const auto clip: ca) {
1014  clips += wxString::Format(wxT("(list (float %s) (float %s))"),
1015  Internat::ToString(clip->GetStartTime()),
1016  Internat::ToString(clip->GetEndTime()));
1017  }
1018  if (mCurNumChannels > 1) clips += wxT(" )");
1019 
1020  float min, max;
1021  auto pair = mCurTrack[i]->GetMinMax(mT0, mT1); // may throw
1022  min = pair.first, max = pair.second;
1023  maxPeak = wxMax(wxMax(fabs(min), fabs(max)), maxPeak);
1024  maxPeakLevel = wxMax(maxPeakLevel, maxPeak);
1025 
1026  // On Debian, NaN samples give maxPeak = 3.40282e+38 (FLT_MAX)
1027  if (!std::isinf(maxPeak) && !std::isnan(maxPeak) && (maxPeak < FLT_MAX)) {
1028  peakString += wxString::Format(wxT("(float %s) "), Internat::ToString(maxPeak));
1029  } else {
1030  peakString += wxT("nil ");
1031  }
1032 
1033  float rms = mCurTrack[i]->GetRMS(mT0, mT1); // may throw
1034  if (!std::isinf(rms) && !std::isnan(rms)) {
1035  rmsString += wxString::Format(wxT("(float %s) "), Internat::ToString(rms));
1036  } else {
1037  rmsString += wxT("nil ");
1038  }
1039  }
1040  // A list of clips for mono, or an array of lists for multi-channel.
1041  cmd += wxString::Format(wxT("(putprop '*TRACK* %s%s ) 'CLIPS)\n"),
1042  (mCurNumChannels == 1) ? wxT("(list ") : wxT("(vector "),
1043  clips);
1044 
1045  (mCurNumChannels > 1)?
1046  cmd += wxString::Format(wxT("(putprop '*SELECTION* (vector %s) 'PEAK)\n"), peakString) :
1047  cmd += wxString::Format(wxT("(putprop '*SELECTION* %s 'PEAK)\n"), peakString);
1048 
1049  if (!std::isinf(maxPeakLevel) && !std::isnan(maxPeakLevel) && (maxPeakLevel < FLT_MAX)) {
1050  cmd += wxString::Format(wxT("(putprop '*SELECTION* (float %s) 'PEAK-LEVEL)\n"),
1051  Internat::ToString(maxPeakLevel));
1052  }
1053 
1054  (mCurNumChannels > 1)?
1055  cmd += wxString::Format(wxT("(putprop '*SELECTION* (vector %s) 'RMS)\n"), rmsString) :
1056  cmd += wxString::Format(wxT("(putprop '*SELECTION* %s 'RMS)\n"), rmsString);
1057  }
1058 
1059  if (GetType() == EffectTypeGenerate) {
1060  nyx_set_audio_params(mCurTrack[0]->GetRate(), 0);
1061  }
1062  else {
1063  // UNSAFE_SAMPLE_COUNT_TRUNCATION
1064  // Danger! Truncation of long long to long!
1065  // Don't say we didn't warn you!
1066 
1067  // Note mCurLen was elsewhere limited to mMaxLen, which is normally
1068  // the greatest long value, and yet even mMaxLen may be experimentally
1069  // increased with a nyquist comment directive.
1070  // See the parsing of "maxlen"
1071 
1072  auto curLen = long(mCurLen.as_long_long());
1073  nyx_set_audio_params(mCurTrack[0]->GetRate(), curLen);
1074 
1075  nyx_set_input_audio(StaticGetCallback, (void *)this,
1076  (int)mCurNumChannels,
1077  curLen, mCurTrack[0]->GetRate());
1078  }
1079 
1080  // Restore the Nyquist sixteenth note symbol for Generate plug-ins.
1081  // See http://bugzilla.audacityteam.org/show_bug.cgi?id=490.
1082  if (GetType() == EffectTypeGenerate) {
1083  cmd += wxT("(setf s 0.25)\n");
1084  }
1085 
1086  if (mDebug || mTrace) {
1087  cmd += wxT("(setf *tracenable* T)\n");
1088  if (mExternal) {
1089  cmd += wxT("(setf *breakenable* T)\n");
1090  }
1091  }
1092  else {
1093  // Explicitly disable backtrace and prevent values
1094  // from being carried through to the output.
1095  // This should be the final command before evaluating the Nyquist script.
1096  cmd += wxT("(setf *tracenable* NIL)\n");
1097  }
1098 
1099  for (unsigned int j = 0; j < mControls.GetCount(); j++) {
1100  if (mControls[j].type == NYQ_CTRL_REAL || mControls[j].type == NYQ_CTRL_FLOAT_TEXT) {
1101  // We use Internat::ToString() rather than "%f" here because we
1102  // always have to use the dot as decimal separator when giving
1103  // numbers to Nyquist, whereas using "%f" will use the user's
1104  // decimal separator which may be a comma in some countries.
1105  cmd += wxString::Format(wxT("(setf %s %s)\n"),
1106  mControls[j].var,
1107  Internat::ToString(mControls[j].val, 14));
1108  }
1109  else if (mControls[j].type == NYQ_CTRL_INT ||
1110  mControls[j].type == NYQ_CTRL_INT_TEXT ||
1111  mControls[j].type == NYQ_CTRL_CHOICE) {
1112  cmd += wxString::Format(wxT("(setf %s %d)\n"),
1113  mControls[j].var,
1114  (int)(mControls[j].val));
1115  }
1116  else if (mControls[j].type == NYQ_CTRL_STRING) {
1117  cmd += wxT("(setf ");
1118  // restrict variable names to 7-bit ASCII:
1119  cmd += mControls[j].var;
1120  cmd += wxT(" \"");
1121  cmd += EscapeString(mControls[j].valStr); // unrestricted value will become quoted UTF-8
1122  cmd += wxT("\")\n");
1123  }
1124  }
1125 
1126  if (mIsSal) {
1127  wxString str = EscapeString(mCmd);
1128  // this is tricky: we need SAL to call main so that we can get a
1129  // SAL traceback in the event of an error (sal-compile catches the
1130  // error and calls sal-error-output), but SAL does not return values.
1131  // We will catch the value in a special global aud:result and if no
1132  // error occurs, we will grab the value with a LISP expression
1133  str += wxT("\nset aud:result = main()\n");
1134 
1135  if (mDebug || mTrace) {
1136  // since we're about to evaluate SAL, remove LISP trace enable and
1137  // break enable (which stops SAL processing) and turn on SAL stack
1138  // trace
1139  cmd += wxT("(setf *tracenable* nil)\n");
1140  cmd += wxT("(setf *breakenable* nil)\n");
1141  cmd += wxT("(setf *sal-traceback* t)\n");
1142  }
1143 
1144  if (mCompiler) {
1145  cmd += wxT("(setf *sal-compiler-debug* t)\n");
1146  }
1147 
1148  cmd += wxT("(setf *sal-call-stack* nil)\n");
1149  // if we do not set this here and an error occurs in main, another
1150  // error will be raised when we try to return the value of aud:result
1151  // which is unbound
1152  cmd += wxT("(setf aud:result nil)\n");
1153  cmd += wxT("(sal-compile-audacity \"") + str + wxT("\" t t nil)\n");
1154  // Capture the value returned by main (saved in aud:result), but
1155  // set aud:result to nil so sound results can be evaluated without
1156  // retaining audio in memory
1157  cmd += wxT("(prog1 aud:result (setf aud:result nil))\n");
1158  }
1159  else {
1160  cmd += mCmd;
1161  }
1162 
1163  // Put the fetch buffers in a clean initial state
1164  for (size_t i = 0; i < mCurNumChannels; i++)
1165  mCurBuffer[i].Free();
1166 
1167  // Guarantee release of memory when done
1168  auto cleanup = finally( [&] {
1169  for (size_t i = 0; i < mCurNumChannels; i++)
1170  mCurBuffer[i].Free();
1171  } );
1172 
1173  // Evaluate the expression, which may invoke the get callback, but often does
1174  // not, leaving that to delayed evaluation of the output sound
1175  rval = nyx_eval_expression(cmd.mb_str(wxConvUTF8));
1176 
1177  // If we're not showing debug window, log errors and warnings:
1178  if (!mDebugOutput.IsEmpty() && !mDebug && !mTrace) {
1179  /* i18n-hint: An effect "returned" a message.*/
1180  wxLogMessage(_("\'%s\' returned:\n%s"), mName, mDebugOutput);
1181  }
1182 
1183  // Audacity has no idea how long Nyquist processing will take, but
1184  // can monitor audio being returned.
1185  // Anything other than audio should be returned almost instantly
1186  // so notify the user that process has completed (bug 558)
1187  if ((rval != nyx_audio) && ((mCount + mCurNumChannels) == mNumSelectedChannels)) {
1188  if (mCurNumChannels == 1) {
1189  TrackProgress(mCount, 1.0, _("Processing complete."));
1190  }
1191  else {
1192  TrackGroupProgress(mCount, 1.0, _("Processing complete."));
1193  }
1194  }
1195 
1196  if (rval == nyx_error) {
1197  // Return value is not valid type.
1198  // Show error in debug window if trace enabled, otherwise log.
1199  if (mTrace) {
1200  /* i18n-hint: "%s" is replaced by name of plug-in.*/
1201  mDebugOutput = wxString::Format(_("nyx_error returned from %s.\n"),
1202  mName.IsEmpty()? _("plug-in") : mName) + mDebugOutput;
1203  mDebug = true;
1204  return false;
1205  }
1206  else {
1207  wxLogMessage(wxT("Nyquist returned nyx_error."));
1208  }
1209  return true;
1210  }
1211 
1212  if (rval == nyx_string) {
1213  wxString msg = NyquistToWxString(nyx_get_string());
1214  if (!msg.IsEmpty()) // Not currently a documented feature, but could be useful as a No-Op.
1215  Effect::MessageBox(msg);
1216 
1217  // True if not process type.
1218  // If not returning audio from process effect,
1219  // return first reult then stop (disables preview)
1220  // but allow all output from Nyquist Prompt.
1221  return (GetType() != EffectTypeProcess || mIsPrompt);
1222  }
1223 
1224  if (rval == nyx_double) {
1225  wxString str;
1226  str.Printf(_("Nyquist returned the value:") + wxString(wxT(" %f")),
1227  nyx_get_double());
1228  Effect::MessageBox(str);
1229  return (GetType() != EffectTypeProcess || mIsPrompt);
1230  }
1231 
1232  if (rval == nyx_int) {
1233  wxString str;
1234  str.Printf(_("Nyquist returned the value:") + wxString(wxT(" %d")),
1235  nyx_get_int());
1236  Effect::MessageBox(str);
1237  return (GetType() != EffectTypeProcess || mIsPrompt);
1238  }
1239 
1240  if (rval == nyx_labels) {
1241  mProjectChanged = true;
1242  unsigned int numLabels = nyx_get_num_labels();
1243  unsigned int l;
1244  LabelTrack *ltrack = NULL;
1245 
1246  TrackListIterator iter(mOutputTracks.get());
1247  for (Track *t = iter.First(); t; t = iter.Next()) {
1248  if (t->GetKind() == Track::Label) {
1249  ltrack = (LabelTrack *)t;
1250  break;
1251  }
1252  }
1253 
1254  if (!ltrack) {
1255  ltrack = static_cast<LabelTrack*>(AddToOutputTracks(mFactory->NewLabelTrack()));
1256  }
1257 
1258  for (l = 0; l < numLabels; l++) {
1259  double t0, t1;
1260  const char *str;
1261 
1262  // PRL: to do:
1263  // let Nyquist analyzers define more complicated selections
1264  nyx_get_label(l, &t0, &t1, &str);
1265 
1266  ltrack->AddLabel(SelectedRegion(t0 + mT0, t1 + mT0), UTF8CTOWX(str), -2);
1267  }
1268  return (GetType() != EffectTypeProcess || mIsPrompt);
1269  }
1270 
1271  wxASSERT(rval == nyx_audio);
1272 
1273  int outChannels = nyx_get_audio_num_channels();
1274  if (outChannels > (int)mCurNumChannels) {
1275  Effect::MessageBox(_("Nyquist returned too many audio channels.\n"));
1276  return false;
1277  }
1278 
1279  if (outChannels == -1) {
1280  Effect::MessageBox(_("Nyquist returned one audio channel as an array.\n"));
1281  return false;
1282  }
1283 
1284  if (outChannels == 0) {
1285  Effect::MessageBox(_("Nyquist returned an empty array.\n"));
1286  return false;
1287  }
1288 
1289  std::unique_ptr<WaveTrack> outputTrack[2];
1290 
1291  double rate = mCurTrack[0]->GetRate();
1292  for (int i = 0; i < outChannels; i++) {
1293  sampleFormat format = mCurTrack[i]->GetSampleFormat();
1294 
1295  if (outChannels == (int)mCurNumChannels) {
1296  rate = mCurTrack[i]->GetRate();
1297  }
1298 
1299  outputTrack[i] = mFactory->NewWaveTrack(format, rate);
1300  outputTrack[i]->SetWaveColorIndex( mCurTrack[i]->GetWaveColorIndex() );
1301 
1302  // Clean the initial buffer states again for the get callbacks
1303  // -- is this really needed?
1304  mCurBuffer[i].Free();
1305  }
1306 
1307  // Now fully evaluate the sound
1308  int success;
1309  {
1310  auto vr0 = valueRestorer( mOutputTrack[0], outputTrack[0].get() );
1311  auto vr1 = valueRestorer( mOutputTrack[1], outputTrack[1].get() );
1312  success = nyx_get_audio(StaticPutCallback, (void *)this);
1313  }
1314 
1315  // See if GetCallback found read errors
1316  if (mFailedFileName.IsOk())
1317  // re-construct an exception
1318  // I wish I had std::exception_ptr instead
1319  // and could re-throw any AudacityException
1320  throw FileException{
1322  else if (mError)
1323  // what, then?
1324  success = false;
1325 
1326  if (!success)
1327  return false;
1328 
1329  for (int i = 0; i < outChannels; i++) {
1330  outputTrack[i]->Flush();
1331  mOutputTime = outputTrack[i]->GetEndTime();
1332 
1333  if (mOutputTime <= 0) {
1334  Effect::MessageBox(_("Nyquist returned nil audio.\n"));
1335  return true;
1336  }
1337  }
1338 
1339  for (size_t i = 0; i < mCurNumChannels; i++) {
1340  WaveTrack *out;
1341 
1342  if (outChannels == (int)mCurNumChannels) {
1343  out = outputTrack[i].get();
1344  }
1345  else {
1346  out = outputTrack[0].get();
1347  }
1348 
1349  if (mMergeClips < 0) {
1350  // Use sample counts to determine default behaviour - times will rarely be equal.
1351  bool bMergeClips = (out->TimeToLongSamples(mT0) + out->TimeToLongSamples(mOutputTime) ==
1352  out->TimeToLongSamples(mT1));
1353  mCurTrack[i]->ClearAndPaste(mT0, mT1, out, mRestoreSplits, bMergeClips);
1354  }
1355  else {
1357  }
1358 
1359  // If we were first in the group adjust non-selected group tracks
1360  if (mFirstInGroup) {
1362  Track *t;
1363  for (t = git.StartWith(mCurTrack[i]); t; t = git.Next())
1364  {
1365  if (!t->GetSelected() && t->IsSyncLockSelected()) {
1366  t->SyncLockAdjust(mT1, mT0 + out->GetEndTime());
1367  }
1368  }
1369  }
1370 
1371  // Only the first channel can be first in its group
1372  mFirstInGroup = false;
1373  }
1374 
1375  mProjectChanged = true;
1376  return true;
1377 }
1378 
1379 // ============================================================================
1380 // NyquistEffect Implementation
1381 // ============================================================================
1382 
1383 wxString NyquistEffect::NyquistToWxString(const char *nyqString)
1384 {
1385  wxString str(nyqString, wxConvUTF8);
1386  if (nyqString != NULL && nyqString[0] && str.IsEmpty()) {
1387  // invalid UTF-8 string, convert as Latin-1
1388  str = _("[Warning: Nyquist returned invalid UTF-8 string, converted here as Latin-1]");
1389  // TODO: internationalization of strings from Nyquist effects, at least
1390  // from those shipped with Audacity
1391  str += LAT1CTOWX(nyqString);
1392  }
1393  return str;
1394 }
1395 
1396 wxString NyquistEffect::EscapeString(const wxString & inStr)
1397 {
1398  wxString str = inStr;
1399 
1400  str.Replace(wxT("\\"), wxT("\\\\"));
1401  str.Replace(wxT("\""), wxT("\\\""));
1402 
1403  return str;
1404 }
1405 
1406 wxArrayString NyquistEffect::ParseChoice(const NyqControl & ctrl)
1407 {
1408  wxArrayString choices = wxStringTokenize(ctrl.label, wxT(","));
1409 
1410  for (size_t i = 0, cnt = choices.GetCount();i < cnt; i++)
1411  {
1412  choices[i] = choices[i].Trim(true).Trim(false);
1413  }
1414 
1415  return choices;
1416 }
1417 
1419 {
1420  mRedirectOutput = true;
1421 }
1422 
1423 void NyquistEffect::SetCommand(const wxString &cmd)
1424 {
1425  mExternal = true;
1426 
1427  ParseCommand(cmd);
1428 }
1429 
1431 {
1432  mBreak = true;
1433 }
1434 
1436 {
1437  mCont = true;
1438 }
1439 
1441 {
1442  mStop = true;
1443 }
1444 
1445 wxString NyquistEffect::UnQuote(const wxString &s)
1446 {
1447  wxString out;
1448  int len = s.Length();
1449 
1450  if (len >= 2 && s[0] == wxT('\"') && s[len - 1] == wxT('\"')) {
1451  return s.Mid(1, len - 2);
1452  }
1453 
1454  return s;
1455 }
1456 
1457 double NyquistEffect::GetCtrlValue(const wxString &s)
1458 {
1459  /* For this to work correctly requires that the plug-in header is
1460  * parsed on each run so that the correct value for "half-srate" may
1461  * be determined.
1462  *
1463  AudacityProject *project = GetActiveProject();
1464  double rate = INT_MAX;
1465  if (project && s.IsSameAs(wxT("half-srate"), false)) {
1466  SelectedTrackListOfKindIterator sel(Track::Wave, project->GetTracks());
1467  for (WaveTrack *t = (WaveTrack *) sel.First(); t; t = (WaveTrack *) sel.Next()) {
1468  rate = std::min(t->GetRate(), rate);
1469  }
1470  return (rate / 2.0);
1471  }
1472  */
1473 
1474  return Internat::CompatibleToDouble(s);
1475 }
1476 
1477 void NyquistEffect::Parse(const wxString &line)
1478 {
1479  wxArrayString tokens;
1480 
1481  int i;
1482  int len = line.Length();
1483  bool sl = false;
1484  bool q = false;
1485  wxString tok = wxT("");
1486 
1487  for (i = 1; i < len; i++) {
1488  wxChar c = line[i];
1489 
1490  if (c == wxT('\\')) {
1491  sl = true;
1492  }
1493  else if (c == wxT('"')) {
1494  q = !q;
1495  }
1496  else {
1497  if ((!q && !sl && c == wxT(' ')) || c == wxT('\t')) {
1498  tokens.Add(tok);
1499  tok = wxT("");
1500  }
1501  else if (sl && c == wxT('n')) {
1502  tok += wxT('\n');
1503  }
1504  else {
1505  tok += c;
1506  }
1507 
1508  sl = false;
1509  }
1510  }
1511 
1512  if (tok != wxT("")) {
1513  tokens.Add(tok);
1514  }
1515 
1516  len = tokens.GetCount();
1517  if (len < 1) {
1518  return;
1519  }
1520 
1521  // Consistency decission is for "plug-in" as the correct spelling
1522  // "plugin" (deprecated) is allowed as an undocumented convenience.
1523  if (len == 2 && tokens[0] == wxT("nyquist") &&
1524  (tokens[1] == wxT("plug-in") || tokens[1] == wxT("plugin"))) {
1525  mOK = true;
1526  return;
1527  }
1528 
1529  if (len >= 2 && tokens[0] == wxT("type")) {
1530  if (tokens[1] == wxT("process")) {
1531  mType = EffectTypeProcess;
1532  }
1533  else if (tokens[1] == wxT("generate")) {
1534  mType = EffectTypeGenerate;
1535  }
1536  else if (tokens[1] == wxT("analyze")) {
1537  mType = EffectTypeAnalyze;
1538  }
1539  if (len >= 3 && tokens[2] == wxT("spectral")) {;
1540  mIsSpectral = true;
1541  }
1542  return;
1543  }
1544 
1545  if (len == 2 && tokens[0] == wxT("codetype")) {
1546  // This will stop ParseProgram() from doing a best guess as program type.
1547  if (tokens[1] == wxT("lisp")) {
1548  mIsSal = false;
1549  mFoundType = true;
1550  }
1551  else if (tokens[1] == wxT("sal")) {
1552  mIsSal = true;
1553  mFoundType = true;
1554  }
1555  return;
1556  }
1557 
1558  // TODO: Update documentation.
1559 
1560  if (len >= 2 && tokens[0] == wxT("debugflags")) {
1561  for (int i = 1; i < len; i++) {
1562  // "trace" sets *tracenable* (LISP) or *sal-traceback* (SAL)
1563  // and displays debug window IF there is anything to show.
1564  if (tokens[i] == wxT("trace")) {
1565  mTrace = true;
1566  }
1567  else if (tokens[i] == wxT("notrace")) {
1568  mTrace = false;
1569  }
1570  else if (tokens[i] == wxT("compiler")) {
1571  mCompiler = true;
1572  }
1573  else if (tokens[i] == wxT("nocompiler")) {
1574  mCompiler = false;
1575  }
1576  }
1577  return;
1578  }
1579 
1580  // We support versions 1, 2 and 3
1581  // (Version 2 added support for string parameters.)
1582  // (Version 3 added support for choice parameters.)
1583  // (Version 4 added support for project/track/selection information.)
1584  if (len >= 2 && tokens[0] == wxT("version")) {
1585  long v;
1586  tokens[1].ToLong(&v);
1587  if (v < 1 || v > 4) {
1588  // This is an unsupported plug-in version
1589  mOK = false;
1590  mInitError.Format(
1591  _("This version of Audacity does not support Nyquist plug-in version %ld"),
1592  v
1593  );
1594  return;
1595  }
1596  mVersion = (int) v;
1597  }
1598 
1599  if (len >= 2 && tokens[0] == wxT("name")) {
1600  mName = UnQuote(tokens[1]);
1601  if (mName.EndsWith(wxT("...")))
1602  {
1603  mName = mName.RemoveLast(3);
1604  }
1605  return;
1606  }
1607 
1608  if (len >= 2 && tokens[0] == wxT("action")) {
1609  mAction = UnQuote(tokens[1]);
1610  return;
1611  }
1612 
1613  if (len >= 2 && tokens[0] == wxT("info")) {
1614  mInfo = UnQuote(tokens[1]);
1615  return;
1616  }
1617 
1618  if (len >= 2 && tokens[0] == wxT("preview")) {
1619  if (tokens[1] == wxT("enabled") || tokens[1] == wxT("true")) {
1620  mEnablePreview = true;
1621  SetLinearEffectFlag(false);
1622  }
1623  else if (tokens[1] == wxT("linear")) {
1624  mEnablePreview = true;
1625  SetLinearEffectFlag(true);
1626  }
1627  else if (tokens[1] == wxT("selection")) {
1628  mEnablePreview = true;
1630  }
1631  else if (tokens[1] == wxT("disabled") || tokens[1] == wxT("false")) {
1632  mEnablePreview = false;
1633  }
1634  return;
1635  }
1636 
1637  // Maximum number of samples to be processed. This can help the
1638  // progress bar if effect does not process all of selection.
1639  if (len >= 2 && tokens[0] == wxT("maxlen")) {
1640  long long v; // Note that Nyquist may overflow at > 2^31 samples (bug 439)
1641  tokens[1].ToLongLong(&v);
1642  mMaxLen = (sampleCount) v;
1643  }
1644 
1645 #if defined(EXPERIMENTAL_NYQUIST_SPLIT_CONTROL)
1646  if (len >= 2 && tokens[0] == wxT("mergeclips")) {
1647  long v;
1648  // -1 = auto (default), 0 = don't merge clips, 1 = do merge clips
1649  tokens[1].ToLong(&v);
1650  mMergeClips = v;
1651  return;
1652  }
1653 
1654  if (len >= 2 && tokens[0] == wxT("restoresplits")) {
1655  long v;
1656  // Splits are restored by default. Set to 0 to prevent.
1657  tokens[1].ToLong(&v);
1658  mRestoreSplits = !!v;
1659  return;
1660  }
1661 #endif
1662 
1663  if (len >= 2 && tokens[0] == wxT("author")) {
1664  mAuthor = UnQuote(tokens[1]);
1665  return;
1666  }
1667 
1668  if (len >= 2 && tokens[0] == wxT("copyright")) {
1669  mCopyright = UnQuote(tokens[1]);
1670  return;
1671  }
1672 
1673  // TODO: Document.
1674  // Page name in Audacity development manual
1675  if (len >= 2 && tokens[0] == wxT("manpage")) {
1676  mManPage = UnQuote(tokens[1]);
1677  return;
1678  }
1679 
1680  // TODO: Document.
1681  // Local Help file
1682  if (len >= 2 && tokens[0] == wxT("helpfile")) {
1683  mHelpFile = UnQuote(tokens[1]);
1684  return;
1685  }
1686 
1687  // TODO: Document.
1688  // Debug button may be disabled for release plug-ins.
1689  if (len >= 2 && tokens[0] == wxT("debugbutton")) {
1690  if (tokens[1] == wxT("disabled") || tokens[1] == wxT("false")) {
1691  mDebugButton = false;
1692  }
1693  return;
1694  }
1695 
1696  if (len >= 6 && tokens[0] == wxT("control")) {
1697  NyqControl ctrl;
1698 
1699  ctrl.var = tokens[1];
1700  ctrl.name = tokens[2];
1701  ctrl.label = tokens[4];
1702  ctrl.valStr = tokens[5];
1703  ctrl.val = GetCtrlValue(ctrl.valStr);
1704 
1705  if (tokens[3] == wxT("string")) {
1706  ctrl.type = NYQ_CTRL_STRING;
1707  }
1708  else if (tokens[3] == wxT("choice")) {
1709  ctrl.type = NYQ_CTRL_CHOICE;
1710  }
1711  else {
1712  if (len < 8) {
1713  return;
1714  }
1715 
1716  if ((tokens[3] == wxT("float")) ||
1717  (tokens[3] == wxT("real"))) // Deprecated
1718  ctrl.type = NYQ_CTRL_REAL;
1719  else if (tokens[3] == wxT("int"))
1720  ctrl.type = NYQ_CTRL_INT;
1721  else if (tokens[3] == wxT("float-text"))
1722  ctrl.type = NYQ_CTRL_FLOAT_TEXT;
1723  else if (tokens[3] == wxT("int-text"))
1724  ctrl.type = NYQ_CTRL_INT_TEXT;
1725  else
1726  {
1727  wxString str;
1728  str.Printf(_("Bad Nyquist 'control' type specification: '%s' in plug-in file '%s'.\nControl not created."),
1729  tokens[3], mFileName.GetFullPath());
1730 
1731  // Too disturbing to show alert before Audacity frame is up.
1732  // Effect::MessageBox(str, wxT("Nyquist Warning"), wxOK | wxICON_EXCLAMATION);
1733 
1734  // Note that the AudacityApp's mLogger has not yet been created,
1735  // so this brings up an alert box, but after the Audacity frame is up.
1736  wxLogWarning(str);
1737  return;
1738  }
1739 
1740  ctrl.lowStr = tokens[6];
1741  if (ctrl.type == NYQ_CTRL_INT_TEXT && ctrl.lowStr.IsSameAs(wxT("nil"), false)) {
1742  ctrl.low = INT_MIN;
1743  }
1744  else if (ctrl.type == NYQ_CTRL_FLOAT_TEXT && ctrl.lowStr.IsSameAs(wxT("nil"), false)) {
1745  ctrl.low = -(FLT_MAX);
1746  }
1747  else {
1748  ctrl.low = GetCtrlValue(ctrl.lowStr);
1749  }
1750 
1751  ctrl.highStr = tokens[7];
1752  if (ctrl.type == NYQ_CTRL_INT_TEXT && ctrl.highStr.IsSameAs(wxT("nil"), false)) {
1753  ctrl.high = INT_MAX;
1754  }
1755  else if (ctrl.type == NYQ_CTRL_FLOAT_TEXT && ctrl.highStr.IsSameAs(wxT("nil"), false)) {
1756  ctrl.high = FLT_MAX;
1757  }
1758  else {
1759  ctrl.high = GetCtrlValue(ctrl.highStr);
1760  }
1761 
1762  if (ctrl.high < ctrl.low) {
1763  ctrl.high = ctrl.low;
1764  }
1765 
1766  if (ctrl.val < ctrl.low) {
1767  ctrl.val = ctrl.low;
1768  }
1769 
1770  if (ctrl.val > ctrl.high) {
1771  ctrl.val = ctrl.high;
1772  }
1773 
1774  ctrl.ticks = 1000;
1775  if (ctrl.type == NYQ_CTRL_INT &&
1776  (ctrl.high - ctrl.low < ctrl.ticks)) {
1777  ctrl.ticks = (int)(ctrl.high - ctrl.low);
1778  }
1779  }
1780 
1781  if( mPresetNames.Index( ctrl.var ) == wxNOT_FOUND )
1782  {
1783  mControls.Add(ctrl);
1784  }
1785  }
1786 
1787  if (len >= 2 && tokens[0] == wxT("categories")) {
1788  for (size_t i = 1; i < tokens.GetCount(); ++i) {
1789  mCategories.Add(tokens[i]);
1790  }
1791  }
1792 }
1793 
1794 bool NyquistEffect::ParseProgram(wxInputStream & stream)
1795 {
1796  if (!stream.IsOk())
1797  {
1798  mInitError = _("Could not open file");
1799  return false;
1800  }
1801 
1802  wxTextInputStream pgm(stream);
1803 
1804  mCmd = wxT("");
1805  mIsSal = false;
1806  mControls.Clear();
1807  mCategories.Clear();
1808  mIsSpectral = false;
1809  mManPage = wxEmptyString; // If not wxEmptyString, must be a page in the Audacity manual.
1810  mHelpFile = wxEmptyString; // If not wxEmptyString, must be a valid HTML help file.
1811  mHelpFileExists = false;
1812  mDebug = false;
1813  mTrace = false;
1814  mDebugButton = true; // Debug button enabled by default.
1815  mEnablePreview = true; // Preview button enabled by default.
1816 
1817  mFoundType = false;
1818  while (!stream.Eof() && stream.IsOk())
1819  {
1820  wxString line = pgm.ReadLine().Trim(false);
1821  if (line.Length() > 1 && line[0] == wxT(';'))
1822  {
1823  Parse(line);
1824  }
1825  else if (!mFoundType && line.Length() > 0)
1826  {
1827  if (line[0] == wxT('(') ||
1828  (line[0] == wxT('#') && line.Length() > 1 && line[1] == wxT('|')))
1829  {
1830  mIsSal = false;
1831  mFoundType = true;
1832  }
1833  else if (line.Upper().Find(wxT("RETURN")) != wxNOT_FOUND)
1834  {
1835  mIsSal = true;
1836  mFoundType = true;
1837  }
1838  }
1839  // preserve comments so that SAL effects compile with proper line numbers
1840  mCmd += line + wxT("\n");
1841  }
1842  if (!mFoundType && mIsPrompt)
1843  {
1844  /* i1n-hint: SAL and LISP are names for variant syntaxes for the
1845  Nyquist programming language. Leave them, and 'return', untranslated. */
1846  Effect::MessageBox(_("Your code looks like SAL syntax, but there is no \'return\' statement.\n\
1847 For SAL, use a return statement such as:\n\treturn *track* * 0.1\n\
1848 or for LISP, begin with an open parenthesis such as:\n\t(mult *track* 0.1)\n ."),
1850  _("Error in Nyquist code"));
1851  /* i18n-hint: refers to programming "languages" */
1852  mInitError = _("Could not determine language");
1853  return false;
1854  // Else just throw it at Nyquist to see what happens
1855  }
1856 
1857  return true;
1858 }
1859 
1861 {
1862  wxFileInputStream stream(mFileName.GetFullPath());
1863 
1864  ParseProgram(stream);
1865 }
1866 
1867 bool NyquistEffect::ParseCommand(const wxString & cmd)
1868 {
1869  wxStringInputStream stream(cmd + wxT(" "));
1870 
1871  return ParseProgram(stream);
1872 }
1873 
1874 int NyquistEffect::StaticGetCallback(float *buffer, int channel,
1875  long start, long len, long totlen,
1876  void *userdata)
1877 {
1878  NyquistEffect *This = (NyquistEffect *)userdata;
1879  return This->GetCallback(buffer, channel, start, len, totlen);
1880 }
1881 
1882 int NyquistEffect::GetCallback(float *buffer, int ch,
1883  long start, long len, long WXUNUSED(totlen))
1884 {
1885  if (mCurBuffer[ch].ptr()) {
1886  if ((mCurStart[ch] + start) < mCurBufferStart[ch] ||
1887  (mCurStart[ch] + start)+len >
1888  mCurBufferStart[ch]+mCurBufferLen[ch]) {
1889  mCurBuffer[ch].Free();
1890  }
1891  }
1892 
1893  if (!mCurBuffer[ch].ptr()) {
1894  mCurBufferStart[ch] = (mCurStart[ch] + start);
1896 
1897  if (mCurBufferLen[ch] < (size_t) len) {
1899  }
1900 
1901  mCurBufferLen[ch] =
1902  limitSampleBufferSize( mCurBufferLen[ch],
1903  mCurStart[ch] + mCurLen - mCurBufferStart[ch] );
1904 
1905  mCurBuffer[ch].Allocate(mCurBufferLen[ch], floatSample);
1906  try {
1907  mCurTrack[ch]->Get(
1908  mCurBuffer[ch].ptr(), floatSample,
1909  mCurBufferStart[ch], mCurBufferLen[ch]);
1910  }
1911  catch ( const FileException& e ) {
1912  if ( e.cause == FileException::Cause::Read )
1914  mError = true;
1915  return -1;
1916  }
1917  catch ( ... ) {
1918  mError = true;
1919  return -1;
1920  }
1921  }
1922 
1923  // We have guaranteed above that this is nonnegative and bounded by
1924  // mCurBufferLen[ch]:
1925  auto offset = ( mCurStart[ch] + start - mCurBufferStart[ch] ).as_size_t();
1926  CopySamples(mCurBuffer[ch].ptr() + offset*SAMPLE_SIZE(floatSample), floatSample,
1927  (samplePtr)buffer, floatSample,
1928  len);
1929 
1930  if (ch == 0) {
1931  double progress = mScale *
1932  ( (start+len)/ mCurLen.as_double() );
1933 
1934  if (progress > mProgressIn) {
1935  mProgressIn = progress;
1936  }
1937 
1939  return -1;
1940  }
1941  }
1942 
1943  return 0;
1944 }
1945 
1946 int NyquistEffect::StaticPutCallback(float *buffer, int channel,
1947  long start, long len, long totlen,
1948  void *userdata)
1949 {
1950  NyquistEffect *This = (NyquistEffect *)userdata;
1951  return This->PutCallback(buffer, channel, start, len, totlen);
1952 }
1953 
1954 int NyquistEffect::PutCallback(float *buffer, int channel,
1955  long start, long len, long totlen)
1956 {
1957  // Don't let C++ exceptions propagate through the Nyquist library
1958  return GuardedCall<int>( [&] {
1959  if (channel == 0) {
1960  double progress = mScale*((float)(start+len)/totlen);
1961 
1962  if (progress > mProgressOut) {
1963  mProgressOut = progress;
1964  }
1965 
1967  return -1;
1968  }
1969  }
1970 
1971  mOutputTrack[channel]->Append((samplePtr)buffer, floatSample, len);
1972 
1973  return 0; // success
1974  }, MakeSimpleGuard( -1 ) ); // translate all exceptions into failure
1975 }
1976 
1978 {
1979  ((NyquistEffect *)This)->OutputCallback(c);
1980 }
1981 
1983 {
1984  // Always collect Nyquist error messages for normal plug-ins
1985  if (!mRedirectOutput) {
1986  mDebugOutput += (char)c;
1987  return;
1988  }
1989 
1990  std::cout << (char)c;
1991 }
1992 
1994 {
1995  ((NyquistEffect *)This)->OSCallback();
1996 }
1997 
1999 {
2000  if (mStop) {
2001  mStop = false;
2002  nyx_stop();
2003  }
2004  else if (mBreak) {
2005  mBreak = false;
2006  nyx_break();
2007  }
2008  else if (mCont) {
2009  mCont = false;
2010  nyx_continue();
2011  }
2012 
2013  // LLL: STF figured out that yielding while the effect is being applied
2014  // produces an EXTREME slowdown. It appears that yielding is not
2015  // really necessary on Linux and Windows.
2016  //
2017  // However, on the Mac, the spinning cursor appears during longer
2018  // Nyquist processing and that may cause the user to think Audacity
2019  // has crashed or hung. In addition, yielding or not on the Mac
2020  // doesn't seem to make much of a difference in execution time.
2021  //
2022  // So, yielding on the Mac only...
2023 #if defined(__WXMAC__)
2024  wxYieldIfNeeded();
2025 #endif
2026 }
2027 
2029 {
2030  wxArrayString audacityPathList = wxGetApp().audacityPathList;
2031  wxArrayString pathList;
2032 
2033  for (size_t i = 0; i < audacityPathList.GetCount(); i++)
2034  {
2035  wxString prefix = audacityPathList[i] + wxFILE_SEP_PATH;
2036  wxGetApp().AddUniquePathToPathList(prefix + wxT("nyquist"), pathList);
2037  wxGetApp().AddUniquePathToPathList(prefix + wxT("plugins"), pathList);
2038  wxGetApp().AddUniquePathToPathList(prefix + wxT("plug-ins"), pathList);
2039  }
2040  pathList.Add(FileNames::PlugInDir());
2041 
2042  return pathList;
2043 }
2044 
2046 {
2047  mCommandText->ChangeValue(mInputCmd);
2048  mVersionCheckBox->SetValue(mVersion <= 3);
2049 
2050  return true;
2051 }
2052 
2054 {
2055  for (size_t i = 0, cnt = mControls.GetCount(); i < cnt; i++)
2056  {
2057  NyqControl & ctrl = mControls[i];
2058 
2059  if (ctrl.type == NYQ_CTRL_CHOICE)
2060  {
2061  wxArrayString choices = ParseChoice(ctrl);
2062 
2063  int val = (int)ctrl.val;
2064  if (val < 0 || val >= (int)choices.GetCount())
2065  {
2066  val = 0;
2067  }
2068 
2069  wxChoice *c = (wxChoice *) mUIParent->FindWindow(ID_Choice + i);
2070  c->SetSelection(val);
2071  }
2072  else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_REAL)
2073  {
2074  // wxTextCtrls are handled by the validators
2075  double range = ctrl.high - ctrl.low;
2076  int val = (int)(0.5 + ctrl.ticks * (ctrl.val - ctrl.low) / range);
2077  wxSlider *s = (wxSlider *) mUIParent->FindWindow(ID_Slider + i);
2078  s->SetValue(val);
2079  }
2080  }
2081 
2082  return true;
2083 }
2084 
2086 {
2087  mInputCmd = mCommandText->GetValue();
2088  mVersion = mVersionCheckBox->GetValue() ? 3 : 4;
2089 
2090  return ParseCommand(mInputCmd);
2091 }
2092 
2094 {
2095  if (mControls.GetCount() == 0)
2096  {
2097  return true;
2098  }
2099 
2100  for (unsigned int i = 0; i < mControls.GetCount(); i++)
2101  {
2102  NyqControl *ctrl = &mControls[i];
2103 
2104  if (ctrl->type == NYQ_CTRL_STRING)
2105  {
2106  continue;
2107  }
2108 
2109  if (ctrl->val == UNINITIALIZED_CONTROL)
2110  {
2111  ctrl->val = GetCtrlValue(ctrl->valStr);
2112  }
2113 
2114  if (ctrl->type == NYQ_CTRL_CHOICE)
2115  {
2116  continue;
2117  }
2118 
2119  if (ctrl->type == NYQ_CTRL_INT_TEXT && ctrl->lowStr.IsSameAs(wxT("nil"), false)) {
2120  ctrl->low = INT_MIN;
2121  }
2122  else if (ctrl->type == NYQ_CTRL_FLOAT_TEXT && ctrl->lowStr.IsSameAs(wxT("nil"), false)) {
2123  ctrl->low = -(FLT_MAX);
2124  }
2125  else {
2126  ctrl->low = GetCtrlValue(ctrl->lowStr);
2127  }
2128 
2129  if (ctrl->type == NYQ_CTRL_INT_TEXT && ctrl->highStr.IsSameAs(wxT("nil"), false)) {
2130  ctrl->high = INT_MAX;
2131  }
2132  else if (ctrl->type == NYQ_CTRL_FLOAT_TEXT && ctrl->highStr.IsSameAs(wxT("nil"), false)) {
2133  ctrl->high = FLT_MAX;
2134  }
2135  else {
2136  ctrl->high = GetCtrlValue(ctrl->highStr);
2137  }
2138 
2139  if (ctrl->high < ctrl->low)
2140  {
2141  ctrl->high = ctrl->low + 1;
2142  }
2143 
2144  if (ctrl->val < ctrl->low)
2145  {
2146  ctrl->val = ctrl->low;
2147  }
2148 
2149  if (ctrl->val > ctrl->high)
2150  {
2151  ctrl->val = ctrl->high;
2152  }
2153 
2154  ctrl->ticks = 1000;
2155  if (ctrl->type == NYQ_CTRL_INT &&
2156  (ctrl->high - ctrl->low < ctrl->ticks))
2157  {
2158  ctrl->ticks = (int)(ctrl->high - ctrl->low);
2159  }
2160  }
2161 
2162  return true;
2163 }
2164 
2166 {
2167  S.StartVerticalLay();
2168  {
2169  S.StartMultiColumn(3, wxEXPAND);
2170  {
2171  S.SetStretchyCol(1);
2172 
2173  S.AddVariableText(_("Enter Nyquist Command: "));
2174 
2175  S.AddSpace(1, 1);
2176 
2177  mVersionCheckBox = S.AddCheckBox(_("&Use legacy (version 3) syntax."),
2178  (mVersion == 3) ? wxT("true") : wxT("false"));
2179  }
2180  S.EndMultiColumn();
2181 
2182  S.StartHorizontalLay(wxEXPAND, 1);
2183  {
2184  mCommandText = S.AddTextWindow(wxT(""));
2185  mCommandText->SetMinSize(wxSize(500, 200));
2186  }
2187  S.EndHorizontalLay();
2188 
2189  S.StartHorizontalLay(wxALIGN_CENTER, 0);
2190  {
2191  S.Id(ID_Load).AddButton(_("&Load"));
2192  S.Id(ID_Save).AddButton(_("&Save"));
2193  }
2194  S.EndHorizontalLay();
2195  }
2196  S.EndVerticalLay();
2197 
2198  mCommandText->SetFocus();
2199 }
2200 
2202 {
2203  S.SetStyle(wxVSCROLL | wxTAB_TRAVERSAL);
2204  wxScrolledWindow *scroller = S.StartScroller(2);
2205  {
2206  S.StartMultiColumn(4);
2207  {
2208  for (size_t i = 0; i < mControls.GetCount(); i++)
2209  {
2210  NyqControl & ctrl = mControls[i];
2211 
2212  S.AddPrompt(ctrl.name + wxT(":"));
2213 
2214  if (ctrl.type == NYQ_CTRL_STRING)
2215  {
2216  S.AddSpace(10, 10);
2217 
2218  wxTextCtrl *item = S.Id(ID_Text + i).AddTextBox( {}, wxT(""), 12);
2219  item->SetValidator(wxGenericValidator(&ctrl.valStr));
2220  }
2221  else if (ctrl.type == NYQ_CTRL_CHOICE)
2222  {
2223  S.AddSpace(10, 10);
2224 
2225  wxArrayString choices = wxStringTokenize(ctrl.label, wxT(","));
2226  S.Id(ID_Choice + i).AddChoice( {}, wxT(""), &choices);
2227  }
2228  else
2229  {
2230  // Integer or Real
2231  if (ctrl.type == NYQ_CTRL_INT_TEXT || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
2232  {
2233  S.AddSpace(10, 10);
2234  }
2235 
2236  wxTextCtrl *item = S.Id(ID_Text+i).AddTextBox( {}, wxT(""),
2237  (ctrl.type == NYQ_CTRL_INT_TEXT ||
2238  ctrl.type == NYQ_CTRL_FLOAT_TEXT) ? 25 : 12);
2239 
2240  double range = ctrl.high - ctrl.low;
2241 
2242  if (ctrl.type == NYQ_CTRL_REAL || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
2243  {
2244  // > 12 decimal places can cause rounding errors in display.
2245  FloatingPointValidator<double> vld(12, &ctrl.val);
2246  vld.SetRange(ctrl.low, ctrl.high);
2247 
2248  // Set number of decimal places
2249  int style = range < 10 ? NUM_VAL_THREE_TRAILING_ZEROES :
2250  range < 100 ? NUM_VAL_TWO_TRAILING_ZEROES :
2251  NUM_VAL_ONE_TRAILING_ZERO;
2252  vld.SetStyle(style);
2253 
2254  item->SetValidator(vld);
2255  }
2256  else
2257  {
2258  IntegerValidator<double> vld(&ctrl.val);
2259  vld.SetRange((int) ctrl.low, (int) ctrl.high);
2260  item->SetValidator(vld);
2261  }
2262 
2263  if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_REAL)
2264  {
2265  S.SetStyle(wxSL_HORIZONTAL);
2266  S.Id(ID_Slider + i).AddSlider( {}, 0, ctrl.ticks, 0);
2267  S.SetSizeHints(150, -1);
2268  }
2269  }
2270 
2271  if (ctrl.type == NYQ_CTRL_CHOICE || ctrl.label.IsEmpty())
2272  {
2273  S.AddSpace(10, 10);
2274  }
2275  else
2276  {
2277  S.AddUnits(ctrl.label);
2278  }
2279  }
2280  }
2281  S.EndMultiColumn();
2282  }
2283  S.EndScroller();
2284 
2285  scroller->SetScrollRate(0, 20);
2286 
2287  // This fools NVDA into not saying "Panel" when the dialog gets focus
2288  scroller->SetName(wxT("\a"));
2289  scroller->SetLabel(wxT("\a"));
2290 }
2291 
2292 // NyquistEffect implementation
2293 
2295 {
2296  return mOK;
2297 }
2298 
2299 void NyquistEffect::OnLoad(wxCommandEvent & WXUNUSED(evt))
2300 {
2301  if (mCommandText->IsModified())
2302  {
2303  if (Effect::MessageBox(_("Current program has been modified.\nDiscard changes?"),
2304  wxYES_NO) == wxNO)
2305  {
2306  return;
2307  }
2308  }
2309 
2311  _("Load Nyquist script"),
2312  mFileName.GetPath(),
2313  wxEmptyString,
2314  _("Nyquist scripts (*.ny)|*.ny|Lisp scripts (*.lsp)|*.lsp|Text files (*.txt)|*.txt|All files|*"),
2315  wxFD_OPEN | wxRESIZE_BORDER);
2316 
2317  if (dlog.ShowModal() != wxID_OK)
2318  {
2319  return;
2320  }
2321 
2322  mFileName = dlog.GetPath();
2323 
2324  if (!mCommandText->LoadFile(mFileName.GetFullPath()))
2325  {
2326  Effect::MessageBox(_("File could not be loaded"));
2327  }
2328 }
2329 
2330 void NyquistEffect::OnSave(wxCommandEvent & WXUNUSED(evt))
2331 {
2333  _("Save Nyquist script"),
2334  mFileName.GetPath(),
2335  mFileName.GetFullName(),
2336  _("Nyquist scripts (*.ny)|*.ny|Lisp scripts (*.lsp)|*.lsp|All files|*"),
2337  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER);
2338 
2339  if (dlog.ShowModal() != wxID_OK)
2340  {
2341  return;
2342  }
2343 
2344  mFileName = dlog.GetPath();
2345 
2346  if (!mCommandText->SaveFile(mFileName.GetFullPath()))
2347  {
2348  Effect::MessageBox(_("File could not be saved"));
2349  }
2350 }
2351 
2352 void NyquistEffect::OnSlider(wxCommandEvent & evt)
2353 {
2354  int i = evt.GetId() - ID_Slider;
2355  NyqControl & ctrl = mControls[i];
2356 
2357  int val = evt.GetInt();
2358  double range = ctrl.high - ctrl.low;
2359  double newVal = (val / (double)ctrl.ticks) * range + ctrl.low;
2360 
2361  // Determine precision for displayed number
2362  int precision = range < 1.0 ? 3 :
2363  range < 10.0 ? 2 :
2364  range < 100.0 ? 1 :
2365  0;
2366 
2367  // If the value is at least one tick different from the current value
2368  // change it (this prevents changes from manually entered values unless
2369  // the slider actually moved)
2370  if (fabs(newVal - ctrl.val) >= (1 / (double)ctrl.ticks) * range &&
2371  fabs(newVal - ctrl.val) >= pow(0.1, precision) / 2)
2372  {
2373  // First round to the appropriate precision
2374  newVal *= pow(10.0, precision);
2375  newVal = floor(newVal + 0.5);
2376  newVal /= pow(10.0, precision);
2377 
2378  ctrl.val = newVal;
2379 
2380  mUIParent->FindWindow(ID_Text + i)->GetValidator()->TransferToWindow();
2381  }
2382 }
2383 
2384 void NyquistEffect::OnChoice(wxCommandEvent & evt)
2385 {
2386  mControls[evt.GetId() - ID_Choice].val = (double) evt.GetInt();
2387 }
2388 
2389 void NyquistEffect::OnText(wxCommandEvent & evt)
2390 {
2391  int i = evt.GetId() - ID_Text;
2392 
2393  NyqControl & ctrl = mControls[i];
2394 
2395  if (wxDynamicCast(evt.GetEventObject(), wxWindow)->GetValidator()->TransferFromWindow())
2396  {
2397  if (ctrl.type == NYQ_CTRL_REAL || ctrl.type == NYQ_CTRL_INT)
2398  {
2399  int pos = (int)floor((ctrl.val - ctrl.low) /
2400  (ctrl.high - ctrl.low) * ctrl.ticks + 0.5);
2401 
2402  wxSlider *slider = (wxSlider *)mUIParent->FindWindow(ID_Slider + i);
2403  slider->SetValue(pos);
2404  }
2405  }
2406 }
2407 
2409 //
2410 // NyquistOutputDialog
2411 //
2413 
2414 
2415 BEGIN_EVENT_TABLE(NyquistOutputDialog, wxDialogWrapper)
2416  EVT_BUTTON(wxID_OK, NyquistOutputDialog::OnOk)
2418 
2419 NyquistOutputDialog::NyquistOutputDialog(wxWindow * parent, wxWindowID id,
2420  const wxString & title,
2421  const wxString & prompt,
2422  const wxString &message)
2423 : wxDialogWrapper{ parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER }
2424 {
2425  SetName(GetTitle());
2426 
2427  wxBoxSizer *mainSizer;
2428  {
2429  auto uMainSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
2430  mainSizer = uMainSizer.get();
2431  wxButton *button;
2432  wxControl *item;
2433 
2434  item = safenew wxStaticText(this, -1, prompt);
2435  item->SetName(prompt); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
2436  mainSizer->Add(item, 0, wxALIGN_LEFT | wxLEFT | wxTOP | wxRIGHT, 10);
2437 
2438  // TODO: use ShowInfoDialog() instead.
2439  // Beware this dialog MUST work with screen readers.
2440  item = safenew wxTextCtrl(this, -1, message,
2441  wxDefaultPosition, wxSize(480, 250),
2442  wxTE_MULTILINE | wxTE_READONLY);
2443  mainSizer->Add(item, 1, wxEXPAND | wxALL, 10);
2444 
2445  {
2446  auto hSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
2447 
2448  /* i18n-hint: In most languages OK is to be translated as OK. It appears on a button.*/
2449  button = safenew wxButton(this, wxID_OK, _("OK"));
2450  button->SetDefault();
2451  hSizer->Add(button, 0, wxALIGN_CENTRE | wxALL, 5);
2452 
2453  mainSizer->Add(hSizer.release(), 0, wxALIGN_CENTRE | wxLEFT | wxBOTTOM | wxRIGHT, 5);
2454  }
2455 
2456  SetAutoLayout(true);
2457  SetSizer(uMainSizer.release());
2458  }
2459 
2460  mainSizer->Fit(this);
2461  mainSizer->SetSizeHints(this);
2462 }
2463 
2464 // ============================================================================
2465 // NyquistOutputDialog implementation
2466 // ============================================================================
2467 
2468 void NyquistOutputDialog::OnOk(wxCommandEvent & /* event */)
2469 {
2470  EndModal(wxID_OK);
2471 }
2472 
bool ShowInterface(wxWindow *parent, bool forceModal=false) override
Definition: Nyquist.cpp:804
#define AUDACITY_REVISION
Definition: Audacity.h:65
wxString highStr
Definition: Nyquist.h:55
wxString mDebugOutput
Definition: Nyquist.h:214
bool mTrace
Definition: Nyquist.h:185
sampleCount mCurLen
Definition: Nyquist.h:222
double mProgressIn
Definition: Nyquist.h:229
bool mStop
Definition: Nyquist.h:179
double mT1
Definition: Effect.h:460
void OnText(wxCommandEvent &evt)
Definition: Nyquist.cpp:2389
EVT_COMMAND_RANGE(ID_Slider, ID_Slider+99, wxEVT_COMMAND_SLIDER_UPDATED, NyquistEffect::OnSlider) EVT_COMMAND_RANGE(ID_Text
wxString GetDescription() override
Definition: Nyquist.cpp:224
bool SaveUserPreset(const wxString &name) override
Definition: Effect.cpp:615
int MessageBox(const wxString &message, long style=DefaultMessageBoxStyle, const wxString &titleStr=wxString{})
Definition: Effect.cpp:2655
wxString mName
Name of the Effect (untranslated)
Definition: Nyquist.h:198
Track * AddToOutputTracks(std::unique_ptr< Track > &&t)
Definition: Effect.cpp:2060
bool TrackProgress(int whichTrack, double frac, const wxString &=wxEmptyString)
Definition: Effect.cpp:1978
void OnChoice(wxCommandEvent &evt)
Definition: Nyquist.cpp:2384
WX_DEFINE_OBJARRAY(NyqControlArray)
bool TotalProgress(double frac)
Definition: Effect.cpp:1970
void Break()
Definition: Nyquist.cpp:1430
bool mIsSal
Definition: Nyquist.h:186
wxString UnQuote(const wxString &s)
Definition: Nyquist.cpp:1445
#define UNINITIALIZED_CONTROL
Definition: Nyquist.cpp:92
void OnSave(const CommandContext &)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
wxString GetVendor() override
Definition: Nyquist.cpp:209
ProgressDialog * mProgress
Definition: Effect.h:452
bool mProjectChanged
Definition: Nyquist.h:213
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true) const
Definition: WaveTrack.cpp:1950
wxString mHelpFile
Definition: Nyquist.h:204
bool CheckWhetherSkipEffect() override
Definition: Nyquist.cpp:484
void SetPreviewFullSelectionFlag(bool previewDurationFlag)
Definition: Effect.cpp:1959
wxFileName mFailedFileName
Definition: Nyquist.h:252
unsigned mNumSelectedChannels
Definition: Nyquist.h:228
bool IsPreviewing()
Definition: Effect.h:361
wxString GetCurrentSettingsGroup() override
Definition: Effect.cpp:814
int type
Definition: Nyquist.h:49
void CopyInputTracks()
Definition: Effect.cpp:2029
void SetLinearEffectFlag(bool linearEffectFlag)
Definition: Effect.cpp:1954
const WaveformSettings & GetWaveformSettings() const
Definition: WaveTrack.cpp:715
AProjectArray gAudacityProjects
Definition: Project.cpp:297
void BuildEffectWindow(ShuttleGui &S)
Definition: Nyquist.cpp:2201
wxString mManPage
Definition: Nyquist.h:203
wxTextCtrl * mCommandText
Definition: Nyquist.h:248
void CopySamples(samplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, unsigned int len, bool highQuality, unsigned int srcStride, unsigned int dstStride)
void Parse(const wxString &line)
Definition: Nyquist.cpp:1477
virtual double GetEndTime() const =0
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2160
static int StaticGetCallback(float *buffer, int channel, long start, long len, long totlen, void *userdata)
Definition: Nyquist.cpp:1874
bool Init() override
Definition: Nyquist.cpp:420
static const wxChar * KEY_Version
Definition: Nyquist.cpp:94
WaveClipPointers SortedClipArray()
Definition: WaveTrack.cpp:2552
#define UTF8CTOWX(X)
Definition: Internat.h:167
double GetCtrlValue(const wxString &s)
Definition: Nyquist.cpp:1457
double mProgressOut
Definition: Nyquist.h:230
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:67
bool SpectralSelectionEnabled() const
static wxArrayString GetNyquistSearchPath()
Definition: Nyquist.cpp:2028
void EndMultiColumn()
virtual void EnableDebug(bool enable=true)
Definition: Effect.cpp:1949
#define NYQUIST_PROMPT_ID
Definition: Nyquist.h:33
A control on a NyquistDialog.
Definition: Nyquist.h:46
bool mExternal
Definition: Nyquist.h:187
wxString valStr
Definition: Nyquist.h:53
bool mHelpFileExists
Definition: Nyquist.h:205
NyqControlArray mControls
Definition: Nyquist.h:217
void ClearAndPaste(double t0, double t1, const Track *src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=NULL)
Definition: WaveTrack.cpp:759
bool TransferDataFromWindow() override
Definition: Nyquist.cpp:868
static void StaticOSCallback(void *userdata)
Definition: Nyquist.cpp:1993
bool ParseCommand(const wxString &cmd)
Definition: Nyquist.cpp:1867
#define XO(s)
Definition: Internat.h:30
An Effect that calls up a Nyquist (XLISP) plug-in, i.e. many possible effects from this one class...
Definition: Nyquist.h:64
EffectType mType
Definition: Nyquist.h:206
wxString lowStr
Definition: Nyquist.h:54
bool TransferDataToWindow() override
Definition: Nyquist.cpp:846
virtual double GetStartTime() const =0
const SpectrogramSettings & GetSpectrogramSettings() const
Definition: WaveTrack.cpp:684
wxString label
Definition: Nyquist.h:52
size_t GetBestBlockSize(sampleCount t) const
Definition: WaveTrack.cpp:1586
void EndScroller()
Definition: ShuttleGui.cpp:770
void OnSlider(wxCommandEvent &evt)
Definition: Nyquist.cpp:2352
wxArrayString mPresetNames
Definition: Effect.h:465
bool LoadUserPreset(const wxString &name) override
Definition: Effect.cpp:599
#define AUDACITY_VERSION
Definition: Audacity.h:63
#define safenew
Definition: Audacity.h:223
wxString var
Definition: Nyquist.h:50
SimpleGuard< R > MakeSimpleGuard(R value)
void BuildPromptWindow(ShuttleGui &S)
Definition: Nyquist.cpp:2165
bool mDebugButton
Definition: Nyquist.h:209
double mF0
Definition: Effect.h:462
void EndHorizontalLay()
Definition: ShuttleGui.cpp:975
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:114
void OnSave(wxCommandEvent &evt)
Definition: Nyquist.cpp:2330
float GetRMS(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:1916
wxString ManualPage() override
Definition: Nyquist.cpp:229
void AddUnits(const wxString &Prompt)
Left aligned text string.
Definition: ShuttleGui.cpp:229
void AddPrompt(const wxString &Prompt)
Right aligned text string.
Definition: ShuttleGui.cpp:215
static wxString BaseDir()
Definition: FileNames.cpp:223
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:161
bool mBreak
Definition: Nyquist.h:180
void EndVerticalLay()
Definition: ShuttleGui.cpp:991
void SetSizeHints(int minX=-1, int minY=-1)
double mOutputTime
Definition: Nyquist.h:226
static wxString HtmlHelpDir()
Definition: FileNames.cpp:173
bool IsInteractive() override
Definition: Nyquist.cpp:263
ID_Text
Definition: Nyquist.cpp:112
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
static wxString ToString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, always uses the dot as decimal separator.
Definition: Internat.cpp:137
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:493
int mVersion
Definition: Nyquist.h:216
sampleCount mCurStart[2]
Definition: Nyquist.h:221
wxCheckBox * AddCheckBox(const wxString &Prompt, const wxString &Selected)
Definition: ShuttleGui.cpp:267
int format
Definition: ExportPCM.cpp:56
unsigned mCount
Definition: Nyquist.h:227
bool TransferDataFromEffectWindow()
Definition: Nyquist.cpp:2093
WaveTrack * mCurTrack[2]
Definition: Nyquist.h:220
bool mFoundType
Definition: Nyquist.h:183
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:121
bool ParseProgram(wxInputStream &stream)
Definition: Nyquist.cpp:1794
Defines a selected portion of a project.
wxScrolledWindow * StartScroller(int iStyle=0)
Definition: ShuttleGui.cpp:733
double low
Definition: Nyquist.h:57
wxDateTime mFileModified
When the script was last modified on disk.
Definition: Nyquist.h:177
void SetWaveColorIndex(int colorIndex)
Definition: WaveTrack.cpp:471
double mScale
Definition: Nyquist.h:232
wxFileName fileName
Definition: FileException.h:47
bool mFirstInGroup
Definition: Nyquist.h:225
void ParseFile()
Definition: Nyquist.cpp:1860
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:966
size_t GetIdealBlockSize()
Definition: WaveTrack.cpp:1624
size_t mCurBufferLen[2]
Definition: Nyquist.h:236
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:998
wxChoice * AddChoice(const wxString &Prompt, const wxString &Selected, const wxArrayString *pChoices)
Definition: ShuttleGui.cpp:331
int GetCallback(float *buffer, int channel, long start, long len, long totlen)
Definition: Nyquist.cpp:1882
bool Process() override
Definition: Nyquist.cpp:491
bool ProcessOne()
Definition: Nyquist.cpp:885
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
ShuttleGui & Id(int id)
bool Delegate(Effect &delegate, wxWindow *parent, SelectedRegion *selectedRegion, bool shouldPrompt)
Definition: Effect.cpp:1249
wxEVT_COMMAND_TEXT_UPDATED
Definition: Nyquist.cpp:112
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:67
bool mCompiler
Definition: Nyquist.h:184
void SetStyle(int Style)
Definition: ShuttleGui.h:252
wxString name
Definition: Nyquist.h:51
#define NYQ_MAX_LEN
Definition: Nyquist.cpp:90
wxString HelpPage() override
Definition: Nyquist.cpp:236
WaveTrack * mOutputTrack[2]
Definition: Nyquist.h:238
static int StaticPutCallback(float *buffer, int channel, long start, long len, long totlen, void *userdata)
Definition: Nyquist.cpp:1946
bool mIsSpectral
Definition: Nyquist.h:188
if(pTrack &&pTrack->GetDisplay()!=WaveTrack::Spectrum)
EffectManager is the class that handles effects and effect categories.
Definition: EffectManager.h:49
int min(int a, int b)
#define NYQUIST_WORKER_ID
Definition: Nyquist.h:34
#define LAT1CTOWX(X)
Definition: Internat.h:168
wxCheckBox * mVersionCheckBox
Definition: Nyquist.h:249
#define AUDACITY_RELEASE
Definition: Audacity.h:64
#define NYQUISTEFFECTS_FAMILY
Definition: Nyquist.h:31
wxString mCmd
Definition: Nyquist.h:197
void Append(samplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1, XMLWriter *blockFileLog=NULL)
Append the sample data to the WaveTrack. You must call Flush() after the last Append.
Definition: WaveTrack.cpp:1536
wxString EscapeString(const wxString &inStr)
Definition: Nyquist.cpp:1396
wxString mAction
Definition: Nyquist.h:199
Dialog used with NyquistEffect.
Definition: Nyquist.h:259
static EffectManager & Get()
bool setF1(double f, bool maySwap=true)
sampleCount mCurBufferStart[2]
Definition: Nyquist.h:235
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
EffectType GetType() override
Definition: Nyquist.cpp:253
bool mRestoreSplits
Definition: Nyquist.h:245
virtual ~NyquistEffect()
Definition: Nyquist.cpp:178
bool mCont
Definition: Nyquist.h:181
bool mError
Definition: Nyquist.h:251
wxString GetFamily() override
Definition: Nyquist.cpp:258
static wxString TempDir()
Definition: FileNames.cpp:86
void OnOk(wxCommandEvent &event)
Definition: Nyquist.cpp:2468
std::unique_ptr< WaveTrack > NewWaveTrack(sampleFormat format=(sampleFormat) 0, double rate=0)
Definition: WaveTrack.cpp:78
wxWindow * mUIParent
Definition: Effect.h:471
An iterator for a TrackList.
Definition: Track.h:339
wxString mCopyright
Definition: Nyquist.h:202
_("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 &)
bool mRedirectOutput
Definition: Nyquist.h:212
bool TransferDataFromPromptWindow()
Definition: Nyquist.cpp:2085
TrackFactory * mFactory
Definition: Effect.h:456
double val
Definition: Nyquist.h:56
static void AddUniquePathToPathList(const wxString &path, wxArrayString &pathList)
ValueRestorer< T > valueRestorer(T &var)
Definition: MemoryX.h:875
bool IsDefault() override
Definition: Nyquist.cpp:273
wxString mProps
Definition: Nyquist.h:242
unsigned mCurNumChannels
Definition: Nyquist.h:219
void OSCallback()
Definition: Nyquist.cpp:1998
bool SetAutomationParameters(EffectAutomationParameters &parms) override
Definition: Nyquist.cpp:327
void Continue()
Definition: Nyquist.cpp:1435
wxFileName mFileName
Name of the Nyquist script file this effect is loaded from.
Definition: Nyquist.h:176
wxTextCtrl * AddTextWindow(const wxString &Value)
Multiline text box that grows.
Definition: ShuttleGui.cpp:551
SampleBuffer mCurBuffer[2]
Definition: Nyquist.h:234
Track * GetLink() const
Definition: Track.cpp:204
bool TransferDataToEffectWindow()
Definition: Nyquist.cpp:2053
wxString mInputCmd
Definition: Nyquist.h:196
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: WaveTrack.cpp:1822
bool mIsPrompt
Definition: Nyquist.h:193
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:302
bool ShowInterface(wxWindow *parent, bool forceModal=false) override
Definition: Effect.cpp:529
sampleFormat GetSampleFormat() const
Definition: WaveTrack.h:140
bool mEnablePreview
Definition: Nyquist.h:208
void PopulateOrExchange(ShuttleGui &S) override
Definition: Nyquist.cpp:832
wxStaticText * AddVariableText(const wxString &Str, bool bCenter=false, int PositionFlags=0)
Definition: ShuttleGui.cpp:373
void OnLoad(wxCommandEvent &evt)
Definition: Nyquist.cpp:2299
void SetSkipStateFlag(bool flag)
wxArrayString audacityPathList
A list of directories that should be searched for Audacity files (plug-ins, help files, etc.).
Definition: AudacityApp.h:138
int GetNumWaveGroups()
Definition: Effect.h:345
wxString GetName()
Definition: Project.cpp:1443
static wxString DataDir()
Audacity user data directory.
Definition: FileNames.cpp:130
int mTrackIndex
Definition: Nyquist.h:224
void OutputCallback(int c)
Definition: Nyquist.cpp:1982
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:1877
double GetEndTime() const
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1852
wxString GetName() override
Definition: Nyquist.cpp:204
bool TrackGroupProgress(int whichGroup, double frac, const wxString &=wxEmptyString)
Definition: Effect.cpp:1986
wxString mPerTrackProps
Definition: Nyquist.h:243
bool mDebug
Definition: Nyquist.h:211
wxString GetVersion() override
Definition: Nyquist.cpp:219
void RedirectOutput()
Definition: Nyquist.cpp:1418
bool GetAutomationParameters(EffectAutomationParameters &parms) override
Definition: Nyquist.cpp:280
double GetRate() const
Definition: Project.h:184
int PutCallback(float *buffer, int channel, long start, long len, long totlen)
Definition: Nyquist.cpp:1954
AudacityApp & wxGetApp()
END_EVENT_TABLE()
wxSizerItem * AddSpace(int width, int height)
wxString mInitError
Definition: Nyquist.h:195
double GetRate() const
Definition: WaveTrack.cpp:397
wxArrayString ParseChoice(const NyqControl &ctrl)
Definition: Nyquist.cpp:1406
virtual void SyncLockAdjust(double oldT1, double newT1)
Definition: Track.cpp:257
double mF1
Definition: Effect.h:463
double high
Definition: Nyquist.h:58
TrackList * GetTracks()
Definition: Project.h:177
bool TransferDataToPromptWindow()
Definition: Nyquist.cpp:2045
int mMergeClips
Definition: Nyquist.h:246
std::unique_ptr< LabelTrack > NewLabelTrack()
Definition: LabelTrack.cpp:99
static wxString NyquistToWxString(const char *nyqString)
Definition: Nyquist.cpp:1383
int AddLabel(const SelectedRegion &region, const wxString &title=wxT(""), int restoreFocus=-1)
double mProgressTot
Definition: Nyquist.h:231
int mUIResultID
Definition: Effect.h:472
wxString mInfo
Definition: Nyquist.h:200
std::shared_ptr< TrackList > mOutputTracks
Definition: Effect.h:458
static void StaticOutputCallback(int c, void *userdata)
Definition: Nyquist.cpp:1977
static wxString PlugInDir()
The user plug-in directory (not a system one)
Definition: FileNames.cpp:208
wxButton * AddButton(const wxString &Text, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:301
int ticks
Definition: Nyquist.h:59
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:192
void SetCommand(const wxString &cmd)
Definition: Nyquist.cpp:1423
sampleCount mMaxLen
Definition: Nyquist.h:223
double mT0
Definition: Effect.h:459
double GetStartTime() const
Get the time at which the first clip in the track starts.
Definition: WaveTrack.cpp:1832
wxString GetPath() override
Definition: Nyquist.cpp:184
wxString GetSymbol() override
Definition: Nyquist.cpp:194
static const wxChar * KEY_Command
Definition: Nyquist.cpp:95
virtual bool EnablePreview(bool enable=true)
Definition: Effect.cpp:1910
wxArrayString mCategories
Definition: Nyquist.h:240
void Flush()
Flush must be called after last Append.
Definition: WaveTrack.cpp:1629
bool setF0(double f, bool maySwap=true)
wxString mAuthor
Definition: Nyquist.h:201
wxSlider * AddSlider(const wxString &Prompt, int pos, int Max, int Min=0)
Definition: ShuttleGui.cpp:456
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:982