wavestream.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 09 May 2006 08:02:35 +0000
changeset 308 02f1dc41d4e7
parent 241 503416fca921
child 381 2064088ea781
permissions -rw-r--r--
Fixed bug #73

Date: Tue, 15 Apr 2003 23:36:17 +0300
From: King Of The Bongo
Subject: [SDL] SDL_mixer patch

Hi, this is modified version of wavestream.c

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