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