# HG changeset patch # User Chris Cannam # Date 1374072001 -3600 # Node ID f0558e69a0740d55f85a3e250b01cefc78e83b2b # Parent 06c64a1c6785c3724b7bf37c84eb8532bfff65d7 Rename Resampling- to DecodingWavFileReader, and use it whenever we have an audio file that is not quickly seekable using libsndfile. Avoids very slow performance when analysing ogg files. diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/AudioFileReader.h --- a/data/fileio/AudioFileReader.h Mon Jul 08 14:49:28 2013 +0100 +++ b/data/fileio/AudioFileReader.h Wed Jul 17 15:40:01 2013 +0100 @@ -65,6 +65,13 @@ typedef std::map TagMap; virtual TagMap getTags() const { return TagMap(); } + /** + * Return true if this file supports fast seek and random + * access. Typically this will be true for uncompressed formats + * and false for compressed ones. + */ + virtual bool isQuicklySeekable() const = 0; + /** * Return interleaved samples for count frames from index start. * The resulting sample block will contain count * diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/AudioFileReaderFactory.cpp --- a/data/fileio/AudioFileReaderFactory.cpp Mon Jul 08 14:49:28 2013 +0100 +++ b/data/fileio/AudioFileReaderFactory.cpp Wed Jul 17 15:40:01 2013 +0100 @@ -16,7 +16,7 @@ #include "AudioFileReaderFactory.h" #include "WavFileReader.h" -#include "ResamplingWavFileReader.h" +#include "DecodingWavFileReader.h" #include "OggVorbisFileReader.h" #include "MP3FileReader.h" #include "QuickTimeFileReader.h" @@ -98,20 +98,22 @@ reader = new WavFileReader(source); - if (targetRate != 0 && - reader->isOK() && - reader->getSampleRate() != targetRate) { + int fileRate = reader->getSampleRate(); - SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", creating resampling reader" << endl; + if (reader->isOK() && + (!reader->isQuicklySeekable() || + (targetRate != 0 && fileRate != targetRate))) { + + SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", seekable " << reader->isQuicklySeekable() << ", creating decoding reader" << endl; delete reader; - reader = new ResamplingWavFileReader + reader = new DecodingWavFileReader (source, threading ? - ResamplingWavFileReader::ResampleThreaded : - ResamplingWavFileReader::ResampleAtOnce, - ResamplingWavFileReader::CacheInTemporaryFile, - targetRate, + DecodingWavFileReader::ResampleThreaded : + DecodingWavFileReader::ResampleAtOnce, + DecodingWavFileReader::CacheInTemporaryFile, + targetRate ? targetRate : fileRate, reporter); if (!reader->isOK()) { delete reader; @@ -209,20 +211,22 @@ reader = new WavFileReader(source); - if (targetRate != 0 && - reader->isOK() && - reader->getSampleRate() != targetRate) { + int fileRate = reader->getSampleRate(); - SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", creating resampling reader" << endl; + if (reader->isOK() && + (!reader->isQuicklySeekable() || + (targetRate != 0 && fileRate != targetRate))) { + + SVDEBUG << "AudioFileReaderFactory::createReader: WAV file rate: " << reader->getSampleRate() << ", seekable " << reader->isQuicklySeekable() << ", creating decoding reader" << endl; delete reader; - reader = new ResamplingWavFileReader + reader = new DecodingWavFileReader (source, threading ? - ResamplingWavFileReader::ResampleThreaded : - ResamplingWavFileReader::ResampleAtOnce, - ResamplingWavFileReader::CacheInTemporaryFile, - targetRate, + DecodingWavFileReader::ResampleThreaded : + DecodingWavFileReader::ResampleAtOnce, + DecodingWavFileReader::CacheInTemporaryFile, + targetRate ? targetRate : fileRate, reporter); } diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/CodedAudioFileReader.h --- a/data/fileio/CodedAudioFileReader.h Mon Jul 08 14:49:28 2013 +0100 +++ b/data/fileio/CodedAudioFileReader.h Wed Jul 17 15:40:01 2013 +0100 @@ -43,6 +43,9 @@ virtual size_t getNativeRate() const { return m_fileRate; } + /// Intermediate cache means all CodedAudioFileReaders are quickly seekable + virtual bool isQuicklySeekable() const { return true; } + signals: void progress(int); diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/DecodingWavFileReader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/DecodingWavFileReader.cpp Wed Jul 17 15:40:01 2013 +0100 @@ -0,0 +1,192 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#include "DecodingWavFileReader.h" + +#include "WavFileReader.h" +#include "base/Profiler.h" +#include "base/ProgressReporter.h" + +#include + +DecodingWavFileReader::DecodingWavFileReader(FileSource source, + ResampleMode resampleMode, + CacheMode mode, + size_t targetRate, + ProgressReporter *reporter) : + CodedAudioFileReader(mode, targetRate), + m_source(source), + m_path(source.getLocalFilename()), + m_cancelled(false), + m_processed(0), + m_completion(0), + m_original(0), + m_reporter(reporter), + m_decodeThread(0) +{ + m_channelCount = 0; + m_fileRate = 0; + + SVDEBUG << "DecodingWavFileReader::DecodingWavFileReader(\"" + << m_path << "\"): rate " << targetRate << endl; + + Profiler profiler("DecodingWavFileReader::DecodingWavFileReader", true); + + m_original = new WavFileReader(m_path); + if (!m_original->isOK()) { + m_error = m_original->getError(); + return; + } + + m_channelCount = m_original->getChannelCount(); + m_fileRate = m_original->getSampleRate(); + + initialiseDecodeCache(); + + if (resampleMode == ResampleAtOnce) { + + if (m_reporter) { + connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); + m_reporter->setMessage + (tr("Decoding %1...").arg(QFileInfo(m_path).fileName())); + } + + size_t blockSize = 16384; + size_t total = m_original->getFrameCount(); + + SampleBlock block; + + for (size_t i = 0; i < total; i += blockSize) { + + size_t count = blockSize; + if (i + count > total) count = total - i; + + m_original->getInterleavedFrames(i, count, block); + addBlock(block); + + if (m_cancelled) break; + } + + if (isDecodeCacheInitialised()) finishDecodeCache(); + endSerialised(); + + if (m_reporter) m_reporter->setProgress(100); + + delete m_original; + m_original = 0; + + } else { + + if (m_reporter) m_reporter->setProgress(100); + + m_decodeThread = new DecodeThread(this); + m_decodeThread->start(); + } +} + +DecodingWavFileReader::~DecodingWavFileReader() +{ + if (m_decodeThread) { + m_cancelled = true; + m_decodeThread->wait(); + delete m_decodeThread; + } + + delete m_original; +} + +void +DecodingWavFileReader::cancelled() +{ + m_cancelled = true; +} + +void +DecodingWavFileReader::DecodeThread::run() +{ + if (m_reader->m_cacheMode == CacheInTemporaryFile) { + m_reader->startSerialised("DecodingWavFileReader::Decode"); + } + + size_t blockSize = 16384; + size_t total = m_reader->m_original->getFrameCount(); + + SampleBlock block; + + for (size_t i = 0; i < total; i += blockSize) { + + size_t count = blockSize; + if (i + count > total) count = total - i; + + m_reader->m_original->getInterleavedFrames(i, count, block); + m_reader->addBlock(block); + + if (m_reader->m_cancelled) break; + } + + if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache(); + m_reader->m_completion = 100; + + m_reader->endSerialised(); + + delete m_reader->m_original; + m_reader->m_original = 0; +} + +void +DecodingWavFileReader::addBlock(const SampleBlock &frames) +{ + addSamplesToDecodeCache(frames); + + m_processed += frames.size(); + + float ratio = float(m_sampleRate) / float(m_fileRate); + + int progress = lrint((float(m_processed) * ratio * 100) / + float(m_original->getFrameCount())); + + if (progress > 99) progress = 99; + m_completion = progress; + + if (m_reporter) { + m_reporter->setProgress(progress); + } +} + +void +DecodingWavFileReader::getSupportedExtensions(std::set &extensions) +{ + WavFileReader::getSupportedExtensions(extensions); +} + +bool +DecodingWavFileReader::supportsExtension(QString extension) +{ + return WavFileReader::supportsExtension(extension); +} + +bool +DecodingWavFileReader::supportsContentType(QString type) +{ + return WavFileReader::supportsContentType(type); +} + +bool +DecodingWavFileReader::supports(FileSource &source) +{ + return WavFileReader::supports(source); +} + + diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/DecodingWavFileReader.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/fileio/DecodingWavFileReader.h Wed Jul 17 15:40:01 2013 +0100 @@ -0,0 +1,87 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + This file copyright 2007 QMUL. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _DECODING_WAV_FILE_READER_H_ +#define _DECODING_WAV_FILE_READER_H_ + +#include "CodedAudioFileReader.h" + +#include "base/Thread.h" + +#include + +class WavFileReader; +class ProgressReporter; + +class DecodingWavFileReader : public CodedAudioFileReader +{ + Q_OBJECT +public: + enum ResampleMode { + ResampleAtOnce, // resample the file on construction, with progress dialog + ResampleThreaded // resample in a background thread after construction + }; + + DecodingWavFileReader(FileSource source, + ResampleMode resampleMode, + CacheMode cacheMode, + size_t targetRate = 0, + ProgressReporter *reporter = 0); + virtual ~DecodingWavFileReader(); + + virtual QString getError() const { return m_error; } + virtual QString getLocation() const { return m_source.getLocation(); } + static void getSupportedExtensions(std::set &extensions); + static bool supportsExtension(QString ext); + static bool supportsContentType(QString type); + static bool supports(FileSource &source); + + virtual int getDecodeCompletion() const { return m_completion; } + + virtual bool isUpdating() const { + return m_decodeThread && m_decodeThread->isRunning(); + } + +public slots: + void cancelled(); + +protected: + FileSource m_source; + QString m_path; + QString m_error; + bool m_cancelled; + size_t m_processed; + int m_completion; + + WavFileReader *m_original; + ProgressReporter *m_reporter; + + void addBlock(const SampleBlock &frames); + + class DecodeThread : public Thread + { + public: + DecodeThread(DecodingWavFileReader *reader) : m_reader(reader) { } + virtual void run(); + + protected: + DecodingWavFileReader *m_reader; + }; + + DecodeThread *m_decodeThread; +}; + +#endif + diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/ResamplingWavFileReader.cpp --- a/data/fileio/ResamplingWavFileReader.cpp Mon Jul 08 14:49:28 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,192 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2007 QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#include "ResamplingWavFileReader.h" - -#include "WavFileReader.h" -#include "base/Profiler.h" -#include "base/ProgressReporter.h" - -#include - -ResamplingWavFileReader::ResamplingWavFileReader(FileSource source, - ResampleMode resampleMode, - CacheMode mode, - size_t targetRate, - ProgressReporter *reporter) : - CodedAudioFileReader(mode, targetRate), - m_source(source), - m_path(source.getLocalFilename()), - m_cancelled(false), - m_processed(0), - m_completion(0), - m_original(0), - m_reporter(reporter), - m_decodeThread(0) -{ - m_channelCount = 0; - m_fileRate = 0; - - SVDEBUG << "ResamplingWavFileReader::ResamplingWavFileReader(\"" - << m_path << "\"): rate " << targetRate << endl; - - Profiler profiler("ResamplingWavFileReader::ResamplingWavFileReader", true); - - m_original = new WavFileReader(m_path); - if (!m_original->isOK()) { - m_error = m_original->getError(); - return; - } - - m_channelCount = m_original->getChannelCount(); - m_fileRate = m_original->getSampleRate(); - - initialiseDecodeCache(); - - if (resampleMode == ResampleAtOnce) { - - if (m_reporter) { - connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); - m_reporter->setMessage - (tr("Resampling %1...").arg(QFileInfo(m_path).fileName())); - } - - size_t blockSize = 16384; - size_t total = m_original->getFrameCount(); - - SampleBlock block; - - for (size_t i = 0; i < total; i += blockSize) { - - size_t count = blockSize; - if (i + count > total) count = total - i; - - m_original->getInterleavedFrames(i, count, block); - addBlock(block); - - if (m_cancelled) break; - } - - if (isDecodeCacheInitialised()) finishDecodeCache(); - endSerialised(); - - if (m_reporter) m_reporter->setProgress(100); - - delete m_original; - m_original = 0; - - } else { - - if (m_reporter) m_reporter->setProgress(100); - - m_decodeThread = new DecodeThread(this); - m_decodeThread->start(); - } -} - -ResamplingWavFileReader::~ResamplingWavFileReader() -{ - if (m_decodeThread) { - m_cancelled = true; - m_decodeThread->wait(); - delete m_decodeThread; - } - - delete m_original; -} - -void -ResamplingWavFileReader::cancelled() -{ - m_cancelled = true; -} - -void -ResamplingWavFileReader::DecodeThread::run() -{ - if (m_reader->m_cacheMode == CacheInTemporaryFile) { - m_reader->startSerialised("ResamplingWavFileReader::Decode"); - } - - size_t blockSize = 16384; - size_t total = m_reader->m_original->getFrameCount(); - - SampleBlock block; - - for (size_t i = 0; i < total; i += blockSize) { - - size_t count = blockSize; - if (i + count > total) count = total - i; - - m_reader->m_original->getInterleavedFrames(i, count, block); - m_reader->addBlock(block); - - if (m_reader->m_cancelled) break; - } - - if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache(); - m_reader->m_completion = 100; - - m_reader->endSerialised(); - - delete m_reader->m_original; - m_reader->m_original = 0; -} - -void -ResamplingWavFileReader::addBlock(const SampleBlock &frames) -{ - addSamplesToDecodeCache(frames); - - m_processed += frames.size(); - - float ratio = float(m_sampleRate) / float(m_fileRate); - - int progress = lrint((float(m_processed) * ratio * 100) / - float(m_original->getFrameCount())); - - if (progress > 99) progress = 99; - m_completion = progress; - - if (m_reporter) { - m_reporter->setProgress(progress); - } -} - -void -ResamplingWavFileReader::getSupportedExtensions(std::set &extensions) -{ - WavFileReader::getSupportedExtensions(extensions); -} - -bool -ResamplingWavFileReader::supportsExtension(QString extension) -{ - return WavFileReader::supportsExtension(extension); -} - -bool -ResamplingWavFileReader::supportsContentType(QString type) -{ - return WavFileReader::supportsContentType(type); -} - -bool -ResamplingWavFileReader::supports(FileSource &source) -{ - return WavFileReader::supports(source); -} - - diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/ResamplingWavFileReader.h --- a/data/fileio/ResamplingWavFileReader.h Mon Jul 08 14:49:28 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +0,0 @@ -/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ - -/* - Sonic Visualiser - An audio file viewer and annotation editor. - Centre for Digital Music, Queen Mary, University of London. - This file copyright 2007 QMUL. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. See the file - COPYING included with this distribution for more information. -*/ - -#ifndef _RESAMPLING_WAV_FILE_READER_H_ -#define _RESAMPLING_WAV_FILE_READER_H_ - -#include "CodedAudioFileReader.h" - -#include "base/Thread.h" - -#include - -class WavFileReader; -class ProgressReporter; - -class ResamplingWavFileReader : public CodedAudioFileReader -{ - Q_OBJECT -public: - enum ResampleMode { - ResampleAtOnce, // resample the file on construction, with progress dialog - ResampleThreaded // resample in a background thread after construction - }; - - ResamplingWavFileReader(FileSource source, - ResampleMode resampleMode, - CacheMode cacheMode, - size_t targetRate = 0, - ProgressReporter *reporter = 0); - virtual ~ResamplingWavFileReader(); - - virtual QString getError() const { return m_error; } - virtual QString getLocation() const { return m_source.getLocation(); } - static void getSupportedExtensions(std::set &extensions); - static bool supportsExtension(QString ext); - static bool supportsContentType(QString type); - static bool supports(FileSource &source); - - virtual int getDecodeCompletion() const { return m_completion; } - - virtual bool isUpdating() const { - return m_decodeThread && m_decodeThread->isRunning(); - } - -public slots: - void cancelled(); - -protected: - FileSource m_source; - QString m_path; - QString m_error; - bool m_cancelled; - size_t m_processed; - int m_completion; - - WavFileReader *m_original; - ProgressReporter *m_reporter; - - void addBlock(const SampleBlock &frames); - - class DecodeThread : public Thread - { - public: - DecodeThread(ResamplingWavFileReader *reader) : m_reader(reader) { } - virtual void run(); - - protected: - ResamplingWavFileReader *m_reader; - }; - - DecodeThread *m_decodeThread; -}; - -#endif - diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/WavFileReader.cpp --- a/data/fileio/WavFileReader.cpp Mon Jul 08 14:49:28 2013 +0100 +++ b/data/fileio/WavFileReader.cpp Wed Jul 17 15:40:01 2013 +0100 @@ -24,6 +24,7 @@ m_file(0), m_source(source), m_path(source.getLocalFilename()), + m_seekable(false), m_buffer(0), m_bufsiz(0), m_lastStart(0), @@ -54,12 +55,26 @@ } if (m_fileInfo.channels > 0) { + m_frameCount = m_fileInfo.frames; m_channelCount = m_fileInfo.channels; m_sampleRate = m_fileInfo.samplerate; + + m_seekable = (m_fileInfo.seekable != 0); + + // Our m_seekable reports whether a file is rapidly seekable, + // so things like Ogg don't qualify. We cautiously report + // every file type of "at least" the historical period of Ogg + // or FLAC as non-seekable. + int type = m_fileInfo.format & SF_FORMAT_TYPEMASK; +// std::cerr << "WavFileReader: format type is " << type << " (flac, ogg are " << SF_FORMAT_FLAC << ", " << SF_FORMAT_OGG << ")" << std::endl; + if (type >= SF_FORMAT_FLAC || type >= SF_FORMAT_OGG) { +// std::cerr << "WavFileReader: Recording as non-seekable" << std::endl; + m_seekable = false; + } } -// std::cerr << "WavFileReader: Frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << std::endl; +// std::cerr << "WavFileReader: Frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << ", seekable " << m_seekable << std::endl; } diff -r 06c64a1c6785 -r f0558e69a074 data/fileio/WavFileReader.h --- a/data/fileio/WavFileReader.h Mon Jul 08 14:49:28 2013 +0100 +++ b/data/fileio/WavFileReader.h Wed Jul 17 15:40:01 2013 +0100 @@ -23,6 +23,16 @@ #include +/** + * Reader for audio files using libsndfile. + * + * This is typically intended for seekable file types that can be read + * directly (e.g. WAV, AIFF etc). + * + * Compressed files supported by libsndfile (e.g. Ogg, FLAC) should + * normally be read using DecodingWavFileReader instead (which decodes + * to an intermediate cached file). + */ class WavFileReader : public AudioFileReader { public: @@ -32,6 +42,8 @@ virtual QString getLocation() const { return m_source.getLocation(); } virtual QString getError() const { return m_error; } + virtual bool isQuicklySeekable() const { return m_seekable; } + /** * Must be safe to call from multiple threads with different * arguments on the same object at the same time. @@ -59,6 +71,8 @@ QString m_path; QString m_error; + bool m_seekable; + mutable QMutex m_mutex; mutable float *m_buffer; mutable size_t m_bufsiz;