Mixxx

/home/maxime/Projets/Mixxx/1.10/mixxx/src/analyserqueue.cpp

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