e75864a82092 — Chris Cannam 14 days ago
Support float wav, skip extended fmt chunk
M Makefile +0 -1
@@ 38,4 38,3 @@ THIRD_PARTY_LIBS	:=
 # no routinely user-modifiable parts
 
 include build/Makefile.inc
-

          
M build/Makefile.inc +11 -8
@@ 29,10 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/AudioReadStreamFactory.o: src/MiniMP3ReadStream.cpp
 src/AudioWriteStreamFactory.o: ./bqaudiostream/AudioWriteStreamFactory.h
 src/AudioWriteStreamFactory.o: ./bqaudiostream/AudioWriteStream.h
 src/AudioWriteStreamFactory.o: ./bqaudiostream/Exceptions.h

          
@@ 42,13 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/OpusReadStream.o: ./bqaudiostream/AudioReadStream.h
-src/MiniMP3ReadStream.o: ./bqaudiostream/AudioReadStream.h
+src/WavFileWriteStream.o: ./bqaudiostream/AudioWriteStream.h

          
M src/SimpleWavFileReadStream.cpp +47 -17
@@ 61,13 61,13 @@ SimpleWavFileReadStream::SimpleWavFileRe
     m_bitDepth(0),
     m_startPos(0),
     m_index(0),
-    m_file(nullptr)
+    m_file(0)
 {
     m_file = new ifstream(filename.c_str(), ios::in | std::ios::binary);
 
     if (!*m_file) {
         delete m_file;
-        m_file = nullptr;
+        m_file = 0;
         throw FileNotFound(m_path);
     }
 

          
@@ 92,14 92,14 @@ SimpleWavFileReadStream::readHeader()
     uint32_t totalSize = readExpectedChunkSize("RIFF");
     readExpectedTag("WAVE");
     uint32_t fmtSize = readExpectedChunkSize("fmt ");
-    if (fmtSize != 16) {
+    if (fmtSize < 16) {
         cout << "fmtSize = " << fmtSize << endl;
-        throw InvalidFileFormat(m_path, "unexpected format chunk size");
+        throw InvalidFileFormat(m_path, "unexpectedly short format chunk");
     }
 
     uint32_t audioFormat = readMandatoryNumber(2);
-    if (audioFormat != 1) {
-        throw InvalidFileFormat(m_path, "PCM formats only supported by this reader");
+    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);

          
@@ 107,23 107,42 @@ SimpleWavFileReadStream::readHeader()
     uint32_t byteRate = readMandatoryNumber(4);
     uint32_t bytesPerSample = readMandatoryNumber(2);
     uint32_t bitsPerSample = readMandatoryNumber(2);
-
+    
     if (bitsPerSample != 8 &&
         bitsPerSample != 16 &&
-        bitsPerSample != 24) {
+        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;
 
-    cerr << "bit depth = " << m_bitDepth << endl;
+    // 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");
+            throw InvalidFileFormat(m_path, "tag expected after fmt chunk");
         }
         uint32_t chunkSize = readChunkSizeAfterTag();
         if (tag == "data") {

          
@@ 135,11 154,6 @@ SimpleWavFileReadStream::readHeader()
             }
         }
     }
-
-    // we don't use these
-    (void)totalSize;
-    (void)byteRate;
-    (void)bytesPerSample;
 }
 
 uint32_t

          
@@ 203,6 217,7 @@ SimpleWavFileReadStream::getFrames(size_
         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;
     }

          
@@ 238,10 253,26 @@ SimpleWavFileReadStream::convertSample24
     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 {};
+    if (!m_file) return 0;
 
     for (int i = 0; i < n; ++i) {
         char c;

          
@@ 271,5 302,4 @@ SimpleWavFileReadStream::le2int(const ve
 
 }
 
-
 #endif

          
M src/SimpleWavFileReadStream.h +3 -1
@@ 70,6 70,7 @@ private:
     int m_channels;
     int m_rate;
     int m_bitDepth;
+    bool m_floatSwap;
     size_t m_startPos;
     size_t m_index;
 

          
@@ 85,7 86,8 @@ private:
     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);
 };

          
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());