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