Audacity  2.2.2
EffectRack.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  EffectRack.cpp
6 
7  Leland Lucius
8 
9  Audacity(R) is copyright (c) 1999-2008 Audacity Team.
10  License: GPL v2. See License.txt.
11 
12 **********************************************************************/
13 
14 #include "../Audacity.h"
15 #include "../Experimental.h"
16 
17 #if defined(EXPERIMENTAL_EFFECTS_RACK)
18 
19 #include "../MemoryX.h"
20 #include "../UndoManager.h"
21 
22 #include <wx/access.h>
23 #include <wx/defs.h>
24 #include <wx/bmpbuttn.h>
25 #include <wx/button.h>
26 #include <wx/dcmemory.h>
27 #include <wx/frame.h>
28 #include <wx/image.h>
29 #include <wx/imaglist.h>
30 #include <wx/settings.h>
31 #include <wx/sizer.h>
32 #include <wx/statline.h>
33 #include <wx/stattext.h>
34 #include <wx/timer.h>
35 #include <wx/tglbtn.h>
36 
37 #include "EffectManager.h"
38 #include "EffectRack.h"
39 #include "../commands/CommandContext.h"
40 #include "../Prefs.h"
41 #include "../Project.h"
42 
43 #include "../../images/EffectRack/EffectRack.h"
44 
45 #define COL_POWER 0
46 #define COL_EDITOR 1
47 #define COL_UP 2
48 #define COL_DOWN 3
49 #define COL_FAV 4
50 #define COL_REMOVE 5
51 #define COL_NAME 6
52 #define NUMCOLS 7
53 
54 #define ID_BASE 20000
55 #define ID_RANGE 100
56 #define ID_POWER (ID_BASE + (COL_POWER * ID_RANGE))
57 #define ID_EDITOR (ID_BASE + (COL_EDITOR * ID_RANGE))
58 #define ID_UP (ID_BASE + (COL_UP * ID_RANGE))
59 #define ID_DOWN (ID_BASE + (COL_DOWN * ID_RANGE))
60 #define ID_FAV (ID_BASE + (COL_FAV * ID_RANGE))
61 #define ID_REMOVE (ID_BASE + (COL_REMOVE * ID_RANGE))
62 #define ID_NAME (ID_BASE + (COL_NAME * ID_RANGE))
63 
64 BEGIN_EVENT_TABLE(EffectRack, wxFrame)
65  EVT_CLOSE(EffectRack::OnClose)
66  EVT_TIMER(wxID_ANY, EffectRack::OnTimer)
67 
68  EVT_BUTTON(wxID_APPLY, EffectRack::OnApply)
69  EVT_TOGGLEBUTTON(wxID_CLEAR, EffectRack::OnBypass)
70 
71  EVT_COMMAND_RANGE(ID_REMOVE, ID_REMOVE + 99, wxEVT_COMMAND_BUTTON_CLICKED, EffectRack::OnRemove)
72  EVT_COMMAND_RANGE(ID_POWER, ID_POWER + 99, wxEVT_COMMAND_BUTTON_CLICKED, EffectRack::OnPower)
73  EVT_COMMAND_RANGE(ID_EDITOR, ID_EDITOR + 99, wxEVT_COMMAND_BUTTON_CLICKED, EffectRack::OnEditor)
74  EVT_COMMAND_RANGE(ID_UP, ID_UP + 99, wxEVT_COMMAND_BUTTON_CLICKED, EffectRack::OnUp)
75  EVT_COMMAND_RANGE(ID_DOWN, ID_DOWN + 99, wxEVT_COMMAND_BUTTON_CLICKED, EffectRack::OnDown)
76  EVT_COMMAND_RANGE(ID_FAV, ID_FAV + 99, wxEVT_COMMAND_BUTTON_CLICKED, EffectRack::OnFav)
78 
79 EffectRack::EffectRack()
80 : wxFrame(GetActiveProject(),
81  wxID_ANY,
82  _("Effects Rack"),
83  wxDefaultPosition,
84  wxDefaultSize,
85  wxSYSTEM_MENU |
86  wxCLOSE_BOX |
87  wxCAPTION |
88  wxFRAME_NO_TASKBAR |
89  wxFRAME_FLOAT_ON_PARENT)
90 {
91  mBypassing = false;
92  mNumEffects = 0;
93  mLastLatency = 0;
94  mTimer.SetOwner(this);
95 
96  mPowerPushed = CreateBitmap(power_on_16x16_xpm, false, false);
97  mPowerRaised = CreateBitmap(power_off_16x16_xpm, true, false);
98  mSettingsPushed = CreateBitmap(settings_up_16x16_xpm, false, true);
99  mSettingsRaised = CreateBitmap(settings_down_16x16_xpm, true, true);
100  mUpDisabled = CreateBitmap(up_9x16_xpm, true, true);
101  mUpPushed = CreateBitmap(up_9x16_xpm, false, true);
102  mUpRaised = CreateBitmap(up_9x16_xpm, true, true);
103  mDownDisabled = CreateBitmap(down_9x16_xpm, true, true);
104  mDownPushed = CreateBitmap(down_9x16_xpm, false, true);
105  mDownRaised = CreateBitmap(down_9x16_xpm, true, true);
106  mFavPushed = CreateBitmap(fav_down_16x16_xpm, false, false);
107  mFavRaised = CreateBitmap(fav_up_16x16_xpm, true, false);
108  mRemovePushed = CreateBitmap(remove_16x16_xpm, false, true);
109  mRemoveRaised = CreateBitmap(remove_16x16_xpm, true, true);
110 
111  {
112  auto bs = std::make_unique<wxBoxSizer>(wxVERTICAL);
113  mPanel = safenew wxPanelWrapper(this, wxID_ANY);
114  bs->Add(mPanel, 1, wxEXPAND);
115  SetSizer(bs.release());
116  }
117 
118  {
119  auto bs = std::make_unique<wxBoxSizer>(wxVERTICAL);
120  {
121  auto hs = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
122  wxASSERT(mPanel); // To justify safenew
123  hs->Add(safenew wxButton(mPanel, wxID_APPLY, _("&Apply")), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
124  hs->AddStretchSpacer();
125  mLatency = safenew wxStaticText(mPanel, wxID_ANY, _("Latency: 0"));
126  hs->Add(mLatency, 0, wxALIGN_CENTER);
127  hs->AddStretchSpacer();
128  hs->Add(safenew wxToggleButton(mPanel, wxID_CLEAR, _("&Bypass")), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL);
129  bs->Add(hs.release(), 0, wxEXPAND);
130  }
131  bs->Add(safenew wxStaticLine(mPanel, wxID_ANY), 0, wxEXPAND);
132 
133  {
134  auto uMainSizer = std::make_unique<wxFlexGridSizer>(7);
135  uMainSizer->AddGrowableCol(6);
136  uMainSizer->SetHGap(0);
137  uMainSizer->SetVGap(0);
138  bs->Add((mMainSizer = uMainSizer.release()), 1, wxEXPAND);
139  }
140 
141  mPanel->SetSizer(bs.release());
142  }
143 
144  wxString oldPath = gPrefs->GetPath();
145  gPrefs->SetPath(wxT("/EffectsRack"));
146  size_t cnt = gPrefs->GetNumberOfEntries();
147  gPrefs->SetPath(oldPath);
148 
150  for (size_t i = 0; i < cnt; i++)
151  {
152  wxString slot;
153  gPrefs->Read(wxString::Format(wxT("/EffectsRack/Slot%02d"), i), &slot);
154 
155  Effect *effect = em.GetEffect(slot.AfterFirst(wxT(',')));
156  if (effect)
157  {
158  Add(effect, slot.BeforeFirst(wxT(',')) == wxT("1"), true);
159  }
160  }
161 
162  Fit();
163 }
164 
165 EffectRack::~EffectRack()
166 {
167  gPrefs->DeleteGroup(wxT("/EffectsRack"));
168 
169  for (size_t i = 0, cnt = mEffects.size(); i < cnt; i++)
170  {
171  if (mFavState[i])
172  {
173  Effect *effect = mEffects[i];
174  gPrefs->Write(wxString::Format(wxT("/EffectsRack/Slot%02d"), i),
175  wxString::Format(wxT("%d,%s"),
176  mPowerState[i],
177  effect->GetID()));
178  }
179  }
180 }
181 
182 void EffectRack::Add(Effect *effect, bool active, bool favorite)
183 {
184  if (mEffects.end() != std::find(mEffects.begin(), mEffects.end(), effect))
185  {
186  return;
187  }
188 
189  wxBitmapButton *bb;
190 
191  wxASSERT(mPanel); // To justify safenew
192  bb = safenew wxBitmapButton(mPanel, ID_POWER + mNumEffects, mPowerRaised);
193  bb->SetBitmapSelected(mPowerRaised);
194  bb->SetName(_("Active State"));
195  bb->SetToolTip(_("Set effect active state"));
196  mPowerState.push_back(active);
197  if (active)
198  {
199  bb->SetBitmapLabel(mPowerPushed);
200  bb->SetBitmapSelected(mPowerPushed);
201  }
202  else
203  {
204  bb->SetBitmapLabel(mPowerRaised);
205  bb->SetBitmapSelected(mPowerRaised);
206  }
207  mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
208 
209  bb = safenew wxBitmapButton(mPanel, ID_EDITOR + mNumEffects, mSettingsRaised);
210  bb->SetBitmapSelected(mSettingsPushed);
211  bb->SetName(_("Show/Hide Editor"));
212  bb->SetToolTip(_("Open/close effect editor"));
213  mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
214 
215  bb = safenew wxBitmapButton(mPanel, ID_UP + mNumEffects, mUpRaised);
216  bb->SetBitmapSelected(mUpPushed);
217  bb->SetBitmapDisabled(mUpDisabled);
218  bb->SetName(_("Move Up"));
219  bb->SetToolTip(_("Move effect up in the rack"));
220  mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
221 
222  bb = safenew wxBitmapButton(mPanel, ID_DOWN + mNumEffects, mDownRaised);
223  bb->SetBitmapSelected(mDownPushed);
224  bb->SetBitmapDisabled(mDownDisabled);
225  bb->SetName(_("Move Down"));
226  bb->SetToolTip(_("Move effect down in the rack"));
227  mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
228 
229  bb = safenew wxBitmapButton(mPanel, ID_FAV + mNumEffects, mFavRaised);
230  bb->SetBitmapSelected(mFavPushed);
231  bb->SetName(_("Favorite"));
232  bb->SetToolTip(_("Mark effect as a favorite"));
233  mFavState.push_back(favorite);
234  if (favorite)
235  {
236  bb->SetBitmapLabel(mFavPushed);
237  bb->SetBitmapSelected(mFavPushed);
238  }
239  else
240  {
241  bb->SetBitmapLabel(mFavRaised);
242  bb->SetBitmapSelected(mFavRaised);
243  }
244  mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
245 
246  bb = safenew wxBitmapButton(mPanel, ID_REMOVE + mNumEffects, mRemoveRaised);
247  bb->SetBitmapSelected(mRemovePushed);
248  bb->SetName(_("Remove"));
249  bb->SetToolTip(_("Remove effect from the rack"));
250  mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
251 
252  wxStaticText *text = safenew wxStaticText(mPanel, ID_NAME + mNumEffects,
253  effect->GetTranslatedName() );
254  text->SetToolTip(_("Name of the effect"));
255  mMainSizer->Add(text, 0, wxEXPAND | wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 5);
256 
257  mMainSizer->Layout();
258  SetSize(GetMinSize());
259  Fit();
260  Update();
261 
262  mEffects.push_back(effect);
263  mNumEffects++;
264 
265  if (!mTimer.IsRunning())
266  {
267  mTimer.Start(1000);
268  }
269 
270  if (active)
271  {
272  UpdateActive();
273  }
274 }
275 
276 void EffectRack::OnClose(wxCloseEvent & evt)
277 {
278  Show(false);
279  evt.Veto();
280 }
281 
282 void EffectRack::OnTimer(wxTimerEvent & WXUNUSED(evt))
283 {
284  int latency = EffectManager::Get().GetRealtimeLatency();
285  if (latency != mLastLatency)
286  {
287  mLatency->SetLabel(wxString::Format(_("Latency: %4d"), latency));
288  mLatency->Refresh();
289  mLastLatency = latency;
290  }
291 }
292 
293 void EffectRack::OnApply(wxCommandEvent & WXUNUSED(evt))
294 {
295  AudacityProject *project = GetActiveProject();
296 
297  bool success = false;
298  auto state = project->GetUndoManager()->GetCurrentState();
299  auto cleanup = finally( [&] {
300  if(!success)
301  project->SetStateTo(state);
302  } );
303 
304  for (size_t i = 0, cnt = mEffects.size(); i < cnt; i++)
305  {
306  if (mPowerState[i])
307  {
308  if (!project->DoEffect(mEffects[i]->GetID(),
309  *project,
311  // If any effect fails (or throws), then stop.
312  return;
313  }
314  }
315 
316  success = true;
317 
318  // Only after all succeed, do the following.
319  for (size_t i = 0, cnt = mEffects.size(); i < cnt; i++)
320  {
321  if (mPowerState[i])
322  {
323  mPowerState[i] = false;
324 
325  wxBitmapButton *btn =
326  static_cast<wxBitmapButton *>(FindWindowById(ID_POWER + i));
327  btn->SetBitmapLabel(mPowerRaised);
328  btn->SetBitmapSelected(mPowerRaised);
329  }
330  }
331 
332  UpdateActive();
333 }
334 
335 void EffectRack::OnBypass(wxCommandEvent & evt)
336 {
337  mBypassing = evt.GetInt() != 0;
338  UpdateActive();
339 }
340 
341 void EffectRack::OnPower(wxCommandEvent & evt)
342 {
343  wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
344 
345  int index = GetEffectIndex(btn);
346  mPowerState[index] = !mPowerState[index];
347  if (mPowerState[index])
348  {
349  btn->SetBitmapLabel(mPowerPushed);
350  btn->SetBitmapSelected(mPowerPushed);
351  }
352  else
353  {
354  btn->SetBitmapLabel(mPowerRaised);
355  btn->SetBitmapSelected(mPowerRaised);
356  }
357 
358  UpdateActive();
359 }
360 
361 void EffectRack::OnEditor(wxCommandEvent & evt)
362 {
363  wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
364 
365  evt.Skip();
366 
367  int index = GetEffectIndex(btn);
368  if (index < 0)
369  {
370  return;
371  }
372 
373  mEffects[index]->PromptUser(GetParent());
374 }
375 
376 void EffectRack::OnUp(wxCommandEvent & evt)
377 {
378  wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
379 
380  evt.Skip();
381 
382  int index = GetEffectIndex(btn);
383  if (index <= 0)
384  {
385  return;
386  }
387 
388  MoveRowUp(index);
389 }
390 
391 void EffectRack::OnDown(wxCommandEvent & evt)
392 {
393  wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
394 
395  evt.Skip();
396 
397  size_t index = GetEffectIndex(btn);
398  if (index < 0 || index == (mMainSizer->GetChildren().GetCount() / NUMCOLS) - 1)
399  {
400  return;
401  }
402 
403  MoveRowUp(index + 1);
404 }
405 
406 void EffectRack::OnFav(wxCommandEvent & evt)
407 {
408  wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
409 
410  int index = GetEffectIndex(btn);
411  mFavState[index] = !mFavState[index];
412  if (mFavState[index])
413  {
414  btn->SetBitmapLabel(mFavPushed);
415  btn->SetBitmapSelected(mFavPushed);
416  }
417  else
418  {
419  btn->SetBitmapLabel(mFavRaised);
420  btn->SetBitmapSelected(mFavRaised);
421  }
422 }
423 
424 void EffectRack::OnRemove(wxCommandEvent & evt)
425 {
426  wxBitmapButton *btn = static_cast<wxBitmapButton *>(evt.GetEventObject());
427 
428  evt.Skip();
429 
430  int index = GetEffectIndex(btn);
431  if (index < 0)
432  {
433  return;
434  }
435 
436  mEffects.erase(mEffects.begin() + index);
437  mPowerState.erase(mPowerState.begin() + index);
438  mFavState.erase(mFavState.begin() + index);
439 
440  if (mEffects.size() == 0)
441  {
442  if (mTimer.IsRunning())
443  {
444  mTimer.Stop();
445  }
446  }
447 
448  index *= NUMCOLS;
449 
450  for (int i = 0; i < NUMCOLS; i++)
451  {
452  std::unique_ptr<wxWindow> w {mMainSizer->GetItem(index)->GetWindow()};
453  mMainSizer->Detach(index);
454  }
455 
456  mMainSizer->Layout();
457  Fit();
458 
459  UpdateActive();
460 }
461 
462 wxBitmap EffectRack::CreateBitmap(const char *xpm[], bool up, bool pusher)
463 {
464  wxMemoryDC dc;
465  wxBitmap pic(xpm);
466 
467  wxBitmap mod(pic.GetWidth() + 6, pic.GetHeight() + 6);
468  dc.SelectObject(mod);
469 #if defined( __WXGTK__ )
470  wxColour newColour = wxSystemSettings::GetColour( wxSYS_COLOUR_BACKGROUND );
471 #else
472  wxColour newColour = wxSystemSettings::GetColour( wxSYS_COLOUR_3DFACE );
473 #endif
474  dc.SetBackground(wxBrush(newColour));
475  dc.Clear();
476 
477  int offset = 3;
478  if (pusher)
479  {
480  if (!up)
481  {
482  offset += 1;
483  }
484  }
485  dc.DrawBitmap(pic, offset, offset, true);
486 
487  dc.SelectObject(wxNullBitmap);
488 
489  return mod;
490 }
491 
492 int EffectRack::GetEffectIndex(wxWindow *win)
493 {
494  int col = (win->GetId() - ID_BASE) / ID_RANGE;
495  int row;
496  int cnt = mMainSizer->GetChildren().GetCount() / NUMCOLS;
497  for (row = 0; row < cnt; row++)
498  {
499  wxSizerItem *si = mMainSizer->GetItem((row * NUMCOLS) + col);
500  if (si->GetWindow() == win)
501  {
502  break;
503  }
504  }
505 
506  if (row == cnt)
507  {
508  return -1;
509  }
510 
511  return row;
512 }
513 
514 void EffectRack::MoveRowUp(int row)
515 {
516  Effect *effect = mEffects[row];
517  mEffects.erase(mEffects.begin() + row);
518  mEffects.insert(mEffects.begin() + row - 1, effect);
519 
520  int state = mPowerState[row];
521  mPowerState.erase(mPowerState.begin() + row);
522  mPowerState.insert(mPowerState.begin() + row - 1, state);
523 
524  state = mFavState[row];
525  mFavState.erase(mFavState.begin() + row);
526  mFavState.insert(mFavState.begin() + row - 1, state);
527 
528  row *= NUMCOLS;
529 
530  for (int i = 0; i < NUMCOLS; i++)
531  {
532  wxSizerItem *si = mMainSizer->GetItem(row + NUMCOLS - 1);
533  wxWindow *w = si->GetWindow();
534  int flags = si->GetFlag();
535  int border = si->GetBorder();
536  int prop = si->GetProportion();
537  mMainSizer->Detach(row + NUMCOLS - 1);
538  mMainSizer->Insert(row - NUMCOLS, w, prop, flags, border);
539  }
540 
541  mMainSizer->Layout();
542  Refresh();
543 
544  UpdateActive();
545 }
546 
547 void EffectRack::UpdateActive()
548 {
549  mActive.clear();
550 
551  if (!mBypassing)
552  {
553  for (size_t i = 0, cnt = mEffects.size(); i < cnt; i++)
554  {
555  if (mPowerState[i])
556  {
557  mActive.push_back(mEffects[i]);
558  }
559  }
560  }
561 
563 }
564 
565 #endif
EVT_COMMAND_RANGE(ID_Slider, ID_Slider+NUMBER_OF_BANDS-1, wxEVT_COMMAND_SLIDER_UPDATED, EffectEqualization::OnSlider) EffectEqualization
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
static const int kConfigured
Definition: Project.h:489
unsigned int GetCurrentState()
bool DoEffect(const PluginID &ID, const CommandContext &context, int flags)
Definition: Menus.cpp:4512
virtual PluginID GetID()
Definition: Effect.cpp:959
Base class for many of the effects in Audacity.
Definition: Effect.h:62
#define safenew
Definition: Audacity.h:230
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
void OnClose(const CommandContext &context)
void RealtimeSetEffects(const EffectArray &mActive)
Effect * GetEffect(const PluginID &ID)
EffectManager is the class that handles effects and effect categories.
Definition: EffectManager.h:45
static EffectManager & Get()
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const wxString & GetTranslatedName()
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
UndoManager * GetUndoManager()
Definition: Project.h:195
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
END_EVENT_TABLE()
int GetRealtimeLatency()
void SetStateTo(unsigned int n)
Definition: Project.cpp:4807