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

wave.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 #include "common/debug.h"
00024 #include "common/textconsole.h"
00025 #include "common/stream.h"
00026 
00027 #include "audio/audiostream.h"
00028 #include "audio/decoders/wave.h"
00029 #include "audio/decoders/adpcm.h"
00030 #include "audio/decoders/mp3.h"
00031 #include "audio/decoders/raw.h"
00032 
00033 namespace Audio {
00034 
00035 // Audio Codecs
00036 enum {
00037     kWaveFormatPCM = 1,
00038     kWaveFormatMSADPCM = 2,
00039     kWaveFormatMSIMAADPCM = 17,
00040     kWaveFormatMP3 = 85
00041 };
00042 
00043 bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags, uint16 *wavType, int *blockAlign_) {
00044     const int32 initialPos = stream.pos();
00045     byte buf[4+1];
00046 
00047     buf[4] = 0;
00048 
00049     stream.read(buf, 4);
00050     if (memcmp(buf, "RIFF", 4) != 0) {
00051         warning("getWavInfo: No 'RIFF' header");
00052         return false;
00053     }
00054 
00055     int32 wavLength = stream.readUint32LE();
00056 
00057     stream.read(buf, 4);
00058     if (memcmp(buf, "WAVE", 4) != 0) {
00059         warning("getWavInfo: No 'WAVE' header");
00060         return false;
00061     }
00062 
00063     stream.read(buf, 4);
00064     if (memcmp(buf, "fact", 4) == 0) {
00065         // Initial fact chunk, so skip over it
00066         uint32 factLen = stream.readUint32LE();
00067         stream.skip(factLen);
00068         stream.read(buf, 4);
00069     }
00070 
00071     if (memcmp(buf, "fmt ", 4) != 0) {
00072         warning("getWavInfo: No 'fmt' header");
00073         return false;
00074     }
00075 
00076     uint32 fmtLength = stream.readUint32LE();
00077     if (fmtLength < 16) {
00078         // A valid fmt chunk always contains at least 16 bytes
00079         warning("getWavInfo: 'fmt' header is too short");
00080         return false;
00081     }
00082 
00083     // Next comes the "type" field of the fmt header. Some typical
00084     // values for it:
00085     // 1  -> uncompressed PCM
00086     // 17 -> IMA ADPCM compressed WAVE
00087     // See <http://www.saettler.com/RIFFNEW/RIFFNEW.htm> for a more complete
00088     // list of common WAVE compression formats...
00089     uint16 type = stream.readUint16LE();    // == 1 for PCM data
00090     uint16 numChannels = stream.readUint16LE(); // 1 for mono, 2 for stereo
00091     uint32 samplesPerSec = stream.readUint32LE();   // in Hz
00092     uint32 avgBytesPerSec = stream.readUint32LE();  // == SampleRate * NumChannels * BitsPerSample/8
00093 
00094     uint16 blockAlign = stream.readUint16LE();  // == NumChannels * BitsPerSample/8
00095     uint16 bitsPerSample = stream.readUint16LE();   // 8, 16 ...
00096     // 8 bit data is unsigned, 16 bit data signed
00097 
00098 
00099     if (wavType != 0)
00100         *wavType = type;
00101 
00102     if (blockAlign_ != 0)
00103         *blockAlign_ = blockAlign;
00104 #if 0
00105     debug("WAVE information:");
00106     debug("  total size: %d", wavLength);
00107     debug("  fmt size: %d", fmtLength);
00108     debug("  type: %d", type);
00109     debug("  numChannels: %d", numChannels);
00110     debug("  samplesPerSec: %d", samplesPerSec);
00111     debug("  avgBytesPerSec: %d", avgBytesPerSec);
00112     debug("  blockAlign: %d", blockAlign);
00113     debug("  bitsPerSample: %d", bitsPerSample);
00114 #endif
00115 
00116     #ifdef USE_MAD
00117     if (type == kWaveFormatMP3) {
00118         bitsPerSample = 8;
00119     } else {
00120     #endif
00121         if (type != kWaveFormatPCM && type != kWaveFormatMSADPCM && type != kWaveFormatMSIMAADPCM) {
00122             #ifdef USE_MAD
00123             warning("getWavInfo: only PCM, MS ADPCM, MP3, or IMA ADPCM data is supported (type %d)", type);
00124             #else
00125             warning("getWavInfo: only PCM, MS ADPCM, or IMA ADPCM data is supported (type %d)", type);
00126             #endif
00127 
00128             return false;
00129         }
00130 
00131         if (blockAlign != numChannels * bitsPerSample / 8 && type != kWaveFormatMSADPCM) {
00132             debug(0, "getWavInfo: blockAlign is invalid");
00133         }
00134 
00135         if (avgBytesPerSec != samplesPerSec * blockAlign && type != kWaveFormatMSADPCM) {
00136             debug(0, "getWavInfo: avgBytesPerSec is invalid");
00137         }
00138     #ifdef USE_MAD
00139     }
00140     #endif
00141 
00142     // Prepare the return values.
00143     rate = samplesPerSec;
00144 
00145     flags = 0;
00146     if (bitsPerSample == 8)     // 8 bit data is unsigned
00147         flags |= Audio::FLAG_UNSIGNED;
00148     else if (bitsPerSample == 16)   // 16 bit data is signed little endian
00149         flags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
00150     else if (bitsPerSample == 4 && (type == kWaveFormatMSADPCM || type == kWaveFormatMSIMAADPCM))
00151         flags |= Audio::FLAG_16BITS;
00152     else {
00153         warning("getWavInfo: unsupported bitsPerSample %d", bitsPerSample);
00154         return false;
00155     }
00156 
00157     if (numChannels == 2)
00158         flags |= Audio::FLAG_STEREO;
00159     else if (numChannels != 1) {
00160         warning("getWavInfo: unsupported number of channels %d", numChannels);
00161         return false;
00162     }
00163 
00164     // It's almost certainly a WAV file, but we still need to find its
00165     // 'data' chunk.
00166 
00167     // Skip over the rest of the fmt chunk.
00168     int offset = fmtLength - 16;
00169 
00170     do {
00171         stream.seek(offset, SEEK_CUR);
00172         if (stream.pos() >= initialPos + wavLength + 8) {
00173             warning("getWavInfo: Can't find 'data' chunk");
00174             return false;
00175         }
00176         stream.read(buf, 4);
00177         offset = stream.readUint32LE();
00178 
00179 #if 0
00180         debug("  found a '%s' tag of size %d", buf, offset);
00181 #endif
00182     } while (memcmp(buf, "data", 4) != 0);
00183 
00184     // Stream now points at 'offset' bytes of sample data...
00185     size = offset;
00186 
00187     return true;
00188 }
00189 
00190 SeekableAudioStream *makeWAVStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
00191     int size, rate;
00192     byte flags;
00193     uint16 type;
00194     int blockAlign;
00195 
00196     if (!loadWAVFromStream(*stream, size, rate, flags, &type, &blockAlign)) {
00197         if (disposeAfterUse == DisposeAfterUse::YES)
00198             delete stream;
00199         return 0;
00200     }
00201 
00202     if (type == kWaveFormatMSIMAADPCM) // MS IMA ADPCM
00203         return makeADPCMStream(stream, disposeAfterUse, size, Audio::kADPCMMSIma, rate, (flags & Audio::FLAG_STEREO) ? 2 : 1, blockAlign);
00204     else if (type == kWaveFormatMSADPCM) // MS ADPCM
00205         return makeADPCMStream(stream, disposeAfterUse, size, Audio::kADPCMMS, rate, (flags & Audio::FLAG_STEREO) ? 2 : 1, blockAlign);
00206     #ifdef USE_MAD
00207     else if (type == kWaveFormatMP3)
00208         return makeMP3Stream(stream, disposeAfterUse);
00209     #endif
00210 
00211     // Raw PCM, make sure the last packet is complete
00212     uint sampleSize = (flags & Audio::FLAG_16BITS ? 2 : 1) * (flags & Audio::FLAG_STEREO ? 2 : 1);
00213     if (size % sampleSize != 0) {
00214         warning("makeWAVStream: Trying to play a WAVE file with an incomplete PCM packet");
00215         size &= ~(sampleSize - 1);
00216     }
00217 
00218     // Raw PCM. Just read everything at once.
00219     // TODO: More elegant would be to wrap the stream.
00220     byte *data = (byte *)malloc(size);
00221     assert(data);
00222     stream->read(data, size);
00223 
00224     if (disposeAfterUse == DisposeAfterUse::YES)
00225         delete stream;
00226 
00227     return makeRawStream(data, size, rate, flags);
00228 }
00229 
00230 } // End of namespace Audio


Generated on Sat Aug 17 2019 05:00:25 for ResidualVM by doxygen 1.7.1
curved edge   curved edge