diff options
Diffstat (limited to 'src/audio')
85 files changed, 19452 insertions, 0 deletions
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c new file mode 100644 index 0000000..bdeacdc --- /dev/null +++ b/src/audio/SDL_audio.c @@ -0,0 +1,695 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include "SDL.h" +#include "SDL_audio_c.h" +#include "SDL_audiomem.h" +#include "SDL_sysaudio.h" + +#ifdef __OS2__ +/* We'll need the DosSetPriority() API! */ +#define INCL_DOSPROCESS +#include <os2.h> +#endif + +/* Available audio drivers */ +static AudioBootStrap *bootstrap[] = { +#if SDL_AUDIO_DRIVER_BSD + &BSD_AUDIO_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_PULSE + &PULSE_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_ALSA + &ALSA_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_OSS + &DSP_bootstrap, + &DMA_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_QNXNTO + &QNXNTOAUDIO_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_SUNAUDIO + &SUNAUDIO_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_DMEDIA + &DMEDIA_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_ARTS + &ARTS_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_ESD + &ESD_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_NAS + &NAS_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_DSOUND + &DSOUND_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_WAVEOUT + &WAVEOUT_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_PAUD + &Paud_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_BAUDIO + &BAUDIO_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_COREAUDIO + &COREAUDIO_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_SNDMGR + &SNDMGR_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_MINT + &MINTAUDIO_GSXB_bootstrap, + &MINTAUDIO_MCSN_bootstrap, + &MINTAUDIO_STFA_bootstrap, + &MINTAUDIO_XBIOS_bootstrap, + &MINTAUDIO_DMA8_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_DISK + &DISKAUD_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_DUMMY + &DUMMYAUD_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_DC + &DCAUD_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_NDS + &NDSAUD_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_MMEAUDIO + &MMEAUDIO_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_DART + &DART_bootstrap, +#endif +#if SDL_AUDIO_DRIVER_EPOCAUDIO + &EPOCAudio_bootstrap, +#endif + NULL +}; +SDL_AudioDevice *current_audio = NULL; + +/* Various local functions */ +int SDL_AudioInit(const char *driver_name); +void SDL_AudioQuit(void); + +/* The general mixing thread function */ +int SDLCALL SDL_RunAudio(void *audiop) +{ + SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop; + Uint8 *stream; + int stream_len; + void *udata; + void (SDLCALL *fill)(void *userdata,Uint8 *stream, int len); + int silence; + + /* Perform any thread setup */ + if ( audio->ThreadInit ) { + audio->ThreadInit(audio); + } + audio->threadid = SDL_ThreadID(); + + /* Set up the mixing function */ + fill = audio->spec.callback; + udata = audio->spec.userdata; + + if ( audio->convert.needed ) { + if ( audio->convert.src_format == AUDIO_U8 ) { + silence = 0x80; + } else { + silence = 0; + } + stream_len = audio->convert.len; + } else { + silence = audio->spec.silence; + stream_len = audio->spec.size; + } + +#ifdef __OS2__ + /* Increase the priority of this thread to make sure that + the audio will be continuous all the time! */ +#ifdef USE_DOSSETPRIORITY + if (SDL_getenv("SDL_USE_TIMECRITICAL_AUDIO")) + { +#ifdef DEBUG_BUILD + printf("[SDL_RunAudio] : Setting priority to TimeCritical+0! (TID%d)\n", SDL_ThreadID()); +#endif + DosSetPriority(PRTYS_THREAD, PRTYC_TIMECRITICAL, 0, 0); + } + else + { +#ifdef DEBUG_BUILD + printf("[SDL_RunAudio] : Setting priority to ForegroundServer+0! (TID%d)\n", SDL_ThreadID()); +#endif + DosSetPriority(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0); + } +#endif +#endif + + /* Loop, filling the audio buffers */ + while ( audio->enabled ) { + + /* Fill the current buffer with sound */ + if ( audio->convert.needed ) { + if ( audio->convert.buf ) { + stream = audio->convert.buf; + } else { + continue; + } + } else { + stream = audio->GetAudioBuf(audio); + if ( stream == NULL ) { + stream = audio->fake_stream; + } + } + + SDL_memset(stream, silence, stream_len); + + if ( ! audio->paused ) { + SDL_mutexP(audio->mixer_lock); + (*fill)(udata, stream, stream_len); + SDL_mutexV(audio->mixer_lock); + } + + /* Convert the audio if necessary */ + if ( audio->convert.needed ) { + SDL_ConvertAudio(&audio->convert); + stream = audio->GetAudioBuf(audio); + if ( stream == NULL ) { + stream = audio->fake_stream; + } + SDL_memcpy(stream, audio->convert.buf, + audio->convert.len_cvt); + } + + /* Ready current buffer for play and change current buffer */ + if ( stream != audio->fake_stream ) { + audio->PlayAudio(audio); + } + + /* Wait for an audio buffer to become available */ + if ( stream == audio->fake_stream ) { + SDL_Delay((audio->spec.samples*1000)/audio->spec.freq); + } else { + audio->WaitAudio(audio); + } + } + + /* Wait for the audio to drain.. */ + if ( audio->WaitDone ) { + audio->WaitDone(audio); + } + +#ifdef __OS2__ +#ifdef DEBUG_BUILD + printf("[SDL_RunAudio] : Task exiting. (TID%d)\n", SDL_ThreadID()); +#endif +#endif + return(0); +} + +static void SDL_LockAudio_Default(SDL_AudioDevice *audio) +{ + if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) { + return; + } + SDL_mutexP(audio->mixer_lock); +} + +static void SDL_UnlockAudio_Default(SDL_AudioDevice *audio) +{ + if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) { + return; + } + SDL_mutexV(audio->mixer_lock); +} + +static Uint16 SDL_ParseAudioFormat(const char *string) +{ + Uint16 format = 0; + + switch (*string) { + case 'U': + ++string; + format |= 0x0000; + break; + case 'S': + ++string; + format |= 0x8000; + break; + default: + return 0; + } + switch (SDL_atoi(string)) { + case 8: + string += 1; + format |= 8; + break; + case 16: + string += 2; + format |= 16; + if ( SDL_strcmp(string, "LSB") == 0 +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + || SDL_strcmp(string, "SYS") == 0 +#endif + ) { + format |= 0x0000; + } + if ( SDL_strcmp(string, "MSB") == 0 +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + || SDL_strcmp(string, "SYS") == 0 +#endif + ) { + format |= 0x1000; + } + break; + default: + return 0; + } + return format; +} + +int SDL_AudioInit(const char *driver_name) +{ + SDL_AudioDevice *audio; + int i = 0, idx; + + /* Check to make sure we don't overwrite 'current_audio' */ + if ( current_audio != NULL ) { + SDL_AudioQuit(); + } + + /* Select the proper audio driver */ + audio = NULL; + idx = 0; +#if SDL_AUDIO_DRIVER_ESD + if ( (driver_name == NULL) && (SDL_getenv("ESPEAKER") != NULL) ) { + /* Ahem, we know that if ESPEAKER is set, user probably wants + to use ESD, but don't start it if it's not already running. + This probably isn't the place to do this, but... Shh! :) + */ + for ( i=0; bootstrap[i]; ++i ) { + if ( SDL_strcasecmp(bootstrap[i]->name, "esd") == 0 ) { +#ifdef HAVE_PUTENV + const char *esd_no_spawn; + + /* Don't start ESD if it's not running */ + esd_no_spawn = getenv("ESD_NO_SPAWN"); + if ( esd_no_spawn == NULL ) { + putenv("ESD_NO_SPAWN=1"); + } +#endif + if ( bootstrap[i]->available() ) { + audio = bootstrap[i]->create(0); + break; + } +#ifdef HAVE_UNSETENV + if ( esd_no_spawn == NULL ) { + unsetenv("ESD_NO_SPAWN"); + } +#endif + } + } + } +#endif /* SDL_AUDIO_DRIVER_ESD */ + if ( audio == NULL ) { + if ( driver_name != NULL ) { +#if 0 /* This will be replaced with a better driver selection API */ + if ( SDL_strrchr(driver_name, ':') != NULL ) { + idx = atoi(SDL_strrchr(driver_name, ':')+1); + } +#endif + for ( i=0; bootstrap[i]; ++i ) { + if (SDL_strcasecmp(bootstrap[i]->name, driver_name) == 0) { + if ( bootstrap[i]->available() ) { + audio=bootstrap[i]->create(idx); + break; + } + } + } + } else { + for ( i=0; bootstrap[i]; ++i ) { + if ( bootstrap[i]->available() ) { + audio = bootstrap[i]->create(idx); + if ( audio != NULL ) { + break; + } + } + } + } + if ( audio == NULL ) { + SDL_SetError("No available audio device"); +#if 0 /* Don't fail SDL_Init() if audio isn't available. + SDL_OpenAudio() will handle it at that point. *sigh* + */ + return(-1); +#endif + } + } + current_audio = audio; + if ( current_audio ) { + current_audio->name = bootstrap[i]->name; + if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) { + current_audio->LockAudio = SDL_LockAudio_Default; + current_audio->UnlockAudio = SDL_UnlockAudio_Default; + } + } + return(0); +} + +char *SDL_AudioDriverName(char *namebuf, int maxlen) +{ + if ( current_audio != NULL ) { + SDL_strlcpy(namebuf, current_audio->name, maxlen); + return(namebuf); + } + return(NULL); +} + +int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained) +{ + SDL_AudioDevice *audio; + const char *env; + + /* Start up the audio driver, if necessary */ + if ( ! current_audio ) { + if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) || + (current_audio == NULL) ) { + return(-1); + } + } + audio = current_audio; + + if (audio->opened) { + SDL_SetError("Audio device is already opened"); + return(-1); + } + + /* Verify some parameters */ + if ( desired->freq == 0 ) { + env = SDL_getenv("SDL_AUDIO_FREQUENCY"); + if ( env ) { + desired->freq = SDL_atoi(env); + } + } + if ( desired->freq == 0 ) { + /* Pick some default audio frequency */ + desired->freq = 22050; + } + if ( desired->format == 0 ) { + env = SDL_getenv("SDL_AUDIO_FORMAT"); + if ( env ) { + desired->format = SDL_ParseAudioFormat(env); + } + } + if ( desired->format == 0 ) { + /* Pick some default audio format */ + desired->format = AUDIO_S16; + } + if ( desired->channels == 0 ) { + env = SDL_getenv("SDL_AUDIO_CHANNELS"); + if ( env ) { + desired->channels = (Uint8)SDL_atoi(env); + } + } + if ( desired->channels == 0 ) { + /* Pick a default number of channels */ + desired->channels = 2; + } + switch ( desired->channels ) { + case 1: /* Mono */ + case 2: /* Stereo */ + case 4: /* surround */ + case 6: /* surround with center and lfe */ + break; + default: + SDL_SetError("1 (mono) and 2 (stereo) channels supported"); + return(-1); + } + if ( desired->samples == 0 ) { + env = SDL_getenv("SDL_AUDIO_SAMPLES"); + if ( env ) { + desired->samples = (Uint16)SDL_atoi(env); + } + } + if ( desired->samples == 0 ) { + /* Pick a default of ~46 ms at desired frequency */ + int samples = (desired->freq / 1000) * 46; + int power2 = 1; + while ( power2 < samples ) { + power2 *= 2; + } + desired->samples = power2; + } + if ( desired->callback == NULL ) { + SDL_SetError("SDL_OpenAudio() passed a NULL callback"); + return(-1); + } + +#if SDL_THREADS_DISABLED + /* Uses interrupt driven audio, without thread */ +#else + /* Create a semaphore for locking the sound buffers */ + audio->mixer_lock = SDL_CreateMutex(); + if ( audio->mixer_lock == NULL ) { + SDL_SetError("Couldn't create mixer lock"); + SDL_CloseAudio(); + return(-1); + } +#endif /* SDL_THREADS_DISABLED */ + + /* Calculate the silence and size of the audio specification */ + SDL_CalculateAudioSpec(desired); + + /* Open the audio subsystem */ + SDL_memcpy(&audio->spec, desired, sizeof(audio->spec)); + audio->convert.needed = 0; + audio->enabled = 1; + audio->paused = 1; + + audio->opened = audio->OpenAudio(audio, &audio->spec)+1; + + if ( ! audio->opened ) { + SDL_CloseAudio(); + return(-1); + } + + /* If the audio driver changes the buffer size, accept it */ + if ( audio->spec.samples != desired->samples ) { + desired->samples = audio->spec.samples; + SDL_CalculateAudioSpec(desired); + } + + /* Allocate a fake audio memory buffer */ + audio->fake_stream = SDL_AllocAudioMem(audio->spec.size); + if ( audio->fake_stream == NULL ) { + SDL_CloseAudio(); + SDL_OutOfMemory(); + return(-1); + } + + /* See if we need to do any conversion */ + if ( obtained != NULL ) { + SDL_memcpy(obtained, &audio->spec, sizeof(audio->spec)); + } else if ( desired->freq != audio->spec.freq || + desired->format != audio->spec.format || + desired->channels != audio->spec.channels ) { + /* Build an audio conversion block */ + if ( SDL_BuildAudioCVT(&audio->convert, + desired->format, desired->channels, + desired->freq, + audio->spec.format, audio->spec.channels, + audio->spec.freq) < 0 ) { + SDL_CloseAudio(); + return(-1); + } + if ( audio->convert.needed ) { + audio->convert.len = (int) ( ((double) audio->spec.size) / + audio->convert.len_ratio ); + audio->convert.buf =(Uint8 *)SDL_AllocAudioMem( + audio->convert.len*audio->convert.len_mult); + if ( audio->convert.buf == NULL ) { + SDL_CloseAudio(); + SDL_OutOfMemory(); + return(-1); + } + } + } + + /* Start the audio thread if necessary */ + switch (audio->opened) { + case 1: + /* Start the audio thread */ +#if (defined(__WIN32__) && !defined(_WIN32_WCE)) && !defined(HAVE_LIBC) && !defined(__SYMBIAN32__) +#undef SDL_CreateThread + audio->thread = SDL_CreateThread(SDL_RunAudio, audio, NULL, NULL); +#else + audio->thread = SDL_CreateThread(SDL_RunAudio, audio); +#endif + if ( audio->thread == NULL ) { + SDL_CloseAudio(); + SDL_SetError("Couldn't create audio thread"); + return(-1); + } + break; + + default: + /* The audio is now playing */ + break; + } + + return(0); +} + +SDL_audiostatus SDL_GetAudioStatus(void) +{ + SDL_AudioDevice *audio = current_audio; + SDL_audiostatus status; + + status = SDL_AUDIO_STOPPED; + if ( audio && audio->enabled ) { + if ( audio->paused ) { + status = SDL_AUDIO_PAUSED; + } else { + status = SDL_AUDIO_PLAYING; + } + } + return(status); +} + +void SDL_PauseAudio (int pause_on) +{ + SDL_AudioDevice *audio = current_audio; + + if ( audio ) { + audio->paused = pause_on; + } +} + +void SDL_LockAudio (void) +{ + SDL_AudioDevice *audio = current_audio; + + /* Obtain a lock on the mixing buffers */ + if ( audio && audio->LockAudio ) { + audio->LockAudio(audio); + } +} + +void SDL_UnlockAudio (void) +{ + SDL_AudioDevice *audio = current_audio; + + /* Release lock on the mixing buffers */ + if ( audio && audio->UnlockAudio ) { + audio->UnlockAudio(audio); + } +} + +void SDL_CloseAudio (void) +{ + SDL_QuitSubSystem(SDL_INIT_AUDIO); +} + +void SDL_AudioQuit(void) +{ + SDL_AudioDevice *audio = current_audio; + + if ( audio ) { + audio->enabled = 0; + if ( audio->thread != NULL ) { + SDL_WaitThread(audio->thread, NULL); + } + if ( audio->mixer_lock != NULL ) { + SDL_DestroyMutex(audio->mixer_lock); + } + if ( audio->fake_stream != NULL ) { + SDL_FreeAudioMem(audio->fake_stream); + } + if ( audio->convert.needed ) { + SDL_FreeAudioMem(audio->convert.buf); + + } + if ( audio->opened ) { + audio->CloseAudio(audio); + audio->opened = 0; + } + /* Free the driver data */ + audio->free(audio); + current_audio = NULL; + } +} + +#define NUM_FORMATS 6 +static int format_idx; +static int format_idx_sub; +static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = { + { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB }, + { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB }, + { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 }, + { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 }, + { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 }, + { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 }, +}; + +Uint16 SDL_FirstAudioFormat(Uint16 format) +{ + for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) { + if ( format_list[format_idx][0] == format ) { + break; + } + } + format_idx_sub = 0; + return(SDL_NextAudioFormat()); +} + +Uint16 SDL_NextAudioFormat(void) +{ + if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) { + return(0); + } + return(format_list[format_idx][format_idx_sub++]); +} + +void SDL_CalculateAudioSpec(SDL_AudioSpec *spec) +{ + switch (spec->format) { + case AUDIO_U8: + spec->silence = 0x80; + break; + default: + spec->silence = 0x00; + break; + } + spec->size = (spec->format&0xFF)/8; + spec->size *= spec->channels; + spec->size *= spec->samples; +} diff --git a/src/audio/SDL_audio_c.h b/src/audio/SDL_audio_c.h new file mode 100644 index 0000000..3a5c102 --- /dev/null +++ b/src/audio/SDL_audio_c.h @@ -0,0 +1,34 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Functions and variables exported from SDL_audio.c for SDL_sysaudio.c */ + +/* Functions to get a list of "close" audio formats */ +extern Uint16 SDL_FirstAudioFormat(Uint16 format); +extern Uint16 SDL_NextAudioFormat(void); + +/* Function to calculate the size and silence for a SDL_AudioSpec */ +extern void SDL_CalculateAudioSpec(SDL_AudioSpec *spec); + +/* The actual mixing thread function */ +extern int SDLCALL SDL_RunAudio(void *audiop); diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c new file mode 100644 index 0000000..5db30b3 --- /dev/null +++ b/src/audio/SDL_audiocvt.c @@ -0,0 +1,1510 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Functions for audio drivers to perform runtime conversion of audio format */ + +#include "SDL_audio.h" + + +/* Effectively mix right and left channels into a single channel */ +void SDLCALL SDL_ConvertMono(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Sint32 sample; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to mono\n"); +#endif + switch (format&0x8018) { + + case AUDIO_U8: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + for ( i=cvt->len_cvt/2; i; --i ) { + sample = src[0] + src[1]; + *dst = (Uint8)(sample / 2); + src += 2; + dst += 1; + } + } + break; + + case AUDIO_S8: { + Sint8 *src, *dst; + + src = (Sint8 *)cvt->buf; + dst = (Sint8 *)cvt->buf; + for ( i=cvt->len_cvt/2; i; --i ) { + sample = src[0] + src[1]; + *dst = (Sint8)(sample / 2); + src += 2; + dst += 1; + } + } + break; + + case AUDIO_U16: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/4; i; --i ) { + sample = (Uint16)((src[0]<<8)|src[1])+ + (Uint16)((src[2]<<8)|src[3]); + sample /= 2; + dst[1] = (sample&0xFF); + sample >>= 8; + dst[0] = (sample&0xFF); + src += 4; + dst += 2; + } + } else { + for ( i=cvt->len_cvt/4; i; --i ) { + sample = (Uint16)((src[1]<<8)|src[0])+ + (Uint16)((src[3]<<8)|src[2]); + sample /= 2; + dst[0] = (sample&0xFF); + sample >>= 8; + dst[1] = (sample&0xFF); + src += 4; + dst += 2; + } + } + } + break; + + case AUDIO_S16: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/4; i; --i ) { + sample = (Sint16)((src[0]<<8)|src[1])+ + (Sint16)((src[2]<<8)|src[3]); + sample /= 2; + dst[1] = (sample&0xFF); + sample >>= 8; + dst[0] = (sample&0xFF); + src += 4; + dst += 2; + } + } else { + for ( i=cvt->len_cvt/4; i; --i ) { + sample = (Sint16)((src[1]<<8)|src[0])+ + (Sint16)((src[3]<<8)|src[2]); + sample /= 2; + dst[0] = (sample&0xFF); + sample >>= 8; + dst[1] = (sample&0xFF); + src += 4; + dst += 2; + } + } + } + break; + } + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Discard top 4 channels */ +void SDLCALL SDL_ConvertStrip(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Sint32 lsample, rsample; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting down to stereo\n"); +#endif + switch (format&0x8018) { + + case AUDIO_U8: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + for ( i=cvt->len_cvt/6; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + src += 6; + dst += 2; + } + } + break; + + case AUDIO_S8: { + Sint8 *src, *dst; + + src = (Sint8 *)cvt->buf; + dst = (Sint8 *)cvt->buf; + for ( i=cvt->len_cvt/6; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + src += 6; + dst += 2; + } + } + break; + + case AUDIO_U16: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/12; i; --i ) { + lsample = (Uint16)((src[0]<<8)|src[1]); + rsample = (Uint16)((src[2]<<8)|src[3]); + dst[1] = (lsample&0xFF); + lsample >>= 8; + dst[0] = (lsample&0xFF); + dst[3] = (rsample&0xFF); + rsample >>= 8; + dst[2] = (rsample&0xFF); + src += 12; + dst += 4; + } + } else { + for ( i=cvt->len_cvt/12; i; --i ) { + lsample = (Uint16)((src[1]<<8)|src[0]); + rsample = (Uint16)((src[3]<<8)|src[2]); + dst[0] = (lsample&0xFF); + lsample >>= 8; + dst[1] = (lsample&0xFF); + dst[2] = (rsample&0xFF); + rsample >>= 8; + dst[3] = (rsample&0xFF); + src += 12; + dst += 4; + } + } + } + break; + + case AUDIO_S16: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/12; i; --i ) { + lsample = (Sint16)((src[0]<<8)|src[1]); + rsample = (Sint16)((src[2]<<8)|src[3]); + dst[1] = (lsample&0xFF); + lsample >>= 8; + dst[0] = (lsample&0xFF); + dst[3] = (rsample&0xFF); + rsample >>= 8; + dst[2] = (rsample&0xFF); + src += 12; + dst += 4; + } + } else { + for ( i=cvt->len_cvt/12; i; --i ) { + lsample = (Sint16)((src[1]<<8)|src[0]); + rsample = (Sint16)((src[3]<<8)|src[2]); + dst[0] = (lsample&0xFF); + lsample >>= 8; + dst[1] = (lsample&0xFF); + dst[2] = (rsample&0xFF); + rsample >>= 8; + dst[3] = (rsample&0xFF); + src += 12; + dst += 4; + } + } + } + break; + } + cvt->len_cvt /= 3; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Discard top 2 channels of 6 */ +void SDLCALL SDL_ConvertStrip_2(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Sint32 lsample, rsample; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting 6 down to quad\n"); +#endif + switch (format&0x8018) { + + case AUDIO_U8: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + for ( i=cvt->len_cvt/4; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + src += 4; + dst += 2; + } + } + break; + + case AUDIO_S8: { + Sint8 *src, *dst; + + src = (Sint8 *)cvt->buf; + dst = (Sint8 *)cvt->buf; + for ( i=cvt->len_cvt/4; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + src += 4; + dst += 2; + } + } + break; + + case AUDIO_U16: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/8; i; --i ) { + lsample = (Uint16)((src[0]<<8)|src[1]); + rsample = (Uint16)((src[2]<<8)|src[3]); + dst[1] = (lsample&0xFF); + lsample >>= 8; + dst[0] = (lsample&0xFF); + dst[3] = (rsample&0xFF); + rsample >>= 8; + dst[2] = (rsample&0xFF); + src += 8; + dst += 4; + } + } else { + for ( i=cvt->len_cvt/8; i; --i ) { + lsample = (Uint16)((src[1]<<8)|src[0]); + rsample = (Uint16)((src[3]<<8)|src[2]); + dst[0] = (lsample&0xFF); + lsample >>= 8; + dst[1] = (lsample&0xFF); + dst[2] = (rsample&0xFF); + rsample >>= 8; + dst[3] = (rsample&0xFF); + src += 8; + dst += 4; + } + } + } + break; + + case AUDIO_S16: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/8; i; --i ) { + lsample = (Sint16)((src[0]<<8)|src[1]); + rsample = (Sint16)((src[2]<<8)|src[3]); + dst[1] = (lsample&0xFF); + lsample >>= 8; + dst[0] = (lsample&0xFF); + dst[3] = (rsample&0xFF); + rsample >>= 8; + dst[2] = (rsample&0xFF); + src += 8; + dst += 4; + } + } else { + for ( i=cvt->len_cvt/8; i; --i ) { + lsample = (Sint16)((src[1]<<8)|src[0]); + rsample = (Sint16)((src[3]<<8)|src[2]); + dst[0] = (lsample&0xFF); + lsample >>= 8; + dst[1] = (lsample&0xFF); + dst[2] = (rsample&0xFF); + rsample >>= 8; + dst[3] = (rsample&0xFF); + src += 8; + dst += 4; + } + } + } + break; + } + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Duplicate a mono channel to both stereo channels */ +void SDLCALL SDL_ConvertStereo(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to stereo\n"); +#endif + if ( (format & 0xFF) == 16 ) { + Uint16 *src, *dst; + + src = (Uint16 *)(cvt->buf+cvt->len_cvt); + dst = (Uint16 *)(cvt->buf+cvt->len_cvt*2); + for ( i=cvt->len_cvt/2; i; --i ) { + dst -= 2; + src -= 1; + dst[0] = src[0]; + dst[1] = src[0]; + } + } else { + Uint8 *src, *dst; + + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + for ( i=cvt->len_cvt; i; --i ) { + dst -= 2; + src -= 1; + dst[0] = src[0]; + dst[1] = src[0]; + } + } + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Duplicate a stereo channel to a pseudo-5.1 stream */ +void SDLCALL SDL_ConvertSurround(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting stereo to surround\n"); +#endif + switch (format&0x8018) { + + case AUDIO_U8: { + Uint8 *src, *dst, lf, rf, ce; + + src = (Uint8 *)(cvt->buf+cvt->len_cvt); + dst = (Uint8 *)(cvt->buf+cvt->len_cvt*3); + for ( i=cvt->len_cvt; i; --i ) { + dst -= 6; + src -= 2; + lf = src[0]; + rf = src[1]; + ce = (lf/2) + (rf/2); + dst[0] = lf; + dst[1] = rf; + dst[2] = lf - ce; + dst[3] = rf - ce; + dst[4] = ce; + dst[5] = ce; + } + } + break; + + case AUDIO_S8: { + Sint8 *src, *dst, lf, rf, ce; + + src = (Sint8 *)cvt->buf+cvt->len_cvt; + dst = (Sint8 *)cvt->buf+cvt->len_cvt*3; + for ( i=cvt->len_cvt; i; --i ) { + dst -= 6; + src -= 2; + lf = src[0]; + rf = src[1]; + ce = (lf/2) + (rf/2); + dst[0] = lf; + dst[1] = rf; + dst[2] = lf - ce; + dst[3] = rf - ce; + dst[4] = ce; + dst[5] = ce; + } + } + break; + + case AUDIO_U16: { + Uint8 *src, *dst; + Uint16 lf, rf, ce, lr, rr; + + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*3; + + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/4; i; --i ) { + dst -= 12; + src -= 4; + lf = (Uint16)((src[0]<<8)|src[1]); + rf = (Uint16)((src[2]<<8)|src[3]); + ce = (lf/2) + (rf/2); + rr = lf - ce; + lr = rf - ce; + dst[1] = (lf&0xFF); + dst[0] = ((lf>>8)&0xFF); + dst[3] = (rf&0xFF); + dst[2] = ((rf>>8)&0xFF); + + dst[1+4] = (lr&0xFF); + dst[0+4] = ((lr>>8)&0xFF); + dst[3+4] = (rr&0xFF); + dst[2+4] = ((rr>>8)&0xFF); + + dst[1+8] = (ce&0xFF); + dst[0+8] = ((ce>>8)&0xFF); + dst[3+8] = (ce&0xFF); + dst[2+8] = ((ce>>8)&0xFF); + } + } else { + for ( i=cvt->len_cvt/4; i; --i ) { + dst -= 12; + src -= 4; + lf = (Uint16)((src[1]<<8)|src[0]); + rf = (Uint16)((src[3]<<8)|src[2]); + ce = (lf/2) + (rf/2); + rr = lf - ce; + lr = rf - ce; + dst[0] = (lf&0xFF); + dst[1] = ((lf>>8)&0xFF); + dst[2] = (rf&0xFF); + dst[3] = ((rf>>8)&0xFF); + + dst[0+4] = (lr&0xFF); + dst[1+4] = ((lr>>8)&0xFF); + dst[2+4] = (rr&0xFF); + dst[3+4] = ((rr>>8)&0xFF); + + dst[0+8] = (ce&0xFF); + dst[1+8] = ((ce>>8)&0xFF); + dst[2+8] = (ce&0xFF); + dst[3+8] = ((ce>>8)&0xFF); + } + } + } + break; + + case AUDIO_S16: { + Uint8 *src, *dst; + Sint16 lf, rf, ce, lr, rr; + + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*3; + + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/4; i; --i ) { + dst -= 12; + src -= 4; + lf = (Sint16)((src[0]<<8)|src[1]); + rf = (Sint16)((src[2]<<8)|src[3]); + ce = (lf/2) + (rf/2); + rr = lf - ce; + lr = rf - ce; + dst[1] = (lf&0xFF); + dst[0] = ((lf>>8)&0xFF); + dst[3] = (rf&0xFF); + dst[2] = ((rf>>8)&0xFF); + + dst[1+4] = (lr&0xFF); + dst[0+4] = ((lr>>8)&0xFF); + dst[3+4] = (rr&0xFF); + dst[2+4] = ((rr>>8)&0xFF); + + dst[1+8] = (ce&0xFF); + dst[0+8] = ((ce>>8)&0xFF); + dst[3+8] = (ce&0xFF); + dst[2+8] = ((ce>>8)&0xFF); + } + } else { + for ( i=cvt->len_cvt/4; i; --i ) { + dst -= 12; + src -= 4; + lf = (Sint16)((src[1]<<8)|src[0]); + rf = (Sint16)((src[3]<<8)|src[2]); + ce = (lf/2) + (rf/2); + rr = lf - ce; + lr = rf - ce; + dst[0] = (lf&0xFF); + dst[1] = ((lf>>8)&0xFF); + dst[2] = (rf&0xFF); + dst[3] = ((rf>>8)&0xFF); + + dst[0+4] = (lr&0xFF); + dst[1+4] = ((lr>>8)&0xFF); + dst[2+4] = (rr&0xFF); + dst[3+4] = ((rr>>8)&0xFF); + + dst[0+8] = (ce&0xFF); + dst[1+8] = ((ce>>8)&0xFF); + dst[2+8] = (ce&0xFF); + dst[3+8] = ((ce>>8)&0xFF); + } + } + } + break; + } + cvt->len_cvt *= 3; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Duplicate a stereo channel to a pseudo-4.0 stream */ +void SDLCALL SDL_ConvertSurround_4(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting stereo to quad\n"); +#endif + switch (format&0x8018) { + + case AUDIO_U8: { + Uint8 *src, *dst, lf, rf, ce; + + src = (Uint8 *)(cvt->buf+cvt->len_cvt); + dst = (Uint8 *)(cvt->buf+cvt->len_cvt*2); + for ( i=cvt->len_cvt; i; --i ) { + dst -= 4; + src -= 2; + lf = src[0]; + rf = src[1]; + ce = (lf/2) + (rf/2); + dst[0] = lf; + dst[1] = rf; + dst[2] = lf - ce; + dst[3] = rf - ce; + } + } + break; + + case AUDIO_S8: { + Sint8 *src, *dst, lf, rf, ce; + + src = (Sint8 *)cvt->buf+cvt->len_cvt; + dst = (Sint8 *)cvt->buf+cvt->len_cvt*2; + for ( i=cvt->len_cvt; i; --i ) { + dst -= 4; + src -= 2; + lf = src[0]; + rf = src[1]; + ce = (lf/2) + (rf/2); + dst[0] = lf; + dst[1] = rf; + dst[2] = lf - ce; + dst[3] = rf - ce; + } + } + break; + + case AUDIO_U16: { + Uint8 *src, *dst; + Uint16 lf, rf, ce, lr, rr; + + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/4; i; --i ) { + dst -= 8; + src -= 4; + lf = (Uint16)((src[0]<<8)|src[1]); + rf = (Uint16)((src[2]<<8)|src[3]); + ce = (lf/2) + (rf/2); + rr = lf - ce; + lr = rf - ce; + dst[1] = (lf&0xFF); + dst[0] = ((lf>>8)&0xFF); + dst[3] = (rf&0xFF); + dst[2] = ((rf>>8)&0xFF); + + dst[1+4] = (lr&0xFF); + dst[0+4] = ((lr>>8)&0xFF); + dst[3+4] = (rr&0xFF); + dst[2+4] = ((rr>>8)&0xFF); + } + } else { + for ( i=cvt->len_cvt/4; i; --i ) { + dst -= 8; + src -= 4; + lf = (Uint16)((src[1]<<8)|src[0]); + rf = (Uint16)((src[3]<<8)|src[2]); + ce = (lf/2) + (rf/2); + rr = lf - ce; + lr = rf - ce; + dst[0] = (lf&0xFF); + dst[1] = ((lf>>8)&0xFF); + dst[2] = (rf&0xFF); + dst[3] = ((rf>>8)&0xFF); + + dst[0+4] = (lr&0xFF); + dst[1+4] = ((lr>>8)&0xFF); + dst[2+4] = (rr&0xFF); + dst[3+4] = ((rr>>8)&0xFF); + } + } + } + break; + + case AUDIO_S16: { + Uint8 *src, *dst; + Sint16 lf, rf, ce, lr, rr; + + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/4; i; --i ) { + dst -= 8; + src -= 4; + lf = (Sint16)((src[0]<<8)|src[1]); + rf = (Sint16)((src[2]<<8)|src[3]); + ce = (lf/2) + (rf/2); + rr = lf - ce; + lr = rf - ce; + dst[1] = (lf&0xFF); + dst[0] = ((lf>>8)&0xFF); + dst[3] = (rf&0xFF); + dst[2] = ((rf>>8)&0xFF); + + dst[1+4] = (lr&0xFF); + dst[0+4] = ((lr>>8)&0xFF); + dst[3+4] = (rr&0xFF); + dst[2+4] = ((rr>>8)&0xFF); + } + } else { + for ( i=cvt->len_cvt/4; i; --i ) { + dst -= 8; + src -= 4; + lf = (Sint16)((src[1]<<8)|src[0]); + rf = (Sint16)((src[3]<<8)|src[2]); + ce = (lf/2) + (rf/2); + rr = lf - ce; + lr = rf - ce; + dst[0] = (lf&0xFF); + dst[1] = ((lf>>8)&0xFF); + dst[2] = (rf&0xFF); + dst[3] = ((rf>>8)&0xFF); + + dst[0+4] = (lr&0xFF); + dst[1+4] = ((lr>>8)&0xFF); + dst[2+4] = (rr&0xFF); + dst[3+4] = ((rr>>8)&0xFF); + } + } + } + break; + } + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Convert 8-bit to 16-bit - LSB */ +void SDLCALL SDL_Convert16LSB(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to 16-bit LSB\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + for ( i=cvt->len_cvt; i; --i ) { + src -= 1; + dst -= 2; + dst[1] = *src; + dst[0] = 0; + } + format = ((format & ~0x0008) | AUDIO_U16LSB); + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} +/* Convert 8-bit to 16-bit - MSB */ +void SDLCALL SDL_Convert16MSB(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to 16-bit MSB\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + for ( i=cvt->len_cvt; i; --i ) { + src -= 1; + dst -= 2; + dst[0] = *src; + dst[1] = 0; + } + format = ((format & ~0x0008) | AUDIO_U16MSB); + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert 16-bit to 8-bit */ +void SDLCALL SDL_Convert8(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to 8-bit\n"); +#endif + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) != 0x1000 ) { /* Little endian */ + ++src; + } + for ( i=cvt->len_cvt/2; i; --i ) { + *dst = *src; + src += 2; + dst += 1; + } + format = ((format & ~0x9010) | AUDIO_U8); + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Toggle signed/unsigned */ +void SDLCALL SDL_ConvertSign(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *data; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio signedness\n"); +#endif + data = cvt->buf; + if ( (format & 0xFF) == 16 ) { + if ( (format & 0x1000) != 0x1000 ) { /* Little endian */ + ++data; + } + for ( i=cvt->len_cvt/2; i; --i ) { + *data ^= 0x80; + data += 2; + } + } else { + for ( i=cvt->len_cvt; i; --i ) { + *data++ ^= 0x80; + } + } + format = (format ^ 0x8000); + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Toggle endianness */ +void SDLCALL SDL_ConvertEndian(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *data, tmp; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio endianness\n"); +#endif + data = cvt->buf; + for ( i=cvt->len_cvt/2; i; --i ) { + tmp = data[0]; + data[0] = data[1]; + data[1] = tmp; + data += 2; + } + format = (format ^ 0x1000); + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert rate up by multiple of 2 */ +void SDLCALL SDL_RateMUL2(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate * 2\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt; i; --i ) { + src -= 1; + dst -= 2; + dst[0] = src[0]; + dst[1] = src[0]; + } + break; + case 16: + for ( i=cvt->len_cvt/2; i; --i ) { + src -= 2; + dst -= 4; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[1]; + } + break; + } + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Convert rate up by multiple of 2, for stereo */ +void SDLCALL SDL_RateMUL2_c2(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate * 2\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt/2; i; --i ) { + src -= 2; + dst -= 4; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[1]; + } + break; + case 16: + for ( i=cvt->len_cvt/4; i; --i ) { + src -= 4; + dst -= 8; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[0]; + dst[5] = src[1]; + dst[6] = src[2]; + dst[7] = src[3]; + } + break; + } + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert rate up by multiple of 2, for quad */ +void SDLCALL SDL_RateMUL2_c4(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate * 2\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt/4; i; --i ) { + src -= 4; + dst -= 8; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[0]; + dst[5] = src[1]; + dst[6] = src[2]; + dst[7] = src[3]; + } + break; + case 16: + for ( i=cvt->len_cvt/8; i; --i ) { + src -= 8; + dst -= 16; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + dst[8] = src[0]; + dst[9] = src[1]; + dst[10] = src[2]; + dst[11] = src[3]; + dst[12] = src[4]; + dst[13] = src[5]; + dst[14] = src[6]; + dst[15] = src[7]; + } + break; + } + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Convert rate up by multiple of 2, for 5.1 */ +void SDLCALL SDL_RateMUL2_c6(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate * 2\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt/6; i; --i ) { + src -= 6; + dst -= 12; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[0]; + dst[7] = src[1]; + dst[8] = src[2]; + dst[9] = src[3]; + dst[10] = src[4]; + dst[11] = src[5]; + } + break; + case 16: + for ( i=cvt->len_cvt/12; i; --i ) { + src -= 12; + dst -= 24; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + dst[8] = src[8]; + dst[9] = src[9]; + dst[10] = src[10]; + dst[11] = src[11]; + dst[12] = src[0]; + dst[13] = src[1]; + dst[14] = src[2]; + dst[15] = src[3]; + dst[16] = src[4]; + dst[17] = src[5]; + dst[18] = src[6]; + dst[19] = src[7]; + dst[20] = src[8]; + dst[21] = src[9]; + dst[22] = src[10]; + dst[23] = src[11]; + } + break; + } + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert rate down by multiple of 2 */ +void SDLCALL SDL_RateDIV2(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate / 2\n"); +#endif + src = cvt->buf; + dst = cvt->buf; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt/2; i; --i ) { + dst[0] = src[0]; + src += 2; + dst += 1; + } + break; + case 16: + for ( i=cvt->len_cvt/4; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + src += 4; + dst += 2; + } + break; + } + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Convert rate down by multiple of 2, for stereo */ +void SDLCALL SDL_RateDIV2_c2(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate / 2\n"); +#endif + src = cvt->buf; + dst = cvt->buf; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt/4; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + src += 4; + dst += 2; + } + break; + case 16: + for ( i=cvt->len_cvt/8; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + src += 8; + dst += 4; + } + break; + } + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Convert rate down by multiple of 2, for quad */ +void SDLCALL SDL_RateDIV2_c4(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate / 2\n"); +#endif + src = cvt->buf; + dst = cvt->buf; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt/8; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + src += 8; + dst += 4; + } + break; + case 16: + for ( i=cvt->len_cvt/16; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + src += 16; + dst += 8; + } + break; + } + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert rate down by multiple of 2, for 5.1 */ +void SDLCALL SDL_RateDIV2_c6(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate / 2\n"); +#endif + src = cvt->buf; + dst = cvt->buf; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt/12; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + src += 12; + dst += 6; + } + break; + case 16: + for ( i=cvt->len_cvt/24; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = src[3]; + dst[4] = src[4]; + dst[5] = src[5]; + dst[6] = src[6]; + dst[7] = src[7]; + dst[8] = src[8]; + dst[9] = src[9]; + dst[10] = src[10]; + dst[11] = src[11]; + src += 24; + dst += 12; + } + break; + } + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Very slow rate conversion routine */ +void SDLCALL SDL_RateSLOW(SDL_AudioCVT *cvt, Uint16 format) +{ + double ipos; + int i, clen; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate * %4.4f\n", 1.0/cvt->rate_incr); +#endif + clen = (int)((double)cvt->len_cvt / cvt->rate_incr); + if ( cvt->rate_incr > 1.0 ) { + switch (format & 0xFF) { + case 8: { + Uint8 *output; + + output = cvt->buf; + ipos = 0.0; + for ( i=clen; i; --i ) { + *output = cvt->buf[(int)ipos]; + ipos += cvt->rate_incr; + output += 1; + } + } + break; + + case 16: { + Uint16 *output; + + clen &= ~1; + output = (Uint16 *)cvt->buf; + ipos = 0.0; + for ( i=clen/2; i; --i ) { + *output=((Uint16 *)cvt->buf)[(int)ipos]; + ipos += cvt->rate_incr; + output += 1; + } + } + break; + } + } else { + switch (format & 0xFF) { + case 8: { + Uint8 *output; + + output = cvt->buf+clen; + ipos = (double)cvt->len_cvt; + for ( i=clen; i; --i ) { + ipos -= cvt->rate_incr; + output -= 1; + *output = cvt->buf[(int)ipos]; + } + } + break; + + case 16: { + Uint16 *output; + + clen &= ~1; + output = (Uint16 *)(cvt->buf+clen); + ipos = (double)cvt->len_cvt/2; + for ( i=clen/2; i; --i ) { + ipos -= cvt->rate_incr; + output -= 1; + *output=((Uint16 *)cvt->buf)[(int)ipos]; + } + } + break; + } + } + cvt->len_cvt = clen; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +int SDL_ConvertAudio(SDL_AudioCVT *cvt) +{ + /* Make sure there's data to convert */ + if ( cvt->buf == NULL ) { + SDL_SetError("No buffer allocated for conversion"); + return(-1); + } + /* Return okay if no conversion is necessary */ + cvt->len_cvt = cvt->len; + if ( cvt->filters[0] == NULL ) { + return(0); + } + + /* Set up the conversion and go! */ + cvt->filter_index = 0; + cvt->filters[0](cvt, cvt->src_format); + return(0); +} + +/* Creates a set of audio filters to convert from one format to another. + Returns -1 if the format conversion is not supported, or 1 if the + audio filter is set up. +*/ + +int SDL_BuildAudioCVT(SDL_AudioCVT *cvt, + Uint16 src_format, Uint8 src_channels, int src_rate, + Uint16 dst_format, Uint8 dst_channels, int dst_rate) +{ +/*printf("Build format %04x->%04x, channels %u->%u, rate %d->%d\n", + src_format, dst_format, src_channels, dst_channels, src_rate, dst_rate);*/ + /* Start off with no conversion necessary */ + cvt->needed = 0; + cvt->filter_index = 0; + cvt->filters[0] = NULL; + cvt->len_mult = 1; + cvt->len_ratio = 1.0; + + /* First filter: Endian conversion from src to dst */ + if ( (src_format & 0x1000) != (dst_format & 0x1000) + && ((src_format & 0xff) == 16) && ((dst_format & 0xff) == 16)) { + cvt->filters[cvt->filter_index++] = SDL_ConvertEndian; + } + + /* Second filter: Sign conversion -- signed/unsigned */ + if ( (src_format & 0x8000) != (dst_format & 0x8000) ) { + cvt->filters[cvt->filter_index++] = SDL_ConvertSign; + } + + /* Next filter: Convert 16 bit <--> 8 bit PCM */ + if ( (src_format & 0xFF) != (dst_format & 0xFF) ) { + switch (dst_format&0x10FF) { + case AUDIO_U8: + cvt->filters[cvt->filter_index++] = + SDL_Convert8; + cvt->len_ratio /= 2; + break; + case AUDIO_U16LSB: + cvt->filters[cvt->filter_index++] = + SDL_Convert16LSB; + cvt->len_mult *= 2; + cvt->len_ratio *= 2; + break; + case AUDIO_U16MSB: + cvt->filters[cvt->filter_index++] = + SDL_Convert16MSB; + cvt->len_mult *= 2; + cvt->len_ratio *= 2; + break; + } + } + + /* Last filter: Mono/Stereo conversion */ + if ( src_channels != dst_channels ) { + if ( (src_channels == 1) && (dst_channels > 1) ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertStereo; + cvt->len_mult *= 2; + src_channels = 2; + cvt->len_ratio *= 2; + } + if ( (src_channels == 2) && + (dst_channels == 6) ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertSurround; + src_channels = 6; + cvt->len_mult *= 3; + cvt->len_ratio *= 3; + } + if ( (src_channels == 2) && + (dst_channels == 4) ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertSurround_4; + src_channels = 4; + cvt->len_mult *= 2; + cvt->len_ratio *= 2; + } + while ( (src_channels*2) <= dst_channels ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertStereo; + cvt->len_mult *= 2; + src_channels *= 2; + cvt->len_ratio *= 2; + } + if ( (src_channels == 6) && + (dst_channels <= 2) ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertStrip; + src_channels = 2; + cvt->len_ratio /= 3; + } + if ( (src_channels == 6) && + (dst_channels == 4) ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertStrip_2; + src_channels = 4; + cvt->len_ratio /= 2; + } + /* This assumes that 4 channel audio is in the format: + Left {front/back} + Right {front/back} + so converting to L/R stereo works properly. + */ + while ( ((src_channels%2) == 0) && + ((src_channels/2) >= dst_channels) ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertMono; + src_channels /= 2; + cvt->len_ratio /= 2; + } + if ( src_channels != dst_channels ) { + /* Uh oh.. */; + } + } + + /* Do rate conversion */ + cvt->rate_incr = 0.0; + if ( (src_rate/100) != (dst_rate/100) ) { + Uint32 hi_rate, lo_rate; + int len_mult; + double len_ratio; + void (SDLCALL *rate_cvt)(SDL_AudioCVT *cvt, Uint16 format); + + if ( src_rate > dst_rate ) { + hi_rate = src_rate; + lo_rate = dst_rate; + switch (src_channels) { + case 1: rate_cvt = SDL_RateDIV2; break; + case 2: rate_cvt = SDL_RateDIV2_c2; break; + case 4: rate_cvt = SDL_RateDIV2_c4; break; + case 6: rate_cvt = SDL_RateDIV2_c6; break; + default: return -1; + } + len_mult = 1; + len_ratio = 0.5; + } else { + hi_rate = dst_rate; + lo_rate = src_rate; + switch (src_channels) { + case 1: rate_cvt = SDL_RateMUL2; break; + case 2: rate_cvt = SDL_RateMUL2_c2; break; + case 4: rate_cvt = SDL_RateMUL2_c4; break; + case 6: rate_cvt = SDL_RateMUL2_c6; break; + default: return -1; + } + len_mult = 2; + len_ratio = 2.0; + } + /* If hi_rate = lo_rate*2^x then conversion is easy */ + while ( ((lo_rate*2)/100) <= (hi_rate/100) ) { + cvt->filters[cvt->filter_index++] = rate_cvt; + cvt->len_mult *= len_mult; + lo_rate *= 2; + cvt->len_ratio *= len_ratio; + } + /* We may need a slow conversion here to finish up */ + if ( (lo_rate/100) != (hi_rate/100) ) { +#if 1 + /* The problem with this is that if the input buffer is + say 1K, and the conversion rate is say 1.1, then the + output buffer is 1.1K, which may not be an acceptable + buffer size for the audio driver (not a power of 2) + */ + /* For now, punt and hope the rate distortion isn't great. + */ +#else + if ( src_rate < dst_rate ) { + cvt->rate_incr = (double)lo_rate/hi_rate; + cvt->len_mult *= 2; + cvt->len_ratio /= cvt->rate_incr; + } else { + cvt->rate_incr = (double)hi_rate/lo_rate; + cvt->len_ratio *= cvt->rate_incr; + } + cvt->filters[cvt->filter_index++] = SDL_RateSLOW; +#endif + } + } + + /* Set up the filter information */ + if ( cvt->filter_index != 0 ) { + cvt->needed = 1; + cvt->src_format = src_format; + cvt->dst_format = dst_format; + cvt->len = 0; + cvt->buf = NULL; + cvt->filters[cvt->filter_index] = NULL; + } + return(cvt->needed); +} diff --git a/src/audio/SDL_audiodev.c b/src/audio/SDL_audiodev.c new file mode 100644 index 0000000..41d882f --- /dev/null +++ b/src/audio/SDL_audiodev.c @@ -0,0 +1,179 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Get the name of the audio device we use for output */ + +#if SDL_AUDIO_DRIVER_BSD || SDL_AUDIO_DRIVER_OSS || SDL_AUDIO_DRIVER_SUNAUDIO + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "SDL_stdinc.h" +#include "SDL_audiodev_c.h" + +#ifndef _PATH_DEV_DSP +#if defined(__NETBSD__) || defined(__OPENBSD__) +#define _PATH_DEV_DSP "/dev/audio" +#else +#define _PATH_DEV_DSP "/dev/dsp" +#endif +#endif +#ifndef _PATH_DEV_DSP24 +#define _PATH_DEV_DSP24 "/dev/sound/dsp" +#endif +#ifndef _PATH_DEV_AUDIO +#define _PATH_DEV_AUDIO "/dev/audio" +#endif + + +int SDL_OpenAudioPath(char *path, int maxlen, int flags, int classic) +{ + const char *audiodev; + int audio_fd; + char audiopath[1024]; + + /* Figure out what our audio device is */ + if ( ((audiodev=SDL_getenv("SDL_PATH_DSP")) == NULL) && + ((audiodev=SDL_getenv("AUDIODEV")) == NULL) ) { + if ( classic ) { + audiodev = _PATH_DEV_AUDIO; + } else { + struct stat sb; + + /* Added support for /dev/sound/\* in Linux 2.4 */ + if ( ((stat("/dev/sound", &sb) == 0) && S_ISDIR(sb.st_mode)) && + ((stat(_PATH_DEV_DSP24, &sb) == 0) && S_ISCHR(sb.st_mode)) ) { + audiodev = _PATH_DEV_DSP24; + } else { + audiodev = _PATH_DEV_DSP; + } + } + } + audio_fd = open(audiodev, flags, 0); + + /* If the first open fails, look for other devices */ + if ( (audio_fd < 0) && (SDL_strlen(audiodev) < (sizeof(audiopath)-3)) ) { + int exists, instance; + struct stat sb; + + instance = 1; + do { /* Don't use errno ENOENT - it may not be thread-safe */ + SDL_snprintf(audiopath, SDL_arraysize(audiopath), + "%s%d", audiodev, instance++); + exists = 0; + if ( stat(audiopath, &sb) == 0 ) { + exists = 1; + audio_fd = open(audiopath, flags, 0); + } + } while ( exists && (audio_fd < 0) ); + audiodev = audiopath; + } + if ( path != NULL ) { + SDL_strlcpy(path, audiodev, maxlen); + path[maxlen-1] = '\0'; + } + return(audio_fd); +} + +#elif SDL_AUDIO_DRIVER_PAUD + +/* Get the name of the audio device we use for output */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include "SDL_stdinc.h" +#include "SDL_audiodev_c.h" + +#ifndef _PATH_DEV_DSP +#define _PATH_DEV_DSP "/dev/%caud%c/%c" +#endif + +char devsettings[][3] = +{ + { 'p', '0', '1' }, { 'p', '0', '2' }, { 'p', '0', '3' }, { 'p', '0', '4' }, + { 'p', '1', '1' }, { 'p', '1', '2' }, { 'p', '1', '3' }, { 'p', '1', '4' }, + { 'p', '2', '1' }, { 'p', '2', '2' }, { 'p', '2', '3' }, { 'p', '2', '4' }, + { 'p', '3', '1' }, { 'p', '3', '2' }, { 'p', '3', '3' }, { 'p', '3', '4' }, + { 'b', '0', '1' }, { 'b', '0', '2' }, { 'b', '0', '3' }, { 'b', '0', '4' }, + { 'b', '1', '1' }, { 'b', '1', '2' }, { 'b', '1', '3' }, { 'b', '1', '4' }, + { 'b', '2', '1' }, { 'b', '2', '2' }, { 'b', '2', '3' }, { 'b', '2', '4' }, + { 'b', '3', '1' }, { 'b', '3', '2' }, { 'b', '3', '3' }, { 'b', '3', '4' }, + { '\0', '\0', '\0' } +}; + +static int OpenUserDefinedDevice(char *path, int maxlen, int flags) +{ + const char *audiodev; + int audio_fd; + + /* Figure out what our audio device is */ + if ((audiodev=SDL_getenv("SDL_PATH_DSP")) == NULL) { + audiodev=SDL_getenv("AUDIODEV"); + } + if ( audiodev == NULL ) { + return -1; + } + audio_fd = open(audiodev, flags, 0); + if ( path != NULL ) { + SDL_strlcpy(path, audiodev, maxlen); + path[maxlen-1] = '\0'; + } + return audio_fd; +} + +int SDL_OpenAudioPath(char *path, int maxlen, int flags, int classic) +{ + struct stat sb; + int audio_fd; + char audiopath[1024]; + int cycle; + + audio_fd = OpenUserDefinedDevice(path,maxlen,flags); + if ( audio_fd != -1 ) { + return audio_fd; + } + + cycle = 0; + while( devsettings[cycle][0] != '\0' ) { + SDL_snprintf( audiopath, SDL_arraysize(audiopath), + _PATH_DEV_DSP, + devsettings[cycle][0], + devsettings[cycle][1], + devsettings[cycle][2]); + + if ( stat(audiopath, &sb) == 0 ) { + audio_fd = open(audiopath, flags, 0); + if ( audio_fd > 0 ) { + if ( path != NULL ) { + SDL_strlcpy( path, audiopath, maxlen ); + } + return audio_fd; + } + } + } + return -1; +} + +#endif /* Audio driver selection */ diff --git a/src/audio/SDL_audiodev_c.h b/src/audio/SDL_audiodev_c.h new file mode 100644 index 0000000..d4c0c9c --- /dev/null +++ b/src/audio/SDL_audiodev_c.h @@ -0,0 +1,26 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Open the audio device, storing the pathname in 'path' */ +extern int SDL_OpenAudioPath(char *path, int maxlen, int flags, int classic); + diff --git a/src/audio/SDL_audiomem.h b/src/audio/SDL_audiomem.h new file mode 100644 index 0000000..3b164ab --- /dev/null +++ b/src/audio/SDL_audiomem.h @@ -0,0 +1,25 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#define SDL_AllocAudioMem SDL_malloc +#define SDL_FreeAudioMem SDL_free diff --git a/src/audio/SDL_mixer.c b/src/audio/SDL_mixer.c new file mode 100644 index 0000000..5a1d7ef --- /dev/null +++ b/src/audio/SDL_mixer.c @@ -0,0 +1,264 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* This provides the default mixing callback for the SDL audio routines */ + +#include "SDL_cpuinfo.h" +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "SDL_sysaudio.h" +#include "SDL_mixer_MMX.h" +#include "SDL_mixer_MMX_VC.h" +#include "SDL_mixer_m68k.h" + +/* This table is used to add two sound values together and pin + * the value to avoid overflow. (used with permission from ARDI) + * Changed to use 0xFE instead of 0xFF for better sound quality. + */ +static const Uint8 mix8[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, + 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, + 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, + 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, + 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, + 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, + 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, + 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, + 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, + 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, + 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE +}; + +/* The volume ranges from 0 - 128 */ +#define ADJUST_VOLUME(s, v) (s = (s*v)/SDL_MIX_MAXVOLUME) +#define ADJUST_VOLUME_U8(s, v) (s = (((s-128)*v)/SDL_MIX_MAXVOLUME)+128) + +void SDL_MixAudio (Uint8 *dst, const Uint8 *src, Uint32 len, int volume) +{ + Uint16 format; + + if ( volume == 0 ) { + return; + } + /* Mix the user-level audio format */ + if ( current_audio ) { + if ( current_audio->convert.needed ) { + format = current_audio->convert.src_format; + } else { + format = current_audio->spec.format; + } + } else { + /* HACK HACK HACK */ + format = AUDIO_S16; + } + switch (format) { + + case AUDIO_U8: { +#if defined(__GNUC__) && defined(__M68000__) && defined(SDL_ASSEMBLY_ROUTINES) + SDL_MixAudio_m68k_U8((char*)dst,(char*)src,(unsigned long)len,(long)volume,(char *)mix8); +#else + Uint8 src_sample; + + while ( len-- ) { + src_sample = *src; + ADJUST_VOLUME_U8(src_sample, volume); + *dst = mix8[*dst+src_sample]; + ++dst; + ++src; + } +#endif + } + break; + + case AUDIO_S8: { +#if defined(SDL_BUGGY_MMX_MIXERS) /* buggy, so we're disabling them. --ryan. */ +#if defined(__GNUC__) && defined(__i386__) && defined(SDL_ASSEMBLY_ROUTINES) + if (SDL_HasMMX()) + { + SDL_MixAudio_MMX_S8((char*)dst,(char*)src,(unsigned int)len,(int)volume); + } + else +#elif ((defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)) && defined(SDL_ASSEMBLY_ROUTINES) + if (SDL_HasMMX()) + { + SDL_MixAudio_MMX_S8_VC((char*)dst,(char*)src,(unsigned int)len,(int)volume); + } + else +#endif +#endif + +#if defined(__GNUC__) && defined(__M68000__) && defined(SDL_ASSEMBLY_ROUTINES) + SDL_MixAudio_m68k_S8((char*)dst,(char*)src,(unsigned long)len,(long)volume); +#else + { + Sint8 *dst8, *src8; + Sint8 src_sample; + int dst_sample; + const int max_audioval = ((1<<(8-1))-1); + const int min_audioval = -(1<<(8-1)); + + src8 = (Sint8 *)src; + dst8 = (Sint8 *)dst; + while ( len-- ) { + src_sample = *src8; + ADJUST_VOLUME(src_sample, volume); + dst_sample = *dst8 + src_sample; + if ( dst_sample > max_audioval ) { + *dst8 = max_audioval; + } else + if ( dst_sample < min_audioval ) { + *dst8 = min_audioval; + } else { + *dst8 = dst_sample; + } + ++dst8; + ++src8; + } + } +#endif + } + break; + + case AUDIO_S16LSB: { +#if defined(SDL_BUGGY_MMX_MIXERS) /* buggy, so we're disabling them. --ryan. */ +#if defined(__GNUC__) && defined(__i386__) && defined(SDL_ASSEMBLY_ROUTINES) + if (SDL_HasMMX()) + { + SDL_MixAudio_MMX_S16((char*)dst,(char*)src,(unsigned int)len,(int)volume); + } + else +#elif ((defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)) && defined(SDL_ASSEMBLY_ROUTINES) + if (SDL_HasMMX()) + { + SDL_MixAudio_MMX_S16_VC((char*)dst,(char*)src,(unsigned int)len,(int)volume); + } + else +#endif +#endif + +#if defined(__GNUC__) && defined(__M68000__) && defined(SDL_ASSEMBLY_ROUTINES) + SDL_MixAudio_m68k_S16LSB((short*)dst,(short*)src,(unsigned long)len,(long)volume); +#else + { + Sint16 src1, src2; + int dst_sample; + const int max_audioval = ((1<<(16-1))-1); + const int min_audioval = -(1<<(16-1)); + + len /= 2; + while ( len-- ) { + src1 = ((src[1])<<8|src[0]); + ADJUST_VOLUME(src1, volume); + src2 = ((dst[1])<<8|dst[0]); + src += 2; + dst_sample = src1+src2; + if ( dst_sample > max_audioval ) { + dst_sample = max_audioval; + } else + if ( dst_sample < min_audioval ) { + dst_sample = min_audioval; + } + dst[0] = dst_sample&0xFF; + dst_sample >>= 8; + dst[1] = dst_sample&0xFF; + dst += 2; + } + } +#endif + } + break; + + case AUDIO_S16MSB: { +#if defined(__GNUC__) && defined(__M68000__) && defined(SDL_ASSEMBLY_ROUTINES) + SDL_MixAudio_m68k_S16MSB((short*)dst,(short*)src,(unsigned long)len,(long)volume); +#else + Sint16 src1, src2; + int dst_sample; + const int max_audioval = ((1<<(16-1))-1); + const int min_audioval = -(1<<(16-1)); + + len /= 2; + while ( len-- ) { + src1 = ((src[0])<<8|src[1]); + ADJUST_VOLUME(src1, volume); + src2 = ((dst[0])<<8|dst[1]); + src += 2; + dst_sample = src1+src2; + if ( dst_sample > max_audioval ) { + dst_sample = max_audioval; + } else + if ( dst_sample < min_audioval ) { + dst_sample = min_audioval; + } + dst[1] = dst_sample&0xFF; + dst_sample >>= 8; + dst[0] = dst_sample&0xFF; + dst += 2; + } +#endif + } + break; + + default: /* If this happens... FIXME! */ + SDL_SetError("SDL_MixAudio(): unknown audio format"); + return; + } +} + diff --git a/src/audio/SDL_mixer_MMX.c b/src/audio/SDL_mixer_MMX.c new file mode 100644 index 0000000..10b1ccb --- /dev/null +++ b/src/audio/SDL_mixer_MMX.c @@ -0,0 +1,207 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + MMX assembler version of SDL_MixAudio for signed little endian 16 bit samples and signed 8 bit samples + Copyright 2002 Stephane Marchesin (stephane.marchesin@wanadoo.fr) + This code is licensed under the LGPL (see COPYING for details) + + Assumes buffer size in bytes is a multiple of 16 + Assumes SDL_MIX_MAXVOLUME = 128 +*/ + + +/*********************************************** +* Mixing for 16 bit signed buffers +***********************************************/ + +#if defined(SDL_BUGGY_MMX_MIXERS) /* buggy, so we're disabling them. --ryan. */ +#if defined(__GNUC__) && defined(__i386__) && defined(SDL_ASSEMBLY_ROUTINES) +void SDL_MixAudio_MMX_S16(char* dst,char* src,unsigned int size,int volume) +{ + __asm__ __volatile__ ( + +" movl %3,%%eax\n" /* eax = volume */ + +" movl %2,%%edx\n" /* edx = size */ + +" shrl $4,%%edx\n" /* process 16 bytes per iteration = 8 samples */ + +" jz .endS16\n" + +" pxor %%mm0,%%mm0\n" + +" movd %%eax,%%mm0\n" +" movq %%mm0,%%mm1\n" +" psllq $16,%%mm0\n" +" por %%mm1,%%mm0\n" +" psllq $16,%%mm0\n" +" por %%mm1,%%mm0\n" +" psllq $16,%%mm0\n" +" por %%mm1,%%mm0\n" /* mm0 = vol|vol|vol|vol */ + +".align 8\n" +" .mixloopS16:\n" + +" movq (%1),%%mm1\n" /* mm1 = a|b|c|d */ + +" movq %%mm1,%%mm2\n" /* mm2 = a|b|c|d */ + +" movq 8(%1),%%mm4\n" /* mm4 = e|f|g|h */ + + /* pré charger le buffer dst dans mm7 */ +" movq (%0),%%mm7\n" /* mm7 = dst[0] */ + + /* multiplier par le volume */ +" pmullw %%mm0,%%mm1\n" /* mm1 = l(a*v)|l(b*v)|l(c*v)|l(d*v) */ + +" pmulhw %%mm0,%%mm2\n" /* mm2 = h(a*v)|h(b*v)|h(c*v)|h(d*v) */ +" movq %%mm4,%%mm5\n" /* mm5 = e|f|g|h */ + +" pmullw %%mm0,%%mm4\n" /* mm4 = l(e*v)|l(f*v)|l(g*v)|l(h*v) */ + +" pmulhw %%mm0,%%mm5\n" /* mm5 = h(e*v)|h(f*v)|h(g*v)|h(h*v) */ +" movq %%mm1,%%mm3\n" /* mm3 = l(a*v)|l(b*v)|l(c*v)|l(d*v) */ + +" punpckhwd %%mm2,%%mm1\n" /* mm1 = a*v|b*v */ + +" movq %%mm4,%%mm6\n" /* mm6 = l(e*v)|l(f*v)|l(g*v)|l(h*v) */ +" punpcklwd %%mm2,%%mm3\n" /* mm3 = c*v|d*v */ + +" punpckhwd %%mm5,%%mm4\n" /* mm4 = e*f|f*v */ + +" punpcklwd %%mm5,%%mm6\n" /* mm6 = g*v|h*v */ + + /* pré charger le buffer dst dans mm5 */ +" movq 8(%0),%%mm5\n" /* mm5 = dst[1] */ + + /* diviser par 128 */ +" psrad $7,%%mm1\n" /* mm1 = a*v/128|b*v/128 , 128 = SDL_MIX_MAXVOLUME */ +" add $16,%1\n" + +" psrad $7,%%mm3\n" /* mm3 = c*v/128|d*v/128 */ + +" psrad $7,%%mm4\n" /* mm4 = e*v/128|f*v/128 */ + + /* mm1 = le sample avec le volume modifié */ +" packssdw %%mm1,%%mm3\n" /* mm3 = s(a*v|b*v|c*v|d*v) */ + +" psrad $7,%%mm6\n" /* mm6= g*v/128|h*v/128 */ +" paddsw %%mm7,%%mm3\n" /* mm3 = adjust_volume(src)+dst */ + + /* mm4 = le sample avec le volume modifié */ +" packssdw %%mm4,%%mm6\n" /* mm6 = s(e*v|f*v|g*v|h*v) */ +" movq %%mm3,(%0)\n" + +" paddsw %%mm5,%%mm6\n" /* mm6 = adjust_volume(src)+dst */ + +" movq %%mm6,8(%0)\n" + +" add $16,%0\n" + +" dec %%edx\n" + +" jnz .mixloopS16\n" + +" emms\n" + +".endS16:\n" + : + : "r" (dst), "r"(src),"m"(size), + "m"(volume) + : "eax","edx","memory" + ); +} + + + +/*////////////////////////////////////////////// */ +/* Mixing for 8 bit signed buffers */ +/*////////////////////////////////////////////// */ + +void SDL_MixAudio_MMX_S8(char* dst,char* src,unsigned int size,int volume) +{ + __asm__ __volatile__ ( + +" movl %3,%%eax\n" /* eax = volume */ + +" movd %%eax,%%mm0\n" +" movq %%mm0,%%mm1\n" +" psllq $16,%%mm0\n" +" por %%mm1,%%mm0\n" +" psllq $16,%%mm0\n" +" por %%mm1,%%mm0\n" +" psllq $16,%%mm0\n" +" por %%mm1,%%mm0\n" + +" movl %2,%%edx\n" /* edx = size */ +" shr $3,%%edx\n" /* process 8 bytes per iteration = 8 samples */ + +" cmp $0,%%edx\n" +" je .endS8\n" + +".align 8\n" +" .mixloopS8:\n" + +" pxor %%mm2,%%mm2\n" /* mm2 = 0 */ +" movq (%1),%%mm1\n" /* mm1 = a|b|c|d|e|f|g|h */ + +" movq %%mm1,%%mm3\n" /* mm3 = a|b|c|d|e|f|g|h */ + + /* on va faire le "sign extension" en faisant un cmp avec 0 qui retourne 1 si <0, 0 si >0 */ +" pcmpgtb %%mm1,%%mm2\n" /* mm2 = 11111111|00000000|00000000.... */ + +" punpckhbw %%mm2,%%mm1\n" /* mm1 = 0|a|0|b|0|c|0|d */ + +" punpcklbw %%mm2,%%mm3\n" /* mm3 = 0|e|0|f|0|g|0|h */ +" movq (%0),%%mm2\n" /* mm2 = destination */ + +" pmullw %%mm0,%%mm1\n" /* mm1 = v*a|v*b|v*c|v*d */ +" add $8,%1\n" + +" pmullw %%mm0,%%mm3\n" /* mm3 = v*e|v*f|v*g|v*h */ +" psraw $7,%%mm1\n" /* mm1 = v*a/128|v*b/128|v*c/128|v*d/128 */ + +" psraw $7,%%mm3\n" /* mm3 = v*e/128|v*f/128|v*g/128|v*h/128 */ + +" packsswb %%mm1,%%mm3\n" /* mm1 = v*a/128|v*b/128|v*c/128|v*d/128|v*e/128|v*f/128|v*g/128|v*h/128 */ + +" paddsb %%mm2,%%mm3\n" /* add to destination buffer */ + +" movq %%mm3,(%0)\n" /* store back to ram */ +" add $8,%0\n" + +" dec %%edx\n" + +" jnz .mixloopS8\n" + +".endS8:\n" +" emms\n" + : + : "r" (dst), "r"(src),"m"(size), + "m"(volume) + : "eax","edx","memory" + ); +} +#endif +#endif diff --git a/src/audio/SDL_mixer_MMX.h b/src/audio/SDL_mixer_MMX.h new file mode 100644 index 0000000..836b259 --- /dev/null +++ b/src/audio/SDL_mixer_MMX.h @@ -0,0 +1,17 @@ +/* + headers for MMX assembler version of SDL_MixAudio + Copyright 2002 Stephane Marchesin (stephane.marchesin@wanadoo.fr) + This code is licensed under the LGPL (see COPYING for details) + + Assumes buffer size in bytes is a multiple of 16 + Assumes SDL_MIX_MAXVOLUME = 128 +*/ +#include "SDL_config.h" + +#if defined(SDL_BUGGY_MMX_MIXERS) /* buggy, so we're disabling them. --ryan. */ +#if defined(__GNUC__) && defined(__i386__) && defined(SDL_ASSEMBLY_ROUTINES) +void SDL_MixAudio_MMX_S16(char* ,char* ,unsigned int ,int ); +void SDL_MixAudio_MMX_S8(char* ,char* ,unsigned int ,int ); +#endif +#endif + diff --git a/src/audio/SDL_mixer_MMX_VC.c b/src/audio/SDL_mixer_MMX_VC.c new file mode 100644 index 0000000..e9d53c1 --- /dev/null +++ b/src/audio/SDL_mixer_MMX_VC.c @@ -0,0 +1,183 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "SDL_mixer_MMX_VC.h" + +#if defined(SDL_BUGGY_MMX_MIXERS) /* buggy, so we're disabling them. --ryan. */ +#if ((defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)) && defined(SDL_ASSEMBLY_ROUTINES) +// MMX assembler version of SDL_MixAudio for signed little endian 16 bit samples and signed 8 bit samples +// Copyright 2002 Stephane Marchesin (stephane.marchesin@wanadoo.fr) +// Converted to Intel ASM notation by Cth +// This code is licensed under the LGPL (see COPYING for details) +// +// Assumes buffer size in bytes is a multiple of 16 +// Assumes SDL_MIX_MAXVOLUME = 128 + + +//////////////////////////////////////////////// +// Mixing for 16 bit signed buffers +//////////////////////////////////////////////// + +void SDL_MixAudio_MMX_S16_VC(char* dst,char* src,unsigned int nSize,int volume) +{ + __asm + { + + push edi + push esi + push ebx + + mov edi, dst // edi = dst + mov esi, src // esi = src + mov eax, volume // eax = volume + mov ebx, nSize // ebx = size + shr ebx, 4 // process 16 bytes per iteration = 8 samples + jz endS16 + + pxor mm0, mm0 + movd mm0, eax //%%eax,%%mm0 + movq mm1, mm0 //%%mm0,%%mm1 + psllq mm0, 16 //$16,%%mm0 + por mm0, mm1 //%%mm1,%%mm0 + psllq mm0, 16 //$16,%%mm0 + por mm0, mm1 //%%mm1,%%mm0 + psllq mm0, 16 //$16,%%mm0 + por mm0, mm1 //%%mm1,%%mm0 // mm0 = vol|vol|vol|vol + + #ifndef __WATCOMC__ + align 16 + #endif +mixloopS16: + movq mm1, [esi] //(%%esi),%%mm1\n" // mm1 = a|b|c|d + movq mm2, mm1 //%%mm1,%%mm2\n" // mm2 = a|b|c|d + movq mm4, [esi + 8] //8(%%esi),%%mm4\n" // mm4 = e|f|g|h + // pre charger le buffer dst dans mm7 + movq mm7, [edi] //(%%edi),%%mm7\n" // mm7 = dst[0]" + // multiplier par le volume + pmullw mm1, mm0 //%%mm0,%%mm1\n" // mm1 = l(a*v)|l(b*v)|l(c*v)|l(d*v) + pmulhw mm2, mm0 //%%mm0,%%mm2\n" // mm2 = h(a*v)|h(b*v)|h(c*v)|h(d*v) + movq mm5, mm4 //%%mm4,%%mm5\n" // mm5 = e|f|g|h + pmullw mm4, mm0 //%%mm0,%%mm4\n" // mm4 = l(e*v)|l(f*v)|l(g*v)|l(h*v) + pmulhw mm5, mm0 //%%mm0,%%mm5\n" // mm5 = h(e*v)|h(f*v)|h(g*v)|h(h*v) + movq mm3, mm1 //%%mm1,%%mm3\n" // mm3 = l(a*v)|l(b*v)|l(c*v)|l(d*v) + punpckhwd mm1, mm2 //%%mm2,%%mm1\n" // mm1 = a*v|b*v + movq mm6, mm4 //%%mm4,%%mm6\n" // mm6 = l(e*v)|l(f*v)|l(g*v)|l(h*v) + punpcklwd mm3, mm2 //%%mm2,%%mm3\n" // mm3 = c*v|d*v + punpckhwd mm4, mm5 //%%mm5,%%mm4\n" // mm4 = e*f|f*v + punpcklwd mm6, mm5 //%%mm5,%%mm6\n" // mm6 = g*v|h*v + // pre charger le buffer dst dans mm5 + movq mm5, [edi + 8] //8(%%edi),%%mm5\n" // mm5 = dst[1] + // diviser par 128 + psrad mm1, 7 //$7,%%mm1\n" // mm1 = a*v/128|b*v/128 , 128 = SDL_MIX_MAXVOLUME + add esi, 16 //$16,%%esi\n" + psrad mm3, 7 //$7,%%mm3\n" // mm3 = c*v/128|d*v/128 + psrad mm4, 7 //$7,%%mm4\n" // mm4 = e*v/128|f*v/128 + // mm1 = le sample avec le volume modifie + packssdw mm3, mm1 //%%mm1,%%mm3\n" // mm3 = s(a*v|b*v|c*v|d*v) + psrad mm6, 7 //$7,%%mm6\n" // mm6= g*v/128|h*v/128 + paddsw mm3, mm7 //%%mm7,%%mm3\n" // mm3 = adjust_volume(src)+dst + // mm4 = le sample avec le volume modifie + packssdw mm6, mm4 //%%mm4,%%mm6\n" // mm6 = s(e*v|f*v|g*v|h*v) + movq [edi], mm3 //%%mm3,(%%edi)\n" + paddsw mm6, mm5 //%%mm5,%%mm6\n" // mm6 = adjust_volume(src)+dst + movq [edi + 8], mm6 //%%mm6,8(%%edi)\n" + add edi, 16 //$16,%%edi\n" + dec ebx //%%ebx\n" + jnz mixloopS16 + +endS16: + emms + + pop ebx + pop esi + pop edi + } + +} + +//////////////////////////////////////////////// +// Mixing for 8 bit signed buffers +//////////////////////////////////////////////// + +void SDL_MixAudio_MMX_S8_VC(char* dst,char* src,unsigned int nSize,int volume) +{ + _asm + { + + push edi + push esi + push ebx + + mov edi, dst //movl %0,%%edi // edi = dst + mov esi, src //%1,%%esi // esi = src + mov eax, volume //%3,%%eax // eax = volume + + movd mm0, eax //%%eax,%%mm0 + movq mm1, mm0 //%%mm0,%%mm1 + psllq mm0, 16 //$16,%%mm0 + por mm0, mm1 //%%mm1,%%mm0 + psllq mm0, 16 //$16,%%mm0 + por mm0, mm1 //%%mm1,%%mm0 + psllq mm0, 16 //$16,%%mm0 + por mm0, mm1 //%%mm1,%%mm0 + + mov ebx, nSize //%2,%%ebx // ebx = size + shr ebx, 3 //$3,%%ebx // process 8 bytes per iteration = 8 samples + cmp ebx, 0 //$0,%%ebx + je endS8 + + #ifndef __WATCOMC__ + align 16 + #endif +mixloopS8: + pxor mm2, mm2 //%%mm2,%%mm2 // mm2 = 0 + movq mm1, [esi] //(%%esi),%%mm1 // mm1 = a|b|c|d|e|f|g|h + movq mm3, mm1 //%%mm1,%%mm3 // mm3 = a|b|c|d|e|f|g|h + // on va faire le "sign extension" en faisant un cmp avec 0 qui retourne 1 si <0, 0 si >0 + pcmpgtb mm2, mm1 //%%mm1,%%mm2 // mm2 = 11111111|00000000|00000000.... + punpckhbw mm1, mm2 //%%mm2,%%mm1 // mm1 = 0|a|0|b|0|c|0|d + punpcklbw mm3, mm2 //%%mm2,%%mm3 // mm3 = 0|e|0|f|0|g|0|h + movq mm2, [edi] //(%%edi),%%mm2 // mm2 = destination + pmullw mm1, mm0 //%%mm0,%%mm1 // mm1 = v*a|v*b|v*c|v*d + add esi, 8 //$8,%%esi + pmullw mm3, mm0 //%%mm0,%%mm3 // mm3 = v*e|v*f|v*g|v*h + psraw mm1, 7 //$7,%%mm1 // mm1 = v*a/128|v*b/128|v*c/128|v*d/128 + psraw mm3, 7 //$7,%%mm3 // mm3 = v*e/128|v*f/128|v*g/128|v*h/128 + packsswb mm3, mm1 //%%mm1,%%mm3 // mm1 = v*a/128|v*b/128|v*c/128|v*d/128|v*e/128|v*f/128|v*g/128|v*h/128 + paddsb mm3, mm2 //%%mm2,%%mm3 // add to destination buffer + movq [edi], mm3 //%%mm3,(%%edi) // store back to ram + add edi, 8 //$8,%%edi + dec ebx //%%ebx + jnz mixloopS8 + +endS8: + emms + + pop ebx + pop esi + pop edi + } +} + +#endif /* SDL_ASSEMBLY_ROUTINES */ +#endif /* SDL_BUGGY_MMX_MIXERS */ diff --git a/src/audio/SDL_mixer_MMX_VC.h b/src/audio/SDL_mixer_MMX_VC.h new file mode 100644 index 0000000..7c67a36 --- /dev/null +++ b/src/audio/SDL_mixer_MMX_VC.h @@ -0,0 +1,38 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + + +#if defined(SDL_BUGGY_MMX_MIXERS) /* buggy, so we're disabling them. --ryan. */ +#if ((defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)) && defined(SDL_ASSEMBLY_ROUTINES) +/* headers for MMX assembler version of SDL_MixAudio + Copyright 2002 Stephane Marchesin (stephane.marchesin@wanadoo.fr) + Converted to Intel ASM notation by Cth + This code is licensed under the LGPL (see COPYING for details) + + Assumes buffer size in bytes is a multiple of 16 + Assumes SDL_MIX_MAXVOLUME = 128 +*/ +void SDL_MixAudio_MMX_S16_VC(char* ,char* ,unsigned int ,int ); +void SDL_MixAudio_MMX_S8_VC(char* ,char* ,unsigned int ,int ); +#endif +#endif diff --git a/src/audio/SDL_mixer_m68k.c b/src/audio/SDL_mixer_m68k.c new file mode 100644 index 0000000..477a6bb --- /dev/null +++ b/src/audio/SDL_mixer_m68k.c @@ -0,0 +1,211 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + m68k assembly mix routines + + Patrice Mandin +*/ + +#if defined(__M68000__) && defined(__GNUC__) +void SDL_MixAudio_m68k_U8(char* dst, char* src, long len, long volume, char* mix8) +{ + __asm__ __volatile__ ( + + "tstl %2\n" +" beqs stoploop_u8\n" +"mixloop_u8:\n" + + /* Mix a sample */ + +" moveq #0,%%d0\n" +" moveq #0,%%d1\n" + +" moveb %1@+,%%d0\n" /* d0 = *src++ */ +" sub #128,%%d0\n" /* d0 -= 128 */ +" muls %3,%%d0\n" /* d0 *= volume (0<=volume<=128) */ +" moveb %0@,%%d1\n" /* d1 = *dst */ +" asr #7,%%d0\n" /* d0 /= 128 (SDL_MIX_MAXVOLUME) */ +" add #128,%%d0\n" /* d0 += 128 */ + +" add %%d1,%%d0\n" + +" moveb %4@(%%d0:w),%0@+\n" + + /* Loop till done */ + +" subql #1,%2\n" +" bhis mixloop_u8\n" +"stoploop_u8:\n" + + : /* no return value */ + : /* input */ + "a"(dst), "a"(src), "d"(len), "d"(volume), "a"(mix8) + : /* clobbered registers */ + "d0", "d1", "cc", "memory" + ); +} + +void SDL_MixAudio_m68k_S8(char* dst, char* src, long len, long volume) +{ + __asm__ __volatile__ ( + + "tstl %2\n" +" beqs stoploop_s8\n" +" moveq #-128,%%d2\n" +" moveq #127,%%d3\n" +"mixloop_s8:\n" + + /* Mix a sample */ + +" moveq #0,%%d0\n" +" moveq #0,%%d1\n" + +" moveb %1@+,%%d0\n" /* d0 = *src++ */ +" muls %3,%%d0\n" /* d0 *= volume (0<=volume<=128) */ +" moveb %0@,%%d1\n" /* d1 = *dst */ +" asr #7,%%d0\n" /* d0 /= 128 (SDL_MIX_MAXVOLUME) */ + +" add %%d1,%%d0\n" + +" cmp %%d2,%%d0\n" +" bges lower_limit_s8\n" +" move %%d2,%%d0\n" +"lower_limit_s8:\n" + +" cmp %%d3,%%d0\n" +" bles upper_limit_s8\n" +" move %%d3,%%d0\n" +"upper_limit_s8:\n" +" moveb %%d0,%0@+\n" + + /* Loop till done */ + +" subql #1,%2\n" +" bhis mixloop_s8\n" +"stoploop_s8:\n" + + : /* no return value */ + : /* input */ + "a"(dst), "a"(src), "d"(len), "d"(volume) + : /* clobbered registers */ + "d0", "d1", "d2", "d3", "cc", "memory" + ); +} + +void SDL_MixAudio_m68k_S16MSB(short* dst, short* src, long len, long volume) +{ + __asm__ __volatile__ ( + + "tstl %2\n" +" beqs stoploop_s16msb\n" +" movel #-32768,%%d2\n" +" movel #32767,%%d3\n" +" lsrl #1,%2\n" +"mixloop_s16msb:\n" + + /* Mix a sample */ + +" move %1@+,%%d0\n" /* d0 = *src++ */ +" muls %3,%%d0\n" /* d0 *= volume (0<=volume<=128) */ +" move %0@,%%d1\n" /* d1 = *dst */ +" extl %%d1\n" /* extend d1 to 32 bits */ +" asrl #7,%%d0\n" /* d0 /= 128 (SDL_MIX_MAXVOLUME) */ + +" addl %%d1,%%d0\n" + +" cmpl %%d2,%%d0\n" +" bges lower_limit_s16msb\n" +" move %%d2,%%d0\n" +"lower_limit_s16msb:\n" + +" cmpl %%d3,%%d0\n" +" bles upper_limit_s16msb\n" +" move %%d3,%%d0\n" +"upper_limit_s16msb:\n" +" move %%d0,%0@+\n" + + /* Loop till done */ + +" subql #1,%2\n" +" bhis mixloop_s16msb\n" +"stoploop_s16msb:\n" + + : /* no return value */ + : /* input */ + "a"(dst), "a"(src), "d"(len), "d"(volume) + : /* clobbered registers */ + "d0", "d1", "d2", "d3", "cc", "memory" + ); +} + +void SDL_MixAudio_m68k_S16LSB(short* dst, short* src, long len, long volume) +{ + __asm__ __volatile__ ( + + "tstl %2\n" +" beqs stoploop_s16lsb\n" +" movel #-32768,%%d2\n" +" movel #32767,%%d3\n" +" lsrl #1,%2\n" +"mixloop_s16lsb:\n" + + /* Mix a sample */ + +" move %1@+,%%d0\n" /* d0 = *src++ */ +" rorw #8,%%d0\n" +" muls %3,%%d0\n" /* d0 *= volume (0<=volume<=128) */ +" move %0@,%%d1\n" /* d1 = *dst */ +" rorw #8,%%d1\n" +" extl %%d1\n" /* extend d1 to 32 bits */ +" asrl #7,%%d0\n" /* d0 /= 128 (SDL_MIX_MAXVOLUME) */ + +" addl %%d1,%%d0\n" + +" cmpl %%d2,%%d0\n" +" bges lower_limit_s16lsb\n" +" move %%d2,%%d0\n" +"lower_limit_s16lsb:\n" + +" cmpl %%d3,%%d0\n" +" bles upper_limit_s16lsb\n" +" move %%d3,%%d0\n" +"upper_limit_s16lsb:\n" +" rorw #8,%%d0\n" +" move %%d0,%0@+\n" + + /* Loop till done */ + +" subql #1,%2\n" +" bhis mixloop_s16lsb\n" +"stoploop_s16lsb:\n" + + : /* no return value */ + : /* input */ + "a"(dst), "a"(src), "d"(len), "d"(volume) + : /* clobbered registers */ + "d0", "d1", "d2", "d3", "cc", "memory" + ); +} +#endif + diff --git a/src/audio/SDL_mixer_m68k.h b/src/audio/SDL_mixer_m68k.h new file mode 100644 index 0000000..12b7f35 --- /dev/null +++ b/src/audio/SDL_mixer_m68k.h @@ -0,0 +1,36 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + m68k assembly mix routines + + Patrice Mandin +*/ + +#if defined(__M68000__) && defined(__GNUC__) +void SDL_MixAudio_m68k_U8(char* dst,char* src, long len, long volume, char* mix8); +void SDL_MixAudio_m68k_S8(char* dst,char* src, long len, long volume); + +void SDL_MixAudio_m68k_S16MSB(short* dst,short* src, long len, long volume); +void SDL_MixAudio_m68k_S16LSB(short* dst,short* src, long len, long volume); +#endif diff --git a/src/audio/SDL_sysaudio.h b/src/audio/SDL_sysaudio.h new file mode 100644 index 0000000..50cf179 --- /dev/null +++ b/src/audio/SDL_sysaudio.h @@ -0,0 +1,184 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is SDL_free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_sysaudio_h +#define _SDL_sysaudio_h + +#include "SDL_mutex.h" +#include "SDL_thread.h" + +/* The SDL audio driver */ +typedef struct SDL_AudioDevice SDL_AudioDevice; + +/* Define the SDL audio driver structure */ +#define _THIS SDL_AudioDevice *_this +#ifndef _STATUS +#define _STATUS SDL_status *status +#endif +struct SDL_AudioDevice { + /* * * */ + /* The name of this audio driver */ + const char *name; + + /* * * */ + /* The description of this audio driver */ + const char *desc; + + /* * * */ + /* Public driver functions */ + int (*OpenAudio)(_THIS, SDL_AudioSpec *spec); + void (*ThreadInit)(_THIS); /* Called by audio thread at start */ + void (*WaitAudio)(_THIS); + void (*PlayAudio)(_THIS); + Uint8 *(*GetAudioBuf)(_THIS); + void (*WaitDone)(_THIS); + void (*CloseAudio)(_THIS); + + /* * * */ + /* Lock / Unlock functions added for the Mac port */ + void (*LockAudio)(_THIS); + void (*UnlockAudio)(_THIS); + + /* * * */ + /* Data common to all devices */ + + /* The current audio specification (shared with audio thread) */ + SDL_AudioSpec spec; + + /* An audio conversion block for audio format emulation */ + SDL_AudioCVT convert; + + /* Current state flags */ + int enabled; + int paused; + int opened; + + /* Fake audio buffer for when the audio hardware is busy */ + Uint8 *fake_stream; + + /* A semaphore for locking the mixing buffers */ + SDL_mutex *mixer_lock; + + /* A thread to feed the audio device */ + SDL_Thread *thread; + Uint32 threadid; + + /* * * */ + /* Data private to this driver */ + struct SDL_PrivateAudioData *hidden; + + /* * * */ + /* The function used to dispose of this structure */ + void (*free)(_THIS); +}; +#undef _THIS + +typedef struct AudioBootStrap { + const char *name; + const char *desc; + int (*available)(void); + SDL_AudioDevice *(*create)(int devindex); +} AudioBootStrap; + +#if SDL_AUDIO_DRIVER_BSD +extern AudioBootStrap BSD_AUDIO_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_PULSE +extern AudioBootStrap PULSE_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_ALSA +extern AudioBootStrap ALSA_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_OSS +extern AudioBootStrap DSP_bootstrap; +extern AudioBootStrap DMA_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_QNXNTO +extern AudioBootStrap QNXNTOAUDIO_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_SUNAUDIO +extern AudioBootStrap SUNAUDIO_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_DMEDIA +extern AudioBootStrap DMEDIA_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_ARTS +extern AudioBootStrap ARTS_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_ESD +extern AudioBootStrap ESD_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_NAS +extern AudioBootStrap NAS_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_DSOUND +extern AudioBootStrap DSOUND_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_WAVEOUT +extern AudioBootStrap WAVEOUT_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_PAUD +extern AudioBootStrap Paud_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_BAUDIO +extern AudioBootStrap BAUDIO_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_COREAUDIO +extern AudioBootStrap COREAUDIO_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_SNDMGR +extern AudioBootStrap SNDMGR_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_MINT +extern AudioBootStrap MINTAUDIO_GSXB_bootstrap; +extern AudioBootStrap MINTAUDIO_MCSN_bootstrap; +extern AudioBootStrap MINTAUDIO_STFA_bootstrap; +extern AudioBootStrap MINTAUDIO_XBIOS_bootstrap; +extern AudioBootStrap MINTAUDIO_DMA8_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_DISK +extern AudioBootStrap DISKAUD_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_DUMMY +extern AudioBootStrap DUMMYAUD_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_DC +extern AudioBootStrap DCAUD_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_NDS +extern AudioBootStrap NDSAUD_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_MMEAUDIO +extern AudioBootStrap MMEAUDIO_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_DART +extern AudioBootStrap DART_bootstrap; +#endif +#if SDL_AUDIO_DRIVER_EPOCAUDIO +extern AudioBootStrap EPOCAudio_bootstrap; +#endif + +/* This is the current audio device */ +extern SDL_AudioDevice *current_audio; + +#endif /* _SDL_sysaudio_h */ diff --git a/src/audio/SDL_wave.c b/src/audio/SDL_wave.c new file mode 100644 index 0000000..a2f1164 --- /dev/null +++ b/src/audio/SDL_wave.c @@ -0,0 +1,600 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Microsoft WAVE file loading routines */ + +#include "SDL_audio.h" +#include "SDL_wave.h" + + +static int ReadChunk(SDL_RWops *src, Chunk *chunk); + +struct MS_ADPCM_decodestate { + Uint8 hPredictor; + Uint16 iDelta; + Sint16 iSamp1; + Sint16 iSamp2; +}; +static struct MS_ADPCM_decoder { + WaveFMT wavefmt; + Uint16 wSamplesPerBlock; + Uint16 wNumCoef; + Sint16 aCoeff[7][2]; + /* * * */ + struct MS_ADPCM_decodestate state[2]; +} MS_ADPCM_state; + +static int InitMS_ADPCM(WaveFMT *format) +{ + Uint8 *rogue_feel; + Uint16 extra_info; + int i; + + /* Set the rogue pointer to the MS_ADPCM specific data */ + MS_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding); + MS_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels); + MS_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency); + MS_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate); + MS_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign); + MS_ADPCM_state.wavefmt.bitspersample = + SDL_SwapLE16(format->bitspersample); + rogue_feel = (Uint8 *)format+sizeof(*format); + if ( sizeof(*format) == 16 ) { + extra_info = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + } + MS_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + MS_ADPCM_state.wNumCoef = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + if ( MS_ADPCM_state.wNumCoef != 7 ) { + SDL_SetError("Unknown set of MS_ADPCM coefficients"); + return(-1); + } + for ( i=0; i<MS_ADPCM_state.wNumCoef; ++i ) { + MS_ADPCM_state.aCoeff[i][0] = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + MS_ADPCM_state.aCoeff[i][1] = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + } + return(0); +} + +static Sint32 MS_ADPCM_nibble(struct MS_ADPCM_decodestate *state, + Uint8 nybble, Sint16 *coeff) +{ + const Sint32 max_audioval = ((1<<(16-1))-1); + const Sint32 min_audioval = -(1<<(16-1)); + const Sint32 adaptive[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + Sint32 new_sample, delta; + + new_sample = ((state->iSamp1 * coeff[0]) + + (state->iSamp2 * coeff[1]))/256; + if ( nybble & 0x08 ) { + new_sample += state->iDelta * (nybble-0x10); + } else { + new_sample += state->iDelta * nybble; + } + if ( new_sample < min_audioval ) { + new_sample = min_audioval; + } else + if ( new_sample > max_audioval ) { + new_sample = max_audioval; + } + delta = ((Sint32)state->iDelta * adaptive[nybble])/256; + if ( delta < 16 ) { + delta = 16; + } + state->iDelta = (Uint16)delta; + state->iSamp2 = state->iSamp1; + state->iSamp1 = (Sint16)new_sample; + return(new_sample); +} + +static int MS_ADPCM_decode(Uint8 **audio_buf, Uint32 *audio_len) +{ + struct MS_ADPCM_decodestate *state[2]; + Uint8 *freeable, *encoded, *decoded; + Sint32 encoded_len, samplesleft; + Sint8 nybble, stereo; + Sint16 *coeff[2]; + Sint32 new_sample; + + /* Allocate the proper sized output buffer */ + encoded_len = *audio_len; + encoded = *audio_buf; + freeable = *audio_buf; + *audio_len = (encoded_len/MS_ADPCM_state.wavefmt.blockalign) * + MS_ADPCM_state.wSamplesPerBlock* + MS_ADPCM_state.wavefmt.channels*sizeof(Sint16); + *audio_buf = (Uint8 *)SDL_malloc(*audio_len); + if ( *audio_buf == NULL ) { + SDL_Error(SDL_ENOMEM); + return(-1); + } + decoded = *audio_buf; + + /* Get ready... Go! */ + stereo = (MS_ADPCM_state.wavefmt.channels == 2); + state[0] = &MS_ADPCM_state.state[0]; + state[1] = &MS_ADPCM_state.state[stereo]; + while ( encoded_len >= MS_ADPCM_state.wavefmt.blockalign ) { + /* Grab the initial information for this block */ + state[0]->hPredictor = *encoded++; + if ( stereo ) { + state[1]->hPredictor = *encoded++; + } + state[0]->iDelta = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + if ( stereo ) { + state[1]->iDelta = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + } + state[0]->iSamp1 = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + if ( stereo ) { + state[1]->iSamp1 = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + } + state[0]->iSamp2 = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + if ( stereo ) { + state[1]->iSamp2 = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + } + coeff[0] = MS_ADPCM_state.aCoeff[state[0]->hPredictor]; + coeff[1] = MS_ADPCM_state.aCoeff[state[1]->hPredictor]; + + /* Store the two initial samples we start with */ + decoded[0] = state[0]->iSamp2&0xFF; + decoded[1] = state[0]->iSamp2>>8; + decoded += 2; + if ( stereo ) { + decoded[0] = state[1]->iSamp2&0xFF; + decoded[1] = state[1]->iSamp2>>8; + decoded += 2; + } + decoded[0] = state[0]->iSamp1&0xFF; + decoded[1] = state[0]->iSamp1>>8; + decoded += 2; + if ( stereo ) { + decoded[0] = state[1]->iSamp1&0xFF; + decoded[1] = state[1]->iSamp1>>8; + decoded += 2; + } + + /* Decode and store the other samples in this block */ + samplesleft = (MS_ADPCM_state.wSamplesPerBlock-2)* + MS_ADPCM_state.wavefmt.channels; + while ( samplesleft > 0 ) { + nybble = (*encoded)>>4; + new_sample = MS_ADPCM_nibble(state[0],nybble,coeff[0]); + decoded[0] = new_sample&0xFF; + new_sample >>= 8; + decoded[1] = new_sample&0xFF; + decoded += 2; + + nybble = (*encoded)&0x0F; + new_sample = MS_ADPCM_nibble(state[1],nybble,coeff[1]); + decoded[0] = new_sample&0xFF; + new_sample >>= 8; + decoded[1] = new_sample&0xFF; + decoded += 2; + + ++encoded; + samplesleft -= 2; + } + encoded_len -= MS_ADPCM_state.wavefmt.blockalign; + } + SDL_free(freeable); + return(0); +} + +struct IMA_ADPCM_decodestate { + Sint32 sample; + Sint8 index; +}; +static struct IMA_ADPCM_decoder { + WaveFMT wavefmt; + Uint16 wSamplesPerBlock; + /* * * */ + struct IMA_ADPCM_decodestate state[2]; +} IMA_ADPCM_state; + +static int InitIMA_ADPCM(WaveFMT *format) +{ + Uint8 *rogue_feel; + Uint16 extra_info; + + /* Set the rogue pointer to the IMA_ADPCM specific data */ + IMA_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding); + IMA_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels); + IMA_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency); + IMA_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate); + IMA_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign); + IMA_ADPCM_state.wavefmt.bitspersample = + SDL_SwapLE16(format->bitspersample); + rogue_feel = (Uint8 *)format+sizeof(*format); + if ( sizeof(*format) == 16 ) { + extra_info = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + } + IMA_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1]<<8)|rogue_feel[0]); + return(0); +} + +static Sint32 IMA_ADPCM_nibble(struct IMA_ADPCM_decodestate *state,Uint8 nybble) +{ + const Sint32 max_audioval = ((1<<(16-1))-1); + const Sint32 min_audioval = -(1<<(16-1)); + const int index_table[16] = { + -1, -1, -1, -1, + 2, 4, 6, 8, + -1, -1, -1, -1, + 2, 4, 6, 8 + }; + const Sint32 step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, + 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, + 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, + 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, + 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, + 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, + 22385, 24623, 27086, 29794, 32767 + }; + Sint32 delta, step; + + /* Compute difference and new sample value */ + step = step_table[state->index]; + delta = step >> 3; + if ( nybble & 0x04 ) delta += step; + if ( nybble & 0x02 ) delta += (step >> 1); + if ( nybble & 0x01 ) delta += (step >> 2); + if ( nybble & 0x08 ) delta = -delta; + state->sample += delta; + + /* Update index value */ + state->index += index_table[nybble]; + if ( state->index > 88 ) { + state->index = 88; + } else + if ( state->index < 0 ) { + state->index = 0; + } + + /* Clamp output sample */ + if ( state->sample > max_audioval ) { + state->sample = max_audioval; + } else + if ( state->sample < min_audioval ) { + state->sample = min_audioval; + } + return(state->sample); +} + +/* Fill the decode buffer with a channel block of data (8 samples) */ +static void Fill_IMA_ADPCM_block(Uint8 *decoded, Uint8 *encoded, + int channel, int numchannels, struct IMA_ADPCM_decodestate *state) +{ + int i; + Sint8 nybble; + Sint32 new_sample; + + decoded += (channel * 2); + for ( i=0; i<4; ++i ) { + nybble = (*encoded)&0x0F; + new_sample = IMA_ADPCM_nibble(state, nybble); + decoded[0] = new_sample&0xFF; + new_sample >>= 8; + decoded[1] = new_sample&0xFF; + decoded += 2 * numchannels; + + nybble = (*encoded)>>4; + new_sample = IMA_ADPCM_nibble(state, nybble); + decoded[0] = new_sample&0xFF; + new_sample >>= 8; + decoded[1] = new_sample&0xFF; + decoded += 2 * numchannels; + + ++encoded; + } +} + +static int IMA_ADPCM_decode(Uint8 **audio_buf, Uint32 *audio_len) +{ + struct IMA_ADPCM_decodestate *state; + Uint8 *freeable, *encoded, *decoded; + Sint32 encoded_len, samplesleft; + unsigned int c, channels; + + /* Check to make sure we have enough variables in the state array */ + channels = IMA_ADPCM_state.wavefmt.channels; + if ( channels > SDL_arraysize(IMA_ADPCM_state.state) ) { + SDL_SetError("IMA ADPCM decoder can only handle %d channels", + SDL_arraysize(IMA_ADPCM_state.state)); + return(-1); + } + state = IMA_ADPCM_state.state; + + /* Allocate the proper sized output buffer */ + encoded_len = *audio_len; + encoded = *audio_buf; + freeable = *audio_buf; + *audio_len = (encoded_len/IMA_ADPCM_state.wavefmt.blockalign) * + IMA_ADPCM_state.wSamplesPerBlock* + IMA_ADPCM_state.wavefmt.channels*sizeof(Sint16); + *audio_buf = (Uint8 *)SDL_malloc(*audio_len); + if ( *audio_buf == NULL ) { + SDL_Error(SDL_ENOMEM); + return(-1); + } + decoded = *audio_buf; + + /* Get ready... Go! */ + while ( encoded_len >= IMA_ADPCM_state.wavefmt.blockalign ) { + /* Grab the initial information for this block */ + for ( c=0; c<channels; ++c ) { + /* Fill the state information for this block */ + state[c].sample = ((encoded[1]<<8)|encoded[0]); + encoded += 2; + if ( state[c].sample & 0x8000 ) { + state[c].sample -= 0x10000; + } + state[c].index = *encoded++; + /* Reserved byte in buffer header, should be 0 */ + if ( *encoded++ != 0 ) { + /* Uh oh, corrupt data? Buggy code? */; + } + + /* Store the initial sample we start with */ + decoded[0] = (Uint8)(state[c].sample&0xFF); + decoded[1] = (Uint8)(state[c].sample>>8); + decoded += 2; + } + + /* Decode and store the other samples in this block */ + samplesleft = (IMA_ADPCM_state.wSamplesPerBlock-1)*channels; + while ( samplesleft > 0 ) { + for ( c=0; c<channels; ++c ) { + Fill_IMA_ADPCM_block(decoded, encoded, + c, channels, &state[c]); + encoded += 4; + samplesleft -= 8; + } + decoded += (channels * 8 * 2); + } + encoded_len -= IMA_ADPCM_state.wavefmt.blockalign; + } + SDL_free(freeable); + return(0); +} + +SDL_AudioSpec * SDL_LoadWAV_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + int was_error; + Chunk chunk; + int lenread; + int MS_ADPCM_encoded, IMA_ADPCM_encoded; + int samplesize; + + /* WAV magic header */ + Uint32 RIFFchunk; + Uint32 wavelen = 0; + Uint32 WAVEmagic; + Uint32 headerDiff = 0; + + /* FMT chunk */ + WaveFMT *format = NULL; + + /* Make sure we are passed a valid data source */ + was_error = 0; + if ( src == NULL ) { + was_error = 1; + goto done; + } + + /* Check the magic header */ + RIFFchunk = SDL_ReadLE32(src); + wavelen = SDL_ReadLE32(src); + if ( wavelen == WAVE ) { /* The RIFFchunk has already been read */ + WAVEmagic = wavelen; + wavelen = RIFFchunk; + RIFFchunk = RIFF; + } else { + WAVEmagic = SDL_ReadLE32(src); + } + if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) { + SDL_SetError("Unrecognized file type (not WAVE)"); + was_error = 1; + goto done; + } + headerDiff += sizeof(Uint32); /* for WAVE */ + + /* Read the audio data format chunk */ + chunk.data = NULL; + do { + if ( chunk.data != NULL ) { + SDL_free(chunk.data); + chunk.data = NULL; + } + lenread = ReadChunk(src, &chunk); + if ( lenread < 0 ) { + was_error = 1; + goto done; + } + /* 2 Uint32's for chunk header+len, plus the lenread */ + headerDiff += lenread + 2 * sizeof(Uint32); + } while ( (chunk.magic == FACT) || (chunk.magic == LIST) ); + + /* Decode the audio data format */ + format = (WaveFMT *)chunk.data; + if ( chunk.magic != FMT ) { + SDL_SetError("Complex WAVE files not supported"); + was_error = 1; + goto done; + } + MS_ADPCM_encoded = IMA_ADPCM_encoded = 0; + switch (SDL_SwapLE16(format->encoding)) { + case PCM_CODE: + /* We can understand this */ + break; + case MS_ADPCM_CODE: + /* Try to understand this */ + if ( InitMS_ADPCM(format) < 0 ) { + was_error = 1; + goto done; + } + MS_ADPCM_encoded = 1; + break; + case IMA_ADPCM_CODE: + /* Try to understand this */ + if ( InitIMA_ADPCM(format) < 0 ) { + was_error = 1; + goto done; + } + IMA_ADPCM_encoded = 1; + break; + case MP3_CODE: + SDL_SetError("MPEG Layer 3 data not supported", + SDL_SwapLE16(format->encoding)); + was_error = 1; + goto done; + default: + SDL_SetError("Unknown WAVE data format: 0x%.4x", + SDL_SwapLE16(format->encoding)); + was_error = 1; + goto done; + } + SDL_memset(spec, 0, (sizeof *spec)); + spec->freq = SDL_SwapLE32(format->frequency); + switch (SDL_SwapLE16(format->bitspersample)) { + case 4: + if ( MS_ADPCM_encoded || IMA_ADPCM_encoded ) { + spec->format = AUDIO_S16; + } else { + was_error = 1; + } + break; + case 8: + spec->format = AUDIO_U8; + break; + case 16: + spec->format = AUDIO_S16; + break; + default: + was_error = 1; + break; + } + if ( was_error ) { + SDL_SetError("Unknown %d-bit PCM data format", + SDL_SwapLE16(format->bitspersample)); + goto done; + } + spec->channels = (Uint8)SDL_SwapLE16(format->channels); + spec->samples = 4096; /* Good default buffer size */ + + /* Read the audio data chunk */ + *audio_buf = NULL; + do { + if ( *audio_buf != NULL ) { + SDL_free(*audio_buf); + *audio_buf = NULL; + } + lenread = ReadChunk(src, &chunk); + if ( lenread < 0 ) { + was_error = 1; + goto done; + } + *audio_len = lenread; + *audio_buf = chunk.data; + if(chunk.magic != DATA) headerDiff += lenread + 2 * sizeof(Uint32); + } while ( chunk.magic != DATA ); + headerDiff += 2 * sizeof(Uint32); /* for the data chunk and len */ + + if ( MS_ADPCM_encoded ) { + if ( MS_ADPCM_decode(audio_buf, audio_len) < 0 ) { + was_error = 1; + goto done; + } + } + if ( IMA_ADPCM_encoded ) { + if ( IMA_ADPCM_decode(audio_buf, audio_len) < 0 ) { + was_error = 1; + goto done; + } + } + + /* 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 ( format != NULL ) { + SDL_free(format); + } + if ( src ) { + if ( freesrc ) { + SDL_RWclose(src); + } else { + /* seek to the end of the file (given by the RIFF chunk) */ + SDL_RWseek(src, wavelen - chunk.length - headerDiff, RW_SEEK_CUR); + } + } + if ( was_error ) { + spec = NULL; + } + return(spec); +} + +/* Since the WAV memory is allocated in the shared library, it must also + be freed here. (Necessary under Win32, VC++) + */ +void SDL_FreeWAV(Uint8 *audio_buf) +{ + if ( audio_buf != NULL ) { + SDL_free(audio_buf); + } +} + +static int ReadChunk(SDL_RWops *src, Chunk *chunk) +{ + chunk->magic = SDL_ReadLE32(src); + chunk->length = SDL_ReadLE32(src); + chunk->data = (Uint8 *)SDL_malloc(chunk->length); + if ( chunk->data == NULL ) { + SDL_Error(SDL_ENOMEM); + return(-1); + } + if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) { + SDL_Error(SDL_EFREAD); + SDL_free(chunk->data); + chunk->data = NULL; + return(-1); + } + return(chunk->length); +} diff --git a/src/audio/SDL_wave.h b/src/audio/SDL_wave.h new file mode 100644 index 0000000..b1ba47f --- /dev/null +++ b/src/audio/SDL_wave.h @@ -0,0 +1,62 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2009 Sam Lantinga + + This library is SDL_free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* 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 0x0001 +#define MS_ADPCM_CODE 0x0002 +#define IMA_ADPCM_CODE 0x0011 +#define MP3_CODE 0x0055 +#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; +} Chunk; + diff --git a/src/audio/alsa/SDL_alsa_audio.c b/src/audio/alsa/SDL_alsa_audio.c new file mode 100644 index 0000000..e3ce985 --- /dev/null +++ b/src/audio/alsa/SDL_alsa_audio.c @@ -0,0 +1,612 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include <sys/types.h> +#include <signal.h> /* For kill() */ + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "SDL_alsa_audio.h" + +#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC +#include "SDL_name.h" +#include "SDL_loadso.h" +#else +#define SDL_NAME(X) X +#endif + + +/* The tag name used by ALSA audio */ +#define DRIVER_NAME "alsa" + +/* Audio driver functions */ +static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void ALSA_WaitAudio(_THIS); +static void ALSA_PlayAudio(_THIS); +static Uint8 *ALSA_GetAudioBuf(_THIS); +static void ALSA_CloseAudio(_THIS); + +#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC + +static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC; +static void *alsa_handle = NULL; +static int alsa_loaded = 0; + +static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode); +static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm); +static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size); +static int (*SDL_NAME(snd_pcm_recover))(snd_pcm_t *pcm, int err, int silent); +static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm); +static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm); +static const char *(*SDL_NAME(snd_strerror))(int errnum); +static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void); +static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void); +static void (*SDL_NAME(snd_pcm_hw_params_copy))(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src); +static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +static int (*SDL_NAME(snd_pcm_hw_params_set_access))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access); +static int (*SDL_NAME(snd_pcm_hw_params_set_format))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val); +static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val); +static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params, unsigned int *val); +static int (*SDL_NAME(snd_pcm_hw_params_set_rate_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +static int (*SDL_NAME(snd_pcm_hw_params_set_period_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir); +static int (*SDL_NAME(snd_pcm_hw_params_get_period_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir); +static int (*SDL_NAME(snd_pcm_hw_params_set_periods_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +static int (*SDL_NAME(snd_pcm_hw_params_get_periods))(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir); +static int (*SDL_NAME(snd_pcm_hw_params_set_buffer_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); +static int (*SDL_NAME(snd_pcm_hw_params_get_buffer_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val); +static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +/* +*/ +static int (*SDL_NAME(snd_pcm_sw_params_current))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams); +static int (*SDL_NAME(snd_pcm_sw_params_set_start_threshold))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val); +static int (*SDL_NAME(snd_pcm_sw_params))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); +static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock); +static int (*SDL_NAME(snd_pcm_wait))(snd_pcm_t *pcm, int timeout); +#define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof) +#define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof) + +/* cast funcs to char* first, to please GCC's strict aliasing rules. */ +static struct { + const char *name; + void **func; +} alsa_functions[] = { + { "snd_pcm_open", (void**)(char*)&SDL_NAME(snd_pcm_open) }, + { "snd_pcm_close", (void**)(char*)&SDL_NAME(snd_pcm_close) }, + { "snd_pcm_writei", (void**)(char*)&SDL_NAME(snd_pcm_writei) }, + { "snd_pcm_recover", (void**)(char*)&SDL_NAME(snd_pcm_recover) }, + { "snd_pcm_prepare", (void**)(char*)&SDL_NAME(snd_pcm_prepare) }, + { "snd_pcm_drain", (void**)(char*)&SDL_NAME(snd_pcm_drain) }, + { "snd_strerror", (void**)(char*)&SDL_NAME(snd_strerror) }, + { "snd_pcm_hw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof) }, + { "snd_pcm_sw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof) }, + { "snd_pcm_hw_params_copy", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_copy) }, + { "snd_pcm_hw_params_any", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_any) }, + { "snd_pcm_hw_params_set_access", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access) }, + { "snd_pcm_hw_params_set_format", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format) }, + { "snd_pcm_hw_params_set_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_channels) }, + { "snd_pcm_hw_params_get_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_channels) }, + { "snd_pcm_hw_params_set_rate_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_rate_near) }, + { "snd_pcm_hw_params_set_period_size_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_period_size_near) }, + { "snd_pcm_hw_params_get_period_size", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_period_size) }, + { "snd_pcm_hw_params_set_periods_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_periods_near) }, + { "snd_pcm_hw_params_get_periods", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_periods) }, + { "snd_pcm_hw_params_set_buffer_size_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_buffer_size_near) }, + { "snd_pcm_hw_params_get_buffer_size", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_buffer_size) }, + { "snd_pcm_hw_params", (void**)(char*)&SDL_NAME(snd_pcm_hw_params) }, + { "snd_pcm_sw_params_current", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_current) }, + { "snd_pcm_sw_params_set_start_threshold", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_start_threshold) }, + { "snd_pcm_sw_params", (void**)(char*)&SDL_NAME(snd_pcm_sw_params) }, + { "snd_pcm_nonblock", (void**)(char*)&SDL_NAME(snd_pcm_nonblock) }, + { "snd_pcm_wait", (void**)(char*)&SDL_NAME(snd_pcm_wait) }, +}; + +static void UnloadALSALibrary(void) { + if (alsa_loaded) { + SDL_UnloadObject(alsa_handle); + alsa_handle = NULL; + alsa_loaded = 0; + } +} + +static int LoadALSALibrary(void) { + int i, retval = -1; + + alsa_handle = SDL_LoadObject(alsa_library); + if (alsa_handle) { + alsa_loaded = 1; + retval = 0; + for (i = 0; i < SDL_arraysize(alsa_functions); i++) { + *alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name); + if (!*alsa_functions[i].func) { + retval = -1; + UnloadALSALibrary(); + break; + } + } + } + return retval; +} + +#else + +static void UnloadALSALibrary(void) { + return; +} + +static int LoadALSALibrary(void) { + return 0; +} + +#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */ + +static const char *get_audio_device(int channels) +{ + const char *device; + + device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */ + if ( device == NULL ) { + switch (channels) { + case 6: + device = "plug:surround51"; + break; + case 4: + device = "plug:surround40"; + break; + default: + device = "default"; + break; + } + } + return device; +} + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + int available; + int status; + snd_pcm_t *handle; + + available = 0; + if (LoadALSALibrary() < 0) { + return available; + } + status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(2), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + if ( status >= 0 ) { + available = 1; + SDL_NAME(snd_pcm_close)(handle); + } + UnloadALSALibrary(); + return(available); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); + UnloadALSALibrary(); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + LoadALSALibrary(); + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = ALSA_OpenAudio; + this->WaitAudio = ALSA_WaitAudio; + this->PlayAudio = ALSA_PlayAudio; + this->GetAudioBuf = ALSA_GetAudioBuf; + this->CloseAudio = ALSA_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap ALSA_bootstrap = { + DRIVER_NAME, "ALSA PCM audio", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void ALSA_WaitAudio(_THIS) +{ + /* We're in blocking mode, so there's nothing to do here */ +} + + +/* + * http://bugzilla.libsdl.org/show_bug.cgi?id=110 + * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE + * and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" + */ +#define SWIZ6(T) \ + T *ptr = (T *) mixbuf; \ + const Uint32 count = (this->spec.samples / 6); \ + Uint32 i; \ + for (i = 0; i < count; i++, ptr += 6) { \ + T tmp; \ + tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \ + tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \ + } + +static __inline__ void swizzle_alsa_channels_6_64bit(_THIS) { SWIZ6(Uint64); } +static __inline__ void swizzle_alsa_channels_6_32bit(_THIS) { SWIZ6(Uint32); } +static __inline__ void swizzle_alsa_channels_6_16bit(_THIS) { SWIZ6(Uint16); } +static __inline__ void swizzle_alsa_channels_6_8bit(_THIS) { SWIZ6(Uint8); } + +#undef SWIZ6 + + +/* + * Called right before feeding this->mixbuf to the hardware. Swizzle channels + * from Windows/Mac order to the format alsalib will want. + */ +static __inline__ void swizzle_alsa_channels(_THIS) +{ + if (this->spec.channels == 6) { + const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */ + if (fmtsize == 16) + swizzle_alsa_channels_6_16bit(this); + else if (fmtsize == 8) + swizzle_alsa_channels_6_8bit(this); + else if (fmtsize == 32) + swizzle_alsa_channels_6_32bit(this); + else if (fmtsize == 64) + swizzle_alsa_channels_6_64bit(this); + } + + /* !!! FIXME: update this for 7.1 if needed, later. */ +} + + +static void ALSA_PlayAudio(_THIS) +{ + int status; + snd_pcm_uframes_t frames_left; + const Uint8 *sample_buf = (const Uint8 *) mixbuf; + const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) * this->spec.channels; + + swizzle_alsa_channels(this); + + frames_left = ((snd_pcm_uframes_t) this->spec.samples); + + while ( frames_left > 0 && this->enabled ) { + /* This works, but needs more testing before going live */ + /*SDL_NAME(snd_pcm_wait)(pcm_handle, -1);*/ + + status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, frames_left); + if ( status < 0 ) { + if ( status == -EAGAIN ) { + /* Apparently snd_pcm_recover() doesn't handle this case - does it assume snd_pcm_wait() above? */ + SDL_Delay(1); + continue; + } + status = SDL_NAME(snd_pcm_recover)(pcm_handle, status, 0); + if ( status < 0 ) { + /* Hmm, not much we can do - abort */ + fprintf(stderr, "ALSA write failed (unrecoverable): %s\n", SDL_NAME(snd_strerror)(status)); + this->enabled = 0; + return; + } + continue; + } + sample_buf += status * frame_size; + frames_left -= status; + } +} + +static Uint8 *ALSA_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void ALSA_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( pcm_handle ) { + SDL_NAME(snd_pcm_drain)(pcm_handle); + SDL_NAME(snd_pcm_close)(pcm_handle); + pcm_handle = NULL; + } +} + +static int ALSA_finalize_hardware(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *hwparams, int override) +{ + int status; + snd_pcm_uframes_t bufsize; + + /* "set" the hardware with the desired parameters */ + status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams); + if ( status < 0 ) { + return(-1); + } + + /* Get samples for the actual buffer size */ + status = SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize); + if ( status < 0 ) { + return(-1); + } + if ( !override && bufsize != spec->samples * 2 ) { + return(-1); + } + + /* FIXME: Is this safe to do? */ + spec->samples = bufsize / 2; + + /* This is useful for debugging */ + if ( getenv("SDL_AUDIO_ALSA_DEBUG") ) { + snd_pcm_uframes_t persize = 0; + unsigned int periods = 0; + + SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, NULL); + SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, NULL); + + fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize); + } + return(0); +} + +static int ALSA_set_period_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override) +{ + const char *env; + int status; + snd_pcm_hw_params_t *hwparams; + snd_pcm_uframes_t frames; + unsigned int periods; + + /* Copy the hardware parameters for this setup */ + snd_pcm_hw_params_alloca(&hwparams); + SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params); + + if ( !override ) { + env = getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE"); + if ( env ) { + override = SDL_atoi(env); + if ( override == 0 ) { + return(-1); + } + } + } + + frames = spec->samples; + status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL); + if ( status < 0 ) { + return(-1); + } + + periods = 2; + status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL); + if ( status < 0 ) { + return(-1); + } + + return ALSA_finalize_hardware(this, spec, hwparams, override); +} + +static int ALSA_set_buffer_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override) +{ + const char *env; + int status; + snd_pcm_hw_params_t *hwparams; + snd_pcm_uframes_t frames; + + /* Copy the hardware parameters for this setup */ + snd_pcm_hw_params_alloca(&hwparams); + SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params); + + if ( !override ) { + env = getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"); + if ( env ) { + override = SDL_atoi(env); + if ( override == 0 ) { + return(-1); + } + } + } + + frames = spec->samples * 2; + status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames); + if ( status < 0 ) { + return(-1); + } + + return ALSA_finalize_hardware(this, spec, hwparams, override); +} + +static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + int status; + snd_pcm_hw_params_t *hwparams; + snd_pcm_sw_params_t *swparams; + snd_pcm_format_t format; + unsigned int rate; + unsigned int channels; + Uint16 test_format; + + /* Open the audio device */ + /* Name of device should depend on # channels in spec */ + status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + + if ( status < 0 ) { + SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status)); + return(-1); + } + + /* Figure out what the hardware is capable of */ + snd_pcm_hw_params_alloca(&hwparams); + status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams); + if ( status < 0 ) { + SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status)); + ALSA_CloseAudio(this); + return(-1); + } + + /* SDL only uses interleaved sample output */ + status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); + if ( status < 0 ) { + SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status)); + ALSA_CloseAudio(this); + return(-1); + } + + /* Try for a closest match on audio format */ + status = -1; + for ( test_format = SDL_FirstAudioFormat(spec->format); + test_format && (status < 0); ) { + switch ( test_format ) { + case AUDIO_U8: + format = SND_PCM_FORMAT_U8; + break; + case AUDIO_S8: + format = SND_PCM_FORMAT_S8; + break; + case AUDIO_S16LSB: + format = SND_PCM_FORMAT_S16_LE; + break; + case AUDIO_S16MSB: + format = SND_PCM_FORMAT_S16_BE; + break; + case AUDIO_U16LSB: + format = SND_PCM_FORMAT_U16_LE; + break; + case AUDIO_U16MSB: + format = SND_PCM_FORMAT_U16_BE; + break; + default: + format = 0; + break; + } + if ( format != 0 ) { + status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format); + } + if ( status < 0 ) { + test_format = SDL_NextAudioFormat(); + } + } + if ( status < 0 ) { + SDL_SetError("Couldn't find any hardware audio formats"); + ALSA_CloseAudio(this); + return(-1); + } + spec->format = test_format; + + /* Set the number of channels */ + status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels); + channels = spec->channels; + if ( status < 0 ) { + status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams, &channels); + if ( status < 0 ) { + SDL_SetError("Couldn't set audio channels"); + ALSA_CloseAudio(this); + return(-1); + } + spec->channels = channels; + } + + /* Set the audio rate */ + rate = spec->freq; + + status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, &rate, NULL); + if ( status < 0 ) { + SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status)); + ALSA_CloseAudio(this); + return(-1); + } + spec->freq = rate; + + /* Set the buffer size, in samples */ + if ( ALSA_set_period_size(this, spec, hwparams, 0) < 0 && + ALSA_set_buffer_size(this, spec, hwparams, 0) < 0 ) { + /* Failed to set desired buffer size, do the best you can... */ + if ( ALSA_set_period_size(this, spec, hwparams, 1) < 0 ) { + SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status)); + ALSA_CloseAudio(this); + return(-1); + } + } + + /* Set the software parameters */ + snd_pcm_sw_params_alloca(&swparams); + status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams); + if ( status < 0 ) { + SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status)); + ALSA_CloseAudio(this); + return(-1); + } + status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1); + if ( status < 0 ) { + SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status)); + ALSA_CloseAudio(this); + return(-1); + } + status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams); + if ( status < 0 ) { + SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status)); + ALSA_CloseAudio(this); + return(-1); + } + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); + if ( mixbuf == NULL ) { + ALSA_CloseAudio(this); + return(-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* Switch to blocking mode for playback */ + SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0); + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/alsa/SDL_alsa_audio.h b/src/audio/alsa/SDL_alsa_audio.h new file mode 100644 index 0000000..252f333 --- /dev/null +++ b/src/audio/alsa/SDL_alsa_audio.h @@ -0,0 +1,48 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +#ifndef _ALSA_PCM_audio_h +#define _ALSA_PCM_audio_h + +#include <alsa/asoundlib.h> + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The audio device handle */ + snd_pcm_t *pcm_handle; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; +}; + +/* Old variable names */ +#define pcm_handle (this->hidden->pcm_handle) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) + +#endif /* _ALSA_PCM_audio_h */ diff --git a/src/audio/arts/SDL_artsaudio.c b/src/audio/arts/SDL_artsaudio.c new file mode 100644 index 0000000..44447a2 --- /dev/null +++ b/src/audio/arts/SDL_artsaudio.c @@ -0,0 +1,348 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif +#include <unistd.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_artsaudio.h" + +#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC +#include "SDL_name.h" +#include "SDL_loadso.h" +#else +#define SDL_NAME(X) X +#endif + +/* The tag name used by artsc audio */ +#define ARTS_DRIVER_NAME "arts" + +/* Audio driver functions */ +static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void ARTS_WaitAudio(_THIS); +static void ARTS_PlayAudio(_THIS); +static Uint8 *ARTS_GetAudioBuf(_THIS); +static void ARTS_CloseAudio(_THIS); + +#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC + +static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC; +static void *arts_handle = NULL; +static int arts_loaded = 0; + +static int (*SDL_NAME(arts_init))(void); +static void (*SDL_NAME(arts_free))(void); +static arts_stream_t (*SDL_NAME(arts_play_stream))(int rate, int bits, int channels, const char *name); +static int (*SDL_NAME(arts_stream_set))(arts_stream_t s, arts_parameter_t param, int value); +static int (*SDL_NAME(arts_stream_get))(arts_stream_t s, arts_parameter_t param); +static int (*SDL_NAME(arts_write))(arts_stream_t s, const void *buffer, int count); +static void (*SDL_NAME(arts_close_stream))(arts_stream_t s); +static int (*SDL_NAME(arts_suspended))(void); +static const char *(*SDL_NAME(arts_error_text))(int errorcode); + +static struct { + const char *name; + void **func; +} arts_functions[] = { + { "arts_init", (void **)&SDL_NAME(arts_init) }, + { "arts_free", (void **)&SDL_NAME(arts_free) }, + { "arts_play_stream", (void **)&SDL_NAME(arts_play_stream) }, + { "arts_stream_set", (void **)&SDL_NAME(arts_stream_set) }, + { "arts_stream_get", (void **)&SDL_NAME(arts_stream_get) }, + { "arts_write", (void **)&SDL_NAME(arts_write) }, + { "arts_close_stream", (void **)&SDL_NAME(arts_close_stream) }, + { "arts_suspended", (void **)&SDL_NAME(arts_suspended) }, + { "arts_error_text", (void **)&SDL_NAME(arts_error_text) }, +}; + +static void UnloadARTSLibrary() +{ + if ( arts_loaded ) { + SDL_UnloadObject(arts_handle); + arts_handle = NULL; + arts_loaded = 0; + } +} + +static int LoadARTSLibrary(void) +{ + int i, retval = -1; + + arts_handle = SDL_LoadObject(arts_library); + if ( arts_handle ) { + arts_loaded = 1; + retval = 0; + for ( i=0; i<SDL_arraysize(arts_functions); ++i ) { + *arts_functions[i].func = SDL_LoadFunction(arts_handle, arts_functions[i].name); + if ( !*arts_functions[i].func ) { + retval = -1; + UnloadARTSLibrary(); + break; + } + } + } + return retval; +} + +#else + +static void UnloadARTSLibrary() +{ + return; +} + +static int LoadARTSLibrary(void) +{ + return 0; +} + +#endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */ + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + int available = 0; + + if ( LoadARTSLibrary() < 0 ) { + return available; + } + if ( SDL_NAME(arts_init)() == 0 ) { + if ( SDL_NAME(arts_suspended)() ) { + /* Play a stream so aRts doesn't crash */ + arts_stream_t stream2; + stream2=SDL_NAME(arts_play_stream)(44100, 16, 2, "SDL"); + SDL_NAME(arts_write)(stream2, "", 0); + SDL_NAME(arts_close_stream)(stream2); + available = 1; + } + SDL_NAME(arts_free)(); + } + UnloadARTSLibrary(); + + return available; +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); + UnloadARTSLibrary(); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + LoadARTSLibrary(); + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + stream = 0; + + /* Set the function pointers */ + this->OpenAudio = ARTS_OpenAudio; + this->WaitAudio = ARTS_WaitAudio; + this->PlayAudio = ARTS_PlayAudio; + this->GetAudioBuf = ARTS_GetAudioBuf; + this->CloseAudio = ARTS_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap ARTS_bootstrap = { + ARTS_DRIVER_NAME, "Analog Realtime Synthesizer", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void ARTS_WaitAudio(_THIS) +{ + Sint32 ticks; + + /* Check to see if the thread-parent process is still alive */ + { static int cnt = 0; + /* Note that this only works with thread implementations + that use a different process id for each thread. + */ + if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ + if ( kill(parent, 0) < 0 ) { + this->enabled = 0; + } + } + } + + /* Use timer for general audio synchronization */ + ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; + if ( ticks > 0 ) { + SDL_Delay(ticks); + } +} + +static void ARTS_PlayAudio(_THIS) +{ + int written; + + /* Write the audio data */ + written = SDL_NAME(arts_write)(stream, mixbuf, mixlen); + + /* If timer synchronization is enabled, set the next write frame */ + if ( frame_ticks ) { + next_frame += frame_ticks; + } + + /* If we couldn't write, assume fatal error for now */ + if ( written < 0 ) { + this->enabled = 0; + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Wrote %d bytes of audio data\n", written); +#endif +} + +static Uint8 *ARTS_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void ARTS_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( stream ) { + SDL_NAME(arts_close_stream)(stream); + stream = 0; + } + SDL_NAME(arts_free)(); +} + +static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + int bits, frag_spec; + Uint16 test_format, format; + int error_code; + + /* Reset the timer synchronization flag */ + frame_ticks = 0.0; + + mixbuf = NULL; + + /* Try for a closest match on audio format */ + format = 0; + bits = 0; + for ( test_format = SDL_FirstAudioFormat(spec->format); + ! format && test_format; ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Trying format 0x%4.4x\n", test_format); +#endif + switch ( test_format ) { + case AUDIO_U8: + bits = 8; + format = 1; + break; + case AUDIO_S16LSB: + bits = 16; + format = 1; + break; + default: + format = 0; + break; + } + if ( ! format ) { + test_format = SDL_NextAudioFormat(); + } + } + if ( format == 0 ) { + SDL_SetError("Couldn't find any hardware audio formats"); + return(-1); + } + spec->format = test_format; + + error_code = SDL_NAME(arts_init)(); + if ( error_code != 0 ) { + SDL_SetError("Unable to initialize ARTS: %s", SDL_NAME(arts_error_text)(error_code)); + return(-1); + } + if ( ! SDL_NAME(arts_suspended)() ) { + SDL_SetError("ARTS can not open audio device"); + return(-1); + } + stream = SDL_NAME(arts_play_stream)(spec->freq, bits, spec->channels, "SDL"); + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + /* Determine the power of two of the fragment size */ + for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec ); + if ( (0x01<<frag_spec) != spec->size ) { + SDL_SetError("Fragment size must be a power of two"); + return(-1); + } + frag_spec |= 0x00020000; /* two fragments, for low latency */ + +#ifdef ARTS_P_PACKET_SETTINGS + SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SETTINGS, frag_spec); +#else + SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SIZE, frag_spec&0xffff); + SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_COUNT, frag_spec>>16); +#endif + spec->size = SDL_NAME(arts_stream_get)(stream, ARTS_P_PACKET_SIZE); + + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); + if ( mixbuf == NULL ) { + return(-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/arts/SDL_artsaudio.h b/src/audio/arts/SDL_artsaudio.h new file mode 100644 index 0000000..b5ccf35 --- /dev/null +++ b/src/audio/arts/SDL_artsaudio.h @@ -0,0 +1,60 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_artscaudio_h +#define _SDL_artscaudio_h + +#include <artsc.h> + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The stream descriptor for the audio device */ + arts_stream_t stream; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; + + /* Support for audio timing using a timer, in addition to select() */ + float frame_ticks; + float next_frame; +}; +#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ + +/* Old variable names */ +#define stream (this->hidden->stream) +#define parent (this->hidden->parent) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) +#define frame_ticks (this->hidden->frame_ticks) +#define next_frame (this->hidden->next_frame) + +#endif /* _SDL_artscaudio_h */ + diff --git a/src/audio/baudio/SDL_beaudio.cc b/src/audio/baudio/SDL_beaudio.cc new file mode 100644 index 0000000..fc23f01 --- /dev/null +++ b/src/audio/baudio/SDL_beaudio.cc @@ -0,0 +1,225 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to the audio stream on BeOS */ + +#include <SoundPlayer.h> + +#include "../../main/beos/SDL_BeApp.h" + +extern "C" { + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" +#include "../../thread/beos/SDL_systhread_c.h" +#include "SDL_beaudio.h" + + +/* Audio driver functions */ +static int BE_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void BE_WaitAudio(_THIS); +static void BE_PlayAudio(_THIS); +static Uint8 *BE_GetAudioBuf(_THIS); +static void BE_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *device; + + /* Initialize all variables that we clean on shutdown */ + device = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( device ) { + SDL_memset(device, 0, (sizeof *device)); + device->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *device->hidden)); + } + if ( (device == NULL) || (device->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( device ) { + SDL_free(device); + } + return(0); + } + SDL_memset(device->hidden, 0, (sizeof *device->hidden)); + + /* Set the function pointers */ + device->OpenAudio = BE_OpenAudio; + device->WaitAudio = BE_WaitAudio; + device->PlayAudio = BE_PlayAudio; + device->GetAudioBuf = BE_GetAudioBuf; + device->CloseAudio = BE_CloseAudio; + + device->free = Audio_DeleteDevice; + + return device; +} + +AudioBootStrap BAUDIO_bootstrap = { + "baudio", "BeOS BSoundPlayer", + Audio_Available, Audio_CreateDevice +}; + +/* The BeOS callback for handling the audio buffer */ +static void FillSound(void *device, void *stream, size_t len, + const media_raw_audio_format &format) +{ + SDL_AudioDevice *audio = (SDL_AudioDevice *)device; + + /* Silence the buffer, since it's ours */ + SDL_memset(stream, audio->spec.silence, len); + + /* Only do soemthing if audio is enabled */ + if ( ! audio->enabled ) + return; + + if ( ! audio->paused ) { + if ( audio->convert.needed ) { + SDL_mutexP(audio->mixer_lock); + (*audio->spec.callback)(audio->spec.userdata, + (Uint8 *)audio->convert.buf,audio->convert.len); + SDL_mutexV(audio->mixer_lock); + SDL_ConvertAudio(&audio->convert); + SDL_memcpy(stream,audio->convert.buf,audio->convert.len_cvt); + } else { + SDL_mutexP(audio->mixer_lock); + (*audio->spec.callback)(audio->spec.userdata, + (Uint8 *)stream, len); + SDL_mutexV(audio->mixer_lock); + } + } + return; +} + +/* Dummy functions -- we don't use thread-based audio */ +void BE_WaitAudio(_THIS) +{ + return; +} +void BE_PlayAudio(_THIS) +{ + return; +} +Uint8 *BE_GetAudioBuf(_THIS) +{ + return(NULL); +} + +void BE_CloseAudio(_THIS) +{ + if ( audio_obj ) { + audio_obj->Stop(); + delete audio_obj; + audio_obj = NULL; + } + + /* Quit the Be Application, if there's nothing left to do */ + SDL_QuitBeApp(); +} + +int BE_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + int valid_datatype = 0; + media_raw_audio_format format; + Uint16 test_format = SDL_FirstAudioFormat(spec->format); + + /* Parse the audio format and fill the Be raw audio format */ + memset(&format, '\0', sizeof (media_raw_audio_format)); + format.byte_order = B_MEDIA_LITTLE_ENDIAN; + format.frame_rate = (float) spec->freq; + format.channel_count = spec->channels; /* !!! FIXME: support > 2? */ + while ((!valid_datatype) && (test_format)) { + valid_datatype = 1; + spec->format = test_format; + switch (test_format) { + case AUDIO_S8: + format.format = media_raw_audio_format::B_AUDIO_CHAR; + break; + + case AUDIO_U8: + format.format = media_raw_audio_format::B_AUDIO_UCHAR; + break; + + case AUDIO_S16LSB: + format.format = media_raw_audio_format::B_AUDIO_SHORT; + break; + + case AUDIO_S16MSB: + format.format = media_raw_audio_format::B_AUDIO_SHORT; + format.byte_order = B_MEDIA_BIG_ENDIAN; + break; + + default: + valid_datatype = 0; + test_format = SDL_NextAudioFormat(); + break; + } + } + + if (!valid_datatype) { /* shouldn't happen, but just in case... */ + SDL_SetError("Unsupported audio format"); + return (-1); + } + + /* Initialize the Be Application, if it's not already started */ + if (SDL_InitBeApp() < 0) { + return (-1); + } + + format.buffer_size = spec->samples; + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + /* Subscribe to the audio stream (creates a new thread) */ + { sigset_t omask; + SDL_MaskSignals(&omask); + audio_obj = new BSoundPlayer(&format, "SDL Audio", FillSound, + NULL, _this); + SDL_UnmaskSignals(&omask); + } + if ( audio_obj->Start() == B_NO_ERROR ) { + audio_obj->SetHasData(true); + } else { + SDL_SetError("Unable to start Be audio"); + return(-1); + } + + /* We're running! */ + return(1); +} + +}; /* Extern C */ diff --git a/src/audio/baudio/SDL_beaudio.h b/src/audio/baudio/SDL_beaudio.h new file mode 100644 index 0000000..8495cb6 --- /dev/null +++ b/src/audio/baudio/SDL_beaudio.h @@ -0,0 +1,39 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_lowaudio_h +#define _SDL_lowaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *_this + +struct SDL_PrivateAudioData { + BSoundPlayer *audio_obj; +}; + +/* Old variable names */ +#define audio_obj (_this->hidden->audio_obj) + +#endif /* _SDL_lowaudio_h */ diff --git a/src/audio/bsd/SDL_bsdaudio.c b/src/audio/bsd/SDL_bsdaudio.c new file mode 100644 index 0000000..f5db020 --- /dev/null +++ b/src/audio/bsd/SDL_bsdaudio.c @@ -0,0 +1,404 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + * Driver for native OpenBSD/NetBSD audio(4). + * vedge@vedge.com.ar. + */ + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/audioio.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_bsdaudio.h" + +/* The tag name used by NetBSD/OpenBSD audio */ +#ifdef __NetBSD__ +#define BSD_AUDIO_DRIVER_NAME "netbsd" +#define BSD_AUDIO_DRIVER_DESC "Native NetBSD audio" +#else +#define BSD_AUDIO_DRIVER_NAME "openbsd" +#define BSD_AUDIO_DRIVER_DESC "Native OpenBSD audio" +#endif + +/* Open the audio device for playback, and don't block if busy */ +/* #define USE_BLOCKING_WRITES */ + +/* Use timer for synchronization */ +/* #define USE_TIMER_SYNC */ + +/* #define DEBUG_AUDIO */ +/* #define DEBUG_AUDIO_STREAM */ + +#ifdef USE_BLOCKING_WRITES +#define OPEN_FLAGS O_WRONLY +#else +#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) +#endif + +/* Audio driver functions */ +static void OBSD_WaitAudio(_THIS); +static int OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void OBSD_PlayAudio(_THIS); +static Uint8 *OBSD_GetAudioBuf(_THIS); +static void OBSD_CloseAudio(_THIS); + +#ifdef DEBUG_AUDIO +static void OBSD_Status(_THIS); +#endif + +/* Audio driver bootstrap functions */ + +static int +Audio_Available(void) +{ + int fd; + int available; + + available = 0; + fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); + if(fd >= 0) { + available = 1; + close(fd); + } + return(available); +} + +static void +Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice +*Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice*)SDL_malloc(sizeof(SDL_AudioDevice)); + if(this) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = + (struct SDL_PrivateAudioData*)SDL_malloc((sizeof *this->hidden)); + } + if((this == NULL) || (this->hidden == NULL)) { + SDL_OutOfMemory(); + if(this) SDL_free(this); + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + audio_fd = -1; + + /* Set the function pointers */ + this->OpenAudio = OBSD_OpenAudio; + this->WaitAudio = OBSD_WaitAudio; + this->PlayAudio = OBSD_PlayAudio; + this->GetAudioBuf = OBSD_GetAudioBuf; + this->CloseAudio = OBSD_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap BSD_AUDIO_bootstrap = { + BSD_AUDIO_DRIVER_NAME, BSD_AUDIO_DRIVER_DESC, + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void +OBSD_WaitAudio(_THIS) +{ +#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */ + /* See if we need to use timed audio synchronization */ + if ( frame_ticks ) { + /* Use timer for general audio synchronization */ + Sint32 ticks; + + ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; + if ( ticks > 0 ) { + SDL_Delay(ticks); + } + } else { + /* Use select() for audio synchronization */ + fd_set fdset; + struct timeval timeout; + + FD_ZERO(&fdset); + FD_SET(audio_fd, &fdset); + timeout.tv_sec = 10; + timeout.tv_usec = 0; +#ifdef DEBUG_AUDIO + fprintf(stderr, "Waiting for audio to get ready\n"); +#endif + if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) { + const char *message = + "Audio timeout - buggy audio driver? (disabled)"; + /* In general we should never print to the screen, + but in this case we have no other way of letting + the user know what happened. + */ + fprintf(stderr, "SDL: %s\n", message); + this->enabled = 0; + /* Don't try to close - may hang */ + audio_fd = -1; +#ifdef DEBUG_AUDIO + fprintf(stderr, "Done disabling audio\n"); +#endif + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Ready!\n"); +#endif + } +#endif /* !USE_BLOCKING_WRITES */ +} + +static void +OBSD_PlayAudio(_THIS) +{ + int written, p=0; + + /* Write the audio data, checking for EAGAIN on broken audio drivers */ + do { + written = write(audio_fd, &mixbuf[p], mixlen-p); + if (written>0) + p += written; + if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR) + { + /* Non recoverable error has occurred. It should be reported!!! */ + perror("audio"); + break; + } + + if ( p < written || ((written < 0) && ((errno == 0) || (errno == EAGAIN))) ) { + SDL_Delay(1); /* Let a little CPU time go by */ + } + } while ( p < written ); + + /* If timer synchronization is enabled, set the next write frame */ + if ( frame_ticks ) { + next_frame += frame_ticks; + } + + /* If we couldn't write, assume fatal error for now */ + if ( written < 0 ) { + this->enabled = 0; + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Wrote %d bytes of audio data\n", written); +#endif +} + +static Uint8 +*OBSD_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void +OBSD_CloseAudio(_THIS) +{ + if(mixbuf != NULL) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if(audio_fd >= 0) { + close(audio_fd); + audio_fd = -1; + } +} + +#ifdef DEBUG_AUDIO +void +OBSD_Status(_THIS) +{ + audio_info_t info; + + if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { + fprintf(stderr,"AUDIO_GETINFO failed.\n"); + return; + } + + fprintf(stderr,"\n" +"[play/record info]\n" +"buffer size : %d bytes\n" +"sample rate : %i Hz\n" +"channels : %i\n" +"precision : %i-bit\n" +"encoding : 0x%x\n" +"seek : %i\n" +"sample count : %i\n" +"EOF count : %i\n" +"paused : %s\n" +"error occured : %s\n" +"waiting : %s\n" +"active : %s\n" +"", + info.play.buffer_size, + info.play.sample_rate, + info.play.channels, + info.play.precision, + info.play.encoding, + info.play.seek, + info.play.samples, + info.play.eof, + info.play.pause ? "yes" : "no", + info.play.error ? "yes" : "no", + info.play.waiting ? "yes" : "no", + info.play.active ? "yes": "no"); + + fprintf(stderr,"\n" +"[audio info]\n" +"monitor_gain : %i\n" +"hw block size : %d bytes\n" +"hi watermark : %i\n" +"lo watermark : %i\n" +"audio mode : %s\n" +"", + info.monitor_gain, + info.blocksize, + info.hiwat, info.lowat, + (info.mode == AUMODE_PLAY) ? "PLAY" + : (info.mode = AUMODE_RECORD) ? "RECORD" + : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL" + : "?")); +} +#endif /* DEBUG_AUDIO */ + +static int +OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + char audiodev[64]; + Uint16 format; + audio_info_t info; + + AUDIO_INITINFO(&info); + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + +#ifdef USE_TIMER_SYNC + frame_ticks = 0.0; +#endif + + /* Open the audio device */ + audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); + if(audio_fd < 0) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return(-1); + } + + /* Set to play mode */ + info.mode = AUMODE_PLAY; + if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) { + SDL_SetError("Couldn't put device into play mode"); + return(-1); + } + + mixbuf = NULL; + AUDIO_INITINFO(&info); + for (format = SDL_FirstAudioFormat(spec->format); + format; format = SDL_NextAudioFormat()) + { + switch(format) { + case AUDIO_U8: + info.play.encoding = AUDIO_ENCODING_ULINEAR; + info.play.precision = 8; + break; + case AUDIO_S8: + info.play.encoding = AUDIO_ENCODING_SLINEAR; + info.play.precision = 8; + break; + case AUDIO_S16LSB: + info.play.encoding = AUDIO_ENCODING_SLINEAR_LE; + info.play.precision = 16; + break; + case AUDIO_S16MSB: + info.play.encoding = AUDIO_ENCODING_SLINEAR_BE; + info.play.precision = 16; + break; + case AUDIO_U16LSB: + info.play.encoding = AUDIO_ENCODING_ULINEAR_LE; + info.play.precision = 16; + break; + case AUDIO_U16MSB: + info.play.encoding = AUDIO_ENCODING_ULINEAR_BE; + info.play.precision = 16; + break; + default: + continue; + } + if (ioctl(audio_fd, AUDIO_SETINFO, &info) == 0) + break; + } + + if(!format) { + SDL_SetError("No supported encoding for 0x%x", spec->format); + return(-1); + } + + spec->format = format; + + AUDIO_INITINFO(&info); + info.play.channels = spec->channels; + if (ioctl(audio_fd, AUDIO_SETINFO, &info) == -1) + spec->channels = 1; + AUDIO_INITINFO(&info); + info.play.sample_rate = spec->freq; + info.blocksize = spec->size; + info.hiwat = 5; + info.lowat = 3; + (void)ioctl(audio_fd, AUDIO_SETINFO, &info); + (void)ioctl(audio_fd, AUDIO_GETINFO, &info); + spec->freq = info.play.sample_rate; + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8*)SDL_AllocAudioMem(mixlen); + if(mixbuf == NULL) { + return(-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + +#ifdef DEBUG_AUDIO + OBSD_Status(this); +#endif + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/bsd/SDL_bsdaudio.h b/src/audio/bsd/SDL_bsdaudio.h new file mode 100644 index 0000000..3e95809 --- /dev/null +++ b/src/audio/bsd/SDL_bsdaudio.h @@ -0,0 +1,58 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +#ifndef _SDL_openbsdaudio_h +#define _SDL_openbsdaudio_h + +#include "../SDL_sysaudio.h" + +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData +{ + /* The file descriptor for the audio device */ + int audio_fd; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; + + /* Support for audio timing using a timer, in addition to select() */ + float frame_ticks; + float next_frame; +}; + +#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ + +/* Old variable names */ +#define audio_fd (this->hidden->audio_fd) +#define parent (this->hidden->parent) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) +#define frame_ticks (this->hidden->frame_ticks) +#define next_frame (this->hidden->next_frame) + +#endif /* _SDL_openbsdaudio_h */ diff --git a/src/audio/dart/SDL_dart.c b/src/audio/dart/SDL_dart.c new file mode 100644 index 0000000..5ebe437 --- /dev/null +++ b/src/audio/dart/SDL_dart.c @@ -0,0 +1,441 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "SDL_dart.h" + +// Buffer states: +#define BUFFER_EMPTY 0 +#define BUFFER_USED 1 + +typedef struct _tMixBufferDesc { + int iBufferUsage; // BUFFER_EMPTY or BUFFER_USED + SDL_AudioDevice *pSDLAudioDevice; +} tMixBufferDesc, *pMixBufferDesc; + + +//--------------------------------------------------------------------- +// DARTEventFunc +// +// This function is called by DART, when an event occures, like end of +// playback of a buffer, etc... +//--------------------------------------------------------------------- +LONG APIENTRY DARTEventFunc(ULONG ulStatus, + PMCI_MIX_BUFFER pBuffer, + ULONG ulFlags) +{ + if (ulFlags && MIX_WRITE_COMPLETE) + { // Playback of buffer completed! + + // Get pointer to buffer description + pMixBufferDesc pBufDesc; + + if (pBuffer) + { + pBufDesc = (pMixBufferDesc) (*pBuffer).ulUserParm; + + if (pBufDesc) + { + SDL_AudioDevice *pSDLAudioDevice = pBufDesc->pSDLAudioDevice; + // Set the buffer to be empty + pBufDesc->iBufferUsage = BUFFER_EMPTY; + // And notify DART feeder thread that it will have to work a bit. + if (pSDLAudioDevice) + DosPostEventSem(pSDLAudioDevice->hidden->hevAudioBufferPlayed); + } + } + } + return TRUE; +} + + +int DART_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + Uint16 test_format = SDL_FirstAudioFormat(spec->format); + int valid_datatype = 0; + MCI_AMP_OPEN_PARMS AmpOpenParms; + MCI_GENERIC_PARMS GenericParms; + int iDeviceOrd = 0; // Default device to be used + int bOpenShared = 1; // Try opening it shared + int iBits = 16; // Default is 16 bits signed + int iFreq = 44100; // Default is 44KHz + int iChannels = 2; // Default is 2 channels (Stereo) + int iNumBufs = 2; // Number of audio buffers: 2 + int iBufSize; + int iOpenMode; + int iSilence; + int rc; + + // First thing is to try to open a given DART device! + SDL_memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS)); + // pszDeviceType should contain the device type in low word, and device ordinal in high word! + AmpOpenParms.pszDeviceType = (PSZ) (MCI_DEVTYPE_AUDIO_AMPMIX | (iDeviceOrd << 16)); + + iOpenMode = MCI_WAIT | MCI_OPEN_TYPE_ID; + if (bOpenShared) iOpenMode |= MCI_OPEN_SHAREABLE; + + rc = mciSendCommand( 0, MCI_OPEN, + iOpenMode, + (PVOID) &AmpOpenParms, 0); + if (rc!=MCIERR_SUCCESS) // No audio available?? + return (-1); + // Save the device ID we got from DART! + // We will use this in the next calls! + iDeviceOrd = AmpOpenParms.usDeviceID; + + // Determine the audio parameters from the AudioSpec + if (spec->channels > 2) + spec->channels = 2; // !!! FIXME: more than stereo support in OS/2? + + while ((!valid_datatype) && (test_format)) { + spec->format = test_format; + valid_datatype = 1; + switch (test_format) { + case AUDIO_U8: + // Unsigned 8 bit audio data + iSilence = 0x80; + iBits = 8; + break; + + case AUDIO_S16LSB: + // Signed 16 bit audio data + iSilence = 0x00; + iBits = 16; + break; + + default: + valid_datatype = 0; + test_format = SDL_NextAudioFormat(); + break; + } + } + + if (!valid_datatype) { // shouldn't happen, but just in case... + // Close DART, and exit with error code! + mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); + SDL_SetError("Unsupported audio format"); + return (-1); + } + + iFreq = spec->freq; + iChannels = spec->channels; + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(spec); + iBufSize = spec->size; + + // Now query this device if it supports the given freq/bits/channels! + SDL_memset(&(_this->hidden->MixSetupParms), 0, sizeof(MCI_MIXSETUP_PARMS)); + _this->hidden->MixSetupParms.ulBitsPerSample = iBits; + _this->hidden->MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM; + _this->hidden->MixSetupParms.ulSamplesPerSec = iFreq; + _this->hidden->MixSetupParms.ulChannels = iChannels; + _this->hidden->MixSetupParms.ulFormatMode = MCI_PLAY; + _this->hidden->MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; + _this->hidden->MixSetupParms.pmixEvent = DARTEventFunc; + rc = mciSendCommand (iDeviceOrd, MCI_MIXSETUP, + MCI_WAIT | MCI_MIXSETUP_QUERYMODE, + &(_this->hidden->MixSetupParms), 0); + if (rc!=MCIERR_SUCCESS) + { // The device cannot handle this format! + // Close DART, and exit with error code! + mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); + SDL_SetError("Audio device doesn't support requested audio format"); + return(-1); + } + // The device can handle this format, so initialize! + rc = mciSendCommand(iDeviceOrd, MCI_MIXSETUP, + MCI_WAIT | MCI_MIXSETUP_INIT, + &(_this->hidden->MixSetupParms), 0); + if (rc!=MCIERR_SUCCESS) + { // The device could not be opened! + // Close DART, and exit with error code! + mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); + SDL_SetError("Audio device could not be set up"); + return(-1); + } + // Ok, the device is initialized. + // Now we should allocate buffers. For this, we need a place where + // the buffer descriptors will be: + _this->hidden->pMixBuffers = (MCI_MIX_BUFFER *) SDL_malloc(sizeof(MCI_MIX_BUFFER)*iNumBufs); + if (!(_this->hidden->pMixBuffers)) + { // Not enough memory! + // Close DART, and exit with error code! + mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); + SDL_SetError("Not enough memory for audio buffer descriptors"); + return(-1); + } + // Now that we have the place for buffer list, we can ask DART for the + // buffers! + _this->hidden->BufferParms.ulNumBuffers = iNumBufs; // Number of buffers + _this->hidden->BufferParms.ulBufferSize = iBufSize; // each with this size + _this->hidden->BufferParms.pBufList = _this->hidden->pMixBuffers; // getting descriptorts into this list + // Allocate buffers! + rc = mciSendCommand(iDeviceOrd, MCI_BUFFER, + MCI_WAIT | MCI_ALLOCATE_MEMORY, + &(_this->hidden->BufferParms), 0); + if ((rc!=MCIERR_SUCCESS) || (iNumBufs != _this->hidden->BufferParms.ulNumBuffers) || (_this->hidden->BufferParms.ulBufferSize==0)) + { // Could not allocate memory! + // Close DART, and exit with error code! + SDL_free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL; + mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); + SDL_SetError("DART could not allocate buffers"); + return(-1); + } + // Ok, we have all the buffers allocated, let's mark them! + { + int i; + for (i=0; i<iNumBufs; i++) + { + pMixBufferDesc pBufferDesc = (pMixBufferDesc) SDL_malloc(sizeof(tMixBufferDesc));; + // Check if this buffer was really allocated by DART + if ((!(_this->hidden->pMixBuffers[i].pBuffer)) || (!pBufferDesc)) + { // Wrong buffer! + // Close DART, and exit with error code! + // Free buffer descriptions + { int j; + for (j=0; j<i; j++) SDL_free((void *)(_this->hidden->pMixBuffers[j].ulUserParm)); + } + // and cleanup + mciSendCommand(iDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0); + SDL_free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL; + mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); + SDL_SetError("Error at internal buffer check"); + return(-1); + } + pBufferDesc->iBufferUsage = BUFFER_EMPTY; + pBufferDesc->pSDLAudioDevice = _this; + + _this->hidden->pMixBuffers[i].ulBufferLength = _this->hidden->BufferParms.ulBufferSize; + _this->hidden->pMixBuffers[i].ulUserParm = (ULONG) pBufferDesc; // User parameter: Description of buffer + _this->hidden->pMixBuffers[i].ulFlags = 0; // Some stuff should be flagged here for DART, like end of + // audio data, but as we will continously send + // audio data, there will be no end.:) + SDL_memset(_this->hidden->pMixBuffers[i].pBuffer, iSilence, iBufSize); + } + } + _this->hidden->iNextFreeBuffer = 0; + _this->hidden->iLastPlayedBuf = -1; + // Create event semaphore + if (DosCreateEventSem(NULL, &(_this->hidden->hevAudioBufferPlayed), 0, FALSE)!=NO_ERROR) + { + // Could not create event semaphore! + { + int i; + for (i=0; i<iNumBufs; i++) SDL_free((void *)(_this->hidden->pMixBuffers[i].ulUserParm)); + } + mciSendCommand(iDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0); + SDL_free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL; + mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0); + SDL_SetError("Could not create event semaphore"); + return(-1); + } + + // Store the new settings in global variables + _this->hidden->iCurrDeviceOrd = iDeviceOrd; + _this->hidden->iCurrFreq = iFreq; + _this->hidden->iCurrBits = iBits; + _this->hidden->iCurrChannels = iChannels; + _this->hidden->iCurrNumBufs = iNumBufs; + _this->hidden->iCurrBufSize = iBufSize; + + return (0); +} + + + +void DART_ThreadInit(_THIS) +{ + return; +} + +/* This function waits until it is possible to write a full sound buffer */ +void DART_WaitAudio(_THIS) +{ + int i; + pMixBufferDesc pBufDesc; + ULONG ulPostCount; + + DosResetEventSem(_this->hidden->hevAudioBufferPlayed, &ulPostCount); + // If there is already an empty buffer, then return now! + for (i=0; i<_this->hidden->iCurrNumBufs; i++) + { + pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[i].ulUserParm; + if (pBufDesc->iBufferUsage == BUFFER_EMPTY) + return; + } + // If there is no empty buffer, wait for one to be empty! + DosWaitEventSem(_this->hidden->hevAudioBufferPlayed, 1000); // Wait max 1 sec!!! Important! + return; +} + +void DART_PlayAudio(_THIS) +{ + int iFreeBuf = _this->hidden->iNextFreeBuffer; + pMixBufferDesc pBufDesc; + + pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[iFreeBuf].ulUserParm; + pBufDesc->iBufferUsage = BUFFER_USED; + // Send it to DART to be queued + _this->hidden->MixSetupParms.pmixWrite(_this->hidden->MixSetupParms.ulMixHandle, + &(_this->hidden->pMixBuffers[iFreeBuf]), 1); + + _this->hidden->iLastPlayedBuf = iFreeBuf; + iFreeBuf = (iFreeBuf+1) % _this->hidden->iCurrNumBufs; + _this->hidden->iNextFreeBuffer = iFreeBuf; +} + +Uint8 *DART_GetAudioBuf(_THIS) +{ + int iFreeBuf; + Uint8 *pResult; + pMixBufferDesc pBufDesc; + + if (_this) + { + if (_this->hidden) + { + iFreeBuf = _this->hidden->iNextFreeBuffer; + pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[iFreeBuf].ulUserParm; + + if (pBufDesc) + { + if (pBufDesc->iBufferUsage == BUFFER_EMPTY) + { + pResult = _this->hidden->pMixBuffers[iFreeBuf].pBuffer; + return pResult; + } + } else + printf("[DART_GetAudioBuf] : ERROR! pBufDesc = %p\n", pBufDesc); + } else + printf("[DART_GetAudioBuf] : ERROR! _this->hidden = %p\n", _this->hidden); + } else + printf("[DART_GetAudioBuf] : ERROR! _this = %p\n", _this); + return NULL; +} + +void DART_WaitDone(_THIS) +{ + pMixBufferDesc pBufDesc; + ULONG ulPostCount; + APIRET rc; + + pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[_this->hidden->iLastPlayedBuf].ulUserParm; + rc = NO_ERROR; + while ((pBufDesc->iBufferUsage != BUFFER_EMPTY) && (rc==NO_ERROR)) + { + DosResetEventSem(_this->hidden->hevAudioBufferPlayed, &ulPostCount); + rc = DosWaitEventSem(_this->hidden->hevAudioBufferPlayed, 1000); // 1 sec timeout! Important! + } +} + +void DART_CloseAudio(_THIS) +{ + MCI_GENERIC_PARMS GenericParms; + int rc; + + // Stop DART playback + rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_STOP, MCI_WAIT, &GenericParms, 0); + if (rc!=MCIERR_SUCCESS) + { +#ifdef SFX_DEBUG_BUILD + printf("Could not stop DART playback!\n"); + fflush(stdout); +#endif + } + + // Close event semaphore + DosCloseEventSem(_this->hidden->hevAudioBufferPlayed); + + // Free memory of buffer descriptions + { + int i; + for (i=0; i<_this->hidden->iCurrNumBufs; i++) SDL_free((void *)(_this->hidden->pMixBuffers[i].ulUserParm)); + } + + // Deallocate buffers + rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0); + + // Free bufferlist + SDL_free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL; + + // Close dart + rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_CLOSE, MCI_WAIT, &(GenericParms), 0); +} + +/* Audio driver bootstrap functions */ + +int Audio_Available(void) +{ + return(1); +} + +void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) + { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) + { + SDL_OutOfMemory(); + if ( this ) + SDL_free(this); + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = DART_OpenAudio; + this->ThreadInit = DART_ThreadInit; + this->WaitAudio = DART_WaitAudio; + this->PlayAudio = DART_PlayAudio; + this->GetAudioBuf = DART_GetAudioBuf; + this->WaitDone = DART_WaitDone; + this->CloseAudio = DART_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap DART_bootstrap = { + "dart", "OS/2 Direct Audio RouTines (DART)", + Audio_Available, Audio_CreateDevice +}; + diff --git a/src/audio/dart/SDL_dart.h b/src/audio/dart/SDL_dart.h new file mode 100644 index 0000000..1f75a35 --- /dev/null +++ b/src/audio/dart/SDL_dart.h @@ -0,0 +1,63 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_lowaudio_h +#define _SDL_lowaudio_h + +#define INCL_TYPES +#define INCL_DOSSEMAPHORES +#define INCL_DOSRESOURCES +#define INCL_DOSMISC +#define INCL_DOSERRORS + +#define INCL_OS2MM +#define INCL_MMIOOS2 +#define INCL_MCIOS2 +#include <os2.h> +#include <os2me.h> // DART stuff and MMIO stuff + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the audio functions */ +#define _THIS SDL_AudioDevice *_this + +/* The DirectSound objects */ +struct SDL_PrivateAudioData +{ + int iCurrDeviceOrd; + int iCurrFreq; + int iCurrBits; + int iCurrChannels; + int iCurrNumBufs; + int iCurrBufSize; + + int iLastPlayedBuf; + int iNextFreeBuffer; + + MCI_BUFFER_PARMS BufferParms; // Sound buffer parameters + MCI_MIX_BUFFER *pMixBuffers; // Sound buffers + MCI_MIXSETUP_PARMS MixSetupParms; // Mixer setup parameters + HEV hevAudioBufferPlayed; // Event semaphore to indicate that an audio buffer has been played by DART +}; + +#endif /* _SDL_lowaudio_h */ diff --git a/src/audio/dc/SDL_dcaudio.c b/src/audio/dc/SDL_dcaudio.c new file mode 100644 index 0000000..8a2c7d2 --- /dev/null +++ b/src/audio/dc/SDL_dcaudio.c @@ -0,0 +1,246 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org + +*/ +#include "SDL_config.h" + +/* Output dreamcast aica */ + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_dcaudio.h" + +#include "aica.h" +#include <dc/spu.h> + +/* Audio driver functions */ +static int DCAUD_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void DCAUD_WaitAudio(_THIS); +static void DCAUD_PlayAudio(_THIS); +static Uint8 *DCAUD_GetAudioBuf(_THIS); +static void DCAUD_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ +static int DCAUD_Available(void) +{ + return 1; +} + +static void DCAUD_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *DCAUD_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = DCAUD_OpenAudio; + this->WaitAudio = DCAUD_WaitAudio; + this->PlayAudio = DCAUD_PlayAudio; + this->GetAudioBuf = DCAUD_GetAudioBuf; + this->CloseAudio = DCAUD_CloseAudio; + + this->free = DCAUD_DeleteDevice; + + spu_init(); + + return this; +} + +AudioBootStrap DCAUD_bootstrap = { + "dcaudio", "Dreamcast AICA audio", + DCAUD_Available, DCAUD_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void DCAUD_WaitAudio(_THIS) +{ + if (this->hidden->playing) { + /* wait */ + while(aica_get_pos(0)/this->spec.samples == this->hidden->nextbuf) { + thd_pass(); + } + } +} + +#define SPU_RAM_BASE 0xa0800000 + +static void spu_memload_stereo8(int leftpos,int rightpos,void *src0,size_t size) +{ + uint8 *src = src0; + uint32 *left = (uint32*)(leftpos +SPU_RAM_BASE); + uint32 *right = (uint32*)(rightpos+SPU_RAM_BASE); + size = (size+7)/8; + while(size--) { + unsigned lval,rval; + lval = *src++; + rval = *src++; + lval|= (*src++)<<8; + rval|= (*src++)<<8; + lval|= (*src++)<<16; + rval|= (*src++)<<16; + lval|= (*src++)<<24; + rval|= (*src++)<<24; + g2_write_32(left++,lval); + g2_write_32(right++,rval); + g2_fifo_wait(); + } +} + +static void spu_memload_stereo16(int leftpos,int rightpos,void *src0,size_t size) +{ + uint16 *src = src0; + uint32 *left = (uint32*)(leftpos +SPU_RAM_BASE); + uint32 *right = (uint32*)(rightpos+SPU_RAM_BASE); + size = (size+7)/8; + while(size--) { + unsigned lval,rval; + lval = *src++; + rval = *src++; + lval|= (*src++)<<16; + rval|= (*src++)<<16; + g2_write_32(left++,lval); + g2_write_32(right++,rval); + g2_fifo_wait(); + } +} + +static void DCAUD_PlayAudio(_THIS) +{ + SDL_AudioSpec *spec = &this->spec; + unsigned int offset; + + if (this->hidden->playing) { + /* wait */ + while(aica_get_pos(0)/spec->samples == this->hidden->nextbuf) { + thd_pass(); + } + } + + offset = this->hidden->nextbuf*spec->size; + this->hidden->nextbuf^=1; + /* Write the audio data, checking for EAGAIN on broken audio drivers */ + if (spec->channels==1) { + spu_memload(this->hidden->leftpos+offset,this->hidden->mixbuf,this->hidden->mixlen); + } else { + offset/=2; + if ((this->spec.format&255)==8) { + spu_memload_stereo8(this->hidden->leftpos+offset,this->hidden->rightpos+offset,this->hidden->mixbuf,this->hidden->mixlen); + } else { + spu_memload_stereo16(this->hidden->leftpos+offset,this->hidden->rightpos+offset,this->hidden->mixbuf,this->hidden->mixlen); + } + } + + if (!this->hidden->playing) { + int mode; + this->hidden->playing = 1; + mode = (spec->format==AUDIO_S8)?SM_8BIT:SM_16BIT; + if (spec->channels==1) { + aica_play(0,mode,this->hidden->leftpos,0,spec->samples*2,spec->freq,255,128,1); + } else { + aica_play(0,mode,this->hidden->leftpos ,0,spec->samples*2,spec->freq,255,0,1); + aica_play(1,mode,this->hidden->rightpos,0,spec->samples*2,spec->freq,255,255,1); + } + } +} + +static Uint8 *DCAUD_GetAudioBuf(_THIS) +{ + return(this->hidden->mixbuf); +} + +static void DCAUD_CloseAudio(_THIS) +{ + aica_stop(0); + if (this->spec.channels==2) aica_stop(1); + if ( this->hidden->mixbuf != NULL ) { + SDL_FreeAudioMem(this->hidden->mixbuf); + this->hidden->mixbuf = NULL; + } +} + +static int DCAUD_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + Uint16 test_format = SDL_FirstAudioFormat(spec->format); + int valid_datatype = 0; + while ((!valid_datatype) && (test_format)) { + spec->format = test_format; + switch (test_format) { + /* only formats Dreamcast accepts... */ + case AUDIO_S8: + case AUDIO_S16LSB: + valid_datatype = 1; + break; + + default: + test_format = SDL_NextAudioFormat(); + break; + } + } + + if (!valid_datatype) { /* shouldn't happen, but just in case... */ + SDL_SetError("Unsupported audio format"); + return (-1); + } + + if (spec->channels > 2) + spec->channels = 2; /* no more than stereo on the Dreamcast. */ + + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(spec); + + /* Allocate mixing buffer */ + this->hidden->mixlen = spec->size; + this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); + if ( this->hidden->mixbuf == NULL ) { + return(-1); + } + SDL_memset(this->hidden->mixbuf, spec->silence, spec->size); + this->hidden->leftpos = 0x11000; + this->hidden->rightpos = 0x11000+spec->size; + this->hidden->playing = 0; + this->hidden->nextbuf = 0; + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/dc/SDL_dcaudio.h b/src/audio/dc/SDL_dcaudio.h new file mode 100644 index 0000000..4a7c7cd --- /dev/null +++ b/src/audio/dc/SDL_dcaudio.h @@ -0,0 +1,41 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_dcaudio_h +#define _SDL_dcaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + Uint8 *mixbuf; + Uint32 mixlen; + int playing; + int leftpos,rightpos; + int nextbuf; +}; + +#endif /* _SDL_dcaudio_h */ diff --git a/src/audio/dc/aica.c b/src/audio/dc/aica.c new file mode 100644 index 0000000..b6a1c93 --- /dev/null +++ b/src/audio/dc/aica.c @@ -0,0 +1,271 @@ +/* This file is part of the Dreamcast function library. + * Please see libdream.c for further details. + * + * (c)2000 Dan Potter + * modify BERO + */ +#include "aica.h" + +#include <arch/irq.h> +#include <dc/spu.h> + +/* #define dc_snd_base ((volatile unsigned char *)0x00800000) */ /* arm side */ +#define dc_snd_base ((volatile unsigned char *)0xa0700000) /* dc side */ + +/* Some convienence macros */ +#define SNDREGADDR(x) (0xa0700000 + (x)) +#define CHNREGADDR(ch,x) SNDREGADDR(0x80*(ch)+(x)) + + +#define SNDREG32(x) (*(volatile unsigned long *)SNDREGADDR(x)) +#define SNDREG8(x) (*(volatile unsigned char *)SNDREGADDR(x)) +#define CHNREG32(ch, x) (*(volatile unsigned long *)CHNREGADDR(ch,x)) +#define CHNREG8(ch, x) (*(volatile unsigned long *)CHNREGADDR(ch,x)) + +#define G2_LOCK(OLD) \ + do { \ + if (!irq_inside_int()) \ + OLD = irq_disable(); \ + /* suspend any G2 DMA here... */ \ + while((*(volatile unsigned int *)0xa05f688c) & 0x20) \ + ; \ + } while(0) + +#define G2_UNLOCK(OLD) \ + do { \ + /* resume any G2 DMA here... */ \ + if (!irq_inside_int()) \ + irq_restore(OLD); \ + } while(0) + + +void aica_init() { + int i, j, old = 0; + + /* Initialize AICA channels */ + G2_LOCK(old); + SNDREG32(0x2800) = 0x0000; + + for (i=0; i<64; i++) { + for (j=0; j<0x80; j+=4) { + if ((j&31)==0) g2_fifo_wait(); + CHNREG32(i, j) = 0; + } + g2_fifo_wait(); + CHNREG32(i,0) = 0x8000; + CHNREG32(i,20) = 0x1f; + } + + SNDREG32(0x2800) = 0x000f; + g2_fifo_wait(); + G2_UNLOCK(old); +} + +/* Translates a volume from linear form to logarithmic form (required by + the AICA chip */ +/* int logs[] = { + +0, 40, 50, 58, 63, 68, 73, 77, 80, 83, 86, 89, 92, 94, 97, 99, 101, 103, +105, 107, 109, 111, 112, 114, 116, 117, 119, 120, 122, 123, 125, 126, 127, +129, 130, 131, 133, 134, 135, 136, 137, 139, 140, 141, 142, 143, 144, 145, +146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159, +160, 161, 162, 162, 163, 164, 165, 166, 166, 167, 168, 169, 170, 170, 171, +172, 172, 173, 174, 175, 175, 176, 177, 177, 178, 179, 180, 180, 181, 182, +182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 188, 189, 190, 190, 191, +191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 198, 199, 199, +200, 201, 201, 202, 202, 203, 203, 204, 204, 205, 205, 206, 206, 207, 207, +208, 208, 209, 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215, +215, 216, 216, 217, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, +222, 222, 223, 223, 224, 224, 225, 225, 225, 226, 226, 227, 227, 228, 228, +228, 229, 229, 230, 230, 230, 231, 231, 232, 232, 232, 233, 233, 234, 234, +234, 235, 235, 236, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240, +240, 241, 241, 241, 242, 242, 243, 243, 243, 244, 244, 244, 245, 245, 245, +246, 246, 247, 247, 247, 248, 248, 248, 249, 249, 249, 250, 250, 250, 251, +251, 251, 252, 252, 252, 253, 253, 253, 254, 254, 254, 255 + +}; */ + +const static unsigned char logs[] = { + 0, 15, 22, 27, 31, 35, 39, 42, 45, 47, 50, 52, 55, 57, 59, 61, + 63, 65, 67, 69, 71, 73, 74, 76, 78, 79, 81, 82, 84, 85, 87, 88, + 90, 91, 92, 94, 95, 96, 98, 99, 100, 102, 103, 104, 105, 106, + 108, 109, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, 121, + 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 146, 147, 148, 149, 150, 151, 152, 152, 153, 154, 155, 156, 156, + 157, 158, 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, 167, + 167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, + 177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, + 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, + 195, 195, 196, 197, 197, 198, 199, 199, 200, 200, 201, 202, 202, + 203, 204, 204, 205, 205, 206, 207, 207, 208, 209, 209, 210, 210, + 211, 212, 212, 213, 213, 214, 215, 215, 216, 216, 217, 217, 218, + 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, 224, 225, 225, + 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 232, 232, 233, + 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 239, 239, 240, + 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, + 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 254, 255 +}; + +/* For the moment this is going to have to suffice, until we really + figure out what these mean. */ +#define AICA_PAN(x) ((x)==0x80?(0):((x)<0x80?(0x1f):(0x0f))) +#define AICA_VOL(x) (0xff - logs[128 + (((x) & 0xff) / 2)]) +//#define AICA_VOL(x) (0xff - logs[x&255]) + +static inline unsigned AICA_FREQ(unsigned freq) { + unsigned long freq_lo, freq_base = 5644800; + int freq_hi = 7; + + /* Need to convert frequency to floating point format + (freq_hi is exponent, freq_lo is mantissa) + Formula is ferq = 44100*2^freq_hi*(1+freq_lo/1024) */ + while (freq < freq_base && freq_hi > -8) { + freq_base >>= 1; + --freq_hi; + } + while (freq < freq_base && freq_hi > -8) { + freq_base >>= 1; + freq_hi--; + } + freq_lo = (freq<<10) / freq_base; + return (freq_hi << 11) | (freq_lo & 1023); +} + +/* Sets up a sound channel completely. This is generally good if you want + a quick and dirty way to play notes. If you want a more comprehensive + set of routines (more like PC wavetable cards) see below. + + ch is the channel to play on (0 - 63) + smpptr is the pointer to the sound data; if you're running off the + SH4, then this ought to be (ptr - 0xa0800000); otherwise it's just + ptr. Basically, it's an offset into sound ram. + mode is one of the mode constants (16 bit, 8 bit, ADPCM) + nsamp is the number of samples to play (not number of bytes!) + freq is the sampling rate of the sound + vol is the volume, 0 to 0xff (0xff is louder) + pan is a panning constant -- 0 is left, 128 is center, 255 is right. + + This routine (and the similar ones) owe a lot to Marcus' sound example -- + I hadn't gotten quite this far into dissecting the individual regs yet. */ +void aica_play(int ch,int mode,unsigned long smpptr,int loopst,int loopend,int freq,int vol,int pan,int loopflag) { +/* int i; +*/ + int val; + int old = 0; + + /* Stop the channel (if it's already playing) */ + aica_stop(ch); + /* doesn't seem to be needed, but it's here just in case */ +/* + for (i=0; i<256; i++) { + asm("nop"); + asm("nop"); + asm("nop"); + asm("nop"); + } +*/ + G2_LOCK(old); + /* Envelope setup. The first of these is the loop point, + e.g., where the sample starts over when it loops. The second + is the loop end. This is the full length of the sample when + you are not looping, or the loop end point when you are (though + storing more than that is a waste of memory if you're not doing + volume enveloping). */ + CHNREG32(ch, 8) = loopst & 0xffff; + CHNREG32(ch, 12) = loopend & 0xffff; + + /* Write resulting values */ + CHNREG32(ch, 24) = AICA_FREQ(freq); + + /* Set volume, pan, and some other things that we don't know what + they do =) */ + CHNREG32(ch, 36) = AICA_PAN(pan) | (0xf<<8); + /* Convert the incoming volume and pan into hardware values */ + /* Vol starts at zero so we can ramp */ + vol = AICA_VOL(vol); + CHNREG32(ch, 40) = 0x24 | (vol<<8); + /* Convert the incoming volume and pan into hardware values */ + /* Vol starts at zero so we can ramp */ + + /* If we supported volume envelopes (which we don't yet) then + this value would set that up. The top 4 bits determine the + envelope speed. f is the fastest, 1 is the slowest, and 0 + seems to be an invalid value and does weird things). The + default (below) sets it into normal mode (play and terminate/loop). + CHNREG32(ch, 16) = 0xf010; + */ + CHNREG32(ch, 16) = 0x1f; /* No volume envelope */ + + + /* Set sample format, buffer address, and looping control. If + 0x0200 mask is set on reg 0, the sample loops infinitely. If + it's not set, the sample plays once and terminates. We'll + also set the bits to start playback here. */ + CHNREG32(ch, 4) = smpptr & 0xffff; + val = 0xc000 | 0x0000 | (mode<<7) | (smpptr >> 16); + if (loopflag) val|=0x200; + + CHNREG32(ch, 0) = val; + + G2_UNLOCK(old); + + /* Enable playback */ + /* CHNREG32(ch, 0) |= 0xc000; */ + g2_fifo_wait(); + +#if 0 + for (i=0xff; i>=vol; i--) { + if ((i&7)==0) g2_fifo_wait(); + CHNREG32(ch, 40) = 0x24 | (i<<8);; + } + + g2_fifo_wait(); +#endif +} + +/* Stop the sound on a given channel */ +void aica_stop(int ch) { + g2_write_32(CHNREGADDR(ch, 0),(g2_read_32(CHNREGADDR(ch, 0)) & ~0x4000) | 0x8000); + g2_fifo_wait(); +} + + +/* The rest of these routines can change the channel in mid-stride so you + can do things like vibrato and panning effects. */ + +/* Set channel volume */ +void aica_vol(int ch,int vol) { +// g2_write_8(CHNREGADDR(ch, 41),AICA_VOL(vol)); + g2_write_32(CHNREGADDR(ch, 40),(g2_read_32(CHNREGADDR(ch, 40))&0xffff00ff)|(AICA_VOL(vol)<<8) ); + g2_fifo_wait(); +} + +/* Set channel pan */ +void aica_pan(int ch,int pan) { +// g2_write_8(CHNREGADDR(ch, 36),AICA_PAN(pan)); + g2_write_32(CHNREGADDR(ch, 36),(g2_read_32(CHNREGADDR(ch, 36))&0xffffff00)|(AICA_PAN(pan)) ); + g2_fifo_wait(); +} + +/* Set channel frequency */ +void aica_freq(int ch,int freq) { + g2_write_32(CHNREGADDR(ch, 24),AICA_FREQ(freq)); + g2_fifo_wait(); +} + +/* Get channel position */ +int aica_get_pos(int ch) { +#if 1 + /* Observe channel ch */ + g2_write_32(SNDREGADDR(0x280c),(g2_read_32(SNDREGADDR(0x280c))&0xffff00ff) | (ch<<8)); + g2_fifo_wait(); + /* Update position counters */ + return g2_read_32(SNDREGADDR(0x2814)) & 0xffff; +#else + /* Observe channel ch */ + g2_write_8(SNDREGADDR(0x280d),ch); + /* Update position counters */ + return g2_read_32(SNDREGADDR(0x2814)) & 0xffff; +#endif +} diff --git a/src/audio/dc/aica.h b/src/audio/dc/aica.h new file mode 100644 index 0000000..2bee433 --- /dev/null +++ b/src/audio/dc/aica.h @@ -0,0 +1,40 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _AICA_H_ +#define _AICA_H_ + +#define AICA_MEM 0xa0800000 + +#define SM_8BIT 1 +#define SM_16BIT 0 +#define SM_ADPCM 2 + +void aica_play(int ch,int mode,unsigned long smpptr,int looptst,int loopend,int freq,int vol,int pan,int loopflag); +void aica_stop(int ch); +void aica_vol(int ch,int vol); +void aica_pan(int ch,int pan); +void aica_freq(int ch,int freq); +int aica_get_pos(int ch); + +#endif diff --git a/src/audio/disk/SDL_diskaudio.c b/src/audio/disk/SDL_diskaudio.c new file mode 100644 index 0000000..fd3595c --- /dev/null +++ b/src/audio/disk/SDL_diskaudio.c @@ -0,0 +1,186 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org + + This file written by Ryan C. Gordon (icculus@icculus.org) +*/ +#include "SDL_config.h" + +/* Output raw audio data to a file. */ + +#if HAVE_STDIO_H +#include <stdio.h> +#endif + +#include "SDL_rwops.h" +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_diskaudio.h" + +/* The tag name used by DISK audio */ +#define DISKAUD_DRIVER_NAME "disk" + +/* environment variables and defaults. */ +#define DISKENVR_OUTFILE "SDL_DISKAUDIOFILE" +#define DISKDEFAULT_OUTFILE "sdlaudio.raw" +#define DISKENVR_WRITEDELAY "SDL_DISKAUDIODELAY" +#define DISKDEFAULT_WRITEDELAY 150 + +/* Audio driver functions */ +static int DISKAUD_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void DISKAUD_WaitAudio(_THIS); +static void DISKAUD_PlayAudio(_THIS); +static Uint8 *DISKAUD_GetAudioBuf(_THIS); +static void DISKAUD_CloseAudio(_THIS); + +static const char *DISKAUD_GetOutputFilename(void) +{ + const char *envr = SDL_getenv(DISKENVR_OUTFILE); + return((envr != NULL) ? envr : DISKDEFAULT_OUTFILE); +} + +/* Audio driver bootstrap functions */ +static int DISKAUD_Available(void) +{ + const char *envr = SDL_getenv("SDL_AUDIODRIVER"); + if (envr && (SDL_strcmp(envr, DISKAUD_DRIVER_NAME) == 0)) { + return(1); + } + return(0); +} + +static void DISKAUD_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *DISKAUD_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + const char *envr; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + envr = SDL_getenv(DISKENVR_WRITEDELAY); + this->hidden->write_delay = (envr) ? SDL_atoi(envr) : DISKDEFAULT_WRITEDELAY; + + /* Set the function pointers */ + this->OpenAudio = DISKAUD_OpenAudio; + this->WaitAudio = DISKAUD_WaitAudio; + this->PlayAudio = DISKAUD_PlayAudio; + this->GetAudioBuf = DISKAUD_GetAudioBuf; + this->CloseAudio = DISKAUD_CloseAudio; + + this->free = DISKAUD_DeleteDevice; + + return this; +} + +AudioBootStrap DISKAUD_bootstrap = { + DISKAUD_DRIVER_NAME, "direct-to-disk audio", + DISKAUD_Available, DISKAUD_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void DISKAUD_WaitAudio(_THIS) +{ + SDL_Delay(this->hidden->write_delay); +} + +static void DISKAUD_PlayAudio(_THIS) +{ + int written; + + /* Write the audio data */ + written = SDL_RWwrite(this->hidden->output, + this->hidden->mixbuf, 1, + this->hidden->mixlen); + + /* If we couldn't write, assume fatal error for now */ + if ( (Uint32)written != this->hidden->mixlen ) { + this->enabled = 0; + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Wrote %d bytes of audio data\n", written); +#endif +} + +static Uint8 *DISKAUD_GetAudioBuf(_THIS) +{ + return(this->hidden->mixbuf); +} + +static void DISKAUD_CloseAudio(_THIS) +{ + if ( this->hidden->mixbuf != NULL ) { + SDL_FreeAudioMem(this->hidden->mixbuf); + this->hidden->mixbuf = NULL; + } + if ( this->hidden->output != NULL ) { + SDL_RWclose(this->hidden->output); + this->hidden->output = NULL; + } +} + +static int DISKAUD_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + const char *fname = DISKAUD_GetOutputFilename(); + + /* Open the audio device */ + this->hidden->output = SDL_RWFromFile(fname, "wb"); + if ( this->hidden->output == NULL ) { + return(-1); + } + +#if HAVE_STDIO_H + fprintf(stderr, "WARNING: You are using the SDL disk writer" + " audio driver!\n Writing to file [%s].\n", fname); +#endif + + /* Allocate mixing buffer */ + this->hidden->mixlen = spec->size; + this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); + if ( this->hidden->mixbuf == NULL ) { + return(-1); + } + SDL_memset(this->hidden->mixbuf, spec->silence, spec->size); + + /* We're ready to rock and roll. :-) */ + return(0); +} + diff --git a/src/audio/disk/SDL_diskaudio.h b/src/audio/disk/SDL_diskaudio.h new file mode 100644 index 0000000..76f0607 --- /dev/null +++ b/src/audio/disk/SDL_diskaudio.h @@ -0,0 +1,41 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_diskaudio_h +#define _SDL_diskaudio_h + +#include "SDL_rwops.h" +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + SDL_RWops *output; + Uint8 *mixbuf; + Uint32 mixlen; + Uint32 write_delay; +}; + +#endif /* _SDL_diskaudio_h */ diff --git a/src/audio/dma/SDL_dmaaudio.c b/src/audio/dma/SDL_dmaaudio.c new file mode 100644 index 0000000..dbb5a20 --- /dev/null +++ b/src/audio/dma/SDL_dmaaudio.c @@ -0,0 +1,455 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include <stdio.h> +#include <string.h> /* For strerror() */ +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H +/* This is installed on some systems */ +#include <soundcard.h> +#else +/* This is recommended by OSS */ +#include <sys/soundcard.h> +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((Uint8 *)-1) +#endif + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_dmaaudio.h" + +/* The tag name used by DMA audio */ +#define DMA_DRIVER_NAME "dma" + +/* Open the audio device for playback, and don't block if busy */ +#define OPEN_FLAGS (O_RDWR|O_NONBLOCK) + +/* Audio driver functions */ +static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void DMA_WaitAudio(_THIS); +static void DMA_PlayAudio(_THIS); +static Uint8 *DMA_GetAudioBuf(_THIS); +static void DMA_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + int available; + int fd; + + available = 0; + + fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); + if ( fd >= 0 ) { + int caps; + struct audio_buf_info info; + + if ( (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) && + (caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) && + (ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0) ) { + available = 1; + } + close(fd); + } + return(available); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + audio_fd = -1; + + /* Set the function pointers */ + this->OpenAudio = DMA_OpenAudio; + this->WaitAudio = DMA_WaitAudio; + this->PlayAudio = DMA_PlayAudio; + this->GetAudioBuf = DMA_GetAudioBuf; + this->CloseAudio = DMA_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap DMA_bootstrap = { + DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void DMA_WaitAudio(_THIS) +{ + fd_set fdset; + + /* Check to see if the thread-parent process is still alive */ + { static int cnt = 0; + /* Note that this only works with thread implementations + that use a different process id for each thread. + */ + if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ + if ( kill(parent, 0) < 0 ) { + this->enabled = 0; + } + } + } + + /* See if we need to use timed audio synchronization */ + if ( frame_ticks ) { + /* Use timer for general audio synchronization */ + Sint32 ticks; + + ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; + if ( ticks > 0 ) { + SDL_Delay(ticks); + } + } else { + /* Use select() for audio synchronization */ + struct timeval timeout; + FD_ZERO(&fdset); + FD_SET(audio_fd, &fdset); + timeout.tv_sec = 10; + timeout.tv_usec = 0; +#ifdef DEBUG_AUDIO + fprintf(stderr, "Waiting for audio to get ready\n"); +#endif + if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) { + const char *message = +#ifdef AUDIO_OSPACE_HACK + "Audio timeout - buggy audio driver? (trying ospace)"; +#else + "Audio timeout - buggy audio driver? (disabled)"; +#endif + /* In general we should never print to the screen, + but in this case we have no other way of letting + the user know what happened. + */ + fprintf(stderr, "SDL: %s\n", message); +#ifdef AUDIO_OSPACE_HACK + /* We may be able to use GET_OSPACE trick */ + frame_ticks = (float)(this->spec->samples*1000) / + this->spec->freq; + next_frame = SDL_GetTicks()+frame_ticks; +#else + this->enabled = 0; + /* Don't try to close - may hang */ + audio_fd = -1; +#ifdef DEBUG_AUDIO + fprintf(stderr, "Done disabling audio\n"); +#endif +#endif /* AUDIO_OSPACE_HACK */ + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Ready!\n"); +#endif + } +} + +static void DMA_PlayAudio(_THIS) +{ + /* If timer synchronization is enabled, set the next write frame */ + if ( frame_ticks ) { + next_frame += frame_ticks; + } + return; +} + +static Uint8 *DMA_GetAudioBuf(_THIS) +{ + count_info info; + int playing; + int filling; + + /* Get number of blocks, looping if we're not using select() */ + do { + if ( ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &info) < 0 ) { + /* Uh oh... */ + this->enabled = 0; + return(NULL); + } + } while ( frame_ticks && (info.blocks < 1) ); +#ifdef DEBUG_AUDIO + if ( info.blocks > 1 ) { + printf("Warning: audio underflow (%d frags)\n", info.blocks-1); + } +#endif + playing = info.ptr / this->spec.size; + filling = (playing + 1)%num_buffers; + return (dma_buf + (filling * this->spec.size)); +} + +static void DMA_CloseAudio(_THIS) +{ + if ( dma_buf != NULL ) { + munmap(dma_buf, dma_len); + dma_buf = NULL; + } + if ( audio_fd >= 0 ) { + close(audio_fd); + audio_fd = -1; + } +} + +static int DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo, + SDL_AudioSpec *spec) +{ + int frag_spec; + int value; + + /* Close and then reopen the audio device */ + close(audio_fd); + audio_fd = open(audiodev, O_RDWR, 0); + if ( audio_fd < 0 ) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return(-1); + } + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + /* Determine the power of two of the fragment size */ + for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec ); + if ( (0x01<<frag_spec) != spec->size ) { + SDL_SetError("Fragment size must be a power of two"); + return(-1); + } + + /* Set the audio buffering parameters */ + if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) { + SDL_SetError("Couldn't set audio fragment spec"); + return(-1); + } + + /* Set the audio format */ + value = format; + if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || + (value != format) ) { + SDL_SetError("Couldn't set audio format"); + return(-1); + } + + /* Set mono or stereo audio */ + value = (spec->channels > 1); + if ( (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) || + (value != stereo) ) { + SDL_SetError("Couldn't set audio channels"); + return(-1); + } + + /* Set the DSP frequency */ + value = spec->freq; + if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) { + SDL_SetError("Couldn't set audio frequency"); + return(-1); + } + spec->freq = value; + + /* We successfully re-opened the audio */ + return(0); +} + +static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + char audiodev[1024]; + int format; + int stereo; + int value; + Uint16 test_format; + struct audio_buf_info info; + + /* Reset the timer synchronization flag */ + frame_ticks = 0.0; + + /* Open the audio device */ + audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); + if ( audio_fd < 0 ) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return(-1); + } + dma_buf = NULL; + ioctl(audio_fd, SNDCTL_DSP_RESET, 0); + + /* Get a list of supported hardware formats */ + if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) { + SDL_SetError("Couldn't get audio format list"); + return(-1); + } + + /* Try for a closest match on audio format */ + format = 0; + for ( test_format = SDL_FirstAudioFormat(spec->format); + ! format && test_format; ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Trying format 0x%4.4x\n", test_format); +#endif + switch ( test_format ) { + case AUDIO_U8: + if ( value & AFMT_U8 ) { + format = AFMT_U8; + } + break; + case AUDIO_S8: + if ( value & AFMT_S8 ) { + format = AFMT_S8; + } + break; + case AUDIO_S16LSB: + if ( value & AFMT_S16_LE ) { + format = AFMT_S16_LE; + } + break; + case AUDIO_S16MSB: + if ( value & AFMT_S16_BE ) { + format = AFMT_S16_BE; + } + break; + case AUDIO_U16LSB: + if ( value & AFMT_U16_LE ) { + format = AFMT_U16_LE; + } + break; + case AUDIO_U16MSB: + if ( value & AFMT_U16_BE ) { + format = AFMT_U16_BE; + } + break; + default: + format = 0; + break; + } + if ( ! format ) { + test_format = SDL_NextAudioFormat(); + } + } + if ( format == 0 ) { + SDL_SetError("Couldn't find any hardware audio formats"); + return(-1); + } + spec->format = test_format; + + /* Set the audio format */ + value = format; + if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || + (value != format) ) { + SDL_SetError("Couldn't set audio format"); + return(-1); + } + + /* Set mono or stereo audio (currently only two channels supported) */ + stereo = (spec->channels > 1); + ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo); + if ( stereo ) { + spec->channels = 2; + } else { + spec->channels = 1; + } + + /* Because some drivers don't allow setting the buffer size + after setting the format, we must re-open the audio device + once we know what format and channels are supported + */ + if ( DMA_ReopenAudio(this, audiodev, format, stereo, spec) < 0 ) { + /* Error is set by DMA_ReopenAudio() */ + return(-1); + } + + /* Memory map the audio buffer */ + if ( ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0 ) { + SDL_SetError("Couldn't get OSPACE parameters"); + return(-1); + } + spec->size = info.fragsize; + spec->samples = spec->size / ((spec->format & 0xFF) / 8); + spec->samples /= spec->channels; + num_buffers = info.fragstotal; + dma_len = num_buffers*spec->size; + dma_buf = (Uint8 *)mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED, + audio_fd, 0); + if ( dma_buf == MAP_FAILED ) { + SDL_SetError("DMA memory map failed"); + dma_buf = NULL; + return(-1); + } + SDL_memset(dma_buf, spec->silence, dma_len); + + /* Check to see if we need to use select() workaround */ + { char *workaround; + workaround = SDL_getenv("SDL_DSP_NOSELECT"); + if ( workaround ) { + frame_ticks = (float)(spec->samples*1000)/spec->freq; + next_frame = SDL_GetTicks()+frame_ticks; + } + } + + /* Trigger audio playback */ + value = 0; + ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value); + value = PCM_ENABLE_OUTPUT; + if ( ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0 ) { + SDL_SetError("Couldn't trigger audio output"); + return(-1); + } + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/dma/SDL_dmaaudio.h b/src/audio/dma/SDL_dmaaudio.h new file mode 100644 index 0000000..9874072 --- /dev/null +++ b/src/audio/dma/SDL_dmaaudio.h @@ -0,0 +1,59 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_dspaudio_h +#define _SDL_dspaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + int audio_fd; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *dma_buf; + int dma_len; + int num_buffers; + + /* Support for audio timing using a timer, in addition to select() */ + float frame_ticks; + float next_frame; +}; +#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ + +/* Old variable names */ +#define audio_fd (this->hidden->audio_fd) +#define parent (this->hidden->parent) +#define dma_buf (this->hidden->dma_buf) +#define dma_len (this->hidden->dma_len) +#define num_buffers (this->hidden->num_buffers) +#define frame_ticks (this->hidden->frame_ticks) +#define next_frame (this->hidden->next_frame) + +#endif /* _SDL_dspaudio_h */ diff --git a/src/audio/dmedia/SDL_irixaudio.c b/src/audio/dmedia/SDL_irixaudio.c new file mode 100644 index 0000000..d17d09b --- /dev/null +++ b/src/audio/dmedia/SDL_irixaudio.c @@ -0,0 +1,242 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer (For IRIX 6.5 and higher) */ +/* patch for IRIX 5 by Georg Schwarz 18/07/2004 */ + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "SDL_irixaudio.h" + + +#ifndef AL_RESOURCE /* as a test whether we use the old IRIX audio libraries */ +#define OLD_IRIX_AUDIO +#define alClosePort(x) ALcloseport(x) +#define alFreeConfig(x) ALfreeconfig(x) +#define alGetFillable(x) ALgetfillable(x) +#define alNewConfig() ALnewconfig() +#define alOpenPort(x,y,z) ALopenport(x,y,z) +#define alSetChannels(x,y) ALsetchannels(x,y) +#define alSetQueueSize(x,y) ALsetqueuesize(x,y) +#define alSetSampFmt(x,y) ALsetsampfmt(x,y) +#define alSetWidth(x,y) ALsetwidth(x,y) +#endif + +/* Audio driver functions */ +static int AL_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void AL_WaitAudio(_THIS); +static void AL_PlayAudio(_THIS); +static Uint8 *AL_GetAudioBuf(_THIS); +static void AL_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + return 1; +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = AL_OpenAudio; + this->WaitAudio = AL_WaitAudio; + this->PlayAudio = AL_PlayAudio; + this->GetAudioBuf = AL_GetAudioBuf; + this->CloseAudio = AL_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap DMEDIA_bootstrap = { + "AL", "IRIX DMedia audio", + Audio_Available, Audio_CreateDevice +}; + + +void static AL_WaitAudio(_THIS) +{ + Sint32 timeleft; + + timeleft = this->spec.samples - alGetFillable(audio_port); + if ( timeleft > 0 ) { + timeleft /= (this->spec.freq/1000); + SDL_Delay((Uint32)timeleft); + } +} + +static void AL_PlayAudio(_THIS) +{ + /* Write the audio data out */ + if ( alWriteFrames(audio_port, mixbuf, this->spec.samples) < 0 ) { + /* Assume fatal error, for now */ + this->enabled = 0; + } +} + +static Uint8 *AL_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void AL_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( audio_port != NULL ) { + alClosePort(audio_port); + audio_port = NULL; + } +} + +static int AL_OpenAudio(_THIS, SDL_AudioSpec * spec) +{ + Uint16 test_format = SDL_FirstAudioFormat(spec->format); + long width = 0; + long fmt = 0; + int valid = 0; + +#ifdef OLD_IRIX_AUDIO + { + long audio_param[2]; + audio_param[0] = AL_OUTPUT_RATE; + audio_param[1] = spec->freq; + valid = (ALsetparams(AL_DEFAULT_DEVICE, audio_param, 2) < 0); + } +#else + { + ALpv audio_param; + audio_param.param = AL_RATE; + audio_param.value.i = spec->freq; + valid = (alSetParams(AL_DEFAULT_OUTPUT, &audio_param, 1) < 0); + } +#endif + + while ((!valid) && (test_format)) { + valid = 1; + spec->format = test_format; + + switch (test_format) { + case AUDIO_S8: + width = AL_SAMPLE_8; + fmt = AL_SAMPFMT_TWOSCOMP; + break; + + case AUDIO_S16SYS: + width = AL_SAMPLE_16; + fmt = AL_SAMPFMT_TWOSCOMP; + break; + + default: + valid = 0; + test_format = SDL_NextAudioFormat(); + break; + } + + if (valid) { + ALconfig audio_config = alNewConfig(); + valid = 0; + if (audio_config) { + if (alSetChannels(audio_config, spec->channels) < 0) { + if (spec->channels > 2) { /* can't handle > stereo? */ + spec->channels = 2; /* try again below. */ + } + } + + if ((alSetSampFmt(audio_config, fmt) >= 0) && + ((!width) || (alSetWidth(audio_config, width) >= 0)) && + (alSetQueueSize(audio_config, spec->samples * 2) >= 0) && + (alSetChannels(audio_config, spec->channels) >= 0)) { + + audio_port = alOpenPort("SDL audio", "w", audio_config); + if (audio_port == NULL) { + /* docs say AL_BAD_CHANNELS happens here, too. */ + int err = oserror(); + if (err == AL_BAD_CHANNELS) { + spec->channels = 2; + alSetChannels(audio_config, spec->channels); + audio_port = alOpenPort("SDL audio", "w", + audio_config); + } + } + + if (audio_port != NULL) { + valid = 1; + } + } + + alFreeConfig(audio_config); + } + } + } + + if (!valid) { + SDL_SetError("Unsupported audio format"); + return (-1); + } + + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(spec); + + /* Allocate mixing buffer */ + mixbuf = (Uint8 *) SDL_AllocAudioMem(spec->size); + if (mixbuf == NULL) { + SDL_OutOfMemory(); + return (-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* We're ready to rock and roll. :-) */ + return (0); +} + diff --git a/src/audio/dmedia/SDL_irixaudio.h b/src/audio/dmedia/SDL_irixaudio.h new file mode 100644 index 0000000..f8bc43a --- /dev/null +++ b/src/audio/dmedia/SDL_irixaudio.h @@ -0,0 +1,45 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_lowaudio_h +#define _SDL_lowaudio_h + +#include <dmedia/audio.h> + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the audio functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The handle for the audio device */ + ALport audio_port; + + Uint8 *mixbuf; /* The app mixing buffer */ +}; + +/* Old variable names */ +#define audio_port (this->hidden->audio_port) +#define mixbuf (this->hidden->mixbuf) + +#endif /* _SDL_lowaudio_h */ diff --git a/src/audio/dsp/SDL_dspaudio.c b/src/audio/dsp/SDL_dspaudio.c new file mode 100644 index 0000000..c394a59 --- /dev/null +++ b/src/audio/dsp/SDL_dspaudio.c @@ -0,0 +1,340 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org + + Modified in Oct 2004 by Hannu Savolainen + hannu@opensound.com +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include <stdio.h> /* For perror() */ +#include <string.h> /* For strerror() */ +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> + +#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H +/* This is installed on some systems */ +#include <soundcard.h> +#else +/* This is recommended by OSS */ +#include <sys/soundcard.h> +#endif + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_dspaudio.h" + +/* The tag name used by DSP audio */ +#define DSP_DRIVER_NAME "dsp" + +/* Open the audio device for playback, and don't block if busy */ +#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) + +/* Audio driver functions */ +static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void DSP_WaitAudio(_THIS); +static void DSP_PlayAudio(_THIS); +static Uint8 *DSP_GetAudioBuf(_THIS); +static void DSP_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + int fd; + int available; + + available = 0; + fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); + if ( fd >= 0 ) { + available = 1; + close(fd); + } + return(available); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + audio_fd = -1; + + /* Set the function pointers */ + this->OpenAudio = DSP_OpenAudio; + this->WaitAudio = DSP_WaitAudio; + this->PlayAudio = DSP_PlayAudio; + this->GetAudioBuf = DSP_GetAudioBuf; + this->CloseAudio = DSP_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap DSP_bootstrap = { + DSP_DRIVER_NAME, "OSS /dev/dsp standard audio", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void DSP_WaitAudio(_THIS) +{ + /* Not needed at all since OSS handles waiting automagically */ +} + +static void DSP_PlayAudio(_THIS) +{ + if (write(audio_fd, mixbuf, mixlen)==-1) + { + perror("Audio write"); + this->enabled = 0; + } + +#ifdef DEBUG_AUDIO + fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen); +#endif +} + +static Uint8 *DSP_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void DSP_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( audio_fd >= 0 ) { + close(audio_fd); + audio_fd = -1; + } +} + +static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + char audiodev[1024]; + int format; + int value; + int frag_spec; + Uint16 test_format; + + /* Make sure fragment size stays a power of 2, or OSS fails. */ + /* I don't know which of these are actually legal values, though... */ + if (spec->channels > 8) + spec->channels = 8; + else if (spec->channels > 4) + spec->channels = 4; + else if (spec->channels > 2) + spec->channels = 2; + + /* Open the audio device */ + audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); + if ( audio_fd < 0 ) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return(-1); + } + mixbuf = NULL; + + /* Make the file descriptor use blocking writes with fcntl() */ + { long flags; + flags = fcntl(audio_fd, F_GETFL); + flags &= ~O_NONBLOCK; + if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) { + SDL_SetError("Couldn't set audio blocking mode"); + DSP_CloseAudio(this); + return(-1); + } + } + + /* Get a list of supported hardware formats */ + if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) { + perror("SNDCTL_DSP_GETFMTS"); + SDL_SetError("Couldn't get audio format list"); + DSP_CloseAudio(this); + return(-1); + } + + /* Try for a closest match on audio format */ + format = 0; + for ( test_format = SDL_FirstAudioFormat(spec->format); + ! format && test_format; ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Trying format 0x%4.4x\n", test_format); +#endif + switch ( test_format ) { + case AUDIO_U8: + if ( value & AFMT_U8 ) { + format = AFMT_U8; + } + break; + case AUDIO_S16LSB: + if ( value & AFMT_S16_LE ) { + format = AFMT_S16_LE; + } + break; + case AUDIO_S16MSB: + if ( value & AFMT_S16_BE ) { + format = AFMT_S16_BE; + } + break; +#if 0 +/* + * These formats are not used by any real life systems so they are not + * needed here. + */ + case AUDIO_S8: + if ( value & AFMT_S8 ) { + format = AFMT_S8; + } + break; + case AUDIO_U16LSB: + if ( value & AFMT_U16_LE ) { + format = AFMT_U16_LE; + } + break; + case AUDIO_U16MSB: + if ( value & AFMT_U16_BE ) { + format = AFMT_U16_BE; + } + break; +#endif + default: + format = 0; + break; + } + if ( ! format ) { + test_format = SDL_NextAudioFormat(); + } + } + if ( format == 0 ) { + SDL_SetError("Couldn't find any hardware audio formats"); + DSP_CloseAudio(this); + return(-1); + } + spec->format = test_format; + + /* Set the audio format */ + value = format; + if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || + (value != format) ) { + perror("SNDCTL_DSP_SETFMT"); + SDL_SetError("Couldn't set audio format"); + DSP_CloseAudio(this); + return(-1); + } + + /* Set the number of channels of output */ + value = spec->channels; + if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) { + perror("SNDCTL_DSP_CHANNELS"); + SDL_SetError("Cannot set the number of channels"); + DSP_CloseAudio(this); + return(-1); + } + spec->channels = value; + + /* Set the DSP frequency */ + value = spec->freq; + if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) { + perror("SNDCTL_DSP_SPEED"); + SDL_SetError("Couldn't set audio frequency"); + DSP_CloseAudio(this); + return(-1); + } + spec->freq = value; + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + /* Determine the power of two of the fragment size */ + for ( frag_spec = 0; (0x01U<<frag_spec) < spec->size; ++frag_spec ); + if ( (0x01U<<frag_spec) != spec->size ) { + SDL_SetError("Fragment size must be a power of two"); + DSP_CloseAudio(this); + return(-1); + } + frag_spec |= 0x00020000; /* two fragments, for low latency */ + + /* Set the audio buffering parameters */ +#ifdef DEBUG_AUDIO + fprintf(stderr, "Requesting %d fragments of size %d\n", + (frag_spec >> 16), 1<<(frag_spec&0xFFFF)); +#endif + if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) { + perror("SNDCTL_DSP_SETFRAGMENT"); + } +#ifdef DEBUG_AUDIO + { audio_buf_info info; + ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info); + fprintf(stderr, "fragments = %d\n", info.fragments); + fprintf(stderr, "fragstotal = %d\n", info.fragstotal); + fprintf(stderr, "fragsize = %d\n", info.fragsize); + fprintf(stderr, "bytes = %d\n", info.bytes); + } +#endif + + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); + if ( mixbuf == NULL ) { + DSP_CloseAudio(this); + return(-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/dsp/SDL_dspaudio.h b/src/audio/dsp/SDL_dspaudio.h new file mode 100644 index 0000000..26d8d2e --- /dev/null +++ b/src/audio/dsp/SDL_dspaudio.h @@ -0,0 +1,53 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_dspaudio_h +#define _SDL_dspaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + int audio_fd; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; +}; +#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ + +/* Old variable names */ +#define audio_fd (this->hidden->audio_fd) +#define parent (this->hidden->parent) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) +#define frame_ticks (this->hidden->frame_ticks) +#define next_frame (this->hidden->next_frame) + +#endif /* _SDL_dspaudio_h */ diff --git a/src/audio/dummy/SDL_dummyaudio.c b/src/audio/dummy/SDL_dummyaudio.c new file mode 100644 index 0000000..9567d96 --- /dev/null +++ b/src/audio/dummy/SDL_dummyaudio.c @@ -0,0 +1,156 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org + + This file written by Ryan C. Gordon (icculus@icculus.org) +*/ +#include "SDL_config.h" + +/* Output audio to nowhere... */ + +#include "SDL_rwops.h" +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_dummyaudio.h" + +/* The tag name used by DUMMY audio */ +#define DUMMYAUD_DRIVER_NAME "dummy" + +/* Audio driver functions */ +static int DUMMYAUD_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void DUMMYAUD_WaitAudio(_THIS); +static void DUMMYAUD_PlayAudio(_THIS); +static Uint8 *DUMMYAUD_GetAudioBuf(_THIS); +static void DUMMYAUD_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ +static int DUMMYAUD_Available(void) +{ + const char *envr = SDL_getenv("SDL_AUDIODRIVER"); + if (envr && (SDL_strcmp(envr, DUMMYAUD_DRIVER_NAME) == 0)) { + return(1); + } + return(0); +} + +static void DUMMYAUD_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *DUMMYAUD_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = DUMMYAUD_OpenAudio; + this->WaitAudio = DUMMYAUD_WaitAudio; + this->PlayAudio = DUMMYAUD_PlayAudio; + this->GetAudioBuf = DUMMYAUD_GetAudioBuf; + this->CloseAudio = DUMMYAUD_CloseAudio; + + this->free = DUMMYAUD_DeleteDevice; + + return this; +} + +AudioBootStrap DUMMYAUD_bootstrap = { + DUMMYAUD_DRIVER_NAME, "SDL dummy audio driver", + DUMMYAUD_Available, DUMMYAUD_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void DUMMYAUD_WaitAudio(_THIS) +{ + /* Don't block on first calls to simulate initial fragment filling. */ + if (this->hidden->initial_calls) + this->hidden->initial_calls--; + else + SDL_Delay(this->hidden->write_delay); +} + +static void DUMMYAUD_PlayAudio(_THIS) +{ + /* no-op...this is a null driver. */ +} + +static Uint8 *DUMMYAUD_GetAudioBuf(_THIS) +{ + return(this->hidden->mixbuf); +} + +static void DUMMYAUD_CloseAudio(_THIS) +{ + if ( this->hidden->mixbuf != NULL ) { + SDL_FreeAudioMem(this->hidden->mixbuf); + this->hidden->mixbuf = NULL; + } +} + +static int DUMMYAUD_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + float bytes_per_sec = 0.0f; + + /* Allocate mixing buffer */ + this->hidden->mixlen = spec->size; + this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); + if ( this->hidden->mixbuf == NULL ) { + return(-1); + } + SDL_memset(this->hidden->mixbuf, spec->silence, spec->size); + + bytes_per_sec = (float) (((spec->format & 0xFF) / 8) * + spec->channels * spec->freq); + + /* + * We try to make this request more audio at the correct rate for + * a given audio spec, so timing stays fairly faithful. + * Also, we have it not block at all for the first two calls, so + * it seems like we're filling two audio fragments right out of the + * gate, like other SDL drivers tend to do. + */ + this->hidden->initial_calls = 2; + this->hidden->write_delay = + (Uint32) ((((float) spec->size) / bytes_per_sec) * 1000.0f); + + /* We're ready to rock and roll. :-) */ + return(0); +} + diff --git a/src/audio/dummy/SDL_dummyaudio.h b/src/audio/dummy/SDL_dummyaudio.h new file mode 100644 index 0000000..e233e2a --- /dev/null +++ b/src/audio/dummy/SDL_dummyaudio.h @@ -0,0 +1,40 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_dummyaudio_h +#define _SDL_dummyaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + Uint8 *mixbuf; + Uint32 mixlen; + Uint32 write_delay; + Uint32 initial_calls; +}; + +#endif /* _SDL_dummyaudio_h */ diff --git a/src/audio/esd/SDL_esdaudio.c b/src/audio/esd/SDL_esdaudio.c new file mode 100644 index 0000000..5514baf --- /dev/null +++ b/src/audio/esd/SDL_esdaudio.c @@ -0,0 +1,323 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to an ESD network stream mixing buffer */ + +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <esd.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_esdaudio.h" + +#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC +#include "SDL_name.h" +#include "SDL_loadso.h" +#else +#define SDL_NAME(X) X +#endif + +/* The tag name used by ESD audio */ +#define ESD_DRIVER_NAME "esd" + +/* Audio driver functions */ +static int ESD_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void ESD_WaitAudio(_THIS); +static void ESD_PlayAudio(_THIS); +static Uint8 *ESD_GetAudioBuf(_THIS); +static void ESD_CloseAudio(_THIS); + +#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC + +static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC; +static void *esd_handle = NULL; +static int esd_loaded = 0; + +static int (*SDL_NAME(esd_open_sound))( const char *host ); +static int (*SDL_NAME(esd_close))( int esd ); +static int (*SDL_NAME(esd_play_stream))( esd_format_t format, int rate, + const char *host, const char *name ); +static struct { + const char *name; + void **func; +} esd_functions[] = { + { "esd_open_sound", (void **)&SDL_NAME(esd_open_sound) }, + { "esd_close", (void **)&SDL_NAME(esd_close) }, + { "esd_play_stream", (void **)&SDL_NAME(esd_play_stream) }, +}; + +static void UnloadESDLibrary() +{ + if ( esd_loaded ) { + SDL_UnloadObject(esd_handle); + esd_handle = NULL; + esd_loaded = 0; + } +} + +static int LoadESDLibrary(void) +{ + int i, retval = -1; + + esd_handle = SDL_LoadObject(esd_library); + if ( esd_handle ) { + esd_loaded = 1; + retval = 0; + for ( i=0; i<SDL_arraysize(esd_functions); ++i ) { + *esd_functions[i].func = SDL_LoadFunction(esd_handle, esd_functions[i].name); + if ( !*esd_functions[i].func ) { + retval = -1; + UnloadESDLibrary(); + break; + } + } + } + return retval; +} + +#else + +static void UnloadESDLibrary() +{ + return; +} + +static int LoadESDLibrary(void) +{ + return 0; +} + +#endif /* SDL_AUDIO_DRIVER_ESD_DYNAMIC */ + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + int connection; + int available; + + available = 0; + if ( LoadESDLibrary() < 0 ) { + return available; + } + connection = SDL_NAME(esd_open_sound)(NULL); + if ( connection >= 0 ) { + available = 1; + SDL_NAME(esd_close)(connection); + } + UnloadESDLibrary(); + return(available); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); + UnloadESDLibrary(); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + LoadESDLibrary(); + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + audio_fd = -1; + + /* Set the function pointers */ + this->OpenAudio = ESD_OpenAudio; + this->WaitAudio = ESD_WaitAudio; + this->PlayAudio = ESD_PlayAudio; + this->GetAudioBuf = ESD_GetAudioBuf; + this->CloseAudio = ESD_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap ESD_bootstrap = { + ESD_DRIVER_NAME, "Enlightened Sound Daemon", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void ESD_WaitAudio(_THIS) +{ + Sint32 ticks; + + /* Check to see if the thread-parent process is still alive */ + { static int cnt = 0; + /* Note that this only works with thread implementations + that use a different process id for each thread. + */ + if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */ + if ( kill(parent, 0) < 0 ) { + this->enabled = 0; + } + } + } + + /* Use timer for general audio synchronization */ + ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; + if ( ticks > 0 ) { + SDL_Delay(ticks); + } +} + +static void ESD_PlayAudio(_THIS) +{ + int written; + + /* Write the audio data, checking for EAGAIN on broken audio drivers */ + do { + written = write(audio_fd, mixbuf, mixlen); + if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) { + SDL_Delay(1); /* Let a little CPU time go by */ + } + } while ( (written < 0) && + ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) ); + + /* Set the next write frame */ + next_frame += frame_ticks; + + /* If we couldn't write, assume fatal error for now */ + if ( written < 0 ) { + this->enabled = 0; + } +} + +static Uint8 *ESD_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void ESD_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( audio_fd >= 0 ) { + SDL_NAME(esd_close)(audio_fd); + audio_fd = -1; + } +} + +/* Try to get the name of the program */ +static char *get_progname(void) +{ + char *progname = NULL; +#ifdef __LINUX__ + FILE *fp; + static char temp[BUFSIZ]; + + SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid()); + fp = fopen(temp, "r"); + if ( fp != NULL ) { + if ( fgets(temp, sizeof(temp)-1, fp) ) { + progname = SDL_strrchr(temp, '/'); + if ( progname == NULL ) { + progname = temp; + } else { + progname = progname+1; + } + } + fclose(fp); + } +#endif + return(progname); +} + +static int ESD_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + esd_format_t format; + + /* Convert audio spec to the ESD audio format */ + format = (ESD_STREAM | ESD_PLAY); + switch ( spec->format & 0xFF ) { + case 8: + format |= ESD_BITS8; + break; + case 16: + format |= ESD_BITS16; + break; + default: + SDL_SetError("Unsupported ESD audio format"); + return(-1); + } + if ( spec->channels == 1 ) { + format |= ESD_MONO; + } else { + format |= ESD_STEREO; + } +#if 0 + spec->samples = ESD_BUF_SIZE; /* Darn, no way to change this yet */ +#endif + + /* Open a connection to the ESD audio server */ + audio_fd = SDL_NAME(esd_play_stream)(format, spec->freq, NULL, get_progname()); + if ( audio_fd < 0 ) { + SDL_SetError("Couldn't open ESD connection"); + return(-1); + } + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + frame_ticks = (float)(spec->samples*1000)/spec->freq; + next_frame = SDL_GetTicks()+frame_ticks; + + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); + if ( mixbuf == NULL ) { + return(-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/esd/SDL_esdaudio.h b/src/audio/esd/SDL_esdaudio.h new file mode 100644 index 0000000..9f8d325 --- /dev/null +++ b/src/audio/esd/SDL_esdaudio.h @@ -0,0 +1,57 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_esdaudio_h +#define _SDL_esdaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + int audio_fd; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; + + /* Support for audio timing using a timer */ + float frame_ticks; + float next_frame; +}; +#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ + +/* Old variable names */ +#define audio_fd (this->hidden->audio_fd) +#define parent (this->hidden->parent) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) +#define frame_ticks (this->hidden->frame_ticks) +#define next_frame (this->hidden->next_frame) + +#endif /* _SDL_esdaudio_h */ diff --git a/src/audio/macosx/SDL_coreaudio.c b/src/audio/macosx/SDL_coreaudio.c new file mode 100644 index 0000000..5842e90 --- /dev/null +++ b/src/audio/macosx/SDL_coreaudio.c @@ -0,0 +1,291 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include <CoreAudio/CoreAudio.h> +#include <CoreServices/CoreServices.h> +#include <AudioUnit/AudioUnit.h> +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050 +#include <AudioUnit/AUNTComponent.h> +#endif + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" +#include "SDL_coreaudio.h" + + +/* Audio driver functions */ + +static int Core_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void Core_WaitAudio(_THIS); +static void Core_PlayAudio(_THIS); +static Uint8 *Core_GetAudioBuf(_THIS); +static void Core_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = Core_OpenAudio; + this->WaitAudio = Core_WaitAudio; + this->PlayAudio = Core_PlayAudio; + this->GetAudioBuf = Core_GetAudioBuf; + this->CloseAudio = Core_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap COREAUDIO_bootstrap = { + "coreaudio", "Mac OS X CoreAudio", + Audio_Available, Audio_CreateDevice +}; + +/* The CoreAudio callback */ +static OSStatus audioCallback (void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + SDL_AudioDevice *this = (SDL_AudioDevice *)inRefCon; + UInt32 remaining, len; + AudioBuffer *abuf; + void *ptr; + UInt32 i; + + /* Only do anything if audio is enabled and not paused */ + if ( ! this->enabled || this->paused ) { + for (i = 0; i < ioData->mNumberBuffers; i++) { + abuf = &ioData->mBuffers[i]; + SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize); + } + return 0; + } + + /* No SDL conversion should be needed here, ever, since we accept + any input format in OpenAudio, and leave the conversion to CoreAudio. + */ + /* + assert(!this->convert.needed); + assert(this->spec.channels == ioData->mNumberChannels); + */ + + for (i = 0; i < ioData->mNumberBuffers; i++) { + abuf = &ioData->mBuffers[i]; + remaining = abuf->mDataByteSize; + ptr = abuf->mData; + while (remaining > 0) { + if (bufferOffset >= bufferSize) { + /* Generate the data */ + SDL_memset(buffer, this->spec.silence, bufferSize); + SDL_mutexP(this->mixer_lock); + (*this->spec.callback)(this->spec.userdata, + buffer, bufferSize); + SDL_mutexV(this->mixer_lock); + bufferOffset = 0; + } + + len = bufferSize - bufferOffset; + if (len > remaining) + len = remaining; + SDL_memcpy(ptr, (char *)buffer + bufferOffset, len); + ptr = (char *)ptr + len; + remaining -= len; + bufferOffset += len; + } + } + + return 0; +} + +/* Dummy functions -- we don't use thread-based audio */ +void Core_WaitAudio(_THIS) +{ + return; +} + +void Core_PlayAudio(_THIS) +{ + return; +} + +Uint8 *Core_GetAudioBuf(_THIS) +{ + return(NULL); +} + +void Core_CloseAudio(_THIS) +{ + OSStatus result; + struct AURenderCallbackStruct callback; + + /* stop processing the audio unit */ + result = AudioOutputUnitStop (outputAudioUnit); + if (result != noErr) { + SDL_SetError("Core_CloseAudio: AudioOutputUnitStop"); + return; + } + + /* Remove the input callback */ + callback.inputProc = 0; + callback.inputProcRefCon = 0; + result = AudioUnitSetProperty (outputAudioUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &callback, + sizeof(callback)); + if (result != noErr) { + SDL_SetError("Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)"); + return; + } + + result = CloseComponent(outputAudioUnit); + if (result != noErr) { + SDL_SetError("Core_CloseAudio: CloseComponent"); + return; + } + + SDL_free(buffer); +} + +#define CHECK_RESULT(msg) \ + if (result != noErr) { \ + SDL_SetError("Failed to start CoreAudio: " msg); \ + return -1; \ + } + + +int Core_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + OSStatus result = noErr; + Component comp; + ComponentDescription desc; + struct AURenderCallbackStruct callback; + AudioStreamBasicDescription requestedDesc; + + /* Setup a AudioStreamBasicDescription with the requested format */ + requestedDesc.mFormatID = kAudioFormatLinearPCM; + requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked; + requestedDesc.mChannelsPerFrame = spec->channels; + requestedDesc.mSampleRate = spec->freq; + + requestedDesc.mBitsPerChannel = spec->format & 0xFF; + if (spec->format & 0x8000) + requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger; + if (spec->format & 0x1000) + requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian; + + requestedDesc.mFramesPerPacket = 1; + requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8; + requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket; + + + /* Locate the default output audio unit */ + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + comp = FindNextComponent (NULL, &desc); + if (comp == NULL) { + SDL_SetError ("Failed to start CoreAudio: FindNextComponent returned NULL"); + return -1; + } + + /* Open & initialize the default output audio unit */ + result = OpenAComponent (comp, &outputAudioUnit); + CHECK_RESULT("OpenAComponent") + + result = AudioUnitInitialize (outputAudioUnit); + CHECK_RESULT("AudioUnitInitialize") + + /* Set the input format of the audio unit. */ + result = AudioUnitSetProperty (outputAudioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &requestedDesc, + sizeof (requestedDesc)); + CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)") + + /* Set the audio callback */ + callback.inputProc = audioCallback; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty (outputAudioUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &callback, + sizeof(callback)); + CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)") + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + /* Allocate a sample buffer */ + bufferOffset = bufferSize = this->spec.size; + buffer = SDL_malloc(bufferSize); + + /* Finally, start processing of the audio unit */ + result = AudioOutputUnitStart (outputAudioUnit); + CHECK_RESULT("AudioOutputUnitStart") + + + /* We're running! */ + return(1); +} diff --git a/src/audio/macosx/SDL_coreaudio.h b/src/audio/macosx/SDL_coreaudio.h new file mode 100644 index 0000000..cf74856 --- /dev/null +++ b/src/audio/macosx/SDL_coreaudio.h @@ -0,0 +1,45 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_coreaudio_h +#define _SDL_coreaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + AudioUnit outputAudioUnit; + void *buffer; + UInt32 bufferOffset; + UInt32 bufferSize; +}; + +/* Old variable names */ +#define outputAudioUnit (this->hidden->outputAudioUnit) +#define buffer (this->hidden->buffer) +#define bufferOffset (this->hidden->bufferOffset) +#define bufferSize (this->hidden->bufferSize) + +#endif /* _SDL_coreaudio_h */ diff --git a/src/audio/macrom/SDL_romaudio.c b/src/audio/macrom/SDL_romaudio.c new file mode 100644 index 0000000..2a29be2 --- /dev/null +++ b/src/audio/macrom/SDL_romaudio.c @@ -0,0 +1,496 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#if defined(__APPLE__) && defined(__MACH__) +# include <Carbon/Carbon.h> +#elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335) +# include <Carbon.h> +#else +# include <Sound.h> /* SoundManager interface */ +# include <Gestalt.h> +# include <DriverServices.h> +#endif + +#if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335) +#if !defined(NewSndCallBackProc) /* avoid circular redefinition... */ +#define NewSndCallBackUPP NewSndCallBackProc +#endif +#if !defined(NewSndCallBackUPP) +#define NewSndCallBackUPP NewSndCallBackProc +#endif +#endif + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" +#include "SDL_romaudio.h" + +/* Audio driver functions */ + +static void Mac_CloseAudio(_THIS); +static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void Mac_LockAudio(_THIS); +static void Mac_UnlockAudio(_THIS); + +/* Audio driver bootstrap functions */ + + +static int Audio_Available(void) +{ + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = Mac_OpenAudio; + this->CloseAudio = Mac_CloseAudio; + this->LockAudio = Mac_LockAudio; + this->UnlockAudio = Mac_UnlockAudio; + this->free = Audio_DeleteDevice; + +#ifdef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */ + this->LockAudio = NULL; + this->UnlockAudio = NULL; +#endif + return this; +} + +AudioBootStrap SNDMGR_bootstrap = { + "sndmgr", "MacOS SoundManager 3.0", + Audio_Available, Audio_CreateDevice +}; + +#if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE) +/* This works correctly on Mac OS X */ + +#pragma options align=power + +static volatile SInt32 audio_is_locked = 0; +static volatile SInt32 need_to_mix = 0; + +static UInt8 *buffer[2]; +static volatile UInt32 running = 0; +static CmpSoundHeader header; +static volatile Uint32 fill_me = 0; + +static void mix_buffer(SDL_AudioDevice *audio, UInt8 *buffer) +{ + if ( ! audio->paused ) { +#ifdef __MACOSX__ + SDL_mutexP(audio->mixer_lock); +#endif + if ( audio->convert.needed ) { + audio->spec.callback(audio->spec.userdata, + (Uint8 *)audio->convert.buf,audio->convert.len); + SDL_ConvertAudio(&audio->convert); + if ( audio->convert.len_cvt != audio->spec.size ) { + /* Uh oh... probably crashes here */; + } + SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt); + } else { + audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size); + } +#ifdef __MACOSX__ + SDL_mutexV(audio->mixer_lock); +#endif + } + + DecrementAtomic((SInt32 *) &need_to_mix); +} + +static void Mac_LockAudio(_THIS) +{ + IncrementAtomic((SInt32 *) &audio_is_locked); +} + +static void Mac_UnlockAudio(_THIS) +{ + SInt32 oldval; + + oldval = DecrementAtomic((SInt32 *) &audio_is_locked); + if ( oldval != 1 ) /* != 1 means audio is still locked. */ + return; + + /* Did we miss the chance to mix in an interrupt? Do it now. */ + if ( BitAndAtomic (0xFFFFFFFF, (UInt32 *) &need_to_mix) ) { + /* + * Note that this could be a problem if you missed an interrupt + * while the audio was locked, and get preempted by a second + * interrupt here, but that means you locked for way too long anyhow. + */ + mix_buffer (this, buffer[fill_me]); + } +} + +static void callBackProc (SndChannel *chan, SndCommand *cmd_passed ) { + UInt32 play_me; + SndCommand cmd; + SDL_AudioDevice *audio = (SDL_AudioDevice *)chan->userInfo; + + IncrementAtomic((SInt32 *) &need_to_mix); + + fill_me = cmd_passed->param2; /* buffer that has just finished playing, so fill it */ + play_me = ! fill_me; /* filled buffer to play _now_ */ + + if ( ! audio->enabled ) { + return; + } + + /* queue previously mixed buffer for playback. */ + header.samplePtr = (Ptr)buffer[play_me]; + cmd.cmd = bufferCmd; + cmd.param1 = 0; + cmd.param2 = (long)&header; + SndDoCommand (chan, &cmd, 0); + + memset (buffer[fill_me], 0, audio->spec.size); + + /* + * if audio device isn't locked, mix the next buffer to be queued in + * the memory block that just finished playing. + */ + if ( ! BitAndAtomic(0xFFFFFFFF, (UInt32 *) &audio_is_locked) ) { + mix_buffer (audio, buffer[fill_me]); + } + + /* set this callback to run again when current buffer drains. */ + if ( running ) { + cmd.cmd = callBackCmd; + cmd.param1 = 0; + cmd.param2 = play_me; + + SndDoCommand (chan, &cmd, 0); + } +} + +static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) { + + SndCallBackUPP callback; + int sample_bits; + int i; + long initOptions; + + /* Very few conversions are required, but... */ + switch (spec->format) { + case AUDIO_S8: + spec->format = AUDIO_U8; + break; + case AUDIO_U16LSB: + spec->format = AUDIO_S16LSB; + break; + case AUDIO_U16MSB: + spec->format = AUDIO_S16MSB; + break; + } + SDL_CalculateAudioSpec(spec); + + /* initialize bufferCmd header */ + memset (&header, 0, sizeof(header)); + callback = (SndCallBackUPP) NewSndCallBackUPP (callBackProc); + sample_bits = spec->size / spec->samples / spec->channels * 8; + +#ifdef DEBUG_AUDIO + fprintf(stderr, + "Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n", + spec->format, spec->channels, sample_bits, spec->freq); +#endif /* DEBUG_AUDIO */ + + header.numChannels = spec->channels; + header.sampleSize = sample_bits; + header.sampleRate = spec->freq << 16; + header.numFrames = spec->samples; + header.encode = cmpSH; + + /* Note that we install the 16bitLittleEndian Converter if needed. */ + if ( spec->format == 0x8010 ) { + header.compressionID = fixedCompression; + header.format = k16BitLittleEndianFormat; + } + + /* allocate 2 buffers */ + for (i=0; i<2; i++) { + buffer[i] = (UInt8*)malloc (sizeof(UInt8) * spec->size); + if (buffer[i] == NULL) { + SDL_OutOfMemory(); + return (-1); + } + memset (buffer[i], 0, spec->size); + } + + /* Create the sound manager channel */ + channel = (SndChannelPtr)SDL_malloc(sizeof(*channel)); + if ( channel == NULL ) { + SDL_OutOfMemory(); + return(-1); + } + if ( spec->channels >= 2 ) { + initOptions = initStereo; + } else { + initOptions = initMono; + } + channel->userInfo = (long)this; + channel->qLength = 128; + if ( SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr ) { + SDL_SetError("Unable to create audio channel"); + SDL_free(channel); + channel = NULL; + return(-1); + } + + /* start playback */ + { + SndCommand cmd; + cmd.cmd = callBackCmd; + cmd.param2 = 0; + running = 1; + SndDoCommand (channel, &cmd, 0); + } + + return 1; +} + +static void Mac_CloseAudio(_THIS) { + + int i; + + running = 0; + + if (channel) { + SndDisposeChannel (channel, true); + channel = NULL; + } + + for ( i=0; i<2; ++i ) { + if ( buffer[i] ) { + SDL_free(buffer[i]); + buffer[i] = NULL; + } + } +} + +#else /* !TARGET_API_MAC_CARBON && !USE_RYANS_SOUNDCODE */ + +static void Mac_LockAudio(_THIS) +{ + /* no-op. */ +} + +static void Mac_UnlockAudio(_THIS) +{ + /* no-op. */ +} + + +/* This function is called by Sound Manager when it has exhausted one of + the buffers, so we'll zero it to silence and fill it with audio if + we're not paused. +*/ +static pascal +void sndDoubleBackProc (SndChannelPtr chan, SndDoubleBufferPtr newbuf) +{ + SDL_AudioDevice *audio = (SDL_AudioDevice *)newbuf->dbUserInfo[0]; + + /* If audio is quitting, don't do anything */ + if ( ! audio->enabled ) { + return; + } + memset (newbuf->dbSoundData, 0, audio->spec.size); + newbuf->dbNumFrames = audio->spec.samples; + if ( ! audio->paused ) { + if ( audio->convert.needed ) { + audio->spec.callback(audio->spec.userdata, + (Uint8 *)audio->convert.buf,audio->convert.len); + SDL_ConvertAudio(&audio->convert); +#if 0 + if ( audio->convert.len_cvt != audio->spec.size ) { + /* Uh oh... probably crashes here */; + } +#endif + SDL_memcpy(newbuf->dbSoundData, audio->convert.buf, + audio->convert.len_cvt); + } else { + audio->spec.callback(audio->spec.userdata, + (Uint8 *)newbuf->dbSoundData, audio->spec.size); + } + } + newbuf->dbFlags |= dbBufferReady; +} + +static int DoubleBufferAudio_Available(void) +{ + int available; + NumVersion sndversion; + long response; + + available = 0; + sndversion = SndSoundManagerVersion(); + if ( sndversion.majorRev >= 3 ) { + if ( Gestalt(gestaltSoundAttr, &response) == noErr ) { + if ( (response & (1 << gestaltSndPlayDoubleBuffer)) ) { + available = 1; + } + } + } else { + if ( Gestalt(gestaltSoundAttr, &response) == noErr ) { + if ( (response & (1 << gestaltHasASC)) ) { + available = 1; + } + } + } + return(available); +} + +static void Mac_CloseAudio(_THIS) +{ + int i; + + if ( channel != NULL ) { + /* Clean up the audio channel */ + SndDisposeChannel(channel, true); + channel = NULL; + } + for ( i=0; i<2; ++i ) { + if ( audio_buf[i] ) { + SDL_free(audio_buf[i]); + audio_buf[i] = NULL; + } + } +} + +static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + SndDoubleBufferHeader2 audio_dbh; + int i; + long initOptions; + int sample_bits; + SndDoubleBackUPP doubleBackProc; + + /* Check to make sure double-buffered audio is available */ + if ( ! DoubleBufferAudio_Available() ) { + SDL_SetError("Sound manager doesn't support double-buffering"); + return(-1); + } + + /* Very few conversions are required, but... */ + switch (spec->format) { + case AUDIO_S8: + spec->format = AUDIO_U8; + break; + case AUDIO_U16LSB: + spec->format = AUDIO_S16LSB; + break; + case AUDIO_U16MSB: + spec->format = AUDIO_S16MSB; + break; + } + SDL_CalculateAudioSpec(spec); + + /* initialize the double-back header */ + SDL_memset(&audio_dbh, 0, sizeof(audio_dbh)); + doubleBackProc = NewSndDoubleBackProc (sndDoubleBackProc); + sample_bits = spec->size / spec->samples / spec->channels * 8; + + audio_dbh.dbhNumChannels = spec->channels; + audio_dbh.dbhSampleSize = sample_bits; + audio_dbh.dbhCompressionID = 0; + audio_dbh.dbhPacketSize = 0; + audio_dbh.dbhSampleRate = spec->freq << 16; + audio_dbh.dbhDoubleBack = doubleBackProc; + audio_dbh.dbhFormat = 0; + + /* Note that we install the 16bitLittleEndian Converter if needed. */ + if ( spec->format == 0x8010 ) { + audio_dbh.dbhCompressionID = fixedCompression; + audio_dbh.dbhFormat = k16BitLittleEndianFormat; + } + + /* allocate the 2 double-back buffers */ + for ( i=0; i<2; ++i ) { + audio_buf[i] = SDL_calloc(1, sizeof(SndDoubleBuffer)+spec->size); + if ( audio_buf[i] == NULL ) { + SDL_OutOfMemory(); + return(-1); + } + audio_buf[i]->dbNumFrames = spec->samples; + audio_buf[i]->dbFlags = dbBufferReady; + audio_buf[i]->dbUserInfo[0] = (long)this; + audio_dbh.dbhBufferPtr[i] = audio_buf[i]; + } + + /* Create the sound manager channel */ + channel = (SndChannelPtr)SDL_malloc(sizeof(*channel)); + if ( channel == NULL ) { + SDL_OutOfMemory(); + return(-1); + } + if ( spec->channels >= 2 ) { + initOptions = initStereo; + } else { + initOptions = initMono; + } + channel->userInfo = 0; + channel->qLength = 128; + if ( SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr ) { + SDL_SetError("Unable to create audio channel"); + SDL_free(channel); + channel = NULL; + return(-1); + } + + /* Start playback */ + if ( SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr)&audio_dbh) + != noErr ) { + SDL_SetError("Unable to play double buffered audio"); + return(-1); + } + + return 1; +} + +#endif /* TARGET_API_MAC_CARBON || USE_RYANS_SOUNDCODE */ + diff --git a/src/audio/macrom/SDL_romaudio.h b/src/audio/macrom/SDL_romaudio.h new file mode 100644 index 0000000..2a892f4 --- /dev/null +++ b/src/audio/macrom/SDL_romaudio.h @@ -0,0 +1,50 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_romaudio_h +#define _SDL_romaudio_h + +#include "../SDL_sysaudio.h" + +/* This is Ryan's improved MacOS sound code, with locking support */ +#define USE_RYANS_SOUNDCODE + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* Sound manager audio channel */ + SndChannelPtr channel; +#if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE) + /* FIXME: Add Ryan's static data here */ +#else + /* Double buffering variables */ + SndDoubleBufferPtr audio_buf[2]; +#endif +}; + +/* Old variable names */ +#define channel (this->hidden->channel) +#define audio_buf (this->hidden->audio_buf) + +#endif /* _SDL_romaudio_h */ diff --git a/src/audio/mint/SDL_mintaudio.c b/src/audio/mint/SDL_mintaudio.c new file mode 100644 index 0000000..221c8eb --- /dev/null +++ b/src/audio/mint/SDL_mintaudio.c @@ -0,0 +1,215 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + Audio interrupt variables and callback function + + Patrice Mandin +*/ + +#include <unistd.h> + +#include <mint/osbind.h> +#include <mint/falcon.h> +#include <mint/mintbind.h> +#include <mint/cookie.h> + +#include "SDL_audio.h" +#include "SDL_mintaudio.h" +#include "SDL_mintaudio_stfa.h" + +/* The audio device */ + +SDL_AudioDevice *SDL_MintAudio_device; +Uint8 *SDL_MintAudio_audiobuf[2]; /* Pointers to buffers */ +unsigned long SDL_MintAudio_audiosize; /* Length of audio buffer=spec->size */ +volatile unsigned short SDL_MintAudio_numbuf; /* Buffer to play */ +volatile unsigned short SDL_MintAudio_mutex; +volatile unsigned long SDL_MintAudio_clocktics; +cookie_stfa_t *SDL_MintAudio_stfa; +unsigned short SDL_MintAudio_hasfpu; + +/* MiNT thread variables */ +SDL_bool SDL_MintAudio_mint_present; +SDL_bool SDL_MintAudio_quit_thread; +SDL_bool SDL_MintAudio_thread_finished; +long SDL_MintAudio_thread_pid; + +/* The callback function, called by each driver whenever needed */ + +void SDL_MintAudio_Callback(void) +{ + Uint8 *buffer; + SDL_AudioDevice *audio = SDL_MintAudio_device; + + buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf]; + SDL_memset(buffer, audio->spec.silence, audio->spec.size); + + if (audio->paused) + return; + + if (audio->convert.needed) { + int silence; + + if ( audio->convert.src_format == AUDIO_U8 ) { + silence = 0x80; + } else { + silence = 0; + } + SDL_memset(audio->convert.buf, silence, audio->convert.len); + audio->spec.callback(audio->spec.userdata, + (Uint8 *)audio->convert.buf,audio->convert.len); + SDL_ConvertAudio(&audio->convert); + SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt); + } else { + audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size); + } +} + +/* Add a new frequency/clock/predivisor to the current list */ +void SDL_MintAudio_AddFrequency(_THIS, Uint32 frequency, Uint32 clock, + Uint32 prediv, int gpio_bits) +{ + int i, p; + + if (MINTAUDIO_freqcount==MINTAUDIO_maxfreqs) { + return; + } + + /* Search where to insert the frequency (highest first) */ + for (p=0; p<MINTAUDIO_freqcount; p++) { + if (frequency > MINTAUDIO_frequencies[p].frequency) { + break; + } + } + + /* Put all following ones farer */ + if (MINTAUDIO_freqcount>0) { + for (i=MINTAUDIO_freqcount; i>p; i--) { + SDL_memcpy(&MINTAUDIO_frequencies[i], &MINTAUDIO_frequencies[i-1], sizeof(mint_frequency_t)); + } + } + + /* And insert new one */ + MINTAUDIO_frequencies[p].frequency = frequency; + MINTAUDIO_frequencies[p].masterclock = clock; + MINTAUDIO_frequencies[p].predivisor = prediv; + MINTAUDIO_frequencies[p].gpio_bits = gpio_bits; + + MINTAUDIO_freqcount++; +} + +/* Search for the nearest frequency */ +int SDL_MintAudio_SearchFrequency(_THIS, int desired_freq) +{ + int i; + + /* Only 1 freq ? */ + if (MINTAUDIO_freqcount==1) { + return 0; + } + + /* Check the array */ + for (i=0; i<MINTAUDIO_freqcount; i++) { + if (desired_freq >= ((MINTAUDIO_frequencies[i].frequency+ + MINTAUDIO_frequencies[i+1].frequency)>>1)) { + return i; + } + } + + /* Not in the array, give the latest */ + return MINTAUDIO_freqcount-1; +} + +/* Check if FPU is present */ +void SDL_MintAudio_CheckFpu(void) +{ + unsigned long cookie_fpu; + + SDL_MintAudio_hasfpu = 0; + if (Getcookie(C__FPU, &cookie_fpu) != C_FOUND) { + return; + } + switch ((cookie_fpu>>16)&0xfffe) { + case 2: + case 4: + case 6: + case 8: + case 16: + SDL_MintAudio_hasfpu = 1; + break; + } +} + +/* The thread function, used under MiNT with xbios */ +int SDL_MintAudio_Thread(long param) +{ + SndBufPtr pointers; + SDL_bool buffers_filled[2] = {SDL_FALSE, SDL_FALSE}; + + SDL_MintAudio_thread_finished = SDL_FALSE; + while (!SDL_MintAudio_quit_thread) { + if (Buffptr(&pointers)!=0) + continue; + + if (( (unsigned long)pointers.play>=(unsigned long)SDL_MintAudio_audiobuf[0]) + && ( (unsigned long)pointers.play<=(unsigned long)SDL_MintAudio_audiobuf[1])) + { + /* DMA is reading buffer #0, setup buffer #1 if not already done */ + if (!buffers_filled[1]) { + SDL_MintAudio_numbuf = 1; + SDL_MintAudio_Callback(); + Setbuffer(0, SDL_MintAudio_audiobuf[1], SDL_MintAudio_audiobuf[1] + SDL_MintAudio_audiosize); + buffers_filled[1]=SDL_TRUE; + buffers_filled[0]=SDL_FALSE; + } + } else { + /* DMA is reading buffer #1, setup buffer #0 if not already done */ + if (!buffers_filled[0]) { + SDL_MintAudio_numbuf = 0; + SDL_MintAudio_Callback(); + Setbuffer(0, SDL_MintAudio_audiobuf[0], SDL_MintAudio_audiobuf[0] + SDL_MintAudio_audiosize); + buffers_filled[0]=SDL_TRUE; + buffers_filled[1]=SDL_FALSE; + } + } + + usleep(100); + } + SDL_MintAudio_thread_finished = SDL_TRUE; + return 0; +} + +void SDL_MintAudio_WaitThread(void) +{ + if (!SDL_MintAudio_mint_present) + return; + + if (SDL_MintAudio_thread_finished) + return; + + SDL_MintAudio_quit_thread = SDL_TRUE; + while (!SDL_MintAudio_thread_finished) { + Syield(); + } +} diff --git a/src/audio/mint/SDL_mintaudio.h b/src/audio/mint/SDL_mintaudio.h new file mode 100644 index 0000000..859e7ba --- /dev/null +++ b/src/audio/mint/SDL_mintaudio.h @@ -0,0 +1,157 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + MiNT audio driver + + Patrice Mandin +*/ + +#ifndef _SDL_mintaudio_h +#define _SDL_mintaudio_h + +#include "../SDL_sysaudio.h" +#include "SDL_mintaudio_stfa.h" + +/* Hidden "this" pointer for the audio functions */ +#define _THIS SDL_AudioDevice *this + +/* 16 predivisors with 3 clocks max. */ +#define MINTAUDIO_maxfreqs (16*3) + +typedef struct { + Uint32 frequency; + Uint32 masterclock; + Uint32 predivisor; + int gpio_bits; /* in case of external clock */ +} mint_frequency_t; + +struct SDL_PrivateAudioData { + mint_frequency_t frequencies[MINTAUDIO_maxfreqs]; + int freq_count; /* Number of frequencies in the array */ + int numfreq; /* Number of selected frequency */ +}; + +/* Old variable names */ + +#define MINTAUDIO_frequencies (this->hidden->frequencies) +#define MINTAUDIO_freqcount (this->hidden->freq_count) +#define MINTAUDIO_numfreq (this->hidden->numfreq) + +/* _MCH cookie (values>>16) */ +enum { + MCH_ST=0, + MCH_STE, + MCH_TT, + MCH_F30, + MCH_CLONE, + MCH_ARANYM +}; + +/* Master clocks for replay frequencies */ +#define MASTERCLOCK_STE 8010666 /* Not sure of this one */ +#define MASTERCLOCK_TT 16107953 /* Not sure of this one */ +#define MASTERCLOCK_FALCON1 25175000 +#define MASTERCLOCK_FALCON2 32000000 /* Only usable for DSP56K */ +#define MASTERCLOCK_FALCONEXT -1 /* Clock on DSP56K port, unknown */ +#define MASTERCLOCK_44K 22579200 /* Standard clock for 44.1 Khz */ +#define MASTERCLOCK_48K 24576000 /* Standard clock for 48 Khz */ + +/* Master clock predivisors */ +#define MASTERPREDIV_STE 160 +#define MASTERPREDIV_TT 320 +#define MASTERPREDIV_FALCON 256 +#define MASTERPREDIV_MILAN 256 + +/* MFP 68901 interrupt sources */ +#ifndef MFP_PARALLEL +enum { + MFP_PARALLEL=0, + MFP_DCD, + MFP_CTS, + MFP_BITBLT, + MFP_TIMERD, + MFP_BAUDRATE=MFP_TIMERD, + MFP_TIMERC, + MFP_200HZ=MFP_TIMERC, + MFP_ACIA, + MFP_DISK, + MFP_TIMERB, + MFP_HBLANK=MFP_TIMERB, + MFP_TERR, + MFP_TBE, + MFP_RERR, + MFP_RBF, + MFP_TIMERA, + MFP_DMASOUND=MFP_TIMERA, + MFP_RING, + MFP_MONODETECT +}; +#endif + +/* Xbtimer() timers */ +#ifndef XB_TIMERA +enum { + XB_TIMERA=0, + XB_TIMERB, + XB_TIMERC, + XB_TIMERD +}; +#endif + +/* Variables */ +extern SDL_AudioDevice *SDL_MintAudio_device; +extern Uint8 *SDL_MintAudio_audiobuf[2]; /* Pointers to buffers */ +extern unsigned long SDL_MintAudio_audiosize; /* Length of audio buffer=spec->size */ +extern volatile unsigned short SDL_MintAudio_numbuf; /* Buffer to play */ +extern volatile unsigned short SDL_MintAudio_mutex; +extern cookie_stfa_t *SDL_MintAudio_stfa; +extern volatile unsigned long SDL_MintAudio_clocktics; +extern unsigned short SDL_MintAudio_hasfpu; /* To preserve fpu registers if needed */ + +/* MiNT thread variables */ +extern SDL_bool SDL_MintAudio_mint_present; +extern SDL_bool SDL_MintAudio_quit_thread; +extern SDL_bool SDL_MintAudio_thread_finished; +extern long SDL_MintAudio_thread_pid; + +/* Functions */ +void SDL_MintAudio_Callback(void); +void SDL_MintAudio_AddFrequency(_THIS, Uint32 frequency, Uint32 clock, + Uint32 prediv, int gpio_bits); +int SDL_MintAudio_SearchFrequency(_THIS, int desired_freq); +void SDL_MintAudio_CheckFpu(void); + +/* MiNT thread functions */ +int SDL_MintAudio_Thread(long param); +void SDL_MintAudio_WaitThread(void); + +/* ASM interrupt functions */ +void SDL_MintAudio_GsxbInterrupt(void); +void SDL_MintAudio_EmptyGsxbInterrupt(void); +void SDL_MintAudio_XbiosInterruptMeasureClock(void); +void SDL_MintAudio_XbiosInterrupt(void); +void SDL_MintAudio_Dma8Interrupt(void); +void SDL_MintAudio_StfaInterrupt(void); + +#endif /* _SDL_mintaudio_h */ diff --git a/src/audio/mint/SDL_mintaudio_dma8.c b/src/audio/mint/SDL_mintaudio_dma8.c new file mode 100644 index 0000000..94be6a5 --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_dma8.c @@ -0,0 +1,361 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + MiNT audio driver + using DMA 8bits (hardware access) + + Patrice Mandin +*/ + +/* Mint includes */ +#include <mint/osbind.h> +#include <mint/falcon.h> +#include <mint/cookie.h> + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" + +#include "../../video/ataricommon/SDL_atarimxalloc_c.h" + +#include "SDL_mintaudio.h" +#include "SDL_mintaudio_dma8.h" + +/*--- Defines ---*/ + +#define MINT_AUDIO_DRIVER_NAME "mint_dma8" + +/* Debug print info */ +#define DEBUG_NAME "audio:dma8: " +#if 0 +#define DEBUG_PRINT(what) \ + { \ + printf what; \ + } +#else +#define DEBUG_PRINT(what) +#endif + +/*--- Static variables ---*/ + +static unsigned long cookie_snd, cookie_mch; + +/*--- Audio driver functions ---*/ + +static void Mint_CloseAudio(_THIS); +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_LockAudio(_THIS); +static void Mint_UnlockAudio(_THIS); + +/* To check/init hardware audio */ +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec); + +/*--- Audio driver bootstrap functions ---*/ + +static int Audio_Available(void) +{ + const char *envr = SDL_getenv("SDL_AUDIODRIVER"); + + /* Check if user asked a different audio driver */ + if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) { + DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n")); + return 0; + } + + /* Cookie _MCH present ? if not, assume ST machine */ + if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) { + cookie_mch = MCH_ST; + } + + /* Cookie _SND present ? if not, assume ST machine */ + if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) { + cookie_snd = SND_PSG; + } + + /* Check if we have 8 bits audio */ + if ((cookie_snd & SND_8BIT)==0) { + DEBUG_PRINT((DEBUG_NAME "no 8 bits sound\n")); + return(0); + } + + /* Check if audio is lockable */ + if (cookie_snd & SND_16BIT) { + if (Locksnd()!=1) { + DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n")); + return(0); + } + + Unlocksnd(); + } + + DEBUG_PRINT((DEBUG_NAME "8 bits audio available!\n")); + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = Mint_OpenAudio; + this->CloseAudio = Mint_CloseAudio; + this->LockAudio = Mint_LockAudio; + this->UnlockAudio = Mint_UnlockAudio; + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap MINTAUDIO_DMA8_bootstrap = { + MINT_AUDIO_DRIVER_NAME, "MiNT DMA 8 bits audio driver", + Audio_Available, Audio_CreateDevice +}; + +static void Mint_LockAudio(_THIS) +{ + void *oldpile; + + /* Stop replay */ + oldpile=(void *)Super(0); + DMAAUDIO_IO.control=0; + Super(oldpile); +} + +static void Mint_UnlockAudio(_THIS) +{ + void *oldpile; + + /* Restart replay */ + oldpile=(void *)Super(0); + DMAAUDIO_IO.control=3; + Super(oldpile); +} + +static void Mint_CloseAudio(_THIS) +{ + void *oldpile; + + /* Stop replay */ + oldpile=(void *)Super(0); + DMAAUDIO_IO.control=0; + Super(oldpile); + + DEBUG_PRINT((DEBUG_NAME "closeaudio: replay stopped\n")); + + /* Disable interrupt */ + Jdisint(MFP_DMASOUND); + + DEBUG_PRINT((DEBUG_NAME "closeaudio: interrupt disabled\n")); + + /* Wait if currently playing sound */ + while (SDL_MintAudio_mutex != 0) { + } + + DEBUG_PRINT((DEBUG_NAME "closeaudio: no more interrupt running\n")); + + /* Clear buffers */ + if (SDL_MintAudio_audiobuf[0]) { + Mfree(SDL_MintAudio_audiobuf[0]); + SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL; + } + + DEBUG_PRINT((DEBUG_NAME "closeaudio: buffers freed\n")); +} + +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec) +{ + int i, masterprediv, sfreq; + unsigned long masterclock; + + DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + if (spec->channels > 2) + spec->channels = 2; + + /* Check formats available */ + spec->format = AUDIO_S8; + + /* Calculate and select the closest frequency */ + sfreq=0; + masterclock=MASTERCLOCK_STE; + masterprediv=MASTERPREDIV_STE; + switch(cookie_mch>>16) { +/* + case MCH_STE: + masterclock=MASTERCLOCK_STE; + masterprediv=MASTERPREDIV_STE; + break; +*/ + case MCH_TT: + masterclock=MASTERCLOCK_TT; + masterprediv=MASTERPREDIV_TT; + break; + case MCH_F30: + case MCH_ARANYM: + masterclock=MASTERCLOCK_FALCON1; + masterprediv=MASTERPREDIV_FALCON; + sfreq=1; + break; + } + + MINTAUDIO_freqcount=0; + for (i=sfreq;i<4;i++) { + SDL_MintAudio_AddFrequency(this, masterclock/(masterprediv*(1<<i)), + masterclock, i-sfreq, -1); + } + +#if 1 + for (i=0; i<MINTAUDIO_freqcount; i++) { + DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n", + i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock, + MINTAUDIO_frequencies[i].predivisor + )); + } +#endif + + MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq); + spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency; + + DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + return 0; +} + +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec) +{ + void *oldpile; + unsigned long buffer; + unsigned char mode; + + /* Set replay tracks */ + if (cookie_snd & SND_16BIT) { + Settracks(0,0); + Setmontracks(0); + } + + oldpile=(void *)Super(0); + + /* Stop currently playing sound */ + DMAAUDIO_IO.control=0; + + /* Set buffer */ + buffer = (unsigned long) SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf]; + DMAAUDIO_IO.start_high = (buffer>>16) & 255; + DMAAUDIO_IO.start_mid = (buffer>>8) & 255; + DMAAUDIO_IO.start_low = buffer & 255; + + buffer += SDL_MintAudio_audiosize; + DMAAUDIO_IO.end_high = (buffer>>16) & 255; + DMAAUDIO_IO.end_mid = (buffer>>8) & 255; + DMAAUDIO_IO.end_low = buffer & 255; + + mode = 3-MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor; + if (spec->channels==1) { + mode |= 1<<7; + } + DMAAUDIO_IO.sound_ctrl = mode; + + /* Set interrupt */ + Jdisint(MFP_DMASOUND); + Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_Dma8Interrupt); + Jenabint(MFP_DMASOUND); + + if (cookie_snd & SND_16BIT) { + if (Setinterrupt(SI_TIMERA, SI_PLAY)<0) { + DEBUG_PRINT((DEBUG_NAME "Setinterrupt() failed\n")); + } + } + + /* Go */ + DMAAUDIO_IO.control = 3; /* playback + repeat */ + + Super(oldpile); +} + +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + SDL_MintAudio_device = this; + + /* Check audio capabilities */ + if (Mint_CheckAudio(this, spec)==-1) { + return -1; + } + + SDL_CalculateAudioSpec(spec); + + /* Allocate memory for audio buffers in DMA-able RAM */ + DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size)); + + SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM); + if (SDL_MintAudio_audiobuf[0]==NULL) { + SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer"); + return (-1); + } + SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ; + SDL_MintAudio_numbuf=0; + SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2); + SDL_MintAudio_audiosize = spec->size; + SDL_MintAudio_mutex = 0; + + DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0])); + DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1])); + + SDL_MintAudio_CheckFpu(); + + /* Setup audio hardware */ + Mint_InitAudio(this, spec); + + return(1); /* We don't use threaded audio */ +} diff --git a/src/audio/mint/SDL_mintaudio_dma8.h b/src/audio/mint/SDL_mintaudio_dma8.h new file mode 100644 index 0000000..9ae010b --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_dma8.h @@ -0,0 +1,85 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + DMA 8bits and Falcon Codec audio definitions + + Patrice Mandin, Didier Méquignon +*/ + +#ifndef _SDL_mintaudio_dma8_h +#define _SDL_mintaudio_dma8_h + +#define DMAAUDIO_IO_BASE (0xffff8900) +struct DMAAUDIO_IO_S { + unsigned char int_ctrl; + unsigned char control; + + unsigned char dummy1; + unsigned char start_high; + unsigned char dummy2; + unsigned char start_mid; + unsigned char dummy3; + unsigned char start_low; + + unsigned char dummy4; + unsigned char cur_high; + unsigned char dummy5; + unsigned char cur_mid; + unsigned char dummy6; + unsigned char cur_low; + + unsigned char dummy7; + unsigned char end_high; + unsigned char dummy8; + unsigned char end_mid; + unsigned char dummy9; + unsigned char end_low; + + unsigned char dummy10[12]; + + unsigned char track_ctrl; /* CODEC only */ + unsigned char sound_ctrl; + unsigned short sound_data; + unsigned short sound_mask; + + unsigned char dummy11[10]; + + unsigned short dev_ctrl; + unsigned short dest_ctrl; + unsigned short sync_div; + unsigned char track_rec; + unsigned char adderin_input; + unsigned char channel_input; + unsigned char channel_amplification; + unsigned char channel_reduction; + + unsigned char dummy12[6]; + + unsigned char data_direction; + unsigned char dummy13; + unsigned char dev_data; +}; +#define DMAAUDIO_IO ((*(volatile struct DMAAUDIO_IO_S *)DMAAUDIO_IO_BASE)) + +#endif /* _SDL_mintaudio_dma8_h */ diff --git a/src/audio/mint/SDL_mintaudio_gsxb.c b/src/audio/mint/SDL_mintaudio_gsxb.c new file mode 100644 index 0000000..58ddc9f --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_gsxb.c @@ -0,0 +1,436 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + MiNT audio driver + using XBIOS functions (GSXB compatible driver) + + Patrice Mandin +*/ + +/* Mint includes */ +#include <mint/osbind.h> +#include <mint/falcon.h> +#include <mint/cookie.h> + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" + +#include "../../video/ataricommon/SDL_atarimxalloc_c.h" + +#include "SDL_mintaudio.h" +#include "SDL_mintaudio_gsxb.h" + +/*--- Defines ---*/ + +#define MINT_AUDIO_DRIVER_NAME "mint_gsxb" + +/* Debug print info */ +#define DEBUG_NAME "audio:gsxb: " +#if 0 +#define DEBUG_PRINT(what) \ + { \ + printf what; \ + } +#else +#define DEBUG_PRINT(what) +#endif + +/*--- Static variables ---*/ + +static unsigned long cookie_snd, cookie_gsxb; + +/*--- Audio driver functions ---*/ + +static void Mint_CloseAudio(_THIS); +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_LockAudio(_THIS); +static void Mint_UnlockAudio(_THIS); + +/* To check/init hardware audio */ +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec); + +/* GSXB callbacks */ +static void Mint_GsxbInterrupt(void); +static void Mint_GsxbNullInterrupt(void); + +/*--- Audio driver bootstrap functions ---*/ + +static int Audio_Available(void) +{ + const char *envr = SDL_getenv("SDL_AUDIODRIVER"); + + /* Check if user asked a different audio driver */ + if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) { + DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n")); + return(0); + } + + /* Cookie _SND present ? if not, assume ST machine */ + if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) { + cookie_snd = SND_PSG; + } + + /* Check if we have 16 bits audio */ + if ((cookie_snd & SND_16BIT)==0) { + DEBUG_PRINT((DEBUG_NAME "no 16 bits sound\n")); + return(0); + } + + /* Cookie GSXB present ? */ + cookie_gsxb = (Getcookie(C_GSXB, &cookie_gsxb) == C_FOUND); + + /* Is it GSXB ? */ + if (((cookie_snd & SND_GSXB)==0) || (cookie_gsxb==0)) { + DEBUG_PRINT((DEBUG_NAME "no GSXB audio\n")); + return(0); + } + + /* Check if audio is lockable */ + if (Locksnd()!=1) { + DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n")); + return(0); + } + + Unlocksnd(); + + DEBUG_PRINT((DEBUG_NAME "GSXB audio available!\n")); + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = Mint_OpenAudio; + this->CloseAudio = Mint_CloseAudio; + this->LockAudio = Mint_LockAudio; + this->UnlockAudio = Mint_UnlockAudio; + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap MINTAUDIO_GSXB_bootstrap = { + MINT_AUDIO_DRIVER_NAME, "MiNT GSXB audio driver", + Audio_Available, Audio_CreateDevice +}; + +static void Mint_LockAudio(_THIS) +{ + /* Stop replay */ + Buffoper(0); +} + +static void Mint_UnlockAudio(_THIS) +{ + /* Restart replay */ + Buffoper(SB_PLA_ENA|SB_PLA_RPT); +} + +static void Mint_CloseAudio(_THIS) +{ + /* Stop replay */ + Buffoper(0); + + /* Uninstall interrupt */ + if (NSetinterrupt(2, SI_NONE, Mint_GsxbNullInterrupt)<0) { + DEBUG_PRINT((DEBUG_NAME "NSetinterrupt() failed in close\n")); + } + + /* Wait if currently playing sound */ + while (SDL_MintAudio_mutex != 0) { + } + + /* Clear buffers */ + if (SDL_MintAudio_audiobuf[0]) { + Mfree(SDL_MintAudio_audiobuf[0]); + SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL; + } + + /* Unlock sound system */ + Unlocksnd(); +} + +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec) +{ + long snd_format; + int i, resolution, format_signed, format_bigendian; + Uint16 test_format = SDL_FirstAudioFormat(spec->format); + int valid_datatype = 0; + + resolution = spec->format & 0x00ff; + format_signed = ((spec->format & 0x8000)!=0); + format_bigendian = ((spec->format & 0x1000)!=0); + + DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + if (spec->channels > 2) { + spec->channels = 2; /* no more than stereo! */ + } + + while ((!valid_datatype) && (test_format)) { + /* Check formats available */ + snd_format = Sndstatus(SND_QUERYFORMATS); + spec->format = test_format; + resolution = spec->format & 0xff; + format_signed = (spec->format & (1<<15)); + format_bigendian = (spec->format & (1<<12)); + switch (test_format) { + case AUDIO_U8: + case AUDIO_S8: + if (snd_format & SND_FORMAT8) { + valid_datatype = 1; + snd_format = Sndstatus(SND_QUERY8BIT); + } + break; + + case AUDIO_U16LSB: + case AUDIO_S16LSB: + case AUDIO_U16MSB: + case AUDIO_S16MSB: + if (snd_format & SND_FORMAT16) { + valid_datatype = 1; + snd_format = Sndstatus(SND_QUERY16BIT); + } + break; + + default: + test_format = SDL_NextAudioFormat(); + break; + } + } + + if (!valid_datatype) { + SDL_SetError("Unsupported audio format"); + return (-1); + } + + /* Check signed/unsigned format */ + if (format_signed) { + if (snd_format & SND_FORMATSIGNED) { + /* Ok */ + } else if (snd_format & SND_FORMATUNSIGNED) { + /* Give unsigned format */ + spec->format = spec->format & (~0x8000); + } + } else { + if (snd_format & SND_FORMATUNSIGNED) { + /* Ok */ + } else if (snd_format & SND_FORMATSIGNED) { + /* Give signed format */ + spec->format |= 0x8000; + } + } + + if (format_bigendian) { + if (snd_format & SND_FORMATBIGENDIAN) { + /* Ok */ + } else if (snd_format & SND_FORMATLITTLEENDIAN) { + /* Give little endian format */ + spec->format = spec->format & (~0x1000); + } + } else { + if (snd_format & SND_FORMATLITTLEENDIAN) { + /* Ok */ + } else if (snd_format & SND_FORMATBIGENDIAN) { + /* Give big endian format */ + spec->format |= 0x1000; + } + } + + /* Calculate and select the closest frequency */ + MINTAUDIO_freqcount=0; + for (i=1;i<4;i++) { + SDL_MintAudio_AddFrequency(this, + MASTERCLOCK_44K/(MASTERPREDIV_MILAN*(1<<i)), MASTERCLOCK_44K, + (1<<i)-1, -1); + } + +#if 1 + for (i=0; i<MINTAUDIO_freqcount; i++) { + DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n", + i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock, + MINTAUDIO_frequencies[i].predivisor + )); + } +#endif + + MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq); + spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency; + + DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + return 0; +} + +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec) +{ + int channels_mode, prediv; + void *buffer; + + /* Stop currently playing sound */ + Buffoper(0); + + /* Set replay tracks */ + Settracks(0,0); + Setmontracks(0); + + /* Select replay format */ + switch (spec->format & 0xff) { + case 8: + if (spec->channels==2) { + channels_mode=STEREO8; + } else { + channels_mode=MONO8; + } + break; + case 16: + if (spec->channels==2) { + channels_mode=STEREO16; + } else { + channels_mode=MONO16; + } + break; + default: + channels_mode=STEREO16; + break; + } + if (Setmode(channels_mode)<0) { + DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n")); + } + + prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor; + Devconnect(DMAPLAY, DAC, CLKEXT, prediv, 1); + + /* Set buffer */ + buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf]; + if (Setbuffer(0, buffer, buffer + spec->size)<0) { + DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n")); + } + + /* Install interrupt */ + if (NSetinterrupt(2, SI_PLAY, Mint_GsxbInterrupt)<0) { + DEBUG_PRINT((DEBUG_NAME "NSetinterrupt() failed\n")); + } + + /* Go */ + Buffoper(SB_PLA_ENA|SB_PLA_RPT); + DEBUG_PRINT((DEBUG_NAME "hardware initialized\n")); +} + +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + /* Lock sound system */ + if (Locksnd()!=1) { + SDL_SetError("Mint_OpenAudio: Audio system already in use"); + return(-1); + } + + SDL_MintAudio_device = this; + + /* Check audio capabilities */ + if (Mint_CheckAudio(this, spec)==-1) { + return -1; + } + + SDL_CalculateAudioSpec(spec); + + /* Allocate memory for audio buffers in DMA-able RAM */ + DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size)); + + SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM); + if (SDL_MintAudio_audiobuf[0]==NULL) { + SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer"); + return (-1); + } + SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ; + SDL_MintAudio_numbuf=0; + SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2); + SDL_MintAudio_audiosize = spec->size; + SDL_MintAudio_mutex = 0; + + DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0])); + DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1])); + + SDL_MintAudio_CheckFpu(); + + /* Setup audio hardware */ + Mint_InitAudio(this, spec); + + return(1); /* We don't use threaded audio */ +} + +static void Mint_GsxbInterrupt(void) +{ + Uint8 *newbuf; + + if (SDL_MintAudio_mutex) + return; + + SDL_MintAudio_mutex=1; + + SDL_MintAudio_numbuf ^= 1; + SDL_MintAudio_Callback(); + newbuf = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf]; + Setbuffer(0, newbuf, newbuf + SDL_MintAudio_audiosize); + + SDL_MintAudio_mutex=0; +} + +static void Mint_GsxbNullInterrupt(void) +{ +} diff --git a/src/audio/mint/SDL_mintaudio_gsxb.h b/src/audio/mint/SDL_mintaudio_gsxb.h new file mode 100644 index 0000000..7c38288 --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_gsxb.h @@ -0,0 +1,108 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + * GSXB audio definitions + * + * Patrice Mandin + */ + +#ifndef _SDL_mintaudio_gsxb_h +#define _SDL_mintaudio_gsxb_h + +#include <mint/falcon.h> /* for trap_14_xxx macros */ + +/* GSXB Cookie */ + +#define C_GSXB 0x47535842L + +/* Bit 5 in cookie _SND */ + +#define SND_GSXB (1<<5) + +/* NSoundcmd modes */ + +#define SETRATE 7 /* Set sample rate */ +#define SET8BITFORMAT 8 /* 8 bits format */ +#define SET16BITFORMAT 9 /* 16 bits format */ +#define SET24BITFORMAT 10 /* 24 bits format */ +#define SET32BITFORMAT 11 /* 32 bits format */ +#define LTATTEN_MASTER 12 /* Attenuation */ +#define RTATTEN_MASTER 13 +#define LTATTEN_MICIN 14 +#define RTATTEN_MICIN 15 +#define LTATTEN_FMGEN 16 +#define RTATTEN_FMGEN 17 +#define LTATTEN_LINEIN 18 +#define RTATTEN_LINEIN 19 +#define LTATTEN_CDIN 20 +#define RTATTEN_CDIN 21 +#define LTATTEN_VIDIN 22 +#define RTATTEN_VIDIN 23 +#define LTATTEN_AUXIN 24 +#define RTATTEN_AUXIN 25 + +/* Setmode modes */ + +#define MONO16 3 +#define STEREO24 4 +#define STEREO32 5 +#define MONO24 6 +#define MONO32 7 + +/* Sndstatus modes */ + +#define SND_QUERYFORMATS 2 +#define SND_QUERYMIXERS 3 +#define SND_QUERYSOURCES 4 +#define SND_QUERYDUPLEX 5 +#define SND_QUERY8BIT 8 +#define SND_QUERY16BIT 9 +#define SND_QUERY24BIT 10 +#define SND_QUERY32BIT 11 + +#define SND_FORMAT8 (1<<0) +#define SND_FORMAT16 (1<<1) +#define SND_FORMAT24 (1<<2) +#define SND_FORMAT32 (1<<3) + +#define SND_FORMATSIGNED (1<<0) +#define SND_FORMATUNSIGNED (1<<1) +#define SND_FORMATBIGENDIAN (1<<2) +#define SND_FORMATLITTLEENDIAN (1<<3) + +/* Devconnect prescalers */ + +#define CLK_44K 1 +#define CLK_22K 3 +#define CLK_11K 7 + +/* Extra xbios functions */ + +#define NSoundcmd(mode,data,data2) \ + (long)trap_14_wwl((short)130,(short)(mode),(short)(data),(long)(data2)) +#define NSetinterrupt(src_inter,cause,inth_addr) \ + (long)trap_14_wwwl((short)135,(short)(src_inter),(short)(cause), \ + (long)(inth_addr)) + +#endif /* _SDL_mintaudio_gsxb_h */ diff --git a/src/audio/mint/SDL_mintaudio_it.S b/src/audio/mint/SDL_mintaudio_it.S new file mode 100644 index 0000000..f2d36e8 --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_it.S @@ -0,0 +1,281 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ + +/* + Audio interrupts + + Patrice Mandin, Didier Méquignon + */ + + .text + + .globl _SDL_MintAudio_Callback + + .globl _SDL_MintAudio_XbiosInterrupt + .globl _SDL_MintAudio_XbiosInterruptMeasureClock + .globl _SDL_MintAudio_Dma8Interrupt + .globl _SDL_MintAudio_StfaInterrupt + + .globl _SDL_MintAudio_mutex + .globl _SDL_MintAudio_audiobuf + .globl _SDL_MintAudio_numbuf + .globl _SDL_MintAudio_audiosize + .globl _SDL_MintAudio_clocktics + .globl _SDL_MintAudio_hasfpu + + .globl _SDL_MintAudio_stfa + +/* + How it works: + - Audio is playing buffer #0 (resp. #1) + - We must calculate a sample in buffer #1 (resp. #0) + so we first call the callback to do it + - Then we swap the buffers +*/ + +#define savptr 0x4a2 +#define savamt 0x46 + +/*--- Xbios interrupt vector to measure Falcon external clock ---*/ + +_SDL_MintAudio_XbiosInterruptMeasureClock: /* 1 mS */ + + btst #0,0xFFFF8901:w /* state DMA sound */ + beqs SDL_MintAudio_EndIntMeasure + addql #1,_SDL_MintAudio_clocktics +SDL_MintAudio_EndIntMeasure: + bclr #5,0xFFFFFA0F:w /* Clear service bit */ + rte + +/*--- Xbios interrupt vector ---*/ + +_SDL_MintAudio_XbiosInterrupt: + + /* Reenable interrupts, so other interrupts can work */ + movew #0x2300,sr + + /* Clear service bit, so other MFP interrupts can work */ + bclr #5,0xfffffa0f:w + + /* Check if we are not already running */ + tstw _SDL_MintAudio_mutex + bne SDL_MintAudio_XbiosEnd + notw _SDL_MintAudio_mutex + + /* Swap buffers */ + eorw #1,_SDL_MintAudio_numbuf + + moveml d0-d7/a0-a6,sp@- + + /* Save FPU if needed */ + tstw _SDL_MintAudio_hasfpu + beqs SDL_MintAudio_Xbios_nofpu1 + .chip 68060 + fsave sp@- + fmoveml fpcr/fpsr/fpiar,sp@- + fmovemx fp0-fp7,sp@- + .chip 68000 +SDL_MintAudio_Xbios_nofpu1: + + /* Callback */ + jsr _SDL_MintAudio_Callback + + /* Restore FPU if needed */ + tstw _SDL_MintAudio_hasfpu + beqs SDL_MintAudio_Xbios_nofpu2 + .chip 68060 + fmovemx sp@+,fp0-fp7 + fmoveml sp@+,fpcr/fpsr/fpiar + frestore sp@+ + .chip 68000 +SDL_MintAudio_Xbios_nofpu2: + + /* Reserve space for registers */ + subl #savamt,savptr + + /* Set new buffer */ + + moveq #0,d0 + movel _SDL_MintAudio_audiosize,d1 + + movew _SDL_MintAudio_numbuf,d0 + lsll #2,d0 + lea _SDL_MintAudio_audiobuf,a0 + movel a0@(d0:l),a1 + + lea a1@(d1:l),a2 + + movel a2,sp@- + movel a1,sp@- + clrw sp@- + movew #131,sp@- + trap #14 + lea sp@(12),sp + + /* Restore registers space */ + addl #savamt,savptr + + moveml sp@+,d0-d7/a0-a6 + + clrw _SDL_MintAudio_mutex +SDL_MintAudio_XbiosEnd: + rte + +/*--- DMA 8 bits interrupt vector ---*/ + +_SDL_MintAudio_Dma8Interrupt: + + /* Reenable interrupts, so other interrupts can work */ + movew #0x2300,sr + + /* Clear service bit, so other MFP interrupts can work */ + bclr #5,0xfffffa0f:w + + /* Check if we are not already running */ + tstw _SDL_MintAudio_mutex + bne SDL_MintAudio_Dma8End + notw _SDL_MintAudio_mutex + + /* Swap buffers */ + eorw #1,_SDL_MintAudio_numbuf + + moveml d0-d1/a0-a1,sp@- + + /* Save FPU if needed */ + tstw _SDL_MintAudio_hasfpu + beqs SDL_MintAudio_Dma8_nofpu1 + .chip 68060 + fsave sp@- + fmoveml fpcr/fpsr/fpiar,sp@- + fmovemx fp0-fp7,sp@- + .chip 68000 +SDL_MintAudio_Dma8_nofpu1: + + /* Callback */ + jsr _SDL_MintAudio_Callback + + /* Restore FPU if needed */ + tstw _SDL_MintAudio_hasfpu + beqs SDL_MintAudio_Dma8_nofpu2 + .chip 68060 + fmovemx sp@+,fp0-fp7 + fmoveml sp@+,fpcr/fpsr/fpiar + frestore sp@+ + .chip 68000 +SDL_MintAudio_Dma8_nofpu2: + + /* Set new buffer */ + + moveq #0,d0 + + movew _SDL_MintAudio_numbuf,d0 + lslw #2,d0 + lea _SDL_MintAudio_audiobuf,a0 + movel a0@(d0:w),d1 + + /* Modify DMA addresses */ + lea 0xffff8900:w,a0 + + moveb d1,a0@(0x07) /* Start address */ + rorl #8,d1 + moveb d1,a0@(0x05) + rorl #8,d1 + moveb d1,a0@(0x03) + swap d1 + + addl _SDL_MintAudio_audiosize,d1 + + moveb d1,a0@(0x13) /* End address */ + rorl #8,d1 + moveb d1,a0@(0x11) + rorl #8,d1 + moveb d1,a0@(0x0f) + + moveml sp@+,d0-d1/a0-a1 + + clrw _SDL_MintAudio_mutex +SDL_MintAudio_Dma8End: + rte + +/*--- STFA interrupt vector ---*/ + +STFA_SOUND_START = 6 +STFA_SOUND_END = STFA_SOUND_START+8 + +_SDL_MintAudio_StfaInterrupt: + + /* Reenable interrupts, so other interrupts can work */ + movew #0x2300,sr + + /* Check if we are not already running */ + tstw _SDL_MintAudio_mutex + bnes SDL_MintAudio_StfaEnd + notw _SDL_MintAudio_mutex + + /* Swap buffers */ + eorw #1,_SDL_MintAudio_numbuf + + moveml d0-d7/a0-a6,sp@- + + /* Save FPU if needed */ + tstw _SDL_MintAudio_hasfpu + beqs SDL_MintAudio_Stfa_nofpu1 + .chip 68060 + fsave sp@- + fmoveml fpcr/fpsr/fpiar,sp@- + fmovemx fp0-fp7,sp@- + .chip 68000 +SDL_MintAudio_Stfa_nofpu1: + + /* Callback */ + jsr _SDL_MintAudio_Callback + + /* Restore FPU if needed */ + tstw _SDL_MintAudio_hasfpu + beqs SDL_MintAudio_Stfa_nofpu2 + .chip 68060 + fmovemx sp@+,fp0-fp7 + fmoveml sp@+,fpcr/fpsr/fpiar + frestore sp@+ + .chip 68000 +SDL_MintAudio_Stfa_nofpu2: + + /* Set new buffer */ + + moveq #0,d0 + movel _SDL_MintAudio_stfa,a1 + + movew _SDL_MintAudio_numbuf,d0 + lslw #2,d0 + lea _SDL_MintAudio_audiobuf,a0 + movel a0@(d0:w),d1 + + /* Modify STFA replay buffers */ + movel d1,a1@(STFA_SOUND_START) + addl _SDL_MintAudio_audiosize,d1 + movel d1,a1@(STFA_SOUND_END) + + moveml sp@+,d0-d7/a0-a6 + + clrw _SDL_MintAudio_mutex +SDL_MintAudio_StfaEnd: + rte diff --git a/src/audio/mint/SDL_mintaudio_mcsn.c b/src/audio/mint/SDL_mintaudio_mcsn.c new file mode 100644 index 0000000..9376902 --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_mcsn.c @@ -0,0 +1,404 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + MiNT audio driver + using XBIOS functions (MacSound compatible driver) + + Patrice Mandin +*/ + +#include <support.h> + +/* Mint includes */ +#include <mint/osbind.h> +#include <mint/falcon.h> +#include <mint/cookie.h> + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" + +#include "../../video/ataricommon/SDL_atarimxalloc_c.h" + +#include "SDL_mintaudio.h" +#include "SDL_mintaudio_mcsn.h" + +/*--- Defines ---*/ + +#define MINT_AUDIO_DRIVER_NAME "mint_mcsn" + +/* Debug print info */ +#define DEBUG_NAME "audio:mcsn: " +#if 0 +#define DEBUG_PRINT(what) \ + { \ + printf what; \ + } +#else +#define DEBUG_PRINT(what) +#endif + +/*--- Static variables ---*/ + +static unsigned long cookie_snd, cookie_mch; +static cookie_mcsn_t *cookie_mcsn; + +/*--- Audio driver functions ---*/ + +static void Mint_CloseAudio(_THIS); +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_LockAudio(_THIS); +static void Mint_UnlockAudio(_THIS); + +/* To check/init hardware audio */ +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec); + +/*--- Audio driver bootstrap functions ---*/ + +static int Audio_Available(void) +{ + unsigned long dummy; + const char *envr = SDL_getenv("SDL_AUDIODRIVER"); + + SDL_MintAudio_mint_present = (Getcookie(C_MiNT, &dummy) == C_FOUND); + + /* We can't use XBIOS in interrupt with Magic, don't know about thread */ + if (Getcookie(C_MagX, &dummy) == C_FOUND) { + return(0); + } + + /* Check if user asked a different audio driver */ + if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) { + DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n")); + return(0); + } + + /* Cookie _MCH present ? if not, assume ST machine */ + if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) { + cookie_mch = MCH_ST; + } + + /* Cookie _SND present ? if not, assume ST machine */ + if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) { + cookie_snd = SND_PSG; + } + + /* Check if we have 16 bits audio */ + if ((cookie_snd & SND_16BIT)==0) { + DEBUG_PRINT((DEBUG_NAME "no 16 bits sound\n")); + return(0); + } + + /* Cookie MCSN present ? */ + if (Getcookie(C_McSn, (long *) &cookie_mcsn) != C_FOUND) { + DEBUG_PRINT((DEBUG_NAME "no MCSN audio\n")); + return(0); + } + + /* Check if interrupt at end of replay */ + if (cookie_mcsn->pint == 0) { + DEBUG_PRINT((DEBUG_NAME "no interrupt at end of replay\n")); + return(0); + } + + /* Check if audio is lockable */ + if (Locksnd()!=1) { + DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n")); + return(0); + } + + Unlocksnd(); + + DEBUG_PRINT((DEBUG_NAME "MCSN audio available!\n")); + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = Mint_OpenAudio; + this->CloseAudio = Mint_CloseAudio; + this->LockAudio = Mint_LockAudio; + this->UnlockAudio = Mint_UnlockAudio; + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap MINTAUDIO_MCSN_bootstrap = { + MINT_AUDIO_DRIVER_NAME, "MiNT MCSN audio driver", + Audio_Available, Audio_CreateDevice +}; + +static void Mint_LockAudio(_THIS) +{ + /* Stop replay */ + Buffoper(0); +} + +static void Mint_UnlockAudio(_THIS) +{ + /* Restart replay */ + Buffoper(SB_PLA_ENA|SB_PLA_RPT); +} + +static void Mint_CloseAudio(_THIS) +{ + /* Stop replay */ + SDL_MintAudio_WaitThread(); + Buffoper(0); + + if (!SDL_MintAudio_mint_present) { + /* Uninstall interrupt */ + Jdisint(MFP_DMASOUND); + } + + /* Wait if currently playing sound */ + while (SDL_MintAudio_mutex != 0) { + } + + /* Clear buffers */ + if (SDL_MintAudio_audiobuf[0]) { + Mfree(SDL_MintAudio_audiobuf[0]); + SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL; + } + + /* Unlock sound system */ + Unlocksnd(); +} + +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec) +{ + int i; + unsigned long masterclock, masterprediv; + + DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + if (spec->channels > 2) { + spec->channels = 2; /* no more than stereo! */ + } + + /* Check formats available */ + MINTAUDIO_freqcount=0; + switch(cookie_mcsn->play) { + case MCSN_ST: + spec->channels=1; + spec->format=8; /* FIXME: is it signed or unsigned ? */ + SDL_MintAudio_AddFrequency(this, 12500, 0, 0, -1); + break; + case MCSN_TT: /* Also STE, Mega STE */ + spec->format=AUDIO_S8; + masterclock=MASTERCLOCK_STE; + masterprediv=MASTERPREDIV_STE; + if ((cookie_mch>>16)==MCH_TT) { + masterclock=MASTERCLOCK_TT; + masterprediv=MASTERPREDIV_TT; + } + for (i=0; i<4; i++) { + SDL_MintAudio_AddFrequency(this, masterclock/(masterprediv*(1<<i)), + masterclock, 3-i, -1); + } + break; + case MCSN_FALCON: /* Also Mac */ + for (i=1; i<12; i++) { + /* Remove unusable Falcon codec predivisors */ + if ((i==6) || (i==8) || (i==10)) { + continue; + } + SDL_MintAudio_AddFrequency(this, MASTERCLOCK_FALCON1/(MASTERPREDIV_FALCON*(i+1)), + CLK25M, i+1, -1); + } + if (cookie_mcsn->res1 != 0) { + for (i=1; i<4; i++) { + SDL_MintAudio_AddFrequency(this, (cookie_mcsn->res1)/(MASTERPREDIV_FALCON*(1<<i)), + CLKEXT, (1<<i)-1, -1); + } + } + spec->format |= 0x8000; /* Audio is always signed */ + if ((spec->format & 0x00ff)==16) { + spec->format |= 0x1000; /* Audio is always big endian */ + spec->channels=2; /* 16 bits always stereo */ + } + break; + } + +#if 0 + for (i=0; i<MINTAUDIO_freqcount; i++) { + DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n", + i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock, + MINTAUDIO_frequencies[i].predivisor + )); + } +#endif + + MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq); + spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency; + + DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + return 0; +} + +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec) +{ + int channels_mode, prediv, dmaclock; + void *buffer; + + /* Stop currently playing sound */ + SDL_MintAudio_quit_thread = SDL_FALSE; + SDL_MintAudio_thread_finished = SDL_TRUE; + SDL_MintAudio_WaitThread(); + Buffoper(0); + + /* Set replay tracks */ + Settracks(0,0); + Setmontracks(0); + + /* Select replay format */ + channels_mode=STEREO16; + switch (spec->format & 0xff) { + case 8: + if (spec->channels==2) { + channels_mode=STEREO8; + } else { + channels_mode=MONO8; + } + break; + } + if (Setmode(channels_mode)<0) { + DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n")); + } + + dmaclock = MINTAUDIO_frequencies[MINTAUDIO_numfreq].masterclock; + prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor; + switch(cookie_mcsn->play) { + case MCSN_TT: + Devconnect(DMAPLAY, DAC, CLK25M, CLKOLD, 1); + Soundcmd(SETPRESCALE, prediv); + DEBUG_PRINT((DEBUG_NAME "STE/TT prescaler selected\n")); + break; + case MCSN_FALCON: + Devconnect(DMAPLAY, DAC, dmaclock, prediv, 1); + DEBUG_PRINT((DEBUG_NAME "Falcon prescaler selected\n")); + break; + } + + /* Set buffer */ + buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf]; + if (Setbuffer(0, buffer, buffer + spec->size)<0) { + DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n")); + } + + if (SDL_MintAudio_mint_present) { + SDL_MintAudio_thread_pid = tfork(SDL_MintAudio_Thread, 0); + } else { + /* Install interrupt */ + Jdisint(MFP_DMASOUND); + Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_XbiosInterrupt); + Jenabint(MFP_DMASOUND); + + if (Setinterrupt(SI_TIMERA, SI_PLAY)<0) { + DEBUG_PRINT((DEBUG_NAME "Setinterrupt() failed\n")); + } + } + + /* Go */ + Buffoper(SB_PLA_ENA|SB_PLA_RPT); + DEBUG_PRINT((DEBUG_NAME "hardware initialized\n")); +} + +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + /* Lock sound system */ + if (Locksnd()!=1) { + SDL_SetError("Mint_OpenAudio: Audio system already in use"); + return(-1); + } + + SDL_MintAudio_device = this; + + /* Check audio capabilities */ + if (Mint_CheckAudio(this, spec)==-1) { + return -1; + } + + SDL_CalculateAudioSpec(spec); + + /* Allocate memory for audio buffers in DMA-able RAM */ + DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size)); + + SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM); + if (SDL_MintAudio_audiobuf[0]==NULL) { + SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer"); + return (-1); + } + SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ; + SDL_MintAudio_numbuf=0; + SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2); + SDL_MintAudio_audiosize = spec->size; + SDL_MintAudio_mutex = 0; + + DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0])); + DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1])); + + SDL_MintAudio_CheckFpu(); + + /* Setup audio hardware */ + Mint_InitAudio(this, spec); + + return(1); /* We don't use SDL threaded audio */ +} diff --git a/src/audio/mint/SDL_mintaudio_mcsn.h b/src/audio/mint/SDL_mintaudio_mcsn.h new file mode 100644 index 0000000..18fd59c --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_mcsn.h @@ -0,0 +1,59 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + MCSN control structure + + Patrice Mandin +*/ + +#ifndef _SDL_mintaudio_mcsh_h +#define _SDL_mintaudio_mcsh_h + +typedef struct { + unsigned short version; /* Version */ + unsigned short size; /* Size of structure */ + + unsigned short play; /* Replay capability */ + unsigned short record; /* Record capability */ + unsigned short dsp; /* DSP56K present */ + unsigned short pint; /* Interrupt at end of replay */ + unsigned short rint; /* Interrupt at end of record */ + + unsigned long res1; /* Frequency of external clock */ + unsigned long res2; + unsigned long res3; + unsigned long res4; +} cookie_mcsn_t __attribute__((packed)); + +enum { + MCSN_ST=0, + MCSN_TT, + MCSN_STE=MCSN_TT, + MCSN_FALCON, + MCSN_MAC=MCSN_FALCON +}; + +#define SETSMPFREQ 7 /* Set sample frequency */ + +#endif /* _SDL_mintaudio_mcsh_h */ diff --git a/src/audio/mint/SDL_mintaudio_stfa.c b/src/audio/mint/SDL_mintaudio_stfa.c new file mode 100644 index 0000000..6940f80 --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_stfa.c @@ -0,0 +1,323 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + MiNT audio driver + using XBIOS functions (STFA driver) + + Patrice Mandin +*/ + +/* Mint includes */ +#include <mint/osbind.h> +#include <mint/falcon.h> +#include <mint/cookie.h> + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" + +#include "../../video/ataricommon/SDL_atarimxalloc_c.h" + +#include "SDL_mintaudio.h" +#include "SDL_mintaudio_stfa.h" + +/*--- Defines ---*/ + +#define MINT_AUDIO_DRIVER_NAME "mint_stfa" + +/* Debug print info */ +#define DEBUG_NAME "audio:stfa: " +#if 0 +#define DEBUG_PRINT(what) \ + { \ + printf what; \ + } +#else +#define DEBUG_PRINT(what) +#endif + +/*--- Static variables ---*/ + +static unsigned long cookie_snd, cookie_mch; +static cookie_stfa_t *cookie_stfa; + +static const int freqs[16]={ + 4995, 6269, 7493, 8192, + 9830, 10971, 12538, 14985, + 16384, 19819, 21943, 24576, + 30720, 32336, 43885, 49152 +}; + +/*--- Audio driver functions ---*/ + +static void Mint_CloseAudio(_THIS); +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_LockAudio(_THIS); +static void Mint_UnlockAudio(_THIS); + +/* To check/init hardware audio */ +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec); + +/*--- Audio driver bootstrap functions ---*/ + +static int Audio_Available(void) +{ + const char *envr = SDL_getenv("SDL_AUDIODRIVER"); + + /* Check if user asked a different audio driver */ + if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) { + DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n")); + return(0); + } + + /* Cookie _MCH present ? if not, assume ST machine */ + if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) { + cookie_mch = MCH_ST; + } + + /* Cookie _SND present ? if not, assume ST machine */ + if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) { + cookie_snd = SND_PSG; + } + + /* Cookie STFA present ? */ + if (Getcookie(C_STFA, (long *) &cookie_stfa) != C_FOUND) { + DEBUG_PRINT((DEBUG_NAME "no STFA audio\n")); + return(0); + } + + SDL_MintAudio_stfa = cookie_stfa; + + DEBUG_PRINT((DEBUG_NAME "STFA audio available!\n")); + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = Mint_OpenAudio; + this->CloseAudio = Mint_CloseAudio; + this->LockAudio = Mint_LockAudio; + this->UnlockAudio = Mint_UnlockAudio; + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap MINTAUDIO_STFA_bootstrap = { + MINT_AUDIO_DRIVER_NAME, "MiNT STFA audio driver", + Audio_Available, Audio_CreateDevice +}; + +static void Mint_LockAudio(_THIS) +{ + void *oldpile; + + /* Stop replay */ + oldpile=(void *)Super(0); + cookie_stfa->sound_enable=STFA_PLAY_DISABLE; + Super(oldpile); +} + +static void Mint_UnlockAudio(_THIS) +{ + void *oldpile; + + /* Restart replay */ + oldpile=(void *)Super(0); + cookie_stfa->sound_enable=STFA_PLAY_ENABLE|STFA_PLAY_REPEAT; + Super(oldpile); +} + +static void Mint_CloseAudio(_THIS) +{ + void *oldpile; + + /* Stop replay */ + oldpile=(void *)Super(0); + cookie_stfa->sound_enable=STFA_PLAY_DISABLE; + Super(oldpile); + + /* Wait if currently playing sound */ + while (SDL_MintAudio_mutex != 0) { + } + + /* Clear buffers */ + if (SDL_MintAudio_audiobuf[0]) { + Mfree(SDL_MintAudio_audiobuf[0]); + SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL; + } +} + +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec) +{ + int i; + + DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + if (spec->channels > 2) { + spec->channels = 2; /* no more than stereo! */ + } + + /* Check formats available */ + MINTAUDIO_freqcount=0; + for (i=0;i<16;i++) { + SDL_MintAudio_AddFrequency(this, freqs[i], 0, i, -1); + } + +#if 1 + for (i=0; i<MINTAUDIO_freqcount; i++) { + DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n", + i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock, + MINTAUDIO_frequencies[i].predivisor + )); + } +#endif + + MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq); + spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency; + + DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + return 0; +} + +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec) +{ + void *buffer; + void *oldpile; + + buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf]; + + oldpile=(void *)Super(0); + + /* Stop replay */ + cookie_stfa->sound_enable=STFA_PLAY_DISABLE; + + /* Select replay format */ + cookie_stfa->sound_control = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor; + if ((spec->format & 0xff)==8) { + cookie_stfa->sound_control |= STFA_FORMAT_8BIT; + } else { + cookie_stfa->sound_control |= STFA_FORMAT_16BIT; + } + if (spec->channels==2) { + cookie_stfa->sound_control |= STFA_FORMAT_STEREO; + } else { + cookie_stfa->sound_control |= STFA_FORMAT_MONO; + } + if ((spec->format & 0x8000)!=0) { + cookie_stfa->sound_control |= STFA_FORMAT_SIGNED; + } else { + cookie_stfa->sound_control |= STFA_FORMAT_UNSIGNED; + } + if ((spec->format & 0x1000)!=0) { + cookie_stfa->sound_control |= STFA_FORMAT_BIGENDIAN; + } else { + cookie_stfa->sound_control |= STFA_FORMAT_LITENDIAN; + } + + /* Set buffer */ + cookie_stfa->sound_start = (unsigned long) buffer; + cookie_stfa->sound_end = (unsigned long) (buffer + spec->size); + + /* Set interrupt */ + cookie_stfa->stfa_it = SDL_MintAudio_StfaInterrupt; + + /* Restart replay */ + cookie_stfa->sound_enable=STFA_PLAY_ENABLE|STFA_PLAY_REPEAT; + + Super(oldpile); + + DEBUG_PRINT((DEBUG_NAME "hardware initialized\n")); +} + +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + SDL_MintAudio_device = this; + + /* Check audio capabilities */ + if (Mint_CheckAudio(this, spec)==-1) { + return -1; + } + + SDL_CalculateAudioSpec(spec); + + /* Allocate memory for audio buffers in DMA-able RAM */ + DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size)); + + SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM); + if (SDL_MintAudio_audiobuf[0]==NULL) { + SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer"); + return (-1); + } + SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ; + SDL_MintAudio_numbuf=0; + SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2); + SDL_MintAudio_audiosize = spec->size; + SDL_MintAudio_mutex = 0; + + DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0])); + DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1])); + + SDL_MintAudio_CheckFpu(); + + /* Setup audio hardware */ + Mint_InitAudio(this, spec); + + return(1); /* We don't use threaded audio */ +} diff --git a/src/audio/mint/SDL_mintaudio_stfa.h b/src/audio/mint/SDL_mintaudio_stfa.h new file mode 100644 index 0000000..006fa75 --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_stfa.h @@ -0,0 +1,100 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + STFA control structure + + Patrice Mandin +*/ + +#ifndef _SDL_mintaudio_stfa_h +#define _SDL_mintaudio_stfa_h + +/*--- Defines ---*/ + +#define C_STFA 0x53544641L /* Sound treiber für atari (seb/The removers) */ + +#define STFA_PLAY_ENABLE (1<<0) +#define STFA_PLAY_DISABLE (0<<0) +#define STFA_PLAY_REPEAT (1<<1) +#define STFA_PLAY_SINGLE (0<<1) + +#define STFA_FORMAT_SIGNED (1<<15) +#define STFA_FORMAT_UNSIGNED (0<<15) +#define STFA_FORMAT_STEREO (1<<14) +#define STFA_FORMAT_MONO (0<<14) +#define STFA_FORMAT_16BIT (1<<13) +#define STFA_FORMAT_8BIT (0<<13) +#define STFA_FORMAT_LITENDIAN (1<<9) +#define STFA_FORMAT_BIGENDIAN (0<<9) +#define STFA_FORMAT_FREQ_MASK 0x0f +enum { + STFA_FORMAT_F4995=0, + STFA_FORMAT_F6269, + STFA_FORMAT_F7493, + STFA_FORMAT_F8192, + + STFA_FORMAT_F9830, + STFA_FORMAT_F10971, + STFA_FORMAT_F12538, + STFA_FORMAT_F14985, + + STFA_FORMAT_F16384, + STFA_FORMAT_F19819, + STFA_FORMAT_F21943, + STFA_FORMAT_F24576, + + STFA_FORMAT_F30720, + STFA_FORMAT_F32336, + STFA_FORMAT_F43885, + STFA_FORMAT_F49152 +}; + +/*--- Types ---*/ + +typedef struct { + unsigned short sound_enable; + unsigned short sound_control; + unsigned short sound_output; + unsigned long sound_start; + unsigned long sound_current; + unsigned long sound_end; + unsigned short version; + void *old_vbl; + void *old_timera; + unsigned long old_mfp_status; + void *new_vbl; + void *drivers_list; + void *play_stop; + unsigned short frequency; + void *set_frequency; + + unsigned short frequency_threshold; + unsigned short *custom_freq_table; + unsigned short stfa_on_off; + void *new_drivers_list; + unsigned long old_bit_2_of_cookie_snd; + void (*stfa_it)(void); +} cookie_stfa_t __attribute__((packed)); + +#endif /* _SDL_mintaudio_stfa_h */ diff --git a/src/audio/mint/SDL_mintaudio_xbios.c b/src/audio/mint/SDL_mintaudio_xbios.c new file mode 100644 index 0000000..0ad6a20 --- /dev/null +++ b/src/audio/mint/SDL_mintaudio_xbios.c @@ -0,0 +1,495 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* + MiNT audio driver + using XBIOS functions (Falcon) + + Patrice Mandin, Didier Méquignon +*/ + +#include <unistd.h> +#include <support.h> + +/* Mint includes */ +#include <mint/osbind.h> +#include <mint/falcon.h> +#include <mint/cookie.h> + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_sysaudio.h" + +#include "../../video/ataricommon/SDL_atarimxalloc_c.h" + +#include "SDL_mintaudio.h" +#include "SDL_mintaudio_dma8.h" + +/*--- Defines ---*/ + +#define MINT_AUDIO_DRIVER_NAME "mint_xbios" + +/* Debug print info */ +#define DEBUG_NAME "audio:xbios: " +#if 0 +#define DEBUG_PRINT(what) \ + { \ + printf what; \ + } +#else +#define DEBUG_PRINT(what) +#endif + +/*--- Static variables ---*/ + +static unsigned long cookie_snd; + +/*--- Audio driver functions ---*/ + +static void Mint_CloseAudio(_THIS); +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_LockAudio(_THIS); +static void Mint_UnlockAudio(_THIS); + +/* To check/init hardware audio */ +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec); +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec); + +/*--- Audio driver bootstrap functions ---*/ + +static int Audio_Available(void) +{ + unsigned long dummy; + const char *envr = SDL_getenv("SDL_AUDIODRIVER"); + + /*SDL_MintAudio_mint_present = (Getcookie(C_MiNT, &dummy) == C_FOUND);*/ + SDL_MintAudio_mint_present = SDL_FALSE; + + /* We can't use XBIOS in interrupt with Magic, don't know about thread */ + /*if (Getcookie(C_MagX, &dummy) == C_FOUND) { + return(0); + }*/ + + /* Check if user asked a different audio driver */ + if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) { + DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n")); + return(0); + } + + /* Cookie _SND present ? if not, assume ST machine */ + if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) { + cookie_snd = SND_PSG; + } + + /* Check if we have 16 bits audio */ + if ((cookie_snd & SND_16BIT)==0) { + DEBUG_PRINT((DEBUG_NAME "no 16 bits sound\n")); + return(0); + } + + /* Check if audio is lockable */ + if (Locksnd()!=1) { + DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n")); + return(0); + } + + Unlocksnd(); + + DEBUG_PRINT((DEBUG_NAME "XBIOS audio available!\n")); + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = Mint_OpenAudio; + this->CloseAudio = Mint_CloseAudio; + this->LockAudio = Mint_LockAudio; + this->UnlockAudio = Mint_UnlockAudio; + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap MINTAUDIO_XBIOS_bootstrap = { + MINT_AUDIO_DRIVER_NAME, "MiNT XBIOS audio driver", + Audio_Available, Audio_CreateDevice +}; + +static void Mint_LockAudio(_THIS) +{ + /* Stop replay */ + Buffoper(0); +} + +static void Mint_UnlockAudio(_THIS) +{ + /* Restart replay */ + Buffoper(SB_PLA_ENA|SB_PLA_RPT); +} + +static void Mint_CloseAudio(_THIS) +{ + /* Stop replay */ + SDL_MintAudio_WaitThread(); + Buffoper(0); + + if (!SDL_MintAudio_mint_present) { + /* Uninstall interrupt */ + Jdisint(MFP_DMASOUND); + } + + /* Wait if currently playing sound */ + while (SDL_MintAudio_mutex != 0) { + } + + /* Clear buffers */ + if (SDL_MintAudio_audiobuf[0]) { + Mfree(SDL_MintAudio_audiobuf[0]); + SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL; + } + + /* Unlock sound system */ + Unlocksnd(); +} + +/* Falcon XBIOS implementation of Devconnect() is buggy with external clock */ +static void Devconnect2(int src, int dst, int sclk, int pre) +{ + static const unsigned short MASK1[3] = { 0, 0x6000, 0 }; + static const unsigned short MASK2[4] = { 0xFFF0, 0xFF8F, 0xF0FF, 0x0FFF }; + static const unsigned short INDEX1[4] = { 1, 3, 5, 7 }; + static const unsigned short INDEX2[4] = { 0, 2, 4, 6 }; + unsigned short sync_div,dev_ctrl,dest_ctrl; + void *oldstack; + + if (dst==0) { + return; + } + + oldstack=(void *)Super(0); + + dev_ctrl = DMAAUDIO_IO.dev_ctrl; + dest_ctrl = DMAAUDIO_IO.dest_ctrl; + dev_ctrl &= MASK2[src]; + + if (src==ADC) { + dev_ctrl |= MASK1[sclk]; + } else { + dev_ctrl |= (INDEX1[sclk] << (src<<4)); + } + + if (dst & DMAREC) { + dest_ctrl &= 0xFFF0; + dest_ctrl |= INDEX1[src]; + } + + if (dst & DSPRECV) { + dest_ctrl &= 0xFF8F; + dest_ctrl |= (INDEX1[src]<<4); + } + + if (dst & EXTOUT) { + dest_ctrl &= 0xF0FF; + dest_ctrl |= (INDEX1[src]<<8); + } + + if (dst & DAC) { + dev_ctrl &= 0x0FFF; + dev_ctrl |= MASK1[sclk]; + dest_ctrl &= 0x0FFF; + dest_ctrl |= (INDEX2[src]<<12); + } + + sync_div = DMAAUDIO_IO.sync_div; + if (sclk==CLKEXT) { + pre<<=8; + sync_div &= 0xF0FF; + } else { + sync_div &= 0xFFF0; + } + sync_div |= pre; + + DMAAUDIO_IO.dev_ctrl = dev_ctrl; + DMAAUDIO_IO.dest_ctrl = dest_ctrl; + DMAAUDIO_IO.sync_div = sync_div; + + Super(oldstack); +} + +static void Mint_CheckExternalClock(_THIS) +{ +#define SIZE_BUF_CLOCK_MEASURE (44100/10) + + unsigned long cookie_snd; + char *buffer; + int i, j; + + /* DSP present with its GPIO port ? */ + if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) { + return; + } + if ((cookie_snd & SND_DSP)==0) { + return; + } + + buffer = Atari_SysMalloc(SIZE_BUF_CLOCK_MEASURE, MX_STRAM); + if (buffer==NULL) { + DEBUG_PRINT((DEBUG_NAME "Not enough memory for the measure\n")); + return; + } + SDL_memset(buffer, 0, SIZE_BUF_CLOCK_MEASURE); + + Buffoper(0); + Settracks(0,0); + Setmontracks(0); + Setmode(MONO8); + Jdisint(MFP_TIMERA); + + for (i=0; i<2; i++) { + Gpio(GPIO_SET,7); /* DSP port gpio outputs */ + Gpio(GPIO_WRITE,2+i); /* 22.5792/24.576 MHz for 44.1/48KHz */ + Devconnect2(DMAPLAY, DAC, CLKEXT, CLK50K); /* Matrix and clock source */ + Setbuffer(0, buffer, buffer + SIZE_BUF_CLOCK_MEASURE); /* Set buffer */ + Xbtimer(XB_TIMERA, 5, 38, SDL_MintAudio_XbiosInterruptMeasureClock); /* delay mode timer A, prediv /64, 1KHz */ + Jenabint(MFP_TIMERA); + SDL_MintAudio_clocktics = 0; + Buffoper(SB_PLA_ENA); + usleep(110000); + + if((Buffoper(-1) & 1)==0) { + if (SDL_MintAudio_clocktics) { + unsigned long khz; + + khz = ((SIZE_BUF_CLOCK_MEASURE/SDL_MintAudio_clocktics) +1) & 0xFFFFFFFE; + DEBUG_PRINT((DEBUG_NAME "measure %d: freq=%lu KHz\n", i+1, khz)); + + if(khz==44) { + for (j=1; j<4; j++) { + SDL_MintAudio_AddFrequency(this, MASTERCLOCK_44K/(MASTERPREDIV_FALCON*(1<<j)), MASTERCLOCK_44K, (1<<j)-1, 2+i); + } + } else if (khz==48) { + for (j=1; j<4; j++) { + SDL_MintAudio_AddFrequency(this, MASTERCLOCK_48K/(MASTERPREDIV_FALCON*(1<<j)), MASTERCLOCK_48K, (1<<j)-1, 2+i); + } + } + } else { + DEBUG_PRINT((DEBUG_NAME "No measure\n")); + } + } else { + DEBUG_PRINT((DEBUG_NAME "No SDMA clock\n")); + } + + Buffoper(0); /* stop */ + Jdisint(MFP_TIMERA); /* Uninstall interrupt */ + } + + Mfree(buffer); +} + +static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec) +{ + int i; + Uint32 extclock; + + DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + if (spec->channels > 2) { + spec->channels = 2; /* no more than stereo! */ + } + + spec->format |= 0x8000; /* Audio is always signed */ + if ((spec->format & 0x00ff)==16) { + spec->format |= 0x1000; /* Audio is always big endian */ + spec->channels=2; /* 16 bits always stereo */ + } + + MINTAUDIO_freqcount=0; + + /* Add external clocks if present */ + Mint_CheckExternalClock(this); + + /* Standard clocks */ + for (i=1;i<12;i++) { + /* Remove unusable Falcon codec predivisors */ + if ((i==6) || (i==8) || (i==10)) { + continue; + } + SDL_MintAudio_AddFrequency(this, MASTERCLOCK_FALCON1/(MASTERPREDIV_FALCON*(i+1)), MASTERCLOCK_FALCON1, i, -1); + } + +#if 1 + for (i=0; i<MINTAUDIO_freqcount; i++) { + DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n", + i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock, + MINTAUDIO_frequencies[i].predivisor + )); + } +#endif + + MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq); + spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency; + + DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff)); + DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0))); + DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0))); + DEBUG_PRINT(("channels=%d, ", spec->channels)); + DEBUG_PRINT(("freq=%d\n", spec->freq)); + + return 0; +} + +static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec) +{ + int channels_mode, dmaclock, prediv; + void *buffer; + + /* Stop currently playing sound */ + SDL_MintAudio_quit_thread = SDL_FALSE; + SDL_MintAudio_thread_finished = SDL_TRUE; + SDL_MintAudio_WaitThread(); + Buffoper(0); + + /* Set replay tracks */ + Settracks(0,0); + Setmontracks(0); + + /* Select replay format */ + channels_mode=STEREO16; + switch (spec->format & 0xff) { + case 8: + if (spec->channels==2) { + channels_mode=STEREO8; + } else { + channels_mode=MONO8; + } + break; + } + if (Setmode(channels_mode)<0) { + DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n")); + } + + dmaclock = MINTAUDIO_frequencies[MINTAUDIO_numfreq].masterclock; + prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor; + if (MINTAUDIO_frequencies[MINTAUDIO_numfreq].gpio_bits != -1) { + Gpio(GPIO_SET,7); /* DSP port gpio outputs */ + Gpio(GPIO_WRITE, MINTAUDIO_frequencies[MINTAUDIO_numfreq].gpio_bits); + Devconnect2(DMAPLAY, DAC|EXTOUT, CLKEXT, prediv); + } else { + Devconnect2(DMAPLAY, DAC, CLK25M, prediv); + } + + /* Set buffer */ + buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf]; + if (Setbuffer(0, buffer, buffer + spec->size)<0) { + DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n")); + } + + if (SDL_MintAudio_mint_present) { + SDL_MintAudio_thread_pid = tfork(SDL_MintAudio_Thread, 0); + } else { + /* Install interrupt */ + Jdisint(MFP_DMASOUND); + /*Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_XbiosInterrupt);*/ + Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_Dma8Interrupt); + Jenabint(MFP_DMASOUND); + + if (Setinterrupt(SI_TIMERA, SI_PLAY)<0) { + DEBUG_PRINT((DEBUG_NAME "Setinterrupt() failed\n")); + } + } + + /* Go */ + Buffoper(SB_PLA_ENA|SB_PLA_RPT); + DEBUG_PRINT((DEBUG_NAME "hardware initialized\n")); +} + +static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + /* Lock sound system */ + if (Locksnd()!=1) { + SDL_SetError("Mint_OpenAudio: Audio system already in use"); + return(-1); + } + + SDL_MintAudio_device = this; + + /* Check audio capabilities */ + if (Mint_CheckAudio(this, spec)==-1) { + return -1; + } + + SDL_CalculateAudioSpec(spec); + + /* Allocate memory for audio buffers in DMA-able RAM */ + DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size)); + + SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM); + if (SDL_MintAudio_audiobuf[0]==NULL) { + SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer"); + return (-1); + } + SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ; + SDL_MintAudio_numbuf=0; + SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2); + SDL_MintAudio_audiosize = spec->size; + SDL_MintAudio_mutex = 0; + + DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0])); + DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1])); + + SDL_MintAudio_CheckFpu(); + + /* Setup audio hardware */ + Mint_InitAudio(this, spec); + + return(1); /* We don't use SDL threaded audio */ +} diff --git a/src/audio/mme/SDL_mmeaudio.c b/src/audio/mme/SDL_mmeaudio.c new file mode 100644 index 0000000..365f5ff --- /dev/null +++ b/src/audio/mme/SDL_mmeaudio.c @@ -0,0 +1,264 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* Tru64 UNIX MME support */ +#include <mme_api.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "SDL_mmeaudio.h" + +static BOOL inUse[NUM_BUFFERS]; + +/* Audio driver functions */ +static int MME_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void MME_WaitAudio(_THIS); +static Uint8 *MME_GetAudioBuf(_THIS); +static void MME_PlayAudio(_THIS); +static void MME_WaitDone(_THIS); +static void MME_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ +static int Audio_Available(void) +{ + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + if ( device ) { + if ( device->hidden ) { + SDL_free(device->hidden); + device->hidden = NULL; + } + SDL_free(device); + device = NULL; + } +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + +/* Initialize all variables that we clean on shutdown */ + this = SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + /* Set the function pointers */ + this->OpenAudio = MME_OpenAudio; + this->WaitAudio = MME_WaitAudio; + this->PlayAudio = MME_PlayAudio; + this->GetAudioBuf = MME_GetAudioBuf; + this->WaitDone = MME_WaitDone; + this->CloseAudio = MME_CloseAudio; + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap MMEAUDIO_bootstrap = { + "waveout", "Tru64 MME WaveOut", + Audio_Available, Audio_CreateDevice +}; + +static void SetMMerror(char *function, MMRESULT code) +{ + int len; + char errbuf[MAXERRORLENGTH]; + + SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function); + len = SDL_strlen(errbuf); + waveOutGetErrorText(code, errbuf+len, MAXERRORLENGTH-len); + SDL_SetError("%s",errbuf); +} + +static void CALLBACK MME_CALLBACK(HWAVEOUT hwo, + UINT uMsg, + DWORD dwInstance, + LPARAM dwParam1, + LPARAM dwParam2) +{ + WAVEHDR *wp = (WAVEHDR *) dwParam1; + + if ( uMsg == WOM_DONE ) + inUse[wp->dwUser] = FALSE; +} + +static int MME_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + MMRESULT result; + int i; + + mixbuf = NULL; + + /* Set basic WAVE format parameters */ + shm = mmeAllocMem(sizeof(*shm)); + if ( shm == NULL ) { + SDL_SetError("Out of memory: shm"); + return(-1); + } + shm->sound = 0; + shm->wFmt.wf.wFormatTag = WAVE_FORMAT_PCM; + + /* Determine the audio parameters from the AudioSpec */ + switch ( spec->format & 0xFF ) { + case 8: + /* Unsigned 8 bit audio data */ + spec->format = AUDIO_U8; + shm->wFmt.wBitsPerSample = 8; + break; + case 16: + /* Signed 16 bit audio data */ + spec->format = AUDIO_S16; + shm->wFmt.wBitsPerSample = 16; + break; + default: + SDL_SetError("Unsupported audio format"); + return(-1); + } + + shm->wFmt.wf.nChannels = spec->channels; + shm->wFmt.wf.nSamplesPerSec = spec->freq; + shm->wFmt.wf.nBlockAlign = + shm->wFmt.wf.nChannels * shm->wFmt.wBitsPerSample / 8; + shm->wFmt.wf.nAvgBytesPerSec = + shm->wFmt.wf.nSamplesPerSec * shm->wFmt.wf.nBlockAlign; + + /* Check the buffer size -- minimum of 1/4 second (word aligned) */ + if ( spec->samples < (spec->freq/4) ) + spec->samples = ((spec->freq/4)+3)&~3; + + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(spec); + + /* Open the audio device */ + result = waveOutOpen(&(shm->sound), + WAVE_MAPPER, + &(shm->wFmt.wf), + MME_CALLBACK, + NULL, + (CALLBACK_FUNCTION|WAVE_OPEN_SHAREABLE)); + if ( result != MMSYSERR_NOERROR ) { + SetMMerror("waveOutOpen()", result); + return(-1); + } + + /* Create the sound buffers */ + mixbuf = (Uint8 *)mmeAllocBuffer(NUM_BUFFERS * (spec->size)); + if ( mixbuf == NULL ) { + SDL_SetError("Out of memory: mixbuf"); + return(-1); + } + + for (i = 0; i < NUM_BUFFERS; i++) { + shm->wHdr[i].lpData = &mixbuf[i * (spec->size)]; + shm->wHdr[i].dwBufferLength = spec->size; + shm->wHdr[i].dwFlags = 0; + shm->wHdr[i].dwUser = i; + shm->wHdr[i].dwLoops = 0; /* loop control counter */ + shm->wHdr[i].lpNext = NULL; /* reserved for driver */ + shm->wHdr[i].reserved = 0; + inUse[i] = FALSE; + } + next_buffer = 0; + return 0; +} + +static void MME_WaitAudio(_THIS) +{ + while ( inUse[next_buffer] ) { + mmeWaitForCallbacks(); + mmeProcessCallbacks(); + } +} + +static Uint8 *MME_GetAudioBuf(_THIS) +{ + Uint8 *retval; + + inUse[next_buffer] = TRUE; + retval = (Uint8 *)(shm->wHdr[next_buffer].lpData); + return retval; +} + +static void MME_PlayAudio(_THIS) +{ + /* Queue it up */ + waveOutWrite(shm->sound, &(shm->wHdr[next_buffer]), sizeof(WAVEHDR)); + next_buffer = (next_buffer+1)%NUM_BUFFERS; +} + +static void MME_WaitDone(_THIS) +{ + MMRESULT result; + int i; + + if ( shm->sound ) { + for (i = 0; i < NUM_BUFFERS; i++) + while ( inUse[i] ) { + mmeWaitForCallbacks(); + mmeProcessCallbacks(); + } + result = waveOutReset(shm->sound); + if ( result != MMSYSERR_NOERROR ) + SetMMerror("waveOutReset()", result); + mmeProcessCallbacks(); + } +} + +static void MME_CloseAudio(_THIS) +{ + MMRESULT result; + + if ( mixbuf ) { + result = mmeFreeBuffer(mixbuf); + if (result != MMSYSERR_NOERROR ) + SetMMerror("mmeFreeBuffer", result); + mixbuf = NULL; + } + + if ( shm ) { + if ( shm->sound ) { + result = waveOutClose(shm->sound); + if (result != MMSYSERR_NOERROR ) + SetMMerror("waveOutClose()", result); + mmeProcessCallbacks(); + } + result = mmeFreeMem(shm); + if (result != MMSYSERR_NOERROR ) + SetMMerror("mmeFreeMem()", result); + shm = NULL; + } +} + diff --git a/src/audio/mme/SDL_mmeaudio.h b/src/audio/mme/SDL_mmeaudio.h new file mode 100644 index 0000000..41f3b30 --- /dev/null +++ b/src/audio/mme/SDL_mmeaudio.h @@ -0,0 +1,51 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#ifndef _SDL_lowaudio_h +#define _SDL_lowaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this +#define NUM_BUFFERS 2 + +struct SharedMem { + HWAVEOUT sound; + WAVEHDR wHdr[NUM_BUFFERS]; + PCMWAVEFORMAT wFmt; +}; + +struct SDL_PrivateAudioData { + Uint8 *mixbuf; /* The raw allocated mixing buffer */ + struct SharedMem *shm; + int next_buffer; +}; + +#define shm (this->hidden->shm) +#define mixbuf (this->hidden->mixbuf) +#define next_buffer (this->hidden->next_buffer) +/* Old variable names */ +#endif /* _SDL_lowaudio_h */ diff --git a/src/audio/nas/SDL_nasaudio.c b/src/audio/nas/SDL_nasaudio.c new file mode 100644 index 0000000..677eb17 --- /dev/null +++ b/src/audio/nas/SDL_nasaudio.c @@ -0,0 +1,423 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org + + This driver was written by: + Erik Inge Bolsø + knan@mo.himolde.no +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include <signal.h> +#include <unistd.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_nasaudio.h" + +#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC +#include "SDL_loadso.h" +#endif + +/* The tag name used by artsc audio */ +#define NAS_DRIVER_NAME "nas" + +static struct SDL_PrivateAudioData *this2 = NULL; + +static void (*NAS_AuCloseServer) (AuServer *); +static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *); +static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *); +static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *); +static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *); +static void (*NAS_AuSetElements) + (AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *); +static void (*NAS_AuWriteElement) + (AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *); +static AuServer *(*NAS_AuOpenServer) + (_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **); +static AuEventHandlerRec *(*NAS_AuRegisterEventHandler) + (AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer); + + +#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC + +static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC; +static void *nas_handle = NULL; + +static int +load_nas_sym(const char *fn, void **addr) +{ + *addr = SDL_LoadFunction(nas_handle, fn); + if (*addr == NULL) { + return 0; + } + return 1; +} + +/* cast funcs to char* first, to please GCC's strict aliasing rules. */ +#define SDL_NAS_SYM(x) \ + if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1 +#else +#define SDL_NAS_SYM(x) NAS_##x = x +#endif + +static int +load_nas_syms(void) +{ + SDL_NAS_SYM(AuCloseServer); + SDL_NAS_SYM(AuNextEvent); + SDL_NAS_SYM(AuDispatchEvent); + SDL_NAS_SYM(AuCreateFlow); + SDL_NAS_SYM(AuStartFlow); + SDL_NAS_SYM(AuSetElements); + SDL_NAS_SYM(AuWriteElement); + SDL_NAS_SYM(AuOpenServer); + SDL_NAS_SYM(AuRegisterEventHandler); + return 0; +} + +#undef SDL_NAS_SYM + +#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC + +static void +UnloadNASLibrary(void) +{ + if (nas_handle != NULL) { + SDL_UnloadObject(nas_handle); + nas_handle = NULL; + } +} + +static int +LoadNASLibrary(void) +{ + int retval = 0; + if (nas_handle == NULL) { + nas_handle = SDL_LoadObject(nas_library); + if (nas_handle == NULL) { + /* Copy error string so we can use it in a new SDL_SetError(). */ + char *origerr = SDL_GetError(); + size_t len = SDL_strlen(origerr) + 1; + char *err = (char *) alloca(len); + SDL_strlcpy(err, origerr, len); + retval = -1; + SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n", + nas_library, err); + } else { + retval = load_nas_syms(); + if (retval < 0) { + UnloadNASLibrary(); + } + } + } + return retval; +} + +#else + +static void +UnloadNASLibrary(void) +{ +} + +static int +LoadNASLibrary(void) +{ + load_nas_syms(); + return 0; +} + +#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */ + + +/* Audio driver functions */ +static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void NAS_WaitAudio(_THIS); +static void NAS_PlayAudio(_THIS); +static Uint8 *NAS_GetAudioBuf(_THIS); +static void NAS_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + if (LoadNASLibrary() == 0) { + AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL); + if (!aud) { + UnloadNASLibrary(); + return 0; + } + NAS_AuCloseServer(aud); + UnloadNASLibrary(); + return 1; + } + return 0; +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + UnloadNASLibrary(); + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + if (LoadNASLibrary() < 0) { + return NULL; + } + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return NULL; + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = NAS_OpenAudio; + this->WaitAudio = NAS_WaitAudio; + this->PlayAudio = NAS_PlayAudio; + this->GetAudioBuf = NAS_GetAudioBuf; + this->CloseAudio = NAS_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap NAS_bootstrap = { + NAS_DRIVER_NAME, "Network Audio System", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void NAS_WaitAudio(_THIS) +{ + while ( this->hidden->buf_free < this->hidden->mixlen ) { + AuEvent ev; + NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev); + NAS_AuDispatchEvent(this->hidden->aud, &ev); + } +} + +static void NAS_PlayAudio(_THIS) +{ + while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events, + in the hope that some of them is LowWater events telling us more + of the buffer is free now than what we think. */ + AuEvent ev; + NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev); + NAS_AuDispatchEvent(this->hidden->aud, &ev); + } + this->hidden->buf_free -= this->hidden->mixlen; + + /* Write the audio data */ + NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL); + + this->hidden->written += this->hidden->mixlen; + +#ifdef DEBUG_AUDIO + fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen); +#endif +} + +static Uint8 *NAS_GetAudioBuf(_THIS) +{ + return(this->hidden->mixbuf); +} + +static void NAS_CloseAudio(_THIS) +{ + if ( this->hidden->mixbuf != NULL ) { + SDL_FreeAudioMem(this->hidden->mixbuf); + this->hidden->mixbuf = NULL; + } + if ( this->hidden->aud ) { + NAS_AuCloseServer(this->hidden->aud); + this->hidden->aud = 0; + } +} + +static unsigned char sdlformat_to_auformat(unsigned int fmt) +{ + switch (fmt) + { + case AUDIO_U8: + return AuFormatLinearUnsigned8; + case AUDIO_S8: + return AuFormatLinearSigned8; + case AUDIO_U16LSB: + return AuFormatLinearUnsigned16LSB; + case AUDIO_U16MSB: + return AuFormatLinearUnsigned16MSB; + case AUDIO_S16LSB: + return AuFormatLinearSigned16LSB; + case AUDIO_S16MSB: + return AuFormatLinearSigned16MSB; + } + return AuNone; +} + +static AuBool +event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd) +{ + switch (ev->type) { + case AuEventTypeElementNotify: { + AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev; + + switch (event->kind) { + case AuElementNotifyKindLowWater: + if (this2->buf_free >= 0) { + this2->really += event->num_bytes; + gettimeofday(&this2->last_tv, 0); + this2->buf_free += event->num_bytes; + } else { + this2->buf_free = event->num_bytes; + } + break; + case AuElementNotifyKindState: + switch (event->cur_state) { + case AuStatePause: + if (event->reason != AuReasonUser) { + if (this2->buf_free >= 0) { + this2->really += event->num_bytes; + gettimeofday(&this2->last_tv, 0); + this2->buf_free += event->num_bytes; + } else { + this2->buf_free = event->num_bytes; + } + } + break; + } + } + } + } + return AuTrue; +} + +static AuDeviceID +find_device(_THIS, int nch) +{ + /* These "Au" things are all macros, not functions... */ + int i; + for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) { + if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) == + AuComponentKindPhysicalOutput) && + AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) { + return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i)); + } + } + return AuNone; +} + +static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + AuElement elms[3]; + int buffer_size; + Uint16 test_format, format; + + this->hidden->mixbuf = NULL; + + /* Try for a closest match on audio format */ + format = 0; + for ( test_format = SDL_FirstAudioFormat(spec->format); + ! format && test_format; ) { + format = sdlformat_to_auformat(test_format); + + if (format == AuNone) { + test_format = SDL_NextAudioFormat(); + } + } + if ( format == 0 ) { + SDL_SetError("Couldn't find any hardware audio formats"); + return(-1); + } + spec->format = test_format; + + this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL); + if (this->hidden->aud == 0) + { + SDL_SetError("Couldn't open connection to NAS server"); + return (-1); + } + + this->hidden->dev = find_device(this, spec->channels); + if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, NULL)))) { + NAS_AuCloseServer(this->hidden->aud); + this->hidden->aud = 0; + SDL_SetError("Couldn't find a fitting playback device on NAS server"); + return (-1); + } + + buffer_size = spec->freq; + if (buffer_size < 4096) + buffer_size = 4096; + + if (buffer_size > 32768) + buffer_size = 32768; /* So that the buffer won't get unmanageably big. */ + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + this2 = this->hidden; + + /* These "Au" things without a NAS_ prefix are macros, not functions... */ + AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue, + buffer_size, buffer_size / 4, 0, NULL); + AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq, + AuUnlimitedSamples, 0, NULL); + NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL); + NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow, + event_handler, (AuPointer) NULL); + + NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL); + + /* Allocate mixing buffer */ + this->hidden->mixlen = spec->size; + this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen); + if ( this->hidden->mixbuf == NULL ) { + return(-1); + } + SDL_memset(this->hidden->mixbuf, spec->silence, spec->size); + + /* Get the parent process id (we're the parent of the audio thread) */ + this->hidden->parent = getpid(); + + /* We're ready to rock and roll. :-) */ + return(0); +} diff --git a/src/audio/nas/SDL_nasaudio.h b/src/audio/nas/SDL_nasaudio.h new file mode 100644 index 0000000..1cd04f8 --- /dev/null +++ b/src/audio/nas/SDL_nasaudio.h @@ -0,0 +1,62 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org + + This driver was written by: + Erik Inge Bolsø + knan@mo.himolde.no +*/ +#include "SDL_config.h" + +#ifndef _SDL_nasaudio_h +#define _SDL_nasaudio_h + +#ifdef __sgi +#include <nas/audiolib.h> +#else +#include <audio/audiolib.h> +#endif +#include <sys/time.h> + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + AuServer* aud; + AuFlowID flow; + AuDeviceID dev; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; + + int written; + int really; + int bps; + struct timeval last_tv; + int buf_free; +}; +#endif /* _SDL_nasaudio_h */ + diff --git a/src/audio/nds/SDL_ndsaudio.c b/src/audio/nds/SDL_ndsaudio.c new file mode 100644 index 0000000..ad68d8c --- /dev/null +++ b/src/audio/nds/SDL_ndsaudio.c @@ -0,0 +1,335 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ +#include <nds.h> +#include "SDL.h" +#include "SDL_endian.h" +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "SDL_ndsaudio.h" +#include "soundcommon.h" + + +/* Audio driver functions */ +static int NDS_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void NDS_WaitAudio(_THIS); +static void NDS_PlayAudio(_THIS); +static Uint8 *NDS_GetAudioBuf(_THIS); +static void NDS_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +u32 framecounter = 0,soundoffset = 0; +static SDL_AudioDevice *sdl_nds_audiodevice; + +//void SoundMixCallback(void *stream,u32 size) +//{ +// //printf("SoundMixCallback\n"); +// +// Uint8 *buffer; +// +// buffer = sdl_nds_audiodevice->hidden->mixbuf; +// memset(buffer, sdl_nds_audiodevice->spec.silence, size); +// +// if (!sdl_nds_audiodevice->paused){ +// +// +// //if (sdl_nds_audiodevice->convert.needed) { +// // int silence; +// +// // if (sdl_nds_audiodevice->convert.src_format == AUDIO_U8 ) { +// // silence = 0x80; +// // } else { +// // silence = 0; +// // } +// // memset(sdl_nds_audiodevice->convert.buf, silence, sdl_nds_audiodevice->convert.len); +// // sdl_nds_audiodevice->spec.callback(sdl_nds_audiodevice->spec.userdata, +// // (Uint8 *)sdl_nds_audiodevice->convert.buf,sdl_nds_audiodevice->convert.len); +// // SDL_ConvertAudio(&sdl_nds_audiodevice->convert); +// // memcpy(buffer, sdl_nds_audiodevice->convert.buf, sdl_nds_audiodevice->convert.len_cvt); +// //} else +// { +// sdl_nds_audiodevice->spec.callback(sdl_nds_audiodevice->spec.userdata, buffer, size); +// //memcpy((Sint16 *)stream,buffer, size); +// } +// +// } +// +// if(soundsystem->format == 8) +// { +// int i; +// s32 *buffer32 = (s32 *)buffer; +// s32 *stream32 = (s32 *)stream; +// for(i=0;i<size/4;i++){ *stream32++ = buffer32[i] ^ 0x80808080;} +// //for(i = 0; i < size; i++) +// // ((s8*)stream)[i]=(buffer[i]^0x80); +// } +// else +// { +// int i; +// for(i = 0; i < size; i++){ +// //((short*)stream)[i] =(short)buffer[i] << 8; // sound 8bit ---> buffer 16bit +// //if (buffer[i] &0x80) +// //((Sint16*)stream)[i] = 0xff00 | buffer[i]; +// ((Sint16*)stream)[i] = (buffer[i] - 128) << 8; +// +// //else +// // ((Sint16*)stream)[i] = buffer[i]; +// } +// //register signed char *pSrc =buffer; +// //register short *pDest =stream; +// //int x; +// // for (x=size; x>0; x--) +// // { +// // register short temp = (((short)*pSrc)-128)<<8; +// // pSrc++; +// // *pDest++ = temp; +// // } +// +// //memcpy((Sint16 *)stream,buffer, size); +// } +//} + +void SoundMixCallback(void *stream,u32 len) +{ + SDL_AudioDevice *audio = (SDL_AudioDevice *)sdl_nds_audiodevice; + + /* Silence the buffer, since it's ours */ + SDL_memset(stream, audio->spec.silence, len); + + /* Only do soemthing if audio is enabled */ + if ( ! audio->enabled ) + return; + + if ( ! audio->paused ) { + if ( audio->convert.needed ) { + //fprintf(stderr,"converting audio\n"); + SDL_mutexP(audio->mixer_lock); + (*audio->spec.callback)(audio->spec.userdata, + (Uint8 *)audio->convert.buf,audio->convert.len); + SDL_mutexV(audio->mixer_lock); + SDL_ConvertAudio(&audio->convert); + SDL_memcpy(stream,audio->convert.buf,audio->convert.len_cvt); + } else { + SDL_mutexP(audio->mixer_lock); + (*audio->spec.callback)(audio->spec.userdata, + (Uint8 *)stream, len); + SDL_mutexV(audio->mixer_lock); + } + } + return; +} +void MixSound(void) +{ + int remain; + + if(soundsystem->format == 8) + { + if((soundsystem->soundcursor + soundsystem->numsamples) > soundsystem->buffersize) + { + SoundMixCallback(&soundsystem->mixbuffer[soundsystem->soundcursor],soundsystem->buffersize - soundsystem->soundcursor); + remain = soundsystem->numsamples - (soundsystem->buffersize - soundsystem->soundcursor); + SoundMixCallback(soundsystem->mixbuffer,remain); + } + else + { + SoundMixCallback(&soundsystem->mixbuffer[soundsystem->soundcursor],soundsystem->numsamples); + } + } + else + { + if((soundsystem->soundcursor + soundsystem->numsamples) > (soundsystem->buffersize >> 1)) + { + SoundMixCallback(&soundsystem->mixbuffer[soundsystem->soundcursor << 1],(soundsystem->buffersize >> 1) - soundsystem->soundcursor); + remain = soundsystem->numsamples - ((soundsystem->buffersize >> 1) - soundsystem->soundcursor); + SoundMixCallback(soundsystem->mixbuffer,remain); + } + else + { + SoundMixCallback(&soundsystem->mixbuffer[soundsystem->soundcursor << 1],soundsystem->numsamples); + } + } +} + +void InterruptHandler(void) +{ + framecounter++; +} +void FiFoHandler(void) +{ + u32 command; + while ( !(REG_IPC_FIFO_CR & (IPC_FIFO_RECV_EMPTY)) ) + { + command = REG_IPC_FIFO_RX; + + switch(command) + { + case FIFO_NONE: + break; + case UPDATEON_ARM9: + REG_IME = 0; + MixSound(); + REG_IME = 1; + SendCommandToArm7(MIXCOMPLETE_ONARM9); + break; + } + } +} + + + + + +static int Audio_Available(void) +{ + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = NDS_OpenAudio; + this->WaitAudio = NDS_WaitAudio; + this->PlayAudio = NDS_PlayAudio; + this->GetAudioBuf = NDS_GetAudioBuf; + this->CloseAudio = NDS_CloseAudio; + + this->free = Audio_DeleteDevice; +//fprintf(stderr,"Audio_CreateDevice\n"); + return this; +} + +AudioBootStrap NDSAUD_bootstrap = { + "nds", "NDS audio", + Audio_Available, Audio_CreateDevice +}; + + +void static NDS_WaitAudio(_THIS) +{ + //printf("NDS_WaitAudio\n"); +} + +static void NDS_PlayAudio(_THIS) +{ + //printf("playing audio\n"); + if (this->paused) + return; + +} + +static Uint8 *NDS_GetAudioBuf(_THIS) +{ + return NULL;//(this->hidden->mixbuf); +} + +static void NDS_CloseAudio(_THIS) +{ +/* if ( this->hidden->mixbuf != NULL ) { + SDL_FreeAudioMem(this->hidden->mixbuf); + this->hidden->mixbuf = NULL; + }*/ +} + +static int NDS_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + //printf("NDS_OpenAudio\n"); + int format = 0; + //switch(spec->format&0xff) { + //case 8: spec->format = AUDIO_S8;format=8; break; + //case 16: spec->format = AUDIO_S16LSB;format=16; break; + //default: + // SDL_SetError("Unsupported audio format"); + // return(-1); + //} + switch (spec->format&~0x1000) { + case AUDIO_S8: + /* Signed 8-bit audio supported */ + format=8; + break; + case AUDIO_U8: + spec->format ^= 0x80;format=8; + break; + case AUDIO_U16: + /* Unsigned 16-bit audio unsupported, convert to S16 */ + spec->format ^=0x8000;format=16; + case AUDIO_S16: + /* Signed 16-bit audio supported */ + format=16; + break; + } + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(spec); + + /* Allocate mixing buffer */ + //this->hidden->mixlen = spec->size; + //this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); + //if ( this->hidden->mixbuf == NULL ) { + // SDL_SetError("Out of Memory"); + // return(-1); + //} + + SDL_NDSAudio_mutex = 0; + sdl_nds_audiodevice=this; + + irqInit(); + irqSet(IRQ_VBLANK,&InterruptHandler); + irqSet(IRQ_FIFO_NOT_EMPTY,&FiFoHandler); + irqEnable(IRQ_FIFO_NOT_EMPTY); + + REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ; + + + + SoundSystemInit(spec->freq,spec->size,0,format); + SoundStartMixer(); + + + return(1); +} diff --git a/src/audio/nds/SDL_ndsaudio.h b/src/audio/nds/SDL_ndsaudio.h new file mode 100644 index 0000000..56e0309 --- /dev/null +++ b/src/audio/nds/SDL_ndsaudio.h @@ -0,0 +1,40 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_lowaudio_h +#define _SDL_lowaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the audio functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + //Uint8 *mixbuf; + //Uint32 mixlen; +}; +unsigned short SDL_NDSAudio_mutex=0; + + +#endif /* _SDL_lowaudio_h */ diff --git a/src/audio/nds/sound9.c b/src/audio/nds/sound9.c new file mode 100644 index 0000000..aa427ae --- /dev/null +++ b/src/audio/nds/sound9.c @@ -0,0 +1,61 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" +#include "SDL_stdinc.h" + +#include "soundcommon.h" + +void SoundSystemInit(u32 rate,u32 buffersize,u8 channel,u8 format) +{ + soundsystem->rate = rate; + + if(format == 8) + soundsystem->buffersize = buffersize; + else if(format == 16) + soundsystem->buffersize = buffersize * sizeof(short); + + soundsystem->mixbuffer = (s8*)SDL_malloc(soundsystem->buffersize); + //soundsystem->soundbuffer = soundsystem->mixbuffer; + soundsystem->format = format; + soundsystem->channel = channel; + soundsystem->prevtimer = 0; + soundsystem->soundcursor = 0; + soundsystem->numsamples = 0; + soundsystem->period = 0x1000000 / rate; + soundsystem->cmd = INIT; +} + +void SoundStartMixer(void) +{ + soundsystem->cmd |= MIX; +} + +void SendCommandToArm7(u32 command) +{ + while (REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL); + if (REG_IPC_FIFO_CR & IPC_FIFO_ERROR) + { + REG_IPC_FIFO_CR |= IPC_FIFO_SEND_CLEAR; + } + + REG_IPC_FIFO_TX = command; +} diff --git a/src/audio/nds/soundcommon.h b/src/audio/nds/soundcommon.h new file mode 100644 index 0000000..81827df --- /dev/null +++ b/src/audio/nds/soundcommon.h @@ -0,0 +1,80 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef __SOUNDCOMMON_H +#define __SOUNDCOMMON_H + +#include <nds.h> + +#define CLOCK (1 << 25) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum +{ + NONE = 0, + INIT = 1, + MIX = 2, + MIXING = 4, + STOP = 8 +}CommandType; + +typedef enum +{ + FIFO_NONE = 0, + UPDATEON_ARM9 = 1, + MIXCOMPLETE_ONARM9 = 2, +}FifoType; + +typedef struct +{ + s8 *mixbuffer;//,*soundbuffer; + u32 rate; + u32 buffersize; + u32 cmd; + u8 channel,format; + u32 soundcursor,numsamples; + s32 prevtimer; + s16 period; +}S_SoundSystem; + +#define soundsystem ((S_SoundSystem*)((u32)(IPC)+sizeof(TransferRegion))) + +#ifdef ARM9 +extern void SoundSystemInit(u32 rate,u32 buffersize,u8 channel,u8 format); +extern void SoundStartMixer(void); +extern void SendCommandToArm7(u32 command); +#else +extern void SoundVBlankIrq(void); +extern void SoundSwapAndMix(void); +extern void SoundSetTimer(int period); +extern void SoundFifoHandler(void); +extern void SendCommandToArm9(u32 command); +#endif + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/audio/nto/SDL_nto_audio.c b/src/audio/nto/SDL_nto_audio.c new file mode 100644 index 0000000..612787c --- /dev/null +++ b/src/audio/nto/SDL_nto_audio.c @@ -0,0 +1,507 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sched.h> +#include <sys/select.h> +#include <sys/neutrino.h> +#include <sys/asoundlib.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "SDL_nto_audio.h" + +/* The tag name used by NTO audio */ +#define DRIVER_NAME "qsa-nto" + +/* default channel communication parameters */ +#define DEFAULT_CPARAMS_RATE 22050 +#define DEFAULT_CPARAMS_VOICES 1 +/* FIXME: need to add in the near future flexible logic with frag_size and frags count */ +#define DEFAULT_CPARAMS_FRAG_SIZE 4096 +#define DEFAULT_CPARAMS_FRAGS_MIN 1 +#define DEFAULT_CPARAMS_FRAGS_MAX 1 + +/* Open the audio device for playback, and don't block if busy */ +#define OPEN_FLAGS SND_PCM_OPEN_PLAYBACK + +#define QSA_NO_WORKAROUNDS 0x00000000 +#define QSA_MMAP_WORKAROUND 0x00000001 + +struct BuggyCards +{ + char* cardname; + unsigned long bugtype; +}; + +#define QSA_WA_CARDS 3 + +struct BuggyCards buggycards[QSA_WA_CARDS]= +{ + {"Sound Blaster Live!", QSA_MMAP_WORKAROUND}, + {"Vortex 8820", QSA_MMAP_WORKAROUND}, + {"Vortex 8830", QSA_MMAP_WORKAROUND}, +}; + +/* Audio driver functions */ +static void NTO_ThreadInit(_THIS); +static int NTO_OpenAudio(_THIS, SDL_AudioSpec* spec); +static void NTO_WaitAudio(_THIS); +static void NTO_PlayAudio(_THIS); +static Uint8* NTO_GetAudioBuf(_THIS); +static void NTO_CloseAudio(_THIS); + +/* card names check to apply the workarounds */ +static int NTO_CheckBuggyCards(_THIS, unsigned long checkfor) +{ + char scardname[33]; + int it; + + if (snd_card_get_name(cardno, scardname, 32)<0) + { + return 0; + } + + for (it=0; it<QSA_WA_CARDS; it++) + { + if (SDL_strcmp(buggycards[it].cardname, scardname)==0) + { + if (buggycards[it].bugtype==checkfor) + { + return 1; + } + } + } + + return 0; +} + +static void NTO_ThreadInit(_THIS) +{ + int status; + struct sched_param param; + + /* increasing default 10 priority to 25 to avoid jerky sound */ + status=SchedGet(0, 0, ¶m); + param.sched_priority=param.sched_curpriority+15; + status=SchedSet(0, 0, SCHED_NOCHANGE, ¶m); +} + +/* PCM transfer channel parameters initialize function */ +static void NTO_InitAudioParams(snd_pcm_channel_params_t* cpars) +{ + SDL_memset(cpars, 0, sizeof(snd_pcm_channel_params_t)); + + cpars->channel = SND_PCM_CHANNEL_PLAYBACK; + cpars->mode = SND_PCM_MODE_BLOCK; + cpars->start_mode = SND_PCM_START_DATA; + cpars->stop_mode = SND_PCM_STOP_STOP; + cpars->format.format = SND_PCM_SFMT_S16_LE; + cpars->format.interleave = 1; + cpars->format.rate = DEFAULT_CPARAMS_RATE; + cpars->format.voices = DEFAULT_CPARAMS_VOICES; + cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE; + cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN; + cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX; +} + +static int NTO_AudioAvailable(void) +{ + /* See if we can open a nonblocking channel. + Return value '1' means we can. + Return value '0' means we cannot. */ + + int available; + int rval; + snd_pcm_t* handle; + + available = 0; + handle = NULL; + + rval = snd_pcm_open_preferred(&handle, NULL, NULL, OPEN_FLAGS); + + if (rval >= 0) + { + available = 1; + + if ((rval = snd_pcm_close(handle)) < 0) + { + SDL_SetError("NTO_AudioAvailable(): snd_pcm_close failed: %s\n", snd_strerror(rval)); + available = 0; + } + } + else + { + SDL_SetError("NTO_AudioAvailable(): there are no available audio devices.\n"); + } + + return (available); +} + +static void NTO_DeleteAudioDevice(SDL_AudioDevice *device) +{ + if ((device)&&(device->hidden)) + { + SDL_free(device->hidden); + } + if (device) + { + SDL_free(device); + } +} + +static SDL_AudioDevice* NTO_CreateAudioDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if (this) + { + SDL_memset(this, 0, sizeof(SDL_AudioDevice)); + this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(struct SDL_PrivateAudioData)); + } + if ((this == NULL) || (this->hidden == NULL)) + { + SDL_OutOfMemory(); + if (this) + { + SDL_free(this); + } + return (0); + } + SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData)); + audio_handle = NULL; + + /* Set the function pointers */ + this->ThreadInit = NTO_ThreadInit; + this->OpenAudio = NTO_OpenAudio; + this->WaitAudio = NTO_WaitAudio; + this->PlayAudio = NTO_PlayAudio; + this->GetAudioBuf = NTO_GetAudioBuf; + this->CloseAudio = NTO_CloseAudio; + + this->free = NTO_DeleteAudioDevice; + + return this; +} + +AudioBootStrap QNXNTOAUDIO_bootstrap = +{ + DRIVER_NAME, "QNX6 QSA-NTO Audio", + NTO_AudioAvailable, + NTO_CreateAudioDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void NTO_WaitAudio(_THIS) +{ + fd_set wfds; + int selectret; + + FD_ZERO(&wfds); + FD_SET(audio_fd, &wfds); + + do { + selectret=select(audio_fd + 1, NULL, &wfds, NULL, NULL); + switch (selectret) + { + case -1: + case 0: SDL_SetError("NTO_WaitAudio(): select() failed: %s\n", strerror(errno)); + return; + default: if (FD_ISSET(audio_fd, &wfds)) + { + return; + } + break; + } + } while(1); +} + +static void NTO_PlayAudio(_THIS) +{ + int written, rval; + int towrite; + void* pcmbuffer; + + if (!this->enabled) + { + return; + } + + towrite = this->spec.size; + pcmbuffer = pcm_buf; + + /* Write the audio data, checking for EAGAIN (buffer full) and underrun */ + do { + written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite); + if (written != towrite) + { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) + { + /* Let a little CPU time go by and try to write again */ + SDL_Delay(1); + /* if we wrote some data */ + towrite -= written; + pcmbuffer += written * this->spec.channels; + continue; + } + else + { + if ((errno == EINVAL) || (errno == EIO)) + { + SDL_memset(&cstatus, 0, sizeof(cstatus)); + cstatus.channel = SND_PCM_CHANNEL_PLAYBACK; + if ((rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0) + { + SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_status failed: %s\n", snd_strerror(rval)); + return; + } + if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY)) + { + if ((rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) + { + SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval)); + return; + } + } + continue; + } + else + { + return; + } + } + } + else + { + /* we wrote all remaining data */ + towrite -= written; + pcmbuffer += written * this->spec.channels; + } + } while ((towrite > 0) && (this->enabled)); + + /* If we couldn't write, assume fatal error for now */ + if (towrite != 0) + { + this->enabled = 0; + } + + return; +} + +static Uint8* NTO_GetAudioBuf(_THIS) +{ + return pcm_buf; +} + +static void NTO_CloseAudio(_THIS) +{ + int rval; + + this->enabled = 0; + + if (audio_handle != NULL) + { + if ((rval = snd_pcm_plugin_flush(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) + { + SDL_SetError("NTO_CloseAudio(): snd_pcm_plugin_flush failed: %s\n", snd_strerror(rval)); + return; + } + if ((rval = snd_pcm_close(audio_handle)) < 0) + { + SDL_SetError("NTO_CloseAudio(): snd_pcm_close failed: %s\n",snd_strerror(rval)); + return; + } + audio_handle = NULL; + } +} + +static int NTO_OpenAudio(_THIS, SDL_AudioSpec* spec) +{ + int rval; + int format; + Uint16 test_format; + int found; + + audio_handle = NULL; + this->enabled = 0; + + if (pcm_buf != NULL) + { + SDL_FreeAudioMem(pcm_buf); + pcm_buf = NULL; + } + + /* initialize channel transfer parameters to default */ + NTO_InitAudioParams(&cparams); + + /* Open the audio device */ + rval = snd_pcm_open_preferred(&audio_handle, &cardno, &deviceno, OPEN_FLAGS); + if (rval < 0) + { + SDL_SetError("NTO_OpenAudio(): snd_pcm_open failed: %s\n", snd_strerror(rval)); + return (-1); + } + + if (!NTO_CheckBuggyCards(this, QSA_MMAP_WORKAROUND)) + { + /* enable count status parameter */ + if ((rval = snd_pcm_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP)) < 0) + { + SDL_SetError("snd_pcm_plugin_set_disable failed: %s\n", snd_strerror(rval)); + return (-1); + } + } + + /* Try for a closest match on audio format */ + format = 0; + /* can't use format as SND_PCM_SFMT_U8 = 0 in nto */ + found = 0; + + for (test_format=SDL_FirstAudioFormat(spec->format); !found ;) + { + /* if match found set format to equivalent ALSA format */ + switch (test_format) + { + case AUDIO_U8: + format = SND_PCM_SFMT_U8; + found = 1; + break; + case AUDIO_S8: + format = SND_PCM_SFMT_S8; + found = 1; + break; + case AUDIO_S16LSB: + format = SND_PCM_SFMT_S16_LE; + found = 1; + break; + case AUDIO_S16MSB: + format = SND_PCM_SFMT_S16_BE; + found = 1; + break; + case AUDIO_U16LSB: + format = SND_PCM_SFMT_U16_LE; + found = 1; + break; + case AUDIO_U16MSB: + format = SND_PCM_SFMT_U16_BE; + found = 1; + break; + default: + break; + } + + if (!found) + { + test_format = SDL_NextAudioFormat(); + } + } + + /* assumes test_format not 0 on success */ + if (test_format == 0) + { + SDL_SetError("NTO_OpenAudio(): Couldn't find any hardware audio formats"); + return (-1); + } + + spec->format = test_format; + + /* Set the audio format */ + cparams.format.format = format; + + /* Set mono or stereo audio (currently only two channels supported) */ + cparams.format.voices = spec->channels; + + /* Set rate */ + cparams.format.rate = spec->freq; + + /* Setup the transfer parameters according to cparams */ + rval = snd_pcm_plugin_params(audio_handle, &cparams); + if (rval < 0) + { + SDL_SetError("NTO_OpenAudio(): snd_pcm_channel_params failed: %s\n", snd_strerror(rval)); + return (-1); + } + + /* Make sure channel is setup right one last time */ + SDL_memset(&csetup, 0x00, sizeof(csetup)); + csetup.channel = SND_PCM_CHANNEL_PLAYBACK; + if (snd_pcm_plugin_setup(audio_handle, &csetup) < 0) + { + SDL_SetError("NTO_OpenAudio(): Unable to setup playback channel\n"); + return -1; + } + + + /* Calculate the final parameters for this audio specification */ + SDL_CalculateAudioSpec(spec); + + pcm_len = spec->size; + + if (pcm_len==0) + { + pcm_len = csetup.buf.block.frag_size * spec->channels * (snd_pcm_format_width(format)/8); + } + + /* Allocate memory to the audio buffer and initialize with silence (Note that + buffer size must be a multiple of fragment size, so find closest multiple) + */ + pcm_buf = (Uint8*)SDL_AllocAudioMem(pcm_len); + if (pcm_buf == NULL) + { + SDL_SetError("NTO_OpenAudio(): pcm buffer allocation failed\n"); + return (-1); + } + SDL_memset(pcm_buf, spec->silence, pcm_len); + + /* get the file descriptor */ + if ((audio_fd = snd_pcm_file_descriptor(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0) + { + SDL_SetError("NTO_OpenAudio(): snd_pcm_file_descriptor failed with error code: %s\n", snd_strerror(rval)); + return (-1); + } + + /* Trigger audio playback */ + rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK); + if (rval < 0) + { + SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval)); + return (-1); + } + + this->enabled = 1; + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + /* We're really ready to rock and roll. :-) */ + return (0); +} diff --git a/src/audio/nto/SDL_nto_audio.h b/src/audio/nto/SDL_nto_audio.h new file mode 100644 index 0000000..cae9225 --- /dev/null +++ b/src/audio/nto/SDL_nto_audio.h @@ -0,0 +1,68 @@ +/* + SDL - Simple DirectMedia Layer + 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 +*/ +#include "SDL_config.h" + +#ifndef __SDL_NTO_AUDIO_H__ +#define __SDL_NTO_AUDIO_H__ + +#include <sys/asoundlib.h> + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the audio functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData +{ + /* The audio device handle */ + int cardno; + int deviceno; + snd_pcm_t* audio_handle; + + /* The audio file descriptor */ + int audio_fd; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8* pcm_buf; + Uint32 pcm_len; + + /* QSA parameters */ + snd_pcm_channel_status_t cstatus; + snd_pcm_channel_params_t cparams; + snd_pcm_channel_setup_t csetup; +}; + +#define cardno (this->hidden->cardno) +#define deviceno (this->hidden->deviceno) +#define audio_handle (this->hidden->audio_handle) +#define audio_fd (this->hidden->audio_fd) +#define parent (this->hidden->parent) +#define pcm_buf (this->hidden->pcm_buf) +#define pcm_len (this->hidden->pcm_len) +#define cstatus (this->hidden->cstatus) +#define cparams (this->hidden->cparams) +#define csetup (this->hidden->csetup) + +#endif /* __SDL_NTO_AUDIO_H__ */ diff --git a/src/audio/paudio/SDL_paudio.c b/src/audio/paudio/SDL_paudio.c new file mode 100644 index 0000000..6795e1e --- /dev/null +++ b/src/audio/paudio/SDL_paudio.c @@ -0,0 +1,511 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Carsten Griwodz + griff@kom.tu-darmstadt.de + + based on linux/SDL_dspaudio.c by Sam Lantinga +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_paudio.h" + +#define DEBUG_AUDIO 1 + +/* A conflict within AIX 4.3.3 <sys/> headers and probably others as well. + * I guess nobody ever uses audio... Shame over AIX header files. */ +#include <sys/machine.h> +#undef BIG_ENDIAN +#include <sys/audio.h> + +/* The tag name used by paud audio */ +#define Paud_DRIVER_NAME "paud" + +/* Open the audio device for playback, and don't block if busy */ +/* #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) */ +#define OPEN_FLAGS O_WRONLY + +/* Audio driver functions */ +static int Paud_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void Paud_WaitAudio(_THIS); +static void Paud_PlayAudio(_THIS); +static Uint8 *Paud_GetAudioBuf(_THIS); +static void Paud_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + int fd; + int available; + + available = 0; + fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0); + if ( fd >= 0 ) { + available = 1; + close(fd); + } + return(available); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + audio_fd = -1; + + /* Set the function pointers */ + this->OpenAudio = Paud_OpenAudio; + this->WaitAudio = Paud_WaitAudio; + this->PlayAudio = Paud_PlayAudio; + this->GetAudioBuf = Paud_GetAudioBuf; + this->CloseAudio = Paud_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap Paud_bootstrap = { + Paud_DRIVER_NAME, "AIX Paudio", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void Paud_WaitAudio(_THIS) +{ + fd_set fdset; + + /* See if we need to use timed audio synchronization */ + if ( frame_ticks ) { + /* Use timer for general audio synchronization */ + Sint32 ticks; + + ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS; + if ( ticks > 0 ) { + SDL_Delay(ticks); + } + } else { + audio_buffer paud_bufinfo; + + /* Use select() for audio synchronization */ + struct timeval timeout; + FD_ZERO(&fdset); + FD_SET(audio_fd, &fdset); + + if ( ioctl(audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0 ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Couldn't get audio buffer information\n"); +#endif + timeout.tv_sec = 10; + timeout.tv_usec = 0; + } else { + long ms_in_buf = paud_bufinfo.write_buf_time; + timeout.tv_sec = ms_in_buf/1000; + ms_in_buf = ms_in_buf - timeout.tv_sec*1000; + timeout.tv_usec = ms_in_buf*1000; +#ifdef DEBUG_AUDIO + fprintf( stderr, + "Waiting for write_buf_time=%ld,%ld\n", + timeout.tv_sec, + timeout.tv_usec ); +#endif + } + +#ifdef DEBUG_AUDIO + fprintf(stderr, "Waiting for audio to get ready\n"); +#endif + if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) { + const char *message = "Audio timeout - buggy audio driver? (disabled)"; + /* + * In general we should never print to the screen, + * but in this case we have no other way of letting + * the user know what happened. + */ + fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message); + this->enabled = 0; + /* Don't try to close - may hang */ + audio_fd = -1; +#ifdef DEBUG_AUDIO + fprintf(stderr, "Done disabling audio\n"); +#endif + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Ready!\n"); +#endif + } +} + +static void Paud_PlayAudio(_THIS) +{ + int written; + + /* Write the audio data, checking for EAGAIN on broken audio drivers */ + do { + written = write(audio_fd, mixbuf, mixlen); + if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) { + SDL_Delay(1); /* Let a little CPU time go by */ + } + } while ( (written < 0) && + ((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) ); + + /* If timer synchronization is enabled, set the next write frame */ + if ( frame_ticks ) { + next_frame += frame_ticks; + } + + /* If we couldn't write, assume fatal error for now */ + if ( written < 0 ) { + this->enabled = 0; + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Wrote %d bytes of audio data\n", written); +#endif +} + +static Uint8 *Paud_GetAudioBuf(_THIS) +{ + return mixbuf; +} + +static void Paud_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( audio_fd >= 0 ) { + close(audio_fd); + audio_fd = -1; + } +} + +static int Paud_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + char audiodev[1024]; + int format; + int bytes_per_sample; + Uint16 test_format; + audio_init paud_init; + audio_buffer paud_bufinfo; + audio_status paud_status; + audio_control paud_control; + audio_change paud_change; + + /* Reset the timer synchronization flag */ + frame_ticks = 0.0; + + /* Open the audio device */ + audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0); + if ( audio_fd < 0 ) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return -1; + } + + /* + * We can't set the buffer size - just ask the device for the maximum + * that we can have. + */ + if ( ioctl(audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0 ) { + SDL_SetError("Couldn't get audio buffer information"); + return -1; + } + + mixbuf = NULL; + + if ( spec->channels > 1 ) + spec->channels = 2; + else + spec->channels = 1; + + /* + * Fields in the audio_init structure: + * + * Ignored by us: + * + * paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only? + * paud.slot_number; * slot number of the adapter + * paud.device_id; * adapter identification number + * + * Input: + * + * paud.srate; * the sampling rate in Hz + * paud.bits_per_sample; * 8, 16, 32, ... + * paud.bsize; * block size for this rate + * paud.mode; * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX + * paud.channels; * 1=mono, 2=stereo + * paud.flags; * FIXED - fixed length data + * * LEFT_ALIGNED, RIGHT_ALIGNED (var len only) + * * TWOS_COMPLEMENT - 2's complement data + * * SIGNED - signed? comment seems wrong in sys/audio.h + * * BIG_ENDIAN + * paud.operation; * PLAY, RECORD + * + * Output: + * + * paud.flags; * PITCH - pitch is supported + * * INPUT - input is supported + * * OUTPUT - output is supported + * * MONITOR - monitor is supported + * * VOLUME - volume is supported + * * VOLUME_DELAY - volume delay is supported + * * BALANCE - balance is supported + * * BALANCE_DELAY - balance delay is supported + * * TREBLE - treble control is supported + * * BASS - bass control is supported + * * BESTFIT_PROVIDED - best fit returned + * * LOAD_CODE - DSP load needed + * paud.rc; * NO_PLAY - DSP code can't do play requests + * * NO_RECORD - DSP code can't do record requests + * * INVALID_REQUEST - request was invalid + * * CONFLICT - conflict with open's flags + * * OVERLOADED - out of DSP MIPS or memory + * paud.position_resolution; * smallest increment for position + */ + + paud_init.srate = spec->freq; + paud_init.mode = PCM; + paud_init.operation = PLAY; + paud_init.channels = spec->channels; + + /* Try for a closest match on audio format */ + format = 0; + for ( test_format = SDL_FirstAudioFormat(spec->format); + ! format && test_format; ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Trying format 0x%4.4x\n", test_format); +#endif + switch ( test_format ) { + case AUDIO_U8: + bytes_per_sample = 1; + paud_init.bits_per_sample = 8; + paud_init.flags = TWOS_COMPLEMENT | FIXED; + format = 1; + break; + case AUDIO_S8: + bytes_per_sample = 1; + paud_init.bits_per_sample = 8; + paud_init.flags = SIGNED | + TWOS_COMPLEMENT | FIXED; + format = 1; + break; + case AUDIO_S16LSB: + bytes_per_sample = 2; + paud_init.bits_per_sample = 16; + paud_init.flags = SIGNED | + TWOS_COMPLEMENT | FIXED; + format = 1; + break; + case AUDIO_S16MSB: + bytes_per_sample = 2; + paud_init.bits_per_sample = 16; + paud_init.flags = BIG_ENDIAN | + SIGNED | + TWOS_COMPLEMENT | FIXED; + format = 1; + break; + case AUDIO_U16LSB: + bytes_per_sample = 2; + paud_init.bits_per_sample = 16; + paud_init.flags = TWOS_COMPLEMENT | FIXED; + format = 1; + break; + case AUDIO_U16MSB: + bytes_per_sample = 2; + paud_init.bits_per_sample = 16; + paud_init.flags = BIG_ENDIAN | + TWOS_COMPLEMENT | FIXED; + format = 1; + break; + default: + break; + } + if ( ! format ) { + test_format = SDL_NextAudioFormat(); + } + } + if ( format == 0 ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Couldn't find any hardware audio formats\n"); +#endif + SDL_SetError("Couldn't find any hardware audio formats"); + return -1; + } + spec->format = test_format; + + /* + * We know the buffer size and the max number of subsequent writes + * that can be pending. If more than one can pend, allow the application + * to do something like double buffering between our write buffer and + * the device's own buffer that we are filling with write() anyway. + * + * We calculate spec->samples like this because SDL_CalculateAudioSpec() + * will give put paud_bufinfo.write_buf_cap (or paud_bufinfo.write_buf_cap/2) + * into spec->size in return. + */ + if ( paud_bufinfo.request_buf_cap == 1 ) + { + spec->samples = paud_bufinfo.write_buf_cap + / bytes_per_sample + / spec->channels; + } + else + { + spec->samples = paud_bufinfo.write_buf_cap + / bytes_per_sample + / spec->channels + / 2; + } + paud_init.bsize = bytes_per_sample * spec->channels; + + SDL_CalculateAudioSpec(spec); + + /* + * The AIX paud device init can't modify the values of the audio_init + * structure that we pass to it. So we don't need any recalculation + * of this stuff and no reinit call as in linux dsp and dma code. + * + * /dev/paud supports all of the encoding formats, so we don't need + * to do anything like reopening the device, either. + */ + if ( ioctl(audio_fd, AUDIO_INIT, &paud_init) < 0 ) { + switch ( paud_init.rc ) + { + case 1 : + SDL_SetError("Couldn't set audio format: DSP can't do play requests"); + return -1; + break; + case 2 : + SDL_SetError("Couldn't set audio format: DSP can't do record requests"); + return -1; + break; + case 4 : + SDL_SetError("Couldn't set audio format: request was invalid"); + return -1; + break; + case 5 : + SDL_SetError("Couldn't set audio format: conflict with open's flags"); + return -1; + break; + case 6 : + SDL_SetError("Couldn't set audio format: out of DSP MIPS or memory"); + return -1; + break; + default : + SDL_SetError("Couldn't set audio format: not documented in sys/audio.h"); + return -1; + break; + } + } + + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); + if ( mixbuf == NULL ) { + return -1; + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* + * Set some paramters: full volume, first speaker that we can find. + * Ignore the other settings for now. + */ + paud_change.input = AUDIO_IGNORE; /* the new input source */ + paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */ + paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */ + paud_change.volume = 0x7fffffff; /* volume level [0-0x7fffffff] */ + paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */ + paud_change.balance = 0x3fffffff; /* the new balance */ + paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */ + paud_change.treble = AUDIO_IGNORE; /* the new treble state */ + paud_change.bass = AUDIO_IGNORE; /* the new bass state */ + paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */ + + paud_control.ioctl_request = AUDIO_CHANGE; + paud_control.request_info = (char*)&paud_change; + if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Can't change audio display settings\n" ); +#endif + } + + /* + * Tell the device to expect data. Actual start will wait for + * the first write() call. + */ + paud_control.ioctl_request = AUDIO_START; + paud_control.position = 0; + if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Can't start audio play\n" ); +#endif + SDL_SetError("Can't start audio play"); + return -1; + } + + /* Check to see if we need to use select() workaround */ + { char *workaround; + workaround = SDL_getenv("SDL_DSP_NOSELECT"); + if ( workaround ) { + frame_ticks = (float)(spec->samples*1000)/spec->freq; + next_frame = SDL_GetTicks()+frame_ticks; + } + } + + /* Get the parent process id (we're the parent of the audio thread) */ + parent = getpid(); + + /* We're ready to rock and roll. :-) */ + return 0; +} + diff --git a/src/audio/paudio/SDL_paudio.h b/src/audio/paudio/SDL_paudio.h new file mode 100644 index 0000000..6e10bbb --- /dev/null +++ b/src/audio/paudio/SDL_paudio.h @@ -0,0 +1,57 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_paudaudio_h +#define _SDL_paudaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + int audio_fd; + + /* The parent process id, to detect when application quits */ + pid_t parent; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; + + /* Support for audio timing using a timer, in addition to select() */ + float frame_ticks; + float next_frame; +}; +#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ + +/* Old variable names */ +#define audio_fd (this->hidden->audio_fd) +#define parent (this->hidden->parent) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) +#define frame_ticks (this->hidden->frame_ticks) +#define next_frame (this->hidden->next_frame) + +#endif /* _SDL_paudaudio_h */ diff --git a/src/audio/pulse/SDL_pulseaudio.c b/src/audio/pulse/SDL_pulseaudio.c new file mode 100644 index 0000000..21e51cd --- /dev/null +++ b/src/audio/pulse/SDL_pulseaudio.c @@ -0,0 +1,534 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Stéphan Kochen + stephan@kochen.nl + + Based on parts of the ALSA and ESounD output drivers. +*/ +#include "SDL_config.h" + +/* Allow access to an PulseAudio network stream mixing buffer */ + +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <errno.h> +#include <pulse/pulseaudio.h> +#include <pulse/simple.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_pulseaudio.h" + +#ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC +#include "SDL_name.h" +#include "SDL_loadso.h" +#else +#define SDL_NAME(X) X +#endif + +/* The tag name used by the driver */ +#define PULSE_DRIVER_NAME "pulse" + +/* Audio driver functions */ +static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void PULSE_WaitAudio(_THIS); +static void PULSE_PlayAudio(_THIS); +static Uint8 *PULSE_GetAudioBuf(_THIS); +static void PULSE_CloseAudio(_THIS); +static void PULSE_WaitDone(_THIS); + +#ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC + +static const char *pulse_library = SDL_AUDIO_DRIVER_PULSE_DYNAMIC; +static void *pulse_handle = NULL; +static int pulse_loaded = 0; + +static pa_simple* (*SDL_NAME(pa_simple_new))( + const char *server, + const char *name, + pa_stream_direction_t dir, + const char *dev, + const char *stream_name, + const pa_sample_spec *ss, + const pa_channel_map *map, + const pa_buffer_attr *attr, + int *error +); +static void (*SDL_NAME(pa_simple_free))(pa_simple *s); + +static pa_channel_map* (*SDL_NAME(pa_channel_map_init_auto))( + pa_channel_map *m, + unsigned channels, + pa_channel_map_def_t def +); + +pa_mainloop * (*SDL_NAME(pa_mainloop_new))(void); +pa_mainloop_api * (*SDL_NAME(pa_mainloop_get_api))(pa_mainloop *m); +int (*SDL_NAME(pa_mainloop_iterate))(pa_mainloop *m, int block, int *retval); +void (*SDL_NAME(pa_mainloop_free))(pa_mainloop *m); + +pa_operation_state_t (*SDL_NAME(pa_operation_get_state))(pa_operation *o); +void (*SDL_NAME(pa_operation_cancel))(pa_operation *o); +void (*SDL_NAME(pa_operation_unref))(pa_operation *o); + +pa_context * (*SDL_NAME(pa_context_new))( + pa_mainloop_api *m, const char *name); +int (*SDL_NAME(pa_context_connect))( + pa_context *c, const char *server, + pa_context_flags_t flags, const pa_spawn_api *api); +pa_context_state_t (*SDL_NAME(pa_context_get_state))(pa_context *c); +void (*SDL_NAME(pa_context_disconnect))(pa_context *c); +void (*SDL_NAME(pa_context_unref))(pa_context *c); + +pa_stream * (*SDL_NAME(pa_stream_new))(pa_context *c, + const char *name, const pa_sample_spec *ss, const pa_channel_map *map); +int (*SDL_NAME(pa_stream_connect_playback))(pa_stream *s, const char *dev, + const pa_buffer_attr *attr, pa_stream_flags_t flags, + pa_cvolume *volume, pa_stream *sync_stream); +pa_stream_state_t (*SDL_NAME(pa_stream_get_state))(pa_stream *s); +size_t (*SDL_NAME(pa_stream_writable_size))(pa_stream *s); +int (*SDL_NAME(pa_stream_write))(pa_stream *s, const void *data, size_t nbytes, + pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek); +pa_operation * (*SDL_NAME(pa_stream_drain))(pa_stream *s, + pa_stream_success_cb_t cb, void *userdata); +int (*SDL_NAME(pa_stream_disconnect))(pa_stream *s); +void (*SDL_NAME(pa_stream_unref))(pa_stream *s); + +static struct { + const char *name; + void **func; +} pulse_functions[] = { + { "pa_simple_new", + (void **)&SDL_NAME(pa_simple_new) }, + { "pa_simple_free", + (void **)&SDL_NAME(pa_simple_free) }, + { "pa_channel_map_init_auto", + (void **)&SDL_NAME(pa_channel_map_init_auto) }, + { "pa_mainloop_new", + (void **)&SDL_NAME(pa_mainloop_new) }, + { "pa_mainloop_get_api", + (void **)&SDL_NAME(pa_mainloop_get_api) }, + { "pa_mainloop_iterate", + (void **)&SDL_NAME(pa_mainloop_iterate) }, + { "pa_mainloop_free", + (void **)&SDL_NAME(pa_mainloop_free) }, + { "pa_operation_get_state", + (void **)&SDL_NAME(pa_operation_get_state) }, + { "pa_operation_cancel", + (void **)&SDL_NAME(pa_operation_cancel) }, + { "pa_operation_unref", + (void **)&SDL_NAME(pa_operation_unref) }, + { "pa_context_new", + (void **)&SDL_NAME(pa_context_new) }, + { "pa_context_connect", + (void **)&SDL_NAME(pa_context_connect) }, + { "pa_context_get_state", + (void **)&SDL_NAME(pa_context_get_state) }, + { "pa_context_disconnect", + (void **)&SDL_NAME(pa_context_disconnect) }, + { "pa_context_unref", + (void **)&SDL_NAME(pa_context_unref) }, + { "pa_stream_new", + (void **)&SDL_NAME(pa_stream_new) }, + { "pa_stream_connect_playback", + (void **)&SDL_NAME(pa_stream_connect_playback) }, + { "pa_stream_get_state", + (void **)&SDL_NAME(pa_stream_get_state) }, + { "pa_stream_writable_size", + (void **)&SDL_NAME(pa_stream_writable_size) }, + { "pa_stream_write", + (void **)&SDL_NAME(pa_stream_write) }, + { "pa_stream_drain", + (void **)&SDL_NAME(pa_stream_drain) }, + { "pa_stream_disconnect", + (void **)&SDL_NAME(pa_stream_disconnect) }, + { "pa_stream_unref", + (void **)&SDL_NAME(pa_stream_unref) }, +}; + +static void UnloadPulseLibrary() +{ + if ( pulse_loaded ) { + SDL_UnloadObject(pulse_handle); + pulse_handle = NULL; + pulse_loaded = 0; + } +} + +static int LoadPulseLibrary(void) +{ + int i, retval = -1; + + pulse_handle = SDL_LoadObject(pulse_library); + if ( pulse_handle ) { + pulse_loaded = 1; + retval = 0; + for ( i=0; i<SDL_arraysize(pulse_functions); ++i ) { + *pulse_functions[i].func = SDL_LoadFunction(pulse_handle, pulse_functions[i].name); + if ( !*pulse_functions[i].func ) { + retval = -1; + UnloadPulseLibrary(); + break; + } + } + } + return retval; +} + +#else + +static void UnloadPulseLibrary() +{ + return; +} + +static int LoadPulseLibrary(void) +{ + return 0; +} + +#endif /* SDL_AUDIO_DRIVER_PULSE_DYNAMIC */ + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + pa_sample_spec paspec; + pa_simple *connection; + int available; + + available = 0; + if ( LoadPulseLibrary() < 0 ) { + return available; + } + + /* Connect with a dummy format. */ + paspec.format = PA_SAMPLE_U8; + paspec.rate = 11025; + paspec.channels = 1; + connection = SDL_NAME(pa_simple_new)( + NULL, /* server */ + "Test stream", /* application name */ + PA_STREAM_PLAYBACK, /* playback mode */ + NULL, /* device on the server */ + "Simple DirectMedia Layer", /* stream description */ + &paspec, /* sample format spec */ + NULL, /* channel map */ + NULL, /* buffering attributes */ + NULL /* error code */ + ); + if ( connection != NULL ) { + available = 1; + SDL_NAME(pa_simple_free)(connection); + } + + UnloadPulseLibrary(); + return(available); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); + UnloadPulseLibrary(); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + LoadPulseLibrary(); + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = PULSE_OpenAudio; + this->WaitAudio = PULSE_WaitAudio; + this->PlayAudio = PULSE_PlayAudio; + this->GetAudioBuf = PULSE_GetAudioBuf; + this->CloseAudio = PULSE_CloseAudio; + this->WaitDone = PULSE_WaitDone; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap PULSE_bootstrap = { + PULSE_DRIVER_NAME, "PulseAudio", + Audio_Available, Audio_CreateDevice +}; + +/* This function waits until it is possible to write a full sound buffer */ +static void PULSE_WaitAudio(_THIS) +{ + int size; + while(1) { + if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY || + SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY || + SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) { + this->enabled = 0; + return; + } + size = SDL_NAME(pa_stream_writable_size)(stream); + if (size >= mixlen) + return; + } +} + +static void PULSE_PlayAudio(_THIS) +{ + /* Write the audio data */ + if (SDL_NAME(pa_stream_write)(stream, mixbuf, mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0) + this->enabled = 0; +} + +static Uint8 *PULSE_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +static void PULSE_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( stream != NULL ) { + SDL_NAME(pa_stream_disconnect)(stream); + SDL_NAME(pa_stream_unref)(stream); + stream = NULL; + } + if (context != NULL) { + SDL_NAME(pa_context_disconnect)(context); + SDL_NAME(pa_context_unref)(context); + context = NULL; + } + if (mainloop != NULL) { + SDL_NAME(pa_mainloop_free)(mainloop); + mainloop = NULL; + } +} + +/* Try to get the name of the program */ +static char *get_progname(void) +{ +#ifdef __LINUX__ + char *progname = NULL; + FILE *fp; + static char temp[BUFSIZ]; + + SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid()); + fp = fopen(temp, "r"); + if ( fp != NULL ) { + if ( fgets(temp, sizeof(temp)-1, fp) ) { + progname = SDL_strrchr(temp, '/'); + if ( progname == NULL ) { + progname = temp; + } else { + progname = progname+1; + } + } + fclose(fp); + } + return(progname); +#elif defined(__NetBSD__) + return getprogname(); +#else + return("unknown"); +#endif +} + +static void stream_drain_complete(pa_stream *s, int success, void *userdata) { +} + +static void PULSE_WaitDone(_THIS) +{ + pa_operation *o; + + o = SDL_NAME(pa_stream_drain)(stream, stream_drain_complete, NULL); + if (!o) + return; + + while (SDL_NAME(pa_operation_get_state)(o) != PA_OPERATION_DONE) { + if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY || + SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY || + SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) { + SDL_NAME(pa_operation_cancel)(o); + break; + } + } + SDL_NAME(pa_operation_unref)(o); +} + +static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + int state; + Uint16 test_format; + pa_sample_spec paspec; + pa_buffer_attr paattr; + pa_channel_map pacmap; + pa_stream_flags_t flags = 0; + + paspec.format = PA_SAMPLE_INVALID; + for ( test_format = SDL_FirstAudioFormat(spec->format); test_format; ) { + switch ( test_format ) { + case AUDIO_U8: + paspec.format = PA_SAMPLE_U8; + break; + case AUDIO_S16LSB: + paspec.format = PA_SAMPLE_S16LE; + break; + case AUDIO_S16MSB: + paspec.format = PA_SAMPLE_S16BE; + break; + } + if ( paspec.format != PA_SAMPLE_INVALID ) + break; + } + if (paspec.format == PA_SAMPLE_INVALID ) { + SDL_SetError("Couldn't find any suitable audio formats"); + return(-1); + } + spec->format = test_format; + + paspec.channels = spec->channels; + paspec.rate = spec->freq; + + /* Calculate the final parameters for this audio specification */ +#ifdef PA_STREAM_ADJUST_LATENCY + spec->samples /= 2; /* Mix in smaller chunck to avoid underruns */ +#endif + SDL_CalculateAudioSpec(spec); + + /* Allocate mixing buffer */ + mixlen = spec->size; + mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen); + if ( mixbuf == NULL ) { + return(-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* Reduced prebuffering compared to the defaults. */ +#ifdef PA_STREAM_ADJUST_LATENCY + paattr.tlength = mixlen * 4; /* 2x original requested bufsize */ + paattr.prebuf = -1; + paattr.maxlength = -1; + paattr.minreq = mixlen; /* -1 can lead to pa_stream_writable_size() + >= mixlen never becoming true */ + flags = PA_STREAM_ADJUST_LATENCY; +#else + paattr.tlength = mixlen*2; + paattr.prebuf = mixlen*2; + paattr.maxlength = mixlen*2; + paattr.minreq = mixlen; +#endif + + /* The SDL ALSA output hints us that we use Windows' channel mapping */ + /* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */ + SDL_NAME(pa_channel_map_init_auto)( + &pacmap, spec->channels, PA_CHANNEL_MAP_WAVEEX); + + /* Set up a new main loop */ + if (!(mainloop = SDL_NAME(pa_mainloop_new)())) { + PULSE_CloseAudio(this); + SDL_SetError("pa_mainloop_new() failed"); + return(-1); + } + + mainloop_api = SDL_NAME(pa_mainloop_get_api)(mainloop); + if (!(context = SDL_NAME(pa_context_new)(mainloop_api, get_progname()))) { + PULSE_CloseAudio(this); + SDL_SetError("pa_context_new() failed"); + return(-1); + } + + /* Connect to the PulseAudio server */ + if (SDL_NAME(pa_context_connect)(context, NULL, 0, NULL) < 0) { + PULSE_CloseAudio(this); + SDL_SetError("Could not setup connection to PulseAudio"); + return(-1); + } + + do { + if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) { + PULSE_CloseAudio(this); + SDL_SetError("pa_mainloop_iterate() failed"); + return(-1); + } + state = SDL_NAME(pa_context_get_state)(context); + if (!PA_CONTEXT_IS_GOOD(state)) { + PULSE_CloseAudio(this); + SDL_SetError("Could not connect to PulseAudio"); + return(-1); + } + } while (state != PA_CONTEXT_READY); + + stream = SDL_NAME(pa_stream_new)( + context, + "Simple DirectMedia Layer", /* stream description */ + &paspec, /* sample format spec */ + &pacmap /* channel map */ + ); + if ( stream == NULL ) { + PULSE_CloseAudio(this); + SDL_SetError("Could not setup PulseAudio stream"); + return(-1); + } + + if (SDL_NAME(pa_stream_connect_playback)(stream, NULL, &paattr, flags, + NULL, NULL) < 0) { + PULSE_CloseAudio(this); + SDL_SetError("Could not connect PulseAudio stream"); + return(-1); + } + + do { + if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) { + PULSE_CloseAudio(this); + SDL_SetError("pa_mainloop_iterate() failed"); + return(-1); + } + state = SDL_NAME(pa_stream_get_state)(stream); + if (!PA_STREAM_IS_GOOD(state)) { + PULSE_CloseAudio(this); + SDL_SetError("Could not create to PulseAudio stream"); + return(-1); + } + } while (state != PA_STREAM_READY); + + return(0); +} diff --git a/src/audio/pulse/SDL_pulseaudio.h b/src/audio/pulse/SDL_pulseaudio.h new file mode 100644 index 0000000..27d27c6 --- /dev/null +++ b/src/audio/pulse/SDL_pulseaudio.h @@ -0,0 +1,71 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Stéphan Kochen + stephan@kochen.nl + + Based on parts of the ALSA and ESounD output drivers. +*/ +#include "SDL_config.h" + +#ifndef _SDL_pulseaudio_h +#define _SDL_pulseaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + pa_mainloop *mainloop; + pa_mainloop_api *mainloop_api; + pa_context *context; + pa_stream *stream; + + /* Raw mixing buffer */ + Uint8 *mixbuf; + int mixlen; +}; + +#if (PA_API_VERSION < 12) +/** Return non-zero if the passed state is one of the connected states */ +static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) { + return + x == PA_CONTEXT_CONNECTING || + x == PA_CONTEXT_AUTHORIZING || + x == PA_CONTEXT_SETTING_NAME || + x == PA_CONTEXT_READY; +} +/** Return non-zero if the passed state is one of the connected states */ +static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) { + return + x == PA_STREAM_CREATING || + x == PA_STREAM_READY; +} +#endif /* pulseaudio <= 0.9.10 */ + +/* Old variable names */ +#define mainloop (this->hidden->mainloop) +#define mainloop_api (this->hidden->mainloop_api) +#define context (this->hidden->context) +#define stream (this->hidden->stream) +#define mixbuf (this->hidden->mixbuf) +#define mixlen (this->hidden->mixlen) + +#endif /* _SDL_pulseaudio_h */ + diff --git a/src/audio/sun/SDL_sunaudio.c b/src/audio/sun/SDL_sunaudio.c new file mode 100644 index 0000000..4a57c68 --- /dev/null +++ b/src/audio/sun/SDL_sunaudio.c @@ -0,0 +1,432 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include <fcntl.h> +#include <errno.h> +#ifdef __NETBSD__ +#include <sys/ioctl.h> +#include <sys/audioio.h> +#endif +#ifdef __SVR4 +#include <sys/audioio.h> +#else +#include <sys/time.h> +#include <sys/types.h> +#endif +#include <unistd.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audiomem.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_sunaudio.h" + +/* Open the audio device for playback, and don't block if busy */ +#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) + +/* Audio driver functions */ +static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void DSP_WaitAudio(_THIS); +static void DSP_PlayAudio(_THIS); +static Uint8 *DSP_GetAudioBuf(_THIS); +static void DSP_CloseAudio(_THIS); + +static Uint8 snd2au(int sample); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + int fd; + int available; + + available = 0; + fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 1); + if ( fd >= 0 ) { + available = 1; + close(fd); + } + return(available); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + audio_fd = -1; + + /* Set the function pointers */ + this->OpenAudio = DSP_OpenAudio; + this->WaitAudio = DSP_WaitAudio; + this->PlayAudio = DSP_PlayAudio; + this->GetAudioBuf = DSP_GetAudioBuf; + this->CloseAudio = DSP_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap SUNAUDIO_bootstrap = { + "audio", "UNIX /dev/audio interface", + Audio_Available, Audio_CreateDevice +}; + +#ifdef DEBUG_AUDIO +void CheckUnderflow(_THIS) +{ +#ifdef AUDIO_GETINFO + audio_info_t info; + int left; + + ioctl(audio_fd, AUDIO_GETINFO, &info); + left = (written - info.play.samples); + if ( written && (left == 0) ) { + fprintf(stderr, "audio underflow!\n"); + } +#endif +} +#endif + +void DSP_WaitAudio(_THIS) +{ +#ifdef AUDIO_GETINFO +#define SLEEP_FUDGE 10 /* 10 ms scheduling fudge factor */ + audio_info_t info; + Sint32 left; + + ioctl(audio_fd, AUDIO_GETINFO, &info); + left = (written - info.play.samples); + if ( left > fragsize ) { + Sint32 sleepy; + + sleepy = ((left - fragsize)/frequency); + sleepy -= SLEEP_FUDGE; + if ( sleepy > 0 ) { + SDL_Delay(sleepy); + } + } +#else + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(audio_fd, &fdset); + select(audio_fd+1, NULL, &fdset, NULL, NULL); +#endif +} + +void DSP_PlayAudio(_THIS) +{ + /* Write the audio data */ + if ( ulaw_only ) { + /* Assuming that this->spec.freq >= 8000 Hz */ + int accum, incr, pos; + Uint8 *aubuf; + + accum = 0; + incr = this->spec.freq/8; + aubuf = ulaw_buf; + switch (audio_fmt & 0xFF) { + case 8: { + Uint8 *sndbuf; + + sndbuf = mixbuf; + for ( pos=0; pos < fragsize; ++pos ) { + *aubuf = snd2au((0x80-*sndbuf)*64); + accum += incr; + while ( accum > 0 ) { + accum -= 1000; + sndbuf += 1; + } + aubuf += 1; + } + } + break; + case 16: { + Sint16 *sndbuf; + + sndbuf = (Sint16 *)mixbuf; + for ( pos=0; pos < fragsize; ++pos ) { + *aubuf = snd2au(*sndbuf/4); + accum += incr; + while ( accum > 0 ) { + accum -= 1000; + sndbuf += 1; + } + aubuf += 1; + } + } + break; + } +#ifdef DEBUG_AUDIO + CheckUnderflow(this); +#endif + if ( write(audio_fd, ulaw_buf, fragsize) < 0 ) { + /* Assume fatal error, for now */ + this->enabled = 0; + } + written += fragsize; + } else { +#ifdef DEBUG_AUDIO + CheckUnderflow(this); +#endif + if ( write(audio_fd, mixbuf, this->spec.size) < 0 ) { + /* Assume fatal error, for now */ + this->enabled = 0; + } + written += fragsize; + } +} + +Uint8 *DSP_GetAudioBuf(_THIS) +{ + return(mixbuf); +} + +void DSP_CloseAudio(_THIS) +{ + if ( mixbuf != NULL ) { + SDL_FreeAudioMem(mixbuf); + mixbuf = NULL; + } + if ( ulaw_buf != NULL ) { + SDL_free(ulaw_buf); + ulaw_buf = NULL; + } + close(audio_fd); +} + +int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + char audiodev[1024]; +#ifdef AUDIO_SETINFO + int enc; +#endif + int desired_freq = spec->freq; + + /* Initialize our freeable variables, in case we fail*/ + audio_fd = -1; + mixbuf = NULL; + ulaw_buf = NULL; + + /* Determine the audio parameters from the AudioSpec */ + switch ( spec->format & 0xFF ) { + + case 8: { /* Unsigned 8 bit audio data */ + spec->format = AUDIO_U8; +#ifdef AUDIO_SETINFO + enc = AUDIO_ENCODING_LINEAR8; +#endif + } + break; + + case 16: { /* Signed 16 bit audio data */ + spec->format = AUDIO_S16SYS; +#ifdef AUDIO_SETINFO + enc = AUDIO_ENCODING_LINEAR; +#endif + } + break; + + default: { + SDL_SetError("Unsupported audio format"); + return(-1); + } + } + audio_fmt = spec->format; + + /* Open the audio device */ + audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 1); + if ( audio_fd < 0 ) { + SDL_SetError("Couldn't open %s: %s", audiodev, + strerror(errno)); + return(-1); + } + + ulaw_only = 0; /* modern Suns do support linear audio */ +#ifdef AUDIO_SETINFO + for(;;) { + audio_info_t info; + AUDIO_INITINFO(&info); /* init all fields to "no change" */ + + /* Try to set the requested settings */ + info.play.sample_rate = spec->freq; + info.play.channels = spec->channels; + info.play.precision = (enc == AUDIO_ENCODING_ULAW) + ? 8 : spec->format & 0xff; + info.play.encoding = enc; + if( ioctl(audio_fd, AUDIO_SETINFO, &info) == 0 ) { + + /* Check to be sure we got what we wanted */ + if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) { + SDL_SetError("Error getting audio parameters: %s", + strerror(errno)); + return -1; + } + if(info.play.encoding == enc + && info.play.precision == (spec->format & 0xff) + && info.play.channels == spec->channels) { + /* Yow! All seems to be well! */ + spec->freq = info.play.sample_rate; + break; + } + } + + switch(enc) { + case AUDIO_ENCODING_LINEAR8: + /* unsigned 8bit apparently not supported here */ + enc = AUDIO_ENCODING_LINEAR; + spec->format = AUDIO_S16SYS; + break; /* try again */ + + case AUDIO_ENCODING_LINEAR: + /* linear 16bit didn't work either, resort to µ-law */ + enc = AUDIO_ENCODING_ULAW; + spec->channels = 1; + spec->freq = 8000; + spec->format = AUDIO_U8; + ulaw_only = 1; + break; + + default: + /* oh well... */ + SDL_SetError("Error setting audio parameters: %s", + strerror(errno)); + return -1; + } + } +#endif /* AUDIO_SETINFO */ + written = 0; + + /* We can actually convert on-the-fly to U-Law */ + if ( ulaw_only ) { + spec->freq = desired_freq; + fragsize = (spec->samples*1000)/(spec->freq/8); + frequency = 8; + ulaw_buf = (Uint8 *)SDL_malloc(fragsize); + if ( ulaw_buf == NULL ) { + SDL_OutOfMemory(); + return(-1); + } + spec->channels = 1; + } else { + fragsize = spec->samples; + frequency = spec->freq/1000; + } +#ifdef DEBUG_AUDIO + fprintf(stderr, "Audio device %s U-Law only\n", + ulaw_only ? "is" : "is not"); + fprintf(stderr, "format=0x%x chan=%d freq=%d\n", + spec->format, spec->channels, spec->freq); +#endif + + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(spec); + + /* Allocate mixing buffer */ + mixbuf = (Uint8 *)SDL_AllocAudioMem(spec->size); + if ( mixbuf == NULL ) { + SDL_OutOfMemory(); + return(-1); + } + SDL_memset(mixbuf, spec->silence, spec->size); + + /* We're ready to rock and roll. :-) */ + return(0); +} + +/************************************************************************/ +/* This function (snd2au()) copyrighted: */ +/************************************************************************/ +/* Copyright 1989 by Rich Gopstein and Harris Corporation */ +/* */ +/* Permission to use, copy, modify, and distribute this software */ +/* and its documentation for any purpose and without fee is */ +/* hereby granted, provided that the above copyright notice */ +/* appears in all copies and that both that copyright notice and */ +/* this permission notice appear in supporting documentation, and */ +/* that the name of Rich Gopstein and Harris Corporation not be */ +/* used in advertising or publicity pertaining to distribution */ +/* of the software without specific, written prior permission. */ +/* Rich Gopstein and Harris Corporation make no representations */ +/* about the suitability of this software for any purpose. It */ +/* provided "as is" without express or implied warranty. */ +/************************************************************************/ + +static Uint8 snd2au(int sample) +{ + + int mask; + + if (sample < 0) { + sample = -sample; + mask = 0x7f; + } else { + mask = 0xff; + } + + if (sample < 32) { + sample = 0xF0 | (15 - sample / 2); + } else if (sample < 96) { + sample = 0xE0 | (15 - (sample - 32) / 4); + } else if (sample < 224) { + sample = 0xD0 | (15 - (sample - 96) / 8); + } else if (sample < 480) { + sample = 0xC0 | (15 - (sample - 224) / 16); + } else if (sample < 992) { + sample = 0xB0 | (15 - (sample - 480) / 32); + } else if (sample < 2016) { + sample = 0xA0 | (15 - (sample - 992) / 64); + } else if (sample < 4064) { + sample = 0x90 | (15 - (sample - 2016) / 128); + } else if (sample < 8160) { + sample = 0x80 | (15 - (sample - 4064) / 256); + } else { + sample = 0x80; + } + return (mask & sample); +} diff --git a/src/audio/sun/SDL_sunaudio.h b/src/audio/sun/SDL_sunaudio.h new file mode 100644 index 0000000..480bd4e --- /dev/null +++ b/src/audio/sun/SDL_sunaudio.h @@ -0,0 +1,55 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_lowaudio_h +#define _SDL_lowaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData { + /* The file descriptor for the audio device */ + int audio_fd; + + Uint16 audio_fmt; /* The app audio format */ + Uint8 *mixbuf; /* The app mixing buffer */ + int ulaw_only; /* Flag -- does hardware only output U-law? */ + Uint8 *ulaw_buf; /* The U-law mixing buffer */ + Sint32 written; /* The number of samples written */ + int fragsize; /* The audio fragment size in samples */ + int frequency; /* The audio frequency in KHz */ +}; + +/* Old variable names */ +#define audio_fd (this->hidden->audio_fd) +#define audio_fmt (this->hidden->audio_fmt) +#define mixbuf (this->hidden->mixbuf) +#define ulaw_only (this->hidden->ulaw_only) +#define ulaw_buf (this->hidden->ulaw_buf) +#define written (this->hidden->written) +#define fragsize (this->hidden->fragsize) +#define frequency (this->hidden->frequency) + +#endif /* _SDL_lowaudio_h */ diff --git a/src/audio/symbian/SDL_epocaudio.cpp b/src/audio/symbian/SDL_epocaudio.cpp new file mode 100644 index 0000000..304083a --- /dev/null +++ b/src/audio/symbian/SDL_epocaudio.cpp @@ -0,0 +1,614 @@ +/* + SDL - Simple DirectMedia Layer + 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@devolution.com +*/ + +/* + SDL_epocaudio.cpp + Epoc based SDL audio driver implementation + + Markus Mertama +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id: SDL_epocaudio.c,v 0.0.0.0 2001/06/19 17:19:56 hercules Exp $"; +#endif + + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> + +#include "epoc_sdl.h" + +#include <e32hal.h> + + +extern "C" { +#include "SDL_audio.h" +#include "SDL_error.h" +#include "SDL_audiomem.h" +#include "SDL_audio_c.h" +#include "SDL_timer.h" +#include "SDL_audiodev_c.h" +} + +#include "SDL_epocaudio.h" + +#include "streamplayer.h" + + +//#define DEBUG_AUDIO + + +/* Audio driver functions */ + +static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec); +static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice); +static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice); +static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice); +static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice); +static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice); + +static int Audio_Available(void); +static SDL_AudioDevice *Audio_CreateDevice(int devindex); +static void Audio_DeleteDevice(SDL_AudioDevice *device); + + +//void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len); + +#ifdef __WINS__ +#define DODUMP +#endif + +#ifdef DODUMP +NONSHARABLE_CLASS(TDump) + { + public: + TInt Open(); + void Close(); + void Dump(const TDesC8& aDes); + private: + RFile iFile; + RFs iFs; + }; + +TInt TDump::Open() + { + TInt err = iFs.Connect(); + if(err == KErrNone) + { +#ifdef __WINS__ +_LIT(target, "C:\\sdlau.raw"); +#else +_LIT(target, "E:\\sdlau.raw"); +#endif + err = iFile.Replace(iFs, target, EFileWrite); + } + return err; + } +void TDump::Close() + { + iFile.Close(); + iFs.Close(); + } +void TDump::Dump(const TDesC8& aDes) + { + iFile.Write(aDes); + } +#endif + + +NONSHARABLE_CLASS(CSimpleWait) : public CTimer + { + public: + void Wait(TTimeIntervalMicroSeconds32 aWait); + static CSimpleWait* NewL(); + private: + CSimpleWait(); + void RunL(); + }; + + +CSimpleWait* CSimpleWait::NewL() + { + CSimpleWait* wait = new (ELeave) CSimpleWait(); + CleanupStack::PushL(wait); + wait->ConstructL(); + CleanupStack::Pop(); + return wait; + } + +void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait) + { + After(aWait); + CActiveScheduler::Start(); + } + +CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard) + { + CActiveScheduler::Add(this); + } + +void CSimpleWait::RunL() + { + CActiveScheduler::Stop(); + } + +const TInt KAudioBuffers(2); + + +NONSHARABLE_CLASS(CEpocAudio) : public CBase, public MStreamObs, public MStreamProvider + { + public: + static void* NewL(TInt BufferSize, TInt aFill); + inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice); + + static void Free(SDL_AudioDevice* thisdevice); + + void Wait(); + void Play(); + // void SetBuffer(const TDesC8& aBuffer); + void ThreadInitL(TAny* aDevice); + void Open(TInt iRate, TInt iChannels, TUint32 aType, TInt aBytes); + ~CEpocAudio(); + TUint8* Buffer(); + TBool SetPause(TBool aPause); + #ifdef DODUMP + void Dump(const TDesC8& aBuf) {iDump.Dump(aBuf);} + #endif + private: + CEpocAudio(TInt aBufferSize); + void Complete(TInt aState, TInt aError); + TPtrC8 Data(); + void ConstructL(TInt aFill); + private: + TInt iBufferSize; + CStreamPlayer* iPlayer; + TInt iBufferRate; + TInt iRate; + TInt iChannels; + TUint32 iType; + TInt iPosition; + TThreadId iTid; + TUint8* iAudioPtr; + TUint8* iBuffer; + // TTimeIntervalMicroSeconds iStart; + TTime iStart; + TInt iTune; + CSimpleWait* iWait; + #ifdef DODUMP + TDump iDump; + #endif + }; + +inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice) + { + return *static_cast<CEpocAudio*>((void*)thisdevice->hidden); + } + +/* + +TBool EndSc(TAny*) + { + CActiveScheduler::Stop(); + } + +LOCAL_C void CleanScL() + { + CIdle* d = CIdle::NewLC(CActive:::EPriorityIdle); + d->Start(TCallBack(EndSc)); + CActiveScheduler::Start(); + + } +*/ + +void CEpocAudio::Free(SDL_AudioDevice* thisdevice) + { + CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden); + if(ea) + { + ASSERT(ea->iTid == RThread().Id()); + delete ea; + thisdevice->hidden = NULL; + + CActiveScheduler* as = CActiveScheduler::Current(); + ASSERT(as->StackDepth() == 0); + delete as; + CActiveScheduler::Install(NULL); + } + ASSERT(thisdevice->hidden == NULL); + } + +CEpocAudio::CEpocAudio(TInt aBufferSize) : iBufferSize(aBufferSize), iPosition(-1) + { + } + +void* CEpocAudio::NewL(TInt aBufferSize, TInt aFill) + { + CEpocAudio* eAudioLib = new (ELeave) CEpocAudio(aBufferSize); + CleanupStack::PushL(eAudioLib); + eAudioLib->ConstructL(aFill); + CleanupStack::Pop(); + return eAudioLib; + } + +void CEpocAudio::ConstructL(TInt aFill) + { + iBuffer = (TUint8*) User::AllocL(KAudioBuffers * iBufferSize); + memset(iBuffer, aFill, KAudioBuffers * iBufferSize); + iAudioPtr = iBuffer; + } + + +TBool CEpocAudio::SetPause(TBool aPause) + { + if(aPause && iPosition >= 0) + { + iPosition = -1; + if(iPlayer != NULL) + iPlayer->Stop(); + } + if(!aPause && iPosition < 0) + { + iPosition = 0; + if(iPlayer != NULL) + iPlayer->Start(); + } + return iPosition < 0; + } + +void CEpocAudio::ThreadInitL(TAny* aDevice) + { + iTid = RThread().Id(); + CActiveScheduler* as = new (ELeave) CActiveScheduler(); + CActiveScheduler::Install(as); + + EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice)); + + iWait = CSimpleWait::NewL(); + + iPlayer = new (ELeave) CStreamPlayer(*this, *this); + iPlayer->ConstructL(); + iPlayer->OpenStream(iRate, iChannels, iType); + + #ifdef DODUMP + User::LeaveIfError(iDump.Open()); + #endif + } + + + +TUint8* CEpocAudio::Buffer() + { + iStart.UniversalTime(); +// iStart = iPlayer->Position(); + return iAudioPtr; + + } + +CEpocAudio::~CEpocAudio() + { + if(iWait != NULL) + iWait->Cancel(); + delete iWait; + if(iPlayer != NULL) + iPlayer->Close(); + delete iPlayer; + delete iBuffer; + } + +void CEpocAudio::Complete(TInt aState, TInt aError) + { + if(aState == MStreamObs::EClose) + { + } + if(iPlayer->Closed()) + return; + switch(aError) + { + case KErrUnderflow: + case KErrInUse: + iPlayer->Start(); + break; + case KErrAbort: + iPlayer->Open(); + } + } + + +void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len) + { +#ifdef DODUMP + const TPtrC8 buf((TUint8*)data, len); + CEpocAudio::Current(thisdevice).Dump(buf); +#endif + } + +const TInt KClip(256); + +TPtrC8 CEpocAudio::Data() + { + if(iPosition < 0) + return KNullDesC8(); + + TPtrC8 data(iAudioPtr + iPosition, KClip); + +#ifdef DODUMP + iDump.Dump(data); +#endif + + iPosition += KClip; + if(iPosition >= iBufferSize) + { + +/* if(iAudioPtr == iBuffer) + iAudioPtr = iBuffer + iBufferSize; + else + iAudioPtr = iBuffer; +*/ + iAudioPtr += iBufferSize; + + if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize) + iAudioPtr = iBuffer; + + iPosition = -1; + if(iWait->IsActive()) + { + iWait->Cancel(); + CActiveScheduler::Stop(); + } + } + return data; + } + + + + +void CEpocAudio::Play() + { + iPosition = 0; + } + +void CEpocAudio::Wait() + { + if(iPosition >= 0 /*&& iPlayer->Playing()*/) + { + const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000); + const TInt64 specTime = bufMs / TInt64(iRate * iChannels * 2); + iWait->After(specTime); + + CActiveScheduler::Start(); + TTime end; + end.UniversalTime(); + const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart); + + +// const TTimeIntervalMicroSeconds end = iPlayer->Position(); + + + + + const TInt diff = specTime - delta.Int64(); + + if(diff > 0 && diff < 200000) + { + User::After(diff); + } + + } + else + { + User::After(10000); +// iWait->Wait(10000); //just give some time... + } + } + +void CEpocAudio::Open(TInt aRate, TInt aChannels, TUint32 aType, TInt aBytes) + { + iRate = aRate; + iChannels = aChannels; + iType = aType; + iBufferRate = iRate * iChannels * aBytes; //1/x + } + + +/* Audio driver bootstrap functions */ + +AudioBootStrap EPOCAudio_bootstrap = { + "epoc\0\0\0", + "EPOC streaming audio\0\0\0", + Audio_Available, + Audio_CreateDevice +}; + + +static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/) +{ + SDL_AudioDevice *thisdevice; + + /* Initialize all variables that we clean on shutdown */ + thisdevice = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice)); + if ( thisdevice ) { + memset(thisdevice, 0, (sizeof *thisdevice)); + thisdevice->hidden = NULL; /*(struct SDL_PrivateAudioData *) + malloc((sizeof thisdevice->hidden)); */ + } + if ( (thisdevice == NULL) /*|| (thisdevice->hidden == NULL) */) { + SDL_OutOfMemory(); + if ( thisdevice ) { + free(thisdevice); + } + return(0); + } +// memset(thisdevice->hidden, 0, (sizeof *thisdevice->hidden)); + + /* Set the function pointers */ + thisdevice->OpenAudio = EPOC_OpenAudio; + thisdevice->WaitAudio = EPOC_WaitAudio; + thisdevice->PlayAudio = EPOC_PlayAudio; + thisdevice->GetAudioBuf = EPOC_GetAudioBuf; + thisdevice->CloseAudio = EPOC_CloseAudio; + thisdevice->ThreadInit = EPOC_ThreadInit; + thisdevice->free = Audio_DeleteDevice; + + return thisdevice; +} + + +static void Audio_DeleteDevice(SDL_AudioDevice *device) + { + //free(device->hidden); + free(device); + } + +static int Audio_Available(void) +{ + return(1); // Audio stream modules should be always there! +} + + +static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec) +{ + SDL_TRACE("SDL:EPOC_OpenAudio"); + + + TUint32 type = KMMFFourCCCodePCM16; + TInt bytes = 2; + + switch(spec->format) + { + case AUDIO_U16LSB: + type = KMMFFourCCCodePCMU16; + break; + case AUDIO_S16LSB: + type = KMMFFourCCCodePCM16; + break; + case AUDIO_U16MSB: + type = KMMFFourCCCodePCMU16B; + break; + case AUDIO_S16MSB: + type = KMMFFourCCCodePCM16B; + break; + //8 bit not supported! + case AUDIO_U8: + case AUDIO_S8: + default: + spec->format = AUDIO_S16LSB; + }; + + + + if(spec->channels > 2) + spec->channels = 2; + + spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq); + + + /* Allocate mixing buffer */ + const TInt buflen = spec->size;// * bytes * spec->channels; +// audiobuf = NULL; + + TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence))); + if(err != KErrNone) + return -1; + + CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes); + + CEpocAudio::Current(thisdevice).SetPause(ETrue); + + // isSDLAudioPaused = 1; + + thisdevice->enabled = 0; /* enable only after audio engine has been initialized!*/ + + /* We're ready to rock and roll. :-) */ + return(0); +} + + +static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice) + { +#ifdef DEBUG_AUDIO + SDL_TRACE("Close audio\n"); +#endif + + CEpocAudio::Free(thisdevice); + } + + +static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice) + { + SDL_TRACE("SDL:EPOC_ThreadInit"); + CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice); + RThread().SetPriority(EPriorityMore); + thisdevice->enabled = 1; + } + +/* This function waits until it is possible to write a full sound buffer */ +static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice) +{ +#ifdef DEBUG_AUDIO + SDL_TRACE1("wait %d audio\n", CEpocAudio::AudioLib().StreamPlayer(KSfxChannel).SyncTime()); + TInt tics = User::TickCount(); +#endif + + CEpocAudio::Current(thisdevice).Wait(); + +#ifdef DEBUG_AUDIO + TInt ntics = User::TickCount() - tics; + SDL_TRACE1("audio waited %d\n", ntics); + SDL_TRACE1("audio at %d\n", tics); +#endif +} + + + +static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice) + { + if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED)) + SDL_Delay(500); //hold on the busy loop + else + CEpocAudio::Current(thisdevice).Play(); + +#ifdef DEBUG_AUDIO + SDL_TRACE("buffer has audio data\n"); +#endif + + +#ifdef DEBUG_AUDIO + SDL_TRACE1("Wrote %d bytes of audio data\n", buflen); +#endif +} + +static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice) + { + return CEpocAudio::Current(thisdevice).Buffer(); + } + + + diff --git a/src/audio/symbian/SDL_epocaudio.h b/src/audio/symbian/SDL_epocaudio.h new file mode 100644 index 0000000..7be79d1 --- /dev/null +++ b/src/audio/symbian/SDL_epocaudio.h @@ -0,0 +1,37 @@ +/* + SDL - Simple DirectMedia Layer + 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@devolution.com +*/ + + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id: SDL_epocaudio.h,v 1.1.2.2 2001/02/10 07:20:03 hercules Exp $"; +#endif + +#ifndef _SDL_EPOCAUDIO_H +#define _SDL_EPOCAUDIO_H + +extern "C" { +#include "SDL_sysaudio.h" +} + + +#endif /* _SDL_EPOCAUDIO_H */ diff --git a/src/audio/symbian/streamplayer.cpp b/src/audio/symbian/streamplayer.cpp new file mode 100644 index 0000000..dd733a1 --- /dev/null +++ b/src/audio/symbian/streamplayer.cpp @@ -0,0 +1,279 @@ +#include "streamplayer.h" +#include<mda/common/audio.h> + + + +const TInt KMaxVolume(256); + +LOCAL_C TInt GetSampleRate(TInt aRate) + { + switch(aRate) + { + case 8000: return TMdaAudioDataSettings::ESampleRate8000Hz; + case 11025: return TMdaAudioDataSettings::ESampleRate11025Hz; + case 12000: return TMdaAudioDataSettings::ESampleRate12000Hz; + case 16000: return TMdaAudioDataSettings::ESampleRate16000Hz; + case 22050: return TMdaAudioDataSettings::ESampleRate22050Hz; + case 24000: return TMdaAudioDataSettings::ESampleRate24000Hz; + case 32000: return TMdaAudioDataSettings::ESampleRate32000Hz; + case 44100: return TMdaAudioDataSettings::ESampleRate44100Hz; + case 48000: return TMdaAudioDataSettings::ESampleRate48000Hz; + case 96000: return TMdaAudioDataSettings::ESampleRate96000Hz; + case 64000: return TMdaAudioDataSettings::ESampleRate64000Hz; + } + return KErrNotFound; + } + +LOCAL_C TInt GetChannels(TInt aChannels) + { + switch(aChannels) + { + case 1: return TMdaAudioDataSettings::EChannelsMono; + case 2: return TMdaAudioDataSettings::EChannelsStereo; + } + return KErrNotFound; + } + +TInt CStreamPlayer::ClosestSupportedRate(TInt aRate) + { + if(aRate > 96000) + return 96000; + TInt rate = aRate; + while(GetSampleRate(rate) == KErrNotFound) + { + ++rate; + } + return rate; + } + +CStreamPlayer::CStreamPlayer(MStreamProvider& aProvider, MStreamObs& aObs) : + iProvider(aProvider), iObs(aObs), iVolume(KMaxVolume) + { + } + +CStreamPlayer::~CStreamPlayer() + { + iState |= EDied; + if(iState & EInited) + Close(); + User::After(100000); //wait buffer to be flushed + ASSERT(iPtr.Length() == 0); + delete iStream; + } + + +void CStreamPlayer::ConstructL() + { + iStream = CMdaAudioOutputStream::NewL(*this, EMdaPriorityMax); + iSilence.SetMax(); + iSilence.FillZ(); + } + + +TInt CStreamPlayer::OpenStream(TInt aRate, TInt aChannels, TUint32 aType) + { + Close(); + + iType = aType; + + iRate = GetSampleRate(aRate); + if(iRate == KErrNotFound) + return KErrNotSupported; + + iChannels = GetChannels(aChannels); + if(iChannels == KErrNotFound) + return KErrNotSupported; + + Open(); + + return KErrNone; + } + + +TInt CStreamPlayer::MaxVolume() const + { + return KMaxVolume; + } + +void CStreamPlayer::SetVolume(TInt aNew) + { + + const TInt maxi = MaxVolume(); + if(aNew > maxi) + return; + if(aNew < 0) + return; + + iVolume = aNew; + + iState |= EVolumeChange; + } + + TInt CStreamPlayer::Volume() const + { + return iVolume; + } + +void CStreamPlayer::Open() + { + TMdaAudioDataSettings audioSettings; + audioSettings.Query(); + audioSettings.iCaps = TMdaAudioDataSettings::ERealTime | + TMdaAudioDataSettings::ESampleRateFixed; + audioSettings.iSampleRate = iRate; + audioSettings.iChannels = iChannels; + audioSettings.iFlags = TMdaAudioDataSettings::ENoNetworkRouting; + audioSettings.iVolume = 0; + + iState &= ~EStopped; + iStream->Open(&audioSettings); + } + +void CStreamPlayer::Stop() + { + if(iState & (EStarted | EInited)) + { + Close(); + iState |= EStopped; + } + } + +void CStreamPlayer::Start() + { + if(iPtr.Length() == 0) + { + iState |= EStarted; + if(iState & EInited) + { + Request(); + } + else if(iState & EStopped) + { + Open(); + } + } + } + +void CStreamPlayer::Close() + { + iState &= ~EInited; + iStream->Stop(); + iState &= ~EStarted; + } + +void CStreamPlayer::Request() + { + if(iState & EInited) + { + iPtr.Set(KNullDesC8); + + if(iState & EVolumeChange) + { + const TReal newVol = iVolume; + const TReal newMax = MaxVolume(); + const TInt maxVol = iStream->MaxVolume(); + const TReal max = static_cast<TReal>(maxVol); + const TReal newvolume = (newVol * max) / newMax; + const TInt vol = static_cast<TReal>(newvolume); + iStream->SetVolume(vol); + iState &= ~EVolumeChange; + } + + if(iState & EStarted) + { + iPtr.Set(iProvider.Data()); + } + if(iPtr.Length() == 0) + { + iPtr.Set(iSilence); + } + TRAPD(err, iStream->WriteL(iPtr)); + if(err != KErrNone) + { + iObs.Complete(MStreamObs::EWrite, err); + } + /* else + { + iProvider.Written(iPtr.Length()); + }*/ + } + } + + +void CStreamPlayer::SetCapsL() + { + iStream->SetDataTypeL(iType); + iStream->SetAudioPropertiesL(iRate, iChannels); + } + +void CStreamPlayer::MaoscOpenComplete(TInt aError) + { + if(aError == KErrNone) + { + TRAPD(err, SetCapsL()); + if(err == KErrNone) + { + iStream->SetPriority(EPriorityNormal, EMdaPriorityPreferenceTime); + iState |= EInited; + + + SetVolume(Volume()); + + if(iState & EStarted) + { + Request(); + } + + } + aError = err; + } + if(!(iState & EDied)) + iObs.Complete(MStreamObs::EInit, aError); + } + +void CStreamPlayer::MaoscBufferCopied(TInt aError, const TDesC8& /*aBuffer*/) + { + iPtr.Set(KNullDesC8); + if(aError == KErrNone) + { + if(iState & EInited) + Request(); + else + iStream->Stop(); + } + else if(!(iState & EDied)) + iObs.Complete(MStreamObs::EPlay, aError); + } + +void CStreamPlayer::MaoscPlayComplete(TInt aError) + { + iPtr.Set(KNullDesC8); + iState &= ~EStarted; + if(!(iState & EDied)) + iObs.Complete(MStreamObs::EClose, aError); + } + +TBool CStreamPlayer::Playing() const + { + return (iState & EInited) && (iState & EStarted); + } + +TBool CStreamPlayer::Closed() const + { + return !(iState & EInited) && !(iState & EDied); + } + + /* +void CStreamPlayer::Request() + { + SetActive(); + TRequestStatus* s = &iStatus; + User::RequestComplete(s, KErrNone); + } + // iTimer.After(0); + */ + + + + + diff --git a/src/audio/symbian/streamplayer.h b/src/audio/symbian/streamplayer.h new file mode 100644 index 0000000..8c6e74f --- /dev/null +++ b/src/audio/symbian/streamplayer.h @@ -0,0 +1,89 @@ +#ifndef STREAMPLAYER_H +#define STREAMPLAYER_H + +#include<MdaAudioOutputStream.h> + +const TInt KSilenceBuffer = 256; + +class MStreamObs + { + public: + enum + { + EInit, + EPlay, + EWrite, + EClose, + }; + virtual void Complete(TInt aState, TInt aError) = 0; + }; + +class MStreamProvider + { + public: + virtual TPtrC8 Data() = 0; + }; + +NONSHARABLE_CLASS(CStreamPlayer) : public CBase, public MMdaAudioOutputStreamCallback + { + public: + CStreamPlayer(MStreamProvider& aProvider, MStreamObs& aObs); + ~CStreamPlayer(); + void ConstructL(); + + static TInt ClosestSupportedRate(TInt aRate); + + TInt OpenStream(TInt aRate, TInt aChannels, TUint32 aType = KMMFFourCCCodePCM16); + + void SetVolume(TInt aNew); + TInt Volume() const; + TInt MaxVolume() const; + + void Stop(); + void Start(); + void Open(); + void Close(); + + TBool Playing() const; + TBool Closed() const; + + private: + + void MaoscOpenComplete(TInt aError) ; + void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer); + void MaoscPlayComplete(TInt aError); + + private: + void Request(); + void SetCapsL(); + + private: + MStreamProvider& iProvider; + MStreamObs& iObs; + TInt iVolume; + + CMdaAudioOutputStream* iStream; + + TInt iRate; + TInt iChannels; + TUint32 iType; + + enum + { + ENone = 0, + EInited = 0x1, + EStarted = 0x2, + EStopped = 0x4, + EVolumeChange = 0x8, + EDied = 0x10 + }; + + TInt iState; + TBuf8<KSilenceBuffer> iSilence; + TPtrC8 iPtr; + + }; + + +#endif + diff --git a/src/audio/ums/SDL_umsaudio.c b/src/audio/ums/SDL_umsaudio.c new file mode 100644 index 0000000..0b11441 --- /dev/null +++ b/src/audio/ums/SDL_umsaudio.c @@ -0,0 +1,547 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Carsten Griwodz + griff@kom.tu-darmstadt.de + + based on linux/SDL_dspaudio.c by Sam Lantinga +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "../SDL_audiodev_c.h" +#include "SDL_umsaudio.h" + +/* The tag name used by UMS audio */ +#define UMS_DRIVER_NAME "ums" + +#define DEBUG_AUDIO 1 + +/* Audio driver functions */ +static int UMS_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void UMS_PlayAudio(_THIS); +static Uint8 *UMS_GetAudioBuf(_THIS); +static void UMS_CloseAudio(_THIS); + +static UMSAudioDevice_ReturnCode UADOpen(_THIS, string device, string mode, long flags); +static UMSAudioDevice_ReturnCode UADClose(_THIS); +static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long* bits); +static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits); +static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate, long* set_rate); +static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order); +static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt); +static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt); +static UMSAudioDevice_ReturnCode UADInitialize(_THIS); +static UMSAudioDevice_ReturnCode UADStart(_THIS); +static UMSAudioDevice_ReturnCode UADStop(_THIS); +static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt ); +static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long* buff_size ); +static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long* buff_size ); +static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long* buff_size ); +static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes, long* bytes_ret ); +static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume ); +static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance ); +static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels ); +static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block ); +static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output, long* left_gain, long* right_gain); +static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer* buff, long samples, long* samples_written); + +/* Audio driver bootstrap functions */ +static int Audio_Available(void) +{ + return 1; +} + +static void Audio_DeleteDevice(_THIS) +{ + if(this->hidden->playbuf._buffer) SDL_free(this->hidden->playbuf._buffer); + if(this->hidden->fillbuf._buffer) SDL_free(this->hidden->fillbuf._buffer); + _somFree( this->hidden->umsdev ); + SDL_free(this->hidden); + SDL_free(this); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* + * Allocate and initialize management storage and private management + * storage for this SDL-using library. + */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); +#ifdef DEBUG_AUDIO + fprintf(stderr, "Creating UMS Audio device\n"); +#endif + + /* + * Calls for UMS env initialization and audio object construction. + */ + this->hidden->ev = somGetGlobalEnvironment(); + this->hidden->umsdev = UMSAudioDeviceNew(); + + /* + * Set the function pointers. + */ + this->OpenAudio = UMS_OpenAudio; + this->WaitAudio = NULL; /* we do blocking output */ + this->PlayAudio = UMS_PlayAudio; + this->GetAudioBuf = UMS_GetAudioBuf; + this->CloseAudio = UMS_CloseAudio; + this->free = Audio_DeleteDevice; + +#ifdef DEBUG_AUDIO + fprintf(stderr, "done\n"); +#endif + return this; +} + +AudioBootStrap UMS_bootstrap = { + UMS_DRIVER_NAME, "AIX UMS audio", + Audio_Available, Audio_CreateDevice +}; + +static Uint8 *UMS_GetAudioBuf(_THIS) +{ +#ifdef DEBUG_AUDIO + fprintf(stderr, "enter UMS_GetAudioBuf\n"); +#endif + return this->hidden->fillbuf._buffer; +/* + long bufSize; + UMSAudioDevice_ReturnCode rc; + + rc = UADSetTimeFormat(this, UMSAudioTypes_Bytes ); + rc = UADWriteBuffSize(this, bufSize ); +*/ +} + +static void UMS_CloseAudio(_THIS) +{ + UMSAudioDevice_ReturnCode rc; + +#ifdef DEBUG_AUDIO + fprintf(stderr, "enter UMS_CloseAudio\n"); +#endif + rc = UADPlayRemainingData(this, TRUE); + rc = UADStop(this); + rc = UADClose(this); +} + +static void UMS_PlayAudio(_THIS) +{ + UMSAudioDevice_ReturnCode rc; + long samplesToWrite; + long samplesWritten; + UMSAudioTypes_Buffer swpbuf; + +#ifdef DEBUG_AUDIO + fprintf(stderr, "enter UMS_PlayAudio\n"); +#endif + samplesToWrite = this->hidden->playbuf._length/this->hidden->bytesPerSample; + do + { + rc = UADWrite(this, &this->hidden->playbuf, + samplesToWrite, + &samplesWritten ); + samplesToWrite -= samplesWritten; + + /* rc values: UMSAudioDevice_Success + * UMSAudioDevice_Failure + * UMSAudioDevice_Preempted + * UMSAudioDevice_Interrupted + * UMSAudioDevice_DeviceError + */ + if ( rc == UMSAudioDevice_DeviceError ) { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Returning from PlayAudio with devices error\n"); +#endif + return; + } + } + while(samplesToWrite>0); + + SDL_LockAudio(); + SDL_memcpy( &swpbuf, &this->hidden->playbuf, sizeof(UMSAudioTypes_Buffer) ); + SDL_memcpy( &this->hidden->playbuf, &this->hidden->fillbuf, sizeof(UMSAudioTypes_Buffer) ); + SDL_memcpy( &this->hidden->fillbuf, &swpbuf, sizeof(UMSAudioTypes_Buffer) ); + SDL_UnlockAudio(); + +#ifdef DEBUG_AUDIO + fprintf(stderr, "Wrote audio data and swapped buffer\n"); +#endif +} + +#if 0 +// /* Set the DSP frequency */ +// value = spec->freq; +// if ( ioctl(this->hidden->audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) { +// SDL_SetError("Couldn't set audio frequency"); +// return(-1); +// } +// spec->freq = value; +#endif + +static int UMS_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + char* audiodev = "/dev/paud0"; + long lgain; + long rgain; + long outRate; + long outBufSize; + long bitsPerSample; + long samplesPerSec; + long success; + Uint16 test_format; + int frag_spec; + UMSAudioDevice_ReturnCode rc; + +#ifdef DEBUG_AUDIO + fprintf(stderr, "enter UMS_OpenAudio\n"); +#endif + rc = UADOpen(this, audiodev,"PLAY", UMSAudioDevice_BlockingIO); + if ( rc != UMSAudioDevice_Success ) { + SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno)); + return -1; + } + + rc = UADSetAudioFormatType(this, "PCM"); + + success = 0; + test_format = SDL_FirstAudioFormat(spec->format); + do + { +#ifdef DEBUG_AUDIO + fprintf(stderr, "Trying format 0x%4.4x\n", test_format); +#endif + switch ( test_format ) + { + case AUDIO_U8: +/* from the mac code: better ? */ +/* sample_bits = spec->size / spec->samples / spec->channels * 8; */ + success = 1; + bitsPerSample = 8; + rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); + rc = UADSetByteOrder(this, "MSB"); /* irrelevant */ + rc = UADSetNumberFormat(this, "UNSIGNED"); + break; + case AUDIO_S8: + success = 1; + bitsPerSample = 8; + rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); + rc = UADSetByteOrder(this, "MSB"); /* irrelevant */ + rc = UADSetNumberFormat(this, "SIGNED"); + break; + case AUDIO_S16LSB: + success = 1; + bitsPerSample = 16; + rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); + rc = UADSetByteOrder(this, "LSB"); + rc = UADSetNumberFormat(this, "SIGNED"); + break; + case AUDIO_S16MSB: + success = 1; + bitsPerSample = 16; + rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); + rc = UADSetByteOrder(this, "MSB"); + rc = UADSetNumberFormat(this, "SIGNED"); + break; + case AUDIO_U16LSB: + success = 1; + bitsPerSample = 16; + rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); + rc = UADSetByteOrder(this, "LSB"); + rc = UADSetNumberFormat(this, "UNSIGNED"); + break; + case AUDIO_U16MSB: + success = 1; + bitsPerSample = 16; + rc = UADSetSampleRate(this, spec->freq << 16, &outRate ); + rc = UADSetByteOrder(this, "MSB"); + rc = UADSetNumberFormat(this, "UNSIGNED"); + break; + default: + break; + } + if ( ! success ) { + test_format = SDL_NextAudioFormat(); + } + } + while ( ! success && test_format ); + + if ( success == 0 ) { + SDL_SetError("Couldn't find any hardware audio formats"); + return -1; + } + + spec->format = test_format; + + for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec ); + if ( (0x01<<frag_spec) != spec->size ) { + SDL_SetError("Fragment size must be a power of two"); + return -1; + } + if ( frag_spec > 2048 ) frag_spec = 2048; + + this->hidden->bytesPerSample = (bitsPerSample / 8) * spec->channels; + samplesPerSec = this->hidden->bytesPerSample * outRate; + + this->hidden->playbuf._length = 0; + this->hidden->playbuf._maximum = spec->size; + this->hidden->playbuf._buffer = (unsigned char*)SDL_malloc(spec->size); + this->hidden->fillbuf._length = 0; + this->hidden->fillbuf._maximum = spec->size; + this->hidden->fillbuf._buffer = (unsigned char*)SDL_malloc(spec->size); + + rc = UADSetBitsPerSample(this, bitsPerSample ); + rc = UADSetDMABufferSize(this, frag_spec, &outBufSize ); + rc = UADSetChannels(this, spec->channels); /* functions reduces to mono or stereo */ + + lgain = 100; /*maximum left input gain*/ + rgain = 100; /*maimum right input gain*/ + rc = UADEnableOutput(this, "LINE_OUT",&lgain,&rgain); + rc = UADInitialize(this); + rc = UADStart(this); + rc = UADSetVolume(this, 100); + rc = UADSetBalance(this, 0); + + /* We're ready to rock and roll. :-) */ + return 0; +} + + +static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long* bits) +{ + return UMSAudioDevice_get_bits_per_sample( this->hidden->umsdev, + this->hidden->ev, + bits ); +} + +static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits) +{ + return UMSAudioDevice_set_bits_per_sample( this->hidden->umsdev, + this->hidden->ev, + bits ); +} + +static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate, long* set_rate) +{ + /* from the mac code: sample rate = spec->freq << 16; */ + return UMSAudioDevice_set_sample_rate( this->hidden->umsdev, + this->hidden->ev, + rate, + set_rate ); +} + +static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order) +{ + return UMSAudioDevice_set_byte_order( this->hidden->umsdev, + this->hidden->ev, + byte_order ); +} + +static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt) +{ + /* possible PCM, A_LAW or MU_LAW */ + return UMSAudioDevice_set_audio_format_type( this->hidden->umsdev, + this->hidden->ev, + fmt ); +} + +static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt) +{ + /* possible SIGNED, UNSIGNED, or TWOS_COMPLEMENT */ + return UMSAudioDevice_set_number_format( this->hidden->umsdev, + this->hidden->ev, + fmt ); +} + +static UMSAudioDevice_ReturnCode UADInitialize(_THIS) +{ + return UMSAudioDevice_initialize( this->hidden->umsdev, + this->hidden->ev ); +} + +static UMSAudioDevice_ReturnCode UADStart(_THIS) +{ + return UMSAudioDevice_start( this->hidden->umsdev, + this->hidden->ev ); +} + +static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt ) +{ + /* + * Switches the time format to the new format, immediately. + * possible UMSAudioTypes_Msecs, UMSAudioTypes_Bytes or UMSAudioTypes_Samples + */ + return UMSAudioDevice_set_time_format( this->hidden->umsdev, + this->hidden->ev, + fmt ); +} + +static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long* buff_size ) +{ + /* + * returns write buffer size in the current time format + */ + return UMSAudioDevice_write_buff_size( this->hidden->umsdev, + this->hidden->ev, + buff_size ); +} + +static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long* buff_size ) +{ + /* + * returns amount of available space in the write buffer + * in the current time format + */ + return UMSAudioDevice_write_buff_remain( this->hidden->umsdev, + this->hidden->ev, + buff_size ); +} + +static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long* buff_size ) +{ + /* + * returns amount of filled space in the write buffer + * in the current time format + */ + return UMSAudioDevice_write_buff_used( this->hidden->umsdev, + this->hidden->ev, + buff_size ); +} + +static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes, long* bytes_ret ) +{ + /* + * Request a new DMA buffer size, maximum requested size 2048. + * Takes effect with next initialize() call. + * Devices may or may not support DMA. + */ + return UMSAudioDevice_set_DMA_buffer_size( this->hidden->umsdev, + this->hidden->ev, + bytes, + bytes_ret ); +} + +static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume ) +{ + /* + * Set the volume. + * Takes effect immediately. + */ + return UMSAudioDevice_set_volume( this->hidden->umsdev, + this->hidden->ev, + volume ); +} + +static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance ) +{ + /* + * Set the balance. + * Takes effect immediately. + */ + return UMSAudioDevice_set_balance( this->hidden->umsdev, + this->hidden->ev, + balance ); +} + +static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels ) +{ + /* + * Set mono or stereo. + * Takes effect with next initialize() call. + */ + if ( channels != 1 ) channels = 2; + return UMSAudioDevice_set_number_of_channels( this->hidden->umsdev, + this->hidden->ev, + channels ); +} + +static UMSAudioDevice_ReturnCode UADOpen(_THIS, string device, string mode, long flags) +{ + return UMSAudioDevice_open( this->hidden->umsdev, + this->hidden->ev, + device, + mode, + flags ); +} + +static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer* buff, + long samples, + long* samples_written) +{ + return UMSAudioDevice_write( this->hidden->umsdev, + this->hidden->ev, + buff, + samples, + samples_written ); +} + +static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block ) +{ + return UMSAudioDevice_play_remaining_data( this->hidden->umsdev, + this->hidden->ev, + block); +} + +static UMSAudioDevice_ReturnCode UADStop(_THIS) +{ + return UMSAudioDevice_stop( this->hidden->umsdev, + this->hidden->ev ); +} + +static UMSAudioDevice_ReturnCode UADClose(_THIS) +{ + return UMSAudioDevice_close( this->hidden->umsdev, + this->hidden->ev ); +} + +static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output, long* left_gain, long* right_gain) +{ + return UMSAudioDevice_enable_output( this->hidden->umsdev, + this->hidden->ev, + output, + left_gain, + right_gain ); +} + diff --git a/src/audio/ums/SDL_umsaudio.h b/src/audio/ums/SDL_umsaudio.h new file mode 100644 index 0000000..cb52737 --- /dev/null +++ b/src/audio/ums/SDL_umsaudio.h @@ -0,0 +1,50 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Carsten Griwodz + griff@kom.tu-darmstadt.de + + based on linux/SDL_dspaudio.h by Sam Lantinga +*/ +#include "SDL_config.h" + +#ifndef _SDL_UMSaudio_h +#define _SDL_UMSaudio_h + +#include <UMS/UMSAudioDevice.h> + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +struct SDL_PrivateAudioData +{ + /* Pointer to the (open) UMS audio device */ + Environment* ev; + UMSAudioDevice umsdev; + + /* Raw mixing buffer */ + UMSAudioTypes_Buffer playbuf; + UMSAudioTypes_Buffer fillbuf; + + long bytesPerSample; +}; + +#endif /* _SDL_UMSaudio_h */ + diff --git a/src/audio/windib/SDL_dibaudio.c b/src/audio/windib/SDL_dibaudio.c new file mode 100644 index 0000000..1e63271 --- /dev/null +++ b/src/audio/windib/SDL_dibaudio.c @@ -0,0 +1,322 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <mmsystem.h> + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "SDL_dibaudio.h" +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) +#include "win_ce_semaphore.h" +#endif + + +/* Audio driver functions */ +static int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void DIB_ThreadInit(_THIS); +static void DIB_WaitAudio(_THIS); +static Uint8 *DIB_GetAudioBuf(_THIS); +static void DIB_PlayAudio(_THIS); +static void DIB_WaitDone(_THIS); +static void DIB_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + return(1); +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = DIB_OpenAudio; + this->ThreadInit = DIB_ThreadInit; + this->WaitAudio = DIB_WaitAudio; + this->PlayAudio = DIB_PlayAudio; + this->GetAudioBuf = DIB_GetAudioBuf; + this->WaitDone = DIB_WaitDone; + this->CloseAudio = DIB_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap WAVEOUT_bootstrap = { + "waveout", "Win95/98/NT/2000 WaveOut", + Audio_Available, Audio_CreateDevice +}; + + +/* The Win32 callback for filling the WAVE device */ +static void CALLBACK FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, + DWORD dwParam1, DWORD dwParam2) +{ + SDL_AudioDevice *this = (SDL_AudioDevice *)dwInstance; + + /* Only service "buffer done playing" messages */ + if ( uMsg != WOM_DONE ) + return; + + /* Signal that we are done playing a buffer */ +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + ReleaseSemaphoreCE(audio_sem, 1, NULL); +#else + ReleaseSemaphore(audio_sem, 1, NULL); +#endif +} + +static void SetMMerror(char *function, MMRESULT code) +{ + size_t len; + char errbuf[MAXERRORLENGTH]; +#ifdef _WIN32_WCE + wchar_t werrbuf[MAXERRORLENGTH]; +#endif + + SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function); + len = SDL_strlen(errbuf); + +#ifdef _WIN32_WCE + /* UNICODE version */ + waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH-len); + WideCharToMultiByte(CP_ACP,0,werrbuf,-1,errbuf+len,MAXERRORLENGTH-len,NULL,NULL); +#else + waveOutGetErrorText(code, errbuf+len, (UINT)(MAXERRORLENGTH-len)); +#endif + + SDL_SetError("%s",errbuf); +} + +/* Set high priority for the audio thread */ +static void DIB_ThreadInit(_THIS) +{ + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); +} + +void DIB_WaitAudio(_THIS) +{ + /* Wait for an audio chunk to finish */ +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + WaitForSemaphoreCE(audio_sem, INFINITE); +#else + WaitForSingleObject(audio_sem, INFINITE); +#endif +} + +Uint8 *DIB_GetAudioBuf(_THIS) +{ + Uint8 *retval; + + retval = (Uint8 *)(wavebuf[next_buffer].lpData); + return retval; +} + +void DIB_PlayAudio(_THIS) +{ + /* Queue it up */ + waveOutWrite(sound, &wavebuf[next_buffer], sizeof(wavebuf[0])); + next_buffer = (next_buffer+1)%NUM_BUFFERS; +} + +void DIB_WaitDone(_THIS) +{ + int i, left; + + do { + left = NUM_BUFFERS; + for ( i=0; i<NUM_BUFFERS; ++i ) { + if ( wavebuf[i].dwFlags & WHDR_DONE ) { + --left; + } + } + if ( left > 0 ) { + SDL_Delay(100); + } + } while ( left > 0 ); +} + +void DIB_CloseAudio(_THIS) +{ + int i; + + /* Close up audio */ + if ( audio_sem ) { +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + CloseSynchHandle(audio_sem); +#else + CloseHandle(audio_sem); +#endif + } + if ( sound ) { + waveOutClose(sound); + } + + /* Clean up mixing buffers */ + for ( i=0; i<NUM_BUFFERS; ++i ) { + if ( wavebuf[i].dwUser != 0xFFFF ) { + waveOutUnprepareHeader(sound, &wavebuf[i], + sizeof(wavebuf[i])); + wavebuf[i].dwUser = 0xFFFF; + } + } + /* Free raw mixing buffer */ + if ( mixbuf != NULL ) { + SDL_free(mixbuf); + mixbuf = NULL; + } +} + +int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + MMRESULT result; + int i; + WAVEFORMATEX waveformat; + + /* Initialize the wavebuf structures for closing */ + sound = NULL; + audio_sem = NULL; + for ( i = 0; i < NUM_BUFFERS; ++i ) + wavebuf[i].dwUser = 0xFFFF; + mixbuf = NULL; + + /* Set basic WAVE format parameters */ + SDL_memset(&waveformat, 0, sizeof(waveformat)); + waveformat.wFormatTag = WAVE_FORMAT_PCM; + + /* Determine the audio parameters from the AudioSpec */ + switch ( spec->format & 0xFF ) { + case 8: + /* Unsigned 8 bit audio data */ + spec->format = AUDIO_U8; + waveformat.wBitsPerSample = 8; + break; + case 16: + /* Signed 16 bit audio data */ + spec->format = AUDIO_S16; + waveformat.wBitsPerSample = 16; + break; + default: + SDL_SetError("Unsupported audio format"); + return(-1); + } + waveformat.nChannels = spec->channels; + waveformat.nSamplesPerSec = spec->freq; + waveformat.nBlockAlign = + waveformat.nChannels * (waveformat.wBitsPerSample/8); + waveformat.nAvgBytesPerSec = + waveformat.nSamplesPerSec * waveformat.nBlockAlign; + + /* Check the buffer size -- minimum of 1/4 second (word aligned) */ + if ( spec->samples < (spec->freq/4) ) + spec->samples = ((spec->freq/4)+3)&~3; + + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(spec); + + /* Open the audio device */ + result = waveOutOpen(&sound, WAVE_MAPPER, &waveformat, + (DWORD_PTR)FillSound, (DWORD_PTR)this, CALLBACK_FUNCTION); + if ( result != MMSYSERR_NOERROR ) { + SetMMerror("waveOutOpen()", result); + return(-1); + } + +#ifdef SOUND_DEBUG + /* Check the sound device we retrieved */ + { + WAVEOUTCAPS caps; + + result = waveOutGetDevCaps((UINT)sound, &caps, sizeof(caps)); + if ( result != MMSYSERR_NOERROR ) { + SetMMerror("waveOutGetDevCaps()", result); + return(-1); + } + printf("Audio device: %s\n", caps.szPname); + } +#endif + + /* Create the audio buffer semaphore */ +#if defined(_WIN32_WCE) && (_WIN32_WCE < 300) + audio_sem = CreateSemaphoreCE(NULL, NUM_BUFFERS-1, NUM_BUFFERS, NULL); +#else + audio_sem = CreateSemaphore(NULL, NUM_BUFFERS-1, NUM_BUFFERS, NULL); +#endif + if ( audio_sem == NULL ) { + SDL_SetError("Couldn't create semaphore"); + return(-1); + } + + /* Create the sound buffers */ + mixbuf = (Uint8 *)SDL_malloc(NUM_BUFFERS*spec->size); + if ( mixbuf == NULL ) { + SDL_SetError("Out of memory"); + return(-1); + } + for ( i = 0; i < NUM_BUFFERS; ++i ) { + SDL_memset(&wavebuf[i], 0, sizeof(wavebuf[i])); + wavebuf[i].lpData = (LPSTR) &mixbuf[i*spec->size]; + wavebuf[i].dwBufferLength = spec->size; + wavebuf[i].dwFlags = WHDR_DONE; + result = waveOutPrepareHeader(sound, &wavebuf[i], + sizeof(wavebuf[i])); + if ( result != MMSYSERR_NOERROR ) { + SetMMerror("waveOutPrepareHeader()", result); + return(-1); + } + } + + /* Ready to go! */ + next_buffer = 0; + return(0); +} diff --git a/src/audio/windib/SDL_dibaudio.h b/src/audio/windib/SDL_dibaudio.h new file mode 100644 index 0000000..1e6036d --- /dev/null +++ b/src/audio/windib/SDL_dibaudio.h @@ -0,0 +1,49 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_lowaudio_h +#define _SDL_lowaudio_h + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +#define NUM_BUFFERS 2 /* -- Don't lower this! */ + +struct SDL_PrivateAudioData { + HWAVEOUT sound; + HANDLE audio_sem; + Uint8 *mixbuf; /* The raw allocated mixing buffer */ + WAVEHDR wavebuf[NUM_BUFFERS]; /* Wave audio fragments */ + int next_buffer; +}; + +/* Old variable names */ +#define sound (this->hidden->sound) +#define audio_sem (this->hidden->audio_sem) +#define mixbuf (this->hidden->mixbuf) +#define wavebuf (this->hidden->wavebuf) +#define next_buffer (this->hidden->next_buffer) + +#endif /* _SDL_lowaudio_h */ diff --git a/src/audio/windx5/SDL_dx5audio.c b/src/audio/windx5/SDL_dx5audio.c new file mode 100644 index 0000000..6998c49 --- /dev/null +++ b/src/audio/windx5/SDL_dx5audio.c @@ -0,0 +1,705 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* Allow access to a raw mixing buffer */ + +#include "SDL_timer.h" +#include "SDL_audio.h" +#include "../SDL_audio_c.h" +#include "SDL_dx5audio.h" + +/* Define this if you want to use DirectX 6 DirectSoundNotify interface */ +//#define USE_POSITION_NOTIFY + +/* DirectX function pointers for audio */ +HRESULT (WINAPI *DSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); + +/* Audio driver functions */ +static int DX5_OpenAudio(_THIS, SDL_AudioSpec *spec); +static void DX5_ThreadInit(_THIS); +static void DX5_WaitAudio_BusyWait(_THIS); +#ifdef USE_POSITION_NOTIFY +static void DX6_WaitAudio_EventWait(_THIS); +#endif +static void DX5_PlayAudio(_THIS); +static Uint8 *DX5_GetAudioBuf(_THIS); +static void DX5_WaitDone(_THIS); +static void DX5_CloseAudio(_THIS); + +/* Audio driver bootstrap functions */ + +static int Audio_Available(void) +{ + HINSTANCE DSoundDLL; + int dsound_ok; + + /* Version check DSOUND.DLL (Is DirectX okay?) */ + dsound_ok = 0; + DSoundDLL = LoadLibrary(TEXT("DSOUND.DLL")); + if ( DSoundDLL != NULL ) { + /* We just use basic DirectSound, we're okay */ + /* Yay! */ + /* Unfortunately, the sound drivers on NT have + higher latencies than the audio buffers used + by many SDL applications, so there are gaps + in the audio - it sounds terrible. Punt for now. + */ + OSVERSIONINFO ver; + ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + GetVersionEx(&ver); + switch (ver.dwPlatformId) { + case VER_PLATFORM_WIN32_NT: + if ( ver.dwMajorVersion > 4 ) { + /* Win2K */ + dsound_ok = 1; + } else { + /* WinNT */ + dsound_ok = 0; + } + break; + default: + /* Win95 or Win98 */ + dsound_ok = 1; + break; + } + /* Now check for DirectX 5 or better - otherwise + * we will fail later in DX5_OpenAudio without a chance + * to fall back to the DIB driver. */ + if (dsound_ok) { + /* DirectSoundCaptureCreate was added in DX5 */ + if (!GetProcAddress(DSoundDLL, TEXT("DirectSoundCaptureCreate"))) + dsound_ok = 0; + + } + /* Clean up.. */ + FreeLibrary(DSoundDLL); + } + return(dsound_ok); +} + +/* Functions for loading the DirectX functions dynamically */ +static HINSTANCE DSoundDLL = NULL; + +static void DX5_Unload(void) +{ + if ( DSoundDLL != NULL ) { + FreeLibrary(DSoundDLL); + DSoundCreate = NULL; + DSoundDLL = NULL; + } +} +static int DX5_Load(void) +{ + int status; + + DX5_Unload(); + DSoundDLL = LoadLibrary(TEXT("DSOUND.DLL")); + if ( DSoundDLL != NULL ) { + DSoundCreate = (void *)GetProcAddress(DSoundDLL, + TEXT("DirectSoundCreate")); + } + if ( DSoundDLL && DSoundCreate ) { + status = 0; + } else { + DX5_Unload(); + status = -1; + } + return status; +} + +static void Audio_DeleteDevice(SDL_AudioDevice *device) +{ + DX5_Unload(); + SDL_free(device->hidden); + SDL_free(device); +} + +static SDL_AudioDevice *Audio_CreateDevice(int devindex) +{ + SDL_AudioDevice *this; + + /* Load DirectX */ + if ( DX5_Load() < 0 ) { + return(NULL); + } + + /* Initialize all variables that we clean on shutdown */ + this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice)); + if ( this ) { + SDL_memset(this, 0, (sizeof *this)); + this->hidden = (struct SDL_PrivateAudioData *) + SDL_malloc((sizeof *this->hidden)); + } + if ( (this == NULL) || (this->hidden == NULL) ) { + SDL_OutOfMemory(); + if ( this ) { + SDL_free(this); + } + return(0); + } + SDL_memset(this->hidden, 0, (sizeof *this->hidden)); + + /* Set the function pointers */ + this->OpenAudio = DX5_OpenAudio; + this->ThreadInit = DX5_ThreadInit; + this->WaitAudio = DX5_WaitAudio_BusyWait; + this->PlayAudio = DX5_PlayAudio; + this->GetAudioBuf = DX5_GetAudioBuf; + this->WaitDone = DX5_WaitDone; + this->CloseAudio = DX5_CloseAudio; + + this->free = Audio_DeleteDevice; + + return this; +} + +AudioBootStrap DSOUND_bootstrap = { + "dsound", "Win95/98/2000 DirectSound", + Audio_Available, Audio_CreateDevice +}; + +static void SetDSerror(const char *function, int code) +{ + static const char *error; + static char errbuf[1024]; + + errbuf[0] = 0; + switch (code) { + case E_NOINTERFACE: + error = + "Unsupported interface\n-- Is DirectX 5.0 or later installed?"; + break; + case DSERR_ALLOCATED: + error = "Audio device in use"; + break; + case DSERR_BADFORMAT: + error = "Unsupported audio format"; + break; + case DSERR_BUFFERLOST: + error = "Mixing buffer was lost"; + break; + case DSERR_CONTROLUNAVAIL: + error = "Control requested is not available"; + break; + case DSERR_INVALIDCALL: + error = "Invalid call for the current state"; + break; + case DSERR_INVALIDPARAM: + error = "Invalid parameter"; + break; + case DSERR_NODRIVER: + error = "No audio device found"; + break; + case DSERR_OUTOFMEMORY: + error = "Out of memory"; + break; + case DSERR_PRIOLEVELNEEDED: + error = "Caller doesn't have priority"; + break; + case DSERR_UNSUPPORTED: + error = "Function not supported"; + break; + default: + SDL_snprintf(errbuf, SDL_arraysize(errbuf), + "%s: Unknown DirectSound error: 0x%x", + function, code); + break; + } + if ( ! errbuf[0] ) { + SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function, error); + } + SDL_SetError("%s", errbuf); + return; +} + +/* DirectSound needs to be associated with a window */ +static HWND mainwin = NULL; +/* */ +void DX5_SoundFocus(HWND hwnd) +{ + mainwin = hwnd; +} + +static void DX5_ThreadInit(_THIS) +{ + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST); +} + +static void DX5_WaitAudio_BusyWait(_THIS) +{ + DWORD status; + DWORD cursor, junk; + HRESULT result; + + /* Semi-busy wait, since we have no way of getting play notification + on a primary mixing buffer located in hardware (DirectX 5.0) + */ + result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, &junk, &cursor); + if ( result != DS_OK ) { + if ( result == DSERR_BUFFERLOST ) { + IDirectSoundBuffer_Restore(mixbuf); + } +#ifdef DEBUG_SOUND + SetDSerror("DirectSound GetCurrentPosition", result); +#endif + return; + } + + while ( (cursor/mixlen) == lastchunk ) { + /* FIXME: find out how much time is left and sleep that long */ + SDL_Delay(1); + + /* Try to restore a lost sound buffer */ + IDirectSoundBuffer_GetStatus(mixbuf, &status); + if ( (status&DSBSTATUS_BUFFERLOST) ) { + IDirectSoundBuffer_Restore(mixbuf); + IDirectSoundBuffer_GetStatus(mixbuf, &status); + if ( (status&DSBSTATUS_BUFFERLOST) ) { + break; + } + } + if ( ! (status&DSBSTATUS_PLAYING) ) { + result = IDirectSoundBuffer_Play(mixbuf, 0, 0, DSBPLAY_LOOPING); + if ( result == DS_OK ) { + continue; + } +#ifdef DEBUG_SOUND + SetDSerror("DirectSound Play", result); +#endif + return; + } + + /* Find out where we are playing */ + result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, + &junk, &cursor); + if ( result != DS_OK ) { + SetDSerror("DirectSound GetCurrentPosition", result); + return; + } + } +} + +#ifdef USE_POSITION_NOTIFY +static void DX6_WaitAudio_EventWait(_THIS) +{ + DWORD status; + HRESULT result; + + /* Try to restore a lost sound buffer */ + IDirectSoundBuffer_GetStatus(mixbuf, &status); + if ( (status&DSBSTATUS_BUFFERLOST) ) { + IDirectSoundBuffer_Restore(mixbuf); + IDirectSoundBuffer_GetStatus(mixbuf, &status); + if ( (status&DSBSTATUS_BUFFERLOST) ) { + return; + } + } + if ( ! (status&DSBSTATUS_PLAYING) ) { + result = IDirectSoundBuffer_Play(mixbuf, 0, 0, DSBPLAY_LOOPING); + if ( result != DS_OK ) { +#ifdef DEBUG_SOUND + SetDSerror("DirectSound Play", result); +#endif + return; + } + } + WaitForSingleObject(audio_event, INFINITE); +} +#endif /* USE_POSITION_NOTIFY */ + +static void DX5_PlayAudio(_THIS) +{ + /* Unlock the buffer, allowing it to play */ + if ( locked_buf ) { + IDirectSoundBuffer_Unlock(mixbuf, locked_buf, mixlen, NULL, 0); + } + +} + +static Uint8 *DX5_GetAudioBuf(_THIS) +{ + DWORD cursor, junk; + HRESULT result; + DWORD rawlen; + + /* Figure out which blocks to fill next */ + locked_buf = NULL; + result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, &junk, &cursor); + if ( result == DSERR_BUFFERLOST ) { + IDirectSoundBuffer_Restore(mixbuf); + result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, + &junk, &cursor); + } + if ( result != DS_OK ) { + SetDSerror("DirectSound GetCurrentPosition", result); + return(NULL); + } + cursor /= mixlen; +#ifdef DEBUG_SOUND + /* Detect audio dropouts */ + { DWORD spot = cursor; + if ( spot < lastchunk ) { + spot += NUM_BUFFERS; + } + if ( spot > lastchunk+1 ) { + fprintf(stderr, "Audio dropout, missed %d fragments\n", + (spot - (lastchunk+1))); + } + } +#endif + lastchunk = cursor; + cursor = (cursor+1)%NUM_BUFFERS; + cursor *= mixlen; + + /* Lock the audio buffer */ + result = IDirectSoundBuffer_Lock(mixbuf, cursor, mixlen, + (LPVOID *)&locked_buf, &rawlen, NULL, &junk, 0); + if ( result == DSERR_BUFFERLOST ) { + IDirectSoundBuffer_Restore(mixbuf); + result = IDirectSoundBuffer_Lock(mixbuf, cursor, mixlen, + (LPVOID *)&locked_buf, &rawlen, NULL, &junk, 0); + } + if ( result != DS_OK ) { + SetDSerror("DirectSound Lock", result); + return(NULL); + } + return(locked_buf); +} + +static void DX5_WaitDone(_THIS) +{ + Uint8 *stream; + + /* Wait for the playing chunk to finish */ + stream = this->GetAudioBuf(this); + if ( stream != NULL ) { + SDL_memset(stream, silence, mixlen); + this->PlayAudio(this); + } + this->WaitAudio(this); + + /* Stop the looping sound buffer */ + IDirectSoundBuffer_Stop(mixbuf); +} + +static void DX5_CloseAudio(_THIS) +{ + if ( sound != NULL ) { + if ( mixbuf != NULL ) { + /* Clean up the audio buffer */ + IDirectSoundBuffer_Release(mixbuf); + mixbuf = NULL; + } + if ( audio_event != NULL ) { + CloseHandle(audio_event); + audio_event = NULL; + } + IDirectSound_Release(sound); + sound = NULL; + } +} + +#ifdef USE_PRIMARY_BUFFER +/* This function tries to create a primary audio buffer, and returns the + number of audio chunks available in the created buffer. +*/ +static int CreatePrimary(LPDIRECTSOUND sndObj, HWND focus, + LPDIRECTSOUNDBUFFER *sndbuf, WAVEFORMATEX *wavefmt, Uint32 chunksize) +{ + HRESULT result; + DSBUFFERDESC format; + DSBCAPS caps; + int numchunks; + + /* Try to set primary mixing privileges */ + result = IDirectSound_SetCooperativeLevel(sndObj, focus, + DSSCL_WRITEPRIMARY); + if ( result != DS_OK ) { +#ifdef DEBUG_SOUND + SetDSerror("DirectSound SetCooperativeLevel", result); +#endif + return(-1); + } + + /* Try to create the primary buffer */ + SDL_memset(&format, 0, sizeof(format)); + format.dwSize = sizeof(format); + format.dwFlags=(DSBCAPS_PRIMARYBUFFER|DSBCAPS_GETCURRENTPOSITION2); + format.dwFlags |= DSBCAPS_STICKYFOCUS; +#ifdef USE_POSITION_NOTIFY + format.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY; +#endif + result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL); + if ( result != DS_OK ) { +#ifdef DEBUG_SOUND + SetDSerror("DirectSound CreateSoundBuffer", result); +#endif + return(-1); + } + + /* Check the size of the fragment buffer */ + SDL_memset(&caps, 0, sizeof(caps)); + caps.dwSize = sizeof(caps); + result = IDirectSoundBuffer_GetCaps(*sndbuf, &caps); + if ( result != DS_OK ) { +#ifdef DEBUG_SOUND + SetDSerror("DirectSound GetCaps", result); +#endif + IDirectSoundBuffer_Release(*sndbuf); + return(-1); + } + if ( (chunksize > caps.dwBufferBytes) || + ((caps.dwBufferBytes%chunksize) != 0) ) { + /* The primary buffer size is not a multiple of 'chunksize' + -- this hopefully doesn't happen when 'chunksize' is a + power of 2. + */ + IDirectSoundBuffer_Release(*sndbuf); + SDL_SetError( +"Primary buffer size is: %d, cannot break it into chunks of %d bytes\n", + caps.dwBufferBytes, chunksize); + return(-1); + } + numchunks = (caps.dwBufferBytes/chunksize); + + /* Set the primary audio format */ + result = IDirectSoundBuffer_SetFormat(*sndbuf, wavefmt); + if ( result != DS_OK ) { +#ifdef DEBUG_SOUND + SetDSerror("DirectSound SetFormat", result); +#endif + IDirectSoundBuffer_Release(*sndbuf); + return(-1); + } + return(numchunks); +} +#endif /* USE_PRIMARY_BUFFER */ + +/* This function tries to create a secondary audio buffer, and returns the + number of audio chunks available in the created buffer. +*/ +static int CreateSecondary(LPDIRECTSOUND sndObj, HWND focus, + LPDIRECTSOUNDBUFFER *sndbuf, WAVEFORMATEX *wavefmt, Uint32 chunksize) +{ + const int numchunks = 8; + HRESULT result; + DSBUFFERDESC format; + LPVOID pvAudioPtr1, pvAudioPtr2; + DWORD dwAudioBytes1, dwAudioBytes2; + + /* Try to set primary mixing privileges */ + if ( focus ) { + result = IDirectSound_SetCooperativeLevel(sndObj, + focus, DSSCL_PRIORITY); + } else { + result = IDirectSound_SetCooperativeLevel(sndObj, + GetDesktopWindow(), DSSCL_NORMAL); + } + if ( result != DS_OK ) { +#ifdef DEBUG_SOUND + SetDSerror("DirectSound SetCooperativeLevel", result); +#endif + return(-1); + } + + /* Try to create the secondary buffer */ + SDL_memset(&format, 0, sizeof(format)); + format.dwSize = sizeof(format); + format.dwFlags = DSBCAPS_GETCURRENTPOSITION2; +#ifdef USE_POSITION_NOTIFY + format.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY; +#endif + if ( ! focus ) { + format.dwFlags |= DSBCAPS_GLOBALFOCUS; + } else { + format.dwFlags |= DSBCAPS_STICKYFOCUS; + } + format.dwBufferBytes = numchunks*chunksize; + if ( (format.dwBufferBytes < DSBSIZE_MIN) || + (format.dwBufferBytes > DSBSIZE_MAX) ) { + SDL_SetError("Sound buffer size must be between %d and %d", + DSBSIZE_MIN/numchunks, DSBSIZE_MAX/numchunks); + return(-1); + } + format.dwReserved = 0; + format.lpwfxFormat = wavefmt; + result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL); + if ( result != DS_OK ) { + SetDSerror("DirectSound CreateSoundBuffer", result); + return(-1); + } + IDirectSoundBuffer_SetFormat(*sndbuf, wavefmt); + + /* Silence the initial audio buffer */ + result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes, + (LPVOID *)&pvAudioPtr1, &dwAudioBytes1, + (LPVOID *)&pvAudioPtr2, &dwAudioBytes2, + DSBLOCK_ENTIREBUFFER); + if ( result == DS_OK ) { + if ( wavefmt->wBitsPerSample == 8 ) { + SDL_memset(pvAudioPtr1, 0x80, dwAudioBytes1); + } else { + SDL_memset(pvAudioPtr1, 0x00, dwAudioBytes1); + } + IDirectSoundBuffer_Unlock(*sndbuf, + (LPVOID)pvAudioPtr1, dwAudioBytes1, + (LPVOID)pvAudioPtr2, dwAudioBytes2); + } + + /* We're ready to go */ + return(numchunks); +} + +/* This function tries to set position notify events on the mixing buffer */ +#ifdef USE_POSITION_NOTIFY +static int CreateAudioEvent(_THIS) +{ + LPDIRECTSOUNDNOTIFY notify; + DSBPOSITIONNOTIFY *notify_positions; + int i, retval; + HRESULT result; + + /* Default to fail on exit */ + retval = -1; + notify = NULL; + + /* Query for the interface */ + result = IDirectSoundBuffer_QueryInterface(mixbuf, + &IID_IDirectSoundNotify, (void *)¬ify); + if ( result != DS_OK ) { + goto done; + } + + /* Allocate the notify structures */ + notify_positions = (DSBPOSITIONNOTIFY *)SDL_malloc(NUM_BUFFERS* + sizeof(*notify_positions)); + if ( notify_positions == NULL ) { + goto done; + } + + /* Create the notify event */ + audio_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if ( audio_event == NULL ) { + goto done; + } + + /* Set up the notify structures */ + for ( i=0; i<NUM_BUFFERS; ++i ) { + notify_positions[i].dwOffset = i*mixlen; + notify_positions[i].hEventNotify = audio_event; + } + result = IDirectSoundNotify_SetNotificationPositions(notify, + NUM_BUFFERS, notify_positions); + if ( result == DS_OK ) { + retval = 0; + } +done: + if ( notify != NULL ) { + IDirectSoundNotify_Release(notify); + } + return(retval); +} +#endif /* USE_POSITION_NOTIFY */ + +static int DX5_OpenAudio(_THIS, SDL_AudioSpec *spec) +{ + HRESULT result; + WAVEFORMATEX waveformat; + + /* Set basic WAVE format parameters */ + SDL_memset(&waveformat, 0, sizeof(waveformat)); + waveformat.wFormatTag = WAVE_FORMAT_PCM; + + /* Determine the audio parameters from the AudioSpec */ + switch ( spec->format & 0xFF ) { + case 8: + /* Unsigned 8 bit audio data */ + spec->format = AUDIO_U8; + silence = 0x80; + waveformat.wBitsPerSample = 8; + break; + case 16: + /* Signed 16 bit audio data */ + spec->format = AUDIO_S16; + silence = 0x00; + waveformat.wBitsPerSample = 16; + break; + default: + SDL_SetError("Unsupported audio format"); + return(-1); + } + waveformat.nChannels = spec->channels; + waveformat.nSamplesPerSec = spec->freq; + waveformat.nBlockAlign = + waveformat.nChannels * (waveformat.wBitsPerSample/8); + waveformat.nAvgBytesPerSec = + waveformat.nSamplesPerSec * waveformat.nBlockAlign; + + /* Update the fragment size as size in bytes */ + SDL_CalculateAudioSpec(spec); + + /* Open the audio device */ + result = DSoundCreate(NULL, &sound, NULL); + if ( result != DS_OK ) { + SetDSerror("DirectSoundCreate", result); + return(-1); + } + + /* Create the audio buffer to which we write */ + NUM_BUFFERS = -1; +#ifdef USE_PRIMARY_BUFFER + if ( mainwin ) { + NUM_BUFFERS = CreatePrimary(sound, mainwin, &mixbuf, + &waveformat, spec->size); + } +#endif /* USE_PRIMARY_BUFFER */ + if ( NUM_BUFFERS < 0 ) { + NUM_BUFFERS = CreateSecondary(sound, mainwin, &mixbuf, + &waveformat, spec->size); + if ( NUM_BUFFERS < 0 ) { + return(-1); + } +#ifdef DEBUG_SOUND + fprintf(stderr, "Using secondary audio buffer\n"); +#endif + } +#ifdef DEBUG_SOUND + else + fprintf(stderr, "Using primary audio buffer\n"); +#endif + + /* The buffer will auto-start playing in DX5_WaitAudio() */ + lastchunk = 0; + mixlen = spec->size; + +#ifdef USE_POSITION_NOTIFY + /* See if we can use DirectX 6 event notification */ + if ( CreateAudioEvent(this) == 0 ) { + this->WaitAudio = DX6_WaitAudio_EventWait; + } else { + this->WaitAudio = DX5_WaitAudio_BusyWait; + } +#endif + return(0); +} + diff --git a/src/audio/windx5/SDL_dx5audio.h b/src/audio/windx5/SDL_dx5audio.h new file mode 100644 index 0000000..797b304 --- /dev/null +++ b/src/audio/windx5/SDL_dx5audio.h @@ -0,0 +1,55 @@ +/* + SDL - Simple DirectMedia Layer + 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 Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef _SDL_lowaudio_h +#define _SDL_lowaudio_h + +#include "directx.h" + +#include "../SDL_sysaudio.h" + +/* Hidden "this" pointer for the video functions */ +#define _THIS SDL_AudioDevice *this + +/* The DirectSound objects */ +struct SDL_PrivateAudioData { + LPDIRECTSOUND sound; + LPDIRECTSOUNDBUFFER mixbuf; + int NUM_BUFFERS; + int mixlen, silence; + DWORD lastchunk; + Uint8 *locked_buf; + HANDLE audio_event; +}; + +/* Old variable names */ +#define sound (this->hidden->sound) +#define mixbuf (this->hidden->mixbuf) +#define NUM_BUFFERS (this->hidden->NUM_BUFFERS) +#define mixlen (this->hidden->mixlen) +#define silence (this->hidden->silence) +#define lastchunk (this->hidden->lastchunk) +#define locked_buf (this->hidden->locked_buf) +#define audio_event (this->hidden->audio_event) + +#endif /* _SDL_lowaudio_h */ diff --git a/src/audio/windx5/directx.h b/src/audio/windx5/directx.h new file mode 100644 index 0000000..d14d6c6 --- /dev/null +++ b/src/audio/windx5/directx.h @@ -0,0 +1,84 @@ + +#ifndef _directx_h +#define _directx_h + +/* Include all of the DirectX 5.0 headers and adds any necessary tweaks */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <mmsystem.h> +#ifndef WIN32 +#define WIN32 +#endif +#undef WINNT + +/* Far pointers don't exist in 32-bit code */ +#ifndef FAR +#define FAR +#endif + +/* Error codes not yet included in Win32 API header files */ +#ifndef MAKE_HRESULT +#define MAKE_HRESULT(sev,fac,code) \ + ((HRESULT)(((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code)))) +#endif + +#ifndef S_OK +#define S_OK (HRESULT)0x00000000L +#endif + +#ifndef SUCCEEDED +#define SUCCEEDED(x) ((HRESULT)(x) >= 0) +#endif +#ifndef FAILED +#define FAILED(x) ((HRESULT)(x)<0) +#endif + +#ifndef E_FAIL +#define E_FAIL (HRESULT)0x80000008L +#endif +#ifndef E_NOINTERFACE +#define E_NOINTERFACE (HRESULT)0x80004002L +#endif +#ifndef E_OUTOFMEMORY +#define E_OUTOFMEMORY (HRESULT)0x8007000EL +#endif +#ifndef E_INVALIDARG +#define E_INVALIDARG (HRESULT)0x80070057L +#endif +#ifndef E_NOTIMPL +#define E_NOTIMPL (HRESULT)0x80004001L +#endif +#ifndef REGDB_E_CLASSNOTREG +#define REGDB_E_CLASSNOTREG (HRESULT)0x80040154L +#endif + +/* Severity codes */ +#ifndef SEVERITY_ERROR +#define SEVERITY_ERROR 1 +#endif + +/* Error facility codes */ +#ifndef FACILITY_WIN32 +#define FACILITY_WIN32 7 +#endif + +#ifndef FIELD_OFFSET +#define FIELD_OFFSET(type, field) ((LONG)&(((type *)0)->field)) +#endif + +/* DirectX headers (if it isn't included, I haven't tested it yet) + */ +/* We need these defines to mark what version of DirectX API we use */ +#define DIRECTDRAW_VERSION 0x0700 +#define DIRECTSOUND_VERSION 0x0500 +#define DIRECTINPUT_VERSION 0x0500 + +#ifdef __GNUC__ +#define NONAMELESSUNION +#endif +#include <ddraw.h> +#include <dsound.h> +#include <dinput.h> + +#endif /* _directx_h */ |