2
SDL_mixer: An audio mixer library based on the SDL library
3
Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
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.
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.
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
23
/* $Id: wavestream.c,v 1.16 2001/12/17 02:39:12 slouken Exp $ */
25
/* This file supports streaming WAV files, without volume adjustment */
30
#include "SDL_audio.h"
31
#include "SDL_mutex.h"
32
#include "SDL_rwops.h"
33
#include "SDL_endian.h"
35
#include "SDL_mixer.h"
36
#include "wavestream.h"
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.
44
/* WAVE files are little-endian */
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" */
60
/* Normally, these three chunks come consecutively in a WAVE file */
61
typedef struct WaveFMT {
62
/* Not saved in the chunk we read:
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 */
74
/* The general chunk found in the WAVE file */
75
typedef struct Chunk {
78
Uint8 *data; /* Data includes magic and length */
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" */
90
/* Currently we only support a single stream at a time */
91
static WAVStream *theWave = NULL;
93
/* This is initialized by the music mixer */
94
static SDL_mutex *music_lock = NULL;
96
/* This is the format of the audio mixer data */
97
static SDL_AudioSpec mixer;
99
/* Function to load the WAV/AIFF stream */
100
static FILE *LoadWAVStream (const char *file, SDL_AudioSpec *spec,
101
long *start, long *stop);
102
static FILE *LoadAIFFStream (const char *file, SDL_AudioSpec *spec,
103
long *start, long *stop);
105
/* Initialize the WAVStream player, with the given mixer settings
106
This function returns 0, or -1 if there was an error.
108
int WAVStream_Init(SDL_AudioSpec *mixerfmt)
110
/* FIXME: clean up the mutex, or move it into music.c */
111
music_lock = SDL_CreateMutex();
112
#ifndef macintosh /* Hmm.. */
113
if ( music_lock == NULL ) {
122
extern void WAVStream_SetVolume(int volume)
126
/* Load a WAV stream from the given file */
127
extern WAVStream *WAVStream_LoadSong(const char *file, const char *magic)
130
SDL_AudioSpec wavespec;
132
if ( ! mixer.format ) {
133
Mix_SetError("WAV music output not started");
136
wave = (WAVStream *)malloc(sizeof *wave);
138
memset(wave, 0, (sizeof *wave));
139
if ( strcmp(magic, "RIFF") == 0 ) {
140
wave->wavefp = LoadWAVStream(file, &wavespec,
141
&wave->start, &wave->stop);
143
if ( strcmp(magic, "FORM") == 0 ) {
144
wave->wavefp = LoadAIFFStream(file, &wavespec,
145
&wave->start, &wave->stop);
147
Mix_SetError("Unknown WAVE format");
149
if ( wave->wavefp == NULL ) {
153
SDL_BuildAudioCVT(&wave->cvt,
154
wavespec.format, wavespec.channels, wavespec.freq,
155
mixer.format, mixer.channels, mixer.freq);
160
/* Start playback of a given WAV stream */
161
extern void WAVStream_Start(WAVStream *wave)
163
SDL_mutexP(music_lock);
164
clearerr(wave->wavefp);
165
fseek(wave->wavefp, wave->start, SEEK_SET);
167
SDL_mutexV(music_lock);
170
/* Play some of a stream previously started with WAVStream_Start()
171
The music_lock is held while this function is called.
173
extern void WAVStream_PlaySome(Uint8 *stream, int len)
177
SDL_mutexP(music_lock);
178
if ( theWave && ((pos=ftell(theWave->wavefp)) < theWave->stop) ) {
179
if ( theWave->cvt.needed ) {
182
original_len=(int)((double)len/theWave->cvt.len_ratio);
183
if ( theWave->cvt.len != original_len ) {
185
if ( theWave->cvt.buf != NULL ) {
186
free(theWave->cvt.buf);
188
worksize = original_len*theWave->cvt.len_mult;
189
theWave->cvt.buf=(Uint8 *)malloc(worksize);
190
if ( theWave->cvt.buf == NULL ) {
191
SDL_mutexV(music_lock);
194
theWave->cvt.len = original_len;
196
if ( (theWave->stop - pos) < original_len ) {
197
original_len = (theWave->stop - pos);
199
original_len = fread(theWave->cvt.buf,1,original_len,theWave->wavefp);
200
/* At least at the time of writing, SDL_ConvertAudio()
201
does byte-order swapping starting at the end of the
202
buffer. Thus, if we are reading 16-bit samples, we
203
had better make damn sure that we get an even
204
number of bytes, or we'll get garbage.
206
if ( (theWave->cvt.src_format & 0x0010) && (original_len & 1) ) {
209
theWave->cvt.len = original_len;
210
SDL_ConvertAudio(&theWave->cvt);
211
memcpy(stream, theWave->cvt.buf, theWave->cvt.len_cvt);
213
if ( (theWave->stop - pos) < len ) {
214
len = (theWave->stop - pos);
216
fread(stream, len, 1, theWave->wavefp);
219
SDL_mutexV(music_lock);
222
/* Stop playback of a stream previously started with WAVStream_Start() */
223
extern void WAVStream_Stop(void)
225
SDL_mutexP(music_lock);
227
SDL_mutexV(music_lock);
230
/* Close the given WAV stream */
231
extern void WAVStream_FreeSong(WAVStream *wave)
234
/* Remove song from the currently playing list */
235
SDL_mutexP(music_lock);
236
if ( wave == theWave ) {
239
SDL_mutexV(music_lock);
241
/* Clean up associated data */
242
if ( wave->wavefp ) {
243
fclose(wave->wavefp);
245
if ( wave->cvt.buf ) {
252
/* Return non-zero if a stream is currently playing */
253
extern int WAVStream_Active(void)
257
SDL_mutexP(music_lock);
259
if ( theWave && (ftell(theWave->wavefp) < theWave->stop) ) {
262
SDL_mutexV(music_lock);
267
static int ReadChunk(SDL_RWops *src, Chunk *chunk, int read_data)
269
chunk->magic = SDL_ReadLE32(src);
270
chunk->length = SDL_ReadLE32(src);
272
chunk->data = (Uint8 *)malloc(chunk->length);
273
if ( chunk->data == NULL ) {
274
Mix_SetError("Out of memory");
277
if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) {
278
Mix_SetError("Couldn't read chunk");
283
SDL_RWseek(src, chunk->length, SEEK_CUR);
285
return(chunk->length);
288
static FILE *LoadWAVStream (const char *file, SDL_AudioSpec *spec,
289
long *start, long *stop)
297
/* WAV magic header */
303
WaveFMT *format = NULL;
305
/* Make sure we are passed a valid data source */
307
wavefp = fopen(file, "rb");
310
src = SDL_RWFromFP(wavefp, 0);
317
/* Check the magic header */
318
RIFFchunk = SDL_ReadLE32(src);
319
wavelen = SDL_ReadLE32(src);
320
WAVEmagic = SDL_ReadLE32(src);
321
if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) {
322
Mix_SetError("Unrecognized file type (not WAVE)");
327
/* Read the audio data format chunk */
330
/* FIXME! Add this logic to SDL_LoadWAV_RW() */
334
lenread = ReadChunk(src, &chunk, 1);
339
} while ( (chunk.magic == FACT) || (chunk.magic == LIST) );
341
/* Decode the audio data format */
342
format = (WaveFMT *)chunk.data;
343
if ( chunk.magic != FMT ) {
345
Mix_SetError("Complex WAVE files not supported");
349
switch (SDL_SwapLE16(format->encoding)) {
351
/* We can understand this */
354
Mix_SetError("Unknown WAVE data format");
358
memset(spec, 0, (sizeof *spec));
359
spec->freq = SDL_SwapLE32(format->frequency);
360
switch (SDL_SwapLE16(format->bitspersample)) {
362
spec->format = AUDIO_U8;
365
spec->format = AUDIO_S16;
368
Mix_SetError("Unknown PCM data format");
372
spec->channels = (Uint8) SDL_SwapLE16(format->channels);
373
spec->samples = 4096; /* Good default buffer size */
375
/* Set the file offset to the DATA chunk data */
378
*start = SDL_RWtell(src) + 2*sizeof(Uint32);
379
lenread = ReadChunk(src, &chunk, 0);
384
} while ( chunk.magic != DATA );
385
*stop = SDL_RWtell(src);
388
if ( format != NULL ) {
403
/* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
404
* I don't pretend to fully understand it.
407
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
409
/* Negative number? */
410
if (sanebuf[0] & 0x80)
414
if (sanebuf[0] <= 0x3F)
418
if (sanebuf[0] > 0x40)
422
if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
425
return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
426
| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
429
static FILE *LoadAIFFStream (const char *file, SDL_AudioSpec *spec,
430
long *start, long *stop)
442
/* AIFF magic header */
448
/* COMM format chunk */
450
Uint32 numsamples = 0;
451
Uint16 samplesize = 0;
453
Uint32 frequency = 0;
456
/* Make sure we are passed a valid data source */
458
wavefp = fopen(file, "rb");
461
src = SDL_RWFromFP(wavefp, 0);
468
/* Check the magic header */
469
FORMchunk = SDL_ReadLE32(src);
470
chunk_length = SDL_ReadBE32(src);
471
AIFFmagic = SDL_ReadLE32(src);
472
if ( (FORMchunk != FORM) || (AIFFmagic != AIFF) ) {
473
Mix_SetError("Unrecognized file type (not AIFF)");
478
/* From what I understand of the specification, chunks may appear in
479
* any order, and we should just ignore unknown ones.
481
* TODO: Better sanity-checking. E.g. what happens if the AIFF file
482
* contains compressed sound data?
489
chunk_type = SDL_ReadLE32(src);
490
chunk_length = SDL_ReadBE32(src);
491
next_chunk = SDL_RWtell(src) + chunk_length;
493
/* Paranoia to avoid infinite loops */
494
if (chunk_length == 0)
497
switch (chunk_type) {
500
offset = SDL_ReadBE32(src);
501
blocksize = SDL_ReadBE32(src);
502
*start = SDL_RWtell(src) + offset;
508
/* Read the audio data format chunk */
509
channels = SDL_ReadBE16(src);
510
numsamples = SDL_ReadBE32(src);
511
samplesize = SDL_ReadBE16(src);
512
SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
513
frequency = SANE_to_Uint32(sane_freq);
519
} while ((!found_SSND || !found_COMM)
520
&& SDL_RWseek(src, next_chunk, SEEK_SET) != -1);
523
Mix_SetError("Bad AIFF file (no SSND chunk)");
529
Mix_SetError("Bad AIFF file (no COMM chunk)");
534
*stop = *start + channels * numsamples * (samplesize / 8);
536
/* Decode the audio data format */
537
memset(spec, 0, (sizeof *spec));
538
spec->freq = frequency;
539
switch (samplesize) {
541
spec->format = AUDIO_S8;
544
spec->format = AUDIO_S16MSB;
547
Mix_SetError("Unknown samplesize in data format");
551
spec->channels = (Uint8) channels;
552
spec->samples = 4096; /* Good default buffer size */