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