⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 wavestream.c

📁 SDL_mixer 是一个基于 SDL 的混音器
💻 C
字号:
/*    SDL_mixer:  An audio mixer library based on the SDL library    Copyright (C) 1997-2004 Sam Lantinga    This library is free software; you can redistribute it and/or    modify it under the terms of the GNU Library General Public    License as published by the Free Software Foundation; either    version 2 of the License, or (at your option) any later version.    This library is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    Library General Public License for more details.    You should have received a copy of the GNU Library General Public    License along with this library; if not, write to the Free    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA    Sam Lantinga    slouken@libsdl.org*//* $Id: wavestream.c 2356 2006-05-09 08:02:35Z slouken $ *//* This file supports streaming WAV files, without volume adjustment */#include <stdlib.h>#include <string.h>#include "SDL_audio.h"#include "SDL_mutex.h"#include "SDL_rwops.h"#include "SDL_endian.h"#include "SDL_mixer.h"#include "wavestream.h"/*    Taken with permission from SDL_wave.h, part of the SDL library,    available at: http://www.libsdl.org/    and placed under the same license as this mixer library.*//* WAVE files are little-endian *//*******************************************//* Define values for Microsoft WAVE format *//*******************************************/#define RIFF		0x46464952		/* "RIFF" */#define WAVE		0x45564157		/* "WAVE" */#define FACT		0x74636166		/* "fact" */#define LIST		0x5453494c		/* "LIST" */#define FMT		0x20746D66		/* "fmt " */#define DATA		0x61746164		/* "data" */#define PCM_CODE	1#define ADPCM_CODE	2#define WAVE_MONO	1#define WAVE_STEREO	2/* Normally, these three chunks come consecutively in a WAVE file */typedef struct WaveFMT {/* Not saved in the chunk we read:	Uint32	FMTchunk;	Uint32	fmtlen;*/	Uint16	encoding;		Uint16	channels;		/* 1 = mono, 2 = stereo */	Uint32	frequency;		/* One of 11025, 22050, or 44100 Hz */	Uint32	byterate;		/* Average bytes per second */	Uint16	blockalign;		/* Bytes per sample block */	Uint16	bitspersample;		/* One of 8, 12, 16, or 4 for ADPCM */} WaveFMT;/* The general chunk found in the WAVE file */typedef struct Chunk {	Uint32 magic;	Uint32 length;	Uint8 *data;			/* Data includes magic and length */} Chunk;/*********************************************//* Define values for AIFF (IFF audio) format *//*********************************************/#define FORM		0x4d524f46		/* "FORM" */#define AIFF		0x46464941		/* "AIFF" */#define SSND		0x444e5353		/* "SSND" */#define COMM		0x4d4d4f43		/* "COMM" *//* Currently we only support a single stream at a time */static WAVStream *music = NULL;/* This is the format of the audio mixer data */static SDL_AudioSpec mixer;static int wavestream_volume = MIX_MAX_VOLUME;/* Function to load the WAV/AIFF stream */static FILE *LoadWAVStream (const char *file, SDL_AudioSpec *spec,					long *start, long *stop);static FILE *LoadAIFFStream (const char *file, SDL_AudioSpec *spec,					long *start, long *stop);/* Initialize the WAVStream player, with the given mixer settings   This function returns 0, or -1 if there was an error. */int WAVStream_Init(SDL_AudioSpec *mixerfmt){	mixer = *mixerfmt;	return(0);}void WAVStream_SetVolume(int volume){	wavestream_volume = volume;}/* Load a WAV stream from the given file */WAVStream *WAVStream_LoadSong(const char *file, const char *magic){	WAVStream *wave;	SDL_AudioSpec wavespec;	if ( ! mixer.format ) {		Mix_SetError("WAV music output not started");		return(NULL);	}	wave = (WAVStream *)malloc(sizeof *wave);	if ( wave ) {		memset(wave, 0, (sizeof *wave));		if ( strcmp(magic, "RIFF") == 0 ) {			wave->wavefp = LoadWAVStream(file, &wavespec,					&wave->start, &wave->stop);		} else		if ( strcmp(magic, "FORM") == 0 ) {			wave->wavefp = LoadAIFFStream(file, &wavespec,					&wave->start, &wave->stop);		} else {			Mix_SetError("Unknown WAVE format");		}		if ( wave->wavefp == NULL ) {			free(wave);			return(NULL);		}		SDL_BuildAudioCVT(&wave->cvt,			wavespec.format, wavespec.channels, wavespec.freq,			mixer.format, mixer.channels, mixer.freq);	}	return(wave);}/* Start playback of a given WAV stream */void WAVStream_Start(WAVStream *wave){	clearerr(wave->wavefp);	fseek(wave->wavefp, wave->start, SEEK_SET);	music = wave;}/* Play some of a stream previously started with WAVStream_Start() */void WAVStream_PlaySome(Uint8 *stream, int len){	long pos;	if ( music && ((pos=ftell(music->wavefp)) < music->stop) ) {		if ( music->cvt.needed ) {			int original_len;			original_len=(int)((double)len/music->cvt.len_ratio);			if ( music->cvt.len != original_len ) {				int worksize;				if ( music->cvt.buf != NULL ) {					free(music->cvt.buf);				}				worksize = original_len*music->cvt.len_mult;				music->cvt.buf=(Uint8 *)malloc(worksize);				if ( music->cvt.buf == NULL ) {					return;				}				music->cvt.len = original_len;			}			if ( (music->stop - pos) < original_len ) {				original_len = (music->stop - pos);			}			original_len = fread(music->cvt.buf,1,original_len,music->wavefp);			/* At least at the time of writing, SDL_ConvertAudio()			   does byte-order swapping starting at the end of the			   buffer. Thus, if we are reading 16-bit samples, we			   had better make damn sure that we get an even			   number of bytes, or we'll get garbage.			 */			if ( (music->cvt.src_format & 0x0010) && (original_len & 1) ) {				original_len--;			}			music->cvt.len = original_len;			SDL_ConvertAudio(&music->cvt);			SDL_MixAudio(stream, music->cvt.buf, music->cvt.len_cvt, wavestream_volume);		} else {			Uint8 *data;			if ( (music->stop - pos) < len ) {				len = (music->stop - pos);			}			data = SDL_stack_alloc(Uint8, len);			if (data)			{						fread(data, len, 1, music->wavefp);				SDL_MixAudio(stream, data, len, wavestream_volume);				SDL_stack_free(data);			}			}	}}/* Stop playback of a stream previously started with WAVStream_Start() */void WAVStream_Stop(void){	music = NULL;}/* Close the given WAV stream */void WAVStream_FreeSong(WAVStream *wave){	if ( wave ) {		/* Clean up associated data */		if ( wave->wavefp ) {			fclose(wave->wavefp);		}		if ( wave->cvt.buf ) {			free(wave->cvt.buf);		}		free(wave);	}}/* Return non-zero if a stream is currently playing */int WAVStream_Active(void){	int active;	active = 0;	if ( music && (ftell(music->wavefp) < music->stop) ) {		active = 1;	}	return(active);}static int ReadChunk(SDL_RWops *src, Chunk *chunk, int read_data){	chunk->magic	= SDL_ReadLE32(src);	chunk->length	= SDL_ReadLE32(src);	if ( read_data ) {		chunk->data = (Uint8 *)malloc(chunk->length);		if ( chunk->data == NULL ) {			Mix_SetError("Out of memory");			return(-1);		}		if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) {			Mix_SetError("Couldn't read chunk");			free(chunk->data);			return(-1);		}	} else {		SDL_RWseek(src, chunk->length, SEEK_CUR);	}	return(chunk->length);}static FILE *LoadWAVStream (const char *file, SDL_AudioSpec *spec,					long *start, long *stop){	int was_error;	FILE *wavefp;	SDL_RWops *src;	Chunk chunk;	int lenread;	/* WAV magic header */	Uint32 RIFFchunk;	Uint32 wavelen;	Uint32 WAVEmagic;	/* FMT chunk */	WaveFMT *format = NULL;	/* Make sure we are passed a valid data source */	was_error = 0;	wavefp = fopen(file, "rb");	src = NULL;	if ( wavefp ) {		src = SDL_RWFromFP(wavefp, 0);	}	if ( src == NULL ) {		was_error = 1;		goto done;	}	/* Check the magic header */	RIFFchunk	= SDL_ReadLE32(src);	wavelen		= SDL_ReadLE32(src);	WAVEmagic	= SDL_ReadLE32(src);	if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) {		Mix_SetError("Unrecognized file type (not WAVE)");		was_error = 1;		goto done;	}	/* Read the audio data format chunk */	chunk.data = NULL;	do {		/* FIXME! Add this logic to SDL_LoadWAV_RW() */		if ( chunk.data ) {			free(chunk.data);		}		lenread = ReadChunk(src, &chunk, 1);		if ( lenread < 0 ) {			was_error = 1;			goto done;		}	} while ( (chunk.magic == FACT) || (chunk.magic == LIST) );	/* Decode the audio data format */	format = (WaveFMT *)chunk.data;	if ( chunk.magic != FMT ) {		free(chunk.data);		Mix_SetError("Complex WAVE files not supported");		was_error = 1;		goto done;	}	switch (SDL_SwapLE16(format->encoding)) {		case PCM_CODE:			/* We can understand this */			break;		default:			Mix_SetError("Unknown WAVE data format");			was_error = 1;			goto done;	}	memset(spec, 0, (sizeof *spec));	spec->freq = SDL_SwapLE32(format->frequency);	switch (SDL_SwapLE16(format->bitspersample)) {		case 8:			spec->format = AUDIO_U8;			break;		case 16:			spec->format = AUDIO_S16;			break;		default:			Mix_SetError("Unknown PCM data format");			was_error = 1;			goto done;	}	spec->channels = (Uint8) SDL_SwapLE16(format->channels);	spec->samples = 4096;		/* Good default buffer size */	/* Set the file offset to the DATA chunk data */	chunk.data = NULL;	do {		*start = SDL_RWtell(src) + 2*sizeof(Uint32);		lenread = ReadChunk(src, &chunk, 0);		if ( lenread < 0 ) {			was_error = 1;			goto done;		}	} while ( chunk.magic != DATA );	*stop = SDL_RWtell(src);done:	if ( format != NULL ) {		free(format);	}	if ( src ) {		SDL_RWclose(src);	}	if ( was_error ) {		if ( wavefp ) {			fclose(wavefp);			wavefp = NULL;		}	}	return(wavefp);}/* I couldn't get SANE_to_double() to work, so I stole this from libsndfile. * I don't pretend to fully understand it. */static Uint32 SANE_to_Uint32 (Uint8 *sanebuf){	/* Negative number? */	if (sanebuf[0] & 0x80)		return 0;	/* Less than 1? */	if (sanebuf[0] <= 0x3F)		return 1;	/* Way too big? */	if (sanebuf[0] > 0x40)		return 0x4000000;	/* Still too big? */	if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)		return 800000000;	return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)		| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);}static FILE *LoadAIFFStream (const char *file, SDL_AudioSpec *spec,					long *start, long *stop){	int was_error;	int found_SSND;	int found_COMM;	FILE *wavefp;	SDL_RWops *src;	Uint32 chunk_type;	Uint32 chunk_length;	long next_chunk;	/* AIFF magic header */	Uint32 FORMchunk;	Uint32 AIFFmagic;	/* SSND chunk        */	Uint32 offset;	Uint32 blocksize;	/* COMM format chunk */	Uint16 channels = 0;	Uint32 numsamples = 0;	Uint16 samplesize = 0;	Uint8 sane_freq[10];	Uint32 frequency = 0;	/* Make sure we are passed a valid data source */	was_error = 0;	wavefp = fopen(file, "rb");	src = NULL;	if ( wavefp ) {		src = SDL_RWFromFP(wavefp, 0);	}	if ( src == NULL ) {		was_error = 1;		goto done;	}	/* Check the magic header */	FORMchunk	= SDL_ReadLE32(src);	chunk_length	= SDL_ReadBE32(src);	AIFFmagic	= SDL_ReadLE32(src);	if ( (FORMchunk != FORM) || (AIFFmagic != AIFF) ) {		Mix_SetError("Unrecognized file type (not AIFF)");		was_error = 1;		goto done;	}	/* From what I understand of the specification, chunks may appear in         * any order, and we should just ignore unknown ones.	 *	 * TODO: Better sanity-checking. E.g. what happens if the AIFF file	 *       contains compressed sound data?         */	found_SSND = 0;	found_COMM = 0;	do {	    chunk_type		= SDL_ReadLE32(src);	    chunk_length	= SDL_ReadBE32(src);	    next_chunk		= SDL_RWtell(src) + chunk_length;	    /* Paranoia to avoid infinite loops */	    if (chunk_length == 0)		break;            switch (chunk_type) {		case SSND:		    found_SSND		= 1;		    offset		= SDL_ReadBE32(src);		    blocksize		= SDL_ReadBE32(src);		    *start		= SDL_RWtell(src) + offset;		    break;		case COMM:		    found_COMM		= 1;		    /* Read the audio data format chunk */		    channels		= SDL_ReadBE16(src);		    numsamples		= SDL_ReadBE32(src);		    samplesize		= SDL_ReadBE16(src);		    SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);		    frequency		= SANE_to_Uint32(sane_freq);		    break;		default:		    break;	    }	} while ((!found_SSND || !found_COMM)		 && SDL_RWseek(src, next_chunk, SEEK_SET) != -1);	if (!found_SSND) {	    Mix_SetError("Bad AIFF file (no SSND chunk)");	    was_error = 1;	    goto done;	}		    	if (!found_COMM) {	    Mix_SetError("Bad AIFF file (no COMM chunk)");	    was_error = 1;	    goto done;	}	*stop = *start + channels * numsamples * (samplesize / 8);	/* Decode the audio data format */	memset(spec, 0, (sizeof *spec));	spec->freq = frequency;	switch (samplesize) {		case 8:			spec->format = AUDIO_S8;			break;		case 16:			spec->format = AUDIO_S16MSB;			break;		default:			Mix_SetError("Unknown samplesize in data format");			was_error = 1;			goto done;	}	spec->channels = (Uint8) channels;	spec->samples = 4096;		/* Good default buffer size */done:	if ( src ) {		SDL_RWclose(src);	}	if ( was_error ) {		if ( wavefp ) {			fclose(wavefp);			wavefp = NULL;		}	}	return(wavefp);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -