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