2009-07-27 05:47:50 +00:00
|
|
|
/*
|
|
|
|
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* libSDL output driver for MultiVoc
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <SDL/SDL.h>
|
2009-07-28 06:32:58 +00:00
|
|
|
#include <SDL/SDL_mixer.h>
|
2010-01-11 17:25:47 +00:00
|
|
|
#include <SDL/SDL_thread.h>
|
2009-07-27 05:47:50 +00:00
|
|
|
#include "driver_sdl.h"
|
|
|
|
|
2009-07-28 06:32:58 +00:00
|
|
|
#ifndef UNREFERENCED_PARAMETER
|
|
|
|
#define UNREFERENCED_PARAMETER(P) (P)=(P)
|
|
|
|
#endif
|
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
enum {
|
|
|
|
SDLErr_Warning = -2,
|
|
|
|
SDLErr_Error = -1,
|
|
|
|
SDLErr_Ok = 0,
|
|
|
|
SDLErr_Uninitialised,
|
|
|
|
SDLErr_InitSubSystem,
|
|
|
|
SDLErr_OpenAudio
|
|
|
|
};
|
|
|
|
|
|
|
|
static int32_t ErrorCode = SDLErr_Ok;
|
|
|
|
static int32_t Initialised = 0;
|
|
|
|
static int32_t Playing = 0;
|
2009-07-31 11:45:29 +00:00
|
|
|
// static int32_t StartedSDL = -1;
|
2009-07-27 05:47:50 +00:00
|
|
|
|
|
|
|
static char *MixBuffer = 0;
|
|
|
|
static int32_t MixBufferSize = 0;
|
|
|
|
static int32_t MixBufferCount = 0;
|
|
|
|
static int32_t MixBufferCurrent = 0;
|
|
|
|
static int32_t MixBufferUsed = 0;
|
|
|
|
static void ( *MixCallBack )( void ) = 0;
|
|
|
|
|
2009-07-28 06:32:58 +00:00
|
|
|
static Mix_Chunk *DummyChunk = NULL;
|
|
|
|
static uint8_t *DummyBuffer = NULL;
|
2010-01-11 17:25:47 +00:00
|
|
|
static int32_t InterruptsDisabled = 0;
|
|
|
|
static SDL_mutex *EffectFence;
|
2009-07-28 06:32:58 +00:00
|
|
|
|
|
|
|
static void fillData(int32_t chan, void *ptr, int32_t remaining, void *udata)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
|
|
|
int32_t len;
|
|
|
|
char *sptr;
|
|
|
|
|
2009-07-28 06:32:58 +00:00
|
|
|
UNREFERENCED_PARAMETER(chan);
|
|
|
|
UNREFERENCED_PARAMETER(udata);
|
|
|
|
|
2010-01-11 17:25:47 +00:00
|
|
|
SDL_LockMutex(EffectFence);
|
|
|
|
|
2009-07-27 05:47:50 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2010-01-11 17:25:47 +00:00
|
|
|
|
|
|
|
SDL_UnlockMutex(EffectFence);
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32_t SDLDrv_GetError(void)
|
|
|
|
{
|
|
|
|
return ErrorCode;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *SDLDrv_ErrorString( int32_t 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;
|
|
|
|
}
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
int32_t SDLDrv_PCM_Init(int32_t *mixrate, int32_t *numchannels, int32_t *samplebits, void * initdata)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2009-07-28 06:32:58 +00:00
|
|
|
int32_t err = 0;
|
2009-07-28 08:10:26 +00:00
|
|
|
int32_t chunksize;
|
2009-07-31 11:45:29 +00:00
|
|
|
uint16_t fmt;
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2009-07-28 08:10:26 +00:00
|
|
|
UNREFERENCED_PARAMETER(numchannels);
|
2009-07-28 06:32:58 +00:00
|
|
|
UNREFERENCED_PARAMETER(initdata);
|
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
|
|
|
}
|
|
|
|
|
2009-07-28 08:10:26 +00:00
|
|
|
chunksize = 512;
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
if (*mixrate >= 16000) chunksize *= 2;
|
|
|
|
if (*mixrate >= 32000) chunksize *= 2;
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2009-08-06 10:12:13 +00:00
|
|
|
err = Mix_OpenAudio(*mixrate, (*samplebits == 8) ? AUDIO_U8 : AUDIO_S16SYS, *numchannels, chunksize);
|
2009-07-27 05:47:50 +00:00
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
ErrorCode = SDLErr_OpenAudio;
|
|
|
|
return SDLErr_Error;
|
|
|
|
}
|
|
|
|
|
2009-08-06 10:12:13 +00:00
|
|
|
if (Mix_QuerySpec(mixrate, &fmt, numchannels))
|
|
|
|
{
|
|
|
|
if (fmt == AUDIO_U8 || fmt == AUDIO_S8) *samplebits = 8;
|
|
|
|
else *samplebits = 16;
|
|
|
|
}
|
2009-07-31 11:45:29 +00:00
|
|
|
|
2009-07-28 06:32:58 +00:00
|
|
|
//Mix_SetPostMix(fillData, NULL);
|
|
|
|
|
2010-01-11 17:25:47 +00:00
|
|
|
EffectFence = SDL_CreateMutex();
|
|
|
|
|
2009-07-28 06:32:58 +00:00
|
|
|
// channel 0 and 1 are actual sounds
|
|
|
|
// dummy channel 2 runs our fillData() callback as an effect
|
|
|
|
Mix_RegisterEffect(2, fillData, NULL, NULL);
|
|
|
|
|
2009-07-28 08:10:26 +00:00
|
|
|
DummyBuffer = (uint8_t *) malloc(chunksize);
|
|
|
|
memset(DummyBuffer, 0, chunksize);
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2009-07-28 08:10:26 +00:00
|
|
|
DummyChunk = Mix_QuickLoad_RAW(DummyBuffer, chunksize);
|
2009-07-28 06:32:58 +00:00
|
|
|
|
|
|
|
Mix_PlayChannel(2, DummyChunk, -1);
|
|
|
|
|
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-08-06 10:12:13 +00:00
|
|
|
else Mix_HaltChannel(-1);
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2009-08-06 10:12:13 +00:00
|
|
|
if (DummyChunk != NULL)
|
|
|
|
{
|
|
|
|
Mix_FreeChunk(DummyChunk);
|
|
|
|
DummyChunk = NULL;
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
2009-08-06 10:12:13 +00:00
|
|
|
if (DummyBuffer != NULL)
|
|
|
|
{
|
|
|
|
free(DummyBuffer);
|
|
|
|
DummyBuffer = NULL;
|
|
|
|
}
|
2009-07-28 06:32:58 +00:00
|
|
|
|
2009-08-06 10:12:13 +00:00
|
|
|
Mix_CloseAudio();
|
2010-01-11 17:25:47 +00:00
|
|
|
|
|
|
|
SDL_DestroyMutex(EffectFence);
|
|
|
|
|
2009-08-06 10:12:13 +00:00
|
|
|
Initialised = 0;
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
int32_t SDLDrv_PCM_BeginPlayback(char *BufferStart, int32_t BufferSize,
|
2009-07-27 05:47:50 +00:00
|
|
|
int32_t NumDivisions, void ( *CallBackFunc )( void ) )
|
|
|
|
{
|
|
|
|
if (!Initialised) {
|
|
|
|
ErrorCode = SDLErr_Uninitialised;
|
|
|
|
return SDLErr_Error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Playing) {
|
2009-07-31 11:45:29 +00:00
|
|
|
SDLDrv_PCM_StopPlayback();
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
MixBuffer = BufferStart;
|
|
|
|
MixBufferSize = BufferSize;
|
|
|
|
MixBufferCount = NumDivisions;
|
|
|
|
MixBufferCurrent = 0;
|
|
|
|
MixBufferUsed = 0;
|
|
|
|
MixCallBack = CallBackFunc;
|
|
|
|
|
|
|
|
// prime the buffer
|
|
|
|
MixCallBack();
|
|
|
|
|
2009-07-28 06:32:58 +00:00
|
|
|
Mix_Resume(-1);
|
2009-07-27 05:47:50 +00:00
|
|
|
|
|
|
|
Playing = 1;
|
|
|
|
|
|
|
|
return SDLErr_Ok;
|
|
|
|
}
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
void SDLDrv_PCM_StopPlayback(void)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
|
|
|
if (!Initialised || !Playing) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-07-28 06:32:58 +00:00
|
|
|
Mix_Pause(-1);
|
2009-07-27 05:47:50 +00:00
|
|
|
|
|
|
|
Playing = 0;
|
|
|
|
}
|
|
|
|
|
2009-07-31 11:45:29 +00:00
|
|
|
void SDLDrv_PCM_Lock(void)
|
2009-07-27 05:47:50 +00:00
|
|
|
{
|
2009-07-28 06:32:58 +00:00
|
|
|
if (InterruptsDisabled++)
|
|
|
|
return;
|
|
|
|
|
2010-01-11 17:25:47 +00:00
|
|
|
SDL_LockMutex(EffectFence);
|
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
|
|
|
{
|
2009-07-28 06:32:58 +00:00
|
|
|
if (--InterruptsDisabled)
|
|
|
|
return;
|
|
|
|
|
2010-01-11 17:25:47 +00:00
|
|
|
SDL_UnlockMutex(EffectFence);
|
2009-07-27 05:47:50 +00:00
|
|
|
}
|
|
|
|
|