Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/waveform/waveformrenderer.cpp

Go to the documentation of this file.
00001 #include <QDebug>
00002 #include <QDomNode>
00003 #include <QImage>
00004 #include <QListIterator>
00005 #include <QObject>
00006 
00007 #include <time.h>
00008 
00009 #include "mathstuff.h"
00010 #include "waveformrenderer.h"
00011 #include "waveformrenderbackground.h"
00012 #include "waveformrenderbeat.h"
00013 #include "waveformrendermark.h"
00014 #include "waveformrendermarkrange.h"
00015 #include "waveformrendersignal.h"
00016 #include "waveformrendersignalpixmap.h"
00017 #include "trackinfoobject.h"
00018 #include "soundsourceproxy.h"
00019 #include "controlobjectthreadmain.h"
00020 #include "controlobject.h"
00021 #include "widget/wwidget.h"
00022 #include "widget/wskincolor.h"
00023 
00024 #define INTERPOLATION 0
00025 
00026 #define DEFAULT_SUBPIXELS_PER_PIXEL 4
00027 #define DEFAULT_PIXELS_PER_SECOND 100
00028 
00029 #define RATE_INCREMENT 0.015
00030 
00031 void WaveformRenderer::run() {
00032     double msecs_old = 0, msecs_elapsed = 0;
00033 
00034     while(!m_bQuit) {
00035 
00036         if(m_iLatency != 0 && m_dPlayPos != -1 && m_dPlayPosOld != -1 && m_iNumSamples != 0) {
00037             QTime now = QTime::currentTime();
00038             double msecs_elapsed = m_playPosTime.msecsTo(now);
00039             double timeratio = double(msecs_elapsed) / m_iLatency;
00040             double adjust = (m_dPlayPos - m_dPlayPosOld) * math_min(1.0f, timeratio);
00041             m_dPlayPosAdjust = adjust;
00042         }
00043 
00044         QThread::msleep(6);
00045     }
00046 }
00047 
00048 WaveformRenderer::WaveformRenderer(const char* group) :
00049     QThread(),
00050     m_pGroup(group),
00051     m_iWidth(0),
00052     m_iHeight(0),
00053     bgColor(0,0,0),
00054     signalColor(255,255,255),
00055     colorMarker(255,255,255),
00056     colorBeat(255,255,255),
00057     colorCue(255,255,255),
00058     m_iNumSamples(0),
00059     m_iPlayPosTime(-1),
00060     m_iPlayPosTimeOld(-1),
00061     m_dPlayPos(0),
00062     m_dPlayPosOld(-1),
00063     m_dRate(0),
00064     m_dRateRange(0),
00065     m_dRateDir(0),
00066     m_iRateAdjusting(0),
00067     m_iDupes(0),
00068     m_dPlayPosAdjust(0),
00069     m_iLatency(0),
00070     m_pSampleBuffer(NULL),
00071     m_pPixmap(NULL),
00072     m_pImage(),
00073     m_iSubpixelsPerPixel(DEFAULT_SUBPIXELS_PER_PIXEL),
00074     m_iPixelsPerSecond(DEFAULT_PIXELS_PER_SECOND),
00075     m_pTrack(NULL),
00076     m_bQuit(false)
00077 {
00078     m_pPlayPos = new ControlObjectThreadMain(
00079         ControlObject::getControl(ConfigKey(group,"visual_playposition")));
00080     if(m_pPlayPos != NULL)
00081         connect(m_pPlayPos, SIGNAL(valueChanged(double)),
00082                 this, SLOT(slotUpdatePlayPos(double)));
00083 
00084 
00085     m_pLatency = new ControlObjectThreadMain(
00086         ControlObject::getControl(ConfigKey("[Master]","latency")));
00087     if(m_pLatency != NULL)
00088         connect(m_pLatency, SIGNAL(valueChanged(double)),
00089                 this, SLOT(slotUpdateLatency(double)));
00090 
00091     m_pRenderBackground = new WaveformRenderBackground(group, this);
00092     m_pRenderSignal = new WaveformRenderSignal(group, this);
00093     m_pRenderSignalPixmap = new WaveformRenderSignalPixmap(group, this);
00094     m_pRenderBeat = new WaveformRenderBeat(group, this);
00095 
00096     m_pCOVisualResample = new ControlObject(ConfigKey(group, "VisualResample"));
00097 
00098     m_pRate = new ControlObjectThreadMain(
00099         ControlObject::getControl(ConfigKey(group, "rate")));
00100     if(m_pRate != NULL) {
00101         connect(m_pRate, SIGNAL(valueChanged(double)),
00102                 this, SLOT(slotUpdateRate(double)));
00103     }
00104 
00105     m_pRateRange = new ControlObjectThreadMain(
00106         ControlObject::getControl(ConfigKey(group, "rateRange")));
00107     if(m_pRateRange != NULL) {
00108         connect(m_pRateRange, SIGNAL(valueChanged(double)),
00109                 this, SLOT(slotUpdateRateRange(double)));
00110     }
00111 
00112     m_pRateDir = new ControlObjectThreadMain(
00113         ControlObject::getControl(ConfigKey(group, "rate_dir")));
00114     if (m_pRateDir) {
00115         connect(m_pRateDir, SIGNAL(valueChanged(double)),
00116                 this, SLOT(slotUpdateRateDir(double)));
00117     }
00118 
00119     if(0)
00120         start();
00121 }
00122 
00123 
00124 WaveformRenderer::~WaveformRenderer() {
00125     qDebug() << this << "~WaveformRenderer()";
00126 
00127     // Wait for the thread to quit
00128     m_bQuit = true;
00129     QThread::wait();
00130 
00131     if(m_pRenderBackground)
00132         delete m_pRenderBackground;
00133     m_pRenderBackground = NULL;
00134 
00135     if(m_pRenderSignalPixmap)
00136         delete m_pRenderSignalPixmap;
00137     m_pRenderSignalPixmap = NULL;
00138 
00139     if(m_pRenderSignal)
00140         delete m_pRenderSignal;
00141     m_pRenderSignal = NULL;
00142 
00143     if(m_pRenderBeat)
00144         delete m_pRenderBeat;
00145     m_pRenderBeat = NULL;
00146 
00147     QMutableListIterator<RenderObject*> iter(m_renderObjects);
00148     while (iter.hasNext()) {
00149         RenderObject* ro = iter.next();
00150         iter.remove();
00151         delete ro;
00152     }
00153 
00154     if(m_pCOVisualResample)
00155         delete m_pCOVisualResample;
00156     m_pCOVisualResample = NULL;
00157 
00158     if (m_pPlayPos)
00159         delete m_pPlayPos;
00160     m_pPlayPos = NULL;
00161 
00162     if (m_pLatency)
00163         delete m_pLatency;
00164     m_pLatency = NULL;;
00165 
00166     if(m_pRate)
00167         delete m_pRate;
00168     m_pRate = NULL;
00169 
00170     if(m_pRateRange)
00171         delete m_pRateRange;
00172     m_pRateRange = NULL;
00173 
00174     if(m_pRateDir)
00175         delete m_pRateDir;
00176     m_pRateDir = NULL;
00177 
00178     if(m_pPlayPos)
00179         delete m_pPlayPos;
00180     m_pPlayPos = NULL;
00181 }
00182 
00183 void WaveformRenderer::slotUpdatePlayPos(double v) {
00184     m_iPlayPosTimeOld = m_iPlayPosTime;
00185     //m_playPosTimeOld = m_playPosTime;
00186     m_dPlayPosOld = m_dPlayPos;
00187     m_dPlayPos = v;
00188     m_iPlayPosTime = clock();
00189     //m_playPosTime = QTime::currentTime();
00190 
00191     m_iDupes = 0;
00192     m_dPlayPosAdjust = 0;
00193 }
00194 
00195 void WaveformRenderer::slotUpdateRate(double v) {
00196     m_dTargetRate = v;
00197 }
00198 
00199 void WaveformRenderer::slotUpdateRateRange(double v) {
00200     m_dRateRange = v;
00201 }
00202 
00203 void WaveformRenderer::slotUpdateRateDir(double v) {
00204     m_dRateDir = v;
00205 }
00206 
00207 void WaveformRenderer::slotUpdateLatency(double v) {
00208     m_iLatency = v;
00209 }
00210 
00211 void WaveformRenderer::resize(int w, int h) {
00212     m_iWidth = w;
00213     m_iHeight = h;
00214 
00215     setupControlObjects();
00216 
00217     // Notify children that we've been resized
00218     m_pRenderBackground->resize(w,h);
00219     m_pRenderSignal->resize(w,h);
00220     m_pRenderSignalPixmap->resize(w,h);
00221     m_pRenderBeat->resize(w,h);
00222 
00223     QListIterator<RenderObject*> iter(m_renderObjects);
00224     while (iter.hasNext()) {
00225         RenderObject* ro = iter.next();
00226         ro->resize(w,h);
00227     }
00228 }
00229 
00230 void WaveformRenderer::setupControlObjects() {
00231 
00232     // the resample rate is the number of samples that correspond to one downsample
00233 
00234     // This set of restrictions provides for a downsampling setup like this:
00235 
00236     // Let a sample be a sample in the original song.
00237     // Let a downsample be a sample in the downsampled buffer
00238     // Let a pixel be a pixel on the screen.
00239 
00240     // W samples -> X downsamples -> Y pixels
00241 
00242     // We start with the restriction that we desire 1 second of
00243     // 'raw' information to be contained within Z real pixels of screen space.
00244 
00245     // 1) 1 second / z pixels = f samples / z pixels  = (f/z) samples per pixel
00246 
00247     // The size of the buffer we interact with is the number of downsamples
00248 
00249     // The ratio of samples to downsamples is N : 1
00250     // The ratio of downsamples to pixels is M : 1
00251 
00252     // Therefore the ratio of samples to pixels is MN : 1
00253 
00254     // Or in other words, we have MN samples per pixel
00255 
00256     // 2) MN samples / pixel
00257 
00258     // We combine 1 and 2 into one constraint:
00259 
00260     // (f/z) = mn, or  f = m * n * z
00261 
00262     // REQUIRE : M * N * Z = F
00263     // M : DOWNSAMPLES PER PIXEL
00264     // N : SAMPLES PER DOWNSAMPLE
00265     // F : SAMPLE RATE OF SONG
00266     // Z : THE USER SEES 1 SECOND OF DATA IN Z PIXELS
00267 
00268     // Solving for N, the number of samples in our downsample buffer,
00269     // we get : N = F / (M*Z)
00270 
00271     // We don't know F, so we're going to transmit M*Z
00272 
00273     double m = m_iSubpixelsPerPixel; // M DOWNSAMPLES PER PIXEL
00274     double z = m_iPixelsPerSecond; // Z PIXELS REPRESENTS 1 SECOND OF DATA
00275 
00276     m_pCOVisualResample->set(m*z);
00277 
00278     //qDebug() << "WaveformRenderer::setupControlObjects - VisualResample: " << m*z;
00279 
00280 }
00281 
00282 void WaveformRenderer::setup(QDomNode node) {
00283 
00284     bgColor.setNamedColor(WWidget::selectNodeQString(node, "BgColor"));
00285     bgColor = WSkinColor::getCorrectColor(bgColor);
00286 
00287     signalColor.setNamedColor(WWidget::selectNodeQString(node, "SignalColor"));
00288     signalColor = WSkinColor::getCorrectColor(signalColor);
00289 
00290     colorMarker.setNamedColor(WWidget::selectNodeQString(node, "MarkerColor"));
00291     colorMarker = WSkinColor::getCorrectColor(colorMarker);
00292 
00293     colorBeat.setNamedColor(WWidget::selectNodeQString(node, "BeatColor"));
00294     colorBeat = WSkinColor::getCorrectColor(colorBeat);
00295 
00296     colorCue.setNamedColor(WWidget::selectNodeQString(node, "CueColor"));
00297     colorCue = WSkinColor::getCorrectColor(colorCue);
00298 
00299     while (m_renderObjects.size() > 0) {
00300         RenderObject* ro = m_renderObjects.takeFirst();
00301         delete ro;
00302     }
00303 
00304     // Process any <Mark> nodes
00305     QDomNode child = node.firstChild();
00306     while (!child.isNull()) {
00307         RenderObject* pRenderObject = NULL;
00308         if (child.nodeName() == "Mark") {
00309             pRenderObject = new WaveformRenderMark(m_pGroup, this);
00310         } else if(child.nodeName() == "MarkRange") {
00311             pRenderObject = new WaveformRenderMarkRange(m_pGroup, this);
00312         }
00313         if (pRenderObject != NULL) {
00314             if (m_pTrack != NULL)
00315                 pRenderObject->newTrack(m_pTrack);
00316             pRenderObject->setup(child);
00317             m_renderObjects.push_back(pRenderObject);
00318         }
00319         child = child.nextSibling();
00320     }
00321 
00322     m_pRenderBackground->setup(node);
00323     m_pRenderSignal->setup(node);
00324     m_pRenderSignalPixmap->setup(node);
00325     m_pRenderBeat->setup(node);
00326 }
00327 
00328 
00329 void WaveformRenderer::precomputePixmap() {
00330     if(m_pSampleBuffer == NULL || m_iNumSamples == 0 || !m_pImage.isNull())
00331         return;
00332 
00333     qDebug() << "Generating a image!";
00334 
00335     int monoSamples = (m_iNumSamples >> 3);
00336     qDebug() << monoSamples << " samples for qimage";
00337     QImage qi(monoSamples, m_iHeight, QImage::Format_RGB32);
00338 
00339     QPainter paint;
00340     paint.begin(&qi);
00341 
00342     paint.fillRect(qi.rect(), QBrush(QColor(255,0,0)));//bgColor));//QColor(0,0,0)));
00343     paint.setPen(QColor(0,255,0));//signalColor);//QColor(0,255,0));
00344     qDebug() << "height " << m_iHeight;
00345     paint.translate(0,m_iHeight/2);
00346     paint.scale(1.0,-1.0);
00347     paint.drawLine(QLine(0,0,monoSamples,0));
00348     //for (int i=0;i<100;i++) {
00349         //paint.drawLine(QLine(i,0,i,m_iHeight/2));
00350         //paint.drawLine(QLine(i,0,i,m_iHeight/2));
00351     //}
00352 
00353     for(int i=0;i<monoSamples;i++) {
00354         //SAMPLE sampl = (*m_pSampleBuffer)[i*2];
00355         //SAMPLE sampr = (*m_pSampleBuffer)[i*2+1];
00356 
00357         //paint.drawLine(QLine(i,-5, i, 0));
00358         paint.drawLine(QLine(i,2, i, 80));
00359         //paint.drawLine(QLine(i,-sampr,i,sampl));
00360     }
00361     paint.end();
00362     qDebug() << "done with image";
00363     qi.save("/home/rryan/foo.bmp", "BMP", 100);
00364     m_pImage = qi;
00365 
00366 
00367     return;
00368 
00369     /*
00370     qDebug() << "Generating a pixmap!";
00371 
00372     // Now generate a pixmap of this
00373     QPixmap *pm = new QPixmap(m_iNumSamples/2, m_iHeight);
00374 
00375     if(pm->isNull()) {
00376         qDebug() << "Built a null pixmap, WTF!";
00377     } else {
00378         qDebug() << " Build a pixmap " << pm->size();
00379     }
00380 
00381     QPainter paint;
00382     paint.begin(pm);
00383 
00384     qDebug() << "Wave Precomp: BG: " << bgColor << " FG:" << signalColor;
00385     paint.fillRect(pm->rect(), QBrush(bgColor));//QColor(0,0,0)));
00386     paint.setPen(signalColor);//QColor(0,255,0));
00387 
00388     paint.translate(0,m_iHeight/2);
00389     paint.scale(1.0,-1.0);
00390     //paint.drawLine(QLine(0,0,resultSamples/2,0));
00391 
00392     for(int i=0;i<m_iNumSamples/2;i++) {
00393         SAMPLE sampl = (*m_pSampleBuffer)[i*2];
00394         SAMPLE sampr = (*m_pSampleBuffer)[i*2+1];
00395 
00396         //paint.drawLine(QLine(i,-15, i, 15));
00397         paint.drawLine(QLine(i,-sampr,i,sampl));
00398     }
00399     paint.end();
00400 
00401     */
00402 }
00403 
00404 bool WaveformRenderer::fetchWaveformFromTrack() {
00405 
00406     if(!m_pTrack)
00407         return false;
00408 
00409     QVector<float> *buffer = m_pTrack->getVisualWaveform();
00410 
00411     if(buffer == NULL)
00412         return false;
00413 
00414     m_pSampleBuffer = buffer;
00415     m_iNumSamples = buffer->size();
00416 
00417     return true;
00418 }
00419 
00420 void WaveformRenderer::drawSignalPixmap(QPainter *pPainter) {
00421 
00422 
00423     //if(m_pImage == NULL)
00424     //return;
00425     if(m_pImage.isNull())
00426         return;
00427 
00428     //double dCurPos = m_pPlayPos->get();
00429     int iCurPos = (int)(m_dPlayPos*m_pImage.width());
00430 
00431     int halfw = m_iWidth/2;
00432     int halfh = m_iHeight/2;
00433 
00434     int totalHeight = m_pImage.height();
00435     int totalWidth = m_pImage.width();
00436     int width = m_iWidth;
00437     int height = m_iHeight;
00438     // widths and heights of the two rects should be the same:
00439     // m_iWidth - 0 = iCurPos + halfw - iCurPos + halfw = m_iWidth (if even)
00440     // -halfh-halfh = -halfh-halfh
00441 
00442     int sx=iCurPos-halfw;
00443     int sy=0;
00444     int tx=0;
00445     int ty=0;
00446 
00447     if(sx < 0) {
00448         sx = 0;
00449         width = iCurPos + halfw;
00450         tx = m_iWidth - width;
00451     } else if(sx + width >= totalWidth) {
00452         //width = (iCurPos - sx) + (totalWidth-iCurPos);
00453         width = halfw + totalWidth - iCurPos;
00454     }
00455 
00456     QRect target(tx,ty,width,height);
00457     QRect source(sx,sy,width,height);
00458 
00459     //qDebug() << "target:" << target;
00460     //qDebug() << "source:" << source;
00461     pPainter->setPen(signalColor);
00462 
00463     pPainter->drawImage(target, m_pImage, source);
00464 
00465 }
00466 
00467 void WaveformRenderer::draw(QPainter* pPainter, QPaintEvent *pEvent) {
00468     double playposadjust = 0;
00469 
00470     if(m_iWidth == 0 || m_iHeight == 0)
00471         return;
00472 
00473 
00474     /*
00475     if(m_dPlayPos != -1 && m_dPlayPosOld != -1 && m_iNumSamples != 0) {
00476         static double elatency = ControlObject::getControl(ConfigKey("[Master]","latency"))->get();
00477         double latency = elatency;
00478         latency *= 4;
00479         latency *= CLOCKS_PER_SEC / 1000.0;
00480 
00481         //int latency = m_iPlayPosTime - m_iPlayPosTimeOld;
00482         double timeelapsed = (clock() - m_iPlayPosTime);
00483         double timeratio = 0;
00484         if(latency != 0)
00485             timeratio = double(timeelapsed) / double(latency);
00486         if(timeratio > 1.0)
00487             timeratio = 1.0;
00488 
00489         double timerun = m_iPlayPosTime - m_iPlayPosTimeOld;
00490 
00491 
00492         playposadjust = ((m_dPlayPos*m_iNumSamples) - (m_dPlayPosOld*m_iNumSamples)) * timeelapsed;
00493         playposadjust /= (latency*m_iNumSamples);
00494 
00495         //qDebug() << m_dPlayPos - m_dPlayPosOld << " " << timerun;
00496 
00497         //qDebug() << "ppold " << m_dPlayPosOld << " pp " << m_dPlayPos << " timeratio " << timeratio;
00498         //qDebug() << "timee" << timeelapsed <<  "playpoadj" << playposadjust;
00499     }
00500     */
00501     m_iDupes++;
00502 
00503     double playpos = m_dPlayPos + m_dPlayPosAdjust;
00504 
00505     //qDebug() << m_dPlayPosAdjust;
00506 
00507     // Gradually stretch the waveform
00508     if (fabs(m_dTargetRate - m_dRate) > RATE_INCREMENT)
00509     {
00510         if ((m_dTargetRate - m_dRate) > 0)
00511         {
00512             m_iRateAdjusting = m_iRateAdjusting > 0 ? m_iRateAdjusting + 1 : 1;
00513             m_dRate = math_min(m_dTargetRate, m_dRate + RATE_INCREMENT * pow(
00514                 static_cast<double>(m_iRateAdjusting), 2) / 80);
00515         }
00516         else
00517         {
00518             m_iRateAdjusting = m_iRateAdjusting < 0 ? m_iRateAdjusting - 1 : -1;
00519             m_dRate = math_max(m_dTargetRate, m_dRate - RATE_INCREMENT * pow(
00520                 static_cast<double>(m_iRateAdjusting), 2) / 80);
00521         }
00522     }
00523     else
00524     {
00525         m_iRateAdjusting = 0;
00526         m_dRate = m_dTargetRate;
00527     }
00528 
00529     // Limit our rate adjustment to < 99%, "Bad Things" might happen otherwise.
00530     double rateAdjust = m_dRateDir * math_min(0.99, m_dRate * m_dRateRange);
00531 
00532     if(m_pSampleBuffer == NULL) {
00533         fetchWaveformFromTrack();
00534     }
00535 
00536     m_pRenderBackground->draw(pPainter, pEvent, m_pSampleBuffer, playpos, rateAdjust);
00537 
00538     pPainter->setPen(signalColor);
00539 
00540     //m_pRenderSignalPixmap->draw(pPainter, pEvent, m_pSampleBuffer, playpos, rateAdjust);
00541     // Translate our coordinate frame from (0,0) at top left
00542     // to (0,0) at left, center. All the subrenderers expect this.
00543     pPainter->translate(0.0,m_iHeight/2.0);
00544 
00545     // Now scale so that positive-y points up.
00546     pPainter->scale(1.0,-1.0);
00547 
00548     // Draw the center horizontal line under the signal.
00549     pPainter->drawLine(QLine(0,0,m_iWidth,0));
00550 
00551     m_pRenderSignal->draw(pPainter, pEvent, m_pSampleBuffer, playpos, rateAdjust);
00552 
00553     // Draw various markers.
00554     m_pRenderBeat->draw(pPainter, pEvent, m_pSampleBuffer, playpos, rateAdjust);
00555 
00556     QListIterator<RenderObject*> iter(m_renderObjects);
00557     while (iter.hasNext()) {
00558         RenderObject* ro = iter.next();
00559         ro->draw(pPainter, pEvent, m_pSampleBuffer, playpos, rateAdjust);
00560     }
00561 
00562     pPainter->setPen(colorMarker);
00563 
00564     // Draw the center vertical line
00565     pPainter->drawLine(QLineF(m_iWidth/2.0,m_iHeight/2.0,m_iWidth/2.0,-m_iHeight/2.0));
00566 
00567 }
00568 
00569 void WaveformRenderer::slotUnloadTrack(TrackPointer pTrack) {
00570     // All RenderObject's must support newTrack() calls with NULL
00571     slotNewTrack(TrackPointer());
00572 }
00573 
00574 void WaveformRenderer::slotNewTrack(TrackPointer pTrack) {
00575 
00576     m_pTrack = pTrack;
00577     m_pSampleBuffer = NULL;
00578     m_iNumSamples = 0;
00579     m_dPlayPos = 0;
00580     m_dPlayPosOld = 0;
00581 
00582     m_pRenderBackground->newTrack(pTrack);
00583     m_pRenderSignal->newTrack(pTrack);
00584     m_pRenderSignalPixmap->newTrack(pTrack);
00585     m_pRenderBeat->newTrack(pTrack);
00586 
00587     QListIterator<RenderObject*> iter(m_renderObjects);
00588     while (iter.hasNext()) {
00589         RenderObject* ro = iter.next();
00590         ro->newTrack(pTrack);
00591     }
00592 }
00593 
00594 int WaveformRenderer::getPixelsPerSecond() {
00595     return m_iPixelsPerSecond;
00596 }
00597 
00598 int WaveformRenderer::getSubpixelsPerPixel() {
00599     return m_iSubpixelsPerPixel;
00600 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines