1 /*
2     SDL_mixer:  An audio mixer library based on the SDL library
3     Copyright (C) 1997-2009 Sam Lantinga
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Library General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Library General Public License for more details.
14 
15     You should have received a copy of the GNU Library General Public
16     License along with this library; if not, write to the Free
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 
19     Sam Lantinga
20     slouken@libsdl.org
21 */
22 
23 /* $Id: wavestream.c 5214 2009-11-08 17:11:09Z slouken $ */
24 
25 /* This file supports streaming WAV files, without volume adjustment */
26 
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "SDL_audio.h"
31 #include "SDL_mutex.h"
32 #include "SDL_rwops.h"
33 #include "SDL_endian.h"
34 
35 #include "SDL_mixer.h"
36 #include "wavestream.h"
37 
38 /*
39     Taken with permission from SDL_wave.h, part of the SDL library,
40     available at: http://www.libsdl.org/
41     and placed under the same license as this mixer library.
42 */
43 
44 /* WAVE files are little-endian */
45 
46 /*******************************************/
47 /* Define values for Microsoft WAVE format */
48 /*******************************************/
49 #define RIFF   0x46464952    /* "RIFF" */
50 #define WAVE   0x45564157    /* "WAVE" */
51 #define FACT   0x74636166    /* "fact" */
52 #define LIST   0x5453494c    /* "LIST" */
53 #define FMT    0x20746D66    /* "fmt " */
54 #define DATA   0x61746164    /* "data" */
55 #define PCM_CODE 1
56 #define ADPCM_CODE 2
57 #define WAVE_MONO  1
58 #define WAVE_STEREO  2
59 
60 /* Normally, these three chunks come consecutively in a WAVE file */
61 typedef struct WaveFMT {
62 /* Not saved in the chunk we read:
63  Uint32  FMTchunk;
64  Uint32  fmtlen;
65 */
66  Uint16  encoding;
67  Uint16  channels;   /* 1 = mono, 2 = stereo */
68  Uint32  frequency;    /* One of 11025, 22050, or 44100 Hz */
69  Uint32  byterate;   /* Average bytes per second */
70  Uint16  blockalign;   /* Bytes per sample block */
71  Uint16  bitspersample;    /* One of 8, 12, 16, or 4 for ADPCM */
72 } WaveFMT;
73 
74 /* The general chunk found in the WAVE file */
75 typedef struct Chunk {
76  Uint32 magic;
77  Uint32 length;
78  Uint8 *data;      /* Data includes magic and length */
79 } Chunk;
80 
81 /*********************************************/
82 /* Define values for AIFF (IFF audio) format */
83 /*********************************************/
84 #define FORM   0x4d524f46    /* "FORM" */
85 #define AIFF   0x46464941    /* "AIFF" */
86 #define SSND   0x444e5353    /* "SSND" */
87 #define COMM   0x4d4d4f43    /* "COMM" */
88 
89 
90 /* Currently we only support a single stream at a time */
91 static WAVStream *music = NULL;
92 
93 /* This is the format of the audio mixer data */
94 static SDL_AudioSpec mixer;
95 static int wavestream_volume = MIX_MAX_VOLUME;
96 
97 /* Function to load the WAV/AIFF stream */
98 static SDL_RWops *LoadWAVStream (SDL_RWops *rw, SDL_AudioSpec *spec,
99          long *start, long *stop);
100 static SDL_RWops *LoadAIFFStream (SDL_RWops *rw, SDL_AudioSpec *spec,
101          long *start, long *stop);
102 
103 /* Initialize the WAVStream player, with the given mixer settings
104    This function returns 0, or -1 if there was an error.
105  */
WAVStream_Init(SDL_AudioSpec * mixerfmt)106 int WAVStream_Init(SDL_AudioSpec *mixerfmt)
107 {
108  mixer = *mixerfmt;
109  return(0);
110 }
111 
WAVStream_SetVolume(int volume)112 void WAVStream_SetVolume(int volume)
113 {
114  wavestream_volume = volume;
115 }
116 
WAVStream_LoadSong(const char * file,const char * magic)117 WAVStream *WAVStream_LoadSong(const char *file, const char *magic)
118 {
119  SDL_RWops *rw;
120  WAVStream *wave;
121 
122  rw = SDL_RWFromFile(file, "rb");
123  if ( rw == NULL ) {
124    SDL_SetError("Couldn't open %s", file);
125    return NULL;
126  }
127  wave = WAVStream_LoadSong_RW(rw, magic);
128  if ( wave == NULL ) {
129    SDL_FreeRW(rw);
130    return NULL;
131  }
132  return wave;
133 }
134 
135 /* Load a WAV stream from the given file */
WAVStream_LoadSong_RW(SDL_RWops * rw,const char * magic)136 WAVStream *WAVStream_LoadSong_RW(SDL_RWops *rw, const char *magic)
137 {
138  WAVStream *wave;
139  SDL_AudioSpec wavespec;
140 
141  if ( ! mixer.format ) {
142    Mix_SetError("WAV music output not started");
143    return(NULL);
144  }
145  wave = (WAVStream *)malloc(sizeof *wave);
146  if ( wave ) {
147    memset(wave, 0, (sizeof *wave));
148    if ( strcmp(magic, "RIFF") == 0 ) {
149      wave->rw = LoadWAVStream(rw, &wavespec,
150          &wave->start, &wave->stop);
151    } else
152    if ( strcmp(magic, "FORM") == 0 ) {
153      wave->rw = LoadAIFFStream(rw, &wavespec,
154          &wave->start, &wave->stop);
155    } else {
156      Mix_SetError("Unknown WAVE format");
157    }
158    if ( wave->rw == NULL ) {
159      free(wave);
160      return(NULL);
161    }
162    SDL_BuildAudioCVT(&wave->cvt,
163      wavespec.format, wavespec.channels, wavespec.freq,
164      mixer.format, mixer.channels, mixer.freq);
165  }
166  return(wave);
167 }
168 
169 /* Start playback of a given WAV stream */
WAVStream_Start(WAVStream * wave)170 void WAVStream_Start(WAVStream *wave)
171 {
172  SDL_RWseek (wave->rw, wave->start, RW_SEEK_SET);
173  music = wave;
174 }
175 
176 /* Play some of a stream previously started with WAVStream_Start() */
WAVStream_PlaySome(Uint8 * stream,int len)177 int WAVStream_PlaySome(Uint8 *stream, int len)
178 {
179  long pos;
180  int left = 0;
181 
182  if ( music && ((pos=SDL_RWtell(music->rw)) < music->stop) ) {
183    if ( music->cvt.needed ) {
184      int original_len;
185 
186      original_len=(int)((double)len/music->cvt.len_ratio);
187      if ( music->cvt.len != original_len ) {
188        int worksize;
189        if ( music->cvt.buf != NULL ) {
190          free(music->cvt.buf);
191        }
192        worksize = original_len*music->cvt.len_mult;
193        music->cvt.buf=(Uint8 *)malloc(worksize);
194        if ( music->cvt.buf == NULL ) {
195          return 0;
196        }
197        music->cvt.len = original_len;
198      }
199      if ( (music->stop - pos) < original_len ) {
200        left = (original_len - (music->stop - pos));
201        original_len -= left;
202        left = (int)((double)left*music->cvt.len_ratio);
203      }
204      original_len = SDL_RWread(music->rw, music->cvt.buf,1,original_len);
205      /* At least at the time of writing, SDL_ConvertAudio()
206         does byte-order swapping starting at the end of the
207         buffer. Thus, if we are reading 16-bit samples, we
208         had better make damn sure that we get an even
209         number of bytes, or we'll get garbage.
210       */
211      if ( (music->cvt.src_format & 0x0010) && (original_len & 1) ) {
212        original_len--;
213      }
214      music->cvt.len = original_len;
215      SDL_ConvertAudio(&music->cvt);
216      SDL_MixAudio(stream, music->cvt.buf, music->cvt.len_cvt, wavestream_volume);
217    } else {
218      Uint8 *data;
219      if ( (music->stop - pos) < len ) {
220        left = (len - (music->stop - pos));
221        len -= left;
222      }
223      data = SDL_stack_alloc(Uint8, len);
224      if (data)
225      {
226        SDL_RWread(music->rw, data, len, 1);
227        SDL_MixAudio(stream, data, len, wavestream_volume);
228        SDL_stack_free(data);
229      }
230    }
231  }
232  return left;
233 }
234 
235 /* Stop playback of a stream previously started with WAVStream_Start() */
WAVStream_Stop(void)236 void WAVStream_Stop(void)
237 {
238  music = NULL;
239 }
240 
241 /* Close the given WAV stream */
WAVStream_FreeSong(WAVStream * wave)242 void WAVStream_FreeSong(WAVStream *wave)
243 {
244  if ( wave ) {
245    /* Clean up associated data */
246    if ( wave->freerw ) {
247      SDL_FreeRW(wave->rw);
248    }
249    if ( wave->cvt.buf ) {
250      free(wave->cvt.buf);
251    }
252    free(wave);
253  }
254 }
255 
256 /* Return non-zero if a stream is currently playing */
WAVStream_Active(void)257 int WAVStream_Active(void)
258 {
259  int active;
260 
261  active = 0;
262  if ( music && (SDL_RWtell(music->rw) < music->stop) ) {
263    active = 1;
264  }
265  return(active);
266 }
267 
ReadChunk(SDL_RWops * src,Chunk * chunk,int read_data)268 static int ReadChunk(SDL_RWops *src, Chunk *chunk, int read_data)
269 {
270  chunk->magic  = SDL_ReadLE32(src);
271  chunk->length = SDL_ReadLE32(src);
272  if ( read_data ) {
273    chunk->data = (Uint8 *)malloc(chunk->length);
274    if ( chunk->data == NULL ) {
275      Mix_SetError("Out of memory");
276      return(-1);
277    }
278    if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) {
279      Mix_SetError("Couldn't read chunk");
280      free(chunk->data);
281      return(-1);
282    }
283  } else {
284    SDL_RWseek(src, chunk->length, RW_SEEK_CUR);
285  }
286  return(chunk->length);
287 }
288 
LoadWAVStream(SDL_RWops * src,SDL_AudioSpec * spec,long * start,long * stop)289 static SDL_RWops *LoadWAVStream (SDL_RWops *src, SDL_AudioSpec *spec,
290          long *start, long *stop)
291 {
292  int was_error;
293  Chunk chunk;
294  int lenread;
295 
296  /* WAV magic header */
297  Uint32 RIFFchunk;
298  Uint32 wavelen;
299  Uint32 WAVEmagic;
300 
301  /* FMT chunk */
302  WaveFMT *format = NULL;
303 
304  was_error = 0;
305 
306  /* Check the magic header */
307  RIFFchunk = SDL_ReadLE32(src);
308  wavelen   = SDL_ReadLE32(src);
309  WAVEmagic = SDL_ReadLE32(src);
310  if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) {
311    Mix_SetError("Unrecognized file type (not WAVE)");
312    was_error = 1;
313    goto done;
314  }
315 
316  /* Read the audio data format chunk */
317  chunk.data = NULL;
318  do {
319    /* FIXME! Add this logic to SDL_LoadWAV_RW() */
320    if ( chunk.data ) {
321      free(chunk.data);
322    }
323    lenread = ReadChunk(src, &chunk, 1);
324    if ( lenread < 0 ) {
325      was_error = 1;
326      goto done;
327    }
328  } while ( (chunk.magic == FACT) || (chunk.magic == LIST) );
329 
330  /* Decode the audio data format */
331  format = (WaveFMT *)chunk.data;
332  if ( chunk.magic != FMT ) {
333    free(chunk.data);
334    Mix_SetError("Complex WAVE files not supported");
335    was_error = 1;
336    goto done;
337  }
338  switch (SDL_SwapLE16(format->encoding)) {
339    case PCM_CODE:
340      /* We can understand this */
341      break;
342    default:
343      Mix_SetError("Unknown WAVE data format");
344      was_error = 1;
345      goto done;
346  }
347  memset(spec, 0, (sizeof *spec));
348  spec->freq = SDL_SwapLE32(format->frequency);
349  switch (SDL_SwapLE16(format->bitspersample)) {
350    case 8:
351      spec->format = AUDIO_U8;
352      break;
353    case 16:
354      spec->format = AUDIO_S16;
355      break;
356    default:
357      Mix_SetError("Unknown PCM data format");
358      was_error = 1;
359      goto done;
360  }
361  spec->channels = (Uint8) SDL_SwapLE16(format->channels);
362  spec->samples = 4096;   /* Good default buffer size */
363 
364  /* Set the file offset to the DATA chunk data */
365  chunk.data = NULL;
366  do {
367    *start = SDL_RWtell(src) + 2*sizeof(Uint32);
368    lenread = ReadChunk(src, &chunk, 0);
369    if ( lenread < 0 ) {
370      was_error = 1;
371      goto done;
372    }
373  } while ( chunk.magic != DATA );
374  *stop = SDL_RWtell(src);
375 
376 done:
377  if ( format != NULL ) {
378    free(format);
379  }
380  if ( was_error ) {
381    return NULL;
382  }
383  return(src);
384 }
385 
386 /* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
387  * I don't pretend to fully understand it.
388  */
389 
SANE_to_Uint32(Uint8 * sanebuf)390 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
391 {
392  /* Negative number? */
393  if (sanebuf[0] & 0x80)
394    return 0;
395 
396  /* Less than 1? */
397  if (sanebuf[0] <= 0x3F)
398    return 1;
399 
400  /* Way too big? */
401  if (sanebuf[0] > 0x40)
402    return 0x4000000;
403 
404  /* Still too big? */
405  if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
406    return 800000000;
407 
408  return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
409    | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
410 }
411 
LoadAIFFStream(SDL_RWops * src,SDL_AudioSpec * spec,long * start,long * stop)412 static SDL_RWops *LoadAIFFStream (SDL_RWops *src, SDL_AudioSpec *spec,
413          long *start, long *stop)
414 {
415  int was_error;
416  int found_SSND;
417  int found_COMM;
418 
419  Uint32 chunk_type;
420  Uint32 chunk_length;
421  long next_chunk;
422 
423  /* AIFF magic header */
424  Uint32 FORMchunk;
425  Uint32 AIFFmagic;
426  /* SSND chunk        */
427  Uint32 offset;
428  Uint32 blocksize;
429  /* COMM format chunk */
430  Uint16 channels = 0;
431  Uint32 numsamples = 0;
432  Uint16 samplesize = 0;
433  Uint8 sane_freq[10];
434  Uint32 frequency = 0;
435 
436  was_error = 0;
437 
438  /* Check the magic header */
439  FORMchunk = SDL_ReadLE32(src);
440  chunk_length  = SDL_ReadBE32(src);
441  AIFFmagic = SDL_ReadLE32(src);
442  if ( (FORMchunk != FORM) || (AIFFmagic != AIFF) ) {
443    Mix_SetError("Unrecognized file type (not AIFF)");
444    was_error = 1;
445    goto done;
446  }
447 
448  /* From what I understand of the specification, chunks may appear in
449          * any order, and we should just ignore unknown ones.
450   *
451   * TODO: Better sanity-checking. E.g. what happens if the AIFF file
452   *       contains compressed sound data?
453          */
454 
455  found_SSND = 0;
456  found_COMM = 0;
457 
458  do {
459      chunk_type    = SDL_ReadLE32(src);
460      chunk_length  = SDL_ReadBE32(src);
461      next_chunk    = SDL_RWtell(src) + chunk_length;
462 
463      /* Paranoia to avoid infinite loops */
464      if (chunk_length == 0)
465    break;
466 
467             switch (chunk_type) {
468    case SSND:
469        found_SSND    = 1;
470        offset    = SDL_ReadBE32(src);
471        blocksize   = SDL_ReadBE32(src);
472        *start    = SDL_RWtell(src) + offset;
473        break;
474 
475    case COMM:
476        found_COMM    = 1;
477 
478        /* Read the audio data format chunk */
479        channels    = SDL_ReadBE16(src);
480        numsamples    = SDL_ReadBE32(src);
481        samplesize    = SDL_ReadBE16(src);
482        SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
483        frequency   = SANE_to_Uint32(sane_freq);
484        break;
485 
486    default:
487        break;
488      }
489  } while ((!found_SSND || !found_COMM)
490     && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != -1);
491 
492  if (!found_SSND) {
493      Mix_SetError("Bad AIFF file (no SSND chunk)");
494      was_error = 1;
495      goto done;
496  }
497 
498  if (!found_COMM) {
499      Mix_SetError("Bad AIFF file (no COMM chunk)");
500      was_error = 1;
501      goto done;
502  }
503 
504  *stop = *start + channels * numsamples * (samplesize / 8);
505 
506  /* Decode the audio data format */
507  memset(spec, 0, (sizeof *spec));
508  spec->freq = frequency;
509  switch (samplesize) {
510    case 8:
511      spec->format = AUDIO_S8;
512      break;
513    case 16:
514      spec->format = AUDIO_S16MSB;
515      break;
516    default:
517      Mix_SetError("Unknown samplesize in data format");
518      was_error = 1;
519      goto done;
520  }
521  spec->channels = (Uint8) channels;
522  spec->samples = 4096;   /* Good default buffer size */
523 
524 done:
525  if ( was_error ) {
526    return NULL;
527  }
528  return(src);
529 }
530 
531