1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 23:59:37 +02:00

Meter display changes as suggested by Peter, Steve, and Bill

Adds a transition from green to yellow starting at -12 db
or (0.50 for linear) and ending at -6 (or .75).  A transition
to red then occors to the end of the meter.

Meter preferences have been added and most of the context
menu items have been moved there.

In addition, you may now click the playback meter to reset
the peak level indicator instead of having to click the
record meter.
This commit is contained in:
lllucius 2014-10-03 06:15:29 +00:00
parent 80797a8471
commit 62e3467b7d
4 changed files with 385 additions and 238 deletions

View File

@ -43,6 +43,7 @@ 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
@ -110,9 +111,7 @@ void MeterToolBar::Populate()
void MeterToolBar::UpdatePrefs()
{
mPlayMeter->UpdatePrefs();
mPlayMeter->HandleLayout();
mRecordMeter->UpdatePrefs();
mRecordMeter->HandleLayout();
RegenerateTooltips();
@ -139,6 +138,11 @@ bool MeterToolBar::DestroyChildren()
return ToolBar::DestroyChildren();
}
void MeterToolBar::OnMeterPrefsUpdated(wxCommandEvent & WXUNUSED(evt))
{
UpdatePrefs();
}
void MeterToolBar::OnSize( wxSizeEvent & WXUNUSED(event) )
{
int width, height;

View File

@ -48,6 +48,7 @@ class MeterToolBar:public ToolBar {
int GetMinToolbarWidth() { return 255; }
private:
void OnMeterPrefsUpdated(wxCommandEvent & evt);
void RegenerateTooltips();
wxGridBagSizer *mSizer;

View File

@ -42,6 +42,7 @@
#include "../AudacityApp.h"
#include <wx/defs.h>
#include <wx/dialog.h>
#include <wx/dcmemory.h>
#include <wx/image.h>
#include <wx/intl.h>
@ -49,6 +50,7 @@
#include <wx/settings.h>
#include <wx/textdlg.h>
#include <wx/numdlg.h>
#include <wx/radiobut.h>
#include <wx/tooltip.h>
#include <wx/msgdlg.h>
@ -72,7 +74,10 @@
#include "../Theme.h"
#include "../AllThemeResources.h"
#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 */
@ -95,12 +100,12 @@ return output;
wxString MeterUpdateMsg::toStringIfClipped()
{
for (int i = 0; i<kMaxMeterBars; i++)
for (int i = 0; i<kMaxMeterBars; i++)
{
if (clipping[i] || (headPeakCount[i] > 0) || (tailPeakCount[i] > 0))
return toString();
if (clipping[i] || (headPeakCount[i] > 0) || (tailPeakCount[i] > 0))
return toString();
}
return wxT("");
return wxT("");
}
//
@ -236,9 +241,9 @@ Meter::Meter(wxWindow* parent, wxWindowID id,
{
int i;
wxColour backgroundColour =
wxColour backgroundColour =
wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);
mBkgndBrush = wxBrush(backgroundColour, wxSOLID);
mBkgndBrush = wxBrush(backgroundColour, wxSOLID);
UpdatePrefs();
@ -327,7 +332,8 @@ Meter::~Meter()
// is active.
if (gAudioIO->IsMonitoring())
gAudioIO->StopStream();
delete mIcon;
if (mIcon)
delete mIcon;
if (mBitmap)
delete mBitmap;
}
@ -339,11 +345,30 @@ void Meter::UpdatePrefs()
// MixerTrackCluster style has no menu, so disallows disabling the meter.
if (mStyle == MixerTrackCluster)
{
mMeterDisabled = 0L;
else if (mIsInput)
mMeterDisabled = gPrefs->Read(wxT("/Meter/MeterInputDisabled"), (long)0);
}
else
mMeterDisabled = gPrefs->Read(wxT("/Meter/MeterOutputDisabled"), (long)0);
{
mStyle = gPrefs->Read(wxT("/Meter/MeterStyle"), wxT("HorizontalStereo")) == wxT("HorizontalStereo") ?
HorizontalStereo :
VerticalStereo;
mDB = gPrefs->Read(wxT("/Meter/MeterType"), wxT("dB")) == wxT("dB");
if (mIsInput)
{
mMeterDisabled = gPrefs->Read(wxT("/Meter/MeterInputDisabled"), (long)0);
}
else
{
mMeterDisabled = gPrefs->Read(wxT("/Meter/MeterOutputDisabled"), (long)0);
}
}
mTimer.Start(1000 / mMeterRefreshRate);
mLayoutValid = false;
Refresh(true);
}
void Meter::OnErase(wxEraseEvent & WXUNUSED(event))
@ -353,28 +378,16 @@ void Meter::OnErase(wxEraseEvent & WXUNUSED(event))
void Meter::OnPaint(wxPaintEvent & WXUNUSED(event))
{
mLayoutValid = false;
wxPaintDC dc(this);
#ifdef __WXMAC__
// Mac OS X automatically double-buffers the screen for you,
// so our bitmap is unneccessary
HandlePaint(dc);
#else
if (!mBitmap)
mBitmap = new wxBitmap(mWidth, mHeight);
wxMemoryDC memDC;
memDC.SelectObject(*mBitmap);
HandlePaint(memDC);
dc.Blit(0, 0, mWidth, mHeight, &memDC, 0, 0, wxCOPY, FALSE);
#endif
}
void Meter::OnSize(wxSizeEvent & WXUNUSED(event))
{
delete mBitmap;
mBitmap = NULL;
GetClientSize(&mWidth, &mHeight);
//::wxMessageBox(wxString::Format(" mHeight=%d, mWidth=%d", mHeight,mWidth));
mLayoutValid = false;
}
@ -427,24 +440,24 @@ void Meter::OnMouse(wxMouseEvent &evt)
}
menu->AppendSeparator();
menu->Append(OnHorizontalID, _("Horizontal Stereo"));
menu->Append(OnVerticalID, _("Vertical Stereo"));
//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->Enable(mStyle==VerticalStereo? OnVerticalID: OnHorizontalID, false);
//menu->AppendSeparator();
menu->Append(OnLinearID, _("Linear"));
menu->Append(OnDBID, _("dB"));
menu->Enable(mDB? OnDBID: OnLinearID, false);
//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->AppendSeparator();
menu->Append(OnPreferencesID, _("Preferences..."));
@ -457,6 +470,9 @@ void Meter::OnMouse(wxMouseEvent &evt)
else if (evt.ButtonDown()) {
if (mIsInput)
StartMonitoring();
else {
Reset(mRate, true);
}
}
}
@ -484,13 +500,12 @@ void Meter::Reset(double sampleRate, bool resetClipping)
mTimer.Stop();
// While it's stopped, empty the queue
MeterUpdateMsg msg;
while(mQueue.Get(msg)) {
}
mQueue.Clear();
mLayoutValid = false;
mTimer.Start(1000 / mMeterRefreshRate);
mLayoutValid = false;
Refresh(false);
}
@ -569,13 +584,6 @@ void Meter::UpdateDisplay(int numChannels, int numFrames, float *sampleData)
for(j=0; j<mNumBars; j++)
msg.rms[j] = sqrt(msg.rms[j]/numFrames);
if (mDB) {
for(j=0; j<mNumBars; j++) {
msg.peak[j] = ToDB(msg.peak[j], mDBRange);
msg.rms[j] = ToDB(msg.rms[j], mDBRange);
}
}
mQueue.Put(msg);
}
@ -621,13 +629,6 @@ void Meter::UpdateDisplay(int numChannels, int numFrames, float *sampleData)
// }
// }
//
// if (mDB) {
// for(j=0; j<mNumBars; j++) {
// msg.peak[j] = ToDB(msg.peak[j], mDBRange);
// msg.rms[j] = ToDB(msg.rms[j], mDBRange);
// }
// }
//
// mQueue.Put(msg);
//}
@ -659,6 +660,12 @@ void Meter::OnMeterUpdate(wxTimerEvent & WXUNUSED(event))
for(j=0; j<mNumBars; j++) {
mBar[j].isclipping = false;
//
if (mDB) {
msg.peak[j] = ToDB(msg.peak[j], mDBRange);
msg.rms[j] = ToDB(msg.rms[j], mDBRange);
}
if (mDecay) {
if (mDB) {
float decayAmount = mDecayRate * deltaT / mDBRange;
@ -772,8 +779,32 @@ bool Meter::IsClipping()
return false;
}
void Meter::HandleLayout()
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.
/* 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.
{
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;
@ -943,202 +974,255 @@ void Meter::HandleLayout()
mLayoutValid = true;
}
void Meter::HandlePaint(wxDC &dc)
void Meter::HandlePaint(wxDC &destDC)
{
int i;
/* i18n-hint: One-letter abbreviation for Left, in VU Meter */
wxString mLeftText = _("L"); // used in a couple of places in this method.
// not a class member so it changes when the UI language is changed.
/* i18n-hint: One-letter abbreviation for Right, in VU Meter */
wxString mRightText = _("R");
dc.SetFont(GetFont());
if (mLeftSize.x == 0) { // Not yet initialized to dc.
if (mStyle != MixerTrackCluster) // MixerTrackCluster style has no L/R labels.
{
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;
}
}
if (!mLayoutValid)
HandleLayout();
{
// Get rid of previous predrawn bitmap
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() );
}
if( !mMeterDisabled )
{
mBkgndBrush.SetColour( GetParent()->GetBackgroundColour() );
}
#endif
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(mBkgndBrush);
dc.DrawRectangle(0, 0, mWidth, mHeight);
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);
// 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.
// 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);
}
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;
// 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);
}
// Give it a recessed look
AColor::Bevel(dc, false, mBar[i].r);
}
// Copy predrawn bitmap to the dest DC
destDC.Blit(0, 0, mWidth, mHeight, &dc, 0, 0);
// And unselect it from the source DC
dc.SelectObject(wxNullBitmap);
}
if (mNumBars>0)
mRuler.Draw(dc);
// MixerTrackCluster style has no L/R labels.
if (mStyle != MixerTrackCluster)
// Go draw the meter bars using current levels
for (int i = 0; i < mNumBars; i++)
{
dc.SetFont(GetFont());
dc.DrawText(mLeftText, mLeftTextPos.x, mLeftTextPos.y);
dc.DrawText(mRightText, mRightTextPos.x, mRightTextPos.y);
DrawMeterBar(destDC, &mBar[i]);
}
for(i=0; i<mNumBars; i++)
DrawMeterBar(dc, &mBar[i]);
}
void Meter::RepaintBarsNow()
{
if (!mLayoutValid)
return;
wxClientDC dc(this);
int i;
#ifdef __WXMAC__
// Mac OS X automatically double-buffers the screen for you,
// so our bitmap is unneccessary
for(i=0; i<mNumBars; i++)
DrawMeterBar(dc, &mBar[i]);
#else
if (!mBitmap)
mBitmap = new wxBitmap(mWidth, mHeight);
wxMemoryDC memDC;
memDC.SelectObject(*mBitmap);
for(i=0; i<mNumBars; i++)
DrawMeterBar(memDC, &mBar[i]);
dc.Blit(mAllBarsRect.x, mAllBarsRect.y,
mAllBarsRect.width, mAllBarsRect.height,
&memDC, mAllBarsRect.x, mAllBarsRect.y,
wxCOPY, false);
#endif
if (mLayoutValid)
{
wxClientDC dc(this);
for (int i = 0; i < mNumBars; i++)
{
DrawMeterBar(dc, &mBar[i]);
}
}
}
void Meter::DrawMeterBar(wxDC &dc, MeterBar *meterBar)
{
// Cache some metrics
wxRect r = meterBar->r;
wxRect rRMS = meterBar->r;
wxCoord x = r.GetLeft();
wxCoord y = r.GetTop();
wxCoord w = r.GetWidth();
wxCoord h = r.GetHeight();
// Map the predrawn bitmap into the source DC
wxMemoryDC srcDC;
srcDC.SelectObject(*mBitmap);
// Setup for erasing the background
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(mBkgndBrush);
dc.DrawRectangle(r);
AColor::Bevel(dc, false, r);
/*
AColor::Dark(&dc, false);
for(i=0; i<mNumTicks; i++)
if (meterBar->vert)
AColor::Line(dc, r.x+r.width/2-1, mTick[i], r.x+r.width/2+1, mTick[i]);
else
AColor::Line(dc, mTick[i], r.y+r.height/2-1, mTick[i], r.y+r.height/2+1);
*/
if (meterBar->vert)
{
// Copy as much of the predrawn meter bar as is required for the
// current peak.
int ht = (int)(meterBar->peak * h + 0.5);
dc.Blit(x, y + h - ht, w, ht, &srcDC, x, y + h - ht);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen(mPen);
// Blank out the rest
dc.DrawRectangle(x + 1, y, w - 1, y + h - ht);
if (meterBar->vert) {
int ht = (int)(meterBar->peakHold * r.height + 0.5);
AColor::Line(dc, r.x+1, r.y + r.height - ht,
r.x+r.width-1, r.y + r.height - ht);
if (ht > 1)
AColor::Line(dc, r.x+1, r.y + r.height - ht + 1,
r.x+r.width-1, r.y + r.height - 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, w, 2, &srcDC, x, y + h - ht);
// Draw the "maximum" peak hold line using a themed color.
dc.SetPen(mPeakPeakPen);
ht = (int)(meterBar->peakPeakHold * r.height + 0.5);
AColor::Line(dc, r.x+1, r.y + r.height - ht,
r.x+r.width-1, r.y + r.height - ht);
AColor::Line(dc, x + 1, y + h - ht, x + w - 1, y + h - ht);
if (ht > 1)
AColor::Line(dc, r.x+1, r.y + r.height - ht + 1,
r.x+r.width-1, r.y + r.height - ht + 1);
dc.SetPen(mPen);
ht = (int)(meterBar->peak * r.height + 0.5);
r = wxRect(r.x, r.y + r.height - ht,
r.width, ht);
ht = (int)(meterBar->rms * rRMS.height + 0.5);
rRMS = wxRect(rRMS.x, rRMS.y + rRMS.height - ht,
rRMS.width, ht);
{
AColor::Line(dc, x + 1, y + h - ht + 1, x + w - 1, y + h - ht + 1);
}
}
else {
int wd = (int)(meterBar->peakHold * r.width + 0.5);
AColor::Line(dc, r.x + wd, r.y + 1, r.x + wd, r.y + r.height - 1);
if (wd > 1)
AColor::Line(dc, r.x + wd - 1, r.y + 1, r.x + wd - 1, r.y + r.height - 1);
// Copy as much of the predrawn meter bar as is required for the
// current peak.
int wd = (int)(meterBar->peak * w + 0.5);
dc.Blit(x, y, wd, h, &srcDC, x, y);
// Blank out the rest
dc.DrawRectangle(x + wd, y + 1, w - wd, h - 1);
// 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 + 1, 2, h, &srcDC, wd, y + 1);
// Draw the "maximum" peak hold line using a themed color.
dc.SetPen(mPeakPeakPen);
wd = (int)(meterBar->peakPeakHold * r.width + 0.5);
AColor::Line(dc, r.x + wd, r.y + 1, r.x + wd, r.y + r.height - 1);
wd = (int)(meterBar->peakPeakHold * w + 0.5);
AColor::Line(dc, x + wd, y + 1, x + wd, y + h - 1);
if (wd > 1)
AColor::Line(dc, r.x + wd - 1, r.y + 1, r.x + wd - 1, r.y + r.height - 1);
dc.SetPen(mPen);
wd = (int)(meterBar->peak * r.width + 0.5);
r = wxRect(r.x, r.y,
wd, r.height);
wd = (int)(meterBar->rms * rRMS.width + 0.5);
rRMS = wxRect(rRMS.x, rRMS.y,
wd, rRMS.height);
{
AColor::Line(dc, x + wd - 1, y + 1, x + wd - 1, y + h - 1);
}
}
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(mBrush);
dc.DrawRectangle(r);
dc.SetBrush(mRMSBrush);
dc.DrawRectangle(rRMS);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.SetPen(mLightPen);
AColor::Line(dc, r.x, r.y, r.x + r.width, r.y);
AColor::Line(dc, r.x, r.y, r.x, r.y + r.height);
dc.SetPen(mDarkPen);
AColor::Line(dc, r.x + r.width, r.y, r.x + r.width, r.y + r.height);
AColor::Line(dc, r.x, r.y + r.height, r.x + r.width, r.y + r.height);
if (mClip) {
// If meter had a clipping indicator, draw or erase it
// LLL: At least I assume that's what "mClip" is supposed to be for as
// it is always "true".
if (mClip)
{
if (meterBar->clipping)
{
dc.SetBrush(mClipBrush);
}
else
{
dc.SetBrush(mBkgndBrush);
}
dc.SetPen(*wxTRANSPARENT_PEN);
dc.DrawRectangle(meterBar->rClip);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
AColor::Bevel(dc, false, meterBar->rClip);
}
// No longer need the source DC, so unselect the predrawn bitmap
srcDC.SelectObject(wxNullBitmap);
}
bool Meter::IsMeterDisabled() {return mMeterDisabled!=0;}
bool Meter::IsMeterDisabled()
{
return mMeterDisabled != 0;
}
void Meter::StartMonitoring()
{
@ -1173,7 +1257,7 @@ void Meter::StartMonitoring()
void Meter::OnDisableMeter(wxCommandEvent & WXUNUSED(event))
{
if (mMeterDisabled) //Enable
{
{
mLightPen = mSavedLightPen;
mDarkPen = mSavedDarkPen;
mBkgndBrush = mSavedBkgndBrush;
@ -1185,12 +1269,15 @@ void Meter::OnDisableMeter(wxCommandEvent & WXUNUSED(event))
Refresh(false);
mMeterDisabled = false;
}
}
else
{
if (mIsInput)
{
if (mIsInput) {
if (gAudioIO->IsMonitoring())
{
gAudioIO->StopStream();
}
}
mSavedLightPen = mLightPen;
mSavedDarkPen = mDarkPen;
@ -1207,15 +1294,15 @@ void Meter::OnDisableMeter(wxCommandEvent & WXUNUSED(event))
Refresh(false);
mMeterDisabled = true;
}
}
if (mIsInput)
{
{
gPrefs->Write(wxT("/Meter/MeterInputDisabled"), mMeterDisabled);
}
}
else
{
{
gPrefs->Write(wxT("/Meter/MeterOutputDisabled"), mMeterDisabled);
}
}
gPrefs->Flush();
}
@ -1286,32 +1373,81 @@ void Meter::OnFloat(wxCommandEvent & WXUNUSED(event))
void Meter::OnPreferences(wxCommandEvent & WXUNUSED(event))
{
wxNumberEntryDialog
d(this,
_("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."),
_("Meter refresh rate per second [1-100]: "),
_("Meter Preferences"),
mMeterRefreshRate,
1,
100);
wxTextCtrl *rate;
wxRadioButton *db;
wxRadioButton *linear;
wxRadioButton *horizontal;
wxRadioButton *vertical;
//#if defined(__WXMAC__)
// WXMAC doesn't support wxFRAME_FLOAT_ON_PARENT, so we do
//
// LL: I've commented this out because if you have, for instance, the meter
// toolbar undocked and large and then you open a dialog like an effect,
// the dialog may appear behind the dialog and you can't move either one.
//
// However, I'm leaving it here because I don't remember why I'd included
// it in the first place.
// SetWindowClass((WindowRef)d.MacGetWindowRef(), kFloatingWindowClass);
//#endif
wxDialog dlg(GetParent(), wxID_ANY, wxString(_("Meter Preferences")));
ShuttleGui S(&dlg, eIsCreating);
S.StartVerticalLay();
{
S.StartStatic(_("Refresh Rate"), 1);
{
S.AddFixedText(_("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."));
S.StartHorizontalLay();
{
rate = S.AddTextBox(_("Meter refresh rate per second [1-100]: "),
wxString::Format(wxT("%d"), mMeterRefreshRate),
10);
rate->SetName(_("Meter refresh rate per second [1-100]"));
wxIntegerValidator<long> vld(&mMeterRefreshRate);
vld.SetRange(0, 100);
rate->SetValidator(vld);
}
S.EndHorizontalLay();
}
S.EndStatic();
if (d.ShowModal() == wxID_OK) {
mMeterRefreshRate = d.GetValue();
gPrefs->Write(wxT("/Meter/MeterRefreshRate"), mMeterRefreshRate);
gPrefs->Flush();
S.StartStatic(_("Meter Style"), 1);
{
S.StartVerticalLay();
{
db = S.AddRadioButton(_("dB"));
db->SetName(_("dB"));
db->SetValue(mDB);
linear = S.AddRadioButtonToGroup(_("Linear"));
linear->SetName(_("Linear"));
linear->SetValue(!mDB);
}
S.EndVerticalLay();
}
S.EndStatic();
S.StartStatic(_("Orientation"), 1);
{
S.StartVerticalLay();
{
horizontal = S.AddRadioButton(_("Horizontal"));
horizontal->SetName(_("Horizontal"));
horizontal->SetValue(mStyle == HorizontalStereo);
vertical = S.AddRadioButtonToGroup(_("Vertical"));
vertical->SetName(_("Vertical"));
vertical->SetValue(mStyle == VerticalStereo);
}
S.EndVerticalLay();
}
S.EndStatic();
S.AddStandardButtons();
}
S.EndVerticalLay();
dlg.Layout();
dlg.Fit();
mTimer.Start(1000 / mMeterRefreshRate);
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/MeterType"), db->GetValue() ? wxT("dB") : wxT("Linear"));
gPrefs->Flush();
wxCommandEvent e(EVT_METER_PREFERENCES_CHANGED);
e.SetEventObject(this);
GetParent()->GetEventHandler()->ProcessEvent(e);
}
}

View File

@ -1,4 +1,4 @@
/**********************************************************************
/**********************************************************************
Audacity: A Digital Audio Editor
@ -24,6 +24,9 @@
#include "../Sequence.h"
#include "Ruler.h"
// Event used to notify all meters of preference changes
DECLARE_EVENT_TYPE(EVT_METER_PREFERENCES_CHANGED, -1);
// Increase this when we add support for multichannel meters
// (most of the code is already there)
const int kMaxMeterBars = 2;
@ -40,6 +43,7 @@ struct MeterBar {
bool isclipping; //ANSWER-ME: What's the diff between these bools?! "clipping" vs "isclipping" is not clear.
int tailPeakCount;
float peakPeakHold;
wxBitmap bitmap;
};
class MeterUpdateMsg
@ -162,10 +166,14 @@ class Meter : public wxPanel
double ToLinearIfDB(double value);
bool IsClipping();
void StartMonitoring();
private:
//
// Event handlers
//
void OnErase(wxEraseEvent &evt);
void OnPaint(wxPaintEvent &evt);
void OnSize(wxSizeEvent &evt);
@ -174,7 +182,7 @@ class Meter : public wxPanel
void OnMeterUpdate(wxTimerEvent &evt);
void HandlePaint(wxDC &dc);
void HandleLayout();
void HandleLayout(wxDC &dc);
//
// Pop-up menu handlers
@ -195,11 +203,7 @@ class Meter : public wxPanel
#endif
void OnFloat(wxCommandEvent &evt);
void OnPreferences(wxCommandEvent &evt);
bool IsClipping();
void StartMonitoring();
private:
void DrawMeterBar(wxDC &dc, MeterBar *meterBar);
void ResetBar(MeterBar *bar, bool resetClipping);
void RepaintBarsNow();
@ -257,6 +261,8 @@ class Meter : public wxPanel
wxBrush mDisabledBkgndBrush;
wxRect mAllBarsRect;
Ruler mRuler;
wxString mLeftText;
wxString mRightText;
DECLARE_EVENT_TABLE()
};