gsplus/src/sdl2snd_driver.c

216 lines
5.7 KiB
C

/*
GSPLUS - Advanced Apple IIGS Emulator Environment
Based on the KEGS emulator written by Kent Dickey
See COPYRIGHT.txt for Copyright information
See LICENSE.txt for license (GPL v2)
*/
#include "SDL.h"
#include "defc.h"
#include "glog.h"
#include "sound.h"
#include <assert.h>
extern word32 *g_sound_shm_addr;
extern int g_sound_shm_pos;
extern int g_audio_enable;
extern int g_preferred_rate;
static byte *playbuf = 0;
static int g_playbuf_buffered = 0;
static SDL_AudioSpec spec;
static int snd_buf;
static int snd_write; /* write position into playbuf */
static /* volatile */ int snd_read = 0;
static int g_sound_paused;
static int g_zeroes_buffered;
static int g_zeroes_seen;
// newer SDL allows you to specify devices. for now, we use what it gives us,
// but this can be made configurable in the future
SDL_AudioDeviceID dev = 0;
void sdlsnd_init(word32 *shmaddr)
{
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
glog("Could not initialize SDL2 audio");
g_audio_enable = 0;
} else {
glog("SDL2 audio initialized");
}
child_sound_loop(-1, -1, shmaddr);
return;
}
void
sound_write_sdl(int real_samps, int size)
{
#ifdef HAVE_SDL
int shm_read;
if (real_samps) {
shm_read = (g_sound_shm_pos - size + SOUND_SHM_SAMP_SIZE)%SOUND_SHM_SAMP_SIZE;
SDL_LockAudioDevice(dev);
while(size > 0) {
if(g_playbuf_buffered >= snd_buf) {
printf("sound_write_sdl failed @%d, %d buffered, %d samples skipped\n",snd_write,g_playbuf_buffered, size);
shm_read += size;
shm_read %= SOUND_SHM_SAMP_SIZE;
size = 0;
} else {
((word32*)playbuf)[snd_write/SAMPLE_CHAN_SIZE] = g_sound_shm_addr[shm_read];
shm_read++;
if (shm_read >= SOUND_SHM_SAMP_SIZE)
shm_read = 0;
snd_write += SAMPLE_CHAN_SIZE;
if (snd_write >= snd_buf)
snd_write = 0;
size--;
g_playbuf_buffered += SAMPLE_CHAN_SIZE;
}
}
assert((snd_buf+snd_write - snd_read)%snd_buf == g_playbuf_buffered%snd_buf);
assert(g_sound_shm_pos == shm_read);
SDL_UnlockAudioDevice(dev);
}
if(g_sound_paused && (g_playbuf_buffered > 0)) {
glogf("Unpausing sound, %d buffered",g_playbuf_buffered);
g_sound_paused = 0;
SDL_PauseAudioDevice(dev, 0);
}
if(!g_sound_paused && (g_playbuf_buffered <= 0)) {
glog("Pausing sound");
g_sound_paused = 1;
SDL_PauseAudioDevice(dev, 1);
}
#endif
}
#ifdef HAVE_SDL
/* Callback for sound */
static void _snd_callback(void* userdata, Uint8 *stream, int len)
{
int i;
/* Slurp off the play buffer */
assert((snd_buf+snd_write - snd_read)%snd_buf == g_playbuf_buffered%snd_buf);
/*printf("slurp %d, %d buffered\n",len, g_playbuf_buffered);*/
for(i = 0; i < len; ++i) {
if(g_playbuf_buffered <= 0) {
stream[i] = 0;
} else {
stream[i] = playbuf[snd_read++];
if(snd_read == snd_buf)
snd_read = 0;
g_playbuf_buffered--;
}
}
#if 0
if (g_playbuf_buffered <= 0) {
printf("snd_callback: buffer empty, Pausing sound\n");
g_sound_paused = 1;
SDL_PauseAudio(1);
}
#endif
//printf("end slurp %d, %d buffered\n",len, g_playbuf_buffered);
}
#endif
long
sound_init_device_sdl()
{
#ifdef HAVE_SDL
long rate;
SDL_AudioSpec wanted;
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
glogf("SDL2 Couldn't init SDL_INIT_AUDIO: %s!", SDL_GetError());
return 0;
}
/* Set the desired format */
wanted.freq = g_preferred_rate;
wanted.format = AUDIO_S16SYS;
wanted.channels = NUM_CHANNELS;
wanted.samples = 512;
wanted.callback = _snd_callback;
wanted.userdata = NULL;
/* Open audio, and get the real spec */
dev = SDL_OpenAudioDevice(NULL, 0, &wanted, &spec, 0);
if (dev == 0) {
glogf("SDL2 Couldn't open audio: %s!", SDL_GetError());
SDL_QuitSubSystem(SDL_INIT_AUDIO);
return 0;
} else {
glogf("SDL2 opened audio device: %d", dev);
}
/* Check everything */
if(spec.channels != wanted.channels) {
glogf("SDL2 Warning, couldn't get stereo audio format!");
//goto snd_error;
}
if(spec.format != wanted.format) {
glog("SDL2 Warning, couldn't get a supported audio format!");
glogf("SDL2 wanted %X, got %X",wanted.format,spec.format);
//goto snd_error;
}
if(spec.freq != wanted.freq) {
glogf("SDL2 wanted rate = %d, got rate = %d", wanted.freq, spec.freq);
}
/* Set things as they really are */
rate = spec.freq;
snd_buf = SOUND_SHM_SAMP_SIZE*SAMPLE_CHAN_SIZE;
playbuf = (byte*) malloc(snd_buf);
if (!playbuf)
goto snd_error;
g_playbuf_buffered = 0;
glogf("SDL2 sound shared memory size=%d", SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);
g_sound_shm_addr = malloc(SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);
memset(g_sound_shm_addr,0,SOUND_SHM_SAMP_SIZE * SAMPLE_CHAN_SIZE);
/* It's all good! */
g_zeroes_buffered = 0;
g_zeroes_seen = 0;
/* Let's start playing sound */
g_sound_paused = 0;
SDL_PauseAudioDevice(dev, 0);
set_audio_rate(rate);
return rate;
snd_error:
/* Oops! Something bad happened, cleanup. */
SDL_CloseAudioDevice(dev);
SDL_QuitSubSystem(SDL_INIT_AUDIO);
if(playbuf)
free((void*)playbuf);
playbuf = 0;
snd_buf = 0;
return 0;
#else
return 0;
#endif
}
void
sound_shutdown_sdl()
{
#ifdef HAVE_SDL
SDL_CloseAudioDevice(dev);
if(playbuf)
free((void*)playbuf);
playbuf = 0;
#endif
}