1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name wav.cpp - wav support */
12 //
13 // (c) Copyright 2003-2005 by Lutz Sammer, Fabrice Rossi and Nehal Mistry
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 /*----------------------------------------------------------------------------
33 -- Includes
34 ----------------------------------------------------------------------------*/
35
36 #include "stratagus.h"
37
38 #include "SDL.h"
39 #include "SDL_endian.h"
40
41 #include "iolib.h"
42 #include "sound_server.h"
43 #include "wav.h"
44
45 /*----------------------------------------------------------------------------
46 -- Declaration
47 ----------------------------------------------------------------------------*/
48
49 /**
50 ** Private wav data structure to handle wav streaming.
51 */
52 struct WavData {
53 CFile *WavFile; /// Wav file handle
54 int ChunkRem; /// Bytes remaining in chunk
55 };
56
57 class CSampleWav : public CSample
58 {
59 public:
60 ~CSampleWav();
61 int Read(void *buf, int len);
62
63 WavData Data;
64 };
65
66 class CSampleWavStream : public CSample
67 {
68 public:
69 ~CSampleWavStream();
70 int Read(void *buf, int len);
71
72 WavData Data;
73 };
74
75 /*----------------------------------------------------------------------------
76 -- Functions
77 ----------------------------------------------------------------------------*/
78
swapEndianness(WavHeader * wavHeader)79 static void swapEndianness(WavHeader *wavHeader)
80 {
81 wavHeader->MagicRiff = SDL_SwapLE32(wavHeader->MagicRiff);
82 wavHeader->Length = SDL_SwapLE32(wavHeader->Length);
83 wavHeader->MagicWave = SDL_SwapLE32(wavHeader->MagicWave);
84 }
85
Check(const WavHeader & wavHeader)86 static bool Check(const WavHeader &wavHeader)
87 {
88 if (wavHeader.MagicRiff != RIFF) {
89 return false;
90 }
91 if (wavHeader.MagicWave != WAVE) {
92 printf("Wrong magic %x (not %x)\n", wavHeader.MagicWave, WAVE);
93 return false;
94 }
95 return true;
96 }
97
swapEndianness(WavChunk * chunk)98 static void swapEndianness(WavChunk *chunk)
99 {
100 chunk->Magic = SDL_SwapLE32(chunk->Magic);
101 chunk->Length = SDL_SwapLE32(chunk->Length);
102 }
103
swapEndianness(WavFMT * wavfmt)104 static void swapEndianness(WavFMT *wavfmt)
105 {
106 wavfmt->Encoding = SDL_SwapLE16(wavfmt->Encoding);
107 wavfmt->Channels = SDL_SwapLE16(wavfmt->Channels);
108 wavfmt->Frequency = SDL_SwapLE32(wavfmt->Frequency);
109 wavfmt->ByteRate = SDL_SwapLE32(wavfmt->ByteRate);
110 wavfmt->SampleSize = SDL_SwapLE16(wavfmt->SampleSize);
111 wavfmt->BitsPerSample = SDL_SwapLE16(wavfmt->BitsPerSample);
112 }
113
IsWavFormatSupported(const WavFMT & wavfmt)114 static bool IsWavFormatSupported(const WavFMT &wavfmt)
115 {
116 if (wavfmt.Encoding != WAV_PCM_CODE) {
117 printf("Unsupported encoding %d\n", wavfmt.Encoding);
118 return false;
119 }
120 if (wavfmt.Channels != WAV_MONO && wavfmt.Channels != WAV_STEREO) {
121 printf("Unsupported channels %d\n", wavfmt.Channels);
122 return false;
123 }
124 if (wavfmt.SampleSize != 1 && wavfmt.SampleSize != 2 && wavfmt.SampleSize != 4) {
125 printf("Unsupported sample size %d\n", wavfmt.SampleSize);
126 return false;
127 }
128 if (wavfmt.BitsPerSample != 8 && wavfmt.BitsPerSample != 16) {
129 printf("Unsupported bits per sample %d\n", wavfmt.BitsPerSample);
130 return false;
131 }
132 Assert(wavfmt.Frequency == 44100 || wavfmt.Frequency == 22050 || wavfmt.Frequency == 11025);
133 return true;
134 }
135
Read(void * buf,int len)136 int CSampleWavStream::Read(void *buf, int len)
137 {
138 WavChunk chunk;
139 unsigned char *sndbuf;
140 int comp; // number of compressed bytes actually read
141 int i;
142 int read;
143 int bufrem;
144
145 if (this->Pos > SOUND_BUFFER_SIZE / 2) {
146 memcpy(this->Buffer, this->Buffer + this->Pos, this->Len);
147 this->Pos = 0;
148 }
149
150 while (this->Len < SOUND_BUFFER_SIZE / 4) {
151 if (!this->Data.ChunkRem) {
152 // read next chunk
153 comp = this->Data.WavFile->read(&chunk, sizeof(chunk));
154
155 if (!comp) {
156 // EOF
157 this->Data.ChunkRem = 0;
158 break;
159 }
160
161 swapEndianness(&chunk);
162 if (chunk.Magic != DATA) {
163 this->Data.WavFile->seek(chunk.Length, SEEK_CUR);
164 continue;
165 }
166 this->Data.ChunkRem = chunk.Length;
167 }
168
169 bufrem = SOUND_BUFFER_SIZE - (this->Pos + this->Len);
170 read = std::min(bufrem, this->Data.ChunkRem);
171 this->Data.ChunkRem -= read;
172
173 sndbuf = this->Buffer + this->Pos + this->Len;
174
175 comp = this->Data.WavFile->read(sndbuf, read);
176 if (!comp) {
177 break;
178 }
179
180 read >>= 1;
181 for (i = 0; i < read; ++i) {
182 ((unsigned short *)sndbuf)[i] = SDL_SwapLE16(((unsigned short *)sndbuf)[i]);
183 }
184
185 this->Len += comp;
186 }
187
188 len = std::min(this->Len, len);
189
190 memcpy(buf, this->Buffer + this->Pos, len);
191 this->Pos += len;
192 this->Len -= len;
193
194 return len;
195 }
196
~CSampleWavStream()197 CSampleWavStream::~CSampleWavStream()
198 {
199 this->Data.WavFile->close();
200 delete this->Data.WavFile;
201 delete[] this->Buffer;
202 }
203
Read(void * buf,int len)204 int CSampleWav::Read(void *buf, int len)
205 {
206 len = std::min(this->Len, len);
207
208 memcpy(buf, this->Buffer + this->Pos, len);
209 this->Pos += len;
210 this->Len -= len;
211
212 return len;
213 }
214
~CSampleWav()215 CSampleWav::~CSampleWav()
216 {
217 delete[] this->Buffer;
218 }
219
220 /**
221 ** Load wav.
222 **
223 ** @param name File name.
224 ** @param flags Load flags.
225 **
226 ** @return Returns the loaded sample.
227 **
228 ** @todo Add ADPCM loading support!
229 */
LoadWav(const char * name,int flags)230 CSample *LoadWav(const char *name, int flags)
231 {
232 CFile *f = new CFile;
233
234 if (f->open(name, CL_OPEN_READ) == -1) {
235 printf("Can't open file '%s'\n", name);
236 delete f;
237 return nullptr;
238 }
239 WavHeader wavHeader;
240 if (f->read(&wavHeader, sizeof(wavHeader)) != sizeof(wavHeader)) {
241 f->close();
242 delete f;
243 return nullptr;
244 }
245 // Convert to native format
246 swapEndianness(&wavHeader);
247
248 if (Check(wavHeader) == false) {
249 f->close();
250 delete f;
251 return nullptr;
252 }
253
254 WavChunk chunk;
255 if (f->read(&chunk, sizeof(chunk)) != sizeof(chunk)) {
256 f->close();
257 delete f;
258 return nullptr;
259 }
260 // Convert to native format
261 swapEndianness(&chunk);
262
263 while (chunk.Magic != FMT) {
264 printf("Discard wavChunk '%x'\n", chunk.Magic);
265 std::vector<char> buffer;
266
267 buffer.resize(chunk.Length);
268 if (f->read(&buffer[0], chunk.Length) != static_cast<int>(chunk.Length)
269 || f->read(&chunk, sizeof(chunk)) != sizeof(chunk)) {
270 f->close();
271 delete f;
272 return nullptr;
273 }
274 // Convert to native format
275 swapEndianness(&chunk);
276 }
277 if (chunk.Length < 16) {
278 printf("Wrong length %d (not %d)\n", chunk.Length, 16);
279 f->close();
280 delete f;
281 return nullptr;
282 }
283 WavFMT wavfmt;
284
285 if (f->read(&wavfmt, sizeof(wavfmt)) != sizeof(wavfmt)) {
286 f->close();
287 delete f;
288 return nullptr;
289 }
290 // Convert to native format
291 swapEndianness(&wavfmt);
292
293 if (chunk.Length != 16) {
294 std::vector<char> buffer;
295 const int extraSize = chunk.Length - 16;
296
297 buffer.resize(extraSize);
298 if (f->read(&buffer[0], extraSize) != extraSize) {
299 f->close();
300 delete f;
301 return nullptr;
302 }
303 }
304
305 // Check if supported
306 if (IsWavFormatSupported(wavfmt) == false) {
307 f->close();
308 delete f;
309 return nullptr;
310 }
311
312 CSample *sample;
313 WavData *data;
314 //
315 // Read sample
316 //
317 if (flags & PlayAudioStream) {
318 CSampleWavStream *sampleWavStream = new CSampleWavStream;
319 sample = sampleWavStream;
320 sampleWavStream->Data.WavFile = f;
321 data = &sampleWavStream->Data;
322 } else {
323 CSampleWav *sampleWav = new CSampleWav;
324 sample = sampleWav;
325 sampleWav->Data.WavFile = f;
326 data = &sampleWav->Data;
327 }
328 sample->Channels = wavfmt.Channels;
329 sample->SampleSize = wavfmt.SampleSize * 8 / sample->Channels;
330 sample->Frequency = wavfmt.Frequency;
331 sample->BitsPerSample = wavfmt.BitsPerSample;
332 sample->Len = 0;
333 sample->Pos = 0;
334 //Wyrmgus start
335 sample->File = name;
336 //Wyrmgus end
337
338 if (flags & PlayAudioStream) {
339 data->ChunkRem = 0;
340 sample->Buffer = new unsigned char[SOUND_BUFFER_SIZE];
341 } else {
342 char sndbuf[SOUND_BUFFER_SIZE];
343
344 sample->Buffer = nullptr;
345 int read = 0;
346 int rem = 0;
347 while (1) {
348 if (!rem) {
349 // read next chunk
350 const int comp = f->read(&chunk, sizeof(chunk));
351
352 if (!comp) {
353 // EOF
354 break;
355 }
356 swapEndianness(&chunk);
357 if (chunk.Magic != DATA) {
358 f->seek(chunk.Length, SEEK_CUR);
359 continue;
360 }
361 rem = chunk.Length;
362 }
363
364 const int bufrem = SOUND_BUFFER_SIZE;
365 read = std::min(bufrem, rem);
366 rem -= read;
367
368 unsigned char *b = new unsigned char[sample->Len + read];
369 Assert(b);
370 memcpy(b, sample->Buffer, sample->Len);
371 delete[] sample->Buffer;
372 sample->Buffer = b;
373
374 const int comp = data->WavFile->read(sndbuf, read);
375 Assert(comp == read);
376
377 if (sample->SampleSize == 16) {
378 read >>= 1;
379 for (int i = 0; i < read; ++i) {
380 ((unsigned short *)(sample->Buffer + sample->Pos + sample->Len))[i] =
381 SDL_SwapLE16(((unsigned short *)sndbuf)[i]);
382 }
383 } else {
384 memcpy((sample->Buffer + sample->Pos + sample->Len), sndbuf, comp);
385 }
386
387 sample->Len += comp;
388 }
389
390 data->WavFile->close();
391 delete data->WavFile;
392 }
393
394 return sample;
395 }
396
397 //@}
398