ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

aiff.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 /*
00024  * The code in this file is based on information found at
00025  * http://www.borg.com/~jglatt/tech/aiff.htm
00026  *
00027  * Also partially based on libav's aiffdec.c
00028  */
00029 
00030 #include "common/debug.h"
00031 #include "common/endian.h"
00032 #include "common/stream.h"
00033 #include "common/substream.h"
00034 #include "common/textconsole.h"
00035 
00036 #include "audio/audiostream.h"
00037 #include "audio/decoders/aiff.h"
00038 #include "audio/decoders/raw.h"
00039 #include "audio/decoders/3do.h"
00040 
00041 namespace Audio {
00042 
00043 uint32 readExtended(Common::SeekableReadStream &stream) {
00044     // The sample rate is stored as an "80 bit IEEE Standard 754 floating
00045     // point number (Standard Apple Numeric Environment [SANE] data type
00046     // Extended).
00047 
00048     byte buf[10];
00049     uint32 mantissa;
00050     uint32 last = 0;
00051     byte exp;
00052 
00053     stream.read(buf, 10);
00054     mantissa = READ_BE_UINT32(buf + 2);
00055     exp = 30 - buf[1];
00056 
00057     while (exp--) {
00058         last = mantissa;
00059         mantissa >>= 1;
00060     }
00061 
00062     if (last & 0x00000001)
00063         mantissa++;
00064 
00065     return mantissa;
00066 }
00067 
00068 // AIFF versions
00069 static const uint32 kVersionAIFF = MKTAG('A', 'I', 'F', 'F');
00070 static const uint32 kVersionAIFC = MKTAG('A', 'I', 'F', 'C');
00071 
00072 // Codecs
00073 static const uint32 kCodecPCM = MKTAG('N', 'O', 'N', 'E'); // very original
00074 
00075 RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
00076     if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M')) {
00077         warning("makeAIFFStream: No 'FORM' header");
00078 
00079         if (disposeAfterUse == DisposeAfterUse::YES)
00080             delete stream;
00081 
00082         return 0;
00083     }
00084 
00085     stream->readUint32BE(); // file size
00086 
00087     uint32 version = stream->readUint32BE();
00088 
00089     if (version != kVersionAIFF && version != kVersionAIFC) {
00090         warning("makeAIFFStream: No 'AIFF' or 'AIFC' header");
00091 
00092         if (disposeAfterUse == DisposeAfterUse::YES)
00093             delete stream;
00094 
00095         return 0;
00096     }
00097 
00098     // From here on, we only care about the COMM and SSND chunks, which are
00099     // the only required chunks.
00100 
00101     bool foundCOMM = false;
00102     bool foundSSND = false;
00103 
00104     uint16 channels = 0, bitsPerSample = 0;
00105     uint32 rate = 0;
00106     uint32 codec = kCodecPCM; // AIFF default
00107     Common::SeekableReadStream *dataStream = 0;
00108 
00109     while (!(foundCOMM && foundSSND) && !stream->err() && !stream->eos()) {
00110         uint32 tag = stream->readUint32BE();
00111         uint32 length = stream->readUint32BE();
00112         uint32 pos = stream->pos();
00113 
00114         if (stream->eos() || stream->err())
00115             break;
00116 
00117         switch (tag) {
00118         case MKTAG('C', 'O', 'M', 'M'):
00119             foundCOMM = true;
00120             channels = stream->readUint16BE();
00121             /* frameCount = */ stream->readUint32BE();
00122             bitsPerSample = stream->readUint16BE();
00123             rate = readExtended(*stream);
00124 
00125             if (version == kVersionAIFC)
00126                 codec = stream->readUint32BE();
00127             break;
00128         case MKTAG('S', 'S', 'N', 'D'):
00129             foundSSND = true;
00130             /* uint32 offset = */ stream->readUint32BE();
00131             /* uint32 blockAlign = */ stream->readUint32BE();
00132             if (dataStream)
00133                 delete dataStream;
00134             dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + length - 8, disposeAfterUse);
00135             break;
00136         case MKTAG('F', 'V', 'E', 'R'):
00137             switch (stream->readUint32BE()) {
00138             case 0:
00139                 version = kVersionAIFF;
00140                 break;
00141             case 0xA2805140:
00142                 version = kVersionAIFC;
00143                 break;
00144             default:
00145                 warning("Unknown AIFF version chunk version");
00146                 break;
00147             }
00148             break;
00149         case MKTAG('w', 'a', 'v', 'e'):
00150             warning("Found unhandled AIFF-C extra data chunk");
00151 
00152             if (!dataStream && disposeAfterUse == DisposeAfterUse::YES)
00153                 delete stream;
00154 
00155             delete dataStream;
00156             return 0;
00157         default:
00158             debug(1, "Skipping AIFF '%s' chunk", tag2str(tag));
00159             break;
00160         }
00161 
00162         uint32 seekPos = pos + length;
00163         if (seekPos < (uint32)stream->size()) {
00164             seekPos += (length & 1); // ensure we're word-aligned
00165         }
00166         stream->seek(seekPos);
00167     }
00168 
00169     if (!foundCOMM) {
00170         warning("makeAIFFStream: Could not find 'COMM' chunk");
00171 
00172         if (!dataStream && disposeAfterUse == DisposeAfterUse::YES)
00173             delete stream;
00174 
00175         delete dataStream;
00176         return 0;
00177     }
00178 
00179     if (!foundSSND) {
00180         warning("makeAIFFStream: Could not find 'SSND' chunk");
00181 
00182         if (disposeAfterUse == DisposeAfterUse::YES)
00183             delete stream;
00184 
00185         return 0;
00186     }
00187 
00188     // We only implement a subset of the AIFF standard.
00189 
00190     if (channels < 1 || channels > 2) {
00191         warning("makeAIFFStream: Only 1 or 2 channels are supported, not %d", channels);
00192         delete dataStream;
00193         return 0;
00194     }
00195 
00196     // Seek to the start of dataStream, required for at least FileStream
00197     dataStream->seek(0);
00198 
00199     switch (codec) {
00200     case kCodecPCM:
00201     case MKTAG('t', 'w', 'o', 's'):
00202     case MKTAG('s', 'o', 'w', 't'): {
00203         // PCM samples are always signed.
00204         byte rawFlags = 0;
00205         if (bitsPerSample == 16)
00206             rawFlags |= Audio::FLAG_16BITS;
00207         if (channels == 2)
00208             rawFlags |= Audio::FLAG_STEREO;
00209         if (codec == MKTAG('s', 'o', 'w', 't'))
00210             rawFlags |= Audio::FLAG_LITTLE_ENDIAN;
00211 
00212         return makeRawStream(dataStream, rate, rawFlags);
00213     }
00214     case MKTAG('i', 'm', 'a', '4'):
00215         // TODO: Use QT IMA ADPCM
00216         warning("Unhandled AIFF-C QT IMA ADPCM compression");
00217         break;
00218     case MKTAG('Q', 'D', 'M', '2'):
00219         // TODO: Need to figure out how to integrate this
00220         // (But hopefully never needed)
00221         warning("Unhandled AIFF-C QDM2 compression");
00222         break;
00223     case MKTAG('A', 'D', 'P', '4'):
00224         // ADP4 on 3DO
00225         return make3DO_ADP4AudioStream(dataStream, rate, channels == 2);
00226     case MKTAG('S', 'D', 'X', '2'):
00227         // SDX2 on 3DO
00228         return make3DO_SDX2AudioStream(dataStream, rate, channels == 2);
00229     default:
00230         warning("Unhandled AIFF-C compression tag '%s'", tag2str(codec));
00231     }
00232 
00233     delete dataStream;
00234     return 0;
00235 }
00236 
00237 } // End of namespace Audio


Generated on Sat Aug 1 2020 05:01:00 for ResidualVM by doxygen 1.7.1
curved edge   curved edge