Browse Source

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.
pull/417/head
Vladimír Vondruš 6 years ago
parent
commit
2ff5d32156
  1. 6
      doc/changelog.dox
  2. 13
      src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt
  3. 48
      src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp
  4. BIN
      src/MagnumPlugins/WavAudioImporter/Test/mono16be.wav
  5. BIN
      src/MagnumPlugins/WavAudioImporter/Test/mono32fbe.wav
  6. BIN
      src/MagnumPlugins/WavAudioImporter/Test/stereo64fbe.wav
  7. 55
      src/MagnumPlugins/WavAudioImporter/WavImporter.cpp
  8. 3
      src/MagnumPlugins/WavAudioImporter/WavImporter.h

6
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

13
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)

48
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<Containers::ArrayView<const char>>);
}
void WavImporterTest::mono16BigEndian() {
Containers::Pointer<AbstractImporter> 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<char>{Containers::InPlaceInit, {
'\x1d', '\x10', '\x71', '\xc5'}}),
TestSuite::Compare::Container<Containers::ArrayView<const char>>);
}
void WavImporterTest::stereo4() {
std::ostringstream out;
Error redirectError{&out};
@ -389,6 +408,19 @@ void WavImporterTest::mono32f() {
TestSuite::Compare::Container<Containers::ArrayView<const char>>);
}
void WavImporterTest::mono32fBigEndian() {
Containers::Pointer<AbstractImporter> 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<char>{Containers::InPlaceInit, {
0, 0, 0, 0, 108, 57, -103, 59, 3, 63, 42, 60, -33, -81, -120, 60}}),
TestSuite::Compare::Container<Containers::ArrayView<const char>>);
}
void WavImporterTest::stereo32f() {
Containers::Pointer<AbstractImporter> 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<Containers::ArrayView<const char>>);
}
void WavImporterTest::stereo64fBigEndian() {
Containers::Pointer<AbstractImporter> 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<char>{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<Containers::ArrayView<const char>>);
}
void WavImporterTest::surround51Channel16() {
std::ostringstream out;
Error redirectError{&out};

BIN
src/MagnumPlugins/WavAudioImporter/Test/mono16be.wav

Binary file not shown.

BIN
src/MagnumPlugins/WavAudioImporter/Test/mono32fbe.wav

Binary file not shown.

BIN
src/MagnumPlugins/WavAudioImporter/Test/stereo64fbe.wav

Binary file not shown.

55
src/MagnumPlugins/WavAudioImporter/WavImporter.cpp

@ -28,7 +28,7 @@
#include <Corrade/Utility/Assert.h>
#include <Corrade/Utility/Debug.h>
#include <Corrade/Utility/Endianness.h>
#include <Corrade/Utility/EndiannessBatch.h>
#include "MagnumPlugins/WavAudioImporter/WavHeader.h"
@ -58,13 +58,19 @@ void WavImporter::doOpenData(Containers::ArrayView<const char> data) {
WavHeaderChunk header(*reinterpret_cast<const WavHeaderChunk*>(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<const char> 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<WavFormatChunk> formatChunk;
UnsignedInt dataChunkSize = 0;
const UnsignedInt headerSize = sizeof(WavHeaderChunk);
@ -83,15 +91,19 @@ void WavImporter::doOpenData(Containers::ArrayView<const char> data) {
/* Skip any chunks that aren't the format or data chunk */
while(headerSize + offset <= header.chunk.chunkSize) {
const RiffChunk* currChunk = reinterpret_cast<const RiffChunk*>(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<const WavFormatChunk*>(currChunk);
formatChunk = WavFormatChunk{*reinterpret_cast<const WavFormatChunk*>(currChunk)};
} else if(std::strncmp(currChunk->chunkId, "data", 4) == 0) {
if(dataChunk != nullptr) {
@ -100,14 +112,13 @@ void WavImporter::doOpenData(Containers::ArrayView<const char> 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<const char> 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<const char> 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<const char*>(dataChunk + 1);
_data = Containers::Array<char>(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<std::uint16_t>(*_data));
else if(formatChunk->bitsPerSample == 32)
Utility::Endianness::swapInPlace(Containers::arrayCast<std::uint32_t>(*_data));
else if(formatChunk->bitsPerSample == 64)
Utility::Endianness::swapInPlace(Containers::arrayCast<std::uint64_t>(*_data));
else CORRADE_INTERNAL_ASSERT(formatChunk->bitsPerSample == 8);
}
}
void WavImporter::doClose() { _data = Containers::NullOpt; }

3
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

Loading…
Cancel
Save