void CachingReaderWorker::run() { unsigned static id = 0; //the id of this thread, for debugging purposes QThread::currentThread()->setObjectName(QString("CachingReaderWorker %1").arg(++id)); CachingReaderChunkReadRequest request; Event::start(m_tag); while (!load_atomic(m_stop)) { if (m_newTrack) { TrackPointer pLoadTrack; { // locking scope QMutexLocker locker(&m_newTrackMutex); pLoadTrack = m_newTrack; m_newTrack = TrackPointer(); } // implicitly unlocks the mutex loadTrack(pLoadTrack); } else if (m_pChunkReadRequestFIFO->read(&request, 1) == 1) { // Read the requested chunk and send the result const ReaderStatusUpdate update(processReadRequest(request)); m_pReaderStatusFIFO->writeBlocking(&update, 1); } else { Event::end(m_tag); m_semaRun.acquire(); Event::start(m_tag); } } }
void CachingReaderWorker::run() { unsigned static id = 0; //the id of this thread, for debugging purposes QThread::currentThread()->setObjectName(QString("CachingReaderWorker %1").arg(++id)); TrackPointer pLoadTrack; ChunkReadRequest request; ReaderStatusUpdate status; Event::start(m_tag); while (!load_atomic(m_stop)) { if (m_newTrack) { m_newTrackMutex.lock(); pLoadTrack = m_newTrack; m_newTrack = TrackPointer(); m_newTrackMutex.unlock(); loadTrack(pLoadTrack); } else if (m_pChunkReadRequestFIFO->read(&request, 1) == 1) { // Read the requested chunks. processChunkReadRequest(&request, &status); m_pReaderStatusFIFO->writeBlocking(&status, 1); } else { Event::end(m_tag); m_semaRun.acquire(); Event::start(m_tag); } } }
void TrackInfoObject::setAnalyserProgress(int progress) { // progress in 0 .. 1000. QAtomicInt so no need for lock. if (progress != load_atomic(m_analyserProgress)) { m_analyserProgress = progress; emit(analyserProgress(progress)); } }
void BulkReader::run() { m_stop = 0; unsigned char data[255]; while (load_atomic(m_stop) == 0) { // Blocked polling: The only problem with this is that we can't close // the device until the block is released, which means the controller // has to send more data //result = hid_read_timeout(m_pHidDevice, data, 255, -1); // This relieves that at the cost of higher CPU usage since we only // block for a short while (500ms) int transferred; int result; result = libusb_bulk_transfer(m_phandle, m_in_epaddr, data, sizeof(data), &transferred, 500); Trace timeout("BulkReader timeout"); if (result >= 0) { Trace process("BulkReader process packet"); //qDebug() << "Read" << result << "bytes, pointer:" << data; QByteArray outData((char*)data, transferred); emit(incomingData(outData, Time::elapsed())); } } qDebug() << "Stopped Reader"; }
void TrackExportWorker::run() { int i = 0; for (const auto& track : m_tracks) { auto fileinfo = track->getFileInfo(); emit(progress(fileinfo.fileName(), i, m_tracks.size())); exportTrack(track); if (load_atomic(m_bStop)) { emit(canceled()); return; } ++i; emit(progress(fileinfo.fileName(), i, m_tracks.size())); } }
void TrackExportWorker::run() { int i = 0; QMap<QString, QFileInfo> copy_list = createCopylist(m_tracks); for (auto it = copy_list.constBegin(); it != copy_list.constEnd(); ++it) { // We emit progress twice per loop, which may seem excessive, but it // guarantees that we emit a sane progress before we start and after // we end. In between, each filename will get its own visible tick // on the bar, which looks really nice. emit(progress(it->fileName(), i, copy_list.size())); copyFile(*it, it.key()); if (load_atomic(m_bStop)) { emit(canceled()); return; } ++i; emit(progress(it->fileName(), i, copy_list.size())); } }
TrackExportWorker::OverwriteAnswer TrackExportWorker::makeOverwriteRequest( QString filename) { // QT's QFuture is not quite right for this type of threaded question-and-answer. // std::future works fine, even with signals and slots. QScopedPointer<std::promise<OverwriteAnswer>> mode_promise( new std::promise<OverwriteAnswer>()); std::future<OverwriteAnswer> mode_future = mode_promise->get_future(); emit(askOverwriteMode(filename, mode_promise.data())); // Block until the user tells us the answer. mode_future.wait(); // We can be either canceled from the other thread, or as a return value // from this call. First check for a call from the other thread. if (load_atomic(m_bStop)) { return OverwriteAnswer::CANCEL; } if (!mode_future.valid()) { qWarning() << "TrackExportWorker::makeOverwriteRequest invalid answer from future"; m_errorMessage = tr("Error exporting tracks"); stop(); return OverwriteAnswer::CANCEL; } OverwriteAnswer answer = mode_future.get(); switch (answer) { case OverwriteAnswer::SKIP_ALL: m_overwriteMode = OverwriteMode::SKIP_ALL; break; case OverwriteAnswer::OVERWRITE_ALL: m_overwriteMode = OverwriteMode::OVERWRITE_ALL; break; case OverwriteAnswer::CANCEL: // Handle cancelation as a result of the question. m_errorMessage = tr("Export process was canceled"); stop(); break; default:; } return answer; }
void HidReader::run() { m_stop = 0; unsigned char *data = new unsigned char[255]; while (load_atomic(m_stop) == 0) { // Blocked polling: The only problem with this is that we can't close // the device until the block is released, which means the controller // has to send more data //result = hid_read_timeout(m_pHidDevice, data, 255, -1); // This relieves that at the cost of higher CPU usage since we only // block for a short while (500ms) int result = hid_read_timeout(m_pHidDevice, data, 255, 500); Trace timeout("HidReader timeout"); if (result > 0) { Trace process("HidReader process packet"); //qDebug() << "Read" << result << "bytes, pointer:" << data; QByteArray outData(reinterpret_cast<char*>(data), result); emit(incomingData(outData)); } } delete [] data; }
int TrackInfoObject::getAnalyserProgress() const { // QAtomicInt so no need for lock. return load_atomic(m_analyserProgress); }
// 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 }