// This is called from the AnalyserQueue thread bool AnalyserQueue::doAnalysis(TrackPointer tio, SoundSourceProxy* pSoundSource) { int totalSamples = pSoundSource->length(); //qDebug() << tio->getFilename() << " has " << totalSamples << " samples."; int processedSamples = 0; QTime progressUpdateInhibitTimer; progressUpdateInhibitTimer.start(); // Inhibit Updates for 60 milliseconds int read = 0; bool dieflag = false; bool cancelled = false; int progress; // progress in 0 ... 100 do { ScopedTimer t("AnalyserQueue::doAnalysis block"); read = pSoundSource->read(kAnalysisBlockSize, m_pSamplesPCM); // To compare apples to apples, let's only look at blocks that are the // full block size. if (read != kAnalysisBlockSize) { t.cancel(); } // Safety net in case something later barfs on 0 sample input if (read == 0) { t.cancel(); break; } // If we get more samples than length, ask the analysers to process // up to the number we promised, then stop reading - AD if (read + processedSamples > totalSamples) { qDebug() << "While processing track of length " << totalSamples << " actually got " << read + processedSamples << " samples, truncating analysis at expected length"; read = totalSamples - processedSamples; dieflag = true; } // Normalize the samples from [SHRT_MIN, SHRT_MAX] to [-1.0, 1.0]. // TODO(rryan): Change the SoundSource API to do this for us. for (int i = 0; i < read; ++i) { m_pSamples[i] = static_cast<CSAMPLE>(m_pSamplesPCM[i]) / SHRT_MAX; } QListIterator<Analyser*> it(m_aq); while (it.hasNext()) { Analyser* an = it.next(); //qDebug() << typeid(*an).name() << ".process()"; an->process(m_pSamples, read); //qDebug() << "Done " << typeid(*an).name() << ".process()"; } // emit progress updates // During the doAnalysis function it goes only to 100% - FINALIZE_PERCENT // because the finalise functions will take also some time processedSamples += read; //fp div here prevents insane signed overflow progress = (int)(((float)processedSamples)/totalSamples * (1000 - FINALIZE_PERCENT)); if (m_progressInfo.track_progress != progress) { if (progressUpdateInhibitTimer.elapsed() > 60) { // Inhibit Updates for 60 milliseconds emitUpdateProgress(tio, progress); progressUpdateInhibitTimer.start(); } } // Since this is a background analysis queue, we should co-operatively // yield every now and then to try and reduce CPU contention. The // analyser queue is CPU intensive so we want to get out of the way of // the audio callback thread. //QThread::yieldCurrentThread(); //QThread::usleep(10); //has something new entered the queue? if (load_atomic(m_aiCheckPriorities)) { m_aiCheckPriorities = false; if (isLoadedTrackWaiting(tio)) { qDebug() << "Interrupting analysis to give preference to a loaded track."; dieflag = true; cancelled = true; } } if (m_exit) { dieflag = true; cancelled = true; } // Ignore blocks in which we decided to bail for stats purposes. if (dieflag || cancelled) { t.cancel(); } } while(read == kAnalysisBlockSize && !dieflag); return !cancelled; //don't return !dieflag or we might reanalyze over and over }
// This is called from the AnalyserQueue thread bool AnalyserQueue::doAnalysis(TrackPointer tio, SoundSourceProxy* pSoundSource) { // TonalAnalyser requires a block size of 65536. Using a different value // breaks the tonal analyser. We need to use a smaller block size becuase on // Linux, the AnalyserQueue can starve the CPU of its resources, resulting // in xruns.. A block size of 8192 seems to do fine. const int ANALYSISBLOCKSIZE = 8192; int totalSamples = pSoundSource->length(); //qDebug() << tio->getFilename() << " has " << totalSamples << " samples."; int processedSamples = 0; SAMPLE* data16 = new SAMPLE[ANALYSISBLOCKSIZE]; CSAMPLE* samples = new CSAMPLE[ANALYSISBLOCKSIZE]; QTime progressUpdateInhibitTimer; progressUpdateInhibitTimer.start(); // Inhibit Updates for 60 milliseconds int read = 0; bool dieflag = false; bool cancelled = false; int progress; // progress in 0 ... 100 do { ScopedTimer t("AnalyserQueue::doAnalysis block"); read = pSoundSource->read(ANALYSISBLOCKSIZE, data16); // To compare apples to apples, let's only look at blocks that are the // full block size. if (read != ANALYSISBLOCKSIZE) { t.cancel(); } // Safety net in case something later barfs on 0 sample input if (read == 0) { t.cancel(); break; } // If we get more samples than length, ask the analysers to process // up to the number we promised, then stop reading - AD if (read + processedSamples > totalSamples) { qDebug() << "While processing track of length " << totalSamples << " actually got " << read + processedSamples << " samples, truncating analysis at expected length"; read = totalSamples - processedSamples; dieflag = true; } for (int i = 0; i < read; ++i) { samples[i] = ((float)data16[i])/32767.0f; } QListIterator<Analyser*> it(m_aq); while (it.hasNext()) { Analyser* an = it.next(); //qDebug() << typeid(*an).name() << ".process()"; an->process(samples, read); //qDebug() << "Done " << typeid(*an).name() << ".process()"; } // emit progress updates // During the doAnalysis function it goes only to 100% - FINALIZE_PERCENT // because the finalise functions will take also some time processedSamples += read; //fp div here prevents insane signed overflow progress = (int)(((float)processedSamples)/totalSamples * (1000 - FINALIZE_PERCENT)); if (m_progressInfo.track_progress != progress) { if (progressUpdateInhibitTimer.elapsed() > 60) { // Inhibit Updates for 60 milliseconds emitUpdateProgress(tio, progress); progressUpdateInhibitTimer.start(); } } // Since this is a background analysis queue, we should co-operatively // yield every now and then to try and reduce CPU contention. The // analyser queue is CPU intensive so we want to get out of the way of // the audio callback thread. //QThread::yieldCurrentThread(); //QThread::usleep(10); //has something new entered the queue? if (m_aiCheckPriorities) { m_aiCheckPriorities = false; if (isLoadedTrackWaiting(tio)) { qDebug() << "Interrupting analysis to give preference to a loaded track."; dieflag = true; cancelled = true; } } if (m_exit) { dieflag = true; cancelled = true; } // Ignore blocks in which we decided to bail for stats purposes. if (dieflag || cancelled) { t.cancel(); } } while(read == ANALYSISBLOCKSIZE && !dieflag); delete[] data16; delete[] samples; return !cancelled; //don't return !dieflag or we might reanalyze over and over }