Audacity  3.0.3
MeterPanel.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  MeterPanel.cpp
6 
7  Dominic Mazzoni
8  Vaughan Johnson
9 
10  2004.06.25 refresh rate limited to 30mS, by ChackoN
11 
12 *******************************************************************//****************************************************************//****************************************************************//****************************************************************//******************************************************************/
40 
41 #include "MeterPanel.h"
42 
43 #include <algorithm>
44 #include <wx/setup.h> // for wxUSE_* macros
45 #include <wx/wxcrtvararg.h>
46 #include <wx/app.h>
47 #include <wx/defs.h>
48 #include <wx/dialog.h>
49 #include <wx/dcbuffer.h>
50 #include <wx/frame.h>
51 #include <wx/image.h>
52 #include <wx/intl.h>
53 #include <wx/menu.h>
54 #include <wx/settings.h>
55 #include <wx/textdlg.h>
56 #include <wx/numdlg.h>
57 #include <wx/radiobut.h>
58 #include <wx/tooltip.h>
59 
60 #include <math.h>
61 
62 #include "../AudioIO.h"
63 #include "../AColor.h"
64 #include "../ImageManipulation.h"
65 #include "Decibels.h"
66 #include "Project.h"
67 #include "../ProjectAudioManager.h"
68 #include "ProjectStatus.h"
69 #include "../ProjectWindows.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(DecibelScaleCutoff.GetDefault()),
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 
990  unsigned numChannels, int numFrames, const float *sampleData)
991 {
992  auto sptr = sampleData;
993  auto num = std::min(numChannels, mNumBars);
994  MeterUpdateMsg msg;
995 
996  memset(&msg, 0, sizeof(msg));
997  msg.numFrames = numFrames;
998 
999  for(int i=0; i<numFrames; i++) {
1000  for(unsigned int j=0; j<num; j++) {
1001  msg.peak[j] = floatMax(msg.peak[j], fabs(sptr[j]));
1002  msg.rms[j] += sptr[j]*sptr[j];
1003 
1004  // In addition to looking for mNumPeakSamplesToClip peaked
1005  // samples in a row, also send the number of peaked samples
1006  // at the head and tail, in case there's a run of peaked samples
1007  // that crosses block boundaries
1008  if (fabs(sptr[j])>=MAX_AUDIO) {
1009  if (msg.headPeakCount[j]==i)
1010  msg.headPeakCount[j]++;
1011  msg.tailPeakCount[j]++;
1012  if (msg.tailPeakCount[j] > mNumPeakSamplesToClip)
1013  msg.clipping[j] = true;
1014  }
1015  else
1016  msg.tailPeakCount[j] = 0;
1017  }
1018  sptr += numChannels;
1019  }
1020  for(unsigned int j=0; j<mNumBars; j++)
1021  msg.rms[j] = sqrt(msg.rms[j]/numFrames);
1022 
1023  mQueue.Put(msg);
1024 }
1025 
1026 // Vaughan, 2010-11-29: This not currently used. See comments in MixerTrackCluster::UpdateMeter().
1027 //void MeterPanel::UpdateDisplay(int numChannels, int numFrames,
1028 // // Need to make these double-indexed arrays if we handle more than 2 channels.
1029 // float* maxLeft, float* rmsLeft,
1030 // float* maxRight, float* rmsRight,
1031 // const size_t kSampleCount)
1032 //{
1033 // int i, j;
1034 // int num = intmin(numChannels, mNumBars);
1035 // MeterUpdateMsg msg;
1036 //
1037 // msg.numFrames = kSampleCount;
1038 // for(j=0; j<mNumBars; j++) {
1039 // msg.peak[j] = 0.0;
1040 // msg.rms[j] = 0.0;
1041 // msg.clipping[j] = false;
1042 // msg.headPeakCount[j] = 0;
1043 // msg.tailPeakCount[j] = 0;
1044 // }
1045 //
1046 // for(i=0; i<numFrames; i++) {
1047 // for(j=0; j<num; j++) {
1048 // msg.peak[j] = floatMax(msg.peak[j], ((j == 0) ? maxLeft[i] : maxRight[i]));
1049 // msg.rms[j] = floatMax(msg.rms[j], ((j == 0) ? rmsLeft[i] : rmsRight[i]));
1050 //
1051 // // In addition to looking for mNumPeakSamplesToClip peaked
1052 // // samples in a row, also send the number of peaked samples
1053 // // at the head and tail, in case there's a run
1054 // // of peaked samples that crosses block boundaries.
1055 // if (fabs((j == 0) ? maxLeft[i] : maxRight[i]) >= MAX_AUDIO)
1056 // {
1057 // if (msg.headPeakCount[j]==i)
1058 // msg.headPeakCount[j]++;
1059 // msg.tailPeakCount[j]++;
1060 // if (msg.tailPeakCount[j] > mNumPeakSamplesToClip)
1061 // msg.clipping[j] = true;
1062 // }
1063 // else
1064 // msg.tailPeakCount[j] = 0;
1065 // }
1066 // }
1067 //
1068 // mQueue.Put(msg);
1069 //}
1070 
1071 void MeterPanel::OnMeterUpdate(wxTimerEvent & WXUNUSED(event))
1072 {
1073  MeterUpdateMsg msg;
1074  int numChanges = 0;
1075 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1076  double maxPeak = 0.0;
1077  bool discarded = false;
1078 #endif
1079 
1080  // We shouldn't receive any events if the meter is disabled, but clear it to be safe
1081  if (mMeterDisabled) {
1082  mQueue.Clear();
1083  return;
1084  }
1085 
1086  auto gAudioIO = AudioIO::Get();
1087 
1088  // There may have been several update messages since the last
1089  // time we got to this function. Catch up to real-time by
1090  // popping them off until there are none left. It is necessary
1091  // to process all of them, otherwise we won't handle peaks and
1092  // peak-hold bars correctly.
1093  while(mQueue.Get(msg)) {
1094  numChanges++;
1095  double deltaT = msg.numFrames / mRate;
1096 
1097  mT += deltaT;
1098  for(unsigned int j=0; j<mNumBars; j++) {
1099  mBar[j].isclipping = false;
1100 
1101  //
1102  if (mDB) {
1103  msg.peak[j] = ToDB(msg.peak[j], mDBRange);
1104  msg.rms[j] = ToDB(msg.rms[j], mDBRange);
1105  }
1106 
1107  if (mDecay) {
1108  if (mDB) {
1109  float decayAmount = mDecayRate * deltaT / mDBRange;
1110  mBar[j].peak = floatMax(msg.peak[j],
1111  mBar[j].peak - decayAmount);
1112  }
1113  else {
1114  double decayAmount = mDecayRate * deltaT;
1115  double decayFactor = DB_TO_LINEAR(-decayAmount);
1116  mBar[j].peak = floatMax(msg.peak[j],
1117  mBar[j].peak * decayFactor);
1118  }
1119  }
1120  else
1121  mBar[j].peak = msg.peak[j];
1122 
1123  // This smooths out the RMS signal
1124  float smooth = pow(0.9, (double)msg.numFrames/1024.0);
1125  mBar[j].rms = mBar[j].rms * smooth + msg.rms[j] * (1.0 - smooth);
1126 
1127  if (mT - mBar[j].peakHoldTime > mPeakHoldDuration ||
1128  mBar[j].peak > mBar[j].peakHold) {
1129  mBar[j].peakHold = mBar[j].peak;
1130  mBar[j].peakHoldTime = mT;
1131  }
1132 
1133  if (mBar[j].peak > mBar[j].peakPeakHold )
1134  mBar[j].peakPeakHold = mBar[j].peak;
1135 
1136  if (msg.clipping[j] ||
1137  mBar[j].tailPeakCount+msg.headPeakCount[j] >=
1139  mBar[j].clipping = true;
1140  mBar[j].isclipping = true;
1141  }
1142 
1143  mBar[j].tailPeakCount = msg.tailPeakCount[j];
1144 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1145  if (mT > gAudioIO->AILAGetLastDecisionTime()) {
1146  discarded = false;
1147  maxPeak = msg.peak[j] > maxPeak ? msg.peak[j] : maxPeak;
1148  wxPrintf("%[email protected]%f ", msg.peak[j], mT);
1149  }
1150  else {
1151  discarded = true;
1152  wxPrintf("%[email protected]%f discarded\n", msg.peak[j], mT);
1153  }
1154 #endif
1155  }
1156  } // while
1157 
1158  if (numChanges > 0) {
1159  #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1160  if (gAudioIO->AILAIsActive() && mIsInput && !discarded) {
1161  gAudioIO->AILAProcess(maxPeak);
1162  putchar('\n');
1163  }
1164  #endif
1165  RepaintBarsNow();
1166  }
1167 }
1168 
1170 {
1171  float maxPeak = 0.;
1172 
1173  for(unsigned int j=0; j<mNumBars; j++)
1174  maxPeak = mBar[j].peak > maxPeak ? mBar[j].peak : maxPeak;
1175 
1176  return(maxPeak);
1177 }
1178 
1179 wxFont MeterPanel::GetFont() const
1180 {
1181  int fontSize = 10;
1182 #if defined __WXMSW__
1183  fontSize = 8;
1184 #endif
1185 
1186  return wxFont(fontSize, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
1187 }
1188 
1189 void MeterPanel::ResetBar(MeterBar *b, bool resetClipping)
1190 {
1191  b->peak = 0.0;
1192  b->rms = 0.0;
1193  b->peakHold = 0.0;
1194  b->peakHoldTime = 0.0;
1195  if (resetClipping)
1196  {
1197  b->clipping = false;
1198  b->peakPeakHold = 0.0;
1199  }
1200  b->isclipping = false;
1201  b->tailPeakCount = 0;
1202 }
1203 
1205 {
1206  for (int c = 0; c < kMaxMeterBars; c++)
1207  if (mBar[c].isclipping)
1208  return true;
1209  return false;
1210 }
1211 
1213 {
1214  mStyle = newStyle;
1215 
1216  // Set dummy ruler bounds so width/height can be retrieved
1217  // NOTE: Make sure the Right and Bottom values are large enough to
1218  // ensure full width/height of digits get calculated.
1219  mRuler.SetBounds(0, 0, 500, 500);
1220 
1221  if (mDB)
1222  {
1225  {
1226  mRuler.SetOrientation(wxHORIZONTAL);
1227  mRuler.SetRange(-mDBRange, 0);
1228  }
1229  else
1230  {
1231  mRuler.SetOrientation(wxVERTICAL);
1232  mRuler.SetRange(0, -mDBRange);
1233  }
1234  }
1235  else
1236  {
1239  {
1240  mRuler.SetOrientation(wxHORIZONTAL);
1241  mRuler.SetRange(0, 1);
1242  }
1243  else
1244  {
1245  mRuler.SetOrientation(wxVERTICAL);
1246  mRuler.SetRange(1, 0);
1247  }
1248  }
1249 
1251 }
1252 
1253 void MeterPanel::SetBarAndClip(int iBar, bool vert)
1254 {
1255  // Save the orientation
1256  mBar[iBar].vert = vert;
1257 
1258  // Create the bar rectangle and educe to fit inside the bevel
1259  mBar[iBar].r = mBar[iBar].b;
1260  mBar[iBar].r.x += 1;
1261  mBar[iBar].r.width -= 1;
1262  mBar[iBar].r.y += 1;
1263  mBar[iBar].r.height -= 1;
1264 
1265  if (vert)
1266  {
1267  if (mClip)
1268  {
1269  // Create the clip rectangle
1270  mBar[iBar].rClip = mBar[iBar].b;
1271  mBar[iBar].rClip.height = 3;
1272 
1273  // Make room for the clipping indicator
1274  mBar[iBar].b.y += 3 + gap;
1275  mBar[iBar].b.height -= 3 + gap;
1276  mBar[iBar].r.y += 3 + gap;
1277  mBar[iBar].r.height -= 3 + gap;
1278  }
1279  }
1280  else
1281  {
1282  if (mClip)
1283  {
1284  // Make room for the clipping indicator
1285  mBar[iBar].b.width -= 4;
1286  mBar[iBar].r.width -= 4;
1287 
1288  // Create the indicator rectangle
1289  mBar[iBar].rClip = mBar[iBar].b;
1290  mBar[iBar].rClip.x = mBar[iBar].b.GetRight() + 1 + gap; // +1 for bevel
1291  mBar[iBar].rClip.width = 3;
1292  }
1293  }
1294 }
1295 
1297 {
1298  // Refresh to reflect any language changes
1299  /* i18n-hint: One-letter abbreviation for Left, in VU Meter */
1300  mLeftText = _("L");
1301  /* i18n-hint: One-letter abbreviation for Right, in VU Meter */
1302  mRightText = _("R");
1303 
1304  dc.SetFont(GetFont());
1305  int iconWidth = 0;
1306  int iconHeight = 0;
1307  int width = mWidth;
1308  int height = mHeight;
1309  int left = 0;
1310  int top = 0;
1311  int barw;
1312  int barh;
1313  int lside;
1314  int rside;
1315 
1316  // MixerTrackCluster has no L/R labels or icon
1317  if (mStyle != MixerTrackCluster)
1318  {
1320  {
1321  SetActiveStyle(width > height ? HorizontalStereo : VerticalStereo);
1322  }
1323 
1325  {
1327  }
1329  {
1331  }
1332 
1333  iconWidth = mIcon->GetWidth();
1334  iconHeight = mIcon->GetHeight();
1335  if (mLeftSize.GetWidth() == 0) // Not yet initialized to dc.
1336  {
1337  dc.GetTextExtent(mLeftText, &mLeftSize.x, &mLeftSize.y);
1338  dc.GetTextExtent(mRightText, &mRightSize.x, &mRightSize.y);
1339  }
1340  }
1341 
1342  int ltxtWidth = mLeftSize.GetWidth();
1343  int ltxtHeight = mLeftSize.GetHeight();
1344  int rtxtWidth = mRightSize.GetWidth();
1345  int rtxtHeight = mRightSize.GetHeight();
1346 
1347  switch (mStyle)
1348  {
1349  default:
1350  wxPrintf(wxT("Style not handled yet!\n"));
1351  break;
1352  case MixerTrackCluster:
1353  // width is now the entire width of the meter canvas
1354  width -= mRulerWidth + left;
1355 
1356  // height is now the entire height of the meter canvas
1357  height -= top + gap;
1358 
1359  // barw is half of the canvas while allowing for a gap between meters
1360  barw = (width - gap) / 2;
1361 
1362  // barh is now the height of the canvas
1363  barh = height;
1364 
1365  // We always have 2 bars
1366  mNumBars = 2;
1367 
1368  // Save dimensions of the left bevel
1369  mBar[0].b = wxRect(left, top, barw, barh);
1370 
1371  // Save dimensions of the right bevel
1372  mBar[1].b = mBar[0].b;
1373  mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge
1374 
1375  // Set bar and clipping indicator dimensions
1376  SetBarAndClip(0, true);
1377  SetBarAndClip(1, true);
1378 
1379  mRuler.SetBounds(mBar[1].r.GetRight() + 1, // +1 for the bevel
1380  mBar[1].r.GetTop(),
1381  mWidth,
1382  mBar[1].r.GetBottom());
1383  mRuler.OfflimitsPixels(0, 0);
1384  break;
1385  case VerticalStereo:
1386  // Determine required width of each side;
1387  lside = intmax(iconWidth, ltxtWidth);
1388  rside = intmax(mRulerWidth, rtxtWidth);
1389 
1390  // left is now the right edge of the icon or L label
1391  left = lside;
1392 
1393  // Ensure there's a margin between top edge of window and the meters
1394  top = gap;
1395 
1396  // Position the icon
1397  mIconRect.SetX(left - iconWidth);
1398  mIconRect.SetY(top);
1399  mIconRect.SetWidth(iconWidth);
1400  mIconRect.SetHeight(iconHeight);
1401 
1402  // Position the L/R labels
1403  mLeftTextPos = wxPoint(left - ltxtWidth - gap, height - gap - ltxtHeight);
1404  mRightTextPos = wxPoint(width - rside - gap, height - gap - rtxtHeight);
1405 
1406  // left is now left edge of left bar
1407  left += gap;
1408 
1409  // width is now the entire width of the meter canvas
1410  width -= gap + rside + gap + left;
1411 
1412  // height is now the entire height of the meter canvas
1413  height -= top + gap;
1414 
1415  // barw is half of the canvas while allowing for a gap between meters
1416  barw = (width - gap) / 2;
1417 
1418  // barh is now the height of the canvas
1419  barh = height;
1420 
1421  // We always have 2 bars
1422  mNumBars = 2;
1423 
1424  // Save dimensions of the left bevel
1425  mBar[0].b = wxRect(left, top, barw, barh);
1426 
1427  // Save dimensions of the right bevel
1428  mBar[1].b = mBar[0].b;
1429  mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge
1430 
1431  // Set bar and clipping indicator dimensions
1432  SetBarAndClip(0, true);
1433  SetBarAndClip(1, true);
1434 
1435  mRuler.SetBounds(mBar[1].r.GetRight() + 1, // +1 for the bevel
1436  mBar[1].r.GetTop(),
1437  mWidth,
1438  mBar[1].r.GetBottom());
1439  mRuler.OfflimitsPixels(mRightTextPos.y - gap, mBar[1].r.GetBottom());
1440  break;
1441  case VerticalStereoCompact:
1442  // Ensure there's a margin between top edge of window and the meters
1443  top = gap;
1444 
1445  // Position the icon
1446  mIconRect.SetX((width - iconWidth) / 2);
1447  mIconRect.SetY(top);
1448  mIconRect.SetWidth(iconWidth);
1449  mIconRect.SetHeight(iconHeight);
1450 
1451  // top is now the top of the bar
1452  top += iconHeight + gap;
1453 
1454  // height is now the entire height of the meter canvas
1455  height -= top + gap + ltxtHeight + gap;
1456 
1457  // barw is half of the canvas while allowing for a gap between meters
1458  barw = (width / 2) - gap;
1459 
1460  // barh is now the height of the canvas
1461  barh = height;
1462 
1463  // We always have 2 bars
1464  mNumBars = 2;
1465 
1466  // Save dimensions of the left bevel
1467  mBar[0].b = wxRect(left, top, barw, barh);
1468 
1469  // Save dimensions of the right bevel
1470  mBar[1].b = mBar[0].b;
1471  mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge
1472 
1473  // Set bar and clipping indicator dimensions
1474  SetBarAndClip(0, true);
1475  SetBarAndClip(1, true);
1476 
1477  // L/R is centered horizontally under each bar
1478  mLeftTextPos = wxPoint(mBar[0].b.GetLeft() + ((mBar[0].b.GetWidth() - ltxtWidth) / 2), top + barh + gap);
1479  mRightTextPos = wxPoint(mBar[1].b.GetLeft() + ((mBar[1].b.GetWidth() - rtxtWidth) / 2), top + barh + gap);
1480 
1482  mBar[1].r.GetTop(),
1483  (mWidth - mRulerWidth) / 2,
1484  mBar[1].r.GetBottom());
1485  mRuler.OfflimitsPixels(0, 0);
1486  break;
1487  case HorizontalStereo:
1488  // Button right next to dragger.
1489  left = 0;
1490 
1491  // Add a gap between bottom of icon and bottom of window
1492  height -= gap;
1493 
1494  // Create icon rectangle
1495  mIconRect.SetX(left);
1496  mIconRect.SetY(height - iconHeight);
1497  mIconRect.SetWidth(iconWidth);
1498  mIconRect.SetHeight(iconHeight);
1499  left = gap;
1500 
1501  // Make sure there's room for icon and gap between the bottom of the meter and icon
1502  height -= iconHeight + gap;
1503 
1504  // L/R is centered vertically and to the left of a each bar
1505  mLeftTextPos = wxPoint(left, (height / 4) - ltxtHeight / 2);
1506  mRightTextPos = wxPoint(left, (height * 3 / 4) - rtxtHeight / 2);
1507 
1508  // Add width of widest of the L/R characters
1509  left += intmax(ltxtWidth, rtxtWidth); //, iconWidth);
1510 
1511  // Add gap between L/R and meter bevel
1512  left += gap;
1513 
1514  // width is now the entire width of the meter canvas
1515  width -= left;
1516 
1517  // barw is now the width of the canvas minus gap between canvas and right window edge
1518  barw = width - gap;
1519 
1520  // barh is half of the canvas while allowing for a gap between meters
1521  barh = (height - gap) / 2;
1522 
1523  // We always have 2 bars
1524  mNumBars = 2;
1525 
1526  // Save dimensions of the top bevel
1527  mBar[0].b = wxRect(left, top, barw, barh);
1528 
1529  // Save dimensions of the bottom bevel
1530  mBar[1].b = mBar[0].b;
1531  mBar[1].b.SetTop(mBar[0].b.GetBottom() + 1 + gap); // +1 for bottom edge
1532 
1533  // Set bar and clipping indicator dimensions
1534  SetBarAndClip(0, false);
1535  SetBarAndClip(1, false);
1536 
1537  mRuler.SetBounds(mBar[1].r.GetLeft(),
1538  mBar[1].r.GetBottom() + 1, // +1 to fit below bevel
1539  mBar[1].r.GetRight(),
1540  mHeight - mBar[1].r.GetBottom() + 1);
1541  mRuler.OfflimitsPixels(0, mIconRect.GetRight() - 4);
1542  break;
1544  // Button right next to dragger.
1545  left = 0;
1546 
1547  // Create icon rectangle
1548  mIconRect.SetX(left);
1549  mIconRect.SetY((height - iconHeight) / 2 -1);
1550  mIconRect.SetWidth(iconWidth);
1551  mIconRect.SetHeight(iconHeight);
1552 
1553  left = gap;
1554  // Add width of icon and gap between icon and L/R
1555  left += iconWidth + gap;
1556 
1557  // L/R is centered vertically and to the left of a each bar
1558  mLeftTextPos = wxPoint(left, (height / 4) - (ltxtHeight / 2));
1559  mRightTextPos = wxPoint(left, (height * 3 / 4) - (ltxtHeight / 2));
1560 
1561  // Add width of widest of the L/R characters and a gap between labels and meter bevel
1562  left += intmax(ltxtWidth, rtxtWidth) + gap;
1563 
1564  // width is now the entire width of the meter canvas
1565  width -= left;
1566 
1567  // barw is now the width of the canvas minus gap between canvas and window edge
1568  barw = width - gap;
1569 
1570  // barh is half of the canvas while allowing for a gap between meters
1571  barh = (height - gap) / 2;
1572 
1573  // We always have 2 bars
1574  mNumBars = 2;
1575 
1576  // Save dimensions of the top bevel
1577  mBar[0].b = wxRect(left, top, barw, barh);
1578 
1579  // Save dimensions of the bottom bevel
1580  // Since the bars butt up against the window's top and bottom edges, we need
1581  // to include an extra pixel in the bottom bar when the window height and
1582  // meter height do not exactly match.
1583  mBar[1].b = mBar[0].b;
1584  mBar[1].b.SetTop(mBar[0].b.GetBottom() + 1 + gap); // +1 for bottom bevel
1585  mBar[1].b.SetHeight(mHeight - mBar[1].b.GetTop() - 1); // +1 for bottom bevel
1586 
1587  // Add clipping indicators - do after setting bar/bevel dimensions above
1588  SetBarAndClip(0, false);
1589  SetBarAndClip(1, false);
1590 
1591  mRuler.SetBounds(mBar[1].r.GetLeft(),
1592  mBar[1].b.GetTop() - (mRulerHeight / 2),
1593  mBar[1].r.GetRight(),
1594  mBar[1].b.GetTop() - (mRulerHeight / 2));
1595  mRuler.OfflimitsPixels(0, 0);
1596  break;
1597  }
1598 
1599  mLayoutValid = true;
1600 }
1601 
1603 {
1604  if (mLayoutValid)
1605  {
1606  // Invalidate the bars so they get redrawn
1607  for (unsigned int i = 0; i < mNumBars; i++)
1608  {
1609  Refresh(false);
1610  }
1611 
1612  // Immediate redraw (using wxPaintDC)
1613  Update();
1614 
1615  return;
1616  }
1617 }
1618 
1620 {
1621  // Cache some metrics
1622  wxCoord x = bar->r.GetLeft();
1623  wxCoord y = bar->r.GetTop();
1624  wxCoord w = bar->r.GetWidth();
1625  wxCoord h = bar->r.GetHeight();
1626  wxCoord ht;
1627  wxCoord wd;
1628 
1629  // Setup for erasing the background
1630  dc.SetPen(*wxTRANSPARENT_PEN);
1632 
1633  if (mGradient)
1634  {
1635  // Map the predrawn bitmap into the source DC
1636  wxMemoryDC srcDC;
1637  srcDC.SelectObject(*mBitmap);
1638 
1639  if (bar->vert)
1640  {
1641  // Copy as much of the predrawn meter bar as is required for the
1642  // current peak.
1643  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1644  ht = (int)(bar->peak * (h - 1) + 0.5);
1645 
1646  // Blank out the rest
1647  if (h - ht)
1648  {
1649  // ht includes peak value...not really needed but doesn't hurt
1650  dc.DrawRectangle(x, y, w, h - ht);
1651  }
1652 
1653  // Copy as much of the predrawn meter bar as is required for the
1654  // current peak.
1655  // +/-1 to include the peak position
1656  if (ht)
1657  {
1658  dc.Blit(x, y + h - ht - 1, w, ht + 1, &srcDC, x, y + h - ht - 1);
1659  }
1660 
1661  // Draw the "recent" peak hold line using the predrawn meter bar so that
1662  // it will be the same color as the original level.
1663  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1664  ht = (int)(bar->peakHold * (h - 1) + 0.5);
1665  if (ht > 1)
1666  {
1667  dc.Blit(x, y + h - ht - 1, w, 2, &srcDC, x, y + h - ht - 1);
1668  }
1669 
1670  // Draw the "maximum" peak hold line
1671  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1672  dc.SetPen(mPeakPeakPen);
1673  ht = (int)(bar->peakPeakHold * (h - 1) + 0.5);
1674  if (ht > 0)
1675  {
1676  AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1);
1677  if (ht > 1)
1678  {
1679  AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht);
1680  }
1681  }
1682  }
1683  else
1684  {
1685  // Calculate the peak position
1686  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1687  wd = (int)(bar->peak * (w - 1) + 0.5);
1688 
1689  // Blank out the rest
1690  if (w - wd)
1691  {
1692  // wd includes peak value...not really needed but doesn't hurt
1693  dc.DrawRectangle(x + wd, y, w - wd, h);
1694  }
1695 
1696  // Copy as much of the predrawn meter bar as is required for the
1697  // current peak. But, only blit() if there's something to copy
1698  // to prevent display corruption.
1699  // +1 to include peak position
1700  if (wd)
1701  {
1702  dc.Blit(x, y, wd + 1, h, &srcDC, x, y);
1703  }
1704 
1705  // Draw the "recent" peak hold line using the predrawn meter bar so that
1706  // it will be the same color as the original level.
1707  // -1 to give a 2 pixel width
1708  wd = (int)(bar->peakHold * (w - 1) + 0.5);
1709  if (wd > 1)
1710  {
1711  dc.Blit(x + wd - 1, y, 2, h, &srcDC, x + wd, y);
1712  }
1713 
1714  // Draw the "maximum" peak hold line using a themed color
1715  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1716  dc.SetPen(mPeakPeakPen);
1717  wd = (int)(bar->peakPeakHold * (w - 1) + 0.5);
1718  if (wd > 0)
1719  {
1720  AColor::Line(dc, x + wd, y, x + wd, y + h - 1);
1721  if (wd > 1)
1722  {
1723  AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1);
1724  }
1725  }
1726  }
1727 
1728  // No longer need the source DC, so unselect the predrawn bitmap
1729  srcDC.SelectObject(wxNullBitmap);
1730  }
1731  else
1732  {
1733  if (bar->vert)
1734  {
1735  // Calculate the peak position
1736  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1737  ht = (int)(bar->peak * (h - 1) + 0.5);
1738 
1739  // Blank out the rest
1740  if (h - ht)
1741  {
1742  // ht includes peak value...not really needed but doesn't hurt
1743  dc.DrawRectangle(x, y, w, h - ht);
1744  }
1745 
1746  // Draw the peak level
1747  // +/-1 to include the peak position
1748  dc.SetPen(*wxTRANSPARENT_PEN);
1749  dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mBrush);
1750  if (ht)
1751  {
1752  dc.DrawRectangle(x, y + h - ht - 1, w, ht + 1);
1753  }
1754 
1755  // Draw the "recent" peak hold line
1756  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1757  dc.SetPen(mPen);
1758  ht = (int)(bar->peakHold * (h - 1) + 0.5);
1759  if (ht > 0)
1760  {
1761  AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1);
1762  if (ht > 1)
1763  {
1764  AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht);
1765  }
1766  }
1767 
1768  // Calculate the rms position
1769  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1770  // +1 to include the rms position
1771  ht = (int)(bar->rms * (h - 1) + 0.5);
1772 
1773  // Draw the RMS level
1774  dc.SetPen(*wxTRANSPARENT_PEN);
1775  dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mRMSBrush);
1776  if (ht)
1777  {
1778  dc.DrawRectangle(x, y + h - ht - 1, w, ht + 1);
1779  }
1780 
1781  // Draw the "maximum" peak hold line
1782  // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1783  dc.SetPen(mPeakPeakPen);
1784  ht = (int)(bar->peakPeakHold * (h - 1) + 0.5);
1785  if (ht > 0)
1786  {
1787  AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1);
1788  if (ht > 1)
1789  {
1790  AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht);
1791  }
1792  }
1793  }
1794  else
1795  {
1796  // Calculate the peak position
1797  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1798  wd = (int)(bar->peak * (w - 1) + 0.5);
1799 
1800  // Blank out the rest
1801  if (w - wd)
1802  {
1803  // wd includes peak value...not really needed but doesn't hurt
1804  dc.DrawRectangle(x + wd, y, w - wd, h);
1805  }
1806 
1807  // Draw the peak level
1808  // +1 to include peak position
1809  dc.SetPen(*wxTRANSPARENT_PEN);
1810  dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mBrush);
1811  if (wd)
1812  {
1813  dc.DrawRectangle(x, y, wd + 1, h);
1814  }
1815 
1816  // Draw the "recent" peak hold line
1817  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1818  dc.SetPen(mPen);
1819  wd = (int)(bar->peakHold * (w - 1) + 0.5);
1820  if (wd > 0)
1821  {
1822  AColor::Line(dc, x + wd, y, x + wd, y + h - 1);
1823  if (wd > 1)
1824  {
1825  AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1);
1826  }
1827  }
1828 
1829  // Calculate the rms position
1830  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1831  wd = (int)(bar->rms * (w - 1) + 0.5);
1832 
1833  // Draw the rms level
1834  // +1 to include the rms position
1835  dc.SetPen(*wxTRANSPARENT_PEN);
1836  dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mRMSBrush);
1837  if (wd)
1838  {
1839  dc.DrawRectangle(x, y, wd + 1, h);
1840  }
1841 
1842  // Draw the "maximum" peak hold line using a themed color
1843  // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout()
1844  dc.SetPen(mPeakPeakPen);
1845  wd = (int)(bar->peakPeakHold * (w - 1) + 0.5);
1846  if (wd > 0)
1847  {
1848  AColor::Line(dc, x + wd, y, x + wd, y + h - 1);
1849  if (wd > 1)
1850  {
1851  AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1);
1852  }
1853  }
1854  }
1855  }
1856 
1857  // If meter had a clipping indicator, draw or erase it
1858  // LLL: At least I assume that's what "mClip" is supposed to be for as
1859  // it is always "true".
1860  if (mClip)
1861  {
1862  if (bar->clipping)
1863  {
1864  dc.SetBrush(mClipBrush);
1865  }
1866  else
1867  {
1869  }
1870  dc.SetPen(*wxTRANSPARENT_PEN);
1871  wxRect r(bar->rClip.GetX() + 1,
1872  bar->rClip.GetY() + 1,
1873  bar->rClip.GetWidth() - 1,
1874  bar->rClip.GetHeight() - 1);
1875  dc.DrawRectangle(r);
1876  }
1877 }
1878 
1880 {
1881  return mMeterDisabled != 0;
1882 }
1883 
1885 {
1886  bool start = !mMonitoring;
1887 
1888  auto gAudioIO = AudioIO::Get();
1889  if (gAudioIO->IsMonitoring()){
1890  gAudioIO->StopStream();
1891  }
1892 
1893  if (start && !gAudioIO->IsBusy()){
1895  if (p){
1896  gAudioIO->StartMonitoring( DefaultPlayOptions( *p ) );
1897  }
1898 
1899  mLayoutValid = false;
1900 
1901  Refresh(false);
1902  }
1903 }
1904 
1906  mMonitoring = false;
1907  auto gAudioIO = AudioIO::Get();
1908  if (gAudioIO->IsMonitoring()){
1909  gAudioIO->StopStream();
1910  }
1911 }
1912 
1913 void MeterPanel::OnAudioIOStatus(wxCommandEvent &evt)
1914 {
1915  evt.Skip();
1916  AudacityProject *p = (AudacityProject *) evt.GetEventObject();
1917 
1918  mActive = (evt.GetInt() != 0) && (p == mProject);
1919 
1920  if( mActive ){
1921  mTimer.Start(1000 / mMeterRefreshRate);
1922  if (evt.GetEventType() == EVT_AUDIOIO_MONITOR)
1923  mMonitoring = mActive;
1924  } else {
1925  mTimer.Stop();
1926  mMonitoring = false;
1927  }
1928 
1929  // Only refresh is we're the active meter
1930  if (IsShownOnScreen())
1931  Refresh(false);
1932 }
1933 
1934 // SaveState() and RestoreState() exist solely for purpose of recreating toolbars
1935 // They should really be querying the project for current audio I/O state, but there
1936 // isn't a clear way of doing that just yet. (It should NOT query AudioIO.)
1938 {
1939  return { true, mMonitoring, mActive };
1940 }
1941 
1943 {
1944  if (!state.mSaved)
1945  return;
1946 
1947  mMonitoring = state.mMonitoring;
1948  mActive = state.mActive;
1949  //wxLogDebug("Restore state for %p, is %i", this, mActive );
1950 
1951  if (mActive)
1952  mTimer.Start(1000 / mMeterRefreshRate);
1953 }
1954 
1955 //
1956 // Pop-up menu
1957 //
1958 
1959 void MeterPanel::ShowMenu(const wxPoint & pos)
1960 {
1961  wxMenu menu;
1962  // Note: these should be kept in the same order as the enum
1963  if (mIsInput) {
1964  wxMenuItem *mi;
1965  if (mMonitoring)
1966  mi = menu.Append(OnMonitorID, _("Stop Monitoring"));
1967  else
1968  mi = menu.Append(OnMonitorID, _("Start Monitoring"));
1969  mi->Enable(!mActive || mMonitoring);
1970  }
1971 
1972  menu.Append(OnPreferencesID, _("Options..."));
1973 
1974  mAccSilent = true; // temporarily make screen readers say (close to) nothing on focus events
1975 
1976  PopupMenu(&menu, pos);
1977 
1978  /* if stop/start monitoring was chosen in the menu, then by this point
1979  OnMonitoring has been called and variables which affect the accessibility
1980  name have been updated so it's now ok for screen readers to read the name of
1981  the button */
1982  mAccSilent = false;
1983 #if wxUSE_ACCESSIBILITY
1984  GetAccessible()->NotifyEvent(wxACC_EVENT_OBJECT_FOCUS,
1985  this,
1986  wxOBJID_CLIENT,
1987  wxACC_SELF);
1988 #endif
1989 }
1990 
1991 void MeterPanel::OnMonitor(wxCommandEvent & WXUNUSED(event))
1992 {
1993  StartMonitoring();
1994 }
1995 
1996 void MeterPanel::OnPreferences(wxCommandEvent & WXUNUSED(event))
1997 {
1998  wxTextCtrl *rate;
1999  wxRadioButton *gradient;
2000  wxRadioButton *rms;
2001  wxRadioButton *db;
2002  wxRadioButton *linear;
2003  wxRadioButton *automatic;
2004  wxRadioButton *horizontal;
2005  wxRadioButton *vertical;
2006  int meterRefreshRate = mMeterRefreshRate;
2007 
2008  auto title = mIsInput ? XO("Recording Meter Options") : XO("Playback Meter Options");
2009 
2010  // Dialog is a child of the project, rather than of the toolbar.
2011  // This determines where it pops up.
2012 
2013  wxDialogWrapper dlg( FindProjectFrame( mProject ), wxID_ANY, title);
2014  dlg.SetName();
2015  ShuttleGui S(&dlg, eIsCreating);
2016  S.StartVerticalLay();
2017  {
2018  S.StartStatic(XO("Refresh Rate"), 0);
2019  {
2020  S.AddFixedText(XO(
2021 "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."));
2022  S.StartHorizontalLay();
2023  {
2024  rate = S.Name(XO("Meter refresh rate per second [1-100]"))
2025  .Validator<IntegerValidator<long>>(
2026  &mMeterRefreshRate, NumValidatorStyle::DEFAULT,
2028  .AddTextBox(XXO("Meter refresh rate per second [1-100]: "),
2029  wxString::Format(wxT("%d"), meterRefreshRate),
2030  10);
2031  }
2032  S.EndHorizontalLay();
2033  }
2034  S.EndStatic();
2035 
2036  S.StartHorizontalLay();
2037  {
2038  S.StartStatic(XO("Meter Style"), 0);
2039  {
2040  S.StartVerticalLay();
2041  {
2042  gradient = S.AddRadioButton(XXO("Gradient"), true, mGradient);
2043  rms = S.AddRadioButtonToGroup(XXO("RMS"), false, mGradient);
2044  }
2045  S.EndVerticalLay();
2046  }
2047  S.EndStatic();
2048 
2049  S.StartStatic(XO("Meter Type"), 0);
2050  {
2051  S.StartVerticalLay();
2052  {
2053  db = S.AddRadioButton(XXO("dB"), true, mDB);
2054  linear = S.AddRadioButtonToGroup(XXO("Linear"), false, mDB);
2055  }
2056  S.EndVerticalLay();
2057  }
2058  S.EndStatic();
2059 
2060  S.StartStatic(XO("Orientation"), 1);
2061  {
2062  S.StartVerticalLay();
2063  {
2064  automatic = S.AddRadioButton(
2065  XXO("Automatic"), AutomaticStereo, mDesiredStyle);
2066  horizontal = S.AddRadioButtonToGroup(
2067  XXO("Horizontal"), HorizontalStereo, mDesiredStyle);
2068  vertical = S.AddRadioButtonToGroup(
2069  XXO("Vertical"), VerticalStereo, mDesiredStyle);
2070  }
2071  S.EndVerticalLay();
2072  }
2073  S.EndStatic();
2074  }
2075  S.EndHorizontalLay();
2076  S.AddStandardButtons();
2077  }
2078  S.EndVerticalLay();
2079  dlg.Layout();
2080  dlg.Fit();
2081 
2082  dlg.CenterOnParent();
2083 
2084  if (dlg.ShowModal() == wxID_OK)
2085  {
2086  wxArrayStringEx style{
2087  wxT("AutomaticStereo") ,
2088  wxT("HorizontalStereo") ,
2089  wxT("VerticalStereo") ,
2090  };
2091 
2092  int s = 0;
2093  s = automatic->GetValue() ? 0 : s;
2094  s = horizontal->GetValue() ? 1 : s;
2095  s = vertical->GetValue() ? 2 : s;
2096 
2097  gPrefs->Write(Key(wxT("Style")), style[s]);
2098  gPrefs->Write(Key(wxT("Bars")), gradient->GetValue() ? wxT("Gradient") : wxT("RMS"));
2099  gPrefs->Write(Key(wxT("Type")), db->GetValue() ? wxT("dB") : wxT("Linear"));
2100  gPrefs->Write(Key(wxT("RefreshRate")), rate->GetValue());
2101 
2102  gPrefs->Flush();
2103 
2104  // Currently, there are 2 playback meters and 2 record meters and any number of
2105  // mixerboard meters, so we have to send out an preferences updated message to
2106  // ensure they all update themselves.
2108  }
2109 }
2110 
2111 wxString MeterPanel::Key(const wxString & key) const
2112 {
2113  if (mStyle == MixerTrackCluster)
2114  {
2115  return wxT("/Meter/Mixerboard/") + key;
2116  }
2117 
2118  if (mIsInput)
2119  {
2120  return wxT("/Meter/Input/") + key;
2121  }
2122 
2123  return wxT("/Meter/Output/") + key;
2124 }
2125 
2126 // This compensates for a but in wxWidgets 3.0.2 for mac:
2127 // Couldn't set focus from keyboard when AcceptsFocus returns false;
2128 // this bypasses that limitation
2130 {
2131  auto temp = TemporarilyAllowFocus();
2132  SetFocus();
2133 }
2134 
2135 
2136 #if wxUSE_ACCESSIBILITY
2137 
2138 MeterAx::MeterAx(wxWindow *window):
2139  WindowAccessible(window)
2140 {
2141 }
2142 
2143 MeterAx::~MeterAx()
2144 {
2145 }
2146 
2147 // Performs the default action. childId is 0 (the action for this object)
2148 // or > 0 (the action for a child).
2149 // Return wxACC_NOT_SUPPORTED if there is no default action for this
2150 // window (e.g. an edit control).
2151 wxAccStatus MeterAx::DoDefaultAction(int WXUNUSED(childId))
2152 {
2153  MeterPanel *m = wxDynamicCast(GetWindow(), MeterPanel);
2154 
2155  if (m && m->mIsInput)
2156  m->StartMonitoring();
2157 
2158  return wxACC_OK;
2159 }
2160 
2161 // Retrieves the address of an IDispatch interface for the specified child.
2162 // All objects must support this property.
2163 wxAccStatus MeterAx::GetChild(int childId, wxAccessible** child)
2164 {
2165  if (childId == wxACC_SELF)
2166  *child = this;
2167  else
2168  *child = NULL;
2169  return wxACC_OK;
2170 }
2171 
2172 // Gets the number of children.
2173 wxAccStatus MeterAx::GetChildCount(int* childCount)
2174 {
2175  *childCount = 0;
2176  return wxACC_OK;
2177 }
2178 
2179 // Gets the default action for this object (0) or > 0 (the action for
2180 // a child). Return wxACC_OK even if there is no action. actionName
2181 // is the action, or the empty string if there is no action. The
2182 // retrieved string describes the action that is performed on an
2183 // object, not what the object does as a result. For example, a
2184 // toolbar button that prints a document has a default action of
2185 // "Press" rather than "Prints the current document."
2186 wxAccStatus MeterAx::GetDefaultAction(int WXUNUSED(childId), wxString* actionName)
2187 {
2188  *actionName = _("Press");
2189  return wxACC_OK;
2190 }
2191 
2192 // Returns the description for this object or a child.
2193 wxAccStatus MeterAx::GetDescription(int WXUNUSED(childId), wxString *description)
2194 {
2195  description->clear();
2196  return wxACC_NOT_SUPPORTED;
2197 }
2198 
2199 // Gets the window with the keyboard focus.
2200 // If childId is 0 and child is NULL, no object in
2201 // this subhierarchy has the focus.
2202 // If this object has the focus, child should be 'this'.
2203 wxAccStatus MeterAx::GetFocus(int* childId, wxAccessible** child)
2204 {
2205  *childId = 0;
2206  *child = this;
2207  return wxACC_OK;
2208 }
2209 
2210 // Returns help text for this object or a child, similar to tooltip text.
2211 wxAccStatus MeterAx::GetHelpText(int WXUNUSED(childId), wxString *helpText)
2212 {
2213  helpText->clear();
2214  return wxACC_NOT_SUPPORTED;
2215 }
2216 
2217 // Returns the keyboard shortcut for this object or child.
2218 // Return e.g. ALT+K
2219 wxAccStatus MeterAx::GetKeyboardShortcut(int WXUNUSED(childId), wxString *shortcut)
2220 {
2221  shortcut->clear();
2222  return wxACC_OK;
2223 }
2224 
2225 // Returns the rectangle for this object (id = 0) or a child element (id > 0).
2226 // rect is in screen coordinates.
2227 wxAccStatus MeterAx::GetLocation(wxRect & rect, int WXUNUSED(elementId))
2228 {
2229  MeterPanel *m = wxDynamicCast(GetWindow(), MeterPanel);
2230 
2231  rect = m->mIconRect;
2232  rect.SetPosition(m->ClientToScreen(rect.GetPosition()));
2233 
2234  return wxACC_OK;
2235 }
2236 
2237 // Gets the name of the specified object.
2238 wxAccStatus MeterAx::GetName(int WXUNUSED(childId), wxString* name)
2239 {
2240  MeterPanel *m = wxDynamicCast(GetWindow(), MeterPanel);
2241 
2242  if (m->mAccSilent)
2243  {
2244  *name = wxT(""); // Jaws reads nothing, and nvda reads "unknown"
2245  }
2246  else
2247  {
2248  *name = m->GetName();
2249  if (name->empty())
2250  *name = m->GetLabel();
2251 
2252  if (name->empty())
2253  *name = _("Meter");
2254 
2255  if (m->mMonitoring)
2256  // translations of strings such as " Monitoring " did not
2257  // always retain the leading space. Therefore a space has
2258  // been added to ensure at least one space, and stop
2259  // words from being merged
2260  *name += wxT(" ") + _(" Monitoring ");
2261  else if (m->mActive)
2262  *name += wxT(" ") + _(" Active ");
2263 
2264  float peak = 0.;
2265  bool clipped = false;
2266  for (unsigned int i = 0; i < m->mNumBars; i++)
2267  {
2268  peak = wxMax(peak, m->mBar[i].peakPeakHold);
2269  if (m->mBar[i].clipping)
2270  clipped = true;
2271  }
2272 
2273  if (m->mDB)
2274  *name += wxT(" ") + wxString::Format(_(" Peak %2.f dB"), (peak * m->mDBRange) - m->mDBRange);
2275  else
2276  *name += wxT(" ") + wxString::Format(_(" Peak %.2f "), peak);
2277 
2278  if (clipped)
2279  *name += wxT(" ") + _(" Clipped ");
2280  }
2281 
2282  return wxACC_OK;
2283 }
2284 
2285 // Returns a role constant.
2286 wxAccStatus MeterAx::GetRole(int WXUNUSED(childId), wxAccRole* role)
2287 {
2288  MeterPanel *m = wxDynamicCast(GetWindow(), MeterPanel);
2289 
2290  if (m->mAccSilent)
2291  *role = wxROLE_NONE; // Jaws and nvda both read nothing
2292  else
2293  *role = wxROLE_SYSTEM_BUTTONDROPDOWN;
2294 
2295  return wxACC_OK;
2296 }
2297 
2298 // Gets a variant representing the selected children
2299 // of this object.
2300 // Acceptable values:
2301 // - a null variant (IsNull() returns TRUE)
2302 // - a list variant (GetType() == wxT("list"))
2303 // - an integer representing the selected child element,
2304 // or 0 if this object is selected (GetType() == wxT("long"))
2305 // - a "void*" pointer to a wxAccessible child object
2306 wxAccStatus MeterAx::GetSelections(wxVariant * WXUNUSED(selections))
2307 {
2308  return wxACC_NOT_IMPLEMENTED;
2309 }
2310 
2311 // Returns a state constant.
2312 wxAccStatus MeterAx::GetState(int WXUNUSED(childId), long* state)
2313 {
2314  MeterPanel *m = wxDynamicCast( GetWindow(), MeterPanel );
2315 
2316  *state = wxACC_STATE_SYSTEM_FOCUSABLE;
2317  *state |= ( m == wxWindow::FindFocus() ? wxACC_STATE_SYSTEM_FOCUSED : 0 );
2318 
2319  return wxACC_OK;
2320 }
2321 
2322 // Returns a localized string representing the value for the object
2323 // or child.
2324 wxAccStatus MeterAx::GetValue(int WXUNUSED(childId), wxString* WXUNUSED(strValue))
2325 {
2326  return wxACC_NOT_SUPPORTED;
2327 }
2328 
2329 #endif
MeterUpdateMsg::rms
float rms[kMaxMeterBars]
Definition: MeterPanel.h:55
MeterPanel::mDecayRate
float mDecayRate
Definition: MeterPanel.h:247
MeterPanel::mT
double mT
Definition: MeterPanel.h:251
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:38
TranslatableString::empty
bool empty() const
Definition: TranslatableString.h:72
ProjectStatus.h
MeterPanel::mRightTextPos
wxPoint mRightTextPos
Definition: MeterPanel.h:268
MeterPanel::mDB
bool mDB
Definition: MeterPanel.h:244
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1184
MeterPanel::OnPaint
void OnPaint(wxPaintEvent &evt)
Definition: MeterPanel.cpp:474
MeterPanel::OnKeyUp
void OnKeyUp(wxKeyEvent &evt)
Definition: MeterPanel.cpp:882
DB_TO_LINEAR
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
MeterPanel::GetMaxPeak
float GetMaxPeak() const override
Definition: MeterPanel.cpp:1169
Ruler::LinearDBFormat
@ LinearDBFormat
Definition: Ruler.h:34
MeterPanel::Style
Style
Definition: MeterPanel.h:103
MeterUpdateQueue::mStart
int mStart
Definition: MeterPanel.h:83
MeterPanel::State::mActive
bool mActive
Definition: MeterPanel.h:183
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
ThemeBase::ColourDistance
int ColourDistance(wxColour &From, wxColour &To)
Definition: Theme.cpp:339
MeterPanel::mNumBars
unsigned mNumBars
Definition: MeterPanel.h:260
MeterPanel::mProject
AudacityProject * mProject
Definition: MeterPanel.h:229
MeterPanel::OnKeyDown
void OnKeyDown(wxKeyEvent &evt)
Definition: MeterPanel.cpp:845
Project.h
DefaultPlayOptions
AudioIOStartStreamOptions DefaultPlayOptions(AudacityProject &project)
Definition: ProjectAudioManager.cpp:1012
MeterPanel::mHeight
int mHeight
Definition: MeterPanel.h:234
wxPanelWrapper
Definition: wxPanelWrapper.h:41
MeterBar::r
wxRect r
Definition: MeterPanel.h:38
MeterPanel::State
Definition: MeterPanel.h:183
MeterPanel::mDecay
bool mDecay
Definition: MeterPanel.h:246
MeterPanel::mActive
bool mActive
Definition: MeterPanel.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: MeterPanel.h:183
MeterPanel::HorizontalStereo
@ HorizontalStereo
Definition: MeterPanel.h:105
MeterPanel::SetActiveStyle
void SetActiveStyle(Style style)
Definition: MeterPanel.cpp:1212
ToDB
static float ToDB(float v, float range)
Definition: MeterPanel.cpp:979
MeterPanel::OnSize
void OnSize(wxSizeEvent &evt)
Definition: MeterPanel.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: MeterPanel.cpp:1253
MeterPanelBase
Inherits wxPanel and has a Meter; exposes shared_ptr to the Meter.
Definition: MeterPanelBase.h:27
ClipZeroToOne
static float ClipZeroToOne(float z)
Definition: MeterPanel.cpp:969
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: MeterPanel.h:40
OnMonitorID
@ OnMonitorID
Definition: MeterPanel.cpp:266
Ruler::RealFormat
@ RealFormat
Definition: Ruler.h:31
TrackInfo::UpdatePrefs
AUDACITY_DLL_API void UpdatePrefs(wxWindow *pParent)
gap
static const int gap
Definition: MeterPanel.cpp:255
MeterPanel.h
MeterPanel::mLeftSize
wxSize mLeftSize
Definition: MeterPanel.h:269
MeterPanel::mPeakHoldDuration
double mPeakHoldDuration
Definition: MeterPanel.h:250
MeterPanel::mBkgndBrush
wxBrush mBkgndBrush
Definition: MeterPanel.h:278
MeterPanel::mRMSBrush
wxBrush mRMSBrush
Definition: MeterPanel.h:276
MeterPanel::SaveState
State SaveState()
Definition: MeterPanel.cpp:1937
MeterPanel::mDisabledBkgndBrush
wxBrush mDisabledBkgndBrush
Definition: MeterPanel.h:279
MeterPanel::RestoreState
void RestoreState(const State &state)
Definition: MeterPanel.cpp:1942
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:100
MeterUpdateMsg::tailPeakCount
int tailPeakCount[kMaxMeterBars]
Definition: MeterPanel.h:58
MeterBar::b
wxRect b
Definition: MeterPanel.h:37
intmax
static int intmax(int a, int b)
Definition: MeterPanel.cpp:964
MeterBar::tailPeakCount
int tailPeakCount
Definition: MeterPanel.h:46
MeterPanel::OnKillFocus
void OnKillFocus(wxFocusEvent &evt)
Definition: MeterPanel.cpp:911
MeterPanel::mRulerHeight
int mRulerHeight
Definition: MeterPanel.h:237
MeterPanel::mRate
double mRate
Definition: MeterPanel.h:252
MeterUpdateMsg::clipping
bool clipping[kMaxMeterBars]
Definition: MeterPanel.h:56
MeterPanel::mLeftText
wxString mLeftText
Definition: MeterPanel.h:281
ThemeBase::Bitmap
wxBitmap & Bitmap(int iIndex)
Definition: Theme.cpp:1216
Setting::Read
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:128
Ruler::OfflimitsPixels
void OfflimitsPixels(int start, int end)
Definition: Ruler.cpp:302
MeterPanel::mQueue
MeterUpdateQueue mQueue
Definition: MeterPanel.h:230
MeterPanel::StartMonitoring
void StartMonitoring()
Definition: MeterPanel.cpp:1884
MeterPanel::mLeftTextPos
wxPoint mLeftTextPos
Definition: MeterPanel.h:267
MeterPanel::SetFocusFromKbd
void SetFocusFromKbd() override
Definition: MeterPanel.cpp:2129
Ruler::GetMaxSize
void GetMaxSize(wxCoord *width, wxCoord *height)
Definition: Ruler.cpp:1603
MixerTrackCluster
Definition: MixerBoard.h:76
MeterBar::isclipping
bool isclipping
Definition: MeterPanel.h:45
MeterUpdateQueue::~MeterUpdateQueue
~MeterUpdateQueue()
Definition: MeterPanel.cpp:201
MeterPanel::mMeterDisabled
long mMeterDisabled
Definition: MeterPanel.h:254
ProjectStatus::Set
void Set(const TranslatableString &msg, StatusBarField field=mainStatusBarField)
Definition: ProjectStatus.cpp:77
MeterBar::vert
bool vert
Definition: MeterPanel.h:36
XXO
#define XXO(s)
Definition: Internat.h:44
MeterPanel::OnSetFocus
void OnSetFocus(wxFocusEvent &evt)
Definition: MeterPanel.cpp:905
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1177
MeterPanel::MixerTrackCluster
@ MixerTrackCluster
Definition: MeterPanel.h:107
MeterPanel::ShowMenu
void ShowMenu(const wxPoint &pos)
Definition: MeterPanel.cpp:1959
MeterBar::rClip
wxRect rClip
Definition: MeterPanel.h:43
MeterPanelBase::TemporarilyAllowFocus
static TempAllowFocus TemporarilyAllowFocus()
Definition: MeterPanelBase.cpp:17
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1167
MeterPanel::State::mMonitoring
bool mMonitoring
Definition: MeterPanel.h:183
MeterPanel::GetFont
wxFont GetFont() const
Definition: MeterPanel.cpp:1179
MeterPanel::mIsFocused
bool mIsFocused
Definition: MeterPanel.h:284
MeterPanel::StopMonitoring
void StopMonitoring()
Definition: MeterPanel.cpp:1905
MAX_REFRESH_RATE
static const long MAX_REFRESH_RATE
Definition: MeterPanel.cpp:156
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1203
MeterPanel::OnErase
void OnErase(wxEraseEvent &evt)
Definition: MeterPanel.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: MeterPanel.cpp:929
ShuttleGuiBase::AddFixedText
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:440
MeterBar::peakPeakHold
float peakPeakHold
Definition: MeterPanel.h:47
MeterPanel::Clear
void Clear() override
Definition: MeterPanel.cpp:406
name
const TranslatableString name
Definition: Distortion.cpp:98
MeterPanel::mMeterRefreshRate
long mMeterRefreshRate
Definition: MeterPanel.h:253
Decibels.h
MeterPanel::mGradient
bool mGradient
Definition: MeterPanel.h:243
MeterPanel::mPeakPeakPen
wxPen mPeakPeakPen
Definition: MeterPanel.h:274
MeterUpdateMsg::toStringIfClipped
wxString toStringIfClipped()
Only print meter updates if clipping may be happening.
Definition: MeterPanel.cpp:177
MeterUpdateQueue::MeterUpdateQueue
MeterUpdateQueue(size_t maxLen)
Definition: MeterPanel.cpp:194
MeterPanel::mRightText
wxString mRightText
Definition: MeterPanel.h:282
MeterPanel::InIcon
bool InIcon(wxMouseEvent *pEvent=nullptr) const
Definition: MeterPanel.cpp:755
ShuttleGui::Validator
ShuttleGui & Validator(const Factory &f)
Definition: ShuttleGui.h:678
MeterPanel::DrawMeterBar
void DrawMeterBar(wxDC &dc, MeterBar *meterBar)
Definition: MeterPanel.cpp:1619
MeterPanel::mDBRange
int mDBRange
Definition: MeterPanel.h:245
MeterPanel::mNumPeakSamplesToClip
int mNumPeakSamplesToClip
Definition: MeterPanel.h:249
Ruler::SetRange
void SetRange(double min, double max)
Definition: Ruler.cpp:188
MeterPanel::mIsInput
bool mIsInput
Definition: MeterPanel.h:239
WindowAccessible
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
MeterUpdateMsg::peak
float peak[kMaxMeterBars]
Definition: MeterPanel.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: MeterPanel.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
MeterPanel::HandleLayout
void HandleLayout(wxDC &dc)
Definition: MeterPanel.cpp:1296
MeterBar::peakHold
float peakHold
Definition: MeterPanel.h:41
MeterPanel::OnContext
void OnContext(wxContextMenuEvent &evt)
Definition: MeterPanel.cpp:826
DecibelScaleCutoff
IntSetting DecibelScaleCutoff
Negation of this value is the lowest dB level that should be shown in dB scales.
Definition: Decibels.cpp:12
MeterPanel::mBar
MeterBar mBar[kMaxMeterBars]
Definition: MeterPanel.h:261
title
static const auto title
Definition: UpdateNoticeDialog.cpp:23
MeterBar::peakHoldTime
double peakHoldTime
Definition: MeterPanel.h:42
MeterPanel::SetStyle
void SetStyle(Style newStyle)
Definition: MeterPanel.cpp:917
MeterPanel::UpdatePrefs
void UpdatePrefs() override
Definition: MeterPanel.cpp:411
MeterPanel::IsClipping
bool IsClipping() const override
Definition: MeterPanel.cpp:1204
MeterUpdateQueue::Get
bool Get(MeterUpdateMsg &msg)
Definition: MeterPanel.cpp:234
MeterUpdateQueue::mEnd
int mEnd
Definition: MeterPanel.h:84
MeterPanel::OnPreferences
void OnPreferences(wxCommandEvent &evt)
Definition: MeterPanel.cpp:1996
ShuttleGuiBase::StartStatic
wxStaticBox * StartStatic(const TranslatableString &Str, int iProp=0)
Definition: ShuttleGui.cpp:893
MeterPanel::AutomaticStereo
@ AutomaticStereo
Definition: MeterPanel.h:104
MeterPanel::mAccSilent
bool mAccSilent
Definition: MeterPanel.h:290
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
MeterPanel::ResetBar
void ResetBar(MeterBar *bar, bool resetClipping)
Definition: MeterPanel.cpp:1189
ShuttleGui::Name
ShuttleGui & Name(const TranslatableString &name)
Definition: ShuttleGui.h:663
MeterPanel::mRuler
Ruler mRuler
Definition: MeterPanel.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: MeterPanel.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:201
PrefStyles
static const wxChar * PrefStyles[]
Definition: MeterPanel.cpp:257
OnMeterUpdateID
@ OnMeterUpdateID
Definition: MeterPanel.cpp:265
MeterPanel::mClip
bool mClip
Definition: MeterPanel.h:248
MeterPanel::OnMonitor
void OnMonitor(wxCommandEvent &evt)
Definition: MeterPanel.cpp:1991
MeterUpdateMsg::toString
wxString toString()
Print out all the values in the meter update message.
Definition: MeterPanel.cpp:160
_
#define _(s)
Definition: Internat.h:75
ProjectStatus::Get
static ProjectStatus & Get(AudacityProject &project)
Definition: ProjectStatus.cpp:35
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
MeterUpdateMsg::numFrames
int numFrames
Definition: MeterPanel.h:53
kMaxMeterBars
const int kMaxMeterBars
Definition: MeterPanel.h:33
MIN_REFRESH_RATE
static const long MIN_REFRESH_RATE
Definition: MeterPanel.cpp:155
MeterPanel::HorizontalStereoCompact
@ HorizontalStereoCompact
Definition: MeterPanel.h:108
MeterPanel::mIcon
std::unique_ptr< wxBitmap > mIcon
Definition: MeterPanel.h:271
MeterPanel
MeterPanel is a panel that paints the meter used for monitoring or playback.
Definition: MeterPanel.h:97
MeterPanel::mRightSize
wxSize mRightSize
Definition: MeterPanel.h:270
Ruler::SetBounds
void SetBounds(int left, int top, int right, int bottom)
Definition: Ruler.cpp:332
MeterUpdateMsg::headPeakCount
int headPeakCount[kMaxMeterBars]
Definition: MeterPanel.h:57
MeterPanel::Key
wxString Key(const wxString &key) const
Definition: MeterPanel.cpp:2111
MeterUpdateQueue::Put
bool Put(MeterUpdateMsg &msg)
Definition: MeterPanel.cpp:213
floatMax
static float floatMax(float a, float b)
Definition: MeterPanel.cpp:952
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: MeterPanel.h:86
LINEAR_TO_DB
#define LINEAR_TO_DB(x)
Definition: MemoryX.h:629
FindProjectFrame
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
Definition: ProjectWindows.cpp:88
ShuttleGui::AddStandardButtons
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxWindow *extra=NULL)
Definition: ShuttleGui.cpp:2444
MeterPanel::UpdateDisplay
void UpdateDisplay(unsigned numChannels, int numFrames, const float *sampleData) override
Update the meters with a block of audio data.
Definition: MeterPanel.cpp:989
MeterPanel::mMonitoring
bool mMonitoring
Definition: MeterPanel.h:256
MeterPanel::VerticalStereo
@ VerticalStereo
Definition: MeterPanel.h:106
MeterPanel::mIconRect
wxRect mIconRect
Definition: MeterPanel.h:266
MeterUpdateQueue::mBufferSize
size_t mBufferSize
Definition: MeterPanel.h:85
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:203
MeterPanel::UpdateSelectedPrefs
void UpdateSelectedPrefs(int) override
Definition: MeterPanel.cpp:463
Prefs.h
MeterPanel::mStyle
Style mStyle
Definition: MeterPanel.h:241
MeterPanel::mBitmap
std::unique_ptr< wxBitmap > mBitmap
Definition: MeterPanel.h:265
MeterPanel::mRulerWidth
int mRulerWidth
Definition: MeterPanel.h:236
MeterBar
A struct used by MeterPanel to hold the position of one bar.
Definition: MeterPanel.h:35
MeterPanel::OnMeterUpdate
void OnMeterUpdate(wxTimerEvent &evt)
Definition: MeterPanel.cpp:1071
MeterPanel::mLayoutValid
bool mLayoutValid
Definition: MeterPanel.h:263
MeterPanel::RepaintBarsNow
void RepaintBarsNow()
Definition: MeterPanel.cpp:1602
ShuttleGuiBase::EndStatic
void EndStatic()
Definition: ShuttleGui.cpp:922
AudioIO::Get
static AudioIO * Get()
Definition: AudioIO.cpp:606
safenew
#define safenew
Definition: MemoryX.h:10
MeterPanel::mPen
wxPen mPen
Definition: MeterPanel.h:272
MeterPanel::VerticalStereoCompact
@ VerticalStereoCompact
Definition: MeterPanel.h:109
MeterPanel::mWidth
int mWidth
Definition: MeterPanel.h:233
MeterBar::clipping
bool clipping
Definition: MeterPanel.h:44
MeterPanel::mHighlighted
bool mHighlighted
Definition: MeterPanel.h:294
MeterUpdateMsg
Message used to update the MeterPanel.
Definition: MeterPanel.h:51
END_EVENT_TABLE
END_EVENT_TABLE()
MeterPanel::OnMouse
void OnMouse(wxMouseEvent &evt)
Definition: MeterPanel.cpp:761
MAX_AUDIO
#define MAX_AUDIO
Definition: MemoryX.h:631
OnPreferencesID
@ OnPreferencesID
Definition: MeterPanel.cpp:267
MeterBar::peak
float peak
Definition: MeterPanel.h:39
MeterPrefsID
static int MeterPrefsID()
Definition: MeterPanel.cpp:457
MeterUpdateQueue::Clear
void Clear()
Definition: MeterPanel.cpp:205
MeterPanel::mBrush
wxBrush mBrush
Definition: MeterPanel.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: MeterPanel.h:242
MeterPanel::OnAudioIOStatus
void OnAudioIOStatus(wxCommandEvent &evt)
Definition: MeterPanel.cpp:1913
MeterPanel::IsMeterDisabled
bool IsMeterDisabled() const override
Find out if the level meter is disabled or not.
Definition: MeterPanel.cpp:1879