Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
62 KB
Referenced Files
None
Subscribers
None
diff --git a/util/input/linux_joystick.cpp b/util/input/linux_joystick.cpp
index 76551d42..adc5bb09 100644
--- a/util/input/linux_joystick.cpp
+++ b/util/input/linux_joystick.cpp
@@ -1,390 +1,390 @@
#ifdef LINUX
/* based on
* jstest.c Version 1.2
*
* Copyright (c) 1996-1999 Vojtech Pavlik
*
* Sponsored by SuSE
*/
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <iostream>
#include <sstream>
#include <linux/input.h>
#include <linux/joystick.h>
#include "linux_joystick.h"
#include "util/debug.h"
using namespace std;
static const char *axis_names[ABS_MAX + 1] = {
"X", "Y", "Z", "Rx", "Ry", "Rz", "Throttle", "Rudder",
"Wheel", "Gas", "Brake", "?", "?", "?", "?", "?",
"Hat0X", "Hat0Y", "Hat1X", "Hat1Y", "Hat2X", "Hat2Y", "Hat3X", "Hat3Y",
"?", "?", "?", "?", "?", "?", "?",
};
static const char *button_names[KEY_MAX - BTN_MISC + 1] = {
"Btn0", "Btn1", "Btn2", "Btn3", "Btn4", "Btn5", "Btn6", "Btn7", "Btn8", "Btn9", "?", "?", "?", "?", "?", "?",
"LeftBtn", "RightBtn", "MiddleBtn", "SideBtn", "ExtraBtn", "ForwardBtn", "BackBtn", "TaskBtn", "?", "?", "?", "?", "?", "?", "?", "?",
"Trigger", "ThumbBtn", "ThumbBtn2", "TopBtn", "TopBtn2", "PinkieBtn", "BaseBtn", "BaseBtn2", "BaseBtn3", "BaseBtn4", "BaseBtn5", "BaseBtn6", "BtnDead",
"BtnA", "BtnB", "BtnC", "BtnX", "BtnY", "BtnZ", "BtnTL", "BtnTR", "BtnTL2", "BtnTR2", "BtnSelect", "BtnStart", "BtnMode", "BtnThumbL", "BtnThumbR", "?",
"?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
"WheelBtn", "Gear up",
};
/* tries to open one /dev/input/jsX and return an open file descriptor */
static int read_joystick(){
for (int num = 0; num < 100; num++){
ostringstream name;
name << "/dev/input/js" << num;
- const char * cname = name.str().c_str();
- int fd = open(cname, O_RDONLY);
+ string raw = name.str();
+ int fd = open(raw.c_str(), O_RDONLY);
if (fd >= 0){
return fd;
}
}
return -1;
}
LinuxJoystick::LinuxJoystick():
button(0),
axis(0){
file = read_joystick();
if (file == -1){
Global::debug(0) << "Could not open joystick" << endl;
} else {
Global::debug(1) << "Opened joystick " << endl;
/* grab useful info */
ioctl(file, JSIOCGVERSION, &version);
ioctl(file, JSIOCGAXES, &axes);
ioctl(file, JSIOCGBUTTONS, &buttons);
ioctl(file, JSIOCGNAME(NAME_LENGTH), name);
ioctl(file, JSIOCGAXMAP, axmap);
ioctl(file, JSIOCGBTNMAP, btnmap);
/* put joystick in nonblocking mode */
fcntl(file, F_SETFL, O_NONBLOCK);
axis = new int[axes];
memset(axis, 0, sizeof(int) * axes);
button = new char[buttons];
memset(button, 0, sizeof(char) * buttons);
Global::debug(1) << "Joystick axis: " << (int)axes << " buttons: " << (int)buttons << endl;
}
}
LinuxJoystick::~LinuxJoystick(){
delete[] axis;
delete[] button;
if (file != -1){
close(file);
}
}
void LinuxJoystick::poll(){
struct js_event js;
if (file == -1){
return;
}
/* from an email by Vojtech Pavlik:
* "if you #include <linux/joystick.h>, the struct is defined in such a way
* that gcc doesn't add padding."
*/
int bytes = read(file, &js, sizeof(struct js_event));
if (bytes == sizeof(struct js_event)){
Global::debug(4) << "Event: type " << (int)js.type << " time " << (int)js.time << " number " << (int)js.number << " value " << (int)js.value << endl;
if (errno != EAGAIN) {
perror("joystick: error reading");
return;
}
switch (js.type & ~JS_EVENT_INIT) {
case JS_EVENT_BUTTON:
button[js.number] = js.value;
break;
case JS_EVENT_AXIS:
axis[js.number] = js.value;
break;
}
}
}
/*
JoystickInput LinuxJoystick::readAll(){
JoystickInput input;
if (file == -1){
return input;
}
if (axes > 0){
if (axis[0] < 0){
input.left = true;
}
if (axis[0] > 0){
input.right = true;
}
if (axes > 1){
if (axis[1] < 0){
input.up = true;
}
if (axis[1] > 0){
input.down = true;
}
}
}
if (buttons > 0 && button[0]){
input.button1 = true;
}
if (buttons > 1 && button[1]){
input.button2 = true;
}
if (buttons > 2 && button[2]){
input.button3 = true;
}
if (buttons > 3 && button[3]){
input.button4 = true;
}
Global::debug(1) << "joystick input up " << input.up
<< " down " << input.down
<< " left " << input.left
<< " right " << input.right
<< " button1 " << input.button1
<< " button2 " << input.button2
<< " button3 " << input.button3
<< " button4 " << input.button4
<< endl;
return input;
}
*/
#if 0
int main (int argc, char **argv)
{
int fd, i;
unsigned char axes = 2;
unsigned char buttons = 2;
int version = 0x000800;
char name[NAME_LENGTH] = "Unknown";
uint16_t btnmap[KEY_MAX - BTN_MISC + 1];
uint8_t axmap[ABS_MAX + 1];
if (argc < 2 || argc > 3 || !strcmp("--help", argv[1])) {
puts("");
puts("Usage: jstest [<mode>] <device>");
puts("");
puts("Modes:");
puts(" --normal One-line mode showing immediate status");
puts(" --old Same as --normal, using 0.x interface");
puts(" --event Prints events as they come in");
puts(" --nonblock Same as --event, in nonblocking mode");
puts(" --select Same as --event, using select() call");
puts("");
return 1;
}
if ((fd = open(argv[argc - 1], O_RDONLY)) < 0) {
perror("jstest");
return 1;
}
ioctl(fd, JSIOCGVERSION, &version);
ioctl(fd, JSIOCGAXES, &axes);
ioctl(fd, JSIOCGBUTTONS, &buttons);
ioctl(fd, JSIOCGNAME(NAME_LENGTH), name);
ioctl(fd, JSIOCGAXMAP, axmap);
ioctl(fd, JSIOCGBTNMAP, btnmap);
printf("Driver version is %d.%d.%d.\n",
version >> 16, (version >> 8) & 0xff, version & 0xff);
printf("Joystick (%s) has %d axes (", name, axes);
for (i = 0; i < axes; i++)
printf("%s%s", i > 0 ? ", " : "", axis_names[axmap[i]]);
puts(")");
printf("and %d buttons (", buttons);
for (i = 0; i < buttons; i++)
printf("%s%s", i > 0 ? ", " : "", button_names[btnmap[i] - BTN_MISC]);
puts(").");
printf("Testing ... (interrupt to exit)\n");
/*
* Old (0.x) interface.
*/
if ((argc == 2 && version < 0x010000) || !strcmp("--old", argv[1])) {
struct JS_DATA_TYPE js;
while (1) {
if (read(fd, &js, JS_RETURN) != JS_RETURN) {
perror("\njstest: error reading");
return 1;
}
printf("Axes: X:%3d Y:%3d Buttons: A:%s B:%s\r",
js.x, js.y, (js.buttons & 1) ? "on " : "off", (js.buttons & 2) ? "on " : "off");
fflush(stdout);
usleep(10000);
}
}
/*
* Event interface, single line readout.
*/
if (argc == 2 || !strcmp("--normal", argv[1])) {
int *axis;
char *button;
int i;
struct js_event js;
axis = calloc(axes, sizeof(int));
button = calloc(buttons, sizeof(char));
while (1) {
if (read(fd, &js, sizeof(struct js_event)) != sizeof(struct js_event)) {
perror("\njstest: error reading");
return 1;
}
switch(js.type & ~JS_EVENT_INIT) {
case JS_EVENT_BUTTON:
button[js.number] = js.value;
break;
case JS_EVENT_AXIS:
axis[js.number] = js.value;
break;
}
printf("\r");
if (axes) {
printf("Axes: ");
for (i = 0; i < axes; i++)
printf("%2d:%6d ", i, axis[i]);
}
if (buttons) {
printf("Buttons: ");
for (i = 0; i < buttons; i++)
printf("%2d:%s ", i, button[i] ? "on " : "off");
}
fflush(stdout);
}
}
/*
* Event interface, events being printed.
*/
if (!strcmp("--event", argv[1])) {
struct js_event js;
while (1) {
if (read(fd, &js, sizeof(struct js_event)) != sizeof(struct js_event)) {
perror("\njstest: error reading");
return 1;
}
printf("Event: type %d, time %d, number %d, value %d\n",
js.type, js.time, js.number, js.value);
fflush(stdout);
}
}
/*
* Reading in nonblocking mode.
*/
if (!strcmp("--nonblock", argv[1])) {
struct js_event js;
fcntl(fd, F_SETFL, O_NONBLOCK);
while (1) {
while (read(fd, &js, sizeof(struct js_event)) == sizeof(struct js_event)) {
printf("Event: type %d, time %d, number %d, value %d\n",
js.type, js.time, js.number, js.value);
}
if (errno != EAGAIN) {
perror("\njstest: error reading");
return 1;
}
usleep(10000);
}
}
/*
* Using select() on joystick fd.
*/
if (!strcmp("--select", argv[1])) {
struct js_event js;
struct timeval tv;
fd_set set;
tv.tv_sec = 1;
tv.tv_usec = 0;
while (1) {
FD_ZERO(&set);
FD_SET(fd, &set);
if (select(fd+1, &set, NULL, NULL, &tv)) {
if (read(fd, &js, sizeof(struct js_event)) != sizeof(struct js_event)) {
perror("\njstest: error reading");
return 1;
}
printf("Event: type %d, time %d, number %d, value %d\n",
js.type, js.time, js.number, js.value);
}
}
}
printf("jstest: unknown mode: %s\n", argv[1]);
return -1;
}
#endif
#endif
diff --git a/util/sound/sdl/mixer/convert.cpp b/util/sound/sdl/mixer/convert.cpp
index f9f55678..c10d6570 100644
--- a/util/sound/sdl/mixer/convert.cpp
+++ b/util/sound/sdl/mixer/convert.cpp
@@ -1,70 +1,72 @@
#include "convert.h"
#include "SDL_mixer.h"
#include "util/sound/audio.h"
Util::Encoding encoding(int format){
switch (format){
case AUDIO_U8: return Util::Unsigned8;
case AUDIO_S16: return Util::Signed16;
case AUDIO_U16: return Util::Unsigned16;
#if SDL_VERSION_ATLEAST(1, 3, 0)
case AUDIO_F32LSB:
case AUDIO_F32MSB: return Util::Float32;
#endif
}
return Util::Signed16;
}
/* use SDL to convert between formats */
int convertFormat(SDL_AudioSpec * wav, int format, Mix_Chunk * chunk){
SDL_AudioCVT wavecvt;
int samplesize;
if (SDL_BuildAudioCVT(&wavecvt,
wav->format, wav->channels, wav->freq,
format, wav->channels, wav->freq) < 0){
SDL_FreeWAV(chunk->abuf);
free(chunk);
return 1;
}
samplesize = ((wav->format & 0xFF)/8)*wav->channels;
wavecvt.len = chunk->alen & ~(samplesize-1);
wavecvt.buf = (Uint8 *)malloc(wavecvt.len*wavecvt.len_mult);
if ( wavecvt.buf == NULL ) {
SDL_SetError("Out of memory");
SDL_FreeWAV(chunk->abuf);
free(chunk);
return 1;
}
memcpy(wavecvt.buf, chunk->abuf, chunk->alen);
SDL_FreeWAV(chunk->abuf);
if ( SDL_ConvertAudio(&wavecvt) < 0 ) {
free(wavecvt.buf);
free(chunk);
return 1;
}
chunk->abuf = wavecvt.buf;
chunk->alen = wavecvt.len_cvt;
return 0;
}
-extern "C" void convertAudio(SDL_AudioSpec * wav, SDL_AudioSpec * mixer, Mix_Chunk *chunk){
+extern "C" int convertAudio(SDL_AudioSpec * wav, SDL_AudioSpec * mixer, Mix_Chunk *chunk){
// printf("Convert format %d, channels %d, frequency %d to format %d, channels %d, frequency %d\n", wav->format, wav->channels, wav->freq, mixer->format, mixer->channels, mixer->freq);
if (convertFormat(wav, mixer->format, chunk)){
printf("Could not convert format!\n");
- return;
+ return 1;
}
// printf("Mixer format %d to encoding %d\n", mixer->format, encoding(mixer->format));
/* format is now what the mixer wants */
Util::AudioConverter convert(encoding(mixer->format), wav->channels, wav->freq,
encoding(mixer->format), mixer->channels, mixer->freq);
unsigned int size = convert.convertedLength(chunk->alen);
unsigned char * data = (unsigned char *) malloc(size > chunk->alen ? size : chunk->alen);
memcpy(data, chunk->abuf, chunk->alen);
convert.convert(data, chunk->alen);
SDL_FreeWAV(chunk->abuf);
chunk->abuf = data;
chunk->alen = size;
+
+ return 0;
}
diff --git a/util/sound/sdl/mixer/convert.h b/util/sound/sdl/mixer/convert.h
index 7ec9d97f..33338bc2 100644
--- a/util/sound/sdl/mixer/convert.h
+++ b/util/sound/sdl/mixer/convert.h
@@ -1,15 +1,15 @@
#ifndef _paintown_sdl_mixer_convert_h
#define _paintown_sdl_mixer_convert_h
struct SDL_AudioSpec;
struct Mix_Chunk;
#ifdef __cplusplus
extern "C" {
#endif
-void convertAudio(SDL_AudioSpec * wav, SDL_AudioSpec * mixer, Mix_Chunk *chunk);
+int convertAudio(SDL_AudioSpec * wav, SDL_AudioSpec * mixer, Mix_Chunk *chunk);
#ifdef __cplusplus
}
#endif
#endif
diff --git a/util/sound/sdl/mixer/mixer.c b/util/sound/sdl/mixer/mixer.c
index 2e0370ce..ee8da73b 100644
--- a/util/sound/sdl/mixer/mixer.c
+++ b/util/sound/sdl/mixer/mixer.c
@@ -1,1453 +1,1456 @@
/*
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
Sam Lantinga
slouken@libsdl.org
*/
/* $Id: mixer.c 5243 2009-11-14 19:31:39Z slouken $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "SDL_mutex.h"
#include "SDL_endian.h"
#include "SDL_timer.h"
#include "SDL_mixer.h"
#include "load_aiff.h"
#include "load_voc.h"
#include "load_ogg.h"
#include "load_flac.h"
#include "dynamic_flac.h"
#include "dynamic_mod.h"
#include "dynamic_mp3.h"
#include "dynamic_ogg.h"
#include "convert.h"
#define __MIX_INTERNAL_EFFECT__
#include "effects_internal.h"
/* Magic numbers for various audio file formats */
#define RIFF 0x46464952 /* "RIFF" */
#define WAVE 0x45564157 /* "WAVE" */
#define FORM 0x4d524f46 /* "FORM" */
#define OGGS 0x5367674f /* "OggS" */
#define CREA 0x61657243 /* "Crea" */
#define FLAC 0x43614C66 /* "fLaC" */
static int audio_opened = 0;
static SDL_AudioSpec mixer;
typedef struct _Mix_effectinfo
{
Mix_EffectFunc_t callback;
Mix_EffectDone_t done_callback;
void *udata;
struct _Mix_effectinfo *next;
} effect_info;
static struct _Mix_Channel {
Mix_Chunk *chunk;
int playing;
int paused;
Uint8 *samples;
int volume;
int looping;
int tag;
Uint32 expire;
Uint32 start_time;
Mix_Fading fading;
int fade_volume;
int fade_volume_reset;
Uint32 fade_length;
Uint32 ticks_fade;
effect_info *effects;
} *mix_channel = NULL;
static effect_info *posteffects = NULL;
static int num_channels;
static int reserved_channels = 0;
/* Support for hooking into the mixer callback system */
static void (*mix_postmix)(void *udata, Uint8 *stream, int len) = NULL;
static void *mix_postmix_data = NULL;
/* rcg07062001 callback to alert when channels are done playing. */
static void (*channel_done_callback)(int channel) = NULL;
/* Music function declarations */
extern int open_music(SDL_AudioSpec *mixer);
extern void close_music(void);
/* Support for user defined music functions, plus the default one */
extern int volatile music_active;
extern void music_mixer(void *udata, Uint8 *stream, int len);
static void (*mix_music)(void *udata, Uint8 *stream, int len) = music_mixer;
static void *music_data = NULL;
/* rcg06042009 report available decoders at runtime. */
static const char **chunk_decoders = NULL;
static int num_decoders = 0;
int Mix_GetNumChunkDecoders(void)
{
return(num_decoders);
}
const char *Mix_GetChunkDecoder(int index)
{
if ((index < 0) || (index >= num_decoders)) {
return NULL;
}
return(chunk_decoders[index]);
}
static void add_chunk_decoder(const char *decoder)
{
void *ptr = realloc(chunk_decoders, (num_decoders + 1) * sizeof (const char **));
if (ptr == NULL) {
return; /* oh well, go on without it. */
}
chunk_decoders = (const char **) ptr;
chunk_decoders[num_decoders++] = decoder;
}
/* rcg06192001 get linked library's version. */
const SDL_version *Mix_Linked_Version(void)
{
static SDL_version linked_version;
SDL_MIXER_VERSION(&linked_version);
return(&linked_version);
}
static int initialized = 0;
int Mix_Init(int flags)
{
int result = 0;
if (flags & MIX_INIT_FLAC) {
#ifdef FLAC_MUSIC
if ((initialized & MIX_INIT_FLAC) || Mix_InitFLAC() == 0) {
result |= MIX_INIT_FLAC;
}
#else
Mix_SetError("Mixer not built with FLAC support");
#endif
}
if (flags & MIX_INIT_MOD) {
#ifdef MOD_MUSIC
if ((initialized & MIX_INIT_MOD) || Mix_InitMOD() == 0) {
result |= MIX_INIT_MOD;
}
#else
Mix_SetError("Mixer not built with MOD support");
#endif
}
if (flags & MIX_INIT_MP3) {
#ifdef MP3_MUSIC
if ((initialized & MIX_INIT_MP3) || Mix_InitMP3() == 0) {
result |= MIX_INIT_MP3;
}
#else
Mix_SetError("Mixer not built with MP3 support");
#endif
}
if (flags & MIX_INIT_OGG) {
#ifdef OGG_MUSIC
if ((initialized & MIX_INIT_OGG) || Mix_InitOgg() == 0) {
result |= MIX_INIT_OGG;
}
#else
Mix_SetError("Mixer not built with Ogg Vorbis support");
#endif
}
initialized |= result;
return (result);
}
void Mix_Quit()
{
#ifdef FLAC_MUSIC
if (initialized & MIX_INIT_FLAC) {
Mix_QuitFLAC();
}
#endif
#ifdef MOD_MUSIC
if (initialized & MIX_INIT_MOD) {
Mix_QuitMOD();
}
#endif
#ifdef MP3_MUSIC
if (initialized & MIX_INIT_MP3) {
Mix_QuitMP3();
}
#endif
#ifdef OGG_MUSIC
if (initialized & MIX_INIT_OGG) {
Mix_QuitOgg();
}
#endif
initialized = 0;
}
static int _Mix_remove_all_effects(int channel, effect_info **e);
/*
* rcg06122001 Cleanup effect callbacks.
* MAKE SURE SDL_LockAudio() is called before this (or you're in the
* audio callback).
*/
static void _Mix_channel_done_playing(int channel)
{
if (channel_done_callback) {
channel_done_callback(channel);
}
/*
* Call internal function directly, to avoid locking audio from
* inside audio callback.
*/
_Mix_remove_all_effects(channel, &mix_channel[channel].effects);
}
static void *Mix_DoEffects(int chan, void *snd, int len)
{
int posteffect = (chan == MIX_CHANNEL_POST);
effect_info *e = ((posteffect) ? posteffects : mix_channel[chan].effects);
void *buf = snd;
if (e != NULL) { /* are there any registered effects? */
/* if this is the postmix, we can just overwrite the original. */
if (!posteffect) {
buf = malloc(len);
if (buf == NULL) {
return(snd);
}
memcpy(buf, snd, len);
}
for (; e != NULL; e = e->next) {
if (e->callback != NULL) {
e->callback(chan, buf, len, e->udata);
}
}
}
/* be sure to free() the return value if != snd ... */
return(buf);
}
/* Mixing function */
static void mix_channels(void *udata, Uint8 *stream, int len)
{
Uint8 *mix_input;
int i, mixable, volume = SDL_MIX_MAXVOLUME;
Uint32 sdl_ticks;
#if SDL_VERSION_ATLEAST(1, 3, 0)
/* Need to initialize the stream in SDL 1.3+ */
memset(stream, mixer.silence, len);
#endif
/* Mix the music (must be done before the channels are added) */
if ( music_active || (mix_music != music_mixer) ) {
mix_music(music_data, stream, len);
}
/* Mix any playing channels... */
sdl_ticks = SDL_GetTicks();
for ( i=0; i<num_channels; ++i ) {
if( ! mix_channel[i].paused ) {
if ( mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks ) {
/* Expiration delay for that channel is reached */
mix_channel[i].playing = 0;
mix_channel[i].fading = MIX_NO_FADING;
mix_channel[i].expire = 0;
_Mix_channel_done_playing(i);
} else if ( mix_channel[i].fading != MIX_NO_FADING ) {
Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade;
if( ticks > mix_channel[i].fade_length ) {
Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */
if( mix_channel[i].fading == MIX_FADING_OUT ) {
mix_channel[i].playing = 0;
mix_channel[i].expire = 0;
_Mix_channel_done_playing(i);
}
mix_channel[i].fading = MIX_NO_FADING;
} else {
if( mix_channel[i].fading == MIX_FADING_OUT ) {
Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks))
/ mix_channel[i].fade_length );
} else {
Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length );
}
}
}
if ( mix_channel[i].playing > 0 ) {
int index = 0;
int remaining = len;
while (mix_channel[i].playing > 0 && index < len) {
remaining = len - index;
volume = (mix_channel[i].volume*mix_channel[i].chunk->volume) / MIX_MAX_VOLUME;
mixable = mix_channel[i].playing;
if ( mixable > remaining ) {
mixable = remaining;
}
mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable);
SDL_MixAudio(stream+index,mix_input,mixable,volume);
if (mix_input != mix_channel[i].samples)
free(mix_input);
mix_channel[i].samples += mixable;
mix_channel[i].playing -= mixable;
index += mixable;
/* rcg06072001 Alert app if channel is done playing. */
if (!mix_channel[i].playing && !mix_channel[i].looping) {
_Mix_channel_done_playing(i);
}
}
/* If looping the sample and we are at its end, make sure
we will still return a full buffer */
while ( mix_channel[i].looping && index < len ) {
int alen = mix_channel[i].chunk->alen;
remaining = len - index;
if (remaining > alen) {
remaining = alen;
}
mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining);
SDL_MixAudio(stream+index, mix_input, remaining, volume);
if (mix_input != mix_channel[i].chunk->abuf)
free(mix_input);
--mix_channel[i].looping;
mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining;
mix_channel[i].playing = mix_channel[i].chunk->alen - remaining;
index += remaining;
}
if ( ! mix_channel[i].playing && mix_channel[i].looping ) {
--mix_channel[i].looping;
mix_channel[i].samples = mix_channel[i].chunk->abuf;
mix_channel[i].playing = mix_channel[i].chunk->alen;
}
}
}
}
/* rcg06122001 run posteffects... */
Mix_DoEffects(MIX_CHANNEL_POST, stream, len);
if ( mix_postmix ) {
mix_postmix(mix_postmix_data, stream, len);
}
}
#if 0
static void PrintFormat(char *title, SDL_AudioSpec *fmt)
{
printf("%s: %d bit %s audio (%s) at %u Hz\n", title, (fmt->format&0xFF),
(fmt->format&0x8000) ? "signed" : "unsigned",
(fmt->channels > 2) ? "surround" :
(fmt->channels > 1) ? "stereo" : "mono", fmt->freq);
}
#endif
/* Open the mixer with a certain desired audio format */
int Mix_OpenAudio(int frequency, Uint16 format, int nchannels, int chunksize)
{
int i;
SDL_AudioSpec desired;
/* If the mixer is already opened, increment open count */
if ( audio_opened ) {
if ( format == mixer.format && nchannels == mixer.channels ) {
++audio_opened;
return(0);
}
while ( audio_opened ) {
Mix_CloseAudio();
}
}
/* Set the desired format and frequency */
desired.freq = frequency;
desired.format = format;
desired.channels = nchannels;
desired.samples = chunksize;
desired.callback = mix_channels;
desired.userdata = NULL;
/* Accept nearly any audio format */
if ( SDL_OpenAudio(&desired, &mixer) < 0 ) {
return(-1);
}
#if 0
PrintFormat("Audio device", &mixer);
#endif
/* Initialize the music players */
if ( open_music(&mixer) < 0 ) {
SDL_CloseAudio();
return(-1);
}
num_channels = MIX_CHANNELS;
mix_channel = (struct _Mix_Channel *) malloc(num_channels * sizeof(struct _Mix_Channel));
/* Clear out the audio channels */
for ( i=0; i<num_channels; ++i ) {
mix_channel[i].chunk = NULL;
mix_channel[i].playing = 0;
mix_channel[i].looping = 0;
mix_channel[i].volume = SDL_MIX_MAXVOLUME;
mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;
mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME;
mix_channel[i].fading = MIX_NO_FADING;
mix_channel[i].tag = -1;
mix_channel[i].expire = 0;
mix_channel[i].effects = NULL;
mix_channel[i].paused = 0;
}
Mix_VolumeMusic(SDL_MIX_MAXVOLUME);
_Mix_InitEffects();
/* This list is (currently) decided at build time. */
add_chunk_decoder("WAVE");
add_chunk_decoder("AIFF");
add_chunk_decoder("VOC");
#ifdef OGG_MUSIC
add_chunk_decoder("OGG");
#endif
#ifdef FLAC_MUSIC
add_chunk_decoder("FLAC");
#endif
audio_opened = 1;
SDL_PauseAudio(0);
return(0);
}
/* Dynamically change the number of channels managed by the mixer.
If decreasing the number of channels, the upper channels are
stopped.
*/
int Mix_AllocateChannels(int numchans)
{
if ( numchans<0 || numchans==num_channels )
return(num_channels);
if ( numchans < num_channels ) {
/* Stop the affected channels */
int i;
for(i=numchans; i < num_channels; i++) {
Mix_UnregisterAllEffects(i);
Mix_HaltChannel(i);
}
}
SDL_LockAudio();
mix_channel = (struct _Mix_Channel *) realloc(mix_channel, numchans * sizeof(struct _Mix_Channel));
if ( numchans > num_channels ) {
/* Initialize the new channels */
int i;
for(i=num_channels; i < numchans; i++) {
mix_channel[i].chunk = NULL;
mix_channel[i].playing = 0;
mix_channel[i].looping = 0;
mix_channel[i].volume = SDL_MIX_MAXVOLUME;
mix_channel[i].fade_volume = SDL_MIX_MAXVOLUME;
mix_channel[i].fade_volume_reset = SDL_MIX_MAXVOLUME;
mix_channel[i].fading = MIX_NO_FADING;
mix_channel[i].tag = -1;
mix_channel[i].expire = 0;
mix_channel[i].effects = NULL;
mix_channel[i].paused = 0;
}
}
num_channels = numchans;
SDL_UnlockAudio();
return(num_channels);
}
/* Return the actual mixer parameters */
int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels)
{
if ( audio_opened ) {
if ( frequency ) {
*frequency = mixer.freq;
}
if ( format ) {
*format = mixer.format;
}
if ( channels ) {
*channels = mixer.channels;
}
}
return(audio_opened);
}
/*
* !!! FIXME: Ideally, we want a Mix_LoadSample_RW(), which will handle the
* generic setup, then call the correct file format loader.
*/
/* Load a wave file */
Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc)
{
Uint32 magic;
Mix_Chunk *chunk;
SDL_AudioSpec wavespec, *loaded;
SDL_AudioCVT wavecvt;
int samplesize;
/* rcg06012001 Make sure src is valid */
if ( ! src ) {
SDL_SetError("Mix_LoadWAV_RW with NULL src");
return(NULL);
}
/* Make sure audio has been opened */
if ( ! audio_opened ) {
SDL_SetError("Audio device hasn't been opened");
if ( freesrc && src ) {
SDL_RWclose(src);
}
return(NULL);
}
/* Allocate the chunk memory */
chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));
if ( chunk == NULL ) {
SDL_SetError("Out of memory");
if ( freesrc ) {
SDL_RWclose(src);
}
return(NULL);
}
/* Find out what kind of audio file this is */
magic = SDL_ReadLE32(src);
/* Seek backwards for compatibility with older loaders */
SDL_RWseek(src, -(int)sizeof(Uint32), RW_SEEK_CUR);
switch (magic) {
case WAVE:
case RIFF:
loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec,
(Uint8 **)&chunk->abuf, &chunk->alen);
break;
case FORM:
loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec,
(Uint8 **)&chunk->abuf, &chunk->alen);
break;
#ifdef OGG_MUSIC
case OGGS:
loaded = Mix_LoadOGG_RW(src, freesrc, &wavespec,
(Uint8 **)&chunk->abuf, &chunk->alen);
break;
#endif
#ifdef FLAC_MUSIC
case FLAC:
loaded = Mix_LoadFLAC_RW(src, freesrc, &wavespec,
(Uint8 **)&chunk->abuf, &chunk->alen);
break;
#endif
case CREA:
loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec,
(Uint8 **)&chunk->abuf, &chunk->alen);
break;
default:
SDL_SetError("Unrecognized sound file type");
return(0);
}
if ( !loaded ) {
free(chunk);
return(NULL);
}
#if 0
PrintFormat("Audio device", &mixer);
PrintFormat("-- Wave file", &wavespec);
#endif
/* Build the audio converter and create conversion buffers */
#if 0
if ( SDL_BuildAudioCVT(&wavecvt,
wavespec.format, wavespec.channels, wavespec.freq,
mixer.format, mixer.channels, mixer.freq) < 0 ) {
SDL_FreeWAV(chunk->abuf);
free(chunk);
return(NULL);
}
samplesize = ((wavespec.format & 0xFF)/8)*wavespec.channels;
wavecvt.len = chunk->alen & ~(samplesize-1);
wavecvt.buf = (Uint8 *)malloc(wavecvt.len*wavecvt.len_mult);
if ( wavecvt.buf == NULL ) {
SDL_SetError("Out of memory");
SDL_FreeWAV(chunk->abuf);
free(chunk);
return(NULL);
}
memcpy(wavecvt.buf, chunk->abuf, chunk->alen);
SDL_FreeWAV(chunk->abuf);
/* Run the audio converter */
if ( SDL_ConvertAudio(&wavecvt) < 0 ) {
free(wavecvt.buf);
free(chunk);
return(NULL);
}
chunk->abuf = wavecvt.buf;
chunk->alen = wavecvt.len_cvt;
#endif
- convertAudio(&wavespec, &mixer, chunk);
- chunk->allocated = 1;
- chunk->volume = MIX_MAX_VOLUME;
- return(chunk);
+ if (!convertAudio(&wavespec, &mixer, chunk)){
+ chunk->allocated = 1;
+ chunk->volume = MIX_MAX_VOLUME;
+ return(chunk);
+ } else {
+ return NULL;
+ }
}
/* Load a wave file of the mixer format from a memory buffer */
Mix_Chunk *Mix_QuickLoad_WAV(Uint8 *mem)
{
Mix_Chunk *chunk;
Uint8 magic[4];
/* Make sure audio has been opened */
if ( ! audio_opened ) {
SDL_SetError("Audio device hasn't been opened");
return(NULL);
}
/* Allocate the chunk memory */
chunk = (Mix_Chunk *)calloc(1,sizeof(Mix_Chunk));
if ( chunk == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
/* Essentially just skip to the audio data (no error checking - fast) */
chunk->allocated = 0;
mem += 12; /* WAV header */
do {
memcpy(magic, mem, 4);
mem += 4;
chunk->alen = ((mem[3]<<24)|(mem[2]<<16)|(mem[1]<<8)|(mem[0]));
mem += 4;
chunk->abuf = mem;
mem += chunk->alen;
} while ( memcmp(magic, "data", 4) != 0 );
chunk->volume = MIX_MAX_VOLUME;
return(chunk);
}
/* Load raw audio data of the mixer format from a memory buffer */
Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len)
{
Mix_Chunk *chunk;
/* Make sure audio has been opened */
if ( ! audio_opened ) {
SDL_SetError("Audio device hasn't been opened");
return(NULL);
}
/* Allocate the chunk memory */
chunk = (Mix_Chunk *)malloc(sizeof(Mix_Chunk));
if ( chunk == NULL ) {
SDL_SetError("Out of memory");
return(NULL);
}
/* Essentially just point at the audio data (no error checking - fast) */
chunk->allocated = 0;
chunk->alen = len;
chunk->abuf = mem;
chunk->volume = MIX_MAX_VOLUME;
return(chunk);
}
/* Free an audio chunk previously loaded */
void Mix_FreeChunk(Mix_Chunk *chunk)
{
int i;
/* Caution -- if the chunk is playing, the mixer will crash */
if ( chunk ) {
/* Guarantee that this chunk isn't playing */
SDL_LockAudio();
if ( mix_channel ) {
for ( i=0; i<num_channels; ++i ) {
if ( chunk == mix_channel[i].chunk ) {
mix_channel[i].playing = 0;
}
}
}
SDL_UnlockAudio();
/* Actually free the chunk */
if ( chunk->allocated ) {
free(chunk->abuf);
}
free(chunk);
}
}
/* Set a function that is called after all mixing is performed.
This can be used to provide real-time visual display of the audio stream
or add a custom mixer filter for the stream data.
*/
void Mix_SetPostMix(void (*mix_func)
(void *udata, Uint8 *stream, int len), void *arg)
{
SDL_LockAudio();
mix_postmix_data = arg;
mix_postmix = mix_func;
SDL_UnlockAudio();
}
/* Add your own music player or mixer function.
If 'mix_func' is NULL, the default music player is re-enabled.
*/
void Mix_HookMusic(void (*mix_func)(void *udata, Uint8 *stream, int len),
void *arg)
{
SDL_LockAudio();
if ( mix_func != NULL ) {
music_data = arg;
mix_music = mix_func;
} else {
music_data = NULL;
mix_music = music_mixer;
}
SDL_UnlockAudio();
}
void *Mix_GetMusicHookData(void)
{
return(music_data);
}
void Mix_ChannelFinished(void (*channel_finished)(int channel))
{
SDL_LockAudio();
channel_done_callback = channel_finished;
SDL_UnlockAudio();
}
/* Reserve the first channels (0 -> n-1) for the application, i.e. don't allocate
them dynamically to the next sample if requested with a -1 value below.
Returns the number of reserved channels.
*/
int Mix_ReserveChannels(int num)
{
if (num > num_channels)
num = num_channels;
reserved_channels = num;
return num;
}
static int checkchunkintegral(Mix_Chunk *chunk)
{
int frame_width = 1;
if ((mixer.format & 0xFF) == 16) frame_width = 2;
frame_width *= mixer.channels;
while (chunk->alen % frame_width) chunk->alen--;
return chunk->alen;
}
/* Play an audio chunk on a specific channel.
If the specified channel is -1, play on the first free channel.
'ticks' is the number of milliseconds at most to play the sample, or -1
if there is no limit.
Returns which channel was used to play the sound.
*/
int Mix_PlayChannelTimed(int which, Mix_Chunk *chunk, int loops, int ticks)
{
int i;
/* Don't play null pointers :-) */
if ( chunk == NULL ) {
Mix_SetError("Tried to play a NULL chunk");
return(-1);
}
if ( !checkchunkintegral(chunk)) {
Mix_SetError("Tried to play a chunk with a bad frame");
return(-1);
}
/* Lock the mixer while modifying the playing channels */
SDL_LockAudio();
{
/* If which is -1, play on the first free channel */
if ( which == -1 ) {
for ( i=reserved_channels; i<num_channels; ++i ) {
if ( mix_channel[i].playing <= 0 )
break;
}
if ( i == num_channels ) {
Mix_SetError("No free channels available");
which = -1;
} else {
which = i;
}
}
/* Queue up the audio data for this channel */
if ( which >= 0 ) {
Uint32 sdl_ticks = SDL_GetTicks();
if (Mix_Playing(which))
_Mix_channel_done_playing(which);
mix_channel[which].samples = chunk->abuf;
mix_channel[which].playing = chunk->alen;
mix_channel[which].looping = loops;
mix_channel[which].chunk = chunk;
mix_channel[which].paused = 0;
mix_channel[which].fading = MIX_NO_FADING;
mix_channel[which].start_time = sdl_ticks;
mix_channel[which].expire = (ticks>0) ? (sdl_ticks + ticks) : 0;
}
}
SDL_UnlockAudio();
/* Return the channel on which the sound is being played */
return(which);
}
/* Change the expiration delay for a channel */
int Mix_ExpireChannel(int which, int ticks)
{
int status = 0;
if ( which == -1 ) {
int i;
for ( i=0; i < num_channels; ++ i ) {
status += Mix_ExpireChannel(i, ticks);
}
} else if ( which < num_channels ) {
SDL_LockAudio();
mix_channel[which].expire = (ticks>0) ? (SDL_GetTicks() + ticks) : 0;
SDL_UnlockAudio();
++ status;
}
return(status);
}
/* Fade in a sound on a channel, over ms milliseconds */
int Mix_FadeInChannelTimed(int which, Mix_Chunk *chunk, int loops, int ms, int ticks)
{
int i;
/* Don't play null pointers :-) */
if ( chunk == NULL ) {
return(-1);
}
if ( !checkchunkintegral(chunk)) {
Mix_SetError("Tried to play a chunk with a bad frame");
return(-1);
}
/* Lock the mixer while modifying the playing channels */
SDL_LockAudio();
{
/* If which is -1, play on the first free channel */
if ( which == -1 ) {
for ( i=reserved_channels; i<num_channels; ++i ) {
if ( mix_channel[i].playing <= 0 )
break;
}
if ( i == num_channels ) {
which = -1;
} else {
which = i;
}
}
/* Queue up the audio data for this channel */
if ( which >= 0 ) {
Uint32 sdl_ticks = SDL_GetTicks();
if (Mix_Playing(which))
_Mix_channel_done_playing(which);
mix_channel[which].samples = chunk->abuf;
mix_channel[which].playing = chunk->alen;
mix_channel[which].looping = loops;
mix_channel[which].chunk = chunk;
mix_channel[which].paused = 0;
mix_channel[which].fading = MIX_FADING_IN;
mix_channel[which].fade_volume = mix_channel[which].volume;
mix_channel[which].fade_volume_reset = mix_channel[which].volume;
mix_channel[which].volume = 0;
mix_channel[which].fade_length = (Uint32)ms;
mix_channel[which].start_time = mix_channel[which].ticks_fade = sdl_ticks;
mix_channel[which].expire = (ticks > 0) ? (sdl_ticks+ticks) : 0;
}
}
SDL_UnlockAudio();
/* Return the channel on which the sound is being played */
return(which);
}
/* Set volume of a particular channel */
int Mix_Volume(int which, int volume)
{
int i;
int prev_volume;
if ( which == -1 ) {
prev_volume = 0;
for ( i=0; i<num_channels; ++i ) {
prev_volume += Mix_Volume(i, volume);
}
prev_volume /= num_channels;
} else {
prev_volume = mix_channel[which].volume;
if ( volume >= 0 ) {
if ( volume > SDL_MIX_MAXVOLUME ) {
volume = SDL_MIX_MAXVOLUME;
}
mix_channel[which].volume = volume;
}
}
return(prev_volume);
}
/* Set volume of a particular chunk */
int Mix_VolumeChunk(Mix_Chunk *chunk, int volume)
{
int prev_volume;
prev_volume = chunk->volume;
if ( volume >= 0 ) {
if ( volume > MIX_MAX_VOLUME ) {
volume = MIX_MAX_VOLUME;
}
chunk->volume = volume;
}
return(prev_volume);
}
/* Halt playing of a particular channel */
int Mix_HaltChannel(int which)
{
int i;
if ( which == -1 ) {
for ( i=0; i<num_channels; ++i ) {
Mix_HaltChannel(i);
}
} else {
SDL_LockAudio();
if (mix_channel[which].playing) {
_Mix_channel_done_playing(which);
mix_channel[which].playing = 0;
}
mix_channel[which].expire = 0;
if(mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */
mix_channel[which].volume = mix_channel[which].fade_volume_reset;
mix_channel[which].fading = MIX_NO_FADING;
SDL_UnlockAudio();
}
return(0);
}
/* Halt playing of a particular group of channels */
int Mix_HaltGroup(int tag)
{
int i;
for ( i=0; i<num_channels; ++i ) {
if( mix_channel[i].tag == tag ) {
Mix_HaltChannel(i);
}
}
return(0);
}
/* Fade out a channel and then stop it automatically */
int Mix_FadeOutChannel(int which, int ms)
{
int status;
status = 0;
if ( audio_opened ) {
if ( which == -1 ) {
int i;
for ( i=0; i<num_channels; ++i ) {
status += Mix_FadeOutChannel(i, ms);
}
} else {
SDL_LockAudio();
if ( mix_channel[which].playing &&
(mix_channel[which].volume > 0) &&
(mix_channel[which].fading != MIX_FADING_OUT) ) {
mix_channel[which].fade_volume = mix_channel[which].volume;
mix_channel[which].fading = MIX_FADING_OUT;
mix_channel[which].fade_length = ms;
mix_channel[which].ticks_fade = SDL_GetTicks();
/* only change fade_volume_reset if we're not fading. */
if (mix_channel[which].fading == MIX_NO_FADING) {
mix_channel[which].fade_volume_reset = mix_channel[which].volume;
}
++status;
}
SDL_UnlockAudio();
}
}
return(status);
}
/* Halt playing of a particular group of channels */
int Mix_FadeOutGroup(int tag, int ms)
{
int i;
int status = 0;
for ( i=0; i<num_channels; ++i ) {
if( mix_channel[i].tag == tag ) {
status += Mix_FadeOutChannel(i,ms);
}
}
return(status);
}
Mix_Fading Mix_FadingChannel(int which)
{
return mix_channel[which].fading;
}
/* Check the status of a specific channel.
If the specified mix_channel is -1, check all mix channels.
*/
int Mix_Playing(int which)
{
int status;
status = 0;
if ( which == -1 ) {
int i;
for ( i=0; i<num_channels; ++i ) {
if ((mix_channel[i].playing > 0) ||
(mix_channel[i].looping > 0))
{
++status;
}
}
} else {
if ((mix_channel[which].playing > 0) ||
(mix_channel[which].looping > 0))
{
++status;
}
}
return(status);
}
/* rcg06072001 Get the chunk associated with a channel. */
Mix_Chunk *Mix_GetChunk(int channel)
{
Mix_Chunk *retval = NULL;
if ((channel >= 0) && (channel < num_channels)) {
retval = mix_channel[channel].chunk;
}
return(retval);
}
/* Close the mixer, halting all playing audio */
void Mix_CloseAudio(void)
{
int i;
if ( audio_opened ) {
if ( audio_opened == 1 ) {
for (i = 0; i < num_channels; i++) {
Mix_UnregisterAllEffects(i);
}
Mix_UnregisterAllEffects(MIX_CHANNEL_POST);
close_music();
Mix_HaltChannel(-1);
_Mix_DeinitEffects();
SDL_CloseAudio();
free(mix_channel);
mix_channel = NULL;
/* rcg06042009 report available decoders at runtime. */
free(chunk_decoders);
chunk_decoders = NULL;
num_decoders = 0;
}
--audio_opened;
}
}
/* Pause a particular channel (or all) */
void Mix_Pause(int which)
{
Uint32 sdl_ticks = SDL_GetTicks();
if ( which == -1 ) {
int i;
for ( i=0; i<num_channels; ++i ) {
if ( mix_channel[i].playing > 0 ) {
mix_channel[i].paused = sdl_ticks;
}
}
} else {
if ( mix_channel[which].playing > 0 ) {
mix_channel[which].paused = sdl_ticks;
}
}
}
/* Resume a paused channel */
void Mix_Resume(int which)
{
Uint32 sdl_ticks = SDL_GetTicks();
SDL_LockAudio();
if ( which == -1 ) {
int i;
for ( i=0; i<num_channels; ++i ) {
if ( mix_channel[i].playing > 0 ) {
if(mix_channel[i].expire > 0)
mix_channel[i].expire += sdl_ticks - mix_channel[i].paused;
mix_channel[i].paused = 0;
}
}
} else {
if ( mix_channel[which].playing > 0 ) {
if(mix_channel[which].expire > 0)
mix_channel[which].expire += sdl_ticks - mix_channel[which].paused;
mix_channel[which].paused = 0;
}
}
SDL_UnlockAudio();
}
int Mix_Paused(int which)
{
if ( which > num_channels )
return(0);
if ( which < 0 ) {
int status = 0;
int i;
for( i=0; i < num_channels; ++i ) {
if ( mix_channel[i].paused ) {
++ status;
}
}
return(status);
} else {
return(mix_channel[which].paused != 0);
}
}
/* Change the group of a channel */
int Mix_GroupChannel(int which, int tag)
{
if ( which < 0 || which > num_channels )
return(0);
SDL_LockAudio();
mix_channel[which].tag = tag;
SDL_UnlockAudio();
return(1);
}
/* Assign several consecutive channels to a group */
int Mix_GroupChannels(int from, int to, int tag)
{
int status = 0;
for( ; from <= to; ++ from ) {
status += Mix_GroupChannel(from, tag);
}
return(status);
}
/* Finds the first available channel in a group of channels */
int Mix_GroupAvailable(int tag)
{
int i;
for( i=0; i < num_channels; i ++ ) {
if ( ((tag == -1) || (tag == mix_channel[i].tag)) &&
(mix_channel[i].playing <= 0) )
return i;
}
return(-1);
}
int Mix_GroupCount(int tag)
{
int count = 0;
int i;
for( i=0; i < num_channels; i ++ ) {
if ( mix_channel[i].tag==tag || tag==-1 )
++ count;
}
return(count);
}
/* Finds the "oldest" sample playing in a group of channels */
int Mix_GroupOldest(int tag)
{
int chan = -1;
Uint32 mintime = SDL_GetTicks();
int i;
for( i=0; i < num_channels; i ++ ) {
if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0
&& mix_channel[i].start_time <= mintime ) {
mintime = mix_channel[i].start_time;
chan = i;
}
}
return(chan);
}
/* Finds the "most recent" (i.e. last) sample playing in a group of channels */
int Mix_GroupNewer(int tag)
{
int chan = -1;
Uint32 maxtime = 0;
int i;
for( i=0; i < num_channels; i ++ ) {
if ( (mix_channel[i].tag==tag || tag==-1) && mix_channel[i].playing > 0
&& mix_channel[i].start_time >= maxtime ) {
maxtime = mix_channel[i].start_time;
chan = i;
}
}
return(chan);
}
/*
* rcg06122001 The special effects exportable API.
* Please see effect_*.c for internally-implemented effects, such
* as Mix_SetPanning().
*/
/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
static int _Mix_register_effect(effect_info **e, Mix_EffectFunc_t f,
Mix_EffectDone_t d, void *arg)
{
effect_info *new_e = malloc(sizeof (effect_info));
if (!e) {
Mix_SetError("Internal error");
return(0);
}
if (f == NULL) {
Mix_SetError("NULL effect callback");
return(0);
}
if (new_e == NULL) {
Mix_SetError("Out of memory");
return(0);
}
new_e->callback = f;
new_e->done_callback = d;
new_e->udata = arg;
new_e->next = NULL;
/* add new effect to end of linked list... */
if (*e == NULL) {
*e = new_e;
} else {
effect_info *cur = *e;
while (1) {
if (cur->next == NULL) {
cur->next = new_e;
break;
}
cur = cur->next;
}
}
return(1);
}
/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
static int _Mix_remove_effect(int channel, effect_info **e, Mix_EffectFunc_t f)
{
effect_info *cur;
effect_info *prev = NULL;
effect_info *next = NULL;
if (!e) {
Mix_SetError("Internal error");
return(0);
}
for (cur = *e; cur != NULL; cur = cur->next) {
if (cur->callback == f) {
next = cur->next;
if (cur->done_callback != NULL) {
cur->done_callback(channel, cur->udata);
}
free(cur);
if (prev == NULL) { /* removing first item of list? */
*e = next;
} else {
prev->next = next;
}
return(1);
}
prev = cur;
}
Mix_SetError("No such effect registered");
return(0);
}
/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
static int _Mix_remove_all_effects(int channel, effect_info **e)
{
effect_info *cur;
effect_info *next;
if (!e) {
Mix_SetError("Internal error");
return(0);
}
for (cur = *e; cur != NULL; cur = next) {
next = cur->next;
if (cur->done_callback != NULL) {
cur->done_callback(channel, cur->udata);
}
free(cur);
}
*e = NULL;
return(1);
}
/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
int _Mix_RegisterEffect_locked(int channel, Mix_EffectFunc_t f,
Mix_EffectDone_t d, void *arg)
{
effect_info **e = NULL;
if (channel == MIX_CHANNEL_POST) {
e = &posteffects;
} else {
if ((channel < 0) || (channel >= num_channels)) {
Mix_SetError("Invalid channel number");
return(0);
}
e = &mix_channel[channel].effects;
}
return _Mix_register_effect(e, f, d, arg);
}
int Mix_RegisterEffect(int channel, Mix_EffectFunc_t f,
Mix_EffectDone_t d, void *arg)
{
int retval;
SDL_LockAudio();
retval = _Mix_RegisterEffect_locked(channel, f, d, arg);
SDL_UnlockAudio();
return retval;
}
/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
int _Mix_UnregisterEffect_locked(int channel, Mix_EffectFunc_t f)
{
effect_info **e = NULL;
if (channel == MIX_CHANNEL_POST) {
e = &posteffects;
} else {
if ((channel < 0) || (channel >= num_channels)) {
Mix_SetError("Invalid channel number");
return(0);
}
e = &mix_channel[channel].effects;
}
return _Mix_remove_effect(channel, e, f);
}
int Mix_UnregisterEffect(int channel, Mix_EffectFunc_t f)
{
int retval;
SDL_LockAudio();
retval = _Mix_UnregisterEffect_locked(channel, f);
SDL_UnlockAudio();
return(retval);
}
/* MAKE SURE you hold the audio lock (SDL_LockAudio()) before calling this! */
int _Mix_UnregisterAllEffects_locked(int channel)
{
effect_info **e = NULL;
if (channel == MIX_CHANNEL_POST) {
e = &posteffects;
} else {
if ((channel < 0) || (channel >= num_channels)) {
Mix_SetError("Invalid channel number");
return(0);
}
e = &mix_channel[channel].effects;
}
return _Mix_remove_all_effects(channel, e);
}
int Mix_UnregisterAllEffects(int channel)
{
int retval;
SDL_LockAudio();
retval = _Mix_UnregisterAllEffects_locked(channel);
SDL_UnlockAudio();
return(retval);
}
/* end of mixer.c ... */
diff --git a/util/sound/sdl/mixer/wavestream.c b/util/sound/sdl/mixer/wavestream.c
index d545f451..d4920c6f 100644
--- a/util/sound/sdl/mixer/wavestream.c
+++ b/util/sound/sdl/mixer/wavestream.c
@@ -1,530 +1,531 @@
/*
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
Sam Lantinga
slouken@libsdl.org
*/
/* $Id: wavestream.c 5214 2009-11-08 17:11:09Z slouken $ */
/* This file supports streaming WAV files, without volume adjustment */
#include <stdlib.h>
#include <string.h>
#include "SDL_audio.h"
#include "SDL_mutex.h"
#include "SDL_rwops.h"
#include "SDL_endian.h"
#include "SDL_mixer.h"
#include "wavestream.h"
/*
Taken with permission from SDL_wave.h, part of the SDL library,
available at: http://www.libsdl.org/
and placed under the same license as this mixer library.
*/
/* WAVE files are little-endian */
/*******************************************/
/* Define values for Microsoft WAVE format */
/*******************************************/
#define RIFF 0x46464952 /* "RIFF" */
#define WAVE 0x45564157 /* "WAVE" */
#define FACT 0x74636166 /* "fact" */
#define LIST 0x5453494c /* "LIST" */
#define FMT 0x20746D66 /* "fmt " */
#define DATA 0x61746164 /* "data" */
#define PCM_CODE 1
#define ADPCM_CODE 2
#define WAVE_MONO 1
#define WAVE_STEREO 2
/* Normally, these three chunks come consecutively in a WAVE file */
typedef struct WaveFMT {
/* Not saved in the chunk we read:
Uint32 FMTchunk;
Uint32 fmtlen;
*/
Uint16 encoding;
Uint16 channels; /* 1 = mono, 2 = stereo */
Uint32 frequency; /* One of 11025, 22050, or 44100 Hz */
Uint32 byterate; /* Average bytes per second */
Uint16 blockalign; /* Bytes per sample block */
Uint16 bitspersample; /* One of 8, 12, 16, or 4 for ADPCM */
} WaveFMT;
/* The general chunk found in the WAVE file */
typedef struct Chunk {
Uint32 magic;
Uint32 length;
Uint8 *data; /* Data includes magic and length */
} Chunk;
/*********************************************/
/* Define values for AIFF (IFF audio) format */
/*********************************************/
#define FORM 0x4d524f46 /* "FORM" */
#define AIFF 0x46464941 /* "AIFF" */
#define SSND 0x444e5353 /* "SSND" */
#define COMM 0x4d4d4f43 /* "COMM" */
/* Currently we only support a single stream at a time */
static WAVStream *music = NULL;
/* This is the format of the audio mixer data */
static SDL_AudioSpec mixer;
static int wavestream_volume = MIX_MAX_VOLUME;
/* Function to load the WAV/AIFF stream */
static 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;
}
WAVStream *WAVStream_LoadSong(const char *file, const char *magic)
{
SDL_RWops *rw;
WAVStream *wave;
rw = SDL_RWFromFile(file, "rb");
if ( rw == NULL ) {
SDL_SetError("Couldn't open %s", file);
return NULL;
}
wave = WAVStream_LoadSong_RW(rw, magic);
if ( wave == NULL ) {
SDL_FreeRW(rw);
return NULL;
}
return wave;
}
/* Load a WAV stream from the given file */
WAVStream *WAVStream_LoadSong_RW(SDL_RWops *rw, const char *magic)
{
WAVStream *wave;
SDL_AudioSpec wavespec;
if ( ! mixer.format ) {
Mix_SetError("WAV music output not started");
return(NULL);
}
wave = (WAVStream *)malloc(sizeof *wave);
if ( wave ) {
memset(wave, 0, (sizeof *wave));
if ( strcmp(magic, "RIFF") == 0 ) {
wave->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 ) {
free(wave);
return(NULL);
}
SDL_BuildAudioCVT(&wave->cvt,
wavespec.format, wavespec.channels, wavespec.freq,
mixer.format, mixer.channels, mixer.freq);
}
return(wave);
}
/* Start playback of a given WAV stream */
void WAVStream_Start(WAVStream *wave)
{
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 ) {
free(music->cvt.buf);
}
worksize = original_len*music->cvt.len_mult;
music->cvt.buf=(Uint8 *)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->freerw ) {
SDL_FreeRW(wave->rw);
}
if ( wave->cvt.buf ) {
free(wave->cvt.buf);
}
free(wave);
}
}
/* Return non-zero if a stream is currently playing */
int WAVStream_Active(void)
{
int active;
active = 0;
if ( music && (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 *)malloc(chunk->length);
if ( chunk->data == NULL ) {
Mix_SetError("Out of memory");
return(-1);
}
if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) {
Mix_SetError("Couldn't read chunk");
free(chunk->data);
return(-1);
}
} else {
SDL_RWseek(src, chunk->length, 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 ) {
free(chunk.data);
}
lenread = ReadChunk(src, &chunk, 1);
if ( lenread < 0 ) {
was_error = 1;
goto done;
}
} while ( (chunk.magic == FACT) || (chunk.magic == LIST) );
/* Decode the audio data format */
format = (WaveFMT *)chunk.data;
if ( chunk.magic != FMT ) {
free(chunk.data);
+ format = NULL;
Mix_SetError("Complex WAVE files not supported");
was_error = 1;
goto done;
}
switch (SDL_SwapLE16(format->encoding)) {
case PCM_CODE:
/* We can understand this */
break;
default:
Mix_SetError("Unknown WAVE data format");
was_error = 1;
goto done;
}
memset(spec, 0, (sizeof *spec));
spec->freq = SDL_SwapLE32(format->frequency);
switch (SDL_SwapLE16(format->bitspersample)) {
case 8:
spec->format = AUDIO_U8;
break;
case 16:
spec->format = AUDIO_S16;
break;
default:
Mix_SetError("Unknown PCM data format");
was_error = 1;
goto done;
}
spec->channels = (Uint8) SDL_SwapLE16(format->channels);
spec->samples = 4096; /* Good default buffer size */
/* Set the file offset to the DATA chunk data */
chunk.data = NULL;
do {
*start = SDL_RWtell(src) + 2*sizeof(Uint32);
lenread = ReadChunk(src, &chunk, 0);
if ( lenread < 0 ) {
was_error = 1;
goto done;
}
} while ( chunk.magic != DATA );
*stop = SDL_RWtell(src);
done:
if ( format != NULL ) {
free(format);
}
if ( 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);
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 16, 12:21 AM (2 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
70432
Default Alt Text
(62 KB)

Event Timeline