Audacity  2.2.2
MixerBoard.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  MixerBoard.cpp
6 
7  Vaughan Johnson, January 2007
8  Dominic Mazzoni
9 
10 **********************************************************************/
11 
12 #include "Audacity.h"
13 
14 #include <cfloat>
15 #include <math.h>
16 
17 #include <wx/dcmemory.h>
18 #include <wx/arrimpl.cpp>
19 #include <wx/icon.h>
20 #include <wx/settings.h> // for wxSystemSettings::GetColour and wxSystemSettings::GetMetric
21 
22 #include "Theme.h"
23 #include "Experimental.h"
24 #include "MixerBoard.h"
25 #include "AColor.h"
26 #include "AllThemeResources.h"
27 #include "AudioIO.h"
28 
29 #ifdef USE_MIDI
30 #include "NoteTrack.h"
31 #endif
32 
33 #include "Project.h"
34 #include "TrackPanel.h" // for EVT_TRACK_PANEL_TIMER
35 #include "UndoManager.h"
36 #include "WaveTrack.h"
37 
38 #include "widgets/Meter.h"
39 
40 
41 #include "../images/MusicalInstruments.h"
42 #ifdef __WXMSW__
43  #include "../images/AudacityLogo.xpm"
44 #else
45  #include "../images/AudacityLogo48x48.xpm"
46 #endif
47 
48 
49 // class MixerTrackSlider
50 
51 BEGIN_EVENT_TABLE(MixerTrackSlider, ASlider)
52  EVT_MOUSE_EVENTS(MixerTrackSlider::OnMouseEvent)
53 
54  EVT_SET_FOCUS(MixerTrackSlider::OnFocus)
55  EVT_KILL_FOCUS(MixerTrackSlider::OnFocus)
56  EVT_COMMAND(wxID_ANY, EVT_CAPTURE_KEY, MixerTrackSlider::OnCaptureKey)
57 
59 
61  wxWindowID id,
62  const wxString &name,
63  const wxPoint & pos,
64  const wxSize & size,
65  int style /*= FRAC_SLIDER*/,
66  bool popup /*= true*/,
67  bool canUseShift /*= true*/,
68  float stepValue /*= STEP_CONTINUOUS*/,
69  int orientation /*= wxHORIZONTAL*/)
70 : ASlider(parent, id, name, pos, size,
71  style, popup, canUseShift, stepValue, orientation)
72 {
73 }
74 
75 void MixerTrackSlider::OnMouseEvent(wxMouseEvent &event)
76 {
77  ASlider::OnMouseEvent(event);
78 
79  if (event.ButtonUp())
80  {
81  MixerTrackCluster* pMixerTrackCluster = (MixerTrackCluster*)(this->GetParent());
82  switch (mStyle)
83  {
84  case DB_SLIDER: pMixerTrackCluster->HandleSliderGain(true); break;
85  case PAN_SLIDER: pMixerTrackCluster->HandleSliderPan(true); break;
86  default: break; // no-op
87  }
88  }
89 }
90 
91 void MixerTrackSlider::OnFocus(wxFocusEvent &event)
92 {
93  if (event.GetEventType() == wxEVT_KILL_FOCUS) {
95  }
96  else {
98  }
99 
100  Refresh(false);
101 
102  event.Skip();
103 }
104 
105 void MixerTrackSlider::OnCaptureKey(wxCommandEvent &event)
106 {
107  wxKeyEvent *kevent = (wxKeyEvent *)event.GetEventObject();
108  int keyCode = kevent->GetKeyCode();
109 
110  // Pass LEFT/RIGHT/UP/DOWN/PAGEUP/PAGEDOWN through for input/output sliders
111  if (keyCode == WXK_LEFT || keyCode == WXK_RIGHT ||
112  keyCode == WXK_UP || keyCode == WXK_DOWN ||
113  keyCode == WXK_PAGEUP || keyCode == WXK_PAGEDOWN) {
114  return;
115  }
116 
117  event.Skip();
118 
119  return;
120 }
121 
122 
123 
124 // class MixerTrackCluster
125 
126 #define kInset 4
127 #define kDoubleInset (2 * kInset)
128 #define kTripleInset (3 * kInset)
129 #define kQuadrupleInset (4 * kInset)
130 
131 #define TRACK_NAME_HEIGHT 18
132 #define MUSICAL_INSTRUMENT_HEIGHT_AND_WIDTH 48
133 #define MUTE_SOLO_HEIGHT 19
134 #define PAN_HEIGHT 24
135 
136 #define kLeftSideStackWidth MUSICAL_INSTRUMENT_HEIGHT_AND_WIDTH - kDoubleInset //vvv Change when numbers shown on slider scale.
137 #define kRightSideStackWidth MUSICAL_INSTRUMENT_HEIGHT_AND_WIDTH + kDoubleInset
138 #define kMixerTrackClusterWidth kLeftSideStackWidth + kRightSideStackWidth + kQuadrupleInset // kDoubleInset margin on both sides
139 
140 enum {
144 #ifdef EXPERIMENTAL_MIDI_OUT
145  ID_SLIDER_VELOCITY,
146 #endif
149 };
150 
151 BEGIN_EVENT_TABLE(MixerTrackCluster, wxPanelWrapper)
152  EVT_MOUSE_EVENTS(MixerTrackCluster::OnMouseEvent)
153  EVT_PAINT(MixerTrackCluster::OnPaint)
154 
156  EVT_SLIDER(ID_SLIDER_PAN, MixerTrackCluster::OnSlider_Pan)
157  EVT_SLIDER(ID_SLIDER_GAIN, MixerTrackCluster::OnSlider_Gain)
158 #ifdef EXPERIMENTAL_MIDI_OUT
159  EVT_SLIDER(ID_SLIDER_VELOCITY, MixerTrackCluster::OnSlider_Velocity)
160 #endif
161  //v EVT_COMMAND_SCROLL(ID_SLIDER_GAIN, MixerTrackCluster::OnSliderScroll_Gain)
163  EVT_COMMAND(ID_TOGGLEBUTTON_SOLO, wxEVT_COMMAND_BUTTON_CLICKED, MixerTrackCluster::OnButton_Solo)
165 
167  MixerBoard* grandParent, AudacityProject* project,
168  const std::shared_ptr<PlayableTrack> &pTrack,
169  const wxPoint& pos /*= wxDefaultPosition*/,
170  const wxSize& size /*= wxDefaultSize*/)
171 : wxPanelWrapper(parent, -1, pos, size)
172 , mTrack{ pTrack }
173 {
174  mMixerBoard = grandParent;
175  mProject = project;
176  wxASSERT( pTrack );
177 
178  SetName(mTrack->GetName());
179 
180  //this->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
181  this->SetBackgroundColour( theTheme.Colour( clrMedium ) );
182  // Not sure why, but sizers weren't getting offset vertically,
183  // probably because not using wxDefaultPosition,
184  // so positions are calculated explicitly below, and sizers code was removed.
185  // (Still available in Audacity_UmixIt branch off 1.2.6.)
186 
187  // track name
188  wxPoint ctrlPos(kDoubleInset, kDoubleInset);
189  wxSize ctrlSize(size.GetWidth() - kQuadrupleInset, TRACK_NAME_HEIGHT);
190  mStaticText_TrackName =
191  safenew auStaticText(this, mTrack->GetName());
192  //v Useful when different tracks are different colors, but not now.
193  // mStaticText_TrackName->SetBackgroundColour(this->GetTrackColor());
194  mStaticText_TrackName->SetForegroundColour(theTheme.Colour(clrMedium));
195  mStaticText_TrackName->SetForegroundColour(theTheme.Colour(clrTrackPanelText));
196  mStaticText_TrackName->SetSize( ctrlSize );
197  mStaticText_TrackName->SetPosition( ctrlPos );
198 
199 
200  // gain and velocity sliders at left (both in same place)
201  ctrlPos.x = kDoubleInset;
202  ctrlPos.y += TRACK_NAME_HEIGHT + kDoubleInset;
203  const int nGainSliderHeight =
204  size.GetHeight() - ctrlPos.y - kQuadrupleInset;
205  ctrlSize.Set(kLeftSideStackWidth - kQuadrupleInset, nGainSliderHeight);
206 
207  mSlider_Gain =
209  this, ID_SLIDER_GAIN,
210  /* i18n-hint: title of the Gain slider, used to adjust the volume */
211  _("Gain"),
212  ctrlPos, ctrlSize, DB_SLIDER, true,
213  true, 0.0, wxVERTICAL);
214  mSlider_Gain->SetName(_("Gain"));
215  this->UpdateGain();
216 #ifdef EXPERIMENTAL_MIDI_OUT
217  mSlider_Velocity =
219  this, ID_SLIDER_VELOCITY,
220  /* i18n-hint: title of the MIDI Velocity slider */
221  _("Velocity"),
222  ctrlPos, ctrlSize, VEL_SLIDER, true,
223  true, 0.0, wxVERTICAL);
224  mSlider_Velocity->SetName(_("Velocity"));
225  this->UpdateVelocity();
226 #endif
227 
228  // other controls and meter at right
229 
230  // musical instrument image
231  ctrlPos.x += kLeftSideStackWidth + kInset; // + kInset to center it in right side stack
233  wxBitmap* bitmap = mMixerBoard->GetMusicalInstrumentBitmap(mTrack.get());
234  wxASSERT(bitmap);
235  mBitmapButton_MusicalInstrument =
236  safenew wxBitmapButton(this, ID_BITMAPBUTTON_MUSICAL_INSTRUMENT, *bitmap,
237  ctrlPos, ctrlSize,
238  wxBU_AUTODRAW, wxDefaultValidator,
239  _("Musical Instrument"));
240  mBitmapButton_MusicalInstrument->SetName(_("Musical Instrument"));
241 
242 
243  // pan slider
244  ctrlPos.x -= kInset; // Remove inset for instrument, so Pan is at leftmost of left side stack.
246  ctrlSize.Set(kRightSideStackWidth, PAN_HEIGHT);
247 
248  // The width of the pan slider must be odd (don't ask).
249  if (!(ctrlSize.x & 1))
250  ctrlSize.x--;
251 
252  mSlider_Pan =
254  this, ID_SLIDER_PAN,
255  /* i18n-hint: Title of the Pan slider, used to move the sound left or right */
256  _("Pan"),
257  ctrlPos, ctrlSize, PAN_SLIDER, true);
258  mSlider_Pan->SetName(_("Pan"));
259 
260  this->UpdatePan();
261 
262  // mute/solo buttons stacked below Pan slider
263  ctrlPos.y += PAN_HEIGHT + kDoubleInset;
264  ctrlSize.Set(mMixerBoard->mMuteSoloWidth, MUTE_SOLO_HEIGHT);
265  mToggleButton_Mute =
267  ctrlPos, ctrlSize,
268  *(mMixerBoard->mImageMuteUp), *(mMixerBoard->mImageMuteOver),
269  *(mMixerBoard->mImageMuteDown), *(mMixerBoard->mImageMuteDown),
270  *(mMixerBoard->mImageMuteDisabled),
271  true); // toggle button
272  mToggleButton_Mute->SetName(_("Mute"));
273  mToggleButton_Mute->SetAlternateImages(
274  1,
275  *(mMixerBoard->mImageMuteUp), *(mMixerBoard->mImageMuteOver),
276  *(mMixerBoard->mImageMuteDown), *(mMixerBoard->mImageMuteDown),
277  *(mMixerBoard->mImageMuteDisabled));
278  this->UpdateMute();
279 
280  ctrlPos.y += MUTE_SOLO_HEIGHT;
281  mToggleButton_Solo =
283  ctrlPos, ctrlSize,
284  *(mMixerBoard->mImageSoloUp), *(mMixerBoard->mImageSoloOver),
285  *(mMixerBoard->mImageSoloDown), *(mMixerBoard->mImageSoloDown),
286  *(mMixerBoard->mImageSoloDisabled),
287  true); // toggle button
288  mToggleButton_Solo->SetName(_("Solo"));
289  this->UpdateSolo();
290  bool bSoloNone = mProject->IsSoloNone();
291  mToggleButton_Solo->Show(!bSoloNone);
292 
293 
294  // meter
295  ctrlPos.y += (bSoloNone ? 0 : MUTE_SOLO_HEIGHT) + kDoubleInset;
296  const int nMeterHeight =
297  nGainSliderHeight -
299  (PAN_HEIGHT + kDoubleInset) -
300  (MUTE_SOLO_HEIGHT + (bSoloNone ? 0 : MUTE_SOLO_HEIGHT) + kDoubleInset);
301  ctrlSize.Set(kRightSideStackWidth, nMeterHeight);
302 
303  mMeter = NULL;
304  if (GetWave()) {
305  mMeter =
306  safenew Meter(GetActiveProject(), // AudacityProject* project,
307  this, -1, // wxWindow* parent, wxWindowID id,
308  false, // bool isInput
309  ctrlPos, ctrlSize, // const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize,
310  Meter::MixerTrackCluster); // Style style = HorizontalStereo,
311  mMeter->SetName(_("Signal Level Meter"));
312  }
313 
314  #if wxUSE_TOOLTIPS
315  mStaticText_TrackName->SetToolTip(mTrack->GetName());
316  mToggleButton_Mute->SetToolTip(_("Mute"));
317  mToggleButton_Solo->SetToolTip(_("Solo"));
318  if (GetWave())
319  mMeter->SetToolTip(_("Signal Level Meter"));
320  #endif // wxUSE_TOOLTIPS
321 
322  #ifdef __WXMAC__
323  wxSizeEvent event(GetSize(), GetId());
324  event.SetEventObject(this);
325  GetEventHandler()->ProcessEvent(event);
326  UpdateGain();
327  #endif
328 }
329 
331 {
332  return dynamic_cast< WaveTrack * >( mTrack.get() );
333 }
334 
336 {
337  auto left = GetWave();
338  if (left)
339  return static_cast<WaveTrack*>(left->GetLink());
340  else
341  return nullptr;
342 }
343 
344 #ifdef EXPERIMENTAL_MIDI_OUT
345 NoteTrack *MixerTrackCluster::GetNote() const
346 {
347  return dynamic_cast< NoteTrack * >( mTrack.get() );
348 }
349 #endif
350 
352 {
353  this->SetBackgroundColour( theTheme.Colour( clrMedium ) );
354  mStaticText_TrackName->SetForegroundColour(theTheme.Colour(clrTrackPanelText));
355  if (mMeter)
356  mMeter->UpdatePrefs(); // in case meter range has changed
357  HandleResize(); // in case prefs "/GUI/Solo" changed
358 }
359 
360 void MixerTrackCluster::HandleResize() // For wxSizeEvents, update gain slider and meter.
361 {
362  wxSize scrolledWindowClientSize = this->GetParent()->GetClientSize();
363  const int newClusterHeight =
364  scrolledWindowClientSize.GetHeight() - kDoubleInset - // nClusterHeight from MixerBoard::UpdateTrackClusters
365  wxSystemSettings::GetMetric(wxSYS_HSCROLL_Y) + // wxScrolledWindow::GetClientSize doesn't account for its scrollbar size.
366  kDoubleInset;
367 
368  this->SetSize(-1, newClusterHeight);
369 
370  // Change only the heights of mSlider_Gain and mMeter.
371  // But update shown status of mToggleButton_Solo, which affects top of mMeter.
372  const int nGainSliderHeight =
373  newClusterHeight -
374  (kInset + // margin above mStaticText_TrackName
375  TRACK_NAME_HEIGHT + kDoubleInset) - // mStaticText_TrackName + margin
376  kQuadrupleInset; // margin below gain slider
377  mSlider_Gain->SetSize(-1, nGainSliderHeight);
378 #ifdef EXPERIMENTAL_MIDI_OUT
379  mSlider_Velocity->SetSize(-1, nGainSliderHeight);
380 #endif
381 
382  bool bSoloNone = mProject->IsSoloNone();
383 
384  mToggleButton_Solo->Show(!bSoloNone);
385 
386  const int nRequiredHeightAboveMeter =
387  MUSICAL_INSTRUMENT_HEIGHT_AND_WIDTH + kDoubleInset +
388  PAN_HEIGHT + kDoubleInset +
389  MUTE_SOLO_HEIGHT + (bSoloNone ? 0 : MUTE_SOLO_HEIGHT) + kDoubleInset;
390  const int nMeterY =
391  kDoubleInset + // margin at top
392  TRACK_NAME_HEIGHT + kDoubleInset +
393  nRequiredHeightAboveMeter;
394  const int nMeterHeight = nGainSliderHeight - nRequiredHeightAboveMeter;
395  if (mMeter)
396  mMeter->SetSize(-1, nMeterY, -1, nMeterHeight);
397 }
398 
399 void MixerTrackCluster::HandleSliderGain(const bool bWantPushState /*= false*/)
400 {
401  float fValue = mSlider_Gain->Get();
402  if (GetWave())
403  GetWave()->SetGain(fValue);
404  if (GetRight())
405  GetRight()->SetGain(fValue);
406 
407  // Update the TrackPanel correspondingly.
409  if (bWantPushState)
410  mProject->TP_PushState(_("Moved gain slider"), _("Gain"), UndoPush::CONSOLIDATE );
411 }
412 
413 #ifdef EXPERIMENTAL_MIDI_OUT
414 void MixerTrackCluster::HandleSliderVelocity(const bool bWantPushState /*= false*/)
415 {
416  float fValue = mSlider_Velocity->Get();
417  if (GetNote())
418  GetNote()->SetVelocity(fValue);
419 
420  // Update the TrackPanel correspondingly.
422  if (bWantPushState)
423  mProject->TP_PushState(_("Moved velocity slider"), _("Velocity"), UndoPush::CONSOLIDATE);
424 }
425 #endif
426 
427 void MixerTrackCluster::HandleSliderPan(const bool bWantPushState /*= false*/)
428 {
429  float fValue = mSlider_Pan->Get();
430  if (GetWave()) // test in case track is a NoteTrack
431  GetWave()->SetPan(fValue);
432  if (GetRight())
433  GetRight()->SetPan(fValue);
434 
435  // Update the TrackPanel correspondingly.
437 
438  if (bWantPushState)
439  mProject->TP_PushState(_("Moved pan slider"), _("Pan"), UndoPush::CONSOLIDATE );
440 }
441 
442 void MixerTrackCluster::ResetMeter(const bool bResetClipping)
443 {
444  if (mMeter)
445  mMeter->Reset(GetWave()->GetRate(), bResetClipping);
446 }
447 
448 
449 // These are used by TrackPanel for synchronizing control states, etc.
450 
451 // Update the controls that can be affected by state change.
453 {
454  this->UpdateName();
455  this->UpdatePan();
456  this->UpdateGain();
457 }
458 
460 {
461  const wxString newName = mTrack->GetName();
462  SetName(newName);
463  mStaticText_TrackName->SetLabel(newName);
464  mStaticText_TrackName->SetName(newName);
465  #if wxUSE_TOOLTIPS
466  mStaticText_TrackName->SetToolTip(newName);
467  #endif
468  mBitmapButton_MusicalInstrument->SetBitmapLabel(
470 }
471 
473 {
474  mToggleButton_Mute->SetAlternateIdx(mTrack->GetSolo() ? 1 : 0);
475  if (mTrack->GetMute())
477  else
479 }
480 
482 {
483  bool bIsSolo = mTrack->GetSolo();
484  if (bIsSolo)
486  else
488  mToggleButton_Mute->SetAlternateIdx(bIsSolo ? 1 : 0);
489 }
490 
492 {
493  if (!GetWave()) {
494  mSlider_Pan->Hide();
495  return;
496  }
497  mSlider_Pan->Set(GetWave()->GetPan());
498 }
499 
501 {
502  if (!GetWave()) {
503  mSlider_Gain->Hide();
504  return;
505  }
506  mSlider_Gain->Set(GetWave()->GetGain());
507 }
508 
509 #ifdef EXPERIMENTAL_MIDI_OUT
510 void MixerTrackCluster::UpdateVelocity()
511 {
512  if (!GetNote()) {
513  mSlider_Velocity->Hide();
514  return;
515  }
516  mSlider_Velocity->Set(GetNote()->GetVelocity());
517 }
518 #endif
519 
520 void MixerTrackCluster::UpdateMeter(const double t0, const double t1)
521 {
522  // NoteTracks do not (currently) register on meters. It would probably be
523  // a good idea to display 16 channel "active" lights rather than a meter
524  if (!GetWave())
525  return;
526 
527  if ((t0 < 0.0) || (t1 < 0.0) || (t0 >= t1) || // bad time value or nothing to show
528  ((mMixerBoard->HasSolo() || mTrack->GetMute()) && !mTrack->GetSolo())
529  )
530  {
531  //v Vaughan, 2011-02-25: Moved the update back to TrackPanel::OnTimer() as it helps with
532  // playback issues reported by Bill and noted on Bug 258, so no assert.
533  // Vaughan, 2011-02-04: Now that we're updating all meters from audacityAudioCallback,
534  // this causes an assert if you click Mute while playing, because ResetMeter() resets
535  // the timer, and wxTimerbase says that can only be done from main thread --
536  // but it seems to work fine.
537  this->ResetMeter(false);
538  return;
539  }
540 
541  // Vaughan, 2010-11-27:
542  // This commented out code is flawed. Mistaken understanding of "frame" vs "window".
543  // Caused me to override Meter::UpdateDisplay().
544  // But I think it's got a good idea, of calling WaveTracks' GetMinMax and GetRMS
545  // instead of passing in all the data and asking the meter to derive peak and rms.
546  // May be worth revisiting as I think it should perform better, because it uses the min/max/rms
547  // stored in blockfiles, rather than calculating them, but for now, changing it to use the
548  // original Meter::UpdateDisplay(). New code is below the previous (now commented out).
549  //
550  //const size_t kFramesPerBuffer = 4;
551  //float min; // dummy, since it's not shown in meters
552  //Floats maxLeft{kFramesPerBuffer};
553  //Floats rmsLeft{kFramesPerBuffer};
554  //Floats maxRight{kFramesPerBuffer};
555  //Floats rmsRight{kFramesPerBuffer};
556  //
557  //#ifdef EXPERIMENTAL_MIDI_OUT
558  // bool bSuccess = (GetWave() != nullptr);
559  //#else
560  // bool bSuccess = true;
561  //#endif
562 
563  //const double dFrameInterval = (t1 - t0) / (double)kFramesPerBuffer;
564  //double dFrameT0 = t0;
565  //double dFrameT1 = t0 + dFrameInterval;
566  //int i = 0;
567  //while (bSuccess && (i < kFramesPerBuffer))
568  //{
569  // bSuccess &=
570  // mTrack->GetMinMax(&min, &(maxLeft[i]), dFrameT0, dFrameT1) &&
571  // mTrack->GetRMS(&(rmsLeft[i]), dFrameT0, dFrameT1);
572  // if (bSuccess && mRightTrack)
573  // bSuccess &=
574  // mRightTrack->GetMinMax(&min, &(maxRight[i]), dFrameT0, dFrameT1) &&
575  // mRightTrack->GetRMS(&(rmsRight[i]), dFrameT0, dFrameT1);
576  // else
577  // {
578  // // Mono: Start with raw values same as left.
579  // // To be modified by bWantPostFadeValues and channel pan/gain.
580  // maxRight[i] = maxLeft[i];
581  // rmsRight[i] = rmsLeft[i];
582  // }
583  // dFrameT0 += dFrameInterval;
584  // dFrameT1 += dFrameInterval;
585  // i++;
586  //}
587  //
588  //const bool bWantPostFadeValues = true; //v Turn this into a checkbox on MixerBoard? For now, always true.
589  //if (bSuccess && bWantPostFadeValues)
590  //if (bSuccess)
591  //{
592  // for (i = 0; i < kFramesPerBuffer; i++)
593  // {
594  // float gain = mTrack->GetChannelGain(0);
595  // maxLeft[i] *= gain;
596  // rmsLeft[i] *= gain;
597  // if (mRightTrack)
598  // gain = mRightTrack->GetChannelGain(1);
599  // maxRight[i] *= gain;
600  // rmsRight[i] *= gain;
601  // }
602  // if ( mMeter ) mMeter->UpdateDisplay(
603  // 2, // If mono, show left track values in both meters, as in MeterToolBar, rather than kNumChannels.
604  // kFramesPerBuffer,
605  // maxLeft, rmsLeft,
606  // maxRight, rmsRight,
607  // mTrack->TimeToLongSamples(t1 - t0));
608  //}
609  //
610 
611  const auto pTrack = GetWave();
612  auto startSample = (sampleCount)((pTrack->GetRate() * t0) + 0.5);
613  auto scnFrames = (sampleCount)((pTrack->GetRate() * (t1 - t0)) + 0.5);
614 
615  // Expect that the difference of t1 and t0 is the part of a track played
616  // in about 1/20 second (ticks of TrackPanel timer), so this won't overflow
617  auto nFrames = scnFrames.as_size_t();
618 
619  Floats tempFloatsArray{ nFrames };
620  decltype(tempFloatsArray) meterFloatsArray;
621  // Don't throw on read error in this drawing update routine
622  bool bSuccess = pTrack->Get((samplePtr)tempFloatsArray.get(),
623  floatSample, startSample, nFrames, fillZero, false);
624  if (bSuccess)
625  {
626  // We always pass a stereo sample array to the meter, as it shows 2 channels.
627  // Mono shows same in both meters.
628  // Since we're not mixing, need to duplicate same signal for "right" channel in mono case.
629  meterFloatsArray = Floats{ 2 * nFrames };
630 
631  // Interleave for stereo. Left/mono first.
632  for (unsigned int index = 0; index < nFrames; index++)
633  meterFloatsArray[2 * index] = tempFloatsArray[index];
634 
635  if (GetRight())
636  // Again, don't throw
637  bSuccess = GetRight()->Get((samplePtr)tempFloatsArray.get(),
638  floatSample, startSample, nFrames, fillZero, false);
639 
640  if (bSuccess)
641  // Interleave right channel, or duplicate same signal for "right" channel in mono case.
642  for (unsigned int index = 0; index < nFrames; index++)
643  meterFloatsArray[(2 * index) + 1] = tempFloatsArray[index];
644  }
645 
646  //const bool bWantPostFadeValues = true; //v Turn this into a checkbox on MixerBoard? For now, always true.
647  //if (bSuccess && bWantPostFadeValues)
648  if (bSuccess)
649  {
650  //vvv Need to apply envelope, too? See Mixer::MixSameRate.
651  float gain = pTrack->GetChannelGain(0);
652  if (gain < 1.0)
653  for (unsigned int index = 0; index < nFrames; index++)
654  meterFloatsArray[2 * index] *= gain;
655  if (GetRight())
656  gain = GetRight()->GetChannelGain(1);
657  else
658  gain = pTrack->GetChannelGain(1);
659  if (gain < 1.0)
660  for (unsigned int index = 0; index < nFrames; index++)
661  meterFloatsArray[(2 * index) + 1] *= gain;
662  // Clip to [-1.0, 1.0] range.
663  for (unsigned int index = 0; index < 2 * nFrames; index++)
664  if (meterFloatsArray[index] < -1.0)
665  meterFloatsArray[index] = -1.0;
666  else if (meterFloatsArray[index] > 1.0)
667  meterFloatsArray[index] = 1.0;
668 
669  mMeter->UpdateDisplay(2, nFrames, meterFloatsArray.get());
670  }
671  else
672  this->ResetMeter(false);
673 }
674 
675 // private
676 
678 {
679  return wxColour(102, 255, 102); // same as Meter playback color
680 }
681 
682 
683 // event handlers
684 
685 void MixerTrackCluster::HandleSelect(bool bShiftDown, bool bControlDown)
686 {
687  mProject->HandleListSelection( mTrack.get(), bShiftDown, bControlDown, true
688  );
689 }
690 
691 void MixerTrackCluster::OnMouseEvent(wxMouseEvent& event)
692 {
693  if (event.ButtonUp())
694  this->HandleSelect(event.ShiftDown(), event.ControlDown());
695  else
696  event.Skip();
697 }
698 
699 void MixerTrackCluster::OnPaint(wxPaintEvent & WXUNUSED(event))
700 {
701  wxPaintDC dc(this);
702 
703  #ifdef __WXMAC__
704  // Fill with correct color, not scroller background. Done automatically on Windows.
705  AColor::Medium(&dc, false);
706  dc.DrawRectangle(this->GetClientRect());
707  #endif
708 
709  wxSize clusterSize = this->GetSize();
710  wxRect bev(0, 0, clusterSize.GetWidth() - 1, clusterSize.GetHeight() - 1);
711 
712  auto selected = mTrack->GetSelected();
713 
714  for (unsigned int i = 0; i < 4; i++) // 4 gives a big bevel, but there were complaints about visibility otherwise.
715  {
716  bev.Inflate(-1, -1);
717  AColor::Bevel(dc, !selected, bev);
718  }
719 }
720 
721 
722 void MixerTrackCluster::OnButton_MusicalInstrument(wxCommandEvent& WXUNUSED(event))
723 {
724  const auto &state = ::wxGetMouseState();
725  this->HandleSelect(state.ShiftDown(), state.ControlDown());
726 }
727 
728 void MixerTrackCluster::OnSlider_Gain(wxCommandEvent& WXUNUSED(event))
729 {
730  this->HandleSliderGain();
731 }
732 
733 #ifdef EXPERIMENTAL_MIDI_OUT
734 void MixerTrackCluster::OnSlider_Velocity(wxCommandEvent& WXUNUSED(event))
735 {
736  this->HandleSliderVelocity();
737 }
738 #endif
739 
740 //v void MixerTrackCluster::OnSliderScroll_Gain(wxScrollEvent& WXUNUSED(event))
741 //{
742  //int sliderValue = (int)(mSlider_Gain->Get()); //v mSlider_Gain->GetValue();
743  //#ifdef __WXMSW__
744  // // Negate because wxSlider on Windows has min at top, max at bottom.
745  // // mSlider_Gain->GetValue() is in [-6,36]. wxSlider has min at top, so this is [-36dB,6dB].
746  // sliderValue = -sliderValue;
747  //#endif
748  //wxString str = _("Gain: ");
749  //if (sliderValue > 0)
750  // str += "+";
751  //str += wxString::Format("%d dB", sliderValue);
752  //mSlider_Gain->SetToolTip(str);
753 //}
754 
755 void MixerTrackCluster::OnSlider_Pan(wxCommandEvent& WXUNUSED(event))
756 {
757  this->HandleSliderPan();
758 }
759 
760 void MixerTrackCluster::OnButton_Mute(wxCommandEvent& WXUNUSED(event))
761 {
763  mToggleButton_Mute->SetAlternateIdx(mTrack->GetSolo() ? 1 : 0);
764 
765  // Update the TrackPanel correspondingly.
766  if (mProject->IsSoloSimple())
767  {
768  // Have to refresh all tracks.
771  }
772  else
773  // Update only the changed track.
775 }
776 
777 void MixerTrackCluster::OnButton_Solo(wxCommandEvent& WXUNUSED(event))
778 {
780  bool bIsSolo = mTrack->GetSolo();
781  mToggleButton_Mute->SetAlternateIdx(bIsSolo ? 1 : 0);
782 
783  // Update the TrackPanel correspondingly.
784  if (mProject->IsSoloSimple())
785  {
786  // Have to refresh all tracks.
789  }
790  // Bug 509: Must repaint all, as many tracks can change with one Solo change.
792 }
793 
794 
795 // class MusicalInstrument
796 
797 MusicalInstrument::MusicalInstrument(std::unique_ptr<wxBitmap> &&pBitmap, const wxString & strXPMfilename)
798 {
799  mBitmap = std::move(pBitmap);
800 
801  int nUnderscoreIndex;
802  wxString strFilename = strXPMfilename;
803  strFilename.MakeLower(); // Make sure, so we don't have to do case insensitive comparison.
804  wxString strKeyword;
805  while ((nUnderscoreIndex = strFilename.Find(wxT('_'))) != -1)
806  {
807  strKeyword = strFilename.Left(nUnderscoreIndex);
808  mKeywords.Add(strKeyword);
809  strFilename = strFilename.Mid(nUnderscoreIndex + 1);
810  }
811  if (!strFilename.IsEmpty()) // Skip trailing underscores.
812  mKeywords.Add(strFilename); // Add the last one.
813 }
814 
816 {
817  mKeywords.Clear();
818 }
819 
820 
821 // class MixerBoardScrolledWindow
822 
823 // wxScrolledWindow ignores mouse clicks in client area,
824 // but they don't get passed to Mixerboard.
825 // We need to catch them to deselect all track clusters.
826 
827 BEGIN_EVENT_TABLE(MixerBoardScrolledWindow, wxScrolledWindow)
828  EVT_MOUSE_EVENTS(MixerBoardScrolledWindow::OnMouseEvent)
830 
832  MixerBoard* parent, wxWindowID id /*= -1*/,
833  const wxPoint& pos /*= wxDefaultPosition*/,
834  const wxSize& size /*= wxDefaultSize*/,
835  long style /*= wxHSCROLL | wxVSCROLL*/)
836 : wxScrolledWindow(parent, id, pos, size, style)
837 {
838  mMixerBoard = parent;
839  mProject = project;
840 }
841 
843 {
844 }
845 
846 void MixerBoardScrolledWindow::OnMouseEvent(wxMouseEvent& event)
847 {
848  if (event.ButtonUp())
849  {
850  //v Even when I implement MixerBoard::OnMouseEvent and call event.Skip()
851  // here, MixerBoard::OnMouseEvent never gets called.
852  // So, added mProject to MixerBoardScrolledWindow and just directly do what's needed here.
853  mProject->SelectNone();
854  }
855  else
856  event.Skip();
857 }
858 
859 
860 // class MixerBoard
861 
862 #define MIXER_BOARD_MIN_HEIGHT 460
863 
864 // Min width is one cluster wide, plus margins.
865 #define MIXER_BOARD_MIN_WIDTH kTripleInset + kMixerTrackClusterWidth + kTripleInset
866 
867 
868 BEGIN_EVENT_TABLE(MixerBoard, wxWindow)
869  EVT_SIZE(MixerBoard::OnSize)
871 
873  wxFrame* parent,
874  const wxPoint& pos /*= wxDefaultPosition*/,
875  const wxSize& size /*= wxDefaultSize*/)
876 : wxWindow(parent, -1, pos, size)
877 {
878  // public data members
879 
880  // mute & solo button images
881  // Create once and store on MixerBoard for use in all MixerTrackClusters.
882  mImageMuteUp = NULL;
883  mImageMuteOver = NULL;
884  mImageMuteDown = NULL;
885  mImageMuteDownWhileSolo = NULL;
886  mImageMuteDisabled = NULL;
887  mImageSoloUp = NULL;
888  mImageSoloOver = NULL;
889  mImageSoloDown = NULL;
890  mImageSoloDisabled = NULL;
891 
892  mMuteSoloWidth = kRightSideStackWidth - kInset; // correct for max width, but really set in MixerBoard::CreateMuteSoloImages
893 
894  // private data members
895  this->LoadMusicalInstruments(); // Set up mMusicalInstruments.
896  mProject = pProject;
897 
898  wxASSERT(pProject); // to justify safenew
899  mScrolledWindow =
901  pProject, // AudacityProject* project,
902  this, -1, // wxWindow* parent, wxWindowID id = -1,
903  this->GetClientAreaOrigin(), // const wxPoint& pos = wxDefaultPosition,
904  size, // const wxSize& size = wxDefaultSize,
905  wxHSCROLL); // long style = wxHSCROLL | wxVSCROLL, const wxString& name = "scrolledWindow")
906 
907  // Set background color to same as TrackPanel background.
908 // #ifdef EXPERIMENTAL_THEMING
909 // mScrolledWindow->SetBackgroundColour(this->GetParent()->GetBackgroundColour());
910 // #else
911 // mScrolledWindow->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
912 // #endif
913  mScrolledWindow->SetBackgroundColour( theTheme.Colour( clrMedium ) );
914 
915  mScrolledWindow->SetScrollRate(10, 0); // no vertical scroll
916  mScrolledWindow->SetVirtualSize(size);
917 
918  /* This doesn't work to make the mScrolledWindow automatically resize, so do it explicitly in OnSize.
919  auto pBoxSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
920  pBoxSizer->Add(mScrolledWindow, 0, wxExpand, 0);
921  this->SetAutoLayout(true);
922  this->SetSizer(pBoxSizer);
923  pBoxSizer->Fit(this);
924  pBoxSizer->SetSizeHints(this);
925  */
926 
927  mPrevT1 = 0.0;
928  mTracks = mProject->GetTracks();
929 
930  // Events from the project don't propagate directly to this other frame, so...
931  mProject->Connect(EVT_TRACK_PANEL_TIMER,
932  wxCommandEventHandler(MixerBoard::OnTimer),
933  NULL,
934  this);
935 }
936 
938 {
939  // private data members
940  mMusicalInstruments.clear();
941 
942  mProject->Disconnect(EVT_TRACK_PANEL_TIMER,
943  wxCommandEventHandler(MixerBoard::OnTimer),
944  NULL,
945  this);
946 }
947 
948 
949 
950 
952 {
954 
955 // Old approach modified things in situ.
956 // However with a theme change there is so much to modify, it is easier
957 // to recreate.
958 #if 0
959  mScrolledWindow->SetBackgroundColour( theTheme.Colour( clrMedium ) );
960  if( mImageMuteUp ){
961  mImageMuteUp.reset();
962  mImageMuteOver.reset();
963  mImageMuteDown.reset();
964  mImageMuteDownWhileSolo.reset();
965  mImageMuteDisabled.reset();
966  mImageSoloUp.reset();
967  mImageSoloOver.reset();
968  mImageSoloDown.reset();
969  mImageSoloDisabled.reset();
970  }
971  for (unsigned int nClusterIndex = 0; nClusterIndex < mMixerTrackClusters.GetCount(); nClusterIndex++)
972  mMixerTrackClusters[nClusterIndex]->UpdatePrefs();
973  Refresh();
974 #endif
975 }
976 
977 // Reassign mixer input strips (MixerTrackClusters) to Track Clusters
978 // both have the same order.
979 // If EXPERIMENTAL_MIDI_OUT, then Note Tracks appear in the
980 // mixer, and we must be able to convert and reuse a MixerTrackCluster
981 // from audio to midi or midi to audio. This task is handled by
982 // UpdateForStateChange().
983 //
985 {
986  if (!mImageMuteUp)
987  this->CreateMuteSoloImages();
988 
989  const int nClusterHeight = mScrolledWindow->GetClientSize().GetHeight() - kDoubleInset;
990  size_t nClusterCount = mMixerTrackClusters.GetCount();
991  unsigned int nClusterIndex = 0;
992  TrackListIterator iterTracks(mTracks);
993  MixerTrackCluster* pMixerTrackCluster = NULL;
994  Track* pTrack;
995 
996  pTrack = iterTracks.First();
997  while (pTrack) {
998  if (auto pPlayableTrack = dynamic_cast<PlayableTrack*>(pTrack))
999  {
1000  auto spTrack = Track::Pointer<PlayableTrack>( pPlayableTrack );
1001  if (nClusterIndex < nClusterCount)
1002  {
1003  // Already showing it.
1004  // Track clusters are maintained in the same order as the WaveTracks.
1005  // Track pointers can change for the "same" track for different states
1006  // on the undo stack, so update the pointers and display name.
1007  mMixerTrackClusters[nClusterIndex]->mTrack = spTrack;
1008  // Assume linked track is wave or null
1009  mMixerTrackClusters[nClusterIndex]->UpdateForStateChange();
1010  }
1011  else
1012  {
1013  // Not already showing it. Add a NEW MixerTrackCluster.
1014  wxPoint clusterPos(
1015  (kInset + // extra inset to left for first one, so it's double
1016  (nClusterIndex *
1017  (kInset + kMixerTrackClusterWidth)) + // left margin and width for each to its left
1018  kInset), // plus left margin for NEW cluster
1019  kInset);
1020  wxSize clusterSize(kMixerTrackClusterWidth, nClusterHeight);
1021  pMixerTrackCluster =
1023  spTrack,
1024  clusterPos, clusterSize);
1025  if (pMixerTrackCluster)
1026  mMixerTrackClusters.Add(pMixerTrackCluster);
1027  }
1028  nClusterIndex++;
1029  }
1030  pTrack = iterTracks.Next(true);
1031  }
1032 
1033  if (pMixerTrackCluster)
1034  {
1035  // Added at least one MixerTrackCluster.
1036  this->UpdateWidth();
1037  this->ResizeTrackClusters();
1038  }
1039  else while (nClusterIndex < nClusterCount)
1040  {
1041  // We've got too many clusters.
1042  // This can happen only on things like Undo New Audio Track or Undo Import
1043  // that don't call RemoveTrackCluster explicitly.
1044  // We've already updated the track pointers for the clusters to the left, so just remove all the rest.
1045  // Successively DELETE from right to left.
1046  RemoveTrackCluster(--nClusterCount);
1047  }
1048 }
1049 
1051 {
1052  return
1053  kInset + // extra margin at left for first one
1054  (mMixerTrackClusters.GetCount() * // number of tracks times
1055  (kInset + kMixerTrackClusterWidth)) + // left margin and width for each
1056  kDoubleInset; // plus final right margin
1057 }
1058 
1060  bool bUp) // Up in TrackPanel is left in MixerBoard.
1061 {
1062  MixerTrackCluster* pMixerTrackCluster;
1063  int nIndex = FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1064  if (pMixerTrackCluster == NULL)
1065  return; // Couldn't find it.
1066 
1067  wxPoint pos;
1068  if (bUp)
1069  { // Move it up (left).
1070  if (nIndex <= 0)
1071  return; // It's already first (0), or not found (-1).
1072 
1073  pos = pMixerTrackCluster->GetPosition();
1074  mMixerTrackClusters[nIndex] = mMixerTrackClusters[nIndex - 1];
1075  mMixerTrackClusters[nIndex]->Move(pos);
1076 
1077  mMixerTrackClusters[nIndex - 1] = pMixerTrackCluster;
1078  pMixerTrackCluster->Move(pos.x - (kInset + kMixerTrackClusterWidth), pos.y);
1079  }
1080  else
1081  { // Move it down (right).
1082  if (((unsigned int)nIndex + 1) >= mMixerTrackClusters.GetCount())
1083  return; // It's already last.
1084 
1085  pos = pMixerTrackCluster->GetPosition();
1086  mMixerTrackClusters[nIndex] = mMixerTrackClusters[nIndex + 1];
1087  mMixerTrackClusters[nIndex]->Move(pos);
1088 
1089  mMixerTrackClusters[nIndex + 1] = pMixerTrackCluster;
1090  pMixerTrackCluster->Move(pos.x + (kInset + kMixerTrackClusterWidth), pos.y);
1091  }
1092 }
1093 
1095 {
1096  // Find and destroy.
1097  MixerTrackCluster* pMixerTrackCluster;
1098  int nIndex = this->FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1099 
1100  if (pMixerTrackCluster == NULL)
1101  return; // Couldn't find it.
1102 
1103  RemoveTrackCluster(nIndex);
1104 }
1105 
1107 {
1108  auto pMixerTrackCluster = mMixerTrackClusters[nIndex];
1109  mMixerTrackClusters.RemoveAt(nIndex);
1110  pMixerTrackCluster->Destroy(); // DELETE is unsafe on wxWindow.
1111 
1112  // Close the gap, if any.
1113  wxPoint pos;
1114  int targetX;
1115  for (unsigned int i = nIndex; i < mMixerTrackClusters.GetCount(); i++)
1116  {
1117  pos = mMixerTrackClusters[i]->GetPosition();
1118  targetX =
1119  kInset + // extra inset to left for first one, so it's double
1120  (i * (kInset + kMixerTrackClusterWidth)) + // left margin and width for each
1121  kInset; // plus left margin for this cluster
1122  if (pos.x != targetX)
1123  mMixerTrackClusters[i]->Move(targetX, pos.y);
1124  }
1125 
1126  this->UpdateWidth();
1127 }
1128 
1129 
1131 {
1132  if (mMusicalInstruments.empty())
1133  return NULL;
1134 
1135  // random choice: return mMusicalInstruments[(int)pTrack % mMusicalInstruments.GetCount()].mBitmap;
1136 
1137  const wxString strTrackName(pTrack->GetName().MakeLower());
1138  size_t nBestItemIndex = 0;
1139  unsigned int nBestScore = 0;
1140  unsigned int nInstrIndex = 0;
1141  unsigned int nKeywordIndex;
1142  unsigned int nNumKeywords;
1143  unsigned int nPointsPerMatch;
1144  unsigned int nScore;
1145  for (nInstrIndex = 0; nInstrIndex < mMusicalInstruments.size(); nInstrIndex++)
1146  {
1147  nScore = 0;
1148 
1149  nNumKeywords = mMusicalInstruments[nInstrIndex]->mKeywords.GetCount();
1150  if (nNumKeywords > 0)
1151  {
1152  nPointsPerMatch = 10 / nNumKeywords;
1153  for (nKeywordIndex = 0; nKeywordIndex < nNumKeywords; nKeywordIndex++)
1154  if (strTrackName.Contains(mMusicalInstruments[nInstrIndex]->mKeywords[nKeywordIndex]))
1155  {
1156  nScore +=
1157  nPointsPerMatch +
1158  // Longer keywords get more points.
1159  (2 * mMusicalInstruments[nInstrIndex]->mKeywords[nKeywordIndex].Length());
1160  }
1161  }
1162 
1163  // Choose later one if just matching nBestScore, for better variety,
1164  // and so default works as last element.
1165  if (nScore >= nBestScore)
1166  {
1167  nBestScore = nScore;
1168  nBestItemIndex = nInstrIndex;
1169  }
1170  }
1171  return mMusicalInstruments[nBestItemIndex]->mBitmap.get();
1172 }
1173 
1175 {
1176  TrackListIterator iterTracks(mTracks);
1177  Track* pTrack;
1178  for (pTrack = iterTracks.First(); pTrack; pTrack = iterTracks.Next()) {
1179  auto pPlayable = dynamic_cast<PlayableTrack *>( pTrack );
1180  if (pPlayable && pPlayable->GetSolo())
1181  return true;
1182  }
1183  return false;
1184 }
1185 
1186 void MixerBoard::RefreshTrackCluster(const PlayableTrack* pTrack, bool bEraseBackground /*= true*/)
1187 {
1188  MixerTrackCluster* pMixerTrackCluster;
1189  this->FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1190  if (pMixerTrackCluster)
1191  pMixerTrackCluster->Refresh(bEraseBackground);
1192 }
1193 
1194 void MixerBoard::RefreshTrackClusters(bool bEraseBackground /*= true*/)
1195 {
1196  for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
1197  mMixerTrackClusters[i]->Refresh(bEraseBackground);
1198 }
1199 
1201 {
1202  for (unsigned int nClusterIndex = 0; nClusterIndex < mMixerTrackClusters.GetCount(); nClusterIndex++)
1203  mMixerTrackClusters[nClusterIndex]->HandleResize();
1204 }
1205 
1206 void MixerBoard::ResetMeters(const bool bResetClipping)
1207 {
1209 
1210  if (!this->IsShown())
1211  return;
1212 
1213  for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
1214  mMixerTrackClusters[i]->ResetMeter(bResetClipping);
1215 }
1216 
1218 {
1219  MixerTrackCluster* pMixerTrackCluster;
1220  this->FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1221  if (pMixerTrackCluster)
1222  pMixerTrackCluster->UpdateName();
1223 }
1224 
1225 void MixerBoard::UpdateMute(const PlayableTrack* pTrack /*= NULL*/) // NULL means update for all tracks.
1226 {
1227  if (pTrack == NULL)
1228  {
1229  for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
1231  }
1232  else
1233  {
1234  MixerTrackCluster* pMixerTrackCluster;
1235  FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1236  if (pMixerTrackCluster)
1237  pMixerTrackCluster->UpdateMute();
1238  }
1239 }
1240 
1241 void MixerBoard::UpdateSolo(const PlayableTrack* pTrack /*= NULL*/) // NULL means update for all tracks.
1242 {
1243  if (pTrack == NULL)
1244  {
1245  for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
1247  }
1248  else
1249  {
1250  MixerTrackCluster* pMixerTrackCluster;
1251  FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1252  if (pMixerTrackCluster)
1253  pMixerTrackCluster->UpdateSolo();
1254  }
1255 }
1256 
1258 {
1259  if (pTrack == NULL)
1260  {
1261  for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
1263  }
1264  else
1265  {
1266  MixerTrackCluster* pMixerTrackCluster;
1267  FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1268  if (pMixerTrackCluster)
1269  pMixerTrackCluster->UpdatePan();
1270  }
1271 }
1272 
1274 {
1275  MixerTrackCluster* pMixerTrackCluster;
1276  FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1277  if (pMixerTrackCluster)
1278  pMixerTrackCluster->UpdateGain();
1279 }
1280 
1281 #ifdef EXPERIMENTAL_MIDI_OUT
1282 void MixerBoard::UpdateVelocity(const PlayableTrack* pTrack)
1283 {
1284  MixerTrackCluster* pMixerTrackCluster;
1285  FindMixerTrackCluster(pTrack, &pMixerTrackCluster);
1286  if (pMixerTrackCluster)
1287  pMixerTrackCluster->UpdateVelocity();
1288 }
1289 #endif
1290 
1291 void MixerBoard::UpdateMeters(const double t1, const bool bLoopedPlay)
1292 {
1293  if (!this->IsShown() || (t1 == BAD_STREAM_TIME))
1294  return;
1295 
1296  if (mPrevT1 == BAD_STREAM_TIME)
1297  {
1298  mPrevT1 = t1;
1299  return;
1300  }
1301 
1302  // In loopedPlay mode, at the end of the loop, mPrevT1 is set to
1303  // selection end, so the next t1 will be less, but we do want to
1304  // keep updating the meters.
1305  if (t1 <= mPrevT1)
1306  {
1307  if (bLoopedPlay)
1308  mPrevT1 = t1;
1309  return;
1310  }
1311 
1312  for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
1313  mMixerTrackClusters[i]->UpdateMeter(mPrevT1, t1);
1314 
1315  mPrevT1 = t1;
1316 }
1317 
1318 
1320 {
1321  int newWidth = this->GetTrackClustersWidth();
1322 
1323  // Min width is one cluster wide, plus margins.
1324  if (newWidth < MIXER_BOARD_MIN_WIDTH)
1325  newWidth = MIXER_BOARD_MIN_WIDTH;
1326 
1327  mScrolledWindow->SetVirtualSize(newWidth, -1);
1328  this->GetParent()->SetSize(newWidth + kDoubleInset, -1);
1329 }
1330 
1331 //
1332 // private methods
1333 //
1334 
1335 
1336 void MixerBoard::MakeButtonBitmap( wxMemoryDC & dc, wxBitmap & WXUNUSED(bitmap), wxRect & bev, const wxString & str, bool up )
1337 {
1338 
1339  int textWidth, textHeight;
1340 
1341  int fontSize = 10;
1342  #ifdef __WXMSW__
1343  fontSize = 8;
1344  #endif
1345  wxFont font(fontSize, wxSWISS, wxNORMAL, wxNORMAL);
1346  GetTextExtent(str, &textWidth, &textHeight, NULL, NULL, &font);
1347 
1348  AColor::UseThemeColour( &dc, clrMedium );
1349  dc.DrawRectangle(bev);
1350 
1351  AColor::Bevel2( dc, up, bev, false );
1352 
1353  wxCoord x = bev.x + (bev.width - textWidth) / 2;
1354  wxCoord y = bev.y + (bev.height - textHeight) / 2;
1355  dc.SetFont(font);
1356  dc.SetTextForeground(theTheme.Colour(clrTrackPanelText));
1357  dc.SetBackgroundMode(wxTRANSPARENT);
1358  dc.DrawText(str, x, y);
1359 // dc.DrawText(str, 0, 0);
1360 }
1361 
1363 {
1364  // Much of this is similar to TrackInfo::MuteOrSoloDrawFunction.
1365  wxMemoryDC dc;
1366  wxString str = _("Mute");
1367 
1368  //mMuteSoloWidth = textWidth + kQuadrupleInset;
1369  //if (mMuteSoloWidth < kRightSideStackWidth - kInset)
1371 
1372  wxBitmap bitmap(mMuteSoloWidth, MUTE_SOLO_HEIGHT,24);
1373  dc.SelectObject(bitmap);
1374  wxRect bev(0, 0, mMuteSoloWidth, MUTE_SOLO_HEIGHT);
1375 
1376  const bool up=true;
1377  const bool down=false;
1378 
1379  MakeButtonBitmap( dc, bitmap, bev, str, up );
1380  mImageMuteUp = std::make_unique<wxImage>(bitmap.ConvertToImage());
1381  mImageMuteOver = std::make_unique<wxImage>(bitmap.ConvertToImage()); // Same as up, for now.
1382 
1383  MakeButtonBitmap( dc, bitmap, bev, str, down );
1384  //AColor::Bevel(dc, false, bev);
1385  mImageMuteDown = std::make_unique<wxImage>(bitmap.ConvertToImage());
1386 
1387  MakeButtonBitmap( dc, bitmap, bev, str, down );
1388  mImageMuteDownWhileSolo = std::make_unique<wxImage>(bitmap.ConvertToImage());
1389 
1390  mImageMuteDisabled = std::make_unique<wxImage>(mMuteSoloWidth, MUTE_SOLO_HEIGHT); // Leave empty because unused.
1391 
1392  str = _("Solo");
1393  MakeButtonBitmap( dc, bitmap, bev, str, up );
1394  mImageSoloUp = std::make_unique<wxImage>(bitmap.ConvertToImage());
1395  mImageSoloOver = std::make_unique<wxImage>(bitmap.ConvertToImage()); // Same as up, for now.
1396 
1397  MakeButtonBitmap( dc, bitmap, bev, str, down );
1398  mImageSoloDown = std::make_unique<wxImage>(bitmap.ConvertToImage());
1399 
1400  mImageSoloDisabled = std::make_unique<wxImage>(mMuteSoloWidth, MUTE_SOLO_HEIGHT); // Leave empty because unused.
1401 }
1402 
1404  MixerTrackCluster** hMixerTrackCluster) const
1405 {
1406  *hMixerTrackCluster = NULL;
1407  for (unsigned int i = 0; i < mMixerTrackClusters.GetCount(); i++)
1408  {
1409  if (mMixerTrackClusters[i]->mTrack.get() == pTrack)
1410  {
1411  *hMixerTrackCluster = mMixerTrackClusters[i];
1412  return i;
1413  }
1414  }
1415  return -1;
1416 }
1417 
1419 {
1420  const struct Data { const char **bitmap; wxString name; } table[] = {
1421  {acoustic_guitar_gtr_xpm, wxT("acoustic_guitar_gtr")},
1422  {acoustic_piano_pno_xpm, wxT("acoustic_piano_pno")},
1423  {back_vocal_bg_vox_xpm, wxT("back_vocal_bg_vox")},
1424  {clap_xpm, wxT("clap")},
1425  {drums_dr_xpm, wxT("drums_dr")},
1426  {electric_bass_guitar_bs_gtr_xpm, wxT("electric_bass_guitar_bs_gtr")},
1427  {electric_guitar_gtr_xpm, wxT("electric_guitar_gtr")},
1428  {electric_piano_pno_key_xpm, wxT("electric_piano_pno_key")},
1429  {kick_xpm, wxT("kick")},
1430  {loop_xpm, wxT("loop")},
1431  {organ_org_xpm, wxT("organ_org")},
1432  {perc_xpm, wxT("perc")},
1433  {sax_xpm, wxT("sax")},
1434  {snare_xpm, wxT("snare")},
1435  {string_violin_cello_xpm, wxT("string_violin_cello")},
1436  {synth_xpm, wxT("synth")},
1437  {tambo_xpm, wxT("tambo")},
1438  {trumpet_horn_xpm, wxT("trumpet_horn")},
1439  {turntable_xpm, wxT("turntable")},
1440  {vibraphone_vibes_xpm, wxT("vibraphone_vibes")},
1441  {vocal_vox_xpm, wxT("vocal_vox")},
1442 
1443  // This one must be last, so it wins when best score is 0.
1444  {_default_instrument_xpm, wxEmptyString},
1445  };
1446 
1448  wxMemoryDC dc;
1449 
1450  for (const auto &data : table) {
1451  auto bmp = std::make_unique<wxBitmap>(data.bitmap);
1452  dc.SelectObject(*bmp);
1453  AColor::Bevel(dc, false, bev);
1454  mMusicalInstruments.push_back(make_movable<MusicalInstrument>(
1455  std::move(bmp), data.name
1456  ));
1457  };
1458 }
1459 
1460 // event handlers
1461 
1462 void MixerBoard::OnSize(wxSizeEvent &evt)
1463 {
1464  // this->FitInside() doesn't work, and it doesn't happen automatically. Is wxScrolledWindow wrong?
1465  mScrolledWindow->SetSize(evt.GetSize());
1466 
1467  this->ResizeTrackClusters();
1468  this->RefreshTrackClusters(true);
1469 }
1470 
1471 void MixerBoard::OnTimer(wxCommandEvent &event)
1472 {
1473  // PRL 12 Jul 2015: Moved the below (with comments) out of TrackPanel::OnTimer.
1474 
1475  // Vaughan, 2011-01-28: No longer doing this on timer.
1476  // Now it's in AudioIO::SetMeters() and AudioIO::StopStream(), as with Meter Toolbar meters.
1477  //if (pMixerBoard)
1478  // pMixerBoard->ResetMeters(false);
1479 
1480  //v Vaughan, 2011-02-25: Moved this update back here from audacityAudioCallback.
1481  // See note there.
1482  // Vaughan, 2010-01-30:
1483  // Since all we're doing here is updating the meters, I moved it to
1484  // audacityAudioCallback where it calls gAudioIO->mOutputMeter->UpdateDisplay().
1485  if (mProject->IsAudioActive())
1486  {
1489  }
1490 
1491  // Let other listeners get the notification
1492  event.Skip();
1493 }
1494 
1495 
1496 // class MixerBoardFrame
1497 
1498 BEGIN_EVENT_TABLE(MixerBoardFrame, wxFrame)
1499  EVT_KEY_DOWN(MixerBoardFrame::OnKeyEvent)
1500  EVT_CLOSE(MixerBoardFrame::OnCloseWindow)
1501  EVT_MAXIMIZE(MixerBoardFrame::OnMaximize)
1502  EVT_SIZE(MixerBoardFrame::OnSize)
1504 
1505 // Default to fitting one track.
1506 const wxSize kDefaultSize =
1508 
1510 : wxFrame(parent, -1,
1511  wxString::Format(_("Audacity Mixer Board%s"),
1512  ((parent->GetName() == wxEmptyString) ?
1513  wxT("") :
1514  wxString::Format(wxT(" - %s"),
1515  parent->GetName()))),
1516  wxDefaultPosition, kDefaultSize,
1517  //vvv Bug in wxFRAME_FLOAT_ON_PARENT:
1518  // If both the project frame and MixerBoardFrame are minimized and you restore MixerBoardFrame,
1519  // you can't restore project frame until you close MixerBoardFrame, but then project frame and
1520  // MixerBoardFrame are restored but MixerBoardFrame is unresponsive because it thinks it's not shown.
1521  // wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT)
1522  wxDEFAULT_FRAME_STYLE)
1523 {
1524  mMixerBoard = safenew MixerBoard(parent, this, wxDefaultPosition, kDefaultSize);
1525 
1526  this->SetSizeHints(MIXER_BOARD_MIN_WIDTH, MIXER_BOARD_MIN_HEIGHT);
1527 
1528  mMixerBoard->UpdateTrackClusters();
1529 
1530  // loads either the XPM or the windows resource, depending on the platform
1531 #if !defined(__WXMAC__) && !defined(__WXX11__)
1532  {
1533 #ifdef __WXMSW__
1534  wxIcon ic{ wxICON(AudacityLogo) };
1535 #else
1536  wxIcon ic{wxICON(AudacityLogo48x48)};
1537 #endif
1538  SetIcon(ic);
1539  }
1540 #endif
1541  Center();
1542 }
1543 
1545 {
1546 }
1547 
1548 
1549 // event handlers
1550 void MixerBoardFrame::OnCloseWindow(wxCloseEvent &WXUNUSED(event))
1551 {
1552  this->Hide();
1553 }
1554 
1555 void MixerBoardFrame::OnMaximize(wxMaximizeEvent &event)
1556 {
1557  // Update the size hints to show all tracks before skipping to let default handling happen.
1559  event.Skip();
1560 }
1561 
1562 void MixerBoardFrame::OnSize(wxSizeEvent & WXUNUSED(event))
1563 {
1564  mMixerBoard->SetSize(this->GetClientSize());
1565 }
1566 
1567 void MixerBoardFrame::OnKeyEvent(wxKeyEvent & event)
1568 {
1569  AudacityProject *project = GetActiveProject();
1570  project->GetCommandManager()->FilterKeyEvent(project, event, true);
1571 }
1572 
1573 
void RefreshTrackClusters(bool bEraseBackground=true)
void SetAlternateIdx(unsigned idx)
Definition: AButton.cpp:248
void OnKeyEvent(wxKeyEvent &evt)
void RedrawProject(const bool bForceWaveTracks=false)
Definition: Project.cpp:1356
#define TRACK_NAME_HEIGHT
Definition: MixerBoard.cpp:131
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:215
void HandleSelect(bool bShiftDown, bool bControlDown)
Definition: MixerBoard.cpp:685
void OnPaint(wxPaintEvent &evt)
Definition: MixerBoard.cpp:699
void TP_PushState(const wxString &longDesc, const wxString &shortDesc, UndoPush flags) override
Definition: Project.cpp:5216
void OnButton_Solo(wxCommandEvent &event)
Definition: MixerBoard.cpp:777
std::unique_ptr< wxImage > mImageMuteDisabled
Definition: MixerBoard.h:265
#define kQuadrupleInset
Definition: MixerBoard.cpp:129
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:88
MixerTrackSlider * mSlider_Pan
Definition: MixerBoard.h:148
void OnMouseEvent(wxMouseEvent &event)
Definition: MixerBoard.cpp:846
void PopUp()
Definition: AButton.cpp:523
void UpdatePrefs()
Definition: Meter.cpp:378
Abstract base class used in importing a file.
Definition: Import.h:32
#define MIXER_BOARD_MIN_HEIGHT
Definition: MixerBoard.cpp:862
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true) const
Definition: WaveTrack.cpp:1950
double GetStreamTime()
During playback, the (unwarped) track time most recently played.
Definition: AudioIO.cpp:2968
static void CaptureKeyboard(wxWindow *handler)
Definition: Project.cpp:5764
void OnSlider_Gain(wxCommandEvent &event)
Definition: MixerBoard.cpp:728
#define MIXER_BOARD_MIN_WIDTH
Definition: MixerBoard.cpp:865
bool WasShiftDown()
Definition: AButton.cpp:485
bool FilterKeyEvent(AudacityProject *project, const wxKeyEvent &evt, bool permit=false)
static void Bevel2(wxDC &dc, bool up, const wxRect &r, bool bSel=false, bool bHighlight=false)
Definition: AColor.cpp:222
std::unique_ptr< wxImage > mImageSoloDisabled
Definition: MixerBoard.h:265
float Get(bool convert=true)
Definition: ASlider.cpp:1703
void ResizeTrackClusters()
VU Meter, for displaying recording/playback level.
Definition: Meter.h:88
AudacityProject * mProject
Definition: MixerBoard.h:276
void OnMouseEvent(wxMouseEvent &event)
Definition: MixerBoard.cpp:691
std::unique_ptr< wxImage > mImageMuteDown
Definition: MixerBoard.h:265
#define kLeftSideStackWidth
Definition: MixerBoard.cpp:136
is like wxStaticText, except it can be themed. wxStaticText can't be.
Definition: Theme.h:184
void OnMaximize(wxMaximizeEvent &event)
void OnFocus(wxFocusEvent &event)
Definition: MixerBoard.cpp:91
void RefreshTrackCluster(const PlayableTrack *pTrack, bool bEraseBackground=true)
void RemoveTrackCluster(const PlayableTrack *pTrack)
std::unique_ptr< wxImage > mImageSoloOver
Definition: MixerBoard.h:265
float GetChannelGain(int channel) const
Definition: WaveTrack.cpp:439
void UpdateForStateChange()
Definition: MixerBoard.cpp:452
CommandManager * GetCommandManager()
Definition: Project.h:331
int mMuteSoloWidth
Definition: MixerBoard.h:269
void LoadMusicalInstruments()
virtual ~MixerBoardScrolledWindow()
Definition: MixerBoard.cpp:842
void UpdatePan(const PlayableTrack *pTrack=NULL)
wxColour GetTrackColor()
Definition: MixerBoard.cpp:677
std::unique_ptr< wxBitmap > mBitmap
Definition: MixerBoard.h:168
int mStyle
Definition: ASlider.h:307
#define safenew
Definition: Audacity.h:223
static void Medium(wxDC *dc, bool selected)
Definition: AColor.cpp:319
#define BAD_STREAM_TIME
Definition: AudioIO.h:78
#define kInset
Definition: MixerBoard.cpp:126
#define kMixerTrackClusterWidth
Definition: MixerBoard.cpp:138
void UpdateMeter(const double t0, const double t1)
Definition: MixerBoard.cpp:520
void OnButton_MusicalInstrument(wxCommandEvent &event)
Definition: MixerBoard.cpp:722
void HandleSliderPan(const bool bWantPushState=false)
Definition: MixerBoard.cpp:427
void CreateMuteSoloImages()
void HandleTrackMute(Track *t, const bool exclusive)
Definition: Project.cpp:5589
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:161
void RefreshTPTrack(Track *pTrk, bool refreshbacking=true)
Definition: Project.cpp:5209
std::unique_ptr< wxImage > mImageMuteDownWhileSolo
Definition: MixerBoard.h:265
void ResetMeter(const bool bResetClipping)
Definition: MixerBoard.cpp:442
void OnMouseEvent(wxMouseEvent &event)
Definition: ASlider.cpp:1646
#define PAN_SLIDER
Definition: ASlider.h:45
#define kDoubleInset
Definition: MixerBoard.cpp:127
static void Bevel(wxDC &dc, bool up, const wxRect &r)
Definition: AColor.cpp:202
wxBitmapButton * mBitmapButton_MusicalInstrument
Definition: MixerBoard.h:145
virtual ~MusicalInstrument()
Definition: MixerBoard.cpp:815
wxBitmap * GetMusicalInstrumentBitmap(const Track *pTrack)
void UpdateSolo(const PlayableTrack *pTrack=NULL)
MixerBoardScrolledWindow * mScrolledWindow
Definition: MixerBoard.h:277
void OnMouseEvent(wxMouseEvent &event)
Definition: MixerBoard.cpp:75
std::unique_ptr< wxImage > mImageSoloUp
Definition: MixerBoard.h:265
std::shared_ptr< PlayableTrack > mTrack
Definition: MixerBoard.h:137
std::unique_ptr< wxImage > mImageMuteOver
Definition: MixerBoard.h:265
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:67
void UpdateWidth()
AudacityProject * mProject
Definition: MixerBoard.h:194
virtual ~MixerBoardFrame()
void UpdateGain(const PlayableTrack *pTrack)
#define MUTE_SOLO_HEIGHT
Definition: MixerBoard.cpp:133
wxString GetName() const
Definition: Track.h:212
void SetGain(float newGain)
Definition: WaveTrack.cpp:419
virtual Track * First(TrackList *val=nullptr)
Definition: Track.cpp:355
void MoveTrackCluster(const PlayableTrack *pTrack, bool bUp)
void OnCaptureKey(wxCommandEvent &event)
Definition: MixerBoard.cpp:105
bool IsAudioActive() const
Definition: Project.cpp:1432
static void UseThemeColour(wxDC *dc, int iBrush, int iPen=-1)
Definition: AColor.cpp:289
void Set(float value)
Definition: ASlider.cpp:1708
#define kRightSideStackWidth
Definition: MixerBoard.cpp:137
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
PlayMode mLastPlayMode
Definition: Project.h:544
An AudioTrack that can be played and stopped.
Definition: Track.h:313
AButton * mToggleButton_Solo
Definition: MixerBoard.h:147
void OnSlider_Pan(wxCommandEvent &event)
Definition: MixerBoard.cpp:755
void UpdateDisplay(unsigned numChannels, int numFrames, float *sampleData)
Update the meters with a block of audio data.
Definition: Meter.cpp:923
void UpdateMeters(const double t1, const bool bLoopedPlay)
int FindMixerTrackCluster(const PlayableTrack *pTrack, MixerTrackCluster **hMixerTrackCluster) const
bool IsSoloNone() const
Definition: Project.h:592
bool IsSoloSimple() const
Definition: Project.h:591
An iterator for a TrackList.
Definition: Track.h:339
_("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 &)
const wxSize kDefaultSize
#define DB_SLIDER
Definition: ASlider.h:44
static void ReleaseKeyboard(wxWindow *handler)
Definition: Project.cpp:5775
void UpdateName(const PlayableTrack *pTrack)
#define VEL_SLIDER
Definition: ASlider.h:48
std::unique_ptr< wxImage > mImageMuteUp
Definition: MixerBoard.h:265
AudioIO * gAudioIO
Definition: AudioIO.cpp:481
void OnTimer(wxCommandEvent &event)
ASlider is a custom slider, allowing for a slicker look and feel.
Definition: ASlider.h:242
const wxChar * name
Definition: Distortion.cpp:94
virtual ~MixerBoard()
Definition: MixerBoard.cpp:937
AButton * mToggleButton_Mute
Definition: MixerBoard.h:146
virtual Track * Next(bool skiplinked=false)
Definition: Track.cpp:396
void OnSize(wxSizeEvent &evt)
void MakeButtonBitmap(wxMemoryDC &dc, wxBitmap &bitmap, wxRect &bev, const wxString &str, bool up)
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:302
void OnSize(wxSizeEvent &evt)
WaveTrack * GetRight() const
Definition: MixerBoard.cpp:335
MusicalInstrumentArray mMusicalInstruments
Definition: MixerBoard.h:275
void OnButton_Mute(wxCommandEvent &event)
Definition: MixerBoard.cpp:760
MixerTrackSlider * mSlider_Gain
Definition: MixerBoard.h:149
void SelectNone()
Definition: Menus.cpp:6031
void OnCloseWindow(wxCloseEvent &WXUNUSED(event))
wxArrayString mKeywords
Definition: MixerBoard.h:169
WaveTrack * GetWave() const
Definition: MixerBoard.cpp:330
std::unique_ptr< wxImage > mImageSoloDown
Definition: MixerBoard.h:265
int GetTrackClustersWidth()
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1214
void PushDown()
Definition: AButton.cpp:517
#define PAN_HEIGHT
Definition: MixerBoard.cpp:134
MixerBoard * mMixerBoard
Definition: MixerBoard.h:140
MusicalInstrument(std::unique_ptr< wxBitmap > &&pBitmap, const wxString &strXPMfilename)
Definition: MixerBoard.cpp:797
void UpdatePrefs()
Definition: MixerBoard.cpp:951
AudacityProject * mProject
Definition: MixerBoard.h:141
END_EVENT_TABLE()
double mPrevT1
Definition: MixerBoard.h:278
void SetPan(float newPan) override
Definition: WaveTrack.cpp:429
Functions for doing the mixdown of the tracks.
Definition: Mix.h:80
MixerTrackClusterArray mMixerTrackClusters
Definition: MixerBoard.h:273
void ResetMeters(const bool bResetClipping)
TrackList * mTracks
Definition: MixerBoard.h:279
MixerBoard * mMixerBoard
Definition: MixerBoard.h:300
void HandleTrackSolo(Track *t, const bool alternate)
Definition: Project.cpp:5674
void HandleSliderGain(const bool bWantPushState=false)
Definition: MixerBoard.cpp:399
void Reset(double sampleRate, bool resetClipping)
This method is thread-safe! Feel free to call from a different thread (like from an audio I/O callbac...
Definition: Meter.cpp:863
void HandleListSelection(Track *t, bool shift, bool ctrl, bool modifyState)
Definition: Menus.cpp:3431
bool HasSolo()
void RecreateMixerBoard()
Definition: Project.cpp:4650
#define MUSICAL_INSTRUMENT_HEIGHT_AND_WIDTH
Definition: MixerBoard.cpp:132
void UpdateMute(const PlayableTrack *pTrack=NULL)
A wxButton with mouse-over behaviour.
Definition: AButton.h:27
A Track that is used for Midi notes. (Somewhat old code).
void UpdateTrackClusters()
Definition: MixerBoard.cpp:984