2009-07-27 05:47:50 +00:00
|
|
|
/*
|
|
|
|
Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au>
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
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.
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
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.
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
See the GNU General Public License for more details.
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
2014-07-20 08:55:56 +00:00
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
*/
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
/**
|
|
|
|
* libSDL output driver for MultiVoc
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "driver_sdl.h"
|
2019-08-08 02:22:16 +00:00
|
|
|
|
|
|
|
#include "compat.h"
|
2012-12-29 10:59:21 +00:00
|
|
|
#include "multivoc.h"
|
2019-08-08 02:22:16 +00:00
|
|
|
#include "mutex.h"
|
|
|
|
#include "sdl_inc.h"
|
2019-10-19 23:47:29 +00:00
|
|
|
#include "vfs.h"
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
enum {
|
|
|
|
SDLErr_Warning = -2,
|
|
|
|
SDLErr_Error = -1,
|
|
|
|
SDLErr_Ok = 0,
|
|
|
|
SDLErr_Uninitialised,
|
|
|
|
SDLErr_InitSubSystem,
|
|
|
|
SDLErr_OpenAudio
|
|
|
|
};
|
|
|
|
|
2019-10-19 23:47:54 +00:00
|
|
|
static int ErrorCode = SDLErr_Ok;
|
|
|
|
static int Initialised;
|
|
|
|
static int Playing;
|
2019-10-19 23:47:29 +00:00
|
|
|
static uint32_t StartedSDL;
|
2009-07-27 05:47:50 +00:00
|
|
|
|
2019-10-19 23:47:19 +00:00
|
|
|
static char *MixBuffer;
|
2019-10-19 23:47:54 +00:00
|
|
|
static int MixBufferSize;
|
|
|
|
static int MixBufferCount;
|
|
|
|
static int MixBufferCurrent;
|
|
|
|
static int MixBufferUsed;
|
2019-10-19 23:47:29 +00:00
|
|
|
static void (*MixCallBack)(void);
|
2009-07-27 05:47:50 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
#if (SDL_MAJOR_VERSION == 2)
|
|
|
|
static SDL_AudioDeviceID audio_dev;
|
|
|
|
#endif
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
static void fillData(void * userdata, Uint8 * ptr, int remaining)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2019-10-19 23:47:54 +00:00
|
|
|
if (!MixBuffer || !MixCallBack)
|
|
|
|
return;
|
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
UNREFERENCED_PARAMETER(userdata);
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
int len;
|
|
|
|
char *sptr;
|
2010-01-11 17:25:47 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
while (remaining > 0) {
|
|
|
|
if (MixBufferUsed == MixBufferSize) {
|
|
|
|
MixCallBack();
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
MixBufferUsed = 0;
|
|
|
|
MixBufferCurrent++;
|
|
|
|
if (MixBufferCurrent >= MixBufferCount) {
|
|
|
|
MixBufferCurrent -= MixBufferCount;
|
|
|
|
}
|
|
|
|
}
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
while (remaining > 0 && MixBufferUsed < MixBufferSize) {
|
|
|
|
sptr = MixBuffer + (MixBufferCurrent * MixBufferSize) + MixBufferUsed;
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
len = MixBufferSize - MixBufferUsed;
|
|
|
|
if (remaining < len) {
|
|
|
|
len = remaining;
|
|
|
|
}
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
memcpy(ptr, sptr, len);
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
ptr += len;
|
2018-10-25 23:33:14 +00:00
|
|
|
MixBufferUsed += len;
|
|
|
|
remaining -= len;
|
|
|
|
}
|
|
|
|
}
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-19 23:47:54 +00:00
|
|
|
int SDLDrv_GetError(void)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
|
|
|
return ErrorCode;
|
|
|
|
}
|
|
|
|
|
2019-10-19 23:48:20 +00:00
|
|
|
const char *SDLDrv_ErrorString(int ErrorNumber)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
|
|
|
const char *ErrorString;
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
switch( ErrorNumber ) {
|
|
|
|
case SDLErr_Warning :
|
|
|
|
case SDLErr_Error :
|
|
|
|
ErrorString = SDLDrv_ErrorString( ErrorCode );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SDLErr_Ok :
|
|
|
|
ErrorString = "SDL Audio ok.";
|
|
|
|
break;
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-10-19 23:47:54 +00:00
|
|
|
int SDLDrv_PCM_Init(int *mixrate, int *numchannels, void * initdata)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2009-07-28 06:32:58 +00:00
|
|
|
UNREFERENCED_PARAMETER(initdata);
|
2009-07-27 05:47:50 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
Uint32 inited;
|
|
|
|
int err = 0;
|
|
|
|
SDL_AudioSpec spec, actual;
|
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
if (Initialised) {
|
2009-07-31 11:45:29 +00:00
|
|
|
SDLDrv_PCM_Shutdown();
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
inited = SDL_WasInit(SDL_INIT_AUDIO);
|
2017-03-01 09:35:58 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
if (!(inited & SDL_INIT_AUDIO)) {
|
2017-03-01 09:35:58 +00:00
|
|
|
err = SDL_InitSubSystem(SDL_INIT_AUDIO);
|
2019-10-19 23:47:29 +00:00
|
|
|
StartedSDL = SDL_WasInit(SDL_INIT_AUDIO);
|
2017-03-01 09:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
ErrorCode = SDLErr_InitSubSystem;
|
|
|
|
return SDLErr_Error;
|
|
|
|
}
|
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
int chunksize = 512;
|
2014-04-15 19:02:48 +00:00
|
|
|
#ifdef __ANDROID__
|
|
|
|
chunksize = droidinfo.audio_buffer_size;
|
|
|
|
#endif
|
2010-01-11 19:00:32 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
spec.freq = *mixrate;
|
|
|
|
spec.format = AUDIO_S16SYS;
|
|
|
|
spec.channels = *numchannels;
|
|
|
|
spec.samples = chunksize;
|
|
|
|
spec.callback = fillData;
|
|
|
|
spec.userdata = nullptr;
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
Bmemset(&actual, 0, sizeof(actual));
|
2009-07-27 05:47:50 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
#if (SDL_MAJOR_VERSION == 1)
|
|
|
|
err = !SDL_OpenAudio(&spec, &actual);
|
|
|
|
#else
|
2019-10-19 23:48:20 +00:00
|
|
|
audio_dev = err = SDL_OpenAudioDevice(nullptr, 0, &spec, &actual, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
2019-10-19 23:47:29 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
if (err == 0) {
|
2009-07-27 05:47:50 +00:00
|
|
|
ErrorCode = SDLErr_OpenAudio;
|
|
|
|
return SDLErr_Error;
|
|
|
|
}
|
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
#if (SDL_MAJOR_VERSION == 1)
|
|
|
|
char drivername[64] = "(error)";
|
|
|
|
SDL_AudioDriverName(drivername, sizeof(drivername));
|
|
|
|
MV_Printf("SDL %s driver\n", drivername);
|
|
|
|
#else
|
2019-10-19 23:48:20 +00:00
|
|
|
auto drivername = Xstrdup(SDL_GetCurrentAudioDriver());
|
2015-07-08 03:34:09 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
for (int i=0;drivername[i] != 0;++i)
|
|
|
|
drivername[i] = toupperlookup[drivername[i]];
|
|
|
|
|
2019-10-19 23:48:20 +00:00
|
|
|
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);
|
2019-10-19 23:47:29 +00:00
|
|
|
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();
|
2015-07-08 03:34:09 +00:00
|
|
|
ErrorCode = SDLErr_OpenAudio;
|
|
|
|
return SDLErr_Error;
|
|
|
|
}
|
2019-10-19 23:47:29 +00:00
|
|
|
#endif
|
|
|
|
err = 0;
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
*mixrate = actual.freq;
|
|
|
|
if (actual.format == AUDIO_U8 || actual.format == AUDIO_S8)
|
|
|
|
{
|
|
|
|
ErrorCode = SDLErr_OpenAudio;
|
|
|
|
err = 1;
|
|
|
|
}
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
*numchannels = actual.channels;
|
|
|
|
if (actual.channels != 1 && actual.channels != 2)
|
|
|
|
{
|
|
|
|
ErrorCode = SDLErr_OpenAudio;
|
|
|
|
err = 1;
|
|
|
|
}
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
if (err)
|
|
|
|
{
|
|
|
|
SDL_CloseAudio();
|
|
|
|
return SDLErr_Error;
|
|
|
|
}
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
Initialised = 1;
|
|
|
|
return SDLErr_Ok;
|
|
|
|
}
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
void SDLDrv_PCM_Shutdown(void)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2009-08-06 10:12:13 +00:00
|
|
|
if (!Initialised)
|
2009-07-27 05:47:50 +00:00
|
|
|
return;
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
if (StartedSDL)
|
|
|
|
SDL_QuitSubSystem(StartedSDL);
|
2010-01-11 17:25:47 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
StartedSDL = 0;
|
2009-08-06 10:12:13 +00:00
|
|
|
Initialised = 0;
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
2019-10-19 23:47:54 +00:00
|
|
|
int SDLDrv_PCM_BeginPlayback(char *BufferStart, int BufferSize,
|
|
|
|
int NumDivisions, void ( *CallBackFunc )( void ) )
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2018-10-25 23:33:14 +00:00
|
|
|
if (!Initialised) {
|
|
|
|
ErrorCode = SDLErr_Uninitialised;
|
|
|
|
return SDLErr_Error;
|
|
|
|
}
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
if (Playing) {
|
|
|
|
SDLDrv_PCM_StopPlayback();
|
|
|
|
}
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
MixBuffer = BufferStart;
|
|
|
|
MixBufferSize = BufferSize;
|
|
|
|
MixBufferCount = NumDivisions;
|
|
|
|
MixBufferCurrent = 0;
|
|
|
|
MixBufferUsed = 0;
|
|
|
|
MixCallBack = CallBackFunc;
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
// prime the buffer
|
|
|
|
MixCallBack();
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
#if (SDL_MAJOR_VERSION == 2)
|
|
|
|
SDL_PauseAudioDevice(audio_dev, 0);
|
|
|
|
#else
|
|
|
|
SDL_PauseAudio(0);
|
|
|
|
#endif
|
2009-07-27 05:47:50 +00:00
|
|
|
Playing = 1;
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
return SDLErr_Ok;
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
void SDLDrv_PCM_StopPlayback(void)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2018-10-25 23:33:14 +00:00
|
|
|
if (!Initialised || !Playing) {
|
|
|
|
return;
|
|
|
|
}
|
2009-07-27 05:47:50 +00:00
|
|
|
|
2019-10-19 23:47:29 +00:00
|
|
|
#if (SDL_MAJOR_VERSION == 2)
|
|
|
|
SDL_PauseAudioDevice(audio_dev, 1);
|
|
|
|
#else
|
|
|
|
SDL_PauseAudio(1);
|
|
|
|
#endif
|
2016-06-05 04:46:28 +00:00
|
|
|
|
2018-10-25 23:33:14 +00:00
|
|
|
Playing = 0;
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
void SDLDrv_PCM_Lock(void)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2019-10-19 23:47:29 +00:00
|
|
|
#if (SDL_MAJOR_VERSION == 2)
|
|
|
|
SDL_LockAudioDevice(audio_dev);
|
|
|
|
#else
|
|
|
|
SDL_LockAudio();
|
|
|
|
#endif
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
void SDLDrv_PCM_Unlock(void)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2019-10-19 23:47:29 +00:00
|
|
|
#if (SDL_MAJOR_VERSION == 2)
|
|
|
|
SDL_UnlockAudioDevice(audio_dev);
|
|
|
|
#else
|
|
|
|
SDL_UnlockAudio();
|
|
|
|
#endif
|
2015-07-08 03:33:56 +00:00
|
|
|
}
|