b6741ac185aa — Chris Cannam 14 days ago
Merge from branch simplewavread, including prior merge from minimp3 branch
M Makefile +3 -3
@@ 9,6 9,7 @@ 
 #  -DHAVE_OGGZ -DHAVE_FISHSOUND
 #                      * Read Ogg/Vorbis files using oggz
 #  -DHAVE_OPUS         * Read Opus files using libopus
+#  -DHAVE_MINIMP3      * Read mp3 files using minimp3
 #  -DHAVE_MEDIAFOUNDATION
 #                      * Read various formats using MediaFoundation on Windows
 #  -DHAVE_COREAUDIO    * Read various formats using CoreAudio on macOS/iOS

          
@@ 17,12 18,12 @@ 
 # will also be provided, as none of the other libraries have write
 # support included here.
 
-AUDIOSTREAM_DEFINES := -DHAVE_LIBSNDFILE -DHAVE_OGGZ -DHAVE_FISHSOUND -DHAVE_OPUS
+AUDIOSTREAM_DEFINES := -DHAVE_LIBSNDFILE -DHAVE_OGGZ -DHAVE_FISHSOUND -DHAVE_OPUS -DHAVE_MINIMP3
 
 
 # Add any related includes and libraries here
 #
-THIRD_PARTY_INCLUDES	:= -I/usr/include/opus
+THIRD_PARTY_INCLUDES	:= -I/usr/include/opus -I../thirdparty/minimp3
 THIRD_PARTY_LIBS	:=
 
 

          
@@ 37,4 38,3 @@ THIRD_PARTY_LIBS	:=
 # no routinely user-modifiable parts
 
 include build/Makefile.inc
-

          
M bqaudiostream/AudioReadStream.h +3 -1
@@ 84,9 84,11 @@ public:
 protected:
     AudioReadStream();
     virtual size_t getFrames(size_t count, float *frames) = 0;
-    int getResampledChunk(int count, float *frames);
     size_t m_channelCount;
     size_t m_sampleRate;
+
+private:
+    int getResampledChunk(int count, float *frames);
     size_t m_retrievalRate;
     size_t m_totalFileFrames;
     size_t m_totalRetrievedFrames;

          
M build/Makefile.inc +12 -5
@@ 29,8 29,12 @@ src/AudioReadStreamFactory.o: ./bqaudios
 src/AudioReadStreamFactory.o: ./bqaudiostream/Exceptions.h
 src/AudioReadStreamFactory.o: src/WavFileReadStream.cpp
 src/AudioReadStreamFactory.o: src/OggVorbisReadStream.cpp
+src/AudioReadStreamFactory.o: src/MiniMP3ReadStream.cpp
 src/AudioReadStreamFactory.o: src/MediaFoundationReadStream.cpp
+src/AudioReadStreamFactory.o: src/SimpleWavFileReadStream.cpp
+src/AudioReadStreamFactory.o: src/SimpleWavFileReadStream.h
 src/AudioReadStreamFactory.o: src/CoreAudioReadStream.cpp
+src/AudioReadStreamFactory.o: src/OpusReadStream.cpp
 src/AudioWriteStreamFactory.o: ./bqaudiostream/AudioWriteStreamFactory.h
 src/AudioWriteStreamFactory.o: ./bqaudiostream/AudioWriteStream.h
 src/AudioWriteStreamFactory.o: ./bqaudiostream/Exceptions.h

          
@@ 40,11 44,14 @@ src/AudioWriteStreamFactory.o: src/Simpl
 src/AudioWriteStreamFactory.o: src/SimpleWavFileWriteStream.h
 src/AudioWriteStreamFactory.o: src/CoreAudioWriteStream.cpp
 src/AudioWriteStreamFactory.o: src/CoreAudioWriteStream.h
-src/Exceptions.o: ./bqaudiostream/Exceptions.h
+src/AudioStreamExceptions.o: ./bqaudiostream/Exceptions.h
+src/CoreAudioReadStream.o: ./bqaudiostream/AudioReadStream.h
+src/CoreAudioWriteStream.o: ./bqaudiostream/AudioWriteStream.h
 src/MediaFoundationReadStream.o: ./bqaudiostream/AudioReadStream.h
