// qsynthMeter.cpp // /**************************************************************************** Copyright (C) 2004-2007, rncbc aka Rui Nuno Capela. All rights reserved. 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *****************************************************************************/ #include "qsynthMeter.h" #include #include #include #include #include // Meter level limits (in dB). #define QSYNTH_METER_MAXDB (+3.0f) #define QSYNTH_METER_MINDB (-70.0f) // The decay rates (magic goes here :). // - value decay rate (faster) #define QSYNTH_METER_DECAY_RATE1 (1.0f - 3E-2f) // - peak decay rate (slower) #define QSYNTH_METER_DECAY_RATE2 (1.0f - 3E-6f) // Number of cycles the peak stays on hold before fall-off. #define QSYNTH_METER_PEAK_FALLOFF 16 //---------------------------------------------------------------------------- // qsynthMeterScale -- Meter bridge scale widget. // Constructor. qsynthMeterScale::qsynthMeterScale( qsynthMeter *pMeter ) : QWidget(pMeter) { m_pMeter = pMeter; m_iLastY = 0; QWidget::setMinimumWidth(16); // QWidget::setBackgroundRole(QPalette::Mid); const QFont& font = QWidget::font(); QWidget::setFont(QFont(font.family(), font.pointSize() - 2)); } // Default destructor. qsynthMeterScale::~qsynthMeterScale (void) { } // Draw IEC scale line and label; assumes labels drawed from top to bottom. void qsynthMeterScale::drawLineLabel ( QPainter *p, int y, const QString& sLabel ) { int iCurrY = QWidget::height() - y; int iWidth = QWidget::width(); const QFontMetrics& fm = p->fontMetrics(); int iMidHeight = (fm.height() >> 1); if (fm.width(sLabel) < iWidth - 5) { p->drawLine(0, iCurrY, 2, iCurrY); if (m_pMeter->portCount() > 1) p->drawLine(iWidth - 3, iCurrY, iWidth - 1, iCurrY); } if (iCurrY < iMidHeight || iCurrY > m_iLastY + iMidHeight) { p->drawText(2, iCurrY - iMidHeight, iWidth - 3, fm.height(), Qt::AlignHCenter | Qt::AlignVCenter, sLabel); m_iLastY = iCurrY + 1; } } // Paint event handler. void qsynthMeterScale::paintEvent ( QPaintEvent * ) { QPainter p(this); p.setFont(QFont("Sans Serif", 5)); p.setPen(QWidget::palette().mid().color()); m_iLastY = 0; drawLineLabel(&p, m_pMeter->iec_level(qsynthMeter::Color0dB), "0"); drawLineLabel(&p, m_pMeter->iec_level(qsynthMeter::Color3dB), "3"); drawLineLabel(&p, m_pMeter->iec_level(qsynthMeter::Color6dB), "6"); drawLineLabel(&p, m_pMeter->iec_level(qsynthMeter::Color10dB), "10"); for (float dB = -20.0f; dB > QSYNTH_METER_MINDB; dB -= 10.0f) drawLineLabel(&p, m_pMeter->iec_scale(dB), QString::number(-int(dB))); } // Resize event handler. void qsynthMeterScale::resizeEvent ( QResizeEvent *pResizeEvent ) { QWidget::resizeEvent(pResizeEvent); // QWidget::repaint(true); } //---------------------------------------------------------------------------- // qsynthMeterValue -- Meter bridge value widget. // Constructor. qsynthMeterValue::qsynthMeterValue ( qsynthMeter *pMeter ) : QFrame(pMeter) { m_pMeter = pMeter; m_fValue = 0.0f; m_iValueHold = 0; m_fValueDecay = QSYNTH_METER_DECAY_RATE1; m_iPeak = 0; m_iPeakHold = 0; m_fPeakDecay = QSYNTH_METER_DECAY_RATE2; m_iPeakColor = qsynthMeter::Color6dB; QWidget::setMinimumWidth(12); QFrame::setBackgroundRole(QPalette::NoRole); QFrame::setFrameShape(QFrame::StyledPanel); QFrame::setFrameShadow(QFrame::Sunken); } // Default destructor. qsynthMeterValue::~qsynthMeterValue (void) { } // Frame value one-way accessors. void qsynthMeterValue::setValue ( float fValue ) { m_fValue = fValue; } // Reset peak holder. void qsynthMeterValue::peakReset (void) { m_iPeak = 0; } // Value refreshment. void qsynthMeterValue::refresh (void) { if (m_fValue > 0.001f || m_iPeak > 0) update(); } // Paint event handler. void qsynthMeterValue::paintEvent ( QPaintEvent * ) { QPainter painter(this); int w = QWidget::width(); int h = QWidget::height(); int y; if (isEnabled()) { painter.fillRect(0, 0, w, h, m_pMeter->color(qsynthMeter::ColorBack)); y = m_pMeter->iec_level(qsynthMeter::Color0dB); painter.setPen(m_pMeter->color(qsynthMeter::ColorFore)); painter.drawLine(0, h - y, w, h - y); } else { painter.fillRect(0, 0, w, h, QWidget::palette().dark().color()); } float dB = QSYNTH_METER_MINDB; if (m_fValue > 0.0f) dB = 20.0f * ::log10f(m_fValue); if (dB < QSYNTH_METER_MINDB) dB = QSYNTH_METER_MINDB; else if (dB > QSYNTH_METER_MAXDB) dB = QSYNTH_METER_MAXDB; int y_over = 0; int y_curr = 0; y = m_pMeter->iec_scale(dB); if (m_iValueHold < y) { m_iValueHold = y; m_fValueDecay = QSYNTH_METER_DECAY_RATE1; } else { m_iValueHold = int(float(m_iValueHold * m_fValueDecay)); if (m_iValueHold < y) { m_iValueHold = y; } else { m_fValueDecay *= m_fValueDecay; y = m_iValueHold; } } int iLevel; for (iLevel = qsynthMeter::Color10dB; iLevel > qsynthMeter::ColorOver && y >= y_over; iLevel--) { y_curr = m_pMeter->iec_level(iLevel); if (y < y_curr) { painter.fillRect(0, h - y, w, y - y_over, m_pMeter->color(iLevel)); } else { painter.fillRect(0, h - y_curr, w, y_curr - y_over, m_pMeter->color(iLevel)); } y_over = y_curr; } if (y > y_over) { painter.fillRect(0, h - y, w, y - y_over, m_pMeter->color(qsynthMeter::ColorOver)); } if (m_iPeak < y) { m_iPeak = y; m_iPeakHold = 0; m_fPeakDecay = QSYNTH_METER_DECAY_RATE2; m_iPeakColor = iLevel; } else if (++m_iPeakHold > m_pMeter->peakFalloff()) { m_iPeak = int(float(m_iPeak * m_fPeakDecay)); if (m_iPeak < y) { m_iPeak = y; } else { if (m_iPeak < m_pMeter->iec_level(qsynthMeter::Color10dB)) m_iPeakColor = qsynthMeter::Color6dB; m_fPeakDecay *= m_fPeakDecay; } } painter.setPen(m_pMeter->color(m_iPeakColor)); painter.drawLine(0, h - m_iPeak, w, h - m_iPeak); } // Resize event handler. void qsynthMeterValue::resizeEvent ( QResizeEvent *pResizeEvent ) { m_iPeak = 0; QWidget::resizeEvent(pResizeEvent); // QWidget::repaint(true); } //---------------------------------------------------------------------------- // qsynthMeter -- Meter bridge slot widget. // Constructor. qsynthMeter::qsynthMeter ( QWidget *pParent ) : QWidget(pParent) { m_iPortCount = 2; // FIXME: Default port count. m_iScaleCount = m_iPortCount; m_ppValues = NULL; m_ppScales = NULL; m_fScale = 0.0f; m_iPeakFalloff = QSYNTH_METER_PEAK_FALLOFF; for (int i = 0; i < LevelCount; i++) m_levels[i] = 0; m_colors[ColorOver] = QColor(240, 0, 20); m_colors[Color0dB] = QColor(240,160, 20); m_colors[Color3dB] = QColor(220,220, 20); m_colors[Color6dB] = QColor(160,220, 20); m_colors[Color10dB] = QColor( 40,160, 40); m_colors[ColorBack] = QColor( 20, 40, 20); m_colors[ColorFore] = QColor( 80, 80, 80); m_pHBoxLayout = new QHBoxLayout(); m_pHBoxLayout->setMargin(2); m_pHBoxLayout->setSpacing(2); QWidget::setLayout(m_pHBoxLayout); QWidget::setBackgroundRole(QPalette::NoRole); if (m_iPortCount > 0) { if (m_iPortCount > 1) m_iScaleCount--; m_ppValues = new qsynthMeterValue *[m_iPortCount]; m_ppScales = new qsynthMeterScale *[m_iScaleCount]; for (int iPort = 0; iPort < m_iPortCount; iPort++) { m_ppValues[iPort] = new qsynthMeterValue(this); m_pHBoxLayout->addWidget(m_ppValues[iPort]); if (iPort < m_iScaleCount) { m_ppScales[iPort] = new qsynthMeterScale(this); m_pHBoxLayout->addWidget(m_ppScales[iPort]); } } int iStripCount = 2 * m_iPortCount; if (m_iPortCount > 1) iStripCount--; QWidget::setMinimumSize(12 * iStripCount, 120); QWidget::setMaximumWidth(16 * iStripCount); } else { QWidget::setMinimumSize(2, 120); QWidget::setMaximumWidth(4); } QWidget::setSizePolicy( QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); } // Default destructor. qsynthMeter::~qsynthMeter (void) { for (int iPort = 0; iPort < m_iPortCount; iPort++) { delete m_ppValues[iPort]; if (iPort < m_iScaleCount) delete m_ppScales[iPort]; } delete [] m_ppScales; delete [] m_ppValues; delete m_pHBoxLayout; } // Child widget accessors. int qsynthMeter::iec_scale ( float dB ) const { float fDef = 1.0; if (dB < -70.0) fDef = 0.0; else if (dB < -60.0) fDef = (dB + 70.0) * 0.0025; else if (dB < -50.0) fDef = (dB + 60.0) * 0.005 + 0.025; else if (dB < -40.0) fDef = (dB + 50.0) * 0.0075 + 0.075; else if (dB < -30.0) fDef = (dB + 40.0) * 0.015 + 0.15; else if (dB < -20.0) fDef = (dB + 30.0) * 0.02 + 0.3; else /* if (dB < 0.0) */ fDef = (dB + 20.0) * 0.025 + 0.5; return (int) (fDef * m_fScale); } int qsynthMeter::iec_level ( int iIndex ) const { return m_levels[iIndex]; } int qsynthMeter::portCount (void) const { return m_iPortCount; } // Peak falloff mode setting. void qsynthMeter::setPeakFalloff ( int iPeakFalloff ) { m_iPeakFalloff = iPeakFalloff; } int qsynthMeter::peakFalloff (void) const { return m_iPeakFalloff; } // Reset peak holder. void qsynthMeter::peakReset (void) { for (int iPort = 0; iPort < m_iPortCount; iPort++) m_ppValues[iPort]->peakReset(); } // Slot refreshment. void qsynthMeter::refresh (void) { for (int iPort = 0; iPort < m_iPortCount; iPort++) m_ppValues[iPort]->refresh(); } // Resize event handler. void qsynthMeter::resizeEvent ( QResizeEvent * ) { m_fScale = 0.85f * float(QWidget::height()); m_levels[Color0dB] = iec_scale( 0.0f); m_levels[Color3dB] = iec_scale( -3.0f); m_levels[Color6dB] = iec_scale( -6.0f); m_levels[Color10dB] = iec_scale(-10.0f); } // Meter value proxy. void qsynthMeter::setValue ( int iPort, float fValue ) { m_ppValues[iPort]->setValue(fValue); } // Common resource accessor. const QColor& qsynthMeter::color ( int iIndex ) const { return m_colors[iIndex]; } // end of qsynthMeter.cpp