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