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