![]() |
Mixxx
|
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 }