![]() |
Mixxx
|
00001 #include <QtDebug> 00002 00003 #include "trackinfoobject.h" 00004 #include "analyserqueue.h" 00005 #include "soundsourceproxy.h" 00006 00007 #ifdef __TONAL__ 00008 #include "tonal/tonalanalyser.h" 00009 #endif 00010 00011 #include "analyserwaveform.h" 00012 #include "analyserwavesummary.h" 00013 #include "analyserbpm.h" 00014 #include "analyserrg.h" 00015 00016 AnalyserQueue::AnalyserQueue() : m_aq(), 00017 m_tioq(), 00018 m_qm(), 00019 m_qwait(), 00020 m_exit(false) 00021 { 00022 00023 } 00024 00025 int AnalyserQueue::numQueuedTracks() 00026 { 00027 m_qm.lock(); 00028 int numQueuedTracks = m_tioq.count(); 00029 m_qm.unlock(); 00030 return numQueuedTracks; 00031 } 00032 00033 void AnalyserQueue::addAnalyser(Analyser* an) { 00034 m_aq.push_back(an); 00035 } 00036 00037 TrackPointer AnalyserQueue::dequeueNextBlocking() { 00038 m_qm.lock(); 00039 00040 if (m_tioq.isEmpty()) { 00041 m_qwait.wait(&m_qm); 00042 00043 if (m_exit) { 00044 m_qm.unlock(); 00045 return TrackPointer(); 00046 } 00047 } 00048 00049 // Implicit cast to TrackPointer from weak pointer 00050 TrackPointer pTrack = m_tioq.dequeue(); 00051 00052 m_qm.unlock(); 00053 00054 // pTrack might be NULL, up to the caller to check. 00055 return pTrack; 00056 } 00057 00058 void AnalyserQueue::doAnalysis(TrackPointer tio, SoundSourceProxy *pSoundSource) { 00059 00060 // TonalAnalyser requires a block size of 65536. Using a different value 00061 // breaks the tonal analyser. We need to use a smaller block size becuase on 00062 // Linux, the AnalyserQueue can starve the CPU of its resources, resulting 00063 // in xruns.. A block size of 8192 seems to do fine. 00064 const int ANALYSISBLOCKSIZE = 8192; 00065 00066 int totalSamples = pSoundSource->length(); 00067 //qDebug() << tio->getFilename() << " has " << totalSamples << " samples."; 00068 int processedSamples = 0; 00069 00070 SAMPLE *data16 = new SAMPLE[ANALYSISBLOCKSIZE]; 00071 CSAMPLE *samples = new CSAMPLE[ANALYSISBLOCKSIZE]; 00072 00073 int read = 0; 00074 bool dieflag = false; 00075 00076 do { 00077 read = pSoundSource->read(ANALYSISBLOCKSIZE, data16); 00078 00079 // Safety net in case something later barfs on 0 sample input 00080 if (read == 0) { 00081 break; 00082 } 00083 00084 // If we get more samples than length, ask the analysers to process 00085 // up to the number we promised, then stop reading - AD 00086 if (read + processedSamples > totalSamples) { 00087 qDebug() << "While processing track of length " << totalSamples << " actually got " 00088 << read + processedSamples << " samples, truncating analysis at expected length"; 00089 read = totalSamples - processedSamples; 00090 dieflag = true; 00091 } 00092 00093 for (int i = 0; i < read; i++) { 00094 samples[i] = ((float)data16[i])/32767.0f; 00095 } 00096 00097 QListIterator<Analyser*> it(m_aq); 00098 00099 while (it.hasNext()) { 00100 Analyser* an = it.next(); 00101 //qDebug() << typeid(*an).name() << ".process()"; 00102 an->process(samples, read); 00103 //qDebug() << "Done " << typeid(*an).name() << ".process()"; 00104 } 00105 00106 // emit progress updates to whoever cares 00107 processedSamples += read; 00108 int progress = ((float)processedSamples)/totalSamples * 100; //fp div here prevents insano signed overflow 00109 emit(trackProgress(tio, progress)); 00110 00111 // Since this is a background analysis queue, we should co-operatively 00112 // yield every now and then to try and reduce CPU contention. The 00113 // analyser queue is CPU intensive so we want to get out of the way of 00114 // the audio callback thread. 00115 //QThread::yieldCurrentThread(); 00116 //QThread::usleep(10); 00117 00118 } while(read == ANALYSISBLOCKSIZE && !dieflag); 00119 00120 delete[] data16; 00121 delete[] samples; 00122 } 00123 00124 void AnalyserQueue::stop() { 00125 m_exit = true; 00126 } 00127 00128 void AnalyserQueue::run() { 00129 00130 unsigned static id = 0; //the id of this thread, for debugging purposes //XXX copypasta (should factor this out somehow), -kousu 2/2009 00131 QThread::currentThread()->setObjectName(QString("AnalyserQueue %1").arg(++id)); 00132 00133 // If there are no analysers, don't waste time running. 00134 if (m_aq.size() == 0) 00135 return; 00136 00137 while (!m_exit) { 00138 TrackPointer next = dequeueNextBlocking(); 00139 00140 if (m_exit) //When exit is set, it makes the above unblock first. 00141 return; 00142 00143 // If the track is NULL, get the next one. Could happen if the track was 00144 // queued but then deleted. 00145 if (!next) 00146 continue; 00147 00148 // Get the audio 00149 SoundSourceProxy * pSoundSource = new SoundSourceProxy(next); 00150 pSoundSource->open(); //Open the file for reading 00151 int iNumSamples = pSoundSource->length(); 00152 int iSampleRate = pSoundSource->getSampleRate(); 00153 00154 if (iNumSamples == 0 || iSampleRate == 0) { 00155 qDebug() << "Skipping invalid file:" << next->getLocation(); 00156 continue; 00157 } 00158 00159 QListIterator<Analyser*> it(m_aq); 00160 00161 while (it.hasNext()) { 00162 it.next()->initialise(next, iSampleRate, iNumSamples); 00163 } 00164 00165 doAnalysis(next, pSoundSource); 00166 00167 QListIterator<Analyser*> itf(m_aq); 00168 00169 while (itf.hasNext()) { 00170 itf.next()->finalise(next); 00171 } 00172 00173 delete pSoundSource; 00174 emit(trackFinished(next)); 00175 00176 m_qm.lock(); 00177 bool empty = m_tioq.isEmpty(); 00178 m_qm.unlock(); 00179 if (empty) { 00180 emit(queueEmpty()); 00181 } 00182 } 00183 } 00184 00185 void AnalyserQueue::queueAnalyseTrack(TrackPointer tio) { 00186 m_qm.lock(); 00187 m_tioq.enqueue(tio); 00188 m_qwait.wakeAll(); 00189 m_qm.unlock(); 00190 } 00191 00192 AnalyserQueue* AnalyserQueue::createAnalyserQueue(QList<Analyser*> analysers) { 00193 AnalyserQueue* ret = new AnalyserQueue(); 00194 00195 QListIterator<Analyser*> it(analysers); 00196 while(it.hasNext()) { 00197 ret->addAnalyser(it.next()); 00198 } 00199 00200 ret->start(QThread::IdlePriority); 00201 return ret; 00202 } 00203 00204 AnalyserQueue* AnalyserQueue::createDefaultAnalyserQueue(ConfigObject<ConfigValue> *_config) { 00205 AnalyserQueue* ret = new AnalyserQueue(); 00206 00207 #ifdef __TONAL__ 00208 ret->addAnalyser(new TonalAnalyser()); 00209 #endif 00210 00211 ret->addAnalyser(new AnalyserWavesummary()); 00212 ret->addAnalyser(new AnalyserWaveform()); 00213 ret->addAnalyser(new AnalyserBPM(_config)); 00214 ret->addAnalyser(new AnalyserGain(_config)); 00215 00216 ret->start(QThread::IdlePriority); 00217 return ret; 00218 } 00219 00220 AnalyserQueue* AnalyserQueue::createPrepareViewAnalyserQueue(ConfigObject<ConfigValue> *_config) { 00221 AnalyserQueue* ret = new AnalyserQueue(); 00222 ret->addAnalyser(new AnalyserWavesummary()); 00223 ret->addAnalyser(new AnalyserBPM(_config)); 00224 ret->addAnalyser(new AnalyserGain(_config)); 00225 ret->start(QThread::IdlePriority); 00226 return ret; 00227 } 00228 00229 AnalyserQueue::~AnalyserQueue() { 00230 QListIterator<Analyser*> it(m_aq); 00231 00232 stop(); 00233 00234 m_qm.lock(); 00235 m_qwait.wakeAll(); 00236 m_qm.unlock(); 00237 00238 wait(); //Wait until thread has actually stopped before proceeding. 00239 00240 while (it.hasNext()) { 00241 Analyser* an = it.next(); 00242 //qDebug() << "AnalyserQueue: deleting " << typeid(an).name(); 00243 delete an; 00244 } 00245 }