gzdoom/output_sdl/output_sdl.c

216 lines
4.7 KiB
C
Raw Permalink Normal View History

#include <stdio.h>
#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;
}