/*************************************************************************** enginevumeter.cpp - description ------------------- copyright : (C) 2002 by Tue and Ken Haste Andersen email : ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "engine/enginevumeter.h" #include "control/controlproxy.h" #include "control/controlpotmeter.h" #include "util/math.h" #include "util/sample.h" EngineVuMeter::EngineVuMeter(QString group) { // The VUmeter widget is controlled via a controlpotmeter, which means // that it should react on the setValue(int) signal. m_ctrlVuMeter = new ControlPotmeter(ConfigKey(group, "VuMeter"), 0., 1.); // left channel VU meter m_ctrlVuMeterL = new ControlPotmeter(ConfigKey(group, "VuMeterL"), 0., 1.); // right channel VU meter m_ctrlVuMeterR = new ControlPotmeter(ConfigKey(group, "VuMeterR"), 0., 1.); // Used controlpotmeter as the example used it :/ perhaps someone with more // knowledge could use something more suitable... m_ctrlPeakIndicator = new ControlPotmeter(ConfigKey(group, "PeakIndicator"), 0., 1.); m_ctrlPeakIndicatorL = new ControlPotmeter(ConfigKey(group, "PeakIndicatorL"), 0., 1.); m_ctrlPeakIndicatorR = new ControlPotmeter(ConfigKey(group, "PeakIndicatorR"), 0., 1.); m_pSampleRate = new ControlProxy("[Master]", "samplerate", this); // Initialize the calculation: reset(); } EngineVuMeter::~EngineVuMeter() { delete m_ctrlVuMeter; delete m_ctrlVuMeterL; delete m_ctrlVuMeterR; delete m_ctrlPeakIndicator; delete m_ctrlPeakIndicatorL; delete m_ctrlPeakIndicatorR; } void EngineVuMeter::process(CSAMPLE* pIn, const int iBufferSize) { CSAMPLE fVolSumL, fVolSumR; int sampleRate = (int)m_pSampleRate->get(); SampleUtil::CLIP_STATUS clipped = SampleUtil::sumAbsPerChannel(&fVolSumL, &fVolSumR, pIn, iBufferSize); m_fRMSvolumeSumL += fVolSumL; m_fRMSvolumeSumR += fVolSumR; m_iSamplesCalculated += iBufferSize / 2; // Are we ready to update the VU meter?: if (m_iSamplesCalculated > (sampleRate / VU_UPDATE_RATE)) { doSmooth(m_fRMSvolumeL, log10(SHRT_MAX * m_fRMSvolumeSumL / (m_iSamplesCalculated * 1000) + 1)); doSmooth(m_fRMSvolumeR, log10(SHRT_MAX * m_fRMSvolumeSumR / (m_iSamplesCalculated * 1000) + 1)); const double epsilon = .0001; // Since VU meters are a rolling sum of audio, the no-op checks in // ControlObject will not prevent us from causing tons of extra // work. Because of this, we use an epsilon here to be gentle on the GUI // and MIDI controllers. if (fabs(m_fRMSvolumeL - m_ctrlVuMeterL->get()) > epsilon) m_ctrlVuMeterL->set(m_fRMSvolumeL); if (fabs(m_fRMSvolumeR - m_ctrlVuMeterR->get()) > epsilon) m_ctrlVuMeterR->set(m_fRMSvolumeR); double fRMSvolume = (m_fRMSvolumeL + m_fRMSvolumeR) / 2.0; if (fabs(fRMSvolume - m_ctrlVuMeter->get()) > epsilon) m_ctrlVuMeter->set(fRMSvolume); // Reset calculation: m_iSamplesCalculated = 0; m_fRMSvolumeSumL = 0; m_fRMSvolumeSumR = 0; } if (clipped & SampleUtil::CLIPPING_LEFT) { m_ctrlPeakIndicatorL->set(1.); m_peakDurationL = PEAK_DURATION * sampleRate / iBufferSize / 2000; } else if (m_peakDurationL <= 0) { m_ctrlPeakIndicatorL->set(0.); } else { --m_peakDurationL; } if (clipped & SampleUtil::CLIPPING_RIGHT) { m_ctrlPeakIndicatorR->set(1.); m_peakDurationR = PEAK_DURATION * sampleRate / iBufferSize / 2000; } else if (m_peakDurationR <= 0) { m_ctrlPeakIndicatorR->set(0.); } else { --m_peakDurationR; } m_ctrlPeakIndicator->set(m_ctrlPeakIndicatorR->get() || m_ctrlPeakIndicatorL->get()); } void EngineVuMeter::doSmooth(CSAMPLE ¤tVolume, CSAMPLE newVolume) { if (currentVolume > newVolume) currentVolume -= DECAY_SMOOTHING * (currentVolume - newVolume); else currentVolume += ATTACK_SMOOTHING * (newVolume - currentVolume); if (currentVolume < 0) currentVolume=0; if (currentVolume > 1.0) currentVolume=1.0; } void EngineVuMeter::reset() { m_ctrlVuMeter->set(0); m_ctrlVuMeterL->set(0); m_ctrlVuMeterR->set(0); m_ctrlPeakIndicator->set(0); m_ctrlPeakIndicatorL->set(0); m_ctrlPeakIndicatorR->set(0); m_iSamplesCalculated = 0; m_fRMSvolumeL = 0; m_fRMSvolumeSumL = 0; m_fRMSvolumeR = 0; m_fRMSvolumeSumR = 0; m_peakDurationL = 0; m_peakDurationR = 0; }