Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/track/beatmatrix.cpp

Go to the documentation of this file.
00001 #include <QtDebug>
00002 #include <QMutexLocker>
00003 
00004 #include "track/beatmatrix.h"
00005 
00006 BeatMatrix::BeatMatrix(TrackPointer pTrack, const QByteArray* pByteArray)
00007         : QObject(),
00008           m_mutex(QMutex::Recursive),
00009           m_iSampleRate(pTrack->getSampleRate()) {
00010     if (pByteArray != NULL) {
00011         readByteArray(pByteArray);
00012     }
00013 }
00014 
00015 BeatMatrix::~BeatMatrix() {
00016 }
00017 
00018 unsigned int BeatMatrix::numBeats() const {
00019     return m_beatList.size();
00020 }
00021 
00022 QByteArray* BeatMatrix::toByteArray() const {
00023     QMutexLocker locker(&m_mutex);
00024     // No guarantees BeatLists are made of a data type which located adjacent
00025     // items in adjacent memory locations.
00026     double* pBuffer = new double[m_beatList.size()];
00027     for (int i = 0; i < m_beatList.size(); ++i) {
00028         pBuffer[i] = m_beatList[i];
00029     }
00030     QByteArray* pByteArray = new QByteArray((char*)pBuffer, sizeof(pBuffer[0]) * m_beatList.size());
00031     delete [] pBuffer;
00032     return pByteArray;
00033 }
00034 
00035 void BeatMatrix::readByteArray(const QByteArray* pByteArray) {
00036     if (pByteArray->size() % sizeof(double) != 0) {
00037         qDebug() << "ERROR: Could not parse BeatMatrix from QByteArray of size" << pByteArray->size();
00038         return;
00039     }
00040 
00041     int numBeats = pByteArray->size() / sizeof(double);
00042     double* pBuffer = (double*)pByteArray->data();
00043     for (int i = 0; i < numBeats; ++i) {
00044         m_beatList.append(pBuffer[i]);
00045     }
00046 }
00047 
00048 
00049 QString BeatMatrix::getVersion() const {
00050     QMutexLocker locker(&m_mutex);
00051     return "BeatMatrix-1.0";
00052 }
00053 
00054 // internal use only
00055 bool BeatMatrix::isValid() const {
00056     return m_iSampleRate > 0 && m_beatList.size() > 0;
00057 }
00058 
00059 double BeatMatrix::findNextBeat(double dSamples) const {
00060     return findNthBeat(dSamples, 1);
00061 }
00062 
00063 double BeatMatrix::findPrevBeat(double dSamples) const {
00064     return findNthBeat(dSamples, -1);
00065 }
00066 
00067 double BeatMatrix::findClosestBeat(double dSamples) const {
00068     QMutexLocker locker(&m_mutex);
00069     if (!isValid()) {
00070         return -1;
00071     }
00072     double nextBeat = findNextBeat(dSamples);
00073     double prevBeat = findPrevBeat(dSamples);
00074     return (nextBeat - dSamples > dSamples - prevBeat) ? prevBeat : nextBeat;
00075 }
00076 
00077 double BeatMatrix::findNthBeat(double dSamples, int n) const {
00078     QMutexLocker locker(&m_mutex);
00079     // Reduce the Sample Offset to a frame offset.
00080     dSamples = floorf(dSamples/2);
00081     BeatList::const_iterator it;
00082     int i;
00083 
00084     if (!isValid() || n == 0) {
00085         return -1;
00086     }
00087 
00088     if (n > 0) {
00089         it = qLowerBound(m_beatList.begin(), m_beatList.end(), dSamples);
00090 
00091         // Count down until n=1
00092         while (it != m_beatList.end()) {
00093             if (n == 1) {
00094                 // Return a Sample Offset
00095                 return (*it * 2);
00096             }
00097             it++; n--;
00098         }
00099     }
00100     else if (n < 0) {
00101         it = qUpperBound(m_beatList.begin(), m_beatList.end(), dSamples);
00102 
00103         // Count up until n=-1
00104         while (it != m_beatList.begin()) {
00105             // qUpperBound starts us off at the position just-one-past the last
00106             // occurence of dSamples-or-smaller in the list. In order to get the
00107             // last instance of dSamples-or-smaller, we decrement it by 1 before
00108             // touching it. The guard of this while loop guarantees this does
00109             // not put us before the start of the loop.
00110             it--;
00111             if (n == -1) {
00112                 // Return a Sample Offset
00113                 return (*it * 2);
00114             }
00115             n++;
00116         }
00117     }
00118 
00119     return -1;
00120 }
00121 
00122 void BeatMatrix::findBeats(double startSample, double stopSample, QList<double>* pBeatsList) const {
00123     QMutexLocker locker(&m_mutex);
00124     if (!isValid() || startSample > stopSample) {
00125         return;
00126     }
00127     BeatList::const_iterator curBeat = qLowerBound(m_beatList.begin(),
00128                                                    m_beatList.end(),
00129                                                    startSample);
00130     BeatList::const_iterator stopBeat = qUpperBound(m_beatList.begin(),
00131                                                     m_beatList.end(),
00132                                                     stopSample);
00133 
00134     for (; curBeat != stopBeat; curBeat++) {
00135         pBeatsList->append(*curBeat);
00136     }
00137 }
00138 
00139 bool BeatMatrix::hasBeatInRange(double startSample, double stopSample) const {
00140     QMutexLocker locker(&m_mutex);
00141     if (!isValid() || startSample > stopSample) {
00142         return false;
00143     }
00144     BeatList::const_iterator startBeat = qLowerBound(m_beatList.begin(),
00145                                                      m_beatList.end(),
00146                                                      startSample);
00147     BeatList::const_iterator stopBeat = qUpperBound(m_beatList.begin(),
00148                                                     m_beatList.end(),
00149                                                     stopSample);
00150 
00151     if (startBeat != stopBeat)
00152         return true;
00153     return false;
00154 }
00155 
00156 double BeatMatrix::getBpm() const {
00157     QMutexLocker locker(&m_mutex);
00158     if (!isValid()) {
00159         return -1;
00160     }
00161 
00162     // TODO(XXX) not actually correct. We need the true song length.
00163     double startSample = *m_beatList.begin();
00164     double stopSample = *(m_beatList.end()-1);
00165     double songDurationMinutes =
00166             (stopSample - startSample) / (60.0f * m_iSampleRate);
00167     return m_beatList.size() / songDurationMinutes;
00168 }
00169 
00170 double BeatMatrix::getBpmRange(double startSample, double stopSample) const {
00171     QMutexLocker locker(&m_mutex);
00172     if (!isValid() || startSample > stopSample) {
00173         return -1;
00174     }
00175 
00176     BeatList::const_iterator startBeat = qLowerBound(m_beatList.begin(),
00177                                                m_beatList.end(),
00178                                                startSample);
00179     BeatList::const_iterator stopBeat = qUpperBound(m_beatList.begin(),
00180                                                     m_beatList.end(),
00181                                                     stopSample);
00182     double rangeDurationMinutes =
00183             (stopSample - startSample) / (60.0f * m_iSampleRate);
00184     // Subtracting returns the number of beats between the samples referred to
00185     // by the start and end.
00186     double beatsInRange = stopBeat - startBeat;
00187 
00188     return beatsInRange / rangeDurationMinutes;
00189 }
00190 
00191 void BeatMatrix::addBeat(double dBeatSample) {
00192     QMutexLocker locker(&m_mutex);
00193 
00194     BeatList::iterator it = qLowerBound(m_beatList.begin(),
00195                                         m_beatList.end(),
00196                                         dBeatSample);
00197     // Don't insert a duplicate beat. TODO(XXX) determine what epsilon to
00198     // consider a beat identical to another.
00199     if (*it == dBeatSample)
00200         return;
00201 
00202     m_beatList.insert(it, dBeatSample);
00203     locker.unlock();
00204     emit(updated());
00205 }
00206 
00207 void BeatMatrix::removeBeat(double dBeatSample) {
00208     QMutexLocker locker(&m_mutex);
00209     BeatList::iterator it = qLowerBound(m_beatList.begin(),
00210                                         m_beatList.end(), dBeatSample);
00211 
00212     // In case there are duplicates, remove every instance of dBeatSample
00213     // TODO(XXX) add invariant checks against this
00214     // TODO(XXX) determine what epsilon to consider a beat identical to another
00215     while (*it == dBeatSample) {
00216         it = m_beatList.erase(it);
00217     }
00218     locker.unlock();
00219     emit(updated());
00220 }
00221 
00222 void BeatMatrix::moveBeat(double dBeatSample, double dNewBeatSample) {
00223     QMutexLocker locker(&m_mutex);
00224 
00225     BeatList::iterator it = qLowerBound(m_beatList.begin(),
00226                                         m_beatList.end(), dBeatSample);
00227 
00228     // Remove all beats from dBeatSample
00229     while (*it == dBeatSample) {
00230         it = m_beatList.erase(it);
00231     }
00232 
00233     // Now add a beat to dNewBeatSample
00234     it = qLowerBound(m_beatList.begin(),
00235                      m_beatList.end(), dNewBeatSample);
00236 
00237     // TODO(XXX) beat epsilon
00238     if (*it != dNewBeatSample) {
00239         m_beatList.insert(it, dNewBeatSample);
00240     }
00241     locker.unlock();
00242     emit(updated());
00243 }
00244 
00245 void BeatMatrix::translate(double dNumSamples) {
00246     QMutexLocker locker(&m_mutex);
00247     if (!isValid()) {
00248         return;
00249     }
00250 
00251     for (BeatList::iterator it = m_beatList.begin();
00252          it != m_beatList.end(); ++it) {
00253         *it += dNumSamples;
00254     }
00255     locker.unlock();
00256     emit(updated());
00257 }
00258 
00259 void BeatMatrix::scale(double dScalePercentage) {
00260     QMutexLocker locker(&m_mutex);
00261     if (!isValid()) {
00262         return;
00263     }
00264     for (BeatList::iterator it = m_beatList.begin();
00265          it != m_beatList.end(); ++it) {
00266         *it *= dScalePercentage;
00267     }
00268     locker.unlock();
00269     emit(updated());
00270 }
00271 
00272 void BeatMatrix::slotTrackBpmUpdated(double dBpm) {
00273     //QMutexLocker locker(&m_mutex);
00274     // TODO(XXX) How do we handle this?
00275 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines