diff --git a/wav/waveaudiostream.cpp b/wav/waveaudiostream.cpp index 9466ca7..fb1d016 100644 --- a/wav/waveaudiostream.cpp +++ b/wav/waveaudiostream.cpp @@ -24,12 +24,80 @@ namespace TagParser { */ void WaveFormatHeader::parse(IoUtilities::BinaryReader &reader) { + Diagnostics diag; + parseExt(reader, 16, diag); +} + +/*! + * \brief Parses the WAVE "fmt " header segment using the specified \a reader. + * \returns Returns the detected media format and the number of bytes read. + * \todo + * - Make sampleRate and bytesPerSecond 32-bit in v9. + * - Make GUID a field of WaveFormatHeader instead of returning the MediaFormat in v9. + * - Replace parse() function with this. + */ +pair WaveFormatHeader::parseExt(IoUtilities::BinaryReader &reader, uint64 maxSize, Diagnostics &diag) +{ + auto result = make_pair(GeneralMediaFormat::Unknown, 0); + if (maxSize < 16) { + diag.emplace_back(DiagLevel::Warning, "\"fmt \" segment is truncated.", "parsing WAVE format header"); + return result; + } + formatTag = reader.readUInt16LE(); + result.first = format(); channelCount = reader.readUInt16LE(); sampleRate = reader.readUInt32LE(); bytesPerSecond = reader.readUInt32LE(); chunkSize = reader.readUInt16LE(); bitsPerSample = reader.readUInt16LE(); + result.second = 16; + + // read extended header unless format is PCM + if (result.first.general == GeneralMediaFormat::Pcm) { + return result; + } + if ((maxSize -= 16) < 2) { + diag.emplace_back(DiagLevel::Warning, "\"fmt \" segment is truncated (extended header missing).", "parsing WAVE format header"); + return result; + } + const auto extensionSize = reader.readUInt16LE(); + result.second += 2; + if ((maxSize -= 2) < 2) { + diag.emplace_back(DiagLevel::Warning, "\"fmt \" segment is truncated (extended header truncated).", "parsing WAVE format header"); + return result; + } + + // skip extended header unless format is "WAVE_FORMAT_EXTENSIBLE" + if (formatTag != 65534) { + reader.stream()->seekg(extensionSize, ios_base::cur); + result.second += extensionSize; + return result; + } + + // read extended header for "WAVE_FORMAT_EXTENSIBLE" + if (extensionSize != 22) { + diag.emplace_back(DiagLevel::Warning, "\"fmt \" extended header has unexptected size.", "parsing WAVE format header"); + return result; + } + bitsPerSample = reader.readUInt16LE(); + reader.stream()->seekg(4, ios_base::cur); // skip channel mask + const auto guid1 = reader.readUInt64BE(); + const auto guid2 = reader.readUInt64BE(); + result.second += 22; + switch (guid2) { + case 0x000800000aa00389b71: + switch (guid1) { + case 0x0100000000001000ul: + result.first = MediaFormat(GeneralMediaFormat::Pcm, SubFormats::PcmIntLe); + break; + case 0x0300000000001000ul: + result.first = MediaFormat(GeneralMediaFormat::Pcm, SubFormats::PcmFloatIeee); + break; + } + break; + } + return result; } /*! @@ -39,7 +107,9 @@ MediaFormat WaveFormatHeader::format() const { switch (formatTag) { case 0x0001u: - return GeneralMediaFormat::Pcm; + return MediaFormat(GeneralMediaFormat::Pcm, SubFormats::PcmIntLe); + case 0x0003u: + return MediaFormat(GeneralMediaFormat::Pcm, SubFormats::PcmFloatIeee); case 0x0050u: return MediaFormat(GeneralMediaFormat::Mpeg1Audio, SubFormats::Mpeg1Layer2); case 0x0055u: @@ -82,7 +152,6 @@ TrackType WaveAudioStream::type() const */ void WaveAudioStream::addInfo(const WaveFormatHeader &waveHeader, AbstractTrack &track) { - track.m_format = waveHeader.format(); track.m_formatId = ConversionUtilities::numberToString(waveHeader.formatTag); track.m_channelCount = waveHeader.channelCount; track.m_samplingFrequency = waveHeader.sampleRate; @@ -106,20 +175,17 @@ void WaveAudioStream::internalParseHeader(Diagnostics &diag) throw NoDataFoundException(); } while (!m_dataOffset) { - uint32 segmentId = m_reader.readUInt32BE(); - uint32 restHeaderLen = m_reader.readUInt32LE(); + const auto segmentId = m_reader.readUInt32BE(); + auto restHeaderLen = m_reader.readUInt32LE(); switch (segmentId) { - case 0x666D7420u: - if (restHeaderLen >= 16u) { - WaveFormatHeader waveHeader; - waveHeader.parse(m_reader); - addInfo(waveHeader, *this); - restHeaderLen -= 16u; - } else { - diag.emplace_back(DiagLevel::Warning, "\"fmt \" segment is truncated.", context); - } - break; - case 0x64617461u: + case 0x666D7420u: { // format segment + WaveFormatHeader waveHeader; + uint64 bytesRead; + tie(m_format, bytesRead) = waveHeader.parseExt(m_reader, restHeaderLen, diag); + addInfo(waveHeader, *this); + restHeaderLen -= bytesRead; + } break; + case 0x64617461u: // data segment m_dataOffset = static_cast(m_istream->tellg()); m_size = restHeaderLen; m_sampleCount = m_size / m_chunkSize; diff --git a/wav/waveaudiostream.h b/wav/waveaudiostream.h index cc65b3d..983c7cd 100644 --- a/wav/waveaudiostream.h +++ b/wav/waveaudiostream.h @@ -10,6 +10,7 @@ public: constexpr WaveFormatHeader(); void parse(IoUtilities::BinaryReader &reader); + std::pair parseExt(IoUtilities::BinaryReader &reader, uint64 maxSize, Diagnostics &diag); MediaFormat format() const; constexpr uint32 bitrate() const;