Audacity 3.2.0
EffectUI.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 EffectUI.cpp
6
7 Leland Lucius
8
9 Audacity(R) is copyright (c) 1999-2008 Audacity Team.
10 License: GPL v2 or later. See License.txt.
11
12**********************************************************************/
13#include "EffectUI.h"
14#include "Effect.h"
16#include "EffectEditor.h"
17#include "EffectPreview.h"
18
19#include "AllThemeResources.h"
20#include "widgets/BasicMenu.h"
21#include "BasicUI.h"
22#include "CommandManager.h"
23#include "ConfigInterface.h"
24#include "EffectManager.h"
25#include "DoEffect.h"
26#include "PluginManager.h"
27#include "ProjectAudioIO.h"
28#include "ProjectHistory.h"
29#include "../ProjectWindowBase.h"
30#include "../ProjectWindows.h"
31#include "TrackFocus.h"
32#include "RealtimeEffectList.h"
34#include "RealtimeEffectState.h"
35#include "Theme.h"
36#include "Viewport.h"
38
40{
41 return PluginManager::GetID(&effect.GetDefinition());
42}
43
45//
46// EffectPanel
47//
49
50class EffectPanel final : public wxPanelWrapper
51{
52public:
53 EffectPanel(wxWindow *parent)
54 : wxPanelWrapper(parent)
55 {
56 // This fools NVDA into not saying "Panel" when the dialog gets focus
59
60 mAcceptsFocus = true;
61 }
62
63 virtual ~EffectPanel()
64 {
65 }
66
67 // ============================================================================
68 // wxWindow implementation
69 // ============================================================================
70
71 bool AcceptsFocus() const override
72 {
73 return mAcceptsFocus;
74 }
75
76 // So that wxPanel is not included in Tab traversal, when required - see wxWidgets bug 15581
77 bool AcceptsFocusFromKeyboard() const override
78 {
79 return mAcceptsFocus;
80 }
81
82 // ============================================================================
83 // EffectPanel implementation
84 // ============================================================================
85 void SetAccept(bool accept)
86 {
87 mAcceptsFocus = accept;
88 }
89
90private:
92};
93
95//
96// EffectUIHost
97//
99
100#include "../../images/Effect.h"
101#include "AudioIO.h"
102#include "../CommonCommandFlags.h"
103#include "../prefs/GUISettings.h" // for RTL_WORKAROUND
104#include "Project.h"
105#include "../ProjectAudioManager.h"
106#include "ShuttleGui.h"
107#include "ViewInfo.h"
108#include "CommandContext.h"
109#include "AudacityMessageBox.h"
110#include "HelpSystem.h"
111#include "../widgets/AButton.h"
112
113#include <wx/button.h>
114#include <wx/checkbox.h>
115#include <wx/dcclient.h>
116#include <wx/dcmemory.h>
117#include <wx/menu.h>
118#include <wx/settings.h>
119#include <wx/sizer.h>
120#include <wx/textctrl.h>
121
122static const int kDummyID = 20000;
123static const int kSaveAsID = 20001;
124static const int kImportID = 20002;
125static const int kExportID = 20003;
126static const int kDefaultsID = 20004;
127static const int kOptionsID = 20005;
128static const int kUserPresetsDummyID = 20006;
129static const int kDeletePresetDummyID = 20007;
130static const int kMenuID = 20100;
131static const int kEnableID = 20101;
133static const int kPlaybackID = 20105;
134static const int kCaptureID = 20106;
135static const int kUserPresetsID = 21000;
136static const int kDeletePresetID = 22000;
137static const int kFactoryPresetsID = 23000;
138
139BEGIN_EVENT_TABLE(EffectUIHost, wxDialogWrapper)
140EVT_INIT_DIALOG(EffectUIHost::OnInitDialog)
141EVT_ERASE_BACKGROUND(EffectUIHost::OnErase)
142EVT_PAINT(EffectUIHost::OnPaint)
143EVT_CLOSE(EffectUIHost::OnClose)
158EVT_IDLE(EffectUIHost::OnIdle)
159EVT_CHAR_HOOK(EffectUIHost::OnCharHook)
161
162namespace {
165
167public:
169 const std::shared_ptr<EffectSettingsAccess> &pSide = {});
170 const EffectSettings &Get() override;
172 std::unique_ptr<Message> pMessage) override;
173 void Set(std::unique_ptr<Message> pMessage) override;
174 void Flush() override;
175 bool IsSameAs(const EffectSettingsAccess &other) const override;
176private:
178 const std::shared_ptr<EffectSettingsAccess> mpMain;
179 const std::weak_ptr<EffectSettingsAccess> mwSide;
180};
181}
182
183EffectSettingsAccessTee::EffectSettingsAccessTee(
185 const std::shared_ptr<EffectSettingsAccess> &pSide
186) : mpMain{ main.shared_from_this() }
187 , mwSide{ pSide }
188{
189}
190
192 return mpMain->Get();
193}
194
196 std::unique_ptr<Message> pMessage)
197{
198 // Move copies of the given settings and message into the side
199 if (auto pSide = mwSide.lock())
200 pSide->Set(EffectSettings{ settings },
201 pMessage ? pMessage->Clone() : nullptr);
202 // Move the given settings and message through
203 mpMain->Set(std::move(settings), std::move(pMessage));
204}
205
206void EffectSettingsAccessTee::Set(std::unique_ptr<Message> pMessage)
207{
208 // Move copies of the given message into the side
209 if (auto pSide = mwSide.lock())
210 pSide->Set(pMessage ? pMessage->Clone() : nullptr);
211 // Move the given message through
212 mpMain->Set(std::move(pMessage));
213}
214
216{
217 mpMain->Flush();
218 if (auto pSide = mwSide.lock())
219 pSide->Flush();
220}
221
223 const EffectSettingsAccess &other) const
224{
225 return mpMain->IsSameAs(other);
226}
227
230 EffectUIServices &client, std::shared_ptr<EffectInstance> &pInstance,
231 EffectSettingsAccess &access,
232 const std::shared_ptr<RealtimeEffectState> &pPriorState)
233: wxDialogWrapper(parent, wxID_ANY, effect.GetDefinition().GetName(),
234 wxDefaultPosition, wxDefaultSize,
235 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMINIMIZE_BOX | wxMAXIMIZE_BOX)
236, mEffectUIHost{ effect }
237, mClient{ client }
238// Grab a pointer to the access object,
239// extending its lifetime while this remains:
240, mpGivenAccess{ access.shared_from_this() }
241, mpAccess{ mpGivenAccess }
242, mwState{ pPriorState }
243, mProject{ project }
244, mParent{ parent }
245, mSupportsRealtime{ mEffectUIHost.GetDefinition().SupportsRealtime() }
246, mHadPriorState{ (pPriorState != nullptr) }
247, mpInstance{ InitializeInstance() }
248, mpOutputs{ pPriorState ? pPriorState->GetOutputs() : nullptr }
249{
250 // Assign the out parameter
251 pInstance = mpInstance;
252#if defined(__WXMAC__)
253 MacMakeWindowFloating(GetHandle());
254#endif
255
256 SetName( effect.GetDefinition().GetName() );
257
258 // This style causes Validate() and TransferDataFromWindow() to visit
259 // sub-windows recursively, applying any wxValidators
260 SetExtraStyle(GetExtraStyle() | wxWS_EX_VALIDATE_RECURSIVELY);
261}
262
264{
265 if (mpEditor)
266 mpEditor->Disconnect();
267 DestroyChildren();
268 wxASSERT(mClosed);
269}
270
271// ============================================================================
272// wxWindow implementation
273// ============================================================================
274
276{
277 // Transfer-to takes const reference to settings
278 const auto pServices =
280 return (!pServices || pServices->TransferDataToWindow(mpAccess->Get())) &&
282 mpEditor->UpdateUI() &&
284 wxDialogWrapper::TransferDataToWindow();
285}
286
288{
290 if (!wxDialogWrapper::Validate())
291 return false;
292
294 if (!wxDialogWrapper::TransferDataFromWindow())
295 return false;
296
298 if (!mpEditor->ValidateUI())
299 return false;
300
301 // Transfer-from takes non-const reference to settings
302 bool result = true;
303 mpAccess->ModifySettings([&](EffectSettings &settings){
304 const auto pServices =
306 // Allow other transfers, and reassignment of settings
307 result = (!pServices || pServices->TransferDataFromWindow(settings));
308 if (result) {
309 auto &definition = mEffectUIHost.GetDefinition();
310 if (definition.GetType() == EffectTypeGenerate) {
311 const auto seconds = settings.extra.GetDuration();
312 // Updating of the last-used generator duration in the config
315 seconds);
316 }
317 }
318 return nullptr;
319 });
320 mpAccess->Flush();
321 return result;
322}
323
324// ============================================================================
325// wxDialog implementation
326// ============================================================================
327
329{
331 {
332 mApplyBtn->SetLabel(XXO("&Generate").Translation());
333 }
334
335 Layout();
336
337 return wxDialogWrapper::ShowModal();
338}
339
340// ============================================================================
341// EffectUIHost implementation
342// ============================================================================
343
344namespace {
346 const wxImage& ImageOn, const wxImage& ImageOff)
347{
348 auto pBtn = safenew AButton(parent, kEnableID,
349 wxDefaultPosition, wxDefaultSize, true);
350 pBtn->SetImages(ImageOff, ImageOff, ImageOn, ImageOn, ImageOff);
351 return pBtn;
352}
353 constexpr int InnerMargin = 3;
354}
355
357{
358 S.StartPanel();
359 {
360 S.SetBorder( InnerMargin );
361
362 S.StartHorizontalLay(wxEXPAND, 0);
363 {
365 {
366 mEnableBtn = MakeBitmapToggleButton(S.GetParent(),
367 theTheme.Image(bmpEffectOn), theTheme.Image(bmpEffectOff));
368 mEnableBtn->SetBackgroundColour(GetBackgroundColour());
369 S
370 .Position(wxALIGN_CENTER | wxTOP | wxBOTTOM)
371 .Name(XO("Power"))
372 .AddWindow(mEnableBtn);
373 }
374
375 mMenuBtn = S.Id( kMenuID )
376 .ToolTip(XO("Manage presets and options"))
377 .AddButton( XO("Presets && settings"), wxALIGN_CENTER | wxTOP | wxBOTTOM );
378
379 S.AddSpace(1, 0, 1);
380
381
382
384 {
385 mDebugBtn = S.Id(eDebugID)
386 .AddButton( XXO("Debu&g"),
387 wxALIGN_CENTER | wxTOP | wxBOTTOM );
388 }
389 }
390 S.EndHorizontalLay();
391 }
392 S.EndPanel();
393}
394
396{
397 mEnabled = mpAccess->Get().extra.GetActive();
398
399 // Build a "host" dialog, framing a panel that the client fills in.
400 // The frame includes buttons to preview, apply, load and save presets, etc.
401 EffectPanel *w {};
402 ShuttleGui S{ this, eIsCreating };
403 {
404 BuildTopBar(S);
405
406 // Make the panel for the client
407 Destroy_ptr<EffectPanel> uw{ safenew EffectPanel( S.GetParent() ) };
408 RTL_WORKAROUND(uw.get());
409
410 // Try to give the window a sensible default/minimum size
411 uw->SetMinSize(wxSize(wxMax(600, mParent->GetSize().GetWidth() * 2 / 3),
412 mParent->GetSize().GetHeight() / 2));
413
414 // Let the client add things to the panel
415 ShuttleGui S1{ uw.get(), eIsCreating };
418 if (!mpEditor)
419 return false;
420
421 mIsGUI = mpEditor->IsGraphicalUI();
423
424 S.StartHorizontalLay( wxEXPAND );
425 {
426 S.Prop( 1 )
427 .Position(wxEXPAND)
428 .AddWindow((w = uw.release()));
429 }
430 S.EndHorizontalLay();
431
433 {
434 S.StartPanel();
435 {
436 S.SetBorder( InnerMargin );
437 S.StartHorizontalLay(wxEXPAND, 0);
438 {
439 if (!mIsBatch)
440 {
443 {
444 S.Id(kPlayID)
445 .ToolTip(XO("Preview effect"))
446 .AddButton( XXO("&Preview"),
447 wxALIGN_CENTER | wxTOP | wxBOTTOM );
448 }
449 }
450
451 S.AddSpace(1, 1, 1);
452 S.Id(wxID_CANCEL)
453 .AddButton(XXO("&Cancel"));
454
455 mApplyBtn = S.Id(wxID_APPLY)
456 .AddButton( XXO("&Apply"));
457 mApplyBtn->SetDefault();
458 }
459 S.EndHorizontalLay();
460 }
461 S.EndPanel();
462 }
463 }
464
465 Layout();
466 Fit();
467 Center();
468
470
471 w->SetAccept(!mIsGUI);
472
473 if (!mIsGUI)
474 {
475 w->SetFocus();
476 }
477 else if (!IsOpenedFromEffectPanel())
478 {
479 mApplyBtn->SetFocus();
480 }
481
483
484 SetMinSize(GetSize());
485 return true;
486}
487
489{
490 return !IsModal();
491}
492
493
494void EffectUIHost::OnInitDialog(wxInitDialogEvent & evt)
495{
496 // Do default handling
497 wxDialogWrapper::OnInitDialog(evt);
498
499#if wxCHECK_VERSION(3, 0, 0)
500 //#warning "check to see if this still needed in wx3"
501#endif
502
503 // Pure hackage coming down the pike...
504 //
505 // I have no idea why, but if a wxTextCtrl is the first control in the
506 // panel, then its contents will not be automatically selected when the
507 // dialog is displayed.
508 //
509 // So, we do the selection manually.
510 wxTextCtrl *focused = wxDynamicCast(FindFocus(), wxTextCtrl);
511 if (focused)
512 {
513 focused->SelectAll();
514 }
515}
516
517void EffectUIHost::OnErase(wxEraseEvent & WXUNUSED(evt))
518{
519 // Ignore it
520}
521
522void EffectUIHost::OnPaint(wxPaintEvent & WXUNUSED(evt))
523{
524 wxPaintDC dc(this);
525
526 dc.Clear();
527}
528
529void EffectUIHost::OnClose(wxCloseEvent & WXUNUSED(evt))
530{
531 DoCancel();
533
534 if (mpEditor)
535 mpEditor->OnClose();
536
537 Hide();
538 Destroy();
539
540#if wxDEBUG_LEVEL
541 mClosed = true;
542#endif
543}
544
545void EffectUIHost::OnApply(wxCommandEvent & evt)
546{
547 auto &project = mProject;
548
549 // On wxGTK (wx2.8.12), the default action is still executed even if
550 // the button is disabled. This appears to affect all wxDialogs, not
551 // just our Effects dialogs. So, this is a only temporary workaround
552 // for legacy effects that disable the OK button. Hopefully this has
553 // been corrected in wx3.
554 if (!mApplyBtn->IsEnabled())
555 {
556 return;
557 }
558
559 // Honor the "select all if none" preference...a little hackish, but whatcha gonna do...
560 if (!mIsBatch &&
564 {
565 auto flags = AlwaysEnabledFlag;
566 bool allowed =
569 flags,
571 if (!allowed)
572 return;
573 }
574
575 if (!TransferDataFromWindow() ||
576 // This is the main place where there is a side-effect on the config
577 // file to remember the last-used settings of an effect, just before
578 // applying the effect destructively.
581 return;
582
583 if (IsModal())
584 {
585 mDismissed = true;
586
587 EndModal(evt.GetId());
588
589 Close();
590
591 return;
592 }
593
594 // Progress dialog no longer yields, so this "shouldn't" be necessary (yet to be proven
595 // for sure), but it is a nice visual cue that something is going on.
596 mApplyBtn->Disable();
597 auto cleanup = finally( [&] { mApplyBtn->Enable(); } );
598
599 CommandContext context( project );
600 // This is absolute hackage...but easy and I can't think of another way just now.
601 //
602 // It should callback to the EffectManager to kick off the processing
605}
606
608{
609 if (!mDismissed) {
610 if (!mHadPriorState) {
611 // For the destructive effect dialog only
612 // Restore effect state from last updated preferences
613 mpAccess->ModifySettings([&](EffectSettings &settings) {
614 // ignore failure
615 return mEffectUIHost.GetDefinition().LoadUserPreset(
616 CurrentSettingsGroup(), settings).value_or(nullptr);
617 });
618 }
619 if (IsModal())
620 EndModal(0);
621 else
622 Hide();
623
624 mDismissed = true;
625 }
626}
627
628void EffectUIHost::OnCancel(wxCommandEvent & WXUNUSED(evt))
629{
630 DoCancel();
631 Close();
632}
633
634void EffectUIHost::OnDebug(wxCommandEvent & evt)
635{
636 OnApply(evt);
637}
638
639namespace {
641{
642 static const auto specialVersion = XO("n/a");
643 auto result = definition.GetVersion();
644 if (result == specialVersion.MSGID())
645 result = specialVersion.Translation();
646 return result;
647}
648}
649
650void EffectUIHost::OnMenu(wxCommandEvent & WXUNUSED(evt))
651{
652 wxMenu menu;
653 menu.Bind(wxEVT_MENU, [](auto&){}, kUserPresetsDummyID);
654 menu.Bind(wxEVT_MENU, [](auto&){}, kDeletePresetDummyID);
656
657 if (mUserPresets.size() == 0)
658 {
659 menu.Append(kUserPresetsDummyID, _("User Presets"))->Enable(false);
660 }
661 else
662 {
663 auto sub = std::make_unique<wxMenu>();
664 for (size_t i = 0, cnt = mUserPresets.size(); i < cnt; i++)
665 {
666 sub->Append(kUserPresetsID + i, mUserPresets[i]);
667 }
668 menu.Append(0, _("User Presets"), sub.release());
669 }
670
671 menu.Append(kSaveAsID, _("Save Preset..."));
672
673 if (mUserPresets.size() == 0)
674 {
675 menu.Append(kDeletePresetDummyID, _("Delete Preset"))->Enable(false);
676 }
677 else
678 {
679 auto sub = std::make_unique<wxMenu>();
680 for (size_t i = 0, cnt = mUserPresets.size(); i < cnt; i++)
681 {
682 sub->Append(kDeletePresetID + i, mUserPresets[i]);
683 }
684 menu.Append(0, _("Delete Preset"), sub.release());
685 }
686
687 menu.AppendSeparator();
688
690
691 {
692 auto sub = std::make_unique<wxMenu>();
693 sub->Append(kDefaultsID, _("Defaults"));
694 if (factory.size() > 0)
695 {
696 sub->AppendSeparator();
697 for (size_t i = 0, cnt = factory.size(); i < cnt; i++)
698 {
699 auto label = factory[i];
700 if (label.empty())
701 {
702 label = _("None");
703 }
704
705 sub->Append(kFactoryPresetsID + i, label);
706 }
707 }
708 menu.Append(0, _("Factory Presets"), sub.release());
709 }
710
711 menu.AppendSeparator();
712 menu.Append(kImportID, _("Import..."))
714 menu.Append(kExportID, _("Export..."))
716 menu.AppendSeparator();
717 menu.Append(kOptionsID, _("Options..."))
718 ->Enable(mEffectUIHost.HasOptions());
719 menu.AppendSeparator();
720
721 {
722 auto sub = std::make_unique<wxMenu>();
723
724 auto &definition = mEffectUIHost.GetDefinition();
725 sub->Append(kDummyID, wxString::Format(_("Type: %s"),
726 ::wxGetTranslation( definition.GetFamily().Translation() )));
727 sub->Append(kDummyID, wxString::Format(_("Name: %s"), definition.GetName().Translation()));
728 sub->Append(kDummyID, wxString::Format(_("Version: %s"),
729 GetVersionForDisplay(definition)));
730 sub->Append(kDummyID, wxString::Format(_("Vendor: %s"), definition.GetVendor().Translation()));
731 sub->Append(kDummyID, wxString::Format(_("Description: %s"), definition.GetDescription().Translation()));
732 sub->Bind(wxEVT_MENU, [](auto&){}, kDummyID);
733
734 menu.Append(0, _("About"), sub.release());
735 }
736
737 wxWindow *btn = FindWindow(kMenuID);
738 wxRect r = btn->GetRect();
739 BasicMenu::Handle{ &menu }.Popup(
741 { r.GetLeft(), r.GetBottom() }
742 );
743}
744
745void EffectUIHost::OnEnable(wxCommandEvent & WXUNUSED(evt))
746{
748
749 auto mpState = mwState.lock();
750 if (mpState) {
751 mpState->SetActive(mEnabled);
753 }
754
756}
757
758void EffectUIHost::OnPlay(wxCommandEvent & WXUNUSED(evt))
759{
761 return;
762
763 auto updater = [this]{ TransferDataToWindow(); };
765 // After restoration of settings and effect state:
766 // In case any dialog control depends on mT1 or mDuration:
767 updater();
768
769 return;
770}
771
773{
774 if (evt.on) {
775 if (evt.pProject == &mProject)
776 mCapturing = true;
777 }
778 else {
779 mCapturing = false;
780 }
782}
783
784void EffectUIHost::OnUserPreset(wxCommandEvent & evt)
785{
786 int preset = evt.GetId() - kUserPresetsID;
787
788 mpAccess->ModifySettings([&](EffectSettings &settings){
789 // ignore failure
790 return mEffectUIHost.GetDefinition().LoadUserPreset(
791 UserPresetsGroup(mUserPresets[preset]), settings).value_or(nullptr);
792 });
794 return;
795}
796
797void EffectUIHost::OnFactoryPreset(wxCommandEvent & evt)
798{
799 mpAccess->ModifySettings([&](EffectSettings &settings){
801 return mEffectUIHost.GetDefinition().LoadFactoryPreset(
802 evt.GetId() - kFactoryPresetsID, settings).value_or(nullptr);
803 });
805 return;
806}
807
808void EffectUIHost::OnDeletePreset(wxCommandEvent & evt)
809{
810 auto preset = mUserPresets[evt.GetId() - kDeletePresetID];
811
812 int res = AudacityMessageBox(
813 XO("Are you sure you want to delete \"%s\"?").Format( preset ),
814 XO("Delete Preset"),
815 wxICON_QUESTION | wxYES_NO);
816 if (res == wxYES)
817 {
820 }
821
823
824 return;
825}
826
827void EffectUIHost::OnSaveAs(wxCommandEvent & WXUNUSED(evt))
828{
829 wxTextCtrl *text;
830 wxString name;
831 wxDialogWrapper dlg(this, wxID_ANY, XO("Save Preset"));
832
833 ShuttleGui S(&dlg, eIsCreating);
834
835 S.StartPanel();
836 {
837 S.StartVerticalLay(1);
838 {
839 S.StartHorizontalLay(wxALIGN_LEFT, 0);
840 {
841 text = S.AddTextBox(XXO("Preset name:"), name, 30);
842 }
843 S.EndHorizontalLay();
844 S.SetBorder(10);
845 S.AddStandardButtons();
846 }
847 S.EndVerticalLay();
848 }
849 S.EndPanel();
850
851 dlg.SetSize(dlg.GetSizer()->GetMinSize());
852 dlg.Center();
853 dlg.Fit();
854
855 while (true)
856 {
857 int rc = dlg.ShowModal();
858
859 if (rc != wxID_OK)
860 {
861 break;
862 }
863
864 name = text->GetValue();
865 if (name.empty())
866 {
868 this,
869 XO("You must specify a name"),
870 XO("Save Preset") );
871 md.Center();
872 md.ShowModal();
873 continue;
874 }
875
876 if ( make_iterator_range( mUserPresets ).contains( name ) )
877 {
879 this,
880 XO("Preset already exists.\n\nReplace?"),
881 XO("Save Preset"),
882 wxYES_NO | wxCANCEL | wxICON_EXCLAMATION );
883 md.Center();
884 int choice = md.ShowModal();
885 if (choice == wxID_CANCEL)
886 {
887 break;
888 }
889
890 if (choice == wxID_NO)
891 {
892 continue;
893 }
894 }
895
900
901 break;
902 }
903
904 return;
905}
906
907void EffectUIHost::OnImport(wxCommandEvent & WXUNUSED(evt))
908{
909 mpAccess->ModifySettings([&](EffectSettings &settings){
910 // ignore failure
911 return mClient.ImportPresets(mEffectUIHost, settings).value_or(nullptr);
912 });
915
916 return;
917}
918
919void EffectUIHost::OnExport(wxCommandEvent & WXUNUSED(evt))
920{
921 // may throw
922 // exceptions are handled in AudacityApp::OnExceptionInMainLoop
925
926 return;
927}
928
929void EffectUIHost::OnOptions(wxCommandEvent & WXUNUSED(evt))
930{
932
933 return;
934}
935
936void EffectUIHost::OnDefaults(wxCommandEvent & WXUNUSED(evt))
937{
938 mpAccess->ModifySettings([&](EffectSettings &settings){
939 // ignore failure
940 return mEffectUIHost.GetDefinition().LoadFactoryDefaults(settings)
941 .value_or(nullptr);
942 });
944 return;
945}
946
947void EffectUIHost::OnIdle(wxIdleEvent &evt)
948{
949 evt.Skip();
950 if (mpAccess)
951 mpAccess->Flush();
952}
953
954void EffectUIHost::OnCharHook(wxKeyEvent& evt)
955{
956 if (!IsEscapeKey(evt))
957 {
958 evt.Skip();
959 return;
960 }
961
963 Close();
964 else
965 {
966 wxCommandEvent cancelEvt { wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL };
967
968 OnCancel(cancelEvt);
969 }
970}
971
973{
974 return (mpTempProjectState == nullptr && mSupportsRealtime);
975}
976
977wxBitmap EffectUIHost::CreateBitmap(const char * const xpm[], bool up, bool pusher)
978{
979 wxMemoryDC dc;
980 wxBitmap pic(xpm);
981
982 wxBitmap mod(pic.GetWidth() + 6, pic.GetHeight() + 6, 24);
983 dc.SelectObject(mod);
984
985#if defined(__WXGTK__)
986 wxColour newColour = wxSystemSettings::GetColour(wxSYS_COLOUR_BACKGROUND);
987#else
988 wxColour newColour = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
989#endif
990
991 dc.SetBackground(wxBrush(newColour));
992 dc.Clear();
993
994 int offset = 3;
995 if (pusher)
996 {
997 if (!up)
998 {
999 offset += 1;
1000 }
1001 }
1002
1003 dc.DrawBitmap(pic, offset, offset, true);
1004
1005 dc.SelectObject(wxNullBitmap);
1006
1007 return mod;
1008}
1009
1011{
1012 if (mIsBatch)
1013 {
1014 return;
1015 }
1016
1018 {
1020 return;
1021 }
1022
1023 mApplyBtn->Enable(!mCapturing);
1024}
1025
1027{
1028 mUserPresets.clear();
1029
1032
1033 std::sort( mUserPresets.begin(), mUserPresets.end() );
1034
1035 return;
1036}
1037
1038std::shared_ptr<EffectInstance> EffectUIHost::InitializeInstance()
1039{
1040 // We are still constructing and the return initializes a const member
1041 std::shared_ptr<EffectInstance> result;
1042
1043 auto mpState = mwState.lock();
1044
1045 bool priorState = (mpState != nullptr);
1046 if (!priorState) {
1047 auto gAudioIO = AudioIO::Get();
1048 mCapturing = gAudioIO->IsStreamActive() && gAudioIO->GetNumCaptureChannels() > 0 && !gAudioIO->IsMonitoring();
1049 }
1050
1052 if (!priorState)
1053 mwState = mpState = mpTempProjectState =
1055 if (mpState) {
1056 // Find the right instance to connect to the dialog
1057 if (!result)
1058 result = mpState->GetInstance();
1059
1060 mpAccess2 = mpState->GetAccess();
1061 if (!(mpAccess2->IsSameAs(*mpAccess)))
1062 // Decorate the given access object
1063 mpAccess = std::make_shared<EffectSettingsAccessTee>(
1065
1066 mEffectStateSubscription = mpState->Subscribe([this](RealtimeEffectStateChange state) {
1069 });
1070 }
1071
1072 if (!priorState) {
1074 switch (event.type) {
1075 case AudioIOEvent::CAPTURE:
1076 OnCapture(event); break;
1077 default:
1078 break;
1079 }
1080 });
1081 }
1082
1083 mInitialized = true;
1084 }
1085 else
1086 result = EffectBase::FindInstance(mEffectUIHost).value_or(nullptr);
1087
1088 return result;
1089}
1090
1092{
1094
1096 if (!IsOpenedFromEffectPanel()) {
1099 mpTempProjectState.reset();
1100 /*
1101 ProjectHistory::Get(mProject).PushState(
1102 XO("Removed %s effect").Format(mpState->GetEffect()->GetName()),
1103 XO("Removed Effect"),
1104 UndoPush::NONE
1105 );
1106 */
1107 }
1108 mInitialized = false;
1109 }
1110}
1111
1113 EffectBase &host, EffectUIServices &client,
1114 EffectSettingsAccess &access)
1115{
1116 // Make sure there is an associated project, whose lifetime will
1117 // govern the lifetime of the dialog, even when the dialog is
1118 // non-modal, as for realtime effects
1119 auto project = FindProjectFromWindow(&parent);
1120 if ( !project )
1121 return {};
1122 std::shared_ptr<EffectInstance> pInstance;
1124 *project, host, client, pInstance, access } };
1125 if (!pInstance) {
1126 dlg->SetClosed();
1127 return {};
1128 }
1129 if (dlg->Initialize()) {
1130 auto pEditor = dlg->GetEditor();
1131 // release() is safe because parent will own it
1132 return { dlg.release(), pInstance, pEditor };
1133 }
1134 return {};
1135}
1136
1137#include "PluginManager.h"
1138#include "ProjectRate.h"
1139#include "../SelectUtilities.h"
1140#include "WaveTrack.h"
1141#include "CommandManager.h"
1142
1144BEGIN_EVENT_TABLE(EffectDialog, wxDialogWrapper)
1147
1148EffectDialog::EffectDialog(wxWindow * parent,
1149 const TranslatableString & title,
1150 int type,
1151 int flags,
1152 int additionalButtons)
1153: wxDialogWrapper(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, flags)
1154{
1155 mType = type;
1156 mAdditionalButtons = additionalButtons;
1157}
1158
1160{
1161 long buttons = eOkButton;
1163 {
1164 buttons |= eCancelButton;
1165 if (mType == EffectTypeProcess)
1166 {
1167 buttons |= ePreviewButton;
1168 }
1169 }
1170
1171 ShuttleGui S(this, eIsCreating);
1172
1173 S.SetBorder(5);
1174 S.StartVerticalLay(true);
1175 {
1177 S.AddStandardButtons(buttons|mAdditionalButtons);
1178 }
1179 S.EndVerticalLay();
1180
1181 Layout();
1182 Fit();
1183 SetMinSize(GetSize());
1184 Center();
1185}
1186
1191{
1192 return;
1193}
1194
1196{
1199
1200 return true;
1201}
1202
1204{
1207
1208 return true;
1209}
1210
1212{
1213 return true;
1214}
1215
1216void EffectDialog::OnPreview(wxCommandEvent & WXUNUSED(evt))
1217{
1218 return;
1219}
1220
1221void EffectDialog::OnOk(wxCommandEvent & WXUNUSED(evt))
1222{
1223 // On wxGTK (wx2.8.12), the default action is still executed even if
1224 // the button is disabled. This appears to affect all wxDialogs, not
1225 // just our Effects dialogs. So, this is a only temporary workaround
1226 // for legacy effects that disable the OK button. Hopefully this has
1227 // been corrected in wx3.
1228 if (FindWindow(wxID_OK)->IsEnabled() && Validate() && TransferDataFromWindow())
1229 {
1230 EndModal(wxID_OK);
1231 }
1232
1233 return;
1234}
1235
1237#include "RealtimeEffectState.h"
EVT_MENU(OnSetPlayRegionToSelectionID, AdornedRulerPanel::OnSetPlayRegionToSelection) EVT_COMMAND(OnTogglePinnedStateID
wxEVT_COMMAND_BUTTON_CLICKED
wxImage(22, 22)
int main(int argc, char *argv[])
EVT_MENU_RANGE(FileHistory::ID_RECENT_FIRST, FileHistory::ID_RECENT_LAST, AudacityApp::OnMRUFile) bool AudacityApp
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
static RegisteredToolbarFactory factory
Abstractions of menus and their items.
Toolkit-neutral facade for basic user interface services.
END_EVENT_TABLE()
wxString PluginID
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:34
const ReservedCommandFlag & TimeSelectedFlag()
const ReservedCommandFlag & WaveTracksSelectedFlag()
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
RegistryPath UserPresetsGroup(const RegistryPath &name)
Compute part of a registry path, given a name which may be empty.
const RegistryPath & CurrentSettingsGroup()
Component of a configuration key path, for last-used destructive settings.
@ EffectTypeAnalyze
@ EffectTypeGenerate
@ EffectTypeTool
@ EffectTypeProcess
void EffectPreview(EffectBase &effect, EffectSettingsAccess &access, std::function< void()> updateUI, bool dryOnly)
Calculate temporary tracks of limited length with effect applied and play.
static PluginID GetID(EffectPlugin &effect)
Definition: EffectUI.cpp:39
static const int kImportID
Definition: EffectUI.cpp:124
static const int kExportID
Definition: EffectUI.cpp:125
static const int kOptionsID
Definition: EffectUI.cpp:127
static const int kCaptureID
Definition: EffectUI.cpp:134
static RealtimeEffectState::EffectFactory::Scope scope
Inject a factory for realtime effects.
Definition: EffectUI.cpp:1239
static const int kDefaultsID
Definition: EffectUI.cpp:126
static const int kDummyID
Definition: EffectUI.cpp:122
static const int kPlaybackID
Definition: EffectUI.cpp:133
static const int kPlayID
Definition: EffectUI.cpp:132
static const int kSaveAsID
Definition: EffectUI.cpp:123
static const int kUserPresetsDummyID
Definition: EffectUI.cpp:128
static const int kFactoryPresetsID
Definition: EffectUI.cpp:137
static const int kEnableID
Definition: EffectUI.cpp:131
static const int kMenuID
Definition: EffectUI.cpp:130
static const int kDeletePresetDummyID
Definition: EffectUI.cpp:129
static const int kUserPresetsID
Definition: EffectUI.cpp:135
static const int kDeletePresetID
Definition: EffectUI.cpp:136
void MacMakeWindowFloating(NSView *handle)
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define RTL_WORKAROUND(pWnd)
Definition: GUISettings.h:20
#define _(s)
Definition: Internat.h:73
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: IteratorX.h:210
#define safenew
Definition: MemoryX.h:10
std::unique_ptr< T, Destroyer< T > > Destroy_ptr
a convenience for using Destroyer
Definition: MemoryX.h:164
static const auto title
an object holding per-project preferred sample rate
AudacityProject * FindProjectFromWindow(wxWindow *pWindow)
RealtimeEffectStateChange
ReverbSettings preset
Definition: ReverbBase.cpp:25
@ eIsSettingToDialog
Definition: ShuttleGui.h:39
@ eIsCreating
Definition: ShuttleGui.h:37
@ eIsGettingFromDialog
Definition: ShuttleGui.h:38
@ eOkButton
Definition: ShuttleGui.h:609
@ eCancelButton
Definition: ShuttleGui.h:610
@ ePreviewButton
Definition: ShuttleGui.h:614
@ eDebugID
Definition: ShuttleGui.h:628
wxString name
Definition: TagsEditor.cpp:166
TranslatableString label
Definition: TagsEditor.cpp:165
const auto project
THEME_API Theme theTheme
Definition: Theme.cpp:82
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:51
static CustomUpdaterValue updater
A wxButton with mouse-over behaviour.
Definition: AButton.h:104
void PushDown()
Definition: AButton.cpp:664
bool IsDown()
Definition: AButton.h:227
void PopUp()
Definition: AButton.cpp:672
Wrap wxMessageDialog so that caption IS translatable.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static AudioIO * Get()
Definition: AudioIO.cpp:126
void RemoveState(AudacityProject &project, ChannelGroup *pGroup, std::shared_ptr< RealtimeEffectState > pState)
Forwards to RealtimeEffectManager::RemoveState with proper init scope.
Definition: AudioIO.cpp:369
std::shared_ptr< RealtimeEffectState > AddState(AudacityProject &project, ChannelGroup *pGroup, const PluginID &id)
Forwards to RealtimeEffectManager::AddState with proper init scope.
Definition: AudioIO.cpp:347
void Popup(const BasicUI::WindowPlacement &window, const Point &pos={})
Display the menu at pos, invoke at most one action, then hide it.
Definition: BasicMenu.cpp:209
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
AudacityProject & project
static CommandManager & Get(AudacityProject &project)
bool ReportIfActionNotAllowed(const TranslatableString &Name, CommandFlag &flags, CommandFlag flagsRqd)
virtual wxString GetVersion() const =0
TranslatableString GetName() const
Base class for many of the effects in Audacity.
Definition: EffectBase.h:33
static std::optional< InstancePointer > FindInstance(EffectPlugin &plugin)
Definition: EffectBase.cpp:239
EffectDefinitionInterface is a ComponentInterface that adds some basic read-only information about ef...
virtual EffectType GetType() const =0
Type determines how it behaves.
virtual bool EnablesDebug() const
Whether the effect dialog should have a Debug button; default, always false.
bool Validate() override
Definition: EffectUI.cpp:1211
bool TransferDataToWindow() override
Definition: EffectUI.cpp:1195
bool TransferDataFromWindow() override
Definition: EffectUI.cpp:1203
virtual void OnPreview(wxCommandEvent &evt)
Definition: EffectUI.cpp:1216
virtual void OnOk(wxCommandEvent &evt)
Definition: EffectUI.cpp:1221
virtual void PopulateOrExchange(ShuttleGui &S)
Definition: EffectUI.cpp:1190
int mAdditionalButtons
Definition: EffectUI.h:197
static constexpr int kPlayID
Definition: EffectEditor.h:76
static const EffectInstanceFactory * GetInstanceFactory(const PluginID &ID)
virtual ~EffectPanel()
Definition: EffectUI.cpp:63
void SetAccept(bool accept)
Definition: EffectUI.cpp:85
bool AcceptsFocus() const override
Definition: EffectUI.cpp:71
bool AcceptsFocusFromKeyboard() const override
Definition: EffectUI.cpp:77
EffectPanel(wxWindow *parent)
Definition: EffectUI.cpp:53
bool mAcceptsFocus
Definition: EffectUI.cpp:91
Factory of instances of an effect.
Definition: EffectPlugin.h:36
virtual bool HasOptions() const =0
virtual bool CanExportPresets() const =0
Whether the effect supports export of presets to files, and importing too.
virtual const EffectSettingsManager & GetDefinition() const =0
virtual bool IsBatchProcessing() const =0
static const RegistryPath & DurationKey()
virtual RegistryPaths GetFactoryPresets() const =0
Report names of factory presets.
virtual bool SaveUserPreset(const RegistryPath &name, const EffectSettings &settings) const =0
Save settings in the configuration file as a user-named preset.
void OnEnable(wxCommandEvent &evt)
Definition: EffectUI.cpp:745
void OnFactoryPreset(wxCommandEvent &evt)
Definition: EffectUI.cpp:797
RegistryPaths mUserPresets
Definition: EffectUI.h:128
void OnDefaults(wxCommandEvent &evt)
Definition: EffectUI.cpp:936
Observer::Subscription mAudioIOSubscription
Definition: EffectUI.h:114
bool HandleCommandKeystrokes() override
Definition: EffectUI.cpp:488
void UpdateControls()
Definition: EffectUI.cpp:1010
bool mIsBatch
Definition: EffectUI.h:132
bool mCapturing
Definition: EffectUI.h:141
wxButton * mMenuBtn
Definition: EffectUI.h:135
virtual ~EffectUIHost()
Definition: EffectUI.cpp:263
void OnErase(wxEraseEvent &evt)
Definition: EffectUI.cpp:517
void CleanupRealtime()
Definition: EffectUI.cpp:1091
AudacityProject & mProject
Definition: EffectUI.h:116
std::shared_ptr< RealtimeEffectState > mpTempProjectState
Definition: EffectUI.h:126
bool mIsGUI
Definition: EffectUI.h:131
EffectBase & mEffectUIHost
Definition: EffectUI.h:118
bool TransferDataToWindow() override
Definition: EffectUI.cpp:275
EffectPlugin::EffectSettingsAccessPtr mpAccess
Definition: EffectUI.h:122
const EffectOutputs *const mpOutputs
Definition: EffectUI.h:155
void OnUserPreset(wxCommandEvent &evt)
Definition: EffectUI.cpp:784
void OnDeletePreset(wxCommandEvent &evt)
Definition: EffectUI.cpp:808
void OnIdle(wxIdleEvent &evt)
Definition: EffectUI.cpp:947
int ShowModal() override
Definition: EffectUI.cpp:328
Observer::Subscription mEffectStateSubscription
Definition: EffectUI.h:114
void DoCancel()
Definition: EffectUI.cpp:607
void OnCapture(AudioIOEvent)
Definition: EffectUI.cpp:772
EffectUIHost(wxWindow *parent, AudacityProject &project, EffectBase &effect, EffectUIServices &client, std::shared_ptr< EffectInstance > &pInstance, EffectSettingsAccess &access, const std::shared_ptr< RealtimeEffectState > &pPriorState={})
Definition: EffectUI.cpp:228
EffectUIServices & mClient
Definition: EffectUI.h:119
void OnClose(wxCloseEvent &evt)
Definition: EffectUI.cpp:529
void OnExport(wxCommandEvent &evt)
Definition: EffectUI.cpp:919
AButton * mEnableBtn
Definition: EffectUI.h:136
void BuildTopBar(ShuttleGui &S)
Definition: EffectUI.cpp:356
void OnSaveAs(wxCommandEvent &evt)
Definition: EffectUI.cpp:827
wxButton * mApplyBtn
Definition: EffectUI.h:134
const bool mHadPriorState
Definition: EffectUI.h:147
void OnOptions(wxCommandEvent &evt)
Definition: EffectUI.cpp:929
void LoadUserPresets()
Definition: EffectUI.cpp:1026
void OnInitDialog(wxInitDialogEvent &evt)
Definition: EffectUI.cpp:494
void OnMenu(wxCommandEvent &evt)
Definition: EffectUI.cpp:650
void OnDebug(wxCommandEvent &evt)
Definition: EffectUI.cpp:634
void OnCharHook(wxKeyEvent &evt)
Definition: EffectUI.cpp:954
bool mEnabled
Definition: EffectUI.h:139
EffectPlugin::EffectSettingsAccessPtr mpAccess2
Definition: EffectUI.h:123
void OnImport(wxCommandEvent &evt)
Definition: EffectUI.cpp:907
std::unique_ptr< EffectEditor > mpEditor
Definition: EffectUI.h:157
std::shared_ptr< EffectInstance > InitializeInstance()
Definition: EffectUI.cpp:1038
void OnApply(wxCommandEvent &evt)
Definition: EffectUI.cpp:545
const bool mSupportsRealtime
Definition: EffectUI.h:130
bool Initialize()
Definition: EffectUI.cpp:395
void OnPlay(wxCommandEvent &evt)
Definition: EffectUI.cpp:758
const std::shared_ptr< EffectInstance > mpInstance
Definition: EffectUI.h:154
void OnCancel(wxCommandEvent &evt)
Definition: EffectUI.cpp:628
bool mInitialized
Definition: EffectUI.h:129
std::weak_ptr< RealtimeEffectState > mwState
Definition: EffectUI.h:124
wxWindow *const mParent
Definition: EffectUI.h:117
wxButton * mDebugBtn
Definition: EffectUI.h:137
wxBitmap CreateBitmap(const char *const xpm[], bool up, bool pusher)
Definition: EffectUI.cpp:977
bool IsOpenedFromEffectPanel() const
Definition: EffectUI.cpp:972
bool TransferDataFromWindow() override
Definition: EffectUI.cpp:287
void OnPaint(wxPaintEvent &evt)
Definition: EffectUI.cpp:522
bool mDismissed
Definition: EffectUI.h:146
virtual void ShowOptions(const EffectPlugin &plugin) const =0
virtual std::unique_ptr< EffectEditor > PopulateUI(const EffectPlugin &plugin, ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) const =0
Adds controls to a panel that is given as the parent window of S
virtual OptionalMessage ImportPresets(const EffectPlugin &plugin, EffectSettings &settings) const =0
virtual void ExportPresets(const EffectPlugin &plugin, const EffectSettings &settings) const =0
Abstract base class used in importing a file.
typename GlobalVariable< EffectFactory, const std::function< const EffectInstanceFactory * >, nullptr, Options... >::Scope Scope
bool isPoint() const
Definition: ViewInfo.h:40
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
void Reset() noexcept
Breaks the connection (constant time)
Definition: Observer.cpp:101
static PluginID GetID(const PluginProvider *provider)
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
wxImage & Image(int iIndex)
Holds a msgid for the translation catalog; may also bind format arguments.
static const TranslatableString Inaudible
A special string value that will have no screen reader pronunciation.
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
void MarkUnsaved()
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
void Set(EffectSettings &&settings, std::unique_ptr< Message > pMessage) override
const std::weak_ptr< EffectSettingsAccess > mwSide
Definition: EffectUI.cpp:179
void Set(std::unique_ptr< Message > pMessage) override
Message-only overload of Set(). In future, this should be the only one.
const std::shared_ptr< EffectSettingsAccess > mpMain
Definition: EffectUI.cpp:178
void Flush() override
Make the last Set changes "persistent" in underlying storage.
Definition: EffectUI.cpp:215
bool IsSameAs(const EffectSettingsAccess &other) const override
Definition: EffectUI.cpp:222
void SetLabel(const TranslatableString &label)
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:202
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:383
bool DoEffect(const PluginID &ID, AudacityProject &project, unsigned flags)
Definition: DoEffect.cpp:30
AUDACITY_DLL_API DialogFactoryResults DialogFactory(wxWindow &parent, EffectBase &host, EffectUIServices &client, EffectSettingsAccess &access)
Definition: EffectUI.cpp:1112
bool RemoveConfigSubgroup(const EffectDefinitionInterface &ident, PluginSettings::ConfigurationType type, const RegistryPath &group)
bool GetConfigSubgroups(const EffectDefinitionInterface &ident, PluginSettings::ConfigurationType type, const RegistryPath &group, RegistryPaths &subgroups)
bool SetConfig(const EffectDefinitionInterface &ident, ConfigurationType type, const RegistryPath &group, const RegistryPath &key, const Value &value)
AButton * MakeBitmapToggleButton(wxWindow *parent, const wxImage &ImageOn, const wxImage &ImageOff)
Definition: EffectUI.cpp:345
wxString GetVersionForDisplay(const EffectDefinitionInterface &definition)
Definition: EffectUI.cpp:640
bool on
Definition: AudioIO.h:68
enum AudioIOEvent::Type type
AudacityProject * pProject
Definition: AudioIO.h:61
Externalized state of a plug-in.
Window placement information for wxWidgetsBasicUI can be constructed from a wxWindow pointer.