/* ************************************************************************** vumeter.cpp - description ------------------- begin : Sun Mar 18 2001 copyright : (C) 2001 by M. Ritscher email : unreachable@gmx.net changes: -------- 18.6.2001, adapted to ecamegapedal, (C) 2001 Kai Vehmanen ***************************************************************************/ /* ************************************************************************** * * * 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 #include #include #include #include // #include #include "qevumeter.h" using namespace std; QEVUMeter::QEVUMeter(QWidget *parent, const char *name) : QFrame(parent,name) { setFixedHeight(QE_METER_HEIGHT * 6); setBackgroundColor(Qt::black); setFrameStyle(WinPanel | Raised); setLineWidth(5); setCaption("VU Meter"); // initializing the maximum value the scale can have scaleMax = width()-2*QE_X_OFFS; scaleFactor = 1; dBTable = 0; sbuf_rep.resize(QE_CHANNELS); data_refreshed_rep = false; // start screen refresh timer startTimer(20); // initialize locks access_lock_rep.set(0); edit_lock_rep.set(0); // set resolution 16bit by default setResolution(16); setDelay(10); // create and prepare Buffer Device BufferDev = new QPixmap(scaleMax, QE_METER_HEIGHT*2); prepareBufferDev(); // initialize buffer for(int i = 0; i< qe_ConstEnd; i++) t[i] = 0; } QEVUMeter::~QEVUMeter(void) { if(dBTable) delete dBTable; } void QEVUMeter::timerEvent( QTimerEvent * ) { if (edit_lock_rep.get() == 0) { access_lock_rep.set(1); if (data_refreshed_rep == true) { newData(); data_refreshed_rep = false; } access_lock_rep.set(0); } } void QEVUMeter::dataUpdated(const SAMPLE_BUFFER* sbuf) { edit_lock_rep.set(1); if (access_lock_rep.get() != 0) { // cerr << "warning, lock-rase detected!" << endl; edit_lock_rep.set(0); return; } int channels = sbuf->number_of_channels(); if (sbuf_rep.size() != static_cast(channels)) sbuf_rep.resize(channels); for(int n = 0; n < QE_CHANNELS; n++) { unsigned int len = sbuf->length_in_samples(); if (sbuf_rep[n].size() != len) sbuf_rep[n].resize(len); if (n < channels) { #if ECA_LIBECASOUND_VERSION > 7 SAMPLE_SPECS::sample_t* chptr = sbuf->buffer[n]; #else SAMPLE_SPECS::sample_type* chptr = sbuf->buffer[n]; #endif for(unsigned int m = 0; m < len; m++) { sbuf_rep[n][m] = chptr[m]; } } } data_refreshed_rep = true; edit_lock_rep.set(0); } /** * Draws the meters */ void QEVUMeter::paintEvent(QPaintEvent*){ int x, xmin, y, tf; y= (QE_METER_HEIGHT * 2); xmin = QE_X_OFFS + 10; QPainter qp; if (qp.begin(this)) { drawFrame(&qp); qp.setPen(Qt::gray); QFont tFont("Courier",8); qp.setFont(tFont); tf = AlignHCenter |AlignVCenter; qp.drawText(QE_X_OFFS+2, y, 10, static_cast((QE_METER_HEIGHT * 1.8)), tf, "dB"); x = scaleMax+QE_X_OFFS - 60*scaleFactor; if( x > xmin) qp.drawText( x,y, 16, QE_METER_HEIGHT*2, tf, "-60"); x = scaleMax+QE_X_OFFS - 50*scaleFactor; if( x > xmin) qp.drawText( x,y, 16, QE_METER_HEIGHT*2, tf, "-50"); x = scaleMax+QE_X_OFFS - 40*scaleFactor; if( x > xmin) qp.drawText( x,y, 16, QE_METER_HEIGHT*2, tf, "-40"); x = scaleMax+QE_X_OFFS - 30*scaleFactor; if( x > xmin) qp.drawText( x,y, 16, QE_METER_HEIGHT*2, tf, "-30"); x = scaleMax+QE_X_OFFS - 25*scaleFactor; if( x > xmin) qp.drawText( x,y, 16, QE_METER_HEIGHT*2, tf, "-25"); x = scaleMax+QE_X_OFFS - 20*scaleFactor; if( x > xmin) qp.drawText( x,y, 16, QE_METER_HEIGHT*2, tf, "-20"); x = scaleMax+QE_X_OFFS - 15*scaleFactor; if( x > xmin) qp.drawText( x,y, 16, QE_METER_HEIGHT*2, tf, "-15"); x = scaleMax+QE_X_OFFS - 10*scaleFactor; if( x > xmin) qp.drawText( x,y, 16, QE_METER_HEIGHT*2, tf, "-10"); x = scaleMax+QE_X_OFFS - 7*scaleFactor; if( x > xmin) qp.drawText( x,y, 10, QE_METER_HEIGHT*2, tf, "-7"); x = scaleMax+QE_X_OFFS - 5*scaleFactor; if( x > xmin) qp.drawText( x,y, 10, QE_METER_HEIGHT*2, tf, "-5"); x = scaleMax+QE_X_OFFS - 3*scaleFactor; if( x > xmin) qp.drawText( x,y, 10, QE_METER_HEIGHT*2, tf, "-3"); x = scaleMax-3; qp.drawText( x,y, 10, QE_METER_HEIGHT*2, tf, "0"); qp.end(); } // draw meters if (t[cleft] != 0) bitBlt( this, QE_X_OFFS, QE_L_Y_OFFS, BufferDev, 0,0, t[cleft], QE_METER_HEIGHT); if (t[cright] != 0) bitBlt( this, QE_X_OFFS, QE_R_Y_OFFS, BufferDev, 0,0, t[cright], QE_METER_HEIGHT); // draw peaks if (t[counterl] > 0) { bitBlt (this, t[peakl]+QE_X_OFFS-QE_PEAK_WIDTH, QE_L_Y_OFFS, BufferDev,t[peakl], 0, QE_PEAK_WIDTH, QE_METER_HEIGHT); } if (t[counterr] > 0) { bitBlt (this, t[peakr]+QE_X_OFFS-QE_PEAK_WIDTH, QE_R_Y_OFFS, BufferDev,t[peakr], 0, QE_PEAK_WIDTH, QE_METER_HEIGHT); } } void QEVUMeter::resizeEvent(QResizeEvent* evt) { // change buffer device before scaleMax otherwise // a new data might access a not existent // area in the buffer device BufferDev->resize((evt->size().width()- 2*QE_X_OFFS), QE_METER_HEIGHT*2 ); prepareBufferDev(); scaleMax = evt->size().width() - 2*QE_X_OFFS; if(dBTable[0] != 0) scaleFactor = scaleMax/abs(dBTable[0]); else scaleFactor = 1; i35dB = scaleMax - 35 * scaleFactor; // FIXME: do I have to call QFrame.resizeEvent here???? } /** * Prepares the Buffer Device from which * a bitBlt operation is done to reflect * the current level. */ void QEVUMeter::prepareBufferDev(void) { QPainter qp; BufferDev->fill(Qt::black); qp.begin(BufferDev); qp.fillRect( 0 ,0, BufferDev->width()*6/10 ,QE_METER_HEIGHT, Qt::green); qp.fillRect( BufferDev->width()*6/10, 0, BufferDev->width()*9/10, QE_METER_HEIGHT, Qt::yellow); qp.fillRect( BufferDev->width()*9/10, 0, BufferDev->width(), QE_METER_HEIGHT, Qt::red); qp.end(); } /** * Sets resolution of sampling device e.g. 8bit 16bit */ void QEVUMeter::setResolution(int res) { tablesize_rep = (int) pow(2.0f,res)>>1; setMinimumWidth(tablesize_rep + QE_X_OFFS*3); if(dBTable) delete dBTable; dBTable = new int[tablesize_rep]; // saves us from log((double i)/ (double)(tablesize_rep -1)); double tempSize = tablesize_rep-1; // filling luckup table converting input values to dB // max input value equals 0dB - 0 equals -Inf for(int i=1; i < tablesize_rep; i++) { dBTable[i] = (int)(20.0 * log10( (double)i/ tempSize)); } // a input value of 0 won't show up on our meter dBTable[0] = dBTable[1]-1; // calculate the factor needed for converting // sample value to screen cooardinates scaleFactor = scaleMax/abs(dBTable[0]); // calculate threshold for peaks i35dB = scaleMax - 35 * scaleFactor; } /** * Input for new sampled data. */ void QEVUMeter::newData(const unsigned char *buffer, unsigned int uDataStart, unsigned int uDataEnd, unsigned int uBuffer_Size) { } /** * Input for new sampled data. */ void QEVUMeter::newData(void) { unsigned int uDataStart = 0; unsigned int uDataEnd = sbuf_rep[0].size(); unsigned int uBuffer_Size = uDataEnd; unsigned int uDots = (uDataEnd >= uDataStart ? uDataEnd - uDataStart : uDataEnd + uBuffer_Size - uDataStart) / 2; unsigned int i,k; t[cleft]=0; t[cright]=0; for (i = 0; i < uDots; i++) { k = (uDataStart + i * 2) % uBuffer_Size; // t[cleft] += abs( (int)(buffer[k] -128)) ; // t[cright] += abs((int)(buffer[k+1]-128)); t[cleft] += abs( (int)(sbuf_rep[SAMPLE_SPECS::ch_left][k] * 32768.0f)); t[cright] += abs((int)(sbuf_rep[SAMPLE_SPECS::ch_right][k] * 32768.0f)); } if (uDots == 0) uDots = 1; t[cleft] /= uDots; t[cright] /= uDots; t[qe_aver] = t[cleft]; if (t[cleft] > tablesize_rep) t[cleft] = tablesize_rep; if (t[cright] > tablesize_rep) t[cright] = tablesize_rep; // convert these value to dB t[qe_db] = t[cleft] = dBTable[t[cleft]]; t[cright] = dBTable[t[cright]]; // convert dB valuse to screen coordinates t[cleft] = scaleMax - scaleFactor* abs(t[cleft]); t[cright] = scaleMax - scaleFactor* abs(t[cright]); /*************************************************** |F| --------scaleMax--------- |F| |F|-QE_X_OFFS-#########################-QE_X_OFFS-|F| |F| |F| 0 width() ****************************************************/ assert(t[cleft] >= 0); // drawing left channel if (t[counterl] == 0) { // ereasing peak bitBlt( this, t[peakl]+QE_X_OFFS-QE_PEAK_WIDTH, QE_L_Y_OFFS, BufferDev, 0, QE_METER_HEIGHT, QE_PEAK_WIDTH, QE_METER_HEIGHT); t[peakl] = 0; t[counterl] --; } // increasing vu bar if(t[cleft] > t[tleft] ) { bitBlt( this, t[tleft]+QE_X_OFFS, QE_L_Y_OFFS, BufferDev, t[tleft], 0,t[cleft]-t[tleft], QE_METER_HEIGHT); t[tleft] = t[cleft]; if((t[cleft] >= t[peakl]) && (t[cleft] > i35dB)) { t[peakl] = t[cleft]; t[counterl] = pDelay; } else if(t[counterl] > 0) { t[counterl]--; } } else if(t[cleft] < t[tleft]) { //decreasing vu bar // peak indicator has been ereased if (t[counterl] < 0) { //if previous level exceeded the //peak treshold paint a new one if (t[tleft] > i35dB) { t[peakl] = t[tleft]; t[counterl] = pDelay; } } else{ t[counterl]--; } // if counter equals delay, we // erease the bar area except the // peak if(t[counterl] == pDelay-1) { //"drawing" peak if( (t[tleft]-t[cleft]) > QE_PEAK_WIDTH) { bitBlt( this, t[cleft]+QE_X_OFFS, QE_L_Y_OFFS, BufferDev, 0, QE_METER_HEIGHT, t[tleft]-t[cleft]-QE_PEAK_WIDTH, QE_METER_HEIGHT); } } else { if( (t[peakl]-t[cleft]) > QE_PEAK_WIDTH ) bitBlt( this, t[cleft]+QE_X_OFFS, QE_L_Y_OFFS, BufferDev, 0, QE_METER_HEIGHT, t[tleft]-t[cleft], QE_METER_HEIGHT); } t[tleft] = t[cleft]; } else if( (t[counterl]>0)) { if(t[cleft] >= t[peakl]) t[counterl]=pDelay; else t[counterl]--; } // right channel if (t[counterr] == 0) { // ereasing peak bitBlt( this, t[peakr]+QE_X_OFFS-QE_PEAK_WIDTH, QE_R_Y_OFFS, BufferDev, 0, QE_METER_HEIGHT, QE_PEAK_WIDTH, QE_METER_HEIGHT); t[peakr] = 0; t[counterr] --; } // increasing vu bar if (t[cright] > t[tright]) { bitBlt( this, t[tright]+QE_X_OFFS, QE_R_Y_OFFS, BufferDev, t[tright], 0,t[cright]-t[tright], QE_METER_HEIGHT); t[tright] = t[cright]; if((t[cright] >= t[peakr]) && (t[cright] > i35dB)) { t[peakr] = t[cright]; t[counterr] = pDelay; } else if(t[counterr] > 0) { t[counterr]--; } } else if(t[cright] < t[tright]) { //decreasing vu bar // peak indicator has been ereased if (t[counterr] < 0) { //if previous level exceeded the //peak treshold paint a new one if (t[tright] > i35dB) { t[peakr] = t[tright]; t[counterr] = pDelay; } } else{ t[counterr]--; } // if counter equals delay, we // erease the bar area except the // peak if (t[counterr] == pDelay-1) { //"drawing" peak if ((t[tright]-t[cright]) > QE_PEAK_WIDTH) { bitBlt( this, t[cright]+QE_X_OFFS, QE_R_Y_OFFS, BufferDev, 0, QE_METER_HEIGHT, t[tright]-t[cright]-QE_PEAK_WIDTH, QE_METER_HEIGHT); } } else { if( (t[peakr]-t[cright]) > QE_PEAK_WIDTH) bitBlt(this, t[cright]+QE_X_OFFS, QE_R_Y_OFFS, BufferDev, 0, QE_METER_HEIGHT, t[tright]-t[cright], QE_METER_HEIGHT); } t[tright] = t[cright]; } else if ((t[counterr]>0)) { if(t[cright] >= t[peakr]) t[counterr]=pDelay; else t[counterr]--; } if (t[cright] > t[tright]) { bitBlt( this, t[tright]+QE_X_OFFS, QE_R_Y_OFFS, BufferDev, t[tright], 0, t[cright]-t[tright], QE_METER_HEIGHT); t[tright] = t[cright]; if ((t[cright] > t[peakr]) && (t[cright] > i35dB)) { t[peakr] = t[cright]; t[counterr] = pDelay; } } else if(t[cright] < t[tright]) { // deleting bitBlt( this, t[cright]+QE_X_OFFS, QE_R_Y_OFFS, BufferDev, 0, QE_METER_HEIGHT, t[tright]-t[cright], QE_METER_HEIGHT); t[tright] = t[cright]; //peak if (t[counterr]!=0) { bitBlt( this, t[peakr]+QE_X_OFFS-QE_PEAK_WIDTH, QE_R_Y_OFFS, BufferDev,t[peakr], 0, QE_PEAK_WIDTH, QE_METER_HEIGHT); t[counterr]--; } else { //ereasing peak bitBlt( this, t[peakr]+QE_X_OFFS-QE_PEAK_WIDTH, QE_R_Y_OFFS, BufferDev,0, QE_METER_HEIGHT, QE_PEAK_WIDTH, QE_METER_HEIGHT); t[peakr] = t[cright]; t[counterr] = pDelay; } } /* void bitBlt (QPaintDevice * dst, int dx, int dy, const QPaintDevice * src, int sx, int sy, int sw, int sh, Qt::RasterOp rop, bool ignoreMask) */ } void QEVUMeter::mousePressEvent(QMouseEvent *e) { if (e->button() == RightButton) emit rbPressed(e->globalPos()); } /** * Adjusts delay of peak indicator */ void QEVUMeter::setDelay( int d){ // adjustment (d+1) is needed as // we test for pDelay-1 in newData (d==0)?pDelay = -1:pDelay = d+1; t[counterl] = 0; t[counterr] = 0; }