diff --git a/src/Magnum/Audio/Buffer.cpp b/src/Magnum/Audio/Buffer.cpp index 2ca64a6d0..705e406ab 100644 --- a/src/Magnum/Audio/Buffer.cpp +++ b/src/Magnum/Audio/Buffer.cpp @@ -38,10 +38,37 @@ Debug& operator<<(Debug& debug, const Buffer::Format value) { _c(Stereo8) _c(Stereo16) + _c(MonoALaw) + _c(StereoALaw) + + _c(MonoMuLaw) + _c(StereoMuLaw) + _c(MonoFloat) _c(StereoFloat) _c(MonoDouble) _c(StereoDouble) + + _c(Quad8) + _c(Quad16) + _c(Quad32) + + _c(Rear8) + _c(Rear16) + _c(Rear32) + + _c(Surround51Channel8) + _c(Surround51Channel16) + _c(Surround51Channel32) + + _c(Surround61Channel8) + _c(Surround61Channel16) + _c(Surround61Channel32) + + _c(Surround71Channel8) + _c(Surround71Channel16) + _c(Surround71Channel32) + #undef _c /* LCOV_EXCL_STOP */ } diff --git a/src/Magnum/Audio/Buffer.h b/src/Magnum/Audio/Buffer.h index 4f521fc21..09062e18d 100644 --- a/src/Magnum/Audio/Buffer.h +++ b/src/Magnum/Audio/Buffer.h @@ -57,6 +57,34 @@ class Buffer { Stereo8 = AL_FORMAT_STEREO8, /**< 8-bit interleaved unsigned stereo */ Stereo16 = AL_FORMAT_STEREO16, /**< 16-bit interleaved signed stereo */ + /** + * @brief 8-bit unsigned mono A-Law Compressed Sound Format + * + * @requires_al_extension Extension @al_extension{EXT,ALAW} + */ + MonoALaw = AL_FORMAT_MONO_ALAW_EXT, + + /** + * @brief 16-bit signed stereo A-Law Compressed Sound Format + * + * @requires_al_extension Extension @al_extension{EXT,ALAW} + */ + StereoALaw = AL_FORMAT_STEREO_ALAW_EXT, + + /** + * @brief 8-bit unsigned mono Mu-Law Compressed Sound Format + * + * @requires_al_extension Extension @al_extension{EXT,MULAW} + */ + MonoMuLaw = AL_FORMAT_MONO_MULAW_EXT, + + /** + * @brief 16-bit signed Mu-Law Compressed Sound Format + * + * @requires_al_extension Extension @al_extension{EXT,MULAW} + */ + StereoMuLaw = AL_FORMAT_STEREO_MULAW_EXT, + /** * @brief 32-bit floating-point mono * @@ -83,7 +111,95 @@ class Buffer { * * @requires_al_extension Extension @al_extension{EXT,double} */ - StereoDouble = AL_FORMAT_STEREO_DOUBLE_EXT + StereoDouble = AL_FORMAT_STEREO_DOUBLE_EXT, + + /** + * @brief 8-bit unsigned quadrophonic + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Quad8 = AL_FORMAT_QUAD8, + + /** + * @brief 16-bit signed quadrophonic + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Quad16 = AL_FORMAT_QUAD16, + + /** + * @brief 32-bit interleaved floating-point quadrophonic + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Quad32 = AL_FORMAT_QUAD32, + + Rear8 = AL_FORMAT_REAR8, + Rear16 = AL_FORMAT_REAR16, + Rear32 = AL_FORMAT_REAR32, + + /** + * @brief 8-bit unsigned 5.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround51Channel8 = AL_FORMAT_51CHN8, + + /** + * @brief 16-bit signed 5.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround51Channel16 = AL_FORMAT_51CHN16, + + /** + * @brief 32-bit interleaved floating-point 5.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround51Channel32 = AL_FORMAT_51CHN32, + + /** + * @brief 8-bit unsigned 6.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround61Channel8 = AL_FORMAT_61CHN8, + + /** + * @brief 16-bit signed 6.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround61Channel16 = AL_FORMAT_61CHN16, + + /** + * @brief 32-bit interleaved floating-point 6.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround61Channel32 = AL_FORMAT_61CHN32, + + /** + * @brief 8-bit unsigned 7.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround71Channel8 = AL_FORMAT_71CHN8, + + /** + * @brief 16-bit signed 7.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround71Channel16 = AL_FORMAT_71CHN16, + + /** + * @brief 32-bit interleaved floating-point 7.1 surround + * + * @requires_al_extension Extension @al_extension{EXT,MCFORMATS} + */ + Surround71Channel32 = AL_FORMAT_71CHN32 }; /** diff --git a/src/Magnum/Audio/Context.cpp b/src/Magnum/Audio/Context.cpp index 7ccb27479..df7d58779 100644 --- a/src/Magnum/Audio/Context.cpp +++ b/src/Magnum/Audio/Context.cpp @@ -44,6 +44,9 @@ const std::vector& Extension::extensions() { static const std::vector extensions{ _extension(AL,EXT,FLOAT32), _extension(AL,EXT,DOUBLE), + _extension(AL,EXT,ALAW), + _extension(AL,EXT,MULAW), + _extension(AL,EXT,MCFORMATS), _extension(ALC,EXT,ENUMERATION), _extension(ALC,SOFTX,HRTF), _extension(ALC,SOFT,HRTF) diff --git a/src/Magnum/Audio/Extensions.h b/src/Magnum/Audio/Extensions.h index 478903e56..b9993d6d0 100644 --- a/src/Magnum/Audio/Extensions.h +++ b/src/Magnum/Audio/Extensions.h @@ -79,6 +79,9 @@ namespace AL { namespace EXT { _extension(AL,EXT,FLOAT32) // #??? _extension(AL,EXT,DOUBLE) // #??? + _extension(AL,EXT,ALAW) // #??? + _extension(AL,EXT,MULAW) // #??? + _extension(AL,EXT,MCFORMATS) // #??? } } namespace ALC { namespace EXT { diff --git a/src/MagnumExternal/OpenAL/extensions.h b/src/MagnumExternal/OpenAL/extensions.h index 90da98455..5beb8bb39 100644 --- a/src/MagnumExternal/OpenAL/extensions.h +++ b/src/MagnumExternal/OpenAL/extensions.h @@ -52,6 +52,40 @@ extern "C" { #define AL_FORMAT_STEREO_DOUBLE_EXT 0x10013 #endif +/* AL_EXT_MULAW */ +#ifndef AL_EXT_MULAW +#define AL_EXT_MULAW 1 +#define AL_FORMAT_MONO_MULAW_EXT 0x10014 +#define AL_FORMAT_STEREO_MULAW_EXT 0x10015 +#endif + +/* AL_EXT_ALAW */ +#ifndef AL_EXT_ALAW +#define AL_EXT_ALAW 1 +#define AL_FORMAT_MONO_ALAW_EXT 0x10016 +#define AL_FORMAT_STEREO_ALAW_EXT 0x10017 +#endif + +/* AL_EXT_MCFORMATS */ +#ifndef AL_EXT_MCFORMATS +#define AL_EXT_MCFORMATS 1 +#define AL_FORMAT_QUAD8 0x1204 +#define AL_FORMAT_QUAD16 0x1205 +#define AL_FORMAT_QUAD32 0x1206 +#define AL_FORMAT_REAR8 0x1207 +#define AL_FORMAT_REAR16 0x1208 +#define AL_FORMAT_REAR32 0x1209 +#define AL_FORMAT_51CHN8 0x120A +#define AL_FORMAT_51CHN16 0x120B +#define AL_FORMAT_51CHN32 0x120C +#define AL_FORMAT_61CHN8 0x120D +#define AL_FORMAT_61CHN16 0x120E +#define AL_FORMAT_61CHN32 0x120F +#define AL_FORMAT_71CHN8 0x1210 +#define AL_FORMAT_71CHN16 0x1211 +#define AL_FORMAT_71CHN32 0x1212 +#endif + /* ALC_SOFTX_HRTF */ #ifndef ALC_SOFTX_HRTF #define ALC_SOFTX_HRTF 1 diff --git a/src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt b/src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt index 8ae4594f0..75aa77178 100644 --- a/src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt +++ b/src/MagnumPlugins/WavAudioImporter/Test/CMakeLists.txt @@ -36,8 +36,26 @@ corrade_add_test(WavAudioImporterTest WavImporterTest.cpp LIBRARIES MagnumWavAud target_include_directories(WavAudioImporterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) if(CORRADE_TARGET_EMSCRIPTEN) + + emscripten_embed_file(WavAudioImporterTest mono8.wav "/mono8.wav") + emscripten_embed_file(WavAudioImporterTest mono8.wav "/mono8junk.wav") + + emscripten_embed_file(WavAudioImporterTest mono8ALaw.wav "/mono8ALaw.wav") + emscripten_embed_file(WavAudioImporterTest mono8MuLaw.wav "/mono8MuLaw.wav") emscripten_embed_file(WavAudioImporterTest mono16.wav "/mono16.wav") + emscripten_embed_file(WavAudioImporterTest stereo8.wav "/stereo8.wav") + emscripten_embed_file(WavAudioImporterTest stereo8ALaw.wav "/stereo8ALaw.wav") + emscripten_embed_file(WavAudioImporterTest stereo8MuLaw.wav "/stereo8MuLaw.wav") + emscripten_embed_file(WavAudioImporterTest stereo16.wav "/stereo16.wav") + + emscripten_embed_file(WavAudioImporterTest mono32f.wav "/mono32f.wav") + emscripten_embed_file(WavAudioImporterTest stereo32f.wav "/stereo32f.wav") + + emscripten_embed_file(WavAudioImporterTest stereo64f.wav "/stereo64f.wav") + + emscripten_embed_file(WavAudioImporterTest surround616.wav "/surround616.wav") + emscripten_embed_file(WavAudioImporterTest unsupportedChannelCount.wav "/unsupportedChannelCount.wav") emscripten_embed_file(WavAudioImporterTest unsupportedFormat.wav "/unsupportedFormat.wav") emscripten_embed_file(WavAudioImporterTest wrongSignature.wav "/wrongSignature.wav") diff --git a/src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp b/src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp index 3896c67e2..fde31d088 100644 --- a/src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp +++ b/src/MagnumPlugins/WavAudioImporter/Test/WavImporterTest.cpp @@ -43,17 +43,45 @@ class WavImporterTest: public TestSuite::Tester { void wrongSignature(); void unsupportedFormat(); void unsupportedChannelCount(); + + void mono8(); + void mono8junk(); + void mono8ALaw(); + void mono8MuLaw(); void mono16(); + void stereo8(); + void stereo8ALaw(); + void stereo8MuLaw(); + void stereo16(); + + void mono32f(); + void stereo32f(); + void stereo64f(); + + void surround616(); }; WavImporterTest::WavImporterTest() { - addTests({&WavImporterTest::wrongSize, + addTests({ + + &WavImporterTest::wrongSize, &WavImporterTest::wrongSignature, &WavImporterTest::unsupportedFormat, &WavImporterTest::unsupportedChannelCount, + &WavImporterTest::mono8, + &WavImporterTest::mono8junk, + &WavImporterTest::mono8ALaw, + &WavImporterTest::mono8MuLaw, &WavImporterTest::mono16, - &WavImporterTest::stereo8}); + &WavImporterTest::stereo8, + &WavImporterTest::stereo8ALaw, + &WavImporterTest::stereo8MuLaw, + &WavImporterTest::stereo16, + &WavImporterTest::mono32f, + &WavImporterTest::stereo32f, + &WavImporterTest::stereo64f, + &WavImporterTest::surround616}); } void WavImporterTest::wrongSize() { @@ -92,6 +120,38 @@ void WavImporterTest::unsupportedChannelCount() { CORRADE_COMPARE(out.str(), "Audio::WavImporter::openData(): unsupported channel count 6 with 8 bits per sample\n"); } +void WavImporterTest::mono8() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "mono8.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::Mono8); + CORRADE_COMPARE(importer.frequency(), 22050); +} + +void WavImporterTest::mono8junk() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "mono8junk.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::Mono8); + CORRADE_COMPARE(importer.frequency(), 22050); +} + +void WavImporterTest::mono8ALaw() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "mono8ALaw.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::MonoALaw); + CORRADE_COMPARE(importer.frequency(), 8000); +} + +void WavImporterTest::mono8MuLaw() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "mono8MuLaw.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::MonoMuLaw); + CORRADE_COMPARE(importer.frequency(), 8000); +} + void WavImporterTest::mono16() { WavImporter importer; CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "mono16.wav"))); @@ -114,6 +174,65 @@ void WavImporterTest::stereo8() { TestSuite::Compare::Container); } +void WavImporterTest::stereo8ALaw() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "stereo8ALaw.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::StereoALaw); + CORRADE_COMPARE(importer.frequency(), 8000); +} + +void WavImporterTest::stereo8MuLaw() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "stereo8MuLaw.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::StereoMuLaw); + CORRADE_COMPARE(importer.frequency(), 8000); +} + + +void WavImporterTest::stereo16() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "stereo16.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::Stereo16); + CORRADE_COMPARE(importer.frequency(), 44100); +} + +void WavImporterTest::mono32f() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "mono32f.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::MonoFloat); + CORRADE_COMPARE(importer.frequency(), 48000); +} + +void WavImporterTest::stereo32f() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "stereo32f.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::StereoFloat); + CORRADE_COMPARE(importer.frequency(), 44100); +} + +void WavImporterTest::stereo64f() { + WavImporter importer; + CORRADE_VERIFY(importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "stereo64f.wav"))); + + CORRADE_COMPARE(importer.format(), Buffer::Format::StereoDouble); + CORRADE_COMPARE(importer.frequency(), 8000); +} + +void WavImporterTest::surround616() { + std::ostringstream out; + Error redirectError{&out}; + + WavImporter importer; + + CORRADE_VERIFY(!importer.openFile(Utility::Directory::join(WAVAUDIOIMPORTER_TEST_DIR, "surround616.wav"))); + CORRADE_COMPARE(out.str(), "Audio::WavImporter::openData(): unsupported audio format: extensible not implememented 65534\n"); +} + }}} CORRADE_TEST_MAIN(Magnum::Audio::Test::WavImporterTest) diff --git a/src/MagnumPlugins/WavAudioImporter/Test/mono32f.wav b/src/MagnumPlugins/WavAudioImporter/Test/mono32f.wav new file mode 100644 index 000000000..d9f2aa0ff Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/mono32f.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/mono8.wav b/src/MagnumPlugins/WavAudioImporter/Test/mono8.wav new file mode 100644 index 000000000..c6fb50669 Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/mono8.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/mono8ALaw.wav b/src/MagnumPlugins/WavAudioImporter/Test/mono8ALaw.wav new file mode 100644 index 000000000..bc3ff161e Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/mono8ALaw.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/mono8MuLaw.wav b/src/MagnumPlugins/WavAudioImporter/Test/mono8MuLaw.wav new file mode 100644 index 000000000..540ab29df Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/mono8MuLaw.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/mono8junk.wav b/src/MagnumPlugins/WavAudioImporter/Test/mono8junk.wav new file mode 100644 index 000000000..32480cbaa Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/mono8junk.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/stereo16.wav b/src/MagnumPlugins/WavAudioImporter/Test/stereo16.wav new file mode 100644 index 000000000..26b3d95de Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/stereo16.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/stereo32f.wav b/src/MagnumPlugins/WavAudioImporter/Test/stereo32f.wav new file mode 100644 index 000000000..e167301a8 Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/stereo32f.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/stereo64f.wav b/src/MagnumPlugins/WavAudioImporter/Test/stereo64f.wav new file mode 100644 index 000000000..ee9c65d25 Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/stereo64f.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/stereo8ALaw.wav b/src/MagnumPlugins/WavAudioImporter/Test/stereo8ALaw.wav new file mode 100644 index 000000000..9f24fc07c Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/stereo8ALaw.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/stereo8MuLaw.wav b/src/MagnumPlugins/WavAudioImporter/Test/stereo8MuLaw.wav new file mode 100644 index 000000000..9c7a1d16a Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/stereo8MuLaw.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/Test/surround616.wav b/src/MagnumPlugins/WavAudioImporter/Test/surround616.wav new file mode 100644 index 000000000..0e3802596 Binary files /dev/null and b/src/MagnumPlugins/WavAudioImporter/Test/surround616.wav differ diff --git a/src/MagnumPlugins/WavAudioImporter/WavHeader.h b/src/MagnumPlugins/WavAudioImporter/WavHeader.h index 4948fea35..8ed8642ae 100644 --- a/src/MagnumPlugins/WavAudioImporter/WavHeader.h +++ b/src/MagnumPlugins/WavAudioImporter/WavHeader.h @@ -33,28 +33,36 @@ namespace Magnum { namespace Audio { +#pragma pack(1) +/** @brief RIFF chunk */ +struct RiffChunk { + char chunkId[4]; /**< @brief chunk name (4 characters) */ + UnsignedInt chunkSize; /**< @brief size of chunk (does not include chunk header) */ +}; +#pragma pack() + #pragma pack(1) /** @brief WAV file header */ -struct WavHeader { - char chunkId[4]; /**< @brief `RIFF` characters */ - UnsignedInt chunkSize; /**< @brief Size of the rest of the file */ +struct WavHeaderChunk { + RiffChunk chunk; /**< @brief Starting RIFF chunk */ char format[4]; /**< @brief `WAVE` characters */ +}; +#pragma pack() - char subChunk1Id[4]; /**< @brief `fmt ` characters */ - UnsignedInt subChunk1Size; /**< @brief 16 for PCM */ +#pragma pack(1) +/** @brief WAV 'fmt' header */ +struct WavFormatChunk { + RiffChunk chunk; /**< @brief Starting RIFF chunk */ UnsignedShort audioFormat; /**< @brief 1 = PCM */ UnsignedShort numChannels; /**< @brief 1 = Mono, 2 = Stereo */ UnsignedInt sampleRate; /**< @brief Sample rate in Hz */ UnsignedInt byteRate; /**< @brief Bytes per second */ UnsignedShort blockAlign; /**< @brief Bytes per sample (all channels) */ UnsignedShort bitsPerSample; /**< @brief Bits per sample (one channel) */ - - char subChunk2Id[4]; /**< @brief `data` characters */ - UnsignedInt subChunk2Size; /**< @brief Size of the following data */ }; #pragma pack() -static_assert(sizeof(WavHeader) == 44, "WavHeader size is not 44 bytes"); +static_assert(sizeof(WavHeaderChunk) + sizeof(WavFormatChunk) + sizeof(RiffChunk) == 44, "WavHeader size is not 44 bytes"); }} diff --git a/src/MagnumPlugins/WavAudioImporter/WavImporter.cpp b/src/MagnumPlugins/WavAudioImporter/WavImporter.cpp index aa001e105..82630c883 100644 --- a/src/MagnumPlugins/WavAudioImporter/WavImporter.cpp +++ b/src/MagnumPlugins/WavAudioImporter/WavImporter.cpp @@ -31,6 +31,12 @@ #include "MagnumPlugins/WavAudioImporter/WavHeader.h" +#define WAVE_FORMAT_PCM 0x0001 +#define WAVE_FORMAT_IEEE_FLOAT 0x0003 +#define WAVE_FORMAT_ALAW 0x0006 +#define WAVE_FORMAT_MULAW 0x0007 +#define WAVE_FORMAT_EXTENSIBLE 0xFFFE + namespace Magnum { namespace Audio { WavImporter::WavImporter() = default; @@ -43,74 +49,173 @@ bool WavImporter::doIsOpened() const { return _data; } void WavImporter::doOpenData(Containers::ArrayView data) { /* Check file size */ - if(data.size() < sizeof(WavHeader)) { + if(data.size() < sizeof(WavHeaderChunk) + sizeof(WavFormatChunk) + sizeof(RiffChunk)) { Error() << "Audio::WavImporter::openData(): the file is too short:" << data.size() << "bytes"; return; } - /* Get header contents and fix endianness */ - WavHeader header(*reinterpret_cast(data.begin())); - Utility::Endianness::littleEndianInPlace(header.chunkSize, - header.subChunk1Size, header.audioFormat, header.numChannels, - header.sampleRate, header.byteRate, header.blockAlign, - header.bitsPerSample, header.subChunk2Size); - - /* Check file signature */ - if(std::strncmp(header.chunkId, "RIFF", 4) != 0 || - std::strncmp(header.format, "WAVE", 4) != 0 || - std::strncmp(header.subChunk1Id, "fmt ", 4) != 0 || - std::strncmp(header.subChunk2Id, "data", 4) != 0) { + /* Get the RIFF/WAV header */ + WavHeaderChunk header(*reinterpret_cast(data.begin())); + + /* Check RIFF/WAV file signature */ + if(std::strncmp(header.chunk.chunkId, "RIFF", 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 file size */ - if(header.chunkSize + 8 != data.size()) { + if(header.chunk.chunkSize < 36 || header.chunk.chunkSize + 8 != data.size()) { Error() << "Audio::WavImporter::openData(): the file has improper size, expected" - << header.chunkSize + 8 << "but got" << data.size(); + << header.chunk.chunkSize + 8 << "but got" << data.size(); + return; + } + + const RiffChunk* dataChunk = nullptr; + const WavFormatChunk* formatChunk = nullptr; + UnsignedInt dataChunkSize = 0; + + const UnsignedInt headerSize = sizeof(WavHeaderChunk); + UnsignedInt offset = 0; + + /* 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); + + if(std::strncmp(currChunk->chunkId, "fmt ", 4) == 0) { + if(formatChunk != nullptr) { + Error() << "Audio::WavImporter::openData(): the file contains too many format chunks"; + return; + } + + formatChunk = reinterpret_cast(currChunk); + + } else if(std::strncmp(currChunk->chunkId, "data", 4) == 0) { + if(dataChunk != nullptr) { + Error() << "Audio::WavImporter::openData(): the file contains too many data chunks"; + return; + } + + dataChunk = currChunk; + dataChunkSize = Utility::Endianness::littleEndian(currChunk->chunkSize); + + break; + } + } + + /* Make sure we actually got a format chunk */ + if(formatChunk == nullptr) { + Error() << "Audio::WavImporter::openData(): the file contains no format chunk"; + return; + } + + /* Make sure we actually got a data chunk */ + if(dataChunk == nullptr) { + Error() << "Audio::WavImporter::openData(): the file contains no data chunk"; return; } + /* Fix endianness on Format chunk */ + Utility::Endianness::littleEndianInPlace( + formatChunk->chunk.chunkSize, formatChunk->audioFormat, formatChunk->numChannels, + formatChunk->sampleRate, formatChunk->byteRate, formatChunk->blockAlign, + formatChunk->bitsPerSample); + /* Check PCM format */ - if(header.audioFormat != 1) { - Error() << "Audio::WavImporter::openData(): unsupported audio format" << header.audioFormat; + if(formatChunk->audioFormat == WAVE_FORMAT_PCM) { + /* Decide about format */ + if(formatChunk->numChannels == 1 && formatChunk->bitsPerSample == 8) + _format = Buffer::Format::Mono8; + else if(formatChunk->numChannels == 1 && formatChunk->bitsPerSample == 16) + _format = Buffer::Format::Mono16; + else if(formatChunk->numChannels == 2 && formatChunk->bitsPerSample == 8) + _format = Buffer::Format::Stereo8; + else if(formatChunk->numChannels == 2 && formatChunk->bitsPerSample == 16) + _format = Buffer::Format::Stereo16; + else { + Error() << "Audio::WavImporter::openData(): unsupported channel count" + << formatChunk->numChannels << "with" << formatChunk->bitsPerSample + << "bits per sample"; + return; + } + /* Check IEEE Float format */ + } else if(formatChunk->audioFormat == WAVE_FORMAT_IEEE_FLOAT) { + if(formatChunk->numChannels == 1 && formatChunk->bitsPerSample == 32) + _format = Buffer::Format::MonoFloat; + else if(formatChunk->numChannels == 2 && formatChunk->bitsPerSample == 32) + _format = Buffer::Format::StereoFloat; + else if(formatChunk->numChannels == 1 && formatChunk->bitsPerSample == 64) + _format = Buffer::Format::MonoDouble; + else if(formatChunk->numChannels == 2 && formatChunk->bitsPerSample == 64) + _format = Buffer::Format::StereoDouble; + else { + Error() << "Audio::WavImporter::openData(): unsupported channel count" + << formatChunk->numChannels << "with" << formatChunk->bitsPerSample + << "bits per sample"; + return; + } + /* Check ALAW format */ + } else if(formatChunk->audioFormat == WAVE_FORMAT_ALAW) { + if(formatChunk->numChannels == 1) + _format = Buffer::Format::MonoALaw; + else if(formatChunk->numChannels == 2) + _format = Buffer::Format::StereoALaw; + else { + Error() << "Audio::WavImporter::openData(): unsupported channel count" + << formatChunk->numChannels << "with" << formatChunk->bitsPerSample + << "bits per sample"; + return; + } + /* Check MULAW format */ + } else if(formatChunk->audioFormat == WAVE_FORMAT_MULAW) { + if(formatChunk->numChannels == 1) + _format = Buffer::Format::MonoMuLaw; + else if(formatChunk->numChannels == 2) + _format = Buffer::Format::StereoMuLaw; + else { + Error() << "Audio::WavImporter::openData(): unsupported channel count" + << formatChunk->numChannels << "with" << formatChunk->bitsPerSample + << "bits per sample"; + return; + } + /* We do not currently support EXTENSIBLE formats */ + } else if(formatChunk->audioFormat == WAVE_FORMAT_EXTENSIBLE) { + Error() << "Audio::WavImporter::openData(): unsupported audio format: extensible not implememented" << formatChunk->audioFormat; + return; + /* Unknown format */ + } else { + Error() << "Audio::WavImporter::openData(): unsupported audio format" << formatChunk->audioFormat; return; } - /* Verify more things */ - if(header.subChunk1Size != 16 || - header.subChunk2Size + 44 != data.size() || - header.blockAlign != header.numChannels*header.bitsPerSample/8 || - header.byteRate != header.sampleRate*header.blockAlign) { - Error() << "Audio::WavImporter::openData(): the file is corrupted"; + /* Size sanity checks */ + if(headerSize + offset > data.size()) { + Error() << "Audio::WavImporter::openData(): file size doesn't match computed size"; return; } - /* Decide about format */ - if(header.numChannels == 1 && header.bitsPerSample == 8) - _format = Buffer::Format::Mono8; - else if(header.numChannels == 1 && header.bitsPerSample == 16) - _format = Buffer::Format::Mono16; - else if(header.numChannels == 2 && header.bitsPerSample == 8) - _format = Buffer::Format::Stereo8; - else if(header.numChannels == 2 && header.bitsPerSample == 16) - _format = Buffer::Format::Stereo16; - else { - Error() << "Audio::WavImporter::openData(): unsupported channel count" - << header.numChannels << "with" << header.bitsPerSample - << "bits per sample"; + /* Format sanity checks */ + if(formatChunk->blockAlign != formatChunk->numChannels * formatChunk->bitsPerSample / 8 || + formatChunk->byteRate != formatChunk->sampleRate * formatChunk->blockAlign) { + Error() << "Audio::WavImporter::openData(): the file is corrupted"; return; } + + /* Save frequency */ - _frequency = header.sampleRate; + _frequency = formatChunk->sampleRate; /** @todo Convert the data from little endian too */ CORRADE_INTERNAL_ASSERT(!Utility::Endianness::isBigEndian()); /* Copy the data */ - _data = Containers::Array(header.subChunk2Size); - std::copy(data.begin()+sizeof(WavHeader), data.end(), _data.begin()); + const char* dataChunkPtr = reinterpret_cast(dataChunk + 1); + _data = Containers::Array(dataChunkSize); + std::copy(dataChunkPtr, dataChunkPtr+dataChunkSize, _data.begin()); return; }