raze-gles/source/audiolib/src/driver_sdl.cpp

321 lines
6.9 KiB
C++
Raw Normal View History

/*
Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/**
* libSDL output driver for MultiVoc
*/
#include "driver_sdl.h"
#include "compat.h"
#include "multivoc.h"
#include "mutex.h"
#include "sdl_inc.h"
#include "vfs.h"
enum {
SDLErr_Warning = -2,
SDLErr_Error = -1,
SDLErr_Ok = 0,
SDLErr_Uninitialised,
SDLErr_InitSubSystem,
SDLErr_OpenAudio
};
static int ErrorCode = SDLErr_Ok;
static int Initialised;
static int Playing;
static uint32_t StartedSDL;
static char *MixBuffer;
static int MixBufferSize;
static int MixBufferCount;
static int MixBufferCurrent;
static int MixBufferUsed;
static void (*MixCallBack)(void);
#if (SDL_MAJOR_VERSION == 2)
static SDL_AudioDeviceID audio_dev;
#endif
static void fillData(void * userdata, Uint8 * ptr, int remaining)
{
if (!MixBuffer || !MixCallBack)
return;
UNREFERENCED_PARAMETER(userdata);
int len;
char *sptr;
while (remaining > 0) {
if (MixBufferUsed == MixBufferSize) {
MixCallBack();
MixBufferUsed = 0;
MixBufferCurrent++;
if (MixBufferCurrent >= MixBufferCount) {
MixBufferCurrent -= MixBufferCount;
}
}
while (remaining > 0 && MixBufferUsed < MixBufferSize) {
sptr = MixBuffer + (MixBufferCurrent * MixBufferSize) + MixBufferUsed;
len = MixBufferSize - MixBufferUsed;
if (remaining < len) {
len = remaining;
}
memcpy(ptr, sptr, len);
ptr += len;
MixBufferUsed += len;
remaining -= len;
}
}
}
int SDLDrv_GetError(void)
{
return ErrorCode;
}
const char *SDLDrv_ErrorString(int ErrorNumber)
{
const char *ErrorString;
switch( ErrorNumber ) {
case SDLErr_Warning :
case SDLErr_Error :
ErrorString = SDLDrv_ErrorString( ErrorCode );
break;
case SDLErr_Ok :
ErrorString = "SDL Audio ok.";
break;
case SDLErr_Uninitialised:
ErrorString = "SDL Audio uninitialised.";
break;
case SDLErr_InitSubSystem:
ErrorString = "SDL Audio: error in Init or InitSubSystem.";
break;
case SDLErr_OpenAudio:
ErrorString = "SDL Audio: error in OpenAudio.";
break;
default:
ErrorString = "Unknown SDL Audio error code.";
break;
}
return ErrorString;
}
int SDLDrv_PCM_Init(int *mixrate, int *numchannels, void * initdata)
{
UNREFERENCED_PARAMETER(initdata);
Uint32 inited;
int err = 0;
SDL_AudioSpec spec, actual;
if (Initialised) {
SDLDrv_PCM_Shutdown();
}
inited = SDL_WasInit(SDL_INIT_AUDIO);
if (!(inited & SDL_INIT_AUDIO)) {
err = SDL_InitSubSystem(SDL_INIT_AUDIO);
StartedSDL = SDL_WasInit(SDL_INIT_AUDIO);
}
if (err < 0) {
ErrorCode = SDLErr_InitSubSystem;
return SDLErr_Error;
}
int chunksize = 512;
#ifdef __ANDROID__
chunksize = droidinfo.audio_buffer_size;
#endif
spec.freq = *mixrate;
spec.format = AUDIO_S16SYS;
spec.channels = *numchannels;
spec.samples = chunksize;
spec.callback = fillData;
spec.userdata = nullptr;
Bmemset(&actual, 0, sizeof(actual));
#if (SDL_MAJOR_VERSION == 1)
err = !SDL_OpenAudio(&spec, &actual);
#else
audio_dev = err = SDL_OpenAudioDevice(nullptr, 0, &spec, &actual, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
#endif
if (err == 0) {
ErrorCode = SDLErr_OpenAudio;
return SDLErr_Error;
}
#if (SDL_MAJOR_VERSION == 1)
char drivername[64] = "(error)";
SDL_AudioDriverName(drivername, sizeof(drivername));
MV_Printf("SDL %s driver\n", drivername);
#else
auto drivername = Xstrdup(SDL_GetCurrentAudioDriver());
for (int i=0;drivername[i] != 0;++i)
drivername[i] = toupperlookup[drivername[i]];
auto devname = Xstrdup(SDL_GetAudioDeviceName(0, 0));
auto pdevname = Bstrchr(devname, '(');
if (pdevname)
{
auto rt = Bstrchr(pdevname++, ')');
if (rt != nullptr) *rt = '\0';
}
else
pdevname = devname;
MV_Printf("SDL %s driver on %s\n", drivername, pdevname);
Xfree(devname);
Xfree(drivername);
#endif
#if (SDL_MAJOR_VERSION == 1)
if (actual.freq == 0 || actual.channels == 0) {
// hack for when SDL said it opened the audio, but clearly didn't
SDL_CloseAudio();
ErrorCode = SDLErr_OpenAudio;
return SDLErr_Error;
}
#endif
err = 0;
*mixrate = actual.freq;
if (actual.format == AUDIO_U8 || actual.format == AUDIO_S8)
{
ErrorCode = SDLErr_OpenAudio;
err = 1;
}
*numchannels = actual.channels;
if (actual.channels != 1 && actual.channels != 2)
{
ErrorCode = SDLErr_OpenAudio;
err = 1;
}
if (err)
{
SDL_CloseAudio();
return SDLErr_Error;
}
Initialised = 1;
return SDLErr_Ok;
}
void SDLDrv_PCM_Shutdown(void)
{
if (!Initialised)
return;
if (StartedSDL)
SDL_QuitSubSystem(StartedSDL);
StartedSDL = 0;
Initialised = 0;
}
int SDLDrv_PCM_BeginPlayback(char *BufferStart, int BufferSize,
int NumDivisions, void ( *CallBackFunc )( void ) )
{
if (!Initialised) {
ErrorCode = SDLErr_Uninitialised;
return SDLErr_Error;
}
if (Playing) {
SDLDrv_PCM_StopPlayback();
}
MixBuffer = BufferStart;
MixBufferSize = BufferSize;
MixBufferCount = NumDivisions;
MixBufferCurrent = 0;
MixBufferUsed = 0;
MixCallBack = CallBackFunc;
// prime the buffer
MixCallBack();
#if (SDL_MAJOR_VERSION == 2)
SDL_PauseAudioDevice(audio_dev, 0);
#else
SDL_PauseAudio(0);
#endif
Playing = 1;
return SDLErr_Ok;
}
void SDLDrv_PCM_StopPlayback(void)
{
if (!Initialised || !Playing) {
return;
}
#if (SDL_MAJOR_VERSION == 2)
SDL_PauseAudioDevice(audio_dev, 1);
#else
SDL_PauseAudio(1);
#endif
Playing = 0;
}
void SDLDrv_PCM_Lock(void)
{
#if (SDL_MAJOR_VERSION == 2)
SDL_LockAudioDevice(audio_dev);
#else
SDL_LockAudio();
#endif
}
void SDLDrv_PCM_Unlock(void)
{
#if (SDL_MAJOR_VERSION == 2)
SDL_UnlockAudioDevice(audio_dev);
#else
SDL_UnlockAudio();
#endif
}