-src/WavFileWriteStream.o: ./bqaudiostream/AudioWriteStream.h
+src/MiniMP3ReadStream.o: ./bqaudiostream/AudioReadStream.h
+src/OggVorbisReadStream.o: ./bqaudiostream/AudioReadStream.h
+src/OpusReadStream.o: ./bqaudiostream/AudioReadStream.h
+src/SimpleWavFileReadStream.o: ./bqaudiostream/AudioReadStream.h
 src/SimpleWavFileWriteStream.o: ./bqaudiostream/AudioWriteStream.h
 src/WavFileReadStream.o: ./bqaudiostream/AudioReadStream.h
-src/CoreAudioWriteStream.o: ./bqaudiostream/AudioWriteStream.h
-src/CoreAudioReadStream.o: ./bqaudiostream/AudioReadStream.h
-src/OggVorbisReadStream.o: ./bqaudiostream/AudioReadStream.h
+src/WavFileWriteStream.o: ./bqaudiostream/AudioWriteStream.h

          
M src/AudioReadStreamFactory.cpp +2 -0
@@ 125,7 125,9 @@ AudioReadStreamFactory::getFileFilter()
 
 #include "WavFileReadStream.cpp"
 #include "OggVorbisReadStream.cpp"
+#include "MiniMP3ReadStream.cpp"
 #include "MediaFoundationReadStream.cpp"
+#include "SimpleWavFileReadStream.cpp"
 #include "CoreAudioReadStream.cpp"
 #include "OpusReadStream.cpp"
 

          
