Ticket #12513: wave.cpp

File wave.cpp, 7.1 KB (added by ctoroman, 3 years ago)
Line 
1/* ScummVM - Graphic Adventure Engine
2 *
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.
6 *
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.
11 *
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.
16 *
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.
20 *
21 */
22
23#include "common/debug.h"
24#include "common/textconsole.h"
25#include "common/stream.h"
26
27#include "audio/audiostream.h"
28#include "audio/decoders/wave_types.h"
29#include "audio/decoders/wave.h"
30#include "audio/decoders/adpcm.h"
31#include "audio/decoders/mp3.h"
32#include "audio/decoders/raw.h"
33
34namespace Audio {
35
36bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags, uint16 *wavType, int *blockAlign_) {
37 const int32 initialPos = stream.pos();
38 byte buf[4+1];
39
40 buf[4] = 0;
41
42 stream.read(buf, 4);
43 if (memcmp(buf, "RIFF", 4) != 0) {
44 warning("getWavInfo: No 'RIFF' header");
45 return false;
46 }
47
48 int32 wavLength = stream.readUint32LE();
49
50 stream.read(buf, 4);
51 if (memcmp(buf, "WAVE", 4) != 0) {
52 warning("getWavInfo: No 'WAVE' header");
53 return false;
54 }
55
56 stream.read(buf, 4);
57 if (memcmp(buf, "fact", 4) == 0) {
58 // Initial fact chunk, so skip over it
59 uint32 factLen = stream.readUint32LE();
60 stream.skip(factLen);
61 stream.read(buf, 4);
62 }
63
64 if (memcmp(buf, "fmt ", 4) != 0) {
65 warning("getWavInfo: No 'fmt' header");
66 return false;
67 }
68
69 uint32 fmtLength = stream.readUint32LE();
70 if (fmtLength < 16) {
71 // A valid fmt chunk always contains at least 16 bytes
72 warning("getWavInfo: 'fmt' header is too short");
73 return false;
74 }
75
76 // Next comes the "type" field of the fmt header. Some typical
77 // values for it:
78 // 1 -> uncompressed PCM
79 // 17 -> IMA ADPCM compressed WAVE
80 // See <http://www.saettler.com/RIFFNEW/RIFFNEW.htm> for a more complete
81 // list of common WAVE compression formats...
82 uint16 type = stream.readUint16LE(); // == 1 for PCM data
83 uint16 numChannels = stream.readUint16LE(); // 1 for mono, 2 for stereo
84 uint32 samplesPerSec = stream.readUint32LE(); // in Hz
85 uint32 avgBytesPerSec = stream.readUint32LE(); // == SampleRate * NumChannels * BitsPerSample/8
86
87 uint16 blockAlign = stream.readUint16LE(); // == NumChannels * BitsPerSample/8
88 uint16 bitsPerSample = stream.readUint16LE(); // 8, 16 ...
89 // 8 bit data is unsigned, 16 bit data signed
90
91
92 if (wavType != 0)
93 *wavType = type;
94
95 if (blockAlign_ != 0)
96 *blockAlign_ = blockAlign;
97#if 0
98 debug("WAVE information:");
99 debug(" total size: %d", wavLength);
100 debug(" fmt size: %d", fmtLength);
101 debug(" type: %d", type);
102 debug(" numChannels: %d", numChannels);
103 debug(" samplesPerSec: %d", samplesPerSec);
104 debug(" avgBytesPerSec: %d", avgBytesPerSec);
105 debug(" blockAlign: %d", blockAlign);
106 debug(" bitsPerSample: %d", bitsPerSample);
107#endif
108
109 #ifdef USE_MAD
110 if (type == kWaveFormatMP3) {
111 bitsPerSample = 8;
112 } else {
113 #endif
114 if (type != kWaveFormatPCM && type != kWaveFormatMSADPCM && type != kWaveFormatMSIMAADPCM) {
115 #ifdef USE_MAD
116 warning("getWavInfo: only PCM, MS ADPCM, MP3, or IMA ADPCM data is supported (type %d)", type);
117 #else
118 warning("getWavInfo: only PCM, MS ADPCM, or IMA ADPCM data is supported (type %d)", type);
119 #endif
120
121 return false;
122 }
123
124 if (blockAlign != numChannels * bitsPerSample / 8 && type != kWaveFormatMSADPCM) {
125 debug(0, "getWavInfo: blockAlign is invalid");
126 }
127
128 if (avgBytesPerSec != samplesPerSec * blockAlign && type != kWaveFormatMSADPCM) {
129 debug(0, "getWavInfo: avgBytesPerSec is invalid");
130 }
131 #ifdef USE_MAD
132 }
133 #endif
134
135 // Prepare the return values.
136 rate = samplesPerSec;
137
138 flags = 0;
139 if (bitsPerSample == 8) // 8 bit data is unsigned
140 flags |= Audio::FLAG_UNSIGNED;
141 else if (bitsPerSample == 16) // 16 bit data is signed little endian
142 flags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
143 else if (bitsPerSample == 24) // 24 bit data is signed little endian
144 flags |= (Audio::FLAG_24BITS | Audio::FLAG_LITTLE_ENDIAN);
145 else if (bitsPerSample == 4 && (type == kWaveFormatMSADPCM || type == kWaveFormatMSIMAADPCM))
146 flags |= Audio::FLAG_16BITS;
147 else {
148 warning("getWavInfo: unsupported bitsPerSample %d", bitsPerSample);
149 return false;
150 }
151
152 if (numChannels == 2)
153 flags |= Audio::FLAG_STEREO;
154 else if (numChannels != 1) {
155 warning("getWavInfo: unsupported number of channels %d", numChannels);
156 return false;
157 }
158
159 // It's almost certainly a WAV file, but we still need to find its
160 // 'data' chunk.
161
162 // Skip over the rest of the fmt chunk.
163 int offset = fmtLength - 16;
164
165 do {
166 stream.seek(offset, SEEK_CUR);
167 if (stream.pos() >= initialPos + wavLength + 8) {
168 warning("getWavInfo: Can't find 'data' chunk");
169 return false;
170 }
171 stream.read(buf, 4);
172 offset = stream.readUint32LE();
173
174#if 0
175 debug(" found a '%s' tag of size %d", buf, offset);
176#endif
177 } while (memcmp(buf, "data", 4) != 0);
178
179 // Stream now points at 'offset' bytes of sample data...
180 size = offset;
181
182 return true;
183}
184
185SeekableAudioStream *makeWAVStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
186 int size, rate;
187 byte flags;
188 uint16 type;
189 int blockAlign;
190
191 if (!loadWAVFromStream(*stream, size, rate, flags, &type, &blockAlign)) {
192 if (disposeAfterUse == DisposeAfterUse::YES)
193 delete stream;
194 return 0;
195 }
196
197 if (type == kWaveFormatMSIMAADPCM) // MS IMA ADPCM
198 return makeADPCMStream(stream, disposeAfterUse, size, Audio::kADPCMMSIma, rate, (flags & Audio::FLAG_STEREO) ? 2 : 1, blockAlign);
199 else if (type == kWaveFormatMSADPCM) // MS ADPCM
200 return makeADPCMStream(stream, disposeAfterUse, size, Audio::kADPCMMS, rate, (flags & Audio::FLAG_STEREO) ? 2 : 1, blockAlign);
201 #ifdef USE_MAD
202 else if (type == kWaveFormatMP3)
203 return makeMP3Stream(stream, disposeAfterUse);
204 #endif
205
206 // Raw PCM, make sure the last packet is complete
207 uint sampleSize = (flags & Audio::FLAG_16BITS ? 2 : 1) * (flags & Audio::FLAG_STEREO ? 2 : 1);
208 if (size % sampleSize != 0) {
209 warning("makeWAVStream: Trying to play a WAVE file with an incomplete PCM packet");
210 size &= ~(sampleSize - 1);
211 }
212
213 // Raw PCM. Just read everything at once.
214 // TODO: More elegant would be to wrap the stream.
215 byte *data = (byte *)malloc(size);
216 assert(data);
217 stream->read(data, size);
218
219 if (disposeAfterUse == DisposeAfterUse::YES)
220 delete stream;
221 return makeRawStream(data, size, rate, flags);
222
223}
224
225} // End of namespace Audio