From 2ff5d32156f56cc35ae6f0be38c85450af5e1439 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 10 Jan 2020 17:38:06 +0100 Subject: [PATCH] WavAudioImporter: make it work on Big-Endian platforms, support RIFX. With the RIFX support I can test for BE support on a LE platform, that's why. Otherwise I wouldn't bother. --- doc/changelog.dox | 6 ++ .../WavAudioImporter/Test/CMakeLists.txt | 13 +++++ .../WavAudioImporter/Test/WavImporterTest.cpp | 48 +++++++++++++++ .../WavAudioImporter/Test/mono16be.wav | Bin 0 -> 48 bytes .../WavAudioImporter/Test/mono32fbe.wav | Bin 0 -> 60 bytes .../WavAudioImporter/Test/stereo64fbe.wav | Bin 0 -> 122 bytes .../WavAudioImporter/WavImporter.cpp | 55 ++++++++++++------ .../WavAudioImporter/WavImporter.h | 3 + 8 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 src/MagnumPlugins/WavAudioImporter/Test/mono16be.wav create mode 100755 src/MagnumPlugins/WavAudioImporter/Test/mono32fbe.wav create mode 100755 src/MagnumPlugins/WavAudioImporter/Test/stereo64fbe.wav diff --git a/doc/changelog.dox b/doc/changelog.dox index e3c8791a3..5829aa620 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -109,6 +109,12 @@ See also: - @ref Resource is now nothrow-movable and thus can be used with growable @ref Corrade::Containers::Array instances +@subsubsection changelog-latest-changes-audio Audio library + +- @ref Audio::WavImporter "WavAudioImporter" now supports also Big-Endian + `RIFX` files and was fixed to work on Big-Endian platforms (see + [mosra/corrade#87](https://github.com/mosra/corrade/issues/87)) + @subsubsection changelog-latest-changes-gl GL library - Added @ref GL::AbstractTexture::bind() and diff --git a/src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt b/src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt index fe74383ed..e7e381887 100644 --- a/src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt @@ -57,12 +57,23 @@ corrade_add_test(WavAudioImporterTest WavImporterTest.cpp zeroSamples.wav + # The *be.wav files are created with artisanal bit-crafting from the + # little-endian counterparts and a good portion of wishful thinking + # (I can only hope those are correct), because ffpeg can't create them + # on its own: + # + # $ ffmpeg -i mono16.wav -f wav -acodec pcm_s16be mono16be.wav + # ... + # [wav @ 0x7fbdd301a200] pcm_s32be codec not supported in WAVE format + # Could not write header for output file #0 (incorrect codec parameters ?): Function not implemented + mono4.wav mono8.wav mono8junk.wav mono8ALaw.wav mono8MuLaw.wav mono16.wav + mono16be.wav mono24.wav stereo4.wav @@ -75,8 +86,10 @@ corrade_add_test(WavAudioImporterTest WavImporterTest.cpp stereo32.wav mono32f.wav + mono32fbe.wav stereo32f.wav stereo64f.wav + stereo64fbe.wav surround51Channel16.wav surround71Channel24.wav) diff --git a/src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp b/src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp index 473256ade..14fcbea49 100644 --- a/src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp +++ b/src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp @@ -58,6 +58,7 @@ struct WavImporterTest: TestSuite::Tester { void mono8ALaw(); void mono8MuLaw(); void mono16(); + void mono16BigEndian(); void stereo4(); void stereo8(); @@ -69,8 +70,10 @@ struct WavImporterTest: TestSuite::Tester { void stereo32(); void mono32f(); + void mono32fBigEndian(); void stereo32f(); void stereo64f(); + void stereo64fBigEndian(); void surround51Channel16(); void surround71Channel24(); @@ -98,6 +101,7 @@ WavImporterTest::WavImporterTest() { &WavImporterTest::mono8ALaw, &WavImporterTest::mono8MuLaw, &WavImporterTest::mono16, + &WavImporterTest::mono16BigEndian, &WavImporterTest::stereo4, &WavImporterTest::stereo8, @@ -109,8 +113,10 @@ WavImporterTest::WavImporterTest() { &WavImporterTest::stereo32, &WavImporterTest::mono32f, + &WavImporterTest::mono32fBigEndian, &WavImporterTest::stereo32f, &WavImporterTest::stereo64f, + &WavImporterTest::stereo64fBigEndian, &WavImporterTest::surround51Channel16, &WavImporterTest::surround71Channel24}); @@ -286,6 +292,19 @@ void WavImporterTest::mono16() { TestSuite::Compare::Container>); } +void WavImporterTest::mono16BigEndian() { + Containers::Pointer importer = _manager.instantiate("WavAudioImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "mono16be.wav"))); + + CORRADE_COMPARE(importer->format(), BufferFormat::Mono16); + CORRADE_COMPARE(importer->frequency(), 44000); + + CORRADE_COMPARE_AS(importer->data(), + (Containers::Array{Containers::InPlaceInit, { + '\x1d', '\x10', '\x71', '\xc5'}}), + TestSuite::Compare::Container>); +} + void WavImporterTest::stereo4() { std::ostringstream out; Error redirectError{&out}; @@ -389,6 +408,19 @@ void WavImporterTest::mono32f() { TestSuite::Compare::Container>); } +void WavImporterTest::mono32fBigEndian() { + Containers::Pointer importer = _manager.instantiate("WavAudioImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "mono32fbe.wav"))); + + CORRADE_COMPARE(importer->format(), BufferFormat::MonoFloat); + CORRADE_COMPARE(importer->frequency(), 48000); + + CORRADE_COMPARE_AS(importer->data(), + (Containers::Array{Containers::InPlaceInit, { + 0, 0, 0, 0, 108, 57, -103, 59, 3, 63, 42, 60, -33, -81, -120, 60}}), + TestSuite::Compare::Container>); +} + void WavImporterTest::stereo32f() { Containers::Pointer importer = _manager.instantiate("WavAudioImporter"); CORRADE_VERIFY(importer->openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "stereo32f.wav"))); @@ -420,6 +452,22 @@ void WavImporterTest::stereo64f() { TestSuite::Compare::Container>); } +void WavImporterTest::stereo64fBigEndian() { + Containers::Pointer importer = _manager.instantiate("WavAudioImporter"); + CORRADE_VERIFY(importer->openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "stereo64fbe.wav"))); + + CORRADE_COMPARE(importer->format(), BufferFormat::StereoDouble); + CORRADE_COMPARE(importer->frequency(), 8000); + + CORRADE_COMPARE_AS(importer->data(), + (Containers::Array{Containers::InPlaceInit, { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, 16, 63, + 0, 0, 0, 0, 0, 0, 24, -65, 0, 0, 0, 0, 0, 0, 0, 0}}), + TestSuite::Compare::Container>); +} + void WavImporterTest::surround51Channel16() { std::ostringstream out; Error redirectError{&out}; diff --git a/src/MagnumPlugins/WavAudioImporter/Test/mono16be.wav b/src/MagnumPlugins/WavAudioImporter/Test/mono16be.wav new file mode 100644 index 0000000000000000000000000000000000000000..a1002e73a43c26c111de514abe9c2adf7aa08bb1 GIT binary patch literal 48 zcmWIYbcWA40BD(El~ing&3F_m>3x39T*tDFfa%(I504zB_@{uWmt|zGccqi XmLvk%KvDd_9x81w0AcKxKvfR_01^xW literal 0 HcmV?d00001 diff --git a/src/MagnumPlugins/WavAudioImporter/WavImporter.cpp b/src/MagnumPlugins/WavAudioImporter/WavImporter.cpp index dc7fac1a3..ebfb2db29 100644 --- a/src/MagnumPlugins/WavAudioImporter/WavImporter.cpp +++ b/src/MagnumPlugins/WavAudioImporter/WavImporter.cpp @@ -28,7 +28,7 @@ #include #include -#include +#include #include "MagnumPlugins/WavAudioImporter/WavHeader.h" @@ -58,13 +58,19 @@ void WavImporter::doOpenData(Containers::ArrayView data) { WavHeaderChunk header(*reinterpret_cast(data.begin())); /* Check RIFF/WAV file signature */ - if(std::strncmp(header.chunk.chunkId, "RIFF", 4) != 0 || + if((std::strncmp(header.chunk.chunkId, "RIFF", 4) != 0 && std::strncmp(header.chunk.chunkId, "RIFX", 4) != 0) || std::strncmp(header.format, "WAVE", 4) != 0) { Error() << "Audio::WavImporter::openData(): the file signature is invalid"; return; } - Utility::Endianness::littleEndianInPlace(header.chunk.chunkSize); + /* Check if the file is Big-Endian. While RIFX files are extremely rare, + this actually allows us to test Big-Endian platform support on LE + platforms. So not a cruft, useful! */ + const bool hasBigEndianData = std::strncmp(header.chunk.chunkId, "RIFX", 4) == 0; + + if(hasBigEndianData != Utility::Endianness::isBigEndian()) + Utility::Endianness::swapInPlace(header.chunk.chunkSize); /* Check file size */ if(header.chunk.chunkSize < 36 || header.chunk.chunkSize + 8 != data.size()) { @@ -74,7 +80,9 @@ void WavImporter::doOpenData(Containers::ArrayView data) { } const RiffChunk* dataChunk = nullptr; - const WavFormatChunk* formatChunk = nullptr; + /* We're doing endian-swapping on this, thus can't be just a reference to + the original data */ + Containers::Optional formatChunk; UnsignedInt dataChunkSize = 0; const UnsignedInt headerSize = sizeof(WavHeaderChunk); @@ -83,15 +91,19 @@ void WavImporter::doOpenData(Containers::ArrayView data) { /* Skip any chunks that aren't the format or data chunk */ while(headerSize + offset <= header.chunk.chunkSize) { const RiffChunk* currChunk = reinterpret_cast(data.begin() + headerSize + offset); - offset += Utility::Endianness::littleEndian(currChunk->chunkSize) + sizeof(RiffChunk); + UnsignedInt chunkSize = currChunk->chunkSize; + if(hasBigEndianData != Utility::Endianness::isBigEndian()) + Utility::Endianness::swapInPlace(chunkSize); + + offset += chunkSize + sizeof(RiffChunk); if(std::strncmp(currChunk->chunkId, "fmt ", 4) == 0) { - if(formatChunk != nullptr) { + if(formatChunk) { Error() << "Audio::WavImporter::openData(): the file contains too many format chunks"; return; } - formatChunk = reinterpret_cast(currChunk); + formatChunk = WavFormatChunk{*reinterpret_cast(currChunk)}; } else if(std::strncmp(currChunk->chunkId, "data", 4) == 0) { if(dataChunk != nullptr) { @@ -100,14 +112,13 @@ void WavImporter::doOpenData(Containers::ArrayView data) { } dataChunk = currChunk; - dataChunkSize = Utility::Endianness::littleEndian(currChunk->chunkSize); - + dataChunkSize = chunkSize; break; } } /* Make sure we actually got a format chunk */ - if(formatChunk == nullptr) { + if(!formatChunk) { Error() << "Audio::WavImporter::openData(): the file contains no format chunk"; return; } @@ -119,10 +130,12 @@ void WavImporter::doOpenData(Containers::ArrayView data) { } /* Fix endianness on Format chunk */ - Utility::Endianness::littleEndianInPlace( - formatChunk->chunk.chunkSize, formatChunk->audioFormat, formatChunk->numChannels, - formatChunk->sampleRate, formatChunk->byteRate, formatChunk->blockAlign, - formatChunk->bitsPerSample); + if(hasBigEndianData != Utility::Endianness::isBigEndian()) + Utility::Endianness::swapInPlace( + formatChunk->chunk.chunkSize, formatChunk->audioFormat, + formatChunk->numChannels, formatChunk->sampleRate, + formatChunk->byteRate, formatChunk->blockAlign, + formatChunk->bitsPerSample); /* Check PCM format */ if(formatChunk->audioFormat == WavAudioFormat::Pcm) { @@ -207,13 +220,21 @@ void WavImporter::doOpenData(Containers::ArrayView data) { /* Save frequency */ _frequency = formatChunk->sampleRate; - /** @todo Convert the data from little endian too */ - CORRADE_INTERNAL_ASSERT(!Utility::Endianness::isBigEndian()); - /* Copy the data */ const char* dataChunkPtr = reinterpret_cast(dataChunk + 1); _data = Containers::Array(dataChunkSize); std::copy(dataChunkPtr, dataChunkPtr+dataChunkSize, _data->begin()); + + /* Fix the data endianness */ + if(hasBigEndianData != Utility::Endianness::isBigEndian()) { + if(formatChunk->bitsPerSample == 16) + Utility::Endianness::swapInPlace(Containers::arrayCast(*_data)); + else if(formatChunk->bitsPerSample == 32) + Utility::Endianness::swapInPlace(Containers::arrayCast(*_data)); + else if(formatChunk->bitsPerSample == 64) + Utility::Endianness::swapInPlace(Containers::arrayCast(*_data)); + else CORRADE_INTERNAL_ASSERT(formatChunk->bitsPerSample == 8); + } } void WavImporter::doClose() { _data = Containers::NullOpt; } diff --git a/src/MagnumPlugins/WavAudioImporter/WavImporter.h b/src/MagnumPlugins/WavAudioImporter/WavImporter.h index efbc2e19e..896ed05f0 100644 --- a/src/MagnumPlugins/WavAudioImporter/WavImporter.h +++ b/src/MagnumPlugins/WavAudioImporter/WavImporter.h @@ -73,6 +73,9 @@ Supports mono and stereo files of the following formats: - A-Law, imported as @ref BufferFormat::MonoALaw / @ref BufferFormat::StereoALaw - μ-Law, imported as @ref BufferFormat::MonoMuLaw / @ref BufferFormat::StereoMuLaw +Both Little-Endian files (with a `RIFF` header) and Big-Endian files (with +a `RIFX` header) are supported, data is converted to machine endian on import. + @section Audio-WavImporter-usage Usage This plugin is built if `WITH_WAVAUDIOIMPORTER` is enabled when building