#include "PommeSound.h" #include "Utilities/bigendianstreams.h" #include static void AIFFAssert(bool condition, const char* message) { if (!condition) { throw std::runtime_error(message); } } void Pomme::Sound::ReadAIFF(std::istream& input, cmixer::WavStream& output) { BigEndianIStream f(input); AIFFAssert('FORM' == f.Read(), "AIFF: invalid FORM"); auto formSize = f.Read(); auto endOfForm = f.Tell() + std::streampos(formSize); auto formType = f.Read(); AIFFAssert(formType == 'AIFF' || formType == 'AIFC', "AIFF: not an AIFF or AIFC file"); // COMM chunk contents int nChannels = 0; int nPackets = 0; int bitDepth = 0; int sampleRate = 0; uint32_t compressionType = 'NONE'; bool gotCOMM = false; while (f.Tell() != endOfForm) { auto ckID = f.Read(); auto ckSize = f.Read(); std::streampos endOfChunk = f.Tell() + std::streampos(ckSize); switch (ckID) { case 'FVER': { auto timestamp = f.Read(); AIFFAssert(timestamp == 0xA2805140u, "AIFF: unrecognized FVER"); break; } case 'COMM': // common chunk, 2-85 { nChannels = f.Read(); nPackets = f.Read(); bitDepth = f.Read(); sampleRate = (int)f.Read80BitFloat(); if (formType == 'AIFC') { compressionType = f.Read(); f.ReadPascalString(); // This is a human-friendly compression name. Skip it. } gotCOMM = true; break; } case 'SSND': { AIFFAssert(gotCOMM, "AIFF: reached SSND before COMM"); AIFFAssert(0 == f.Read(), "AIFF: unexpected offset/blockSize in SSND"); // sampled sound data is here const int ssndSize = ckSize - 8; if (compressionType == 'NONE') { // Raw big-endian PCM -- just init the WavStream without decoding auto spanOut = output.GetBuffer(ssndSize); f.Read(spanOut.data(), ssndSize); output.Init(sampleRate, bitDepth, nChannels, true, spanOut); } else { auto ssnd = std::vector(ssndSize); f.Read(ssnd.data(), ssndSize); auto codec = Pomme::Sound::GetCodec(compressionType); auto spanIn = std::span(ssnd); auto spanOut = output.GetBuffer(nChannels * nPackets * codec->SamplesPerPacket() * 2); codec->Decode(nChannels, spanIn, spanOut); output.Init(sampleRate, 16, nChannels, false, spanOut); } break; } default: f.Goto(int(endOfChunk)); break; } AIFFAssert(f.Tell() == endOfChunk, "AIFF: incorrect end-of-chunk position"); } }