Audacity 3.2.0
RealtimeEffectPanel.cpp
Go to the documentation of this file.
1/*!********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file RealtimeEffectPanel.cpp
6
7 @author Vitaly Sverchinsky
8
9**********************************************************************/
10
11#include "RealtimeEffectPanel.h"
12
13#include <wx/app.h>
14#include <wx/sizer.h>
15#include <wx/splitter.h>
16#include <wx/statbmp.h>
17#include <wx/stattext.h>
18#include <wx/menu.h>
19#include <wx/wupdlock.h>
20#include <wx/hyperlink.h>
21
22#include <wx/dcbuffer.h>
23
24#include "HelpSystem.h"
25#include "Theme.h"
26#include "AllThemeResources.h"
27#include "AudioIO.h"
28#include "BasicUI.h"
29#include "Observer.h"
30#include "PluginManager.h"
31#include "Project.h"
32#include "ProjectHistory.h"
33#include "ProjectWindow.h"
34#include "ProjectWindows.h"
35#include "TrackFocus.h"
36#include "AColor.h"
37#include "WaveTrack.h"
38#include "effects/EffectUI.h"
40#include "RealtimeEffectList.h"
41#include "RealtimeEffectState.h"
43#include "UndoManager.h"
44#include "Prefs.h"
45#include "BasicUI.h"
47#include "ListNavigationPanel.h"
48#include "MovableControl.h"
49#include "menus/MenuHelper.h"
50#include "prefs/EffectsPrefs.h"
51
52#if wxUSE_ACCESSIBILITY
53#include "WindowAccessible.h"
54#endif
55
56namespace
57{
58 using namespace MenuRegistry;
59 class RealtimeEffectsMenuVisitor final : public Visitor<Traits> {
60 wxMenu& mMenu;
61 wxMenu* mMenuPtr { nullptr };
62 int mMenuItemIdCounter { wxID_HIGHEST };
63 std::vector<Identifier> mIndexedPluginList;
64 int mMenuLevelCounter { 0 };
65 public:
66 RealtimeEffectsMenuVisitor(wxMenu& menu) : Visitor<Traits>{ std::tuple{
67 [this](const MenuRegistry::MenuItem &menuItem, const auto&) {
68 //Don't create a group item for root
69 if (mMenuLevelCounter != 0)
70 {
71 auto submenu = std::make_unique<wxMenu>();
72 mMenuPtr->AppendSubMenu(submenu.get(), menuItem.GetTitle().Translation());
73 mMenuPtr = submenu.release();
74 }
75 ++mMenuLevelCounter;
76 },
77
78 [this](const MenuRegistry::CommandItem &commandItem, const auto&) {
79 mMenuPtr->Append(mMenuItemIdCounter, commandItem.label_in.Translation());
80 mIndexedPluginList.push_back(commandItem.name);
81 ++mMenuItemIdCounter;
82 },
83
84 [this](const MenuRegistry::MenuItem &, const auto&) {
85 --mMenuLevelCounter;
86 if (mMenuLevelCounter != 0)
87 {
88 assert(mMenuPtr->GetParent() != nullptr);
89 mMenuPtr = mMenuPtr->GetParent();
90 }
91 }},
92
93 [this]() {
94 mMenuPtr->AppendSeparator();
95 }}
96 , mMenu(menu), mMenuPtr(&mMenu)
97 {}
98
99 Identifier GetPluginID(int menuIndex) const
100 {
101 assert(menuIndex >= wxID_HIGHEST && menuIndex < (wxID_HIGHEST + mIndexedPluginList.size()));
102 return mIndexedPluginList[menuIndex - wxID_HIGHEST];
103 }
104 };
105
106 template <typename Visitor>
108 {
109 if (!track.IsLeader())
110 return;
111 auto& effects = RealtimeEffectList::Get(track);
112 effects.Visit(
113 [visitor](auto& effectState, bool)
114 {
115 auto& ui = RealtimeEffectStateUI::Get(effectState);
116 visitor(ui);
117 });
118 }
119
121 {
123 track, [&](auto& ui) { ui.UpdateTrackData(track); });
124 }
125
127 {
129 track,
130 [&](auto& ui)
131 {
132 if (ui.IsShown())
133 {
134 ui.Hide(&project);
135 ui.Show(project);
136 }
137 });
138 }
139 //fwd
140 class RealtimeEffectControl;
141
142 class DropHintLine : public wxWindow
143 {
144 public:
145 DropHintLine(wxWindow *parent,
146 wxWindowID id,
147 const wxPoint& pos = wxDefaultPosition,
148 const wxSize& size = wxDefaultSize)
149 : wxWindow(parent, id, pos, size, wxNO_BORDER, wxEmptyString)
150 {
151 wxWindow::SetBackgroundStyle(wxBG_STYLE_PAINT);
152 Bind(wxEVT_PAINT, &DropHintLine::OnPaint, this);
153 }
154
155 bool AcceptsFocus() const override { return false; }
156
157 private:
158 void OnPaint(wxPaintEvent&)
159 {
160 wxBufferedPaintDC dc(this);
161 const auto rect = wxRect(GetSize());
162
163 dc.SetPen(*wxTRANSPARENT_PEN);
164 dc.SetBrush(GetBackgroundColour());
165 dc.DrawRectangle(rect);
166 }
167 };
168
169 class HyperLinkCtrlWrapper : public ListNavigationEnabled<wxHyperlinkCtrl>
170 {
171 public:
172 HyperLinkCtrlWrapper(wxWindow *parent,
173 wxWindowID id,
174 const wxString& label,
175 const wxString& url,
176 const wxPoint& pos = wxDefaultPosition,
177 const wxSize& size = wxDefaultSize,
178 long style = wxHL_DEFAULT_STYLE,
179 const wxString& name = wxHyperlinkCtrlNameStr)
180 {
181 Create(parent, id, label, url, pos, size, style, name);
182 }
183
184 void Create(wxWindow *parent,
185 wxWindowID id,
186 const wxString& label,
187 const wxString& url,
188 const wxPoint& pos = wxDefaultPosition,
189 const wxSize& size = wxDefaultSize,
190 long style = wxHL_DEFAULT_STYLE,
191 const wxString& name = wxHyperlinkCtrlNameStr)
192 {
194 Bind(wxEVT_PAINT, &HyperLinkCtrlWrapper::OnPaint, this);
195 }
196
197 void OnPaint(wxPaintEvent& evt)
198 {
199 wxPaintDC dc(this);
200 dc.SetFont(GetFont());
201 dc.SetTextForeground(GetForegroundColour());
202 dc.SetTextBackground(GetBackgroundColour());
203
204 auto labelRect = GetLabelRect();
205
206 dc.DrawText(GetLabel(), labelRect.GetTopLeft());
207 if (HasFocus())
208 AColor::DrawFocus(dc, labelRect);
209 }
210 };
211
212#if wxUSE_ACCESSIBILITY
213 class RealtimeEffectControlAx : public wxAccessible
214 {
215 public:
216 RealtimeEffectControlAx(wxWindow* win = nullptr) : wxAccessible(win) { }
217
218 wxAccStatus GetName(int childId, wxString* name) override
219 {
220 if(childId != wxACC_SELF)
221 return wxACC_NOT_IMPLEMENTED;
222
223 if(auto movable = wxDynamicCast(GetWindow(), MovableControl))
224 //i18n-hint: argument - position of the effect in the effect stack
225 *name = wxString::Format(_("Effect %d"), movable->FindIndexInParent() + 1);
226 return wxACC_OK;
227 }
228
229 wxAccStatus GetChildCount(int* childCount) override
230 {
231 const auto window = GetWindow();
232 *childCount = window->GetChildren().size();
233 return wxACC_OK;
234 }
235
236 wxAccStatus GetChild(int childId, wxAccessible** child) override
237 {
238 if(childId == wxACC_SELF)
239 *child = this;
240 else
241 {
242 const auto window = GetWindow();
243 const auto& children = window->GetChildren();
244 const auto childIndex = childId - 1;
245 if(childIndex < children.size())
246 *child = children[childIndex]->GetAccessible();
247 else
248 *child = nullptr;
249 }
250 return wxACC_OK;
251 }
252
253 wxAccStatus GetRole(int childId, wxAccRole* role) override
254 {
255 if(childId != wxACC_SELF)
256 return wxACC_NOT_IMPLEMENTED;
257
258 *role = wxROLE_SYSTEM_PANE;
259 return wxACC_OK;
260 }
261
262 wxAccStatus GetState(int childId, long* state) override
263 {
264 if(childId != wxACC_SELF)
265 return wxACC_NOT_IMPLEMENTED;
266
267 const auto window = GetWindow();
268 if(!window->IsEnabled())
269 *state = wxACC_STATE_SYSTEM_UNAVAILABLE;
270 else
271 {
272 *state = wxACC_STATE_SYSTEM_FOCUSABLE;
273 if(window->HasFocus())
274 *state |= wxACC_STATE_SYSTEM_FOCUSED;
275 }
276 return wxACC_OK;
277 }
278 };
279#endif
280
282 {
283 public:
284 virtual std::optional<wxString> PickEffect(wxWindow* parent, const wxString& selectedEffectID) = 0;
285 };
286
287 //UI control that represents individual effect from the effect list
288 class RealtimeEffectControl : public ListNavigationEnabled<MovableControl>
289 {
290 wxWeakRef<AudacityProject> mProject;
291 std::shared_ptr<SampleTrack> mTrack;
292 std::shared_ptr<RealtimeEffectState> mEffectState;
293 std::shared_ptr<EffectSettingsAccess> mSettingsAccess;
294
295 RealtimeEffectPicker* mEffectPicker { nullptr };
296
297 ThemedAButtonWrapper<AButton>* mChangeButton{nullptr};
298 AButton* mEnableButton{nullptr};
300
302
303 public:
305
306 RealtimeEffectControl(wxWindow* parent,
307 RealtimeEffectPicker* effectPicker,
308 wxWindowID winid,
309 const wxPoint& pos = wxDefaultPosition,
310 const wxSize& size = wxDefaultSize)
311 {
312 Create(parent, effectPicker, winid, pos, size);
313 }
314
315 void Create(wxWindow* parent,
316 RealtimeEffectPicker* effectPicker,
317 wxWindowID winid,
318 const wxPoint& pos = wxDefaultPosition,
319 const wxSize& size = wxDefaultSize)
320 {
321 mEffectPicker = effectPicker;
322
323 //Prevents flickering and paint order issues
324 MovableControl::SetBackgroundStyle(wxBG_STYLE_PAINT);
325 MovableControl::Create(parent, winid, pos, size, wxNO_BORDER | wxWANTS_CHARS);
326
327 Bind(wxEVT_PAINT, &RealtimeEffectControl::OnPaint, this);
328 Bind(wxEVT_SET_FOCUS, &RealtimeEffectControl::OnFocusChange, this);
329 Bind(wxEVT_KILL_FOCUS, &RealtimeEffectControl::OnFocusChange, this);
330
331 auto sizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
332
333 //On/off button
334 auto enableButton = safenew ThemedAButtonWrapper<AButton>(this);
335 enableButton->SetTranslatableLabel(XO("Power"));
336 enableButton->SetImageIndices(0, bmpEffectOff, bmpEffectOff, bmpEffectOn, bmpEffectOn, bmpEffectOff);
337 enableButton->SetButtonToggles(true);
338 enableButton->SetBackgroundColorIndex(clrEffectListItemBackground);
339 mEnableButton = enableButton;
340
341 enableButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
342
343 mEffectState->SetActive(mEnableButton->IsDown());
344 if (mProject)
345 UndoManager::Get(*mProject).MarkUnsaved();
346 });
347
348 //Central button with effect name, show settings
349 const auto optionsButton = safenew ThemedAButtonWrapper<AButton>(this, wxID_ANY);
350 optionsButton->SetImageIndices(0,
351 bmpHButtonNormal,
352 bmpHButtonHover,
353 bmpHButtonDown,
354 bmpHButtonHover,
355 bmpHButtonDisabled);
356 optionsButton->SetBackgroundColorIndex(clrEffectListItemBackground);
357 optionsButton->SetForegroundColorIndex(clrTrackPanelText);
358 optionsButton->SetButtonType(AButton::TextButton);
359 optionsButton->Bind(wxEVT_BUTTON, &RealtimeEffectControl::OnOptionsClicked, this);
360
361 //Remove/replace effect
362 auto changeButton = safenew ThemedAButtonWrapper<AButton>(this);
363 changeButton->SetImageIndices(0, bmpMoreNormal, bmpMoreHover, bmpMoreDown, bmpMoreHover, bmpMoreDisabled);
364 changeButton->SetBackgroundColorIndex(clrEffectListItemBackground);
365 changeButton->SetTranslatableLabel(XO("Replace effect"));
366 changeButton->Bind(wxEVT_BUTTON, &RealtimeEffectControl::OnChangeButtonClicked, this);
367
368 auto dragArea = safenew wxStaticBitmap(this, wxID_ANY, theTheme.Bitmap(bmpDragArea));
369 dragArea->Disable();
370 sizer->Add(dragArea, 0, wxLEFT | wxCENTER, 5);
371 sizer->Add(enableButton, 0, wxLEFT | wxCENTER, 5);
372 sizer->Add(optionsButton, 1, wxLEFT | wxCENTER, 5);
373 sizer->Add(changeButton, 0, wxLEFT | wxRIGHT | wxCENTER, 5);
374 mChangeButton = changeButton;
375 mOptionsButton = optionsButton;
376
377 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
378 vSizer->Add(sizer.release(), 0, wxUP | wxDOWN | wxEXPAND, 10);
379
380 SetSizer(vSizer.release());
381
382#if wxUSE_ACCESSIBILITY
383 SetAccessible(safenew RealtimeEffectControlAx(this));
384#endif
385 }
386
387 static const PluginDescriptor *GetPlugin(const PluginID &ID) {
388 auto desc = PluginManager::Get().GetPlugin(ID);
389 return desc;
390 }
391
394 {
395 const auto &ID = mEffectState->GetID();
396 const auto desc = GetPlugin(ID);
397 return desc
398 ? desc->GetSymbol().Msgid()
399 : XO("%s (missing)")
400 .Format(PluginManager::GetEffectNameFromID(ID).GET());
401 }
402
404 const std::shared_ptr<SampleTrack>& track,
405 const std::shared_ptr<RealtimeEffectState> &pState)
406 {
407 mProject = &project;
408 mTrack = track;
409 mEffectState = pState;
410
411 mSubscription = mEffectState->Subscribe([this](RealtimeEffectStateChange state) {
413 ? mEnableButton->PushDown()
414 : mEnableButton->PopUp();
415
416 if (mProject)
417 ProjectHistory::Get(*mProject).ModifyState(false);
418 });
419
421 if (pState) {
422 label = GetEffectName();
423 mSettingsAccess = pState->GetAccess();
424 }
425 else
426 mSettingsAccess.reset();
427 if (mEnableButton)
428 mSettingsAccess && mSettingsAccess->Get().extra.GetActive()
429 ? mEnableButton->PushDown()
430 : mEnableButton->PopUp();
431 if (mOptionsButton)
432 {
433 mOptionsButton->SetTranslatableLabel(label);
434 mOptionsButton->SetEnabled(pState && GetPlugin(pState->GetID()));
435 }
436 }
437
439 {
440 if(mProject == nullptr || mEffectState == nullptr)
441 return;
442
443 auto& ui = RealtimeEffectStateUI::Get(*mEffectState);
444 // Don't need autosave for the effect that is being removed
445 ui.Hide();
446
447 auto effectName = GetEffectName();
448 //After AudioIO::RemoveState call this will be destroyed
449 auto project = mProject.get();
450 auto trackName = mTrack->GetName();
451
452 AudioIO::Get()->RemoveState(*project, &*mTrack, mEffectState);
458 XO("Removed %s from %s").Format(effectName, trackName),
461 XO("Remove %s").Format(effectName)
462 );
463 }
464
465 void OnOptionsClicked(wxCommandEvent& event)
466 {
467 if(mProject == nullptr || mEffectState == nullptr)
468 return;//not initialized
469
470 const auto ID = mEffectState->GetID();
471 const auto effectPlugin = EffectManager::Get().GetEffect(ID);
472
473 if(effectPlugin == nullptr)
474 {
476 return;
477 }
478
479 auto& effectStateUI = RealtimeEffectStateUI::Get(*mEffectState);
480
481 effectStateUI.UpdateTrackData(*mTrack);
482 effectStateUI.Toggle( *mProject );
483 }
484
485 void OnChangeButtonClicked(wxCommandEvent& event)
486 {
487 if(!mTrack || mProject == nullptr)
488 return;
489 if(mEffectState == nullptr)
490 return;//not initialized
491
492 const auto effectID = mEffectPicker->PickEffect(mChangeButton, mEffectState->GetID());
493 if(!effectID)
494 return;//nothing
495
496 if(effectID->empty())
497 {
498 RemoveFromList();
499 return;
500 }
501
502 auto &em = RealtimeEffectManager::Get(*mProject);
503 auto oIndex = em.FindState(&*mTrack, mEffectState);
504 if (!oIndex)
505 return;
506
507 auto oldName = GetEffectName();
508 auto &project = *mProject;
509 auto trackName = mTrack->GetName();
510 if (auto state = AudioIO::Get()
511 ->ReplaceState(project, &*mTrack, *oIndex, *effectID)
512 ){
513 // Message subscription took care of updating the button text
514 // and destroyed `this`!
515 auto effect = state->GetEffect();
516 assert(effect); // postcondition of ReplaceState
518 /*i18n-hint: undo history,
519 first and second parameters - realtime effect names
520 */
521 XO("Replaced %s with %s")
522 .Format(oldName, effect->GetName()),
525 XO("Replace %s").Format(oldName));
526 }
527 }
528
529 void OnPaint(wxPaintEvent&)
530 {
531 wxBufferedPaintDC dc(this);
532 const auto rect = wxRect(GetSize());
533
534 dc.SetPen(*wxTRANSPARENT_PEN);
535 dc.SetBrush(GetBackgroundColour());
536 dc.DrawRectangle(rect);
537
538 dc.SetPen(theTheme.Colour(clrEffectListItemBorder));
539 dc.SetBrush(theTheme.Colour(clrEffectListItemBorder));
540 dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight());
541
542 if(HasFocus())
543 AColor::DrawFocus(dc, GetClientRect().Deflate(3, 3));
544 }
545
546 void OnFocusChange(wxFocusEvent& evt)
547 {
548 Refresh(false);
549 evt.Skip();
550 }
551 };
552
553 static wxString GetSafeVendor(const PluginDescriptor& descriptor)
554 {
555 if (descriptor.GetVendor().empty())
556 return XO("Unknown").Translation();
557
558 return descriptor.GetVendor();
559 }
560}
561
563 : public wxScrolledWindow
564 , public RealtimeEffectPicker
565 , public PrefsListener
566{
567 wxWeakRef<AudacityProject> mProject;
568 std::shared_ptr<SampleTrack> mTrack;
570 wxStaticText* mAddEffectHint{nullptr};
571 wxWindow* mAddEffectTutorialLink{nullptr};
572 wxWindow* mEffectListContainer{nullptr};
573
574 std::unique_ptr<MenuRegistry::MenuItem> mEffectMenuRoot;
575
578
579public:
580 RealtimeEffectListWindow(wxWindow *parent,
581 wxWindowID winid = wxID_ANY,
582 const wxPoint& pos = wxDefaultPosition,
583 const wxSize& size = wxDefaultSize,
584 long style = wxScrolledWindowStyle,
585 const wxString& name = wxPanelNameStr)
586 : wxScrolledWindow(parent, winid, pos, size, style, name)
587 {
588 Bind(wxEVT_SIZE, &RealtimeEffectListWindow::OnSizeChanged, this);
589#ifdef __WXMSW__
590 //Fixes flickering on redraw
591 wxScrolledWindow::SetDoubleBuffered(true);
592#endif
593 auto rootSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
594
595 auto effectListContainer = safenew ThemedWindowWrapper<wxPanel>(this, wxID_ANY);
596 effectListContainer->SetBackgroundColorIndex(clrMedium);
597 effectListContainer->SetSizer(safenew wxBoxSizer(wxVERTICAL));
598 effectListContainer->SetDoubleBuffered(true);
599 effectListContainer->Hide();
600 mEffectListContainer = effectListContainer;
601
602 auto addEffect = safenew ThemedAButtonWrapper<AButton>(this, wxID_ANY);
603 addEffect->SetImageIndices(0,
604 bmpHButtonNormal,
605 bmpHButtonHover,
606 bmpHButtonDown,
607 bmpHButtonHover,
608 bmpHButtonDisabled);
609 addEffect->SetTranslatableLabel(XO("Add effect"));
610 addEffect->SetButtonType(AButton::TextButton);
611 addEffect->SetBackgroundColorIndex(clrMedium);
612 addEffect->SetForegroundColorIndex(clrTrackPanelText);
613 addEffect->Bind(wxEVT_BUTTON, &RealtimeEffectListWindow::OnAddEffectClicked, this);
614 mAddEffect = addEffect;
615
616 auto addEffectHint = safenew ThemedWindowWrapper<wxStaticText>(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
617 //Workaround: text is set in the OnSizeChange
618 addEffectHint->SetForegroundColorIndex(clrTrackPanelText);
619 mAddEffectHint = addEffectHint;
620
621 auto addEffectTutorialLink = safenew ThemedWindowWrapper<wxHyperlinkCtrl>(
622 this, wxID_ANY, _("Watch video"),
623 "https://www.audacityteam.org/realtime-video", wxDefaultPosition,
624 wxDefaultSize, wxHL_ALIGN_LEFT | wxHL_CONTEXTMENU);
625
626 //i18n-hint: Hyperlink to the effects stack panel tutorial video
627 addEffectTutorialLink->SetTranslatableLabel(XO("Watch video"));
628#if wxUSE_ACCESSIBILITY
629 safenew WindowAccessible(addEffectTutorialLink);
630#endif
631
632 addEffectTutorialLink->Bind(
633 wxEVT_HYPERLINK, [](wxHyperlinkEvent& event)
634 { BasicUI::OpenInDefaultBrowser(event.GetURL()); });
635
636 mAddEffectTutorialLink = addEffectTutorialLink;
637
638 //indicates the insertion position of the item
639 auto dropHintLine = safenew ThemedWindowWrapper<DropHintLine>(effectListContainer, wxID_ANY);
640 dropHintLine->SetBackgroundColorIndex(clrDropHintHighlight);
641 dropHintLine->Hide();
642
643 rootSizer->Add(mEffectListContainer, 0, wxEXPAND | wxBOTTOM, 10);
644 rootSizer->Add(addEffect, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 20);
645 rootSizer->Add(addEffectHint, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 20);
646 rootSizer->Add(addEffectTutorialLink, 0, wxLEFT | wxRIGHT | wxEXPAND, 20);
647
648 SetSizer(rootSizer.release());
649 SetMinSize({});
650
651 Bind(EVT_MOVABLE_CONTROL_DRAG_STARTED, [dropHintLine](const MovableControlEvent& event)
652 {
653 if(auto window = dynamic_cast<wxWindow*>(event.GetEventObject()))
654 window->Raise();
655 });
656 Bind(EVT_MOVABLE_CONTROL_DRAG_POSITION, [this, dropHintLine](const MovableControlEvent& event)
657 {
658 constexpr auto DropHintLineHeight { 3 };//px
659
660 auto sizer = mEffectListContainer->GetSizer();
661 assert(sizer != nullptr);
662
663 if(event.GetSourceIndex() == event.GetTargetIndex())
664 {
665 //do not display hint line if position didn't change
666 dropHintLine->Hide();
667 return;
668 }
669
670 if(!dropHintLine->IsShown())
671 {
672 dropHintLine->Show();
673 dropHintLine->Raise();
674 if(auto window = dynamic_cast<wxWindow*>(event.GetEventObject()))
675 window->Raise();
676 }
677
678 auto item = sizer->GetItem(event.GetTargetIndex());
679 dropHintLine->SetSize(item->GetSize().x, DropHintLineHeight);
680
681 if(event.GetTargetIndex() > event.GetSourceIndex())
682 dropHintLine->SetPosition(item->GetRect().GetBottomLeft() - wxPoint(0, DropHintLineHeight));
683 else
684 dropHintLine->SetPosition(item->GetRect().GetTopLeft());
685 });
686 Bind(EVT_MOVABLE_CONTROL_DRAG_FINISHED, [this, dropHintLine](const MovableControlEvent& event)
687 {
688 dropHintLine->Hide();
689
690 if(mProject == nullptr)
691 return;
692
693 auto& effectList = RealtimeEffectList::Get(*mTrack);
694 const auto from = event.GetSourceIndex();
695 const auto to = event.GetTargetIndex();
696 if(from != to)
697 {
698 auto effectName =
699 effectList.GetStateAt(from)->GetEffect()->GetName();
700 bool up = (to < from);
701 effectList.MoveEffect(from, to);
703 (up
708 ? XO("Moved %s up in %s")
713 : XO("Moved %s down in %s"))
714 .Format(effectName, mTrack->GetName()),
715 XO("Change effect order"), UndoPush::CONSOLIDATE);
716 }
717 else
718 {
719 wxWindowUpdateLocker freeze(this);
720 Layout();
721 }
722 });
723 SetScrollRate(0, 20);
724
727 {
729 });
731 }
732
733 void UpdatePrefs() override
734 {
736 }
737
738 std::optional<wxString> PickEffect(wxWindow* parent, const wxString& selectedEffectID) override
739 {
740 if (mProject == nullptr)
741 return {};
742
743 wxMenu menu;
744 if(!selectedEffectID.empty())
745 {
746 //no need to handle language change since menu creates its own event loop
747 menu.Append(wxID_REMOVE, _("No Effect"));
748 menu.AppendSeparator();
749 }
750
751 RealtimeEffectsMenuVisitor visitor { menu };
752
754
755 int commandId = wxID_NONE;
756
757 menu.AppendSeparator();
758 menu.Append(wxID_MORE, _("Get more effects..."));
759
760 menu.Bind(wxEVT_MENU, [&](wxCommandEvent evt) { commandId = evt.GetId(); });
761
762 if(parent->PopupMenu(&menu, parent->GetClientRect().GetLeftBottom()) && commandId != wxID_NONE)
763 {
764 if(commandId == wxID_REMOVE)
765 return wxString {};
766 else if(commandId == wxID_MORE)
767 OpenInDefaultBrowser("https://plugins.audacityteam.org/");
768 else
769 return visitor.GetPluginID(commandId).GET();
770 }
771
772 return {};
773 }
774
776 {
777 using namespace MenuRegistry;
778 auto root = Menu("", TranslatableString{});
779
780 static auto realtimeEffectPredicate = [](const PluginDescriptor& desc)
781 {
782 return desc.IsEffectRealtime();
783 };
784
785 const auto groupby = RealtimeEffectsGroupBy.Read();
786
787 auto analyzeSection = Section("", Menu("", XO("Analyze")));
788 auto submenu =
789 static_cast<MenuItem*>(analyzeSection->begin()->get());
791 *submenu,
793 {}, groupby, nullptr,
794 realtimeEffectPredicate
795 );
796
797 if(!submenu->empty())
798 {
799 root->push_back(move(analyzeSection));
800 }
801
803 *root,
805 {}, groupby, nullptr,
806 realtimeEffectPredicate
807 );
808
809 mEffectMenuRoot.swap(root);
810 }
811
812 void OnSizeChanged(wxSizeEvent& event)
813 {
814 if(auto sizerItem = GetSizer()->GetItem(mAddEffectHint))
815 {
816 //We need to wrap the text whenever panel width changes and adjust widget height
817 //so that text is fully visible, but there is no height-for-width layout algorithm
818 //in wxWidgets yet, so for now we just do it manually
819
820 //Restore original text, because 'Wrap' will replace it with wrapped one
821 mAddEffectHint->SetLabel(_("Realtime effects are non-destructive and can be changed at any time."));
822 mAddEffectHint->Wrap(GetClientSize().x - sizerItem->GetBorder() * 2);
823 mAddEffectHint->InvalidateBestSize();
824 }
825 event.Skip();
826 }
827
829 {
830 auto sizer = mEffectListContainer->GetSizer();
831 const auto insertItem = [this, &msg](){
832 auto& effects = RealtimeEffectList::Get(*mTrack);
833 InsertEffectRow(msg.srcIndex, effects.GetStateAt(msg.srcIndex));
834 mAddEffectHint->Hide();
836 };
837 const auto removeItem = [&](){
839 // Don't need to auto-save changed settings of effect that is deleted
840 // Undo history push will do it anyway
841 ui.Hide();
842
843 auto window = sizer->GetItem(msg.srcIndex)->GetWindow();
844 sizer->Remove(msg.srcIndex);
845 wxTheApp->CallAfter([ref = wxWeakRef { window }] {
846 if(ref) ref->Destroy();
847 });
848
849 if(sizer->IsEmpty())
850 {
851 if(mEffectListContainer->IsDescendant(FindFocus()))
852 mAddEffect->SetFocus();
853
854 mEffectListContainer->Hide();
855 mAddEffectHint->Show();
857 }
858 };
859
860 wxWindowUpdateLocker freeze(this);
862 {
863 const auto sizer = mEffectListContainer->GetSizer();
864
865 const auto movedItem = sizer->GetItem(msg.srcIndex);
866
867 const auto proportion = movedItem->GetProportion();
868 const auto flag = movedItem->GetFlag();
869 const auto border = movedItem->GetBorder();
870 const auto window = movedItem->GetWindow();
871
872 if(msg.srcIndex < msg.dstIndex)
873 window->MoveAfterInTabOrder(sizer->GetItem(msg.dstIndex)->GetWindow());
874 else
875 window->MoveBeforeInTabOrder(sizer->GetItem(msg.dstIndex)->GetWindow());
876
877 sizer->Remove(msg.srcIndex);
878 sizer->Insert(msg.dstIndex, window, proportion, flag, border);
879 }
881 {
882 insertItem();
883 }
885 {
886 removeItem();
887 }
889 {
890 insertItem();
891 }
893 {
894 removeItem();
895 }
896 SendSizeEventToParent();
897 }
898
900 {
902
903 mTrack.reset();
904 mProject = nullptr;
906 }
907
909 const std::shared_ptr<SampleTrack>& track)
910 {
911 if (mTrack == track)
912 return;
913
915
916 mTrack = track;
917 mProject = &project;
919
920 if (track)
921 {
922 auto& effects = RealtimeEffectList::Get(*mTrack);
923 mEffectListItemMovedSubscription = effects.Subscribe(
925
927 }
928 }
929
930 void EnableEffects(bool enable)
931 {
932 if (mTrack)
933 RealtimeEffectList::Get(*mTrack).SetActive(enable);
934 }
935
937 {
938 wxWindowUpdateLocker freeze(this);
939
940 const auto hadFocus = mEffectListContainer->IsDescendant(FindFocus());
941 //delete items that were added to the sizer
942 mEffectListContainer->Hide();
943 mEffectListContainer->GetSizer()->Clear(true);
944
945
946 if(!mTrack || RealtimeEffectList::Get(*mTrack).GetStatesCount() == 0)
947 mEffectListContainer->Hide();
948
949 auto isEmpty{true};
950 if(mTrack)
951 {
952 auto& effects = RealtimeEffectList::Get(*mTrack);
953 isEmpty = effects.GetStatesCount() == 0;
954 for(size_t i = 0, count = effects.GetStatesCount(); i < count; ++i)
955 InsertEffectRow(i, effects.GetStateAt(i));
956 }
958 //Workaround for GTK: Underlying GTK widget does not update
959 //its size when wxWindow size is set to zero
960 mEffectListContainer->Show(!isEmpty);
961 mAddEffectHint->Show(isEmpty);
962 mAddEffectTutorialLink->Show(isEmpty);
963
964 SendSizeEventToParent();
965 }
966
967 void OnAddEffectClicked(const wxCommandEvent& event)
968 {
969 if(!mTrack || mProject == nullptr)
970 return;
971
972 const auto effectID = PickEffect(dynamic_cast<wxWindow*>(event.GetEventObject()), {});
973
974 if(!effectID || effectID->empty())
975 return;
976
977 auto plug = PluginManager::Get().GetPlugin(*effectID);
978 if(!plug)
979 return;
980
983 XO("This plugin could not be loaded.\nIt may have been deleted."),
985 .Caption(XO("Plugin Error")));
986
987 return;
988 }
989
990 if(auto state = AudioIO::Get()->AddState(*mProject, &*mTrack, *effectID))
991 {
992 auto effect = state->GetEffect();
993 assert(effect); // postcondition of AddState
994 const auto effectName = effect->GetName();
1000 XO("Added %s to %s").Format(effectName, mTrack->GetName()),
1001 //i18n-hint: undo history record
1002 XO("Add %s").Format(effectName));
1003 }
1004 }
1005
1006 void InsertEffectRow(size_t index,
1007 const std::shared_ptr<RealtimeEffectState> &pState)
1008 {
1009 if(mProject == nullptr)
1010 return;
1011
1012 // See comment in ReloadEffectsList
1013 if(!mEffectListContainer->IsShown())
1014 mEffectListContainer->Show();
1015
1017 row->SetBackgroundColorIndex(clrEffectListItemBackground);
1018 row->SetEffect(*mProject, mTrack, pState);
1019 mEffectListContainer->GetSizer()->Insert(index, row, 0, wxEXPAND);
1020 }
1021};
1022
1023
1025{
1027
1029 : mProject { project }
1030 {}
1031
1032 void UpdatePrefs() override
1033 {
1034 auto& trackList = TrackList::Get(mProject);
1035 for (auto waveTrack : trackList.Any<WaveTrack>())
1037 }
1038};
1039
1040namespace {
1041AttachedWindows::RegisteredFactory sKey{
1042[](AudacityProject &project) -> wxWeakRef<wxWindow> {
1043 constexpr auto EffectsPanelMinWidth { 255 };
1044
1045 const auto pProjectWindow = &ProjectWindow::Get(project);
1047 project, pProjectWindow->GetContainerWindow(), wxID_ANY);
1048 effectsPanel->SetMinSize({EffectsPanelMinWidth, -1});
1049 effectsPanel->SetName(_("Realtime effects"));
1050 effectsPanel->SetBackgroundColorIndex(clrMedium);
1051 effectsPanel->Hide();//initially hidden
1052 return effectsPanel;
1053}
1054};
1055}
1056
1058{
1060}
1061
1062const RealtimeEffectPanel &
1064{
1065 return Get(const_cast<AudacityProject &>(project));
1066}
1067
1069 AudacityProject& project, wxWindow* parent, wxWindowID id, const wxPoint& pos,
1070 const wxSize& size,
1071 long style, const wxString& name)
1072 : wxPanel(parent, id, pos, size, style, name)
1073 , mProject(project)
1074 , mPrefsListenerHelper(std::make_unique<PrefsListenerHelper>(project))
1075{
1076 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1077
1078 auto header = safenew ThemedWindowWrapper<ListNavigationPanel>(this, wxID_ANY);
1079#if wxUSE_ACCESSIBILITY
1080 safenew WindowAccessible(header);
1081#endif
1082 header->SetBackgroundColorIndex(clrMedium);
1083 {
1084 auto hSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
1085 auto toggleEffects = safenew ThemedAButtonWrapper<AButton>(header);
1086 toggleEffects->SetImageIndices(0, bmpEffectOff, bmpEffectOff, bmpEffectOn, bmpEffectOn, bmpEffectOff);
1087 toggleEffects->SetButtonToggles(true);
1088 toggleEffects->SetTranslatableLabel(XO("Power"));
1089 toggleEffects->SetBackgroundColorIndex(clrMedium);
1090 mToggleEffects = toggleEffects;
1091
1092 toggleEffects->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
1093 if (mEffectList)
1094 {
1096
1099 }
1100 });
1101
1102 hSizer->Add(toggleEffects, 0, wxSTRETCH_NOT | wxALIGN_CENTER | wxLEFT, 5);
1103 {
1104 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1105
1106 auto headerText = safenew ThemedWindowWrapper<wxStaticText>(header, wxID_ANY, wxEmptyString);
1107 headerText->SetFont(wxFont(wxFontInfo().Bold()));
1108 headerText->SetTranslatableLabel(XO("Realtime Effects"));
1109 headerText->SetForegroundColorIndex(clrTrackPanelText);
1110
1111 auto trackTitle = safenew ThemedWindowWrapper<wxStaticText>(header, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
1112 trackTitle->SetForegroundColorIndex(clrTrackPanelText);
1113 mTrackTitle = trackTitle;
1114
1115 vSizer->Add(headerText);
1116 vSizer->Add(trackTitle);
1117
1118 hSizer->Add(vSizer.release(), 1, wxEXPAND | wxALL, 10);
1119 }
1120 auto close = safenew ThemedAButtonWrapper<AButton>(header);
1121 close->SetTranslatableLabel(XO("Close"));
1122 close->SetImageIndices(0, bmpCloseNormal, bmpCloseHover, bmpCloseDown, bmpCloseHover, bmpCloseDisabled);
1123 close->SetBackgroundColorIndex(clrMedium);
1124
1125 close->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Close(); });
1126
1127 hSizer->Add(close, 0, wxSTRETCH_NOT | wxALIGN_CENTER | wxRIGHT, 5);
1128
1129 header->SetSizer(hSizer.release());
1130 }
1131 vSizer->Add(header, 0, wxEXPAND);
1132
1133 auto effectList = safenew ThemedWindowWrapper<RealtimeEffectListWindow>(this, wxID_ANY);
1134 effectList->SetBackgroundColorIndex(clrMedium);
1135 vSizer->Add(effectList, 1, wxEXPAND);
1136
1137 mHeader = header;
1138 mEffectList = effectList;
1139
1140 SetSizerAndFit(vSizer.release());
1141
1142 Bind(wxEVT_CHAR_HOOK, &RealtimeEffectPanel::OnCharHook, this);
1144 auto track = evt.mpTrack.lock();
1145 auto waveTrack = std::dynamic_pointer_cast<WaveTrack>(track);
1146
1147 if (waveTrack == nullptr)
1148 return;
1149
1150 switch (evt.mType)
1151 {
1153 if (mCurrentTrack.lock() == waveTrack)
1154 mTrackTitle->SetLabel(track->GetName());
1155 UpdateRealtimeEffectUIData(*waveTrack);
1156 break;
1158 if (evt.mExtra == 0)
1159 mPotentiallyRemovedTracks.push_back(waveTrack);
1160 break;
1162 // Addition can be fired as a part of "replace" event.
1163 // Calling UpdateRealtimeEffectUIData is mostly no-op,
1164 // it will just create a new State and Access for it.
1165 UpdateRealtimeEffectUIData(*waveTrack);
1166 break;
1167 default:
1168 break;
1169 }
1170 });
1171
1173 [this](UndoRedoMessage message)
1174 {
1175 if (
1176 message.type == UndoRedoMessage::Type::Purge ||
1177 message.type == UndoRedoMessage::Type::BeginPurge ||
1178 message.type == UndoRedoMessage::Type::EndPurge)
1179 return;
1180
1181 auto& trackList = TrackList::Get(mProject);
1182
1183 // Realtime effect UI is only updated on Undo or Redo
1184 auto waveTracks = trackList.Any<WaveTrack>();
1185
1186 if (
1187 message.type == UndoRedoMessage::Type::UndoOrRedo ||
1188 message.type == UndoRedoMessage::Type::Reset)
1189 {
1190 for (auto waveTrack : waveTracks)
1191 UpdateRealtimeEffectUIData(*waveTrack);
1192 }
1193
1194 // But mPotentiallyRemovedTracks processing happens as fast as possible.
1195 // This event is fired right after the track is deleted, so we do not
1196 // hold the strong reference to the track much longer than need.
1197 if (mPotentiallyRemovedTracks.empty())
1198 return;
1199
1200 // Collect RealtimeEffectUIs that are currently shown
1201 // for the potentially removed tracks
1202 std::vector<RealtimeEffectStateUI*> shownUIs;
1203
1204 for (auto track : mPotentiallyRemovedTracks)
1205 {
1206 // By construction, track cannot be null
1207 assert(track != nullptr);
1208
1210 *track,
1211 [&shownUIs](auto& ui)
1212 {
1213 if (ui.IsShown())
1214 shownUIs.push_back(&ui);
1215 });
1216 }
1217
1218 // For every UI shown - check if the corresponding state
1219 // is reachable from the current track list.
1220 for (auto effectUI : shownUIs)
1221 {
1222 bool reachable = false;
1223
1224 for (auto track : waveTracks)
1225 {
1227 *track,
1228 [effectUI, &reachable](auto& ui)
1229 {
1230 if (effectUI == &ui)
1231 reachable = true;
1232 });
1233
1234 if (reachable)
1235 break;
1236 }
1237
1238 if (!reachable)
1239 // Don't need to autosave for an unreachable state
1240 effectUI->Hide();
1241 }
1242
1244 });
1245
1247 .Subscribe([this](const TrackFocusChangeMessage& msg) {
1248 if (IsShown())
1249 {
1250 auto& trackFocus = TrackFocus::Get(mProject);
1251 ShowPanel(dynamic_cast<SampleTrack *>(trackFocus.Get()), false);
1252 }
1253 });
1254
1255 Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent&) {
1256 HidePanel(); });
1257}
1258
1260{
1261}
1262
1264{
1265 if(track == nullptr)
1266 {
1267 ResetTrack();
1268 return;
1269 }
1270
1271 wxWindowUpdateLocker freeze(this);
1272
1274
1275 auto &projectWindow = ProjectWindow::Get(mProject);
1276 const auto pContainerWindow = projectWindow.GetContainerWindow();
1277 if (pContainerWindow->GetWindow1() != this)
1278 {
1279 //Restore previous effects window size
1280 pContainerWindow->SplitVertically(
1281 this,
1282 projectWindow.GetTrackListWindow(),
1283 this->GetSize().GetWidth());
1284 }
1285 if(focus)
1286 SetFocus();
1287 projectWindow.Layout();
1288}
1289
1291{
1292 wxWindowUpdateLocker freeze(this);
1293
1294 auto &projectWindow = ProjectWindow::Get(mProject);
1295 const auto pContainerWindow = projectWindow.GetContainerWindow();
1296 const auto pTrackListWindow = projectWindow.GetTrackListWindow();
1297 if (pContainerWindow->GetWindow2() == nullptr)
1298 //only effects panel is present, restore split positions before removing effects panel
1299 //Workaround: ::Replace and ::Initialize do not work here...
1300 pContainerWindow->SplitVertically(this, pTrackListWindow);
1301
1302 pContainerWindow->Unsplit(this);
1303 pTrackListWindow->SetFocus();
1304 projectWindow.Layout();
1305}
1306
1307void RealtimeEffectPanel::SetTrack(const std::shared_ptr<SampleTrack>& track)
1308{
1309 //Avoid creation-on-demand of a useless, empty list in case the track is of non-wave type.
1310 if(track && dynamic_cast<WaveTrack*>(&*track) != nullptr)
1311 {
1312 mTrackTitle->SetLabel(track->GetName());
1314 track && RealtimeEffectList::Get(*track).IsActive()
1316 : mToggleEffects->PopUp();
1317 mEffectList->SetTrack(mProject, track);
1318
1319 mCurrentTrack = track;
1320 //i18n-hint: argument - track name
1321 mHeader->SetName(wxString::Format(_("Realtime effects for %s"), track->GetName()));
1322 }
1323 else
1324 ResetTrack();
1325}
1326
1328{
1329 mTrackTitle->SetLabel(wxEmptyString);
1332 mCurrentTrack.reset();
1333 mHeader->SetName(wxEmptyString);
1334}
1335
1337{
1338 mHeader->SetFocus();
1339}
1340
1342{
1343 if(evt.GetKeyCode() == WXK_ESCAPE && IsShown() && IsDescendant(FindFocus()))
1344 Close();
1345 else
1346 evt.Skip();
1347}
Toolkit-neutral facade for basic user interface services.
wxString PluginID
const TranslatableString name
Definition: Distortion.cpp:76
@ EffectTypeAnalyze
@ EffectTypeProcess
ChoiceSetting RealtimeEffectsGroupBy
XO("Cut/Copy/Paste")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:9
AUDACITY_DLL_API AttachedWindows & GetAttachedWindows(AudacityProject &project)
accessors for certain important windows associated with each project
RealtimeEffectStateChange
for(int ii=0, nn=names.size();ii< nn;++ii)
TranslatableString label
Definition: TagsEditor.cpp:165
const auto project
THEME_API Theme theTheme
Definition: Theme.cpp:82
int id
static std::once_flag flag
A wxButton with mouse-over behaviour.
Definition: AButton.h:104
void PushDown()
Definition: AButton.cpp:577
bool IsDown()
Definition: AButton.h:208
void Disable()
Definition: AButton.cpp:560
void Enable()
Definition: AButton.cpp:551
void PopUp()
Definition: AButton.cpp:585
@ TextButton
Definition: AButton.h:112
void SetEnabled(bool state)
Definition: AButton.h:182
static void DrawFocus(wxDC &dc, wxRect &r)
Definition: AColor.cpp:235
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:371
wxString Read() const
Definition: Prefs.cpp:388
Subclass & Get(const RegisteredFactory &key)
Get reference to an attachment, creating on demand if not present, down-cast it to Subclass.
Definition: ClientData.h:317
EffectPlugin * GetEffect(const PluginID &ID)
static EffectManager & Get()
Abstract base class used in importing a file.
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
Changes default arrow navigation to behave more list- or table-like. Instead of searching focusable i...
void Create(wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxString &name=wxPanelNameStr)
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
A move-only handle representing a connection to a Publisher.
Definition: Observer.h:70
void Reset() noexcept
Breaks the connection (constant time)
Definition: Observer.cpp:101
const wxString & GetVendor() const
static Identifier GetEffectNameFromID(const PluginID &ID)
static bool IsPluginAvailable(const PluginDescriptor &plug)
const PluginDescriptor * GetPlugin(const PluginID &ID) const
static PluginManager & Get()
A listener notified of changes in preferences.
Definition: Prefs.h:652
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectWindow & Get(AudacityProject &project)
static RealtimeEffectList & Get(AudacityProject &project)
size_t GetStatesCount() const noexcept
bool IsActive() const
Non-blocking atomic boolean load.
void SetActive(bool value)
Done by main thread only, under a lock guard.
void OnEffectListItemChange(const RealtimeEffectListMessage &msg)
RealtimeEffectListWindow(wxWindow *parent, wxWindowID winid=wxID_ANY, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxScrolledWindowStyle, const wxString &name=wxPanelNameStr)
void OnAddEffectClicked(const wxCommandEvent &event)
void OnSizeChanged(wxSizeEvent &event)
std::unique_ptr< MenuRegistry::MenuItem > mEffectMenuRoot
std::shared_ptr< SampleTrack > mTrack
std::optional< wxString > PickEffect(wxWindow *parent, const wxString &selectedEffectID) override
void SetTrack(AudacityProject &project, const std::shared_ptr< SampleTrack > &track)
Observer::Subscription mEffectListItemMovedSubscription
void InsertEffectRow(size_t index, const std::shared_ptr< RealtimeEffectState > &pState)
Observer::Subscription mPluginsChangedSubscription
wxWeakRef< AudacityProject > mProject
static RealtimeEffectManager & Get(AudacityProject &project)
UI Panel that displays realtime effects from the effect stack of an individual track,...
Observer::Subscription mUndoSubscription
wxStaticText * mTrackTitle
void ShowPanel(SampleTrack *track, bool focus)
RealtimeEffectListWindow * mEffectList
void OnCharHook(wxKeyEvent &evt)
std::weak_ptr< SampleTrack > mCurrentTrack
static RealtimeEffectPanel & Get(AudacityProject &project)
std::vector< std::shared_ptr< SampleTrack > > mPotentiallyRemovedTracks
RealtimeEffectPanel(AudacityProject &project, wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=0, const wxString &name=wxPanelNameStr)
Observer::Subscription mFocusChangeSubscription
AudacityProject & mProject
Observer::Subscription mTrackListChanged
void SetTrack(const std::shared_ptr< SampleTrack > &track)
Shows effects from the effect stack of the track.
static RealtimeEffectStateUI & Get(RealtimeEffectState &state)
bool IsLeader() const override
Definition: Track.cpp:291
wxColour & Colour(int iIndex)
wxBitmap & Bitmap(int iIndex)
Track * Get()
Definition: TrackFocus.cpp:156
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:160
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1097
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:71
void MarkUnsaved()
A Track that contains audio waveform data.
Definition: WaveTrack.h:227
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
DropHintLine(wxWindow *parent, wxWindowID id, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize)
static const PluginDescriptor * GetPlugin(const PluginID &ID)
void Create(wxWindow *parent, RealtimeEffectPicker *effectPicker, wxWindowID winid, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize)
void SetEffect(AudacityProject &project, const std::shared_ptr< SampleTrack > &track, const std::shared_ptr< RealtimeEffectState > &pState)
RealtimeEffectControl(wxWindow *parent, RealtimeEffectPicker *effectPicker, wxWindowID winid, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize)
virtual std::optional< wxString > PickEffect(wxWindow *parent, const wxString &selectedEffectID)=0
bool OpenInDefaultBrowser(const wxString &url)
Open an URL in default browser.
Definition: BasicUI.cpp:240
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:277
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:373
void PopulateEffectsMenu(Group &menuItems, EffectType type, CommandFlag batchflags, const wxString &groupby, void(*onMenuCommand)(const CommandContext &), std::function< bool(const PluginDescriptor &)> pred={})
Definition: MenuHelper.cpp:563
constexpr auto Section
Definition: MenuRegistry.h:436
constexpr auto Menu
Items will appear in a main toolbar menu or in a sub-menu.
Definition: MenuRegistry.h:445
void VisitWithFunctions(const VisitorFunctions< RegistryTraits > &visitors, const GroupItem< RegistryTraits > *pTopItem, const GroupItem< RegistryTraits > *pRegistry={}, typename RegistryTraits::ComputedItemContextType &computedItemContext=RegistryTraits::ComputedItemContextType::Instance)
Definition: Registry.h:623
const TranslatableString desc
Definition: ExportPCM.cpp:51
void VisitRealtimeEffectStateUIs(SampleTrack &track, Visitor &&visitor)
static wxString GetSafeVendor(const PluginDescriptor &descriptor)
void ReopenRealtimeEffectUIData(AudacityProject &project, SampleTrack &track)
STL namespace.
const TranslatableString label_in
Definition: MenuRegistry.h:321
const auto & GetTitle() const
Definition: MenuRegistry.h:226
@ Remove
Effect item was removed from the list at srcIndex position. affectedState is removed state.
@ DidReplace
Effect item was replaced with a new item at srcIndex position. affectedState is an old state.
@ Move
Item position has changed, from srcIndex to dstIndex. affectedState is the moved state.
@ Insert
New effect item was added to the list at srcIndex position. affectedState is a new state.
@ WillReplace
Effect item will be replaced with a new item at srcIndex position. affectedState is the state to be r...
std::shared_ptr< RealtimeEffectState > affectedState
const Identifier name
Definition: Registry.h:86
Notification of changes in individual tracks of TrackList, or of TrackList's composition.
Definition: Track.h:946
const int mExtra
Definition: Track.h:982
const std::weak_ptr< Track > mpTrack
Definition: Track.h:981
const Type mType
Definition: Track.h:980
@ DELETION
Posted when a track has been deleted from a tracklist. Also posted when one track replaces another.
Definition: Track.h:968
@ ADDITION
Posted when a track has been added to a tracklist. Also posted when one track replaces another.
Definition: Track.h:962
@ TRACK_DATA_CHANGE
Posted when certain fields of a track change.
Definition: Track.h:952
Type of message published by UndoManager.
Definition: UndoManager.h:55
enum UndoRedoMessage::Type type