A => src/MiniMP3ReadStream.cpp +133 -0
@@ 0,0 1,133 @@ 
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/*
+    bqaudiostream
+
+    A small library wrapping various audio file read/write
+    implementations in C++.
+
+    Copyright 2007-2020 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifdef HAVE_MINIMP3
+
+#define MINIMP3_IMPLEMENTATION
+#define MINIMP3_FLOAT_OUTPUT
+#include <minimp3_ex.h>
+
+#include "MiniMP3ReadStream.h"
+
+#include <sstream>
+
+namespace breakfastquay
+{
+
+static vector<string>
+getMiniMP3Extensions()
+{
+    vector<string> extensions;
+    extensions.push_back("mp3");
+    return extensions;
+}
+
+static
+AudioReadStreamBuilder<MiniMP3ReadStream>
+minimp3builder(
+    string("http://breakfastquay.com/rdf/turbot/audiostream/MiniMP3ReadStream"),
+    getMiniMP3Extensions()
+    );
+
+class MiniMP3ReadStream::D
+{
+public:
+    D() { }
+    mp3dec_ex_t dec;
+};
+
+MiniMP3ReadStream::MiniMP3ReadStream(string path) :
+    m_path(path),
+    m_d(new D)
+{
+    //!!! Windows: use wchar versions
+    
+    m_channelCount = 0;
+    m_sampleRate = 0;
+
+    int err = mp3dec_ex_open(&m_d->dec, path.c_str(), MP3D_DO_NOT_SCAN);
+    if (err) {
+        ostringstream os;
+        os << "MiniMP3ReadStream: Unable to open file (error code " << err << ")";
+        m_error = os.str();
+        if (err == MP3D_E_IOERROR) {
+            throw FileNotFound(m_path);
+        } else {
+            throw InvalidFileFormat(m_path, "failed to open audio file");
+        }
+    }
+
+    m_channelCount = m_d->dec.info.channels;
+    m_sampleRate = m_d->dec.info.hz;
+}
+
+size_t
+MiniMP3ReadStream::getFrames(size_t count, float *frames)
+{
+    if (m_error != "") return 0;
+    if (count == 0) return 0;
+
+//    cerr << "getFrames: working" << endl;
+
+    size_t desired = count * m_channelCount;
+    size_t obtained = mp3dec_ex_read(&m_d->dec, frames, desired);
+
+    //!!! have to provide our own tags support?
+    
+    if (obtained < desired) {
+        if (m_d->dec.last_error) {
+            ostringstream os;
+            os << "MiniMP3ReadStream: Failed to read from file (error code "
+               << m_d->dec.last_error << ")";
+            m_error = os.str();
+            mp3dec_ex_close(&m_d->dec);
+            throw InvalidFileFormat(m_path, "error in decoder");
+        }
+    }
+
+    return obtained / m_channelCount;
+}
+
+MiniMP3ReadStream::~MiniMP3ReadStream()
+{
+    if (m_error != "") {
+        mp3dec_ex_close(&m_d->dec);
+    }
+    delete m_d;
+}
+
+}
+
+#endif
+

          
A => src/MiniMP3ReadStream.h +72 -0
@@ 0,0 1,72 @@ 
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/*
+    bqaudiostream
+
+    A small library wrapping various audio file read/write
+    implementations in C++.
+
+    Copyright 2007-2020 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQ_MINIMP3_READ_STREAM_H
+#define BQ_MINIMP3_READ_STREAM_H
+
+#include "AudioReadStream.h"
+
+#ifdef HAVE_MINIMP3
+
+namespace breakfastquay
+{
+    
+class MiniMP3ReadStream : public AudioReadStream
+{
+public:
+    MiniMP3ReadStream(std::string path);
+    virtual ~MiniMP3ReadStream();
+
+    virtual std::string getTrackName() const { return m_track; }
+    virtual std::string getArtistName() const { return m_artist; }
+
+    virtual std::string getError() const { return m_error; }
+
+protected:
+    virtual size_t getFrames(size_t count, float *frames);
+
+    std::string m_path;
+    std::string m_error;
+    std::string m_track;
+    std::string m_artist;
+
+    class D;
+    D *m_d;
+};
+
+}
+
+#endif
+
+#endif

          
A => src/SimpleWavFileReadStream.cpp +305 -0
@@ 0,0 1,305 @@ 
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/*
+    bqaudiostream
+
+    A small library wrapping various audio file read/write
+    implementations in C++.
+
+    Copyright 2007-2020 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#include "SimpleWavFileReadStream.h"
+
+#if ! (defined(HAVE_LIBSNDFILE) || defined(HAVE_SNDFILE))
+
+#include <iostream>
+
+using namespace std;
+
+namespace breakfastquay
+{
+
+static vector<string> extensions() {
+    vector<string> ee;
+    ee.push_back("wav");
+    return ee;
+}
+
+static 
+AudioReadStreamBuilder<SimpleWavFileReadStream>
+simplewavbuilder(
+    string("http://breakfastquay.com/rdf/turbot/audiostream/SimpleWavFileReadStream"),
+    extensions()
+    );
+
+SimpleWavFileReadStream::SimpleWavFileReadStream(std::string filename) :
+    m_path(filename),
+    m_bitDepth(0),
+    m_startPos(0),
+    m_index(0),
+    m_file(0)
+{
+    m_file = new ifstream(filename.c_str(), ios::in | std::ios::binary);
+
+    if (!*m_file) {
+        delete m_file;
+        m_file = 0;
+        throw FileNotFound(m_path);
+    }
+
+    readHeader();
+}
+
+SimpleWavFileReadStream::~SimpleWavFileReadStream()
+{
+    if (m_file) {
+        m_file->close();
+        delete m_file;
+    }
+}
+
+void
+SimpleWavFileReadStream::readHeader()
+{
+    if (!m_file) {
+        throw std::logic_error("internal error: no file in readHeader");
+    }
+    
+    uint32_t totalSize = readExpectedChunkSize("RIFF");
+    readExpectedTag("WAVE");
+    uint32_t fmtSize = readExpectedChunkSize("fmt ");
+    if (fmtSize < 16) {
+        cout << "fmtSize = " << fmtSize << endl;
+        throw InvalidFileFormat(m_path, "unexpectedly short format chunk");
+    }
+
+    uint32_t audioFormat = readMandatoryNumber(2);
+    if (audioFormat != 1 && audioFormat != 3) {
+        throw InvalidFileFormat(m_path, "only PCM and float WAV formats are supported by this reader");
+    }
+
+    uint32_t channels = readMandatoryNumber(2);
+    uint32_t sampleRate = readMandatoryNumber(4);
+    uint32_t byteRate = readMandatoryNumber(4);
+    uint32_t bytesPerSample = readMandatoryNumber(2);
+    uint32_t bitsPerSample = readMandatoryNumber(2);
+    
+    if (bitsPerSample != 8 &&
+        bitsPerSample != 16 &&
+        bitsPerSample != 24 &&
+        bitsPerSample != 32) {
+        throw InvalidFileFormat(m_path, "unsupported bit depth");
+    }
+
+    if (bitsPerSample == 32) {
+        if (audioFormat == 1) {
+            throw InvalidFileFormat(m_path, "32-bit samples are only supported in float format, not PCM");
+        } else {
+            char *buf = (char *)malloc(sizeof(float));
+            *(float *)buf = -0.f;
+            m_floatSwap = (buf[0] != '\0');
+        }
+    }
+
+    m_channelCount = channels;
+    m_sampleRate = sampleRate;
+    m_bitDepth = bitsPerSample;
+
+    // we don't use these
+    (void)totalSize;
+    (void)byteRate;
+    (void)bytesPerSample;
+
+    // and we ignore extended format chunk data
+    if (fmtSize > 16) {
+        m_file->ignore(fmtSize - 16);
+    }
+    
+    while (true) {
+        string tag = readTag();
+        if (tag == "") {
+            throw InvalidFileFormat(m_path, "tag expected after fmt chunk");
+        }
+        uint32_t chunkSize = readChunkSizeAfterTag();
+        if (tag == "data") {
+            break;
+        } else {
+            m_file->ignore(chunkSize);
+            if (!m_file->good()) {
+                throw InvalidFileFormat(m_path, "incomplete chunk");
+            }
+        }
+    }
+}
+
+uint32_t
+SimpleWavFileReadStream::readExpectedChunkSize(string tag)
+{
+    readExpectedTag(tag);
+    return readChunkSizeAfterTag();
+}
+
+void
+SimpleWavFileReadStream::readExpectedTag(string tag)
+{
+    string actual = readTag();
+    if (actual != tag) {
+        throw InvalidFileFormat(m_path, "failed to read tag \"" + tag + "\" (found \"" + actual + "\"");
+    }
+}
+
+string
+SimpleWavFileReadStream::readTag()
+{
+    vector<uint8_t> v(4);
+    if (getBytes(4, v) != 4) {
+        throw InvalidFileFormat(m_path, "incomplete tag");
+    }
+    string tag((const char *)v.data(), 4);
+    return tag;
+}
+
+uint32_t
+SimpleWavFileReadStream::readMandatoryNumber(int length)
+{
+    vector<uint8_t> v(length);
+    if (getBytes(length, v) != length) {
+        throw InvalidFileFormat(m_path, "incomplete number");
+    }
+    return le2int(v);
+}
+
+uint32_t
+SimpleWavFileReadStream::readChunkSizeAfterTag()
+{
+    return readMandatoryNumber(4);
+}
+
+size_t
+SimpleWavFileReadStream::getFrames(size_t count, float *frames)
+{
+    int sampleSize = m_bitDepth / 8;
+    vector<uint8_t> buf(sampleSize);
+    
+    size_t requested = count * m_channelCount;
+    size_t got = 0;
+    
+    while (got < requested) {
+        int gotHere = getBytes(sampleSize, buf);
+        if (gotHere < sampleSize) {
+            return got / m_channelCount;
+        }
+        switch (m_bitDepth) {
+        case 8: frames[got] = convertSample8(buf); break;
+        case 16: frames[got] = convertSample16(buf); break;
+        case 24: frames[got] = convertSample24(buf); break;
+        case 32: frames[got] = convertSampleFloat(buf); break;
+        }
+        ++got;
+    }
+
+    return got / m_channelCount;
+}
+
+float
+SimpleWavFileReadStream::convertSample8(const vector<uint8_t> &v)
+{
+    return float(int32_t(v[0]) - 128) / 128.0;
+}
+
+float
+SimpleWavFileReadStream::convertSample16(const vector<uint8_t> &v)
+{
+    uint32_t b0 = v[0], b1 = v[1];
+
+    uint32_t bb = ((b0 << 16) | (b1 << 24));
+    int32_t i(bb);
+
+    return float(i) / 2147483647.f;
+}
+
+float
+SimpleWavFileReadStream::convertSample24(const vector<uint8_t> &v)
+{
+    uint32_t b0 = v[0], b1 = v[1], b2 = v[2];
+
+    uint32_t bb = ((b0 << 8) | (b1 << 16) | (b2 << 24));
+    int32_t i(bb);
+
+    return float(i) / 2147483647.f;
+}
+
+float
+SimpleWavFileReadStream::convertSampleFloat(const vector<uint8_t> &v)
+{
+    if (!m_floatSwap) {
+        const uint8_t *buf = v.data();
+        return *(const float *)buf;
+    } else {
+        vector<uint8_t> vv(4);
+        for (int i = 0; i < 4; ++i) {
+            vv[i] = v[3-i];
+        }
+        const uint8_t *buf = vv.data();
+        return *(const float *)buf;
+    }
+}
+
+int
+SimpleWavFileReadStream::getBytes(int n, vector<uint8_t> &v)
+{
+    if (!m_file) return 0;
+
+    for (int i = 0; i < n; ++i) {
+        char c;
+        m_file->get(c);
+        if (!m_file->good()) {
+            return i;
+        } else {
+            v[i] = (uint8_t)c;
+        }
+    }
+
+    return n;
+}
+
+uint32_t
+SimpleWavFileReadStream::le2int(const vector<uint8_t> &le)
+{
+    uint32_t n = 0;
+    int len = int(le.size());
+
+    for (int i = 0; i < len; ++i) {
+        n += (uint32_t(le[i]) << (8 * i));
+    }
+
+    return n;
+}
+
+}
+
+#endif

          
A => src/SimpleWavFileReadStream.h +100 -0
@@ 0,0 1,100 @@ 
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/*
+    bqaudiostream
+
+    A small library wrapping various audio file read/write
+    implementations in C++.
+
+    Copyright 2007-2020 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQ_SIMPLE_WAV_FILE_READ_STREAM_H
+#define BQ_SIMPLE_WAV_FILE_READ_STREAM_H
+
+#include "AudioReadStream.h"
+
+// If we have libsndfile, we shouldn't be using this class
+#if ! (defined(HAVE_LIBSNDFILE) || defined(HAVE_SNDFILE))
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+namespace breakfastquay
+{
+
+class SimpleWavFileReadStream : public AudioReadStream
+{
+public:
+    SimpleWavFileReadStream(std::string path);
+    virtual ~SimpleWavFileReadStream();
+
+    virtual std::string getTrackName() const { return m_track; }
+    virtual std::string getArtistName() const { return m_artist; }
+
+    virtual std::string getError() const { return m_error; }
+
+protected:
+    virtual size_t getFrames(size_t count, float *frames);
+    
+private:
+    std::string m_path;
+    std::string m_error;
+    std::string m_track;
+    std::string m_artist;
+    
+    int m_channels;
+    int m_rate;
+    int m_bitDepth;
+    bool m_floatSwap;
+    size_t m_startPos;
+    size_t m_index;
+
+    std::ifstream *m_file;
+
+    void readHeader();
+    uint32_t readExpectedChunkSize(std::string tag);
+    void readExpectedTag(std::string tag);
+    std::string readTag();
+    uint32_t readChunkSizeAfterTag();
+    uint32_t readMandatoryNumber(int length);
+
+    float convertSample8(const vector<uint8_t> &);
+    float convertSample16(const vector<uint8_t> &);
+    float convertSample24(const vector<uint8_t> &);
+    float convertSampleFloat(const vector<uint8_t> &);
+
+    int getBytes(int n, std::vector<uint8_t> &);
+    static uint32_t le2int(const std::vector<uint8_t> &le);
+};
+
+}
+
+#endif
+
+#endif
+

          
M src/SimpleWavFileWriteStream.cpp +13 -12
@@ 83,7 83,7 @@ SimpleWavFileWriteStream::~SimpleWavFile
     }
 
     m_file->seekp(0, ios::end);
-    unsigned int totalSize = m_file->tellp();
+    uint32_t totalSize = m_file->tellp();
 
     // seek to first length position
     m_file->seekp(4, ios::beg);

          
@@ 107,26 107,27 @@ void
 SimpleWavFileWriteStream::putBytes(string s)
 {
     if (!m_file) return;
-    for (unsigned int i = 0; i < s.length(); i++) {
-        *m_file << (unsigned char)s[i];
+    for (uint32_t i = 0; i < s.length(); i++) {
+        *m_file << (uint8_t)s[i];
     }
 }
 
 void
-SimpleWavFileWriteStream::putBytes(const unsigned char *buffer, size_t n)
+SimpleWavFileWriteStream::putBytes(const uint8_t *buffer, size_t n)
 {
     if (!m_file) return;
     m_file->write((const char *)buffer, n);
 }
 
 string
-SimpleWavFileWriteStream::int2le(unsigned int value, unsigned int length)
+SimpleWavFileWriteStream::int2le(uint32_t value, uint32_t length)
 {
-    string r;
+    string r(length, '\0');
 
-    do {
-        r += (unsigned char)((long)((value >> (8 * r.length())) & 0xff));
-    } while (r.length() < length);
+    for (int i = 0; i < length; ++i) {
+        r[i] = (uint8_t)(value & 0xff);
+        value >>= 8;
+    }
 
     return r;
 }

          
@@ 179,8 180,8 @@ SimpleWavFileWriteStream::putInterleaved
         for (size_t c = 0; c < getChannelCount(); ++c) {
             
             double f = frames[i * getChannelCount() + c];
-            unsigned int u = 0;
-            unsigned char ubuf[4];
+            uint32_t u = 0;
+            uint8_t ubuf[4];
             if (f < -1.0) f = -1.0;
             if (f > 1.0) f = 1.0;
             

          
@@ 188,7 189,7 @@ SimpleWavFileWriteStream::putInterleaved
 
             case 24:
                 f = f * 2147483647.0;
-                u = (unsigned int)(int(f));
+                u = (uint32_t)(int(f));
                 u >>= 8;
                 ubuf[0] = (u & 0xff);
                 u >>= 8;

          
M src/SimpleWavFileWriteStream.h +3 -3
@@ 32,8 32,8 @@ 
     Software without prior written authorization.
 */
 
