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 "PendingTracks.h"
45#include "Prefs.h"
46#include "BasicUI.h"
48#include "ListNavigationPanel.h"
49#include "MovableControl.h"
50#include "menus/MenuHelper.h"
51#include "prefs/EffectsPrefs.h"
52
53#if wxUSE_ACCESSIBILITY
54#include "WindowAccessible.h"
55#endif
56
57namespace
58{
59 using namespace MenuRegistry;
60 class RealtimeEffectsMenuVisitor final : public Visitor<Traits> {
61 wxMenu& mMenu;
62 wxMenu* mMenuPtr { nullptr };
63 int mMenuItemIdCounter { wxID_HIGHEST };
64 std::vector<Identifier> mIndexedPluginList;
65 int mMenuLevelCounter { 0 };
66 public:
67 RealtimeEffectsMenuVisitor(wxMenu& menu) : Visitor<Traits>{ std::tuple{
68 [this](const MenuRegistry::MenuItem &menuItem, const auto&) {
69 //Don't create a group item for root
70 if (mMenuLevelCounter != 0)
71 {
72 auto submenu = std::make_unique<wxMenu>();
73 mMenuPtr->AppendSubMenu(submenu.get(), menuItem.GetTitle().Translation());
74 mMenuPtr = submenu.release();
75 }
76 ++mMenuLevelCounter;
77 },
78
79 [this](const MenuRegistry::CommandItem &commandItem, const auto&) {
80 mMenuPtr->Append(mMenuItemIdCounter, commandItem.label_in.Translation());
81 mIndexedPluginList.push_back(commandItem.name);
82 ++mMenuItemIdCounter;
83 },
84
85 [this](const MenuRegistry::MenuItem &, const auto&) {
86 --mMenuLevelCounter;
87 if (mMenuLevelCounter != 0)
88 {
89 assert(mMenuPtr->GetParent() != nullptr);
90 mMenuPtr = mMenuPtr->GetParent();
91 }
92 }},
93
94 [this]() {
95 mMenuPtr->AppendSeparator();
96 }}
97 , mMenu(menu), mMenuPtr(&mMenu)
98 {}
99
100 Identifier GetPluginID(int menuIndex) const
101 {
102 assert(menuIndex >= wxID_HIGHEST && menuIndex < (wxID_HIGHEST + mIndexedPluginList.size()));
103 return mIndexedPluginList[menuIndex - wxID_HIGHEST];
104 }
105 };
106
107 template <typename Visitor>
109 {
110 auto& effects = RealtimeEffectList::Get(track);
111 effects.Visit(
112 [visitor](auto& effectState, bool)
113 {
114 auto& ui = RealtimeEffectStateUI::Get(effectState);
115 visitor(ui);
116 });
117 }
118
120 {
122 track, [&](auto& ui) { ui.UpdateTrackData(track); });
123 }
124
126 {
128 track,
129 [&](auto& ui)
130 {
131 if (ui.IsShown())
132 {
133 ui.Hide(&project);
134 ui.Show(project);
135 }
136 });
137 }
138 //fwd
139 class RealtimeEffectControl;
140
141 class DropHintLine : public wxWindow
142 {
143 public:
144 DropHintLine(wxWindow *parent,
145 wxWindowID id,
146 const wxPoint& pos = wxDefaultPosition,
147 const wxSize& size = wxDefaultSize)
148 : wxWindow(parent, id, pos, size, wxNO_BORDER, wxEmptyString)
149 {
150 wxWindow::SetBackgroundStyle(wxBG_STYLE_PAINT);
151 Bind(wxEVT_PAINT, &DropHintLine::OnPaint, this);
152 }
153
154 bool AcceptsFocus() const override { return false; }
155
156 private:
157 void OnPaint(wxPaintEvent&)
158 {
159 wxBufferedPaintDC dc(this);
160 const auto rect = wxRect(GetSize());
161
162 dc.SetPen(*wxTRANSPARENT_PEN);
163 dc.SetBrush(GetBackgroundColour());
164 dc.DrawRectangle(rect);
165 }
166 };
167
168 class HyperLinkCtrlWrapper : public ListNavigationEnabled<wxHyperlinkCtrl>
169 {
170 public:
171 HyperLinkCtrlWrapper(wxWindow *parent,
172 wxWindowID id,
173 const wxString& label,
174 const wxString& url,
175 const wxPoint& pos = wxDefaultPosition,
176 const wxSize& size = wxDefaultSize,
177 long style = wxHL_DEFAULT_STYLE,
178 const wxString& name = wxHyperlinkCtrlNameStr)
179 {
180 Create(parent, id, label, url, pos, size, style, name);
181 }
182
183 void Create(wxWindow *parent,
184 wxWindowID id,
185 const wxString& label,
186 const wxString& url,
187 const wxPoint& pos = wxDefaultPosition,
188 const wxSize& size = wxDefaultSize,
189 long style = wxHL_DEFAULT_STYLE,
190 const wxString& name = wxHyperlinkCtrlNameStr)
191 {
193 Bind(wxEVT_PAINT, &HyperLinkCtrlWrapper::OnPaint, this);
194 }
195
196 void OnPaint(wxPaintEvent& evt)
197 {
198 wxPaintDC dc(this);
199 dc.SetFont(GetFont());
200 dc.SetTextForeground(GetForegroundColour());
201 dc.SetTextBackground(GetBackgroundColour());
202
203 auto labelRect = GetLabelRect();
204
205 dc.DrawText(GetLabel(), labelRect.GetTopLeft());
206 if (HasFocus())
207 AColor::DrawFocus(dc, labelRect);
208 }
209 };
210
211#if wxUSE_ACCESSIBILITY
212 class RealtimeEffectControlAx : public wxAccessible
213 {
214 public:
215 RealtimeEffectControlAx(wxWindow* win = nullptr) : wxAccessible(win) { }
216
217 wxAccStatus GetName(int childId, wxString* name) override
218 {
219 if(childId != wxACC_SELF)
220 return wxACC_NOT_IMPLEMENTED;
221
222 if(auto movable = wxDynamicCast(GetWindow(), MovableControl))
223 //i18n-hint: argument - position of the effect in the effect stack
224 *name = wxString::Format(_("Effect %d"), movable->FindIndexInParent() + 1);
225 return wxACC_OK;
226 }
227
228 wxAccStatus GetChildCount(int* childCount) override
229 {
230 const auto window = GetWindow();
231 *childCount = window->GetChildren().size();
232 return wxACC_OK;
233 }
234
235 wxAccStatus GetChild(int childId, wxAccessible** child) override
236 {
237 if(childId == wxACC_SELF)
238 *child = this;
239 else
240 {
241 const auto window = GetWindow();
242 const auto& children = window->GetChildren();
243 const auto childIndex = childId - 1;
244 if(childIndex < children.size())
245 *child = children[childIndex]->GetAccessible();
246 else
247 *child = nullptr;
248 }
249 return wxACC_OK;
250 }
251
252 wxAccStatus GetRole(int childId, wxAccRole* role) override
253 {
254 if(childId != wxACC_SELF)
255 return wxACC_NOT_IMPLEMENTED;
256
257 *role = wxROLE_SYSTEM_PANE;
258 return wxACC_OK;
259 }
260
261 wxAccStatus GetState(int childId, long* state) override
262 {
263 if(childId != wxACC_SELF)
264 return wxACC_NOT_IMPLEMENTED;
265
266 const auto window = GetWindow();
267 if(!window->IsEnabled())
268 *state = wxACC_STATE_SYSTEM_UNAVAILABLE;
269 else
270 {
271 *state = wxACC_STATE_SYSTEM_FOCUSABLE;
272 if(window->HasFocus())
273 *state |= wxACC_STATE_SYSTEM_FOCUSED;
274 }
275 return wxACC_OK;
276 }
277 };
278#endif
279
281 {
282 public:
283 virtual std::optional<wxString> PickEffect(wxWindow* parent, const wxString& selectedEffectID) = 0;
284 };
285
286 //UI control that represents individual effect from the effect list
287 class RealtimeEffectControl : public ListNavigationEnabled<MovableControl>
288 {
289 wxWeakRef<AudacityProject> mProject;
290 std::shared_ptr<SampleTrack> mTrack;
291 std::shared_ptr<RealtimeEffectState> mEffectState;
292 std::shared_ptr<EffectSettingsAccess> mSettingsAccess;
293
294 RealtimeEffectPicker* mEffectPicker { nullptr };
295
296 ThemedAButtonWrapper<AButton>* mChangeButton{nullptr};
297 AButton* mEnableButton{nullptr};
299
301
302 public:
304
305 RealtimeEffectControl(wxWindow* parent,
306 RealtimeEffectPicker* effectPicker,
307 wxWindowID winid,
308 const wxPoint& pos = wxDefaultPosition,
309 const wxSize& size = wxDefaultSize)
310 {
311 Create(parent, effectPicker, winid, pos, size);
312 }
313
314 void Create(wxWindow* parent,
315 RealtimeEffectPicker* effectPicker,
316 wxWindowID winid,
317 const wxPoint& pos = wxDefaultPosition,
318 const wxSize& size = wxDefaultSize)
319 {
320 mEffectPicker = effectPicker;
321
322 //Prevents flickering and paint order issues
323 MovableControl::SetBackgroundStyle(wxBG_STYLE_PAINT);
324 MovableControl::Create(parent, winid, pos, size, wxNO_BORDER | wxWANTS_CHARS);
325
326 Bind(wxEVT_PAINT, &RealtimeEffectControl::OnPaint, this);
327 Bind(wxEVT_SET_FOCUS, &RealtimeEffectControl::OnFocusChange, this);
328 Bind(wxEVT_KILL_FOCUS, &RealtimeEffectControl::OnFocusChange, this);
329
330 auto sizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
331
332 //On/off button
333 auto enableButton = safenew ThemedAButtonWrapper<AButton>(this);
334 enableButton->SetTranslatableLabel(XO("Power"));
335 enableButton->SetImageIndices(0, bmpEffectOff, bmpEffectOff, bmpEffectOn, bmpEffectOn, bmpEffectOff);
336 enableButton->SetButtonToggles(true);
337 enableButton->SetBackgroundColorIndex(clrEffectListItemBackground);
338 mEnableButton = enableButton;
339
340 enableButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
341
342 mEffectState->SetActive(mEnableButton->IsDown());
343 if (mProject)
344 UndoManager::Get(*mProject).MarkUnsaved();
345 });
346
347 //Central button with effect name, show settings
348 const auto optionsButton = safenew ThemedAButtonWrapper<AButton>(this, wxID_ANY);
349 optionsButton->SetImageIndices(0,
350 bmpHButtonNormal,
351 bmpHButtonHover,
352 bmpHButtonDown,
353 bmpHButtonHover,
354 bmpHButtonDisabled);
355 optionsButton->SetBackgroundColorIndex(clrEffectListItemBackground);
356 optionsButton->SetForegroundColorIndex(clrTrackPanelText);
357 optionsButton->SetButtonType(AButton::TextButton);
358 optionsButton->Bind(wxEVT_BUTTON, &RealtimeEffectControl::OnOptionsClicked, this);
359
360 //Remove/replace effect
361 auto changeButton = safenew ThemedAButtonWrapper<AButton>(this);
362 changeButton->SetImageIndices(0, bmpMoreNormal, bmpMoreHover, bmpMoreDown, bmpMoreHover, bmpMoreDisabled);
363 changeButton->SetBackgroundColorIndex(clrEffectListItemBackground);
364 changeButton->SetTranslatableLabel(XO("Replace effect"));
365 changeButton->Bind(wxEVT_BUTTON, &RealtimeEffectControl::OnChangeButtonClicked, this);
366
367 auto dragArea = safenew wxStaticBitmap(this, wxID_ANY, theTheme.Bitmap(bmpDragArea));
368 dragArea->Disable();
369 sizer->Add(dragArea, 0, wxLEFT | wxCENTER, 5);
370 sizer->Add(enableButton, 0, wxLEFT | wxCENTER, 5);
371 sizer->Add(optionsButton, 1, wxLEFT | wxCENTER, 5);
372 sizer->Add(changeButton, 0, wxLEFT | wxRIGHT | wxCENTER, 5);
373 mChangeButton = changeButton;
374 mOptionsButton = optionsButton;
375
376 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
377 vSizer->Add(sizer.release(), 0, wxUP | wxDOWN | wxEXPAND, 10);
378
379 SetSizer(vSizer.release());
380
381#if wxUSE_ACCESSIBILITY
382 SetAccessible(safenew RealtimeEffectControlAx(this));
383#endif
384 }
385
386 static const PluginDescriptor *GetPlugin(const PluginID &ID) {
387 auto desc = PluginManager::Get().GetPlugin(ID);
388 return desc;
389 }
390
393 {
394 const auto &ID = mEffectState->GetID();
395 const auto desc = GetPlugin(ID);
396 return desc
397 ? desc->GetSymbol().Msgid()
398 : XO("%s (missing)")
399 .Format(PluginManager::GetEffectNameFromID(ID).GET());
400 }
401
403 const std::shared_ptr<SampleTrack>& track,
404 const std::shared_ptr<RealtimeEffectState> &pState)
405 {
406 mProject = &project;
407 mTrack = track;
408 mEffectState = pState;
409
410 mSubscription = mEffectState->Subscribe([this](RealtimeEffectStateChange state) {
412 ? mEnableButton->PushDown()
413 : mEnableButton->PopUp();
414
415 if (mProject)
416 ProjectHistory::Get(*mProject).ModifyState(false);
417 });
418
420 if (pState) {
421 label = GetEffectName();
422 mSettingsAccess = pState->GetAccess();
423 }
424 else
425 mSettingsAccess.reset();
426 if (mEnableButton)
427 mSettingsAccess && mSettingsAccess->Get().extra.GetActive()
428 ? mEnableButton->PushDown()
429 : mEnableButton->PopUp();
430 if (mOptionsButton)
431 {
432 mOptionsButton->SetTranslatableLabel(label);
433 mOptionsButton->SetEnabled(pState && GetPlugin(pState->GetID()));
434 }
435 }
436
438 {
439 if(mProject == nullptr || mEffectState == nullptr)
440 return;
441
442 auto& ui = RealtimeEffectStateUI::Get(*mEffectState);
443 // Don't need autosave for the effect that is being removed
444 ui.Hide();
445
446 auto effectName = GetEffectName();
447 //After AudioIO::RemoveState call this will be destroyed
448 auto project = mProject.get();
449 auto trackName = mTrack->GetName();
450
451 AudioIO::Get()->RemoveState(*project, &*mTrack, mEffectState);
457 XO("Removed %s from %s").Format(effectName, trackName),
460 XO("Remove %s").Format(effectName)
461 );
462 }
463
464 void OnOptionsClicked(wxCommandEvent& event)
465 {
466 if(mProject == nullptr || mEffectState == nullptr)
467 return;//not initialized
468
469 const auto ID = mEffectState->GetID();
470 const auto effectPlugin = EffectManager::Get().GetEffect(ID);
471
472 if(effectPlugin == nullptr)
473 {
475 return;
476 }
477
478 auto& effectStateUI = RealtimeEffectStateUI::Get(*mEffectState);
479
480 effectStateUI.UpdateTrackData(*mTrack);
481 effectStateUI.Toggle( *mProject );
482 }
483
484 void OnChangeButtonClicked(wxCommandEvent& event)
485 {
486 if(!mTrack || mProject == nullptr)
487 return;
488 if(mEffectState == nullptr)
489 return;//not initialized
490
491 const auto effectID = mEffectPicker->PickEffect(mChangeButton, mEffectState->GetID());
492 if(!effectID)
493 return;//nothing
494
495 if(effectID->empty())
496 {
497 RemoveFromList();
498 return;
499 }
500
501 auto &em = RealtimeEffectManager::Get(*mProject);
502 auto oIndex = em.FindState(&*mTrack, mEffectState);
503 if (!oIndex)
504 return;
505
506 auto oldName = GetEffectName();
507 auto &project = *mProject;
508 auto trackName = mTrack->GetName();
509 if (auto state = AudioIO::Get()
510 ->ReplaceState(project, &*mTrack, *oIndex, *effectID)
511 ){
512 // Message subscription took care of updating the button text
513 // and destroyed `this`!
514 auto effect = state->GetEffect();
515 assert(effect); // postcondition of ReplaceState
517 /*i18n-hint: undo history,
518 first and second parameters - realtime effect names
519 */
520 XO("Replaced %s with %s")
521 .Format(oldName, effect->GetName()),
524 XO("Replace %s").Format(oldName));
525 }
526 }
527
528 void OnPaint(wxPaintEvent&)
529 {
530 wxBufferedPaintDC dc(this);
531 const auto rect = wxRect(GetSize());
532
533 dc.SetPen(*wxTRANSPARENT_PEN);
534 dc.SetBrush(GetBackgroundColour());
535 dc.DrawRectangle(rect);
536
537 dc.SetPen(theTheme.Colour(clrEffectListItemBorder));
538 dc.SetBrush(theTheme.Colour(clrEffectListItemBorder));
539 dc.DrawLine(rect.GetBottomLeft(), rect.GetBottomRight());
540
541 if(HasFocus())
542 AColor::DrawFocus(dc, GetClientRect().Deflate(3, 3));
543 }
544
545 void OnFocusChange(wxFocusEvent& evt)
546 {
547 Refresh(false);
548 evt.Skip();
549 }
550 };
551
552 static wxString GetSafeVendor(const PluginDescriptor& descriptor)
553 {
554 if (descriptor.GetVendor().empty())
555 return XO("Unknown").Translation();
556
557 return descriptor.GetVendor();
558 }
559}
560
562 : public wxScrolledWindow
563 , public RealtimeEffectPicker
564 , public PrefsListener
565{
566 wxWeakRef<AudacityProject> mProject;
567 std::shared_ptr<SampleTrack> mTrack;
569 wxStaticText* mAddEffectHint{nullptr};
570 wxWindow* mAddEffectTutorialLink{nullptr};
571 wxWindow* mEffectListContainer{nullptr};
572
573 std::unique_ptr<MenuRegistry::MenuItem> mEffectMenuRoot;
574
577
578public:
579 RealtimeEffectListWindow(wxWindow *parent,
580 wxWindowID winid = wxID_ANY,
581 const wxPoint& pos = wxDefaultPosition,
582 const wxSize& size = wxDefaultSize,
583 long style = wxScrolledWindowStyle,
584 const wxString& name = wxPanelNameStr)
585 : wxScrolledWindow(parent, winid, pos, size, style, name)
586 {
587 Bind(wxEVT_SIZE, &RealtimeEffectListWindow::OnSizeChanged, this);
588#ifdef __WXMSW__
589 //Fixes flickering on redraw
590 wxScrolledWindow::SetDoubleBuffered(true);
591#endif
592 auto rootSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
593
594 auto effectListContainer = safenew ThemedWindowWrapper<wxPanel>(this, wxID_ANY);
595 effectListContainer->SetBackgroundColorIndex(clrMedium);
596 effectListContainer->SetSizer(safenew wxBoxSizer(wxVERTICAL));
597 effectListContainer->SetDoubleBuffered(true);
598 effectListContainer->Hide();
599 mEffectListContainer = effectListContainer;
600
601 auto addEffect = safenew ThemedAButtonWrapper<AButton>(this, wxID_ANY);
602 addEffect->SetImageIndices(0,
603 bmpHButtonNormal,
604 bmpHButtonHover,
605 bmpHButtonDown,
606 bmpHButtonHover,
607 bmpHButtonDisabled);
608 addEffect->SetTranslatableLabel(XO("Add effect"));
609 addEffect->SetButtonType(AButton::TextButton);
610 addEffect->SetBackgroundColorIndex(clrMedium);
611 addEffect->SetForegroundColorIndex(clrTrackPanelText);
612 addEffect->Bind(wxEVT_BUTTON, &RealtimeEffectListWindow::OnAddEffectClicked, this);
613 mAddEffect = addEffect;
614
615 auto addEffectHint = safenew ThemedWindowWrapper<wxStaticText>(this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_NO_AUTORESIZE);
616 //Workaround: text is set in the OnSizeChange
617 addEffectHint->SetForegroundColorIndex(clrTrackPanelText);
618 mAddEffectHint = addEffectHint;
619
620 auto addEffectTutorialLink = safenew ThemedWindowWrapper<wxHyperlinkCtrl>(
621 this, wxID_ANY, _("Watch video"),
622 "https://www.audacityteam.org/realtime-video", wxDefaultPosition,
623 wxDefaultSize, wxHL_ALIGN_LEFT | wxHL_CONTEXTMENU);
624
625 //i18n-hint: Hyperlink to the effects stack panel tutorial video
626 addEffectTutorialLink->SetTranslatableLabel(XO("Watch video"));
627#if wxUSE_ACCESSIBILITY
628 safenew WindowAccessible(addEffectTutorialLink);
629#endif
630
631 addEffectTutorialLink->Bind(
632 wxEVT_HYPERLINK, [](wxHyperlinkEvent& event)
633 { BasicUI::OpenInDefaultBrowser(event.GetURL()); });
634
635 mAddEffectTutorialLink = addEffectTutorialLink;
636
637 //indicates the insertion position of the item
638 auto dropHintLine = safenew ThemedWindowWrapper<DropHintLine>(effectListContainer, wxID_ANY);
639 dropHintLine->SetBackgroundColorIndex(clrDropHintHighlight);
640 dropHintLine->Hide();
641
642 rootSizer->Add(mEffectListContainer, 0, wxEXPAND | wxBOTTOM, 10);
643 rootSizer->Add(addEffect, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 20);
644 rootSizer->Add(addEffectHint, 0, wxLEFT | wxRIGHT | wxBOTTOM | wxEXPAND, 20);
645 rootSizer->Add(addEffectTutorialLink, 0, wxLEFT | wxRIGHT | wxEXPAND, 20);
646
647 SetSizer(rootSizer.release());
648 SetMinSize({});
649
650 Bind(EVT_MOVABLE_CONTROL_DRAG_STARTED, [dropHintLine](const MovableControlEvent& event)
651 {
652 if(auto window = dynamic_cast<wxWindow*>(event.GetEventObject()))
653 window->Raise();
654 });
655 Bind(EVT_MOVABLE_CONTROL_DRAG_POSITION, [this, dropHintLine](const MovableControlEvent& event)
656 {
657 constexpr auto DropHintLineHeight { 3 };//px
658
659 auto sizer = mEffectListContainer->GetSizer();
660 assert(sizer != nullptr);
661
662 if(event.GetSourceIndex() == event.GetTargetIndex())
663 {
664 //do not display hint line if position didn't change
665 dropHintLine->Hide();
666 return;
667 }
668
669 if(!dropHintLine->IsShown())
670 {
671 dropHintLine->Show();
672 dropHintLine->Raise();
673 if(auto window = dynamic_cast<wxWindow*>(event.GetEventObject()))
674 window->Raise();
675 }
676
677 auto item = sizer->GetItem(event.GetTargetIndex());
678 dropHintLine->SetSize(item->GetSize().x, DropHintLineHeight);
679
680 if(event.GetTargetIndex() > event.GetSourceIndex())
681 dropHintLine->SetPosition(item->GetRect().GetBottomLeft() - wxPoint(0, DropHintLineHeight));
682 else
683 dropHintLine->SetPosition(item->GetRect().GetTopLeft());
684 });
685 Bind(EVT_MOVABLE_CONTROL_DRAG_FINISHED, [this, dropHintLine](const MovableControlEvent& event)
686 {
687 dropHintLine->Hide();
688
689 if(mProject == nullptr)
690 return;
691
692 auto& effectList = RealtimeEffectList::Get(*mTrack);
693 const auto from = event.GetSourceIndex();
694 const auto to = event.GetTargetIndex();
695 if(from != to)
696 {
697 auto effectName =
698 effectList.GetStateAt(from)->GetEffect()->GetName();
699 bool up = (to < from);
700 effectList.MoveEffect(from, to);
702 (up
707 ? XO("Moved %s up in %s")
712 : XO("Moved %s down in %s"))
713 .Format(effectName, mTrack->GetName()),
714 XO("Change effect order"), UndoPush::CONSOLIDATE);
715 }
716 else
717 {
718 wxWindowUpdateLocker freeze(this);
719 Layout();
720 }
721 });
722 SetScrollRate(0, 20);
723
726 {
728 });
730 }
731
732 void UpdatePrefs() override
733 {
735 }
736
737 std::optional<wxString> PickEffect(wxWindow* parent, const wxString& selectedEffectID) override
738 {
739 if (mProject == nullptr)
740 return {};
741
742 wxMenu menu;
743 if(!selectedEffectID.empty())
744 {
745 //no need to handle language change since menu creates its own event loop
746 menu.Append(wxID_REMOVE, _("No Effect"));
747 menu.AppendSeparator();
748 }
749
750 RealtimeEffectsMenuVisitor visitor { menu };
751
753
754 int commandId = wxID_NONE;
755
756 menu.AppendSeparator();
757 menu.Append(wxID_MORE, _("Get more effects..."));
758
759 menu.Bind(wxEVT_MENU, [&](wxCommandEvent evt) { commandId = evt.GetId(); });
760
761 if(parent->PopupMenu(&menu, parent->GetClientRect().GetLeftBottom()) && commandId != wxID_NONE)
762 {
763 if(commandId == wxID_REMOVE)
764 return wxString {};
765 else if(commandId == wxID_MORE)
766 OpenInDefaultBrowser("https://plugins.audacityteam.org/");
767 else
768 return visitor.GetPluginID(commandId).GET();
769 }
770
771 return {};
772 }
773
775 {
776 using namespace MenuRegistry;
777 auto root = Menu("", TranslatableString{});
778
779 static auto realtimeEffectPredicate = [](const PluginDescriptor& desc)
780 {
781 return desc.IsEffectRealtime();
782 };
783
784 const auto groupby = RealtimeEffectsGroupBy.Read();
785
786 auto analyzeSection = Section("", Menu("", XO("Analyze")));
787 auto submenu =
788 static_cast<MenuItem*>(analyzeSection->begin()->get());
790 *submenu,
792 {}, groupby, nullptr,
793 realtimeEffectPredicate
794 );
795
796 if(!submenu->empty())
797 {
798 root->push_back(move(analyzeSection));
799 }
800
802 *root,
804 {}, groupby, nullptr,
805 realtimeEffectPredicate
806 );
807
808 mEffectMenuRoot.swap(root);
809 }
810
811 void OnSizeChanged(wxSizeEvent& event)
812 {
813 if(auto sizerItem = GetSizer()->GetItem(mAddEffectHint))
814 {
815 //We need to wrap the text whenever panel width changes and adjust widget height
816 //so that text is fully visible, but there is no height-for-width layout algorithm
817 //in wxWidgets yet, so for now we just do it manually
818
819 //Restore original text, because 'Wrap' will replace it with wrapped one
820 mAddEffectHint->SetLabel(_("Realtime effects are non-destructive and can be changed at any time."));
821 mAddEffectHint->Wrap(GetClientSize().x - sizerItem->GetBorder() * 2);
822 mAddEffectHint->InvalidateBestSize();
823 }
824 event.Skip();
825 }
826
828 {
829 auto sizer = mEffectListContainer->GetSizer();
830 const auto insertItem = [this, &msg](){
831 auto& effects = RealtimeEffectList::Get(*mTrack);
832 InsertEffectRow(msg.srcIndex, effects.GetStateAt(msg.srcIndex));
833 mAddEffectHint->Hide();
835 };
836 const auto removeItem = [&](){
838 // Don't need to auto-save changed settings of effect that is deleted
839 // Undo history push will do it anyway
840 ui.Hide();
841
842 auto window = sizer->GetItem(msg.srcIndex)->GetWindow();
843 sizer->Remove(msg.srcIndex);
844 wxTheApp->CallAfter([ref = wxWeakRef { window }] {
845 if(ref) ref->Destroy();
846 });
847
848 if(sizer->IsEmpty())
849 {
850 if(mEffectListContainer->IsDescendant(FindFocus()))
851 mAddEffect->SetFocus();
852
853 mEffectListContainer->Hide();
854 mAddEffectHint->Show();
856 }
857 };
858
859 wxWindowUpdateLocker freeze(this);
861 {
862 const auto sizer = mEffectListContainer->GetSizer();
863
864 const auto movedItem = sizer->GetItem(msg.srcIndex);
865
866 const auto proportion = movedItem->GetProportion();
867 const auto flag = movedItem->GetFlag();
868 const auto border = movedItem->GetBorder();
869 const auto window = movedItem->GetWindow();
870
871 if(msg.srcIndex < msg.dstIndex)
872 window->MoveAfterInTabOrder(sizer->GetItem(msg.dstIndex)->GetWindow());
873 else
874 window->MoveBeforeInTabOrder(sizer->GetItem(msg.dstIndex)->GetWindow());
875
876 sizer->Remove(msg.srcIndex);
877 sizer->Insert(msg.dstIndex, window, proportion, flag, border);
878 }
880 {
881 insertItem();
882 }
884 {
885 removeItem();
886 }
888 {
889 insertItem();
890 }
892 {
893 removeItem();
894 }
895 SendSizeEventToParent();
896 }
897
899 {
901
902 mTrack.reset();
903 mProject = nullptr;
905 }
906
908 const std::shared_ptr<SampleTrack>& track)
909 {
910 if (mTrack == track)
911 return;
912
914
915 mTrack = track;
916 mProject = &project;
918
919 if (track)
920 {
921 auto& effects = RealtimeEffectList::Get(*mTrack);
922 mEffectListItemMovedSubscription = effects.Subscribe(
924
926 }
927 }
928
929 void EnableEffects(bool enable)
930 {
931 if (mTrack)
932 RealtimeEffectList::Get(*mTrack).SetActive(enable);
933 }
934
936 {
937 wxWindowUpdateLocker freeze(this);
938
939 const auto hadFocus = mEffectListContainer->IsDescendant(FindFocus());
940 //delete items that were added to the sizer
941 mEffectListContainer->Hide();
942 mEffectListContainer->GetSizer()->Clear(true);
943
944
945 if(!mTrack || RealtimeEffectList::Get(*mTrack).GetStatesCount() == 0)
946 mEffectListContainer->Hide();
947
948 auto isEmpty{true};
949 if(mTrack)
950 {
951 auto& effects = RealtimeEffectList::Get(*mTrack);
952 isEmpty = effects.GetStatesCount() == 0;
953 for(size_t i = 0, count = effects.GetStatesCount(); i < count; ++i)
954 InsertEffectRow(i, effects.GetStateAt(i));
955 }
957 //Workaround for GTK: Underlying GTK widget does not update
958 //its size when wxWindow size is set to zero
959 mEffectListContainer->Show(!isEmpty);
960 mAddEffectHint->Show(isEmpty);
961 mAddEffectTutorialLink->Show(isEmpty);
962
963 SendSizeEventToParent();
964 }
965
966 void OnAddEffectClicked(const wxCommandEvent& event)
967 {
968 if(!mTrack || mProject == nullptr)
969 return;
970
971 const auto effectID = PickEffect(dynamic_cast<wxWindow*>(event.GetEventObject()), {});
972
973 if(!effectID || effectID->empty())
974 return;
975
976 auto plug = PluginManager::Get().GetPlugin(*effectID);
977 if(!plug)
978 return;
979
982 XO("This plugin could not be loaded.\nIt may have been deleted."),
984 .Caption(XO("Plugin Error")));
985
986 return;
987 }
988
989 if(auto state = AudioIO::Get()->AddState(*mProject, &*mTrack, *effectID))
990 {
991 auto effect = state->GetEffect();
992 assert(effect); // postcondition of AddState
993 const auto effectName = effect->GetName();
999 XO("Added %s to %s").Format(effectName, mTrack->GetName()),
1000 //i18n-hint: undo history record
1001 XO("Add %s").Format(effectName));
1002 }
1003 }
1004
1005 void InsertEffectRow(size_t index,
1006 const std::shared_ptr<RealtimeEffectState> &pState)
1007 {
1008 if(mProject == nullptr)
1009 return;
1010
1011 // See comment in ReloadEffectsList
1012 if(!mEffectListContainer->IsShown())
1013 mEffectListContainer->Show();
1014
1016 row->SetBackgroundColorIndex(clrEffectListItemBackground);
1017 row->SetEffect(*mProject, mTrack, pState);
1018 mEffectListContainer->GetSizer()->Insert(index, row, 0, wxEXPAND);
1019 }
1020};
1021
1022
1024{
1026
1028 : mProject { project }
1029 {}
1030
1031 void UpdatePrefs() override
1032 {
1033 auto& trackList = TrackList::Get(mProject);
1034 for (auto waveTrack : trackList.Any<WaveTrack>())
1036 }
1037};
1038
1039namespace {
1040AttachedWindows::RegisteredFactory sKey{
1041[](AudacityProject &project) -> wxWeakRef<wxWindow> {
1042 constexpr auto EffectsPanelMinWidth { 255 };
1043
1044 const auto pProjectWindow = &ProjectWindow::Get(project);
1046 project, pProjectWindow->GetContainerWindow(), wxID_ANY);
1047 effectsPanel->SetMinSize({EffectsPanelMinWidth, -1});
1048 effectsPanel->SetName(_("Realtime effects"));
1049 effectsPanel->SetBackgroundColorIndex(clrMedium);
1050 effectsPanel->Hide();//initially hidden
1051 return effectsPanel;
1052}
1053};
1054}
1055
1057{
1059}
1060
1061const RealtimeEffectPanel &
1063{
1064 return Get(const_cast<AudacityProject &>(project));
1065}
1066
1068 AudacityProject& project, wxWindow* parent, wxWindowID id, const wxPoint& pos,
1069 const wxSize& size,
1070 long style, const wxString& name)
1071 : wxPanel(parent, id, pos, size, style, name)
1072 , mProject(project)
1073 , mPrefsListenerHelper(std::make_unique<PrefsListenerHelper>(project))
1074{
1075 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1076
1077 auto header = safenew ThemedWindowWrapper<ListNavigationPanel>(this, wxID_ANY);
1078#if wxUSE_ACCESSIBILITY
1079 safenew WindowAccessible(header);
1080#endif
1081 header->SetBackgroundColorIndex(clrMedium);
1082 {
1083 auto hSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
1084 auto toggleEffects = safenew ThemedAButtonWrapper<AButton>(header);
1085 toggleEffects->SetImageIndices(0, bmpEffectOff, bmpEffectOff, bmpEffectOn, bmpEffectOn, bmpEffectOff);
1086 toggleEffects->SetButtonToggles(true);
1087 toggleEffects->SetTranslatableLabel(XO("Power"));
1088 toggleEffects->SetBackgroundColorIndex(clrMedium);
1089 mToggleEffects = toggleEffects;
1090
1091 toggleEffects->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) {
1092 if (mEffectList)
1093 {
1095
1098 }
1099 });
1100
1101 hSizer->Add(toggleEffects, 0, wxSTRETCH_NOT | wxALIGN_CENTER | wxLEFT, 5);
1102 {
1103 auto vSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1104
1105 auto headerText = safenew ThemedWindowWrapper<wxStaticText>(header, wxID_ANY, wxEmptyString);
1106 headerText->SetFont(wxFont(wxFontInfo().Bold()));
1107 headerText->SetTranslatableLabel(XO("Realtime Effects"));
1108 headerText->SetForegroundColorIndex(clrTrackPanelText);
1109
1110 auto trackTitle = safenew ThemedWindowWrapper<wxStaticText>(header, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
1111 trackTitle->SetForegroundColorIndex(clrTrackPanelText);
1112 mTrackTitle = trackTitle;
1113
1114 vSizer->Add(headerText);
1115 vSizer->Add(trackTitle);
1116
1117 hSizer->Add(vSizer.release(), 1, wxEXPAND | wxALL, 10);
1118 }
1119 auto close = safenew ThemedAButtonWrapper<AButton>(header);
1120 close->SetTranslatableLabel(XO("Close"));
1121 close->SetImageIndices(0, bmpCloseNormal, bmpCloseHover, bmpCloseDown, bmpCloseHover, bmpCloseDisabled);
1122 close->SetBackgroundColorIndex(clrMedium);
1123
1124 close->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Close(); });
1125
1126 hSizer->Add(close, 0, wxSTRETCH_NOT | wxALIGN_CENTER | wxRIGHT, 5);
1127
1128 header->SetSizer(hSizer.release());
1129 }
1130 vSizer->Add(header, 0, wxEXPAND);
1131
1132 auto effectList = safenew ThemedWindowWrapper<RealtimeEffectListWindow>(this, wxID_ANY);
1133 effectList->SetBackgroundColorIndex(clrMedium);
1134 vSizer->Add(effectList, 1, wxEXPAND);
1135
1136 mHeader = header;
1137 mEffectList = effectList;
1138
1139 SetSizerAndFit(vSizer.release());
1140
1141 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:369
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:318
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
static PendingTracks & Get(AudacityProject &project)
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)
wxColour & Colour(int iIndex)
wxBitmap & Bitmap(int iIndex)
Track * Get()
Definition: TrackFocus.cpp:156
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:146
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:950
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
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:203
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:279
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:375
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:803
const int mExtra
Definition: Track.h:839
const std::weak_ptr< Track > mpTrack
Definition: Track.h:838
const Type mType
Definition: Track.h:837
@ DELETION
Posted when a track has been deleted from a tracklist. Also posted when one track replaces another.
Definition: Track.h:825
@ ADDITION
Posted when a track has been added to a tracklist. Also posted when one track replaces another.
Definition: Track.h:819
@ TRACK_DATA_CHANGE
Posted when certain fields of a track change.
Definition: Track.h:809
Type of message published by UndoManager.
Definition: UndoManager.h:55
enum UndoRedoMessage::Type type