# HG changeset patch # User Chris Cannam # Date 1180023622 0 # Node ID 71dfc6ab3b54529c0475b7f9058456b6bfa975fd # Parent 524bcd89743b72caa6c1e78233df00659f9d2ec3 * Threaded mp3/ogg file reading. Not activated yet, as it doesn't work in context (SV needs to know the duration of its main model at the outset) diff -r 524bcd89743b -r 71dfc6ab3b54 data/fileio/AudioFileReaderFactory.cpp --- a/data/fileio/AudioFileReaderFactory.cpp Thu May 10 12:48:26 2007 +0000 +++ b/data/fileio/AudioFileReaderFactory.cpp Thu May 24 16:20:22 2007 +0000 @@ -73,7 +73,9 @@ MP3FileReader::getSupportedExtensions(extensions); if (extensions.find(ext) != extensions.end()) { reader = new MP3FileReader - (path, true, MP3FileReader::CacheInTemporaryFile); + (path, + MP3FileReader::DecodeAtOnce, + MP3FileReader::CacheInTemporaryFile); } } #endif @@ -84,7 +86,9 @@ OggVorbisFileReader::getSupportedExtensions(extensions); if (extensions.find(ext) != extensions.end()) { reader = new OggVorbisFileReader - (path, true, OggVorbisFileReader::CacheInTemporaryFile); + (path, + OggVorbisFileReader::DecodeAtOnce, + OggVorbisFileReader::CacheInTemporaryFile); } } #endif @@ -119,7 +123,9 @@ #ifdef HAVE_OGGZ #ifdef HAVE_FISHSOUND reader = new OggVorbisFileReader - (path, true, OggVorbisFileReader::CacheInTemporaryFile); + (path, + OggVorbisFileReader::DecodeAtOnce, + OggVorbisFileReader::CacheInTemporaryFile); if (reader->isOK()) return reader; if (reader->getError() != "") { std::cerr << "AudioFileReaderFactory: Ogg file reader error: \"" @@ -134,7 +140,9 @@ #ifdef HAVE_MAD reader = new MP3FileReader - (path, true, MP3FileReader::CacheInTemporaryFile); + (path, + MP3FileReader::DecodeAtOnce, + MP3FileReader::CacheInTemporaryFile); if (reader->isOK()) return reader; if (reader->getError() != "") { std::cerr << "AudioFileReaderFactory: MP3 file reader error: \"" diff -r 524bcd89743b -r 71dfc6ab3b54 data/fileio/CodedAudioFileReader.cpp --- a/data/fileio/CodedAudioFileReader.cpp Thu May 10 12:48:26 2007 +0000 +++ b/data/fileio/CodedAudioFileReader.cpp Thu May 24 16:20:22 2007 +0000 @@ -22,6 +22,7 @@ #include #include +#include CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode) : m_cacheMode(cacheMode), @@ -36,6 +37,8 @@ CodedAudioFileReader::~CodedAudioFileReader() { + QMutexLocker locker(&m_cacheMutex); + if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr); if (m_cacheFileReader) delete m_cacheFileReader; if (m_cacheWriteBuffer) delete[] m_cacheWriteBuffer; @@ -50,6 +53,8 @@ void CodedAudioFileReader::initialiseDecodeCache() { + QMutexLocker locker(&m_cacheMutex); + if (m_cacheMode == CacheInTemporaryFile) { m_cacheWriteBuffer = new float[m_cacheWriteBufferSize * m_channelCount]; @@ -88,6 +93,8 @@ void CodedAudioFileReader::addSampleToDecodeCache(float sample) { + QMutexLocker locker(&m_cacheMutex); + if (!m_initialised) return; switch (m_cacheMode) { @@ -117,6 +124,8 @@ void CodedAudioFileReader::finishDecodeCache() { + QMutexLocker locker(&m_cacheMutex); + Profiler profiler("CodedAudioFileReader::finishDecodeCache", true); if (!m_initialised) { @@ -164,6 +173,9 @@ CodedAudioFileReader::getInterleavedFrames(size_t start, size_t count, SampleBlock &frames) const { + //!!! we want to ensure this doesn't require a lock -- at the + // moment it does need one, but it doesn't have one... + if (!m_initialised) return; switch (m_cacheMode) { diff -r 524bcd89743b -r 71dfc6ab3b54 data/fileio/CodedAudioFileReader.h --- a/data/fileio/CodedAudioFileReader.h Thu May 10 12:48:26 2007 +0000 +++ b/data/fileio/CodedAudioFileReader.h Thu May 24 16:20:22 2007 +0000 @@ -19,6 +19,7 @@ #include "AudioFileReader.h" #include +#include class WavFileReader; @@ -43,6 +44,7 @@ void finishDecodeCache(); bool isDecodeCacheInitialised() const { return m_initialised; } + QMutex m_cacheMutex; CacheMode m_cacheMode; SampleBlock m_data; bool m_initialised; diff -r 524bcd89743b -r 71dfc6ab3b54 data/fileio/MP3FileReader.cpp --- a/data/fileio/MP3FileReader.cpp Thu May 10 12:48:26 2007 +0000 +++ b/data/fileio/MP3FileReader.cpp Thu May 24 16:20:22 2007 +0000 @@ -29,7 +29,7 @@ #include #include -MP3FileReader::MP3FileReader(QString path, bool showProgress, CacheMode mode) : +MP3FileReader::MP3FileReader(QString path, DecodeMode decodeMode, CacheMode mode) : CodedAudioFileReader(mode), m_path(path) { @@ -41,6 +41,8 @@ m_bitrateDenom = 0; m_frameCount = 0; m_cancelled = false; + m_done = false; + m_progress = 0; struct stat stat; if (::stat(path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) { @@ -60,10 +62,10 @@ return; } - unsigned char *filebuffer = 0; + m_filebuffer = 0; try { - filebuffer = new unsigned char[m_fileSize]; + m_filebuffer = new unsigned char[m_fileSize]; } catch (...) { m_error = QString("Out of memory"); ::close(fd); @@ -73,11 +75,11 @@ ssize_t sz = 0; size_t offset = 0; while (offset < m_fileSize) { - sz = ::read(fd, filebuffer + offset, m_fileSize - offset); + sz = ::read(fd, m_filebuffer + offset, m_fileSize - offset); if (sz < 0) { m_error = QString("Read error for file %1 (after %2 bytes)") .arg(path).arg(offset); - delete[] filebuffer; + delete[] m_filebuffer; ::close(fd); return; } else if (sz == 0) { @@ -91,33 +93,59 @@ ::close(fd); - if (showProgress) { + if (decodeMode == DecodeAtOnce) { + m_progress = new QProgressDialog (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()), QObject::tr("Stop"), 0, 100); m_progress->hide(); - } - if (!decode(filebuffer, m_fileSize)) { - m_error = QString("Failed to decode file %1.").arg(path); - delete[] filebuffer; - return; - } - - if (isDecodeCacheInitialised()) finishDecodeCache(); + if (!decode(m_filebuffer, m_fileSize)) { + m_error = QString("Failed to decode file %1.").arg(path); + } + + delete[] m_filebuffer; + m_filebuffer = 0; - if (showProgress) { + if (isDecodeCacheInitialised()) finishDecodeCache(); + delete m_progress; m_progress = 0; + + } else { + + m_decodeThread = new DecodeThread(this); + m_decodeThread->start(); + + while (m_channelCount == 0 && !m_done) { + usleep(10); + } } - - delete[] filebuffer; } MP3FileReader::~MP3FileReader() { + if (m_decodeThread) { + m_decodeThread->wait(); + delete m_decodeThread; + } } +void +MP3FileReader::DecodeThread::run() +{ + if (!m_reader->decode(m_reader->m_filebuffer, m_reader->m_fileSize)) { + m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path); + } + + delete[] m_reader->m_filebuffer; + m_reader->m_filebuffer = 0; + + if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache(); + + m_reader->m_done = true; +} + bool MP3FileReader::decode(void *mm, size_t sz) { @@ -132,6 +160,7 @@ mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); mad_decoder_finish(&decoder); + m_done = true; return true; } @@ -180,16 +209,18 @@ double duration = double(m_fileSize * 8) / bitrate; double elapsed = double(m_frameCount) / m_sampleRate; double percent = ((elapsed * 100.0) / duration); - int progress = int(percent); - if (progress < 1) progress = 1; - if (progress > 99) progress = 99; - if (progress > m_progress->value()) { - m_progress->setValue(progress); - m_progress->show(); - m_progress->raise(); - qApp->processEvents(); - if (m_progress->wasCanceled()) { - m_cancelled = true; + if (m_progress) { + int progress = int(percent); + if (progress < 1) progress = 1; + if (progress > 99) progress = 99; + if (progress > m_progress->value()) { + m_progress->setValue(progress); + m_progress->show(); + m_progress->raise(); + qApp->processEvents(); + if (m_progress->wasCanceled()) { + m_cancelled = true; + } } } } diff -r 524bcd89743b -r 71dfc6ab3b54 data/fileio/MP3FileReader.h --- a/data/fileio/MP3FileReader.h Thu May 10 12:48:26 2007 +0000 +++ b/data/fileio/MP3FileReader.h Thu May 24 16:20:22 2007 +0000 @@ -20,6 +20,7 @@ #include "CodedAudioFileReader.h" +#include "base/Thread.h" #include #include @@ -29,19 +30,31 @@ class MP3FileReader : public CodedAudioFileReader { public: - MP3FileReader(QString path, bool showProgress, CacheMode cacheMode); + enum DecodeMode { + DecodeAtOnce, // decode the file on construction, with progress dialog + DecodeThreaded // decode in a background thread after construction + }; + + MP3FileReader(QString path, DecodeMode decodeMode, CacheMode cacheMode); virtual ~MP3FileReader(); virtual QString getError() const { return m_error; } static void getSupportedExtensions(std::set &extensions); + virtual bool isUpdating() const { + return m_decodeThread && m_decodeThread->isRunning(); + } + protected: QString m_path; QString m_error; size_t m_fileSize; double m_bitrateNum; size_t m_bitrateDenom; + bool m_done; + + unsigned char *m_filebuffer; QProgressDialog *m_progress; bool m_cancelled; @@ -59,6 +72,18 @@ static enum mad_flow input(void *, struct mad_stream *); static enum mad_flow output(void *, struct mad_header const *, struct mad_pcm *); static enum mad_flow error(void *, struct mad_stream *, struct mad_frame *); + + class DecodeThread : public Thread + { + public: + DecodeThread(MP3FileReader *reader) : m_reader(reader) { } + virtual void run(); + + protected: + MP3FileReader *m_reader; + }; + + DecodeThread *m_decodeThread; }; #endif diff -r 524bcd89743b -r 71dfc6ab3b54 data/fileio/OggVorbisFileReader.cpp --- a/data/fileio/OggVorbisFileReader.cpp Thu May 10 12:48:26 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.cpp Thu May 24 16:20:22 2007 +0000 @@ -32,14 +32,16 @@ static int instances = 0; -OggVorbisFileReader::OggVorbisFileReader(QString path, bool showProgress, +OggVorbisFileReader::OggVorbisFileReader(QString path, + DecodeMode decodeMode, CacheMode mode) : CodedAudioFileReader(mode), m_path(path), m_progress(0), m_fileSize(0), m_bytesRead(0), - m_cancelled(false) + m_cancelled(false), + m_decodeThread(0) { m_frameCount = 0; m_channelCount = 0; @@ -52,8 +54,7 @@ QFileInfo info(path); m_fileSize = info.size(); - OGGZ *oggz; - if (!(oggz = oggz_open(path.toLocal8Bit().data(), OGGZ_READ))) { + if (!(m_oggz = oggz_open(path.toLocal8Bit().data(), OGGZ_READ))) { m_error = QString("File %1 is not an OGG file.").arg(path); return; } @@ -62,34 +63,63 @@ m_fishSound = fish_sound_new(FISH_SOUND_DECODE, &fsinfo); fish_sound_set_decoded_callback(m_fishSound, acceptFrames, this); - oggz_set_read_callback(oggz, -1, readPacket, this); + oggz_set_read_callback(m_oggz, -1, readPacket, this); - if (showProgress) { + if (decodeMode == DecodeAtOnce) { + m_progress = new QProgressDialog (QObject::tr("Decoding %1...").arg(QFileInfo(path).fileName()), QObject::tr("Stop"), 0, 100); m_progress->hide(); - } - while (oggz_read(oggz, 1024) > 0); + while (oggz_read(m_oggz, 1024) > 0); + + fish_sound_delete(m_fishSound); + m_fishSound = 0; + oggz_close(m_oggz); + m_oggz = 0; - fish_sound_delete(m_fishSound); - m_fishSound = 0; - oggz_close(oggz); + if (isDecodeCacheInitialised()) finishDecodeCache(); - if (isDecodeCacheInitialised()) finishDecodeCache(); + if (decodeMode == DecodeAtOnce) { + delete m_progress; + m_progress = 0; + } - if (showProgress) { - delete m_progress; - m_progress = 0; + } else { + + while (oggz_read(m_oggz, 1024) > 0 && + m_channelCount == 0); + + if (m_channelCount > 0) { + m_decodeThread = new DecodeThread(this); + m_decodeThread->start(); + } } } OggVorbisFileReader::~OggVorbisFileReader() { std::cerr << "OggVorbisFileReader::~OggVorbisFileReader(" << m_path.toLocal8Bit().data() << "): now have " << (--instances) << " instances" << std::endl; + if (m_decodeThread) { + m_decodeThread->wait(); + delete m_decodeThread; + } } +void +OggVorbisFileReader::DecodeThread::run() +{ + while (oggz_read(m_reader->m_oggz, 1024) > 0); + + fish_sound_delete(m_reader->m_fishSound); + m_reader->m_fishSound = 0; + oggz_close(m_reader->m_oggz); + m_reader->m_oggz = 0; + + if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache(); +} + int OggVorbisFileReader::readPacket(OGGZ *, ogg_packet *packet, long, void *data) { @@ -144,7 +174,6 @@ for (long i = 0; i < nframes; ++i) { for (size_t c = 0; c < reader->m_channelCount; ++c) { reader->addSampleToDecodeCache(frames[c][i]); -// reader->m_data.push_back(frames[c][i]); } } diff -r 524bcd89743b -r 71dfc6ab3b54 data/fileio/OggVorbisFileReader.h --- a/data/fileio/OggVorbisFileReader.h Thu May 10 12:48:26 2007 +0000 +++ b/data/fileio/OggVorbisFileReader.h Thu May 24 16:20:22 2007 +0000 @@ -21,6 +21,7 @@ #include "CodedAudioFileReader.h" +#include "base/Thread.h" #include #include @@ -31,17 +32,28 @@ class OggVorbisFileReader : public CodedAudioFileReader { public: - OggVorbisFileReader(QString path, bool showProgress, CacheMode cacheMode); + enum DecodeMode { + DecodeAtOnce, // decode the file on construction, with progress dialog + DecodeThreaded // decode in a background thread after construction + }; + + OggVorbisFileReader(QString path, DecodeMode decodeMode, + CacheMode cacheMode); virtual ~OggVorbisFileReader(); virtual QString getError() const { return m_error; } static void getSupportedExtensions(std::set &extensions); + virtual bool isUpdating() const { + return m_decodeThread && m_decodeThread->isRunning(); + } + protected: QString m_path; QString m_error; + OGGZ *m_oggz; FishSound *m_fishSound; QProgressDialog *m_progress; size_t m_fileSize; @@ -50,6 +62,18 @@ static int readPacket(OGGZ *, ogg_packet *, long, void *); static int acceptFrames(FishSound *, float **, long, void *); + + class DecodeThread : public Thread + { + public: + DecodeThread(OggVorbisFileReader *reader) : m_reader(reader) { } + virtual void run(); + + protected: + OggVorbisFileReader *m_reader; + }; + + DecodeThread *m_decodeThread; }; #endif diff -r 524bcd89743b -r 71dfc6ab3b54 system/Init.cpp --- a/system/Init.cpp Thu May 10 12:48:26 2007 +0000 +++ b/system/Init.cpp Thu May 24 16:20:22 2007 +0000 @@ -84,9 +84,9 @@ qApp->setFont(fn); #else #ifdef Q_WS_X11 - QFont fn = qApp->font(); - fn.setPointSize(fn.pointSize() + 2); - qApp->setFont(fn); +// QFont fn = qApp->font(); +// fn.setPointSize(fn.pointSize() + 2); +// qApp->setFont(fn); #endif #endif }