#include #include "fmod.h" #include "fmod_output.h" #include "SDL.h" #define CONVERTBUFFER_SIZE 4096 // in bytes #define D(x) #define FALSE 0 #define TRUE 1 typedef int BOOL; struct AudioData { FMOD_OUTPUT_STATE *Output; BOOL ConvertU8toS8; BOOL ConvertU16toS16; int BytesPerSample; }; FMOD_SOUND_FORMAT Format_SDLtoFMOD(Uint16 format) { if ((format & (AUDIO_U8 | AUDIO_U16LSB)) == AUDIO_U8) { return FMOD_SOUND_FORMAT_PCM8; } return FMOD_SOUND_FORMAT_PCM16; } Uint16 Format_FMODtoSDL(FMOD_SOUND_FORMAT format) { switch (format) { case FMOD_SOUND_FORMAT_PCM8: return AUDIO_S8; case FMOD_SOUND_FORMAT_PCM16: return AUDIO_S16SYS; default: return AUDIO_S16SYS; } } static void SDLCALL AudioCallback(void *userdata, Uint8 *stream, int len) { struct AudioData *data = (struct AudioData *)userdata; int i; data->Output->readfrommixer(data->Output, stream, len / data->BytesPerSample); if (data->ConvertU8toS8) { for (i = 0; i < len; ++i) { stream[i] -= 0x80; } } else if (data->ConvertU16toS16) { len /= 2; for (i = 0; i < len; ++i) { ((short *)stream)[i] -= 0x8000; } } } static FMOD_RESULT F_CALLBACK GetNumDrivers(FMOD_OUTPUT_STATE *output_state, int *numdrivers) { if (numdrivers == NULL) { return FMOD_ERR_INVALID_PARAM; } *numdrivers = 1; return FMOD_OK; } static FMOD_RESULT F_CALLBACK GetDriverName(FMOD_OUTPUT_STATE *output_state, int id, char *name, int namelen) { if (id != 0 || name == NULL) { return FMOD_ERR_INVALID_PARAM; } strncpy(name, "SDL default", namelen); return FMOD_OK; } static FMOD_RESULT F_CALLBACK GetDriverCaps(FMOD_OUTPUT_STATE *output_state, int id, FMOD_CAPS *caps) { if (id != 0 || caps == NULL) { return FMOD_ERR_INVALID_PARAM; } *caps = FMOD_CAPS_OUTPUT_FORMAT_PCM8 | FMOD_CAPS_OUTPUT_FORMAT_PCM16 | FMOD_CAPS_OUTPUT_MULTICHANNEL; return FMOD_OK; } static FMOD_RESULT F_CALLBACK Init(FMOD_OUTPUT_STATE *output_state, int selecteddriver, FMOD_INITFLAGS flags, int *outputrate, int outputchannels, FMOD_SOUND_FORMAT *outputformat, int dspbufferlength, int dspnumbuffers, void *extradriverdata) { SDL_AudioSpec desired, obtained; struct AudioData *data; if (selecteddriver != 0 || outputrate == NULL || outputformat == NULL) { D(printf("invalid param\n")); return FMOD_ERR_INVALID_PARAM; } if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { D(printf("init subsystem failed\n")); return FMOD_ERR_OUTPUT_INIT; } data = malloc(sizeof(*data)); if (data == NULL) { D(printf("nomem\n")); SDL_QuitSubSystem(SDL_INIT_AUDIO); return FMOD_ERR_MEMORY; } desired.freq = *outputrate; desired.format = Format_FMODtoSDL(*outputformat); desired.channels = outputchannels; desired.samples = dspbufferlength; desired.callback = AudioCallback; desired.userdata = data; if (SDL_OpenAudio(&desired, &obtained) < 0) { D(printf("openaudio failed\n")); free(data); SDL_QuitSubSystem(SDL_INIT_AUDIO); return FMOD_ERR_OUTPUT_INIT; } if (obtained.channels != outputchannels) { // Obtained channels don't match what we wanted. SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); free(data); return FMOD_ERR_OUTPUT_CREATEBUFFER; } data->Output = output_state; data->ConvertU8toS8 = FALSE; data->ConvertU16toS16 = FALSE; if (obtained.format == AUDIO_U8) { data->ConvertU8toS8 = TRUE; D(printf("convert u8 to s8\n")); } else if (obtained.format == AUDIO_U16SYS) { data->ConvertU16toS16 = TRUE; D(printf("convert u16 to s16\n")); } output_state->plugindata = data; *outputrate = obtained.freq; *outputformat = Format_SDLtoFMOD(obtained.format); data->BytesPerSample = *outputformat == FMOD_SOUND_FORMAT_PCM16 ? 2 : 1; data->BytesPerSample *= desired.channels; D(printf("init ok\n")); SDL_PauseAudio(0); return FMOD_OK; } static FMOD_RESULT F_CALLBACK Close(FMOD_OUTPUT_STATE *output_state) { struct AudioData *data = (struct AudioData *)output_state->plugindata; D(printf("Close\n")); if (data != NULL) { SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); free(data); } return FMOD_OK; } static FMOD_RESULT F_CALLBACK Update(FMOD_OUTPUT_STATE *update) { return FMOD_OK; } static FMOD_RESULT F_CALLBACK GetHandle(FMOD_OUTPUT_STATE *output_state, void **handle) { D(printf("Gethandle\n")); // SDL's audio isn't multi-instanced, so this is pretty meaningless if (handle == NULL) { return FMOD_ERR_INVALID_PARAM; } *handle = output_state->plugindata; return FMOD_OK; } static FMOD_OUTPUT_DESCRIPTION Desc = { "SDL Output", // name 1, // version 0, // polling GetNumDrivers, GetDriverName, GetDriverCaps, Init, Close, Update, GetHandle, NULL, // getposition NULL, // lock NULL // unlock }; F_DECLSPEC F_DLLEXPORT FMOD_OUTPUT_DESCRIPTION * F_API FMODGetOutputDescription() { return &Desc; }