wavestream.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 13 Jan 2012 03:15:19 -0500
changeset 561 87bdb4c81c0b
parent 542 3de4970b36d4
child 601 05123263dab3
child 876 fab846266822
permissions -rw-r--r--
Fixed memory crash loading Ogg Vorbis files on Windows
The pointer to the audio data could come from SDL_LoadWAV_RW() or from our other loaders, and we need to use the correct free() to release the memory. So we'll just use the SDL memory functions for consistency.
This pretty much only matters on Windows where we can't guarantee a certain C runtime so we provide our own malloc() and friends.
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2012 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