238 lines
5.0 KiB
C
238 lines
5.0 KiB
C
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* Support for SDL sound
|
|
*
|
|
* Copyright 1997 Bernd Schmidt
|
|
* Copyright 2003-2006 Richard Drummond
|
|
*/
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
|
|
#include "options.h"
|
|
#include "gensound.h"
|
|
#include "sounddep/sound.h"
|
|
#include "threaddep/thread.h"
|
|
#include <SDL_audio.h>
|
|
#include <SDL.h>
|
|
|
|
static int have_sound = 0;
|
|
|
|
uae_u16 sndbuffer[44100];
|
|
uae_u16 *sndbufpt;
|
|
int sndbufsize;
|
|
static SDL_AudioSpec spec;
|
|
|
|
static smp_comm_pipe to_sound_pipe;
|
|
static uae_sem_t data_available_sem, callback_done_sem, sound_init_sem;
|
|
|
|
static int in_callback, closing_sound;
|
|
|
|
static void clearbuffer (void)
|
|
{
|
|
memset (sndbuffer, 0, sizeof (sndbuffer));
|
|
}
|
|
|
|
/* This shouldn't be necessary . . . */
|
|
static void dummy_callback (void *userdata, Uint8 *stream, int len)
|
|
{
|
|
return;
|
|
}
|
|
|
|
static void sound_callback (void *userdata, Uint8 *stream, int len)
|
|
{
|
|
if (closing_sound)
|
|
return;
|
|
in_callback = 1;
|
|
/* Wait for data to finish. */
|
|
uae_sem_wait (&data_available_sem);
|
|
if (! closing_sound) {
|
|
memcpy (stream, sndbuffer, sndbufsize);
|
|
/* Notify writer that we're done. */
|
|
uae_sem_post (&callback_done_sem);
|
|
}
|
|
in_callback = 0;
|
|
}
|
|
|
|
void finish_sound_buffer (void)
|
|
{
|
|
uae_sem_post (&data_available_sem);
|
|
uae_sem_wait (&callback_done_sem);
|
|
}
|
|
|
|
/* Try to determine whether sound is available. */
|
|
int setup_sound (void)
|
|
{
|
|
int success = 0;
|
|
|
|
if (SDL_InitSubSystem (SDL_INIT_AUDIO) == 0) {
|
|
spec.freq = currprefs.sound_freq;
|
|
spec.format = AUDIO_S16SYS;
|
|
spec.channels = currprefs.sound_stereo ? 2 : 1;
|
|
spec.callback = dummy_callback;
|
|
spec.samples = spec.freq * currprefs.sound_latency / 1000;
|
|
spec.callback = sound_callback;
|
|
spec.userdata = 0;
|
|
|
|
if (SDL_OpenAudio (&spec, 0) < 0) {
|
|
write_log ("Couldn't open audio: %s\n", SDL_GetError());
|
|
SDL_QuitSubSystem (SDL_INIT_AUDIO);
|
|
} else {
|
|
success = 1;
|
|
SDL_CloseAudio ();
|
|
}
|
|
}
|
|
|
|
sound_available = success;
|
|
|
|
return sound_available;
|
|
}
|
|
|
|
static int open_sound (void)
|
|
{
|
|
spec.freq = currprefs.sound_freq;
|
|
spec.format = AUDIO_S16SYS;
|
|
spec.channels = currprefs.sound_stereo ? 2 : 1;
|
|
spec.samples = spec.freq * currprefs.sound_latency / 1000;
|
|
spec.callback = sound_callback;
|
|
spec.userdata = 0;
|
|
|
|
clearbuffer();
|
|
if (SDL_OpenAudio (&spec, NULL) < 0) {
|
|
write_log ("Couldn't open audio: %s\n", SDL_GetError());
|
|
return 0;
|
|
}
|
|
|
|
init_sound_table16 ();
|
|
sample_handler = currprefs.sound_stereo ? sample16s_handler : sample16_handler;
|
|
|
|
have_sound = 1;
|
|
sound_available = 1;
|
|
obtainedfreq = currprefs.sound_freq;
|
|
sndbufsize = spec.samples * 2 * spec.channels;
|
|
write_log ("SDL sound driver found and configured at %d Hz, buffer is %d ms (%d bytes).\n",
|
|
spec.freq, spec.samples * 1000 / spec.freq, sndbufsize);
|
|
sndbufpt = sndbuffer;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void *sound_thread (void *dummy)
|
|
{
|
|
for (;;) {
|
|
int cmd = read_comm_pipe_int_blocking (&to_sound_pipe);
|
|
int n;
|
|
|
|
switch (cmd) {
|
|
case 0:
|
|
open_sound ();
|
|
uae_sem_post (&sound_init_sem);
|
|
break;
|
|
case 1:
|
|
uae_sem_post (&sound_init_sem);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We need a thread for this, since communication between finish_sound_buffer
|
|
* and the callback works through semaphores. In theory, this is unnecessary,
|
|
* since SDL uses a sound thread internally, and the callback runs in its
|
|
* context. But we don't want to depend on SDL's internals too much. */
|
|
static void init_sound_thread (void)
|
|
{
|
|
uae_thread_id tid;
|
|
|
|
init_comm_pipe (&to_sound_pipe, 20, 1);
|
|
uae_sem_init (&data_available_sem, 0, 0);
|
|
uae_sem_init (&callback_done_sem, 0, 0);
|
|
uae_sem_init (&sound_init_sem, 0, 0);
|
|
uae_start_thread (sound_thread, NULL, &tid);
|
|
}
|
|
|
|
void close_sound (void)
|
|
{
|
|
if (! have_sound)
|
|
return;
|
|
|
|
SDL_PauseAudio (1);
|
|
clearbuffer();
|
|
if (in_callback) {
|
|
closing_sound = 1;
|
|
uae_sem_post (&data_available_sem);
|
|
}
|
|
write_comm_pipe_int (&to_sound_pipe, 1, 1);
|
|
uae_sem_wait (&sound_init_sem);
|
|
SDL_CloseAudio ();
|
|
uae_sem_destroy (&data_available_sem);
|
|
uae_sem_destroy (&sound_init_sem);
|
|
uae_sem_destroy (&callback_done_sem);
|
|
have_sound = 0;
|
|
|
|
#ifdef DRIVESOUND
|
|
driveclick_free();
|
|
#endif
|
|
}
|
|
|
|
int init_sound (void)
|
|
{
|
|
in_callback = 0;
|
|
closing_sound = 0;
|
|
|
|
init_sound_thread ();
|
|
write_comm_pipe_int (&to_sound_pipe, 0, 1);
|
|
uae_sem_wait (&sound_init_sem);
|
|
SDL_PauseAudio (0);
|
|
|
|
#ifdef DRIVESOUND
|
|
driveclick_init();
|
|
#endif
|
|
|
|
return have_sound;
|
|
}
|
|
|
|
void pause_sound (void)
|
|
{
|
|
|
|
SDL_PauseAudio (1);
|
|
clearbuffer();
|
|
if (in_callback) {
|
|
closing_sound = 1;
|
|
uae_sem_post (&data_available_sem);
|
|
}
|
|
}
|
|
|
|
void resume_sound (void)
|
|
{
|
|
clearbuffer();
|
|
SDL_PauseAudio (0);
|
|
closing_sound = 0;
|
|
}
|
|
|
|
void reset_sound (void)
|
|
{
|
|
clearbuffer();
|
|
return;
|
|
}
|
|
|
|
void sound_volume (int dir)
|
|
{
|
|
}
|
|
|
|
/*
|
|
* Handle audio specific cfgfile options
|
|
*/
|
|
void audio_default_options (struct uae_prefs *p)
|
|
{
|
|
}
|
|
|
|
void audio_save_options (FILE *f, const struct uae_prefs *p)
|
|
{
|
|
}
|
|
|
|
int audio_parse_option (struct uae_prefs *p, const char *option, const char *value)
|
|
{
|
|
return 0;
|
|
}
|