wavestream.c
author Ozkan Sezer <sezeroz@gmail.com>
Sun, 07 Oct 2018 01:20:30 +0300
branchSDL-1.2
changeset 876 fab846266822
parent 561 87bdb4c81c0b
permissions -rw-r--r--
backport fix for bug #2604. (from 2.0 branch commit dd498db4e82e).
     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 		Mix_SetError("Complex WAVE files not supported");
   328 		was_error = 1;
   329 		goto done;
   330 	}
   331 	switch (SDL_SwapLE16(format->encoding)) {
   332 		case PCM_CODE:
   333 			/* We can understand this */
   334 			break;
   335 		default:
   336 			Mix_SetError("Unknown WAVE data format");
   337 			was_error = 1;
   338 			goto done;
   339 	}
   340 	memset(spec, 0, (sizeof *spec));
   341 	spec->freq = SDL_SwapLE32(format->frequency);
   342 	switch (SDL_SwapLE16(format->bitspersample)) {
   343 		case 8:
   344 			spec->format = AUDIO_U8;
   345 			break;
   346 		case 16:
   347 			spec->format = AUDIO_S16;
   348 			break;
   349 		default:
   350 			Mix_SetError("Unknown PCM data format");
   351 			was_error = 1;
   352 			goto done;
   353 	}
   354 	spec->channels = (Uint8) SDL_SwapLE16(format->channels);
   355 	spec->samples = 4096;		/* Good default buffer size */
   356 
   357 	/* Set the file offset to the DATA chunk data */
   358 	chunk.data = NULL;
   359 	do {
   360 		*start = SDL_RWtell(src) + 2*sizeof(Uint32);
   361 		lenread = ReadChunk(src, &chunk, 0);
   362 		if ( lenread < 0 ) {
   363 			was_error = 1;
   364 			goto done;
   365 		}
   366 	} while ( chunk.magic != DATA );
   367 	*stop = SDL_RWtell(src);
   368 
   369 done:
   370 	if ( format != NULL ) {
   371 		SDL_free(format);
   372 	}
   373 	if ( was_error ) {
   374 		return NULL;
   375 	}
   376 	return(src);
   377 }
   378 
   379 /* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
   380  * I don't pretend to fully understand it.
   381  */
   382 
   383 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
   384 {
   385 	/* Negative number? */
   386 	if (sanebuf[0] & 0x80)
   387 		return 0;
   388 
   389 	/* Less than 1? */
   390 	if (sanebuf[0] <= 0x3F)
   391 		return 1;
   392 
   393 	/* Way too big? */
   394 	if (sanebuf[0] > 0x40)
   395 		return 0x4000000;
   396 
   397 	/* Still too big? */
   398 	if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
   399 		return 800000000;
   400 
   401 	return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
   402 		| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
   403 }
   404 
   405 static SDL_RWops *LoadAIFFStream (SDL_RWops *src, SDL_AudioSpec *spec,
   406 					long *start, long *stop)
   407 {
   408 	int was_error;
   409 	int found_SSND;
   410 	int found_COMM;
   411 
   412 	Uint32 chunk_type;
   413 	Uint32 chunk_length;
   414 	long next_chunk;
   415 
   416 	/* AIFF magic header */
   417 	Uint32 FORMchunk;
   418 	Uint32 AIFFmagic;
   419 	/* SSND chunk        */
   420 	Uint32 offset;
   421 	Uint32 blocksize;
   422 	/* COMM format chunk */
   423 	Uint16 channels = 0;
   424 	Uint32 numsamples = 0;
   425 	Uint16 samplesize = 0;
   426 	Uint8 sane_freq[10];
   427 	Uint32 frequency = 0;
   428 
   429 	was_error = 0;
   430 
   431 	/* Check the magic header */
   432 	FORMchunk	= SDL_ReadLE32(src);
   433 	chunk_length	= SDL_ReadBE32(src);
   434 	AIFFmagic	= SDL_ReadLE32(src);
   435 	if ( (FORMchunk != FORM) || (AIFFmagic != AIFF) ) {
   436 		Mix_SetError("Unrecognized file type (not AIFF)");
   437 		was_error = 1;
   438 		goto done;
   439 	}
   440 
   441 	/* From what I understand of the specification, chunks may appear in
   442          * any order, and we should just ignore unknown ones.
   443 	 *
   444 	 * TODO: Better sanity-checking. E.g. what happens if the AIFF file
   445 	 *       contains compressed sound data?
   446          */
   447 
   448 	found_SSND = 0;
   449 	found_COMM = 0;
   450 
   451 	do {
   452 	    chunk_type		= SDL_ReadLE32(src);
   453 	    chunk_length	= SDL_ReadBE32(src);
   454 	    next_chunk		= SDL_RWtell(src) + chunk_length;
   455 
   456 	    /* Paranoia to avoid infinite loops */
   457 	    if (chunk_length == 0)
   458 		break;
   459 
   460             switch (chunk_type) {
   461 		case SSND:
   462 		    found_SSND		= 1;
   463 		    offset		= SDL_ReadBE32(src);
   464 		    blocksize		= SDL_ReadBE32(src);
   465 		    *start		= SDL_RWtell(src) + offset;
   466 		    break;
   467 
   468 		case COMM:
   469 		    found_COMM		= 1;
   470 
   471 		    /* Read the audio data format chunk */
   472 		    channels		= SDL_ReadBE16(src);
   473 		    numsamples		= SDL_ReadBE32(src);
   474 		    samplesize		= SDL_ReadBE16(src);
   475 		    SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
   476 		    frequency		= SANE_to_Uint32(sane_freq);
   477 		    break;
   478 
   479 		default:
   480 		    break;
   481 	    }
   482 	} while ((!found_SSND || !found_COMM)
   483 		 && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != -1);
   484 
   485 	if (!found_SSND) {
   486 	    Mix_SetError("Bad AIFF file (no SSND chunk)");
   487 	    was_error = 1;
   488 	    goto done;
   489 	}
   490 		    
   491 	if (!found_COMM) {
   492 	    Mix_SetError("Bad AIFF file (no COMM chunk)");
   493 	    was_error = 1;
   494 	    goto done;
   495 	}
   496 
   497 	*stop = *start + channels * numsamples * (samplesize / 8);
   498 
   499 	/* Decode the audio data format */
   500 	memset(spec, 0, (sizeof *spec));
   501 	spec->freq = frequency;
   502 	switch (samplesize) {
   503 		case 8:
   504 			spec->format = AUDIO_S8;
   505 			break;
   506 		case 16:
   507 			spec->format = AUDIO_S16MSB;
   508 			break;
   509 		default:
   510 			Mix_SetError("Unknown samplesize in data format");
   511 			was_error = 1;
   512 			goto done;
   513 	}
   514 	spec->channels = (Uint8) channels;
   515 	spec->samples = 4096;		/* Good default buffer size */
   516 
   517 done:
   518 	if ( was_error ) {
   519 		return NULL;
   520 	}
   521 	return(src);
   522 }
   523