wavestream.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 17 Jun 2015 00:11:41 -0700
changeset 705 fe757163b8f7
parent 693 b9ed366f0154
child 709 1ef7c1254ffb
permissions -rw-r--r--
Fixed bug 3018 - Loading MIDI music using FluidSynth leaks memory.

Philipp Wiesemann

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