Audacity  3.0.3
Meter.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Meter.cpp
6 
7  Dominic Mazzoni
8  Vaughan Johnson
9 
10  2004.06.25 refresh rate limited to 30mS, by ChackoN
11 
12 *******************************************************************//****************************************************************//****************************************************************//****************************************************************//******************************************************************/
40 
41 
42 #include "Meter.h"
43 
44 #include <algorithm>
45 #include <wx/setup.h> // for wxUSE_* macros
46 #include <wx/wxcrtvararg.h>
47 #include <wx/app.h>
48 #include <wx/defs.h>
49 #include <wx/dialog.h>
50 #include <wx/dcbuffer.h>
51 #include <wx/frame.h>
52 #include <wx/image.h>
53 #include <wx/intl.h>
54 #include <wx/menu.h>
55 #include <wx/settings.h>
56 #include <wx/textdlg.h>
57 #include <wx/numdlg.h>
58 #include <wx/radiobut.h>
59 #include <wx/tooltip.h>
60 
61 #include <math.h>
62 
63 #include "../AudioIO.h"
64 #include "../AColor.h"
65 #include "../ImageManipulation.h"
66 #include "../prefs/GUISettings.h"
67 #include "../Project.h"
68 #include "../ProjectAudioManager.h"
69 #include "../ProjectStatus.h"
70 #include "Prefs.h"
71 #include "../ShuttleGui.h"
72 #include "../Theme.h"
73 
74 #include "../AllThemeResources.h"
75 #include "../widgets/valnum.h"
76 
77 #if wxUSE_ACCESSIBILITY
78 #include "WindowAccessible.h"
79 
80 class MeterAx final : public WindowAccessible
81 {
82 public:
83  MeterAx(wxWindow * window);
84 
85  virtual ~ MeterAx();
86 
87  // Performs the default action. childId is 0 (the action for this object)
88  // or > 0 (the action for a child).
89  // Return wxACC_NOT_SUPPORTED if there is no default action for this
90  // window (e.g. an edit control).
91  wxAccStatus DoDefaultAction(int childId) override;
92 
93  // Retrieves the address of an IDispatch interface for the specified child.
94  // All objects must support this property.
95  wxAccStatus GetChild(int childId, wxAccessible** child) override;
96 
97  // Gets the number of children.
98  wxAccStatus GetChildCount(int* childCount) override;
99 
100  // Gets the default action for this object (0) or > 0 (the action for a child).
101  // Return wxACC_OK even if there is no action. actionName is the action, or the empty
102  // string if there is no action.
103  // The retrieved string describes the action that is performed on an object,
104  // not what the object does as a result. For example, a toolbar button that prints
105  // a document has a default action of "Press" rather than "Prints the current document."
106  wxAccStatus GetDefaultAction(int childId, wxString *actionName) override;
107 
108  // Returns the description for this object or a child.
109  wxAccStatus GetDescription(int childId, wxString *description) override;
110 
111  // Gets the window with the keyboard focus.
112  // If childId is 0 and child is NULL, no object in
113  // this subhierarchy has the focus.
114  // If this object has the focus, child should be 'this'.
115  wxAccStatus GetFocus(int *childId, wxAccessible **child) override;
116 
117  // Returns help text for this object or a child, similar to tooltip text.
118  wxAccStatus GetHelpText(int childId, wxString *helpText) override;
119 
120  // Returns the keyboard shortcut for this object or child.
121  // Return e.g. ALT+K
122  wxAccStatus GetKeyboardShortcut(int childId, wxString *shortcut) override;
123 
124  // Returns the rectangle for this object (id = 0) or a child element (id > 0).
125  // rect is in screen coordinates.
126  wxAccStatus GetLocation(wxRect& rect, int elementId) override;
127 
128  // Gets the name of the specified object.
129  wxAccStatus GetName(int childId, wxString *name) override;
130 
131  // Returns a role constant.
132  wxAccStatus GetRole(int childId, wxAccRole *role) override;
133 
134  // Gets a variant representing the selected children
135  // of this object.
136  // Acceptable values:
137  // - a null variant (IsNull() returns TRUE)
138  // - a list variant (GetType() == wxT("list"))
139  // - an integer representing the selected child element,
140  // or 0 if this object is selected (GetType() == wxT("long"))
141  // - a "void*" pointer to a wxAccessible child object
142  wxAccStatus GetSelections(wxVariant *selections) override;
143 
144  // Returns a state constant.
145  wxAccStatus GetState(int childId, long* state) override;
146 
147  // Returns a localized string representing the value for the object
148  // or child.
149  wxAccStatus GetValue(int childId, wxString* strValue) override;
150 
151 };
152 
153 #endif // wxUSE_ACCESSIBILITY
154 
155 static const long MIN_REFRESH_RATE = 1;
156 static const long MAX_REFRESH_RATE = 100;
157 
158 /* Updates to the meter are passed across via meter updates, each contained in
159  * a MeterUpdateMsg object */
161 {
162 wxString output; // somewhere to build up a string in
163 output = wxString::Format(wxT("Meter update msg: %i channels, %i samples\n"), \
165 for (int i = 0; i<kMaxMeterBars; i++)
166  { // for each channel of the meters
167  output += wxString::Format(wxT("%f peak, %f rms "), peak[i], rms[i]);
168  if (clipping[i])
169  output += wxString::Format(wxT("clipped "));
170  else
171  output += wxString::Format(wxT("no clip "));
172  output += wxString::Format(wxT("%i head, %i tail\n"), headPeakCount[i], tailPeakCount[i]);
173  }
174 return output;
175 }
176 
178 {
179  for (int i = 0; i<kMaxMeterBars; i++)
180  {
181  if (clipping[i] || (headPeakCount[i] > 0) || (tailPeakCount[i] > 0))
182  return toString();
183  }
184  return wxT("");
185 }
186 
187 //
188 // The MeterPanel passes itself messages via this queue so that it can
189 // communicate between the audio thread and the GUI thread.
190 // This class is as simple as possible in order to be thread-safe
191 // without needing mutexes.
192 //
193 
195  mBufferSize(maxLen)
196 {
197  Clear();
198 }
199 
200 // destructor
202 {
203 }
204 
206 {
207  mStart = 0;
208  mEnd = 0;
209 }
210 
211 // Add a message to the end of the queue. Return false if the
212 // queue was full.
214 {
215  // mStart can be greater than mEnd because it is all mod mBufferSize
216  wxASSERT( (mEnd + mBufferSize - mStart) >= 0 );
217  int len = (mEnd + mBufferSize - mStart) % mBufferSize;
218 
219  // Never completely fill the queue, because then the
220  // state is ambiguous (mStart==mEnd)
221  if (len + 1 >= (int)(mBufferSize))
222  return false;
223 
224  //wxLogDebug(wxT("Put: %s"), msg.toString());
225 
226  mBuffer[mEnd] = msg;
227  mEnd = (mEnd+1)%mBufferSize;
228 
229  return true;
230 }
231 
232 // Get the next message from the start of the queue.
233 // Return false if the queue was empty.
235 {
236  int len = (mEnd + mBufferSize - mStart) % mBufferSize;
237 
238  if (len == 0)
239  return false;
240 
241  msg = mBuffer[mStart];
242  mStart = (mStart+1)%mBufferSize;
243 
244  return true;
245 }
246 
247 //
248 // MeterPanel class
249 //
250 
251 #include "../../images/SpeakerMenu.xpm"
252 #include "../../images/MicMenu.xpm"
253 
254 // How many pixels between items?
255 const static int gap = 2;
256 
257 const static wxChar *PrefStyles[] =
258 {
259  wxT("AutomaticStereo"),
260  wxT("HorizontalStereo"),
261  wxT("VerticalStereo")
262 };
263 
264 enum {
268 };
269 
270 BEGIN_EVENT_TABLE(MeterPanel, MeterPanelBase)
272  EVT_MOUSE_EVENTS(MeterPanel::OnMouse)
273  EVT_CONTEXT_MENU(MeterPanel::OnContext)
274  EVT_KEY_DOWN(MeterPanel::OnKeyDown)
275  EVT_KEY_UP(MeterPanel::OnKeyUp)
276  EVT_SET_FOCUS(MeterPanel::OnSetFocus)
277  EVT_KILL_FOCUS(MeterPanel::OnKillFocus)
278  EVT_ERASE_BACKGROUND(MeterPanel::OnErase)
279  EVT_PAINT(MeterPanel::OnPaint)
280  EVT_SIZE(MeterPanel::OnSize)
284 
286 
288  wxWindow* parent, wxWindowID id,
289  bool isInput,
290  const wxPoint& pos /*= wxDefaultPosition*/,
291  const wxSize& size /*= wxDefaultSize*/,
292  Style style /*= HorizontalStereo*/,
293  float fDecayRate /*= 60.0f*/)
294 : MeterPanelBase(parent, id, pos, size, wxTAB_TRAVERSAL | wxNO_BORDER | wxWANTS_CHARS),
295  mProject(project),
296  mQueue(1024),
297  mWidth(size.x),
298  mHeight(size.y),
299  mIsInput(isInput),
300  mDesiredStyle(style),
301  mGradient(true),
302  mDB(true),
303  mDBRange(ENV_DB_RANGE),
304  mDecay(true),
305  mDecayRate(fDecayRate),
306  mClip(true),
307  mNumPeakSamplesToClip(3),
308  mPeakHoldDuration(3),
309  mT(0),
310  mRate(0),
311  mMonitoring(false),
312  mActive(false),
313  mNumBars(0),
314  mLayoutValid(false),
315  mBitmap{},
316  mIcon{},
317  mAccSilent(false)
318 {
319  // i18n-hint: Noun (the meter is used for playback or record level monitoring)
320  SetName( XO("Meter") );
321  // Suppress warnings about the header file
322  wxUnusedVar(SpeakerMenu_xpm);
323  wxUnusedVar(MicMenu_xpm);
324  wxUnusedVar(PrefStyles);
325 
326  mStyle = mDesiredStyle;
327 
328  mIsFocused = false;
329 
330 #if wxUSE_ACCESSIBILITY
331  SetAccessible(safenew MeterAx(this));
332 #endif
333 
334  // Do this BEFORE UpdatePrefs()!
335  mRuler.SetFonts(GetFont(), GetFont(), GetFont());
336  mRuler.SetFlip(mStyle != MixerTrackCluster);
337  mRuler.SetLabelEdges(true);
338  //mRuler.SetTickColour( wxColour( 0,0,255 ) );
339 
340  UpdatePrefs();
341 
342  wxColour backgroundColour = theTheme.Colour( clrMedium);
343  mBkgndBrush = wxBrush(backgroundColour, wxBRUSHSTYLE_SOLID);
344  SetBackgroundColour( backgroundColour );
345 
346  mPeakPeakPen = wxPen(theTheme.Colour( clrMeterPeak), 1, wxPENSTYLE_SOLID);
347  mDisabledPen = wxPen(theTheme.Colour( clrMeterDisabledPen), 1, wxPENSTYLE_SOLID);
348 
349  if (mIsInput) {
350  wxTheApp->Bind(EVT_AUDIOIO_MONITOR,
352  this);
353  wxTheApp->Bind(EVT_AUDIOIO_CAPTURE,
355  this);
356 
357  mPen = wxPen( theTheme.Colour( clrMeterInputPen ), 1, wxPENSTYLE_SOLID);
358  mBrush = wxBrush( theTheme.Colour( clrMeterInputBrush ), wxBRUSHSTYLE_SOLID);
359  mRMSBrush = wxBrush( theTheme.Colour( clrMeterInputRMSBrush ), wxBRUSHSTYLE_SOLID);
360  mClipBrush = wxBrush( theTheme.Colour( clrMeterInputClipBrush ), wxBRUSHSTYLE_SOLID);
361 // mLightPen = wxPen( theTheme.Colour( clrMeterInputLightPen ), 1, wxSOLID);
362 // mDarkPen = wxPen( theTheme.Colour( clrMeterInputDarkPen ), 1, wxSOLID);
363  }
364  else {
365  // Register for AudioIO events
366  wxTheApp->Bind(EVT_AUDIOIO_PLAYBACK,
368  this);
369 
370  mPen = wxPen( theTheme.Colour( clrMeterOutputPen ), 1, wxPENSTYLE_SOLID);
371  mBrush = wxBrush( theTheme.Colour( clrMeterOutputBrush ), wxBRUSHSTYLE_SOLID);
372  mRMSBrush = wxBrush( theTheme.Colour( clrMeterOutputRMSBrush ), wxBRUSHSTYLE_SOLID);
373  mClipBrush = wxBrush( theTheme.Colour( clrMeterOutputClipBrush ), wxBRUSHSTYLE_SOLID);
374 // mLightPen = wxPen( theTheme.Colour( clrMeterOutputLightPen ), 1, wxSOLID);
375 // mDarkPen = wxPen( theTheme.Colour( clrMeterOutputDarkPen ), 1, wxSOLID);
376  }
377 
378 // mDisabledBkgndBrush = wxBrush(theTheme.Colour( clrMeterDisabledBrush), wxSOLID);
379  // No longer show a difference in the background colour when not monitoring.
380  // We have the tip instead.
381  mDisabledBkgndBrush = mBkgndBrush;
382 
383  // MixerTrackCluster style has no menu, so disallows SetStyle, so never needs icon.
384  if (mStyle != MixerTrackCluster)
385  {
386  if(mIsInput)
387  {
388  //mIcon = NEW wxBitmap(MicMenuNarrow_xpm);
389  mIcon = std::make_unique<wxBitmap>(wxBitmap(theTheme.Bitmap(bmpMic)));
390  }
391  else
392  {
393  //mIcon = NEW wxBitmap(SpeakerMenuNarrow_xpm);
394  mIcon = std::make_unique<wxBitmap>(wxBitmap(theTheme.Bitmap(bmpSpeaker)));
395  }
396  }
397 
398  mTimer.SetOwner(this, OnMeterUpdateID);
399  // TODO: Yikes. Hard coded sample rate.
400  // JKC: I've looked at this, and it's benignish. It just means that the meter
401  // balistics are right for 44KHz and a bit more frisky than they should be
402  // for higher sample rates.
403  Reset(44100.0, true);
404 }
405 
407 {
408  mQueue.Clear();
409 }
410 
412 {
414 
417  gPrefs->Read(Key(wxT("RefreshRate")), 30)));
418  mGradient = gPrefs->Read(Key(wxT("Bars")), wxT("Gradient")) == wxT("Gradient");
419  mDB = gPrefs->Read(Key(wxT("Type")), wxT("dB")) == wxT("dB");
420  mMeterDisabled = gPrefs->Read(Key(wxT("Disabled")), (long)0);
421 
423  {
424  wxString style = gPrefs->Read(Key(wxT("Style")));
425  if (style == wxT("AutomaticStereo"))
426  {
428  }
429  else if (style == wxT("HorizontalStereo"))
430  {
432  }
433  else if (style == wxT("VerticalStereo"))
434  {
436  }
437  else
438  {
440  }
441  }
442 
443  // Set the desired orientation (resets ruler orientation)
445 
446  // Reset to ensure NEW size is retrieved when language changes
447  mLeftSize = wxSize(0, 0);
448  mRightSize = wxSize(0, 0);
449 
450  Reset(mRate, false);
451 
452  mLayoutValid = false;
453 
454  Refresh(false);
455 }
456 
457 static int MeterPrefsID()
458 {
459  static int value = wxNewId();
460  return value;
461 }
462 
464 {
465  if (id == MeterPrefsID())
466  UpdatePrefs();
467 }
468 
469 void MeterPanel::OnErase(wxEraseEvent & WXUNUSED(event))
470 {
471  // Ignore it to prevent flashing
472 }
473 
474 void MeterPanel::OnPaint(wxPaintEvent & WXUNUSED(event))
475 {
476 #if defined(__WXMAC__)
477  auto paintDC = std::make_unique<wxPaintDC>(this);
478 #else
479  std::unique_ptr<wxDC> paintDC{ wxAutoBufferedPaintDCFactory(this) };
480 #endif
481  wxDC & destDC = *paintDC;
482  wxColour clrText = theTheme.Colour( clrTrackPanelText );
483  wxColour clrBoxFill = theTheme.Colour( clrMedium );
484 
485  if (mLayoutValid == false || (mStyle == MixerTrackCluster ))
486  {
487  // Create a NEW one using current size and select into the DC
488  mBitmap = std::make_unique<wxBitmap>();
489  mBitmap->Create(mWidth, mHeight, destDC);
490  wxMemoryDC dc;
491  dc.SelectObject(*mBitmap);
492 
493  // Go calculate all of the layout metrics
494  HandleLayout(dc);
495 
496  // Start with a clean background
497  // LLL: Should research USE_AQUA_THEME usefulness...
498 //#ifndef USE_AQUA_THEME
499 #ifdef EXPERIMENTAL_THEMING
500  //if( !mMeterDisabled )
501  //{
502  // mBkgndBrush.SetColour( GetParent()->GetBackgroundColour() );
503  //}
504 #endif
505 
506  mBkgndBrush.SetColour( GetBackgroundColour() );
507  dc.SetPen(*wxTRANSPARENT_PEN);
508  dc.SetBrush(mBkgndBrush);
509  dc.DrawRectangle(0, 0, mWidth, mHeight);
510 //#endif
511 
512  // MixerTrackCluster style has no icon or L/R labels
513  if (mStyle != MixerTrackCluster)
514  {
515  bool highlight = InIcon();
516  dc.DrawBitmap( theTheme.Bitmap( highlight ?
517  bmpHiliteUpButtonSmall : bmpUpButtonSmall ),
518  mIconRect.GetPosition(), false );
519 
520  dc.DrawBitmap(*mIcon, mIconRect.GetPosition(), true);
521  dc.SetFont(GetFont());
522  dc.SetTextForeground( clrText );
523  dc.SetTextBackground( clrBoxFill );
524  dc.DrawText(mLeftText, mLeftTextPos.x, mLeftTextPos.y);
525  dc.DrawText(mRightText, mRightTextPos.x, mRightTextPos.y);
526  }
527 
528  // Setup the colors for the 3 sections of the meter bars
529  wxColor green(117, 215, 112);
530  wxColor yellow(255, 255, 0);
531  wxColor red(255, 0, 0);
532 
533  // Bug #2473 - (Sort of) Hack to make text on meters more
534  // visible with darker backgrounds. It would be better to have
535  // different colors entirely and as part of the theme.
536  if (GetBackgroundColour().GetLuminance() < 0.25)
537  {
538  green = wxColor(117-100, 215-100, 112-100);
539  yellow = wxColor(255-100, 255-100, 0);
540  red = wxColor(255-100, 0, 0);
541  }
542  else if (GetBackgroundColour().GetLuminance() < 0.50)
543  {
544  green = wxColor(117-50, 215-50, 112-50);
545  yellow = wxColor(255-50, 255-50, 0);
546  red = wxColor(255-50, 0, 0);
547  }
548 
549  // Draw the meter bars at maximum levels
550  for (unsigned int i = 0; i < mNumBars; i++)
551  {
552  // Give it a recessed look
553  AColor::Bevel(dc, false, mBar[i].b);
554 
555  // Draw the clip indicator bevel
556  if (mClip)
557  {
558  AColor::Bevel(dc, false, mBar[i].rClip);
559  }
560 
561  // Cache bar rect
562  wxRect r = mBar[i].r;
563 
564  if (mGradient)
565  {
566  // Calculate the size of the two gradiant segments of the meter
567  double gradw;
568  double gradh;
569  if (mDB)
570  {
571  gradw = (double) r.GetWidth() / mDBRange * 6.0;
572  gradh = (double) r.GetHeight() / mDBRange * 6.0;
573  }
574  else
575  {
576  gradw = (double) r.GetWidth() / 100 * 25;
577  gradh = (double) r.GetHeight() / 100 * 25;
578  }
579 
580  if (mBar[i].vert)
581  {
582  // Draw the "critical" segment (starts at top of meter and works down)
583  r.SetHeight(gradh);
584  dc.GradientFillLinear(r, red, yellow, wxSOUTH);
585 
586  // Draw the "warning" segment
587  r.SetTop(r.GetBottom());
588  dc.GradientFillLinear(r, yellow, green, wxSOUTH);
589 
590  // Draw the "safe" segment
591  r.SetTop(r.GetBottom());
592  r.SetBottom(mBar[i].r.GetBottom());
593  dc.SetPen(*wxTRANSPARENT_PEN);
594  dc.SetBrush(green);
595  dc.DrawRectangle(r);
596  }
597  else
598  {
599  // Draw the "safe" segment
600  r.SetWidth(r.GetWidth() - (int) (gradw + gradw + 0.5));
601  dc.SetPen(*wxTRANSPARENT_PEN);
602  dc.SetBrush(green);
603  dc.DrawRectangle(r);
604 
605  // Draw the "warning" segment
606  r.SetLeft(r.GetRight() + 1);
607  r.SetWidth(floor(gradw));
608  dc.GradientFillLinear(r, green, yellow);
609 
610  // Draw the "critical" segment
611  r.SetLeft(r.GetRight() + 1);
612  r.SetRight(mBar[i].r.GetRight());
613  dc.GradientFillLinear(r, yellow, red);
614  }
615 #ifdef EXPERIMENTAL_METER_LED_STYLE
616  if (!mBar[i].vert)
617  {
618  wxRect r = mBar[i].r;
619  wxPen BackgroundPen;
620  BackgroundPen.SetColour( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE) );
621  dc.SetPen( BackgroundPen );
622  int i;
623  for(i=0;i<r.width;i++)
624  {
625  // 2 pixel spacing between the LEDs
626  if( (i%7)<2 ){
627  AColor::Line( dc, i+r.x, r.y, i+r.x, r.y+r.height );
628  } else {
629  // The LEDs have triangular ends.
630  // This code shapes the ends.
631  int j = abs( (i%7)-4);
632  AColor::Line( dc, i+r.x, r.y, i+r.x, r.y+j +1);
633  AColor::Line( dc, i+r.x, r.y+r.height-j, i+r.x, r.y+r.height );
634  }
635  }
636  }
637 #endif
638  }
639  }
640  mRuler.SetTickColour( clrText );
641  dc.SetTextForeground( clrText );
642  // Draw the ruler
643 #ifndef EXPERIMENTAL_DA
644  mRuler.Draw(dc);
645 #endif
646 
647  // Bitmap created...unselect
648  dc.SelectObject(wxNullBitmap);
649  }
650 
651  // Copy predrawn bitmap to the dest DC
652  destDC.DrawBitmap(*mBitmap, 0, 0);
653 
654  // Go draw the meter bars, Left & Right channels using current levels
655  for (unsigned int i = 0; i < mNumBars; i++)
656  {
657  DrawMeterBar(destDC, &mBar[i]);
658  }
659 
660  destDC.SetTextForeground( clrText );
661 
662 #ifndef EXPERIMENTAL_DA
663  // We can have numbers over the bars, in which case we have to draw them each time.
665  {
666  mRuler.SetTickColour( clrText );
667  // If the text colour is too similar to the meter colour, then we need a background
668  // for the text. We require a total of at least one full-scale RGB difference.
669  int d = theTheme.ColourDistance( clrText, theTheme.Colour( clrMeterOutputRMSBrush ) );
670  if( d < 256 )
671  {
672  destDC.SetBackgroundMode( wxSOLID );
673  destDC.SetTextBackground( clrBoxFill );
674  }
675  mRuler.Draw(destDC);
676  }
677 #endif
678 
679  // Let the user know they can click to start monitoring
680  if( mIsInput && !mActive )
681  {
682  destDC.SetFont( GetFont() );
683 
684  wxArrayStringEx texts{
685  _("Click to Start Monitoring") ,
686  _("Click for Monitoring") ,
687  _("Click to Start") ,
688  _("Click") ,
689  };
690 
691  for( size_t i = 0, cnt = texts.size(); i < cnt; i++ )
692  {
693  wxString Text = wxT(" ") + texts[i] + wxT(" ");
694  wxSize Siz = destDC.GetTextExtent( Text );
695  Siz.SetWidth( Siz.GetWidth() + gap );
696  Siz.SetHeight( Siz.GetHeight() + gap );
697 
698  if( mBar[0].vert)
699  {
700  if( Siz.GetWidth() < mBar[0].r.GetHeight() )
701  {
702  wxRect r( mBar[1].b.GetLeft() - (int) (Siz.GetHeight() / 2.0) + 0.5,
703  mBar[0].r.GetTop() + (int) ((mBar[0].r.GetHeight() - Siz.GetWidth()) / 2.0) + 0.5,
704  Siz.GetHeight(),
705  Siz.GetWidth() );
706 
707  destDC.SetBrush( wxBrush( clrBoxFill ) );
708  destDC.SetPen( *wxWHITE_PEN );
709  destDC.DrawRectangle( r );
710  destDC.SetBackgroundMode( wxTRANSPARENT );
711  r.SetTop( r.GetBottom() + (gap / 2) );
712  destDC.SetTextForeground( clrText );
713  destDC.DrawRotatedText( Text, r.GetPosition(), 90 );
714  break;
715  }
716  }
717  else
718  {
719  if( Siz.GetWidth() < mBar[0].r.GetWidth() )
720  {
721  wxRect r( mBar[0].r.GetLeft() + (int) ((mBar[0].r.GetWidth() - Siz.GetWidth()) / 2.0) + 0.5,
722  mBar[1].b.GetTop() - (int) (Siz.GetHeight() / 2.0) + 0.5,
723  Siz.GetWidth(),
724  Siz.GetHeight() );
725 
726  destDC.SetBrush( wxBrush( clrBoxFill ) );
727  destDC.SetPen( *wxWHITE_PEN );
728  destDC.DrawRectangle( r );
729  destDC.SetBackgroundMode( wxTRANSPARENT );
730  r.SetLeft( r.GetLeft() + (gap / 2) );
731  r.SetTop( r.GetTop() + (gap / 2));
732  destDC.SetTextForeground( clrText );
733  destDC.DrawText( Text, r.GetPosition() );
734  break;
735  }
736  }
737  }
738  }
739 
740  if (mIsFocused)
741  {
742  wxRect r = mIconRect;
743  AColor::DrawFocus(destDC, r.Inflate(1, 1));
744  }
745 }
746 
747 void MeterPanel::OnSize(wxSizeEvent & WXUNUSED(event))
748 {
749  GetClientSize(&mWidth, &mHeight);
750 
751  mLayoutValid = false;
752  Refresh();
753 }
754 
755 bool MeterPanel::InIcon(wxMouseEvent *pEvent) const
756 {
757  auto point = pEvent ? pEvent->GetPosition() : ScreenToClient(::wxGetMousePosition());
758  return mIconRect.Contains(point);
759 }
760 
761 void MeterPanel::OnMouse(wxMouseEvent &evt)
762 {
763  bool shouldHighlight = InIcon(&evt);
764  if ((evt.GetEventType() == wxEVT_MOTION || evt.Entering() || evt.Leaving()) &&
765  (mHighlighted != shouldHighlight)) {
766  mHighlighted = shouldHighlight;
767  mLayoutValid = false;
768  Refresh();
769  }
770 
771  if (mStyle == MixerTrackCluster) // MixerTrackCluster style has no menu.
772  return;
773 
774  #if wxUSE_TOOLTIPS // Not available in wxX11
775  if (evt.Leaving()){
776  ProjectStatus::Get( *mProject ).Set({});
777  }
778  else if (evt.Entering()) {
779  // Display the tooltip in the status bar
780  wxToolTip * pTip = this->GetToolTip();
781  if( pTip ) {
782  auto tipText = Verbatim( pTip->GetTip() );
783  ProjectStatus::Get( *mProject ).Set(tipText);
784  }
785  }
786  #endif
787 
788  if (evt.RightDown() ||
789  (evt.ButtonDown() && InIcon(&evt)))
790  {
791  wxMenu menu;
792  // Note: these should be kept in the same order as the enum
793  if (mIsInput) {
794  wxMenuItem *mi;
795  if (mMonitoring)
796  mi = menu.Append(OnMonitorID, _("Stop Monitoring"));
797  else
798  mi = menu.Append(OnMonitorID, _("Start Monitoring"));
799  mi->Enable(!mActive || mMonitoring);
800  }
801 
802  menu.Append(OnPreferencesID, _("Options..."));
803 
804  if (evt.RightDown()) {
805  ShowMenu(evt.GetPosition());
806  }
807  else {
808  ShowMenu(wxPoint(mIconRect.x + 1, mIconRect.y + mIconRect.height + 1));
809  }
810  }
811  else if (evt.LeftDown()) {
812  if (mIsInput) {
813  if (mActive && !mMonitoring) {
814  Reset(mRate, true);
815  }
816  else {
817  StartMonitoring();
818  }
819  }
820  else {
821  Reset(mRate, true);
822  }
823  }
824 }
825 
826 void MeterPanel::OnContext(wxContextMenuEvent &evt)
827 {
828 #if defined(__WXMSW__)
829  if (mHadKeyDown)
830 #endif
831  if (mStyle != MixerTrackCluster) // MixerTrackCluster style has no menu.
832  {
833  ShowMenu(wxPoint(mIconRect.x + 1, mIconRect.y + mIconRect.height + 1));
834  }
835  else
836  {
837  evt.Skip();
838  }
839 
840 #if defined(__WXMSW__)
841  mHadKeyDown = false;
842 #endif
843 }
844 
845 void MeterPanel::OnKeyDown(wxKeyEvent &evt)
846 {
847  switch (evt.GetKeyCode())
848  {
849  // These are handled in the OnKeyUp handler because, on Windows at least, the
850  // key up event will be passed on to the menu if we show it here. This causes
851  // the default sound to be heard if assigned.
852  //
853  // But, again on Windows, when the user selects a menu item, it is handled by
854  // the menu and the key up event is passed along to our OnKeyUp() handler, so
855  // we have to ignore it, otherwise we'd just show the menu again.
856  case WXK_RETURN:
857  case WXK_NUMPAD_ENTER:
858  case WXK_WINDOWS_MENU:
859  case WXK_MENU:
860 #if defined(__WXMSW__)
861  mHadKeyDown = true;
862 #endif
863  break;
864  case WXK_RIGHT:
865  Navigate(wxNavigationKeyEvent::IsForward);
866  break;
867  case WXK_LEFT:
868  Navigate(wxNavigationKeyEvent::IsBackward);
869  break;
870  case WXK_TAB:
871  if (evt.ShiftDown())
872  Navigate(wxNavigationKeyEvent::IsBackward);
873  else
874  Navigate(wxNavigationKeyEvent::IsForward);
875  break;
876  default:
877  evt.Skip();
878  break;
879  }
880 }
881 
882 void MeterPanel::OnKeyUp(wxKeyEvent &evt)
883 {
884  switch (evt.GetKeyCode())
885  {
886  case WXK_RETURN:
887  case WXK_NUMPAD_ENTER:
888 #if defined(__WXMSW__)
889  if (mHadKeyDown)
890 #endif
891  if (mStyle != MixerTrackCluster) // MixerTrackCluster style has no menu.
892  {
893  ShowMenu(wxPoint(mIconRect.x + 1, mIconRect.y + mIconRect.height + 1));
894  }
895 #if defined(__WXMSW__)
896  mHadKeyDown = false;
897 #endif
898  break;
899  default:
900  evt.Skip();
901  break;
902  }
903 }
904 
905 void MeterPanel::OnSetFocus(wxFocusEvent & WXUNUSED(evt))
906 {
907  mIsFocused = true;
908  Refresh(false);
909 }
910 
911 void MeterPanel::OnKillFocus(wxFocusEvent & WXUNUSED(evt))
912 {
913  mIsFocused = false;
914  Refresh(false);
915 }
916 
918 {
919  if (mStyle != newStyle && mDesiredStyle == AutomaticStereo)
920  {
921  SetActiveStyle(newStyle);
922 
923  mLayoutValid = false;
924 
925  Refresh(false);
926  }
927 }
928 
929 void MeterPanel::Reset(double sampleRate, bool resetClipping)
930 {
931  mT = 0;
932  mRate = sampleRate;
933  for (int j = 0; j < kMaxMeterBars; j++)
934  {
935  ResetBar(&mBar[j], resetClipping);
936  }
937 
938  // wxTimers seem to be a little unreliable - sometimes they stop for
939  // no good reason, so this "primes" it every now and then...
940  mTimer.Stop();
941 
942  // While it's stopped, empty the queue
943  mQueue.Clear();
944 
945  mLayoutValid = false;
946 
947  mTimer.Start(1000 / mMeterRefreshRate);
948 
949  Refresh(false);
950 }
951 
952 static float floatMax(float a, float b)
953 {
954  return a>b? a: b;
955 }
956 
957 /* Unused as yet.
958 static int intmin(int a, int b)
959 {
960  return a<b? a: b;
961 }
962 */
963 
964 static int intmax(int a, int b)
965 {
966  return a>b? a: b;
967 }
968 
969 static float ClipZeroToOne(float z)
970 {
971  if (z > 1.0)
972  return 1.0;
973  else if (z < 0.0)
974  return 0.0;
975  else
976  return z;
977 }
978 
979 static float ToDB(float v, float range)
980 {
981  double db;
982  if (v > 0)
983  db = LINEAR_TO_DB(fabs(v));
984  else
985  db = -999;
986  return ClipZeroToOne((db + range) / range);
987 }
988 
989 void MeterPanel::UpdateDisplay(unsigned numChannels, int numFrames, float *sampleData)
990 {
991  float *sptr = sampleData;
992  auto num = std::min(numChannels, mNumBars);
993  MeterUpdateMsg msg;
994 
995  memset(&msg, 0, sizeof(msg));
996  msg.numFrames = numFrames;
997 
998  for(int i=0; i<numFrames; i++) {
999  for(unsigned int j=0; j<num; j++) {
1000  msg.peak[j] = floatMax(msg.peak[j], fabs(sptr[j]));
1001  msg.rms[j] += sptr[j]*sptr[j];
1002 
1003  // In addition to looking for mNumPeakSamplesToClip peaked
1004  // samples in a row, also send the number of peaked samples
1005  // at the head and tail, in case there's a run of peaked samples
1006  // that crosses block boundaries
1007  if (fabs(sptr[j])>=MAX_AUDIO) {
1008  if (msg.headPeakCount[j]==i)
1009  msg.headPeakCount[j]++;
1010  msg.tailPeakCount[j]++;
1011  if (msg.tailPeakCount[j] > mNumPeakSamplesToClip)
1012  msg.clipping[j] = true;
1013  }
1014  else
1015  msg.tailPeakCount[j] = 0;
1016  }
1017  sptr += numChannels;
1018  }
1019  for(unsigned int j=0; j<mNumBars; j++)
1020  msg.rms[j] = sqrt(msg.rms[j]/numFrames);
1021 
1022  mQueue.Put(msg);
1023 }
1024 
1025 // Vaughan, 2010-11-29: This not currently used. See comments in MixerTrackCluster::UpdateMeter().
1026 //void MeterPanel::UpdateDisplay(int numChannels, int numFrames,
1027 // // Need to make these double-indexed arrays if we handle more than 2 channels.
1028 // float* maxLeft, float* rmsLeft,
1029 // float* maxRight, float* rmsRight,
1030 // const size_t kSampleCount)
1031 //{
1032 // int i, j;
1033 // int num = intmin(numChannels, mNumBars);
1034 // MeterUpdateMsg msg;
1035 //
1036 // msg.numFrames = kSampleCount;
1037 // for(j=0; j<mNumBars; j++) {
1038 // msg.peak[j] = 0.0;
1039 // msg.rms[j] = 0.0;
1040 // msg.clipping[j] = false;
1041 // msg.headPeakCount[j] = 0;
1042 // msg.tailPeakCount[j] = 0;
1043 // }
1044 //
1045 // for(i=0; i<numFrames; i++) {
1046 // for(j=0; j<num; j++) {
1047 // msg.peak[j] = floatMax(msg.peak[j], ((j == 0) ? maxLeft[i] : maxRight[i]));
1048 // msg.rms[j] = floatMax(msg.rms[j], ((j == 0) ? rmsLeft[i] : rmsRight[i]));
1049 //
1050 // // In addition to looking for mNumPeakSamplesToClip peaked
1051 // // samples in a row, also send the number of peaked samples
1052 // // at the head and tail, in case there's a run
1053 // // of peaked samples that crosses block boundaries.
1054 // if (fabs((j == 0) ? maxLeft[i] : maxRight[i]) >= MAX_AUDIO)
1055 // {
1056 // if (msg.headPeakCount[j]==i)
1057 // msg.headPeakCount[j]++;
1058 // msg.tailPeakCount[j]++;
1059 // if (msg.tailPeakCount[j] > mNumPeakSamplesToClip)
1060 // msg.clipping[j] = true;
1061 // }
1062 // else
1063 // msg.tailPeakCount[j] = 0;
1064 // }
1065 // }
1066 //
1067 // mQueue.Put(msg);
1068 //}
1069 
1070 void MeterPanel::OnMeterUpdate(wxTimerEvent & WXUNUSED(event))
1071 {
1072  MeterUpdateMsg msg;
1073  int numChanges = 0;
1074 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1075  double maxPeak = 0.0;
1076  bool discarded = false;
1077 #endif
1078 
1079  // We shouldn't receive any events if the meter is disabled, but clear it to be safe
1080  if (mMeterDisabled) {
1081  mQueue.Clear();
1082  return;
1083  }
1084 
1085  auto gAudioIO = AudioIO::Get();
1086 
1087  // There may have been several update messages since the last
1088  // time we got to this function. Catch up to real-time by
1089  // popping them off until there are none left. It is necessary
1090  // to process all of them, otherwise we won't handle peaks and
1091  // peak-hold bars correctly.
1092  while(mQueue.Get(msg)) {
1093  numChanges++;
1094  double deltaT = msg.numFrames / mRate;
1095 
1096  mT += deltaT;
1097  for(unsigned int j=0; j<mNumBars; j++) {
1098  mBar[j].isclipping = false;
1099 
1100  //
1101  if (mDB) {
1102  msg.peak[j] = ToDB(msg.peak[j], mDBRange);
1103  msg.rms[j] = ToDB(msg.rms[j], mDBRange);
1104  }
1105 
1106  if (mDecay) {
1107  if (mDB) {
1108  float decayAmount = mDecayRate * deltaT / mDBRange;
1109  mBar[j].peak = floatMax(msg.peak[j],
1110  mBar[j].peak - decayAmount);
1111  }
1112  else {
1113  double decayAmount = mDecayRate * deltaT;
1114  double decayFactor = DB_TO_LINEAR(-decayAmount);
1115  mBar[j].peak = floatMax(msg.peak[j],
1116  mBar[j].peak * decayFactor);
1117  }
1118  }
1119  else
1120  mBar[j].peak = msg.peak[j];
1121 
1122  // This smooths out the RMS signal
1123  float smooth = pow(0.9, (double)msg.numFrames/1024.0);
1124  mBar[j].rms = mBar[j].rms * smooth + msg.rms[j] * (1.0 - smooth);
1125 
1126  if (mT - mBar[j].peakHoldTime > mPeakHoldDuration ||
1127  mBar[j].peak > mBar[j].peakHold) {
1128  mBar[j].peakHold = mBar[j].peak;
1129  mBar[j].peakHoldTime = mT;
1130  }
1131 
1132  if (mBar[j].peak > mBar[j].peakPeakHold )
1133  mBar[j].peakPeakHold = mBar[j].peak;
1134 
1135  if (msg.clipping[j] ||
1136  mBar[j].tailPeakCount+msg.headPeakCount[j] >=
1138  mBar[j].clipping = true;
1139  mBar[j].isclipping = true;
1140  }
1141 
1142  mBar[j].tailPeakCount = msg.tailPeakCount[j];
1143 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1144  if (mT > gAudioIO->AILAGetLastDecisionTime()) {
1145  discarded = false;
1146  maxPeak = msg.peak[j] > maxPeak ? msg.peak[j] : maxPeak;
1147  wxPrintf("%[email protected]%f ", msg.peak[j], mT);
1148  }
1149  else {
1150  discarded = true;
1151  wxPrintf("%[email protected]%f discarded\n", msg.peak[j], mT);
1152  }
1153 #endif
1154  }
1155  } // while
1156 
1157  if (numChanges > 0) {
1158  #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1159  if (gAudioIO->AILAIsActive() && mIsInput && !discarded) {
1160  gAudioIO->AILAProcess(maxPeak);
1161  putchar('\n');
1162  }
1163  #endif
1164  RepaintBarsNow();
1165  }
1166 }
1167 
1169 {
1170  float maxPeak = 0.;
1171 
1172  for(unsigned int j=0; j<mNumBars; j++)
1173  maxPeak = mBar[j].peak > maxPeak ? mBar[j].peak : maxPeak;
1174 
1175  return(maxPeak);
1176 }
1177 
1178 wxFont MeterPanel::GetFont() const
1179 {
1180  int fontSize = 10;
1181 #if defined __WXMSW__
1182  fontSize = 8;
1183 #endif
1184 
1185  return wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1186 }
1187 
1188 void MeterPanel::ResetBar(MeterBar *b, bool resetClipping)
1189 {
1190  b->peak = 0.0;
1191  b->rms = 0.0;
1192  b->peakHold = 0.0;
1193  b->peakHoldTime = 0.0;
1194  if (resetClipping)
1195  {
1196  b->clipping = false;
1197  b->peakPeakHold = 0.0;
1198  }
1199  b->isclipping = false;
1200  b->tailPeakCount = 0;
1201 }
1202 
1204 {
1205  for (int c = 0; c < kMaxMeterBars; c++)
1206  if (mBar[c].isclipping)
1207  return true;
1208  return false;
1209 }
1210 
1212 {
1213  mStyle = newStyle;
1214 
1215  // Set dummy ruler bounds so width/height can be retrieved
1216  // NOTE: Make sure the Right and Bottom values are large enough to
1217  // ensure full width/height of digits get calculated.
1218  mRuler.SetBounds(0, 0, 500, 500);
1219 
1220  if (mDB)
1221  {
1224  {
1225  mRuler.SetOrientation(wxHORIZONTAL);
1226  mRuler.SetRange(-mDBRange, 0);
1227  }
1228  else
1229  {
1230  mRuler.SetOrientation(wxVERTICAL);
1231  mRuler.SetRange(0, -mDBRange);
1232  }
1233  }
1234  else
1235  {
1238  {
1239  mRuler.SetOrientation(wxHORIZONTAL);
1240  mRuler.SetRange(0, 1);
1241  }
1242  else
1243  {
1244  mRuler.SetOrientation(wxVERTICAL);
1245  mRuler.SetRange(1, 0);
1246  }
1247  }
1248 
1250 }
1251 
1252 void MeterPanel::SetBarAndClip(int iBar, bool vert)
1253 {
1254  // Save the orientation
1255  mBar[iBar].vert = vert;
1256 
1257  // Create the bar rectangle and educe to fit inside the bevel
1258  mBar[iBar].r = mBar[iBar].b;
1259  mBar[iBar].r.x += 1;
1260  mBar[iBar].r.width -= 1;
1261  mBar[iBar].r.y += 1;
1262  mBar[iBar].r.height -= 1;
1263 
1264  if (vert)
1265  {
1266  if (mClip)
1267  {
1268  // Create the clip rectangle
1269  mBar[iBar].rClip = mBar[iBar].b;
1270  mBar[iBar].rClip.height = 3;
1271 
1272  // Make room for the clipping indicator
1273  mBar[iBar].b.y += 3 + gap;
1274  mBar[iBar].b.height -= 3 + gap;
1275  mBar[iBar].r.y += 3 + gap;
1276  mBar[iBar].r.height -= 3 + gap;
1277  }
1278  }
1279  else
1280  {
1281  if (mClip)
1282  {
1283  // Make room for the clipping indicator
1284  mBar[iBar].b.width -= 4;
1285  mBar[iBar].r.width -= 4;
1286 
1287  // Create the indicator rectangle
1288  mBar[iBar].rClip = mBar[iBar].b;
1289  mBar[iBar].rClip.x = mBar[iBar].b.GetRight() + 1 + gap; // +1 for bevel
1290  mBar[iBar].rClip.width = 3;
1291  }
1292  }
1293 }
1294 
1296 {
1297  // Refresh to reflect any language changes
1298  /* i18n-hint: One-letter abbreviation for Left, in VU Meter */
1299  mLeftText = _("L");
1300  /* i18n-hint: One-letter abbreviation for Right, in VU Meter */
1301  mRightText = _("R");
1302 
1303  dc.SetFont(GetFont());
1304  int iconWidth = 0;
1305  int iconHeight = 0;
1306  int width = mWidth;
1307  int height = mHeight;
1308  int left = 0;
1309  int top = 0;
1310  int barw;
1311  int barh;
1312  int lside;
1313  int rside;
1314 
1315  // MixerTrackCluster has no L/R labels or icon
1316  if (mStyle != MixerTrackCluster)
1317  {
1319  {
1320  SetActiveStyle(width > height ? HorizontalStereo : VerticalStereo);
1321  }
1322 
1324  {
1326  }
1328  {
1330  }
1331 
1332  iconWidth = mIcon->GetWidth();
1333  iconHeight = mIcon->GetHeight();
1334  if (mLeftSize.GetWidth() == 0) // Not yet initialized to dc.
1335  {
1336  dc.GetTextExtent(mLeftText, &mLeftSize.x, &mLeftSize.y);
1337  dc.GetTextExtent(mRightText, &mRightSize.x, &mRightSize.y);
1338  }
1339  }
1340 
1341  int ltxtWidth = mLeftSize.GetWidth();
1342  int ltxtHeight = mLeftSize.GetHeight();
1343  int rtxtWidth = mRightSize.GetWidth();
1344  int rtxtHeight = mRightSize.GetHeight();
1345 
1346  switch (mStyle)
1347  {
1348  default:
1349  wxPrintf(wxT("Style not handled yet!\n"));
1350  break;
1351  case MixerTrackCluster:
1352  // width is now the entire width of the meter canvas
1353  width -= mRulerWidth + left;
1354 
1355  // height is now the entire height of the meter canvas
1356  height -= top + gap;
1357 
1358  // barw is half of the canvas while allowing for a gap between meters
1359  barw = (width - gap) / 2;
1360 
1361  // barh is now the height of the canvas
1362  barh = height;
1363 
1364  // We always have 2 bars
1365  mNumBars = 2;
1366 
1367  // Save dimensions of the left bevel
1368  mBar[0].b = wxRect(left, top, barw, barh);
1369 
1370  // Save dimensions of the right bevel
1371  mBar[1].b = mBar[0].b;
1372  mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge
1373 
1374  // Set bar and clipping indicator dimensions
1375  SetBarAndClip(0, true);
1376  SetBarAndClip(1, true);
1377 
1378  mRuler.SetBounds(mBar[1].r.GetRight() + 1, // +1 for the bevel
1379  mBar[1].r.GetTop(),
1380  mWidth,
1381  mBar[1].r.GetBottom());
1382  mRuler.OfflimitsPixels(0, 0);
1383  break;
1384  case VerticalStereo:
1385  // Determine required width of each side;
1386  lside = intmax(iconWidth, ltxtWidth);
1387  rside = intmax(mRulerWidth, rtxtWidth);
1388 
1389  // left is now the right edge of the icon or L label
1390  left = lside;
1391 
1392  // Ensure there's a margin between top edge of window and the meters
1393  top = gap;
1394 
1395  // Position the icon
1396  mIconRect.SetX(left - iconWidth);
1397  mIconRect.SetY(top);
1398  mIconRect.SetWidth(iconWidth);
1399  mIconRect.SetHeight(iconHeight);
1400 
1401  // Position the L/R labels
1402  mLeftTextPos = wxPoint(left - ltxtWidth - gap, height - gap - ltxtHeight);
1403  mRightTextPos = wxPoint(width - rside - gap, height - gap - rtxtHeight);
1404 
1405  // left is now left edge of left bar
1406  left += gap;
1407 
1408  // width is now the entire width of the meter canvas
1409  width -= gap + rside + gap + left;
1410 
1411  // height is now the entire height of the meter canvas
1412  height -= top + gap;
1413 
1414  // barw is half of the canvas while allowing for a gap between meters
1415  barw = (width - gap) / 2;
1416 
1417  // barh is now the height of the canvas
1418  barh = height;
1419 
1420  // We always have 2 bars
1421  mNumBars = 2;
1422 
1423  // Save dimensions of the left bevel
1424  mBar[0].b = wxRect(left, top, barw, barh);
1425 
1426  // Save dimensions of the right bevel
1427  mBar[1].b = mBar[0].b;
1428  mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge
1429 
1430  // Set bar and clipping indicator dimensions
1431  SetBarAndClip(0, true);
1432  SetBarAndClip(1, true);
1433 
1434  mRuler.SetBounds(mBar[1].r.GetRight() + 1, // +1 for the bevel
1435  mBar[1].r.GetTop(),
1436  mWidth,
1437  mBar[1].r.GetBottom());
1438  mRuler.OfflimitsPixels(mRightTextPos.y - gap, mBar[1].r.GetBottom());
1439  break;
1440  case VerticalStereoCompact:
1441  // Ensure there's a margin between top edge of window and the meters
1442  top = gap;
1443 
1444  // Position the icon
1445  mIconRect.SetX((width - iconWidth) / 2);
1446  mIconRect.SetY(top);
1447  mIconRect.SetWidth(iconWidth);
1448  mIconRect.SetHeight(iconHeight);
1449 
1450  // top is now the top of the bar
1451  top += iconHeight + gap;
1452 
1453  // height is now the entire height of the meter canvas
1454  height -= top + gap + ltxtHeight + gap;
1455 
1456  // barw is half of the canvas while allowing for a gap between meters
1457  barw = (width / 2) - gap;
1458 
1459  // barh is now the height of the canvas
1460  barh = height;
1461 
1462  // We always have 2 bars
1463  mNumBars = 2;
1464 
1465  // Save dimensions of the left bevel
1466  mBar[0].b = wxRect(left, top, barw, barh);
1467 
1468  // Save dimensions of the right bevel
1469  mBar[1].b = mBar[0].b;
1470  mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge
1471 
1472  // Set bar and clipping indicator dimensions
1473  SetBarAndClip(0, true);
1474  SetBarAndClip(1, true);
1475 
1476  // L/R is centered horizontally under each bar
1477  mLeftTextPos = wxPoint(mBar[0].b.GetLeft() + ((mBar[0].b.GetWidth() - ltxtWidth) / 2), top + barh + gap);
1478  mRightTextPos = wxPoint(mBar[1].b.GetLeft() + ((mBar[1].b.GetWidth() - rtxtWidth) / 2), top + barh + gap);
1479 
1481  mBar[1].r.GetTop(),
1482  (mWidth - mRulerWidth) / 2,
1483  mBar[1].r.GetBottom());
1484  mRuler.OfflimitsPixels(0, 0);
1485  break;
1486  case HorizontalStereo:
1487  // Button right next to dragger.
1488  left = 0;
1489 
1490  // Add a gap between bottom of icon and bottom of window
1491  height -= gap;
1492 
1493  // Create icon rectangle
1494  mIconRect.SetX(left);
1495  mIconRect.SetY(height - iconHeight);
1496  mIconRect.SetWidth(iconWidth);
1497  mIconRect.SetHeight(iconHeight);
1498  left = gap;
1499 
1500  // Make sure there's room for icon and gap between the bottom of the meter and icon
1501  height -= iconHeight + gap;
1502 
1503  // L/R is centered vertically and to the left of a each bar
1504  mLeftTextPos = wxPoint(left, (height / 4) - ltxtHeight / 2);
1505  mRightTextPos = wxPoint(left, (height * 3 / 4) - rtxtHeight / 2);
1506 
1507  // Add width of widest of the L/R characters
1508  left += intmax(ltxtWidth, rtxtWidth); //, iconWidth);
1509 
1510  // Add gap between L/R and meter bevel
1511  left += gap;
1512 
1513  // width is now the entire width of the meter canvas
1514  width -= left;
1515 
1516  // barw is now the width of the canvas minus gap between canvas and right window edge
1517  barw = width - gap;
1518 
1519  // barh is half of the canvas while allowing for a gap between meters
1520  barh = (height - gap) / 2;
1521 
1522  // We always have 2 bars
1523  mNumBars = 2;
1524 
1525  // Save dimensions of the top bevel
1526  mBar[0].b = wxRect(left, top, barw, barh);
1527 
1528  // Save dimensions of the bottom bevel
1529  mBar[1].b = mBar[0].b;
1530  mBar[1].b.SetTop(mBar[0].b.GetBottom() + 1 + gap); // +1 for bottom edge
1531 
1532  // Set bar and clipping indicator dimensions
1533  SetBarAndClip(0, false);
1534  SetBarAndClip(1, false);
1535 
1536  mRuler.SetBounds(mBar[1].r.GetLeft(),
1537  mBar[1].r.GetBottom() + 1, // +1 to fit below bevel
1538  mBar[1].r.GetRight(),
1539  mHeight - mBar[1].r.GetBottom() + 1);
1540  mRuler.OfflimitsPixels(0, mIconRect.GetRight() - 4);
1541  break;
1543  // Button right next to dragger.
1544  left = 0;
1545 
1546  // Create icon rectangle
1547  mIconRect.SetX(left);
1548  mIconRect.SetY((height - iconHeight) / 2 -1);
1549  mIconRect.SetWidth(iconWidth);
1550  mIconRect.SetHeight(iconHeight);
1551 
1552  left = gap;
1553  // Add width of icon and gap between icon and L/R
1554  left += iconWidth + gap;
1555 
1556  // L/R is centered vertically and to the left of a each bar
1557  mLeftTextPos = wxPoint(left, (height / 4) - (ltxtHeight / 2));
1558  mRightTextPos = wxPoint(left, (height * 3 / 4) - (ltxtHeight / 2));
1559 
1560  // Add width of widest of the L/R characters and a gap between labels and meter bevel
1561  left += intmax(ltxtWidth, rtxtWidth) + gap;
1562 
1563  // width is now the entire width of the meter canvas
1564  width -= left;
1565 
1566  // barw is now the width of the canvas minus gap between canvas and window edge
1567  barw = width - gap;
1568 
1569  // barh is half of the canvas while allowing for a gap between meters
1570  barh = (height - gap) / 2;
1571 
1572  // We always have 2 bars
1573  mNumBars = 2;
1574 
1575  // Save dimensions of the top bevel
1576  mBar[0].b = wxRect(left, top, barw, barh);
1577 
1578  // Save dimensions of the bottom bevel
1579  // Since the bars butt up against the window's top and bottom edges, we need
1580  // to include an extra pixel in the bottom bar when the window height and
1581  // meter height do not exactly match.
1582  mBar[1].b = mBar[0].b;
1583  mBar[1].b.SetTop(mBar[0].b.GetBottom() + 1 + gap); // +1 for bottom bevel
1584  mBar[1].b.SetHeight(mHeight - mBar[1].b.GetTop() - 1); // +1 for bottom bevel
1585 
1586  // Add clipping indicators - do after setting bar/bevel dimensions above
1587  SetBarAndClip(0, false);
1588  SetBarAndClip(1, false);
1589 
1590  mRuler.SetBounds(mBar[1].r.GetLeft(),
1591  mBar[1].b.GetTop() - (mRulerHeight / 2),
1592  mBar[1].r.GetRight(),
1593  mBar[1].b.GetTop() - (mRulerHeight / 2));
1594  mRuler.OfflimitsPixels(0, 0);
1595  break;
1596  }
1597 
1598  mLayoutValid = true;
1599 }
1600 
1602 {
1603  if (mLayoutValid)
1604  {
1605  // Invalidate the bars so they get redrawn
1606  for (unsigned int i = 0; i < mNumBars; i++)
1607  {
1608  Refresh(false);
1609  }
1610 
1611  // Immediate redraw (using wxPaintDC)
1612  Update();
1613 
1614  return;
1615  }
1616 }
1617 
1619 {
1620  // Cache some metrics
1621  wxCoord x = bar->r.GetLeft();
1622  wxCoord y = bar->r.GetTop();
1623  wxCoord w = bar->r.GetWidth();
1624  wxCoord h = bar->r.GetHeight();
1625  wxCoord ht;
1626  wxCoord wd;
1627 
1628  // Setup for erasing the background
1629  dc.SetPen(*wxTRANSPARENT_PEN);
1631 
1632  if (mGradient)
1633  {
1634  // Map the predrawn bitmap into the source DC
1635  wxMemoryDC srcDC;
1636  srcDC.SelectObject(*mBitmap);
1637 
1638  if (bar->vert)
1639  {
1640  // Copy as much of the predrawn meter bar as is required for the
1641  // current peak.
1642  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1643  ht = (int)(bar->peak * (h - 1) + 0.5);
1644 
1645  // Blank out the rest
1646  if (h - ht)
1647  {
1648  // ht includes peak value...not really needed but doesn't hurt
1649  dc.DrawRectangle(x, y, w, h - ht);
1650  }
1651 
1652  // Copy as much of the predrawn meter bar as is required for the
1653  // current peak.
1654  // +/-1 to include the peak position
1655  if (ht)
1656  {
1657  dc.Blit(x, y + h - ht - 1, w, ht + 1, &srcDC, x, y + h - ht - 1);
1658  }
1659 
1660  // Draw the "recent" peak hold line using the predrawn meter bar so that
1661  // it will be the same color as the original level.
1662  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1663  ht = (int)(bar->peakHold * (h - 1) + 0.5);
1664  if (ht > 1)
1665  {
1666  dc.Blit(x, y + h - ht - 1, w, 2, &srcDC, x, y + h - ht - 1);
1667  }
1668 
1669  // Draw the "maximum" peak hold line
1670  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1671  dc.SetPen(mPeakPeakPen);
1672  ht = (int)(bar->peakPeakHold * (h - 1) + 0.5);
1673  if (ht > 0)
1674  {
1675  AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1);
1676  if (ht > 1)
1677  {
1678  AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht);
1679  }
1680  }
1681  }
1682  else
1683  {
1684  // Calculate the peak position
1685  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1686  wd = (int)(bar->peak * (w - 1) + 0.5);
1687 
1688  // Blank out the rest
1689  if (w - wd)
1690  {
1691  // wd includes peak value...not really needed but doesn't hurt
1692  dc.DrawRectangle(x + wd, y, w - wd, h);
1693  }
1694 
1695  // Copy as much of the predrawn meter bar as is required for the
1696  // current peak. But, only blit() if there's something to copy
1697  // to prevent display corruption.
1698  // +1 to include peak position
1699  if (wd)
1700  {
1701  dc.Blit(x, y, wd + 1, h, &srcDC, x, y);
1702  }
1703 
1704  // Draw the "recent" peak hold line using the predrawn meter bar so that
1705  // it will be the same color as the original level.
1706  // -1 to give a 2 pixel width
1707  wd = (int)(bar->peakHold * (w - 1) + 0.5);
1708  if (wd > 1)
1709  {
1710  dc.Blit(x + wd - 1, y, 2, h, &srcDC, x + wd, y);
1711  }
1712 
1713  // Draw the "maximum" peak hold line using a themed color
1714  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1715  dc.SetPen(mPeakPeakPen);
1716  wd = (int)(bar->peakPeakHold * (w - 1) + 0.5);
1717  if (wd > 0)
1718  {
1719  AColor::Line(dc, x + wd, y, x + wd, y + h - 1);
1720  if (wd > 1)
1721  {
1722  AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1);
1723  }
1724  }
1725  }
1726 
1727  // No longer need the source DC, so unselect the predrawn bitmap
1728  srcDC.SelectObject(wxNullBitmap);
1729  }
1730  else
1731  {
1732  if (bar->vert)
1733  {
1734  // Calculate the peak position
1735  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1736  ht = (int)(bar->peak * (h - 1) + 0.5);
1737 
1738  // Blank out the rest
1739  if (h - ht)
1740  {
1741  // ht includes peak value...not really needed but doesn't hurt
1742  dc.DrawRectangle(x, y, w, h - ht);
1743  }
1744 
1745  // Draw the peak level
1746  // +/-1 to include the peak position
1747  dc.SetPen(*wxTRANSPARENT_PEN);
1748  dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mBrush);
1749  if (ht)
1750  {
1751  dc.DrawRectangle(x, y + h - ht - 1, w, ht + 1);
1752  }
1753 
1754  // Draw the "recent" peak hold line
1755  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1756  dc.SetPen(mPen);
1757  ht = (int)(bar->peakHold * (h - 1) + 0.5);
1758  if (ht > 0)
1759  {
1760  AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1);
1761  if (ht > 1)
1762  {
1763  AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht);
1764  }
1765  }
1766 
1767  // Calculate the rms position
1768  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1769  // +1 to include the rms position
1770  ht = (int)(bar->rms * (h - 1) + 0.5);
1771 
1772  // Draw the RMS level
1773  dc.SetPen(*wxTRANSPARENT_PEN);
1774  dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mRMSBrush);
1775  if (ht)
1776  {
1777  dc.DrawRectangle(x, y + h - ht - 1, w, ht + 1);
1778  }
1779 
1780  // Draw the "maximum" peak hold line
1781  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1782  dc.SetPen(mPeakPeakPen);
1783  ht = (int)(bar->peakPeakHold * (h - 1) + 0.5);
1784  if (ht > 0)
1785  {
1786  AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1);
1787  if (ht > 1)
1788  {
1789  AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht);
1790  }
1791  }
1792  }
1793  else
1794  {
1795  // Calculate the peak position
1796  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1797  wd = (int)(bar->peak * (w - 1) + 0.5);
1798 
1799  // Blank out the rest
1800  if (w - wd)
1801  {
1802  // wd includes peak value...not really needed but doesn't hurt
1803  dc.DrawRectangle(x + wd, y, w - wd, h);
1804  }
1805 
1806  // Draw the peak level
1807  // +1 to include peak position
1808  dc.SetPen(*wxTRANSPARENT_PEN);
1809  dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mBrush);
1810  if (wd)
1811  {
1812  dc.DrawRectangle(x, y, wd + 1, h);
1813  }
1814 
1815  // Draw the "recent" peak hold line
1816  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1817  dc.SetPen(mPen);
1818  wd = (int)(bar->peakHold * (w - 1) + 0.5);
1819  if (wd > 0)
1820  {
1821  AColor::Line(dc, x + wd, y, x + wd, y + h - 1);
1822  if (wd > 1)
1823  {
1824  AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1);
1825  }
1826  }
1827 
1828  // Calculate the rms position
1829  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1830  wd = (int)(bar->rms * (w - 1) + 0.5);
1831 
1832  // Draw the rms level
1833  // +1 to include the rms position
1834  dc.SetPen(*wxTRANSPARENT_PEN);
1835  dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mRMSBrush);
1836  if (wd)
1837  {
1838  dc.DrawRectangle(x, y, wd + 1, h);
1839  }
1840 
1841  // Draw the "maximum" peak hold line using a themed color
1842  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1843  dc.SetPen(mPeakPeakPen);
1844  wd = (int)(bar->peakPeakHold * (w - 1) + 0.5);
1845  if (wd > 0)
1846  {
1847  AColor::Line(dc, x + wd, y, x + wd, y + h - 1);
1848  if (wd > 1)
1849  {
1850  AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1);
1851  }
1852  }
1853  }
1854  }
1855 
1856  // If meter had a clipping indicator, draw or erase it
1857  // LLL: At least I assume that's what "mClip" is supposed to be for as
1858  // it is always "true".
1859  if (mClip)
1860  {
1861  if (bar->clipping)
1862  {
1863  dc.SetBrush(mClipBrush);
1864  }
1865  else
1866  {
1868  }
1869  dc.SetPen(*wxTRANSPARENT_PEN);
1870  wxRect r(bar->rClip.GetX() + 1,
1871  bar->rClip.GetY() + 1,
1872  bar->rClip.GetWidth() - 1,
1873  bar->rClip.GetHeight() - 1);
1874  dc.DrawRectangle(r);
1875  }
1876 }
1877 
1879 {
1880  return mMeterDisabled != 0;
1881 }
1882 
1884 {
1885  bool start = !mMonitoring;
1886 
1887  auto gAudioIO = AudioIO::Get();
1888  if (gAudioIO->IsMonitoring()){
1889  gAudioIO->StopStream();
1890  }
1891 
1892  if (start && !gAudioIO->IsBusy()){
1894  if (p){
1895  gAudioIO->StartMonitoring( DefaultPlayOptions( *p ) );
1896  }
1897 
1898  mLayoutValid = false;
1899 
1900  Refresh(false);
1901  }
1902 }
1903 
1905  mMonitoring = false;
1906  auto gAudioIO = AudioIO::Get();
1907  if (gAudioIO->IsMonitoring()){
1908  gAudioIO->StopStream();
1909  }
1910 }
1911 
1912 void MeterPanel::OnAudioIOStatus(wxCommandEvent &evt)
1913 {
1914  evt.Skip();
1915  AudacityProject *p = (AudacityProject *) evt.GetEventObject();
1916 
1917  mActive = (evt.GetInt() != 0) && (p == mProject);
1918 
1919  if( mActive ){
1920  mTimer.Start(1000 / mMeterRefreshRate);
1921  if (evt.GetEventType() == EVT_AUDIOIO_MONITOR)
1922  mMonitoring = mActive;
1923  } else {
1924  mTimer.Stop();
1925  mMonitoring = false;
1926  }
1927 
1928  // Only refresh is we're the active meter
1929  if (IsShownOnScreen())
1930  Refresh(false);
1931 }
1932 
1933 // SaveState() and RestoreState() exist solely for purpose of recreating toolbars
1934 // They should really be querying the project for current audio I/O state, but there
1935 // isn't a clear way of doing that just yet. (It should NOT query AudioIO.)
1937 {
1938  return { true, mMonitoring, mActive };
1939 }
1940 
1942 {
1943  if (!state.mSaved)
1944  return;
1945 
1946  mMonitoring = state.mMonitoring;
1947  mActive = state.mActive;
1948  //wxLogDebug("Restore state for %p, is %i", this, mActive );
1949 
1950  if (mActive)
1951  mTimer.Start(1000 / mMeterRefreshRate);
1952 }
1953 
1954 //
1955 // Pop-up menu
1956 //
1957 
1958 void MeterPanel::ShowMenu(const wxPoint & pos)
1959 {
1960  wxMenu menu;
1961  // Note: these should be kept in the same order as the enum
1962  if (mIsInput) {
1963  wxMenuItem *mi;
1964  if (mMonitoring)
1965  mi = menu.Append(OnMonitorID, _("Stop Monitoring"));
1966  else
1967  mi = menu.Append(OnMonitorID, _("Start Monitoring"));
1968  mi->Enable(!mActive || mMonitoring);
1969  }
1970 
1971  menu.Append(OnPreferencesID, _("Options..."));
1972 
1973  mAccSilent = true; // temporarily make screen readers say (close to) nothing on focus events
1974 
1975  PopupMenu(&menu, pos);
1976 
1977  /* if stop/start monitoring was chosen in the menu, then by this point
1978  OnMonitoring has been called and variables which affect the accessibility
1979  name have been updated so it's now ok for screen readers to read the name of
1980  the button */
1981  mAccSilent = false;
1982 #if wxUSE_ACCESSIBILITY
1983  GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
1984  this,
1985  wxOBJID_CLIENT,
1986  wxACC_SELF);
1987 #endif
1988 }
1989 
1990 void MeterPanel::OnMonitor(wxCommandEvent & WXUNUSED(event))
1991 {
1992  StartMonitoring();
1993 }
1994 
1995 void MeterPanel::OnPreferences(wxCommandEvent & WXUNUSED(event))
1996 {
1997  wxTextCtrl *rate;
1998  wxRadioButton *gradient;
1999  wxRadioButton *rms;
2000  wxRadioButton *db;
2001  wxRadioButton *linear;
2002  wxRadioButton *automatic;
2003  wxRadioButton *horizontal;
2004  wxRadioButton *vertical;
2005  int meterRefreshRate = mMeterRefreshRate;
2006 
2007  auto title = mIsInput ? XO("Recording Meter Options") : XO("Playback Meter Options");
2008 
2009  // Dialog is a child of the project, rather than of the toolbar.
2010  // This determines where it pops up.
2011 
2012  wxDialogWrapper dlg( FindProjectFrame( mProject ), wxID_ANY, title);
2013  dlg.SetName();
2014  ShuttleGui S(&dlg, eIsCreating);
2015  S.StartVerticalLay();
2016  {
2017  S.StartStatic(XO("Refresh Rate"), 0);
2018  {
2019  S.AddFixedText(XO(
2020 "Higher refresh rates make the meter show more frequent\nchanges. A rate of 30 per second or less should prevent\nthe meter affecting audio quality on slower machines."));
2021  S.StartHorizontalLay();
2022  {
2023  rate = S.Name(XO("Meter refresh rate per second [1-100]"))
2024  .Validator<IntegerValidator<long>>(
2025  &mMeterRefreshRate, NumValidatorStyle::DEFAULT,
2027  .AddTextBox(XXO("Meter refresh rate per second [1-100]: "),
2028  wxString::Format(wxT("%d"), meterRefreshRate),
2029  10);
2030  }
2031  S.EndHorizontalLay();
2032  }
2033  S.EndStatic();
2034 
2035  S.StartHorizontalLay();
2036  {
2037  S.StartStatic(XO("Meter Style"), 0);
2038  {
2039  S.StartVerticalLay();
2040  {
2041  gradient = S.AddRadioButton(XXO("Gradient"), true, mGradient);
2042  rms = S.AddRadioButtonToGroup(XXO("RMS"), false, mGradient);
2043  }
2044  S.EndVerticalLay();
2045  }
2046  S.EndStatic();
2047 
2048  S.StartStatic(XO("Meter Type"), 0);
2049  {
2050  S.StartVerticalLay();
2051  {
2052  db = S.AddRadioButton(XXO("dB"), true, mDB);
2053  linear = S.AddRadioButtonToGroup(XXO("Linear"), false, mDB);
2054  }
2055  S.EndVerticalLay();
2056  }
2057  S.EndStatic();
2058 
2059  S.StartStatic(XO("Orientation"), 1);
2060  {
2061  S.StartVerticalLay();
2062  {
2063  automatic = S.AddRadioButton(
2064  XXO("Automatic"), AutomaticStereo, mDesiredStyle);
2065  horizontal = S.AddRadioButtonToGroup(
2066  XXO("Horizontal"), HorizontalStereo, mDesiredStyle);
2067  vertical = S.AddRadioButtonToGroup(
2068  XXO("Vertical"), VerticalStereo, mDesiredStyle);
2069  }
2070  S.EndVerticalLay();
2071  }
2072  S.EndStatic();
2073  }
2074  S.EndHorizontalLay();
2075  S.AddStandardButtons();
2076  }
2077  S.EndVerticalLay();
2078  dlg.Layout();
2079  dlg.Fit();
2080 
2081  dlg.CenterOnParent();
2082 
2083  if (dlg.ShowModal() == wxID_OK)
2084  {
2085  wxArrayStringEx style{
2086  wxT("AutomaticStereo") ,
2087  wxT("HorizontalStereo") ,
2088  wxT("VerticalStereo") ,
2089  };
2090 
2091  int s = 0;
2092  s = automatic->GetValue() ? 0 : s;
2093  s = horizontal->GetValue() ? 1 : s;
2094  s = vertical->GetValue() ? 2 : s;
2095 
2096  gPrefs->Write(Key(wxT("Style")), style[s]);
2097  gPrefs->Write(Key(wxT("Bars")), gradient->GetValue() ? wxT("Gradient") : wxT("RMS"));
2098  gPrefs->Write(Key(wxT("Type")), db->GetValue() ? wxT("dB") : wxT("Linear"));
2099  gPrefs->Write(Key(wxT("RefreshRate")), rate->GetValue());
2100 
2101  gPrefs->Flush();
2102 
2103  // Currently, there are 2 playback meters and 2 record meters and any number of
2104  // mixerboard meters, so we have to send out an preferences updated message to
2105  // ensure they all update themselves.
2107  }
2108 }
2109 
2110 wxString MeterPanel::Key(const wxString & key) const
2111 {
2112  if (mStyle == MixerTrackCluster)
2113  {
2114  return wxT("/Meter/Mixerboard/") + key;
2115  }
2116 
2117  if (mIsInput)
2118  {
2119  return wxT("/Meter/Input/") + key;
2120  }
2121 
2122  return wxT("/Meter/Output/") + key;
2123 }
2124 
2125 // This compensates for a but in wxWidgets 3.0.2 for mac:
2126 // Couldn't set focus from keyboard when AcceptsFocus returns false;
2127 // this bypasses that limitation
2129 {
2130  auto temp = TemporarilyAllowFocus();
2131  SetFocus();
2132 }
2133 
2134 
2135 #if wxUSE_ACCESSIBILITY
2136 
2137 MeterAx::MeterAx(wxWindow *window):
2138  WindowAccessible(window)
2139 {
2140 }
2141 
2142 MeterAx::~MeterAx()
2143 {
2144 }
2145 
2146 // Performs the default action. childId is 0 (the action for this object)
2147 // or > 0 (the action for a child).
2148 // Return wxACC_NOT_SUPPORTED if there is no default action for this
2149 // window (e.g. an edit control).
2150 wxAccStatus MeterAx::DoDefaultAction(int WXUNUSED(childId))
2151 {
2152  MeterPanel *m = wxDynamicCast(GetWindow(), MeterPanel);
2153 
2154  if (m && m->mIsInput)
2155  m->StartMonitoring();
2156 
2157  return wxACC_OK;
2158 }
2159 
2160 // Retrieves the address of an IDispatch interface for the specified child.
2161 // All objects must support this property.
2162 wxAccStatus MeterAx::GetChild(int childId, wxAccessible** child)
2163 {
2164  if (childId == wxACC_SELF)
2165  *child = this;
2166  else
2167  *child = NULL;
2168  return wxACC_OK;
2169 }
2170 
2171 // Gets the number of children.
2172 wxAccStatus MeterAx::GetChildCount(int* childCount)
2173 {
2174  *childCount = 0;
2175  return wxACC_OK;
2176 }
2177 
2178 // Gets the default action for this object (0) or > 0 (the action for
2179 // a child). Return wxACC_OK even if there is no action. actionName
2180 // is the action, or the empty string if there is no action. The
2181 // retrieved string describes the action that is performed on an
2182 // object, not what the object does as a result. For example, a
2183 // toolbar button that prints a document has a default action of
2184 // "Press" rather than "Prints the current document."
2185 wxAccStatus MeterAx::GetDefaultAction(int WXUNUSED(childId), wxString* actionName)
2186 {
2187  *actionName = _("Press");
2188  return wxACC_OK;
2189 }
2190 
2191 // Returns the description for this object or a child.
2192 wxAccStatus MeterAx::GetDescription(int WXUNUSED(childId), wxString *description)
2193 {
2194  description->clear();
2195  return wxACC_NOT_SUPPORTED;
2196 }
2197 
2198 // Gets the window with the keyboard focus.
2199 // If childId is 0 and child is NULL, no object in
2200 // this subhierarchy has the focus.
2201 // If this object has the focus, child should be 'this'.
2202 wxAccStatus MeterAx::GetFocus(int* childId, wxAccessible** child)
2203 {
2204  *childId = 0;
2205  *child = this;
2206  return wxACC_OK;
2207 }
2208 
2209 // Returns help text for this object or a child, similar to tooltip text.
2210 wxAccStatus MeterAx::GetHelpText(int WXUNUSED(childId), wxString *helpText)
2211 {
2212  helpText->clear();
2213  return wxACC_NOT_SUPPORTED;
2214 }
2215 
2216 // Returns the keyboard shortcut for this object or child.
2217 // Return e.g. ALT+K
2218 wxAccStatus MeterAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut)
2219 {
2220  shortcut->clear();
2221  return wxACC_OK;
2222 }
2223 
2224 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
2225 // rect is in screen coordinates.
2226 wxAccStatus MeterAx::GetLocation(wxRect & rect, int WXUNUSED(elementId))
2227 {
2228  MeterPanel *m = wxDynamicCast(GetWindow(), MeterPanel);
2229 
2230  rect = m->mIconRect;
2231  rect.SetPosition(m->ClientToScreen(rect.GetPosition()));
2232 
2233  return wxACC_OK;
2234 }
2235 
2236 // Gets the name of the specified object.
2237 wxAccStatus MeterAx::GetName(int WXUNUSED(childId), wxString* name)
2238 {
2239  MeterPanel *m = wxDynamicCast(GetWindow(), MeterPanel);
2240 
2241  if (m->mAccSilent)
2242  {
2243  *name = wxT(""); // Jaws reads nothing, and nvda reads "unknown"
2244  }
2245  else
2246  {
2247  *name = m->GetName();
2248  if (name->empty())
2249  *name = m->GetLabel();
2250 
2251  if (name->empty())
2252  *name = _("Meter");
2253 
2254  if (m->mMonitoring)
2255  // translations of strings such as " Monitoring " did not
2256  // always retain the leading space. Therefore a space has
2257  // been added to ensure at least one space, and stop
2258  // words from being merged
2259  *name += wxT(" ") + _(" Monitoring ");
2260  else if (m->mActive)
2261  *name += wxT(" ") + _(" Active ");
2262 
2263  float peak = 0.;
2264  bool clipped = false;
2265  for (unsigned int i = 0; i < m->mNumBars; i++)
2266  {
2267  peak = wxMax(peak, m->mBar[i].peakPeakHold);
2268  if (m->mBar[i].clipping)
2269  clipped = true;
2270  }
2271 
2272  if (m->mDB)
2273  *name += wxT(" ") + wxString::Format(_(" Peak %2.f dB"), (peak * m->mDBRange) - m->mDBRange);
2274  else
2275  *name += wxT(" ") + wxString::Format(_(" Peak %.2f "), peak);
2276 
2277  if (clipped)
2278  *name += wxT(" ") + _(" Clipped ");
2279  }
2280 
2281  return wxACC_OK;
2282 }
2283 
2284 // Returns a role constant.
2285 wxAccStatus MeterAx::GetRole(int WXUNUSED(childId), wxAccRole* role)
2286 {
2287  MeterPanel *m = wxDynamicCast(GetWindow(), MeterPanel);
2288 
2289  if (m->mAccSilent)
2290  *role = wxROLE_NONE; // Jaws and nvda both read nothing
2291  else
2292  *role = wxROLE_SYSTEM_BUTTONDROPDOWN;
2293 
2294  return wxACC_OK;
2295 }
2296 
2297 // Gets a variant representing the selected children
2298 // of this object.
2299 // Acceptable values:
2300 // - a null variant (IsNull() returns TRUE)
2301 // - a list variant (GetType() == wxT("list"))
2302 // - an integer representing the selected child element,
2303 // or 0 if this object is selected (GetType() == wxT("long"))
2304 // - a "void*" pointer to a wxAccessible child object
2305 wxAccStatus MeterAx::GetSelections(wxVariant * WXUNUSED(selections))
2306 {
2307  return wxACC_NOT_IMPLEMENTED;
2308 }
2309 
2310 // Returns a state constant.
2311 wxAccStatus MeterAx::GetState(int WXUNUSED(childId), long* state)
2312 {
2313  MeterPanel *m = wxDynamicCast( GetWindow(), MeterPanel );
2314 
2315  *state = wxACC_STATE_SYSTEM_FOCUSABLE;
2316  *state |= ( m == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0 );
2317 
2318  return wxACC_OK;
2319 }
2320 
2321 // Returns a localized string representing the value for the object
2322 // or child.
2323 wxAccStatus MeterAx::GetValue(int WXUNUSED(childId), wxString* WXUNUSED(strValue))
2324 {
2325  return wxACC_NOT_SUPPORTED;
2326 }
2327 
2328 #endif
MeterUpdateMsg::rms
float rms[kMaxMeterBars]
Definition: Meter.h:55
MeterPanel::mDecayRate
float mDecayRate
Definition: Meter.h:247
MeterPanel::mT
double mT
Definition: Meter.h:251
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:38
TranslatableString::empty
bool empty() const
Definition: TranslatableString.h:72
MeterPanel::UpdateDisplay
void UpdateDisplay(unsigned numChannels, int numFrames, float *sampleData) override
Update the meters with a block of audio data.
Definition: Meter.cpp:989
MeterPanel::mRightTextPos
wxPoint mRightTextPos
Definition: Meter.h:268
MeterPanel::mDB
bool mDB
Definition: Meter.h:244
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1184
MeterPanel::OnPaint
void OnPaint(wxPaintEvent &evt)
Definition: Meter.cpp:474
MeterPanel::OnKeyUp
void OnKeyUp(wxKeyEvent &evt)
Definition: Meter.cpp:882
DB_TO_LINEAR
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
MeterPanel::GetMaxPeak
float GetMaxPeak() const override
Definition: Meter.cpp:1168
Ruler::LinearDBFormat
@ LinearDBFormat
Definition: Ruler.h:34
MeterPanel::Style
Style
Definition: Meter.h:103
MeterUpdateQueue::mStart
int mStart
Definition: Meter.h:83
MeterPanel::State::mActive
bool mActive
Definition: Meter.h:183
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
ThemeBase::ColourDistance
int ColourDistance(wxColour &From, wxColour &To)
Definition: Theme.cpp:339
ENV_DB_RANGE
#define ENV_DB_RANGE
Definition: GUISettings.h:16
MeterPanel::mNumBars
unsigned mNumBars
Definition: Meter.h:260
MeterPanel::mProject
AudacityProject * mProject
Definition: Meter.h:229
MeterPanel::OnKeyDown
void OnKeyDown(wxKeyEvent &evt)
Definition: Meter.cpp:845
DefaultPlayOptions
AudioIOStartStreamOptions DefaultPlayOptions(AudacityProject &project)
Definition: ProjectAudioManager.cpp:1000
MeterPanel::mHeight
int mHeight
Definition: Meter.h:234
wxPanelWrapper
Definition: wxPanelWrapper.h:41
MeterBar::r
wxRect r
Definition: Meter.h:38
OnMonitorID
@ OnMonitorID
Definition: Meter.cpp:266
intmax
static int intmax(int a, int b)
Definition: Meter.cpp:964
MeterPanel::State
Definition: Meter.h:183
MeterPanel::mDecay
bool mDecay
Definition: Meter.h:246
MeterPanel::mActive
bool mActive
Definition: Meter.h:258
ShuttleGuiBase::AddRadioButtonToGroup
wxRadioButton * AddRadioButtonToGroup(const TranslatableString &Prompt, int selector=1, int initValue=0)
Definition: ShuttleGui.cpp:574
AColor::Line
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:112
MeterPanel::State::mSaved
bool mSaved
Definition: Meter.h:183
MeterPanel::HorizontalStereo
@ HorizontalStereo
Definition: Meter.h:105
MeterPanel::SetActiveStyle
void SetActiveStyle(Style style)
Definition: Meter.cpp:1211
MeterPanel::OnSize
void OnSize(wxSizeEvent &evt)
Definition: Meter.cpp:747
XO
#define XO(s)
Definition: Internat.h:31
Ruler::SetTickColour
void SetTickColour(const wxColour &colour)
Definition: Ruler.h:148
MeterPanel::SetBarAndClip
void SetBarAndClip(int iBar, bool vert)
Definition: Meter.cpp:1252
MeterPanelBase
Definition: MeterPanelBase.h:18
Ruler::SetFormat
void SetFormat(RulerFormat format)
Definition: Ruler.cpp:131
Ruler::Draw
void Draw(wxDC &dc) const
Definition: Ruler.cpp:1428
MeterBar::rms
float rms
Definition: Meter.h:40
Ruler::RealFormat
@ RealFormat
Definition: Ruler.h:31
TrackInfo::UpdatePrefs
AUDACITY_DLL_API void UpdatePrefs(wxWindow *pParent)
MeterPanel::mLeftSize
wxSize mLeftSize
Definition: Meter.h:269
MeterPanel::mPeakHoldDuration
double mPeakHoldDuration
Definition: Meter.h:250
MeterPanel::mBkgndBrush
wxBrush mBkgndBrush
Definition: Meter.h:278
MeterPanel::mRMSBrush
wxBrush mRMSBrush
Definition: Meter.h:276
Meter.h
MeterPanel::SaveState
State SaveState()
Definition: Meter.cpp:1936
MeterPanel::mDisabledBkgndBrush
wxBrush mDisabledBkgndBrush
Definition: Meter.h:279
MeterPanel::RestoreState
void RestoreState(const State &state)
Definition: Meter.cpp:1941
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
PrefsListener::Broadcast
static void Broadcast(int id=0)
Call this static function to notify all PrefsListener objects.
Definition: Prefs.cpp:94
MeterUpdateMsg::tailPeakCount
int tailPeakCount[kMaxMeterBars]
Definition: Meter.h:58
MeterBar::b
wxRect b
Definition: Meter.h:37
MeterBar::tailPeakCount
int tailPeakCount
Definition: Meter.h:46
MeterPanel::OnKillFocus
void OnKillFocus(wxFocusEvent &evt)
Definition: Meter.cpp:911
MeterPanel::mRulerHeight
int mRulerHeight
Definition: Meter.h:237
MeterPanel::mRate
double mRate
Definition: Meter.h:252
MeterUpdateMsg::clipping
bool clipping[kMaxMeterBars]
Definition: Meter.h:56
MeterPanel::mLeftText
wxString mLeftText
Definition: Meter.h:281
ThemeBase::Bitmap
wxBitmap & Bitmap(int iIndex)
Definition: Theme.cpp:1216
ToDB
static float ToDB(float v, float range)
Definition: Meter.cpp:979
MAX_REFRESH_RATE
static const long MAX_REFRESH_RATE
Definition: Meter.cpp:156
Ruler::OfflimitsPixels
void OfflimitsPixels(int start, int end)
Definition: Ruler.cpp:302
MeterPanel::mQueue
MeterUpdateQueue mQueue
Definition: Meter.h:230
MeterPanel::StartMonitoring
void StartMonitoring()
Definition: Meter.cpp:1883
MeterPanel::mLeftTextPos
wxPoint mLeftTextPos
Definition: Meter.h:267
floatMax
static float floatMax(float a, float b)
Definition: Meter.cpp:952
MeterPanel::SetFocusFromKbd
void SetFocusFromKbd() override
Definition: Meter.cpp:2128
Ruler::GetMaxSize
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:1603
MixerTrackCluster
Definition: MixerBoard.h:76
MeterBar::isclipping
bool isclipping
Definition: Meter.h:45
OnPreferencesID
@ OnPreferencesID
Definition: Meter.cpp:267
MeterUpdateQueue::~MeterUpdateQueue
~MeterUpdateQueue()
Definition: Meter.cpp:201
MIN_REFRESH_RATE
static const long MIN_REFRESH_RATE
Definition: Meter.cpp:155
MeterPanel::mMeterDisabled
long mMeterDisabled
Definition: Meter.h:254
ProjectStatus::Set
void Set(const TranslatableString &msg, StatusBarField field=mainStatusBarField)
Definition: ProjectStatus.cpp:65
MeterBar::vert
bool vert
Definition: Meter.h:36
XXO
#define XXO(s)
Definition: Internat.h:44
MeterPanel::OnSetFocus
void OnSetFocus(wxFocusEvent &evt)
Definition: Meter.cpp:905
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1177
MeterPanel::MixerTrackCluster
@ MixerTrackCluster
Definition: Meter.h:107
kMaxMeterBars
const int kMaxMeterBars
Definition: Meter.h:33
MeterPanel::ShowMenu
void ShowMenu(const wxPoint &pos)
Definition: Meter.cpp:1958
MeterBar::rClip
wxRect rClip
Definition: Meter.h:43
MeterPanelBase::TemporarilyAllowFocus
static TempAllowFocus TemporarilyAllowFocus()
Definition: MeterPanelBase.cpp:19
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1167
MeterPanel::State::mMonitoring
bool mMonitoring
Definition: Meter.h:183
MeterPanel::GetFont
wxFont GetFont() const
Definition: Meter.cpp:1178
MeterPanel::mIsFocused
bool mIsFocused
Definition: Meter.h:284
MeterPanel::StopMonitoring
void StopMonitoring()
Definition: Meter.cpp:1904
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1203
MeterPanel::OnErase
void OnErase(wxEraseEvent &evt)
Definition: Meter.cpp:469
MeterPanel::Reset
void Reset(double sampleRate, bool resetClipping) override
This method is thread-safe! Feel free to call from a different thread (like from an audio I/O callbac...
Definition: Meter.cpp:929
ShuttleGuiBase::AddFixedText
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:440
FindProjectFrame
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null.
Definition: Project.h:173
MeterBar::peakPeakHold
float peakPeakHold
Definition: Meter.h:47
MeterPanel::Clear
void Clear() override
Definition: Meter.cpp:406
name
const TranslatableString name
Definition: Distortion.cpp:98
MeterPanel::mMeterRefreshRate
long mMeterRefreshRate
Definition: Meter.h:253
MeterPanel::mGradient
bool mGradient
Definition: Meter.h:243
gap
static const int gap
Definition: Meter.cpp:255
MeterPanel::mPeakPeakPen
wxPen mPeakPeakPen
Definition: Meter.h:274
MeterUpdateMsg::toStringIfClipped
wxString toStringIfClipped()
Only print meter updates if clipping may be happening.
Definition: Meter.cpp:177
MeterUpdateQueue::MeterUpdateQueue
MeterUpdateQueue(size_t maxLen)
Definition: Meter.cpp:194
MeterPanel::mRightText
wxString mRightText
Definition: Meter.h:282
MeterPanel::InIcon
bool InIcon(wxMouseEvent *pEvent=nullptr) const
Definition: Meter.cpp:755
ShuttleGui::Validator
ShuttleGui & Validator(const Factory &f)
Definition: ShuttleGui.h:678
MeterPanel::DrawMeterBar
void DrawMeterBar(wxDC &dc, MeterBar *meterBar)
Definition: Meter.cpp:1618
MeterPanel::mDBRange
int mDBRange
Definition: Meter.h:245
MeterPanel::mNumPeakSamplesToClip
int mNumPeakSamplesToClip
Definition: Meter.h:249
Ruler::SetRange
void SetRange(double min, double max)
Definition: Ruler.cpp:188
MeterPanel::mIsInput
bool mIsInput
Definition: Meter.h:239
WindowAccessible
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
MeterUpdateMsg::peak
float peak[kMaxMeterBars]
Definition: Meter.h:54
Ruler::SetOrientation
void SetOrientation(int orient)
Definition: Ruler.cpp:174
AColor::DrawFocus
static void DrawFocus(wxDC &dc, wxRect &r)
Definition: AColor.cpp:160
MeterPanel::mTimer
wxTimer mTimer
Definition: Meter.h:231
ShuttleGuiBase::AddRadioButton
wxRadioButton * AddRadioButton(const TranslatableString &Prompt, int selector=0, int initValue=0)
Definition: ShuttleGui.cpp:568
wxDialogWrapper::SetName
void SetName(const TranslatableString &title)
Definition: wxPanelWrapper.cpp:76
ENV_DB_KEY
#define ENV_DB_KEY
Definition: GUISettings.h:15
MeterPanel::HandleLayout
void HandleLayout(wxDC &dc)
Definition: Meter.cpp:1295
MeterBar::peakHold
float peakHold
Definition: Meter.h:41
MeterPanel::OnContext
void OnContext(wxContextMenuEvent &evt)
Definition: Meter.cpp:826
MeterPanel::mBar
MeterBar mBar[kMaxMeterBars]
Definition: Meter.h:261
title
static const auto title
Definition: UpdateNoticeDialog.cpp:23
MeterBar::peakHoldTime
double peakHoldTime
Definition: Meter.h:42
MeterPanel::SetStyle
void SetStyle(Style newStyle)
Definition: Meter.cpp:917
MeterPanel::UpdatePrefs
void UpdatePrefs() override
Definition: Meter.cpp:411
MeterPanel::IsClipping
bool IsClipping() const override
Definition: Meter.cpp:1203
MeterUpdateQueue::Get
bool Get(MeterUpdateMsg &msg)
Definition: Meter.cpp:234
MeterUpdateQueue::mEnd
int mEnd
Definition: Meter.h:84
MeterPanel::OnPreferences
void OnPreferences(wxCommandEvent &evt)
Definition: Meter.cpp:1995
ShuttleGuiBase::StartStatic
wxStaticBox * StartStatic(const TranslatableString &Str, int iProp=0)
Definition: ShuttleGui.cpp:893
MeterPanel::AutomaticStereo
@ AutomaticStereo
Definition: Meter.h:104
MeterPanel::mAccSilent
bool mAccSilent
Definition: Meter.h:290
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
MeterPanel::ResetBar
void ResetBar(MeterBar *bar, bool resetClipping)
Definition: Meter.cpp:1188
ShuttleGui::Name
ShuttleGui & Name(const TranslatableString &name)
Definition: ShuttleGui.h:663
MeterPanel::mRuler
Ruler mRuler
Definition: Meter.h:280
wxDialogWrapper
Definition: wxPanelWrapper.h:81
AColor::Bevel
static void Bevel(wxDC &dc, bool up, const wxRect &r)
Definition: AColor.cpp:191
MeterPanel::mClipBrush
wxBrush mClipBrush
Definition: Meter.h:277
WindowAccessible.h
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
key
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: CommandManager.cpp:197
MeterPanel::mClip
bool mClip
Definition: Meter.h:248
MeterPanel::OnMonitor
void OnMonitor(wxCommandEvent &evt)
Definition: Meter.cpp:1990
MeterUpdateMsg::toString
wxString toString()
Print out all the values in the meter update message.
Definition: Meter.cpp:160
_
#define _(s)
Definition: Internat.h:75
ProjectStatus::Get
static ProjectStatus & Get(AudacityProject &project)
Definition: ProjectStatus.cpp:23
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:113
MeterUpdateMsg::numFrames
int numFrames
Definition: Meter.h:53
MeterPanel::HorizontalStereoCompact
@ HorizontalStereoCompact
Definition: Meter.h:108
MeterPanel::mIcon
std::unique_ptr< wxBitmap > mIcon
Definition: Meter.h:271
MeterPanel
MeterPanel is a panel that paints the meter used for monitoring or playback.
Definition: Meter.h:97
MeterPanel::mRightSize
wxSize mRightSize
Definition: Meter.h:270
Ruler::SetBounds
void SetBounds(int left, int top, int right, int bottom)
Definition: Ruler.cpp:332
MeterUpdateMsg::headPeakCount
int headPeakCount[kMaxMeterBars]
Definition: Meter.h:57
MeterPanel::Key
wxString Key(const wxString &key) const
Definition: Meter.cpp:2110
MeterUpdateQueue::Put
bool Put(MeterUpdateMsg &msg)
Definition: Meter.cpp:213
Verbatim
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
Definition: TranslatableString.h:321
IMPLEMENT_CLASS
IMPLEMENT_CLASS(ControlToolBar, ToolBar)
ThemeBase::Colour
wxColour & Colour(int iIndex)
Definition: Theme.cpp:1197
MeterUpdateQueue::mBuffer
ArrayOf< MeterUpdateMsg > mBuffer
Definition: Meter.h:86
LINEAR_TO_DB
#define LINEAR_TO_DB(x)
Definition: MemoryX.h:629
ShuttleGui::AddStandardButtons
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxWindow *extra=NULL)
Definition: ShuttleGui.cpp:2432
MeterPanel::mMonitoring
bool mMonitoring
Definition: Meter.h:256
MeterPanel::VerticalStereo
@ VerticalStereo
Definition: Meter.h:106
MeterPanel::mIconRect
wxRect mIconRect
Definition: Meter.h:266
MeterUpdateQueue::mBufferSize
size_t mBufferSize
Definition: Meter.h:85
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:203
MeterPanel::UpdateSelectedPrefs
void UpdateSelectedPrefs(int) override
Definition: Meter.cpp:463
Prefs.h
MeterPanel::mStyle
Style mStyle
Definition: Meter.h:241
MeterPanel::mBitmap
std::unique_ptr< wxBitmap > mBitmap
Definition: Meter.h:265
MeterPanel::mRulerWidth
int mRulerWidth
Definition: Meter.h:236
MeterBar
A struct used by MeterPanel to hold the position of one bar.
Definition: Meter.h:35
MeterPanel::OnMeterUpdate
void OnMeterUpdate(wxTimerEvent &evt)
Definition: Meter.cpp:1070
MeterPanel::mLayoutValid
bool mLayoutValid
Definition: Meter.h:263
MeterPanel::RepaintBarsNow
void RepaintBarsNow()
Definition: Meter.cpp:1601
ShuttleGuiBase::EndStatic
void EndStatic()
Definition: ShuttleGui.cpp:922
AudioIO::Get
static AudioIO * Get()
Definition: AudioIO.cpp:505
OnMeterUpdateID
@ OnMeterUpdateID
Definition: Meter.cpp:265
safenew
#define safenew
Definition: MemoryX.h:10
MeterPrefsID
static int MeterPrefsID()
Definition: Meter.cpp:457
MeterPanel::mPen
wxPen mPen
Definition: Meter.h:272
MeterPanel::VerticalStereoCompact
@ VerticalStereoCompact
Definition: Meter.h:109
MeterPanel::mWidth
int mWidth
Definition: Meter.h:233
MeterBar::clipping
bool clipping
Definition: Meter.h:44
MeterPanel::mHighlighted
bool mHighlighted
Definition: Meter.h:294
ClipZeroToOne
static float ClipZeroToOne(float z)
Definition: Meter.cpp:969
MeterUpdateMsg
Message used to update the MeterPanel.
Definition: Meter.h:51
END_EVENT_TABLE
END_EVENT_TABLE()
MeterPanel::OnMouse
void OnMouse(wxMouseEvent &evt)
Definition: Meter.cpp:761
MAX_AUDIO
#define MAX_AUDIO
Definition: MemoryX.h:631
MeterBar::peak
float peak
Definition: Meter.h:39
PrefStyles
static const wxChar * PrefStyles[]
Definition: Meter.cpp:257
MeterUpdateQueue::Clear
void Clear()
Definition: Meter.cpp:205
MeterPanel::mBrush
wxBrush mBrush
Definition: Meter.h:275
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:631
MeterPanel::mDesiredStyle
Style mDesiredStyle
Definition: Meter.h:242
MeterPanel::OnAudioIOStatus
void OnAudioIOStatus(wxCommandEvent &evt)
Definition: Meter.cpp:1912
MeterPanel::IsMeterDisabled
bool IsMeterDisabled() const override
Find out if the level meter is disabled or not.
Definition: Meter.cpp:1878