# HG changeset patch # User Chris Cannam # Date 1194022231 0 # Node ID 1d656dcda8efc3a66aa9d6fa593f75dd9ae3cae3 # Parent bb6e4c46e20224773dceea715de96fc1b4ea1b53 * some tweaks to improve usability of these classes in a console application diff -r bb6e4c46e202 -r 1d656dcda8ef base/TempDirectory.cpp --- a/base/TempDirectory.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/base/TempDirectory.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -241,7 +241,7 @@ if (GetProcessStatus(pid) == ProcessNotRunning) { std::cerr << "INFO: Found abandoned temporary directory from " - << "an old Sonic Visualiser process\n(pid=" << pid + << "a previous, defunct process\n(pid=" << pid << ", directory=\"" << dir.filePath(dir[i]).toStdString() << "\"). Removing it..." << std::endl; diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/AudioFileReader.cpp --- a/data/fileio/AudioFileReader.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/AudioFileReader.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -15,3 +15,26 @@ #include "AudioFileReader.h" +void +AudioFileReader::getDeInterleavedFrames(size_t start, size_t count, + std::vector &frames) const +{ + SampleBlock interleaved; + getInterleavedFrames(start, count, interleaved); + + size_t channels = getChannelCount(); + size_t rc = interleaved.size() / channels; + + frames.clear(); + + for (size_t c = 0; c < channels; ++c) { + frames.push_back(SampleBlock()); + } + + for (size_t i = 0; i < rc; ++i) { + for (size_t c = 0; c < channels; ++c) { + frames[c].push_back(interleaved[i * channels + c]); + } + } +} + diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/AudioFileReader.h --- a/data/fileio/AudioFileReader.h Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/AudioFileReader.h Fri Nov 02 16:50:31 2007 +0000 @@ -17,10 +17,13 @@ #define _AUDIO_FILE_READER_H_ #include -#include "model/Model.h" // for SampleBlock #include "FileSource.h" +#include + +typedef std::vector SampleBlock; + class AudioFileReader : public QObject { Q_OBJECT @@ -45,6 +48,10 @@ virtual QString getTitle() const { return ""; } /** + * Return interleaved samples for count frames from index start. + * The resulting sample block will contain count * + * getChannelCount() samples (or fewer if end of file is reached). + * * The subclass implementations of this function must be * thread-safe -- that is, safe to call from multiple threads with * different arguments on the same object at the same time. @@ -52,6 +59,16 @@ virtual void getInterleavedFrames(size_t start, size_t count, SampleBlock &frames) const = 0; + /** + * Return de-interleaved samples for count frames from index + * start. Implemented in this class (it calls + * getInterleavedFrames and de-interleaves). The resulting vector + * will contain getChannelCount() sample blocks of count samples + * each (or fewer if end of file is reached). + */ + virtual void getDeInterleavedFrames(size_t start, size_t count, + std::vector &frames) const; + // only subclasses that do not know exactly how long the audio // file is until it's been completely decoded should implement this virtual int getDecodeCompletion() const { return 100; } // % diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/AudioFileReaderFactory.cpp --- a/data/fileio/AudioFileReaderFactory.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/AudioFileReaderFactory.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -56,9 +56,21 @@ AudioFileReader * AudioFileReaderFactory::createReader(FileSource source, size_t targetRate) { + return create(source, targetRate, false); +} + +AudioFileReader * +AudioFileReaderFactory::createThreadingReader(FileSource source, size_t targetRate) +{ + return create(source, targetRate, true); +} + +AudioFileReader * +AudioFileReaderFactory::create(FileSource source, size_t targetRate, bool threading) +{ QString err; - std::cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation().toStdString() << "\"): Requested rate: " << targetRate << std::endl; +// std::cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation().toStdString() << "\"): Requested rate: " << targetRate << std::endl; if (!source.isOK() || !source.isAvailable()) { std::cerr << "AudioFileReaderFactory::createReader(\"" << source.getLocation().toStdString() << "\": Source unavailable" << std::endl; @@ -83,7 +95,9 @@ delete reader; reader = new ResamplingWavFileReader (source, - ResamplingWavFileReader::ResampleThreaded, + threading ? + ResamplingWavFileReader::ResampleThreaded : + ResamplingWavFileReader::ResampleAtOnce, ResamplingWavFileReader::CacheInTemporaryFile, targetRate); } @@ -95,7 +109,9 @@ if (OggVorbisFileReader::supports(source)) { reader = new OggVorbisFileReader (source, - OggVorbisFileReader::DecodeThreaded, + threading ? + OggVorbisFileReader::DecodeThreaded : + OggVorbisFileReader::DecodeAtOnce, OggVorbisFileReader::CacheInTemporaryFile, targetRate); } @@ -108,7 +124,9 @@ if (MP3FileReader::supports(source)) { reader = new MP3FileReader (source, - MP3FileReader::DecodeThreaded, + threading ? + MP3FileReader::DecodeThreaded : + MP3FileReader::DecodeAtOnce, MP3FileReader::CacheInTemporaryFile, targetRate); } @@ -120,7 +138,9 @@ if (QuickTimeFileReader::supports(source)) { reader = new QuickTimeFileReader (source, - QuickTimeFileReader::DecodeThreaded, + threading ? + QuickTimeFileReader::DecodeThreaded : + QuickTimeFileReader::DecodeAtOnce, QuickTimeFileReader::CacheInTemporaryFile, targetRate); } @@ -129,7 +149,7 @@ if (reader) { if (reader->isOK()) { - std::cerr << "AudioFileReaderFactory: Reader is OK" << std::endl; +// std::cerr << "AudioFileReaderFactory: Reader is OK" << std::endl; return reader; } std::cerr << "AudioFileReaderFactory: Preferred reader for " diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/AudioFileReaderFactory.h --- a/data/fileio/AudioFileReaderFactory.h Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/AudioFileReaderFactory.h Fri Nov 02 16:50:31 2007 +0000 @@ -44,7 +44,30 @@ * * Caller owns the returned object and must delete it after use. */ - static AudioFileReader *createReader(FileSource source, size_t targetRate = 0); + static AudioFileReader *createReader(FileSource source, + size_t targetRate = 0); + + /** + * Return an audio file reader initialised to the file at the + * given path, or NULL if no suitable reader for this path is + * available or the file cannot be opened. If the reader supports + * threaded decoding, it will be used and the file decoded in a + * background thread. + * + * If targetRate is non-zero, the file will be resampled to that + * rate (transparently). You can query reader->getNativeRate() + * if you want to find out whether the file is being resampled + * or not. + * + * Caller owns the returned object and must delete it after use. + */ + static AudioFileReader *createThreadingReader(FileSource source, + size_t targetRate = 0); + +protected: + static AudioFileReader *create(FileSource source, + size_t targetRate, + bool threading); }; #endif diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/CodedAudioFileReader.cpp --- a/data/fileio/CodedAudioFileReader.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/CodedAudioFileReader.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -40,7 +40,7 @@ m_resampler(0), m_resampleBuffer(0) { - std::cerr << "CodedAudioFileReader::CodedAudioFileReader: rate " << targetRate << std::endl; +// std::cerr << "CodedAudioFileReader::CodedAudioFileReader: rate " << targetRate << std::endl; m_frameCount = 0; m_sampleRate = targetRate; diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/FileSource.cpp --- a/data/fileio/FileSource.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/FileSource.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -27,6 +27,8 @@ #include +//#define DEBUG_FILE_SOURCE 1 + int FileSource::m_count = 0; @@ -56,7 +58,9 @@ m_progressShowTimer(this), m_refCounted(false) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::FileSource(" << fileOrUrl.toStdString() << ")" << std::endl; +#endif if (!canHandleScheme(m_url)) { std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; @@ -106,7 +110,9 @@ m_progressShowTimer(this), m_refCounted(false) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::FileSource(" << url.toString().toStdString() << ") [as url]" << std::endl; +#endif if (!canHandleScheme(m_url)) { std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; @@ -132,7 +138,9 @@ m_progressShowTimer(0), m_refCounted(false) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::FileSource(" << m_url.toString().toStdString() << ") [copy ctor]" << std::endl; +#endif if (!canHandleScheme(m_url)) { std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; @@ -144,11 +152,15 @@ m_localFilename = rf.m_localFilename; } else { QMutexLocker locker(&m_mapMutex); +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::FileSource(copy ctor): ref count is " << m_refCountMap[m_url] << std::endl; +#endif if (m_refCountMap[m_url] > 0) { m_refCountMap[m_url]++; +#ifdef DEBUG_FILE_SOURCE std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl; +#endif m_localFilename = m_remoteLocalMap[m_url]; m_refCounted = true; } else { @@ -162,7 +174,9 @@ FileSource::~FileSource() { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource(" << m_url.toString().toStdString() << ")::~FileSource" << std::endl; +#endif cleanup(); @@ -185,7 +199,9 @@ } if (createCacheFile()) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::init: Already have this one" << std::endl; +#endif m_ok = true; if (!QFileInfo(m_localFilename).exists()) { m_lastStatus = 404; @@ -202,8 +218,10 @@ QString scheme = m_url.scheme().toLower(); +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::init: Don't have local copy of \"" << m_url.toString().toStdString() << "\", retrieving" << std::endl; +#endif if (scheme == "http") { initHttp(); @@ -223,7 +241,9 @@ // but has got there first cleanup(); m_refCountMap[m_url]++; +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::init: Another FileSource has got there first, abandoning our download and using theirs" << std::endl; +#endif m_localFilename = m_remoteLocalMap[m_url]; m_refCounted = true; m_ok = true; @@ -305,8 +325,10 @@ QString path = "/" + QString(m_url.toEncoded()).section('/', 3); +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource: path is \"" << path.toStdString() << "\"" << std::endl; +#endif m_http->get(path, m_localFile); } @@ -387,8 +409,10 @@ bool available = true; if (!m_ok) available = false; else available = (m_lastStatus / 100 == 2); +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::isAvailable: " << (available ? "yes" : "no") << std::endl; +#endif return available; } @@ -480,11 +504,15 @@ if (m_lastStatus / 100 >= 4) { m_errorString = QString("%1 %2") .arg(resp.statusCode()).arg(resp.reasonPhrase()); +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::responseHeaderReceived: " << m_errorString.toStdString() << std::endl; +#endif } else { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::responseHeaderReceived: " << m_lastStatus << std::endl; +#endif if (resp.hasContentType()) m_contentType = resp.contentType(); } emit statusAvailable(); @@ -493,15 +521,19 @@ void FileSource::ftpCommandFinished(int id, bool error) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::ftpCommandFinished(" << id << ", " << error << ")" << std::endl; +#endif if (!m_ftp) return; QFtp::Command command = m_ftp->currentCommand(); if (!error) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::ftpCommandFinished: success for command " << command << std::endl; +#endif return; } @@ -521,11 +553,11 @@ void FileSource::dataTransferProgress(qint64 done, qint64 total) { - if (!m_progressDialog) return; - int percent = int((double(done) / double(total)) * 100.0 - 0.1); emit progress(percent); + if (!m_progressDialog) return; + if (percent > 0) { m_progressDialog->setValue(percent); m_progressDialog->show(); @@ -545,12 +577,14 @@ void FileSource::done(bool error) { + emit progress(100); + +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::done(" << error << ")" << std::endl; +#endif if (m_done) return; - emit progress(100); - if (error) { if (m_http) { m_errorString = m_http->errorString(); @@ -577,7 +611,9 @@ } if (error) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::done: error is " << error << ", deleting cache file" << std::endl; +#endif deleteCacheFile(); } @@ -589,7 +625,9 @@ void FileSource::deleteCacheFile() { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::deleteCacheFile(\"" << m_localFilename.toStdString() << "\")" << std::endl; +#endif cleanup(); @@ -598,7 +636,9 @@ } if (!isRemote()) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "not a cache file" << std::endl; +#endif return; } @@ -609,7 +649,9 @@ if (m_refCountMap[m_url] > 0) { m_refCountMap[m_url]--; +#ifdef DEBUG_FILE_SOURCE std::cerr << "reduced ref count to " << m_refCountMap[m_url] << std::endl; +#endif if (m_refCountMap[m_url] > 0) { m_done = true; return; @@ -620,9 +662,13 @@ m_fileCreationMutex.lock(); if (!QFile(m_localFilename).remove()) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl; +#endif } else { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::deleteCacheFile: Deleted cache file \"" << m_localFilename.toStdString() << "\"" << std::endl; +#endif m_localFilename = ""; } @@ -643,12 +689,16 @@ { QMutexLocker locker(&m_mapMutex); +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::createCacheFile: refcount is " << m_refCountMap[m_url] << std::endl; +#endif if (m_refCountMap[m_url] > 0) { m_refCountMap[m_url]++; m_localFilename = m_remoteLocalMap[m_url]; +#ifdef DEBUG_FILE_SOURCE std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl; +#endif m_refCounted = true; return true; } @@ -658,7 +708,9 @@ try { dir = TempDirectory::getInstance()->getSubDirectoryPath("download"); } catch (DirectoryCreationFailed f) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl; +#endif return ""; } @@ -682,7 +734,9 @@ QString filepath(dir.filePath(filename)); +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::createCacheFile: URL is \"" << m_url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl; +#endif QMutexLocker fcLocker(&m_fileCreationMutex); @@ -691,10 +745,11 @@ if (QFileInfo(filepath).exists() || !QFile(filepath).open(QFile::WriteOnly)) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::createCacheFile: Failed to create local file \"" << filepath.toStdString() << "\" for URL \"" << m_url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl; - +#endif if (extension == "") { filename = QString("%1_%2").arg(base).arg(m_count); @@ -706,19 +761,45 @@ if (QFileInfo(filepath).exists() || !QFile(filepath).open(QFile::WriteOnly)) { +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::createCacheFile: ERROR: Failed to create local file \"" << filepath.toStdString() << "\" for URL \"" << m_url.toString().toStdString() << "\" (or file already exists)" << std::endl; +#endif return ""; } } +#ifdef DEBUG_FILE_SOURCE std::cerr << "FileSource::createCacheFile: url " << m_url.toString().toStdString() << " -> local filename " << filepath.toStdString() << std::endl; +#endif m_localFilename = filepath; return false; } + +FileSourceProgressPrinter::FileSourceProgressPrinter() : + m_lastProgress(0) +{ +} + +FileSourceProgressPrinter::~FileSourceProgressPrinter() +{ + if (m_lastProgress > 0 && m_lastProgress != 100) { + std::cerr << "\r\n"; + } +} + +void +FileSourceProgressPrinter::progress(int progress) +{ + if (progress == m_lastProgress) return; + if (progress == 100) std::cerr << "\r\n"; + else std::cerr << "\r" << progress << "%"; + m_lastProgress = progress; +} + diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/FileSource.h --- a/data/fileio/FileSource.h Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/FileSource.h Fri Nov 02 16:50:31 2007 +0000 @@ -237,4 +237,19 @@ static int m_count; }; +class FileSourceProgressPrinter : public QObject +{ + Q_OBJECT + +public: + FileSourceProgressPrinter(); + virtual ~FileSourceProgressPrinter(); + +public slots: + void progress(int); + +protected: + int m_lastProgress; +}; + #endif diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/MP3FileReader.cpp --- a/data/fileio/MP3FileReader.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/MP3FileReader.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -106,10 +106,12 @@ if (decodeMode == DecodeAtOnce) { - m_progress = new QProgressDialog - (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()), - QObject::tr("Stop"), 0, 100); - m_progress->hide(); + if (dynamic_cast(QCoreApplication::instance())) { + m_progress = new QProgressDialog + (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()), + QObject::tr("Stop"), 0, 100); + m_progress->hide(); + } if (!decode(m_filebuffer, m_fileSize)) { m_error = QString("Failed to decode file %1.").arg(m_path); diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/OggVorbisFileReader.cpp --- a/data/fileio/OggVorbisFileReader.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -70,10 +70,12 @@ if (decodeMode == DecodeAtOnce) { - m_progress = new QProgressDialog - (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()), - QObject::tr("Stop"), 0, 100); - m_progress->hide(); + if (dynamic_cast(QCoreApplication::instance())) { + m_progress = new QProgressDialog + (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()), + QObject::tr("Stop"), 0, 100); + m_progress->hide(); + } while (oggz_read(m_oggz, 1024) > 0); diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/QuickTimeFileReader.cpp --- a/data/fileio/QuickTimeFileReader.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/QuickTimeFileReader.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -216,10 +216,12 @@ if (decodeMode == DecodeAtOnce) { - m_progress = new QProgressDialog - (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()), - QObject::tr("Stop"), 0, 100); - m_progress->hide(); + if (dynamic_cast(QCoreApplication::instance())) { + m_progress = new QProgressDialog + (QObject::tr("Decoding %1...").arg(QFileInfo(m_path).fileName()), + QObject::tr("Stop"), 0, 100); + m_progress->hide(); + } while (1) { diff -r bb6e4c46e202 -r 1d656dcda8ef data/fileio/ResamplingWavFileReader.cpp --- a/data/fileio/ResamplingWavFileReader.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/fileio/ResamplingWavFileReader.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -57,10 +57,12 @@ if (resampleMode == ResampleAtOnce) { - m_progress = new QProgressDialog - (QObject::tr("Resampling %1...").arg(QFileInfo(m_path).fileName()), - QObject::tr("Stop"), 0, 100); - m_progress->hide(); + if (dynamic_cast(QCoreApplication::instance())) { + m_progress = new QProgressDialog + (QObject::tr("Resampling %1...").arg(QFileInfo(m_path).fileName()), + QObject::tr("Stop"), 0, 100); + m_progress->hide(); + } size_t blockSize = 16384; size_t total = m_original->getFrameCount(); diff -r bb6e4c46e202 -r 1d656dcda8ef data/model/WaveFileModel.cpp --- a/data/model/WaveFileModel.cpp Thu Nov 01 16:02:01 2007 +0000 +++ b/data/model/WaveFileModel.cpp Fri Nov 02 16:50:31 2007 +0000 @@ -51,7 +51,8 @@ { m_source.waitForData(); if (m_source.isOK()) { - m_reader = AudioFileReaderFactory::createReader(m_source, targetRate); + m_reader = AudioFileReaderFactory::createThreadingReader + (m_source, targetRate); if (m_reader) { std::cerr << "WaveFileModel::WaveFileModel: reader rate: " << m_reader->getSampleRate() << std::endl;