mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-18 02:02:05 +00:00
216 lines
4.7 KiB
C
216 lines
4.7 KiB
C
|
#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;
|
||
|
}
|