Mixxx
|
00001 #include <QMutexLocker> 00002 #include <QDebug> 00003 00004 #include "track/beatgrid.h" 00005 #include "mathstuff.h" 00006 00007 static int kFrameSize = 2; 00008 00009 struct BeatGridData { 00010 double bpm; 00011 double firstBeat; 00012 }; 00013 00014 BeatGrid::BeatGrid(TrackPointer pTrack, const QByteArray* pByteArray) 00015 : QObject(), 00016 m_mutex(QMutex::Recursive), 00017 m_iSampleRate(pTrack->getSampleRate()), 00018 m_dBpm(0.0), 00019 m_dFirstBeat(0.0f), 00020 m_dBeatLength(0.0f) { 00021 connect(pTrack.data(), SIGNAL(bpmUpdated(double)), 00022 this, SLOT(slotTrackBpmUpdated(double)), 00023 Qt::DirectConnection); 00024 slotTrackBpmUpdated(pTrack->getBpm()); 00025 00026 qDebug() << "New BeatGrid"; 00027 if (pByteArray != NULL) { 00028 readByteArray(pByteArray); 00029 } 00030 } 00031 00032 BeatGrid::~BeatGrid() { 00033 00034 } 00035 00036 void BeatGrid::setGrid(double dBpm, double dFirstBeatSample) { 00037 QMutexLocker lock(&m_mutex); 00038 m_dBpm = dBpm; 00039 m_dFirstBeat = dFirstBeatSample; 00040 // Calculate beat length as sample offsets 00041 m_dBeatLength = (60.0 * m_iSampleRate / m_dBpm) * kFrameSize; 00042 } 00043 00044 QByteArray* BeatGrid::toByteArray() const { 00045 QMutexLocker locker(&m_mutex); 00046 BeatGridData blob = { m_dBpm, (m_dFirstBeat / kFrameSize) }; 00047 QByteArray* pByteArray = new QByteArray((char *)&blob, sizeof(blob)); 00048 // Caller is responsible for delete 00049 return pByteArray; 00050 } 00051 00052 void BeatGrid::readByteArray(const QByteArray* pByteArray) { 00053 if ( pByteArray->size() != sizeof(BeatGridData)) 00054 return; 00055 BeatGridData *blob = (BeatGridData *)pByteArray->data(); 00056 // We serialize into frame offsets but use sample offsets at runtime 00057 setGrid(blob->bpm, blob->firstBeat * kFrameSize); 00058 } 00059 00060 QString BeatGrid::getVersion() const { 00061 QMutexLocker locker(&m_mutex); 00062 return "BeatGrid-1.0"; 00063 } 00064 00065 // internal use only 00066 bool BeatGrid::isValid() const { 00067 return m_iSampleRate > 0 && m_dBpm > 0; 00068 } 00069 00070 // This could be implemented in the Beats Class itself. 00071 // If necessary, the child class can redefine it. 00072 double BeatGrid::findNextBeat(double dSamples) const { 00073 return findNthBeat(dSamples, +1); 00074 } 00075 00076 // This could be implemented in the Beats Class itself. 00077 // If necessary, the child class can redefine it. 00078 double BeatGrid::findPrevBeat(double dSamples) const { 00079 return findNthBeat(dSamples, -1); 00080 } 00081 00082 // This is an internal call. This could be implemented in the Beats Class itself. 00083 double BeatGrid::findClosestBeat(double dSamples) const { 00084 QMutexLocker locker(&m_mutex); 00085 if (!isValid()) { 00086 return -1; 00087 } 00088 double nextBeat = findNextBeat(dSamples); 00089 double prevBeat = findPrevBeat(dSamples); 00090 return (nextBeat - dSamples > dSamples - prevBeat) ? prevBeat : nextBeat; 00091 } 00092 00093 double BeatGrid::findNthBeat(double dSamples, int n) const { 00094 QMutexLocker locker(&m_mutex); 00095 if (!isValid() || n == 0) { 00096 return -1; 00097 } 00098 00099 double beatFraction = (dSamples - m_dFirstBeat) / m_dBeatLength; 00100 double prevBeat = floorf(beatFraction); 00101 double nextBeat = ceilf(beatFraction); 00102 00103 // If the position is within 1/100th of the next or previous beat, treat it 00104 // as if it is that beat. 00105 const double kEpsilon = .01; 00106 00107 if (fabs(nextBeat - beatFraction) < kEpsilon) { 00108 beatFraction = nextBeat; 00109 } else if (fabs(prevBeat - beatFraction) < kEpsilon) { 00110 beatFraction = prevBeat; 00111 } 00112 00113 double dClosestBeat; 00114 if (n > 0) { 00115 // We're going forward, so use ceilf to round up to the next multiple of 00116 // m_dBeatLength 00117 dClosestBeat = ceilf(beatFraction) * m_dBeatLength + m_dFirstBeat; 00118 n = n - 1; 00119 } else { 00120 // We're going backward, so use floorf to round down to the next multiple 00121 // of m_dBeatLength 00122 dClosestBeat = floorf(beatFraction) * m_dBeatLength + m_dFirstBeat; 00123 n = n + 1; 00124 } 00125 00126 double dResult = dClosestBeat + n * m_dBeatLength; 00127 if (!even(dResult)) { 00128 dResult--; 00129 } 00130 return dResult; 00131 } 00132 00133 void BeatGrid::findBeats(double startSample, double stopSample, QList<double>* pBeatsList) const { 00134 QMutexLocker locker(&m_mutex); 00135 if (!isValid() || startSample > stopSample) { 00136 return; 00137 } 00138 double curBeat = findNextBeat(startSample); 00139 while (curBeat <= stopSample) { 00140 pBeatsList->append(curBeat); 00141 curBeat += m_dBeatLength; 00142 } 00143 } 00144 00145 bool BeatGrid::hasBeatInRange(double startSample, double stopSample) const { 00146 QMutexLocker locker(&m_mutex); 00147 if (!isValid() || startSample > stopSample) { 00148 return false; 00149 } 00150 double curBeat = findNextBeat(startSample); 00151 if (curBeat <= stopSample) { 00152 return true; 00153 } 00154 return false; 00155 } 00156 00157 double BeatGrid::getBpm() const { 00158 QMutexLocker locker(&m_mutex); 00159 if (!isValid()) { 00160 return 0; 00161 } 00162 return m_dBpm; 00163 } 00164 00165 double BeatGrid::getBpmRange(double startSample, double stopSample) const { 00166 QMutexLocker locker(&m_mutex); 00167 if (!isValid() || startSample > stopSample) { 00168 return -1; 00169 } 00170 return m_dBpm; 00171 } 00172 00173 void BeatGrid::addBeat(double dBeatSample) { 00174 //QMutexLocker locker(&m_mutex); 00175 return; 00176 } 00177 00178 void BeatGrid::removeBeat(double dBeatSample) { 00179 //QMutexLocker locker(&m_mutex); 00180 return; 00181 } 00182 00183 void BeatGrid::moveBeat(double dBeatSample, double dNewBeatSample) { 00184 //QMutexLocker locker(&m_mutex); 00185 return; 00186 } 00187 00188 void BeatGrid::translate(double dNumSamples) { 00189 QMutexLocker locker(&m_mutex); 00190 if (!isValid()) { 00191 return; 00192 } 00193 m_dFirstBeat += dNumSamples; 00194 locker.unlock(); 00195 emit(updated()); 00196 } 00197 00198 void BeatGrid::scale(double dScalePercentage) { 00199 QMutexLocker locker(&m_mutex); 00200 if (!isValid()) { 00201 return; 00202 } 00203 m_dBpm *= dScalePercentage; 00204 locker.unlock(); 00205 emit(updated()); 00206 } 00207 00208 void BeatGrid::slotTrackBpmUpdated(double dBpm) { 00209 QMutexLocker locker(&m_mutex); 00210 m_dBpm = dBpm; 00211 m_dBeatLength = (60.0 * m_iSampleRate / m_dBpm) * kFrameSize; 00212 }