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