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