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