diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/Makefile b/contrib/sdk/sources/SDL_mixer-1.2.12/Makefile new file mode 100644 index 0000000000..95ad034901 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/Makefile @@ -0,0 +1,33 @@ +CC = kos32-gcc +AR = kos32-ar +LD = kos32-ld +STRIP = kos32-strip + +LIBNAME=libSDL_mixer + +SDK_DIR:= $(abspath ../../../sdk) + +OBJS = effect_stereoreverse.o \ + effect_position.o \ + effects_internal.o \ + music.o \ + mixer.o \ + load_ogg.o \ + music_ogg.o \ + dynamic_ogg.o \ + wavestream.o \ + load_aiff.o \ + load_voc.o + +CFLAGS = -c -O2 -mpreferred-stack-boundary=2 -fno-ident -fomit-frame-pointer -fno-stack-check -fno-stack-protector -mno-stack-arg-probe -fno-exceptions -fno-asynchronous-unwind-tables -ffast-math -mno-ms-bitfields -march=pentium-mmx -UWIN32 -U_Win32 -U_WIN32 -U__MINGW32__ -I../newlib/libc/include/ -I../SDL-1.2.2_newlib/include -I../libogg-1.3.5/include -I.. -I../libvorbis-1.3.7/include -DOGG_MUSIC + +all: $(LIBNAME).a + +$(LIBNAME).a: $(OBJS) + $(AR) -crs $(SDK_DIR)/lib/$(LIBNAME).a $(OBJS) + +%.o : %.c Makefile + $(CC) $(CFLAGS) -o $@ $< + +clean: + rm -f */*.o \ rm *.o \ rm */*/*.o diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/SDL_mixer.h b/contrib/sdk/sources/SDL_mixer-1.2.12/SDL_mixer.h new file mode 100644 index 0000000000..48cb12966f --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/SDL_mixer.h @@ -0,0 +1,635 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifndef _SDL_MIXER_H +#define _SDL_MIXER_H + +#include "SDL_types.h" +#include "SDL_rwops.h" +#include "SDL_audio.h" +#include "SDL_endian.h" +#include "SDL_version.h" +#include "SDL_stdinc.h" +#include "begin_code.h" + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Printable format: "%d.%d.%d", MAJOR, MINOR, PATCHLEVEL +*/ +#define SDL_MIXER_MAJOR_VERSION 1 +#define SDL_MIXER_MINOR_VERSION 2 +#define SDL_MIXER_PATCHLEVEL 12 + +/* This macro can be used to fill a version structure with the compile-time + * version of the SDL_mixer library. + */ +#define SDL_MIXER_VERSION(X) \ +{ \ + (X)->major = SDL_MIXER_MAJOR_VERSION; \ + (X)->minor = SDL_MIXER_MINOR_VERSION; \ + (X)->patch = SDL_MIXER_PATCHLEVEL; \ +} + +/* Backwards compatibility */ +#define MIX_MAJOR_VERSION SDL_MIXER_MAJOR_VERSION +#define MIX_MINOR_VERSION SDL_MIXER_MINOR_VERSION +#define MIX_PATCHLEVEL SDL_MIXER_PATCHLEVEL +#define MIX_VERSION(X) SDL_MIXER_VERSION(X) + +/* This function gets the version of the dynamically linked SDL_mixer library. + it should NOT be used to fill a version structure, instead you should + use the SDL_MIXER_VERSION() macro. + */ +extern DECLSPEC const SDL_version * SDLCALL Mix_Linked_Version(void); + +typedef enum +{ + MIX_INIT_FLAC = 0x00000001, + MIX_INIT_MOD = 0x00000002, + MIX_INIT_MP3 = 0x00000004, + MIX_INIT_OGG = 0x00000008, + MIX_INIT_FLUIDSYNTH = 0x00000010 +} MIX_InitFlags; + +/* Loads dynamic libraries and prepares them for use. Flags should be + one or more flags from MIX_InitFlags OR'd together. + It returns the flags successfully initialized, or 0 on failure. + */ +extern DECLSPEC int SDLCALL Mix_Init(int flags); + +/* Unloads libraries loaded with Mix_Init */ +extern DECLSPEC void SDLCALL Mix_Quit(void); + + +/* The default mixer has 8 simultaneous mixing channels */ +#ifndef MIX_CHANNELS +#define MIX_CHANNELS 8 +#endif + +/* Good default values for a PC soundcard */ +#define MIX_DEFAULT_FREQUENCY 22050 +#if SDL_BYTEORDER == SDL_LIL_ENDIAN +#define MIX_DEFAULT_FORMAT AUDIO_S16LSB +#else +#define MIX_DEFAULT_FORMAT AUDIO_S16MSB +#endif +#define MIX_DEFAULT_CHANNELS 2 +#define MIX_MAX_VOLUME 128 /* Volume of a chunk */ + +/* The internal format for an audio chunk */ +typedef struct Mix_Chunk { + int allocated; + Uint8 *abuf; + Uint32 alen; + Uint8 volume; /* Per-sample volume, 0-128 */ +} Mix_Chunk; + +/* The different fading types supported */ +typedef enum { + MIX_NO_FADING, + MIX_FADING_OUT, + MIX_FADING_IN +} Mix_Fading; + +typedef enum { + MUS_NONE, + MUS_CMD, + MUS_WAV, + MUS_MOD, + MUS_MID, + MUS_OGG, + MUS_MP3, + MUS_MP3_MAD, + MUS_FLAC, + MUS_MODPLUG +} Mix_MusicType; + +/* The internal format for a music chunk interpreted via mikmod */ +typedef struct _Mix_Music Mix_Music; + +/* Open the mixer with a certain audio format */ +extern DECLSPEC int SDLCALL Mix_OpenAudio(int frequency, Uint16 format, int channels, + int chunksize); + +/* Dynamically change the number of channels managed by the mixer. + If decreasing the number of channels, the upper channels are + stopped. + This function returns the new number of allocated channels. + */ +extern DECLSPEC int SDLCALL Mix_AllocateChannels(int numchans); + +/* Find out what the actual audio device parameters are. + This function returns 1 if the audio has been opened, 0 otherwise. + */ +extern DECLSPEC int SDLCALL Mix_QuerySpec(int *frequency,Uint16 *format,int *channels); + +/* Load a wave file or a music (.mod .s3m .it .xm) file */ +extern DECLSPEC Mix_Chunk * SDLCALL Mix_LoadWAV_RW(SDL_RWops *src, int freesrc); +#define Mix_LoadWAV(file) Mix_LoadWAV_RW(SDL_RWFromFile(file, "rb"), 1) +extern DECLSPEC Mix_Music * SDLCALL Mix_LoadMUS(const char *file); + +/* Load a music file from an SDL_RWop object (Ogg and MikMod specific currently) + Matt Campbell (matt@campbellhome.dhs.org) April 2000 */ +extern DECLSPEC Mix_Music * SDLCALL Mix_LoadMUS_RW(SDL_RWops *rw); + +/* Load a music file from an SDL_RWop object assuming a specific format */ +extern DECLSPEC Mix_Music * SDLCALL Mix_LoadMUSType_RW(SDL_RWops *rw, Mix_MusicType type, int freesrc); + +/* Load a wave file of the mixer format from a memory buffer */ +extern DECLSPEC Mix_Chunk * SDLCALL Mix_QuickLoad_WAV(Uint8 *mem); + +/* Load raw audio data of the mixer format from a memory buffer */ +extern DECLSPEC Mix_Chunk * SDLCALL Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len); + +/* Free an audio chunk previously loaded */ +extern DECLSPEC void SDLCALL Mix_FreeChunk(Mix_Chunk *chunk); +extern DECLSPEC void SDLCALL Mix_FreeMusic(Mix_Music *music); + +/* Get a list of chunk/music decoders that this build of SDL_mixer provides. + This list can change between builds AND runs of the program, if external + libraries that add functionality become available. + You must successfully call Mix_OpenAudio() before calling these functions. + This API is only available in SDL_mixer 1.2.9 and later. + + // usage... + int i; + const int total = Mix_GetNumChunkDecoders(); + for (i = 0; i < total; i++) + printf("Supported chunk decoder: [%s]\n", Mix_GetChunkDecoder(i)); + + Appearing in this list doesn't promise your specific audio file will + decode...but it's handy to know if you have, say, a functioning Timidity + install. + + These return values are static, read-only data; do not modify or free it. + The pointers remain valid until you call Mix_CloseAudio(). +*/ +extern DECLSPEC int SDLCALL Mix_GetNumChunkDecoders(void); +extern DECLSPEC const char * SDLCALL Mix_GetChunkDecoder(int index); +extern DECLSPEC int SDLCALL Mix_GetNumMusicDecoders(void); +extern DECLSPEC const char * SDLCALL Mix_GetMusicDecoder(int index); + +/* Find out the music format of a mixer music, or the currently playing + music, if 'music' is NULL. +*/ +extern DECLSPEC Mix_MusicType SDLCALL Mix_GetMusicType(const Mix_Music *music); + +/* 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. +*/ +extern DECLSPEC void SDLCALL Mix_SetPostMix(void (*mix_func) + (void *udata, Uint8 *stream, int len), void *arg); + +/* Add your own music player or additional mixer function. + If 'mix_func' is NULL, the default music player is re-enabled. + */ +extern DECLSPEC void SDLCALL Mix_HookMusic(void (*mix_func) + (void *udata, Uint8 *stream, int len), void *arg); + +/* Add your own callback when the music has finished playing. + This callback is only called if the music finishes naturally. + */ +extern DECLSPEC void SDLCALL Mix_HookMusicFinished(void (*music_finished)(void)); + +/* Get a pointer to the user data for the current music hook */ +extern DECLSPEC void * SDLCALL Mix_GetMusicHookData(void); + +/* + * Add your own callback when a channel has finished playing. NULL + * to disable callback. The callback may be called from the mixer's audio + * callback or it could be called as a result of Mix_HaltChannel(), etc. + * do not call SDL_LockAudio() from this callback; you will either be + * inside the audio callback, or SDL_mixer will explicitly lock the audio + * before calling your callback. + */ +extern DECLSPEC void SDLCALL Mix_ChannelFinished(void (*channel_finished)(int channel)); + + +/* Special Effects API by ryan c. gordon. (icculus@icculus.org) */ + +#define MIX_CHANNEL_POST -2 + +/* This is the format of a special effect callback: + * + * myeffect(int chan, void *stream, int len, void *udata); + * + * (chan) is the channel number that your effect is affecting. (stream) is + * the buffer of data to work upon. (len) is the size of (stream), and + * (udata) is a user-defined bit of data, which you pass as the last arg of + * Mix_RegisterEffect(), and is passed back unmolested to your callback. + * Your effect changes the contents of (stream) based on whatever parameters + * are significant, or just leaves it be, if you prefer. You can do whatever + * you like to the buffer, though, and it will continue in its changed state + * down the mixing pipeline, through any other effect functions, then finally + * to be mixed with the rest of the channels and music for the final output + * stream. + * + * DO NOT EVER call SDL_LockAudio() from your callback function! + */ +typedef void (*Mix_EffectFunc_t)(int chan, void *stream, int len, void *udata); + +/* + * This is a callback that signifies that a channel has finished all its + * loops and has completed playback. This gets called if the buffer + * plays out normally, or if you call Mix_HaltChannel(), implicitly stop + * a channel via Mix_AllocateChannels(), or unregister a callback while + * it's still playing. + * + * DO NOT EVER call SDL_LockAudio() from your callback function! + */ +typedef void (*Mix_EffectDone_t)(int chan, void *udata); + + +/* Register a special effect function. At mixing time, the channel data is + * copied into a buffer and passed through each registered effect function. + * After it passes through all the functions, it is mixed into the final + * output stream. The copy to buffer is performed once, then each effect + * function performs on the output of the previous effect. Understand that + * this extra copy to a buffer is not performed if there are no effects + * registered for a given chunk, which saves CPU cycles, and any given + * effect will be extra cycles, too, so it is crucial that your code run + * fast. Also note that the data that your function is given is in the + * format of the sound device, and not the format you gave to Mix_OpenAudio(), + * although they may in reality be the same. This is an unfortunate but + * necessary speed concern. Use Mix_QuerySpec() to determine if you can + * handle the data before you register your effect, and take appropriate + * actions. + * You may also specify a callback (Mix_EffectDone_t) that is called when + * the channel finishes playing. This gives you a more fine-grained control + * than Mix_ChannelFinished(), in case you need to free effect-specific + * resources, etc. If you don't need this, you can specify NULL. + * You may set the callbacks before or after calling Mix_PlayChannel(). + * Things like Mix_SetPanning() are just internal special effect functions, + * so if you are using that, you've already incurred the overhead of a copy + * to a separate buffer, and that these effects will be in the queue with + * any functions you've registered. The list of registered effects for a + * channel is reset when a chunk finishes playing, so you need to explicitly + * set them with each call to Mix_PlayChannel*(). + * You may also register a special effect function that is to be run after + * final mixing occurs. The rules for these callbacks are identical to those + * in Mix_RegisterEffect, but they are run after all the channels and the + * music have been mixed into a single stream, whereas channel-specific + * effects run on a given channel before any other mixing occurs. These + * global effect callbacks are call "posteffects". Posteffects only have + * their Mix_EffectDone_t function called when they are unregistered (since + * the main output stream is never "done" in the same sense as a channel). + * You must unregister them manually when you've had enough. Your callback + * will be told that the channel being mixed is (MIX_CHANNEL_POST) if the + * processing is considered a posteffect. + * + * After all these effects have finished processing, the callback registered + * through Mix_SetPostMix() runs, and then the stream goes to the audio + * device. + * + * DO NOT EVER call SDL_LockAudio() from your callback function! + * + * returns zero if error (no such channel), nonzero if added. + * Error messages can be retrieved from Mix_GetError(). + */ +extern DECLSPEC int SDLCALL Mix_RegisterEffect(int chan, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg); + + +/* You may not need to call this explicitly, unless you need to stop an + * effect from processing in the middle of a chunk's playback. + * Posteffects are never implicitly unregistered as they are for channels, + * but they may be explicitly unregistered through this function by + * specifying MIX_CHANNEL_POST for a channel. + * returns zero if error (no such channel or effect), nonzero if removed. + * Error messages can be retrieved from Mix_GetError(). + */ +extern DECLSPEC int SDLCALL Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f); + + +/* You may not need to call this explicitly, unless you need to stop all + * effects from processing in the middle of a chunk's playback. Note that + * this will also shut off some internal effect processing, since + * Mix_SetPanning() and others may use this API under the hood. This is + * called internally when a channel completes playback. + * Posteffects are never implicitly unregistered as they are for channels, + * but they may be explicitly unregistered through this function by + * specifying MIX_CHANNEL_POST for a channel. + * returns zero if error (no such channel), nonzero if all effects removed. + * Error messages can be retrieved from Mix_GetError(). + */ +extern DECLSPEC int SDLCALL Mix_UnregisterAllEffects(int channel); + + +#define MIX_EFFECTSMAXSPEED "MIX_EFFECTSMAXSPEED" + +/* + * These are the internally-defined mixing effects. They use the same API that + * effects defined in the application use, but are provided here as a + * convenience. Some effects can reduce their quality or use more memory in + * the name of speed; to enable this, make sure the environment variable + * MIX_EFFECTSMAXSPEED (see above) is defined before you call + * Mix_OpenAudio(). + */ + + +/* Set the panning of a channel. The left and right channels are specified + * as integers between 0 and 255, quietest to loudest, respectively. + * + * Technically, this is just individual volume control for a sample with + * two (stereo) channels, so it can be used for more than just panning. + * If you want real panning, call it like this: + * + * Mix_SetPanning(channel, left, 255 - left); + * + * ...which isn't so hard. + * + * Setting (channel) to MIX_CHANNEL_POST registers this as a posteffect, and + * the panning will be done to the final mixed stream before passing it on + * to the audio device. + * + * This uses the Mix_RegisterEffect() API internally, and returns without + * registering the effect function if the audio device is not configured + * for stereo output. Setting both (left) and (right) to 255 causes this + * effect to be unregistered, since that is the data's normal state. + * + * returns zero if error (no such channel or Mix_RegisterEffect() fails), + * nonzero if panning effect enabled. Note that an audio device in mono + * mode is a no-op, but this call will return successful in that case. + * Error messages can be retrieved from Mix_GetError(). + */ +extern DECLSPEC int SDLCALL Mix_SetPanning(int channel, Uint8 left, Uint8 right); + + +/* Set the position of a channel. (angle) is an integer from 0 to 360, that + * specifies the location of the sound in relation to the listener. (angle) + * will be reduced as neccesary (540 becomes 180 degrees, -100 becomes 260). + * Angle 0 is due north, and rotates clockwise as the value increases. + * For efficiency, the precision of this effect may be limited (angles 1 + * through 7 might all produce the same effect, 8 through 15 are equal, etc). + * (distance) is an integer between 0 and 255 that specifies the space + * between the sound and the listener. The larger the number, the further + * away the sound is. Using 255 does not guarantee that the channel will be + * culled from the mixing process or be completely silent. For efficiency, + * the precision of this effect may be limited (distance 0 through 5 might + * all produce the same effect, 6 through 10 are equal, etc). Setting (angle) + * and (distance) to 0 unregisters this effect, since the data would be + * unchanged. + * + * If you need more precise positional audio, consider using OpenAL for + * spatialized effects instead of SDL_mixer. This is only meant to be a + * basic effect for simple "3D" games. + * + * If the audio device is configured for mono output, then you won't get + * any effectiveness from the angle; however, distance attenuation on the + * channel will still occur. While this effect will function with stereo + * voices, it makes more sense to use voices with only one channel of sound, + * so when they are mixed through this effect, the positioning will sound + * correct. You can convert them to mono through SDL before giving them to + * the mixer in the first place if you like. + * + * Setting (channel) to MIX_CHANNEL_POST registers this as a posteffect, and + * the positioning will be done to the final mixed stream before passing it + * on to the audio device. + * + * This is a convenience wrapper over Mix_SetDistance() and Mix_SetPanning(). + * + * returns zero if error (no such channel or Mix_RegisterEffect() fails), + * nonzero if position effect is enabled. + * Error messages can be retrieved from Mix_GetError(). + */ +extern DECLSPEC int SDLCALL Mix_SetPosition(int channel, Sint16 angle, Uint8 distance); + + +/* Set the "distance" of a channel. (distance) is an integer from 0 to 255 + * that specifies the location of the sound in relation to the listener. + * Distance 0 is overlapping the listener, and 255 is as far away as possible + * A distance of 255 does not guarantee silence; in such a case, you might + * want to try changing the chunk's volume, or just cull the sample from the + * mixing process with Mix_HaltChannel(). + * For efficiency, the precision of this effect may be limited (distances 1 + * through 7 might all produce the same effect, 8 through 15 are equal, etc). + * (distance) is an integer between 0 and 255 that specifies the space + * between the sound and the listener. The larger the number, the further + * away the sound is. + * Setting (distance) to 0 unregisters this effect, since the data would be + * unchanged. + * If you need more precise positional audio, consider using OpenAL for + * spatialized effects instead of SDL_mixer. This is only meant to be a + * basic effect for simple "3D" games. + * + * Setting (channel) to MIX_CHANNEL_POST registers this as a posteffect, and + * the distance attenuation will be done to the final mixed stream before + * passing it on to the audio device. + * + * This uses the Mix_RegisterEffect() API internally. + * + * returns zero if error (no such channel or Mix_RegisterEffect() fails), + * nonzero if position effect is enabled. + * Error messages can be retrieved from Mix_GetError(). + */ +extern DECLSPEC int SDLCALL Mix_SetDistance(int channel, Uint8 distance); + + +/* + * !!! FIXME : Haven't implemented, since the effect goes past the + * end of the sound buffer. Will have to think about this. + * --ryan. + */ +#if 0 +/* Causes an echo effect to be mixed into a sound. (echo) is the amount + * of echo to mix. 0 is no echo, 255 is infinite (and probably not + * what you want). + * + * Setting (channel) to MIX_CHANNEL_POST registers this as a posteffect, and + * the reverbing will be done to the final mixed stream before passing it on + * to the audio device. + * + * This uses the Mix_RegisterEffect() API internally. If you specify an echo + * of zero, the effect is unregistered, as the data is already in that state. + * + * returns zero if error (no such channel or Mix_RegisterEffect() fails), + * nonzero if reversing effect is enabled. + * Error messages can be retrieved from Mix_GetError(). + */ +extern no_parse_DECLSPEC int SDLCALL Mix_SetReverb(int channel, Uint8 echo); +#endif + +/* Causes a channel to reverse its stereo. This is handy if the user has his + * speakers hooked up backwards, or you would like to have a minor bit of + * psychedelia in your sound code. :) Calling this function with (flip) + * set to non-zero reverses the chunks's usual channels. If (flip) is zero, + * the effect is unregistered. + * + * This uses the Mix_RegisterEffect() API internally, and thus is probably + * more CPU intensive than having the user just plug in his speakers + * correctly. Mix_SetReverseStereo() returns without registering the effect + * function if the audio device is not configured for stereo output. + * + * If you specify MIX_CHANNEL_POST for (channel), then this the effect is used + * on the final mixed stream before sending it on to the audio device (a + * posteffect). + * + * returns zero if error (no such channel or Mix_RegisterEffect() fails), + * nonzero if reversing effect is enabled. Note that an audio device in mono + * mode is a no-op, but this call will return successful in that case. + * Error messages can be retrieved from Mix_GetError(). + */ +extern DECLSPEC int SDLCALL Mix_SetReverseStereo(int channel, int flip); + +/* end of effects API. --ryan. */ + + +/* 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. + */ +extern DECLSPEC int SDLCALL Mix_ReserveChannels(int num); + +/* Channel grouping functions */ + +/* Attach a tag to a channel. A tag can be assigned to several mixer + channels, to form groups of channels. + If 'tag' is -1, the tag is removed (actually -1 is the tag used to + represent the group of all the channels). + Returns true if everything was OK. + */ +extern DECLSPEC int SDLCALL Mix_GroupChannel(int which, int tag); +/* Assign several consecutive channels to a group */ +extern DECLSPEC int SDLCALL Mix_GroupChannels(int from, int to, int tag); +/* Finds the first available channel in a group of channels, + returning -1 if none are available. + */ +extern DECLSPEC int SDLCALL Mix_GroupAvailable(int tag); +/* Returns the number of channels in a group. This is also a subtle + way to get the total number of channels when 'tag' is -1 + */ +extern DECLSPEC int SDLCALL Mix_GroupCount(int tag); +/* Finds the "oldest" sample playing in a group of channels */ +extern DECLSPEC int SDLCALL Mix_GroupOldest(int tag); +/* Finds the "most recent" (i.e. last) sample playing in a group of channels */ +extern DECLSPEC int SDLCALL Mix_GroupNewer(int tag); + +/* Play an audio chunk on a specific channel. + If the specified channel is -1, play on the first free channel. + If 'loops' is greater than zero, loop the sound that many times. + If 'loops' is -1, loop inifinitely (~65000 times). + Returns which channel was used to play the sound. +*/ +#define Mix_PlayChannel(channel,chunk,loops) Mix_PlayChannelTimed(channel,chunk,loops,-1) +/* The same as above, but the sound is played at most 'ticks' milliseconds */ +extern DECLSPEC int SDLCALL Mix_PlayChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ticks); +extern DECLSPEC int SDLCALL Mix_PlayMusic(Mix_Music *music, int loops); + +/* Fade in music or a channel over "ms" milliseconds, same semantics as the "Play" functions */ +extern DECLSPEC int SDLCALL Mix_FadeInMusic(Mix_Music *music, int loops, int ms); +extern DECLSPEC int SDLCALL Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position); +#define Mix_FadeInChannel(channel,chunk,loops,ms) Mix_FadeInChannelTimed(channel,chunk,loops,ms,-1) +extern DECLSPEC int SDLCALL Mix_FadeInChannelTimed(int channel, Mix_Chunk *chunk, int loops, int ms, int ticks); + +/* Set the volume in the range of 0-128 of a specific channel or chunk. + If the specified channel is -1, set volume for all channels. + Returns the original volume. + If the specified volume is -1, just return the current volume. +*/ +extern DECLSPEC int SDLCALL Mix_Volume(int channel, int volume); +extern DECLSPEC int SDLCALL Mix_VolumeChunk(Mix_Chunk *chunk, int volume); +extern DECLSPEC int SDLCALL Mix_VolumeMusic(int volume); + +/* Halt playing of a particular channel */ +extern DECLSPEC int SDLCALL Mix_HaltChannel(int channel); +extern DECLSPEC int SDLCALL Mix_HaltGroup(int tag); +extern DECLSPEC int SDLCALL Mix_HaltMusic(void); + +/* Change the expiration delay for a particular channel. + The sample will stop playing after the 'ticks' milliseconds have elapsed, + or remove the expiration if 'ticks' is -1 +*/ +extern DECLSPEC int SDLCALL Mix_ExpireChannel(int channel, int ticks); + +/* Halt a channel, fading it out progressively till it's silent + The ms parameter indicates the number of milliseconds the fading + will take. + */ +extern DECLSPEC int SDLCALL Mix_FadeOutChannel(int which, int ms); +extern DECLSPEC int SDLCALL Mix_FadeOutGroup(int tag, int ms); +extern DECLSPEC int SDLCALL Mix_FadeOutMusic(int ms); + +/* Query the fading status of a channel */ +extern DECLSPEC Mix_Fading SDLCALL Mix_FadingMusic(void); +extern DECLSPEC Mix_Fading SDLCALL Mix_FadingChannel(int which); + +/* Pause/Resume a particular channel */ +extern DECLSPEC void SDLCALL Mix_Pause(int channel); +extern DECLSPEC void SDLCALL Mix_Resume(int channel); +extern DECLSPEC int SDLCALL Mix_Paused(int channel); + +/* Pause/Resume the music stream */ +extern DECLSPEC void SDLCALL Mix_PauseMusic(void); +extern DECLSPEC void SDLCALL Mix_ResumeMusic(void); +extern DECLSPEC void SDLCALL Mix_RewindMusic(void); +extern DECLSPEC int SDLCALL Mix_PausedMusic(void); + +/* Set the current position in the music stream. + This returns 0 if successful, or -1 if it failed or isn't implemented. + This function is only implemented for MOD music formats (set pattern + order number) and for OGG, FLAC, MP3_MAD, and MODPLUG music (set + position in seconds), at the moment. +*/ +extern DECLSPEC int SDLCALL Mix_SetMusicPosition(double position); + +/* Check the status of a specific channel. + If the specified channel is -1, check all channels. +*/ +extern DECLSPEC int SDLCALL Mix_Playing(int channel); +extern DECLSPEC int SDLCALL Mix_PlayingMusic(void); + +/* Stop music and set external music playback command */ +extern DECLSPEC int SDLCALL Mix_SetMusicCMD(const char *command); + +/* Synchro value is set by MikMod from modules while playing */ +extern DECLSPEC int SDLCALL Mix_SetSynchroValue(int value); +extern DECLSPEC int SDLCALL Mix_GetSynchroValue(void); + +/* Set/Get/Iterate SoundFonts paths to use by supported MIDI backends */ +extern DECLSPEC int SDLCALL Mix_SetSoundFonts(const char *paths); +extern DECLSPEC const char* SDLCALL Mix_GetSoundFonts(void); +extern DECLSPEC int SDLCALL Mix_EachSoundFont(int (*function)(const char*, void*), void *data); + +/* Get the Mix_Chunk currently associated with a mixer channel + Returns NULL if it's an invalid channel, or there's no chunk associated. +*/ +extern DECLSPEC Mix_Chunk * SDLCALL Mix_GetChunk(int channel); + +/* Close the mixer, halting all playing audio */ +extern DECLSPEC void SDLCALL Mix_CloseAudio(void); + +/* We'll use SDL for reporting errors */ +#define Mix_SetError SDL_SetError +#define Mix_GetError SDL_GetError + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif +#include "close_code.h" + +#endif /* _SDL_MIXER_H */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/dynamic_ogg.c b/contrib/sdk/sources/SDL_mixer-1.2.12/dynamic_ogg.c new file mode 100644 index 0000000000..260ebbdb68 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/dynamic_ogg.c @@ -0,0 +1,128 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef OGG_MUSIC +#include "dynamic_ogg.h" + +vorbis_loader vorbis = { + 0, NULL +}; + +#ifdef OGG_DYNAMIC +int Mix_InitOgg() +{ + if ( vorbis.loaded == 0 ) { + vorbis.handle = SDL_LoadObject(OGG_DYNAMIC); + if ( vorbis.handle == NULL ) { + return -1; + } + vorbis.ov_clear = + (int (*)(OggVorbis_File *)) + SDL_LoadFunction(vorbis.handle, "ov_clear"); + if ( vorbis.ov_clear == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_info = + (vorbis_info *(*)(OggVorbis_File *,int)) + SDL_LoadFunction(vorbis.handle, "ov_info"); + if ( vorbis.ov_info == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_open_callbacks = + (int (*)(void *, OggVorbis_File *, char *, long, ov_callbacks)) + SDL_LoadFunction(vorbis.handle, "ov_open_callbacks"); + if ( vorbis.ov_open_callbacks == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_pcm_total = + (ogg_int64_t (*)(OggVorbis_File *,int)) + SDL_LoadFunction(vorbis.handle, "ov_pcm_total"); + if ( vorbis.ov_pcm_total == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_read = +#ifdef OGG_USE_TREMOR + (long (*)(OggVorbis_File *,char *,int,int *)) +#else + (long (*)(OggVorbis_File *,char *,int,int,int,int,int *)) +#endif + SDL_LoadFunction(vorbis.handle, "ov_read"); + if ( vorbis.ov_read == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + vorbis.ov_time_seek = +#ifdef OGG_USE_TREMOR + (long (*)(OggVorbis_File *,ogg_int64_t)) +#else + (int (*)(OggVorbis_File *,double)) +#endif + SDL_LoadFunction(vorbis.handle, "ov_time_seek"); + if ( vorbis.ov_time_seek == NULL ) { + SDL_UnloadObject(vorbis.handle); + return -1; + } + } + ++vorbis.loaded; + + return 0; +} +void Mix_QuitOgg() +{ + if ( vorbis.loaded == 0 ) { + return; + } + if ( vorbis.loaded == 1 ) { + SDL_UnloadObject(vorbis.handle); + } + --vorbis.loaded; +} +#else +int Mix_InitOgg() +{ + if ( vorbis.loaded == 0 ) { + vorbis.ov_clear = ov_clear; + vorbis.ov_info = ov_info; + vorbis.ov_open_callbacks = ov_open_callbacks; + vorbis.ov_pcm_total = ov_pcm_total; + vorbis.ov_read = ov_read; + vorbis.ov_time_seek = ov_time_seek; + } + ++vorbis.loaded; + + return 0; +} +void Mix_QuitOgg() +{ + if ( vorbis.loaded == 0 ) { + return; + } + if ( vorbis.loaded == 1 ) { + } + --vorbis.loaded; +} +#endif /* OGG_DYNAMIC */ + +#endif /* OGG_MUSIC */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/dynamic_ogg.h b/contrib/sdk/sources/SDL_mixer-1.2.12/dynamic_ogg.h new file mode 100644 index 0000000000..822458d49e --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/dynamic_ogg.h @@ -0,0 +1,53 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifdef OGG_MUSIC +#ifdef OGG_USE_TREMOR +#include +#else +#include +#endif + +typedef struct { + int loaded; + void *handle; + int (*ov_clear)(OggVorbis_File *vf); + vorbis_info *(*ov_info)(OggVorbis_File *vf,int link); + int (*ov_open_callbacks)(void *datasource, OggVorbis_File *vf, char *initial, long ibytes, ov_callbacks callbacks); + ogg_int64_t (*ov_pcm_total)(OggVorbis_File *vf,int i); +#ifdef OGG_USE_TREMOR + long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int *bitstream); +#else + long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream); +#endif +#ifdef OGG_USE_TREMOR + int (*ov_time_seek)(OggVorbis_File *vf,ogg_int64_t pos); +#else + int (*ov_time_seek)(OggVorbis_File *vf,double pos); +#endif +} vorbis_loader; + +extern vorbis_loader vorbis; + +#endif /* OGG_MUSIC */ + +extern int Mix_InitOgg(); +extern void Mix_QuitOgg(); diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/effect_position.c b/contrib/sdk/sources/SDL_mixer-1.2.12/effect_position.c new file mode 100644 index 0000000000..f7208776ce --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/effect_position.c @@ -0,0 +1,1619 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This file by Ryan C. Gordon (icculus@icculus.org) + + These are some internally supported special effects that use SDL_mixer's + effect callback API. They are meant for speed over quality. :) +*/ + +/* $Id$ */ + +#include +#include +#include + +#include "SDL.h" +#include "SDL_mixer.h" +#include "SDL_endian.h" + +#define __MIX_INTERNAL_EFFECT__ +#include "effects_internal.h" + +/* profile code: + #include + #include + struct timeval tv1; + struct timeval tv2; + + gettimeofday(&tv1, NULL); + + ... do your thing here ... + + gettimeofday(&tv2, NULL); + printf("%ld\n", tv2.tv_usec - tv1.tv_usec); +*/ + + +/* + * Positional effects...panning, distance attenuation, etc. + */ + +typedef struct _Eff_positionargs +{ + volatile float left_f; + volatile float right_f; + volatile Uint8 left_u8; + volatile Uint8 right_u8; + volatile float left_rear_f; + volatile float right_rear_f; + volatile float center_f; + volatile float lfe_f; + volatile Uint8 left_rear_u8; + volatile Uint8 right_rear_u8; + volatile Uint8 center_u8; + volatile Uint8 lfe_u8; + volatile float distance_f; + volatile Uint8 distance_u8; + volatile Sint16 room_angle; + volatile int in_use; + volatile int channels; +} position_args; + +static position_args **pos_args_array = NULL; +static position_args *pos_args_global = NULL; +static int position_channels = 0; + +void _Eff_PositionDeinit(void) +{ + int i; + for (i = 0; i < position_channels; i++) { + SDL_free(pos_args_array[i]); + } + + position_channels = 0; + + SDL_free(pos_args_global); + pos_args_global = NULL; + SDL_free(pos_args_array); + pos_args_array = NULL; +} + + +/* This just frees up the callback-specific data. */ +static void _Eff_PositionDone(int channel, void *udata) +{ + if (channel < 0) { + if (pos_args_global != NULL) { + SDL_free(pos_args_global); + pos_args_global = NULL; + } + } + + else if (pos_args_array[channel] != NULL) { + SDL_free(pos_args_array[channel]); + pos_args_array[channel] = NULL; + } +} + + +static void _Eff_position_u8(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint8 *ptr = (Uint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Uint16) != 0) { + *ptr = (Uint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + if (args->room_angle == 180) + for (i = 0; i < len; i += sizeof (Uint8) * 2) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + } + else for (i = 0; i < len; i += sizeof (Uint8) * 2) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + } +} +static void _Eff_position_u8_c4(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint8 *ptr = (Uint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Uint16) != 0) { + *ptr = (Uint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + if (args->room_angle == 0) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 90) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 180) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 270) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + } +} + + +static void _Eff_position_u8_c6(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint8 *ptr = (Uint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Uint16) != 0) { + *ptr = (Uint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + if (args->room_angle == 0) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->center_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->lfe_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 90) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f/2) + 128) + + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f/2) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->lfe_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 180) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f/2) + 128) + + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f/2) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->lfe_f) * args->distance_f) + 128); + ptr++; + } + else if (args->room_angle == 270) + for (i = 0; i < len; i += sizeof (Uint8) * 6) { + /* must adjust the sample so that 0 is the center */ + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_rear_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->right_f) * args->distance_f) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_f) * args->distance_f/2) + 128) + + (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->left_rear_f) * args->distance_f/2) + 128); + ptr++; + *ptr = (Uint8) ((Sint8) ((((float) (Sint8) (*ptr - 128)) + * args->lfe_f) * args->distance_f) + 128); + ptr++; + } +} + + +/* + * This one runs about 10.1 times faster than the non-table version, with + * no loss in quality. It does, however, require 64k of memory for the + * lookup table. Also, this will only update position information once per + * call; the non-table version always checks the arguments for each sample, + * in case the user has called Mix_SetPanning() or whatnot again while this + * callback is running. + */ +static void _Eff_position_table_u8(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint8 *ptr = (Uint8 *) stream; + Uint32 *p; + int i; + Uint8 *l = ((Uint8 *) _Eff_volume_table) + (256 * args->left_u8); + Uint8 *r = ((Uint8 *) _Eff_volume_table) + (256 * args->right_u8); + Uint8 *d = ((Uint8 *) _Eff_volume_table) + (256 * args->distance_u8); + + if (args->room_angle == 180) { + Uint8 *temp = l; + l = r; + r = temp; + } + /* + * if there's only a mono channnel, then l[] and r[] are always + * volume 255, and are therefore throwaways. Still, we have to + * be sure not to overrun the audio buffer... + */ + while (len % sizeof (Uint32) != 0) { + *ptr = d[l[*ptr]]; + ptr++; + if (args->channels > 1) { + *ptr = d[r[*ptr]]; + ptr++; + } + len -= args->channels; + } + + p = (Uint32 *) ptr; + + for (i = 0; i < len; i += sizeof (Uint32)) { +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + *p = (d[l[(*p & 0xFF000000) >> 24]] << 24) | + (d[r[(*p & 0x00FF0000) >> 16]] << 16) | + (d[l[(*p & 0x0000FF00) >> 8]] << 8) | + (d[r[(*p & 0x000000FF) ]] ) ; +#else + *p = (d[r[(*p & 0xFF000000) >> 24]] << 24) | + (d[l[(*p & 0x00FF0000) >> 16]] << 16) | + (d[r[(*p & 0x0000FF00) >> 8]] << 8) | + (d[l[(*p & 0x000000FF) ]] ) ; +#endif + ++p; + } +} + + +static void _Eff_position_s8(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Sint8 *ptr = (Sint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Sint16) != 0) { + *ptr = (Sint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + if (args->room_angle == 180) + for (i = 0; i < len; i += sizeof (Sint8) * 2) { + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); + ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); + ptr++; + } + else + for (i = 0; i < len; i += sizeof (Sint8) * 2) { + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); + ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); + ptr++; + } +} +static void _Eff_position_s8_c4(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Sint8 *ptr = (Sint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Sint16) != 0) { + *ptr = (Sint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + for (i = 0; i < len; i += sizeof (Sint8) * 4) { + switch (args->room_angle) { + case 0: + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + break; + case 90: + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + break; + case 180: + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + break; + case 270: + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + break; + } + } +} +static void _Eff_position_s8_c6(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Sint8 *ptr = (Sint8 *) stream; + int i; + + /* + * if there's only a mono channnel (the only way we wouldn't have + * a len divisible by 2 here), then left_f and right_f are always + * 1.0, and are therefore throwaways. + */ + if (len % sizeof (Sint16) != 0) { + *ptr = (Sint8) (((float) *ptr) * args->distance_f); + ptr++; + len--; + } + + for (i = 0; i < len; i += sizeof (Sint8) * 6) { + switch (args->room_angle) { + case 0: + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->center_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; + break; + case 90: + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f / 2) + + (Sint8)((((float) *ptr) * args->right_f) * args->distance_f / 2); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; + break; + case 180: + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f / 2) + + (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f / 2); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; + break; + case 270: + *ptr = (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_rear_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->right_f) * args->distance_f); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->left_f) * args->distance_f / 2) + + (Sint8)((((float) *ptr) * args->left_rear_f) * args->distance_f / 2); ptr++; + *ptr = (Sint8)((((float) *ptr) * args->lfe_f) * args->distance_f); ptr++; + break; + } + } +} + + +/* + * This one runs about 10.1 times faster than the non-table version, with + * no loss in quality. It does, however, require 64k of memory for the + * lookup table. Also, this will only update position information once per + * call; the non-table version always checks the arguments for each sample, + * in case the user has called Mix_SetPanning() or whatnot again while this + * callback is running. + */ +static void _Eff_position_table_s8(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Sint8 *ptr = (Sint8 *) stream; + Uint32 *p; + int i; + Sint8 *l = ((Sint8 *) _Eff_volume_table) + (256 * args->left_u8); + Sint8 *r = ((Sint8 *) _Eff_volume_table) + (256 * args->right_u8); + Sint8 *d = ((Sint8 *) _Eff_volume_table) + (256 * args->distance_u8); + + if (args->room_angle == 180) { + Sint8 *temp = l; + l = r; + r = temp; + } + + + while (len % sizeof (Uint32) != 0) { + *ptr = d[l[*ptr]]; + ptr++; + if (args->channels > 1) { + *ptr = d[r[*ptr]]; + ptr++; + } + len -= args->channels; + } + + p = (Uint32 *) ptr; + + for (i = 0; i < len; i += sizeof (Uint32)) { +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + *p = (d[l[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) | + (d[r[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) | + (d[l[((Sint16)(Sint8)((*p & 0x0000FF00) >> 8))+128]] << 8) | + (d[r[((Sint16)(Sint8)((*p & 0x000000FF) ))+128]] ) ; +#else + *p = (d[r[((Sint16)(Sint8)((*p & 0xFF000000) >> 24))+128]] << 24) | + (d[l[((Sint16)(Sint8)((*p & 0x00FF0000) >> 16))+128]] << 16) | + (d[r[((Sint16)(Sint8)((*p & 0x0000FF00) >> 8))+128]] << 8) | + (d[l[((Sint16)(Sint8)((*p & 0x000000FF) ))+128]] ) ; +#endif + ++p; + } + + +} + + +/* !!! FIXME : Optimize the code for 16-bit samples? */ + +static void _Eff_position_u16lsb(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Uint16) * 2) { + Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + + if (args->room_angle == 180) { + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + } + else { + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + } + } +} +static void _Eff_position_u16lsb_c4(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Uint16) * 4) { + Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); + Sint16 samplr = (Sint16) (SDL_SwapLE16(*(ptr+2)) - 32768); + Sint16 samprr = (Sint16) (SDL_SwapLE16(*(ptr+3)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) + * args->distance_f) + 32768); + Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) + * args->distance_f) + 32768); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + break; + case 90: + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + break; + case 180: + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + break; + case 270: + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + break; + } + } +} +static void _Eff_position_u16lsb_c6(int chan, void *stream, int len, void *udata) +{ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Uint16) * 6) { + Sint16 sampl = (Sint16) (SDL_SwapLE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapLE16(*(ptr+1)) - 32768); + Sint16 samplr = (Sint16) (SDL_SwapLE16(*(ptr+2)) - 32768); + Sint16 samprr = (Sint16) (SDL_SwapLE16(*(ptr+3)) - 32768); + Sint16 sampce = (Sint16) (SDL_SwapLE16(*(ptr+4)) - 32768); + Sint16 sampwf = (Sint16) (SDL_SwapLE16(*(ptr+5)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) + * args->distance_f) + 32768); + Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) + * args->distance_f) + 32768); + Uint16 swapce = (Uint16) ((Sint16) (((float) sampce * args->center_f) + * args->distance_f) + 32768); + Uint16 swapwf = (Uint16) ((Sint16) (((float) sampwf * args->lfe_f) + * args->distance_f) + 32768); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapce); + *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); + break; + case 90: + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr)/2 + (Uint16) SDL_SwapLE16(swaprr)/2; + *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); + break; + case 180: + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr)/2 + (Uint16) SDL_SwapLE16(swaplr)/2; + *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); + break; + case 270: + *(ptr++) = (Uint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl); + *(ptr++) = (Uint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapr); + *(ptr++) = (Uint16) SDL_SwapLE16(swapl)/2 + (Uint16) SDL_SwapLE16(swaplr)/2; + *(ptr++) = (Uint16) SDL_SwapLE16(swapwf); + break; + } + } +} + +static void _Eff_position_s16lsb(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 2 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + +#if 0 + if (len % (sizeof(Sint16) * 2)) { + fprintf(stderr,"Not an even number of frames! len=%d\n", len); + return; + } +#endif + + for (i = 0; i < len; i += sizeof (Sint16) * 2) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * + args->right_f) * args->distance_f); + if (args->room_angle == 180) { + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + } + else { + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + } + } +} +static void _Eff_position_s16lsb_c4(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 4 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 4) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * + args->right_f) * args->distance_f); + Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * + args->left_rear_f) * args->distance_f); + Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+2))) * + args->right_rear_f) * args->distance_f); + switch (args->room_angle) { + case 0: + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + break; + case 90: + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + break; + case 180: + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + break; + case 270: + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + break; + } + } +} + +static void _Eff_position_s16lsb_c6(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 6 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 6) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+1))) * + args->right_f) * args->distance_f); + Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+2))) * + args->left_rear_f) * args->distance_f); + Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+3))) * + args->right_rear_f) * args->distance_f); + Sint16 swapce = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+4))) * + args->center_f) * args->distance_f); + Sint16 swapwf = (Sint16) ((((float) (Sint16) SDL_SwapLE16(*(ptr+5))) * + args->lfe_f) * args->distance_f); + switch (args->room_angle) { + case 0: + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapce); + *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); + break; + case 90: + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr)/2 + (Sint16) SDL_SwapLE16(swaprr)/2; + *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); + break; + case 180: + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr)/2 + (Sint16) SDL_SwapLE16(swaplr)/2; + *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); + break; + case 270: + *(ptr++) = (Sint16) SDL_SwapLE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl); + *(ptr++) = (Sint16) SDL_SwapLE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapr); + *(ptr++) = (Sint16) SDL_SwapLE16(swapl)/2 + (Sint16) SDL_SwapLE16(swaplr)/2; + *(ptr++) = (Sint16) SDL_SwapLE16(swapwf); + break; + } + } +} + +static void _Eff_position_u16msb(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 2 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 2) { + Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + + if (args->room_angle == 180) { + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + } + else { + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + } + } +} +static void _Eff_position_u16msb_c4(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 4 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 4) { + Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); + Sint16 samplr = (Sint16) (SDL_SwapBE16(*(ptr+2)) - 32768); + Sint16 samprr = (Sint16) (SDL_SwapBE16(*(ptr+3)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) + * args->distance_f) + 32768); + Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) + * args->distance_f) + 32768); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + break; + case 90: + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + break; + case 180: + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + break; + case 270: + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + break; + } + } +} +static void _Eff_position_u16msb_c6(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 6 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Uint16 *ptr = (Uint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 6) { + Sint16 sampl = (Sint16) (SDL_SwapBE16(*(ptr+0)) - 32768); + Sint16 sampr = (Sint16) (SDL_SwapBE16(*(ptr+1)) - 32768); + Sint16 samplr = (Sint16) (SDL_SwapBE16(*(ptr+2)) - 32768); + Sint16 samprr = (Sint16) (SDL_SwapBE16(*(ptr+3)) - 32768); + Sint16 sampce = (Sint16) (SDL_SwapBE16(*(ptr+4)) - 32768); + Sint16 sampwf = (Sint16) (SDL_SwapBE16(*(ptr+5)) - 32768); + + Uint16 swapl = (Uint16) ((Sint16) (((float) sampl * args->left_f) + * args->distance_f) + 32768); + Uint16 swapr = (Uint16) ((Sint16) (((float) sampr * args->right_f) + * args->distance_f) + 32768); + Uint16 swaplr = (Uint16) ((Sint16) (((float) samplr * args->left_rear_f) + * args->distance_f) + 32768); + Uint16 swaprr = (Uint16) ((Sint16) (((float) samprr * args->right_rear_f) + * args->distance_f) + 32768); + Uint16 swapce = (Uint16) ((Sint16) (((float) sampce * args->center_f) + * args->distance_f) + 32768); + Uint16 swapwf = (Uint16) ((Sint16) (((float) sampwf * args->lfe_f) + * args->distance_f) + 32768); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapce); + *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); + break; + case 90: + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr)/2 + (Uint16) SDL_SwapBE16(swaprr)/2; + *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); + break; + case 180: + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr)/2 + (Uint16) SDL_SwapBE16(swaplr)/2; + *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); + break; + case 270: + *(ptr++) = (Uint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl); + *(ptr++) = (Uint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapr); + *(ptr++) = (Uint16) SDL_SwapBE16(swapl)/2 + (Uint16) SDL_SwapBE16(swaplr)/2; + *(ptr++) = (Uint16) SDL_SwapBE16(swapwf); + break; + } + } +} + +static void _Eff_position_s16msb(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 2 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 2) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * + args->right_f) * args->distance_f); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + } +} +static void _Eff_position_s16msb_c4(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 4 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 4) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * + args->right_f) * args->distance_f); + Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+2))) * + args->left_rear_f) * args->distance_f); + Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+3))) * + args->right_rear_f) * args->distance_f); + switch (args->room_angle) { + case 0: + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + break; + case 90: + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + break; + case 180: + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + break; + case 270: + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + break; + } + } +} +static void _Eff_position_s16msb_c6(int chan, void *stream, int len, void *udata) +{ + /* 16 signed bits (lsb) * 6 channels. */ + volatile position_args *args = (volatile position_args *) udata; + Sint16 *ptr = (Sint16 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Sint16) * 6) { + Sint16 swapl = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+0))) * + args->left_f) * args->distance_f); + Sint16 swapr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+1))) * + args->right_f) * args->distance_f); + Sint16 swaplr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+2))) * + args->left_rear_f) * args->distance_f); + Sint16 swaprr = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+3))) * + args->right_rear_f) * args->distance_f); + Sint16 swapce = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+4))) * + args->center_f) * args->distance_f); + Sint16 swapwf = (Sint16) ((((float) (Sint16) SDL_SwapBE16(*(ptr+5))) * + args->lfe_f) * args->distance_f); + + switch (args->room_angle) { + case 0: + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapce); + *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); + break; + case 90: + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr)/2 + (Sint16) SDL_SwapBE16(swaprr)/2; + *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); + break; + case 180: + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr)/2 + (Sint16) SDL_SwapBE16(swaplr)/2; + *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); + break; + case 270: + *(ptr++) = (Sint16) SDL_SwapBE16(swaplr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl); + *(ptr++) = (Sint16) SDL_SwapBE16(swaprr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapr); + *(ptr++) = (Sint16) SDL_SwapBE16(swapl)/2 + (Sint16) SDL_SwapBE16(swaplr)/2; + *(ptr++) = (Sint16) SDL_SwapBE16(swapwf); + break; + } + } +} + +static void init_position_args(position_args *args) +{ + memset(args, '\0', sizeof (position_args)); + args->in_use = 0; + args->room_angle = 0; + args->left_u8 = args->right_u8 = args->distance_u8 = 255; + args->left_f = args->right_f = args->distance_f = 1.0f; + args->left_rear_u8 = args->right_rear_u8 = args->center_u8 = args->lfe_u8 = 255; + args->left_rear_f = args->right_rear_f = args->center_f = args->lfe_f = 1.0f; + Mix_QuerySpec(NULL, NULL, (int *) &args->channels); +} + + +static position_args *get_position_arg(int channel) +{ + void *rc; + int i; + + if (channel < 0) { + if (pos_args_global == NULL) { + pos_args_global = SDL_malloc(sizeof (position_args)); + if (pos_args_global == NULL) { + Mix_SetError("Out of memory"); + return(NULL); + } + init_position_args(pos_args_global); + } + + return(pos_args_global); + } + + if (channel >= position_channels) { + rc = SDL_realloc(pos_args_array, (channel + 1) * sizeof (position_args *)); + if (rc == NULL) { + Mix_SetError("Out of memory"); + return(NULL); + } + pos_args_array = (position_args **) rc; + for (i = position_channels; i <= channel; i++) { + pos_args_array[i] = NULL; + } + position_channels = channel + 1; + } + + if (pos_args_array[channel] == NULL) { + pos_args_array[channel] = (position_args *)SDL_malloc(sizeof(position_args)); + if (pos_args_array[channel] == NULL) { + Mix_SetError("Out of memory"); + return(NULL); + } + init_position_args(pos_args_array[channel]); + } + + return(pos_args_array[channel]); +} + + +static Mix_EffectFunc_t get_position_effect_func(Uint16 format, int channels) +{ + Mix_EffectFunc_t f = NULL; + + switch (format) { + case AUDIO_U8: + switch (channels) { + case 1: + case 2: + f = (_Eff_build_volume_table_u8()) ? _Eff_position_table_u8 : + _Eff_position_u8; + break; + case 4: + f = _Eff_position_u8_c4; + break; + case 6: + f = _Eff_position_u8_c6; + break; + } + break; + + case AUDIO_S8: + switch (channels) { + case 1: + case 2: + f = (_Eff_build_volume_table_s8()) ? _Eff_position_table_s8 : + _Eff_position_s8; + break; + case 4: + f = _Eff_position_s8_c4; + break; + case 6: + f = _Eff_position_s8_c6; + break; + } + break; + + case AUDIO_U16LSB: + switch (channels) { + case 1: + case 2: + f = _Eff_position_u16lsb; + break; + case 4: + f = _Eff_position_u16lsb_c4; + break; + case 6: + f = _Eff_position_u16lsb_c6; + break; + } + break; + + case AUDIO_S16LSB: + switch (channels) { + case 1: + case 2: + f = _Eff_position_s16lsb; + break; + case 4: + f = _Eff_position_s16lsb_c4; + break; + case 6: + f = _Eff_position_s16lsb_c6; + break; + } + break; + + case AUDIO_U16MSB: + switch (channels) { + case 1: + case 2: + f = _Eff_position_u16msb; + break; + case 4: + f = _Eff_position_u16msb_c4; + break; + case 6: + f = _Eff_position_u16msb_c6; + break; + } + break; + + case AUDIO_S16MSB: + switch (channels) { + case 1: + case 2: + f = _Eff_position_s16msb; + break; + case 4: + f = _Eff_position_s16msb_c4; + break; + case 6: + f = _Eff_position_s16msb_c6; + break; + } + break; + + default: + Mix_SetError("Unsupported audio format"); + } + + return(f); +} + +static Uint8 speaker_amplitude[6]; + +static void set_amplitudes(int channels, int angle, int room_angle) +{ + int left = 255, right = 255; + int left_rear = 255, right_rear = 255, center = 255; + + angle = SDL_abs(angle) % 360; /* make angle between 0 and 359. */ + + if (channels == 2) + { + /* + * We only attenuate by position if the angle falls on the far side + * of center; That is, an angle that's due north would not attenuate + * either channel. Due west attenuates the right channel to 0.0, and + * due east attenuates the left channel to 0.0. Slightly east of + * center attenuates the left channel a little, and the right channel + * not at all. I think of this as occlusion by one's own head. :) + * + * ...so, we split our angle circle into four quadrants... + */ + if (angle < 90) { + left = 255 - ((int) (255.0f * (((float) angle) / 89.0f))); + } else if (angle < 180) { + left = (int) (255.0f * (((float) (angle - 90)) / 89.0f)); + } else if (angle < 270) { + right = 255 - ((int) (255.0f * (((float) (angle - 180)) / 89.0f))); + } else { + right = (int) (255.0f * (((float) (angle - 270)) / 89.0f)); + } + } + + if (channels == 4 || channels == 6) + { + /* + * An angle that's due north does not attenuate the center channel. + * An angle in the first quadrant, 0-90, does not attenuate the RF. + * + * ...so, we split our angle circle into 8 ... + * + * CE + * 0 + * LF | RF + * | + * 270<-------|----------->90 + * | + * LR | RR + * 180 + * + */ + if (angle < 45) { + left = ((int) (255.0f * (((float) (180 - angle)) / 179.0f))); + left_rear = 255 - ((int) (255.0f * (((float) (angle + 45)) / 89.0f))); + right_rear = 255 - ((int) (255.0f * (((float) (90 - angle)) / 179.0f))); + } else if (angle < 90) { + center = ((int) (255.0f * (((float) (225 - angle)) / 179.0f))); + left = ((int) (255.0f * (((float) (180 - angle)) / 179.0f))); + left_rear = 255 - ((int) (255.0f * (((float) (135 - angle)) / 89.0f))); + right_rear = ((int) (255.0f * (((float) (90 + angle)) / 179.0f))); + } else if (angle < 135) { + center = ((int) (255.0f * (((float) (225 - angle)) / 179.0f))); + left = 255 - ((int) (255.0f * (((float) (angle - 45)) / 89.0f))); + right = ((int) (255.0f * (((float) (270 - angle)) / 179.0f))); + left_rear = ((int) (255.0f * (((float) (angle)) / 179.0f))); + } else if (angle < 180) { + center = 255 - ((int) (255.0f * (((float) (angle - 90)) / 89.0f))); + left = 255 - ((int) (255.0f * (((float) (225 - angle)) / 89.0f))); + right = ((int) (255.0f * (((float) (270 - angle)) / 179.0f))); + left_rear = ((int) (255.0f * (((float) (angle)) / 179.0f))); + } else if (angle < 225) { + center = 255 - ((int) (255.0f * (((float) (270 - angle)) / 89.0f))); + left = ((int) (255.0f * (((float) (angle - 90)) / 179.0f))); + right = 255 - ((int) (255.0f * (((float) (angle - 135)) / 89.0f))); + right_rear = ((int) (255.0f * (((float) (360 - angle)) / 179.0f))); + } else if (angle < 270) { + center = ((int) (255.0f * (((float) (angle - 135)) / 179.0f))); + left = ((int) (255.0f * (((float) (angle - 90)) / 179.0f))); + right = 255 - ((int) (255.0f * (((float) (315 - angle)) / 89.0f))); + right_rear = ((int) (255.0f * (((float) (360 - angle)) / 179.0f))); + } else if (angle < 315) { + center = ((int) (255.0f * (((float) (angle - 135)) / 179.0f))); + right = ((int) (255.0f * (((float) (angle - 180)) / 179.0f))); + left_rear = ((int) (255.0f * (((float) (450 - angle)) / 179.0f))); + right_rear = 255 - ((int) (255.0f * (((float) (angle - 225)) / 89.0f))); + } else { + right = ((int) (255.0f * (((float) (angle - 180)) / 179.0f))); + left_rear = ((int) (255.0f * (((float) (450 - angle)) / 179.0f))); + right_rear = 255 - ((int) (255.0f * (((float) (405 - angle)) / 89.0f))); + } + } + + if (left < 0) left = 0; if (left > 255) left = 255; + if (right < 0) right = 0; if (right > 255) right = 255; + if (left_rear < 0) left_rear = 0; if (left_rear > 255) left_rear = 255; + if (right_rear < 0) right_rear = 0; if (right_rear > 255) right_rear = 255; + if (center < 0) center = 0; if (center > 255) center = 255; + + if (room_angle == 90) { + speaker_amplitude[0] = (Uint8)left_rear; + speaker_amplitude[1] = (Uint8)left; + speaker_amplitude[2] = (Uint8)right_rear; + speaker_amplitude[3] = (Uint8)right; + } + else if (room_angle == 180) { + if (channels == 2) { + speaker_amplitude[0] = (Uint8)right; + speaker_amplitude[1] = (Uint8)left; + } + else { + speaker_amplitude[0] = (Uint8)right_rear; + speaker_amplitude[1] = (Uint8)left_rear; + speaker_amplitude[2] = (Uint8)right; + speaker_amplitude[3] = (Uint8)left; + } + } + else if (room_angle == 270) { + speaker_amplitude[0] = (Uint8)right; + speaker_amplitude[1] = (Uint8)right_rear; + speaker_amplitude[2] = (Uint8)left; + speaker_amplitude[3] = (Uint8)left_rear; + } + else { + speaker_amplitude[0] = (Uint8)left; + speaker_amplitude[1] = (Uint8)right; + speaker_amplitude[2] = (Uint8)left_rear; + speaker_amplitude[3] = (Uint8)right_rear; + } + speaker_amplitude[4] = (Uint8)center; + speaker_amplitude[5] = 255; +} + +int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance); + +int Mix_SetPanning(int channel, Uint8 left, Uint8 right) +{ + Mix_EffectFunc_t f = NULL; + int channels; + Uint16 format; + position_args *args = NULL; + int retval = 1; + + Mix_QuerySpec(NULL, &format, &channels); + + if (channels != 2 && channels != 4 && channels != 6) /* it's a no-op; we call that successful. */ + return(1); + + if (channels > 2) { + /* left = right = 255 => angle = 0, to unregister effect as when channels = 2 */ + /* left = 255 => angle = -90; left = 0 => angle = +89 */ + int angle = 0; + if ((left != 255) || (right != 255)) { + angle = (int)left; + angle = 127 - angle; + angle = -angle; + angle = angle * 90 / 128; /* Make it larger for more effect? */ + } + return( Mix_SetPosition(channel, angle, 0) ); + } + + f = get_position_effect_func(format, channels); + if (f == NULL) + return(0); + + SDL_LockAudio(); + args = get_position_arg(channel); + if (!args) { + SDL_UnlockAudio(); + return(0); + } + + /* it's a no-op; unregister the effect, if it's registered. */ + if ((args->distance_u8 == 255) && (left == 255) && (right == 255)) { + if (args->in_use) { + retval = _Mix_UnregisterEffect_locked(channel, f); + SDL_UnlockAudio(); + return(retval); + } else { + SDL_UnlockAudio(); + return(1); + } + } + + args->left_u8 = left; + args->left_f = ((float) left) / 255.0f; + args->right_u8 = right; + args->right_f = ((float) right) / 255.0f; + args->room_angle = 0; + + if (!args->in_use) { + args->in_use = 1; + retval=_Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void*)args); + } + + SDL_UnlockAudio(); + return(retval); +} + + +int Mix_SetDistance(int channel, Uint8 distance) +{ + Mix_EffectFunc_t f = NULL; + Uint16 format; + position_args *args = NULL; + int channels; + int retval = 1; + + Mix_QuerySpec(NULL, &format, &channels); + f = get_position_effect_func(format, channels); + if (f == NULL) + return(0); + + SDL_LockAudio(); + args = get_position_arg(channel); + if (!args) { + SDL_UnlockAudio(); + return(0); + } + + distance = 255 - distance; /* flip it to our scale. */ + + /* it's a no-op; unregister the effect, if it's registered. */ + if ((distance == 255) && (args->left_u8 == 255) && (args->right_u8 == 255)) { + if (args->in_use) { + retval = _Mix_UnregisterEffect_locked(channel, f); + SDL_UnlockAudio(); + return(retval); + } else { + SDL_UnlockAudio(); + return(1); + } + } + + args->distance_u8 = distance; + args->distance_f = ((float) distance) / 255.0f; + if (!args->in_use) { + args->in_use = 1; + retval = _Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void *) args); + } + + SDL_UnlockAudio(); + return(retval); +} + + +int Mix_SetPosition(int channel, Sint16 angle, Uint8 distance) +{ + Mix_EffectFunc_t f = NULL; + Uint16 format; + int channels; + position_args *args = NULL; + Sint16 room_angle = 0; + int retval = 1; + + Mix_QuerySpec(NULL, &format, &channels); + f = get_position_effect_func(format, channels); + if (f == NULL) + return(0); + + angle = SDL_abs(angle) % 360; /* make angle between 0 and 359. */ + + SDL_LockAudio(); + args = get_position_arg(channel); + if (!args) { + SDL_UnlockAudio(); + return(0); + } + + /* it's a no-op; unregister the effect, if it's registered. */ + if ((!distance) && (!angle)) { + if (args->in_use) { + retval = _Mix_UnregisterEffect_locked(channel, f); + SDL_UnlockAudio(); + return(retval); + } else { + SDL_UnlockAudio(); + return(1); + } + } + + if (channels == 2) + { + if (angle > 180) + room_angle = 180; /* exchange left and right channels */ + else room_angle = 0; + } + + if (channels == 4 || channels == 6) + { + if (angle > 315) room_angle = 0; + else if (angle > 225) room_angle = 270; + else if (angle > 135) room_angle = 180; + else if (angle > 45) room_angle = 90; + else room_angle = 0; + } + + + distance = 255 - distance; /* flip it to scale Mix_SetDistance() uses. */ + + set_amplitudes(channels, angle, room_angle); + + args->left_u8 = speaker_amplitude[0]; + args->left_f = ((float) speaker_amplitude[0]) / 255.0f; + args->right_u8 = speaker_amplitude[1]; + args->right_f = ((float) speaker_amplitude[1]) / 255.0f; + args->left_rear_u8 = speaker_amplitude[2]; + args->left_rear_f = ((float) speaker_amplitude[2]) / 255.0f; + args->right_rear_u8 = speaker_amplitude[3]; + args->right_rear_f = ((float) speaker_amplitude[3]) / 255.0f; + args->center_u8 = speaker_amplitude[4]; + args->center_f = ((float) speaker_amplitude[4]) / 255.0f; + args->lfe_u8 = speaker_amplitude[5]; + args->lfe_f = ((float) speaker_amplitude[5]) / 255.0f; + args->distance_u8 = distance; + args->distance_f = ((float) distance) / 255.0f; + args->room_angle = room_angle; + if (!args->in_use) { + args->in_use = 1; + retval = _Mix_RegisterEffect_locked(channel, f, _Eff_PositionDone, (void *) args); + } + + SDL_UnlockAudio(); + return(retval); +} + + +/* end of effects_position.c ... */ + diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/effect_stereoreverse.c b/contrib/sdk/sources/SDL_mixer-1.2.12/effect_stereoreverse.c new file mode 100644 index 0000000000..a18ee9c019 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/effect_stereoreverse.c @@ -0,0 +1,120 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This file by Ryan C. Gordon (icculus@icculus.org) + + These are some internally supported special effects that use SDL_mixer's + effect callback API. They are meant for speed over quality. :) +*/ + +/* $Id$ */ + +#include +#include + +#include "SDL.h" +#include "SDL_mixer.h" + +#define __MIX_INTERNAL_EFFECT__ +#include "effects_internal.h" + +/* profile code: + #include + #include + struct timeval tv1; + struct timeval tv2; + + gettimeofday(&tv1, NULL); + + ... do your thing here ... + + gettimeofday(&tv2, NULL); + printf("%ld\n", tv2.tv_usec - tv1.tv_usec); +*/ + + + +/* + * Stereo reversal effect...this one's pretty straightforward... + */ + +static void _Eff_reversestereo16(int chan, void *stream, int len, void *udata) +{ + /* 16 bits * 2 channels. */ + Uint32 *ptr = (Uint32 *) stream; + int i; + + for (i = 0; i < len; i += sizeof (Uint32), ptr++) { + *ptr = (((*ptr) & 0xFFFF0000) >> 16) | (((*ptr) & 0x0000FFFF) << 16); + } +} + + +static void _Eff_reversestereo8(int chan, void *stream, int len, void *udata) +{ + /* 8 bits * 2 channels. */ + Uint32 *ptr = (Uint32 *) stream; + int i; + + /* get the last two bytes if len is not divisible by four... */ + if (len % sizeof (Uint32) != 0) { + Uint16 *p = (Uint16 *) (((Uint8 *) stream) + (len - 2)); + *p = (Uint16)((((*p) & 0xFF00) >> 8) | (((*ptr) & 0x00FF) << 8)); + len -= 2; + } + + for (i = 0; i < len; i += sizeof (Uint32), ptr++) { + *ptr = (((*ptr) & 0x0000FF00) >> 8) | (((*ptr) & 0x000000FF) << 8) | + (((*ptr) & 0xFF000000) >> 8) | (((*ptr) & 0x00FF0000) << 8); + } +} + + +int Mix_SetReverseStereo(int channel, int flip) +{ + Mix_EffectFunc_t f = NULL; + int channels; + Uint16 format; + + Mix_QuerySpec(NULL, &format, &channels); + + if (channels == 2) { + if ((format & 0xFF) == 16) + f = _Eff_reversestereo16; + else if ((format & 0xFF) == 8) + f = _Eff_reversestereo8; + else { + Mix_SetError("Unsupported audio format"); + return(0); + } + + if (!flip) { + return(Mix_UnregisterEffect(channel, f)); + } else { + return(Mix_RegisterEffect(channel, f, NULL, NULL)); + } + } + + return(1); +} + + +/* end of effect_stereoreverse.c ... */ + diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/effects_internal.c b/contrib/sdk/sources/SDL_mixer-1.2.12/effects_internal.c new file mode 100644 index 0000000000..c73e5ddf56 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/effects_internal.c @@ -0,0 +1,124 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This file by Ryan C. Gordon (icculus@icculus.org) + + These are some helper functions for the internal mixer special effects. +*/ + +/* $Id$ */ + + + /* ------ These are used internally only. Don't touch. ------ */ + + + +#include +#include +#include "SDL_mixer.h" + +#define __MIX_INTERNAL_EFFECT__ +#include "effects_internal.h" + +/* Should we favor speed over memory usage and/or quality of output? */ +int _Mix_effects_max_speed = 0; + + +void _Mix_InitEffects(void) +{ + _Mix_effects_max_speed = (SDL_getenv(MIX_EFFECTSMAXSPEED) != NULL); +} + +void _Mix_DeinitEffects(void) +{ + _Eff_PositionDeinit(); +} + + +void *_Eff_volume_table = NULL; + + +/* Build the volume table for Uint8-format samples. + * + * Each column of the table is a possible sample, while each row of the + * table is a volume. Volume is a Uint8, where 0 is silence and 255 is full + * volume. So _Eff_volume_table[128][mysample] would be the value of + * mysample, at half volume. + */ +void *_Eff_build_volume_table_u8(void) +{ + int volume; + int sample; + Uint8 *rc; + + if (!_Mix_effects_max_speed) { + return(NULL); + } + + if (!_Eff_volume_table) { + rc = SDL_malloc(256 * 256); + if (rc) { + _Eff_volume_table = (void *) rc; + for (volume = 0; volume < 256; volume++) { + for (sample = -128; sample < 128; sample ++) { + *rc = (Uint8)(((float) sample) * ((float) volume / 255.0)) + + 128; + rc++; + } + } + } + } + + return(_Eff_volume_table); +} + + +/* Build the volume table for Sint8-format samples. + * + * Each column of the table is a possible sample, while each row of the + * table is a volume. Volume is a Uint8, where 0 is silence and 255 is full + * volume. So _Eff_volume_table[128][mysample+128] would be the value of + * mysample, at half volume. + */ +void *_Eff_build_volume_table_s8(void) +{ + int volume; + int sample; + Sint8 *rc; + + if (!_Eff_volume_table) { + rc = SDL_malloc(256 * 256); + if (rc) { + _Eff_volume_table = (void *) rc; + for (volume = 0; volume < 256; volume++) { + for (sample = -128; sample < 128; sample ++) { + *rc = (Sint8)(((float) sample) * ((float) volume / 255.0)); + rc++; + } + } + } + } + + return(_Eff_volume_table); +} + + +/* end of effects.c ... */ + diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/effects_internal.h b/contrib/sdk/sources/SDL_mixer-1.2.12/effects_internal.h new file mode 100644 index 0000000000..12b4b3b2da --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/effects_internal.h @@ -0,0 +1,60 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifndef _INCLUDE_EFFECTS_INTERNAL_H_ +#define _INCLUDE_EFFECTS_INTERNAL_H_ + +#ifndef __MIX_INTERNAL_EFFECT__ +#error You should not include this file or use these functions. +#endif + +#include "SDL_mixer.h" + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +extern int _Mix_effects_max_speed; +extern void *_Eff_volume_table; +void *_Eff_build_volume_table_u8(void); +void *_Eff_build_volume_table_s8(void); + +void _Mix_InitEffects(void); +void _Mix_DeinitEffects(void); +void _Eff_PositionDeinit(void); + +int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg); +int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f); +int _Mix_UnregisterAllEffects_locked(int channel); + + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/load_aiff.c b/contrib/sdk/sources/SDL_mixer-1.2.12/load_aiff.c new file mode 100644 index 0000000000..b56537e94e --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/load_aiff.c @@ -0,0 +1,250 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode an AIFF file into a waveform. + It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadAIFF_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se) + 8SVX file support added by Marc Le Douarain (mavati@club-internet.fr) + in december 2002. +*/ + +/* $Id$ */ + +#include +#include + +#include "SDL_endian.h" +#include "SDL_mixer.h" +#include "load_aiff.h" + +/*********************************************/ +/* Define values for AIFF (IFF audio) format */ +/*********************************************/ +#define FORM 0x4d524f46 /* "FORM" */ + +#define AIFF 0x46464941 /* "AIFF" */ +#define SSND 0x444e5353 /* "SSND" */ +#define COMM 0x4d4d4f43 /* "COMM" */ + +#define _8SVX 0x58565338 /* "8SVX" */ +#define VHDR 0x52444856 /* "VHDR" */ +#define BODY 0x59444F42 /* "BODY" */ + +/* This function was taken from libsndfile. I don't pretend to fully + * understand it. + */ + +static Uint32 SANE_to_Uint32 (Uint8 *sanebuf) +{ + /* Is the frequency outside of what we can represent with Uint32? */ + if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40) + || (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) ) + return 0; + + return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) + | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]); +} + +/* This function is based on SDL_LoadWAV_RW(). */ + +SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + int was_error; + int found_SSND; + int found_COMM; + int found_VHDR; + int found_BODY; + long start = 0; + + 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; + if ( src == NULL ) { + was_error = 1; + goto done; + } + + FORMchunk = SDL_ReadLE32(src); + chunk_length = SDL_ReadBE32(src); + if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */ + AIFFmagic = chunk_length; + chunk_length = FORMchunk; + FORMchunk = FORM; + } else { + AIFFmagic = SDL_ReadLE32(src); + } + if ( (FORMchunk != FORM) || ( (AIFFmagic != AIFF) && (AIFFmagic != _8SVX) ) ) { + SDL_SetError("Unrecognized file type (not AIFF nor 8SVX)"); + was_error = 1; + goto done; + } + + /* TODO: Better santity-checking. */ + + found_SSND = 0; + found_COMM = 0; + found_VHDR = 0; + found_BODY = 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; + 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); + if (frequency == 0) { + SDL_SetError("Bad AIFF sample frequency"); + was_error = 1; + goto done; + } + break; + + case VHDR: + found_VHDR = 1; + SDL_ReadBE32(src); + SDL_ReadBE32(src); + SDL_ReadBE32(src); + frequency = SDL_ReadBE16(src); + channels = 1; + samplesize = 8; + break; + + case BODY: + found_BODY = 1; + numsamples = chunk_length; + start = SDL_RWtell(src); + break; + + default: + break; + } + /* a 0 pad byte can be stored for any odd-length chunk */ + if (chunk_length&1) + next_chunk++; + } while ( ( ( (AIFFmagic == AIFF) && ( !found_SSND || !found_COMM ) ) + || ( (AIFFmagic == _8SVX ) && ( !found_VHDR || !found_BODY ) ) ) + && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != 1 ); + + if ( (AIFFmagic == AIFF) && !found_SSND ) { + SDL_SetError("Bad AIFF (no SSND chunk)"); + was_error = 1; + goto done; + } + + if ( (AIFFmagic == AIFF) && !found_COMM ) { + SDL_SetError("Bad AIFF (no COMM chunk)"); + was_error = 1; + goto done; + } + + if ( (AIFFmagic == _8SVX) && !found_VHDR ) { + SDL_SetError("Bad 8SVX (no VHDR chunk)"); + was_error = 1; + goto done; + } + + if ( (AIFFmagic == _8SVX) && !found_BODY ) { + SDL_SetError("Bad 8SVX (no BODY chunk)"); + was_error = 1; + goto done; + } + + /* 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: + SDL_SetError("Unsupported AIFF samplesize"); + was_error = 1; + goto done; + } + spec->channels = (Uint8) channels; + spec->samples = 4096; /* Good default buffer size */ + + *audio_len = channels * numsamples * (samplesize / 8); + *audio_buf = (Uint8 *)SDL_malloc(*audio_len); + if ( *audio_buf == NULL ) { + SDL_SetError("Out of memory"); + return(NULL); + } + SDL_RWseek(src, start, RW_SEEK_SET); + if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) { + SDL_SetError("Unable to read audio data"); + return(NULL); + } + + /* Don't return a buffer that isn't a multiple of samplesize */ + *audio_len &= ~((samplesize / 8) - 1); + +done: + if ( freesrc && src ) { + SDL_RWclose(src); + } + if ( was_error ) { + spec = NULL; + } + return(spec); +} + diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/load_aiff.h b/contrib/sdk/sources/SDL_mixer-1.2.12/load_aiff.h new file mode 100644 index 0000000000..ed55d36440 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/load_aiff.h @@ -0,0 +1,31 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2009 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 + + This is the source needed to decode an AIFF file into a waveform. + It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadAIFF_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se) +*/ + +/* $Id$ */ + +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/load_ogg.c b/contrib/sdk/sources/SDL_mixer-1.2.12/load_ogg.c new file mode 100644 index 0000000000..2d0ee65838 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/load_ogg.c @@ -0,0 +1,163 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode an Ogg Vorbis into a waveform. + This file by Vaclav Slavik (vaclav.slavik@matfyz.cz). +*/ + +/* $Id$ */ + +#ifdef OGG_MUSIC + +#include +#include +#include + +#include "SDL_mutex.h" +#include "SDL_endian.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" +#include "dynamic_ogg.h" +#include "load_ogg.h" + +static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb); +} + +static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence) +{ + return SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence); +} + +static int sdl_close_func_freesrc(void *datasource) +{ + return SDL_RWclose((SDL_RWops*)datasource); +} + +static int sdl_close_func_nofreesrc(void *datasource) +{ + return SDL_RWseek((SDL_RWops*)datasource, 0, RW_SEEK_SET); +} + +static long sdl_tell_func(void *datasource) +{ + return SDL_RWtell((SDL_RWops*)datasource); +} + + +/* don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + OggVorbis_File vf; + ov_callbacks callbacks; + vorbis_info *info; + Uint8 *buf; + int bitstream = -1; + long samplesize; + long samples; + int read, to_read; + int must_close = 1; + int was_error = 1; + + if ( (!src) || (!audio_buf) || (!audio_len) ) /* sanity checks. */ + goto done; + + if ( !Mix_Init(MIX_INIT_OGG) ) + goto done; + + callbacks.read_func = sdl_read_func; + callbacks.seek_func = sdl_seek_func; + callbacks.tell_func = sdl_tell_func; + callbacks.close_func = freesrc ? + sdl_close_func_freesrc : sdl_close_func_nofreesrc; + + if (vorbis.ov_open_callbacks(src, &vf, NULL, 0, callbacks) != 0) + { + SDL_SetError("OGG bitstream is not valid Vorbis stream!"); + goto done; + } + + must_close = 0; + + info = vorbis.ov_info(&vf, -1); + + *audio_buf = NULL; + *audio_len = 0; + memset(spec, '\0', sizeof (SDL_AudioSpec)); + + spec->format = AUDIO_S16; + spec->channels = info->channels; + spec->freq = info->rate; + spec->samples = 4096; /* buffer size */ + + samples = (long)vorbis.ov_pcm_total(&vf, -1); + + *audio_len = spec->size = samples * spec->channels * 2; + *audio_buf = SDL_malloc(*audio_len); + if (*audio_buf == NULL) + goto done; + + buf = *audio_buf; + to_read = *audio_len; +#ifdef OGG_USE_TREMOR + for (read = vorbis.ov_read(&vf, (char *)buf, to_read, &bitstream); + read > 0; + read = vorbis.ov_read(&vf, (char *)buf, to_read, &bitstream)) +#else + for (read = vorbis.ov_read(&vf, (char *)buf, to_read, 0/*LE*/, 2/*16bit*/, 1/*signed*/, &bitstream); + read > 0; + read = vorbis.ov_read(&vf, (char *)buf, to_read, 0, 2, 1, &bitstream)) +#endif + { + if (read == OV_HOLE || read == OV_EBADLINK) + break; /* error */ + + to_read -= read; + buf += read; + } + + vorbis.ov_clear(&vf); + was_error = 0; + + /* Don't return a buffer that isn't a multiple of samplesize */ + samplesize = ((spec->format & 0xFF)/8)*spec->channels; + *audio_len &= ~(samplesize-1); + +done: + if (src && must_close) + { + if (freesrc) + SDL_RWclose(src); + else + SDL_RWseek(src, 0, RW_SEEK_SET); + } + + if ( was_error ) + spec = NULL; + + return(spec); +} /* Mix_LoadOGG_RW */ + +/* end of load_ogg.c ... */ + +#endif diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/load_ogg.h b/contrib/sdk/sources/SDL_mixer-1.2.12/load_ogg.h new file mode 100644 index 0000000000..e63b04f7b3 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/load_ogg.h @@ -0,0 +1,31 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode an Ogg Vorbis into a waveform. + This file by Vaclav Slavik (vaclav.slavik@matfyz.cz). +*/ + +/* $Id$ */ + +#ifdef OGG_MUSIC +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadOGG_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); +#endif diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/load_voc.c b/contrib/sdk/sources/SDL_mixer-1.2.12/load_voc.c new file mode 100644 index 0000000000..ba489fc472 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/load_voc.c @@ -0,0 +1,462 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode a Creative Labs VOC file into a + waveform. It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadVOC_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Ryan C. Gordon (icculus@icculus.org). + + Heavily borrowed from sox v12.17.1's voc.c. + (http://www.freshmeat.net/projects/sox/) +*/ + +/* $Id$ */ + +#include +#include +#include + +#include "SDL_mutex.h" +#include "SDL_endian.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" +#include "load_voc.h" + +/* Private data for VOC file */ +typedef struct vocstuff { + Uint32 rest; /* bytes remaining in current block */ + Uint32 rate; /* rate code (byte) of this chunk */ + int silent; /* sound or silence? */ + Uint32 srate; /* rate code (byte) of silence */ + Uint32 blockseek; /* start of current output block */ + Uint32 samples; /* number of samples output */ + Uint32 size; /* word length of data */ + Uint8 channels; /* number of sound channels */ + int has_extended; /* Has an extended block been read? */ +} vs_t; + +/* Size field */ +/* SJB: note that the 1st 3 are sometimes used as sizeof(type) */ +#define ST_SIZE_BYTE 1 +#define ST_SIZE_8BIT 1 +#define ST_SIZE_WORD 2 +#define ST_SIZE_16BIT 2 +#define ST_SIZE_DWORD 4 +#define ST_SIZE_32BIT 4 +#define ST_SIZE_FLOAT 5 +#define ST_SIZE_DOUBLE 6 +#define ST_SIZE_IEEE 7 /* IEEE 80-bit floats. */ + +/* Style field */ +#define ST_ENCODING_UNSIGNED 1 /* unsigned linear: Sound Blaster */ +#define ST_ENCODING_SIGN2 2 /* signed linear 2's comp: Mac */ +#define ST_ENCODING_ULAW 3 /* U-law signed logs: US telephony, SPARC */ +#define ST_ENCODING_ALAW 4 /* A-law signed logs: non-US telephony */ +#define ST_ENCODING_ADPCM 5 /* Compressed PCM */ +#define ST_ENCODING_IMA_ADPCM 6 /* Compressed PCM */ +#define ST_ENCODING_GSM 7 /* GSM 6.10 33-byte frame lossy compression */ + +#define VOC_TERM 0 +#define VOC_DATA 1 +#define VOC_CONT 2 +#define VOC_SILENCE 3 +#define VOC_MARKER 4 +#define VOC_TEXT 5 +#define VOC_LOOP 6 +#define VOC_LOOPEND 7 +#define VOC_EXTENDED 8 +#define VOC_DATA_16 9 + + +static int voc_check_header(SDL_RWops *src) +{ + /* VOC magic header */ + Uint8 signature[20]; /* "Creative Voice File\032" */ + Uint16 datablockofs; + + SDL_RWseek(src, 0, RW_SEEK_SET); + + if (SDL_RWread(src, signature, sizeof (signature), 1) != 1) + return(0); + + if (memcmp(signature, "Creative Voice File\032", sizeof (signature)) != 0) { + SDL_SetError("Unrecognized file type (not VOC)"); + return(0); + } + + /* get the offset where the first datablock is located */ + if (SDL_RWread(src, &datablockofs, sizeof (Uint16), 1) != 1) + return(0); + + datablockofs = SDL_SwapLE16(datablockofs); + + if (SDL_RWseek(src, datablockofs, RW_SEEK_SET) != datablockofs) + return(0); + + return(1); /* success! */ +} /* voc_check_header */ + + +/* Read next block header, save info, leave position at start of data */ +static int voc_get_block(SDL_RWops *src, vs_t *v, SDL_AudioSpec *spec) +{ + Uint8 bits24[3]; + Uint8 uc, block; + Uint32 sblen; + Uint16 new_rate_short; + Uint32 new_rate_long; + Uint8 trash[6]; + Uint16 period; + unsigned int i; + + v->silent = 0; + while (v->rest == 0) + { + if (SDL_RWread(src, &block, sizeof (block), 1) != 1) + return 1; /* assume that's the end of the file. */ + + if (block == VOC_TERM) + return 1; + + if (SDL_RWread(src, bits24, sizeof (bits24), 1) != 1) + return 1; /* assume that's the end of the file. */ + + /* Size is an 24-bit value. Ugh. */ + sblen = ( (bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16) ); + + switch(block) + { + case VOC_DATA: + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + /* When DATA block preceeded by an EXTENDED */ + /* block, the DATA blocks rate value is invalid */ + if (!v->has_extended) + { + if (uc == 0) + { + SDL_SetError("VOC Sample rate is zero?"); + return 0; + } + + if ((v->rate != -1) && (uc != v->rate)) + { + SDL_SetError("VOC sample rate codes differ"); + return 0; + } + + v->rate = uc; + spec->freq = (Uint16)(1000000.0/(256 - v->rate)); + v->channels = 1; + } + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + if (uc != 0) + { + SDL_SetError("VOC decoder only interprets 8-bit data"); + return 0; + } + + v->has_extended = 0; + v->rest = sblen - 2; + v->size = ST_SIZE_BYTE; + return 1; + + case VOC_DATA_16: + if (SDL_RWread(src, &new_rate_long, sizeof (new_rate_long), 1) != 1) + return 0; + new_rate_long = SDL_SwapLE32(new_rate_long); + if (new_rate_long == 0) + { + SDL_SetError("VOC Sample rate is zero?"); + return 0; + } + if ((v->rate != -1) && (new_rate_long != v->rate)) + { + SDL_SetError("VOC sample rate codes differ"); + return 0; + } + v->rate = new_rate_long; + spec->freq = new_rate_long; + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + switch (uc) + { + case 8: v->size = ST_SIZE_BYTE; break; + case 16: v->size = ST_SIZE_WORD; break; + default: + SDL_SetError("VOC with unknown data size"); + return 0; + } + + if (SDL_RWread(src, &v->channels, sizeof (Uint8), 1) != 1) + return 0; + + if (SDL_RWread(src, trash, sizeof (Uint8), 6) != 6) + return 0; + + v->rest = sblen - 12; + return 1; + + case VOC_CONT: + v->rest = sblen; + return 1; + + case VOC_SILENCE: + if (SDL_RWread(src, &period, sizeof (period), 1) != 1) + return 0; + period = SDL_SwapLE16(period); + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + if (uc == 0) + { + SDL_SetError("VOC silence sample rate is zero"); + return 0; + } + + /* + * Some silence-packed files have gratuitously + * different sample rate codes in silence. + * Adjust period. + */ + if ((v->rate != -1) && (uc != v->rate)) + period = (Uint16)((period * (256 - uc))/(256 - v->rate)); + else + v->rate = uc; + v->rest = period; + v->silent = 1; + return 1; + + case VOC_LOOP: + case VOC_LOOPEND: + for(i = 0; i < sblen; i++) /* skip repeat loops. */ + { + if (SDL_RWread(src, trash, sizeof (Uint8), 1) != 1) + return 0; + } + break; + + case VOC_EXTENDED: + /* An Extended block is followed by a data block */ + /* Set this byte so we know to use the rate */ + /* value from the extended block and not the */ + /* data block. */ + v->has_extended = 1; + if (SDL_RWread(src, &new_rate_short, sizeof (new_rate_short), 1) != 1) + return 0; + new_rate_short = SDL_SwapLE16(new_rate_short); + if (new_rate_short == 0) + { + SDL_SetError("VOC sample rate is zero"); + return 0; + } + if ((v->rate != -1) && (new_rate_short != v->rate)) + { + SDL_SetError("VOC sample rate codes differ"); + return 0; + } + v->rate = new_rate_short; + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + if (uc != 0) + { + SDL_SetError("VOC decoder only interprets 8-bit data"); + return 0; + } + + if (SDL_RWread(src, &uc, sizeof (uc), 1) != 1) + return 0; + + if (uc) + spec->channels = 2; /* Stereo */ + /* Needed number of channels before finishing + compute for rate */ + spec->freq = (256000000L/(65536L - v->rate))/spec->channels; + /* An extended block must be followed by a data */ + /* block to be valid so loop back to top so it */ + /* can be grabed. */ + continue; + + case VOC_MARKER: + if (SDL_RWread(src, trash, sizeof (Uint8), 2) != 2) + return 0; + + /* Falling! Falling! */ + + default: /* text block or other krapola. */ + for(i = 0; i < sblen; i++) + { + if (SDL_RWread(src, &trash, sizeof (Uint8), 1) != 1) + return 0; + } + + if (block == VOC_TEXT) + continue; /* get next block */ + } + } + + return 1; +} + + +static int voc_read(SDL_RWops *src, vs_t *v, Uint8 *buf, SDL_AudioSpec *spec) +{ + int done = 0; + Uint8 silence = 0x80; + + if (v->rest == 0) + { + if (!voc_get_block(src, v, spec)) + return 0; + } + + if (v->rest == 0) + return 0; + + if (v->silent) + { + if (v->size == ST_SIZE_WORD) + silence = 0x00; + + /* Fill in silence */ + memset(buf, silence, v->rest); + done = v->rest; + v->rest = 0; + } + + else + { + done = SDL_RWread(src, buf, 1, v->rest); + v->rest -= done; + if (v->size == ST_SIZE_WORD) + { + #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + Uint16 *samples = (Uint16 *)buf; + for (; v->rest > 0; v->rest -= 2) + { + *samples = SDL_SwapLE16(*samples); + samples++; + } + #endif + done >>= 1; + } + } + + return done; +} /* voc_read */ + + +/* don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + vs_t v; + int was_error = 1; + int samplesize; + Uint8 *fillptr; + void *ptr; + + if ( (!src) || (!audio_buf) || (!audio_len) ) /* sanity checks. */ + goto done; + + if ( !voc_check_header(src) ) + goto done; + + v.rate = -1; + v.rest = 0; + v.has_extended = 0; + *audio_buf = NULL; + *audio_len = 0; + memset(spec, '\0', sizeof (SDL_AudioSpec)); + + if (!voc_get_block(src, &v, spec)) + goto done; + + if (v.rate == -1) + { + SDL_SetError("VOC data had no sound!"); + goto done; + } + + spec->format = ((v.size == ST_SIZE_WORD) ? AUDIO_S16 : AUDIO_U8); + if (spec->channels == 0) + spec->channels = v.channels; + + *audio_len = v.rest; + *audio_buf = SDL_malloc(v.rest); + if (*audio_buf == NULL) + goto done; + + fillptr = *audio_buf; + + while (voc_read(src, &v, fillptr, spec) > 0) + { + if (!voc_get_block(src, &v, spec)) + goto done; + + *audio_len += v.rest; + ptr = SDL_realloc(*audio_buf, *audio_len); + if (ptr == NULL) + { + SDL_free(*audio_buf); + *audio_buf = NULL; + *audio_len = 0; + goto done; + } + + *audio_buf = ptr; + fillptr = ((Uint8 *) ptr) + (*audio_len - v.rest); + } + + spec->samples = (Uint16)(*audio_len / v.size); + + was_error = 0; /* success, baby! */ + + /* Don't return a buffer that isn't a multiple of samplesize */ + samplesize = ((spec->format & 0xFF)/8)*spec->channels; + *audio_len &= ~(samplesize-1); + +done: + if (src) + { + if (freesrc) + SDL_RWclose(src); + else + SDL_RWseek(src, 0, RW_SEEK_SET); + } + + if ( was_error ) + spec = NULL; + + return(spec); +} /* Mix_LoadVOC_RW */ + +/* end of load_voc.c ... */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/load_voc.h b/contrib/sdk/sources/SDL_mixer-1.2.12/load_voc.h new file mode 100644 index 0000000000..20ae23ca40 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/load_voc.h @@ -0,0 +1,36 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + This is the source needed to decode a Creative Labs VOC file into a + waveform. It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadVOC_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Ryan C. Gordon (icculus@icculus.org). + + Heavily borrowed from sox v12.17.1's voc.c. + (http://www.freshmeat.net/projects/sox/) +*/ + +/* $Id$ */ + +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/mixer.c b/contrib/sdk/sources/SDL_mixer-1.2.12/mixer.c new file mode 100644 index 0000000000..d12985e841 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/mixer.c @@ -0,0 +1,1488 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#include +#include +#include + +#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" +//#include "load_flac.h" +//#include "dynamic_flac.h" +//#include "dynamic_mod.h" +//#include "dynamic_mp3.h" +//#include "dynamic_ogg.h" + +#define __MIX_INTERNAL_EFFECT__ +#include "effects_internal.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" */ +#define FLAC 0x43614C66 /* "fLaC" */ + +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; + int fade_volume_reset; + 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; + +/* rcg06042009 report available decoders at runtime. */ +static const char **chunk_decoders = NULL; +static int num_decoders = 0; + +/* Semicolon-separated SoundFont paths */ +#ifdef MID_MUSIC +extern char* soundfont_paths; +#endif + +int Mix_GetNumChunkDecoders(void) +{ + return(num_decoders); +} + +const char *Mix_GetChunkDecoder(int index) +{ + if ((index < 0) || (index >= num_decoders)) { + return NULL; + } + return(chunk_decoders[index]); +} + +static void add_chunk_decoder(const char *decoder) +{ + void *ptr = SDL_realloc(chunk_decoders, (num_decoders + 1) * sizeof (const char **)); + if (ptr == NULL) { + return; /* oh well, go on without it. */ + } + chunk_decoders = (const char **) ptr; + chunk_decoders[num_decoders++] = decoder; +} + +/* 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 initialized = 0; + +int Mix_Init(int flags) +{ + int result = 0; + + if (flags & MIX_INIT_FLUIDSYNTH) { +#ifdef USE_FLUIDSYNTH_MIDI + if ((initialized & MIX_INIT_FLUIDSYNTH) || Mix_InitFluidSynth() == 0) { + result |= MIX_INIT_FLUIDSYNTH; + } +#else + Mix_SetError("Mixer not built with FluidSynth support"); +#endif + } + if (flags & MIX_INIT_FLAC) { +#ifdef FLAC_MUSIC + if ((initialized & MIX_INIT_FLAC) || Mix_InitFLAC() == 0) { + result |= MIX_INIT_FLAC; + } +#else + Mix_SetError("Mixer not built with FLAC support"); +#endif + } + if (flags & MIX_INIT_MOD) { +#ifdef MOD_MUSIC + if ((initialized & MIX_INIT_MOD) || Mix_InitMOD() == 0) { + result |= MIX_INIT_MOD; + } +#else + Mix_SetError("Mixer not built with MOD support"); +#endif + } + if (flags & MIX_INIT_MP3) { +#ifdef MP3_MUSIC + if ((initialized & MIX_INIT_MP3) || Mix_InitMP3() == 0) { + result |= MIX_INIT_MP3; + } +#else + Mix_SetError("Mixer not built with MP3 support"); +#endif + } + if (flags & MIX_INIT_OGG) { +#ifdef OGG_MUSIC + if ((initialized & MIX_INIT_OGG) || Mix_InitOgg() == 0) { + result |= MIX_INIT_OGG; + } +#else + Mix_SetError("Mixer not built with Ogg Vorbis support"); +#endif + } + initialized |= result; + + return (result); +} + +void Mix_Quit() +{ +#ifdef USE_FLUIDSYNTH_MIDI + if (initialized & MIX_INIT_FLUIDSYNTH) { + Mix_QuitFluidSynth(); + } +#endif +#ifdef FLAC_MUSIC + if (initialized & MIX_INIT_FLAC) { + Mix_QuitFLAC(); + } +#endif +#ifdef MOD_MUSIC + if (initialized & MIX_INIT_MOD) { + Mix_QuitMOD(); + } +#endif +#ifdef MP3_MUSIC + if (initialized & MIX_INIT_MP3) { + Mix_QuitMP3(); + } +#endif +#ifdef OGG_MUSIC + if (initialized & MIX_INIT_OGG) { + Mix_QuitOgg(); + } +#endif +#ifdef MID_MUSIC + if (soundfont_paths) { + SDL_free(soundfont_paths); + } +#endif + initialized = 0; +} + +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 = SDL_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 SDL_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 = SDL_MIX_MAXVOLUME; + Uint32 sdl_ticks; + +#if SDL_VERSION_ATLEAST(1, 3, 0) + /* Need to initialize the stream in SDL 1.3+ */ + memset(stream, mixer.silence, len); +#endif + + /* 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 0 && mix_channel[i].expire < sdl_ticks ) { + /* Expiration delay for that channel is reached */ + mix_channel[i].playing = 0; + mix_channel[i].looping = 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 ) { + Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */ + if( mix_channel[i].fading == MIX_FADING_OUT ) { + mix_channel[i].playing = 0; + mix_channel[i].looping = 0; + mix_channel[i].expire = 0; + _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) + SDL_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) + SDL_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 ) { + --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); + } +} + +#if 0 +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); +} +#endif + + +/* 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 *) SDL_malloc(num_channels * sizeof(struct _Mix_Channel)); + + /* Clear out the audio channels */ + for ( i=0; i 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].fade_volume_reset = 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 *)SDL_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), RW_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 +#ifdef FLAC_MUSIC + case FLAC: + loaded = Mix_LoadFLAC_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 ) { + SDL_free(chunk); + if ( freesrc ) { + SDL_RWclose(src); + } + return(NULL); + } + +#if 0 + PrintFormat("Audio device", &mixer); + PrintFormat("-- Wave file", &wavespec); +#endif + + /* Build the audio converter and create conversion buffers */ + if ( wavespec.format != mixer.format || + wavespec.channels != mixer.channels || + wavespec.freq != mixer.freq ) { + if ( SDL_BuildAudioCVT(&wavecvt, + wavespec.format, wavespec.channels, wavespec.freq, + mixer.format, mixer.channels, mixer.freq) < 0 ) { + SDL_free(chunk->abuf); + SDL_free(chunk); + return(NULL); + } + samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels; + wavecvt.len = chunk->alen & ~(samplesize-1); + wavecvt.buf = (Uint8 *)SDL_calloc(1, wavecvt.len*wavecvt.len_mult); + if ( wavecvt.buf == NULL ) { + SDL_SetError("Out of memory"); + SDL_free(chunk->abuf); + SDL_free(chunk); + return(NULL); + } + memcpy(wavecvt.buf, chunk->abuf, chunk->alen); + SDL_free(chunk->abuf); + + /* Run the audio converter */ + if ( SDL_ConvertAudio(&wavecvt) < 0 ) { + SDL_free(wavecvt.buf); + SDL_free(chunk); + return(NULL); + } + + chunk->abuf = wavecvt.buf; + chunk->alen = wavecvt.len_cvt; + } + + chunk->allocated = 1; + 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 *)SDL_calloc(1,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 *)SDL_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; iallocated ) { + SDL_free(chunk->abuf); + } + SDL_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) +{ + int frame_width = 1; + + if ((mixer.format & 0xFF) == 16) frame_width = 2; + frame_width *= mixer.channels; + while (chunk->alen % frame_width) chunk->alen--; + return chunk->alen; +} + +/* Play an audio chunk on a specific channel. + If the specified channel is -1, play on the first free channel. + 'ticks' is the number of milliseconds at most to play the sample, or -1 + if there is no limit. + Returns which channel was used to play the sound. +*/ +int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks) +{ + int i; + + /* Don't play null pointers :-) */ + if ( chunk == NULL ) { + Mix_SetError("Tried to play a NULL chunk"); + return(-1); + } + if ( !checkchunkintegral(chunk)) { + Mix_SetError("Tried to play a chunk with a bad frame"); + return(-1); + } + + /* Lock the mixer while modifying the playing channels */ + SDL_LockAudio(); + { + /* If which is -1, play on the first free channel */ + if ( which == -1 ) { + for ( i=reserved_channels; i= 0 && which < num_channels ) { + Uint32 sdl_ticks = SDL_GetTicks(); + if (Mix_Playing(which)) + _Mix_channel_done_playing(which); + mix_channel[which].samples = chunk->abuf; + mix_channel[which].playing = chunk->alen; + mix_channel[which].looping = loops; + mix_channel[which].chunk = chunk; + mix_channel[which].paused = 0; + mix_channel[which].fading = MIX_NO_FADING; + mix_channel[which].start_time = sdl_ticks; + mix_channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : 0; + } + } + SDL_UnlockAudio(); + + /* Return the channel on which the sound is being played */ + return(which); +} + +/* Change the expiration delay for a channel */ +int Mix_ExpireChannel(int which, int ticks) +{ + int status = 0; + + if ( which == -1 ) { + int i; + for ( i=0; i < num_channels; ++ i ) { + status += Mix_ExpireChannel(i, ticks); + } + } else if ( which < num_channels ) { + SDL_LockAudio(); + mix_channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : 0; + SDL_UnlockAudio(); + ++ status; + } + return(status); +} + +/* Fade in a sound on a channel, over ms milliseconds */ +int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks) +{ + int i; + + /* Don't play null pointers :-) */ + if ( chunk == NULL ) { + return(-1); + } + if ( !checkchunkintegral(chunk)) { + Mix_SetError("Tried to play a chunk with a bad frame"); + return(-1); + } + + /* Lock the mixer while modifying the playing channels */ + SDL_LockAudio(); + { + /* If which is -1, play on the first free channel */ + if ( which == -1 ) { + for ( i=reserved_channels; i= 0 && which < num_channels ) { + Uint32 sdl_ticks = SDL_GetTicks(); + if (Mix_Playing(which)) + _Mix_channel_done_playing(which); + mix_channel[which].samples = chunk->abuf; + mix_channel[which].playing = chunk->alen; + mix_channel[which].looping = loops; + mix_channel[which].chunk = chunk; + mix_channel[which].paused = 0; + mix_channel[which].fading = MIX_FADING_IN; + mix_channel[which].fade_volume = mix_channel[which].volume; + mix_channel[which].fade_volume_reset = mix_channel[which].volume; + mix_channel[which].volume = 0; + mix_channel[which].fade_length = (Uint32)ms; + mix_channel[which].start_time = mix_channel[which].ticks_fade = sdl_ticks; + mix_channel[which].expire = (ticks > 0) ? (sdl_ticks+ticks) : 0; + } + } + SDL_UnlockAudio(); + + /* Return the channel on which the sound is being played */ + return(which); +} + +/* Set volume of a particular channel */ +int Mix_Volume(int which, int volume) +{ + int i; + int prev_volume = 0; + + if ( which == -1 ) { + for ( i=0; i= 0 ) { + if ( volume > SDL_MIX_MAXVOLUME ) { + volume = SDL_MIX_MAXVOLUME; + } + mix_channel[which].volume = volume; + } + } + return(prev_volume); +} +/* Set volume of a particular chunk */ +int Mix_VolumeChunk(Mix_Chunk *chunk, int volume) +{ + int prev_volume; + + prev_volume = chunk->volume; + if ( volume >= 0 ) { + if ( volume > MIX_MAX_VOLUME ) { + volume = MIX_MAX_VOLUME; + } + chunk->volume = volume; + } + return(prev_volume); +} + +/* Halt playing of a particular channel */ +int Mix_HaltChannel(int which) +{ + int i; + + if ( which == -1 ) { + for ( i=0; i 0) && + (mix_channel[which].fading != MIX_FADING_OUT) ) { + mix_channel[which].fade_volume = mix_channel[which].volume; + mix_channel[which].fading = MIX_FADING_OUT; + mix_channel[which].fade_length = ms; + mix_channel[which].ticks_fade = SDL_GetTicks(); + + /* only change fade_volume_reset if we're not fading. */ + if (mix_channel[which].fading == MIX_NO_FADING) { + mix_channel[which].fade_volume_reset = mix_channel[which].volume; + } + ++status; + } + SDL_UnlockAudio(); + } + } + return(status); +} + +/* Halt playing of a particular group of channels */ +int Mix_FadeOutGroup(int tag, int ms) +{ + int i; + int status = 0; + for ( i=0; i= num_channels ) { + return MIX_NO_FADING; + } + return mix_channel[which].fading; +} + +/* Check the status of a specific channel. + If the specified mix_channel is -1, check all mix channels. +*/ +int Mix_Playing(int which) +{ + int status; + + status = 0; + if ( which == -1 ) { + int i; + + for ( i=0; i 0) || + (mix_channel[i].looping > 0)) + { + ++status; + } + } + } else if ( which < num_channels ) { + if ( (mix_channel[which].playing > 0) || + (mix_channel[which].looping > 0) ) + { + ++status; + } + } + return(status); +} + +/* rcg06072001 Get the chunk associated with a channel. */ +Mix_Chunk *Mix_GetChunk(int channel) +{ + Mix_Chunk *retval = NULL; + + if ((channel >= 0) && (channel < num_channels)) { + retval = mix_channel[channel].chunk; + } + + return(retval); +} + +/* Close the mixer, halting all playing audio */ +void Mix_CloseAudio(void) +{ + int i; + + if ( audio_opened ) { + if ( audio_opened == 1 ) { + for (i = 0; i < num_channels; i++) { + Mix_UnregisterAllEffects(i); + } + Mix_UnregisterAllEffects(MIX_CHANNEL_POST); + close_music(); + Mix_HaltChannel(-1); + _Mix_DeinitEffects(); + SDL_CloseAudio(); + SDL_free(mix_channel); + mix_channel = NULL; + + /* rcg06042009 report available decoders at runtime. */ + SDL_free(chunk_decoders); + chunk_decoders = NULL; + num_decoders = 0; + } + --audio_opened; + } +} + +/* Pause a particular channel (or all) */ +void Mix_Pause(int which) +{ + Uint32 sdl_ticks = SDL_GetTicks(); + if ( which == -1 ) { + int i; + + for ( i=0; i 0 ) { + mix_channel[i].paused = sdl_ticks; + } + } + } else if ( which < num_channels ) { + if ( mix_channel[which].playing > 0 ) { + mix_channel[which].paused = sdl_ticks; + } + } +} + +/* Resume a paused channel */ +void Mix_Resume(int which) +{ + Uint32 sdl_ticks = SDL_GetTicks(); + + SDL_LockAudio(); + if ( which == -1 ) { + int i; + + for ( i=0; i 0 ) { + if(mix_channel[i].expire > 0) + mix_channel[i].expire += sdl_ticks - mix_channel[i].paused; + mix_channel[i].paused = 0; + } + } + } else if ( which < num_channels ) { + if ( mix_channel[which].playing > 0 ) { + if(mix_channel[which].expire > 0) + mix_channel[which].expire += sdl_ticks - mix_channel[which].paused; + mix_channel[which].paused = 0; + } + } + SDL_UnlockAudio(); +} + +int Mix_Paused(int which) +{ + if ( which < 0 ) { + int status = 0; + int i; + for( i=0; i < num_channels; ++i ) { + if ( mix_channel[i].paused ) { + ++ status; + } + } + return(status); + } else if ( which < num_channels ) { + return(mix_channel[which].paused != 0); + } else { + return(0); + } +} + +/* Change the group of a channel */ +int Mix_GroupChannel(int which, int tag) +{ + if ( which < 0 || which > num_channels ) + return(0); + + SDL_LockAudio(); + mix_channel[which].tag = tag; + SDL_UnlockAudio(); + return(1); +} + +/* Assign several consecutive channels to a group */ +int Mix_GroupChannels(int from, int to, int tag) +{ + int status = 0; + for( ; from <= to; ++ from ) { + status += Mix_GroupChannel(from, tag); + } + return(status); +} + +/* Finds the first available channel in a group of channels */ +int Mix_GroupAvailable(int tag) +{ + int i; + for( i=0; i < num_channels; i ++ ) { + if ( ((tag == -1) || (tag == mix_channel[i].tag)) && + (mix_channel[i].playing <= 0) ) + return i; + } + return(-1); +} + +int Mix_GroupCount(int tag) +{ + int count = 0; + int i; + for( i=0; i < num_channels; i ++ ) { + if ( mix_channel[i].tag==tag || tag==-1 ) + ++ count; + } + return(count); +} + +/* Finds the "oldest" sample playing in a group of channels */ +int Mix_GroupOldest(int tag) +{ + int chan = -1; + Uint32 mintime = SDL_GetTicks(); + int i; + for( i=0; i < num_channels; i ++ ) { + if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0 + && mix_channel[i].start_time <= mintime ) { + mintime = mix_channel[i].start_time; + chan = i; + } + } + return(chan); +} + +/* Finds the "most recent" (i.e. last) sample playing in a group of channels */ +int Mix_GroupNewer(int tag) +{ + int chan = -1; + Uint32 maxtime = 0; + int i; + for( i=0; i < num_channels; i ++ ) { + if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0 + && mix_channel[i].start_time >= maxtime ) { + maxtime = mix_channel[i].start_time; + chan = i; + } + } + return(chan); +} + + + +/* + * rcg06122001 The special effects exportable API. + * Please see effect_*.c for internally-implemented effects, such + * as Mix_SetPanning(). + */ + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg) +{ + effect_info *new_e; + + if (!e) { + Mix_SetError("Internal error"); + return(0); + } + + if (f == NULL) { + Mix_SetError("NULL effect callback"); + return(0); + } + + new_e = SDL_malloc(sizeof (effect_info)); + if (new_e == NULL) { + Mix_SetError("Out of memory"); + return(0); + } + + new_e->callback = f; + new_e->done_callback = d; + new_e->udata = arg; + new_e->next = NULL; + + /* add new effect to end of linked list... */ + if (*e == NULL) { + *e = new_e; + } else { + effect_info *cur = *e; + while (1) { + if (cur->next == NULL) { + cur->next = new_e; + break; + } + cur = cur->next; + } + } + + return(1); +} + + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f) +{ + effect_info *cur; + effect_info *prev = NULL; + effect_info *next = NULL; + + if (!e) { + Mix_SetError("Internal error"); + return(0); + } + + for (cur = *e; cur != NULL; cur = cur->next) { + if (cur->callback == f) { + next = cur->next; + if (cur->done_callback != NULL) { + cur->done_callback(channel, cur->udata); + } + SDL_free(cur); + + if (prev == NULL) { /* removing first item of list? */ + *e = next; + } else { + prev->next = next; + } + return(1); + } + prev = cur; + } + + Mix_SetError("No such effect registered"); + return(0); +} + + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +static int _Mix_remove_all_effects(int channel, effect_info **e) +{ + effect_info *cur; + effect_info *next; + + if (!e) { + Mix_SetError("Internal error"); + return(0); + } + + for (cur = *e; cur != NULL; cur = next) { + next = cur->next; + if (cur->done_callback != NULL) { + cur->done_callback(channel, cur->udata); + } + SDL_free(cur); + } + *e = NULL; + + return(1); +} + + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg) +{ + effect_info **e = NULL; + + if (channel == MIX_CHANNEL_POST) { + e = &posteffects; + } else { + if ((channel < 0) || (channel >= num_channels)) { + Mix_SetError("Invalid channel number"); + return(0); + } + e = &mix_channel[channel].effects; + } + + return _Mix_register_effect(e, f, d, arg); +} + +int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f, + Mix_EffectDone_t d, void *arg) +{ + int retval; + SDL_LockAudio(); + retval = _Mix_RegisterEffect_locked(channel, f, d, arg); + SDL_UnlockAudio(); + return retval; +} + + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f) +{ + effect_info **e = NULL; + + if (channel == MIX_CHANNEL_POST) { + e = &posteffects; + } else { + if ((channel < 0) || (channel >= num_channels)) { + Mix_SetError("Invalid channel number"); + return(0); + } + e = &mix_channel[channel].effects; + } + + return _Mix_remove_effect(channel, e, f); +} + +int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f) +{ + int retval; + SDL_LockAudio(); + retval = _Mix_UnregisterEffect_locked(channel, f); + SDL_UnlockAudio(); + return(retval); +} + +/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */ +int _Mix_UnregisterAllEffects_locked(int channel) +{ + effect_info **e = NULL; + + if (channel == MIX_CHANNEL_POST) { + e = &posteffects; + } else { + if ((channel < 0) || (channel >= num_channels)) { + Mix_SetError("Invalid channel number"); + return(0); + } + e = &mix_channel[channel].effects; + } + + return _Mix_remove_all_effects(channel, e); +} + +int Mix_UnregisterAllEffects(int channel) +{ + int retval; + SDL_LockAudio(); + retval = _Mix_UnregisterAllEffects_locked(channel); + SDL_UnlockAudio(); + return(retval); +} + +/* end of mixer.c ... */ + diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/music.c b/contrib/sdk/sources/SDL_mixer-1.2.12/music.c new file mode 100644 index 0000000000..b8622ea532 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/music.c @@ -0,0 +1,1597 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#include +#include +#include +#include +#include "SDL_endian.h" +#include "SDL_audio.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" + +#ifdef CMD_MUSIC +#include "music_cmd.h" +#endif +#ifdef WAV_MUSIC +#include "wavestream.h" +#endif +#ifdef MODPLUG_MUSIC +#include "music_modplug.h" +#endif +#ifdef MOD_MUSIC +#include "music_mod.h" +#endif +#ifdef MID_MUSIC +# ifdef USE_TIMIDITY_MIDI +# include "timidity.h" +# endif +# ifdef USE_FLUIDSYNTH_MIDI +# include "fluidsynth.h" +# endif +# ifdef USE_NATIVE_MIDI +# include "native_midi.h" +# endif +#endif +#ifdef OGG_MUSIC +#include "music_ogg.h" +#endif +#ifdef MP3_MUSIC +#include "dynamic_mp3.h" +#endif +#ifdef MP3_MAD_MUSIC +#include "music_mad.h" +#endif +#ifdef FLAC_MUSIC +#include "music_flac.h" +#endif + +#if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) +static SDL_AudioSpec used_mixer; +#endif + + +int volatile music_active = 1; +static int volatile music_stopped = 0; +static int music_loops = 0; +static char *music_cmd = NULL; +static Mix_Music * volatile music_playing = NULL; +static int music_volume = MIX_MAX_VOLUME; + +struct _Mix_Music { + Mix_MusicType type; + union { +#ifdef CMD_MUSIC + MusicCMD *cmd; +#endif +#ifdef WAV_MUSIC + WAVStream *wave; +#endif +#ifdef MODPLUG_MUSIC + modplug_data *modplug; +#endif +#ifdef MOD_MUSIC + struct MODULE *module; +#endif +#ifdef MID_MUSIC +#ifdef USE_TIMIDITY_MIDI + MidiSong *midi; +#endif +#ifdef USE_FLUIDSYNTH_MIDI + FluidSynthMidiSong *fluidsynthmidi; +#endif +#ifdef USE_NATIVE_MIDI + NativeMidiSong *nativemidi; +#endif +#endif +#ifdef OGG_MUSIC + OGG_music *ogg; +#endif +#ifdef MP3_MUSIC + SMPEG *mp3; +#endif +#ifdef MP3_MAD_MUSIC + mad_data *mp3_mad; +#endif +#ifdef FLAC_MUSIC + FLAC_music *flac; +#endif + } data; + Mix_Fading fading; + int fade_step; + int fade_steps; + int error; +}; +#ifdef MID_MUSIC +#ifdef USE_TIMIDITY_MIDI +static int timidity_ok; +static int samplesize; +#endif +#ifdef USE_FLUIDSYNTH_MIDI +static int fluidsynth_ok; +#endif +#ifdef USE_NATIVE_MIDI +static int native_midi_ok; +#endif +#endif + +/* Used to calculate fading steps */ +static int ms_per_step; + +/* rcg06042009 report available decoders at runtime. */ +static const char **music_decoders = NULL; +static int num_decoders = 0; + +/* Semicolon-separated SoundFont paths */ +#ifdef MID_MUSIC +char* soundfont_paths = NULL; +#endif + +int Mix_GetNumMusicDecoders(void) +{ + return(num_decoders); +} + +const char *Mix_GetMusicDecoder(int index) +{ + if ((index < 0) || (index >= num_decoders)) { + return NULL; + } + return(music_decoders[index]); +} + +static void add_music_decoder(const char *decoder) +{ + void *ptr = SDL_realloc(music_decoders, (num_decoders + 1) * sizeof (const char **)); + if (ptr == NULL) { + return; /* oh well, go on without it. */ + } + music_decoders = (const char **) ptr; + music_decoders[num_decoders++] = decoder; +} + +/* Local low-level functions prototypes */ +static void music_internal_initialize_volume(void); +static void music_internal_volume(int volume); +static int music_internal_play(Mix_Music *music, double position); +static int music_internal_position(double position); +static int music_internal_playing(); +static void music_internal_halt(void); + + +/* Support for hooking when the music has finished */ +static void (*music_finished_hook)(void) = NULL; + +void Mix_HookMusicFinished(void (*music_finished)(void)) +{ + SDL_LockAudio(); + music_finished_hook = music_finished; + SDL_UnlockAudio(); +} + + +/* If music isn't playing, halt it if no looping is required, restart it */ +/* otherwhise. NOP if the music is playing */ +static int music_halt_or_loop (void) +{ + /* Restart music if it has to loop */ + + if (!music_internal_playing()) + { +#ifdef USE_NATIVE_MIDI + /* Native MIDI handles looping internally */ + if (music_playing->type == MUS_MID && native_midi_ok) { + music_loops = 0; + } +#endif + + /* Restart music if it has to loop at a high level */ + if (music_loops) + { + Mix_Fading current_fade; + --music_loops; + current_fade = music_playing->fading; + music_internal_play(music_playing, 0.0); + music_playing->fading = current_fade; + } + else + { + music_internal_halt(); + if (music_finished_hook) + music_finished_hook(); + + return 0; + } + } + + return 1; +} + + + +/* Mixing function */ +void music_mixer(void *udata, Uint8 *stream, int len) +{ + int left = 0; + + if ( music_playing && music_active ) { + /* Handle fading */ + if ( music_playing->fading != MIX_NO_FADING ) { + if ( music_playing->fade_step++ < music_playing->fade_steps ) { + int volume; + int fade_step = music_playing->fade_step; + int fade_steps = music_playing->fade_steps; + + if ( music_playing->fading == MIX_FADING_OUT ) { + volume = (music_volume * (fade_steps-fade_step)) / fade_steps; + } else { /* Fading in */ + volume = (music_volume * fade_step) / fade_steps; + } + music_internal_volume(volume); + } else { + if ( music_playing->fading == MIX_FADING_OUT ) { + music_internal_halt(); + if ( music_finished_hook ) { + music_finished_hook(); + } + return; + } + music_playing->fading = MIX_NO_FADING; + } + } + + music_halt_or_loop(); + if (!music_internal_playing()) + return; + + switch (music_playing->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + /* The playing is done externally */ + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + left = WAVStream_PlaySome(stream, len); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + left = modplug_playAudio(music_playing->data.modplug, stream, len); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + left = MOD_playAudio(music_playing->data.module, stream, len); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + /* Native midi is handled asynchronously */ + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_playsome(music_playing->data.fluidsynthmidi, stream, len); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + int samples = len / samplesize; + Timidity_PlaySome(stream, samples); + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + + left = OGG_playAudio(music_playing->data.ogg, stream, len); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + left = FLAC_playAudio(music_playing->data.flac, stream, len); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + left = (len - smpeg.SMPEG_playAudio(music_playing->data.mp3, stream, len)); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + left = mad_getSamples(music_playing->data.mp3_mad, stream, len); + break; +#endif + default: + /* Unknown music type?? */ + break; + } + } + +skip: + /* Handle seamless music looping */ + if (left > 0 && left < len) { + music_halt_or_loop(); + if (music_internal_playing()) + music_mixer(udata, stream+(len-left), left); + } +} + +/* Initialize the music players with a certain desired audio format */ +int open_music(SDL_AudioSpec *mixer) +{ +#ifdef WAV_MUSIC + if ( WAVStream_Init(mixer) == 0 ) { + add_music_decoder("WAVE"); + } +#endif +#ifdef MODPLUG_MUSIC + if ( modplug_init(mixer) == 0 ) { + add_music_decoder("MODPLUG"); + } +#endif +#ifdef MOD_MUSIC + if ( MOD_init(mixer) == 0 ) { + add_music_decoder("MIKMOD"); + } +#endif +#ifdef MID_MUSIC +#ifdef USE_TIMIDITY_MIDI + samplesize = mixer->size / mixer->samples; + if ( Timidity_Init(mixer->freq, mixer->format, + mixer->channels, mixer->samples) == 0 ) { + timidity_ok = 1; + add_music_decoder("TIMIDITY"); + } else { + timidity_ok = 0; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_init(mixer) == 0 ) { + fluidsynth_ok = 1; + add_music_decoder("FLUIDSYNTH"); + } else { + fluidsynth_ok = 0; + } +#endif +#ifdef USE_NATIVE_MIDI +#ifdef USE_FLUIDSYNTH_MIDI + native_midi_ok = !fluidsynth_ok; + if ( native_midi_ok ) +#endif +#ifdef USE_TIMIDITY_MIDI + native_midi_ok = !timidity_ok; + if ( !native_midi_ok ) { + native_midi_ok = (getenv("SDL_NATIVE_MUSIC") != NULL); + } + if ( native_midi_ok ) +#endif + native_midi_ok = native_midi_detect(); + if ( native_midi_ok ) + add_music_decoder("NATIVEMIDI"); +#endif +#endif +#ifdef OGG_MUSIC + if ( OGG_init(mixer) == 0 ) { + add_music_decoder("OGG"); + } +#endif +#ifdef FLAC_MUSIC + if ( FLAC_init(mixer) == 0 ) { + add_music_decoder("FLAC"); + } +#endif +#if defined(MP3_MUSIC) || defined(MP3_MAD_MUSIC) + /* Keep a copy of the mixer */ + used_mixer = *mixer; + add_music_decoder("MP3"); +#endif + + music_playing = NULL; + music_stopped = 0; + Mix_VolumeMusic(SDL_MIX_MAXVOLUME); + + /* Calculate the number of ms for each callback */ + ms_per_step = (int) (((float)mixer->samples * 1000.0) / mixer->freq); + + return(0); +} + +/* Portable case-insensitive string compare function */ +int MIX_string_equals(const char *str1, const char *str2) +{ + while ( *str1 && *str2 ) { + if ( toupper((unsigned char)*str1) != + toupper((unsigned char)*str2) ) + break; + ++str1; + ++str2; + } + return (!*str1 && !*str2); +} + +static int detect_mp3(Uint8 *magic) +{ + if ( strncmp((char *)magic, "ID3", 3) == 0 ) { + return 1; + } + + /* Detection code lifted from SMPEG */ + if(((magic[0] & 0xff) != 0xff) || // No sync bits + ((magic[1] & 0xf0) != 0xf0) || // + ((magic[2] & 0xf0) == 0x00) || // Bitrate is 0 + ((magic[2] & 0xf0) == 0xf0) || // Bitrate is 15 + ((magic[2] & 0x0c) == 0x0c) || // Frequency is 3 + ((magic[1] & 0x06) == 0x00)) { // Layer is 4 + return(0); + } + return 1; +} + +/* MUS_MOD can't be auto-detected. If no other format was detected, MOD is + * assumed and MUS_MOD will be returned, meaning that the format might not + * actually be MOD-based. + * + * Returns MUS_NONE in case of errors. */ +static Mix_MusicType detect_music_type(SDL_RWops *rw) +{ + Uint8 magic[5]; + Uint8 moremagic[9]; + + int start = SDL_RWtell(rw); + if (SDL_RWread(rw, magic, 1, 4) != 4 || SDL_RWread(rw, moremagic, 1, 8) != 8 ) { + Mix_SetError("Couldn't read from RWops"); + return MUS_NONE; + } + SDL_RWseek(rw, start, RW_SEEK_SET); + magic[4]='\0'; + moremagic[8] = '\0'; + + /* WAVE files have the magic four bytes "RIFF" + AIFF files have the magic 12 bytes "FORM" XXXX "AIFF" */ + if (((strcmp((char *)magic, "RIFF") == 0) && (strcmp((char *)(moremagic+4), "WAVE") == 0)) || + (strcmp((char *)magic, "FORM") == 0)) { + return MUS_WAV; + } + + /* Ogg Vorbis files have the magic four bytes "OggS" */ + if (strcmp((char *)magic, "OggS") == 0) { + return MUS_OGG; + } + + /* FLAC files have the magic four bytes "fLaC" */ + if (strcmp((char *)magic, "fLaC") == 0) { + return MUS_FLAC; + } + + /* MIDI files have the magic four bytes "MThd" */ + if (strcmp((char *)magic, "MThd") == 0) { + return MUS_MID; + } + + if (detect_mp3(magic)) { + return MUS_MP3; + } + + /* Assume MOD format. + * + * Apparently there is no way to check if the file is really a MOD, + * or there are too many formats supported by MikMod/ModPlug, or + * MikMod/ModPlug does this check by itself. */ + return MUS_MOD; +} + +/* Load a music file */ +Mix_Music *Mix_LoadMUS(const char *file) +{ + SDL_RWops *rw; + Mix_Music *music; + Mix_MusicType type; + char *ext = strrchr(file, '.'); + +#ifdef CMD_MUSIC + if ( music_cmd ) { + /* Allocate memory for the music structure */ + music = (Mix_Music *)SDL_malloc(sizeof(Mix_Music)); + if ( music == NULL ) { + Mix_SetError("Out of memory"); + return(NULL); + } + music->error = 0; + music->type = MUS_CMD; + music->data.cmd = MusicCMD_LoadSong(music_cmd, file); + if ( music->data.cmd == NULL ) { + SDL_free(music); + music == NULL; + } + return music; + } +#endif + + rw = SDL_RWFromFile(file, "rb"); + if ( rw == NULL ) { + Mix_SetError("Couldn't open '%s'", file); + return NULL; + } + + /* Use the extension as a first guess on the file type */ + type = MUS_NONE; + ext = strrchr(file, '.'); + /* No need to guard these with #ifdef *_MUSIC stuff, + * since we simply call Mix_LoadMUSType_RW() later */ + if ( ext ) { + ++ext; /* skip the dot in the extension */ + if ( MIX_string_equals(ext, "WAV") ) { + type = MUS_WAV; + } else if ( MIX_string_equals(ext, "MID") || + MIX_string_equals(ext, "MIDI") || + MIX_string_equals(ext, "KAR") ) { + type = MUS_MID; + } else if ( MIX_string_equals(ext, "OGG") ) { + type = MUS_OGG; + } else if ( MIX_string_equals(ext, "FLAC") ) { + type = MUS_FLAC; + } else if ( MIX_string_equals(ext, "MPG") || + MIX_string_equals(ext, "MPEG") || + MIX_string_equals(ext, "MP3") || + MIX_string_equals(ext, "MAD") ) { + type = MUS_MP3; + } + } + if ( type == MUS_NONE ) { + type = detect_music_type(rw); + } + + /* We need to know if a specific error occurs; if not, we'll set a + * generic one, so we clear the current one. */ + Mix_SetError(""); + music = Mix_LoadMUSType_RW(rw, type, SDL_TRUE); + if ( music == NULL && Mix_GetError()[0] == '\0' ) { + SDL_FreeRW(rw); + Mix_SetError("Couldn't open '%s'", file); + } + return music; +} + +Mix_Music *Mix_LoadMUS_RW(SDL_RWops *rw) +{ + return Mix_LoadMUSType_RW(rw, MUS_NONE, SDL_FALSE); +} + +Mix_Music *Mix_LoadMUSType_RW(SDL_RWops *rw, Mix_MusicType type, int freesrc) +{ + Mix_Music *music; + + if (!rw) { + Mix_SetError("RWops pointer is NULL"); + return NULL; + } + + /* If the caller wants auto-detection, figure out what kind of file + * this is. */ + if (type == MUS_NONE) { + if ((type = detect_music_type(rw)) == MUS_NONE) { + /* Don't call Mix_SetError() here since detect_music_type() + * does that. */ + return NULL; + } + } + + /* Allocate memory for the music structure */ + music = (Mix_Music *)SDL_malloc(sizeof(Mix_Music)); + if (music == NULL ) { + Mix_SetError("Out of memory"); + return NULL; + } + music->error = 0; + + switch (type) { +#ifdef WAV_MUSIC + case MUS_WAV: + /* The WAVE loader needs the first 4 bytes of the header */ + { + Uint8 magic[5]; + int start = SDL_RWtell(rw); + if (SDL_RWread(rw, magic, 1, 4) != 4) { + Mix_SetError("Couldn't read from RWops"); + return MUS_NONE; + } + SDL_RWseek(rw, start, RW_SEEK_SET); + magic[4] = '\0'; + music->type = MUS_WAV; + music->data.wave = WAVStream_LoadSong_RW(rw, (char *)magic, freesrc); + } + if (music->data.wave == NULL) { + music->error = 1; + } + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + music->type = MUS_OGG; + music->data.ogg = OGG_new_RW(rw, freesrc); + if ( music->data.ogg == NULL ) { + music->error = 1; + } + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + music->type = MUS_FLAC; + music->data.flac = FLAC_new_RW(rw, freesrc); + if ( music->data.flac == NULL ) { + music->error = 1; + } + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + if ( Mix_Init(MIX_INIT_MP3) ) { + SMPEG_Info info; + music->type = MUS_MP3; + music->data.mp3 = smpeg.SMPEG_new_rwops(rw, &info, 0); + if ( !info.has_audio ) { + Mix_SetError("MPEG file does not have any audio stream."); + music->error = 1; + } else { + smpeg.SMPEG_actualSpec(music->data.mp3, &used_mixer); + } + } else { + music->error = 1; + } + break; +#elif defined(MP3_MAD_MUSIC) + case MUS_MP3: + music->type = MUS_MP3_MAD; + music->data.mp3_mad = mad_openFileRW(rw, &used_mixer, freesrc); + if (music->data.mp3_mad == 0) { + Mix_SetError("Could not initialize MPEG stream."); + music->error = 1; + } + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: + music->type = MUS_MID; +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + music->data.nativemidi = native_midi_loadsong_RW(rw, freesrc); + if ( music->data.nativemidi == NULL ) { + Mix_SetError("%s", native_midi_error()); + music->error = 1; + } + break; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + music->data.fluidsynthmidi = fluidsynth_loadsong_RW(rw, freesrc); + if ( music->data.fluidsynthmidi == NULL ) { + music->error = 1; + } + break; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + music->data.midi = Timidity_LoadSong_RW(rw, freesrc); + if ( music->data.midi == NULL ) { + Mix_SetError("%s", Timidity_Error()); + music->error = 1; + } + } else { + Mix_SetError("%s", Timidity_Error()); + music->error = 1; + } +#endif + break; +#endif +#if defined(MODPLUG_MUSIC) || defined(MOD_MUSIC) + case MUS_MOD: + music->error = 1; +#ifdef MODPLUG_MUSIC + if ( music->error ) { + music->type = MUS_MODPLUG; + music->data.modplug = modplug_new_RW(rw, freesrc); + if ( music->data.modplug ) { + music->error = 0; + } + } +#endif +#ifdef MOD_MUSIC + if ( music->error ) { + music->type = MUS_MOD; + music->data.module = MOD_new_RW(rw, freesrc); + if ( music->data.module ) { + music->error = 0; + } + } +#endif + break; +#endif + + default: + Mix_SetError("Unrecognized music format"); + music->error=1; + } /* switch (want) */ + + + if (music->error) { + SDL_free(music); + music=NULL; + } + return(music); +} + +/* Free a music chunk previously loaded */ +void Mix_FreeMusic(Mix_Music *music) +{ + if ( music ) { + /* Stop the music if it's currently playing */ + SDL_LockAudio(); + if ( music == music_playing ) { + /* Wait for any fade out to finish */ + while ( music->fading == MIX_FADING_OUT ) { + SDL_UnlockAudio(); + SDL_Delay(100); + SDL_LockAudio(); + } + if ( music == music_playing ) { + music_internal_halt(); + } + } + SDL_UnlockAudio(); + switch (music->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + MusicCMD_FreeSong(music->data.cmd); + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_FreeSong(music->data.wave); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + modplug_delete(music->data.modplug); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_delete(music->data.module); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + native_midi_freesong(music->data.nativemidi); + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_freesong(music->data.fluidsynthmidi); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + Timidity_FreeSong(music->data.midi); + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_delete(music->data.ogg); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_delete(music->data.flac); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_delete(music->data.mp3); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_closeFile(music->data.mp3_mad); + break; +#endif + default: + /* Unknown music type?? */ + break; + } + + skip: + SDL_free(music); + } +} + +/* Find out the music format of a mixer music, or the currently playing + music, if 'music' is NULL. +*/ +Mix_MusicType Mix_GetMusicType(const Mix_Music *music) +{ + Mix_MusicType type = MUS_NONE; + + if ( music ) { + type = music->type; + } else { + SDL_LockAudio(); + if ( music_playing ) { + type = music_playing->type; + } + SDL_UnlockAudio(); + } + return(type); +} + +/* Play a music chunk. Returns 0, or -1 if there was an error. + */ +static int music_internal_play(Mix_Music *music, double position) +{ + int retval = 0; + +#if defined(__MACOSX__) && defined(USE_NATIVE_MIDI) + /* This fixes a bug with native MIDI on Mac OS X, where you + can't really stop and restart MIDI from the audio callback. + */ + if ( music == music_playing && music->type == MUS_MID && native_midi_ok ) { + /* Just a seek suffices to restart playing */ + music_internal_position(position); + return 0; + } +#endif + + /* Note the music we're playing */ + if ( music_playing ) { + music_internal_halt(); + } + music_playing = music; + + /* Set the initial volume */ + if ( music->type != MUS_MOD ) { + music_internal_initialize_volume(); + } + + /* Set up for playback */ + switch (music->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + MusicCMD_Start(music->data.cmd); + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_Start(music->data.wave); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + /* can't set volume until file is loaded, so finally set it now */ + music_internal_initialize_volume(); + modplug_play(music->data.modplug); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_play(music->data.module); + /* Player_SetVolume() does nothing before Player_Start() */ + music_internal_initialize_volume(); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + native_midi_start(music->data.nativemidi, music_loops); + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if (fluidsynth_ok ) { + fluidsynth_start(music->data.fluidsynthmidi); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + Timidity_Start(music->data.midi); + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_play(music->data.ogg); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_play(music->data.flac); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_enableaudio(music->data.mp3,1); + smpeg.SMPEG_enablevideo(music->data.mp3,0); + smpeg.SMPEG_play(music_playing->data.mp3); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_start(music->data.mp3_mad); + break; +#endif + default: + Mix_SetError("Can't play unknown music type"); + retval = -1; + break; + } + +skip: + /* Set the playback position, note any errors if an offset is used */ + if ( retval == 0 ) { + if ( position > 0.0 ) { + if ( music_internal_position(position) < 0 ) { + Mix_SetError("Position not implemented for music type"); + retval = -1; + } + } else { + music_internal_position(0.0); + } + } + + /* If the setup failed, we're not playing any music anymore */ + if ( retval < 0 ) { + music_playing = NULL; + } + return(retval); +} +int Mix_FadeInMusicPos(Mix_Music *music, int loops, int ms, double position) +{ + int retval; + + if ( ms_per_step == 0 ) { + SDL_SetError("Audio device hasn't been opened"); + return(-1); + } + + /* Don't play null pointers :-) */ + if ( music == NULL ) { + Mix_SetError("music parameter was NULL"); + return(-1); + } + + /* Setup the data */ + if ( ms ) { + music->fading = MIX_FADING_IN; + } else { + music->fading = MIX_NO_FADING; + } + music->fade_step = 0; + music->fade_steps = ms/ms_per_step; + + /* Play the puppy */ + SDL_LockAudio(); + /* If the current music is fading out, wait for the fade to complete */ + while ( music_playing && (music_playing->fading == MIX_FADING_OUT) ) { + SDL_UnlockAudio(); + SDL_Delay(100); + SDL_LockAudio(); + } + music_active = 1; + if (loops == 1) { + /* Loop is the number of times to play the audio */ + loops = 0; + } + music_loops = loops; + retval = music_internal_play(music, position); + SDL_UnlockAudio(); + + return(retval); +} +int Mix_FadeInMusic(Mix_Music *music, int loops, int ms) +{ + return Mix_FadeInMusicPos(music, loops, ms, 0.0); +} +int Mix_PlayMusic(Mix_Music *music, int loops) +{ + return Mix_FadeInMusicPos(music, loops, 0, 0.0); +} + +/* Set the playing music position */ +int music_internal_position(double position) +{ + int retval = 0; + + switch (music_playing->type) { +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + modplug_jump_to_time(music_playing->data.modplug, position); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_jump_to_time(music_playing->data.module, position); + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_jump_to_time(music_playing->data.ogg, position); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_jump_to_time(music_playing->data.flac, position); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_rewind(music_playing->data.mp3); + smpeg.SMPEG_play(music_playing->data.mp3); + if ( position > 0.0 ) { + smpeg.SMPEG_skip(music_playing->data.mp3, (float)position); + } + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_seek(music_playing->data.mp3_mad, position); + break; +#endif + default: + /* TODO: Implement this for other music backends */ + retval = -1; + break; + } + return(retval); +} +int Mix_SetMusicPosition(double position) +{ + int retval; + + SDL_LockAudio(); + if ( music_playing ) { + retval = music_internal_position(position); + if ( retval < 0 ) { + Mix_SetError("Position not implemented for music type"); + } + } else { + Mix_SetError("Music isn't playing"); + retval = -1; + } + SDL_UnlockAudio(); + + return(retval); +} + +/* Set the music's initial volume */ +static void music_internal_initialize_volume(void) +{ + if ( music_playing->fading == MIX_FADING_IN ) { + music_internal_volume(0); + } else { + music_internal_volume(music_volume); + } +} + +/* Set the music volume */ +static void music_internal_volume(int volume) +{ + switch (music_playing->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + MusicCMD_SetVolume(volume); + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_SetVolume(volume); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + modplug_setvolume(music_playing->data.modplug, volume); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_setvolume(music_playing->data.module, volume); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + native_midi_setvolume(volume); + return; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_setvolume(music_playing->data.fluidsynthmidi, volume); + return; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + Timidity_SetVolume(volume); + return; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_setvolume(music_playing->data.ogg, volume); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_setvolume(music_playing->data.flac, volume); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_setvolume(music_playing->data.mp3,(int)(((float)volume/(float)MIX_MAX_VOLUME)*100.0)); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_setVolume(music_playing->data.mp3_mad, volume); + break; +#endif + default: + /* Unknown music type?? */ + break; + } +} +int Mix_VolumeMusic(int volume) +{ + int prev_volume; + + prev_volume = music_volume; + if ( volume < 0 ) { + return prev_volume; + } + if ( volume > SDL_MIX_MAXVOLUME ) { + volume = SDL_MIX_MAXVOLUME; + } + music_volume = volume; + SDL_LockAudio(); + if ( music_playing ) { + music_internal_volume(music_volume); + } + SDL_UnlockAudio(); + return(prev_volume); +} + +/* Halt playing of music */ +static void music_internal_halt(void) +{ + switch (music_playing->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + MusicCMD_Stop(music_playing->data.cmd); + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + WAVStream_Stop(); + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + modplug_stop(music_playing->data.modplug); + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + MOD_stop(music_playing->data.module); + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + native_midi_stop(); + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + fluidsynth_stop(music_playing->data.fluidsynthmidi); + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + Timidity_Stop(); + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + OGG_stop(music_playing->data.ogg); + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + FLAC_stop(music_playing->data.flac); + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + smpeg.SMPEG_stop(music_playing->data.mp3); + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + mad_stop(music_playing->data.mp3_mad); + break; +#endif + default: + /* Unknown music type?? */ + return; + } + +skip: + music_playing->fading = MIX_NO_FADING; + music_playing = NULL; +} +int Mix_HaltMusic(void) +{ + SDL_LockAudio(); + if ( music_playing ) { + music_internal_halt(); + } + SDL_UnlockAudio(); + + return(0); +} + +/* Progressively stop the music */ +int Mix_FadeOutMusic(int ms) +{ + int retval = 0; + + if ( ms_per_step == 0 ) { + SDL_SetError("Audio device hasn't been opened"); + return 0; + } + + if (ms <= 0) { /* just halt immediately. */ + Mix_HaltMusic(); + return 1; + } + + SDL_LockAudio(); + if ( music_playing) { + int fade_steps = (ms + ms_per_step - 1)/ms_per_step; + if ( music_playing->fading == MIX_NO_FADING ) { + music_playing->fade_step = 0; + } else { + int step; + int old_fade_steps = music_playing->fade_steps; + if ( music_playing->fading == MIX_FADING_OUT ) { + step = music_playing->fade_step; + } else { + step = old_fade_steps + - music_playing->fade_step + 1; + } + music_playing->fade_step = (step * fade_steps) + / old_fade_steps; + } + music_playing->fading = MIX_FADING_OUT; + music_playing->fade_steps = fade_steps; + retval = 1; + } + SDL_UnlockAudio(); + + return(retval); +} + +Mix_Fading Mix_FadingMusic(void) +{ + Mix_Fading fading = MIX_NO_FADING; + + SDL_LockAudio(); + if ( music_playing ) { + fading = music_playing->fading; + } + SDL_UnlockAudio(); + + return(fading); +} + +/* Pause/Resume the music stream */ +void Mix_PauseMusic(void) +{ + music_active = 0; +} + +void Mix_ResumeMusic(void) +{ + music_active = 1; +} + +void Mix_RewindMusic(void) +{ + Mix_SetMusicPosition(0.0); +} + +int Mix_PausedMusic(void) +{ + return (music_active == 0); +} + +/* Check the status of the music */ +static int music_internal_playing() +{ + int playing = 1; + + if (music_playing == NULL) { + return 0; + } + + switch (music_playing->type) { +#ifdef CMD_MUSIC + case MUS_CMD: + if (!MusicCMD_Active(music_playing->data.cmd)) { + playing = 0; + } + break; +#endif +#ifdef WAV_MUSIC + case MUS_WAV: + if ( ! WAVStream_Active() ) { + playing = 0; + } + break; +#endif +#ifdef MODPLUG_MUSIC + case MUS_MODPLUG: + if ( ! modplug_playing(music_playing->data.modplug) ) { + playing = 0; + } + break; +#endif +#ifdef MOD_MUSIC + case MUS_MOD: + if ( ! MOD_playing(music_playing->data.module) ) { + playing = 0; + } + break; +#endif +#ifdef MID_MUSIC + case MUS_MID: +#ifdef USE_NATIVE_MIDI + if ( native_midi_ok ) { + if ( ! native_midi_active() ) + playing = 0; + goto skip; + } +#endif +#ifdef USE_FLUIDSYNTH_MIDI + if ( fluidsynth_ok ) { + if ( ! fluidsynth_active(music_playing->data.fluidsynthmidi) ) + playing = 0; + goto skip; + } +#endif +#ifdef USE_TIMIDITY_MIDI + if ( timidity_ok ) { + if ( ! Timidity_Active() ) + playing = 0; + goto skip; + } +#endif + break; +#endif +#ifdef OGG_MUSIC + case MUS_OGG: + if ( ! OGG_playing(music_playing->data.ogg) ) { + playing = 0; + } + break; +#endif +#ifdef FLAC_MUSIC + case MUS_FLAC: + if ( ! FLAC_playing(music_playing->data.flac) ) { + playing = 0; + } + break; +#endif +#ifdef MP3_MUSIC + case MUS_MP3: + if ( smpeg.SMPEG_status(music_playing->data.mp3) != SMPEG_PLAYING ) + playing = 0; + break; +#endif +#ifdef MP3_MAD_MUSIC + case MUS_MP3_MAD: + if (!mad_isPlaying(music_playing->data.mp3_mad)) { + playing = 0; + } + break; +#endif + default: + playing = 0; + break; + } + +skip: + return(playing); +} +int Mix_PlayingMusic(void) +{ + int playing = 0; + + SDL_LockAudio(); + if ( music_playing ) { + playing = music_loops || music_internal_playing(); + } + SDL_UnlockAudio(); + + return(playing); +} + +/* Set the external music playback command */ +int Mix_SetMusicCMD(const char *command) +{ + Mix_HaltMusic(); + if ( music_cmd ) { + SDL_free(music_cmd); + music_cmd = NULL; + } + if ( command ) { + music_cmd = (char *)SDL_malloc(strlen(command)+1); + if ( music_cmd == NULL ) { + return(-1); + } + strcpy(music_cmd, command); + } + return(0); +} + +int Mix_SetSynchroValue(int i) +{ + /* Not supported by any players at this time */ + return(-1); +} + +int Mix_GetSynchroValue(void) +{ + /* Not supported by any players at this time */ + return(-1); +} + + +/* Uninitialize the music players */ +void close_music(void) +{ + Mix_HaltMusic(); +#ifdef CMD_MUSIC + Mix_SetMusicCMD(NULL); +#endif +#ifdef MODPLUG_MUSIC + modplug_exit(); +#endif +#ifdef MOD_MUSIC + MOD_exit(); +#endif +#ifdef MID_MUSIC +# ifdef USE_TIMIDITY_MIDI + Timidity_Close(); +# endif +#endif + + /* rcg06042009 report available decoders at runtime. */ + SDL_free(music_decoders); + music_decoders = NULL; + num_decoders = 0; + + ms_per_step = 0; +} + +int Mix_SetSoundFonts(const char *paths) +{ +#ifdef MID_MUSIC + if (soundfont_paths) { + SDL_free(soundfont_paths); + soundfont_paths = NULL; + } + + if (paths) { + if (!(soundfont_paths = SDL_strdup(paths))) { + Mix_SetError("Insufficient memory to set SoundFonts"); + return 0; + } + } +#endif + return 1; +} + +#ifdef MID_MUSIC +const char* Mix_GetSoundFonts(void) +{ + const char* force = getenv("SDL_FORCE_SOUNDFONTS"); + + if (!soundfont_paths || (force && force[0] == '1')) { + return getenv("SDL_SOUNDFONTS"); + } else { + return soundfont_paths; + } +} + +int Mix_EachSoundFont(int (*function)(const char*, void*), void *data) +{ + char *context, *path, *paths; + const char* cpaths = Mix_GetSoundFonts(); + + if (!cpaths) { + Mix_SetError("No SoundFonts have been requested"); + return 0; + } + + if (!(paths = SDL_strdup(cpaths))) { + Mix_SetError("Insufficient memory to iterate over SoundFonts"); + return 0; + } + +#if defined(__MINGW32__) || defined(__MINGW64__) + for (path = strtok(paths, ";"); path; path = strtok(NULL, ";")) { +#elif defined(_WIN32) + for (path = strtok_s(paths, ";", &context); path; path = strtok_s(NULL, ";", &context)) { +#else + for (path = strtok_r(paths, ":;", &context); path; path = strtok_r(NULL, ":;", &context)) { +#endif + if (!function(path, data)) { + SDL_free(paths); + return 0; + } + } + + SDL_free(paths); + return 1; +} +#endif diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/music_ogg.c b/contrib/sdk/sources/SDL_mixer-1.2.12/music_ogg.c new file mode 100644 index 0000000000..18a094b2ec --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/music_ogg.c @@ -0,0 +1,234 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifdef OGG_MUSIC + +/* This file supports Ogg Vorbis music streams */ + +#include +#include +#include + +#include "SDL_mixer.h" +#include "dynamic_ogg.h" +#include "music_ogg.h" + +/* This is the format of the audio mixer data */ +static SDL_AudioSpec mixer; + +/* Initialize the Ogg Vorbis player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +int OGG_init(SDL_AudioSpec *mixerfmt) +{ + mixer = *mixerfmt; + return(0); +} + +/* Set the volume for an OGG stream */ +void OGG_setvolume(OGG_music *music, int volume) +{ + music->volume = volume; +} + +static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) +{ + return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb); +} + +static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence) +{ + return SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence); +} + +static long sdl_tell_func(void *datasource) +{ + return SDL_RWtell((SDL_RWops*)datasource); +} + +/* Load an OGG stream from an SDL_RWops object */ +OGG_music *OGG_new_RW(SDL_RWops *rw, int freerw) +{ + OGG_music *music; + ov_callbacks callbacks; + + if ( !Mix_Init(MIX_INIT_OGG) ) { + if ( freerw ) { + SDL_RWclose(rw); + } + return(NULL); + } + + SDL_memset(&callbacks, 0, sizeof(callbacks)); + callbacks.read_func = sdl_read_func; + callbacks.seek_func = sdl_seek_func; + callbacks.tell_func = sdl_tell_func; + + music = (OGG_music *)SDL_malloc(sizeof *music); + if ( music ) { + /* Initialize the music structure */ + memset(music, 0, (sizeof *music)); + music->rw = rw; + music->freerw = freerw; + OGG_stop(music); + OGG_setvolume(music, MIX_MAX_VOLUME); + music->section = -1; + + if ( vorbis.ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) { + SDL_free(music); + if ( freerw ) { + SDL_RWclose(rw); + } + SDL_SetError("Not an Ogg Vorbis audio stream"); + return(NULL); + } + } else { + if ( freerw ) { + SDL_RWclose(rw); + } + SDL_OutOfMemory(); + return(NULL); + } + return(music); +} + +/* Start playback of a given OGG stream */ +void OGG_play(OGG_music *music) +{ + music->playing = 1; +} + +/* Return non-zero if a stream is currently playing */ +int OGG_playing(OGG_music *music) +{ + return(music->playing); +} + +/* Read some Ogg stream data and convert it for output */ +static void OGG_getsome(OGG_music *music) +{ + int section; + int len; + char data[4096]; + SDL_AudioCVT *cvt; + +#ifdef OGG_USE_TREMOR + len = vorbis.ov_read(&music->vf, data, sizeof(data), §ion); +#else + len = vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, §ion); +#endif + if ( len <= 0 ) { + if ( len == 0 ) { + music->playing = 0; + } + return; + } + cvt = &music->cvt; + if ( section != music->section ) { + vorbis_info *vi; + + vi = vorbis.ov_info(&music->vf, -1); + SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate, + mixer.format,mixer.channels,mixer.freq); + if ( cvt->buf ) { + SDL_free(cvt->buf); + } + cvt->buf = (Uint8 *)SDL_malloc(sizeof(data)*cvt->len_mult); + music->section = section; + } + if ( cvt->buf ) { + memcpy(cvt->buf, data, len); + if ( cvt->needed ) { + cvt->len = len; + SDL_ConvertAudio(cvt); + } else { + cvt->len_cvt = len; + } + music->len_available = music->cvt.len_cvt; + music->snd_available = music->cvt.buf; + } else { + SDL_SetError("Out of memory"); + music->playing = 0; + } +} + +/* Play some of a stream previously started with OGG_play() */ +int OGG_playAudio(OGG_music *music, Uint8 *snd, int len) +{ + int mixable; + + while ( (len > 0) && music->playing ) { + if ( ! music->len_available ) { + OGG_getsome(music); + } + mixable = len; + if ( mixable > music->len_available ) { + mixable = music->len_available; + } + if ( music->volume == MIX_MAX_VOLUME ) { + memcpy(snd, music->snd_available, mixable); + } else { + SDL_MixAudio(snd, music->snd_available, mixable, + music->volume); + } + music->len_available -= mixable; + music->snd_available += mixable; + len -= mixable; + snd += mixable; + } + + return len; +} + +/* Stop playback of a stream previously started with OGG_play() */ +void OGG_stop(OGG_music *music) +{ + music->playing = 0; +} + +/* Close the given OGG stream */ +void OGG_delete(OGG_music *music) +{ + if ( music ) { + if ( music->cvt.buf ) { + SDL_free(music->cvt.buf); + } + if ( music->freerw ) { + SDL_RWclose(music->rw); + } + vorbis.ov_clear(&music->vf); + SDL_free(music); + } +} + +/* Jump (seek) to a given position (time is in seconds) */ +void OGG_jump_to_time(OGG_music *music, double time) +{ +#ifdef OGG_USE_TREMOR + vorbis.ov_time_seek( &music->vf, (ogg_int64_t)time ); +#else + vorbis.ov_time_seek( &music->vf, time ); +#endif +} + +#endif /* OGG_MUSIC */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/music_ogg.h b/contrib/sdk/sources/SDL_mixer-1.2.12/music_ogg.h new file mode 100644 index 0000000000..4d93a2bc62 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/music_ogg.h @@ -0,0 +1,75 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +#ifdef OGG_MUSIC + +/* This file supports Ogg Vorbis music streams */ + +#ifdef OGG_USE_TREMOR +#include +#else +#include +#endif + +typedef struct { + SDL_RWops *rw; + int freerw; + int playing; + int volume; + OggVorbis_File vf; + int section; + SDL_AudioCVT cvt; + int len_available; + Uint8 *snd_available; +} OGG_music; + +/* Initialize the Ogg Vorbis player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +extern int OGG_init(SDL_AudioSpec *mixer); + +/* Set the volume for an OGG stream */ +extern void OGG_setvolume(OGG_music *music, int volume); + +/* Load an OGG stream from an SDL_RWops object */ +extern OGG_music *OGG_new_RW(SDL_RWops *rw, int freerw); + +/* Start playback of a given OGG stream */ +extern void OGG_play(OGG_music *music); + +/* Return non-zero if a stream is currently playing */ +extern int OGG_playing(OGG_music *music); + +/* Play some of a stream previously started with OGG_play() */ +extern int OGG_playAudio(OGG_music *music, Uint8 *stream, int len); + +/* Stop playback of a stream previously started with OGG_play() */ +extern void OGG_stop(OGG_music *music); + +/* Close the given OGG stream */ +extern void OGG_delete(OGG_music *music); + +/* Jump (seek) to a given position (time is in seconds) */ +extern void OGG_jump_to_time(OGG_music *music, double time); + +#endif /* OGG_MUSIC */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi.h b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi.h new file mode 100644 index 0000000000..17e4769d86 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi.h @@ -0,0 +1,38 @@ +/* + native_midi: Hardware Midi support for the SDL_mixer library + Copyright (C) 2000 Florian 'Proff' Schulze + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _NATIVE_MIDI_H_ +#define _NATIVE_MIDI_H_ + +#include + +typedef struct _NativeMidiSong NativeMidiSong; + +int native_midi_detect(); +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw); +void native_midi_freesong(NativeMidiSong *song); +void native_midi_start(NativeMidiSong *song, int loops); +void native_midi_stop(); +int native_midi_active(); +void native_midi_setvolume(int volume); +const char *native_midi_error(void); + +#endif /* _NATIVE_MIDI_H_ */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_common.c b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_common.c new file mode 100644 index 0000000000..12294750f8 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_common.c @@ -0,0 +1,409 @@ +/* + native_midi: Hardware Midi support for the SDL_mixer library + Copyright (C) 2000,2001 Florian 'Proff' Schulze + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + + +#include "native_midi_common.h" + +#include "../SDL_mixer.h" + +#include +#include +#include + + +/* The maximum number of midi tracks that we can handle +#define MIDI_TRACKS 32 */ + + +/* A single midi track as read from the midi file */ +typedef struct +{ + Uint8 *data; /* MIDI message stream */ + int len; /* length of the track data */ +} MIDITrack; + +/* A midi file, stripped down to the absolute minimum - divison & track data */ +typedef struct +{ + int division; /* number of pulses per quarter note (ppqn) */ + int nTracks; /* number of tracks */ + MIDITrack *track; /* tracks */ +} MIDIFile; + + +/* Some macros that help us stay endianess-independant */ +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define BE_SHORT(x) (x) +#define BE_LONG(x) (x) +#else +#define BE_SHORT(x) ((((x)&0xFF)<<8) | (((x)>>8)&0xFF)) +#define BE_LONG(x) ((((x)&0x0000FF)<<24) | \ + (((x)&0x00FF00)<<8) | \ + (((x)&0xFF0000)>>8) | \ + (((x)>>24)&0xFF)) +#endif + + + +/* Get Variable Length Quantity */ +static int GetVLQ(MIDITrack *track, int *currentPos) +{ + int l = 0; + Uint8 c; + while(1) + { + c = track->data[*currentPos]; + (*currentPos)++; + l += (c & 0x7f); + if (!(c & 0x80)) + return l; + l <<= 7; + } +} + +/* Create a single MIDIEvent */ +static MIDIEvent *CreateEvent(Uint32 time, Uint8 event, Uint8 a, Uint8 b) +{ + MIDIEvent *newEvent; + + newEvent = calloc(1, sizeof(MIDIEvent)); + + if (newEvent) + { + newEvent->time = time; + newEvent->status = event; + newEvent->data[0] = a; + newEvent->data[1] = b; + } + else + Mix_SetError("Out of memory"); + + return newEvent; +} + +/* Convert a single midi track to a list of MIDIEvents */ +static MIDIEvent *MIDITracktoStream(MIDITrack *track) +{ + Uint32 atime = 0; + Uint32 len = 0; + Uint8 event,type,a,b; + Uint8 laststatus = 0; + Uint8 lastchan = 0; + int currentPos = 0; + int end = 0; + MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */ + MIDIEvent *currentEvent = head; + + while (!end) + { + if (currentPos >= track->len) + break; /* End of data stream reached */ + + atime += GetVLQ(track, ¤tPos); + event = track->data[currentPos++]; + + /* Handle SysEx seperatly */ + if (((event>>4) & 0x0F) == MIDI_STATUS_SYSEX) + { + if (event == 0xFF) + { + type = track->data[currentPos]; + currentPos++; + switch(type) + { + case 0x2f: /* End of data marker */ + end = 1; + case 0x51: /* Tempo change */ + /* + a=track->data[currentPos]; + b=track->data[currentPos+1]; + c=track->data[currentPos+2]; + AddEvent(song, atime, MEVT_TEMPO, c, b, a); + */ + break; + } + } + else + type = 0; + + len = GetVLQ(track, ¤tPos); + + /* Create an event and attach the extra data, if any */ + currentEvent->next = CreateEvent(atime, event, type, 0); + currentEvent = currentEvent->next; + if (NULL == currentEvent) + { + FreeMIDIEventList(head); + return NULL; + } + if (len) + { + currentEvent->extraLen = len; + currentEvent->extraData = malloc(len); + memcpy(currentEvent->extraData, &(track->data[currentPos]), len); + currentPos += len; + } + } + else + { + a = event; + if (a & 0x80) /* It's a status byte */ + { + /* Extract channel and status information */ + lastchan = a & 0x0F; + laststatus = (a>>4) & 0x0F; + + /* Read the next byte which should always be a data byte */ + a = track->data[currentPos++] & 0x7F; + } + switch(laststatus) + { + case MIDI_STATUS_NOTE_OFF: + case MIDI_STATUS_NOTE_ON: /* Note on */ + case MIDI_STATUS_AFTERTOUCH: /* Key Pressure */ + case MIDI_STATUS_CONTROLLER: /* Control change */ + case MIDI_STATUS_PITCH_WHEEL: /* Pitch wheel */ + b = track->data[currentPos++] & 0x7F; + currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, b); + currentEvent = currentEvent->next; + if (NULL == currentEvent) + { + FreeMIDIEventList(head); + return NULL; + } + break; + + case MIDI_STATUS_PROG_CHANGE: /* Program change */ + case MIDI_STATUS_PRESSURE: /* Channel pressure */ + a &= 0x7f; + currentEvent->next = CreateEvent(atime, (Uint8)((laststatus<<4)+lastchan), a, 0); + currentEvent = currentEvent->next; + if (NULL == currentEvent) + { + FreeMIDIEventList(head); + return NULL; + } + break; + + default: /* Sysex already handled above */ + break; + } + } + } + + currentEvent = head->next; + free(head); /* release the dummy head event */ + return currentEvent; +} + +/* + * Convert a midi song, consisting of up to 32 tracks, to a list of MIDIEvents. + * To do so, first convert the tracks seperatly, then interweave the resulting + * MIDIEvent-Lists to one big list. + */ +static MIDIEvent *MIDItoStream(MIDIFile *mididata) +{ + MIDIEvent **track; + MIDIEvent *head = CreateEvent(0,0,0,0); /* dummy event to make handling the list easier */ + MIDIEvent *currentEvent = head; + int trackID; + + if (NULL == head) + return NULL; + + track = (MIDIEvent**) calloc(1, sizeof(MIDIEvent*) * mididata->nTracks); + if (NULL == head) + return NULL; + + /* First, convert all tracks to MIDIEvent lists */ + for (trackID = 0; trackID < mididata->nTracks; trackID++) + track[trackID] = MIDITracktoStream(&mididata->track[trackID]); + + /* Now, merge the lists. */ + /* TODO */ + while(1) + { + Uint32 lowestTime = INT_MAX; + int currentTrackID = -1; + + /* Find the next event */ + for (trackID = 0; trackID < mididata->nTracks; trackID++) + { + if (track[trackID] && (track[trackID]->time < lowestTime)) + { + currentTrackID = trackID; + lowestTime = track[currentTrackID]->time; + } + } + + /* Check if we processes all events */ + if (currentTrackID == -1) + break; + + currentEvent->next = track[currentTrackID]; + track[currentTrackID] = track[currentTrackID]->next; + + currentEvent = currentEvent->next; + + + lowestTime = 0; + } + + /* Make sure the list is properly terminated */ + currentEvent->next = 0; + + currentEvent = head->next; + free(track); + free(head); /* release the dummy head event */ + return currentEvent; +} + +static int ReadMIDIFile(MIDIFile *mididata, SDL_RWops *rw) +{ + int i = 0; + Uint32 ID; + Uint32 size; + Uint16 format; + Uint16 tracks; + Uint16 division; + + if (!mididata) + return 0; + if (!rw) + return 0; + + /* Make sure this is really a MIDI file */ + SDL_RWread(rw, &ID, 1, 4); + if (BE_LONG(ID) != 'MThd') + return 0; + + /* Header size must be 6 */ + SDL_RWread(rw, &size, 1, 4); + size = BE_LONG(size); + if (size != 6) + return 0; + + /* We only support format 0 and 1, but not 2 */ + SDL_RWread(rw, &format, 1, 2); + format = BE_SHORT(format); + if (format != 0 && format != 1) + return 0; + + SDL_RWread(rw, &tracks, 1, 2); + tracks = BE_SHORT(tracks); + mididata->nTracks = tracks; + + /* Allocate tracks */ + mididata->track = (MIDITrack*) calloc(1, sizeof(MIDITrack) * mididata->nTracks); + if (NULL == mididata->track) + { + Mix_SetError("Out of memory"); + goto bail; + } + + /* Retrieve the PPQN value, needed for playback */ + SDL_RWread(rw, &division, 1, 2); + mididata->division = BE_SHORT(division); + + + for (i=0; itrack[i].len = size; + mididata->track[i].data = malloc(size); + if (NULL == mididata->track[i].data) + { + Mix_SetError("Out of memory"); + goto bail; + } + SDL_RWread(rw, mididata->track[i].data, 1, size); + } + return 1; + +bail: + for(;i >= 0; i--) + { + if (mididata->track[i].data) + free(mididata->track[i].data); + } + + return 0; +} + +MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division) +{ + MIDIFile *mididata = NULL; + MIDIEvent *eventList; + int trackID; + + mididata = calloc(1, sizeof(MIDIFile)); + if (!mididata) + return NULL; + + /* Open the file */ + if ( rw != NULL ) + { + /* Read in the data */ + if ( ! ReadMIDIFile(mididata, rw)) + { + free(mididata); + return NULL; + } + } + else + { + free(mididata); + return NULL; + } + + if (division) + *division = mididata->division; + + eventList = MIDItoStream(mididata); + + for(trackID = 0; trackID < mididata->nTracks; trackID++) + { + if (mididata->track[trackID].data) + free(mididata->track[trackID].data); + } + free(mididata->track); + free(mididata); + + return eventList; +} + +void FreeMIDIEventList(MIDIEvent *head) +{ + MIDIEvent *cur, *next; + + cur = head; + + while (cur) + { + next = cur->next; + if (cur->extraData) + free (cur->extraData); + free (cur); + cur = next; + } +} diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_common.h b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_common.h new file mode 100644 index 0000000000..e0400272d8 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_common.h @@ -0,0 +1,63 @@ +/* + native_midi: Hardware Midi support for the SDL_mixer library + Copyright (C) 2000,2001 Florian 'Proff' Schulze + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _NATIVE_MIDI_COMMON_H_ +#define _NATIVE_MIDI_COMMON_H_ + +#include "SDL.h" + +/* Midi Status Bytes */ +#define MIDI_STATUS_NOTE_OFF 0x8 +#define MIDI_STATUS_NOTE_ON 0x9 +#define MIDI_STATUS_AFTERTOUCH 0xA +#define MIDI_STATUS_CONTROLLER 0xB +#define MIDI_STATUS_PROG_CHANGE 0xC +#define MIDI_STATUS_PRESSURE 0xD +#define MIDI_STATUS_PITCH_WHEEL 0xE +#define MIDI_STATUS_SYSEX 0xF + +/* We store the midi events in a linked list; this way it is + easy to shuffle the tracks together later on; and we are + flexible in the size of each elemnt. + */ +typedef struct MIDIEvent +{ + Uint32 time; /* Time at which this midi events occurs */ + Uint8 status; /* Status byte */ + Uint8 data[2]; /* 1 or 2 bytes additional data for most events */ + + Uint32 extraLen; /* For some SysEx events, we need additional storage */ + Uint8 *extraData; + + struct MIDIEvent *next; +} MIDIEvent; + + +/* Load a midifile to memory, converting it to a list of MIDIEvents. + This function returns a linked lists of MIDIEvents, 0 if an error occured. + */ +MIDIEvent *CreateMIDIEventList(SDL_RWops *rw, Uint16 *division); + +/* Release a MIDIEvent list after usage. */ +void FreeMIDIEventList(MIDIEvent *head); + + +#endif /* _NATIVE_MIDI_COMMON_H_ */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_haiku.cpp b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_haiku.cpp new file mode 100644 index 0000000000..8de350e80a --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_haiku.cpp @@ -0,0 +1,281 @@ +/* + native_midi_haiku: Native Midi support on Haiku for the SDL_mixer library + Copyright (C) 2010 Egor Suvorov + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +#ifdef __HAIKU__ +#include +#include +#include +#include +#include +#include +#include +#include +extern "C" { +#include "native_midi.h" +#include "native_midi_common.h" +} + +bool compareMIDIEvent(const MIDIEvent &a, const MIDIEvent &b) +{ + return a.time < b.time; +} + +class MidiEventsStore : public BMidi +{ + public: + MidiEventsStore() + { + fPlaying = false; + fLoops = 0; + } + virtual status_t Import(SDL_RWops *rw) + { + fEvs = CreateMIDIEventList(rw, &fDivision); + if (!fEvs) { + return B_BAD_MIDI_DATA; + } + fTotal = 0; + for (MIDIEvent *x = fEvs; x; x = x->next) fTotal++; + fPos = fTotal; + + sort_events(); + return B_OK; + } + virtual void Run() + { + fPlaying = true; + fPos = 0; + MIDIEvent *ev = fEvs; + + uint32 startTime = B_NOW; + while (KeepRunning()) + { + if (!ev) { + if (fLoops && fEvs) { + --fLoops; + fPos = 0; + ev = fEvs; + } else + break; + } + SprayEvent(ev, ev->time + startTime); + ev = ev->next; + fPos++; + } + fPos = fTotal; + fPlaying = false; + } + virtual ~MidiEventsStore() + { + if (!fEvs) return; + FreeMIDIEventList(fEvs); + fEvs = 0; + } + + bool IsPlaying() + { + return fPlaying; + } + + void SetLoops(int loops) + { + fLoops = loops; + } + + protected: + MIDIEvent *fEvs; + Uint16 fDivision; + + int fPos, fTotal; + int fLoops; + bool fPlaying; + + void SprayEvent(MIDIEvent *ev, uint32 time) + { + switch (ev->status & 0xF0) + { + case B_NOTE_OFF: + SprayNoteOff((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case B_NOTE_ON: + SprayNoteOn((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case B_KEY_PRESSURE: + SprayKeyPressure((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case B_CONTROL_CHANGE: + SprayControlChange((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case B_PROGRAM_CHANGE: + SprayProgramChange((ev->status & 0x0F) + 1, ev->data[0], time); + break; + case B_CHANNEL_PRESSURE: + SprayChannelPressure((ev->status & 0x0F) + 1, ev->data[0], time); + break; + case B_PITCH_BEND: + SprayPitchBend((ev->status & 0x0F) + 1, ev->data[0], ev->data[1], time); + break; + case 0xF: + switch (ev->status) + { + case B_SYS_EX_START: + SpraySystemExclusive(ev->extraData, ev->extraLen, time); + break; + case B_MIDI_TIME_CODE: + case B_SONG_POSITION: + case B_SONG_SELECT: + case B_CABLE_MESSAGE: + case B_TUNE_REQUEST: + case B_SYS_EX_END: + SpraySystemCommon(ev->status, ev->data[0], ev->data[1], time); + break; + case B_TIMING_CLOCK: + case B_START: + case B_STOP: + case B_CONTINUE: + case B_ACTIVE_SENSING: + SpraySystemRealTime(ev->status, time); + break; + case B_SYSTEM_RESET: + if (ev->data[0] == 0x51 && ev->data[1] == 0x03) + { + assert(ev->extraLen == 3); + int val = (ev->extraData[0] << 16) | (ev->extraData[1] << 8) | ev->extraData[2]; + int tempo = 60000000 / val; + SprayTempoChange(tempo, time); + } + else + { + SpraySystemRealTime(ev->status, time); + } + } + break; + } + } + + void sort_events() + { + MIDIEvent *items = new MIDIEvent[fTotal]; + MIDIEvent *x = fEvs; + for (int i = 0; i < fTotal; i++) + { + memcpy(items + i, x, sizeof(MIDIEvent)); + x = x->next; + } + std::sort(items, items + fTotal, compareMIDIEvent); + + x = fEvs; + for (int i = 0; i < fTotal; i++) + { + MIDIEvent *ne = x->next; + memcpy(x, items + i, sizeof(MIDIEvent)); + x->next = ne; + x = ne; + } + + for (x = fEvs; x && x->next; x = x->next) + assert(x->time <= x->next->time); + + delete[] items; + } +}; + +BMidiSynth synth; +struct _NativeMidiSong { + MidiEventsStore *store; +} *currentSong = NULL; + +char lasterr[1024]; + +int native_midi_detect() +{ + status_t res = synth.EnableInput(true, false); + return res == B_OK; +} + +void native_midi_setvolume(int volume) +{ + if (volume < 0) volume = 0; + if (volume > 128) volume = 128; + synth.SetVolume(volume / 128.0); +} + +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw) +{ + NativeMidiSong *song = new NativeMidiSong; + song->store = new MidiEventsStore; + status_t res = song->store->Import(rw); + + if (freerw) { + SDL_RWclose(rw); + } + if (res != B_OK) + { + snprintf(lasterr, sizeof lasterr, "Cannot Import() midi file: status_t=%d", res); + delete song->store; + delete song; + return NULL; + } + return song; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + if (song == NULL) return; + song->store->Stop(); + song->store->Disconnect(&synth); + if (currentSong == song) + { + currentSong = NULL; + } + delete song->store; + delete song; song = 0; +} +void native_midi_start(NativeMidiSong *song, int loops) +{ + native_midi_stop(); + song->store->Connect(&synth); + song->store->SetLoops(loops); + song->store->Start(); + currentSong = song; +} +void native_midi_stop() +{ + if (currentSong == NULL) return; + currentSong->store->Stop(); + currentSong->store->Disconnect(&synth); + while (currentSong->store->IsPlaying()) + usleep(1000); + currentSong = NULL; +} +int native_midi_active() +{ + if (currentSong == NULL) return 0; + return currentSong->store->IsPlaying(); +} + +const char* native_midi_error(void) +{ + return lasterr; +} + +#endif /* __HAIKU__ */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_mac.c b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_mac.c new file mode 100644 index 0000000000..01e7877472 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_mac.c @@ -0,0 +1,644 @@ +/* + native_midi_mac: Native Midi support on MacOS for the SDL_mixer library + Copyright (C) 2001 Max Horn + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" +#include "SDL_endian.h" + +#if __MACOS__ /*|| __MACOSX__ */ + +#include "native_midi.h" +#include "native_midi_common.h" + +#if __MACOSX__ +#include +#else +#include +#endif + +#include +#include +#include + + +/* Native Midi song */ +struct _NativeMidiSong +{ + Uint32 *tuneSequence; + Uint32 *tuneHeader; +}; + +enum +{ + /* number of (32-bit) long words in a note request event */ + kNoteRequestEventLength = ((sizeof(NoteRequest)/sizeof(long)) + 2), + + /* number of (32-bit) long words in a marker event */ + kMarkerEventLength = 1, + + /* number of (32-bit) long words in a general event, minus its data */ + kGeneralEventLength = 2 +}; + +#define ERROR_BUF_SIZE 256 +#define BUFFER_INCREMENT 5000 + +#define REST_IF_NECESSARY() do {\ + int timeDiff = eventPos->time - lastEventTime; \ + if(timeDiff) \ + { \ + timeDiff = (int)(timeDiff*tick); \ + qtma_StuffRestEvent(*tunePos, timeDiff); \ + tunePos++; \ + lastEventTime = eventPos->time; \ + } \ + } while(0) + + +static Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts); +static Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts); + +/* The global TunePlayer instance */ +static TunePlayer gTunePlayer = NULL; +static int gInstaceCount = 0; +static Uint32 *gCurrentTuneSequence = NULL; +static char gErrorBuffer[ERROR_BUF_SIZE] = ""; + + +/* Check whether QuickTime is available */ +int native_midi_detect() +{ + /* TODO */ + return 1; +} + +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw) +{ + NativeMidiSong *song = NULL; + MIDIEvent *evntlist = NULL; + int part_to_inst[32]; + int part_poly_max[32]; + int numParts = 0; + Uint16 ppqn; + + /* Init the arrays */ + memset(part_poly_max,0,sizeof(part_poly_max)); + memset(part_to_inst,-1,sizeof(part_to_inst)); + + /* Attempt to load the midi file */ + evntlist = CreateMIDIEventList(rw, &ppqn); + if (!evntlist) + goto bail; + + /* Allocate memory for the song struct */ + song = malloc(sizeof(NativeMidiSong)); + if (!song) + goto bail; + + /* Build a tune sequence from the event list */ + song->tuneSequence = BuildTuneSequence(evntlist, ppqn, part_poly_max, part_to_inst, &numParts); + if(!song->tuneSequence) + goto bail; + + /* Now build a tune header from the data we collect above, create + all parts as needed and assign them the correct instrument. + */ + song->tuneHeader = BuildTuneHeader(part_poly_max, part_to_inst, numParts); + if(!song->tuneHeader) + goto bail; + + /* Increment the instance count */ + gInstaceCount++; + if (gTunePlayer == NULL) + gTunePlayer = OpenDefaultComponent(kTunePlayerComponentType, 0); + + /* Finally, free the event list */ + FreeMIDIEventList(evntlist); + + if (freerw) { + SDL_RWclose(rw); + } + return song; + +bail: + if (evntlist) + FreeMIDIEventList(evntlist); + + if (song) + { + if(song->tuneSequence) + free(song->tuneSequence); + + if(song->tuneHeader) + DisposePtr((Ptr)song->tuneHeader); + + free(song); + } + + if (freerw) { + SDL_RWclose(rw); + } + return NULL; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + if(!song || !song->tuneSequence) + return; + + /* If this is the currently playing song, stop it now */ + if (song->tuneSequence == gCurrentTuneSequence) + native_midi_stop(); + + /* Finally, free the data storage */ + free(song->tuneSequence); + DisposePtr((Ptr)song->tuneHeader); + free(song); + + /* Increment the instance count */ + gInstaceCount--; + if ((gTunePlayer != NULL) && (gInstaceCount == 0)) + { + CloseComponent(gTunePlayer); + gTunePlayer = NULL; + } +} + +void native_midi_start(NativeMidiSong *song, int loops) +{ + UInt32 queueFlags = 0; + ComponentResult tpError; + + assert (gTunePlayer != NULL); + + /* FIXME: is this code even used anymore? */ + assert (loops == 0); + + SDL_PauseAudio(1); + SDL_UnlockAudio(); + + /* First, stop the currently playing music */ + native_midi_stop(); + + /* Set up the queue flags */ + queueFlags = kTuneStartNow; + + /* Set the time scale (units per second), we want milliseconds */ + tpError = TuneSetTimeScale(gTunePlayer, 1000); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TuneSetTimeScale", ERROR_BUF_SIZE); + goto done; + } + + /* Set the header, to tell what instruments are used */ + tpError = TuneSetHeader(gTunePlayer, (UInt32 *)song->tuneHeader); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TuneSetHeader", ERROR_BUF_SIZE); + goto done; + } + + /* Have it allocate whatever resources are needed */ + tpError = TunePreroll(gTunePlayer); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TunePreroll", ERROR_BUF_SIZE); + goto done; + } + + /* We want to play at normal volume */ + tpError = TuneSetVolume(gTunePlayer, 0x00010000); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TuneSetVolume", ERROR_BUF_SIZE); + goto done; + } + + /* Finally, start playing the full song */ + gCurrentTuneSequence = song->tuneSequence; + tpError = TuneQueue(gTunePlayer, (UInt32 *)song->tuneSequence, 0x00010000, 0, 0xFFFFFFFF, queueFlags, NULL, 0); + if (tpError != noErr) + { + strncpy (gErrorBuffer, "MIDI error during TuneQueue", ERROR_BUF_SIZE); + goto done; + } + +done: + SDL_LockAudio(); + SDL_PauseAudio(0); +} + +void native_midi_stop() +{ + if (gTunePlayer == NULL) + return; + + /* Stop music */ + TuneStop(gTunePlayer, 0); + + /* Deallocate all instruments */ + TuneUnroll(gTunePlayer); +} + +int native_midi_active() +{ + if (gTunePlayer != NULL) + { + TuneStatus ts; + + TuneGetStatus(gTunePlayer,&ts); + return ts.queueTime != 0; + } + else + return 0; +} + +void native_midi_setvolume(int volume) +{ + if (gTunePlayer == NULL) + return; + + /* QTMA olume may range from 0.0 to 1.0 (in 16.16 fixed point encoding) */ + TuneSetVolume(gTunePlayer, (0x00010000 * volume)/SDL_MIX_MAXVOLUME); +} + +const char *native_midi_error(void) +{ + return gErrorBuffer; +} + +Uint32 *BuildTuneSequence(MIDIEvent *evntlist, int ppqn, int part_poly_max[32], int part_to_inst[32], int *numParts) +{ + int part_poly[32]; + int channel_to_part[16]; + + int channel_pan[16]; + int channel_vol[16]; + int channel_pitch_bend[16]; + + int lastEventTime = 0; + int tempo = 500000; + double Ippqn = 1.0 / (1000*ppqn); + double tick = tempo * Ippqn; + MIDIEvent *eventPos = evntlist; + MIDIEvent *noteOffPos; + Uint32 *tunePos, *endPos; + Uint32 *tuneSequence; + size_t tuneSize; + + /* allocate space for the tune header */ + tuneSize = 5000; + tuneSequence = (Uint32 *)malloc(tuneSize * sizeof(Uint32)); + if (tuneSequence == NULL) + return NULL; + + /* Set starting position in our tune memory */ + tunePos = tuneSequence; + endPos = tuneSequence + tuneSize; + + /* Initialise the arrays */ + memset(part_poly,0,sizeof(part_poly)); + + memset(channel_to_part,-1,sizeof(channel_to_part)); + memset(channel_pan,-1,sizeof(channel_pan)); + memset(channel_vol,-1,sizeof(channel_vol)); + memset(channel_pitch_bend,-1,sizeof(channel_pitch_bend)); + + *numParts = 0; + + /* + * Now the major work - iterate over all GM events, + * and turn them into QuickTime Music format. + * At the same time, calculate the max. polyphony for each part, + * and also the part->instrument mapping. + */ + while(eventPos) + { + int status = (eventPos->status&0xF0)>>4; + int channel = eventPos->status&0x0F; + int part = channel_to_part[channel]; + int velocity, pitch; + int value, controller; + int bend; + int newInst; + + /* Check if we are running low on space... */ + if((tunePos+16) > endPos) + { + /* Resize our data storage. */ + Uint32 *oldTuneSequence = tuneSequence; + + tuneSize += BUFFER_INCREMENT; + tuneSequence = (Uint32 *)realloc(tuneSequence, tuneSize * sizeof(Uint32)); + if(oldTuneSequence != tuneSequence) + tunePos += tuneSequence - oldTuneSequence; + endPos = tuneSequence + tuneSize; + } + + switch (status) + { + case MIDI_STATUS_NOTE_OFF: + assert(part>=0 && part<=31); + + /* Keep track of the polyphony of the current part */ + part_poly[part]--; + break; + case MIDI_STATUS_NOTE_ON: + if (part < 0) + { + /* If no part is specified yet, we default to the first instrument, which + is piano (or the first drum kit if we are on the drum channel) + */ + int newInst; + + if (channel == 9) + newInst = kFirstDrumkit + 1; /* the first drum kit is the "no drum" kit! */ + else + newInst = kFirstGMInstrument; + part = channel_to_part[channel] = *numParts; + part_to_inst[(*numParts)++] = newInst; + } + /* TODO - add support for more than 32 parts using eXtended QTMA events */ + assert(part<=31); + + /* Decode pitch & velocity */ + pitch = eventPos->data[0]; + velocity = eventPos->data[1]; + + if (velocity == 0) + { + /* was a NOTE OFF in disguise, so we decrement the polyphony */ + part_poly[part]--; + } + else + { + /* Keep track of the polyphony of the current part */ + int foo = ++part_poly[part]; + if (part_poly_max[part] < foo) + part_poly_max[part] = foo; + + /* Now scan forward to find the matching NOTE OFF event */ + for(noteOffPos = eventPos; noteOffPos; noteOffPos = noteOffPos->next) + { + if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_OFF + && channel == (eventPos->status&0x0F) + && pitch == noteOffPos->data[0]) + break; + /* NOTE ON with velocity == 0 is the same as a NOTE OFF */ + if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_ON + && channel == (eventPos->status&0x0F) + && pitch == noteOffPos->data[0] + && 0 == noteOffPos->data[1]) + break; + } + + /* Did we find a note off? Should always be the case, but who knows... */ + if (noteOffPos) + { + /* We found a NOTE OFF, now calculate the note duration */ + int duration = (int)((noteOffPos->time - eventPos->time)*tick); + + REST_IF_NECESSARY(); + /* Now we need to check if we get along with a normal Note Event, or if we need an extended one... */ + if (duration < 2048 && pitch>=32 && pitch<=95 && velocity>=0 && velocity<=127) + { + qtma_StuffNoteEvent(*tunePos, part, pitch, velocity, duration); + tunePos++; + } + else + { + qtma_StuffXNoteEvent(*tunePos, *(tunePos+1), part, pitch, velocity, duration); + tunePos+=2; + } + } + } + break; + case MIDI_STATUS_AFTERTOUCH: + /* NYI - use kControllerAfterTouch. But how are the parameters to be mapped? */ + break; + case MIDI_STATUS_CONTROLLER: + controller = eventPos->data[0]; + value = eventPos->data[1]; + + switch(controller) + { + case 0: /* bank change - igore for now */ + break; + case kControllerVolume: + if(channel_vol[channel] != value<<8) + { + channel_vol[channel] = value<<8; + if(part>=0 && part<=31) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]); + tunePos++; + } + } + break; + case kControllerPan: + if(channel_pan[channel] != (value << 1) + 256) + { + channel_pan[channel] = (value << 1) + 256; + if(part>=0 && part<=31) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]); + tunePos++; + } + } + break; + default: + /* No other controllers implemented yet */; + break; + } + + break; + case MIDI_STATUS_PROG_CHANGE: + /* Instrument changed */ + newInst = eventPos->data[0]; + + /* Channel 9 (the 10th channel) is different, it indicates a drum kit */ + if (channel == 9) + newInst += kFirstDrumkit; + else + newInst += kFirstGMInstrument; + /* Only if the instrument for this channel *really* changed, add a new part. */ + if(newInst != part_to_inst[part]) + { + /* TODO maybe make use of kGeneralEventPartChange here, + to help QT reuse note channels? + */ + part = channel_to_part[channel] = *numParts; + part_to_inst[(*numParts)++] = newInst; + + if(channel_vol[channel] >= 0) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]); + tunePos++; + } + if(channel_pan[channel] >= 0) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]); + tunePos++; + } + if(channel_pitch_bend[channel] >= 0) + { + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, channel_pitch_bend[channel]); + tunePos++; + } + } + break; + case MIDI_STATUS_PRESSURE: + /* NYI */ + break; + case MIDI_STATUS_PITCH_WHEEL: + /* In the midi spec, 0x2000 = center, 0x0000 = - 2 semitones, 0x3FFF = +2 semitones + but for QTMA, we specify it as a 8.8 fixed point of semitones + TODO: detect "pitch bend range changes" & honor them! + */ + bend = (eventPos->data[0] & 0x7f) | ((eventPos->data[1] & 0x7f) << 7); + + /* "Center" the bend */ + bend -= 0x2000; + + /* Move it to our format: */ + bend <<= 4; + + /* If it turns out the pitch bend didn't change, stop here */ + if(channel_pitch_bend[channel] == bend) + break; + + channel_pitch_bend[channel] = bend; + if(part>=0 && part<=31) + { + /* Stuff a control event */ + REST_IF_NECESSARY(); + qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, bend); + tunePos++; + } + break; + case MIDI_STATUS_SYSEX: + if (eventPos->status == 0xFF && eventPos->data[0] == 0x51) /* Tempo change */ + { + tempo = (eventPos->extraData[0] << 16) + + (eventPos->extraData[1] << 8) + + eventPos->extraData[2]; + + tick = tempo * Ippqn; + } + break; + } + + /* on to the next event */ + eventPos = eventPos->next; + } + + /* Finally, place an end marker */ + *tunePos = kEndMarkerValue; + + return tuneSequence; +} + +Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts) +{ + Uint32 *myHeader; + Uint32 *myPos1, *myPos2; /* pointers to the head and tail long words of a music event */ + NoteRequest *myNoteRequest; + NoteAllocator myNoteAllocator; /* for the NAStuffToneDescription call */ + ComponentResult myErr = noErr; + int part; + + myHeader = NULL; + myNoteAllocator = NULL; + + /* + * Open up the Note Allocator + */ + myNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType,0); + if (myNoteAllocator == NULL) + goto bail; + + /* + * Allocate space for the tune header + */ + myHeader = (Uint32 *) + NewPtrClear((numParts * kNoteRequestEventLength + kMarkerEventLength) * sizeof(Uint32)); + if (myHeader == NULL) + goto bail; + + myPos1 = myHeader; + + /* + * Loop over all parts + */ + for(part = 0; part < numParts; ++part) + { + /* + * Stuff request for the instrument with the given polyphony + */ + myPos2 = myPos1 + (kNoteRequestEventLength - 1); /* last longword of general event */ + qtma_StuffGeneralEvent(*myPos1, *myPos2, part, kGeneralEventNoteRequest, kNoteRequestEventLength); + myNoteRequest = (NoteRequest *)(myPos1 + 1); + myNoteRequest->info.flags = 0; + /* I'm told by the Apple people that the Quicktime types were poorly designed and it was + * too late to change them. On little endian, the BigEndian(Short|Fixed) types are structs + * while on big endian they are primitive types. Furthermore, Quicktime failed to + * provide setter and getter functions. To get this to work, we need to case the + * code for the two possible situations. + * My assumption is that the right-side value was always expected to be BigEndian + * as it was written way before the Universal Binary transition. So in the little endian + * case, OSSwap is used. + */ +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + myNoteRequest->info.polyphony.bigEndianValue = OSSwapHostToBigInt16(part_poly_max[part]); + myNoteRequest->info.typicalPolyphony.bigEndianValue = OSSwapHostToBigInt32(0x00010000); +#else + myNoteRequest->info.polyphony = part_poly_max[part]; + myNoteRequest->info.typicalPolyphony = 0x00010000; +#endif + myErr = NAStuffToneDescription(myNoteAllocator,part_to_inst[part],&myNoteRequest->tone); + if (myErr != noErr) + goto bail; + + /* move pointer to beginning of next event */ + myPos1 += kNoteRequestEventLength; + } + + *myPos1 = kEndMarkerValue; /* end of sequence marker */ + + +bail: + if(myNoteAllocator) + CloseComponent(myNoteAllocator); + + /* if we encountered an error, dispose of the storage we allocated and return NULL */ + if (myErr != noErr) { + DisposePtr((Ptr)myHeader); + myHeader = NULL; + } + + return myHeader; +} + +#endif /* MacOS native MIDI support */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_macosx.c b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_macosx.c new file mode 100644 index 0000000000..8fefbc9616 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_macosx.c @@ -0,0 +1,322 @@ +/* + native_midi_macosx: Native Midi support on Mac OS X for the SDL_mixer library + Copyright (C) 2009 Ryan C. Gordon + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* This is Mac OS X only, using Core MIDI. + Mac OS 9 support via QuickTime is in native_midi_mac.c */ + +#include "SDL_config.h" + +#if __MACOSX__ + +#include +#include +#include + +#include "../SDL_mixer.h" +#include "SDL_endian.h" +#include "native_midi.h" + +/* Native Midi song */ +struct _NativeMidiSong +{ + MusicPlayer player; + MusicSequence sequence; + MusicTimeStamp endTime; + AudioUnit audiounit; + int loops; +}; + +static NativeMidiSong *currentsong = NULL; +static int latched_volume = MIX_MAX_VOLUME; + +static OSStatus +GetSequenceLength(MusicSequence sequence, MusicTimeStamp *_sequenceLength) +{ + // http://lists.apple.com/archives/Coreaudio-api/2003/Jul/msg00370.html + // figure out sequence length + UInt32 ntracks, i; + MusicTimeStamp sequenceLength = 0; + OSStatus err; + + err = MusicSequenceGetTrackCount(sequence, &ntracks); + if (err != noErr) + return err; + + for (i = 0; i < ntracks; ++i) + { + MusicTrack track; + MusicTimeStamp tracklen = 0; + UInt32 tracklenlen = sizeof (tracklen); + + err = MusicSequenceGetIndTrack(sequence, i, &track); + if (err != noErr) + return err; + + err = MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, + &tracklen, &tracklenlen); + if (err != noErr) + return err; + + if (sequenceLength < tracklen) + sequenceLength = tracklen; + } + + *_sequenceLength = sequenceLength; + + return noErr; +} + + +/* we're looking for the sequence output audiounit. */ +static OSStatus +GetSequenceAudioUnit(MusicSequence sequence, AudioUnit *aunit) +{ + AUGraph graph; + UInt32 nodecount, i; + OSStatus err; + + err = MusicSequenceGetAUGraph(sequence, &graph); + if (err != noErr) + return err; + + err = AUGraphGetNodeCount(graph, &nodecount); + if (err != noErr) + return err; + + for (i = 0; i < nodecount; i++) { + AUNode node; + + if (AUGraphGetIndNode(graph, i, &node) != noErr) + continue; /* better luck next time. */ + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 /* this is deprecated, but works back to 10.0 */ + { + struct ComponentDescription desc; + UInt32 classdatasize = 0; + void *classdata = NULL; + err = AUGraphGetNodeInfo(graph, node, &desc, &classdatasize, + &classdata, aunit); + if (err != noErr) + continue; + else if (desc.componentType != kAudioUnitType_Output) + continue; + else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) + continue; + } + #else /* not deprecated, but requires 10.5 or later */ + { + AudioComponentDescription desc; + if (AUGraphNodeInfo(graph, node, &desc, aunit) != noErr) + continue; + else if (desc.componentType != kAudioUnitType_Output) + continue; + else if (desc.componentSubType != kAudioUnitSubType_DefaultOutput) + continue; + } + #endif + + return noErr; /* found it! */ + } + + return kAUGraphErr_NodeNotFound; +} + + +int native_midi_detect() +{ + return 1; /* always available. */ +} + +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw) +{ + NativeMidiSong *retval = NULL; + void *buf = NULL; + int len = 0; + CFDataRef data = NULL; + + if (SDL_RWseek(rw, 0, RW_SEEK_END) < 0) + goto fail; + len = SDL_RWtell(rw); + if (len < 0) + goto fail; + if (SDL_RWseek(rw, 0, RW_SEEK_SET) < 0) + goto fail; + + buf = malloc(len); + if (buf == NULL) + goto fail; + + if (SDL_RWread(rw, buf, len, 1) != 1) + goto fail; + + retval = malloc(sizeof(NativeMidiSong)); + if (retval == NULL) + goto fail; + + memset(retval, '\0', sizeof (*retval)); + + if (NewMusicPlayer(&retval->player) != noErr) + goto fail; + if (NewMusicSequence(&retval->sequence) != noErr) + goto fail; + + data = CFDataCreate(NULL, (const UInt8 *) buf, len); + if (data == NULL) + goto fail; + + free(buf); + buf = NULL; + + #if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4 /* this is deprecated, but works back to 10.3 */ + if (MusicSequenceLoadSMFDataWithFlags(retval->sequence, data, 0) != noErr) + goto fail; + #else /* not deprecated, but requires 10.5 or later */ + if (MusicSequenceFileLoadData(retval->sequence, data, 0, 0) != noErr) + goto fail; + #endif + + CFRelease(data); + data = NULL; + + if (GetSequenceLength(retval->sequence, &retval->endTime) != noErr) + goto fail; + + if (MusicPlayerSetSequence(retval->player, retval->sequence) != noErr) + goto fail; + + if (freerw) + SDL_RWclose(rw); + + return retval; + +fail: + if (retval) { + if (retval->sequence) + DisposeMusicSequence(retval->sequence); + if (retval->player) + DisposeMusicPlayer(retval->player); + free(retval); + } + + if (data) + CFRelease(data); + + if (buf) + free(buf); + + if (freerw) + SDL_RWclose(rw); + + return NULL; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + if (song != NULL) { + if (currentsong == song) + currentsong = NULL; + MusicPlayerStop(song->player); + DisposeMusicSequence(song->sequence); + DisposeMusicPlayer(song->player); + free(song); + } +} + +void native_midi_start(NativeMidiSong *song, int loops) +{ + int vol; + + if (song == NULL) + return; + + SDL_PauseAudio(1); + SDL_UnlockAudio(); + + if (currentsong) + MusicPlayerStop(currentsong->player); + + currentsong = song; + currentsong->loops = loops; + + MusicPlayerPreroll(song->player); + MusicPlayerSetTime(song->player, 0); + MusicPlayerStart(song->player); + + GetSequenceAudioUnit(song->sequence, &song->audiounit); + + vol = latched_volume; + latched_volume++; /* just make this not match. */ + native_midi_setvolume(vol); + + SDL_LockAudio(); + SDL_PauseAudio(0); +} + +void native_midi_stop() +{ + if (currentsong) { + SDL_PauseAudio(1); + SDL_UnlockAudio(); + MusicPlayerStop(currentsong->player); + currentsong = NULL; + SDL_LockAudio(); + SDL_PauseAudio(0); + } +} + +int native_midi_active() +{ + MusicTimeStamp currentTime = 0; + if (currentsong == NULL) + return 0; + + MusicPlayerGetTime(currentsong->player, ¤tTime); + if ((currentTime < currentsong->endTime) || + (currentTime >= kMusicTimeStamp_EndOfTrack)) { + return 1; + } else if (currentsong->loops) { + --currentsong->loops; + MusicPlayerSetTime(currentsong->player, 0); + return 1; + } + return 0; +} + +void native_midi_setvolume(int volume) +{ + if (latched_volume == volume) + return; + + latched_volume = volume; + if ((currentsong) && (currentsong->audiounit)) { + const float floatvol = ((float) volume) / ((float) MIX_MAX_VOLUME); + AudioUnitSetParameter(currentsong->audiounit, kHALOutputParam_Volume, + kAudioUnitScope_Global, 0, floatvol, 0); + } +} + +const char *native_midi_error(void) +{ + return ""; /* !!! FIXME */ +} + +#endif /* Mac OS X native MIDI support */ + diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_win32.c b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_win32.c new file mode 100644 index 0000000000..187d989ff3 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/native_midi/native_midi_win32.c @@ -0,0 +1,312 @@ +/* + native_midi: Hardware Midi support for the SDL_mixer library + Copyright (C) 2000,2001 Florian 'Proff' Schulze + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "SDL_config.h" + +/* everything below is currently one very big bad hack ;) Proff */ + +#if __WIN32__ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include "native_midi.h" +#include "native_midi_common.h" + +struct _NativeMidiSong { + int MusicLoaded; + int MusicPlaying; + int Loops; + int CurrentHdr; + MIDIHDR MidiStreamHdr[2]; + MIDIEVENT *NewEvents; + Uint16 ppqn; + int Size; + int NewPos; +}; + +static UINT MidiDevice=MIDI_MAPPER; +static HMIDISTRM hMidiStream; +static NativeMidiSong *currentsong; + +static int BlockOut(NativeMidiSong *song) +{ + MMRESULT err; + int BlockSize; + MIDIHDR *hdr; + + if ((song->MusicLoaded) && (song->NewEvents)) + { + // proff 12/8/98: Added for safety + song->CurrentHdr = !song->CurrentHdr; + hdr = &song->MidiStreamHdr[song->CurrentHdr]; + midiOutUnprepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR)); + if (song->NewPos>=song->Size) + return 0; + BlockSize=(song->Size-song->NewPos); + if (BlockSize<=0) + return 0; + if (BlockSize>36000) + BlockSize=36000; + hdr->lpData=(void *)((unsigned char *)song->NewEvents+song->NewPos); + song->NewPos+=BlockSize; + hdr->dwBufferLength=BlockSize; + hdr->dwBytesRecorded=BlockSize; + hdr->dwFlags=0; + hdr->dwOffset=0; + err=midiOutPrepareHeader((HMIDIOUT)hMidiStream,hdr,sizeof(MIDIHDR)); + if (err!=MMSYSERR_NOERROR) + return 0; + err=midiStreamOut(hMidiStream,hdr,sizeof(MIDIHDR)); + return 0; + } + return 1; +} + +static void MIDItoStream(NativeMidiSong *song, MIDIEvent *evntlist) +{ + int eventcount; + MIDIEvent *event; + MIDIEVENT *newevent; + + eventcount=0; + event=evntlist; + while (event) + { + eventcount++; + event=event->next; + } + song->NewEvents=malloc(eventcount*3*sizeof(DWORD)); + if (!song->NewEvents) + return; + memset(song->NewEvents,0,(eventcount*3*sizeof(DWORD))); + + eventcount=0; + event=evntlist; + newevent=song->NewEvents; + while (event) + { + int status = (event->status&0xF0)>>4; + switch (status) + { + case MIDI_STATUS_NOTE_OFF: + case MIDI_STATUS_NOTE_ON: + case MIDI_STATUS_AFTERTOUCH: + case MIDI_STATUS_CONTROLLER: + case MIDI_STATUS_PROG_CHANGE: + case MIDI_STATUS_PRESSURE: + case MIDI_STATUS_PITCH_WHEEL: + newevent->dwDeltaTime=event->time; + newevent->dwEvent=(event->status|0x80)|(event->data[0]<<8)|(event->data[1]<<16)|(MEVT_SHORTMSG<<24); + newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); + eventcount++; + break; + + case MIDI_STATUS_SYSEX: + if (event->status == 0xFF && event->data[0] == 0x51) /* Tempo change */ + { + int tempo = (event->extraData[0] << 16) | + (event->extraData[1] << 8) | + event->extraData[2]; + newevent->dwDeltaTime=event->time; + newevent->dwEvent=(MEVT_TEMPO<<24) | tempo; + newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); + eventcount++; + } + break; + } + + event=event->next; + } + + song->Size=eventcount*3*sizeof(DWORD); + + { + int time; + int temptime; + + song->NewPos=0; + time=0; + newevent=song->NewEvents; + while (song->NewPosSize) + { + temptime=newevent->dwDeltaTime; + newevent->dwDeltaTime-=time; + time=temptime; + if ((song->NewPos+12)>=song->Size) + newevent->dwEvent |= MEVT_F_CALLBACK; + newevent=(MIDIEVENT*)((char*)newevent+(3*sizeof(DWORD))); + song->NewPos+=12; + } + } + song->NewPos=0; + song->MusicLoaded=1; +} + +void CALLBACK MidiProc( HMIDIIN hMidi, UINT uMsg, DWORD_PTR dwInstance, + DWORD_PTR dwParam1, DWORD_PTR dwParam2 ) +{ + switch( uMsg ) + { + case MOM_DONE: + if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)¤tsong->MidiStreamHdr[currentsong->CurrentHdr])) + BlockOut(currentsong); + break; + case MOM_POSITIONCB: + if ((currentsong->MusicLoaded) && (dwParam1 == (DWORD_PTR)¤tsong->MidiStreamHdr[currentsong->CurrentHdr])) { + if (currentsong->Loops) { + --currentsong->Loops; + currentsong->NewPos=0; + BlockOut(currentsong); + } else { + currentsong->MusicPlaying=0; + } + } + break; + default: + break; + } +} + +int native_midi_detect() +{ + MMRESULT merr; + HMIDISTRM MidiStream; + + merr=midiStreamOpen(&MidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION); + if (merr!=MMSYSERR_NOERROR) + return 0; + midiStreamClose(MidiStream); + return 1; +} + +NativeMidiSong *native_midi_loadsong_RW(SDL_RWops *rw, int freerw) +{ + NativeMidiSong *newsong; + MIDIEvent *evntlist = NULL; + + newsong=malloc(sizeof(NativeMidiSong)); + if (!newsong) { + if (freerw) { + SDL_RWclose(rw); + } + return NULL; + } + memset(newsong,0,sizeof(NativeMidiSong)); + + /* Attempt to load the midi file */ + evntlist = CreateMIDIEventList(rw, &newsong->ppqn); + if (!evntlist) + { + free(newsong); + if (freerw) { + SDL_RWclose(rw); + } + return NULL; + } + + MIDItoStream(newsong, evntlist); + + FreeMIDIEventList(evntlist); + + if (freerw) { + SDL_RWclose(rw); + } + return newsong; +} + +void native_midi_freesong(NativeMidiSong *song) +{ + if (hMidiStream) + { + midiStreamStop(hMidiStream); + midiStreamClose(hMidiStream); + } + if (song) + { + if (song->NewEvents) + free(song->NewEvents); + free(song); + } +} + +void native_midi_start(NativeMidiSong *song, int loops) +{ + MMRESULT merr; + MIDIPROPTIMEDIV mptd; + + native_midi_stop(); + if (!hMidiStream) + { + merr=midiStreamOpen(&hMidiStream,&MidiDevice,(DWORD)1,(DWORD_PTR)MidiProc,(DWORD_PTR)0,CALLBACK_FUNCTION); + if (merr!=MMSYSERR_NOERROR) + { + hMidiStream = NULL; // should I do midiStreamClose(hMidiStream) before? + return; + } + //midiStreamStop(hMidiStream); + currentsong=song; + currentsong->NewPos=0; + currentsong->MusicPlaying=1; + currentsong->Loops=loops; + mptd.cbStruct=sizeof(MIDIPROPTIMEDIV); + mptd.dwTimeDiv=currentsong->ppqn; + merr=midiStreamProperty(hMidiStream,(LPBYTE)&mptd,MIDIPROP_SET | MIDIPROP_TIMEDIV); + BlockOut(song); + merr=midiStreamRestart(hMidiStream); + } +} + +void native_midi_stop() +{ + if (!hMidiStream) + return; + midiStreamStop(hMidiStream); + midiStreamClose(hMidiStream); + currentsong=NULL; + hMidiStream = NULL; +} + +int native_midi_active() +{ + return currentsong->MusicPlaying; +} + +void native_midi_setvolume(int volume) +{ + int calcVolume; + if (volume > 128) + volume = 128; + if (volume < 0) + volume = 0; + calcVolume = (65535 * volume / 128); + + midiOutSetVolume((HMIDIOUT)hMidiStream, MAKELONG(calcVolume , calcVolume)); +} + +const char *native_midi_error(void) +{ + return ""; +} + +#endif /* Windows native MIDI support */ diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/wavestream.c b/contrib/sdk/sources/SDL_mixer-1.2.12/wavestream.c new file mode 100644 index 0000000000..2056726aa1 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/wavestream.c @@ -0,0 +1,526 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +/* This file supports streaming WAV files, without volume adjustment */ + +#include +#include + +#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 + +#define SDL_stack_alloc(type, count) (type*)SDL_malloc(sizeof(type)*(count)) + +/* 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 SDL_RWops *LoadWAVStream (SDL_RWops *rw, SDL_AudioSpec *spec, + long *start, long *stop); +static SDL_RWops *LoadAIFFStream (SDL_RWops *rw, 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 RWops object */ +WAVStream *WAVStream_LoadSong_RW(SDL_RWops *rw, const char *magic, int freerw) +{ + WAVStream *wave; + SDL_AudioSpec wavespec; + + if ( ! mixer.format ) { + Mix_SetError("WAV music output not started"); + if ( freerw ) { + SDL_RWclose(rw); + } + return(NULL); + } + wave = (WAVStream *)SDL_malloc(sizeof *wave); + if ( wave ) { + memset(wave, 0, (sizeof *wave)); + wave->freerw = freerw; + if ( strcmp(magic, "RIFF") == 0 ) { + wave->rw = LoadWAVStream(rw, &wavespec, + &wave->start, &wave->stop); + } else + if ( strcmp(magic, "FORM") == 0 ) { + wave->rw = LoadAIFFStream(rw, &wavespec, + &wave->start, &wave->stop); + } else { + Mix_SetError("Unknown WAVE format"); + } + if ( wave->rw == NULL ) { + SDL_free(wave); + if ( freerw ) { + SDL_RWclose(rw); + } + return(NULL); + } + SDL_BuildAudioCVT(&wave->cvt, + wavespec.format, wavespec.channels, wavespec.freq, + mixer.format, mixer.channels, mixer.freq); + } else { + SDL_OutOfMemory(); + if ( freerw ) { + SDL_RWclose(rw); + } + return(NULL); + } + return(wave); +} + +/* Start playback of a given WAV stream */ +void WAVStream_Start(WAVStream *wave) +{ + SDL_RWseek (wave->rw, wave->start, RW_SEEK_SET); + music = wave; +} + +/* Play some of a stream previously started with WAVStream_Start() */ +int WAVStream_PlaySome(Uint8 *stream, int len) +{ + long pos; + int left = 0; + + if ( music && ((pos=SDL_RWtell(music->rw)) < 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 ) { + SDL_free(music->cvt.buf); + } + worksize = original_len*music->cvt.len_mult; + music->cvt.buf=(Uint8 *)SDL_malloc(worksize); + if ( music->cvt.buf == NULL ) { + return 0; + } + music->cvt.len = original_len; + } + if ( (music->stop - pos) < original_len ) { + left = (original_len - (music->stop - pos)); + original_len -= left; + left = (int)((double)left*music->cvt.len_ratio); + } + original_len = SDL_RWread(music->rw, music->cvt.buf,1,original_len); + /* 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 ) { + left = (len - (music->stop - pos)); + len -= left; + } + data = SDL_stack_alloc(Uint8, len); + if (data) + { + SDL_RWread(music->rw, data, len, 1); + SDL_MixAudio(stream, data, len, wavestream_volume); + SDL_stack_free(data); + } + } + } + return left; +} + +/* 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->cvt.buf ) { + SDL_free(wave->cvt.buf); + } + if ( wave->freerw ) { + SDL_RWclose(wave->rw); + } + SDL_free(wave); + } +} + +/* Return non-zero if a stream is currently playing */ +int WAVStream_Active(void) +{ + int active; + + active = 0; + if ( music && (SDL_RWtell(music->rw) < 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 *)SDL_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"); + SDL_free(chunk->data); + return(-1); + } + } else { + SDL_RWseek(src, chunk->length, RW_SEEK_CUR); + } + return(chunk->length); +} + +static SDL_RWops *LoadWAVStream (SDL_RWops *src, SDL_AudioSpec *spec, + long *start, long *stop) +{ + int was_error; + Chunk chunk; + int lenread; + + /* WAV magic header */ + Uint32 RIFFchunk; + Uint32 wavelen; + Uint32 WAVEmagic; + + /* FMT chunk */ + WaveFMT *format = NULL; + + was_error = 0; + + /* 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 ) { + SDL_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 ) { + SDL_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 ) { + SDL_free(format); + } + if ( was_error ) { + return NULL; + } + return(src); +} + +/* 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 SDL_RWops *LoadAIFFStream (SDL_RWops *src, SDL_AudioSpec *spec, + long *start, long *stop) +{ + int was_error; + int found_SSND; + int found_COMM; + + 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; + + was_error = 0; + + /* 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, RW_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 ( was_error ) { + return NULL; + } + return(src); +} + diff --git a/contrib/sdk/sources/SDL_mixer-1.2.12/wavestream.h b/contrib/sdk/sources/SDL_mixer-1.2.12/wavestream.h new file mode 100644 index 0000000000..9d119dc152 --- /dev/null +++ b/contrib/sdk/sources/SDL_mixer-1.2.12/wavestream.h @@ -0,0 +1,60 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2012 Sam Lantinga + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ + +/* $Id$ */ + +/* This file supports streaming WAV files, without volume adjustment */ + +#include + +typedef struct { + SDL_RWops *rw; + SDL_bool freerw; + long start; + long stop; + SDL_AudioCVT cvt; +} WAVStream; + +/* Initialize the WAVStream player, with the given mixer settings + This function returns 0, or -1 if there was an error. + */ +extern int WAVStream_Init(SDL_AudioSpec *mixer); + +/* Unimplemented */ +extern void WAVStream_SetVolume(int volume); + +/* Load a WAV stream from an SDL_RWops object */ +extern WAVStream *WAVStream_LoadSong_RW(SDL_RWops *rw, const char *magic, int freerw); + +/* Start playback of a given WAV stream */ +extern void WAVStream_Start(WAVStream *wave); + +/* Play some of a stream previously started with WAVStream_Start() */ +extern int WAVStream_PlaySome(Uint8 *stream, int len); + +/* Stop playback of a stream previously started with WAVStream_Start() */ +extern void WAVStream_Stop(void); + +/* Close the given WAV stream */ +extern void WAVStream_FreeSong(WAVStream *wave); + +/* Return non-zero if a stream is currently playing */ +extern int WAVStream_Active(void);