-#ifndef BQ_SIMPLE_WAV_FILE_WRITE_STREAM_H_
-#define BQ_SIMPLE_WAV_FILE_WRITE_STREAM_H_
+#ifndef BQ_SIMPLE_WAV_FILE_WRITE_STREAM_H
+#define BQ_SIMPLE_WAV_FILE_WRITE_STREAM_H
 
 #include "AudioWriteStream.h"
 

          
@@ 62,7 62,7 @@ protected:
     std::ofstream *m_file;
 
     void writeFormatChunk();
-    std::string int2le(unsigned int value, unsigned int length);
+    std::string int2le(uint32_t value, uint32_t length);
     void putBytes(std::string);
     void putBytes(const unsigned char *, size_t);
 };

          
M test/TestAudioStreamRead.h +1 -1
@@ 60,7 60,7 @@ private slots:
     {
         QFETCH(QString, audiofile);
 
-        cerr << "\n\n*** audiofile = " << audiofile.toLocal8Bit().data() << "\n\n" << endl;
+//        cerr << "\n\n*** audiofile = " << audiofile.toLocal8Bit().data() << "\n\n" << endl;
 
         try {
 

          
M test/TestWavReadWrite.h +9 -9
@@ 18,17 18,17 @@ namespace breakfastquay {
 
 static const float DB_FLOOR = -1000.0;
 
-static float to_dB(float ratio)
+static float to_dBV(float ratio)
 {
     if (ratio == 0.0) return DB_FLOOR;
-    float dB = 10 * log10f(ratio);
+    float dB = 20 * log10f(ratio);
     return dB;
 }
 
-static float from_dB(float dB)
+static float from_dBV(float dB)
 {
     if (dB == DB_FLOOR) return 0.0;
-    float m = powf(10.0, dB / 10.0);
+    float m = powf(10.0, dB / 20.0);
     return m;
 }
 

          
@@ 123,8 123,8 @@ private slots:
 
         QVERIFY(ws);
     
-        float error = from_dB(-10);
-        float warning = from_dB(-25);
+        float error = from_dBV(-10);
+        float warning = from_dBV(-25);
         float maxdiff = 0.f;
         float mda = 0.f, mdb = 0.f;
         int maxdiffindex = -1;

          
@@ 157,14 157,14 @@ private slots:
 
         QString message = QString("Max diff is %1 (%2 dB) at index %3 (a = %4, b = %5) [error threshold %6 (%7 dB), warning threshold %8 (%9 dB)]")
             .arg(maxdiff)
-            .arg(to_dB(maxdiff))
+            .arg(to_dBV(maxdiff))
             .arg(maxdiffindex)
             .arg(mda)
             .arg(mdb)
             .arg(error)
-            .arg(to_dB(error))
+            .arg(to_dBV(error))
             .arg(warning)
-            .arg(to_dB(warning));
+            .arg(to_dBV(warning));
 
         QVERIFY2(maxdiff < error, message.toLocal8Bit().data());
 

          
M test/test.pro +1 -1
@@ 10,7 10,7 @@ QT -= gui
 DESTDIR = .
 QMAKE_LIBDIR += . ..
 
-LIBS += -L.. -lbqaudiostream -L../../bqresample -lbqresample -L../../bqvec -lbqvec -lsndfile -loggz -lfishsound -lopusfile -lopus -logg
+LIBS += -L.. -lbqaudiostream -L../../bqresample -lbqresample -L../../bqvec -lbqvec -lsndfile -loggz -lfishsound -lopusfile -lopus -logg -lsamplerate
 
 INCLUDEPATH += . .. ../../bqvec ../../bqresample ../../bqthingfactory
 DEPENDPATH += . .. ../../bqvec ../../bqresample ../../bqthingfactory