diff --git a/images/MicMenu.xpm b/images/MicMenu.xpm new file mode 100644 index 000000000..5a684ecdc --- /dev/null +++ b/images/MicMenu.xpm @@ -0,0 +1,40 @@ +/* XPM */ +static const char * MicMenu_xpm[] = { +"24 19 18 1", +" c None", +". c #000000", +"+ c #DCDCDC", +"@ c #B5B5B5", +"# c #8C8C8C", +"$ c #D6D6D6", +"% c #9C9C9C", +"& c #6B6B6B", +"* c #EFEFEF", +"= c #C6C6C6", +"- c #737373", +"; c #4A4A4A", +"> c #DEDEDE", +", c #424242", +"' c #212121", +") c #ADADAD", +"! c #848484", +"~ c #636363", +" .... ", +" .+@+#. ", +" ..$+%+&. ", +" .+*.=+-+;. ", +" .>+*.%+,+. ", +" .+=+=.'+'. ", +" ..+)+;.... ", +" ..%+-+'.. ", +" .+..!~;.. ", +" .+....... ", +" .+... ", +" .+... ", +" .+... ", +" .+... ", +" .... .....", +" .... ", +" .. .....", +" . ... ", +". . "}; diff --git a/images/SpeakerMenu.xpm b/images/SpeakerMenu.xpm new file mode 100644 index 000000000..2d9204050 --- /dev/null +++ b/images/SpeakerMenu.xpm @@ -0,0 +1,36 @@ +/* XPM */ +static const char * SpeakerMenu_xpm[] = { +"24 19 14 1", +" c None", +". c #C1C1C1", +"+ c #2F2F2F", +"@ c #707070", +"# c #4B4B4B", +"$ c #000000", +"% c #090909", +"& c #383838", +"* c #838383", +"= c #7A7A7A", +"- c #414141", +"; c #8C8C8C", +"> c #131313", +", c #676767", +" ", +" ", +" . ", +" + ", +" @ #@ ", +" $ % ", +" &$ & *& ", +" ===$$ -= $ ", +" $$$$$ $ $ ", +" $$$$$ $ $ ", +" &&&$$ *& $ ", +" $$ % $ ", +" @$ @ #@ ", +" + ;+ ", +" . >. $$$$$", +" , ", +" $$$$$", +" $$$ ", +" $ "}; diff --git a/src/toolbars/MeterToolBar.cpp b/src/toolbars/MeterToolBar.cpp index b973af209..c8c080b8a 100644 --- a/src/toolbars/MeterToolBar.cpp +++ b/src/toolbars/MeterToolBar.cpp @@ -44,7 +44,6 @@ IMPLEMENT_CLASS(MeterToolBar, ToolBar); BEGIN_EVENT_TABLE( MeterToolBar, ToolBar ) EVT_SIZE( MeterToolBar::OnSize ) - EVT_COMMAND(wxID_ANY, EVT_METER_PREFERENCES_CHANGED, MeterToolBar::OnMeterPrefsUpdated) END_EVENT_TABLE() //Standard contructor @@ -127,9 +126,16 @@ void MeterToolBar::Populate() void MeterToolBar::UpdatePrefs() { if( mPlayMeter ) + { mPlayMeter->UpdatePrefs(); + mPlayMeter->Refresh(); + } + if( mRecordMeter ) + { mRecordMeter->UpdatePrefs(); + mRecordMeter->Refresh(); + } RegenerateTooltips(); @@ -150,13 +156,9 @@ void MeterToolBar::RegenerateTooltips() #endif } -void MeterToolBar::OnMeterPrefsUpdated(wxCommandEvent & WXUNUSED(evt)) -{ - UpdatePrefs(); -} - -void MeterToolBar::OnSize( wxSizeEvent & WXUNUSED(event) ) +void MeterToolBar::OnSize( wxSizeEvent & event) //WXUNUSED(event) ) { + event.Skip(); int width, height; // We can be resized before populating...protect against it @@ -192,20 +194,10 @@ void MeterToolBar::OnSize( wxSizeEvent & WXUNUSED(event) ) } if( mRecordMeter ) { - mRecordMeter->SetStyle(bHorizontal ? Meter::HorizontalStereo : Meter::VerticalStereo); mRecordMeter->SetMinSize( wxSize( width, height )); - Meter *play = mProject->GetPlaybackMeter(); - if( play ) { - play->SetStyle(bHorizontal ? Meter::HorizontalStereo : Meter::VerticalStereo); - } } if( mPlayMeter ) { - mPlayMeter->SetStyle(bHorizontal ? Meter::HorizontalStereo : Meter::VerticalStereo); mPlayMeter->SetMinSize( wxSize( width, height )); - Meter *record = mProject->GetCaptureMeter(); - if( record ) { - record->SetStyle(bHorizontal ? Meter::HorizontalStereo : Meter::VerticalStereo); - } mSizer->SetItemPosition( mPlayMeter, pos ); } @@ -220,18 +212,10 @@ bool MeterToolBar::Expose( bool show ) Meter *meter; if( mPlayMeter ) { mProject->SetPlaybackMeter( mPlayMeter ); - meter = mProject->GetCaptureMeter(); - if( meter ) { - meter->SetStyle( mPlayMeter->GetStyle() ); - } } if( mRecordMeter ) { mProject->SetCaptureMeter( mRecordMeter ); - meter = mProject->GetPlaybackMeter(); - if( meter ) { - meter->SetStyle( mRecordMeter->GetStyle() ); - } } } else { if( mPlayMeter && mProject->GetPlaybackMeter() == mPlayMeter ) { diff --git a/src/toolbars/MeterToolBar.h b/src/toolbars/MeterToolBar.h index 51bb4f2e8..cc5822539 100644 --- a/src/toolbars/MeterToolBar.h +++ b/src/toolbars/MeterToolBar.h @@ -48,11 +48,10 @@ class MeterToolBar:public ToolBar { int GetInitialWidth() {return (mWhichMeters == (kWithRecordMeter + kWithPlayMeter)) ? 338 : 460;} // Separate bars used to be smaller. - int GetMinToolbarWidth() { return 100; } + int GetMinToolbarWidth() { return 50; } wxSize GetDockedSize(); private: - void OnMeterPrefsUpdated(wxCommandEvent & evt); void RegenerateTooltips(); AudacityProject *mProject; diff --git a/src/toolbars/ToolBar.cpp b/src/toolbars/ToolBar.cpp index 4b3e2ad6f..b0fca25ea 100644 --- a/src/toolbars/ToolBar.cpp +++ b/src/toolbars/ToolBar.cpp @@ -387,7 +387,7 @@ bool ToolBar::Expose( bool show ) bool was = mVisible; SetVisible( show ); - + if( IsDocked() ) { Show( show ); diff --git a/src/widgets/Meter.cpp b/src/widgets/Meter.cpp index 59f56e57b..dd15ed711 100644 --- a/src/widgets/Meter.cpp +++ b/src/widgets/Meter.cpp @@ -62,7 +62,6 @@ #include "../AudioIO.h" #include "../AColor.h" #include "../ImageManipulation.h" -//#include "../../images/MixerImages.h" #include "../Project.h" #include "../toolbars/MeterToolBar.h" #include "../toolbars/ControlToolBar.h" @@ -73,9 +72,6 @@ #include "../Experimental.h" #include "../widgets/valnum.h" -// Event used to notify all meters of preference changes -DEFINE_EVENT_TYPE(EVT_METER_PREFERENCES_CHANGED); - /* Updates to the meter are passed accross via meter updates, each contained in * a MeterUpdateMsg object */ wxString MeterUpdateMsg::toString() @@ -169,20 +165,25 @@ bool MeterUpdateQueue::Get(MeterUpdateMsg &msg) // Meter class // +#include "../../images/SpeakerMenu.xpm" +#include "../../images/MicMenu.xpm" + +// How many pixels between items? +const static int gap = 2; + +// Event used to notify all meters of preference changes +DEFINE_EVENT_TYPE(EVT_METER_PREFERENCES_CHANGED); + +const static wxChar *PrefStyles[] = +{ + wxT("AutomaticStereo"), + wxT("HorizontalStereo"), + wxT("VerticalStereo") +}; + enum { OnMeterUpdateID = 6000, - OnDisableMeterID, OnMonitorID, - OnHorizontalID, - OnAutomatedInputLevelAdjustmentID, - OnVerticalID, - OnMultiID, - OnEqualizerID, - OnWaveformID, - OnLinearID, - OnDBID, - OnClipID, - OnFloatID, OnPreferencesID }; @@ -192,20 +193,7 @@ BEGIN_EVENT_TABLE(Meter, wxPanel) EVT_ERASE_BACKGROUND(Meter::OnErase) EVT_PAINT(Meter::OnPaint) EVT_SIZE(Meter::OnSize) - EVT_MENU(OnDisableMeterID, Meter::OnDisableMeter) - EVT_MENU(OnHorizontalID, Meter::OnHorizontal) - EVT_MENU(OnVerticalID, Meter::OnVertical) - EVT_MENU(OnMultiID, Meter::OnMulti) - EVT_MENU(OnEqualizerID, Meter::OnEqualizer) - EVT_MENU(OnWaveformID, Meter::OnWaveform) - EVT_MENU(OnLinearID, Meter::OnLinear) - EVT_MENU(OnDBID, Meter::OnDB) - EVT_MENU(OnClipID, Meter::OnClip) EVT_MENU(OnMonitorID, Meter::OnMonitor) -#ifdef AUTOMATED_INPUT_LEVEL_ADJUSTMENT - EVT_MENU(OnAutomatedInputLevelAdjustmentID, Meter::OnAutomatedInputLevelAdjustment) -#endif - EVT_MENU(OnFloatID, Meter::OnFloat) EVT_MENU(OnPreferencesID, Meter::OnPreferences) END_EVENT_TABLE() @@ -221,9 +209,10 @@ Meter::Meter(AudacityProject *project, : wxPanel(parent, id, pos, size), mProject(project), mQueue(1024), - mWidth(size.x), mHeight(size.y), + mWidth(size.x), + mHeight(size.y), mIsInput(isInput), - mStyle(style), + mDesiredStyle(style), mGradient(true), mDB(true), mDBRange(ENV_DB_RANGE), @@ -241,19 +230,22 @@ Meter::Meter(AudacityProject *project, mBitmap(NULL), mIcon(NULL) { - int i; + mStyle = mDesiredStyle; + + UpdatePrefs(); wxColour backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE); mBkgndBrush = wxBrush(backgroundColour, wxSOLID); - UpdatePrefs(); - mPeakPeakPen = wxPen(theTheme.Colour( clrMeterPeak), 1, wxSOLID); mDisabledPen = wxPen(theTheme.Colour( clrMeterDisabledPen), 1, wxSOLID); - mLeftSize = wxSize(0, 0); - mRightSize = wxSize(0, 0); + // Register for our preference update event + wxTheApp->Connect(EVT_METER_PREFERENCES_CHANGED, + wxCommandEventHandler(Meter::OnMeterPrefsUpdated), + NULL, + this); if (mIsInput) { // Register for AudioIO Monitor events @@ -289,9 +281,20 @@ Meter::Meter(AudacityProject *project, // MixerTrackCluster style has no menu, so disallows SetStyle, so never needs icon. if (mStyle != MixerTrackCluster) - CreateIcon(2); + { + if(mIsInput) + { + mIcon = new wxBitmap(MicMenu_xpm); + } + else + { + mIcon = new wxBitmap(SpeakerMenu_xpm); + } + } mRuler.SetFonts(GetFont(), GetFont(), GetFont()); + mRuler.SetFlip(true); + mRuler.SetLabelEdges(true); mTimer.SetOwner(this, OnMeterUpdateID); // TODO: Yikes. Hard coded sample rate. @@ -299,10 +302,6 @@ Meter::Meter(AudacityProject *project, // balistics are right for 44KHz and a bit more frisky than they should be // for higher sample rates. Reset(44100.0, true); - for(i=0; iDisconnect(EVT_METER_PREFERENCES_CHANGED, + wxCommandEventHandler(Meter::OnMeterPrefsUpdated), + NULL, + this); + // LLL: This prevents a crash during termination if monitoring // is active. if (gAudioIO->IsMonitoring()) @@ -357,37 +343,45 @@ Meter::~Meter() void Meter::UpdatePrefs() { mDBRange = gPrefs->Read(wxT("/GUI/EnvdBRange"), ENV_DB_RANGE); - mMeterRefreshRate = gPrefs->Read(wxT("/Meter/MeterRefreshRate"), 30); - // MixerTrackCluster style has no menu, so disallows disabling the meter. - if (mStyle == MixerTrackCluster) + mMeterRefreshRate = gPrefs->Read(Key(wxT("RefreshRate")), 30); + mGradient = gPrefs->Read(Key(wxT("Bars")), wxT("Gradient")) == wxT("Gradient"); + mDB = gPrefs->Read(Key(wxT("Type")), wxT("dB")) == wxT("dB"); + mMeterDisabled = gPrefs->Read(Key(wxT("Disabled")), (long)0); + + if (mDesiredStyle != MixerTrackCluster) { - mMeterDisabled = 0L; - } - else - { - mStyle = gPrefs->Read(wxT("/Meter/MeterStyle"), wxT("HorizontalStereo")) == wxT("HorizontalStereo") ? - HorizontalStereo : - VerticalStereo; - - mGradient = gPrefs->Read(wxT("/Meter/MeterBars"), wxT("Gradient")) == wxT("Gradient"); - mDB = gPrefs->Read(wxT("/Meter/MeterType"), wxT("dB")) == wxT("dB"); - - if (mIsInput) + wxString style = gPrefs->Read(Key(wxT("Style"))); + if (style == wxT("AutomaticStereo")) { - // Default is disabled i.e. metering off when we start. - mMeterDisabled = gPrefs->Read(wxT("/Meter/MeterInputDisabled"), (long)0); + mDesiredStyle = AutomaticStereo; + } + else if (style == wxT("HorizontalStereo")) + { + mDesiredStyle = HorizontalStereo; + } + else if (style == wxT("VerticalStereo")) + { + mDesiredStyle = VerticalStereo; } else { - mMeterDisabled = gPrefs->Read(wxT("/Meter/MeterOutputDisabled"), (long)0); + mDesiredStyle = AutomaticStereo; } } + // Set the desired orientation (resets ruler orientation) + SetActiveStyle(mDesiredStyle); + + // Reset to ensure new size is retrieved when language changes + mLeftSize = wxSize(0, 0); + mRightSize = wxSize(0, 0); + + Reset(mRate, false); + mTimer.Start(1000 / mMeterRefreshRate); mLayoutValid = false; - Refresh(true); } void Meter::OnErase(wxEraseEvent & WXUNUSED(event)) @@ -397,10 +391,224 @@ void Meter::OnErase(wxEraseEvent & WXUNUSED(event)) void Meter::OnPaint(wxPaintEvent & WXUNUSED(event)) { - mLayoutValid = false; + wxPaintDC paintDC(this); + wxDC & destDC = paintDC; - wxPaintDC dc(this); - HandlePaint(dc); + if (mLayoutValid == false) + { + if (mBitmap) + { + delete mBitmap; + } + + // Create a new one using current size and select into the DC + mBitmap = new wxBitmap(mWidth, mHeight); + wxMemoryDC dc; + dc.SelectObject(*mBitmap); + + // Go calculate all of the layout metrics + HandleLayout(dc); + + // Start with a clean background + // LLL: Should research USE_AQUA_THEME usefulness... +#ifndef USE_AQUA_THEME +#ifdef EXPERIMENTAL_THEMING + if( !mMeterDisabled ) + { + mBkgndBrush.SetColour( GetParent()->GetBackgroundColour() ); + } +#endif + + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(mBkgndBrush); + dc.DrawRectangle(0, 0, mWidth, mHeight); +#endif + + // MixerTrackCluster style has no icon or L/R labels + if (mStyle != MixerTrackCluster) + { + dc.DrawBitmap(*mIcon, mIconRect.GetPosition(), true); + dc.SetFont(GetFont()); + dc.DrawText(mLeftText, mLeftTextPos.x, mLeftTextPos.y); + dc.DrawText(mRightText, mRightTextPos.x, mRightTextPos.y); + } + + // Setup the colors for the 3 sections of the meter bars + wxColor green(117, 215, 112); + wxColor yellow(255, 255, 0); + wxColor red(255, 0, 0); + + // Draw the meter bars at maximum levels + for (int i = 0; i < mNumBars; i++) + { + // Give it a recessed look + AColor::Bevel(dc, false, mBar[i].b); + + // Draw the clip indicator bevel + if (mClip) + { + AColor::Bevel(dc, false, mBar[i].rClip); + } + + // Cache bar rect + wxRect r = mBar[i].r; + + if (mGradient) + { + // Calculate the size of the two gradiant segments of the meter + double gradw; + double gradh; + if (mDB) + { + gradw = (double) r.GetWidth() / mDBRange * 6.0; + gradh = (double) r.GetHeight() / mDBRange * 6.0; + } + else + { + gradw = (double) r.GetWidth() / 100 * 25; + gradh = (double) r.GetHeight() / 100 * 25; + } + + if (mBar[i].vert) + { + // Draw the "critical" segment (starts at top of meter and works down) + r.SetHeight(gradh); + dc.GradientFillLinear(r, red, yellow, wxSOUTH); + + // Draw the "warning" segment + r.SetTop(r.GetBottom()); + dc.GradientFillLinear(r, yellow, green, wxSOUTH); + + // Draw the "safe" segment + r.SetTop(r.GetBottom()); + r.SetBottom(mBar[i].r.GetBottom()); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(green); + dc.DrawRectangle(r); + } + else + { + // Draw the "safe" segment + r.SetWidth(r.GetWidth() - (int) (gradw + gradw + 0.5)); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(green); + dc.DrawRectangle(r); + + // Draw the "warning" segment + r.SetLeft(r.GetRight() + 1); + r.SetWidth(floor(gradw)); + dc.GradientFillLinear(r, green, yellow); + + // Draw the "critical" segment + r.SetLeft(r.GetRight() + 1); + r.SetRight(mBar[i].r.GetRight()); + dc.GradientFillLinear(r, yellow, red); + } +#ifdef EXPERIMENTAL_METER_LED_STYLE + if (!mBar[i].vert) + { + wxRect r = mBar[i].r; + wxPen BackgroundPen; + BackgroundPen.SetColour( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE) ); + dc.SetPen( BackgroundPen ); + int i; + for(i=0;iAppend(OnMonitorID, _("Stop Monitoring")); else menu->Append(OnMonitorID, _("Start Monitoring")); - - #ifdef AUTOMATED_INPUT_LEVEL_ADJUSTMENT - if (gAudioIO->AILAIsActive()) - menu->Append(OnAutomatedInputLevelAdjustmentID, _("Stop Automated Recording Level Adjustment")); - else - menu->Append(OnAutomatedInputLevelAdjustmentID, _("Start Automated Recording Level Adjustment")); - - bool AVActive; - gPrefs->Read(wxT("/AudioIO/AutomatedInputLevelAdjustment"), &AVActive, false); - if (!AVActive || !GetActiveProject()->GetControlToolBar()->IsRecordDown()) - menu->Enable(OnAutomatedInputLevelAdjustmentID, false); - #endif - } - //menu->AppendSeparator(); - - //menu->Append(OnHorizontalID, _("Horizontal Stereo")); - //menu->Append(OnVerticalID, _("Vertical Stereo")); - //menu->Append(OnMultiID, _("Vertical Multichannel")); - //menu->Append(OnEqualizerID, _("Equalizer")); - //menu->Append(OnWaveformID, _("Waveform")); - //menu->Enable(OnHorizontalID + mStyle, false); - //menu->Enable(mStyle==VerticalStereo? OnVerticalID: OnHorizontalID, false); - //menu->AppendSeparator(); - - //menu->Append(OnLinearID, _("Linear")); - //menu->Append(OnDBID, _("dB")); - //menu->Enable(mDB? OnDBID: OnLinearID, false); - //menu->AppendSeparator(); - //menu->Append(OnClipID, _("Turn on clipping")); - //menu->AppendSeparator(); - //menu->Append(OnFloatID, _("Float Window")); - //menu->AppendSeparator(); menu->Append(OnPreferencesID, _("Preferences...")); - if (evt.RightDown()) PopupMenu(menu, evt.m_x, evt.m_y); else - PopupMenu(menu, mMenuRect.x + 1, mMenuRect.y + mMenuRect.height + 1); + PopupMenu(menu, mIconRect.x + 1, mIconRect.y + mIconRect.height + 1); + delete menu; } else if (evt.LeftDown()) { - if (mIsInput) + if (mIsInput) { StartMonitoring(); + } else { Reset(mRate, true); } + } } void Meter::SetStyle(Meter::Style newStyle) { - // MixerTrackCluster disallows style change. - if (mStyle == MixerTrackCluster) - return; - if (mStyle != newStyle) + if (mStyle != newStyle && mDesiredStyle == AutomaticStereo) { - mStyle = newStyle; - gPrefs->Write(wxT("/Meter/MeterStyle"), newStyle == Meter::HorizontalStereo ? wxT("HorizontalStereo") : wxT("VerticalStereo")); + SetActiveStyle(newStyle); + mLayoutValid = false; - Refresh(true); + + Refresh(false); } } void Meter::Reset(double sampleRate, bool resetClipping) { - int j; - mT = 0; mRate = sampleRate; - for(j=0; jclipping = false; - b->peakPeakHold =0.0; + b->peakPeakHold = 0.0; } b->isclipping = false; b->tailPeakCount = 0; } -bool Meter::IsClipping() +bool Meter::IsClipping() const { for (int c = 0; c < kMaxMeterBars; c++) if (mBar[c].isclipping) @@ -799,643 +968,684 @@ bool Meter::IsClipping() return false; } -void Meter::SetBarClip( int iBar ) +void Meter::SetActiveStyle(Style newStyle) { - mBar[iBar].vert = false; - ResetBar(&mBar[iBar], false); - if (!mClip) - return; - mBar[iBar].rClip = mBar[iBar].r; - mBar[iBar].rClip.x += mBar[iBar].rClip.width-3; - mBar[iBar].rClip.width = 3; - mBar[iBar].r.width -= 4; + mStyle = newStyle; + + // Set dummy ruler bounds so width/height can be retrieved + // NOTE: Make sure the Right and Bottom values are large enough to + // ensure full width/height of digits get calculated. + mRuler.SetBounds(0, 0, 500, 500); + + if (mDB) + { + mRuler.SetFormat(Ruler::LinearDBFormat); + if (mStyle == HorizontalStereo || mStyle == HorizontalStereoCompact) + { + mRuler.SetOrientation(wxHORIZONTAL); + mRuler.SetRange(-mDBRange, 0); + } + else + { + mRuler.SetOrientation(wxVERTICAL); + mRuler.SetRange(0, -mDBRange); + } + } + else + { + mRuler.SetFormat(Ruler::RealFormat); + if (mStyle == HorizontalStereo || mStyle == HorizontalStereoCompact) + { + mRuler.SetOrientation(wxHORIZONTAL); + mRuler.SetRange(0, 1); + } + else + { + mRuler.SetOrientation(wxVERTICAL); + mRuler.SetRange(1, 0); + } + } + + mRuler.GetMaxSize(&mRulerWidth, &mRulerHeight); +} + +void Meter::SetBarAndClip(int iBar, bool vert) +{ + // Save the orientation + mBar[iBar].vert = vert; + + // Create the bar rectangle and educe to fit inside the bevel + mBar[iBar].r = mBar[iBar].b; + mBar[iBar].r.x += 1; + mBar[iBar].r.width -= 1; + mBar[iBar].r.y += 1; + mBar[iBar].r.height -= 1; + + if (vert) + { + if (mClip) + { + // Create the clip rectangle + mBar[iBar].rClip = mBar[iBar].b; + mBar[iBar].rClip.height = 3; + + // Make room for the clipping indicator + mBar[iBar].b.y += 3 + gap; + mBar[iBar].b.height -= 3 + gap; + mBar[iBar].r.y += 3 + gap; + mBar[iBar].r.height -= 3 + gap; + } + } + else + { + if (mClip) + { + // Make room for the clipping indicator + mBar[iBar].b.width -= 4; + mBar[iBar].r.width -= 4; + + // Create the indicator rectangle + mBar[iBar].rClip = mBar[iBar].b; + mBar[iBar].rClip.x = mBar[iBar].b.GetRight() + 1 + gap; // +1 for bevel + mBar[iBar].rClip.width = 3; + } + } } void Meter::HandleLayout(wxDC &dc) { // Refresh to reflect any language changes /* i18n-hint: One-letter abbreviation for Left, in VU Meter */ - mLeftText = _("L"); // used in a couple of places in this method. + mLeftText = _("L"); /* i18n-hint: One-letter abbreviation for Right, in VU Meter */ mRightText = _("R"); dc.SetFont(GetFont()); - if (mLeftSize.x == 0) { // Not yet initialized to dc. - if (mStyle != MixerTrackCluster) // MixerTrackCluster style has no L/R labels. + int iconWidth = 0; + int iconHeight = 0; + int width = mWidth; + int height = mHeight; + int left = 0; + int top = 0; + int barw; + int barh; + int lside; + int rside; + + // MixerTrackCluster has no L/R labels or icon + if (mStyle != MixerTrackCluster) + { + if (mDesiredStyle == AutomaticStereo) + { + SetActiveStyle(width > height ? HorizontalStereo : VerticalStereo); + } + + if (mStyle == HorizontalStereoCompact || mStyle == HorizontalStereo) + { + SetActiveStyle(height < 50 ? HorizontalStereoCompact : HorizontalStereo); + } + else if (mStyle == VerticalStereoCompact || mStyle == VerticalStereo) + { + SetActiveStyle(width < 100 ? VerticalStereoCompact : VerticalStereo); + } + + iconWidth = mIcon->GetWidth(); + iconHeight = mIcon->GetHeight(); + if (mLeftSize.GetWidth() == 0) // Not yet initialized to dc. { dc.GetTextExtent(mLeftText, &mLeftSize.x, &mLeftSize.y); dc.GetTextExtent(mRightText, &mRightSize.x, &mRightSize.y); } - if ((mStyle == VerticalStereo) || (mStyle == MixerTrackCluster)) // VerticalMulti? - { - // For any vertical style, also need mRightSize big enough for Ruler width. - int rulerWidth; - int rulerHeight; - dc.GetTextExtent(wxT("-888"), &rulerWidth, &rulerHeight); // -888 is nice and wide. - if (mRightSize.x < rulerWidth) - mRightSize.x = rulerWidth; - } } - int iconWidth = 0; - int iconHeight = 0; - int menuWidth = 0; - int menuHeight = 0; - if (mStyle != MixerTrackCluster) + int ltxtWidth = mLeftSize.GetWidth(); + int ltxtHeight = mLeftSize.GetHeight(); + int rtxtWidth = mRightSize.GetWidth(); + int rtxtHeight = mRightSize.GetHeight(); + + switch (mStyle) { - iconWidth = mIcon->GetWidth(); - iconHeight = mIcon->GetHeight(); - menuWidth = 17; - menuHeight = 14; - } - int width = mWidth; - int height = mHeight; - int left = 0, top = 0; - int right, bottom; - int barw, barh; - int i; - - mRuler.SetFlip(true); - mRuler.SetLabelEdges(true); - - // How many pixels between items? - const int iSpacer = 2; - - Meter::Style AdjustedMeterStyle = mStyle; - if( (mStyle == HorizontalStereo) && (height < 50 ) ) - AdjustedMeterStyle = HorizontalStereoCompact; - - switch(AdjustedMeterStyle) { default: wxPrintf(wxT("Style not handled yet!\n")); break; - case VerticalStereo: - mMenuRect = wxRect(mWidth - menuWidth - 5, mHeight - menuHeight - 2, - menuWidth, menuHeight); - if (mHeight < (menuHeight + iconHeight + 8)) - mIconPos = wxPoint(-999, -999); // Don't display - else - mIconPos = wxPoint(mWidth - iconWidth - 1, 1); - width = intmin(mWidth-(iconWidth+2), mWidth-(menuWidth+3)); - // No break. Fall-through to MixerTrackCluster is intentional. case MixerTrackCluster: - // Doesn't show menu, icon, or L/R labels, - // but otherwise like VerticalStereo. + // width is now the entire width of the meter canvas + width -= mRulerWidth + left; - if (width >= mLeftSize.x + mRightSize.x + 24) { - if (mStyle != MixerTrackCluster) - { - mLeftTextPos = wxPoint(2, height-2-mLeftSize.y); - left += mLeftSize.x+4; - } - mRightTextPos = wxPoint(width-mLeftSize.x, height-2-mLeftSize.y); - width -= mLeftSize.x + mRightSize.x + 8; //v ...but then -8 in UmixIt? -- for vertical only? - } - barw = (width-2)/2; - barh = height - 4; + // height is now the entire height of the meter canvas + height -= top + gap; + + // barw is half of the canvas while allowing for a gap between meters + barw = (width - gap) / 2; + + // barh is now the height of the canvas + barh = height; + + // We always have 2 bars mNumBars = 2; - mBar[0].vert = true; - ResetBar(&mBar[0], false); - mBar[0].r = wxRect(left + width/2 - barw - 1, 2, barw, barh); - if (mClip) { - mBar[0].rClip = mBar[0].r; - mBar[0].rClip.height = 3; - mBar[0].r.y += 4; - mBar[0].r.height -= 4; - } - mBar[1].vert = true; - ResetBar(&mBar[1], false); - mBar[1].r = wxRect(left + width/2 + 1, 2, barw, barh); - if (mClip) { - mBar[1].rClip = mBar[1].r; - mBar[1].rClip.height = 3; - mBar[1].r.y += 4; - mBar[1].r.height -= 4; - } - mRuler.SetOrientation(wxVERTICAL); - mRuler.SetBounds(mBar[1].r.x + mBar[1].r.width + 1, - mBar[1].r.y + 1, - mBar[1].r.x + width, - mBar[1].r.y + mBar[1].r.height - 1); - if (mDB) { - mRuler.SetRange(0, -mDBRange); - mRuler.SetFormat(Ruler::LinearDBFormat); - } - else { - mRuler.SetRange(1, 0); - mRuler.SetFormat(Ruler::RealFormat); - } - if (mStyle != MixerTrackCluster) // MixerTrackCluster style has no menu. - mRuler.OfflimitsPixels(mMenuRect.y-mBar[1].r.y, mBar[1].r.height); + + // Save dimensions of the left bevel + mBar[0].b = wxRect(left, top, barw, barh); + + // Save dimensions of the right bevel + mBar[1].b = mBar[0].b; + mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge + + // Set bar and clipping indicator dimensions + SetBarAndClip(0, true); + SetBarAndClip(1, true); + + mRuler.SetBounds(mBar[1].r.GetRight() + 1, // +1 for the bevel + mBar[1].r.GetTop(), + mWidth, + mBar[1].r.GetBottom()); + mRuler.OfflimitsPixels(0, 0); + break; + case VerticalStereo: + // Determine required width of each side; + lside = intmax(iconWidth, ltxtWidth); + rside = intmax(mRulerWidth, rtxtWidth); + + // left is now the right edge of the icon or L label + left = gap + lside; + + // Ensure there's a margin between top edge of window and the meters + top = gap; + + // Position the icon + mIconRect.SetX(left - iconWidth); + mIconRect.SetY(top); + mIconRect.SetWidth(iconWidth); + mIconRect.SetHeight(iconHeight); + + // Position the L/R labels + mLeftTextPos = wxPoint(left - ltxtWidth - gap, height - gap - ltxtHeight); + mRightTextPos = wxPoint(width - rside - gap, height - gap - rtxtHeight); + + // left is now left edge of left bar + left += gap; + + // width is now the entire width of the meter canvas + width -= gap + rside + gap + left; + + // height is now the entire height of the meter canvas + height -= top + gap; + + // barw is half of the canvas while allowing for a gap between meters + barw = (width - gap) / 2; + + // barh is now the height of the canvas + barh = height; + + // We always have 2 bars + mNumBars = 2; + + // Save dimensions of the left bevel + mBar[0].b = wxRect(left, top, barw, barh); + + // Save dimensions of the right bevel + mBar[1].b = mBar[0].b; + mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge + + // Set bar and clipping indicator dimensions + SetBarAndClip(0, true); + SetBarAndClip(1, true); + + mRuler.SetBounds(mBar[1].r.GetRight() + 1, // +1 for the bevel + mBar[1].r.GetTop(), + mWidth, + mBar[1].r.GetBottom()); + mRuler.OfflimitsPixels(mRightTextPos.y - gap, mBar[1].r.GetBottom()); + break; + case VerticalStereoCompact: + // Ensure there's a margin between top edge of window and the meters + top = gap; + + // Position the icon + mIconRect.SetX((width - iconWidth) / 2); + mIconRect.SetY(top); + mIconRect.SetWidth(iconWidth); + mIconRect.SetHeight(iconHeight); + + // top is now the top of the bar + top += iconHeight + gap; + + // height is now the entire height of the meter canvas + height -= top + gap + ltxtHeight + gap; + + // barw is half of the canvas while allowing for a gap between meters + barw = (width / 2) - gap; + + // barh is now the height of the canvas + barh = height; + + // We always have 2 bars + mNumBars = 2; + + // Save dimensions of the left bevel + mBar[0].b = wxRect(left, top, barw, barh); + + // Save dimensions of the right bevel + mBar[1].b = mBar[0].b; + mBar[1].b.SetLeft(mBar[0].b.GetRight() + 1 + gap); // +1 for right edge + + // Set bar and clipping indicator dimensions + SetBarAndClip(0, true); + SetBarAndClip(1, true); + + // L/R is centered horizontally under each bar + mLeftTextPos = wxPoint(mBar[0].b.GetLeft() + ((mBar[0].b.GetWidth() - ltxtWidth) / 2), top + barh + gap); + mRightTextPos = wxPoint(mBar[1].b.GetLeft() + ((mBar[1].b.GetWidth() - rtxtWidth) / 2), top + barh + gap); + + mRuler.SetBounds((mWidth - mRulerWidth) / 2, + mBar[1].r.GetTop(), + (mWidth - mRulerWidth) / 2, + mBar[1].r.GetBottom()); + mRuler.OfflimitsPixels(0, 0); break; case HorizontalStereo: - if (mWidth < menuWidth + iconWidth + 8) { - mIconPos = wxPoint(-999, -999); // Don't display icon - mMenuRect = wxRect(2, mHeight - menuHeight - 2, - menuWidth, menuHeight); - } - else { - mIconPos = wxPoint(2, mHeight - iconHeight); - mMenuRect = wxRect(iconWidth + 2, mHeight - menuHeight - 5, - menuWidth, menuHeight); - } - height = intmin(height-(menuHeight+3), height-iconHeight) - 2; - left = 2 + intmax(mLeftSize.x, mRightSize.x); - width -= left; - mLeftTextPos = wxPoint(2, (height)/4 - mLeftSize.y/2); - mRightTextPos = wxPoint(2, (height*3)/4 - mLeftSize.y/2); - barw = width - 4; - barh = (height-2)/2; - mNumBars = 2; - mBar[0].r = wxRect(left+2, height/2 - barh - 1, barw, barh); - SetBarClip( 0 ); - mBar[1].r = wxRect(left+2, height/2 + 1, barw, barh); - SetBarClip( 1 ); + // Ensure there's a margin between left edge of window and items + left = gap; - mRuler.SetOrientation(wxHORIZONTAL); - mRuler.SetBounds(mBar[1].r.x, - mBar[1].r.y + mBar[1].r.height + 1, - mBar[1].r.x + mBar[1].r.width, - mWidth); - if (mDB) { - mRuler.SetRange(-mDBRange, 0); - mRuler.SetFormat(Ruler::LinearDBFormat); - } - else { - mRuler.SetRange(0, 1); - mRuler.SetFormat(Ruler::RealFormat); - } - mRuler.OfflimitsPixels(0, mMenuRect.x+mMenuRect.width-4); + // Add a gap between bottom of icon and bottom of window + height -= gap; + + // Create icon rectangle + mIconRect.SetX(left); + mIconRect.SetY(height - iconHeight); + mIconRect.SetWidth(iconWidth); + mIconRect.SetHeight(iconHeight); + + // Make sure there's room for icon and gap between the bottom of the meter and icon + height -= iconHeight + gap; + + // L/R is centered vertically and to the left of a each bar + mLeftTextPos = wxPoint(left, (height / 4) - ltxtHeight / 2); + mRightTextPos = wxPoint(left, (height * 3 / 4) - rtxtHeight / 2); + + // Add width of widest of the L/R characters + left += intmax(ltxtWidth, rtxtWidth); //, iconWidth); + + // Add gap between L/R and meter bevel + left += gap; + + // width is now the entire width of the meter canvas + width -= left; + + // barw is now the width of the canvas minus gap between canvas and right window edge + barw = width - gap; + + // barh is half of the canvas while allowing for a gap between meters + barh = (height - gap) / 2; + + // We always have 2 bars + mNumBars = 2; + + // Save dimensions of the top bevel + mBar[0].b = wxRect(left, top, barw, barh); + + // Save dimensions of the bottom bevel + mBar[1].b = mBar[0].b; + mBar[1].b.SetTop(mBar[0].b.GetBottom() + 1 + gap); // +1 for bottom edge + + // Set bar and clipping indicator dimensions + SetBarAndClip(0, false); + SetBarAndClip(1, false); + + mRuler.SetBounds(mBar[1].r.GetLeft(), + mBar[1].r.GetBottom() + 1, // +1 to fit below bevel + mBar[1].r.GetRight(), + mHeight - mBar[1].r.GetBottom() + 1); + mRuler.OfflimitsPixels(0, mIconRect.GetRight() - 4); break; case HorizontalStereoCompact: - left = iSpacer; - mIconPos = wxPoint(left, (height-iconHeight)/2); - left += iconWidth + 2 *iSpacer; - mLeftTextPos = wxPoint(left, (height)/4 - mLeftSize.y/2); - mRightTextPos = wxPoint(left, (height*3)/4 - mLeftSize.y/2); - left += intmax(mLeftSize.x, mRightSize.x) + iSpacer; + // Ensure there's a margin between left edge of window and items + left = gap; + // Create icon rectangle + mIconRect.SetX(left); + mIconRect.SetY((height - iconHeight) / 2); + mIconRect.SetWidth(iconWidth); + mIconRect.SetHeight(iconHeight); + + // Add width of icon and gap between icon and L/R + left += iconWidth + gap; + + // L/R is centered vertically and to the left of a each bar + mLeftTextPos = wxPoint(left, (height / 4) - (ltxtHeight / 2)); + mRightTextPos = wxPoint(left, (height * 3 / 4) - (ltxtHeight / 2)); + + // Add width of widest of the L/R characters and a gap between labels and meter bevel + left += intmax(ltxtWidth, rtxtWidth) + gap; + + // width is now the entire width of the meter canvas width -= left; - barw = width - 4 - menuWidth - 3* iSpacer; - barh = (height-2)/2; + + // barw is now the width of the canvas minus gap between canvas and window edge + barw = width - gap; + + // barh is half of the canvas while allowing for a gap between meters + barh = (height - gap) / 2; + + // We always have 2 bars mNumBars = 2; - mBar[0].r = wxRect(left, 0, barw, barh); - SetBarClip( 0 ); - mBar[1].r = wxRect(left, 2 + barh, barw, barh); - SetBarClip( 1 ); - left += barw + 2* iSpacer; - mMenuRect = wxRect(left, (height-menuHeight)/2, menuWidth, menuHeight); - left += menuWidth + 2* iSpacer; + // Save dimensions of the top bevel + mBar[0].b = wxRect(left, top, barw, barh); + // Save dimensions of the bottom bevel + // Since the bars butt up against the window's top and bottom edges, we need + // to include an extra pixel in the bottom bar when the window height and + // meter height do not exactly match. + mBar[1].b = mBar[0].b; + mBar[1].b.SetTop(mBar[0].b.GetBottom() + 1 + gap); // +1 for bottom bevel + mBar[1].b.SetHeight(mHeight - mBar[1].b.GetTop() - 1); // +1 for bottom bevel - mRuler.SetOrientation(wxHORIZONTAL); - { - int BarMid = (mBar[0].r.y + mBar[1].r.y + mBar[1].r.height ) / 2; - const int RulerHeight = 24; - const int TextDownBy = 2; - mRuler.SetBounds(mBar[0].r.x, - BarMid +TextDownBy - RulerHeight/2, - mBar[1].r.x + mBar[1].r.width, - BarMid +TextDownBy + RulerHeight/2); - } - if (mDB) { - mRuler.SetRange(-mDBRange, 0); - mRuler.SetFormat(Ruler::LinearDBFormat); - } - else { - mRuler.SetRange(0, 1); - mRuler.SetFormat(Ruler::RealFormat); - } - mRuler.OfflimitsPixels(0,0); - break; - case Waveform: - mNumBars = 0; + // Add clipping indicators - do after setting bar/bevel dimensions above + SetBarAndClip(0, false); + SetBarAndClip(1, false); + + mRuler.SetBounds(mBar[1].r.GetLeft(), + mBar[1].b.GetTop() - (mRulerHeight / 2), + mBar[1].r.GetRight(), + mBar[1].b.GetTop() - (mRulerHeight / 2)); + mRuler.OfflimitsPixels(0, 0); break; } - if (mNumBars > 0) { - // Compute bounding rectangle of all bars (to save time when - // blitting just the bars to the screen) - left = mBar[0].r.x; - top = mBar[0].r.y; - right = mBar[0].r.x + mBar[0].r.width; - bottom = mBar[0].r.y + mBar[0].r.height; - for(i=1; iGetBackgroundColour() ); - } -#endif - - dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush(mBkgndBrush); - dc.DrawRectangle(0, 0, mWidth, mHeight); -#endif - - // MixerTrackCluster style has no icon or menu. - if (mStyle != MixerTrackCluster) - { - dc.DrawBitmap(*mIcon, mIconPos.x, mIconPos.y, true); - - // Draws a beveled button and a down pointing triangle. - // The style and sizing matches the ones in the title - // bar of the waveform left-hand-side panels. - wxRect r = mMenuRect; - AColor::Bevel(dc, true, r); - dc.SetBrush(*wxBLACK_BRUSH); - dc.SetPen(*wxBLACK_PEN); - AColor::Arrow(dc, - r.x + 3, - r.y + 5, - 10); - } - - // Draw the ruler - // LLL: Why test the number of bars? Not a very good meter without bars... - if (mNumBars > 0) - { - mRuler.Draw(dc); - } - - // MixerTrackCluster style has no L/R labels. - if (mStyle != MixerTrackCluster) - { - dc.SetFont(GetFont()); - dc.DrawText(mLeftText, mLeftTextPos.x, mLeftTextPos.y); - dc.DrawText(mRightText, mRightTextPos.x, mRightTextPos.y); - } - - // Setup the colors for the 3 sections of the meter bars - wxColor green(117, 215, 112); - wxColor yellow(255, 255, 0); - wxColor red(255, 0, 0); - - // Draw the meter bars at maximum levels - for (int i = 0; i < mNumBars; i++) - { - // Cache bar rect - wxRect r = mBar[i].r; - - if (mGradient) - { - // Calculate the size of the two gradiant segments of the meter - double gradw; - double gradh; - if (mDB) - { - gradw = (double) r.GetWidth() / mDBRange * 6.0; - gradh = (double) r.GetHeight() / mDBRange * 6.0; - } - else - { - gradw = (double) r.GetWidth() / 100 * 25; - gradh = (double) r.GetHeight() / 100 * 25; - } - - if (mBar[i].vert) - { - // Draw the "critical" segment (starts at top of meter and works down) - r.SetHeight(gradh); - dc.GradientFillLinear(r, red, yellow, wxSOUTH); - - // Draw the "warning" segment - r.SetTop(r.GetBottom()); - dc.GradientFillLinear(r, yellow, green, wxSOUTH); - - // Draw the "safe" segment - r.SetTop(r.GetBottom()); - r.SetBottom(mBar[i].r.GetBottom()); - dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush(green); - dc.DrawRectangle(r); - } - else - { - // Draw the "safe" segment - r.SetWidth(r.GetWidth() - (int) (gradw + gradw + 0.5)); - dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush(green); - dc.DrawRectangle(r); - - // Draw the "warning" segment - r.SetLeft(r.GetRight() + 1); - r.SetWidth(floor(gradw)); - dc.GradientFillLinear(r, green, yellow); - - // Draw the "critical" segment - r.SetLeft(r.GetRight() + 1); - r.SetRight(mBar[i].r.GetRight()); - dc.GradientFillLinear(r, yellow, red); - } -#ifdef EXPERIMENTAL_METER_LED_STYLE - if (!mBar[i].vert) - { - wxRect r = mBar[i].r; - wxPen BackgroundPen; - BackgroundPen.SetColour( wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE) ); - dc.SetPen( BackgroundPen ); - int i; - for(i=0;iSetPen(*wxTRANSPARENT_PEN); - dc->SetBrush(mBkgndBrush); - dc->DrawRectangle(mBar[0].r.GetLeft(), - mBar[0].r.GetBottom() + 1, - mBar[0].r.GetWidth(), - mBar[1].r.GetTop() - mBar[0].r.GetBottom() - 1); - AColor::Bevel(*dc, false, mBar[0].r); - AColor::Bevel(*dc, false, mBar[1].r); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(mBkgndBrush); + dc.DrawRectangle(mBar[0].b.GetLeft(), + mBar[0].b.GetBottom() + 1, + mBar[0].b.GetWidth(), + mBar[1].b.GetTop() - mBar[0].b.GetBottom() - 1); + AColor::Bevel(dc, false, mBar[0].b); + AColor::Bevel(dc, false, mBar[1].b); + } + else if (mStyle == VerticalStereoCompact) + { + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(mBkgndBrush); + dc.DrawRectangle(mBar[0].b.GetRight() + 1, + mBar[0].b.GetTop(), + mBar[1].b.GetLeft() - mBar[0].b.GetRight() - 1, + mBar[0].b.GetHeight()); + AColor::Bevel(dc, false, mBar[0].b); + AColor::Bevel(dc, false, mBar[1].b); } #endif - // We can have numbers over the bars, in which case we have to draw them each time. - if( mRuler.mRect.Intersects( mBar[0].r ) ) - mRuler.Draw(*dc); + // Compact style requires redrawing ruler + if (mStyle == HorizontalStereoCompact || mStyle == VerticalStereoCompact) + { + mRuler.Draw(dc); + } } } -void Meter::DrawMeterBar(wxDC &dc, MeterBar *meterBar) +void Meter::DrawMeterBar(wxDC &dc, MeterBar *bar) { - // Cache some metrics (and adjust to be inside the bevel) - wxRect r = meterBar->r; - wxCoord x = r.GetLeft() + 1; - wxCoord y = r.GetTop() + 1; - wxCoord w = r.GetWidth() - 1; - wxCoord h = r.GetHeight() - 1; - - // Map the predrawn bitmap into the source DC - wxMemoryDC srcDC; - srcDC.SelectObject(*mBitmap); + // Cache some metrics + wxCoord x = bar->r.GetLeft(); + wxCoord y = bar->r.GetTop(); + wxCoord w = bar->r.GetWidth(); + wxCoord h = bar->r.GetHeight(); + wxCoord ht; + wxCoord wd; // Setup for erasing the background dc.SetPen(*wxTRANSPARENT_PEN); dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mBkgndBrush); - int ht; - int wd; if (mGradient) { - if (meterBar->vert) + // Map the predrawn bitmap into the source DC + wxMemoryDC srcDC; + srcDC.SelectObject(*mBitmap); + + if (bar->vert) { // Copy as much of the predrawn meter bar as is required for the // current peak. - ht = (int)(meterBar->peak * h + 0.5); - dc.Blit(x, y + h - ht, w, ht, &srcDC, x, y + h - ht); + // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + ht = (int)(bar->peak * (h - 1) + 0.5); // Blank out the rest - dc.DrawRectangle(x, y, w, y + h - ht); + if (h - ht) + { + // ht includes peak value...not really needed but doesn't hurt + dc.DrawRectangle(x, y, w, h - ht); + } + + // Copy as much of the predrawn meter bar as is required for the + // current peak. + // +/-1 to include the peak position + if (ht) + { + dc.Blit(x, y + h - ht - 1, w, ht + 1, &srcDC, x, y + h - ht - 1); + } // Draw the "recent" peak hold line using the predrawn meter bar so that // it will be the same color as the original level. - ht = (int)(meterBar->peakHold * h + 0.5); - dc.Blit(x, y + h - ht - 1, w, 2, &srcDC, x, y + h - ht - 1); + // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + ht = (int)(bar->peakHold * (h - 1) + 0.5); + if (ht > 0) + { + AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1); + if (ht > 1) + { + AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht); + } + } // Draw the "maximum" peak hold line + // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout() dc.SetPen(mPeakPeakPen); - ht = (int)(meterBar->peakPeakHold * h + 0.5); - AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1); - if (ht > 1) + ht = (int)(bar->peakPeakHold * (h - 1) + 0.5); + if (ht > 0) { - AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht); + AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1); + if (ht > 1) + { + AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht); + } } } else { - // Copy as much of the predrawn meter bar as is required for the - // current peak. But, only blit() if there's something to copy - // to prevent display corruption. - wd = (int)(meterBar->peak * w + 0.5); - if (wd) - dc.Blit(x, y, wd, h, &srcDC, x, y); + // Calculate the peak position + // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + wd = (int)(bar->peak * (w - 1) + 0.5); // Blank out the rest if (w - wd) + { + // wd includes peak value...not really needed but doesn't hurt dc.DrawRectangle(x + wd, y, w - wd, h); + } + + // Copy as much of the predrawn meter bar as is required for the + // current peak. But, only blit() if there's something to copy + // to prevent display corruption. + // +1 to include peak position + if (wd) + { + dc.Blit(x, y, wd + 1, h, &srcDC, x, y); + } // Draw the "recent" peak hold line using the predrawn meter bar so that // it will be the same color as the original level. - wd = (int)(meterBar->peakHold * w + 0.5); - dc.Blit(wd, y, 2, h, &srcDC, wd, y); - - // Draw the "maximum" peak hold line using a themed color. - dc.SetPen(mPeakPeakPen); - wd = (int)(meterBar->peakPeakHold * w + 0.5); - AColor::Line(dc, x + wd, y, x + wd, y + h - 1); - if (wd > 1) + // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + wd = (int)(bar->peakHold * (w - 1) + 0.5); + if (wd > 0) { - AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1); + AColor::Line(dc, x + wd, y, x + wd, y + h - 1); + if (wd > 1) + { + AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1); + } + } + + // Draw the "maximum" peak hold line using a themed color + // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + dc.SetPen(mPeakPeakPen); + wd = (int)(bar->peakPeakHold * (w - 1) + 0.5); + if (wd > 0) + { + AColor::Line(dc, x + wd, y, x + wd, y + h - 1); + if (wd > 1) + { + AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1); + } } } + + // No longer need the source DC, so unselect the predrawn bitmap + srcDC.SelectObject(wxNullBitmap); } else { - wxRect rRMS; - - if (meterBar->vert) + if (bar->vert) { - // Calculate the peak and rms rectangles - ht = (int)(meterBar->peak * h + 0.5); - r = wxRect(x, y + h - ht, w, ht); - ht = (int)(meterBar->rms * h + 0.5); - rRMS = wxRect(x, y + h - ht, w, ht); + // Calculate the peak position + // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + ht = (int)(bar->peak * (h - 1) + 0.5); // Blank out the rest - dc.DrawRectangle(x, y, w, y + h - ht); + if (h - ht) + { + // ht includes peak value...not really needed but doesn't hurt + dc.DrawRectangle(x, y, w, h - ht); + } - // Reset the colors - dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.SetPen(mPen); + // Draw the peak level + // +/-1 to include the peak position + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mBrush); + if (ht) + { + dc.DrawRectangle(x, y + h - ht - 1, w, ht + 1); + } // Draw the "recent" peak hold line - int ht = (int)(meterBar->peakHold * h + 0.5); - AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1); - if (ht > 1) + // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + int ht = (int)(bar->peakHold * (h - 1) + 0.5); + if (ht > 0) { - AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht); + AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1); + if (ht > 1) + { + AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht); + } + } + + // Calculate the rms position + // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + // +1 to include the rms position + ht = (int)(bar->rms * (h - 1) + 0.5); + + // Draw the RMS level + dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mRMSBrush); + if (ht) + { + dc.DrawRectangle(x, y + h - ht - 1, w, ht + 1); } // Draw the "maximum" peak hold line + // (h - 1) corresponds to the mRuler.SetBounds() in HandleLayout() dc.SetPen(mPeakPeakPen); - ht = (int)(meterBar->peakPeakHold * h + 0.5); - AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1); - if (ht > 1) + ht = (int)(bar->peakPeakHold * (h - 1) + 0.5); + if (ht > 0) { - AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht); + AColor::Line(dc, x, y + h - ht - 1, x + w - 1, y + h - ht - 1); + if (ht > 1) + { + AColor::Line(dc, x, y + h - ht, x + w - 1, y + h - ht); + } } } else { - // Calculate the peak and rms rectangles - wd = (int)(meterBar->peak * w + 0.5); - r = wxRect(x, y, wd, h); - wd = (int)(meterBar->rms * w + 0.5); - rRMS = wxRect(x, y, wd, h); + // Calculate the peak position + // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + wd = (int)(bar->peak * (w - 1) + 0.5); // Blank out the rest - dc.DrawRectangle(x + wd, y, w - wd, h); + if (w - wd) + { + // wd includes peak value...not really needed but doesn't hurt + dc.DrawRectangle(x + wd, y, w - wd, h); + } - // Reset the colors - dc.SetBrush(*wxTRANSPARENT_BRUSH); - dc.SetPen(mPen); + // Draw the peak level + // +1 to include peak position + dc.SetPen(*wxTRANSPARENT_PEN); + dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mBrush); + if (wd) + { + dc.DrawRectangle(x, y, wd + 1, h); + } // Draw the "recent" peak hold line - wd = (int)(meterBar->peakHold * w + 0.5); - AColor::Line(dc, x + wd, y, x + wd, y + h - 1); - if (wd > 1) + // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + wd = (int)(bar->peakHold * (w - 1) + 0.5); + if (wd > 0) { - AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1); + AColor::Line(dc, x + wd, y, x + wd, y + h - 1); + if (wd > 1) + { + AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1); + } + } + + // Calculate the rms position + // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout() + wd = (int)(bar->rms * (w - 1) + 0.5); + + // Draw the rms level + // +1 to include the rms position + dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mRMSBrush); + if (wd) + { + dc.DrawRectangle(x, y, wd + 1, h); } // Draw the "maximum" peak hold line using a themed color + // (w - 1) corresponds to the mRuler.SetBounds() in HandleLayout() dc.SetPen(mPeakPeakPen); - wd = (int)(meterBar->peakPeakHold * w + 0.5); - AColor::Line(dc, x + wd, y, x + wd, y + h - 1); - if (wd > 1) + wd = (int)(bar->peakPeakHold * (w - 1) + 0.5); + if (wd > 0) { - AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1); + AColor::Line(dc, x + wd, y, x + wd, y + h - 1); + if (wd > 1) + { + AColor::Line(dc, x + wd - 1, y, x + wd - 1, y + h - 1); + } } } - - // Draw the peak and rms levels - dc.SetPen(*wxTRANSPARENT_PEN); - dc.SetBrush( mMeterDisabled ? mDisabledBkgndBrush : mBrush); - dc.DrawRectangle(r); - dc.SetBrush( mMeterDisabled ? mDisabledBkgndBrush : mRMSBrush); - dc.DrawRectangle(rRMS); } // If meter had a clipping indicator, draw or erase it @@ -1443,7 +1653,7 @@ void Meter::DrawMeterBar(wxDC &dc, MeterBar *meterBar) // it is always "true". if (mClip) { - if (meterBar->clipping) + if (bar->clipping) { dc.SetBrush(mClipBrush); } @@ -1452,16 +1662,15 @@ void Meter::DrawMeterBar(wxDC &dc, MeterBar *meterBar) dc.SetBrush(mMeterDisabled ? mDisabledBkgndBrush : mBkgndBrush); } dc.SetPen(*wxTRANSPARENT_PEN); - dc.DrawRectangle(meterBar->rClip); - dc.SetBrush(*wxTRANSPARENT_BRUSH); - AColor::Bevel(dc, false, meterBar->rClip); + wxRect r(bar->rClip.GetX() + 1, + bar->rClip.GetY() + 1, + bar->rClip.GetWidth() - 1, + bar->rClip.GetHeight() - 1); + dc.DrawRectangle(r); } - - // No longer need the source DC, so unselect the predrawn bitmap - srcDC.SelectObject(wxNullBitmap); } -bool Meter::IsMeterDisabled() +bool Meter::IsMeterDisabled() const { return mMeterDisabled != 0; } @@ -1479,12 +1688,10 @@ void Meter::StartMonitoring() if (p){ gAudioIO->StartMonitoring(p->GetRate()); } - - // Update preferences also forces tooltips to be changed. - wxCommandEvent e(EVT_METER_PREFERENCES_CHANGED); - e.SetEventObject(this); - GetParent()->GetEventHandler()->ProcessEvent(e); - mMonitoring = true; + + mLayoutValid = false; + + Refresh(false); } } @@ -1522,107 +1729,25 @@ void Meter::OnAudioIOStatus(wxCommandEvent &evt) mMonitoring = false; } - Refresh(); + Refresh(false); } // // Pop-up menu handlers // -void Meter::OnDisableMeter(wxCommandEvent & WXUNUSED(event)) -{ - if (mMeterDisabled) //Enable - { - mMeterDisabled = false; - Refresh(false); - } - else - { - if (mIsInput) - { - if (gAudioIO->IsMonitoring()) - { - gAudioIO->StopStream(); - } - mMeterDisabled = true; - } - mLayoutValid = false; - Refresh(false); - } - if (mIsInput) - { - gPrefs->Write(wxT("/Meter/MeterInputDisabled"), mMeterDisabled); - } - else - { - gPrefs->Write(wxT("/Meter/MeterOutputDisabled"), mMeterDisabled); - } - gPrefs->Flush(); -} - -void Meter::OnHorizontal(wxCommandEvent & WXUNUSED(event)) -{ - SetStyle(HorizontalStereo); -} - -void Meter::OnVertical(wxCommandEvent & WXUNUSED(event)) -{ - SetStyle(VerticalStereo); -} - -void Meter::OnMulti(wxCommandEvent & WXUNUSED(event)) -{ - SetStyle(VerticalMulti); -} - -void Meter::OnEqualizer(wxCommandEvent & WXUNUSED(event)) -{ - SetStyle(Equalizer); -} - -void Meter::OnWaveform(wxCommandEvent & WXUNUSED(event)) -{ - SetStyle(Waveform); -} - -void Meter::OnLinear(wxCommandEvent & WXUNUSED(event)) -{ - mDB = false; - mLayoutValid = false; - Refresh(false); -} - -void Meter::OnDB(wxCommandEvent & WXUNUSED(event)) -{ - mDB = true; - mLayoutValid = false; - Refresh(false); -} - -void Meter::OnClip(wxCommandEvent & WXUNUSED(event)) -{ -} - void Meter::OnMonitor(wxCommandEvent & WXUNUSED(event)) { StartMonitoring(); } -#ifdef AUTOMATED_INPUT_LEVEL_ADJUSTMENT -void Meter::OnAutomatedInputLevelAdjustment(wxCommandEvent &evt) +void Meter::OnMeterPrefsUpdated(wxCommandEvent & evt) { - if (gAudioIO->AILAIsActive()) { - gAudioIO->AILADisable(); - AudacityProject *p = GetActiveProject(); - if (p) p->TP_DisplayStatusMessage(_("Automated Recording Level Adjustment stopped as requested by user.")); - } - else - gAudioIO->AILAInitialize(); -} -#endif + evt.Skip(); -void Meter::OnFloat(wxCommandEvent & WXUNUSED(event)) -{ + UpdatePrefs(); + + Refresh(false); } void Meter::OnPreferences(wxCommandEvent & WXUNUSED(event)) @@ -1632,8 +1757,10 @@ void Meter::OnPreferences(wxCommandEvent & WXUNUSED(event)) wxRadioButton *rms; wxRadioButton *db; wxRadioButton *linear; + wxRadioButton *automatic; wxRadioButton *horizontal; wxRadioButton *vertical; + int meterRefreshRate = mMeterRefreshRate; // Dialog is a child of the project, rather than of the toolbar. // This determines where it pops up. @@ -1647,7 +1774,7 @@ void Meter::OnPreferences(wxCommandEvent & WXUNUSED(event)) S.StartHorizontalLay(); { rate = S.AddTextBox(_("Meter refresh rate per second [1-100]: "), - wxString::Format(wxT("%d"), (int) mMeterRefreshRate), + wxString::Format(wxT("%d"), meterRefreshRate), 10); rate->SetName(_("Meter refresh rate per second [1-100]")); wxIntegerValidator vld(&mMeterRefreshRate); @@ -1696,13 +1823,17 @@ void Meter::OnPreferences(wxCommandEvent & WXUNUSED(event)) { S.StartVerticalLay(); { - horizontal = S.AddRadioButton(_("Horizontal")); + automatic = S.AddRadioButton(_("Automatic")); + automatic->SetName(_("Automatic")); + automatic->SetValue(mDesiredStyle == AutomaticStereo); + + horizontal = S.AddRadioButtonToGroup(_("Horizontal")); horizontal->SetName(_("Horizontal")); - horizontal->SetValue(mStyle == HorizontalStereo); + horizontal->SetValue(mDesiredStyle == HorizontalStereo); vertical = S.AddRadioButtonToGroup(_("Vertical")); vertical->SetName(_("Vertical")); - vertical->SetValue(mStyle == VerticalStereo); + vertical->SetValue(mDesiredStyle == VerticalStereo); } S.EndVerticalLay(); } @@ -1719,15 +1850,44 @@ void Meter::OnPreferences(wxCommandEvent & WXUNUSED(event)) if (dlg.ShowModal() == wxID_OK) { - gPrefs->Write(wxT("/Meter/MeterRefreshRate"), mMeterRefreshRate); - gPrefs->Write(wxT("/Meter/MeterStyle"), horizontal->GetValue() ? wxT("HorizontalStereo") : wxT("VerticalStereo")); - gPrefs->Write(wxT("/Meter/MeterBars"), gradient->GetValue() ? wxT("Gradient") : wxT("RMS")); - gPrefs->Write(wxT("/Meter/MeterType"), db->GetValue() ? wxT("dB") : wxT("Linear")); + wxChar *style[] = + { + wxT("AutomaticStereo"), + wxT("HorizontalStereo"), + wxT("VerticalStereo") + }; + int s = 0; + s = automatic->GetValue() ? 0 : s; + s = horizontal->GetValue() ? 1 : s; + s = vertical->GetValue() ? 2 : s; + + gPrefs->Write(Key(wxT("Style")), style[s]); + gPrefs->Write(Key(wxT("Bars")), gradient->GetValue() ? wxT("Gradient") : wxT("RMS")); + gPrefs->Write(Key(wxT("Type")), db->GetValue() ? wxT("dB") : wxT("Linear")); + gPrefs->Write(Key(wxT("RefreshRate")), rate->GetValue()); gPrefs->Flush(); + // Currently, there are 2 playback meters and 2 record meters and any number of + // mixerboard meters, so we have to send out an preferences updated message to + // ensure they all update themselves. wxCommandEvent e(EVT_METER_PREFERENCES_CHANGED); e.SetEventObject(this); GetParent()->GetEventHandler()->ProcessEvent(e); } } + +wxString Meter::Key(const wxString & key) const +{ + if (mStyle == MixerTrackCluster) + { + return wxT("/Meter/Mixerboard/") + key; + } + + if (mIsInput) + { + return wxT("/Meter/Input/") + key; + } + + return wxT("/Meter/Output/") + key; +} diff --git a/src/widgets/Meter.h b/src/widgets/Meter.h index 3090c2c7d..bcb49500a 100644 --- a/src/widgets/Meter.h +++ b/src/widgets/Meter.h @@ -33,7 +33,8 @@ const int kMaxMeterBars = 2; struct MeterBar { bool vert; - wxRect r; + wxRect b; // Bevel around bar + wxRect r; // True bar drawing area float peak; float rms; float peakHold; @@ -92,13 +93,12 @@ class Meter : public wxPanel // These should be kept in the same order as they appear // in the menu enum Style { + AutomaticStereo, HorizontalStereo, VerticalStereo, - VerticalMulti, - Equalizer, - Waveform, MixerTrackCluster, // Doesn't show menu, icon, or L/R labels, but otherwise like VerticalStereo. - HorizontalStereoCompact // Thinner. + HorizontalStereoCompact, // Thinner. + VerticalStereoCompact, // Narrower. }; @@ -115,7 +115,8 @@ class Meter : public wxPanel void UpdatePrefs(); void Clear(); - Style GetStyle() { return mStyle; } + Style GetStyle() const { return mStyle; } + Style GetDesiredStyle() const { return mDesiredStyle; } void SetStyle(Style newStyle); /** \brief @@ -149,6 +150,7 @@ class Meter : public wxPanel */ void UpdateDisplay(int numChannels, int numFrames, float *sampleData); + // Vaughan, 2010-11-29: This not currently used. See comments in MixerTrackCluster::UpdateMeter(). //void UpdateDisplay(int numChannels, int numFrames, // // Need to make these double-indexed max and min arrays if we handle more than 2 channels. @@ -161,13 +163,11 @@ class Meter : public wxPanel * This method is thread-safe! Feel free to call from a * different thread (like from an audio I/O callback). */ - bool IsMeterDisabled(); + bool IsMeterDisabled() const; - float GetMaxPeak(); + float GetMaxPeak() const; - double ToLinearIfDB(double value); - - bool IsClipping(); + bool IsClipping() const; void StartMonitoring(); @@ -184,34 +184,23 @@ class Meter : public wxPanel void OnMeterUpdate(wxTimerEvent &evt); - void HandlePaint(wxDC &dc); void HandleLayout(wxDC &dc); + void SetActiveStyle(Style style); + void SetBarAndClip(int iBar, bool vert); + void DrawMeterBar(wxDC &dc, MeterBar *meterBar); + void ResetBar(MeterBar *bar, bool resetClipping); + void RepaintBarsNow(); + wxFont GetFont() const; // // Pop-up menu handlers // - void OnDisableMeter(wxCommandEvent &evt); - void OnHorizontal(wxCommandEvent &evt); - void OnVertical(wxCommandEvent &evt); - void OnMulti(wxCommandEvent &evt); - void OnEqualizer(wxCommandEvent &evt); - void OnWaveform(wxCommandEvent &evt); - void OnLinear(wxCommandEvent &evt); - void OnDB(wxCommandEvent &evt); - void OnClip(wxCommandEvent &evt); void OnMonitor(wxCommandEvent &evt); -#ifdef AUTOMATED_INPUT_LEVEL_ADJUSTMENT - void OnAutomatedInputLevelAdjustment(wxCommandEvent &evt); -#endif - void OnFloat(wxCommandEvent &evt); void OnPreferences(wxCommandEvent &evt); - void SetBarClip( int iBar ); - void DrawMeterBar(wxDC &dc, MeterBar *meterBar); - void ResetBar(MeterBar *bar, bool resetClipping); - void RepaintBarsNow(); - void CreateIcon(int aquaOffset); - wxFont GetFont(); + void OnMeterPrefsUpdated(wxCommandEvent &evt); + + wxString Key(const wxString & key) const; AudacityProject *mProject; MeterUpdateQueue mQueue; @@ -220,9 +209,13 @@ class Meter : public wxPanel int mWidth; int mHeight; + int mRulerWidth; + int mRulerHeight; + bool mIsInput; - Style mStyle, mSavedStyle; + Style mStyle; + Style mDesiredStyle; bool mGradient; bool mDB; int mDBRange; @@ -246,8 +239,7 @@ class Meter : public wxPanel bool mLayoutValid; wxBitmap *mBitmap; - wxRect mMenuRect; - wxPoint mIconPos; + wxRect mIconRect; wxPoint mLeftTextPos; wxPoint mRightTextPos; wxSize mLeftSize; @@ -261,7 +253,6 @@ class Meter : public wxPanel wxBrush mClipBrush; wxBrush mBkgndBrush; wxBrush mDisabledBkgndBrush; - wxRect mAllBarsRect; Ruler mRuler; wxString mLeftText; wxString mRightText;