0x47 stub
[scummvm-innocent.git] / sound / wave.cpp
blob76ba5178a546431737fc2cd0bfcbeb8a26cbe7fe
1 /* ScummVM - Graphic Adventure Engine
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 * $URL$
22 * $Id$
26 #include "common/debug.h"
27 #include "common/util.h"
28 #include "common/stream.h"
30 #include "sound/audiostream.h"
31 #include "sound/mixer.h"
32 #include "sound/wave.h"
33 #include "sound/adpcm.h"
35 namespace Audio {
37 bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags, uint16 *wavType, int *blockAlign_) {
38 const int32 initialPos = stream.pos();
39 byte buf[4+1];
41 buf[4] = 0;
43 stream.read(buf, 4);
44 if (memcmp(buf, "RIFF", 4) != 0) {
45 warning("getWavInfo: No 'RIFF' header");
46 return false;
49 int32 wavLength = stream.readUint32LE();
51 stream.read(buf, 4);
52 if (memcmp(buf, "WAVE", 4) != 0) {
53 warning("getWavInfo: No 'WAVE' header");
54 return false;
57 stream.read(buf, 4);
58 if (memcmp(buf, "fmt ", 4) != 0) {
59 warning("getWavInfo: No 'fmt' header");
60 return false;
63 uint32 fmtLength = stream.readUint32LE();
64 if (fmtLength < 16) {
65 // A valid fmt chunk always contains at least 16 bytes
66 warning("getWavInfo: 'fmt' header is too short");
67 return false;
70 // Next comes the "type" field of the fmt header. Some typical
71 // values for it:
72 // 1 -> uncompressed PCM
73 // 17 -> IMA ADPCM compressed WAVE
74 // See <http://www.saettler.com/RIFFNEW/RIFFNEW.htm> for a more complete
75 // list of common WAVE compression formats...
76 uint16 type = stream.readUint16LE(); // == 1 for PCM data
77 uint16 numChannels = stream.readUint16LE(); // 1 for mono, 2 for stereo
78 uint32 samplesPerSec = stream.readUint32LE(); // in Hz
79 uint32 avgBytesPerSec = stream.readUint32LE(); // == SampleRate * NumChannels * BitsPerSample/8
81 uint16 blockAlign = stream.readUint16LE(); // == NumChannels * BitsPerSample/8
82 uint16 bitsPerSample = stream.readUint16LE(); // 8, 16 ...
83 // 8 bit data is unsigned, 16 bit data signed
86 if (wavType != 0)
87 *wavType = type;
89 if (blockAlign_ != 0)
90 *blockAlign_ = blockAlign;
91 #if 0
92 printf("WAVE information:\n");
93 printf(" total size: %d\n", wavLength);
94 printf(" fmt size: %d\n", fmtLength);
95 printf(" type: %d\n", type);
96 printf(" numChannels: %d\n", numChannels);
97 printf(" samplesPerSec: %d\n", samplesPerSec);
98 printf(" avgBytesPerSec: %d\n", avgBytesPerSec);
99 printf(" blockAlign: %d\n", blockAlign);
100 printf(" bitsPerSample: %d\n", bitsPerSample);
101 #endif
103 if (type != 1 && type != 2 && type != 17) {
104 warning("getWavInfo: only PCM, MS ADPCM or IMA ADPCM data is supported (type %d)", type);
105 return false;
108 if (blockAlign != numChannels * bitsPerSample / 8 && type != 2) {
109 debug(0, "getWavInfo: blockAlign is invalid");
112 if (avgBytesPerSec != samplesPerSec * blockAlign && type != 2) {
113 debug(0, "getWavInfo: avgBytesPerSec is invalid");
116 // Prepare the return values.
117 rate = samplesPerSec;
119 flags = 0;
120 if (bitsPerSample == 8) // 8 bit data is unsigned
121 flags |= Audio::Mixer::FLAG_UNSIGNED;
122 else if (bitsPerSample == 16) // 16 bit data is signed little endian
123 flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
124 else if (bitsPerSample == 4 && type == 17) // MS IMA ADPCM compressed. We decompress it
125 flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
126 else if (bitsPerSample == 4 && type == 2) // MS ADPCM compressed. We decompress it
127 flags |= (Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_LITTLE_ENDIAN);
128 else {
129 warning("getWavInfo: unsupported bitsPerSample %d", bitsPerSample);
130 return false;
133 if (numChannels == 2)
134 flags |= Audio::Mixer::FLAG_STEREO;
135 else if (numChannels != 1) {
136 warning("getWavInfo: unsupported number of channels %d", numChannels);
137 return false;
140 // It's almost certainly a WAV file, but we still need to find its
141 // 'data' chunk.
143 // Skip over the rest of the fmt chunk.
144 int offset = fmtLength - 16;
146 do {
147 stream.seek(offset, SEEK_CUR);
148 if (stream.pos() >= initialPos + wavLength + 8) {
149 warning("getWavInfo: Can't find 'data' chunk");
150 return false;
152 stream.read(buf, 4);
153 offset = stream.readUint32LE();
155 #if 0
156 printf(" found a '%s' tag of size %d\n", buf, offset);
157 #endif
158 } while (memcmp(buf, "data", 4) != 0);
160 // Stream now points at 'offset' bytes of sample data...
161 size = offset;
163 return true;
166 AudioStream *makeWAVStream(Common::SeekableReadStream *stream, bool disposeAfterUse) {
167 int size, rate;
168 byte *data, flags;
169 uint16 type;
170 int blockAlign;
172 if (!loadWAVFromStream(*stream, size, rate, flags, &type, &blockAlign)) {
173 if (disposeAfterUse)
174 delete stream;
175 return 0;
178 if (type == 17) { // MS IMA ADPCM
179 Audio::AudioStream *sndStream = Audio::makeADPCMStream(stream, false, size, Audio::kADPCMMSIma, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
180 data = (byte *)malloc(size * 4);
181 assert(data);
182 size = sndStream->readBuffer((int16*)data, size * 2);
183 size *= 2; // 16bits.
184 delete sndStream;
185 } else if (type == 2) { // MS ADPCM
186 Audio::AudioStream *sndStream = Audio::makeADPCMStream(stream, false, size, Audio::kADPCMMS, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign);
187 data = (byte *)malloc(size * 4);
188 assert(data);
189 size = sndStream->readBuffer((int16*)data, size * 2);
190 size *= 2; // 16bits.
191 delete sndStream;
192 } else {
193 // Plain data. Just read everything at once.
194 // TODO: More elegant would be to wrap the stream.
195 data = (byte *)malloc(size);
196 assert(data);
197 stream->read(data, size);
200 if (disposeAfterUse)
201 delete stream;
203 // Since we allocated our own buffer for the data, we must set the autofree flag.
204 flags |= Audio::Mixer::FLAG_AUTOFREE;
206 return makeLinearInputStream(data, size, rate, flags, 0, 0);
209 } // End of namespace Audio