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