~ubuntu-branches/ubuntu/karmic/frozen-bubble/karmic

« back to all changes in this revision

Viewing changes to SDL_mixer_patched/wavestream.c

  • Committer: Bazaar Package Importer
  • Author(s): Josselin Mouette
  • Date: 2002-04-17 09:21:51 UTC
  • Revision ID: james.westby@ubuntu.com-20020417092151-7ye6ril7bgg9g0he
Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
    SDL_mixer:  An audio mixer library based on the SDL library
 
3
    Copyright (C) 1997, 1998, 1999, 2000, 2001  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,v 1.16 2001/12/17 02:39:12 slouken Exp $ */
 
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 *theWave = NULL;
 
92
 
 
93
/* This is initialized by the music mixer */
 
94
static SDL_mutex *music_lock = NULL;
 
95
 
 
96
/* This is the format of the audio mixer data */
 
97
static SDL_AudioSpec mixer;
 
98
 
 
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);
 
104
 
 
105
/* Initialize the WAVStream player, with the given mixer settings
 
106
   This function returns 0, or -1 if there was an error.
 
107
 */
 
108
int WAVStream_Init(SDL_AudioSpec *mixerfmt)
 
109
{
 
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 ) {
 
114
                return(-1);
 
115
        }
 
116
#endif
 
117
        mixer = *mixerfmt;
 
118
        return(0);
 
119
}
 
120
 
 
121
/* Unimplemented */
 
122
extern void WAVStream_SetVolume(int volume)
 
123
{
 
124
}
 
125
 
 
126
/* Load a WAV stream from the given file */
 
127
extern WAVStream *WAVStream_LoadSong(const char *file, const char *magic)
 
128
{
 
129
        WAVStream *wave;
 
130
        SDL_AudioSpec wavespec;
 
131
 
 
132
        if ( ! mixer.format ) {
 
133
                Mix_SetError("WAV music output not started");
 
134
                return(NULL);
 
135
        }
 
136
        wave = (WAVStream *)malloc(sizeof *wave);
 
137
        if ( wave ) {
 
138
                memset(wave, 0, (sizeof *wave));
 
139
                if ( strcmp(magic, "RIFF") == 0 ) {
 
140
                        wave->wavefp = LoadWAVStream(file, &wavespec,
 
141
                                        &wave->start, &wave->stop);
 
142
                } else
 
143
                if ( strcmp(magic, "FORM") == 0 ) {
 
144
                        wave->wavefp = LoadAIFFStream(file, &wavespec,
 
145
                                        &wave->start, &wave->stop);
 
146
                } else {
 
147
                        Mix_SetError("Unknown WAVE format");
 
148
                }
 
149
                if ( wave->wavefp == NULL ) {
 
150
                        free(wave);
 
151
                        return(NULL);
 
152
                }
 
153
                SDL_BuildAudioCVT(&wave->cvt,
 
154
                        wavespec.format, wavespec.channels, wavespec.freq,
 
155
                        mixer.format, mixer.channels, mixer.freq);
 
156
        }
 
157
        return(wave);
 
158
}
 
159
 
 
160
/* Start playback of a given WAV stream */
 
161
extern void WAVStream_Start(WAVStream *wave)
 
162
{
 
163
        SDL_mutexP(music_lock);
 
164
        clearerr(wave->wavefp);
 
165
        fseek(wave->wavefp, wave->start, SEEK_SET);
 
166
        theWave = wave;
 
167
        SDL_mutexV(music_lock);
 
168
}
 
169
 
 
170
/* Play some of a stream previously started with WAVStream_Start()
 
171
   The music_lock is held while this function is called.
 
172
 */
 
173
extern void WAVStream_PlaySome(Uint8 *stream, int len)
 
174
{
 
175
        long pos;
 
176
 
 
177
        SDL_mutexP(music_lock);
 
178
        if ( theWave && ((pos=ftell(theWave->wavefp)) < theWave->stop) ) {
 
179
                if ( theWave->cvt.needed ) {
 
180
                        int original_len;
 
181
 
 
182
                        original_len=(int)((double)len/theWave->cvt.len_ratio);
 
183
                        if ( theWave->cvt.len != original_len ) {
 
184
                                int worksize;
 
185
                                if ( theWave->cvt.buf != NULL ) {
 
186
                                        free(theWave->cvt.buf);
 
187
                                }
 
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);
 
192
                                        return;
 
193
                                }
 
194
                                theWave->cvt.len = original_len;
 
195
                        }
 
196
                        if ( (theWave->stop - pos) < original_len ) {
 
197
                                original_len = (theWave->stop - pos);
 
198
                        }
 
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.
 
205
                         */
 
206
                        if ( (theWave->cvt.src_format & 0x0010) && (original_len & 1) ) {
 
207
                                original_len--;
 
208
                        }
 
209
                        theWave->cvt.len = original_len;
 
210
                        SDL_ConvertAudio(&theWave->cvt);
 
211
                        memcpy(stream, theWave->cvt.buf, theWave->cvt.len_cvt);
 
212
                } else {
 
213
                        if ( (theWave->stop - pos) < len ) {
 
214
                                len = (theWave->stop - pos);
 
215
                        }
 
216
                        fread(stream, len, 1, theWave->wavefp);
 
217
                }
 
218
        }
 
219
        SDL_mutexV(music_lock);
 
220
}
 
221
 
 
222
/* Stop playback of a stream previously started with WAVStream_Start() */
 
223
extern void WAVStream_Stop(void)
 
224
{
 
225
        SDL_mutexP(music_lock);
 
226
        theWave = NULL;
 
227
        SDL_mutexV(music_lock);
 
228
}
 
229
 
 
230
/* Close the given WAV stream */
 
231
extern void WAVStream_FreeSong(WAVStream *wave)
 
232
{
 
233
        if ( wave ) {
 
234
                /* Remove song from the currently playing list */
 
235
                SDL_mutexP(music_lock);
 
236
                if ( wave == theWave ) {
 
237
                        theWave = NULL;
 
238
                }
 
239
                SDL_mutexV(music_lock);
 
240
 
 
241
                /* Clean up associated data */
 
242
                if ( wave->wavefp ) {
 
243
                        fclose(wave->wavefp);
 
244
                }
 
245
                if ( wave->cvt.buf ) {
 
246
                        free(wave->cvt.buf);
 
247
                }
 
248
                free(wave);
 
249
        }
 
250
}
 
251
 
 
252
/* Return non-zero if a stream is currently playing */
 
253
extern int WAVStream_Active(void)
 
254
{
 
255
        int active;
 
256
 
 
257
        SDL_mutexP(music_lock);
 
258
        active = 0;
 
259
        if ( theWave && (ftell(theWave->wavefp) < theWave->stop) ) {
 
260
                active = 1;
 
261
        }
 
262
        SDL_mutexV(music_lock);
 
263
 
 
264
        return(active);
 
265
}
 
266
 
 
267
static int ReadChunk(SDL_RWops *src, Chunk *chunk, int read_data)
 
268
{
 
269
        chunk->magic    = SDL_ReadLE32(src);
 
270
        chunk->length   = SDL_ReadLE32(src);
 
271
        if ( read_data ) {
 
272
                chunk->data = (Uint8 *)malloc(chunk->length);
 
273
                if ( chunk->data == NULL ) {
 
274
                        Mix_SetError("Out of memory");
 
275
                        return(-1);
 
276
                }
 
277
                if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) {
 
278
                        Mix_SetError("Couldn't read chunk");
 
279
                        free(chunk->data);
 
280
                        return(-1);
 
281
                }
 
282
        } else {
 
283
                SDL_RWseek(src, chunk->length, SEEK_CUR);
 
284
        }
 
285
        return(chunk->length);
 
286
}
 
287
 
 
288
static FILE *LoadWAVStream (const char *file, SDL_AudioSpec *spec,
 
289
                                        long *start, long *stop)
 
290
{
 
291
        int was_error;
 
292
        FILE *wavefp;
 
293
        SDL_RWops *src;
 
294
        Chunk chunk;
 
295
        int lenread;
 
296
 
 
297
        /* WAV magic header */
 
298
        Uint32 RIFFchunk;
 
299
        Uint32 wavelen;
 
300
        Uint32 WAVEmagic;
 
301
 
 
302
        /* FMT chunk */
 
303
        WaveFMT *format = NULL;
 
304
 
 
305
        /* Make sure we are passed a valid data source */
 
306
        was_error = 0;
 
307
        wavefp = fopen(file, "rb");
 
308
        src = NULL;
 
309
        if ( wavefp ) {
 
310
                src = SDL_RWFromFP(wavefp, 0);
 
311
        }
 
312
        if ( src == NULL ) {
 
313
                was_error = 1;
 
314
                goto done;
 
315
        }
 
316
 
 
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)");
 
323
                was_error = 1;
 
324
                goto done;
 
325
        }
 
326
 
 
327
        /* Read the audio data format chunk */
 
328
        chunk.data = NULL;
 
329
        do {
 
330
                /* FIXME! Add this logic to SDL_LoadWAV_RW() */
 
331
                if ( chunk.data ) {
 
332
                        free(chunk.data);
 
333
                }
 
334
                lenread = ReadChunk(src, &chunk, 1);
 
335
                if ( lenread < 0 ) {
 
336
                        was_error = 1;
 
337
                        goto done;
 
338
                }
 
339
        } while ( (chunk.magic == FACT) || (chunk.magic == LIST) );
 
340
 
 
341
        /* Decode the audio data format */
 
342
        format = (WaveFMT *)chunk.data;
 
343
        if ( chunk.magic != FMT ) {
 
344
                free(chunk.data);
 
345
                Mix_SetError("Complex WAVE files not supported");
 
346
                was_error = 1;
 
347
                goto done;
 
348
        }
 
349
        switch (SDL_SwapLE16(format->encoding)) {
 
350
                case PCM_CODE:
 
351
                        /* We can understand this */
 
352
                        break;
 
353
                default:
 
354
                        Mix_SetError("Unknown WAVE data format");
 
355
                        was_error = 1;
 
356
                        goto done;
 
357
        }
 
358
        memset(spec, 0, (sizeof *spec));
 
359
        spec->freq = SDL_SwapLE32(format->frequency);
 
360
        switch (SDL_SwapLE16(format->bitspersample)) {
 
361
                case 8:
 
362
                        spec->format = AUDIO_U8;
 
363
                        break;
 
364
                case 16:
 
365
                        spec->format = AUDIO_S16;
 
366
                        break;
 
367
                default:
 
368
                        Mix_SetError("Unknown PCM data format");
 
369
                        was_error = 1;
 
370
                        goto done;
 
371
        }
 
372
        spec->channels = (Uint8) SDL_SwapLE16(format->channels);
 
373
        spec->samples = 4096;           /* Good default buffer size */
 
374
 
 
375
        /* Set the file offset to the DATA chunk data */
 
376
        chunk.data = NULL;
 
377
        do {
 
378
                *start = SDL_RWtell(src) + 2*sizeof(Uint32);
 
379
                lenread = ReadChunk(src, &chunk, 0);
 
380
                if ( lenread < 0 ) {
 
381
                        was_error = 1;
 
382
                        goto done;
 
383
                }
 
384
        } while ( chunk.magic != DATA );
 
385
        *stop = SDL_RWtell(src);
 
386
 
 
387
done:
 
388
        if ( format != NULL ) {
 
389
                free(format);
 
390
        }
 
391
        if ( src ) {
 
392
                SDL_RWclose(src);
 
393
        }
 
394
        if ( was_error ) {
 
395
                if ( wavefp ) {
 
396
                        fclose(wavefp);
 
397
                        wavefp = NULL;
 
398
                }
 
399
        }
 
400
        return(wavefp);
 
401
}
 
402
 
 
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.
 
405
 */
 
406
 
 
407
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
 
408
{
 
409
        /* Negative number? */
 
410
        if (sanebuf[0] & 0x80)
 
411
                return 0;
 
412
 
 
413
        /* Less than 1? */
 
414
        if (sanebuf[0] <= 0x3F)
 
415
                return 1;
 
416
 
 
417
        /* Way too big? */
 
418
        if (sanebuf[0] > 0x40)
 
419
                return 0x4000000;
 
420
 
 
421
        /* Still too big? */
 
422
        if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
 
423
                return 800000000;
 
424
 
 
425
        return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
 
426
                | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
 
427
}
 
428
 
 
429
static FILE *LoadAIFFStream (const char *file, SDL_AudioSpec *spec,
 
430
                                        long *start, long *stop)
 
431
{
 
432
        int was_error;
 
433
        int found_SSND;
 
434
        int found_COMM;
 
435
        FILE *wavefp;
 
436
        SDL_RWops *src;
 
437
 
 
438
        Uint32 chunk_type;
 
439
        Uint32 chunk_length;
 
440
        long next_chunk;
 
441
 
 
442
        /* AIFF magic header */
 
443
        Uint32 FORMchunk;
 
444
        Uint32 AIFFmagic;
 
445
        /* SSND chunk        */
 
446
        Uint32 offset;
 
447
        Uint32 blocksize;
 
448
        /* COMM format chunk */
 
449
        Uint16 channels = 0;
 
450
        Uint32 numsamples = 0;
 
451
        Uint16 samplesize = 0;
 
452
        Uint8 sane_freq[10];
 
453
        Uint32 frequency = 0;
 
454
 
 
455
 
 
456
        /* Make sure we are passed a valid data source */
 
457
        was_error = 0;
 
458
        wavefp = fopen(file, "rb");
 
459
        src = NULL;
 
460
        if ( wavefp ) {
 
461
                src = SDL_RWFromFP(wavefp, 0);
 
462
        }
 
463
        if ( src == NULL ) {
 
464
                was_error = 1;
 
465
                goto done;
 
466
        }
 
467
 
 
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)");
 
474
                was_error = 1;
 
475
                goto done;
 
476
        }
 
477
 
 
478
        /* From what I understand of the specification, chunks may appear in
 
479
         * any order, and we should just ignore unknown ones.
 
480
         *
 
481
         * TODO: Better sanity-checking. E.g. what happens if the AIFF file
 
482
         *       contains compressed sound data?
 
483
         */
 
484
 
 
485
        found_SSND = 0;
 
486
        found_COMM = 0;
 
487
 
 
488
        do {
 
489
            chunk_type          = SDL_ReadLE32(src);
 
490
            chunk_length        = SDL_ReadBE32(src);
 
491
            next_chunk          = SDL_RWtell(src) + chunk_length;
 
492
 
 
493
            /* Paranoia to avoid infinite loops */
 
494
            if (chunk_length == 0)
 
495
                break;
 
496
 
 
497
            switch (chunk_type) {
 
498
                case SSND:
 
499
                    found_SSND          = 1;
 
500
                    offset              = SDL_ReadBE32(src);
 
501
                    blocksize           = SDL_ReadBE32(src);
 
502
                    *start              = SDL_RWtell(src) + offset;
 
503
                    break;
 
504
 
 
505
                case COMM:
 
506
                    found_COMM          = 1;
 
507
 
 
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);
 
514
                    break;
 
515
 
 
516
                default:
 
517
                    break;
 
518
            }
 
519
        } while ((!found_SSND || !found_COMM)
 
520
                 && SDL_RWseek(src, next_chunk, SEEK_SET) != -1);
 
521
 
 
522
        if (!found_SSND) {
 
523
            Mix_SetError("Bad AIFF file (no SSND chunk)");
 
524
            was_error = 1;
 
525
            goto done;
 
526
        }
 
527
                    
 
528
        if (!found_COMM) {
 
529
            Mix_SetError("Bad AIFF file (no COMM chunk)");
 
530
            was_error = 1;
 
531
            goto done;
 
532
        }
 
533
 
 
534
        *stop = *start + channels * numsamples * (samplesize / 8);
 
535
 
 
536
        /* Decode the audio data format */
 
537
        memset(spec, 0, (sizeof *spec));
 
538
        spec->freq = frequency;
 
539
        switch (samplesize) {
 
540
                case 8:
 
541
                        spec->format = AUDIO_S8;
 
542
                        break;
 
543
                case 16:
 
544
                        spec->format = AUDIO_S16MSB;
 
545
                        break;
 
546
                default:
 
547
                        Mix_SetError("Unknown samplesize in data format");
 
548
                        was_error = 1;
 
549
                        goto done;
 
550
        }
 
551
        spec->channels = (Uint8) channels;
 
552
        spec->samples = 4096;           /* Good default buffer size */
 
553
 
 
554
done:
 
555
        if ( src ) {
 
556
                SDL_RWclose(src);
 
557
        }
 
558
        if ( was_error ) {
 
559
                if ( wavefp ) {
 
560
                        fclose(wavefp);
 
561
                        wavefp = NULL;
 
562
                }
 
563
        }
 
564
        return(wavefp);
 
565
}