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