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

📄 mixer.c

📁 SDL_mixer 是一个基于 SDL 的混音器
💻 C
📖 第 1 页 / 共 2 页
字号:
/*    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: mixer.c 2290 2006-05-01 03:34:46Z slouken $ */#include <stdio.h>#include <stdlib.h>#include <string.h>#include "SDL_mutex.h"#include "SDL_endian.h"#include "SDL_timer.h"#include "SDL_mixer.h"#include "load_aiff.h"#include "load_voc.h"#include "load_ogg.h"/* Magic numbers for various audio file formats */#define RIFF		0x46464952		/* "RIFF" */#define WAVE		0x45564157		/* "WAVE" */#define FORM		0x4d524f46		/* "FORM" */#define OGGS		0x5367674f		/* "OggS" */#define CREA	    	0x61657243		/* "Crea" */static int audio_opened = 0;static SDL_AudioSpec mixer;typedef struct _Mix_effectinfo{	Mix_EffectFunc_t callback;	Mix_EffectDone_t done_callback;	void *udata;	struct _Mix_effectinfo *next;} effect_info;static struct _Mix_Channel {	Mix_Chunk *chunk;	int playing;	int paused;	Uint8 *samples;	int volume;	int looping;	int tag;	Uint32 expire;	Uint32 start_time;	Mix_Fading fading;	int fade_volume;	Uint32 fade_length;	Uint32 ticks_fade;	effect_info *effects;} *mix_channel = NULL;static effect_info *posteffects = NULL;static int num_channels;static int reserved_channels = 0;/* Support for hooking into the mixer callback system */static void (*mix_postmix)(void *udata, Uint8 *stream, int len) = NULL;static void *mix_postmix_data = NULL;/* rcg07062001 callback to alert when channels are done playing. */static void (*channel_done_callback)(int channel) = NULL;/* Music function declarations */extern int open_music(SDL_AudioSpec *mixer);extern void close_music(void);/* Support for user defined music functions, plus the default one */extern int volatile music_active;extern void music_mixer(void *udata, Uint8 *stream, int len);static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer;static void *music_data = NULL;/* rcg06192001 get linked library's version. */const SDL_version *Mix_Linked_Version(void){	static SDL_version linked_version;	SDL_MIXER_VERSION(&linked_version);	return(&linked_version);}static int _Mix_remove_all_effects(int channel, effect_info **e);/* * rcg06122001 Cleanup effect callbacks. *  MAKE SURE SDL_LockAudio() is called before this (or you're in the *   audio callback). */static void _Mix_channel_done_playing(int channel){	if (channel_done_callback) {	    channel_done_callback(channel);	}	/*	 * Call internal function directly, to avoid locking audio from	 *   inside audio callback.	 */	_Mix_remove_all_effects(channel, &mix_channel[channel].effects);}static void *Mix_DoEffects(int chan, void *snd, int len){	int posteffect = (chan == MIX_CHANNEL_POST);	effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects);	void *buf = snd;	if (e != NULL) {    /* are there any registered effects? */		/* if this is the postmix, we can just overwrite the original. */		if (!posteffect) {			buf = malloc(len);			if (buf == NULL) {				return(snd);			}		    memcpy(buf, snd, len);		}		for (; e != NULL; e = e->next) {			if (e->callback != NULL) {				e->callback(chan, buf, len, e->udata);			}		}	}	/* be sure to free() the return value if != snd ... */	return(buf);}/* Mixing function */static void mix_channels(void *udata, Uint8 *stream, int len){	Uint8 *mix_input;	int i, mixable, volume;	Uint32 sdl_ticks;	/* Mix the music (must be done before the channels are added) */	if ( music_active || (mix_music != music_mixer) ) {		mix_music(music_data, stream, len);	}	/* Mix any playing channels... */	sdl_ticks = SDL_GetTicks();	for ( i=0; i<num_channels; ++i ) {		if( ! mix_channel[i].paused ) {			if ( mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks ) {				/* Expiration delay for that channel is reached */				mix_channel[i].playing = 0;				mix_channel[i].fading = MIX_NO_FADING;				mix_channel[i].expire = 0;				_Mix_channel_done_playing(i);			} else if ( mix_channel[i].fading != MIX_NO_FADING ) {				Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade;				if( ticks > mix_channel[i].fade_length ) {					if( mix_channel[i].fading == MIX_FADING_OUT ) {						mix_channel[i].playing = 0;						mix_channel[i].expire = 0;						Mix_Volume(i, mix_channel[i].fade_volume); /* Restore the volume */						_Mix_channel_done_playing(i);					}					mix_channel[i].fading = MIX_NO_FADING;				} else {					if( mix_channel[i].fading == MIX_FADING_OUT ) {						Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks))								   / mix_channel[i].fade_length );					} else {						Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length );					}				}			}			if ( mix_channel[i].playing > 0 ) {				int index = 0;				int remaining = len;				while (mix_channel[i].playing > 0 && index < len) {					remaining = len - index;					volume = (mix_channel[i].volume*mix_channel[i].chunk->volume) / MIX_MAX_VOLUME;					mixable = mix_channel[i].playing;					if ( mixable > remaining ) {						mixable = remaining;					}					mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable);					SDL_MixAudio(stream+index,mix_input,mixable,volume);					if (mix_input != mix_channel[i].samples)						free(mix_input);					mix_channel[i].samples += mixable;					mix_channel[i].playing -= mixable;					index += mixable;					/* rcg06072001 Alert app if channel is done playing. */					if (!mix_channel[i].playing && !mix_channel[i].looping) {						_Mix_channel_done_playing(i);					}				}				/* If looping the sample and we are at its end, make sure				   we will still return a full buffer */				while ( mix_channel[i].looping && index < len ) {					int alen = mix_channel[i].chunk->alen;					remaining = len - index;				    	if (remaining > alen) {						remaining = alen;				    	}					mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining);					SDL_MixAudio(stream+index, mix_input, remaining, volume);					if (mix_input != mix_channel[i].chunk->abuf)						free(mix_input);					--mix_channel[i].looping;					mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining;					mix_channel[i].playing = mix_channel[i].chunk->alen - remaining;					index += remaining;				}				if ( ! mix_channel[i].playing && mix_channel[i].looping ) {					if ( --mix_channel[i].looping ) {						mix_channel[i].samples = mix_channel[i].chunk->abuf;						mix_channel[i].playing = mix_channel[i].chunk->alen;					}				}			}		}	}	/* rcg06122001 run posteffects... */	Mix_DoEffects(MIX_CHANNEL_POST, stream, len);	if ( mix_postmix ) {		mix_postmix(mix_postmix_data, stream, len);	}}static void PrintFormat(char *title, SDL_AudioSpec *fmt){	printf("%s: %d bit %s audio (%s) at %u Hz\n", title, (fmt->format&0xFF),			(fmt->format&0x8000) ? "signed" : "unsigned",			(fmt->channels > 2) ? "surround" :			(fmt->channels > 1) ? "stereo" : "mono", fmt->freq);}void _Mix_InitEffects(void);/* Open the mixer with a certain desired audio format */int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize){	int i;	SDL_AudioSpec desired;	/* If the mixer is already opened, increment open count */	if ( audio_opened ) {		if ( format == mixer.format && nchannels == mixer.channels ) {	    	++audio_opened;	    	return(0);		}		while ( audio_opened ) {			Mix_CloseAudio();		}	}	/* Set the desired format and frequency */	desired.freq = frequency;	desired.format = format;	desired.channels = nchannels;	desired.samples = chunksize;	desired.callback = mix_channels;	desired.userdata = NULL;	/* Accept nearly any audio format */	if ( SDL_OpenAudio(&desired, &mixer) < 0 ) {		return(-1);	}#if 0	PrintFormat("Audio device", &mixer);#endif	/* Initialize the music players */	if ( open_music(&mixer) < 0 ) {		SDL_CloseAudio();		return(-1);	}	num_channels = MIX_CHANNELS;	mix_channel = (struct _Mix_Channel *) malloc(num_channels * sizeof(struct _Mix_Channel));	/* Clear out the audio channels */	for ( i=0; i<num_channels; ++i ) {		mix_channel[i].chunk = NULL;		mix_channel[i].playing = 0;		mix_channel[i].looping = 0;		mix_channel[i].volume = SDL_MIX_MAXVOLUME;		mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;		mix_channel[i].fading = MIX_NO_FADING;		mix_channel[i].tag = -1;		mix_channel[i].expire = 0;		mix_channel[i].effects = NULL;		mix_channel[i].paused = 0;	}	Mix_VolumeMusic(SDL_MIX_MAXVOLUME);	_Mix_InitEffects();	audio_opened = 1;	SDL_PauseAudio(0);	return(0);}/* Dynamically change the number of channels managed by the mixer.   If decreasing the number of channels, the upper channels are   stopped. */int Mix_AllocateChannels(int numchans){	if ( numchans<0 || numchans==num_channels )		return(num_channels);	if ( numchans < num_channels ) {		/* Stop the affected channels */		int i;		for(i=numchans; i < num_channels; i++) {			Mix_HaltChannel(i);		}	}	SDL_LockAudio();	mix_channel = (struct _Mix_Channel *) realloc(mix_channel, numchans * sizeof(struct _Mix_Channel));	if ( numchans > num_channels ) {		/* Initialize the new channels */		int i;		for(i=num_channels; i < numchans; i++) {			mix_channel[i].chunk = NULL;			mix_channel[i].playing = 0;			mix_channel[i].looping = 0;			mix_channel[i].volume = SDL_MIX_MAXVOLUME;			mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;			mix_channel[i].fading = MIX_NO_FADING;			mix_channel[i].tag = -1;			mix_channel[i].expire = 0;			mix_channel[i].effects = NULL;			mix_channel[i].paused = 0;		}	}	num_channels = numchans;	SDL_UnlockAudio();	return(num_channels);}/* Return the actual mixer parameters */int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels){	if ( audio_opened ) {		if ( frequency ) {			*frequency = mixer.freq;		}		if ( format ) {			*format = mixer.format;		}		if ( channels ) {			*channels = mixer.channels;		}	}	return(audio_opened);}/* * !!! FIXME: Ideally, we want a Mix_LoadSample_RW(), which will handle the *             generic setup, then call the correct file format loader. *//* Load a wave file */Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc){	Uint32 magic;	Mix_Chunk *chunk;	SDL_AudioSpec wavespec, *loaded;	SDL_AudioCVT wavecvt;	int samplesize;	/* rcg06012001 Make sure src is valid */	if ( ! src ) {		SDL_SetError("Mix_LoadWAV_RW with NULL src");		return(NULL);	}	/* Make sure audio has been opened */	if ( ! audio_opened ) {		SDL_SetError("Audio device hasn't been opened");		if ( freesrc && src ) {			SDL_RWclose(src);		}		return(NULL);	}	/* Allocate the chunk memory */	chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));	if ( chunk == NULL ) {		SDL_SetError("Out of memory");		if ( freesrc ) {			SDL_RWclose(src);		}		return(NULL);	}	/* Find out what kind of audio file this is */	magic = SDL_ReadLE32(src);	/* Seek backwards for compatibility with older loaders */	SDL_RWseek(src, -(int)sizeof(Uint32), SEEK_CUR);	switch (magic) {		case WAVE:		case RIFF:			loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec,					(Uint8 **)&chunk->abuf, &chunk->alen);			break;		case FORM:			loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec,					(Uint8 **)&chunk->abuf, &chunk->alen);			break;#ifdef OGG_MUSIC		case OGGS:			loaded = Mix_LoadOGG_RW(src, freesrc, &wavespec,					(Uint8 **)&chunk->abuf, &chunk->alen);			break;#endif		case CREA:			loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec,					(Uint8 **)&chunk->abuf, &chunk->alen);			break;		default:			SDL_SetError("Unrecognized sound file type");			return(0);				}	if ( !loaded ) {		free(chunk);		return(NULL);	}#if 0	PrintFormat("Audio device", &mixer);	PrintFormat("-- Wave file", &wavespec);#endif	/* Build the audio converter and create conversion buffers */	if ( SDL_BuildAudioCVT(&wavecvt,			wavespec.format, wavespec.channels, wavespec.freq,			mixer.format, mixer.channels, mixer.freq) < 0 ) {		SDL_FreeWAV(chunk->abuf);		free(chunk);		return(NULL);	}	samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels;	wavecvt.len = chunk->alen & ~(samplesize-1);	wavecvt.buf = (Uint8 *)malloc(wavecvt.len*wavecvt.len_mult);	if ( wavecvt.buf == NULL ) {		SDL_SetError("Out of memory");		SDL_FreeWAV(chunk->abuf);		free(chunk);		return(NULL);	}	memcpy(wavecvt.buf, chunk->abuf, chunk->alen);	SDL_FreeWAV(chunk->abuf);	/* Run the audio converter */	if ( SDL_ConvertAudio(&wavecvt) < 0 ) {		free(wavecvt.buf);		free(chunk);		return(NULL);	}	chunk->allocated = 1;	chunk->abuf = wavecvt.buf;	chunk->alen = wavecvt.len_cvt;	chunk->volume = MIX_MAX_VOLUME;	return(chunk);}/* Load a wave file of the mixer format from a memory buffer */Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem){	Mix_Chunk *chunk;	Uint8 magic[4];	/* Make sure audio has been opened */	if ( ! audio_opened ) {		SDL_SetError("Audio device hasn't been opened");		return(NULL);	}	/* Allocate the chunk memory */	chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));	if ( chunk == NULL ) {		SDL_SetError("Out of memory");		return(NULL);	}	/* Essentially just skip to the audio data (no error checking - fast) */	chunk->allocated = 0;	mem += 12; /* WAV header */	do {		memcpy(magic, mem, 4);		mem += 4;		chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0]));		mem += 4;		chunk->abuf = mem;		mem += chunk->alen;	} while ( memcmp(magic, "data", 4) != 0 );	chunk->volume = MIX_MAX_VOLUME;	return(chunk);}/* Load raw audio data of the mixer format from a memory buffer */Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len){	Mix_Chunk *chunk;	/* Make sure audio has been opened */	if ( ! audio_opened ) {		SDL_SetError("Audio device hasn't been opened");		return(NULL);	}	/* Allocate the chunk memory */	chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));	if ( chunk == NULL ) {		SDL_SetError("Out of memory");		return(NULL);	}	/* Essentially just point at the audio data (no error checking - fast) */	chunk->allocated = 0;	chunk->alen = len;	chunk->abuf = mem;	chunk->volume = MIX_MAX_VOLUME;	return(chunk);}/* Free an audio chunk previously loaded */void Mix_FreeChunk(Mix_Chunk *chunk){	int i;	/* Caution -- if the chunk is playing, the mixer will crash */	if ( chunk ) {		/* Guarantee that this chunk isn't playing */		SDL_LockAudio();		if ( mix_channel ) {			for ( i=0; i<num_channels; ++i ) {				if ( chunk == mix_channel[i].chunk ) {					mix_channel[i].playing = 0;				}			}		}		SDL_UnlockAudio();		/* Actually free the chunk */		if ( chunk->allocated ) {			free(chunk->abuf);		}		free(chunk);	}}/* Set a function that is called after all mixing is performed.   This can be used to provide real-time visual display of the audio stream   or add a custom mixer filter for the stream data.*/void Mix_SetPostMix(void (*mix_func)                    (void *udata, Uint8 *stream, int len), void *arg){	SDL_LockAudio();	mix_postmix_data = arg;	mix_postmix = mix_func;	SDL_UnlockAudio();}/* Add your own music player or mixer function.   If 'mix_func' is NULL, the default music player is re-enabled. */void Mix_HookMusic(void (*mix_func)(void *udata, Uint8 *stream, int len),                                                                void *arg){	SDL_LockAudio();	if ( mix_func != NULL ) {		music_data = arg;		mix_music = mix_func;	} else {		music_data = NULL;		mix_music = music_mixer;	}	SDL_UnlockAudio();}void *Mix_GetMusicHookData(void){	return(music_data);}void Mix_ChannelFinished(void (*channel_finished)(int channel)){	SDL_LockAudio();	channel_done_callback = channel_finished;	SDL_UnlockAudio();}/* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate   them dynamically to the next sample if requested with a -1 value below.   Returns the number of reserved channels. */int Mix_ReserveChannels(int num){	if (num > num_channels)		num = num_channels;	reserved_channels = num;	return num;}static int checkchunkintegral(Mix_Chunk *chunk){

⌨️ 快捷键说明

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