⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 meter.cpp

📁 Audacity是一款用於錄音和編輯聲音的、免費的開放源碼軟體。它可以執行於Mac OS X、Microsoft Windows、GNU/Linux和其它作業系統
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/**********************************************************************  Audacity: A Digital Audio Editor  Meter.cpp  Dominic Mazzoni  VU Meter, for displaying recording/playback level  This is a bunch of common code that can display many different  forms of VU meters and other displays.  2004.06.25 refresh rate limited to 30mS, by ChackoN**********************************************************************/#include "../Audacity.h"#include <wx/defs.h>#include <wx/dcmemory.h>#include <wx/image.h>#include <wx/intl.h>#include <wx/menu.h>#include <wx/settings.h>#include <wx/textdlg.h>#include <wx/numdlg.h>#include <wx/tooltip.h>#include <wx/msgdlg.h>#include <math.h>#include "Meter.h"#include "../AudioIO.h"#include "../AColor.h"#include "../ImageManipulation.h"#include "../../images/MixerImages.h"#include "../Project.h"#include "../MeterToolBar.h"#include "../Prefs.h"//// The Meter passes itself messages via this queue so that it can// communicate between the audio thread and the GUI thread.// This class is as simple as possible in order to be thread-safe// without needing mutexes.//MeterUpdateQueue::MeterUpdateQueue(int maxLen):   mBufferSize(maxLen){   mBuffer = new MeterUpdateMsg[mBufferSize];   Clear();}// destructorMeterUpdateQueue::~MeterUpdateQueue(){   delete[] mBuffer;}void MeterUpdateQueue::Clear(){   mStart = 0;   mEnd = 0;}// Add a message to the end of the queue.  Return false if the// queue was full.bool MeterUpdateQueue::Put(MeterUpdateMsg &msg){   int len = (mEnd + mBufferSize - mStart) % mBufferSize;   // Never completely fill the queue, because then the   // state is ambiguous (mStart==mEnd)   if (len >= mBufferSize-1)      return false;   mBuffer[mEnd] = msg;   mEnd = (mEnd+1)%mBufferSize;   return true;}// Get the next message from the start of the queue.// Return false if the queue was empty.bool MeterUpdateQueue::Get(MeterUpdateMsg &msg){   int len = (mEnd + mBufferSize - mStart) % mBufferSize;   if (len == 0)      return false;   msg = mBuffer[mStart];   mStart = (mStart+1)%mBufferSize;   return true;}//// Meter class//enum {   OnMeterUpdateID = 6000,   OnDisableMeterID,   OnMonitorID,   OnHorizontalID,   OnVerticalID,   OnMultiID,   OnEqualizerID,   OnWaveformID,   OnLinearID,   OnDBID,   OnClipID,   OnFloatID,   OnPreferencesID};BEGIN_EVENT_TABLE(Meter, wxPanel)   EVT_TIMER(OnMeterUpdateID, Meter::OnMeterUpdate)   EVT_MOUSE_EVENTS(Meter::OnMouse)   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)   EVT_MENU(OnFloatID, Meter::OnFloat)   EVT_MENU(OnPreferencesID, Meter::OnPreferences)END_EVENT_TABLE()IMPLEMENT_CLASS(Meter, wxPanel)Meter::Meter(wxWindow* parent, wxWindowID id,             bool isInput,             const wxPoint& pos /*= wxDefaultPosition*/,             const wxSize& size /*= wxDefaultSize*/):   wxPanel(parent, id, pos, size),   mQueue(1024),   mWidth(size.x), mHeight(size.y),   mIsInput(isInput),   mStyle(HorizontalStereo),   mDB(true),   mDBRange(60),   mDecay(true),   mDecayRate(60),   mClip(true),   mNumPeakSamplesToClip(3),   mPeakHoldDuration(3),   mT(0),   mRate(0),   mNumBars(0),   mLayoutValid(false),   mBitmap(NULL),   mBackgroundBitmap(NULL),   mIcon(NULL){   int i;    wxColour backgroundColour =      wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE);    mBkgndBrush = wxBrush(backgroundColour, wxSOLID);   mMeterRefreshRate = gPrefs->Read(wxT("/Meter/MeterRefreshRate"), 30);   if (mIsInput) {      mMeterDisabled = gPrefs->Read(wxT("/Meter/MeterInputDisabled"), (long)0);   }   else {      mMeterDisabled = gPrefs->Read(wxT("/Meter/MeterOutputDisabled"), (long)0);   }//   CreateIcon(2);   wxColour origColour(204, 204, 204);   mPeakPeakPen = wxPen(wxColour(102, 102, 255), 1, wxSOLID);   mDisabledPen = wxPen(wxColour(192, 192, 192), 1, wxSOLID);   /* i18n-hint: One-letter abbreviation for Left, in VU Meter */   mLeftText = _("L");   /* i18n-hint: One-letter abbreviation for Right, in VU Meter */   mRightText = _("R");   mLeftSize = wxSize(0, 0);   mRightSize = wxSize(0, 0);   if (mIsInput) {      mPen = wxPen(wxColour(204, 70, 70), 1, wxSOLID);      mBrush = wxBrush(wxColour(204, 70, 70), wxSOLID);      mRMSBrush = wxBrush(wxColour(255, 102, 102), wxSOLID);      mClipBrush = wxBrush(wxColour(255, 53, 53), wxSOLID);      mLightPen = wxPen(wxColour(255, 153, 153), 1, wxSOLID);      mDarkPen = wxPen(wxColour(153, 61, 61), 1, wxSOLID);   }   else {      mPen = wxPen(wxColour(70, 204, 70), 1, wxSOLID);      mBrush = wxBrush(wxColour(70, 204, 70), wxSOLID);      mRMSBrush = wxBrush(wxColour(102, 255, 102), wxSOLID);      mClipBrush = wxBrush(wxColour(255, 53, 53), wxSOLID);      mLightPen = wxPen(wxColour(153, 255, 153), 1, wxSOLID);      mDarkPen = wxPen(wxColour(61, 164, 61), 1, wxSOLID);   }      mDisabledBkgndBrush = wxBrush(wxColour(160, 160, 160), wxSOLID);//   mDisabledBkgndBrush = wxBrush(//            wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DSHADOW), wxSOLID);//            wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT), wxSOLID);   if (mMeterDisabled) {      mSavedBkgndBrush = mBkgndBrush;      mSavedBrush = mBrush;      mSavedRMSBrush = mRMSBrush;      mBkgndBrush = mDisabledBkgndBrush;      mBrush = mDisabledBkgndBrush;      mRMSBrush = mDisabledBkgndBrush;   }   CreateIcon(2);   mRuler.SetFonts(GetFont(), GetFont());   mTimer.SetOwner(this, OnMeterUpdateID);   Reset(44100.0, true);   for(i=0; i<kMaxMeterBars; i++)      mBar[i].clipping = false;}void Meter::CreateIcon(int aquaOffset){   wxColour backgroundColour =  mBkgndBrush.GetColour();//      wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);//   mBkgndBrush = wxBrush(backgroundColour, wxSOLID);   wxImage *image, *alpha;   if (mIcon) {      delete mIcon;      mIcon = NULL;   }   if (mIsInput) {      image = new wxImage(wxBitmap(Mic).ConvertToImage());      alpha = new wxImage(wxBitmap(MicAlpha).ConvertToImage());   }   else {      image = new wxImage(wxBitmap(Speaker).ConvertToImage());      alpha = new wxImage(wxBitmap(SpeakerAlpha).ConvertToImage());   }   wxImage *bkgnd = CreateSysBackground(25, 25, aquaOffset,					backgroundColour);   wxImage *final = OverlayImage(bkgnd, image, alpha, 0, 0);   mIcon = new wxBitmap(final);   delete image;   delete alpha;   delete bkgnd;   delete final;}Meter::~Meter(){   delete mIcon;   if (mBitmap)      delete mBitmap;   if (mBackgroundBitmap)     delete mBackgroundBitmap;}void Meter::OnPaint(wxPaintEvent &evt){   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 &evt){   delete mBitmap;   mBitmap = NULL;   GetClientSize(&mWidth, &mHeight);//::wxMessageBox(wxString::Format(" mHeight=%d, mWidth=%d", mHeight,mWidth));   mLayoutValid = false;}void Meter::OnMouse(wxMouseEvent &evt){  #if wxUSE_TOOLTIPS // Not available in wxX11   if (evt.Leaving()){      GetActiveProject()->TP_DisplayStatusMessage(wxT(""));   }   else if (evt.Entering()) {      // Display the tooltip in the status bar      wxToolTip * pTip = this->GetToolTip();      if( pTip ) {         wxString tipText = pTip->GetTip();         GetActiveProject()->TP_DisplayStatusMessage(tipText);      }   }  #endif   if (evt.RightDown() ||       (evt.ButtonDown() && mMenuRect.Inside(evt.m_x, evt.m_y))) {      wxMenu *menu = new wxMenu();      // Note: these should be kept in the same order as the enum      if (mMeterDisabled)         menu->Append(OnDisableMeterID, _("Enable Meter"));      else         menu->Append(OnDisableMeterID, _("Disable Meter"));      if (mIsInput) {         if (gAudioIO->IsMonitoring())            menu->Append(OnMonitorID, _("Stop Monitoring"));         else            menu->Append(OnMonitorID, _("Start Monitoring"));      }      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);      delete menu;   }          else if (evt.ButtonDown()) {      if (mIsInput)         StartMonitoring();   }}void Meter::SetStyle(Meter::Style newStyle){   mStyle = newStyle;   mLayoutValid = false;   Refresh(true);}void Meter::Reset(double sampleRate, bool resetClipping){   int j;   mT = 0;   mRate = sampleRate;   for(j=0; j<mNumBars; j++)      ResetBar(&mBar[j], resetClipping);   // wxTimers seem to be a little unreliable - sometimes they stop for   // no good reason, so this "primes" it every now and then...   mTimer.Stop();   // While it's stopped, empty the queue   MeterUpdateMsg msg;   while(mQueue.Get(msg)) {   }   mTimer.Start(25); // every 25 ms -> ~40 updates per second   mLayoutValid = false;   Refresh(false);}static float floatMax(float a, float b){   return a>b? a: b;}static int intmin(int a, int b){   return a<b? a: b;}static int intmax(int a, int b){   return a>b? a: b;}static float ClipZeroToOne(float z){   if (z > 1.0)      return 1.0;   else if (z < 0.0)      return 0.0;   else      return z;}static float ToDB(float v, float range){   double db;   if (v > 0)      db = 20 * log10(fabs(v));   else      db = -999;   return ClipZeroToOne((db + range) / range);}void Meter::UpdateDisplay(int numChannels, int numFrames, float *sampleData){   int i, j;   float *sptr = sampleData;   int num = intmin(numChannels, mNumBars);   MeterUpdateMsg msg;   msg.numFrames = numFrames;   for(j=0; j<mNumBars; j++) {      msg.peak[j] = 0;      msg.rms[j] = 0;      msg.clipping[j] = false;      msg.headPeakCount[j] = 0;      msg.tailPeakCount[j] = 0;   }   for(i=0; i<numFrames; i++) {      for(j=0; j<num; j++) {         msg.peak[j] = floatMax(msg.peak[j], sptr[j]);         msg.rms[j] += sptr[j]*sptr[j];         // In addition to looking for mNumPeakSamplesToClip peaked         // samples in a row, also send the number of peaked samples         // at the head and tail, in case there's a run of          // Send the number of peaked samples at the head and tail,         // in case there's a run of peaked samples that crosses         // block boundaries         if (fabs(sptr[j])>=1.0) {            if (msg.headPeakCount[j]==i)               msg.headPeakCount[j]++;            msg.tailPeakCount[j]++;            if (msg.tailPeakCount[j] > mNumPeakSamplesToClip)               msg.clipping[j] = true;         }         else            msg.tailPeakCount[j] = 0;      }      sptr += numChannels;   }   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);}void Meter::OnMeterUpdate(wxTimerEvent &evt){   MeterUpdateMsg msg;   int numChanges = 0;     // There may have been several update messages since the last   // time we got to this function.  Catch up to real-time by   // popping them off until there are none left.  It is necessary   // to process all of them, otherwise we won't handle peaks and   // peak-hold bars correctly.   while(mQueue.Get(msg)) {      numChanges++;      double deltaT = msg.numFrames / mRate;      int j;            if (mMeterDisabled)         return;            mT += deltaT;      for(j=0; j<mNumBars; j++) {         if (mDecay) {            if (mDB) {               float decayAmount = mDecayRate * deltaT / mDBRange;               mBar[j].peak = floatMax(msg.peak[j],                                       mBar[j].peak - decayAmount);            }            else {               double decayAmount = mDecayRate * deltaT;               double decayFactor = pow(10.0, -decayAmount/20);               mBar[j].peak = floatMax(msg.peak[j],                                       mBar[j].peak * decayFactor);            }         }         else            mBar[j].peak = msg.peak[j];         // This smooths out the RMS signal         mBar[j].rms = mBar[j].rms * 0.9 + msg.rms[j] * 0.1;                  if (mT - mBar[j].peakHoldTime > mPeakHoldDuration ||             mBar[j].peak > mBar[j].peakHold) {            mBar[j].peakHold = mBar[j].peak;            mBar[j].peakHoldTime = mT;         }                  if (mBar[j].peak > mBar[j].peakPeakHold )            mBar[j].peakPeakHold = mBar[j].peak;                  if (msg.clipping[j] ||

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -