2010-09-25 17:46:12 +02:00
|
|
|
#include "gtk_s9x.h"
|
|
|
|
#include "gtk_sound_driver_alsa.h"
|
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
static void
|
|
|
|
alsa_samples_available (void *data)
|
|
|
|
{
|
|
|
|
((S9xAlsaSoundDriver *) data)->samples_available ();
|
|
|
|
}
|
|
|
|
|
|
|
|
S9xAlsaSoundDriver::S9xAlsaSoundDriver (void)
|
|
|
|
{
|
|
|
|
pcm = NULL;
|
|
|
|
sound_buffer = NULL;
|
|
|
|
sound_buffer_size = 0;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
S9xAlsaSoundDriver::init (void)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
S9xAlsaSoundDriver::terminate (void)
|
|
|
|
{
|
|
|
|
stop ();
|
|
|
|
|
|
|
|
S9xSetSamplesAvailableCallback (NULL, NULL);
|
|
|
|
|
|
|
|
if (pcm)
|
|
|
|
{
|
|
|
|
snd_pcm_drain (pcm);
|
|
|
|
snd_pcm_close (pcm);
|
|
|
|
pcm = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound_buffer)
|
|
|
|
{
|
|
|
|
free (sound_buffer);
|
|
|
|
sound_buffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
S9xAlsaSoundDriver::start (void)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
S9xAlsaSoundDriver::stop (void)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool8
|
|
|
|
S9xAlsaSoundDriver::open_device (void)
|
|
|
|
{
|
|
|
|
int err;
|
2017-11-20 19:07:54 +01:00
|
|
|
unsigned int periods = 8;
|
|
|
|
unsigned int buffer_size = gui_config->sound_buffer_size * 1000;
|
2010-09-25 17:46:12 +02:00
|
|
|
snd_pcm_sw_params_t *sw_params;
|
2017-11-20 19:07:54 +01:00
|
|
|
snd_pcm_hw_params_t *hw_params;
|
2010-09-25 17:46:12 +02:00
|
|
|
snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
|
|
|
|
|
|
|
|
printf ("ALSA sound driver initializing...\n");
|
|
|
|
printf (" --> (Device: default)...");
|
|
|
|
|
2016-09-27 22:25:37 +02:00
|
|
|
err = snd_pcm_open (&pcm,
|
|
|
|
"default",
|
|
|
|
SND_PCM_STREAM_PLAYBACK,
|
|
|
|
SND_PCM_NONBLOCK);
|
|
|
|
|
|
|
|
if (err < 0)
|
2010-09-25 17:46:12 +02:00
|
|
|
{
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf ("OK\n");
|
|
|
|
|
|
|
|
printf (" --> (%s, %s, %dhz, %d ms)...",
|
|
|
|
Settings.SixteenBitSound ? "16-bit" : "8-bit",
|
|
|
|
Settings.Stereo ? "Stereo" : "Mono",
|
|
|
|
Settings.SoundPlaybackRate,
|
|
|
|
gui_config->sound_buffer_size);
|
|
|
|
|
2017-11-20 19:07:54 +01:00
|
|
|
snd_pcm_hw_params_alloca (&hw_params);
|
|
|
|
snd_pcm_hw_params_any (pcm, hw_params);
|
|
|
|
snd_pcm_hw_params_set_format (pcm, hw_params, Settings.SixteenBitSound ? SND_PCM_FORMAT_S16 : SND_PCM_FORMAT_U8);
|
|
|
|
snd_pcm_hw_params_set_access (pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
|
|
snd_pcm_hw_params_set_channels (pcm, hw_params, Settings.Stereo ? 2 : 1);
|
|
|
|
snd_pcm_hw_params_set_rate_near (pcm, hw_params, &Settings.SoundPlaybackRate, NULL);
|
|
|
|
snd_pcm_hw_params_set_rate_resample (pcm, hw_params, 0);
|
|
|
|
snd_pcm_hw_params_set_buffer_time_near (pcm, hw_params, &buffer_size, NULL);
|
|
|
|
snd_pcm_hw_params_set_periods_near (pcm, hw_params, &periods, NULL);
|
|
|
|
|
|
|
|
if ((err = snd_pcm_hw_params (pcm, hw_params)) < 0)
|
2010-09-25 17:46:12 +02:00
|
|
|
goto close_fail;
|
|
|
|
|
|
|
|
snd_pcm_sw_params_alloca (&sw_params);
|
|
|
|
snd_pcm_sw_params_current (pcm, sw_params);
|
|
|
|
snd_pcm_get_params (pcm, &alsa_buffer_size, &alsa_period_size);
|
|
|
|
/* Don't start until we're [nearly] full */
|
2017-11-20 19:07:54 +01:00
|
|
|
snd_pcm_sw_params_set_start_threshold (pcm, sw_params, (alsa_buffer_size / 2));
|
2010-09-25 17:46:12 +02:00
|
|
|
err = snd_pcm_sw_params (pcm, sw_params);
|
2017-11-20 19:07:54 +01:00
|
|
|
|
|
|
|
output_buffer_size = snd_pcm_frames_to_bytes (pcm, alsa_buffer_size);
|
|
|
|
|
2010-09-25 17:46:12 +02:00
|
|
|
if (err < 0)
|
|
|
|
goto close_fail;
|
|
|
|
|
|
|
|
printf ("OK\n");
|
|
|
|
|
|
|
|
S9xSetSamplesAvailableCallback (alsa_samples_available, this);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
close_fail:
|
|
|
|
snd_pcm_drain (pcm);
|
|
|
|
snd_pcm_close (pcm);
|
|
|
|
pcm = NULL;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
printf ("Failed: %s\n", snd_strerror (err));
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
S9xAlsaSoundDriver::samples_available (void)
|
|
|
|
{
|
|
|
|
snd_pcm_sframes_t frames_written, frames;
|
|
|
|
int bytes;
|
|
|
|
|
2017-11-20 19:07:54 +01:00
|
|
|
frames = snd_pcm_avail (pcm);
|
|
|
|
|
|
|
|
if (Settings.DynamicRateControl)
|
|
|
|
{
|
|
|
|
S9xUpdateDynamicRate (snd_pcm_frames_to_bytes (pcm, frames),
|
|
|
|
output_buffer_size);
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
|
|
|
|
if (frames < 0)
|
|
|
|
{
|
|
|
|
frames = snd_pcm_recover (pcm, frames, 1);
|
2017-11-20 19:07:54 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
S9xFinalizeSamples ();
|
|
|
|
|
|
|
|
if (Settings.DynamicRateControl)
|
|
|
|
{
|
|
|
|
// Using rate control, we should always keep the emulator's sound buffers empty to
|
|
|
|
// maintain an accurate measurement.
|
|
|
|
if (frames < (S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0)))
|
|
|
|
{
|
|
|
|
S9xClearSamples ();
|
|
|
|
return;
|
|
|
|
}
|
2010-09-25 17:46:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
frames = MIN (frames, S9xGetSampleCount () >> (Settings.Stereo ? 1 : 0));
|
|
|
|
|
|
|
|
bytes = snd_pcm_frames_to_bytes (pcm, frames);
|
2017-11-20 19:07:54 +01:00
|
|
|
|
2010-09-25 17:46:12 +02:00
|
|
|
if (bytes <= 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound_buffer_size < bytes || sound_buffer == NULL)
|
|
|
|
{
|
|
|
|
sound_buffer = (uint8 *) realloc (sound_buffer, bytes);
|
|
|
|
sound_buffer_size = bytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
S9xMixSamples (sound_buffer, frames << (Settings.Stereo ? 1 : 0));
|
|
|
|
|
|
|
|
frames_written = 0;
|
|
|
|
|
|
|
|
while (frames_written < frames)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
result = snd_pcm_writei (pcm,
|
|
|
|
sound_buffer +
|
|
|
|
snd_pcm_frames_to_bytes (pcm, frames_written),
|
|
|
|
frames - frames_written);
|
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
{
|
|
|
|
result = snd_pcm_recover (pcm, result, 1);
|
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
frames_written += result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|