Audacity  3.0.3
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 
28 #include "Nyquist.h"
29 
30 #include <algorithm>
31 #include <cmath>
32 
33 #include <locale.h>
34 
35 #include <wx/button.h>
36 #include <wx/checkbox.h>
37 #include <wx/choice.h>
38 #include <wx/datetime.h>
39 #include <wx/intl.h>
40 #include <wx/log.h>
41 #include <wx/scrolwin.h>
42 #include <wx/sizer.h>
43 #include <wx/slider.h>
44 #include <wx/sstream.h>
45 #include <wx/stattext.h>
46 #include <wx/textdlg.h>
47 #include <wx/tokenzr.h>
48 #include <wx/txtstrm.h>
49 #include <wx/valgen.h>
50 #include <wx/wfstream.h>
51 #include <wx/numformatter.h>
52 #include <wx/stdpaths.h>
53 
54 #include "../EffectManager.h"
55 #include "../../FileNames.h"
56 #include "../../LabelTrack.h"
57 #include "../../NoteTrack.h"
58 #include "../../TimeTrack.h"
59 #include "../../prefs/SpectrogramSettings.h"
60 #include "../../Project.h"
61 #include "../../ProjectSettings.h"
62 #include "../../ShuttleGetDefinition.h"
63 #include "../../ShuttleGui.h"
64 #include "../../TempDirectory.h"
65 #include "../../ViewInfo.h"
66 #include "../../WaveClip.h"
67 #include "../../WaveTrack.h"
68 #include "../../widgets/valnum.h"
69 #include "../../widgets/AudacityMessageBox.h"
70 #include "../../Prefs.h"
71 #include "../../wxFileNameWrapper.h"
72 #include "../../prefs/GUIPrefs.h"
73 #include "../../tracks/playabletrack/wavetrack/ui/WaveTrackView.h"
74 #include "../../tracks/playabletrack/wavetrack/ui/WaveTrackViewConstants.h"
75 #include "../../widgets/NumericTextCtrl.h"
76 #include "../../widgets/ProgressDialog.h"
77 
78 #include "../../widgets/FileDialog/FileDialog.h"
79 
80 #ifndef nyx_returns_start_and_end_time
81 #error You need to update lib-src/libnyquist
82 #endif
83 
84 #include <locale.h>
85 #include <iostream>
86 #include <ostream>
87 #include <sstream>
88 #include <float.h>
89 
91 
92 enum
93 {
94  ID_Editor = 10000,
97 
98  ID_Slider = 11000,
99  ID_Text = 12000,
100  ID_Choice = 13000,
101  ID_Time = 14000,
102  ID_FILE = 15000
103 };
104 
105 // Protect Nyquist from selections greater than 2^31 samples (bug 439)
106 #define NYQ_MAX_LEN (std::numeric_limits<long>::max())
107 
108 #define UNINITIALIZED_CONTROL ((double)99999999.99)
109 
110 static const wxChar *KEY_Command = wxT("Command");
111 static const wxChar *KEY_Parameters = wxT("Parameters");
112 
114 //
115 // NyquistEffect
116 //
118 
119 BEGIN_EVENT_TABLE(NyquistEffect, wxEvtHandler)
122 
124  wxEVT_COMMAND_SLIDER_UPDATED, NyquistEffect::OnSlider)
128  wxEVT_COMMAND_CHOICE_SELECTED, NyquistEffect::OnChoice)
132  wxEVT_COMMAND_BUTTON_CLICKED, NyquistEffect::OnFileButton)
134 
135 NyquistEffect::NyquistEffect(const wxString &fName)
136 {
137  mOutputTrack[0] = mOutputTrack[1] = nullptr;
138 
139  mAction = XO("Applying Nyquist Effect...");
140  mIsPrompt = false;
141  mExternal = false;
142  mCompiler = false;
143  mTrace = false;
144  mRedirectOutput = false;
145  mDebug = false;
146  mIsSal = false;
147  mOK = false;
148  mAuthor = XO("n/a");
149  mReleaseVersion = XO("n/a");
150  mCopyright = XO("n/a");
151 
152  // set clip/split handling when applying over clip boundary.
153  mRestoreSplits = true; // Default: Restore split lines.
154  mMergeClips = -1; // Default (auto): Merge if length remains unchanged.
155 
156  mVersion = 4;
157 
158  mStop = false;
159  mBreak = false;
160  mCont = false;
161  mIsTool = false;
162 
163  mMaxLen = NYQ_MAX_LEN;
164 
165  // Interactive Nyquist
166  if (fName == NYQUIST_PROMPT_ID) {
167  mName = XO("Nyquist Prompt");
168  mType = EffectTypeTool;
169  mIsTool = true;
170  mPromptName = mName;
171  mPromptType = mType;
172  mOK = true;
173  mIsPrompt = true;
174  return;
175  }
176 
177  if (fName == NYQUIST_WORKER_ID) {
178  // Effect spawned from Nyquist Prompt
179 /* i18n-hint: It is acceptable to translate this the same as for "Nyquist Prompt" */
180  mName = XO("Nyquist Worker");
181  return;
182  }
183 
184  mFileName = fName;
185  // Use the file name verbatim as effect name.
186  // This is only a default name, overridden if we find a $name line:
187  mName = Verbatim( mFileName.GetName() );
188  mFileModified = mFileName.GetModificationTime();
189  ParseFile();
190 
191  if (!mOK && mInitError.empty())
192  mInitError = XO("Ill-formed Nyquist plug-in header");
193 }
194 
196 {
197 }
198 
199 // ComponentInterface implementation
200 
202 {
203  if (mIsPrompt)
204  return NYQUIST_PROMPT_ID;
205 
206  return mFileName.GetFullPath();
207 }
208 
210 {
211  if (mIsPrompt)
212  return XO("Nyquist Prompt");
213 
214  return mName;
215 }
216 
218 {
219  if (mIsPrompt)
220  {
221  return XO("Audacity");
222  }
223 
224  return mAuthor;
225 }
226 
228 {
229  // Are Nyquist version strings really supposed to be translatable?
230  // See commit a06e561 which used XO for at least one of them
231  return mReleaseVersion.Translation();
232 }
233 
235 {
236  return mCopyright;
237 }
238 
240 {
241  return mIsPrompt
242  ? wxT("Nyquist_Prompt")
243  : mManPage;
244 }
245 
247 {
249  wxString fileName;
250 
251  for (size_t i = 0, cnt = paths.size(); i < cnt; i++) {
252  fileName = wxFileName(paths[i] + wxT("/") + mHelpFile).GetFullPath();
253  if (wxFileExists(fileName)) {
254  mHelpFileExists = true;
255  return fileName;
256  }
257  }
258  return wxEmptyString;
259 }
260 
261 // EffectDefinitionInterface implementation
262 
264 {
265  return mType;
266 }
267 
269 {
270  if (mIsTool)
271  return EffectTypeTool;
272  return mType;
273 }
274 
276 {
277  return NYQUISTEFFECTS_FAMILY;
278 }
279 
281 {
282  if (mIsPrompt)
283  {
284  return true;
285  }
286 
287  return mControls.size() != 0;
288 }
289 
291 {
292  return mIsPrompt;
293 }
294 
295 // EffectClientInterface implementation
297 {
298  // For now we assume Nyquist can do get and set better than DefineParams can,
299  // And so we ONLY use it for getting the signature.
300  auto pGa = dynamic_cast<ShuttleGetAutomation*>(&S);
301  if( pGa ){
302  GetAutomationParameters( *(pGa->mpEap) );
303  return true;
304  }
305  auto pSa = dynamic_cast<ShuttleSetAutomation*>(&S);
306  if( pSa ){
307  SetAutomationParameters( *(pSa->mpEap) );
308  return true;
309  }
310  auto pSd = dynamic_cast<ShuttleGetDefinition*>(&S);
311  if( pSd == nullptr )
312  return true;
313  //wxASSERT( pSd );
314 
315  if (mExternal)
316  return true;
317 
318  if (mIsPrompt)
319  {
320  S.Define( mInputCmd, KEY_Command, "" );
322  return true;
323  }
324 
325  for (size_t c = 0, cnt = mControls.size(); c < cnt; c++)
326  {
327  NyqControl & ctrl = mControls[c];
328  double d = ctrl.val;
329 
330  if (d == UNINITIALIZED_CONTROL && ctrl.type != NYQ_CTRL_STRING)
331  {
332  d = GetCtrlValue(ctrl.valStr);
333  }
334 
335  if (ctrl.type == NYQ_CTRL_FLOAT || ctrl.type == NYQ_CTRL_FLOAT_TEXT ||
336  ctrl.type == NYQ_CTRL_TIME)
337  {
338  S.Define( d, static_cast<const wxChar*>( ctrl.var.c_str() ), (double)0.0, ctrl.low, ctrl.high, 1.0);
339  }
340  else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_INT_TEXT)
341  {
342  int x=d;
343  S.Define( x, static_cast<const wxChar*>( ctrl.var.c_str() ), 0, ctrl.low, ctrl.high, 1);
344  //parms.Write(ctrl.var, (int) d);
345  }
346  else if (ctrl.type == NYQ_CTRL_CHOICE)
347  {
348  // untranslated
349  int x=d;
350  //parms.WriteEnum(ctrl.var, (int) d, choices);
351  S.DefineEnum( x, static_cast<const wxChar*>( ctrl.var.c_str() ), 0,
352  ctrl.choices.data(), ctrl.choices.size() );
353  }
354  else if (ctrl.type == NYQ_CTRL_STRING || ctrl.type == NYQ_CTRL_FILE)
355  {
356  S.Define( ctrl.valStr, ctrl.var, "" , ctrl.lowStr, ctrl.highStr );
357  //parms.Write(ctrl.var, ctrl.valStr);
358  }
359  }
360  return true;
361 }
362 
364 {
365  if (mIsPrompt)
366  {
367  parms.Write(KEY_Command, mInputCmd);
368  parms.Write(KEY_Parameters, mParameters);
369 
370  return true;
371  }
372 
373  for (size_t c = 0, cnt = mControls.size(); c < cnt; c++)
374  {
375  NyqControl & ctrl = mControls[c];
376  double d = ctrl.val;
377 
378  if (d == UNINITIALIZED_CONTROL && ctrl.type != NYQ_CTRL_STRING)
379  {
380  d = GetCtrlValue(ctrl.valStr);
381  }
382 
383  if (ctrl.type == NYQ_CTRL_FLOAT || ctrl.type == NYQ_CTRL_FLOAT_TEXT ||
384  ctrl.type == NYQ_CTRL_TIME)
385  {
386  parms.Write(ctrl.var, d);
387  }
388  else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_INT_TEXT)
389  {
390  parms.Write(ctrl.var, (int) d);
391  }
392  else if (ctrl.type == NYQ_CTRL_CHOICE)
393  {
394  // untranslated
395  parms.WriteEnum(ctrl.var, (int) d,
396  ctrl.choices.data(), ctrl.choices.size());
397  }
398  else if (ctrl.type == NYQ_CTRL_STRING)
399  {
400  parms.Write(ctrl.var, ctrl.valStr);
401  }
402  else if (ctrl.type == NYQ_CTRL_FILE)
403  {
404  resolveFilePath(ctrl.valStr);
405  parms.Write(ctrl.var, ctrl.valStr);
406  }
407  }
408 
409  return true;
410 }
411 
413 {
414  if (mIsPrompt)
415  {
416  parms.Read(KEY_Command, &mInputCmd, wxEmptyString);
417  parms.Read(KEY_Parameters, &mParameters, wxEmptyString);
418 
419  if (!mInputCmd.empty())
420  {
422  }
423 
424  if (!mParameters.empty())
425  {
426  parms.SetParameters(mParameters);
427  }
428 
429  if (!IsBatchProcessing())
430  {
432  }
433 
434  mPromptType = mType;
436  mExternal = true;
437 
438  if (!IsBatchProcessing())
439  {
440  return true;
441  }
442  }
443 
444  // Constants to document what the true/false values mean.
445  const auto kTestOnly = true;
446  const auto kTestAndSet = false;
447 
448  // badCount will encompass both actual bad values and missing values.
449  // We probably never actually have bad values when using the dialogs
450  // since the dialog validation will catch them.
451  int badCount;
452  // When batch processing, we just ignore missing/bad parameters.
453  // We'll end up using defaults in those cases.
454  if (!IsBatchProcessing()) {
455  badCount = SetLispVarsFromParameters(parms, kTestOnly);
456  if (badCount > 0)
457  return false;
458  }
459 
460  badCount = SetLispVarsFromParameters(parms, kTestAndSet);
461  // We never do anything with badCount here.
462  // It might be non zero, for missing parameters, and we allow that,
463  // and don't distinguish that from an out-of-range value.
464  return true;
465 }
466 
467 // Sets the lisp variables form the parameters.
468 // returns the number of bad settings.
469 // We can run this just testing for bad values, or actually setting when
470 // the values are good.
472 {
473  int badCount = 0;
474  // First pass verifies values
475  for (size_t c = 0, cnt = mControls.size(); c < cnt; c++)
476  {
477  NyqControl & ctrl = mControls[c];
478  bool good = false;
479 
480  // This GetCtrlValue code is preserved from former code,
481  // but probably is pointless. The value d isn't used later,
482  // and GetCtrlValue does not appear to have important needed
483  // side effects.
484  if (!bTestOnly) {
485  double d = ctrl.val;
486  if (d == UNINITIALIZED_CONTROL && ctrl.type != NYQ_CTRL_STRING)
487  {
488  d = GetCtrlValue(ctrl.valStr);
489  }
490  }
491 
492  if (ctrl.type == NYQ_CTRL_FLOAT || ctrl.type == NYQ_CTRL_FLOAT_TEXT ||
493  ctrl.type == NYQ_CTRL_TIME)
494  {
495  double val;
496  good = parms.Read(ctrl.var, &val) &&
497  val >= ctrl.low &&
498  val <= ctrl.high;
499  if (good && !bTestOnly)
500  ctrl.val = val;
501  }
502  else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_INT_TEXT)
503  {
504  int val;
505  good = parms.Read(ctrl.var, &val) &&
506  val >= ctrl.low &&
507  val <= ctrl.high;
508  if (good && !bTestOnly)
509  ctrl.val = (double)val;
510  }
511  else if (ctrl.type == NYQ_CTRL_CHOICE)
512  {
513  int val;
514  // untranslated
515  good = parms.ReadEnum(ctrl.var, &val,
516  ctrl.choices.data(), ctrl.choices.size()) &&
517  val != wxNOT_FOUND;
518  if (good && !bTestOnly)
519  ctrl.val = (double)val;
520  }
521  else if (ctrl.type == NYQ_CTRL_STRING || ctrl.type == NYQ_CTRL_FILE)
522  {
523  wxString val;
524  good = parms.Read(ctrl.var, &val);
525  if (good && !bTestOnly)
526  ctrl.valStr = val;
527  }
528  else if (ctrl.type == NYQ_CTRL_TEXT)
529  {
530  // This "control" is just fixed text (nothing to save or restore),
531  // Does not count for good/bad counting.
532  good = true;
533  }
534  badCount += !good ? 1 : 0;
535  }
536  return badCount;
537 }
538 
539 // Effect Implementation
541 {
542  // When Nyquist Prompt spawns an effect GUI, Init() is called for Nyquist Prompt,
543  // and then again for the spawned (mExternal) effect.
544 
545  // EffectType may not be defined in script, so
546  // reset each time we call the Nyquist Prompt.
547  if (mIsPrompt) {
548  mName = mPromptName;
549  // Reset effect type each time we call the Nyquist Prompt.
550  mType = mPromptType;
551  mIsSpectral = false;
552  mDebugButton = true; // Debug button always enabled for Nyquist Prompt.
553  mEnablePreview = true; // Preview button always enabled for Nyquist Prompt.
554  mVersion = 4;
555  }
556 
557  // As of Audacity 2.1.2 rc1, 'spectral' effects are allowed only if
558  // the selected track(s) are in a spectrogram view, and there is at
559  // least one frequency bound and Spectral Selection is enabled for the
560  // selected track(s) - (but don't apply to Nyquist Prompt).
561 
562  if (!mIsPrompt && mIsSpectral) {
563  auto *project = FindProject();
564  bool bAllowSpectralEditing = false;
565  bool hasSpectral = false;
566 
567  for ( auto t :
568  TrackList::Get( *project ).Selected< const WaveTrack >() ) {
569  const auto displays = WaveTrackView::Get(*t).GetDisplays();
570  if (displays.end() != std::find(
571  displays.begin(), displays.end(),
572  WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} }))
573  hasSpectral = true;
574  if ( hasSpectral &&
575  (t->GetSpectrogramSettings().SpectralSelectionEnabled())) {
576  bAllowSpectralEditing = true;
577  break;
578  }
579  }
580 
581  if (!bAllowSpectralEditing || ((mF0 < 0.0) && (mF1 < 0.0))) {
582  if (!hasSpectral) {
584  XO("Enable track spectrogram view before\n"
585  "applying 'Spectral' effects."),
586  wxOK | wxICON_EXCLAMATION | wxCENTRE,
587  XO("Error") );
588  } else {
590  XO("To use 'Spectral effects', enable 'Spectral Selection'\n"
591  "in the track Spectrogram settings and select the\n"
592  "frequency range for the effect to act on."),
593  wxOK | wxICON_EXCLAMATION | wxCENTRE,
594  XO("Error") );
595  }
596  return false;
597  }
598  }
599 
600  if (!mIsPrompt && !mExternal)
601  {
602  //TODO: (bugs):
603  // 1) If there is more than one plug-in with the same name, GetModificationTime may pick the wrong one.
604  // 2) If the ;type is changed after the effect has been registered, the plug-in will appear in the wrong menu.
605 
606  //TODO: If we want to auto-add parameters from spectral selection,
607  //we will need to modify this test.
608  //Note that removing it stops the caching of parameter values,
609  //(during this session).
610  if (mFileName.GetModificationTime().IsLaterThan(mFileModified))
611  {
612  SaveUserPreset(GetCurrentSettingsGroup());
613 
614  mMaxLen = NYQ_MAX_LEN;
615  ParseFile();
616  mFileModified = mFileName.GetModificationTime();
617 
618  LoadUserPreset(GetCurrentSettingsGroup());
619  }
620  }
621 
622  return true;
623 }
624 
626 {
627  // If we're a prompt and we have controls, then we've already processed
628  // the audio, so skip further processing.
629  return (mIsPrompt && mControls.size() > 0 && !IsBatchProcessing());
630 }
631 
632 static void RegisterFunctions();
633 
635 {
636  // Check for reentrant Nyquist commands.
637  // I'm choosing to mark skipped Nyquist commands as successful even though
638  // they are skipped. The reason is that when Nyquist calls out to a chain,
639  // and that chain contains Nyquist, it will be clearer if the chain completes
640  // skipping Nyquist, rather than doing nothing at all.
641  if( mReentryCount > 0 )
642  return true;
643 
644  // Restore the reentry counter (to zero) when we exit.
645  auto countRestorer = valueRestorer( mReentryCount);
646  mReentryCount++;
648 
649  bool success = true;
650  int nEffectsSoFar = nEffectsDone;
651  mProjectChanged = false;
653  em.SetSkipStateFlag(false);
654 
655  // This code was added in a fix for bug 2392 (no preview for Nyquist)
656  // It was commented out in a fix for bug 2428 (no progress dialog from a macro)
657  //if (mExternal) {
658  // mProgress->Hide();
659  //}
660 
661  mOutputTime = 0;
662  mCount = 0;
663  mProgressIn = 0;
664  mProgressOut = 0;
665  mProgressTot = 0;
666  mScale = (GetType() == EffectTypeProcess ? 0.5 : 1.0) / GetNumWaveGroups();
667 
668  mStop = false;
669  mBreak = false;
670  mCont = false;
671 
672  mTrackIndex = 0;
673 
674  // If in tool mode, then we don't do anything with the track and selection.
675  const bool bOnePassTool = (GetType() == EffectTypeTool);
676 
677  // We must copy all the tracks, because Paste needs label tracks to ensure
678  // correct sync-lock group behavior when the timeline is affected; then we just want
679  // to operate on the selected wave tracks
680  if ( !bOnePassTool )
681  CopyInputTracks(true);
682 
683  mNumSelectedChannels = bOnePassTool
684  ? 0
685  : mOutputTracks->Selected< const WaveTrack >().size();
686 
687  mDebugOutput = {};
688  if (!mHelpFile.empty() && !mHelpFileExists) {
689  mDebugOutput = XO(
690 "error: File \"%s\" specified in header but not found in plug-in path.\n")
691  .Format( mHelpFile );
692  }
693 
694  if (mVersion >= 4)
695  {
696  auto project = FindProject();
697 
698  mProps = wxEmptyString;
699 
700  mProps += wxString::Format(wxT("(putprop '*AUDACITY* (list %d %d %d) 'VERSION)\n"), AUDACITY_VERSION, AUDACITY_RELEASE, AUDACITY_REVISION);
701  wxString lang = gPrefs->Read(wxT("/Locale/Language"), wxT(""));
702  lang = (lang.empty())? GUIPrefs::SetLang(lang) : lang;
703  mProps += wxString::Format(wxT("(putprop '*AUDACITY* \"%s\" 'LANGUAGE)\n"), lang);
704 
705  mProps += wxString::Format(wxT("(setf *DECIMAL-SEPARATOR* #\\%c)\n"), wxNumberFormatter::GetDecimalSeparator());
706 
707  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'BASE)\n"), EscapeString(FileNames::BaseDir()));
708  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'DATA)\n"), EscapeString(FileNames::DataDir()));
709  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'HELP)\n"), EscapeString(FileNames::HtmlHelpDir().RemoveLast()));
710  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'TEMP)\n"), EscapeString(TempDirectory::TempDir()));
711  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'SYS-TEMP)\n"), EscapeString(wxStandardPaths::Get().GetTempDir()));
712  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'DOCUMENTS)\n"), EscapeString(wxStandardPaths::Get().GetDocumentsDir()));
713  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'HOME)\n"), EscapeString(wxGetHomeDir()));
714 
716  wxString list;
717  for (size_t i = 0, cnt = paths.size(); i < cnt; i++)
718  {
719  list += wxT("\"") + EscapeString(paths[i]) + wxT("\" ");
720  }
721  list = list.RemoveLast();
722 
723  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* (list %s) 'PLUGIN)\n"), list);
724  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* (list %s) 'PLUG-IN)\n"), list);
725  mProps += wxString::Format(wxT("(putprop '*SYSTEM-DIR* \"%s\" 'USER-PLUG-IN)\n"),
727 
728  // Date and time:
729  wxDateTime now = wxDateTime::Now();
730  int year = now.GetYear();
731  int doy = now.GetDayOfYear();
732  int dom = now.GetDay();
733  // enumerated constants
734  wxDateTime::Month month = now.GetMonth();
735  wxDateTime::WeekDay day = now.GetWeekDay();
736 
737  // Date/time as a list: year, day of year, hour, minute, seconds
738  mProps += wxString::Format(wxT("(setf *SYSTEM-TIME* (list %d %d %d %d %d))\n"),
739  year, doy, now.GetHour(), now.GetMinute(), now.GetSecond());
740 
741  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'DATE)\n"), now.FormatDate());
742  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'TIME)\n"), now.FormatTime());
743  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'ISO-DATE)\n"), now.FormatISODate());
744  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'ISO-TIME)\n"), now.FormatISOTime());
745  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* %d 'YEAR)\n"), year);
746  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* %d 'DAY)\n"), dom); // day of month
747  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* %d 'MONTH)\n"), month);
748  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'MONTH-NAME)\n"), now.GetMonthName(month));
749  mProps += wxString::Format(wxT("(putprop '*SYSTEM-TIME* \"%s\" 'DAY-NAME)\n"), now.GetWeekDayName(day));
750 
751  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'PROJECTS)\n"),
752  (int) AllProjects{}.size());
753  mProps += wxString::Format(wxT("(putprop '*PROJECT* \"%s\" 'NAME)\n"), EscapeString(project->GetProjectName()));
754 
755  int numTracks = 0;
756  int numWave = 0;
757  int numLabel = 0;
758  int numMidi = 0;
759  int numTime = 0;
760  wxString waveTrackList; // track positions of selected audio tracks.
761 
762  {
763  auto countRange = TrackList::Get( *project ).Leaders();
764  for (auto t : countRange) {
765  t->TypeSwitch( [&](const WaveTrack *) {
766  numWave++;
767  if (t->GetSelected())
768  waveTrackList += wxString::Format(wxT("%d "), 1 + numTracks);
769  });
770  numTracks++;
771  }
772  numLabel = countRange.Filter<const LabelTrack>().size();
773  #if defined(USE_MIDI)
774  numMidi = countRange.Filter<const NoteTrack>().size();
775  #endif
776  numTime = countRange.Filter<const TimeTrack>().size();
777  }
778 
779  // We use Internat::ToString() rather than "%g" here because we
780  // always have to use the dot as decimal separator when giving
781  // numbers to Nyquist, whereas using "%g" will use the user's
782  // decimal separator which may be a comma in some countries.
783  mProps += wxString::Format(wxT("(putprop '*PROJECT* (float %s) 'RATE)\n"),
785  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'TRACKS)\n"), numTracks);
786  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'WAVETRACKS)\n"), numWave);
787  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'LABELTRACKS)\n"), numLabel);
788  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'MIDITRACKS)\n"), numMidi);
789  mProps += wxString::Format(wxT("(putprop '*PROJECT* %d 'TIMETRACKS)\n"), numTime);
790 
791  double previewLen = 6.0;
792  gPrefs->Read(wxT("/AudioIO/EffectsPreviewLen"), &previewLen);
793  mProps += wxString::Format(wxT("(putprop '*PROJECT* (float %s) 'PREVIEW-DURATION)\n"),
794  Internat::ToString(previewLen));
795 
796  // *PREVIEWP* is true when previewing (better than relying on track view).
797  wxString isPreviewing = (this->IsPreviewing())? wxT("T") : wxT("NIL");
798  mProps += wxString::Format(wxT("(setf *PREVIEWP* %s)\n"), isPreviewing);
799 
800  mProps += wxString::Format(wxT("(putprop '*SELECTION* (float %s) 'START)\n"),
802  mProps += wxString::Format(wxT("(putprop '*SELECTION* (float %s) 'END)\n"),
804  mProps += wxString::Format(wxT("(putprop '*SELECTION* (list %s) 'TRACKS)\n"), waveTrackList);
805  mProps += wxString::Format(wxT("(putprop '*SELECTION* %d 'CHANNELS)\n"), mNumSelectedChannels);
806  }
807 
808  // Nyquist Prompt does not require a selection, but effects do.
809  if (!bOnePassTool && (mNumSelectedChannels == 0)) {
810  auto message = XO("Audio selection required.");
812  message,
813  wxOK | wxCENTRE | wxICON_EXCLAMATION,
814  XO("Nyquist Error") );
815  }
816 
818  if (!bOnePassTool)
819  pRange.emplace(mOutputTracks->Selected< WaveTrack >() + &Track::IsLeader);
820 
821  // Keep track of whether the current track is first selected in its sync-lock group
822  // (we have no idea what the length of the returned audio will be, so we have
823  // to handle sync-lock group behavior the "old" way).
824  mFirstInGroup = true;
825  Track *gtLast = NULL;
826 
827  for (;
828  bOnePassTool || pRange->first != pRange->second;
829  (void) (!pRange || (++pRange->first, true))
830  ) {
831  // Prepare to accumulate more debug output in OutputCallback
833  mDebugOutput = Verbatim( "%s" ).Format( std::cref( mDebugOutputStr ) );
834 
835  mCurTrack[0] = pRange ? *pRange->first : nullptr;
836  mCurNumChannels = 1;
837  if ( (mT1 >= mT0) || bOnePassTool ) {
838  if (bOnePassTool) {
839  }
840  else {
841  auto channels = TrackList::Channels(mCurTrack[0]);
842  if (channels.size() > 1) {
843  // TODO: more-than-two-channels
844  // Pay attention to consistency of mNumSelectedChannels
845  // with the running tally made by this loop!
846  mCurNumChannels = 2;
847 
848  mCurTrack[1] = * ++ channels.first;
849  if (mCurTrack[1]->GetRate() != mCurTrack[0]->GetRate()) {
851  XO(
852 "Sorry, cannot apply effect on stereo tracks where the tracks don't match."),
853  wxOK | wxCENTRE );
854  success = false;
855  goto finish;
856  }
858  }
859 
860  // Check whether we're in the same group as the last selected track
861  Track *gt = *TrackList::SyncLockGroup(mCurTrack[0]).first;
862  mFirstInGroup = !gtLast || (gtLast != gt);
863  gtLast = gt;
864 
866  auto end = mCurTrack[0]->TimeToLongSamples(mT1);
867  mCurLen = end - mCurStart[0];
868 
869  if (mCurLen > NYQ_MAX_LEN) {
870  float hours = (float)NYQ_MAX_LEN / (44100 * 60 * 60);
871  const auto message =
872  XO(
873 "Selection too long for Nyquist code.\nMaximum allowed selection is %ld samples\n(about %.1f hours at 44100 Hz sample rate).")
874  .Format((long)NYQ_MAX_LEN, hours);
876  message,
877  wxOK | wxCENTRE,
878  XO("Nyquist Error") );
879  if (!mProjectChanged)
880  em.SetSkipStateFlag(true);
881  return false;
882  }
883 
885  }
886 
887  mProgressIn = 0.0;
888  mProgressOut = 0.0;
889 
890  // libnyquist breaks except in LC_NUMERIC=="C".
891  //
892  // Note that we must set the locale to "C" even before calling
893  // nyx_init() because otherwise some effects will not work!
894  //
895  // MB: setlocale is not thread-safe. Should use uselocale()
896  // if available, or fix libnyquist to be locale-independent.
897  // See also http://bugzilla.audacityteam.org/show_bug.cgi?id=642#c9
898  // for further info about this thread safety question.
899  wxString prevlocale = wxSetlocale(LC_NUMERIC, NULL);
900  wxSetlocale(LC_NUMERIC, wxString(wxT("C")));
901 
902  nyx_init();
903  nyx_set_os_callback(StaticOSCallback, (void *)this);
904  nyx_capture_output(StaticOutputCallback, (void *)this);
905 
906  auto cleanup = finally( [&] {
907  nyx_capture_output(NULL, (void *)NULL);
908  nyx_set_os_callback(NULL, (void *)NULL);
909  nyx_cleanup();
910  } );
911 
912 
913  if (mVersion >= 4)
914  {
915  mPerTrackProps = wxEmptyString;
916  wxString lowHz = wxT("nil");
917  wxString highHz = wxT("nil");
918  wxString centerHz = wxT("nil");
919  wxString bandwidth = wxT("nil");
920 
921 #if defined(EXPERIMENTAL_SPECTRAL_EDITING)
922  if (mF0 >= 0.0) {
923  lowHz.Printf(wxT("(float %s)"), Internat::ToString(mF0));
924  }
925 
926  if (mF1 >= 0.0) {
927  highHz.Printf(wxT("(float %s)"), Internat::ToString(mF1));
928  }
929 
930  if ((mF0 >= 0.0) && (mF1 >= 0.0)) {
931  centerHz.Printf(wxT("(float %s)"), Internat::ToString(sqrt(mF0 * mF1)));
932  }
933 
934  if ((mF0 > 0.0) && (mF1 >= mF0)) {
935  // with very small values, bandwidth calculation may be inf.
936  // (Observed on Linux)
937  double bw = log(mF1 / mF0) / log(2.0);
938  if (!std::isinf(bw)) {
939  bandwidth.Printf(wxT("(float %s)"), Internat::ToString(bw));
940  }
941  }
942 
943 #endif
944  mPerTrackProps += wxString::Format(wxT("(putprop '*SELECTION* %s 'LOW-HZ)\n"), lowHz);
945  mPerTrackProps += wxString::Format(wxT("(putprop '*SELECTION* %s 'CENTER-HZ)\n"), centerHz);
946  mPerTrackProps += wxString::Format(wxT("(putprop '*SELECTION* %s 'HIGH-HZ)\n"), highHz);
947  mPerTrackProps += wxString::Format(wxT("(putprop '*SELECTION* %s 'BANDWIDTH)\n"), bandwidth);
948  }
949 
950  success = ProcessOne();
951 
952  // Reset previous locale
953  wxSetlocale(LC_NUMERIC, prevlocale);
954 
955  if (!success || bOnePassTool) {
956  goto finish;
957  }
959  }
960 
962  }
963 
964  if (mOutputTime > 0.0) {
965  mT1 = mT0 + mOutputTime;
966  }
967 
968 finish:
969 
970  // Show debug window if trace set in plug-in header and something to show.
971  mDebug = (mTrace && !mDebugOutput.Translation().empty())? true : mDebug;
972 
973  if (mDebug && !mRedirectOutput) {
975  mName,
976  XO("Debug Output: "),
977  mDebugOutput);
978  dlog.CentreOnParent();
979  dlog.ShowModal();
980  }
981 
982  // Has rug been pulled from under us by some effect done within Nyquist??
983  if( !bOnePassTool && ( nEffectsSoFar == nEffectsDone ))
984  ReplaceProcessedTracks(success);
985  else{
986  ReplaceProcessedTracks(false); // Do not use the results.
987  // Selection is to be set to whatever it is in the project.
988  auto project = FindProject();
989  if (project) {
990  auto &selectedRegion = ViewInfo::Get( *project ).selectedRegion;
991  mT0 = selectedRegion.t0();
992  mT1 = selectedRegion.t1();
993  }
994  else {
995  mT0 = 0;
996  mT1 = -1;
997  }
998 
999  }
1000 
1001  if (!mProjectChanged)
1002  em.SetSkipStateFlag(true);
1003 
1004  return success;
1005 }
1006 
1008  wxWindow &parent, const EffectDialogFactory &factory, bool forceModal)
1009 {
1010  bool res = true;
1012  // Show the normal (prompt or effect) interface
1013  res = Effect::ShowInterface(parent, factory, forceModal);
1014  }
1015 
1016 
1017  // Remember if the user clicked debug
1018  mDebug = (mUIResultID == eDebugID);
1019 
1020  // We're done if the user clicked "Close", we are not the Nyquist Prompt,
1021  // or the program currently loaded into the prompt doesn't have a UI.
1022  if (!res || !mIsPrompt || mControls.size() == 0)
1023  {
1024  return res;
1025  }
1026 
1028 
1029  if (IsBatchProcessing())
1030  {
1031  effect.SetBatchProcessing(true);
1032  effect.SetCommand(mInputCmd);
1033 
1034  CommandParameters cp;
1036  effect.SetAutomationParameters(cp);
1037 
1038  // Show the normal (prompt or effect) interface
1039  res = effect.ShowInterface(parent, factory, forceModal);
1040  if (res)
1041  {
1042  CommandParameters cp;
1043  effect.GetAutomationParameters(cp);
1045  }
1046  }
1047  else
1048  {
1049  effect.SetCommand(mInputCmd);
1050  effect.mDebug = (mUIResultID == eDebugID);
1051  res = Delegate(effect, parent, factory);
1052  mT0 = effect.mT0;
1053  mT1 = effect.mT1;
1054  }
1055 
1056  return res;
1057 }
1058 
1060 {
1061  if (mIsPrompt)
1062  {
1063  BuildPromptWindow(S);
1064  }
1065  else
1066  {
1067  BuildEffectWindow(S);
1068  }
1069 
1071 }
1072 
1074 {
1075  mUIParent->TransferDataToWindow();
1076 
1077  bool success;
1078  if (mIsPrompt)
1079  {
1080  success = TransferDataToPromptWindow();
1081  }
1082  else
1083  {
1084  success = TransferDataToEffectWindow();
1085  }
1086 
1087  if (success)
1088  {
1090  }
1091 
1092  return success;
1093 }
1094 
1096 {
1097  if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
1098  {
1099  return false;
1100  }
1101 
1102  if (mIsPrompt)
1103  {
1105  }
1107 }
1108 
1109 // NyquistEffect implementation
1110 
1112 {
1113  mpException = {};
1114 
1115  nyx_rval rval;
1116 
1117  wxString cmd;
1118  cmd += wxT("(snd-set-latency 0.1)");
1119 
1120  // A tool may be using AUD-DO which will potentially invalidate *TRACK*
1121  // so tools do not get *TRACK*.
1122  if (GetType() == EffectTypeTool)
1123  cmd += wxT("(setf S 0.25)\n"); // No Track.
1124  else if (mVersion >= 4) {
1125  nyx_set_audio_name("*TRACK*");
1126  cmd += wxT("(setf S 0.25)\n");
1127  }
1128  else {
1129  nyx_set_audio_name("S");
1130  cmd += wxT("(setf *TRACK* '*unbound*)\n");
1131  }
1132 
1133  if(mVersion >= 4) {
1134  cmd += mProps;
1135  cmd += mPerTrackProps;
1136  }
1137 
1138  if( (mVersion >= 4) && (GetType() != EffectTypeTool) ) {
1139  // Set the track TYPE and VIEW properties
1140  wxString type;
1141  wxString view;
1142  wxString bitFormat;
1143  wxString spectralEditp;
1144 
1145  mCurTrack[0]->TypeSwitch(
1146  [&](const WaveTrack *wt) {
1147  type = wxT("wave");
1148  spectralEditp = mCurTrack[0]->GetSpectrogramSettings().SpectralSelectionEnabled()? wxT("T") : wxT("NIL");
1149  auto displays = WaveTrackView::Get( *wt ).GetDisplays();
1150  auto format = [&]( decltype(displays[0]) display ) {
1151  // Get the English name of the view type, without menu codes,
1152  // as a string that Lisp can examine
1153  return wxString::Format( wxT("\"%s\""),
1154  display.name.Stripped().Debug() );
1155  };
1156  if (displays.empty())
1157  view = wxT("NIL");
1158  else if (displays.size() == 1)
1159  view = format( displays[0] );
1160  else {
1161  view = wxT("(list");
1162  for ( auto display : displays )
1163  view += wxString(wxT(" ")) + format( display );
1164  view += wxT(")");
1165  }
1166  },
1167 #if defined(USE_MIDI)
1168  [&](const NoteTrack *) {
1169  type = wxT("midi");
1170  view = wxT("\"Midi\"");
1171  },
1172 #endif
1173  [&](const LabelTrack *) {
1174  type = wxT("label");
1175  view = wxT("\"Label\"");
1176  },
1177  [&](const TimeTrack *) {
1178  type = wxT("time");
1179  view = wxT("\"Time\"");
1180  }
1181  );
1182 
1183  cmd += wxString::Format(wxT("(putprop '*TRACK* %d 'INDEX)\n"), ++mTrackIndex);
1184  cmd += wxString::Format(wxT("(putprop '*TRACK* \"%s\" 'NAME)\n"), EscapeString(mCurTrack[0]->GetName()));
1185  cmd += wxString::Format(wxT("(putprop '*TRACK* \"%s\" 'TYPE)\n"), type);
1186  // Note: "View" property may change when Audacity's choice of track views has stabilized.
1187  cmd += wxString::Format(wxT("(putprop '*TRACK* %s 'VIEW)\n"), view);
1188  cmd += wxString::Format(wxT("(putprop '*TRACK* %d 'CHANNELS)\n"), mCurNumChannels);
1189 
1190  //NOTE: Audacity 2.1.3 True if spectral selection is enabled regardless of track view.
1191  cmd += wxString::Format(wxT("(putprop '*TRACK* %s 'SPECTRAL-EDIT-ENABLED)\n"), spectralEditp);
1192 
1193  auto channels = TrackList::Channels( mCurTrack[0] );
1194  double startTime = channels.min( &Track::GetStartTime );
1195  double endTime = channels.max( &Track::GetEndTime );
1196 
1197  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'START-TIME)\n"),
1198  Internat::ToString(startTime));
1199  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'END-TIME)\n"),
1200  Internat::ToString(endTime));
1201  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'GAIN)\n"),
1202  Internat::ToString(mCurTrack[0]->GetGain()));
1203  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'PAN)\n"),
1204  Internat::ToString(mCurTrack[0]->GetPan()));
1205  cmd += wxString::Format(wxT("(putprop '*TRACK* (float %s) 'RATE)\n"),
1206  Internat::ToString(mCurTrack[0]->GetRate()));
1207 
1208  switch (mCurTrack[0]->GetSampleFormat())
1209  {
1210  case int16Sample:
1211  bitFormat = wxT("16");
1212  break;
1213  case int24Sample:
1214  bitFormat = wxT("24");
1215  break;
1216  case floatSample:
1217  bitFormat = wxT("32.0");
1218  break;
1219  }
1220  cmd += wxString::Format(wxT("(putprop '*TRACK* %s 'FORMAT)\n"), bitFormat);
1221 
1222  float maxPeakLevel = 0.0; // Deprecated as of 2.1.3
1223  wxString clips, peakString, rmsString;
1224  for (size_t i = 0; i < mCurNumChannels; i++) {
1225  auto ca = mCurTrack[i]->SortedClipArray();
1226  float maxPeak = 0.0;
1227 
1228  // A list of clips for mono, or an array of lists for multi-channel.
1229  if (mCurNumChannels > 1) {
1230  clips += wxT("(list ");
1231  }
1232  // Each clip is a list (start-time, end-time)
1233  // Limit number of clips added to avoid argument stack overflow error (bug 2300).
1234  for (size_t i=0; i<ca.size(); i++) {
1235  if (i < 1000) {
1236  clips += wxString::Format(wxT("(list (float %s) (float %s))"),
1237  Internat::ToString(ca[i]->GetStartTime()),
1238  Internat::ToString(ca[i]->GetEndTime()));
1239  } else if (i == 1000) {
1240  // If final clip is NIL, plug-in developer knows there are more than 1000 clips in channel.
1241  clips += "NIL";
1242  } else if (i > 1000) {
1243  break;
1244  }
1245  }
1246  if (mCurNumChannels > 1) clips += wxT(" )");
1247 
1248  float min, max;
1249  auto pair = mCurTrack[i]->GetMinMax(mT0, mT1); // may throw
1250  min = pair.first, max = pair.second;
1251  maxPeak = wxMax(wxMax(fabs(min), fabs(max)), maxPeak);
1252  maxPeakLevel = wxMax(maxPeakLevel, maxPeak);
1253 
1254  // On Debian, NaN samples give maxPeak = 3.40282e+38 (FLT_MAX)
1255  if (!std::isinf(maxPeak) && !std::isnan(maxPeak) && (maxPeak < FLT_MAX)) {
1256  peakString += wxString::Format(wxT("(float %s) "), Internat::ToString(maxPeak));
1257  } else {
1258  peakString += wxT("nil ");
1259  }
1260 
1261  float rms = mCurTrack[i]->GetRMS(mT0, mT1); // may throw
1262  if (!std::isinf(rms) && !std::isnan(rms)) {
1263  rmsString += wxString::Format(wxT("(float %s) "), Internat::ToString(rms));
1264  } else {
1265  rmsString += wxT("NIL ");
1266  }
1267  }
1268  // A list of clips for mono, or an array of lists for multi-channel.
1269  cmd += wxString::Format(wxT("(putprop '*TRACK* %s%s ) 'CLIPS)\n"),
1270  (mCurNumChannels == 1) ? wxT("(list ") : wxT("(vector "),
1271  clips);
1272 
1273  (mCurNumChannels > 1)?
1274  cmd += wxString::Format(wxT("(putprop '*SELECTION* (vector %s) 'PEAK)\n"), peakString) :
1275  cmd += wxString::Format(wxT("(putprop '*SELECTION* %s 'PEAK)\n"), peakString);
1276 
1277  if (!std::isinf(maxPeakLevel) && !std::isnan(maxPeakLevel) && (maxPeakLevel < FLT_MAX)) {
1278  cmd += wxString::Format(wxT("(putprop '*SELECTION* (float %s) 'PEAK-LEVEL)\n"),
1279  Internat::ToString(maxPeakLevel));
1280  }
1281 
1282  (mCurNumChannels > 1)?
1283  cmd += wxString::Format(wxT("(putprop '*SELECTION* (vector %s) 'RMS)\n"), rmsString) :
1284  cmd += wxString::Format(wxT("(putprop '*SELECTION* %s 'RMS)\n"), rmsString);
1285  }
1286 
1287  // If in tool mode, then we don't do anything with the track and selection.
1288  if (GetType() == EffectTypeTool) {
1289  nyx_set_audio_params(44100, 0);
1290  }
1291  else if (GetType() == EffectTypeGenerate) {
1292  nyx_set_audio_params(mCurTrack[0]->GetRate(), 0);
1293  }
1294  else {
1295  auto curLen = mCurLen.as_long_long();
1296  nyx_set_audio_params(mCurTrack[0]->GetRate(), curLen);
1297 
1298  nyx_set_input_audio(StaticGetCallback, (void *)this,
1299  (int)mCurNumChannels,
1300  curLen, mCurTrack[0]->GetRate());
1301  }
1302 
1303  // Restore the Nyquist sixteenth note symbol for Generate plug-ins.
1304  // See http://bugzilla.audacityteam.org/show_bug.cgi?id=490.
1305  if (GetType() == EffectTypeGenerate) {
1306  cmd += wxT("(setf s 0.25)\n");
1307  }
1308 
1309  if (mDebug || mTrace) {
1310  cmd += wxT("(setf *tracenable* T)\n");
1311  if (mExternal) {
1312  cmd += wxT("(setf *breakenable* T)\n");
1313  }
1314  }
1315  else {
1316  // Explicitly disable backtrace and prevent values
1317  // from being carried through to the output.
1318  // This should be the final command before evaluating the Nyquist script.
1319  cmd += wxT("(setf *tracenable* NIL)\n");
1320  }
1321 
1322  for (unsigned int j = 0; j < mControls.size(); j++) {
1323  if (mControls[j].type == NYQ_CTRL_FLOAT || mControls[j].type == NYQ_CTRL_FLOAT_TEXT ||
1324  mControls[j].type == NYQ_CTRL_TIME) {
1325  // We use Internat::ToString() rather than "%f" here because we
1326  // always have to use the dot as decimal separator when giving
1327  // numbers to Nyquist, whereas using "%f" will use the user's
1328  // decimal separator which may be a comma in some countries.
1329  cmd += wxString::Format(wxT("(setf %s %s)\n"),
1330  mControls[j].var,
1331  Internat::ToString(mControls[j].val, 14));
1332  }
1333  else if (mControls[j].type == NYQ_CTRL_INT ||
1334  mControls[j].type == NYQ_CTRL_INT_TEXT ||
1335  mControls[j].type == NYQ_CTRL_CHOICE) {
1336  cmd += wxString::Format(wxT("(setf %s %d)\n"),
1337  mControls[j].var,
1338  (int)(mControls[j].val));
1339  }
1340  else if (mControls[j].type == NYQ_CTRL_STRING || mControls[j].type == NYQ_CTRL_FILE) {
1341  cmd += wxT("(setf ");
1342  // restrict variable names to 7-bit ASCII:
1343  cmd += mControls[j].var;
1344  cmd += wxT(" \"");
1345  cmd += EscapeString(mControls[j].valStr); // unrestricted value will become quoted UTF-8
1346  cmd += wxT("\")\n");
1347  }
1348  }
1349 
1350  if (mIsSal) {
1351  wxString str = EscapeString(mCmd);
1352  // this is tricky: we need SAL to call main so that we can get a
1353  // SAL traceback in the event of an error (sal-compile catches the
1354  // error and calls sal-error-output), but SAL does not return values.
1355  // We will catch the value in a special global aud:result and if no
1356  // error occurs, we will grab the value with a LISP expression
1357  str += wxT("\nset aud:result = main()\n");
1358 
1359  if (mDebug || mTrace) {
1360  // since we're about to evaluate SAL, remove LISP trace enable and
1361  // break enable (which stops SAL processing) and turn on SAL stack
1362  // trace
1363  cmd += wxT("(setf *tracenable* nil)\n");
1364  cmd += wxT("(setf *breakenable* nil)\n");
1365  cmd += wxT("(setf *sal-traceback* t)\n");
1366  }
1367 
1368  if (mCompiler) {
1369  cmd += wxT("(setf *sal-compiler-debug* t)\n");
1370  }
1371 
1372  cmd += wxT("(setf *sal-call-stack* nil)\n");
1373  // if we do not set this here and an error occurs in main, another
1374  // error will be raised when we try to return the value of aud:result
1375  // which is unbound
1376  cmd += wxT("(setf aud:result nil)\n");
1377  cmd += wxT("(sal-compile-audacity \"") + str + wxT("\" t t nil)\n");
1378  // Capture the value returned by main (saved in aud:result), but
1379  // set aud:result to nil so sound results can be evaluated without
1380  // retaining audio in memory
1381  cmd += wxT("(prog1 aud:result (setf aud:result nil))\n");
1382  }
1383  else {
1384  cmd += mCmd;
1385  }
1386 
1387  // Put the fetch buffers in a clean initial state
1388  for (size_t i = 0; i < mCurNumChannels; i++)
1389  mCurBuffer[i].Free();
1390 
1391  // Guarantee release of memory when done
1392  auto cleanup = finally( [&] {
1393  for (size_t i = 0; i < mCurNumChannels; i++)
1394  mCurBuffer[i].Free();
1395  } );
1396 
1397  // Evaluate the expression, which may invoke the get callback, but often does
1398  // not, leaving that to delayed evaluation of the output sound
1399  rval = nyx_eval_expression(cmd.mb_str(wxConvUTF8));
1400 
1401  // If we're not showing debug window, log errors and warnings:
1402  const auto output = mDebugOutput.Translation();
1403  if (!output.empty() && !mDebug && !mTrace) {
1404  /* i18n-hint: An effect "returned" a message.*/
1405  wxLogMessage(wxT("\'%s\' returned:\n%s"),
1406  mName.Translation(), output);
1407  }
1408 
1409  // Audacity has no idea how long Nyquist processing will take, but
1410  // can monitor audio being returned.
1411  // Anything other than audio should be returned almost instantly
1412  // so notify the user that process has completed (bug 558)
1413  if ((rval != nyx_audio) && ((mCount + mCurNumChannels) == mNumSelectedChannels)) {
1414  if (mCurNumChannels == 1) {
1415  TrackProgress(mCount, 1.0, XO("Processing complete."));
1416  }
1417  else {
1418  TrackGroupProgress(mCount, 1.0, XO("Processing complete."));
1419  }
1420  }
1421 
1422  if ((rval == nyx_audio) && (GetType() == EffectTypeTool)) {
1423  // Catch this first so that we can also handle other errors.
1424  mDebugOutput =
1425  /* i18n-hint: Don't translate ';type tool'. */
1426  XO("';type tool' effects cannot return audio from Nyquist.\n")
1427  + mDebugOutput;
1428  rval = nyx_error;
1429  }
1430 
1431  if ((rval == nyx_labels) && (GetType() == EffectTypeTool)) {
1432  // Catch this first so that we can also handle other errors.
1433  mDebugOutput =
1434  /* i18n-hint: Don't translate ';type tool'. */
1435  XO("';type tool' effects cannot return labels from Nyquist.\n")
1436  + mDebugOutput;
1437  rval = nyx_error;
1438  }
1439 
1440  if (rval == nyx_error) {
1441  // Return value is not valid type.
1442  // Show error in debug window if trace enabled, otherwise log.
1443  if (mTrace) {
1444  /* i18n-hint: "%s" is replaced by name of plug-in.*/
1445  mDebugOutput = XO("nyx_error returned from %s.\n")
1446  .Format( mName.empty() ? XO("plug-in") : mName )
1447  + mDebugOutput;
1448  mDebug = true;
1449  }
1450  else {
1451  wxLogMessage(
1452  "Nyquist returned nyx_error:\n%s", mDebugOutput.Translation());
1453  }
1454  return false;
1455  }
1456 
1457  if (rval == nyx_list) {
1458  wxLogMessage("Nyquist returned nyx_list");
1459  if (GetType() == EffectTypeTool) {
1460  mProjectChanged = true;
1461  } else {
1462  Effect::MessageBox(XO("Nyquist returned a list.") );
1463  }
1464  return true;
1465  }
1466 
1467  if (rval == nyx_string) {
1468  // Assume the string has already been translated within the Lisp runtime
1469  // if necessary, by one of the gettext functions defined below, before it
1470  // is communicated back to C++
1471  auto msg = Verbatim( NyquistToWxString(nyx_get_string()) );
1472  if (!msg.empty()) { // Empty string may be used as a No-Op return value.
1473  Effect::MessageBox( msg );
1474  }
1475  else if (GetType() == EffectTypeTool) {
1476  // ;tools may change the project with aud-do commands so
1477  // it is essential that the state is added to history.
1478  mProjectChanged = true;
1479  return true;
1480  }
1481  else {
1482  // A true no-op.
1483  return true;
1484  }
1485 
1486  // True if not process type.
1487  // If not returning audio from process effect,
1488  // return first result then stop (disables preview)
1489  // but allow all output from Nyquist Prompt.
1490  return (GetType() != EffectTypeProcess || mIsPrompt);
1491  }
1492 
1493  if (rval == nyx_double) {
1494  auto str = XO("Nyquist returned the value: %f")
1495  .Format(nyx_get_double());
1496  Effect::MessageBox( str );
1497  return (GetType() != EffectTypeProcess || mIsPrompt);
1498  }
1499 
1500  if (rval == nyx_int) {
1501  auto str = XO("Nyquist returned the value: %d")
1502  .Format(nyx_get_int());
1503  Effect::MessageBox( str );
1504  return (GetType() != EffectTypeProcess || mIsPrompt);
1505  }
1506 
1507  if (rval == nyx_labels) {
1508  mProjectChanged = true;
1509  unsigned int numLabels = nyx_get_num_labels();
1510  unsigned int l;
1511  auto ltrack = * mOutputTracks->Any< LabelTrack >().begin();
1512  if (!ltrack) {
1513  ltrack = static_cast<LabelTrack*>(
1514  AddToOutputTracks(std::make_shared<LabelTrack>()));
1515  }
1516 
1517  for (l = 0; l < numLabels; l++) {
1518  double t0, t1;
1519  const char *str;
1520 
1521  // PRL: to do:
1522  // let Nyquist analyzers define more complicated selections
1523  nyx_get_label(l, &t0, &t1, &str);
1524 
1525  ltrack->AddLabel(SelectedRegion(t0 + mT0, t1 + mT0), UTF8CTOWX(str));
1526  }
1527  return (GetType() != EffectTypeProcess || mIsPrompt);
1528  }
1529 
1530  wxASSERT(rval == nyx_audio);
1531 
1532  int outChannels = nyx_get_audio_num_channels();
1533  if (outChannels > (int)mCurNumChannels) {
1534  Effect::MessageBox( XO("Nyquist returned too many audio channels.\n") );
1535  return false;
1536  }
1537 
1538  if (outChannels == -1) {
1540  XO("Nyquist returned one audio channel as an array.\n") );
1541  return false;
1542  }
1543 
1544  if (outChannels == 0) {
1545  Effect::MessageBox( XO("Nyquist returned an empty array.\n") );
1546  return false;
1547  }
1548 
1549  std::shared_ptr<WaveTrack> outputTrack[2];
1550 
1551  double rate = mCurTrack[0]->GetRate();
1552  for (int i = 0; i < outChannels; i++) {
1553  if (outChannels == (int)mCurNumChannels) {
1554  rate = mCurTrack[i]->GetRate();
1555  }
1556 
1557  outputTrack[i] = mCurTrack[i]->EmptyCopy();
1558  outputTrack[i]->SetRate( rate );
1559 
1560  // Clean the initial buffer states again for the get callbacks
1561  // -- is this really needed?
1562  mCurBuffer[i].Free();
1563  }
1564 
1565  // Now fully evaluate the sound
1566  int success;
1567  {
1568  auto vr0 = valueRestorer( mOutputTrack[0], outputTrack[0].get() );
1569  auto vr1 = valueRestorer( mOutputTrack[1], outputTrack[1].get() );
1570  success = nyx_get_audio(StaticPutCallback, (void *)this);
1571  }
1572 
1573  // See if GetCallback found read errors
1574  {
1575  auto pException = mpException;
1576  mpException = {};
1577  if (pException)
1578  std::rethrow_exception( pException );
1579  }
1580 
1581  if (!success)
1582  return false;
1583 
1584  for (int i = 0; i < outChannels; i++) {
1585  outputTrack[i]->Flush();
1586  mOutputTime = outputTrack[i]->GetEndTime();
1587 
1588  if (mOutputTime <= 0) {
1589  Effect::MessageBox( XO("Nyquist returned nil audio.\n") );
1590  return false;
1591  }
1592  }
1593 
1594  for (size_t i = 0; i < mCurNumChannels; i++) {
1595  WaveTrack *out;
1596 
1597  if (outChannels == (int)mCurNumChannels) {
1598  out = outputTrack[i].get();
1599  }
1600  else {
1601  out = outputTrack[0].get();
1602  }
1603 
1604  if (mMergeClips < 0) {
1605  // Use sample counts to determine default behaviour - times will rarely be equal.
1606  bool bMergeClips = (out->TimeToLongSamples(mT0) + out->TimeToLongSamples(mOutputTime) ==
1607  out->TimeToLongSamples(mT1));
1608  mCurTrack[i]->ClearAndPaste(mT0, mT1, out, mRestoreSplits, bMergeClips);
1609  }
1610  else {
1612  }
1613 
1614  // If we were first in the group adjust non-selected group tracks
1615  if (mFirstInGroup) {
1616  for (auto t : TrackList::SyncLockGroup(mCurTrack[i]))
1617  {
1618  if (!t->GetSelected() && t->IsSyncLockSelected()) {
1619  t->SyncLockAdjust(mT1, mT0 + out->GetEndTime());
1620  }
1621  }
1622  }
1623 
1624  // Only the first channel can be first in its group
1625  mFirstInGroup = false;
1626  }
1627 
1628  mProjectChanged = true;
1629  return true;
1630 }
1631 
1632 // ============================================================================
1633 // NyquistEffect Implementation
1634 // ============================================================================
1635 
1636 wxString NyquistEffect::NyquistToWxString(const char *nyqString)
1637 {
1638  wxString str(nyqString, wxConvUTF8);
1639  if (nyqString != NULL && nyqString[0] && str.empty()) {
1640  // invalid UTF-8 string, convert as Latin-1
1641  str = _("[Warning: Nyquist returned invalid UTF-8 string, converted here as Latin-1]");
1642  // TODO: internationalization of strings from Nyquist effects, at least
1643  // from those shipped with Audacity
1644  str += LAT1CTOWX(nyqString);
1645  }
1646  return str;
1647 }
1648 
1649 wxString NyquistEffect::EscapeString(const wxString & inStr)
1650 {
1651  wxString str = inStr;
1652 
1653  str.Replace(wxT("\\"), wxT("\\\\"));
1654  str.Replace(wxT("\""), wxT("\\\""));
1655 
1656  return str;
1657 }
1658 
1659 std::vector<EnumValueSymbol> NyquistEffect::ParseChoice(const wxString & text)
1660 {
1661  std::vector<EnumValueSymbol> results;
1662  if (text[0] == wxT('(')) {
1663  // New style: expecting a Lisp-like list of strings
1664  Tokenizer tzer;
1665  tzer.Tokenize(text, true, 1, 1);
1666  auto &choices = tzer.tokens;
1667  wxString extra;
1668  for (auto &choice : choices) {
1669  auto label = UnQuote(choice, true, &extra);
1670  if (extra.empty())
1671  results.push_back( TranslatableString{ label, {} } );
1672  else
1673  results.push_back(
1674  { extra, TranslatableString{ label, {} } } );
1675  }
1676  }
1677  else {
1678  // Old style: expecting a comma-separated list of
1679  // un-internationalized names, ignoring leading and trailing spaces
1680  // on each; and the whole may be quoted
1681  auto choices = wxStringTokenize(
1682  text[0] == wxT('"') ? text.Mid(1, text.length() - 2) : text,
1683  wxT(",")
1684  );
1685  for (auto &choice : choices)
1686  results.push_back( { choice.Trim(true).Trim(false) } );
1687  }
1688  return results;
1689 }
1690 
1692 {
1693  // todo: error handling
1694  FileExtensions results;
1695  if (text[0] == wxT('(')) {
1696  Tokenizer tzer;
1697  tzer.Tokenize(text, true, 1, 1);
1698  for (const auto &token : tzer.tokens)
1699  results.push_back( UnQuote( token ) );
1700  }
1701  return results;
1702 }
1703 
1705 {
1706  // todo: error handling
1707  FileNames::FileType result;
1708  if (text[0] == wxT('(')) {
1709  Tokenizer tzer;
1710  tzer.Tokenize(text, true, 1, 1);
1711  auto &tokens = tzer.tokens;
1712  if ( tokens.size() == 2 )
1713  result =
1714  { UnQuoteMsgid( tokens[0] ), ParseFileExtensions( tokens[1] ) };
1715  }
1716  return result;
1717 }
1718 
1720 {
1721  // todo: error handling
1722  FileNames::FileTypes results;
1723  if (text[0] == wxT('(')) {
1724  Tokenizer tzer;
1725  tzer.Tokenize(text, true, 1, 1);
1726  auto &types = tzer.tokens;
1727  if ( !types.empty() && types[0][0] == wxT('(') )
1728  for (auto &type : types)
1729  results.push_back( ParseFileType( type ) );
1730  }
1731  if ( results.empty() ) {
1732  // Old-style is a specially formatted string, maybe translated
1733  // Parse it for compatibility
1734  auto str = UnQuote( text );
1735  auto pieces = wxSplit( str, '|' );
1736  // Should have an even number
1737  auto size = pieces.size();
1738  if ( size % 2 == 1 )
1739  --size, pieces.pop_back();
1740  for ( size_t ii = 0; ii < size; ii += 2 ) {
1741  FileExtensions extensions;
1742  auto extensionStrings = wxSplit( pieces[ii + 1], ';' );
1743  for ( const auto &extensionString : extensionStrings )
1744  if ( extensionString.StartsWith( wxT("*.") ) ) {
1745  auto ext = extensionString.substr( 2 );
1746  if (ext == wxT("*"))
1747  // "*.*" to match all
1748  ext.clear();
1749  extensions.push_back( ext );
1750  }
1751  results.push_back( { Verbatim( pieces[ii] ), extensions } );
1752  }
1753  }
1754  return results;
1755 }
1756 
1758 {
1759  mRedirectOutput = true;
1760 }
1761 
1762 void NyquistEffect::SetCommand(const wxString &cmd)
1763 {
1764  mExternal = true;
1765 
1766  if (cmd.size()) {
1767  ParseCommand(cmd);
1768  }
1769 }
1770 
1772 {
1773  mBreak = true;
1774 }
1775 
1777 {
1778  mCont = true;
1779 }
1780 
1782 {
1783  mStop = true;
1784 }
1785 
1786 TranslatableString NyquistEffect::UnQuoteMsgid(const wxString &s, bool allowParens,
1787  wxString *pExtraString)
1788 {
1789  if (pExtraString)
1790  *pExtraString = wxString{};
1791 
1792  int len = s.length();
1793  if (len >= 2 && s[0] == wxT('\"') && s[len - 1] == wxT('\"')) {
1794  auto unquoted = s.Mid(1, len - 2);
1795  // Sorry, no context strings, yet
1796  return TranslatableString{ unquoted, {} };
1797  }
1798  else if (allowParens &&
1799  len >= 2 && s[0] == wxT('(') && s[len - 1] == wxT(')')) {
1800  Tokenizer tzer;
1801  tzer.Tokenize(s, true, 1, 1);
1802  auto &tokens = tzer.tokens;
1803  if (tokens.size() > 1) {
1804  if (pExtraString && tokens[1][0] == '(') {
1805  // A choice with a distinct internal string form like
1806  // ("InternalString" (_ "Visible string"))
1807  // Recur to find the two strings
1808  *pExtraString = UnQuote(tokens[0], false);
1809  return UnQuoteMsgid(tokens[1]);
1810  }
1811  else {
1812  // Assume the first token was _ -- we don't check that
1813  // And the second is the string, which is internationalized
1814  // Sorry, no context strings, yet
1815  return UnQuoteMsgid( tokens[1], false );
1816  }
1817  }
1818  else
1819  return {};
1820  }
1821  else
1822  // If string was not quoted, assume no translation exists
1823  return Verbatim( s );
1824 }
1825 
1826 wxString NyquistEffect::UnQuote(const wxString &s, bool allowParens,
1827  wxString *pExtraString)
1828 {
1829  return UnQuoteMsgid( s, allowParens, pExtraString ).Translation();
1830 }
1831 
1832 double NyquistEffect::GetCtrlValue(const wxString &s)
1833 {
1834  /* For this to work correctly requires that the plug-in header is
1835  * parsed on each run so that the correct value for "half-srate" may
1836  * be determined.
1837  *
1838  auto project = FindProject();
1839  if (project && s.IsSameAs(wxT("half-srate"), false)) {
1840  auto rate =
1841  TrackList::Get( *project ).Selected< const WaveTrack >()
1842  .min( &WaveTrack::GetRate );
1843  return (rate / 2.0);
1844  }
1845  */
1846 
1847  return Internat::CompatibleToDouble(s);
1848 }
1849 
1851  const wxString &line, bool eof,
1852  size_t trimStart, size_t trimEnd)
1853 {
1854  auto endToken = [&]{
1855  if (!tok.empty()) {
1856  tokens.push_back(tok);
1857  tok = wxT("");
1858  }
1859  };
1860 
1861  for (auto c :
1862  make_iterator_range(line.begin() + trimStart, line.end() - trimEnd)) {
1863  if (q && !sl && c == wxT('\\')) {
1864  // begin escaped character, only within quotes
1865  sl = true;
1866  continue;
1867  }
1868 
1869  if (!sl && c == wxT('"')) {
1870  // Unescaped quote
1871  if (!q) {
1872  // start of string
1873  if (!paren)
1874  // finish previous token
1875  endToken();
1876  // Include the delimiter in the token
1877  tok += c;
1878  q = true;
1879  }
1880  else {
1881  // end of string
1882  // Include the delimiter in the token
1883  tok += c;
1884  if (!paren)
1885  endToken();
1886  q = false;
1887  }
1888  }
1889  else if (!q && !paren && (c == wxT(' ') || c == wxT('\t')))
1890  // Unenclosed whitespace
1891  // Separate tokens; don't accumulate this character
1892  endToken();
1893  else if (!q && c == wxT(';'))
1894  // semicolon not in quotes, but maybe in parentheses
1895  // Lisp style comments with ; (but not with #| ... |#) are allowed
1896  // within a wrapped header multi-line, so that i18n hint comments may
1897  // be placed before strings and found by xgettext
1898  break;
1899  else if (!q && c == wxT('(')) {
1900  // Start of list or sublist
1901  if (++paren == 1)
1902  // finish previous token; begin list, including the delimiter
1903  endToken(), tok += c;
1904  else
1905  // defer tokenizing of nested list to a later pass over the token
1906  tok += c;
1907  }
1908  else if (!q && c == wxT(')')) {
1909  // End of list or sublist
1910  if (--paren == 0)
1911  // finish list, including the delimiter
1912  tok += c, endToken();
1913  else if (paren < 0)
1914  // forgive unbalanced right paren
1915  paren = 0, endToken();
1916  else
1917  // nested list; deferred tokenizing
1918  tok += c;
1919  }
1920  else {
1921  if (sl && paren)
1922  // Escaped character in string inside list, to be parsed again
1923  // Put the escape back for the next pass
1924  tok += wxT('\\');
1925  if (sl && !paren && c == 'n')
1926  // Convert \n to newline, the only special escape besides \\ or \"
1927  // But this should not be used if a string needs to localize.
1928  // Instead, simply put a line break in the string.
1929  c = '\n';
1930  tok += c;
1931  }
1932 
1933  sl = false;
1934  }
1935 
1936  if (eof || (!q && !paren)) {
1937  endToken();
1938  return true;
1939  }
1940  else {
1941  // End of line but not of file, and a string or list is yet unclosed
1942  // If a string, accumulate a newline character
1943  if (q)
1944  tok += wxT('\n');
1945  return false;
1946  }
1947 }
1948 
1950  Tokenizer &tzer, const wxString &line, bool eof, bool first)
1951 {
1952  if ( !tzer.Tokenize(line, eof, first ? 1 : 0, 0) )
1953  return false;
1954 
1955  const auto &tokens = tzer.tokens;
1956  int len = tokens.size();
1957  if (len < 1) {
1958  return true;
1959  }
1960 
1961  // Consistency decision is for "plug-in" as the correct spelling
1962  // "plugin" (deprecated) is allowed as an undocumented convenience.
1963  if (len == 2 && tokens[0] == wxT("nyquist") &&
1964  (tokens[1] == wxT("plug-in") || tokens[1] == wxT("plugin"))) {
1965  mOK = true;
1966  return true;
1967  }
1968 
1969  if (len >= 2 && tokens[0] == wxT("type")) {
1970  wxString tok = tokens[1];
1971  mIsTool = false;
1972  if (tok == wxT("tool")) {
1973  mIsTool = true;
1975  // we allow
1976  // ;type tool
1977  // ;type tool process
1978  // ;type tool generate
1979  // ;type tool analyze
1980  // The last three are placed in the tool menu, but are processed as
1981  // process, generate or analyze.
1982  if (len >= 3)
1983  tok = tokens[2];
1984  }
1985 
1986  if (tok == wxT("process")) {
1988  }
1989  else if (tok == wxT("generate")) {
1991  }
1992  else if (tok == wxT("analyze")) {
1994  }
1995 
1996  if (len >= 3 && tokens[2] == wxT("spectral")) {;
1997  mIsSpectral = true;
1998  }
1999  return true;
2000  }
2001 
2002  if (len == 2 && tokens[0] == wxT("codetype")) {
2003  // This will stop ParseProgram() from doing a best guess as program type.
2004  if (tokens[1] == wxT("lisp")) {
2005  mIsSal = false;
2006  mFoundType = true;
2007  }
2008  else if (tokens[1] == wxT("sal")) {
2009  mIsSal = true;
2010  mFoundType = true;
2011  }
2012  return true;
2013  }
2014 
2015  if (len >= 2 && tokens[0] == wxT("debugflags")) {
2016  for (int i = 1; i < len; i++) {
2017  // "trace" sets *tracenable* (LISP) or *sal-traceback* (SAL)
2018  // and displays debug window IF there is anything to show.
2019  if (tokens[i] == wxT("trace")) {
2020  mTrace = true;
2021  }
2022  else if (tokens[i] == wxT("notrace")) {
2023  mTrace = false;
2024  }
2025  else if (tokens[i] == wxT("compiler")) {
2026  mCompiler = true;
2027  }
2028  else if (tokens[i] == wxT("nocompiler")) {
2029  mCompiler = false;
2030  }
2031  }
2032  return true;
2033  }
2034 
2035  // We support versions 1, 2 and 3
2036  // (Version 2 added support for string parameters.)
2037  // (Version 3 added support for choice parameters.)
2038  // (Version 4 added support for project/track/selection information.)
2039  if (len >= 2 && tokens[0] == wxT("version")) {
2040  long v;
2041  tokens[1].ToLong(&v);
2042  if (v < 1 || v > 4) {
2043  // This is an unsupported plug-in version
2044  mOK = false;
2045  mInitError = XO(
2046 "This version of Audacity does not support Nyquist plug-in version %ld")
2047  .Format( v );
2048  return true;
2049  }
2050  mVersion = (int) v;
2051  }
2052 
2053  if (len >= 2 && tokens[0] == wxT("name")) {
2054  auto name = UnQuote(tokens[1]);
2055  // Strip ... from name if it's present, perhaps in third party plug-ins
2056  // Menu system puts ... back if there are any controls
2057  // This redundant naming convention must NOT be followed for
2058  // shipped Nyquist effects with internationalization. Else the msgid
2059  // later looked up will lack the ... and will not be found.
2060  if (name.EndsWith(wxT("...")))
2061  name = name.RemoveLast(3);
2062  mName = TranslatableString{ name, {} };
2063  return true;
2064  }
2065 
2066  if (len >= 2 && tokens[0] == wxT("action")) {
2067  mAction = TranslatableString{ UnQuote(tokens[1]), {} };
2068  return true;
2069  }
2070 
2071  if (len >= 2 && tokens[0] == wxT("info")) {
2072  mInfo = TranslatableString{ UnQuote(tokens[1]), {} };
2073  return true;
2074  }
2075 
2076  if (len >= 2 && tokens[0] == wxT("preview")) {
2077  if (tokens[1] == wxT("enabled") || tokens[1] == wxT("true")) {
2078  mEnablePreview = true;
2079  SetLinearEffectFlag(false);
2080  }
2081  else if (tokens[1] == wxT("linear")) {
2082  mEnablePreview = true;
2083  SetLinearEffectFlag(true);
2084  }
2085  else if (tokens[1] == wxT("selection")) {
2086  mEnablePreview = true;
2088  }
2089  else if (tokens[1] == wxT("disabled") || tokens[1] == wxT("false")) {
2090  mEnablePreview = false;
2091  }
2092  return true;
2093  }
2094 
2095  // Maximum number of samples to be processed. This can help the
2096  // progress bar if effect does not process all of selection.
2097  if (len >= 2 && tokens[0] == wxT("maxlen")) {
2098  long long v; // Note that Nyquist may overflow at > 2^31 samples (bug 439)
2099  tokens[1].ToLongLong(&v);
2100  mMaxLen = (sampleCount) v;
2101  }
2102 
2103 #if defined(EXPERIMENTAL_NYQUIST_SPLIT_CONTROL)
2104  if (len >= 2 && tokens[0] == wxT("mergeclips")) {
2105  long v;
2106  // -1 = auto (default), 0 = don't merge clips, 1 = do merge clips
2107  tokens[1].ToLong(&v);
2108  mMergeClips = v;
2109  return true;
2110  }
2111 
2112  if (len >= 2 && tokens[0] == wxT("restoresplits")) {
2113  long v;
2114  // Splits are restored by default. Set to 0 to prevent.
2115  tokens[1].ToLong(&v);
2116  mRestoreSplits = !!v;
2117  return true;
2118  }
2119 #endif
2120 
2121  if (len >= 2 && tokens[0] == wxT("author")) {
2122  mAuthor = TranslatableString{ UnQuote(tokens[1]), {} };
2123  return true;
2124  }
2125 
2126  if (len >= 2 && tokens[0] == wxT("release")) {
2127  // Value must be quoted if the release version string contains spaces.
2128  mReleaseVersion =
2129  TranslatableString{ UnQuote(tokens[1]), {} };
2130  return true;
2131  }
2132 
2133  if (len >= 2 && tokens[0] == wxT("copyright")) {
2134  mCopyright = TranslatableString{ UnQuote(tokens[1]), {} };
2135  return true;
2136  }
2137 
2138  // Page name in Audacity development manual
2139  if (len >= 2 && tokens[0] == wxT("manpage")) {
2140  // do not translate
2141  mManPage = UnQuote(tokens[1], false);
2142  return true;
2143  }
2144 
2145  // Local Help file
2146  if (len >= 2 && tokens[0] == wxT("helpfile")) {
2147  // do not translate
2148  mHelpFile = UnQuote(tokens[1], false);
2149  return true;
2150  }
2151 
2152  // Debug button may be disabled for release plug-ins.
2153  if (len >= 2 && tokens[0] == wxT("debugbutton")) {
2154  if (tokens[1] == wxT("disabled") || tokens[1] == wxT("false")) {
2155  mDebugButton = false;
2156  }
2157  return true;
2158  }
2159 
2160 
2161  if (len >= 3 && tokens[0] == wxT("control")) {
2162  NyqControl ctrl;
2163 
2164  if (len == 3 && tokens[1] == wxT("text")) {
2165  ctrl.var = tokens[1];
2166  ctrl.label = UnQuote( tokens[2] );
2167  ctrl.type = NYQ_CTRL_TEXT;
2168  }
2169  else if (len >= 5)
2170  {
2171  ctrl.var = tokens[1];
2172  ctrl.name = UnQuote( tokens[2] );
2173  // 3 is type, below
2174  ctrl.label = tokens[4];
2175 
2176  // valStr may or may not be a quoted string
2177  ctrl.valStr = len > 5 ? tokens[5] : wxT("");
2178  ctrl.val = GetCtrlValue(ctrl.valStr);
2179  if (ctrl.valStr.length() > 0 &&
2180  (ctrl.valStr[0] == wxT('(') ||
2181  ctrl.valStr[0] == wxT('"')))
2182  ctrl.valStr = UnQuote( ctrl.valStr );
2183 
2184  // 6 is minimum, below
2185  // 7 is maximum, below
2186 
2187  if (tokens[3] == wxT("string")) {
2188  ctrl.type = NYQ_CTRL_STRING;
2189  ctrl.label = UnQuote( ctrl.label );
2190  }
2191  else if (tokens[3] == wxT("choice")) {
2192  ctrl.type = NYQ_CTRL_CHOICE;
2193  ctrl.choices = ParseChoice(ctrl.label);
2194  ctrl.label = wxT("");
2195  }
2196  else if (tokens[3] == wxT("file")) {
2197  ctrl.type = NYQ_CTRL_FILE;
2198  ctrl.fileTypes = ParseFileTypes(tokens[6]);
2199  // will determine file dialog styles:
2200  ctrl.highStr = UnQuote( tokens[7] );
2201  ctrl.label = UnQuote(ctrl.label);
2202  }
2203  else {
2204  ctrl.label = UnQuote( ctrl.label );
2205 
2206  if (len < 8) {
2207  return true;
2208  }
2209 
2210  if ((tokens[3] == wxT("float")) ||
2211  (tokens[3] == wxT("real"))) // Deprecated
2212  ctrl.type = NYQ_CTRL_FLOAT;
2213  else if (tokens[3] == wxT("int"))
2214  ctrl.type = NYQ_CTRL_INT;
2215  else if (tokens[3] == wxT("float-text"))
2216  ctrl.type = NYQ_CTRL_FLOAT_TEXT;
2217  else if (tokens[3] == wxT("int-text"))
2218  ctrl.type = NYQ_CTRL_INT_TEXT;
2219  else if (tokens[3] == wxT("time"))
2220  ctrl.type = NYQ_CTRL_TIME;
2221  else
2222  {
2223  wxString str;
2224  str.Printf(wxT("Bad Nyquist 'control' type specification: '%s' in plug-in file '%s'.\nControl not created."),
2225  tokens[3], mFileName.GetFullPath());
2226 
2227  // Too disturbing to show alert before Audacity frame is up.
2228  // Effect::MessageBox(
2229  // str,
2230  // wxOK | wxICON_EXCLAMATION,
2231  // XO("Nyquist Warning") );
2232 
2233  // Note that the AudacityApp's mLogger has not yet been created,
2234  // so this brings up an alert box, but after the Audacity frame is up.
2235  wxLogWarning(str);
2236  return true;
2237  }
2238 
2239  ctrl.lowStr = UnQuote( tokens[6] );
2240  if (ctrl.type == NYQ_CTRL_INT_TEXT && ctrl.lowStr.IsSameAs(wxT("nil"), false)) {
2241  ctrl.low = INT_MIN;
2242  }
2243  else if (ctrl.type == NYQ_CTRL_FLOAT_TEXT && ctrl.lowStr.IsSameAs(wxT("nil"), false)) {
2244  ctrl.low = -(FLT_MAX);
2245  }
2246  else if (ctrl.type == NYQ_CTRL_TIME && ctrl.lowStr.IsSameAs(wxT("nil"), false)) {
2247  ctrl.low = 0.0;
2248  }
2249  else {
2250  ctrl.low = GetCtrlValue(ctrl.lowStr);
2251  }
2252 
2253  ctrl.highStr = UnQuote( tokens[7] );
2254  if (ctrl.type == NYQ_CTRL_INT_TEXT && ctrl.highStr.IsSameAs(wxT("nil"), false)) {
2255  ctrl.high = INT_MAX;
2256  }
2257  else if ((ctrl.type == NYQ_CTRL_FLOAT_TEXT || ctrl.type == NYQ_CTRL_TIME) &&
2258  ctrl.highStr.IsSameAs(wxT("nil"), false))
2259  {
2260  ctrl.high = FLT_MAX;
2261  }
2262  else {
2263  ctrl.high = GetCtrlValue(ctrl.highStr);
2264  }
2265 
2266  if (ctrl.high < ctrl.low) {
2267  ctrl.high = ctrl.low;
2268  }
2269 
2270  if (ctrl.val < ctrl.low) {
2271  ctrl.val = ctrl.low;
2272  }
2273 
2274  if (ctrl.val > ctrl.high) {
2275  ctrl.val = ctrl.high;
2276  }
2277 
2278  ctrl.ticks = 1000;
2279  if (ctrl.type == NYQ_CTRL_INT &&
2280  (ctrl.high - ctrl.low < ctrl.ticks)) {
2281  ctrl.ticks = (int)(ctrl.high - ctrl.low);
2282  }
2283  }
2284  }
2285 
2286  if( ! make_iterator_range( mPresetNames ).contains( ctrl.var ) )
2287  {
2288  mControls.push_back(ctrl);
2289  }
2290  }
2291 
2292  // Deprecated
2293  if (len >= 2 && tokens[0] == wxT("categories")) {
2294  for (size_t i = 1; i < tokens.size(); ++i) {
2295  mCategories.push_back(tokens[i]);
2296  }
2297  }
2298  return true;
2299 }
2300 
2301 bool NyquistEffect::ParseProgram(wxInputStream & stream)
2302 {
2303  if (!stream.IsOk())
2304  {
2305  mInitError = XO("Could not open file");
2306  return false;
2307  }
2308 
2309  wxTextInputStream pgm(stream, wxT(" \t"), wxConvAuto());
2310 
2311  mCmd = wxT("");
2312  mCmd.Alloc(10000);
2313  mIsSal = false;
2314  mControls.clear();
2315  mCategories.clear();
2316  mIsSpectral = false;
2317  mManPage = wxEmptyString; // If not wxEmptyString, must be a page in the Audacity manual.
2318  mHelpFile = wxEmptyString; // If not wxEmptyString, must be a valid HTML help file.
2319  mHelpFileExists = false;
2320  mDebug = false;
2321  mTrace = false;
2322  mDebugButton = true; // Debug button enabled by default.
2323  mEnablePreview = true; // Preview button enabled by default.
2324 
2325  // Bug 1934.
2326  // All Nyquist plug-ins should have a ';type' field, but if they don't we default to
2327  // being an Effect.
2329 
2330  mFoundType = false;
2331  while (!stream.Eof() && stream.IsOk())
2332  {
2333  wxString line = pgm.ReadLine();
2334  if (line.length() > 1 &&
2335  // New in 2.3.0: allow magic comment lines to start with $
2336  // The trick is that xgettext will not consider such lines comments
2337  // and will extract the strings they contain
2338  (line[0] == wxT(';') || line[0] == wxT('$')) )
2339  {
2340  Tokenizer tzer;
2341  unsigned nLines = 1;
2342  bool done;
2343  // Allow continuations within control lines.
2344  bool control =
2345  line[0] == wxT('$') || line.StartsWith( wxT(";control") );
2346  do
2347  done = Parse(tzer, line, !control || stream.Eof(), nLines == 1);
2348  while(!done &&
2349  (line = pgm.ReadLine(), ++nLines, true));
2350 
2351  // Don't pass these lines to the interpreter, so it doesn't get confused
2352  // by $, but pass blanks,
2353  // so that SAL effects compile with proper line numbers
2354  while (nLines --)
2355  mCmd += wxT('\n');
2356  }
2357  else
2358  {
2359  if(!mFoundType && line.length() > 0) {
2360  if (line[0] == wxT('(') ||
2361  (line[0] == wxT('#') && line.length() > 1 && line[1] == wxT('|')))
2362  {
2363  mIsSal = false;
2364  mFoundType = true;
2365  }
2366  else if (line.Upper().Find(wxT("RETURN")) != wxNOT_FOUND)
2367  {
2368  mIsSal = true;
2369  mFoundType = true;
2370  }
2371  }
2372  mCmd += line + wxT("\n");
2373  }
2374  }
2375  if (!mFoundType && mIsPrompt)
2376  {
2377  /* i1n-hint: SAL and LISP are names for variant syntaxes for the
2378  Nyquist programming language. Leave them, and 'return', untranslated. */
2380  XO(
2381 "Your code looks like SAL syntax, but there is no \'return\' statement.\n\
2382 For SAL, use a return statement such as:\n\treturn *track* * 0.1\n\
2383 or for LISP, begin with an open parenthesis such as:\n\t(mult *track* 0.1)\n ."),
2385  XO("Error in Nyquist code") );
2386  /* i18n-hint: refers to programming "languages" */
2387  mInitError = XO("Could not determine language");
2388  return false;
2389  // Else just throw it at Nyquist to see what happens
2390  }
2391 
2392  return true;
2393 }
2394 
2396 {
2397  wxFileInputStream rawStream(mFileName.GetFullPath());
2398  wxBufferedInputStream stream(rawStream, 10000);
2399 
2400  ParseProgram(stream);
2401 }
2402 
2403 bool NyquistEffect::ParseCommand(const wxString & cmd)
2404 {
2405  wxStringInputStream stream(cmd + wxT(" "));
2406 
2407  return ParseProgram(stream);
2408 }
2409 
2410 int NyquistEffect::StaticGetCallback(float *buffer, int channel,
2411  int64_t start, int64_t len, int64_t totlen,
2412  void *userdata)
2413 {
2414  NyquistEffect *This = (NyquistEffect *)userdata;
2415  return This->GetCallback(buffer, channel, start, len, totlen);
2416 }
2417 
2418 int NyquistEffect::GetCallback(float *buffer, int ch,
2419  int64_t start, int64_t len, int64_t WXUNUSED(totlen))
2420 {
2421  if (mCurBuffer[ch].ptr()) {
2422  if ((mCurStart[ch] + start) < mCurBufferStart[ch] ||
2423  (mCurStart[ch] + start)+len >
2424  mCurBufferStart[ch]+mCurBufferLen[ch]) {
2425  mCurBuffer[ch].Free();
2426  }
2427  }
2428 
2429  if (!mCurBuffer[ch].ptr()) {
2430  mCurBufferStart[ch] = (mCurStart[ch] + start);
2432 
2433  if (mCurBufferLen[ch] < (size_t) len) {
2435  }
2436 
2437  mCurBufferLen[ch] =
2439  mCurStart[ch] + mCurLen - mCurBufferStart[ch] );
2440 
2442  try {
2443  mCurTrack[ch]->Get(
2444  mCurBuffer[ch].ptr(), floatSample,
2445  mCurBufferStart[ch], mCurBufferLen[ch]);
2446  }
2447  catch ( ... ) {
2448  // Save the exception object for re-throw when out of the library
2449  mpException = std::current_exception();
2450  return -1;
2451  }
2452  }
2453 
2454  // We have guaranteed above that this is nonnegative and bounded by
2455  // mCurBufferLen[ch]:
2456  auto offset = ( mCurStart[ch] + start - mCurBufferStart[ch] ).as_size_t();
2458  (samplePtr)buffer, floatSample,
2459  len);
2460 
2461  if (ch == 0) {
2462  double progress = mScale *
2463  ( (start+len)/ mCurLen.as_double() );
2464 
2465  if (progress > mProgressIn) {
2466  mProgressIn = progress;
2467  }
2468 
2470  return -1;
2471  }
2472  }
2473 
2474  return 0;
2475 }
2476 
2477 int NyquistEffect::StaticPutCallback(float *buffer, int channel,
2478  int64_t start, int64_t len, int64_t totlen,
2479  void *userdata)
2480 {
2481  NyquistEffect *This = (NyquistEffect *)userdata;
2482  return This->PutCallback(buffer, channel, start, len, totlen);
2483 }
2484 
2485 int NyquistEffect::PutCallback(float *buffer, int channel,
2486  int64_t start, int64_t len, int64_t totlen)
2487 {
2488  // Don't let C++ exceptions propagate through the Nyquist library
2489  return GuardedCall<int>( [&] {
2490  if (channel == 0) {
2491  double progress = mScale*((float)(start+len)/totlen);
2492 
2493  if (progress > mProgressOut) {
2494  mProgressOut = progress;
2495  }
2496 
2498  return -1;
2499  }
2500  }
2501 
2502  mOutputTrack[channel]->Append((samplePtr)buffer, floatSample, len);
2503 
2504  return 0; // success
2505  }, MakeSimpleGuard( -1 ) ); // translate all exceptions into failure
2506 }
2507 
2509 {
2510  ((NyquistEffect *)This)->OutputCallback(c);
2511 }
2512 
2514 {
2515  // Always collect Nyquist error messages for normal plug-ins
2516  if (!mRedirectOutput) {
2517  mDebugOutputStr += (wxChar)c;
2518  return;
2519  }
2520 
2521  std::cout << (char)c;
2522 }
2523 
2525 {
2526  ((NyquistEffect *)This)->OSCallback();
2527 }
2528 
2530 {
2531  if (mStop) {
2532  mStop = false;
2533  nyx_stop();
2534  }
2535  else if (mBreak) {
2536  mBreak = false;
2537  nyx_break();
2538  }
2539  else if (mCont) {
2540  mCont = false;
2541  nyx_continue();
2542  }
2543 
2544  // LLL: STF figured out that yielding while the effect is being applied
2545  // produces an EXTREME slowdown. It appears that yielding is not
2546  // really necessary on Linux and Windows.
2547  //
2548  // However, on the Mac, the spinning cursor appears during longer
2549  // Nyquist processing and that may cause the user to think Audacity
2550  // has crashed or hung. In addition, yielding or not on the Mac
2551  // doesn't seem to make much of a difference in execution time.
2552  //
2553  // So, yielding on the Mac only...
2554 #if defined(__WXMAC__)
2555  wxYieldIfNeeded();
2556 #endif
2557 }
2558 
2560 {
2561  const auto &audacityPathList = FileNames::AudacityPathList();
2562  FilePaths pathList;
2563 
2564  for (size_t i = 0; i < audacityPathList.size(); i++)
2565  {
2566  wxString prefix = audacityPathList[i] + wxFILE_SEP_PATH;
2567  FileNames::AddUniquePathToPathList(prefix + wxT("nyquist"), pathList);
2568  FileNames::AddUniquePathToPathList(prefix + wxT("plugins"), pathList);
2569  FileNames::AddUniquePathToPathList(prefix + wxT("plug-ins"), pathList);
2570  }
2571  pathList.push_back(FileNames::PlugInDir());
2572 
2573  return pathList;
2574 }
2575 
2577 {
2578  mCommandText->ChangeValue(mInputCmd);
2579 
2580  return true;
2581 }
2582 
2584 {
2585  for (size_t i = 0, cnt = mControls.size(); i < cnt; i++)
2586  {
2587  NyqControl & ctrl = mControls[i];
2588 
2589  if (ctrl.type == NYQ_CTRL_CHOICE)
2590  {
2591  const auto count = ctrl.choices.size();
2592 
2593  int val = (int)ctrl.val;
2594  if (val < 0 || val >= (int)count)
2595  {
2596  val = 0;
2597  }
2598 
2599  wxChoice *c = (wxChoice *) mUIParent->FindWindow(ID_Choice + i);
2600  c->SetSelection(val);
2601  }
2602  else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_FLOAT)
2603  {
2604  // wxTextCtrls are handled by the validators
2605  double range = ctrl.high - ctrl.low;
2606  int val = (int)(0.5 + ctrl.ticks * (ctrl.val - ctrl.low) / range);
2607  wxSlider *s = (wxSlider *) mUIParent->FindWindow(ID_Slider + i);
2608  s->SetValue(val);
2609  }
2610  else if (ctrl.type == NYQ_CTRL_TIME)
2611  {
2612  NumericTextCtrl *n = (NumericTextCtrl *) mUIParent->FindWindow(ID_Time + i);
2613  n->SetValue(ctrl.val);
2614  }
2615  }
2616 
2617  return true;
2618 }
2619 
2621 {
2622  mInputCmd = mCommandText->GetValue();
2623 
2624  // Un-correct smart quoting, bothersomely applied in wxTextCtrl by
2625  // the native widget of MacOS 10.9 SDK
2626  const wxString left = wxT("\u201c"), right = wxT("\u201d"), dumb = '"';
2627  mInputCmd.Replace(left, dumb, true);
2628  mInputCmd.Replace(right, dumb, true);
2629 
2630  const wxString leftSingle = wxT("\u2018"), rightSingle = wxT("\u2019"),
2631  dumbSingle = '\'';
2632  mInputCmd.Replace(leftSingle, dumbSingle, true);
2633  mInputCmd.Replace(rightSingle, dumbSingle, true);
2634 
2635  return ParseCommand(mInputCmd);
2636 }
2637 
2639 {
2640  if (mControls.size() == 0)
2641  {
2642  return true;
2643  }
2644 
2645  for (unsigned int i = 0; i < mControls.size(); i++)
2646  {
2647  NyqControl *ctrl = &mControls[i];
2648 
2649  if (ctrl->type == NYQ_CTRL_STRING || ctrl->type == NYQ_CTRL_TEXT)
2650  {
2651  continue;
2652  }
2653 
2654  if (ctrl->val == UNINITIALIZED_CONTROL)
2655  {
2656  ctrl->val = GetCtrlValue(ctrl->valStr);
2657  }
2658 
2659  if (ctrl->type == NYQ_CTRL_CHOICE)
2660  {
2661  continue;
2662  }
2663 
2664  if (ctrl->type == NYQ_CTRL_FILE)
2665  {
2666  resolveFilePath(ctrl->valStr);
2667 
2668  wxString path;
2669  if (ctrl->valStr.StartsWith("\"", &path))
2670  {
2671  // Validate if a list of quoted paths.
2672  if (path.EndsWith("\"", &path))
2673  {
2674  path.Replace("\"\"", "\"");
2675  wxStringTokenizer tokenizer(path, "\"");
2676  while (tokenizer.HasMoreTokens())
2677  {
2678  wxString token = tokenizer.GetNextToken();
2679  if(!validatePath(token))
2680  {
2681  const auto message =
2682  XO("\"%s\" is not a valid file path.").Format( token );
2684  message,
2685  wxOK | wxICON_EXCLAMATION | wxCENTRE,
2686  XO("Error") );
2687  return false;
2688  }
2689  }
2690  continue;
2691  }
2692  else
2693  {
2694  const auto message =
2695  /* i18n-hint: Warning that there is one quotation mark rather than a pair.*/
2696  XO("Mismatched quotes in\n%s").Format( ctrl->valStr );
2698  message,
2699  wxOK | wxICON_EXCLAMATION | wxCENTRE,
2700  XO("Error") );
2701  return false;
2702  }
2703  }
2704  // Validate a single path.
2705  else if (validatePath(ctrl->valStr))
2706  {
2707  continue;
2708  }
2709 
2710  // Validation failed
2711  const auto message =
2712  XO("\"%s\" is not a valid file path.").Format( ctrl->valStr );
2714  message,
2715  wxOK | wxICON_EXCLAMATION | wxCENTRE,
2716  XO("Error") );
2717  return false;
2718  }
2719 
2720  if (ctrl->type == NYQ_CTRL_TIME)
2721  {
2722  NumericTextCtrl *n = (NumericTextCtrl *) mUIParent->FindWindow(ID_Time + i);
2723  ctrl->val = n->GetValue();
2724  }
2725 
2726  if (ctrl->type == NYQ_CTRL_INT_TEXT && ctrl->lowStr.IsSameAs(wxT("nil"), false)) {
2727  ctrl->low = INT_MIN;
2728  }
2729  else if ((ctrl->type == NYQ_CTRL_FLOAT_TEXT || ctrl->type == NYQ_CTRL_TIME) &&
2730  ctrl->lowStr.IsSameAs(wxT("nil"), false))
2731  {
2732  ctrl->low = -(FLT_MAX);
2733  }
2734  else
2735  {
2736  ctrl->low = GetCtrlValue(ctrl->lowStr);
2737  }
2738 
2739  if (ctrl->type == NYQ_CTRL_INT_TEXT && ctrl->highStr.IsSameAs(wxT("nil"), false)) {
2740  ctrl->high = INT_MAX;
2741  }
2742  else if ((ctrl->type == NYQ_CTRL_FLOAT_TEXT || ctrl->type == NYQ_CTRL_TIME) &&
2743  ctrl->highStr.IsSameAs(wxT("nil"), false))
2744  {
2745  ctrl->high = FLT_MAX;
2746  }
2747  else
2748  {
2749  ctrl->high = GetCtrlValue(ctrl->highStr);
2750  }
2751 
2752  if (ctrl->high < ctrl->low)
2753  {
2754  ctrl->high = ctrl->low + 1;
2755  }
2756 
2757  if (ctrl->val < ctrl->low)
2758  {
2759  ctrl->val = ctrl->low;
2760  }
2761 
2762  if (ctrl->val > ctrl->high)
2763  {
2764  ctrl->val = ctrl->high;
2765  }
2766 
2767  ctrl->ticks = 1000;
2768  if (ctrl->type == NYQ_CTRL_INT &&
2769  (ctrl->high - ctrl->low < ctrl->ticks))
2770  {
2771  ctrl->ticks = (int)(ctrl->high - ctrl->low);
2772  }
2773  }
2774 
2775  return true;
2776 }
2777 
2779 {
2780  S.StartVerticalLay();
2781  {
2782  S.StartMultiColumn(3, wxEXPAND);
2783  {
2784  S.SetStretchyCol(1);
2785 
2786  S.AddVariableText(XO("Enter Nyquist Command: "));
2787 
2788  S.AddSpace(1, 1);
2789  }
2790  S.EndMultiColumn();
2791 
2792  S.StartHorizontalLay(wxEXPAND, 1);
2793  {
2794  mCommandText = S.Focus()
2795  .MinSize( { 500, 200 } )
2796  .AddTextWindow(wxT(""));
2797  }
2798  S.EndHorizontalLay();
2799 
2800  S.StartHorizontalLay(wxALIGN_CENTER, 0);
2801  {
2802  S.Id(ID_Load).AddButton(XXO("&Load"));
2803  S.Id(ID_Save).AddButton(XXO("&Save"));
2804  }
2805  S.EndHorizontalLay();
2806  }
2807  S.EndVerticalLay();
2808 }
2809 
2811 {
2812  wxScrolledWindow *scroller = S.Style(wxVSCROLL | wxTAB_TRAVERSAL)
2813  .StartScroller(2);
2814  {
2815  S.StartMultiColumn(4);
2816  {
2817  for (size_t i = 0; i < mControls.size(); i++)
2818  {
2819  NyqControl & ctrl = mControls[i];
2820 
2821  if (ctrl.type == NYQ_CTRL_TEXT)
2822  {
2823  S.EndMultiColumn();
2824  S.StartHorizontalLay(wxALIGN_LEFT, 0);
2825  {
2826  S.AddSpace(0, 10);
2827  S.AddFixedText( Verbatim( ctrl.label ), false );
2828  }
2829  S.EndHorizontalLay();
2830  S.StartMultiColumn(4);
2831  }
2832  else
2833  {
2834  auto prompt = XXO("%s:").Format( ctrl.name );
2835  S.AddPrompt( prompt );
2836 
2837  if (ctrl.type == NYQ_CTRL_STRING)
2838  {
2839  S.AddSpace(10, 10);
2840 
2841  auto item = S.Id(ID_Text + i)
2842  .Validator<wxGenericValidator>(&ctrl.valStr)
2843  .Name( prompt )
2844  .AddTextBox( {}, wxT(""), 50);
2845  }
2846  else if (ctrl.type == NYQ_CTRL_CHOICE)
2847  {
2848  S.AddSpace(10, 10);
2849 
2850  S.Id(ID_Choice + i).AddChoice( {},
2851  Msgids( ctrl.choices.data(), ctrl.choices.size() ) );
2852  }
2853  else if (ctrl.type == NYQ_CTRL_TIME)
2854  {
2855  S.AddSpace(10, 10);
2856 
2857  const auto options = NumericTextCtrl::Options{}
2858  .AutoPos(true)
2859  .MenuEnabled(true)
2860  .ReadOnly(false);
2861 
2862  NumericTextCtrl *time = safenew
2863  NumericTextCtrl(S.GetParent(), (ID_Time + i),
2866  ctrl.val,
2867  mProjectRate,
2868  options);
2869  S
2870  .Name( prompt )
2871  .Position(wxALIGN_LEFT | wxALL)
2872  .AddWindow(time);
2873  }
2874  else if (ctrl.type == NYQ_CTRL_FILE)
2875  {
2876  S.AddSpace(10, 10);
2877 
2878  // Get default file extension if specified in wildcards
2879  FileExtension defaultExtension;
2880  if (!ctrl.fileTypes.empty()) {
2881  const auto &type = ctrl.fileTypes[0];
2882  if ( !type.extensions.empty() )
2883  defaultExtension = type.extensions[0];
2884  }
2885  resolveFilePath(ctrl.valStr, defaultExtension);
2886 
2887  wxTextCtrl *item = S.Id(ID_Text+i)
2888  .Name( prompt )
2889  .AddTextBox( {}, wxT(""), 40);
2890  item->SetValidator(wxGenericValidator(&ctrl.valStr));
2891 
2892  if (ctrl.label.empty())
2893  // We'd expect wxFileSelectorPromptStr to already be translated, but apparently not.
2894  ctrl.label = wxGetTranslation( wxFileSelectorPromptStr );
2895  S.Id(ID_FILE + i).AddButton(
2896  Verbatim(ctrl.label), wxALIGN_LEFT);
2897  }
2898  else
2899  {
2900  // Integer or Real
2901  if (ctrl.type == NYQ_CTRL_INT_TEXT || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
2902  {
2903  S.AddSpace(10, 10);
2904  }
2905 
2906  S.Id(ID_Text+i);
2907  if (ctrl.type == NYQ_CTRL_FLOAT || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
2908  {
2909  double range = ctrl.high - ctrl.low;
2910  S.Validator<FloatingPointValidator<double>>(
2911  // > 12 decimal places can cause rounding errors in display.
2912  12, &ctrl.val,
2913  // Set number of decimal places
2914  (range < 10
2915  ? NumValidatorStyle::THREE_TRAILING_ZEROES
2916  : range < 100
2917  ? NumValidatorStyle::TWO_TRAILING_ZEROES
2918  : NumValidatorStyle::ONE_TRAILING_ZERO),
2919  ctrl.low, ctrl.high
2920  );
2921  }
2922  else
2923  {
2924  S.Validator<IntegerValidator<double>>(
2925  &ctrl.val, NumValidatorStyle::DEFAULT,
2926  (int) ctrl.low, (int) ctrl.high);
2927  }
2928  wxTextCtrl *item = S
2929  .Name( prompt )
2930  .AddTextBox( {}, wxT(""),
2931  (ctrl.type == NYQ_CTRL_INT_TEXT ||
2932  ctrl.type == NYQ_CTRL_FLOAT_TEXT) ? 25 : 12);
2933 
2934  if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_FLOAT)
2935  {
2936  S.Id(ID_Slider + i)
2937  .Style(wxSL_HORIZONTAL)
2938  .MinSize( { 150, -1 } )
2939  .AddSlider( {}, 0, ctrl.ticks, 0);
2940  }
2941  }
2942 
2943  if (ctrl.type != NYQ_CTRL_FILE)
2944  {
2945  if (ctrl.type == NYQ_CTRL_CHOICE || ctrl.label.empty())
2946  {
2947  S.AddSpace(10, 10);
2948  }
2949  else
2950  {
2951  S.AddUnits( Verbatim( ctrl.label ) );
2952  }
2953  }
2954  }
2955  }
2956  }
2957  S.EndMultiColumn();
2958  }
2959  S.EndScroller();
2960 
2961  scroller->SetScrollRate(0, 20);
2962 
2963  // This fools NVDA into not saying "Panel" when the dialog gets focus
2964  scroller->SetName(wxT("\a"));
2965  scroller->SetLabel(wxT("\a"));
2966 }
2967 
2968 // NyquistEffect implementation
2969 
2971 {
2972  return mOK;
2973 }
2974 
2975 static const FileNames::FileType
2976  /* i18n-hint: Nyquist is the name of a programming language */
2977  NyquistScripts = { XO("Nyquist scripts"), { wxT("ny") }, true }
2978  /* i18n-hint: Lisp is the name of a programming language */
2979  , LispScripts = { XO("Lisp scripts"), { wxT("lsp") }, true }
2980 ;
2981 
2982 void NyquistEffect::OnLoad(wxCommandEvent & WXUNUSED(evt))
2983 {
2984  if (mCommandText->IsModified())
2985  {
2986  if (wxNO == Effect::MessageBox(
2987  XO("Current program has been modified.\nDiscard changes?"),
2988  wxYES_NO ) )
2989  {
2990  return;
2991  }
2992  }
2993 
2994  FileDialogWrapper dlog(
2995  mUIParent,
2996  XO("Load Nyquist script"),
2997  mFileName.GetPath(),
2998  wxEmptyString,
2999  {
3000  NyquistScripts,
3001  LispScripts,
3002  FileNames::TextFiles,
3003  FileNames::AllFiles
3004  },
3005  wxFD_OPEN | wxRESIZE_BORDER);
3006 
3007  if (dlog.ShowModal() != wxID_OK)
3008  {
3009  return;
3010  }
3011 
3012  mFileName = dlog.GetPath();
3013 
3014  if (!mCommandText->LoadFile(mFileName.GetFullPath()))
3015  {
3016  Effect::MessageBox( XO("File could not be loaded") );
3017  }
3018 }
3019 
3020 void NyquistEffect::OnSave(wxCommandEvent & WXUNUSED(evt))
3021 {
3022  FileDialogWrapper dlog(
3023  mUIParent,
3024  XO("Save Nyquist script"),
3025  mFileName.GetPath(),
3026  mFileName.GetFullName(),
3027  {
3028  NyquistScripts,
3029  LispScripts,
3030  FileNames::AllFiles
3031  },
3032  wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER);
3033 
3034  if (dlog.ShowModal() != wxID_OK)
3035  {
3036  return;
3037  }
3038 
3039  mFileName = dlog.GetPath();
3040 
3041  if (!mCommandText->SaveFile(mFileName.GetFullPath()))
3042  {
3043  Effect::MessageBox( XO("File could not be saved") );
3044  }
3045 }
3046 
3047 void NyquistEffect::OnSlider(wxCommandEvent & evt)
3048 {
3049  int i = evt.GetId() - ID_Slider;
3050  NyqControl & ctrl = mControls[i];
3051 
3052  int val = evt.GetInt();
3053  double range = ctrl.high - ctrl.low;
3054  double newVal = (val / (double)ctrl.ticks) * range + ctrl.low;
3055 
3056  // Determine precision for displayed number
3057  int precision = range < 1.0 ? 3 :
3058  range < 10.0 ? 2 :
3059  range < 100.0 ? 1 :
3060  0;
3061 
3062  // If the value is at least one tick different from the current value
3063  // change it (this prevents changes from manually entered values unless
3064  // the slider actually moved)
3065  if (fabs(newVal - ctrl.val) >= (1 / (double)ctrl.ticks) * range &&
3066  fabs(newVal - ctrl.val) >= pow(0.1, precision) / 2)
3067  {
3068  // First round to the appropriate precision
3069  newVal *= pow(10.0, precision);
3070  newVal = floor(newVal + 0.5);
3071  newVal /= pow(10.0, precision);
3072 
3073  ctrl.val = newVal;
3074 
3075  mUIParent->FindWindow(ID_Text + i)->GetValidator()->TransferToWindow();
3076  }
3077 }
3078 
3079 void NyquistEffect::OnChoice(wxCommandEvent & evt)
3080 {
3081  mControls[evt.GetId() - ID_Choice].val = (double) evt.GetInt();
3082 }
3083 
3084 void NyquistEffect::OnTime(wxCommandEvent& evt)
3085 {
3086  int i = evt.GetId() - ID_Time;
3087  static double value = 0.0;
3088  NyqControl & ctrl = mControls[i];
3089 
3090  NumericTextCtrl *n = (NumericTextCtrl *) mUIParent->FindWindow(ID_Time + i);
3091  double val = n->GetValue();
3092 
3093  // Observed that two events transmitted on each control change (Linux)
3094  // so skip if value has not changed.
3095  if (val != value) {
3096  if (val < ctrl.low || val > ctrl.high) {
3097  const auto message = XO("Value range:\n%s to %s")
3098  .Format( ToTimeFormat(ctrl.low), ToTimeFormat(ctrl.high) );
3100  message,
3101  wxOK | wxCENTRE,
3102  XO("Value Error") );
3103  }
3104 
3105  if (val < ctrl.low)
3106  val = ctrl.low;
3107  else if (val > ctrl.high)
3108  val = ctrl.high;
3109 
3110  n->SetValue(val);
3111  value = val;
3112  }
3113 }
3114 
3115 void NyquistEffect::OnFileButton(wxCommandEvent& evt)
3116 {
3117  int i = evt.GetId() - ID_FILE;
3118  NyqControl & ctrl = mControls[i];
3119 
3120  // Get style flags:
3121  // Ensure legal combinations so that wxWidgets does not throw an assert error.
3122  unsigned int flags = 0;
3123  if (!ctrl.highStr.empty())
3124  {
3125  wxStringTokenizer tokenizer(ctrl.highStr, ",");
3126  while ( tokenizer.HasMoreTokens() )
3127  {
3128  wxString token = tokenizer.GetNextToken().Trim(true).Trim(false);
3129  if (token.IsSameAs("open", false))
3130  {
3131  flags |= wxFD_OPEN;
3132  flags &= ~wxFD_SAVE;
3133  flags &= ~wxFD_OVERWRITE_PROMPT;
3134  }
3135  else if (token.IsSameAs("save", false))
3136  {
3137  flags |= wxFD_SAVE;
3138  flags &= ~wxFD_OPEN;
3139  flags &= ~wxFD_MULTIPLE;
3140  flags &= ~wxFD_FILE_MUST_EXIST;
3141  }
3142  else if (token.IsSameAs("overwrite", false) && !(flags & wxFD_OPEN))
3143  {
3144  flags |= wxFD_OVERWRITE_PROMPT;
3145  }
3146  else if (token.IsSameAs("exists", false) && !(flags & wxFD_SAVE))
3147  {
3148  flags |= wxFD_FILE_MUST_EXIST;
3149  }
3150  else if (token.IsSameAs("multiple", false) && !(flags & wxFD_SAVE))
3151  {
3152  flags |= wxFD_MULTIPLE;
3153  }
3154  }
3155  }
3156 
3157  resolveFilePath(ctrl.valStr);
3158 
3159  wxFileName fname = ctrl.valStr;
3160  wxString defaultDir = fname.GetPath();
3161  wxString defaultFile = fname.GetName();
3162  auto message = XO("Select a file");
3163 
3164  if (flags & wxFD_MULTIPLE)
3165  message = XO("Select one or more files");
3166  else if (flags & wxFD_SAVE)
3167  message = XO("Save file as");
3168 
3169  FileDialogWrapper openFileDialog(mUIParent->FindWindow(ID_FILE + i),
3170  message,
3171  defaultDir,
3172  defaultFile,
3173  ctrl.fileTypes,
3174  flags); // styles
3175 
3176  if (openFileDialog.ShowModal() == wxID_CANCEL)
3177  {
3178  return;
3179  }
3180 
3181  wxString path;
3182  // When multiple files selected, return file paths as a list of quoted strings.
3183  if (flags & wxFD_MULTIPLE)
3184  {
3185  wxArrayString selectedFiles;
3186  openFileDialog.GetPaths(selectedFiles);
3187 
3188  for (size_t sf = 0; sf < selectedFiles.size(); sf++) {
3189  path += "\"";
3190  path += selectedFiles[sf];
3191  path += "\"";
3192  }
3193  ctrl.valStr = path;
3194  }
3195  else
3196  {
3197  ctrl.valStr = openFileDialog.GetPath();
3198  }
3199 
3200  mUIParent->FindWindow(ID_Text + i)->GetValidator()->TransferToWindow();
3201 }
3202 
3203 void NyquistEffect::resolveFilePath(wxString& path, FileExtension extension /* empty string */)
3204 {
3205 #if defined(__WXMSW__)
3206  path.Replace("/", wxFileName::GetPathSeparator());
3207 #endif
3208 
3209  path.Trim(true).Trim(false);
3210 
3211  typedef std::unordered_map<wxString, FilePath> map;
3212  map pathKeys = {
3213  {"*home*", wxGetHomeDir()},
3214  {"~", wxGetHomeDir()},
3215  {"*default*", FileNames::DefaultToDocumentsFolder("").GetPath()},
3216  {"*export*", FileNames::FindDefaultPath(FileNames::Operation::Export)},
3217  {"*save*", FileNames::FindDefaultPath(FileNames::Operation::Save)},
3218  {"*config*", FileNames::DataDir()}
3219  };
3220 
3221  int characters = path.Find(wxFileName::GetPathSeparator());
3222  if(characters == wxNOT_FOUND) // Just a path or just a file name
3223  {
3224  if (path.empty())
3225  path = "*default*";
3226 
3227  if (pathKeys.find(path) != pathKeys.end())
3228  {
3229  // Keyword found, so assume this is the intended directory.
3230  path = pathKeys[path] + wxFileName::GetPathSeparator();
3231  }
3232  else // Just a file name
3233  {
3234  path = pathKeys["*default*"] + wxFileName::GetPathSeparator() + path;
3235  }
3236  }
3237  else // path + file name
3238  {
3239  wxString firstDir = path.Left(characters);
3240  wxString rest = path.Mid(characters);
3241 
3242  if (pathKeys.find(firstDir) != pathKeys.end())
3243  {
3244  path = pathKeys[firstDir] + rest;
3245  }
3246  }
3247 
3248  wxFileName fname = path;
3249 
3250  // If the directory is invalid, better to leave it as is (invalid) so that
3251  // the user sees the error rather than an unexpected file path.
3252  if (fname.wxFileName::IsOk() && fname.GetFullName().empty())
3253  {
3254  path = fname.GetPathWithSep() + _("untitled");
3255  if (!extension.empty())
3256  path = path + '.' + extension;
3257  }
3258 }
3259 
3260 
3261 bool NyquistEffect::validatePath(wxString path)
3262 {
3263  wxFileName fname = path;
3264  wxString dir = fname.GetPath();
3265 
3266  return (fname.wxFileName::IsOk() &&
3267  wxFileName::DirExists(dir) &&
3268  !fname.GetFullName().empty());
3269 }
3270 
3271 
3273 {
3274  int seconds = static_cast<int>(t);
3275  int hh = seconds / 3600;
3276  int mm = seconds % 3600;
3277  mm = mm / 60;
3278  return wxString::Format("%d:%d:%.3f", hh, mm, t - (hh * 3600 + mm * 60));
3279 }
3280 
3281 
3282 void NyquistEffect::OnText(wxCommandEvent & evt)
3283 {
3284  int i = evt.GetId() - ID_Text;
3285 
3286  NyqControl & ctrl = mControls[i];
3287 
3288  if (wxDynamicCast(evt.GetEventObject(), wxWindow)->GetValidator()->TransferFromWindow())
3289  {
3290  if (ctrl.type == NYQ_CTRL_FLOAT || ctrl.type == NYQ_CTRL_INT)
3291  {
3292  int pos = (int)floor((ctrl.val - ctrl.low) /
3293  (ctrl.high - ctrl.low) * ctrl.ticks + 0.5);
3294 
3295  wxSlider *slider = (wxSlider *)mUIParent->FindWindow(ID_Slider + i);
3296  slider->SetValue(pos);
3297  }
3298  }
3299 }
3300 
3302 //
3303 // NyquistOutputDialog
3304 //
3306 
3307 
3308 BEGIN_EVENT_TABLE(NyquistOutputDialog, wxDialogWrapper)
3311 
3312 NyquistOutputDialog::NyquistOutputDialog(wxWindow * parent, wxWindowID id,
3313  const TranslatableString & title,
3314  const TranslatableString & prompt,
3315  const TranslatableString &message)
3316 : wxDialogWrapper{ parent, id, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER }
3317 {
3318  SetName();
3319 
3320  ShuttleGui S{ this, eIsCreating };
3321  {
3322  S.SetBorder(10);
3323 
3324  S.AddVariableText( prompt, false, wxALIGN_LEFT | wxLEFT | wxTOP | wxRIGHT );
3325 
3326  // TODO: use ShowInfoDialog() instead.
3327  // Beware this dialog MUST work with screen readers.
3328  S.Prop( 1 )
3329  .Position(wxEXPAND | wxALL)
3330  .MinSize( { 480, 250 } )
3331  .Style(wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH)
3332  .AddTextWindow( message.Translation() );
3333 
3334  S.SetBorder( 5 );
3335 
3336  S.StartHorizontalLay(wxALIGN_CENTRE | wxLEFT | wxBOTTOM | wxRIGHT, 0 );
3337  {
3338  /* i18n-hint: In most languages OK is to be translated as OK. It appears on a button.*/
3339  S.Id(wxID_OK).AddButton( XXO("OK"), wxALIGN_CENTRE, true );
3340  }
3341  S.EndHorizontalLay();
3342 
3343  }
3344 
3345  SetAutoLayout(true);
3346  GetSizer()->Fit(this);
3347  GetSizer()->SetSizeHints(this);
3348 }
3349 
3350 // ============================================================================
3351 // NyquistOutputDialog implementation
3352 // ============================================================================
3353 
3354 void NyquistOutputDialog::OnOk(wxCommandEvent & /* event */)
3355 {
3356  EndModal(wxID_OK);
3357 }
3358 
3359 // Registration of extra functions in XLisp.
3360 #include "../../../lib-src/libnyquist/nyquist/xlisp/xlisp.h"
3361 
3362 static LVAL gettext()
3363 {
3364  auto string = UTF8CTOWX(getstring(xlgastring()));
3365 #if !HAS_I18N_CONTEXTS
3366  // allow ignored context argument
3367  if ( moreargs() )
3368  nextarg();
3369 #endif
3370  xllastarg();
3371  return cvstring(GetCustomTranslation(string).mb_str(wxConvUTF8));
3372 }
3373 
3374 static LVAL gettextc()
3375 {
3376 #if HAS_I18N_CONTEXTS
3377  auto string = UTF8CTOWX(getstring(xlgastring()));
3378  auto context = UTF8CTOWX(getstring(xlgastring()));
3379  xllastarg();
3380  return cvstring(wxGetTranslation( string, "", 0, "", context )
3381  .mb_str(wxConvUTF8));
3382 #else
3383  return gettext();
3384 #endif
3385 }
3386 
3387 static LVAL ngettext()
3388 {
3389  auto string1 = UTF8CTOWX(getstring(xlgastring()));
3390  auto string2 = UTF8CTOWX(getstring(xlgastring()));
3391  auto number = getfixnum(xlgafixnum());
3392 #if !HAS_I18N_CONTEXTS
3393  // allow ignored context argument
3394  if ( moreargs() )
3395  nextarg();
3396 #endif
3397  xllastarg();
3398  return cvstring(
3399  wxGetTranslation(string1, string2, number).mb_str(wxConvUTF8));
3400 }
3401 
3402 static LVAL ngettextc()
3403 {
3404 #if HAS_I18N_CONTEXTS
3405  auto string1 = UTF8CTOWX(getstring(xlgastring()));
3406  auto string2 = UTF8CTOWX(getstring(xlgastring()));
3407  auto number = getfixnum(xlgafixnum());
3408  auto context = UTF8CTOWX(getstring(xlgastring()));
3409  xllastarg();
3410  return cvstring(wxGetTranslation( string1, string2, number, "", context )
3411  .mb_str(wxConvUTF8));
3412 #else
3413  return ngettext();
3414 #endif
3415 }
3416 
3417 void * nyq_make_opaque_string( int size, unsigned char *src ){
3418  LVAL dst;
3419  unsigned char * dstp;
3420  dst = new_string((int)(size+2));
3421  dstp = getstring(dst);
3422 
3423  /* copy the source to the destination */
3424  while (size-- > 0)
3425  *dstp++ = *src++;
3426  *dstp = '\0';
3427 
3428  return (void*)dst;
3429 }
3430 
3431 void * nyq_reformat_aud_do_response(const wxString & Str) {
3432  LVAL dst;
3433  LVAL message;
3434  LVAL success;
3435  wxString Left = Str.BeforeLast('\n').BeforeLast('\n').ToAscii();
3436  wxString Right = Str.BeforeLast('\n').AfterLast('\n').ToAscii();
3437  message = cvstring(Left);
3438  success = Right.EndsWith("OK") ? s_true : nullptr;
3439  dst = cons(message, success);
3440  return (void *)dst;
3441 }
3442 
3443 #include "../../commands/ScriptCommandRelay.h"
3444 
3445 
3446 /* xlc_aud_do -- interface to C routine aud_do */
3447 
3448 LVAL xlc_aud_do(void)
3449 {
3450 // Based on string-trim...
3451  unsigned char *leftp;
3452  LVAL src,dst;
3453 
3454  /* get the string */
3455  src = xlgastring();
3456  xllastarg();
3457 
3458  /* setup the string pointer */
3459  leftp = getstring(src);
3460 
3461  // Go call my real function here...
3462  dst = (LVAL)ExecForLisp( (char *)leftp );
3463 
3464  //dst = cons(dst, (LVAL)1);
3465  /* return the new string */
3466  return (dst);
3467 }
3468 
3469 static void RegisterFunctions()
3470 {
3471  // Add functions to XLisp. Do this only once,
3472  // before the first call to nyx_init.
3473  static bool firstTime = true;
3474  if (firstTime) {
3475  firstTime = false;
3476 
3477  // All function names must be UP-CASED
3478  static const FUNDEF functions[] = {
3479  { "_", SUBR, gettext },
3480  { "_C", SUBR, gettextc },
3481  { "NGETTEXT", SUBR, ngettext },
3482  { "NGETTEXTC", SUBR, ngettextc },
3483  { "AUD-DO", SUBR, xlc_aud_do },
3484  };
3485 
3486  xlbindfunctions( functions, WXSIZEOF( functions ) );
3487  }
3488 }
Effect::IsPreviewing
bool IsPreviewing()
Definition: Effect.h:362
gettextc
static LVAL gettextc()
Definition: Nyquist.cpp:3374
EVT_BUTTON
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
Optional::emplace
X & emplace(Args &&... args)
Definition: MemoryX.h:259
ShuttleSetAutomation
Shuttle that sets parameters to a value (from a string)
Definition: Shuttle.h:105
FileExtension
wxString FileExtension
Definition: Types.h:267
TranslatableString
Definition: Types.h:290
ViewInfo::Get
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:162
NyquistEffect::GetCallback
int GetCallback(float *buffer, int channel, int64_t start, int64_t len, int64_t totlen)
Definition: Nyquist.cpp:2418
WaveTrack::Flush
void Flush()
Flush must be called after last Append.
Definition: WaveTrack.cpp:1608
CommandParameters
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the Shuttle cla...
Definition: EffectAutomationParameters.h:67
NyquistEffect::GetDescription
TranslatableString GetDescription() override
Definition: Nyquist.cpp:234
NyquistEffect::OnLoad
void OnLoad(wxCommandEvent &evt)
Definition: Nyquist.cpp:2982
NyquistEffect::GetFamily
EffectFamilySymbol GetFamily() override
Definition: Nyquist.cpp:275
SampleBuffer::Allocate
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:68
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:36
NyquistEffect::IsDefault
bool IsDefault() override
Definition: Nyquist.cpp:290
CommandParameters::SetParameters
bool SetParameters(const wxString &parms)
Definition: EffectAutomationParameters.h:287
NyquistEffect::BuildPromptWindow
void BuildPromptWindow(ShuttleGui &S)
Definition: Nyquist.cpp:2778
NyquistEffect::mTrackIndex
int mTrackIndex
Definition: Nyquist.h:260
NyquistEffect::IsInteractive
bool IsInteractive() override
Definition: Nyquist.cpp:280
TranslatableString::empty
bool empty() const
Definition: Types.h:329
NyquistEffect::GetCtrlValue
double GetCtrlValue(const wxString &s)
Definition: Nyquist.cpp:1832
valueRestorer
ValueRestorer< T > valueRestorer(T &var)
inline functions provide convenient parameter type deduction
Definition: MemoryX.h:430
NyquistEffect::mMaxLen
sampleCount mMaxLen
Definition: Nyquist.h:259
ShuttleGuiBase::AddChoice
wxChoice * AddChoice(const TranslatableString &Prompt, const TranslatableStrings &choices, int Selected=-1)
Definition: ShuttleGui.cpp:391
Effect::FindProject
const AudacityProject * FindProject() const
Definition: Effect.cpp:2271
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1177
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:68
NyquistEffect::Stop
void Stop()
Definition: Nyquist.cpp:1781
CommandParameters::WriteEnum
bool WriteEnum(const wxString &key, int value, const EnumValueSymbol choices[], size_t nChoices)
Definition: EffectAutomationParameters.h:203
EffectTypeProcess
@ EffectTypeProcess
Definition: EffectInterface.h:59
ShuttleGui::Focus
ShuttleGui & Focus(bool focused=true)
Definition: ShuttleGui.h:651
NyquistEffect::GetPath
PluginPath GetPath() override
Definition: Nyquist.cpp:201
RegisterFunctions
static void RegisterFunctions()
Definition: Nyquist.cpp:3469
Effect::SetLinearEffectFlag
void SetLinearEffectFlag(bool linearEffectFlag)
Definition: Effect.cpp:2000
make_iterator_range
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:625
NyquistEffect::GetType
EffectType GetType() override
Definition: Nyquist.cpp:263
NyquistEffect::mProgressIn
double mProgressIn
Definition: Nyquist.h:265
Optional
Like a smart pointer, allows for object to not exist (nullptr)
Definition: MemoryX.h:210
Effect::EnableDebug
virtual void EnableDebug(bool enable=true)
Definition: Effect.cpp:1995
ID_FILE
@ ID_FILE
Definition: Nyquist.cpp:102
NumericTextCtrl::Options::AutoPos
Options & AutoPos(bool enable)
Definition: NumericTextCtrl.h:189
Effect::GetSelectionFormat
virtual NumericFormatSymbol GetSelectionFormat()
Definition: Effect.cpp:830
NyquistEffect::OutputCallback
void OutputCallback(int c)
Definition: Nyquist.cpp:2513
NyquistEffect::mInputCmd
wxString mInputCmd
Definition: Nyquist.h:224
NyquistEffect::mStop
bool mStop
Definition: Nyquist.h:206
NyquistEffect::mTrace
bool mTrace
Definition: Nyquist.h:212
NyquistEffect::ToTimeFormat
wxString ToTimeFormat(double t)
Definition: Nyquist.cpp:3272
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:67
NyquistEffect::TransferDataToPromptWindow
bool TransferDataToPromptWindow()
Definition: Nyquist.cpp:2576
NyquistEffect::mHelpFileExists
bool mHelpFileExists
Definition: Nyquist.h:239
Track::GetEndTime
virtual double GetEndTime() const =0
Effect::MessageBox
int MessageBox(const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
Definition: Effect.cpp:2478
NYQ_CTRL_STRING
@ NYQ_CTRL_STRING
Definition: Nyquist.h:30
WaveTrack::GetEndTime
double GetEndTime() const override
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1797
NyquistEffect
An Effect that calls up a Nyquist (XLISP) plug-in, i.e. many possible effects from this one class.
Definition: Nyquist.h:65
Effect::CopyInputTracks
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:2070
NyquistEffect::mInitError
TranslatableString mInitError
Definition: Nyquist.h:223
NyquistEffect::mBreak
bool mBreak
Definition: Nyquist.h:207
NyquistEffect::mEnablePreview
bool mEnablePreview
Definition: Nyquist.h:243
UNINITIALIZED_CONTROL
#define UNINITIALIZED_CONTROL
Definition: Nyquist.cpp:108
NyqControl::lowStr
wxString lowStr
Definition: Nyquist.h:55
EffectTypeGenerate
@ EffectTypeGenerate
Definition: EffectInterface.h:58
int24Sample
@ int24Sample
Definition: Types.h:721
NyquistEffect::HelpPage
wxString HelpPage() override
Definition: Nyquist.cpp:246
Effect::mUIResultID
int mUIResultID
Definition: Effect.h:478
TrackList::Channels
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1467
NyquistEffect::ParseCommand
bool ParseCommand(const wxString &cmd)
Definition: Nyquist.cpp:2403
NyquistEffect::mCurBuffer
SampleBuffer mCurBuffer[2]
Definition: Nyquist.h:270
Effect::SetPreviewFullSelectionFlag
void SetPreviewFullSelectionFlag(bool previewDurationFlag)
Definition: Effect.cpp:2005
Effect::mT1
double mT1
Definition: Effect.h:466
Effect::Delegate
bool Delegate(Effect &delegate, wxWindow &parent, const EffectDialogFactory &factory)
Definition: Effect.cpp:1339
FileNames::DataDir
AUDACITY_DLL_API FilePath DataDir()
Audacity user data directory.
NyquistEffect::OnSlider
void OnSlider(wxCommandEvent &evt)
Definition: Nyquist.cpp:3047
MakeSimpleGuard
SimpleGuard< R > MakeSimpleGuard(R value)
Convert a value to a handler function returning that value, suitable for GuardedCall<R>
Definition: AudacityException.h:151
NyquistEffect::GetClassification
EffectType GetClassification() override
Definition: Nyquist.cpp:268
NyquistEffect::ParseFileTypes
FileNames::FileTypes ParseFileTypes(const wxString &text)
Definition: Nyquist.cpp:1719
NyquistEffect::mDebug
bool mDebug
Definition: Nyquist.h:246
ShuttleGui::AddSpace
wxSizerItem * AddSpace(int width, int height, int prop=0)
Definition: ShuttleGui.cpp:2421
NyquistEffect::mFirstInGroup
bool mFirstInGroup
Definition: Nyquist.h:261
FileNames::AddUniquePathToPathList
AUDACITY_DLL_API void AddUniquePathToPathList(const FilePath &path, FilePaths &pathList)
gettext
static LVAL gettext()
Definition: Nyquist.cpp:3362
NyquistEffect::mpException
std::exception_ptr mpException
Definition: Nyquist.h:286
ShuttleGui::MinSize
ShuttleGui & MinSize()
Definition: ShuttleGui.h:740
XO
#define XO(s)
Definition: Internat.h:32
Effect::EnablePreview
virtual bool EnablePreview(bool enable=true)
Definition: Effect.cpp:1956
WaveTrack::GetMinMax
std::pair< float, float > GetMinMax(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:1822
GetCustomTranslation
AUDACITY_DLL_API const wxString & GetCustomTranslation(const wxString &str1)
Definition: Internat.cpp:76
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:39
WaveTrack::SetRate
void SetRate(double newRate)
Definition: WaveTrack.cpp:365
NyquistEffect::Tokenizer::tok
wxString tok
Definition: Nyquist.h:170
ShuttleParams
Shuttle that deals with parameters. This is a base class with lots of virtual functions that do nothi...
Definition: Shuttle.h:61
LabelTrack
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:88
NyquistEffect::mDebugOutput
TranslatableString mDebugOutput
Definition: Nyquist.h:250
NyquistEffect::validatePath
bool validatePath(wxString path)
Definition: Nyquist.cpp:3261
CommandParameters::GetParameters
bool GetParameters(wxString &parms)
Definition: EffectAutomationParameters.h:259
NYQUISTEFFECTS_FAMILY
#define NYQUISTEFFECTS_FAMILY
Definition: Effect.h:58
NyquistEffect::mCont
bool mCont
Definition: Nyquist.h:208
NYQ_CTRL_FILE
@ NYQ_CTRL_FILE
Definition: Nyquist.h:36
ShuttleGuiBase::EndMultiColumn
void EndMultiColumn()
Definition: ShuttleGui.cpp:1212
Effect::IsBatchProcessing
virtual bool IsBatchProcessing()
Definition: Effect.cpp:1189
NyquistEffect::ParseProgram
bool ParseProgram(wxInputStream &stream)
Definition: Nyquist.cpp:2301
floatSample
@ floatSample
Definition: Types.h:722
NyquistEffect::mCategories
wxArrayString mCategories
Definition: Nyquist.h:276
NyquistEffect::BuildEffectWindow
void BuildEffectWindow(ShuttleGui &S)
Definition: Nyquist.cpp:2810
NyquistEffect::ParseFile
void ParseFile()
Definition: Nyquist.cpp:2395
NyqControl::choices
std::vector< EnumValueSymbol > choices
Definition: Nyquist.h:52
EffectManager::Get
static EffectManager & Get()
Definition: EffectManager.cpp:42
ExecForLisp
void * ExecForLisp(char *pIn)
Definition: ScriptCommandRelay.cpp:92
FileNames::AudacityPathList
AUDACITY_DLL_API const FilePaths & AudacityPathList()
A list of directories that should be searched for Audacity files (plug-ins, help files,...
WaveTrack::GetRMS
float GetRMS(double t0, double t1, bool mayThrow=true) const
Definition: WaveTrack.cpp:1861
NyquistEffect::mParameters
wxString mParameters
Definition: Nyquist.h:225
WaveTrack::GetSpectrogramSettings
const SpectrogramSettings & GetSpectrogramSettings() const
Definition: WaveTrack.cpp:665
Effect::AddToOutputTracks
Track * AddToOutputTracks(const std::shared_ptr< Track > &t)
Definition: Effect.cpp:2097
Effect::DefaultMessageBoxStyle
@ DefaultMessageBoxStyle
Definition: Effect.h:273
nyq_reformat_aud_do_response
void * nyq_reformat_aud_do_response(const wxString &Str)
Definition: Nyquist.cpp:3431
WaveTrack::ClearAndPaste
void ClearAndPaste(double t0, double t1, const Track *src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=NULL)
Definition: WaveTrack.cpp:750
NyquistEffect::Init
bool Init() override
Definition: Nyquist.cpp:540
NumericTextCtrl
Definition: NumericTextCtrl.h:171
NyqControl::high
double high
Definition: Nyquist.h:59
xlc_aud_do
LVAL xlc_aud_do(void)
Definition: Nyquist.cpp:3448
NyquistEffect::mRestoreSplits
bool mRestoreSplits
Definition: Nyquist.h:281
wxArrayStringEx
Definition: MemoryX.h:663
NumericTextCtrl::SetValue
void SetValue(double newValue)
Definition: NumericTextCtrl.cpp:1472
NyquistEffect::resolveFilePath
void resolveFilePath(wxString &path, FileExtension extension={})
Definition: Nyquist.cpp:3203
NyquistEffect::Parse
bool Parse(Tokenizer &tokenizer, const wxString &line, bool eof, bool first)
Definition: Nyquist.cpp:1949
NyquistEffect::UnQuote
static wxString UnQuote(const wxString &s, bool allowParens=true, wxString *pExtraString=nullptr)
Definition: Nyquist.cpp:1826
NYQ_MAX_LEN
#define NYQ_MAX_LEN
Definition: Nyquist.cpp:106
WaveTrack::EmptyCopy
Holder EmptyCopy(const SampleBlockFactoryPtr &pFactory={}) const
Definition: WaveTrack.cpp:574
wxEVT_COMMAND_TEXT_UPDATED
wxEVT_COMMAND_TEXT_UPDATED
Definition: Nyquist.cpp:126
NyquistEffect::GetVersion
wxString GetVersion() override
Definition: Nyquist.cpp:227
ComponentInterfaceSymbol
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Definition: ComponentInterface.h:60
NyquistEffect::UnQuoteMsgid
static TranslatableString UnQuoteMsgid(const wxString &s, bool allowParens=true, wxString *pExtraString=nullptr)
Definition: Nyquist.cpp:1786
NyqControl::val
double val
Definition: Nyquist.h:57
NumericTextCtrl::Options
Definition: NumericTextCtrl.h:177
ShuttleGui::Id
ShuttleGui & Id(int id)
Definition: ShuttleGui.cpp:2248
EffectManager::SetSkipStateFlag
void SetSkipStateFlag(bool flag)
Definition: EffectManager.cpp:213
SampleBuffer::Free
void Free()
Definition: SampleFormat.h:76
WaveTrack::Get
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
Definition: WaveTrack.cpp:1895
WaveTrack::SortedClipArray
WaveClipPointers SortedClipArray()
Definition: WaveTrack.cpp:2502
NyquistEffect::mReleaseVersion
TranslatableString mReleaseVersion
Definition: Nyquist.h:235
NyquistEffect::mType
EffectType mType
Definition: Nyquist.h:240
NyquistEffect::mNumSelectedChannels
unsigned mNumSelectedChannels
Definition: Nyquist.h:264
limitSampleBufferSize
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: Types.h:706
ID_Time
NyquistEffect::OnText ID_Time
Definition: Nyquist.cpp:129
ShuttleGuiBase::EndScroller
void EndScroller()
Definition: ShuttleGui.cpp:964
FileNames::BaseDir
AUDACITY_DLL_API FilePath BaseDir()
NyquistEffect::mIsSpectral
bool mIsSpectral
Definition: Nyquist.h:215
NYQ_CTRL_TIME
@ NYQ_CTRL_TIME
Definition: Nyquist.h:35
AllProjects::size
size_t size() const
Definition: Project.cpp:26
CommandParameters::ReadEnum
bool ReadEnum(const wxString &key, int *pi, const EnumValueSymbol choices[], size_t nChoices, const ObsoleteMap obsoletes[]=nullptr, size_t nObsoletes=0) const
Definition: EffectAutomationParameters.h:166
ShuttleGui::Style
ShuttleGui & Style(long iStyle)
Definition: ShuttleGui.h:734
ID_Text
ID_Text
Definition: Nyquist.cpp:125
Track::GetStartTime
virtual double GetStartTime() const =0
NyquistEffect::mInfo
TranslatableString mInfo
Definition: Nyquist.h:230
NyqControl::highStr
wxString highStr
Definition: Nyquist.h:56
Effect::mProjectRate
double mProjectRate
Definition: Effect.h:457
GUIPrefs::SetLang
static wxString SetLang(const wxString &lang)
Definition: GUIPrefs.cpp:274
NyquistEffect::Tokenizer
Definition: Nyquist.h:166
NyquistEffect::Tokenizer::paren
int paren
Definition: Nyquist.h:169
NyquistEffect::mPerTrackProps
wxString mPerTrackProps
Definition: Nyquist.h:279
NyquistEffect::mName
TranslatableString mName
Name of the Effect (untranslated)
Definition: Nyquist.h:227
NyquistEffect::EscapeString
wxString EscapeString(const wxString &inStr)
Definition: Nyquist.cpp:1649
eDebugID
@ eDebugID
Definition: ShuttleGui.h:626
samplePtr
char * samplePtr
Definition: Types.h:737
NyquistEffect::mScale
double mScale
Definition: Nyquist.h:268
ViewInfo::selectedRegion
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:199
XXO
#define XXO(s)
Definition: Internat.h:45
ShuttleGuiBase::StartScroller
wxScrolledWindow * StartScroller(int iStyle=0)
Definition: ShuttleGui.cpp:931
NyquistEffect::OnChoice
void OnChoice(wxCommandEvent &evt)
Definition: Nyquist.cpp:3079
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1170
EffectManager
EffectManager is the class that handles effects and effect categories.
Definition: EffectManager.h:46
NyquistEffect::TransferDataFromEffectWindow
bool TransferDataFromEffectWindow()
Definition: Nyquist.cpp:2638
Effect::mT0
double mT0
Definition: Effect.h:465
factory
static RegisteredToolbarFactory factory
Definition: ControlToolBar.cpp:804
sampleCount::as_double
double as_double() const
Definition: Types.h:616
NyquistEffect::mControls
std::vector< NyqControl > mControls
Definition: Nyquist.h:253
NyquistEffect::mProjectChanged
bool mProjectChanged
Definition: Nyquist.h:248
WaveTrackSubViewType
Definition: WaveTrackViewConstants.h:91
NyquistEffect::GetNyquistSearchPath
static FilePaths GetNyquistSearchPath()
Definition: Nyquist.cpp:2559
NYQ_CTRL_INT
@ NYQ_CTRL_INT
Definition: Nyquist.h:28
ShuttleGetAutomation
Shuttle that gets parameter values into a string.
Definition: Shuttle.h:87
label
TranslatableString label
Definition: Tags.cpp:755
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1160
WaveTrack::GetIdealBlockSize
size_t GetIdealBlockSize()
Definition: WaveTrack.cpp:1598
ShuttleGuiBase::AddTextBox
wxTextCtrl * AddTextBox(const TranslatableString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:631
ShuttleGuiBase::StartMultiColumn
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:1203
sampleCount::as_long_long
long long as_long_long() const
Definition: Types.h:618
NyquistEffect::mProgressOut
double mProgressOut
Definition: Nyquist.h:266
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1196
FileDialog::GetPath
virtual wxString GetPath() const
Definition: gtk/FileDialogPrivate.cpp:514
NyquistEffect::Tokenizer::Tokenize
bool Tokenize(const wxString &line, bool eof, size_t trimStart, size_t trimEnd)
Definition: Nyquist.cpp:1850
NyquistEffect::Tokenizer::q
bool q
Definition: Nyquist.h:168
NyquistEffect::mCopyright
TranslatableString mCopyright
Definition: Nyquist.h:236
NyquistEffect::mCompiler
bool mCompiler
Definition: Nyquist.h:211
ShuttleGuiBase::AddUnits
void AddUnits(const TranslatableString &Prompt, int wrapWidth=0)
Left aligned text string.
Definition: ShuttleGui.cpp:256
ShuttleGuiBase::AddFixedText
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:433
NyquistEffect::mIsSal
bool mIsSal
Definition: Nyquist.h:213
ID_Save
@ ID_Save
Definition: Nyquist.cpp:96
NyquistEffect::mPromptName
TranslatableString mPromptName
Definition: Nyquist.h:228
NyquistEffect::PutCallback
int PutCallback(float *buffer, int channel, int64_t start, int64_t len, int64_t totlen)
Definition: Nyquist.cpp:2485
ShuttleParams::DefineEnum
virtual void DefineEnum(int &var, const wxChar *key, const int vdefault, const EnumValueSymbol strings[], size_t nStrings)
Definition: Shuttle.cpp:339
Effect::ReplaceProcessedTracks
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2192
NyquistEffect::StaticOSCallback
static void StaticOSCallback(void *userdata)
Definition: Nyquist.cpp:2524
NyqControl::ticks
int ticks
Definition: Nyquist.h:60
Msgids
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Definition: Internat.cpp:267
name
const TranslatableString name
Definition: Distortion.cpp:98
UTF8CTOWX
#define UTF8CTOWX(X)
Definition: Internat.h:160
NyquistEffect::mPromptType
EffectType mPromptType
Definition: Nyquist.h:241
NyquistEffect::mAction
TranslatableString mAction
Definition: Nyquist.h:229
NumericConverter::TIME
@ TIME
Definition: NumericTextCtrl.h:51
NyquistEffect::mIsPrompt
bool mIsPrompt
Definition: Nyquist.h:221
PluginPath
wxString PluginPath
type alias for identifying a Plugin supplied by a module, each module defining its own interpretation...
Definition: Types.h:257
NyquistEffect::~NyquistEffect
virtual ~NyquistEffect()
Definition: Nyquist.cpp:195
NyquistEffect::TransferDataFromWindow
bool TransferDataFromWindow() override
Definition: Nyquist.cpp:1095
EffectTypeTool
@ EffectTypeTool
Definition: EffectInterface.h:61
format
int format
Definition: ExportPCM.cpp:54
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:503
NyquistEffect::SetAutomationParameters
bool SetAutomationParameters(CommandParameters &parms) override
Definition: Nyquist.cpp:412
NyquistEffect::TransferDataToEffectWindow
bool TransferDataToEffectWindow()
Definition: Nyquist.cpp:2583
ShuttleGui::Validator
ShuttleGui & Validator(const Factory &f)
Definition: ShuttleGui.h:685
NyqControl::type
int type
Definition: Nyquist.h:48
NumericTextCtrl::Options::MenuEnabled
Options & MenuEnabled(bool enable)
Definition: NumericTextCtrl.h:191
NyquistEffect::OSCallback
void OSCallback()
Definition: Nyquist.cpp:2529
NyqControl
A control on a NyquistDialog.
Definition: Nyquist.h:40
NyquistEffect::mFileName
wxFileName mFileName
Name of the Nyquist script file this effect is loaded from.
Definition: Nyquist.h:203
NYQ_CTRL_TEXT
@ NYQ_CTRL_TEXT
Definition: Nyquist.h:34
EffectClientInterface::EffectDialogFactory
std::function< wxDialog *(wxWindow &parent, EffectHostInterface *, EffectUIClientInterface *) > EffectDialogFactory
Definition: EffectInterface.h:149
NyquistEffect::GetSymbol
ComponentInterfaceSymbol GetSymbol() override
Definition: Nyquist.cpp:209
Effect::TestUIFlags
virtual unsigned TestUIFlags(unsigned mask)
Definition: Effect.cpp:1185
NyquistEffect::StaticGetCallback
static int StaticGetCallback(float *buffer, int channel, int64_t start, int64_t len, int64_t totlen, void *userdata)
Definition: Nyquist.cpp:2410
ID_Choice
@ ID_Choice
Definition: Nyquist.cpp:100
NyquistEffect::mIsTool
bool mIsTool
Definition: Nyquist.h:216
WaveTrack::Append
bool Append(constSamplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1)
Append the sample data to the WaveTrack. You must call Flush() after the last Append.
Definition: WaveTrack.cpp:1541
NYQUIST_WORKER_ID
#define NYQUIST_WORKER_ID
Definition: Effect.h:61
Effect::mOutputTracks
std::shared_ptr< TrackList > mOutputTracks
Definition: Effect.h:464
SpectrogramSettings::SpectralSelectionEnabled
bool SpectralSelectionEnabled() const
Definition: SpectrogramSettings.cpp:600
ID_Load
@ ID_Load
Definition: Nyquist.cpp:95
NyquistEffect::StaticPutCallback
static int StaticPutCallback(float *buffer, int channel, int64_t start, int64_t len, int64_t totlen, void *userdata)
Definition: Nyquist.cpp:2477
NyquistEffect::ParseChoice
static std::vector< EnumValueSymbol > ParseChoice(const wxString &text)
Definition: Nyquist.cpp:1659
NyquistEffect::mHelpFile
wxString mHelpFile
Definition: Nyquist.h:238
Nyquist.h
NyquistEffect::IsOk
bool IsOk()
Definition: Nyquist.cpp:2970
NyquistEffect::TransferDataToWindow
bool TransferDataToWindow() override
Definition: Nyquist.cpp:1073
NyquistEffect::mManPage
wxString mManPage
Definition: Nyquist.h:237
WaveTrackView::GetDisplays
std::vector< WaveTrackSubView::Type > GetDisplays() const
Definition: WaveTrackView.cpp:841
ShuttleGuiBase::AddButton
wxButton * AddButton(const TranslatableString &Text, int PositionFlags=wxALIGN_CENTRE, bool setDefault=false)
Definition: ShuttleGui.cpp:353
CopySamples
void CopySamples(constSamplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, unsigned int len, bool highQuality, unsigned int srcStride, unsigned int dstStride)
Definition: SampleFormat.cpp:102
EffectManager::kRepeatNyquistPrompt
@ kRepeatNyquistPrompt
Definition: EffectManager.h:61
ngettextc
static LVAL ngettextc()
Definition: Nyquist.cpp:3402
NyquistScripts
static const FileNames::FileType NyquistScripts
Definition: Nyquist.cpp:2977
NyquistEffect::SetLispVarsFromParameters
int SetLispVarsFromParameters(CommandParameters &parms, bool bTestOnly)
Definition: Nyquist.cpp:471
NyquistEffect::mProgressTot
double mProgressTot
Definition: Nyquist.h:267
NyquistEffect::NyquistToWxString
static wxString NyquistToWxString(const char *nyqString)
Definition: Nyquist.cpp:1636
NYQ_CTRL_FLOAT
@ NYQ_CTRL_FLOAT
Definition: Nyquist.h:29
LAT1CTOWX
#define LAT1CTOWX(X)
Definition: Internat.h:161
id
int id
Definition: WaveTrackControls.cpp:589
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
NyquistEffect::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S) override
Definition: Nyquist.cpp:1059
ShuttleGui::Name
ShuttleGui & Name(const TranslatableString &name)
Definition: ShuttleGui.h:670
NyquistEffect::GetAutomationParameters
bool GetAutomationParameters(CommandParameters &parms) override
Definition: Nyquist.cpp:363
wxDialogWrapper
Definition: wxPanelWrapper.h:81
NyquistEffect::mRedirectOutput
bool mRedirectOutput
Definition: Nyquist.h:247
Effect::GetNumWaveGroups
int GetNumWaveGroups()
Definition: Effect.h:345
NyquistEffect::mFoundType
bool mFoundType
Definition: Nyquist.h:210
Track::TypeSwitch
R TypeSwitch(const Functions &...functions)
Use this function rather than testing track type explicitly and making down-casts.
Definition: Track.h:692
FileNames::PlugInDir
AUDACITY_DLL_API FilePath PlugInDir()
The user plug-in directory (not a system one)
FileNames::HtmlHelpDir
AUDACITY_DLL_API FilePath HtmlHelpDir()
ShuttleGuiBase::AddWindow
wxWindow * AddWindow(wxWindow *pWindow)
Definition: ShuttleGui.cpp:292
NyquistEffect::Process
bool Process() override
Definition: Nyquist.cpp:634
WaveTrack::TimeToLongSamples
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: WaveTrack.cpp:1767
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
TrackList::Leaders
auto Leaders() -> TrackIterRange< TrackType >
Definition: Track.h:1388
NyquistEffect::ProcessOne
bool ProcessOne()
Definition: Nyquist.cpp:1111
NyquistEffect::OnFileButton
void OnFileButton(wxCommandEvent &evt)
Definition: Nyquist.cpp:3115
ShuttleGui::Position
ShuttleGui & Position(int flags)
Definition: ShuttleGui.h:719
NyquistEffect::CheckWhetherSkipEffect
bool CheckWhetherSkipEffect() override
Definition: Nyquist.cpp:625
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:238
Effect::mUIParent
wxWindow * mUIParent
Definition: Effect.h:477
FileDialogWrapper
Definition: wxPanelWrapper.h:165
Track::IsLeader
bool IsLeader() const
Definition: Track.cpp:370
FileNames::DefaultToDocumentsFolder
AUDACITY_DLL_API wxFileNameWrapper DefaultToDocumentsFolder(const wxString &preference)
NyquistEffect::ParseFileType
FileNames::FileType ParseFileType(const wxString &text)
Definition: Nyquist.cpp:1704
NyqControl::name
wxString name
Definition: Nyquist.h:50
NyquistEffect::mMergeClips
int mMergeClips
Definition: Nyquist.h:282
_
#define _(s)
Definition: Internat.h:76
ID_Editor
@ ID_Editor
Definition: Nyquist.cpp:94
sampleCount
Definition: Types.h:589
NyquistOutputDialog
Dialog used with NyquistEffect.
Definition: Nyquist.h:294
EffectTypeAnalyze
@ EffectTypeAnalyze
Definition: EffectInterface.h:60
NyqControl::low
double low
Definition: Nyquist.h:58
NyquistEffect::mCurBufferStart
sampleCount mCurBufferStart[2]
Definition: Nyquist.h:271
NYQ_CTRL_FLOAT_TEXT
@ NYQ_CTRL_FLOAT_TEXT
Definition: Nyquist.h:33
ProjectSettings::GetRate
double GetRate() const
Definition: ProjectSettings.cpp:166
Verbatim
TranslatableString Verbatim(wxString str)
Definition: Types.h:581
WaveTrackView::Get
static WaveTrackView & Get(WaveTrack &track)
Definition: WaveTrackView.cpp:763
NyquistEffect::mCurLen
sampleCount mCurLen
Definition: Nyquist.h:258
NyqControl::fileTypes
FileNames::FileTypes fileTypes
Definition: Nyquist.h:53
NyquistEffect::mCurStart
sampleCount mCurStart[2]
Definition: Nyquist.h:257
AllProjects
an object of class AllProjects acts like a standard library container, but refers to a global array o...
Definition: Project.h:38
NumericTextCtrl::Options::ReadOnly
Options & ReadOnly(bool enable)
Definition: NumericTextCtrl.h:190
NyquistEffect::mOK
bool mOK
Definition: Nyquist.h:222
NyquistEffect::mCurTrack
WaveTrack * mCurTrack[2]
Definition: Nyquist.h:256
NyqControl::valStr
wxString valStr
Definition: Nyquist.h:54
NyquistEffect::mProps
wxString mProps
Definition: Nyquist.h:278
int16Sample
@ int16Sample
Definition: Types.h:720
Effect::nEffectsDone
static int nEffectsDone
Definition: Effect.h:375
NYQ_CTRL_CHOICE
@ NYQ_CTRL_CHOICE
Definition: Nyquist.h:31
NyqControl::label
wxString label
Definition: Nyquist.h:51
LispScripts
static const FileNames::FileType LispScripts
Definition: Nyquist.cpp:2979
NyquistEffect::Continue
void Continue()
Definition: Nyquist.cpp:1776
NyquistEffect::Tokenizer::tokens
wxArrayStringEx tokens
Definition: Nyquist.h:171
NyquistEffect::SetCommand
void SetCommand(const wxString &cmd)
Definition: Nyquist.cpp:1762
NYQUIST_PROMPT_ID
#define NYQUIST_PROMPT_ID
Definition: Effect.h:60
NyquistEffect::mCount
unsigned mCount
Definition: Nyquist.h:263
Effect::TotalProgress
bool TotalProgress(double frac, const TranslatableString &={})
Definition: Effect.cpp:2016
NyquistOutputDialog::OnOk
void OnOk(wxCommandEvent &event)
Definition: Nyquist.cpp:3354
NyquistEffect::OnSave
void OnSave(wxCommandEvent &evt)
Definition: Nyquist.cpp:3020
EVT_COMMAND_RANGE
EVT_COMMAND_RANGE(ID_Slider, ID_Slider+99, wxEVT_COMMAND_SLIDER_UPDATED, NyquistEffect::OnSlider) EVT_COMMAND_RANGE(ID_Text
FileNames::FileTypes
std::vector< FileType > FileTypes
Definition: FileNames.h:79
NyquistEffect::mExternal
bool mExternal
Definition: Nyquist.h:214
WaveTrack::GetBestBlockSize
size_t GetBestBlockSize(sampleCount t) const
Definition: WaveTrack.cpp:1560
Effect::mPresetNames
wxArrayString mPresetNames
Definition: Effect.h:471
NyquistEffect::ParseFileExtensions
FileExtensions ParseFileExtensions(const wxString &text)
Definition: Nyquist.cpp:1691
ShuttleGetDefinition
Shuttle that retrieves a JSON format definition of a command's parameters.
Definition: ShuttleGetDefinition.h:22
ShuttleGuiBase::AddPrompt
void AddPrompt(const TranslatableString &Prompt, int wrapWidth=0)
Right aligned text string.
Definition: ShuttleGui.cpp:231
TranslatableString::Translation
wxString Translation() const
Definition: Types.h:337
ShuttleParams::Define
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:332
NyquistEffect::mCmd
wxString mCmd
Definition: Nyquist.h:226
NyquistEffect::mCurBufferLen
size_t mCurBufferLen[2]
Definition: Nyquist.h:272
FileNames::FindDefaultPath
AUDACITY_DLL_API FilePath FindDefaultPath(Operation op)
NyquistEffect::TransferDataFromPromptWindow
bool TransferDataFromPromptWindow()
Definition: Nyquist.cpp:2620
NyquistEffect::OnText
void OnText(wxCommandEvent &evt)
Definition: Nyquist.cpp:3282
NyquistEffect::mAuthor
TranslatableString mAuthor
Definition: Nyquist.h:231
TranslatableString::Format
TranslatableString & Format(Args &&...args) &
Definition: Types.h:363
ComponentInterface::GetName
TranslatableString GetName()
Definition: PluginManager.cpp:3233
NyquistEffect::StaticOutputCallback
static void StaticOutputCallback(int c, void *userdata)
Definition: Nyquist.cpp:2508
ShuttleGuiBase::AddVariableText
wxStaticText * AddVariableText(const TranslatableString &Str, bool bCenter=false, int PositionFlags=0, int wrapWidth=0)
Definition: ShuttleGui.cpp:456
NyquistEffect::Tokenizer::sl
bool sl
Definition: Nyquist.h:167
NyquistEffect::mOutputTrack
WaveTrack * mOutputTrack[2]
Definition: Nyquist.h:274
NyquistEffect::ShowInterface
bool ShowInterface(wxWindow &parent, const EffectDialogFactory &factory, bool forceModal=false) override
Definition: Nyquist.cpp:1007
NyquistEffect::GetVendor
VendorSymbol GetVendor() override
Definition: Nyquist.cpp:217
NyquistEffect::mDebugOutputStr
wxString mDebugOutputStr
Definition: Nyquist.h:249
nyq_make_opaque_string
void * nyq_make_opaque_string(int size, unsigned char *src)
Definition: Nyquist.cpp:3417
NyquistEffect::mVersion
int mVersion
Definition: Nyquist.h:252
EffectType
EffectType
Definition: EffectInterface.h:55
safenew
#define safenew
Definition: MemoryX.h:8
TrackList::SyncLockGroup
static TrackIterRange< Track > SyncLockGroup(Track *pTrack)
Definition: Track.cpp:639
NyqControl::var
wxString var
Definition: Nyquist.h:49
NyquistEffect::ManualPage
wxString ManualPage() override
Definition: Nyquist.cpp:239
KEY_Command
static const wxChar * KEY_Command
Definition: Nyquist.cpp:110
ShuttleGuiBase::SetStretchyCol
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:195
NyquistEffect::DefineParams
bool DefineParams(ShuttleParams &S) override
Definition: Nyquist.cpp:296
Effect::ShowInterface
bool ShowInterface(wxWindow &parent, const EffectDialogFactory &factory, bool forceModal=false) override
Definition: Effect.cpp:480
Effect::TrackGroupProgress
bool TrackGroupProgress(int whichGroup, double frac, const TranslatableString &={})
Definition: Effect.cpp:2032
FileDialog::ShowModal
virtual int ShowModal()
Definition: gtk/FileDialogPrivate.cpp:445
NumericConverter::GetValue
double GetValue()
Definition: NumericTextCtrl.cpp:1172
TrackList::Selected
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1371
NyquistEffect::mCommandText
wxTextCtrl * mCommandText
Definition: Nyquist.h:284
END_EVENT_TABLE
END_EVENT_TABLE()
Effect::TrackProgress
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={})
Definition: Effect.cpp:2024
NyquistEffect::mDebugButton
bool mDebugButton
Definition: Nyquist.h:244
TimeTrack
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:24
NyquistEffect::OnTime
void OnTime(wxCommandEvent &evt)
Definition: Nyquist.cpp:3084
Internat::ToString
static wxString ToString(double numberToConvert, int digitsAfterDecimalPoint=-1)
Convert a number to a string, always uses the dot as decimal separator.
Definition: Internat.cpp:155
FileNames::FileType
Definition: FileNames.h:55
FileDialog::GetPaths
virtual void GetPaths(wxArrayString &paths) const
Definition: gtk/FileDialogPrivate.cpp:524
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: Types.h:732
NoteTrack
A Track that is used for Midi notes. (Somewhat old code).
Definition: NoteTrack.h:66
NyquistEffect::mReentryCount
static int mReentryCount
Definition: Nyquist.h:120
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:638
Internat::CompatibleToDouble
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:139
ID_Slider
@ ID_Slider
Definition: Nyquist.cpp:98
NyquistEffect::Break
void Break()
Definition: Nyquist.cpp:1771
KEY_Parameters
static const wxChar * KEY_Parameters
Definition: Nyquist.cpp:111
ngettext
static LVAL ngettext()
Definition: Nyquist.cpp:3387
NyquistEffect::mCurNumChannels
unsigned mCurNumChannels
Definition: Nyquist.h:255
NyquistEffect::mOutputTime
double mOutputTime
Definition: Nyquist.h:262
NYQ_CTRL_INT_TEXT
@ NYQ_CTRL_INT_TEXT
Definition: Nyquist.h:32
Effect::SetBatchProcessing
virtual void SetBatchProcessing(bool start)
Definition: Effect.cpp:1194
SelectedRegion
Defines a selected portion of a project.
Definition: SelectedRegion.h:38
TempDirectory::TempDir
AUDACITY_DLL_API wxString TempDir()
Definition: TempDirectory.cpp:26
WaveTrack::GetRate
double GetRate() const
Definition: WaveTrack.cpp:360
NyquistEffect::RedirectOutput
void RedirectOutput()
Definition: Nyquist.cpp:1757