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 <wx/choice.h>
31#include <wx/scrolwin.h>
32#include <wx/sizer.h>
33#include <wx/tokenzr.h>
34#include <wx/utils.h> // wxYieldIfNeeded
35#include <wx/valgen.h>
36
37#include "../../commands/ScriptCommandRelay.h" // ExecFromMain
38#include "../../tracks/playabletrack/wavetrack/ui/WaveChannelView.h"
39#include "../../widgets/NumericTextCtrl.h"
40#include "../../widgets/valnum.h"
41#include "../EffectEditor.h"
42#include "BasicUI.h"
43#include "ShuttleGui.h"
44#include "EffectManager.h"
45
46#include <nyx.h>
47#ifndef nyx_returns_start_and_end_time
48#error You need to update lib-src/libnyquist
49#endif
50
51#include <cfloat>
52
53enum
54{
55 ID_Editor = 10000,
58
59 ID_Slider = 11000,
60 ID_Text = 12000,
61 ID_Choice = 13000,
62 ID_Time = 14000,
63 ID_FILE = 15000
64};
65
66BEGIN_EVENT_TABLE(NyquistEffect, wxEvtHandler)
69
71 wxEVT_COMMAND_SLIDER_UPDATED, NyquistEffect::OnSlider)
75 wxEVT_COMMAND_CHOICE_SELECTED, NyquistEffect::OnChoice)
81
82int NyquistEffect::ShowHostInterface(EffectBase &plugin,
83 wxWindow &parent, const EffectDialogFactory &factory,
84 std::shared_ptr<EffectInstance> &pInstance, EffectSettingsAccess &access,
85 bool forceModal)
86{
87 int res = wxID_APPLY;
89 // Show the normal (prompt or effect) interface
91 parent, factory, pInstance, access, forceModal);
92 }
93
94
95 // Remember if the user clicked debug
96 mDebug = (res == eDebugID);
97
98 // We're done if the user clicked "Close", we are not the Nyquist Prompt,
99 // or the program currently loaded into the prompt doesn't have a UI.
100 if (!res || !mIsPrompt || mControls.size() == 0 || !pInstance)
101 return res;
102
103 // Nyquist prompt was OK, but gave us some magic ;control comments to
104 // reinterpret into a second dialog
105
107 effect.SetCommand(mInputCmd);
108 Finally Do{[&]{
109 // A second dialog will use effect as a pushed event handler.
110 // wxWidgets delays window destruction until idle time.
111 // Yield to destroy the dialog while effect is still in scope.
113 }};
114
115 // Must give effect its own settings to interpret, not those in access
116 // Let's also give it its own instance
117 auto newSettings = effect.MakeSettings();
118 auto pNewInstance = effect.MakeInstance();
119 auto newAccess = std::make_shared<SimpleEffectSettingsAccess>(newSettings);
120
121 if (IsBatchProcessing()) {
122 effect.SetBatchProcessing();
123
125 cp.SetParameters(mParameters);
126 effect.LoadSettings(cp, newSettings);
127
128 // Show the normal (prompt or effect) interface
129 // Don't pass this as first argument, pass the worker to itself
130 res = effect.ShowHostInterface(effect,
131 parent, factory, pNewInstance, *newAccess, forceModal);
132 if (res) {
134 effect.SaveSettings(newSettings, cp);
135 cp.GetParameters(mParameters);
136 }
137 }
138 else {
139 if (!factory)
140 return 0;
141 // Don't pass this as first argument, pass the worker to itself
142 res = effect.ShowHostInterface(effect,
143 parent, factory, pNewInstance, *newAccess, false );
144 if (!res)
145 return 0;
146
147 // Wrap the new settings in the old settings
148 access.ModifySettings([&](EffectSettings &settings){
149 auto &nyquistSettings = GetSettings(settings);
150 nyquistSettings.proxySettings = std::move(newSettings);
151 nyquistSettings.proxyDebug = this->mDebug;
152 nyquistSettings.controls = move(effect.mControls);
153 return nullptr;
154 });
155 }
156 if (!pNewInstance)
157 // Propagate the failure from nested ShowHostInterface
158 pInstance.reset();
159 return res;
160}
161
162std::unique_ptr<EffectEditor> NyquistEffect::PopulateOrExchange(
164 const EffectOutputs *)
165{
166 mUIParent = S.GetParent();
167 if (mIsPrompt)
169 else
171 return nullptr;
172}
173
175{
176 mUIParent->TransferDataToWindow();
177
178 bool success;
179 if (mIsPrompt)
180 {
181 success = TransferDataToPromptWindow();
182 }
183 else
184 {
185 success = TransferDataToEffectWindow();
186 }
187
188 if (success)
189 {
191 }
192
193 return success;
194}
195
197{
198 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
199 {
200 return false;
201 }
202
203 if (mIsPrompt)
204 {
206 }
208}
209
211{
212 mCommandText->ChangeValue(mInputCmd);
213
214 return true;
215}
216
218{
219 for (size_t i = 0, cnt = mControls.size(); i < cnt; i++)
220 {
221 NyqControl & ctrl = mControls[i];
222
223 if (ctrl.type == NYQ_CTRL_CHOICE)
224 {
225 const auto count = ctrl.choices.size();
226
227 int val = (int)ctrl.val;
228 if (val < 0 || val >= (int)count)
229 {
230 val = 0;
231 }
232
233 wxChoice *c = (wxChoice *) mUIParent->FindWindow(ID_Choice + i);
234 c->SetSelection(val);
235 }
236 else if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_FLOAT)
237 {
238 // wxTextCtrls are handled by the validators
239 double range = ctrl.high - ctrl.low;
240 int val = (int)(0.5 + ctrl.ticks * (ctrl.val - ctrl.low) / range);
241 wxSlider *s = (wxSlider *) mUIParent->FindWindow(ID_Slider + i);
242 s->SetValue(val);
243 }
244 else if (ctrl.type == NYQ_CTRL_TIME)
245 {
246 NumericTextCtrl *n = (NumericTextCtrl *) mUIParent->FindWindow(ID_Time + i);
247 n->SetValue(ctrl.val);
248 }
249 }
250
251 return true;
252}
253
255{
256 mInputCmd = mCommandText->GetValue();
257
258 // Un-correct smart quoting, bothersomely applied in wxTextCtrl by
259 // the native widget of MacOS 10.9 SDK
260 const wxString left = wxT("\u201c"), right = wxT("\u201d"), dumb = '"';
261 mInputCmd.Replace(left, dumb, true);
262 mInputCmd.Replace(right, dumb, true);
263
264 const wxString leftSingle = wxT("\u2018"), rightSingle = wxT("\u2019"),
265 dumbSingle = '\'';
266 mInputCmd.Replace(leftSingle, dumbSingle, true);
267 mInputCmd.Replace(rightSingle, dumbSingle, true);
268
269 return ParseCommand(mInputCmd);
270}
271
273{
274 if (mControls.size() == 0)
275 {
276 return true;
277 }
278
279 for (unsigned int i = 0; i < mControls.size(); i++)
280 {
281 NyqControl *ctrl = &mControls[i];
282
283 if (ctrl->type == NYQ_CTRL_STRING || ctrl->type == NYQ_CTRL_TEXT)
284 {
285 continue;
286 }
287
288 if (ctrl->val == UNINITIALIZED_CONTROL)
289 {
290 ctrl->val = GetCtrlValue(ctrl->valStr);
291 }
292
293 if (ctrl->type == NYQ_CTRL_CHOICE)
294 {
295 continue;
296 }
297
298 if (ctrl->type == NYQ_CTRL_FILE)
299 {
300 resolveFilePath(ctrl->valStr);
301
302 wxString path;
303 if (ctrl->valStr.StartsWith("\"", &path))
304 {
305 // Validate if a list of quoted paths.
306 if (path.EndsWith("\"", &path))
307 {
308 path.Replace("\"\"", "\"");
309 wxStringTokenizer tokenizer(path, "\"");
310 while (tokenizer.HasMoreTokens())
311 {
312 wxString token = tokenizer.GetNextToken();
313 if(!validatePath(token))
314 {
315 const auto message =
316 XO("\"%s\" is not a valid file path.").Format( token );
318 message,
319 wxOK | wxICON_EXCLAMATION | wxCENTRE,
320 XO("Error") );
321 return false;
322 }
323 }
324 continue;
325 }
326 else
327 {
328 const auto message =
329 /* i18n-hint: Warning that there is one quotation mark rather than a pair.*/
330 XO("Mismatched quotes in\n%s").Format( ctrl->valStr );
332 message,
333 wxOK | wxICON_EXCLAMATION | wxCENTRE,
334 XO("Error") );
335 return false;
336 }
337 }
338 // Validate a single path.
339 else if (validatePath(ctrl->valStr))
340 {
341 continue;
342 }
343
344 // Validation failed
345 const auto message =
346 XO("\"%s\" is not a valid file path.").Format( ctrl->valStr );
348 message,
349 wxOK | wxICON_EXCLAMATION | wxCENTRE,
350 XO("Error") );
351 return false;
352 }
353
354 if (ctrl->type == NYQ_CTRL_TIME)
355 {
356 NumericTextCtrl *n = (NumericTextCtrl *) mUIParent->FindWindow(ID_Time + i);
357 ctrl->val = n->GetValue();
358 }
359
360 if (ctrl->type == NYQ_CTRL_INT_TEXT && ctrl->lowStr.IsSameAs(wxT("nil"), false)) {
361 ctrl->low = INT_MIN;
362 }
363 else if ((ctrl->type == NYQ_CTRL_FLOAT_TEXT || ctrl->type == NYQ_CTRL_TIME) &&
364 ctrl->lowStr.IsSameAs(wxT("nil"), false))
365 {
366 ctrl->low = -(FLT_MAX);
367 }
368 else
369 {
370 ctrl->low = GetCtrlValue(ctrl->lowStr);
371 }
372
373 if (ctrl->type == NYQ_CTRL_INT_TEXT && ctrl->highStr.IsSameAs(wxT("nil"), false)) {
374 ctrl->high = INT_MAX;
375 }
376 else if ((ctrl->type == NYQ_CTRL_FLOAT_TEXT || ctrl->type == NYQ_CTRL_TIME) &&
377 ctrl->highStr.IsSameAs(wxT("nil"), false))
378 {
379 ctrl->high = FLT_MAX;
380 }
381 else
382 {
383 ctrl->high = GetCtrlValue(ctrl->highStr);
384 }
385
386 if (ctrl->high < ctrl->low)
387 {
388 ctrl->high = ctrl->low + 1;
389 }
390
391 if (ctrl->val < ctrl->low)
392 {
393 ctrl->val = ctrl->low;
394 }
395
396 if (ctrl->val > ctrl->high)
397 {
398 ctrl->val = ctrl->high;
399 }
400
401 ctrl->ticks = 1000;
402 if (ctrl->type == NYQ_CTRL_INT &&
403 (ctrl->high - ctrl->low < ctrl->ticks))
404 {
405 ctrl->ticks = (int)(ctrl->high - ctrl->low);
406 }
407 }
408
409 return true;
410}
411
413{
414 S.StartVerticalLay();
415 {
416 S.StartMultiColumn(3, wxEXPAND);
417 {
418 S.SetStretchyCol(1);
419
420 S.AddVariableText(XO("Enter Nyquist Command: "));
421
422 S.AddSpace(1, 1);
423 }
424 S.EndMultiColumn();
425
426 S.StartHorizontalLay(wxEXPAND, 1);
427 {
428 mCommandText = S.Focus()
429 .MinSize( { 500, 200 } )
430 .AddTextWindow(wxT(""));
431 }
432 S.EndHorizontalLay();
433
434 S.StartHorizontalLay(wxALIGN_CENTER, 0);
435 {
436 S.Id(ID_Load).AddButton(XXO("&Load"));
437 S.Id(ID_Save).AddButton(XXO("&Save"));
438 }
439 S.EndHorizontalLay();
440 }
441 S.EndVerticalLay();
442}
443
445{
446 wxScrolledWindow *scroller = S.Style(wxVSCROLL | wxTAB_TRAVERSAL)
447 .StartScroller(2);
448 {
449 S.StartMultiColumn(4);
450 {
451 for (size_t i = 0; i < mControls.size(); i++)
452 {
453 NyqControl & ctrl = mControls[i];
454
455 if (ctrl.type == NYQ_CTRL_TEXT)
456 {
457 S.EndMultiColumn();
458 S.StartHorizontalLay(wxALIGN_LEFT, 0);
459 {
460 S.AddSpace(0, 10);
461 S.AddFixedText( Verbatim( ctrl.label ), false );
462 }
463 S.EndHorizontalLay();
464 S.StartMultiColumn(4);
465 }
466 else
467 {
468 auto prompt = XXO("%s:").Format( ctrl.name );
469 S.AddPrompt( prompt );
470
471 if (ctrl.type == NYQ_CTRL_STRING)
472 {
473 S.AddSpace(10, 10);
474
475 auto item = S.Id(ID_Text + i)
476 .Validator<wxGenericValidator>(&ctrl.valStr)
477 .Name( prompt )
478 .AddTextBox( {}, wxT(""), 50);
479 }
480 else if (ctrl.type == NYQ_CTRL_CHOICE)
481 {
482 S.AddSpace(10, 10);
483
484 S.Id(ID_Choice + i).AddChoice( {},
485 Msgids( ctrl.choices.data(), ctrl.choices.size() ) );
486 }
487 else if (ctrl.type == NYQ_CTRL_TIME)
488 {
489 S.AddSpace(10, 10);
490
491 const auto options = NumericTextCtrl::Options{}
492 .AutoPos(true)
493 .MenuEnabled(true)
494 .ReadOnly(false);
495
498 S.GetParent(), (ID_Time + i),
501 ctrl.val,
502 options);
503 S
504 .Name( prompt )
505 .Position(wxALIGN_LEFT | wxALL)
506 .AddWindow(time);
507 }
508 else if (ctrl.type == NYQ_CTRL_FILE)
509 {
510 S.AddSpace(10, 10);
511
512 // Get default file extension if specified in wildcards
513 FileExtension defaultExtension;
514 if (!ctrl.fileTypes.empty()) {
515 const auto &type = ctrl.fileTypes[0];
516 if ( !type.extensions.empty() )
517 defaultExtension = type.extensions[0];
518 }
519 resolveFilePath(ctrl.valStr, defaultExtension);
520
521 wxTextCtrl *item = S.Id(ID_Text+i)
522 .Name( prompt )
523 .AddTextBox( {}, wxT(""), 40);
524 item->SetValidator(wxGenericValidator(&ctrl.valStr));
525
526 if (ctrl.label.empty())
527 // We'd expect wxFileSelectorPromptStr to already be translated, but apparently not.
528 ctrl.label = wxGetTranslation( wxFileSelectorPromptStr );
529 S.Id(ID_FILE + i).AddButton(
530 Verbatim(ctrl.label), wxALIGN_LEFT);
531 }
532 else
533 {
534 // Integer or Real
535 if (ctrl.type == NYQ_CTRL_INT_TEXT || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
536 {
537 S.AddSpace(10, 10);
538 }
539
540 S.Id(ID_Text+i);
541 if (ctrl.type == NYQ_CTRL_FLOAT || ctrl.type == NYQ_CTRL_FLOAT_TEXT)
542 {
543 double range = ctrl.high - ctrl.low;
544 S.Validator<FloatingPointValidator<double>>(
545 // > 12 decimal places can cause rounding errors in display.
546 12, &ctrl.val,
547 // Set number of decimal places
548 (range < 10
549 ? NumValidatorStyle::THREE_TRAILING_ZEROES
550 : range < 100
551 ? NumValidatorStyle::TWO_TRAILING_ZEROES
552 : NumValidatorStyle::ONE_TRAILING_ZERO),
553 ctrl.low, ctrl.high
554 );
555 }
556 else
557 {
558 S.Validator<IntegerValidator<double>>(
559 &ctrl.val, NumValidatorStyle::DEFAULT,
560 (int) ctrl.low, (int) ctrl.high);
561 }
562 wxTextCtrl *item = S
563 .Name( prompt )
564 .AddTextBox( {}, wxT(""),
565 (ctrl.type == NYQ_CTRL_INT_TEXT ||
566 ctrl.type == NYQ_CTRL_FLOAT_TEXT) ? 25 : 12);
567
568 if (ctrl.type == NYQ_CTRL_INT || ctrl.type == NYQ_CTRL_FLOAT)
569 {
570 S.Id(ID_Slider + i)
571 .Style(wxSL_HORIZONTAL)
572 .MinSize( { 150, -1 } )
573 .AddSlider( {}, 0, ctrl.ticks, 0);
574 }
575 }
576
577 if (ctrl.type != NYQ_CTRL_FILE)
578 {
579 if (ctrl.type == NYQ_CTRL_CHOICE || ctrl.label.empty())
580 {
581 S.AddSpace(10, 10);
582 }
583 else
584 {
585 S.AddUnits( Verbatim( ctrl.label ) );
586 }
587 }
588 }
589 }
590 }
591 S.EndMultiColumn();
592 }
593 S.EndScroller();
594
595 scroller->SetScrollRate(0, 20);
596
597 // This fools NVDA into not saying "Panel" when the dialog gets focus
598 scroller->SetName(wxT("\a"));
599 scroller->SetLabel(wxT("\a"));
600}
601
602static const FileNames::FileType
603 /* i18n-hint: Nyquist is the name of a programming language */
604 NyquistScripts = { XO("Nyquist scripts"), { wxT("ny") }, true }
605 /* i18n-hint: Lisp is the name of a programming language */
606 , LispScripts = { XO("Lisp scripts"), { wxT("lsp") }, true }
607;
608
609void NyquistEffect::OnLoad(wxCommandEvent & WXUNUSED(evt))
610{
611 if (mCommandText->IsModified())
612 {
613 if (wxNO == EffectUIServices::DoMessageBox(*this,
614 XO("Current program has been modified.\nDiscard changes?"),
615 wxYES_NO ) )
616 {
617 return;
618 }
619 }
620
622 mUIParent,
623 XO("Load Nyquist script"),
624 mFileName.GetPath(),
625 wxEmptyString,
626 {
627 NyquistScripts,
628 LispScripts,
629 FileNames::TextFiles,
630 FileNames::AllFiles
631 },
632 wxFD_OPEN | wxRESIZE_BORDER);
633
634 if (dlog.ShowModal() != wxID_OK)
635 {
636 return;
637 }
638
639 mFileName = dlog.GetPath();
640
641 if (!mCommandText->LoadFile(mFileName.GetFullPath()))
642 {
643 EffectUIServices::DoMessageBox(*this, XO("File could not be loaded"));
644 }
645}
646
647void NyquistEffect::OnSave(wxCommandEvent & WXUNUSED(evt))
648{
650 mUIParent,
651 XO("Save Nyquist script"),
652 mFileName.GetPath(),
653 mFileName.GetFullName(),
654 {
655 NyquistScripts,
656 LispScripts,
657 FileNames::AllFiles
658 },
659 wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER);
660
661 if (dlog.ShowModal() != wxID_OK)
662 {
663 return;
664 }
665
666 mFileName = dlog.GetPath();
667
668 if (!mCommandText->SaveFile(mFileName.GetFullPath()))
669 {
670 EffectUIServices::DoMessageBox(*this, XO("File could not be saved"));
671 }
672}
673
674void NyquistEffect::OnSlider(wxCommandEvent & evt)
675{
676 int i = evt.GetId() - ID_Slider;
677 NyqControl & ctrl = mControls[i];
678
679 int val = evt.GetInt();
680 double range = ctrl.high - ctrl.low;
681 double newVal = (val / (double)ctrl.ticks) * range + ctrl.low;
682
683 // Determine precision for displayed number
684 int precision = range < 1.0 ? 3 :
685 range < 10.0 ? 2 :
686 range < 100.0 ? 1 :
687 0;
688
689 // If the value is at least one tick different from the current value
690 // change it (this prevents changes from manually entered values unless
691 // the slider actually moved)
692 if (fabs(newVal - ctrl.val) >= (1 / (double)ctrl.ticks) * range &&
693 fabs(newVal - ctrl.val) >= pow(0.1, precision) / 2)
694 {
695 // First round to the appropriate precision
696 newVal *= pow(10.0, precision);
697 newVal = floor(newVal + 0.5);
698 newVal /= pow(10.0, precision);
699
700 ctrl.val = newVal;
701
702 mUIParent->FindWindow(ID_Text + i)->GetValidator()->TransferToWindow();
703 }
704}
705
706void NyquistEffect::OnChoice(wxCommandEvent & evt)
707{
708 mControls[evt.GetId() - ID_Choice].val = (double) evt.GetInt();
709}
710
711void NyquistEffect::OnTime(wxCommandEvent& evt)
712{
713 int i = evt.GetId() - ID_Time;
714 static double value = 0.0;
715 NyqControl & ctrl = mControls[i];
716
717 NumericTextCtrl *n = (NumericTextCtrl *) mUIParent->FindWindow(ID_Time + i);
718 double val = n->GetValue();
719
720 // Observed that two events transmitted on each control change (Linux)
721 // so skip if value has not changed.
722 if (val != value) {
723 if (val < ctrl.low || val > ctrl.high) {
724 const auto message = XO("Value range:\n%s to %s")
725 .Format( ToTimeFormat(ctrl.low), ToTimeFormat(ctrl.high) );
727 message,
728 wxOK | wxCENTRE,
729 XO("Value Error") );
730 }
731
732 if (val < ctrl.low)
733 val = ctrl.low;
734 else if (val > ctrl.high)
735 val = ctrl.high;
736
737 n->SetValue(val);
738 value = val;
739 }
740}
741
742void NyquistEffect::OnFileButton(wxCommandEvent& evt)
743{
744 int i = evt.GetId() - ID_FILE;
745 NyqControl & ctrl = mControls[i];
746
747 // Get style flags:
748 // Ensure legal combinations so that wxWidgets does not throw an assert error.
749 unsigned int flags = 0;
750 if (!ctrl.highStr.empty())
751 {
752 wxStringTokenizer tokenizer(ctrl.highStr, ",");
753 while ( tokenizer.HasMoreTokens() )
754 {
755 wxString token = tokenizer.GetNextToken().Trim(true).Trim(false);
756 if (token.IsSameAs("open", false))
757 {
758 flags |= wxFD_OPEN;
759 flags &= ~wxFD_SAVE;
760 flags &= ~wxFD_OVERWRITE_PROMPT;
761 }
762 else if (token.IsSameAs("save", false))
763 {
764 flags |= wxFD_SAVE;
765 flags &= ~wxFD_OPEN;
766 flags &= ~wxFD_MULTIPLE;
767 flags &= ~wxFD_FILE_MUST_EXIST;
768 }
769 else if (token.IsSameAs("overwrite", false) && !(flags & wxFD_OPEN))
770 {
771 flags |= wxFD_OVERWRITE_PROMPT;
772 }
773 else if (token.IsSameAs("exists", false) && !(flags & wxFD_SAVE))
774 {
775 flags |= wxFD_FILE_MUST_EXIST;
776 }
777 else if (token.IsSameAs("multiple", false) && !(flags & wxFD_SAVE))
778 {
779 flags |= wxFD_MULTIPLE;
780 }
781 }
782 }
783
785
786 wxFileName fname = ctrl.valStr;
787 wxString defaultDir = fname.GetPath();
788 wxString defaultFile = fname.GetName();
789 auto message = XO("Select a file");
790
791 if (flags & wxFD_MULTIPLE)
792 message = XO("Select one or more files");
793 else if (flags & wxFD_SAVE)
794 message = XO("Save file as");
795
796 FileDialogWrapper openFileDialog(mUIParent->FindWindow(ID_FILE + i),
797 message,
798 defaultDir,
799 defaultFile,
800 ctrl.fileTypes,
801 flags); // styles
802
803 if (openFileDialog.ShowModal() == wxID_CANCEL)
804 {
805 return;
806 }
807
808 wxString path;
809 // When multiple files selected, return file paths as a list of quoted strings.
810 if (flags & wxFD_MULTIPLE)
811 {
812 wxArrayString selectedFiles;
813 openFileDialog.GetPaths(selectedFiles);
814
815 for (size_t sf = 0; sf < selectedFiles.size(); sf++) {
816 path += "\"";
817 path += selectedFiles[sf];
818 path += "\"";
819 }
820 ctrl.valStr = path;
821 }
822 else
823 {
824 ctrl.valStr = openFileDialog.GetPath();
825 }
826
827 mUIParent->FindWindow(ID_Text + i)->GetValidator()->TransferToWindow();
828}
829
830void NyquistEffect::OnText(wxCommandEvent & evt)
831{
832 int i = evt.GetId() - ID_Text;
833
834 NyqControl & ctrl = mControls[i];
835
836 if (wxDynamicCast(evt.GetEventObject(), wxWindow)->GetValidator()->TransferFromWindow())
837 {
838 if (ctrl.type == NYQ_CTRL_FLOAT || ctrl.type == NYQ_CTRL_INT)
839 {
840 int pos = (int)floor((ctrl.val - ctrl.low) /
841 (ctrl.high - ctrl.low) * ctrl.ticks + 0.5);
842
843 wxSlider *slider = (wxSlider *)mUIParent->FindWindow(ID_Slider + i);
844 slider->SetValue(pos);
845 }
846 }
847}
848
850//
851// NyquistOutputDialog
852//
854
855
856BEGIN_EVENT_TABLE(NyquistOutputDialog, wxDialogWrapper)
859
861 const TranslatableString &message)
862: wxDialogWrapper{ nullptr, -1, title, wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER }
863{
864 SetName();
865
866 ShuttleGui S{ this, eIsCreating };
867 {
868 S.SetBorder(10);
869
870 S.AddVariableText( XO("Debug Output: "), false, wxALIGN_LEFT | wxLEFT | wxTOP | wxRIGHT );
871
872 // TODO: use ShowInfoDialog() instead.
873 // Beware this dialog MUST work with screen readers.
874 S.Prop( 1 )
875 .Position(wxEXPAND | wxALL)
876 .MinSize( { 480, 250 } )
877 .Style(wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH)
878 .AddTextWindow( message.Translation() );
879
880 S.SetBorder( 5 );
881
882 S.StartHorizontalLay(wxALIGN_CENTRE | wxLEFT | wxBOTTOM | wxRIGHT, 0 );
883 {
884 /* i18n-hint: In most languages OK is to be translated as OK. It appears on a button.*/
885 S.Id(wxID_OK).AddButton( XXO("OK"), wxALIGN_CENTRE, true );
886 }
887 S.EndHorizontalLay();
888
889 }
890
891 SetAutoLayout(true);
892 GetSizer()->Fit(this);
893 GetSizer()->SetSizeHints(this);
894}
895
896// ============================================================================
897// NyquistOutputDialog implementation
898// ============================================================================
899
900void NyquistOutputDialog::OnOk(wxCommandEvent & /* event */)
901{
902 EndModal(wxID_OK);
903}
904
906 [](const PluginID& path) {
907 // Returned object must implement EffectUIServices for display of wxWidget
908 // UI to be possible.
909 return std::make_unique<NyquistEffect>(path);
910 }
911};
912
914 [](const WaveTrack* track) {
915 auto pView = WaveChannelView::FindFirst(track);
916 return pView ? pView->GetDisplays() :
917 std::vector<WaveChannelSubView::Type> {};
918 }
919};
920
922 [](const TranslatableString& title, const TranslatableString& message) {
923 NyquistOutputDialog dialog { title, message };
924 dialog.CentreOnParent();
925 dialog.ShowModal();
926 }
927};
928
930 [](wxString* pIn, wxString* pOut) { ExecFromMain(pIn, pOut); }
931};
932
934// LLL: STF figured out that yielding while the effect is being applied
935// produces an EXTREME slowdown. It appears that yielding is not
936// really necessary on Linux and Windows.
937//
938// However, on the Mac, the spinning cursor appears during longer
939// Nyquist processing and that may cause the user to think Audacity
940// has crashed or hung. In addition, yielding or not on the Mac
941// doesn't seem to make much of a difference in execution time.
942//
943// So, yielding on the Mac only...
944#if defined(__WXMAC__)
945 wxYieldIfNeeded();
946#endif
947} };
wxEVT_COMMAND_BUTTON_CLICKED
wxT("CloseDown"))
static RegisteredToolbarFactory factory
Toolkit-neutral facade for basic user interface services.
END_EVENT_TABLE()
wxString PluginID
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
std::function< DialogFactoryResults(wxWindow &parent, EffectBase &, EffectUIServices &, EffectSettingsAccess &) > EffectDialogFactory
Type of function that creates a dialog for an effect.
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
wxString FileExtension
File extension, not including any leading dot.
Definition: Identifier.h:224
#define safenew
Definition: MemoryX.h:10
static const auto title
const NumericConverterType & NumericConverterType_TIME()
static NyquistBase::ShowDebugOutputHook::Scope showDebugOutputHookScope
Definition: Nyquist.cpp:921
static NyquistBase::GetEffectHook::Scope getEffectHookScope
Definition: Nyquist.cpp:905
static const FileNames::FileType LispScripts
Definition: Nyquist.cpp:606
wxEVT_COMMAND_TEXT_UPDATED
Definition: Nyquist.cpp:73
static NyquistBase::YieldIfNeededHook::Scope yieldIfNeededHookScope
Definition: Nyquist.cpp:933
static const FileNames::FileType NyquistScripts
Definition: Nyquist.cpp:604
EVT_COMMAND_RANGE(ID_Slider, ID_Slider+99, wxEVT_COMMAND_SLIDER_UPDATED, NyquistEffect::OnSlider) EVT_COMMAND_RANGE(ID_Text
static NyquistBase::ExecFromMainHook::Scope execFromMainHookScope
Definition: Nyquist.cpp:929
@ ID_Editor
Definition: Nyquist.cpp:55
@ ID_Load
Definition: Nyquist.cpp:56
@ ID_FILE
Definition: Nyquist.cpp:63
@ ID_Choice
Definition: Nyquist.cpp:61
@ ID_Slider
Definition: Nyquist.cpp:59
@ ID_Save
Definition: Nyquist.cpp:57
static NyquistBase::GetDisplaysHook::Scope getDisplaysHookScope
Definition: Nyquist.cpp:913
ID_Text
Definition: Nyquist.cpp:72
NyquistEffect::OnText ID_Time
Definition: Nyquist.cpp:76
#define UNINITIALIZED_CONTROL
Definition: NyquistBase.h:26
@ NYQ_CTRL_STRING
Definition: NyquistBase.h:32
@ NYQ_CTRL_TEXT
Definition: NyquistBase.h:36
@ NYQ_CTRL_TIME
Definition: NyquistBase.h:37
@ NYQ_CTRL_INT_TEXT
Definition: NyquistBase.h:34
@ NYQ_CTRL_INT
Definition: NyquistBase.h:30
@ NYQ_CTRL_CHOICE
Definition: NyquistBase.h:33
@ NYQ_CTRL_FLOAT_TEXT
Definition: NyquistBase.h:35
@ NYQ_CTRL_FILE
Definition: NyquistBase.h:38
@ NYQ_CTRL_FLOAT
Definition: NyquistBase.h:31
#define NYQUIST_WORKER_ID
Definition: NyquistBase.h:25
int ExecFromMain(wxString *pIn, wxString *pOut)
Executes a command on the main (GUI) thread.
TranslatableStrings Msgids(const EnumValueSymbol strings[], size_t nStrings)
Convenience function often useful when adding choice controls.
@ eIsCreating
Definition: ShuttleGui.h:37
@ eDebugID
Definition: ShuttleGui.h:628
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:51
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the SettingsVis...
bool GetParameters(wxString &parms)
bool SetParameters(const wxString &parms)
Base class for many of the effects in Audacity.
Definition: EffectBase.h:33
double mProjectRate
Definition: EffectBase.h:119
static bool EnablePreview(wxWindow *parent, bool enable=true)
void SetBatchProcessing() override
Definition: Effect.cpp:300
virtual NumericFormatID GetSelectionFormat()
Definition: Effect.cpp:187
unsigned TestUIFlags(unsigned mask)
Definition: Effect.cpp:291
Performs effect computation.
Hold values to send to effect output meters.
static int DoMessageBox(const EffectPlugin &plugin, const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
virtual int ShowHostInterface(EffectBase &plugin, wxWindow &parent, const EffectDialogFactory &factory, std::shared_ptr< EffectInstance > &pInstance, EffectSettingsAccess &access, bool forceModal=false)
EffectSettings MakeSettings() const override
Definition: Effect.h:156
virtual wxString GetPath() const
virtual void GetPaths(wxArrayString &paths) const
virtual int ShowModal()
static FormatterContext SampleRateContext(double sampleRate)
typename GlobalVariable< GetEffectHook, const std::function< std::unique_ptr< NyquistBase > >, nullptr, Options... >::Scope Scope
void SetValue(double newValue)
A control on a NyquistDialog.
Definition: NyquistBase.h:42
wxString highStr
Definition: NyquistBase.h:58
double high
Definition: NyquistBase.h:61
wxString label
Definition: NyquistBase.h:53
wxString name
Definition: NyquistBase.h:52
std::vector< EnumValueSymbol > choices
Definition: NyquistBase.h:54
wxString valStr
Definition: NyquistBase.h:56
double low
Definition: NyquistBase.h:60
double val
Definition: NyquistBase.h:59
FileNames::FileTypes fileTypes
Definition: NyquistBase.h:55
wxString lowStr
Definition: NyquistBase.h:57
const bool mIsPrompt
Definition: NyquistBase.h:251
void SetCommand(const wxString &cmd)
static void resolveFilePath(wxString &path, FileExtension extension={})
bool validatePath(wxString path)
bool LoadSettings(const CommandParameters &parms, EffectSettings &settings) const override
Restore settings from keys and values.
wxString mInputCmd
Definition: NyquistBase.h:254
bool SaveSettings(const EffectSettings &settings, CommandParameters &parms) const override
Store settings as keys and values.
wxFileName mFileName
Name of the Nyquist script file this effect is loaded from.
Definition: NyquistBase.h:233
static double GetCtrlValue(const wxString &s)
wxString ToTimeFormat(double t)
bool mEnablePreview
Definition: NyquistBase.h:275
bool ParseCommand(const wxString &cmd)
std::vector< NyqControl > mControls
Definition: NyquistBase.h:287
wxWeakRef< wxWindow > mUIParent
Definition: Nyquist.h:65
wxTextCtrl * mCommandText
Definition: Nyquist.h:51
bool TransferDataFromPromptWindow()
Definition: Nyquist.cpp:254
bool TransferDataFromWindow(EffectSettings &settings) override
Definition: Nyquist.cpp:196
void BuildEffectWindow(ShuttleGui &S)
Definition: Nyquist.cpp:444
void OnLoad(wxCommandEvent &evt)
Definition: Nyquist.cpp:609
bool TransferDataFromEffectWindow()
Definition: Nyquist.cpp:272
void OnFileButton(wxCommandEvent &evt)
Definition: Nyquist.cpp:742
bool TransferDataToPromptWindow()
Definition: Nyquist.cpp:210
int ShowHostInterface(EffectBase &plugin, wxWindow &parent, const EffectDialogFactory &factory, std::shared_ptr< EffectInstance > &pInstance, EffectSettingsAccess &access, bool forceModal=false) override
bool TransferDataToEffectWindow()
Definition: Nyquist.cpp:217
std::unique_ptr< EffectEditor > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) override
Add controls to effect panel; always succeeds.
Definition: Nyquist.cpp:162
bool TransferDataToWindow(const EffectSettings &settings) override
Definition: Nyquist.cpp:174
void OnSlider(wxCommandEvent &evt)
Definition: Nyquist.cpp:674
void OnSave(wxCommandEvent &evt)
Definition: Nyquist.cpp:647
void BuildPromptWindow(ShuttleGui &S)
Definition: Nyquist.cpp:412
void OnTime(wxCommandEvent &evt)
Definition: Nyquist.cpp:711
void OnChoice(wxCommandEvent &evt)
Definition: Nyquist.cpp:706
void OnText(wxCommandEvent &evt)
Definition: Nyquist.cpp:830
Dialog used with NyquistBase.
Definition: Nyquist.h:69
void OnOk(wxCommandEvent &event)
Definition: Nyquist.cpp:900
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
std::shared_ptr< EffectInstance > MakeInstance() const override
Make an object maintaining short-term state of an Effect.
Holds a msgid for the translation catalog; may also bind format arguments.
static WaveChannelView * FindFirst(WaveTrack *pWt)
If pWt is not null, return a pointer to the view of the first channel.
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
void Yield()
Dispatch waiting events, including actions enqueued by CallAfter.
Definition: BasicUI.cpp:225
DynamicRangeProcessorSettings GetSettings(EffectSettingsAccess &access)
STL namespace.
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:175
Options & MenuEnabled(bool enable)
Options & AutoPos(bool enable)
Options & ReadOnly(bool enable)