wavestream.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 21 May 2013 21:21:23 -0700
changeset 617 87116a42526e
parent 601 05123263dab3
child 621 944412baab72
permissions -rw-r--r--
Cleaned up whitespace for the 2.0.0 release
     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 *rw, const char *magic, int freerw)
   118 {
   119     WAVStream *wave;
   120     SDL_AudioSpec wavespec;
   121 
   122     if ( ! mixer.format ) {
   123         Mix_SetError("WAV music output not started");
   124         if ( freerw ) {
   125             SDL_RWclose(rw);
   126         }
   127         return(NULL);
   128     }
   129     wave = (WAVStream *)SDL_malloc(sizeof *wave);
   130     if ( wave ) {
   131         memset(wave, 0, (sizeof *wave));
   132         wave->freerw = freerw;
   133         if ( strcmp(magic, "RIFF") == 0 ) {
   134             wave->rw = LoadWAVStream(rw, &wavespec,
   135                     &wave->start, &wave->stop);
   136         } else
   137         if ( strcmp(magic, "FORM") == 0 ) {
   138             wave->rw = LoadAIFFStream(rw, &wavespec,
   139                     &wave->start, &wave->stop);
   140         } else {
   141             Mix_SetError("Unknown WAVE format");
   142         }
   143         if ( wave->rw == NULL ) {
   144             SDL_free(wave);
   145             if ( freerw ) {
   146                 SDL_RWclose(rw);
   147             }
   148             return(NULL);
   149         }
   150         SDL_BuildAudioCVT(&wave->cvt,
   151             wavespec.format, wavespec.channels, wavespec.freq,
   152             mixer.format, mixer.channels, mixer.freq);
   153     } else {
   154         SDL_OutOfMemory();
   155         if ( freerw ) {
   156             SDL_RWclose(rw);
   157         }
   158         return(NULL);
   159     }
   160     return(wave);
   161 }
   162 
   163 /* Start playback of a given WAV stream */
   164 void WAVStream_Start(WAVStream *wave)
   165 {
   166     SDL_RWseek (wave->rw, wave->start, RW_SEEK_SET);
   167     music = wave;
   168 }
   169 
   170 /* Play some of a stream previously started with WAVStream_Start() */
   171 int WAVStream_PlaySome(Uint8 *stream, int len)
   172 {
   173     long pos;
   174     int left = 0;
   175 
   176     if ( music && ((pos=SDL_RWtell(music->rw)) < music->stop) ) {
   177         if ( music->cvt.needed ) {
   178             int original_len;
   179 
   180             original_len=(int)((double)len/music->cvt.len_ratio);
   181             if ( music->cvt.len != original_len ) {
   182                 int worksize;
   183                 if ( music->cvt.buf != NULL ) {
   184                     SDL_free(music->cvt.buf);
   185                 }
   186                 worksize = original_len*music->cvt.len_mult;
   187                 music->cvt.buf=(Uint8 *)SDL_malloc(worksize);
   188                 if ( music->cvt.buf == NULL ) {
   189                     return 0;
   190                 }
   191                 music->cvt.len = original_len;
   192             }
   193             if ( (music->stop - pos) < original_len ) {
   194                 left = (original_len - (music->stop - pos));
   195                 original_len -= left;
   196                 left = (int)((double)left*music->cvt.len_ratio);
   197             }
   198             original_len = SDL_RWread(music->rw, music->cvt.buf,1,original_len);
   199             /* At least at the time of writing, SDL_ConvertAudio()
   200                does byte-order swapping starting at the end of the
   201                buffer. Thus, if we are reading 16-bit samples, we
   202                had better make damn sure that we get an even
   203                number of bytes, or we'll get garbage.
   204              */
   205             if ( (music->cvt.src_format & 0x0010) && (original_len & 1) ) {
   206                 original_len--;
   207             }
   208             music->cvt.len = original_len;
   209             SDL_ConvertAudio(&music->cvt);
   210             SDL_MixAudio(stream, music->cvt.buf, music->cvt.len_cvt, wavestream_volume);
   211         } else {
   212             Uint8 *data;
   213             if ( (music->stop - pos) < len ) {
   214                 left = (len - (music->stop - pos));
   215                 len -= left;
   216             }
   217             data = SDL_stack_alloc(Uint8, len);
   218             if (data)
   219             {
   220                 SDL_RWread(music->rw, data, len, 1);
   221                 SDL_MixAudio(stream, data, len, wavestream_volume);
   222                 SDL_stack_free(data);
   223             }
   224         }
   225     }
   226     return left;
   227 }
   228 
   229 /* Stop playback of a stream previously started with WAVStream_Start() */
   230 void WAVStream_Stop(void)
   231 {
   232     music = NULL;
   233 }
   234 
   235 /* Close the given WAV stream */
   236 void WAVStream_FreeSong(WAVStream *wave)
   237 {
   238     if ( wave ) {
   239         /* Clean up associated data */
   240         if ( wave->cvt.buf ) {
   241             SDL_free(wave->cvt.buf);
   242         }
   243         if ( wave->freerw ) {
   244             SDL_RWclose(wave->rw);
   245         }
   246         SDL_free(wave);
   247     }
   248 }
   249 
   250 /* Return non-zero if a stream is currently playing */
   251 int WAVStream_Active(void)
   252 {
   253     int active;
   254 
   255     active = 0;
   256     if ( music && (SDL_RWtell(music->rw) < music->stop) ) {
   257         active = 1;
   258     }
   259     return(active);
   260 }
   261 
   262 static int ReadChunk(SDL_RWops *src, Chunk *chunk, int read_data)
   263 {
   264     chunk->magic    = SDL_ReadLE32(src);
   265     chunk->length   = SDL_ReadLE32(src);
   266     if ( read_data ) {
   267         chunk->data = (Uint8 *)SDL_malloc(chunk->length);
   268         if ( chunk->data == NULL ) {
   269             Mix_SetError("Out of memory");
   270             return(-1);
   271         }
   272         if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) {
   273             Mix_SetError("Couldn't read chunk");
   274             SDL_free(chunk->data);
   275             return(-1);
   276         }
   277     } else {
   278         SDL_RWseek(src, chunk->length, RW_SEEK_CUR);
   279     }
   280     return(chunk->length);
   281 }
   282 
   283 static SDL_RWops *LoadWAVStream (SDL_RWops *src, SDL_AudioSpec *spec,
   284                     long *start, long *stop)
   285 {
   286     int was_error;
   287     Chunk chunk;
   288     int lenread;
   289 
   290     /* WAV magic header */
   291     Uint32 RIFFchunk;
   292     Uint32 wavelen;
   293     Uint32 WAVEmagic;
   294 
   295     /* FMT chunk */
   296     WaveFMT *format = NULL;
   297 
   298     was_error = 0;
   299 
   300     /* Check the magic header */
   301     RIFFchunk   = SDL_ReadLE32(src);
   302     wavelen     = SDL_ReadLE32(src);
   303     WAVEmagic   = SDL_ReadLE32(src);
   304     if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) {
   305         Mix_SetError("Unrecognized file type (not WAVE)");
   306         was_error = 1;
   307         goto done;
   308     }
   309 
   310     /* Read the audio data format chunk */
   311     chunk.data = NULL;
   312     do {
   313         /* FIXME! Add this logic to SDL_LoadWAV_RW() */
   314         if ( chunk.data ) {
   315             SDL_free(chunk.data);
   316         }
   317         lenread = ReadChunk(src, &chunk, 1);
   318         if ( lenread < 0 ) {
   319             was_error = 1;
   320             goto done;
   321         }
   322     } while ( (chunk.magic == FACT) || (chunk.magic == LIST) );
   323 
   324     /* Decode the audio data format */
   325     format = (WaveFMT *)chunk.data;
   326     if ( chunk.magic != FMT ) {
   327         SDL_free(chunk.data);
   328         Mix_SetError("Complex WAVE files not supported");
   329         was_error = 1;
   330         goto done;
   331     }
   332     switch (SDL_SwapLE16(format->encoding)) {
   333         case PCM_CODE:
   334             /* We can understand this */
   335             break;
   336         default:
   337             Mix_SetError("Unknown WAVE data format");
   338             was_error = 1;
   339             goto done;
   340     }
   341     memset(spec, 0, (sizeof *spec));
   342     spec->freq = SDL_SwapLE32(format->frequency);
   343     switch (SDL_SwapLE16(format->bitspersample)) {
   344         case 8:
   345             spec->format = AUDIO_U8;
   346             break;
   347         case 16:
   348             spec->format = AUDIO_S16;
   349             break;
   350         default:
   351             Mix_SetError("Unknown PCM data format");
   352             was_error = 1;
   353             goto done;
   354     }
   355     spec->channels = (Uint8) SDL_SwapLE16(format->channels);
   356     spec->samples = 4096;       /* Good default buffer size */
   357 
   358     /* Set the file offset to the DATA chunk data */
   359     chunk.data = NULL;
   360     do {
   361         *start = SDL_RWtell(src) + 2*sizeof(Uint32);
   362         lenread = ReadChunk(src, &chunk, 0);
   363         if ( lenread < 0 ) {
   364             was_error = 1;
   365             goto done;
   366         }
   367     } while ( chunk.magic != DATA );
   368     *stop = SDL_RWtell(src);
   369 
   370 done:
   371     if ( format != NULL ) {
   372         SDL_free(format);
   373     }
   374     if ( was_error ) {
   375         return NULL;
   376     }
   377     return(src);
   378 }
   379 
   380 /* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
   381  * I don't pretend to fully understand it.
   382  */
   383 
   384 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
   385 {
   386     /* Negative number? */
   387     if (sanebuf[0] & 0x80)
   388         return 0;
   389 
   390     /* Less than 1? */
   391     if (sanebuf[0] <= 0x3F)
   392         return 1;
   393 
   394     /* Way too big? */
   395     if (sanebuf[0] > 0x40)
   396         return 0x4000000;
   397 
   398     /* Still too big? */
   399     if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
   400         return 800000000;
   401 
   402     return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
   403         | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
   404 }
   405 
   406 static SDL_RWops *LoadAIFFStream (SDL_RWops *src, SDL_AudioSpec *spec,
   407                     long *start, long *stop)
   408 {
   409     int was_error;
   410     int found_SSND;
   411     int found_COMM;
   412 
   413     Uint32 chunk_type;
   414     Uint32 chunk_length;
   415     long next_chunk;
   416 
   417     /* AIFF magic header */
   418     Uint32 FORMchunk;
   419     Uint32 AIFFmagic;
   420     /* SSND chunk        */
   421     Uint32 offset;
   422     Uint32 blocksize;
   423     /* COMM format chunk */
   424     Uint16 channels = 0;
   425     Uint32 numsamples = 0;
   426     Uint16 samplesize = 0;
   427     Uint8 sane_freq[10];
   428     Uint32 frequency = 0;
   429 
   430     was_error = 0;
   431 
   432     /* Check the magic header */
   433     FORMchunk   = SDL_ReadLE32(src);
   434     chunk_length    = SDL_ReadBE32(src);
   435     AIFFmagic   = SDL_ReadLE32(src);
   436     if ( (FORMchunk != FORM) || (AIFFmagic != AIFF) ) {
   437         Mix_SetError("Unrecognized file type (not AIFF)");
   438         was_error = 1;
   439         goto done;
   440     }
   441 
   442     /* From what I understand of the specification, chunks may appear in
   443          * any order, and we should just ignore unknown ones.
   444      *
   445      * TODO: Better sanity-checking. E.g. what happens if the AIFF file
   446      *       contains compressed sound data?
   447          */
   448 
   449     found_SSND = 0;
   450     found_COMM = 0;
   451 
   452     do {
   453         chunk_type      = SDL_ReadLE32(src);
   454         chunk_length    = SDL_ReadBE32(src);
   455         next_chunk      = SDL_RWtell(src) + chunk_length;
   456 
   457         /* Paranoia to avoid infinite loops */
   458         if (chunk_length == 0)
   459         break;
   460 
   461             switch (chunk_type) {
   462         case SSND:
   463             found_SSND      = 1;
   464             offset      = SDL_ReadBE32(src);
   465             blocksize       = SDL_ReadBE32(src);
   466             *start      = SDL_RWtell(src) + offset;
   467             break;
   468 
   469         case COMM:
   470             found_COMM      = 1;
   471 
   472             /* Read the audio data format chunk */
   473             channels        = SDL_ReadBE16(src);
   474             numsamples      = SDL_ReadBE32(src);
   475             samplesize      = SDL_ReadBE16(src);
   476             SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
   477             frequency       = SANE_to_Uint32(sane_freq);
   478             break;
   479 
   480         default:
   481             break;
   482         }
   483     } while ((!found_SSND || !found_COMM)
   484          && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != -1);
   485 
   486     if (!found_SSND) {
   487         Mix_SetError("Bad AIFF file (no SSND chunk)");
   488         was_error = 1;
   489         goto done;
   490     }
   491 
   492     if (!found_COMM) {
   493         Mix_SetError("Bad AIFF file (no COMM chunk)");
   494         was_error = 1;
   495         goto done;
   496     }
   497 
   498     *stop = *start + channels * numsamples * (samplesize / 8);
   499 
   500     /* Decode the audio data format */
   501     memset(spec, 0, (sizeof *spec));
   502     spec->freq = frequency;
   503     switch (samplesize) {
   504         case 8:
   505             spec->format = AUDIO_S8;
   506             break;
   507         case 16:
   508             spec->format = AUDIO_S16MSB;
   509             break;
   510         default:
   511             Mix_SetError("Unknown samplesize in data format");
   512             was_error = 1;
   513             goto done;
   514     }
   515     spec->channels = (Uint8) channels;
   516     spec->samples = 4096;       /* Good default buffer size */
   517 
   518 done:
   519     if ( was_error ) {
   520         return NULL;
   521     }
   522     return(src);
   523 }
   524