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         stream->seek(pos + length + (length & 1)); // ensure we're also word-aligned
00163     }
00164 
00165     if (!foundCOMM) {
00166         warning("makeAIFFStream: Cound not find 'COMM' chunk");
00167 
00168         if (!dataStream && disposeAfterUse == DisposeAfterUse::YES)
00169             delete stream;
00170 
00171         delete dataStream;
00172         return 0;
00173     }
00174 
00175     if (!foundSSND) {
00176         warning("makeAIFFStream: Cound not find 'SSND' chunk");
00177 
00178         if (disposeAfterUse == DisposeAfterUse::YES)
00179             delete stream;
00180 
00181         return 0;
00182     }
00183 
00184     // We only implement a subset of the AIFF standard.
00185 
00186     if (channels < 1 || channels > 2) {
00187         warning("makeAIFFStream: Only 1 or 2 channels are supported, not %d", channels);
00188         delete dataStream;
00189         return 0;
00190     }
00191 
00192     // Seek to the start of dataStream, required for at least FileStream
00193     dataStream->seek(0);
00194 
00195     switch (codec) {
00196     case kCodecPCM:
00197     case MKTAG('t', 'w', 'o', 's'):
00198     case MKTAG('s', 'o', 'w', 't'): {
00199         // PCM samples are always signed.
00200         byte rawFlags = 0;
00201         if (bitsPerSample == 16)
00202             rawFlags |= Audio::FLAG_16BITS;
00203         if (channels == 2)
00204             rawFlags |= Audio::FLAG_STEREO;
00205         if (codec == MKTAG('s', 'o', 'w', 't'))
00206             rawFlags |= Audio::FLAG_LITTLE_ENDIAN;
00207 
00208         return makeRawStream(dataStream, rate, rawFlags);
00209     }
00210     case MKTAG('i', 'm', 'a', '4'):
00211         // TODO: Use QT IMA ADPCM
00212         warning("Unhandled AIFF-C QT IMA ADPCM compression");
00213         break;
00214     case MKTAG('Q', 'D', 'M', '2'):
00215         // TODO: Need to figure out how to integrate this
00216         // (But hopefully never needed)
00217         warning("Unhandled AIFF-C QDM2 compression");
00218         break;
00219     case MKTAG('A', 'D', 'P', '4'):
00220         // ADP4 on 3DO
00221         return make3DO_ADP4AudioStream(dataStream, rate, channels == 2);
00222     case MKTAG('S', 'D', 'X', '2'):
00223         // SDX2 on 3DO
00224         return make3DO_SDX2AudioStream(dataStream, rate, channels == 2);
00225     default:
00226         warning("Unhandled AIFF-C compression tag '%s'", tag2str(codec));
00227     }
00228 
00229     delete dataStream;
00230     return 0;
00231 }
00232 
00233 } // End of namespace Audio


Generated on Sat May 18 2019 05:00:54 for ResidualVM by doxygen 1.7.1
curved edge   curved edge