#include #include #include "dsl.h" #include "util.h" #include "SDL.h" #include "SDL_mixer.h" extern volatile int MV_MixPage; static int DSL_ErrorCode = DSL_Ok; static int mixer_initialized; static void ( *_CallBackFunc )( void ); static volatile char *_BufferStart; static int _BufferSize; static int _NumDivisions; static int _SampleRate; static int _remainder; static Mix_Chunk *blank; static unsigned char *blank_buf; /* possible todo ideas: cache sdl/sdl mixer error messages. */ char *DSL_ErrorString( int ErrorNumber ) { char *ErrorString; switch (ErrorNumber) { case DSL_Warning: case DSL_Error: ErrorString = DSL_ErrorString(DSL_ErrorCode); break; case DSL_Ok: ErrorString = "SDL Driver ok."; break; case DSL_SDLInitFailure: ErrorString = "SDL Audio initialization failed."; break; case DSL_MixerActive: ErrorString = "SDL Mixer already initialized."; break; case DSL_MixerInitFailure: ErrorString = "SDL Mixer initialization failed."; break; default: ErrorString = "Unknown SDL Driver error."; break; } return ErrorString; } static void DSL_SetErrorCode(int ErrorCode) { DSL_ErrorCode = ErrorCode; } int DSL_Init( void ) { DSL_SetErrorCode(DSL_Ok); if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { DSL_SetErrorCode(DSL_SDLInitFailure); return DSL_Error; } return DSL_Ok; } void DSL_Shutdown( void ) { DSL_StopPlayback(); } static void mixer_callback(int chan, void *stream, int len, void *udata) { Uint8 *stptr; Uint8 *fxptr; int copysize; /* len should equal _BufferSize, else this is screwed up */ stptr = (Uint8 *)stream; if (_remainder > 0) { copysize = min(len, _remainder); fxptr = (Uint8 *)(&_BufferStart[MV_MixPage * _BufferSize]); memcpy(stptr, fxptr+(_BufferSize-_remainder), copysize); len -= copysize; _remainder -= copysize; stptr += copysize; } while (len > 0) { /* new buffer */ _CallBackFunc(); fxptr = (Uint8 *)(&_BufferStart[MV_MixPage * _BufferSize]); copysize = min(len, _BufferSize); memcpy(stptr, fxptr, copysize); len -= copysize; stptr += copysize; } _remainder = len; } int DSL_BeginBufferedPlayback( char *BufferStart, int BufferSize, int NumDivisions, unsigned SampleRate, int MixMode, void ( *CallBackFunc )( void ) ) { Uint16 format; Uint8 *tmp; int channels; int chunksize; if (mixer_initialized) { DSL_SetErrorCode(DSL_MixerActive); return DSL_Error; } _CallBackFunc = CallBackFunc; _BufferStart = BufferStart; _BufferSize = (BufferSize / NumDivisions); _NumDivisions = NumDivisions; _SampleRate = SampleRate; _remainder = 0; format = (MixMode & SIXTEEN_BIT) ? AUDIO_S16SYS : AUDIO_U8; channels = (MixMode & STEREO) ? 2 : 1; /* 23ms is typically ideal (11025,22050,44100) 46ms isn't bad */ chunksize = 512; if (SampleRate >= 16000) chunksize *= 2; if (SampleRate >= 32000) chunksize *= 2; /* // SDL mixer does this already if (MixMode & SIXTEEN_BIT) chunksize *= 2; if (MixMode & STEREO) chunksize *= 2; */ if (Mix_OpenAudio(SampleRate, format, channels, chunksize) < 0) { DSL_SetErrorCode(DSL_MixerInitFailure); return DSL_Error; } /* Mix_SetPostMix(mixer_callback, NULL); */ /* have to use a channel because postmix will overwrite the music... */ Mix_RegisterEffect(0, mixer_callback, NULL, NULL); /* create a dummy sample just to allocate that channel */ blank_buf = (Uint8 *)malloc(4096); memset(blank_buf, 0, 4096); blank = Mix_QuickLoad_RAW(blank_buf, 4096); Mix_PlayChannel(0, blank, -1); mixer_initialized = 1; return DSL_Ok; } void DSL_StopPlayback( void ) { if (mixer_initialized) { Mix_HaltChannel(0); } if (blank != NULL) { Mix_FreeChunk(blank); } blank = NULL; if (blank_buf != NULL) { free(blank_buf); } blank_buf = NULL; if (mixer_initialized) { SDL_UnlockAudio(); /* just in case */ Mix_CloseAudio(); } mixer_initialized = 0; } unsigned DSL_GetPlaybackRate( void ) { return _SampleRate; } unsigned long DisableInterrupts( void ) { SDL_LockAudio(); return 0; } void RestoreInterrupts( unsigned long flags ) { SDL_UnlockAudio(); }