![]() |
Mixxx
|
00001 // 00002 // C++ Implementation: woverview 00003 // 00004 // Description: 00005 // 00006 // 00007 // Author: Tue Haste Andersen <haste@diku.dk>, (C) 2003 00008 // 00009 // Copyright: See COPYING file that comes with this distribution 00010 // 00011 // 00012 00013 #include <QBrush> 00014 #include <QtDebug> 00015 #include <QMouseEvent> 00016 #include <QPaintEvent> 00017 #include <qpainter.h> 00018 #include <QtDebug> 00019 #include <qpixmap.h> 00020 #include <qapplication.h> 00021 00022 #include "controlobject.h" 00023 #include "controlobjectthreadmain.h" 00024 #include "woverview.h" 00025 #include "wskincolor.h" 00026 #include "trackinfoobject.h" 00027 #include "mathstuff.h" 00028 00029 WOverview::WOverview(const char *pGroup, QWidget * parent) 00030 : WWidget(parent), 00031 m_pGroup(pGroup) { 00032 m_waveformSummary = QByteArray(); 00033 m_liSampleDuration = 0; 00034 m_iPos = 0; 00035 m_bDrag = false; 00036 m_pScreenBuffer = 0; 00037 00038 // Analyse progress 00039 m_iProgress = 0; 00040 m_analysing = false; 00041 00042 setAcceptDrops(true); 00043 00044 m_pLoopStart = ControlObject::getControl( 00045 ConfigKey(m_pGroup, "loop_start_position")); 00046 connect(m_pLoopStart, SIGNAL(valueChanged(double)), 00047 this, SLOT(loopStartChanged(double))); 00048 connect(m_pLoopStart, SIGNAL(valueChangedFromEngine(double)), 00049 this, SLOT(loopStartChanged(double))); 00050 loopStartChanged(m_pLoopStart->get()); 00051 m_pLoopEnd = ControlObject::getControl( 00052 ConfigKey(m_pGroup, "loop_end_position")); 00053 connect(m_pLoopEnd, SIGNAL(valueChanged(double)), 00054 this, SLOT(loopEndChanged(double))); 00055 connect(m_pLoopEnd, SIGNAL(valueChangedFromEngine(double)), 00056 this, SLOT(loopEndChanged(double))); 00057 loopEndChanged(m_pLoopEnd->get()); 00058 m_pLoopEnabled = ControlObject::getControl( 00059 ConfigKey(m_pGroup, "loop_enabled")); 00060 connect(m_pLoopEnabled, SIGNAL(valueChanged(double)), 00061 this, SLOT(loopEnabledChanged(double))); 00062 connect(m_pLoopEnabled, SIGNAL(valueChangedFromEngine(double)), 00063 this, SLOT(loopEnabledChanged(double))); 00064 loopEnabledChanged(m_pLoopEnabled->get()); 00065 00066 QString pattern = "hotcue_%1_position"; 00067 00068 int i = 1; 00069 ConfigKey hotcueKey; 00070 hotcueKey.group = m_pGroup; 00071 hotcueKey.item = pattern.arg(i); 00072 ControlObject* pControl = ControlObject::getControl(hotcueKey); 00073 00074 //qDebug() << "Connecting hotcue controls."; 00075 while (pControl) { 00076 m_hotcueControls.push_back(pControl); 00077 m_hotcues.push_back(pControl->get()); 00078 m_hotcueMap[pControl] = i; 00079 00080 //qDebug() << "Connecting hotcue" << hotcueKey.group << hotcueKey.item; 00081 00082 connect(pControl, SIGNAL(valueChangedFromEngine(double)), 00083 this, SLOT(cueChanged(double))); 00084 connect(pControl, SIGNAL(valueChanged(double)), 00085 this, SLOT(cueChanged(double))); 00086 00087 hotcueKey.item = pattern.arg(++i); 00088 pControl = ControlObject::getControl(hotcueKey); 00089 } 00090 00091 waveformChanged = false; 00092 } 00093 00094 WOverview::~WOverview() 00095 { 00096 if(m_pScreenBuffer != NULL) { 00097 delete m_pScreenBuffer; 00098 m_pScreenBuffer = NULL; 00099 } 00100 } 00101 00102 void WOverview::setup(QDomNode node) 00103 { 00104 // Set constants for line drawing 00105 /* 00106 m_qMarkerPos1.setX(x/2); 00107 m_qMarkerPos1.setY(0); 00108 m_qMarkerPos2.setX(x/2); 00109 m_qMarkerPos2.setY(y); 00110 m_qMousePos.setX(x/2); 00111 m_qMousePos.setY(y/2); 00112 */ 00113 00114 // Background color and pixmap, default background color to transparent 00115 m_qColorBackground = QColor(0, 0, 0, 0); 00116 if (!selectNode(node, "BgColor").isNull()) { 00117 m_qColorBackground.setNamedColor(selectNodeQString(node, "BgColor")); 00118 m_qColorBackground = WSkinColor::getCorrectColor(m_qColorBackground); 00119 } 00120 00121 // Clear the background pixmap, if it exists. 00122 m_backgroundPixmap = QPixmap(); 00123 m_backgroundPixmapPath = WWidget::selectNodeQString(node, "BgPixmap"); 00124 if (m_backgroundPixmapPath != "") { 00125 m_backgroundPixmap = QPixmap(WWidget::getPath(m_backgroundPixmapPath)); 00126 } 00127 00128 QPalette palette; //Qt4 update according to http://doc.trolltech.com/4.4/qwidget-qt3.html#setBackgroundColor (this could probably be cleaner maybe?) 00129 palette.setColor(this->backgroundRole(), m_qColorBackground); 00130 setPalette(palette); 00131 00132 // If we're doing a warm boot, free the pixmap, and flag it to be regenerated. 00133 if(m_pScreenBuffer != NULL) { 00134 00135 // Since setup is called from MixxxView, we know that 00136 // the current thread is the GUI thread. That way we know 00137 // that paintEvent is not going on right now, and m_pScreenBuffer 00138 // is therefore safe to delete. 00139 00140 delete m_pScreenBuffer; 00141 } 00142 00143 m_pScreenBuffer = new QPixmap(size()); 00144 // Flag the pixmap for regeneration. 00145 waveformChanged = true; 00146 00147 m_qColorSignal.setNamedColor(selectNodeQString(node, "SignalColor")); 00148 m_qColorSignal = WSkinColor::getCorrectColor(m_qColorSignal); 00149 m_qColorMarker.setNamedColor(selectNodeQString(node, "MarkerColor")); 00150 m_qColorMarker = WSkinColor::getCorrectColor(m_qColorMarker); 00151 00152 m_qColorProgress = m_qColorSignal; 00153 if (!selectNode(node, "ProgressColor").isNull()) { 00154 m_qColorProgress.setNamedColor(selectNodeQString(node, "ProgressColor")); 00155 m_qColorProgress = WSkinColor::getCorrectColor(m_qColorProgress); 00156 } 00157 00158 m_iProgressAlpha = 80; 00159 if (!selectNode(node, "ProgressAlpha").isNull()) { 00160 m_iProgressAlpha = selectNodeInt(node, "ProgressAlpha"); 00161 } 00162 } 00163 00164 void WOverview::setValue(double fValue) 00165 { 00166 if (!m_bDrag) 00167 { 00168 // Calculate handle position 00169 m_iPos = (int)(((fValue-14.)/100.)*((double)width()-2.)); 00170 update(); 00171 } 00172 } 00173 00174 void WOverview::slotTrackLoaded(TrackPointer pTrack) 00175 { 00176 // Set current track of this overview widget 00177 m_pCurrentTrack = pTrack; 00178 00179 // If the track already has been analysed slotLoadNewWaveform will reset 00180 // this parameter. 00181 m_analysing = true; 00182 m_iProgress = 0; 00183 update(); 00184 00185 if (pTrack) { 00186 TrackInfoObject* pTrackInfo = pTrack.data(); 00187 00188 // Connect wavesummaryUpdated signals to our update slots. 00189 connect(pTrackInfo, SIGNAL(wavesummaryUpdated(TrackInfoObject*)), 00190 this, SLOT(slotLoadNewWaveform(TrackInfoObject*))); 00191 // Now in case the track's wavesummary is already done, load it. 00192 slotLoadNewWaveform(pTrackInfo); 00193 00194 } 00195 } 00196 00197 void WOverview::slotLoadNewWaveform(TrackInfoObject* pTrack) 00198 { 00199 //Update this widget with new waveform summary data from the new track. 00200 setData(pTrack->getWaveSummary(), 00201 pTrack->getDuration()*pTrack->getSampleRate()*pTrack->getChannels()); 00202 00203 // If no data is available, we can expect it to be analysed. 00204 if (!pTrack->getWaveSummary()->isNull() && !pTrack->getWaveSummary()->isEmpty()) 00205 { 00206 m_analysing = false; 00207 m_iProgress = 0; 00208 } 00209 00210 update(); 00211 } 00212 00213 void WOverview::slotUnloadTrack(TrackPointer pTrack) { 00214 // Unset current track of this overview widget 00215 m_pCurrentTrack.clear(); 00216 00217 if (pTrack) { 00218 disconnect(pTrack.data(), SIGNAL(wavesummaryUpdated(TrackInfoObject*)), 00219 this, SLOT(slotLoadNewWaveform(TrackInfoObject*))); 00220 } 00221 QByteArray ba; 00222 setData(&ba, 0); 00223 m_analysing = false; 00224 m_iProgress = 0; 00225 update(); 00226 } 00227 00228 void WOverview::slotTrackProgress(TrackPointer pTrack, int progress) 00229 { 00230 if (pTrack == m_pCurrentTrack) { 00231 if (progress != m_iProgress) { 00232 m_iProgress = progress; 00233 update(); 00234 } 00235 } 00236 } 00237 00238 void WOverview::cueChanged(double v) { 00239 //qDebug() << "WOverview::cueChanged()"; 00240 QObject* pSender = sender(); 00241 if (!pSender) 00242 return; 00243 00244 if (!m_hotcueMap.contains(pSender)) 00245 return; 00246 00247 int hotcue = m_hotcueMap[pSender]; 00248 m_hotcues[hotcue] = v; 00249 //qDebug() << "hotcue" << hotcue << "position" << v; 00250 update(); 00251 } 00252 00253 void WOverview::loopStartChanged(double v) { 00254 m_dLoopStart = v; 00255 update(); 00256 } 00257 00258 void WOverview::loopEndChanged(double v) { 00259 m_dLoopEnd = v; 00260 update(); 00261 } 00262 00263 void WOverview::loopEnabledChanged(double v) { 00264 m_bLoopEnabled = !(v == 0.0f); 00265 update(); 00266 } 00267 00268 void WOverview::setData(const QByteArray* pWaveformSummary, long liSampleDuration) 00269 { 00270 //COPY THE EFFING WAVESUMMARY BYTES so we don't end up with 00271 //a bad pointer in case the TrackInfoObject that pWavefromSummary originates 00272 //from is deallocated. -- Albert Dec 22, 2009 00273 m_waveformSummary = *pWaveformSummary; 00274 m_liSampleDuration = liSampleDuration; 00275 00276 waveformChanged = true; 00277 } 00278 00279 void WOverview::redrawPixmap() { 00280 // Fill with transparent pixels 00281 m_pScreenBuffer->fill(m_qColorBackground); 00282 00283 QPainter paint(m_pScreenBuffer); 00284 if (!m_backgroundPixmap.isNull()) { 00285 paint.drawTiledPixmap(m_pScreenBuffer->rect(), m_backgroundPixmap, QPoint(0,0)); 00286 } else { 00287 paint.fillRect(m_pScreenBuffer->rect(), m_qColorBackground); 00288 } 00289 00290 if (!m_waveformSummary.size()) { 00291 update(); 00292 return; 00293 } 00294 00295 float yscale = (((float)(height()-2)/2.)/128.); //32768.; 00296 float xscale = (float)m_waveformSummary.size()/width(); 00297 00298 float hfcMax = 0.; 00299 for (unsigned int i=2; i<m_waveformSummary.size(); i=i+3) 00300 { 00301 if (m_waveformSummary.at(i)+128>hfcMax) 00302 hfcMax = m_waveformSummary.at(i)+128; 00303 } 00304 00305 // Draw waveform using hfc to determine color 00306 for (int i=0; i<width(); ++i) 00307 { 00308 //const QColor kqLightColor("#2bff00"); 00309 const QColor kqLightColor = m_qColorSignal; 00310 const QColor kqDarkColor("#1ba200"); 00311 00312 float fMin = 0.; 00313 float fMax = 0.; 00314 00315 if ((unsigned int)width()>m_waveformSummary.size()/3) 00316 { 00317 float pos = (float)(m_waveformSummary.size()-3)/3.*(float)i/(float)width(); 00318 int idx = (int)floor(pos); 00319 float fraction = pos-(float)idx; 00320 00321 char v1, v2; 00322 00323 float hfc = (m_waveformSummary.at(idx*3+2)+128.)/hfcMax; 00324 00325 int r = kqDarkColor.red() + (int)((kqLightColor.red()-kqDarkColor.red())*hfc); 00326 int g = kqDarkColor.green()+ (int)((kqLightColor.green()-kqDarkColor.green())*hfc); 00327 int b = kqDarkColor.blue() + (int)((kqLightColor.blue()-kqDarkColor.blue())*hfc); 00328 paint.setPen(QColor(r,g,b)); 00329 00330 v1 = m_waveformSummary.at(idx*3); 00331 v2 = m_waveformSummary.at((idx+1)*3); 00332 fMin = v1 + (v2-v1)*fraction; 00333 00334 v1 = m_waveformSummary.at(idx*3+1); 00335 v2 = m_waveformSummary.at((idx+1)*3+1); 00336 fMax = v1 + (v2-v1)*fraction; 00337 } 00338 else 00339 { 00340 int idxStart = (int)(i*xscale); 00341 while (idxStart%3!=0 && idxStart>0) 00342 idxStart--; 00343 00344 int idxEnd = (int)math_min((i+1)*xscale,m_waveformSummary.size()); 00345 while (idxEnd%3!=0 && idxEnd>0) 00346 idxEnd--; 00347 00348 for (int j=idxStart; j<idxEnd; j+=3) 00349 { 00350 fMin += m_waveformSummary.at(j); 00351 fMax += m_waveformSummary.at(j+1); 00352 } 00353 fMin /= ((idxEnd-idxStart)/2.); 00354 fMax /= ((idxEnd-idxStart)/2.); 00355 00356 float hfc = (m_waveformSummary.at(idxStart+2)+128.)/hfcMax; 00357 //qDebug() << "hfc " << hfc; 00358 int r = kqDarkColor.red() + (int)((kqLightColor.red()-kqDarkColor.red())*hfc); 00359 int g = kqDarkColor.green()+ (int)((kqLightColor.green()-kqDarkColor.green())*hfc); 00360 int b = kqDarkColor.blue() + (int)((kqLightColor.blue()-kqDarkColor.blue())*hfc); 00361 paint.setPen(QColor(r,g,b)); 00362 } 00363 // qDebug() << "min " << fMin << ", max " << fMax; 00364 paint.drawLine(i, height()/2-(int)(fMin*yscale), i, height()/2-(int)(fMax*yscale)); 00365 } 00366 00367 paint.end(); 00368 update(); 00369 } 00370 00371 void WOverview::mouseMoveEvent(QMouseEvent * e) 00372 { 00373 m_iPos = e->x()-m_iStartMousePos; 00374 00375 if (m_iPos>width()-2) 00376 m_iPos = width()-2; 00377 else if (m_iPos<0) 00378 m_iPos = 0; 00379 00380 00381 // Update display 00382 update(); 00383 } 00384 00385 void WOverview::mouseReleaseEvent(QMouseEvent * e) 00386 { 00387 mouseMoveEvent(e); 00388 00389 // value ranges from 0 to 127 map to -1 to 1 00390 float fValue = ((double)m_iPos*(100./(double)(width()-2))) + 14.; 00391 00392 if (e->button()==Qt::RightButton) 00393 emit(valueChangedRightUp(fValue)); 00394 else 00395 emit(valueChangedLeftUp(fValue)); 00396 00397 m_bDrag = false; 00398 } 00399 00400 void WOverview::mousePressEvent(QMouseEvent * e) 00401 { 00402 m_iStartMousePos = 0; 00403 mouseMoveEvent(e); 00404 m_bDrag = true; 00405 } 00406 00407 void WOverview::paintEvent(QPaintEvent *) 00408 { 00409 if(!m_pScreenBuffer) 00410 return; 00411 00412 if (waveformChanged) { 00413 redrawPixmap(); 00414 waveformChanged = false; 00415 } 00416 00417 QPainter paint(this); 00418 // Fill with transparent pixels 00419 paint.fillRect(rect(), QColor(0,0,0,0)); 00420 00421 // Draw waveform, then playpos 00422 paint.drawPixmap(rect(), *m_pScreenBuffer, m_pScreenBuffer->rect()); 00423 00424 // Draw progress bar for track analysis 00425 paintTrackProgress(paint); 00426 00427 if (m_liSampleDuration > 0) { 00428 // Draw play position 00429 paint.setPen(m_qColorMarker); 00430 paint.drawLine(m_iPos, 0, m_iPos, height()); 00431 paint.drawLine(m_iPos+1, 0, m_iPos+1, height()); 00432 //paint.drawLine(m_iPos-1, 0, m_iPos-1, height()); 00433 00434 float fPos; 00435 00436 // Draw loop markers 00437 QColor loopColor = m_qColorMarker; 00438 if (!m_bLoopEnabled) { 00439 loopColor = m_qColorSignal; 00440 } 00441 paint.setPen(loopColor); 00442 if (m_dLoopStart != -1.0) { 00443 fPos = m_dLoopStart * (width() - 2) / m_liSampleDuration; 00444 paint.drawLine(fPos, 0, fPos, height()); 00445 } 00446 if (m_dLoopEnd != -1.0) { 00447 fPos = m_dLoopEnd * (width() - 2) / m_liSampleDuration; 00448 paint.drawLine(fPos, 0, fPos, height()); 00449 } 00450 00451 if (m_dLoopStart != -1.0 && m_dLoopEnd != -1.0) { 00452 //loopColor.setAlphaF(0.5); 00453 paint.setOpacity(0.5); 00454 //paint.setPen(loopColor); 00455 paint.setBrush(QBrush(loopColor)); 00456 float sPos = m_dLoopStart * (width() - 2) / m_liSampleDuration; 00457 float ePos = m_dLoopEnd * (width() - 2) / m_liSampleDuration; 00458 QRectF rect(QPointF(sPos, 0), QPointF(ePos, height()-1)); 00459 paint.drawRect(rect); 00460 paint.setOpacity(1.0); 00461 } 00462 00463 QFont font; 00464 font.setBold(false); 00465 int textWidth = 8; 00466 int textHeight = 10; 00467 font.setPixelSize(2*textHeight); 00468 paint.setPen(m_qColorMarker); 00469 paint.setFont(font); 00470 00471 // Draw hotcues 00472 for (int i = 0; i < m_hotcues.size(); ++i) { 00473 int position = m_hotcues[i]; 00474 if (position == -1) 00475 continue; 00476 fPos = float(position) * (width()-2) / m_liSampleDuration; 00477 //qDebug() << "Drawing cue" << i << "at" << fPos; 00478 00479 paint.drawLine(fPos, 0, 00480 fPos, height()); 00481 // paint.drawLine(fPos+1, 0, 00482 // fPos+1, height()); 00483 00484 // int halfHeight = height()/2; 00485 // QRectF rect(QPointF(fPos-textWidth, halfHeight-textHeight), 00486 // QPointF(fPos+textWidth, halfHeight+textHeight)); 00487 00488 // paint.drawText(rect, Qt::AlignCenter, QString("%1").arg(i+1)); 00489 } 00490 } 00491 00492 paint.end(); 00493 } 00494 00495 void WOverview::paintTrackProgress(QPainter& pPainter) { 00496 if (m_analysing) { 00497 // Prepare rectangle 00498 QRectF buf = m_pScreenBuffer->rect(); 00499 qreal width = static_cast<float>(buf.width() * m_iProgress) / 100.0f; 00500 qreal height = buf.height(); 00501 QRectF bar(0, 0, width, height); 00502 00503 // Prepare color 00504 QColor color = m_qColorProgress; 00505 color.setAlpha(m_iProgressAlpha); 00506 00507 // Paint translucent rectangle representing analysis progress 00508 pPainter.fillRect(bar, color); 00509 } 00510 } 00511 00512 QColor WOverview::getMarkerColor() { 00513 return m_qColorMarker; 00514 } 00515 00516 QColor WOverview::getSignalColor() { 00517 return m_qColorSignal; 00518 } 00519 00520 void WOverview::dragEnterEvent(QDragEnterEvent* event) { 00521 // Accept the enter event if the thing is a filepath and nothing's playing 00522 // in this deck. 00523 if (event->mimeData()->hasUrls() && 00524 event->mimeData()->urls().size() > 0) { 00525 ControlObject *pPlayCO = ControlObject::getControl( 00526 ConfigKey(m_pGroup, "play")); 00527 if (pPlayCO && pPlayCO->get()) { 00528 event->ignore(); 00529 } else { 00530 event->acceptProposedAction(); 00531 } 00532 } 00533 } 00534 00535 void WOverview::dropEvent(QDropEvent* event) { 00536 if (event->mimeData()->hasUrls() && 00537 event->mimeData()->urls().size() > 0) { 00538 QList<QUrl> urls(event->mimeData()->urls()); 00539 QUrl url = urls.first(); 00540 QString name = url.toLocalFile(); 00541 //If the file is on a network share, try just converting the URL to a string... 00542 if (name == "") { 00543 name = url.toString(); 00544 } 00545 event->accept(); 00546 emit(trackDropped(name, m_pGroup)); 00547 } else { 00548 event->ignore(); 00549 } 00550 }