Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/widget/woverview.cpp

Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines