changeset 1583:c8fad3c14a2b bqaudiostream

Start wiring in BQAudioStream stuff
author Chris Cannam
date Thu, 06 Dec 2018 12:50:28 +0000
parents 07f23b90701a
children ff18abb88563
files data/fileio/AudioFileReaderFactory.cpp data/fileio/BQAFileReader.cpp data/fileio/BQAFileReader.h data/fileio/CoreAudioFileReader.cpp data/fileio/CoreAudioFileReader.h data/fileio/OggVorbisFileReader.cpp data/fileio/OggVorbisFileReader.h files.pri
diffstat 8 files changed, 323 insertions(+), 793 deletions(-) [+]
line wrap: on
line diff
--- a/data/fileio/AudioFileReaderFactory.cpp	Wed Nov 14 15:46:35 2018 +0000
+++ b/data/fileio/AudioFileReaderFactory.cpp	Thu Dec 06 12:50:28 2018 +0000
@@ -17,9 +17,8 @@
 
 #include "WavFileReader.h"
 #include "DecodingWavFileReader.h"
-#include "OggVorbisFileReader.h"
 #include "MP3FileReader.h"
-#include "CoreAudioFileReader.h"
+#include "BQAFileReader.h"
 #include "AudioFileSizeEstimator.h"
 
 #include "base/StorageAdviser.h"
@@ -28,26 +27,21 @@
 #include <QFileInfo>
 #include <iostream>
 
+using namespace std;
+
 QString
 AudioFileReaderFactory::getKnownExtensions()
 {
-    std::set<QString> extensions;
+    set<QString> extensions;
 
     WavFileReader::getSupportedExtensions(extensions);
 #ifdef HAVE_MAD
     MP3FileReader::getSupportedExtensions(extensions);
 #endif
-#ifdef HAVE_OGGZ
-#ifdef HAVE_FISHSOUND
-    OggVorbisFileReader::getSupportedExtensions(extensions);
-#endif
-#endif
-#ifdef HAVE_COREAUDIO
-    CoreAudioFileReader::getSupportedExtensions(extensions);
-#endif
+    BQAFileReader::getSupportedExtensions(extensions);
 
     QString rv;
-    for (std::set<QString>::const_iterator i = extensions.begin();
+    for (set<QString>::const_iterator i = extensions.begin();
          i != extensions.end(); ++i) {
         if (i != extensions.begin()) rv += " ";
         rv += "*." + *i;
@@ -126,27 +120,41 @@
             SVDEBUG << "AudioFileReaderFactory: Source not officially handled by any reader, trying again with each reader in turn"
                     << endl;
         }
-    
-#ifdef HAVE_OGGZ
-#ifdef HAVE_FISHSOUND
-        // If we have the "real" Ogg reader, use that first. Otherwise
-        // the WavFileReader will likely accept Ogg files (as
-        // libsndfile supports them) but it has no ability to return
-        // file metadata, so we get a slightly less useful result.
-        if (anyReader || OggVorbisFileReader::supports(source)) {
 
-            reader = new OggVorbisFileReader
-                (source, decodeMode, cacheMode, targetRate, normalised, reporter);
+#ifdef HAVE_MAD
+        if (anyReader || MP3FileReader::supports(source)) {
+
+            MP3FileReader::GaplessMode gapless =
+                params.gaplessMode == GaplessMode::Gapless ?
+                MP3FileReader::GaplessMode::Gapless :
+                MP3FileReader::GaplessMode::Gappy;
+            
+            reader = new MP3FileReader
+                (source, decodeMode, cacheMode, gapless,
+                 targetRate, normalised, reporter);
 
             if (reader->isOK()) {
-                SVDEBUG << "AudioFileReaderFactory: Ogg file reader is OK, returning it" << endl;
+                SVDEBUG << "AudioFileReaderFactory: MP3 file reader is OK, returning it" << endl;
                 return reader;
             } else {
                 delete reader;
             }
         }
 #endif
-#endif
+
+        if (anyReader || BQAFileReader::supports(source)) {
+
+            reader = new BQAFileReader
+                (source, decodeMode, cacheMode, 
+                 targetRate, normalised, reporter);
+
+            if (reader->isOK()) {
+                SVDEBUG << "AudioFileReaderFactory: BQA reader is OK, returning it" << endl;
+                return reader;
+            } else {
+                delete reader;
+            }
+        }
 
         if (anyReader || WavFileReader::supports(source)) {
 
@@ -178,43 +186,6 @@
                 delete reader;
             }
         }
-
-#ifdef HAVE_MAD
-        if (anyReader || MP3FileReader::supports(source)) {
-
-            MP3FileReader::GaplessMode gapless =
-                params.gaplessMode == GaplessMode::Gapless ?
-                MP3FileReader::GaplessMode::Gapless :
-                MP3FileReader::GaplessMode::Gappy;
-            
-            reader = new MP3FileReader
-                (source, decodeMode, cacheMode, gapless,
-                 targetRate, normalised, reporter);
-
-            if (reader->isOK()) {
-                SVDEBUG << "AudioFileReaderFactory: MP3 file reader is OK, returning it" << endl;
-                return reader;
-            } else {
-                delete reader;
-            }
-        }
-#endif
-
-#ifdef HAVE_COREAUDIO
-        if (anyReader || CoreAudioFileReader::supports(source)) {
-
-            reader = new CoreAudioFileReader
-                (source, decodeMode, cacheMode, targetRate, normalised, reporter);
-
-            if (reader->isOK()) {
-                SVDEBUG << "AudioFileReaderFactory: CoreAudio reader is OK, returning it" << endl;
-                return reader;
-            } else {
-                delete reader;
-            }
-        }
-#endif
-
     }
     
     SVCERR << "AudioFileReaderFactory::Failed to create a reader for "
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fileio/BQAFileReader.cpp	Thu Dec 06 12:50:28 2018 +0000
@@ -0,0 +1,200 @@
+/* -*- 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 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 "BQAFileReader.h"
+
+#include <bqaudiostream/AudioReadStreamFactory.h>
+#include <bqaudiostream/AudioReadStream.h>
+#include <bqaudiostream/Exceptions.h>
+
+#include "base/Profiler.h"
+#include "base/ProgressReporter.h"
+
+#include <QFileInfo>
+
+using namespace std;
+
+BQAFileReader::BQAFileReader(FileSource source,
+			     DecodeMode decodeMode,
+			     CacheMode mode,
+			     sv_samplerate_t targetRate,
+			     bool normalised,
+			     ProgressReporter *reporter) :
+    CodedAudioFileReader(mode, targetRate, normalised),
+    m_source(source),
+    m_path(source.getLocalFilename()),
+    m_cancelled(false),
+    m_completion(0),
+    m_reporter(reporter),
+    m_decodeThread(0)
+{
+    SVDEBUG << "BQAFileReader: local path: \"" << m_path
+            << "\", decode mode: " << decodeMode << " ("
+            << (decodeMode == DecodeAtOnce ? "DecodeAtOnce" : "DecodeThreaded")
+            << ")" << endl;
+
+    m_channelCount = 0;
+    m_fileRate = 0;
+
+    Profiler profiler("BQAFileReader::BQAFileReader");
+
+    try {
+	m_stream = breakfastquay::AudioReadStreamFactory::createReadStream
+	    (m_path.toUtf8().data());
+    } catch (const std::exception &e) {
+	m_error = e.what();
+	m_stream = 0;
+	return;
+    }
+
+    m_channelCount = m_stream->getChannelCount();
+    m_fileRate = m_stream->getSampleRate();
+
+    initialiseDecodeCache();
+
+    if (decodeMode == DecodeAtOnce) {
+
+        if (m_reporter) {
+            connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
+            m_reporter->setMessage
+                (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
+        }
+
+        sv_frame_t blockSize = 65536;
+        floatvec_t block(blockSize * m_channelCount, 0.f);
+
+	while (true) {
+	    try {
+		sv_frame_t retrieved = 
+		    m_stream->getInterleavedFrames(blockSize, block.data());
+
+		addSamplesToDecodeCache(block.data(), retrieved);
+
+		if (retrieved < blockSize) {
+		    break;
+		}
+	    } catch (const breakfastquay::InvalidFileFormat &f) {
+		m_error = f.what();
+		break;
+	    }
+
+	    if (m_cancelled) break;
+        }
+
+        if (isDecodeCacheInitialised()) finishDecodeCache();
+        endSerialised();
+
+        if (m_reporter) m_reporter->setProgress(100);
+
+        delete m_stream;
+        m_stream = 0;
+
+    } else {
+
+        if (m_reporter) m_reporter->setProgress(100);
+
+        m_decodeThread = new DecodeThread(this);
+        m_decodeThread->start();
+    }
+
+//!!! todo metadata - maker, title, tags
+}
+
+BQAFileReader::~BQAFileReader()
+{
+    if (m_decodeThread) {
+        m_cancelled = true;
+        m_decodeThread->wait();
+        delete m_decodeThread;
+    }
+    
+    delete m_stream;
+}
+
+void
+BQAFileReader::cancelled()
+{
+    m_cancelled = true;
+}
+
+void
+BQAFileReader::DecodeThread::run()
+{
+    if (m_reader->m_cacheMode == CacheInTemporaryFile) {
+        m_reader->startSerialised("BQAFileReader::Decode");
+    }
+
+    sv_frame_t blockSize = 65536;
+    floatvec_t block(blockSize * m_reader->getChannelCount(), 0.f);
+    
+    while (true) {
+	try {
+	    sv_frame_t retrieved = 
+		m_reader->m_stream->getInterleavedFrames
+		(blockSize, block.data());
+
+	    m_reader->addSamplesToDecodeCache(block.data(), retrieved);
+
+	    if (retrieved < blockSize) {
+		break;
+	    }
+	} catch (const breakfastquay::InvalidFileFormat &f) {
+	    m_reader->m_error = f.what();
+	    break;
+	}
+
+	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_stream;
+    m_reader->m_stream = 0;
+} 
+
+void
+BQAFileReader::getSupportedExtensions(set<QString> &extensions)
+{
+    vector<string> exts = 
+        breakfastquay::AudioReadStreamFactory::getSupportedFileExtensions();
+    for (auto e: exts) {
+        extensions.insert(QString::fromUtf8(e.c_str()));
+    }
+}
+
+bool
+BQAFileReader::supportsExtension(QString extension)
+{
+    set<QString> extensions;
+    getSupportedExtensions(extensions);
+    return (extensions.find(extension.toLower()) != extensions.end());
+}
+
+bool
+BQAFileReader::supportsContentType(QString type)
+{
+//!!! todo
+    return false;
+}
+
+bool
+BQAFileReader::supports(FileSource &source)
+{
+    return (supportsExtension(source.getExtension()) ||
+            supportsContentType(source.getContentType()));
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fileio/BQAFileReader.h	Thu Dec 06 12:50:28 2018 +0000
@@ -0,0 +1,89 @@
+/* -*- 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 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 SV_BQA_FILE_READER_H
+#define SV_BQA_FILE_READER_H
+
+#include <bqaudiostream/AudioReadStreamFactory.h>
+
+#include "CodedAudioFileReader.h"
+#include "base/Thread.h"
+
+#include <set>
+
+class ProgressReporter;
+
+/**
+ * Audio file reader using bqaudiostream library AudioReadStream
+ * classes.
+ */
+class BQAFileReader : public CodedAudioFileReader
+{
+    Q_OBJECT
+
+public:
+    BQAFileReader(FileSource source,
+                  DecodeMode decodeMode,
+                  CacheMode cacheMode,
+                  sv_samplerate_t targetRate = 0,
+                  bool normalised = false,
+                  ProgressReporter *reporter = 0);
+    virtual ~BQAFileReader();
+
+    virtual QString getError() const { return m_error; }
+    virtual QString getLocation() const { return m_source.getLocation(); }
+    virtual QString getTitle() const { return m_title; }
+    virtual QString getMaker() const { return m_maker; }
+    virtual TagMap getTags() const { return m_tags; }
+    
+    static void getSupportedExtensions(std::set<QString> &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;
+    QString m_title;
+    QString m_maker;
+    TagMap m_tags;
+
+    breakfastquay::AudioReadStream *m_stream;
+
+    bool m_cancelled;
+    int m_completion;
+    ProgressReporter *m_reporter;
+    
+    class DecodeThread : public Thread {
+    public:
+        DecodeThread(BQAFileReader *reader) : m_reader(reader) { }
+        virtual void run();
+    protected:
+	BQAFileReader *m_reader;
+    };
+    DecodeThread *m_decodeThread;
+};
+
+#endif
+
--- a/data/fileio/CoreAudioFileReader.cpp	Wed Nov 14 15:46:35 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,251 +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 2006-2012 Chris Cannam and 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.
- */
-
-#ifdef HAVE_COREAUDIO
-
-#include "CoreAudioFileReader.h"
-#include "base/Profiler.h"
-#include "base/ProgressReporter.h"
-#include "system/System.h"
-
-#include <QFileInfo>
-
-#if !defined(__COREAUDIO_USE_FLAT_INCLUDES__)
-#include <AudioToolbox/AudioToolbox.h>
-#include <AudioToolbox/ExtendedAudioFile.h>
-#else
-#include "AudioToolbox.h"
-#include "ExtendedAudioFile.h"
-#endif
-
-class CoreAudioFileReader::D
-{
-public:
-    D() : blockSize(1024), valid(false) { }
-
-    ExtAudioFileRef file;
-    AudioBufferList buffer;
-    OSStatus err;
-    AudioStreamBasicDescription asbd;
-    int blockSize;
-    bool valid;
-};
-
-static QString
-codestr(OSStatus err)
-{
-    char text[5];
-    UInt32 uerr = err;
-    text[0] = (uerr >> 24) & 0xff;
-    text[1] = (uerr >> 16) & 0xff;
-    text[2] = (uerr >> 8) & 0xff;
-    text[3] = (uerr) & 0xff;
-    text[4] = '\0';
-    return QString("%1 (%2)").arg(err).arg(QString::fromLocal8Bit(text));
-}
-
-CoreAudioFileReader::CoreAudioFileReader(FileSource source,
-                                         DecodeMode /* decodeMode */,
-                                         CacheMode mode,
-                                         sv_samplerate_t targetRate,
-                                         bool normalised,
-                                         ProgressReporter *reporter) :
-    CodedAudioFileReader(mode, targetRate, normalised),
-    m_source(source),
-    m_path(source.getLocalFilename()),
-    m_d(new D),
-    m_reporter(reporter),
-    m_cancelled(false),
-    m_completion(0),
-    m_decodeThread(0)
-{
-    SVDEBUG << "CoreAudioFileReader: local path: \"" << m_path << "\"" << endl;
-
-    m_channelCount = 0;
-    m_fileRate = 0;
-
-    m_d->buffer.mBuffers[0].mData = 0;
-
-    Profiler profiler("CoreAudioFileReader::CoreAudioFileReader", true);
-
-    QByteArray ba = m_path.toLocal8Bit();
-
-    CFURLRef url = CFURLCreateFromFileSystemRepresentation
-        (kCFAllocatorDefault,
-         (const UInt8 *)ba.data(),
-         (CFIndex)ba.length(),
-         false);
-
-    //!!! how do we find out if the file open fails because of DRM protection?
-
-//#if (MACOSX_DEPLOYMENT_TARGET <= 1040 && MAC_OS_X_VERSION_MIN_REQUIRED <= 1040)
-//    FSRef fsref;
-//    if (!CFURLGetFSRef(url, &fsref)) { // returns Boolean, not error code
-//        m_error = "CoreAudioReadStream: Error looking up FS ref (file not found?)";
-//        return;
-//    }
-//    m_d->err = ExtAudioFileOpen(&fsref, &m_d->file);
-//#else
-    m_d->err = ExtAudioFileOpenURL(url, &m_d->file);
-//#endif
-
-    CFRelease(url);
-
-    if (m_d->err) { 
-        m_error = "CoreAudioReadStream: Error opening file: code " + codestr(m_d->err);
-        return;
-    }
-    if (!m_d->file) { 
-        m_error = "CoreAudioReadStream: Failed to open file, but no error reported!";
-        return;
-    }
-    
-    UInt32 propsize = sizeof(AudioStreamBasicDescription);
-    m_d->err = ExtAudioFileGetProperty
-        (m_d->file, kExtAudioFileProperty_FileDataFormat, &propsize, &m_d->asbd);
-    
-    if (m_d->err) {
-        m_error = "CoreAudioReadStream: Error in getting basic description: code " + codestr(m_d->err);
-        ExtAudioFileDispose(m_d->file);
-        return;
-    }
-        
-    m_channelCount = m_d->asbd.mChannelsPerFrame;
-    m_fileRate = m_d->asbd.mSampleRate;
-
-    SVDEBUG << "CoreAudioFileReader: " << m_channelCount << " channels, " << m_fileRate << " Hz" << endl;
-
-    m_d->asbd.mFormatID = kAudioFormatLinearPCM;
-    m_d->asbd.mFormatFlags =
-        kAudioFormatFlagIsFloat |
-        kAudioFormatFlagIsPacked |
-        kAudioFormatFlagsNativeEndian;
-    m_d->asbd.mBitsPerChannel = sizeof(float) * 8;
-    m_d->asbd.mBytesPerFrame = sizeof(float) * m_channelCount;
-    m_d->asbd.mBytesPerPacket = sizeof(float) * m_channelCount;
-    m_d->asbd.mFramesPerPacket = 1;
-    m_d->asbd.mReserved = 0;
-        
-    m_d->err = ExtAudioFileSetProperty
-        (m_d->file, kExtAudioFileProperty_ClientDataFormat, propsize, &m_d->asbd);
-    
-    if (m_d->err) {
-        m_error = "CoreAudioReadStream: Error in setting client format: code " + codestr(m_d->err);
-        ExtAudioFileDispose(m_d->file);
-        return;
-    }
-
-    m_d->buffer.mNumberBuffers = 1;
-    m_d->buffer.mBuffers[0].mNumberChannels = m_channelCount;
-    m_d->buffer.mBuffers[0].mDataByteSize = sizeof(float) * m_channelCount * m_d->blockSize;
-    m_d->buffer.mBuffers[0].mData = new float[m_channelCount * m_d->blockSize];
-
-    m_d->valid = true;
-
-    initialiseDecodeCache();
-
-    if (m_reporter) {
-        connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
-        m_reporter->setMessage
-            (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
-    }
-
-    while (1) {
-
-        UInt32 framesRead = m_d->blockSize;
-        m_d->err = ExtAudioFileRead(m_d->file, &framesRead, &m_d->buffer);
-
-        if (m_d->err) {
-            m_error = QString("Error in CoreAudio decoding: code %1")
-                .arg(m_d->err);
-            break;
-        }
-
-        //!!! progress?
-
-        //    cerr << "Read " << framesRead << " frames (block size " << m_d->blockSize << ")" << endl;
-
-        // buffers are interleaved unless specified otherwise
-        addSamplesToDecodeCache((float *)m_d->buffer.mBuffers[0].mData, framesRead);
-
-        if ((int)framesRead < m_d->blockSize) break;
-    }
-
-    finishDecodeCache();
-    endSerialised();
-
-    m_completion = 100;
-}
-
-
-CoreAudioFileReader::~CoreAudioFileReader()
-{
-    SVDEBUG << "CoreAudioFileReader::~CoreAudioFileReader" << endl;
-
-    if (m_d->valid) {
-        ExtAudioFileDispose(m_d->file);
-        delete[] (float *)(m_d->buffer.mBuffers[0].mData);
-    }
-
-    delete m_d;
-}
-
-void
-CoreAudioFileReader::cancelled()
-{
-  m_cancelled = true;
-}
-
-void
-CoreAudioFileReader::getSupportedExtensions(std::set<QString> &extensions)
-{
-    extensions.insert("aiff");
-    extensions.insert("aif");
-    extensions.insert("au");
-    extensions.insert("m4a");
-    extensions.insert("m4b");
-    extensions.insert("m4p");
-    extensions.insert("mp3");
-    extensions.insert("mp4");
-    extensions.insert("wav");
-}
-
-bool
-CoreAudioFileReader::supportsExtension(QString extension)
-{
-    std::set<QString> extensions;
-    getSupportedExtensions(extensions);
-    return (extensions.find(extension.toLower()) != extensions.end());
-}
-
-bool
-CoreAudioFileReader::supportsContentType(QString type)
-{
-    return (type == "audio/x-aiff" ||
-            type == "audio/x-wav" ||
-            type == "audio/mpeg" ||
-            type == "audio/basic" ||
-            type == "audio/x-aac");
-}
-
-bool
-CoreAudioFileReader::supports(FileSource &source)
-{
-    return (supportsExtension(source.getExtension()) ||
-            supportsContentType(source.getContentType()));
-}
-
-#endif
-
--- a/data/fileio/CoreAudioFileReader.h	Wed Nov 14 15:46:35 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +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 2006-2012 Chris Cannam and 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 _COREAUDIO_FILE_READER_H_
-#define _COREAUDIO_FILE_READER_H_
-
-#ifdef HAVE_COREAUDIO
-
-#include "CodedAudioFileReader.h"
-
-#include "base/Thread.h"
-
-#include <set>
-
-class ProgressReporter;
-
-class CoreAudioFileReader : public CodedAudioFileReader
-{
-    Q_OBJECT
-
-public:
-    CoreAudioFileReader(FileSource source,
-                        DecodeMode decodeMode,
-                        CacheMode cacheMode,
-                        sv_samplerate_t targetRate = 0,
-                        bool normalised = false,
-                        ProgressReporter *reporter = nullptr);
-    virtual ~CoreAudioFileReader();
-
-    virtual QString getError() const { return m_error; }
-    virtual QString getLocation() const { return m_source.getLocation(); }
-    virtual QString getTitle() const { return m_title; }
-    
-    static void getSupportedExtensions(std::set<QString> &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;
-    QString m_title;
-
-    class D;
-    D *m_d;
-
-    ProgressReporter *m_reporter;
-    bool m_cancelled;
-    int m_completion;
-
-    class DecodeThread : public Thread
-    {
-    public:
-       // DecodeThread(QuickTimeFileReader *reader) : m_reader(reader) { }
-        virtual void run();
-
-    protected:
-       // QuickTimeFileReader *m_reader;
-    };
-
-    DecodeThread *m_decodeThread;
-};
-
-#endif
-
-#endif
--- a/data/fileio/OggVorbisFileReader.cpp	Wed Nov 14 15:46:35 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +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 2006 Chris Cannam.
-    
-    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.
-*/
-
-#ifdef HAVE_OGGZ
-#ifdef HAVE_FISHSOUND
-
-#include "OggVorbisFileReader.h"
-
-#include "base/ProgressReporter.h"
-#include "base/Profiler.h"
-#include "system/System.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <cmath>
-
-#include <QFileInfo>
-
-//static int instances = 0;
-
-OggVorbisFileReader::OggVorbisFileReader(FileSource source,
-                                         DecodeMode decodeMode,
-                                         CacheMode mode,
-                                         sv_samplerate_t targetRate,
-                                         bool normalised,
-                                         ProgressReporter *reporter) :
-    CodedAudioFileReader(mode, targetRate, normalised),
-    m_source(source),
-    m_path(source.getLocalFilename()),
-    m_qfile(0),
-    m_ffile(0),
-    m_oggz(0),
-    m_fishSound(0),
-    m_reporter(reporter),
-    m_fileSize(0),
-    m_bytesRead(0),
-    m_commentsRead(false),
-    m_cancelled(false),
-    m_completion(0),
-    m_decodeThread(0)
-{
-    SVDEBUG << "OggVorbisFileReader: local path: \"" << m_path
-            << "\", decode mode: " << decodeMode << " ("
-            << (decodeMode == DecodeAtOnce ? "DecodeAtOnce" : "DecodeThreaded")
-            << ")" << endl;
-
-    m_channelCount = 0;
-    m_fileRate = 0;
-
-//    SVDEBUG << "OggVorbisFileReader::OggVorbisFileReader(" << m_path << "): now have " << (++instances) << " instances" << endl;
-
-    Profiler profiler("OggVorbisFileReader::OggVorbisFileReader");
-
-    // These shenanigans are to avoid using oggz_open(..) with a local
-    // codepage on Windows (make sure proper filename encoding is used)
-    
-    m_qfile = new QFile(m_path);
-    if (!m_qfile->open(QIODevice::ReadOnly)) {
-        m_error = QString("Failed to open file %1 for reading.").arg(m_path);
-        SVDEBUG << "OggVorbisFileReader: " << m_error << endl;
-        delete m_qfile;
-        m_qfile = 0;
-        return;
-    }
-    
-    m_fileSize = m_qfile->size();
-
-    m_ffile = fdopen(dup(m_qfile->handle()), "rb");
-    if (!m_ffile) {
-        m_error = QString("Failed to open file pointer for file %1").arg(m_path);
-        SVDEBUG << "OggVorbisFileReader: " << m_error << endl;
-        delete m_qfile;
-        m_qfile = 0;
-        return;
-    }
-    
-    if (!(m_oggz = oggz_open_stdio(m_ffile, OGGZ_READ))) {
-        m_error = QString("File %1 is not an OGG file.").arg(m_path);
-        fclose(m_ffile);
-        m_ffile = 0;
-        delete m_qfile;
-        m_qfile = 0;
-        return;
-    }
-
-    FishSoundInfo fsinfo;
-    m_fishSound = fish_sound_new(FISH_SOUND_DECODE, &fsinfo);
-
-    fish_sound_set_decoded_callback(m_fishSound, acceptFrames, this);
-    oggz_set_read_callback(m_oggz, -1, (OggzReadPacket)readPacket, this);
-
-    if (decodeMode == DecodeAtOnce) {
-
-        if (m_reporter) {
-            connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled()));
-            m_reporter->setMessage
-                (tr("Decoding %1...").arg(QFileInfo(m_path).fileName()));
-        }
-
-        while (oggz_read(m_oggz, 1024) > 0);
-        
-        fish_sound_delete(m_fishSound);
-        m_fishSound = 0;
-        oggz_close(m_oggz);
-        m_oggz = 0;
-
-        if (isDecodeCacheInitialised()) finishDecodeCache();
-        endSerialised();
-
-    } else {
-
-        if (m_reporter) m_reporter->setProgress(100);
-
-        while (oggz_read(m_oggz, 1024) > 0 &&
-               (m_channelCount == 0 || m_fileRate == 0 || m_sampleRate == 0));
-
-        if (m_channelCount > 0) {
-            m_decodeThread = new DecodeThread(this);
-            m_decodeThread->start();
-        }
-    }
-}
-
-OggVorbisFileReader::~OggVorbisFileReader()
-{
-//    SVDEBUG << "OggVorbisFileReader::~OggVorbisFileReader(" << m_path << "): now have " << (--instances) << " instances" << endl;
-    if (m_decodeThread) {
-        m_cancelled = true;
-        m_decodeThread->wait();
-        delete m_decodeThread;
-    }
-    if (m_qfile) {
-        // don't fclose m_ffile; oggz_close did that
-        delete m_qfile;
-        m_qfile = 0;
-    }
-}
-
-void
-OggVorbisFileReader::cancelled()
-{
-    m_cancelled = true; 
-}
-
-void
-OggVorbisFileReader::DecodeThread::run()
-{
-    if (m_reader->m_cacheMode == CacheInTemporaryFile) {
-        m_reader->m_completion = 1;
-        m_reader->startSerialised("OggVorbisFileReader::Decode");
-    }
-
-    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;
-
-    // don't fclose m_ffile; oggz_close did that
-
-    delete m_reader->m_qfile;
-    m_reader->m_qfile = 0;
-    
-    if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache();
-    m_reader->m_completion = 100;
-
-    m_reader->endSerialised();
-} 
-
-int
-OggVorbisFileReader::readPacket(OGGZ *, ogg_packet *packet, long, void *data)
-{
-    OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
-    FishSound *fs = reader->m_fishSound;
-
-    fish_sound_prepare_truncation(fs, packet->granulepos, int(packet->e_o_s));
-    fish_sound_decode(fs, packet->packet, packet->bytes);
-
-    reader->m_bytesRead += packet->bytes;
-
-    // The number of bytes read by this function is smaller than
-    // the file size because of the packet headers
-    int p = int(lrint(double(reader->m_bytesRead) * 114 /
-                      double(reader->m_fileSize)));
-    if (p > 99) p = 99;
-    reader->m_completion = p;
-    reader->progress(p);
-
-    if (reader->m_fileSize > 0 && reader->m_reporter) {
-        reader->m_reporter->setProgress(p);
-    }
-
-    if (reader->m_cancelled) return 1;
-    return 0;
-}
-
-int
-OggVorbisFileReader::acceptFrames(FishSound *fs, float **frames, long nframes,
-                                  void *data)
-{
-    OggVorbisFileReader *reader = (OggVorbisFileReader *)data;
-
-    if (!reader->m_commentsRead) {
-        const FishSoundComment *comment;
-        comment = fish_sound_comment_first_byname(fs, (char *)"TITLE");
-        if (comment && comment->value) {
-            reader->m_title = QString::fromUtf8(comment->value);
-        }
-        comment = fish_sound_comment_first_byname(fs, (char *)"ARTIST");
-        if (comment && comment->value) {
-            reader->m_maker = QString::fromUtf8(comment->value);
-        }
-        comment = fish_sound_comment_first(fs);
-        while (comment) {
-            reader->m_tags[QString::fromUtf8(comment->name).toUpper()] =
-                QString::fromUtf8(comment->value);
-            comment = fish_sound_comment_next(fs, comment);
-        }
-        reader->m_commentsRead = true;
-    }
-
-    if (reader->m_channelCount == 0) {
-        FishSoundInfo fsinfo;
-        fish_sound_command(fs, FISH_SOUND_GET_INFO,
-                           &fsinfo, sizeof(FishSoundInfo));
-        reader->m_fileRate = fsinfo.samplerate;
-        reader->m_channelCount = fsinfo.channels;
-        reader->initialiseDecodeCache();
-    }
-
-    if (nframes > 0) {
-        reader->addSamplesToDecodeCache(frames, nframes);
-    }
-
-    if (reader->m_cancelled) return 1;
-    return 0;
-}
-
-void
-OggVorbisFileReader::getSupportedExtensions(std::set<QString> &extensions)
-{
-    extensions.insert("ogg");
-    extensions.insert("oga");
-}
-
-bool
-OggVorbisFileReader::supportsExtension(QString extension)
-{
-    std::set<QString> extensions;
-    getSupportedExtensions(extensions);
-    return (extensions.find(extension.toLower()) != extensions.end());
-}
-
-bool
-OggVorbisFileReader::supportsContentType(QString type)
-{
-    return (type == "application/ogg");
-}
-
-bool
-OggVorbisFileReader::supports(FileSource &source)
-{
-    return (supportsExtension(source.getExtension()) ||
-            supportsContentType(source.getContentType()));
-}
-
-#endif
-#endif
--- a/data/fileio/OggVorbisFileReader.h	Wed Nov 14 15:46:35 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,106 +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 2006 Chris Cannam.
-    
-    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 SV_OGG_VORBIS_FILE_READER_H
-#define SV_OGG_VORBIS_FILE_READER_H
-
-#ifdef HAVE_OGGZ
-#ifdef HAVE_FISHSOUND
-
-#include "CodedAudioFileReader.h"
-
-#include "base/Thread.h"
-#include <oggz/oggz.h>
-#include <fishsound/fishsound.h>
-
-#include <cstdio>
-
-#include <set>
-
-class ProgressReporter;
-
-class OggVorbisFileReader : public CodedAudioFileReader
-{
-    Q_OBJECT
-
-public:
-    OggVorbisFileReader(FileSource source,
-                        DecodeMode decodeMode,
-                        CacheMode cacheMode,
-                        sv_samplerate_t targetRate = 0,
-                        bool normalised = false,
-                        ProgressReporter *reporter = nullptr);
-    virtual ~OggVorbisFileReader();
-
-    virtual QString getError() const { return m_error; }
-
-    virtual QString getLocation() const { return m_source.getLocation(); }
-    virtual QString getTitle() const { return m_title; }
-    virtual QString getMaker() const { return m_maker; }
-    virtual TagMap getTags() const { return m_tags; }
-    
-    static void getSupportedExtensions(std::set<QString> &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;
-    QString m_title;
-    QString m_maker;
-    TagMap m_tags;
-
-    QFile *m_qfile;
-    FILE *m_ffile;
-    OGGZ *m_oggz;
-    FishSound *m_fishSound;
-    ProgressReporter *m_reporter;
-    sv_frame_t m_fileSize;
-    sv_frame_t m_bytesRead;
-    bool m_commentsRead;
-    bool m_cancelled;
-    int m_completion;
- 
-    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
-#endif
-
-#endif
--- a/files.pri	Wed Nov 14 15:46:35 2018 +0000
+++ b/files.pri	Thu Dec 06 12:50:28 2018 +0000
@@ -47,6 +47,7 @@
            data/fileio/AudioFileReader.h \
            data/fileio/AudioFileReaderFactory.h \
            data/fileio/AudioFileSizeEstimator.h \
+           data/fileio/BQAFileReader.h \
            data/fileio/BZipFileDevice.h \
            data/fileio/CachedFile.h \
            data/fileio/CodedAudioFileReader.h \
@@ -62,9 +63,7 @@
            data/fileio/MIDIFileReader.h \
            data/fileio/MIDIFileWriter.h \
            data/fileio/MP3FileReader.h \
-           data/fileio/OggVorbisFileReader.h \
            data/fileio/PlaylistFileReader.h \
-           data/fileio/CoreAudioFileReader.h \
            data/fileio/DecodingWavFileReader.h \
            data/fileio/WavFileReader.h \
            data/fileio/WavFileWriter.h \
@@ -182,6 +181,7 @@
            data/fileio/AudioFileReader.cpp \
            data/fileio/AudioFileReaderFactory.cpp \
            data/fileio/AudioFileSizeEstimator.cpp \
+           data/fileio/BQAFileReader.cpp \
            data/fileio/BZipFileDevice.cpp \
            data/fileio/CachedFile.cpp \
            data/fileio/CodedAudioFileReader.cpp \
@@ -194,9 +194,7 @@
            data/fileio/MIDIFileReader.cpp \
            data/fileio/MIDIFileWriter.cpp \
            data/fileio/MP3FileReader.cpp \
-           data/fileio/OggVorbisFileReader.cpp \
            data/fileio/PlaylistFileReader.cpp \
-           data/fileio/CoreAudioFileReader.cpp \
            data/fileio/DecodingWavFileReader.cpp \
            data/fileio/WavFileReader.cpp \
            data/fileio/WavFileWriter.cpp \