Added port(WIP) SDL_mixer-1.2.12
OGG, AIFF and VOC only support. git-svn-id: svn://kolibrios.org@9565 a494cfbc-eb01-0410-851d-a64ba20cac60
This commit is contained in:
parent
7ff1bcb168
commit
cb09ffbbd3
|
@ -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
|
|
@ -0,0 +1,635 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 */
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 */
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <tremor/ivorbisfile.h>
|
||||
#else
|
||||
#include <vorbis/vorbisfile.h>
|
||||
#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();
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_mixer.h"
|
||||
|
||||
#define __MIX_INTERNAL_EFFECT__
|
||||
#include "effects_internal.h"
|
||||
|
||||
/* profile code:
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
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 ... */
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#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 ... */
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SDL_mutex.h"
|
||||
#include "SDL_endian.h"
|
||||
#include "SDL_timer.h"
|
||||
|
||||
#include "SDL_mixer.h"
|
||||
#include "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
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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
|
|
@ -0,0 +1,462 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SDL_mutex.h"
|
||||
#include "SDL_endian.h"
|
||||
#include "SDL_timer.h"
|
||||
|
||||
#include "SDL_mixer.h"
|
||||
#include "load_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 ... */
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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);
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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 */
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <tremor/ivorbisfile.h>
|
||||
#else
|
||||
#include <vorbis/vorbisfile.h>
|
||||
#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 */
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
native_midi: Hardware Midi support for the SDL_mixer library
|
||||
Copyright (C) 2000 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
|
||||
|
||||
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 <SDL_rwops.h>
|
||||
|
||||
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_ */
|
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
native_midi: Hardware Midi support for the SDL_mixer library
|
||||
Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
/* 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; i<tracks; i++)
|
||||
{
|
||||
SDL_RWread(rw, &ID, 1, 4); /* We might want to verify this is MTrk... */
|
||||
SDL_RWread(rw, &size, 1, 4);
|
||||
size = BE_LONG(size);
|
||||
mididata->track[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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
native_midi: Hardware Midi support for the SDL_mixer library
|
||||
Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
|
||||
|
||||
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_ */
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
native_midi_haiku: Native Midi support on Haiku for the SDL_mixer library
|
||||
Copyright (C) 2010 Egor Suvorov <egor_suvorov@mail.ru>
|
||||
|
||||
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <MidiStore.h>
|
||||
#include <MidiDefs.h>
|
||||
#include <MidiSynthFile.h>
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
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__ */
|
|
@ -0,0 +1,644 @@
|
|||
/*
|
||||
native_midi_mac: Native Midi support on MacOS for the SDL_mixer library
|
||||
Copyright (C) 2001 Max Horn <max@quendi.de>
|
||||
|
||||
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 <QuickTime/QuickTimeMusic.h>
|
||||
#else
|
||||
#include <QuickTimeMusic.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
/* 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 */
|
|
@ -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 <icculus@icculus.org>
|
||||
|
||||
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 <Carbon/Carbon.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
#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 */
|
||||
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
native_midi: Hardware Midi support for the SDL_mixer library
|
||||
Copyright (C) 2000,2001 Florian 'Proff' Schulze <florian.proff.schulze@gmx.net>
|
||||
|
||||
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 <windows.h>
|
||||
#include <windowsx.h>
|
||||
#include <mmsystem.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#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->NewPos<song->Size)
|
||||
{
|
||||
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 */
|
|
@ -0,0 +1,526 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_mutex.h"
|
||||
#include "SDL_rwops.h"
|
||||
#include "SDL_endian.h"
|
||||
|
||||
#include "SDL_mixer.h"
|
||||
#include "wavestream.h"
|
||||
|
||||
/*
|
||||
Taken with permission from SDL_wave.h, part of the SDL library,
|
||||
available at: http://www.libsdl.org/
|
||||
and placed under the same license as this mixer library.
|
||||
*/
|
||||
|
||||
/* WAVE files are little-endian */
|
||||
|
||||
/*******************************************/
|
||||
/* Define values for Microsoft WAVE format */
|
||||
/*******************************************/
|
||||
#define RIFF 0x46464952 /* "RIFF" */
|
||||
#define WAVE 0x45564157 /* "WAVE" */
|
||||
#define FACT 0x74636166 /* "fact" */
|
||||
#define LIST 0x5453494c /* "LIST" */
|
||||
#define FMT 0x20746D66 /* "fmt " */
|
||||
#define DATA 0x61746164 /* "data" */
|
||||
#define PCM_CODE 1
|
||||
#define ADPCM_CODE 2
|
||||
#define WAVE_MONO 1
|
||||
#define WAVE_STEREO 2
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
SDL_mixer: An audio mixer library based on the SDL library
|
||||
Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
|
||||
|
||||
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 <stdio.h>
|
||||
|
||||
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);
|
Loading…
Reference in New Issue