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