2018-11-15 23:42:29 +00:00
|
|
|
/*****************************************************************************\
|
|
|
|
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
|
|
|
|
This file is licensed under the Snes9x License.
|
|
|
|
For further information, consult the LICENSE file in the root directory.
|
|
|
|
\*****************************************************************************/
|
|
|
|
|
2010-09-25 15:46:12 +00:00
|
|
|
#include "gtk_sound_driver_alsa.h"
|
2019-02-07 01:41:33 +00:00
|
|
|
#include "gtk_s9x.h"
|
2020-07-04 22:53:38 +00:00
|
|
|
#include "snes9x.h"
|
|
|
|
#include "apu/apu.h"
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
#include <fcntl.h>
|
2010-09-25 15:46:12 +00:00
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
static void alsa_samples_available(void *data)
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
((S9xAlsaSoundDriver *)data)->samples_available();
|
2010-09-25 15:46:12 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
S9xAlsaSoundDriver::S9xAlsaSoundDriver()
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
|
|
|
pcm = NULL;
|
|
|
|
sound_buffer = NULL;
|
|
|
|
sound_buffer_size = 0;
|
|
|
|
}
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
void S9xAlsaSoundDriver::init()
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
void S9xAlsaSoundDriver::terminate()
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
stop();
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
S9xSetSamplesAvailableCallback(NULL, NULL);
|
2010-09-25 15:46:12 +00:00
|
|
|
|
|
|
|
if (pcm)
|
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
snd_pcm_drain(pcm);
|
|
|
|
snd_pcm_close(pcm);
|
2010-09-25 15:46:12 +00:00
|
|
|
pcm = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sound_buffer)
|
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
free(sound_buffer);
|
2010-09-25 15:46:12 +00:00
|
|
|
sound_buffer = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
void S9xAlsaSoundDriver::start()
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
void S9xAlsaSoundDriver::stop()
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-12-28 22:32:32 +00:00
|
|
|
bool S9xAlsaSoundDriver::open_device()
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
|
|
|
int err;
|
2017-11-20 18:07:54 +00:00
|
|
|
unsigned int periods = 8;
|
|
|
|
unsigned int buffer_size = gui_config->sound_buffer_size * 1000;
|
2010-09-25 15:46:12 +00:00
|
|
|
snd_pcm_sw_params_t *sw_params;
|
2017-11-20 18:07:54 +00:00
|
|
|
snd_pcm_hw_params_t *hw_params;
|
2010-09-25 15:46:12 +00:00
|
|
|
snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
|
2018-06-16 23:13:21 +00:00
|
|
|
unsigned int min = 0;
|
|
|
|
unsigned int max = 0;
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
printf("ALSA sound driver initializing...\n");
|
|
|
|
printf(" --> (Device: default)...\n");
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
err = snd_pcm_open(&pcm,
|
|
|
|
"default",
|
|
|
|
SND_PCM_STREAM_PLAYBACK,
|
|
|
|
SND_PCM_NONBLOCK);
|
2016-09-27 20:25:37 +00:00
|
|
|
|
|
|
|
if (err < 0)
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
printf(" --> (16-bit Stereo, %dhz, %d ms)...\n",
|
|
|
|
Settings.SoundPlaybackRate,
|
|
|
|
gui_config->sound_buffer_size);
|
|
|
|
|
|
|
|
snd_pcm_hw_params_alloca(&hw_params);
|
|
|
|
snd_pcm_hw_params_any(pcm, hw_params);
|
|
|
|
snd_pcm_hw_params_set_format(pcm, hw_params, SND_PCM_FORMAT_S16);
|
|
|
|
snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
|
|
snd_pcm_hw_params_set_rate_resample(pcm, hw_params, 0);
|
|
|
|
snd_pcm_hw_params_set_channels(pcm, hw_params, 2);
|
|
|
|
|
|
|
|
snd_pcm_hw_params_get_rate_min(hw_params, &min, NULL);
|
|
|
|
snd_pcm_hw_params_get_rate_max(hw_params, &max, NULL);
|
|
|
|
printf(" --> Available rates: %d to %d\n", min, max);
|
2018-06-16 23:13:21 +00:00
|
|
|
if (Settings.SoundPlaybackRate > max && Settings.SoundPlaybackRate < min)
|
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
printf(" Rate %d not available. Using %d instead.\n", Settings.SoundPlaybackRate, max);
|
2018-06-16 23:13:21 +00:00
|
|
|
Settings.SoundPlaybackRate = max;
|
|
|
|
}
|
2019-02-07 01:41:33 +00:00
|
|
|
snd_pcm_hw_params_set_rate_near(pcm, hw_params, &Settings.SoundPlaybackRate, NULL);
|
2018-06-16 23:13:21 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
snd_pcm_hw_params_get_buffer_time_min(hw_params, &min, NULL);
|
|
|
|
snd_pcm_hw_params_get_buffer_time_max(hw_params, &max, NULL);
|
|
|
|
printf(" --> Available buffer sizes: %dms to %dms\n", min / 1000, max / 1000);
|
2018-06-16 23:13:21 +00:00
|
|
|
if (buffer_size < min && buffer_size > max)
|
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
printf(" Buffer size %dms not available. Using %d instead.\n", buffer_size / 1000, (min + max) / 2000);
|
2018-06-16 23:13:21 +00:00
|
|
|
buffer_size = (min + max) / 2;
|
|
|
|
}
|
2019-02-07 01:41:33 +00:00
|
|
|
snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, &buffer_size, NULL);
|
2018-06-16 23:13:21 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
snd_pcm_hw_params_get_periods_min(hw_params, &min, NULL);
|
|
|
|
snd_pcm_hw_params_get_periods_max(hw_params, &max, NULL);
|
|
|
|
printf(" --> Period ranges: %d to %d blocks\n", min, max);
|
2018-06-16 23:13:21 +00:00
|
|
|
if (periods > max)
|
|
|
|
{
|
|
|
|
periods = max;
|
|
|
|
}
|
2019-02-07 01:41:33 +00:00
|
|
|
snd_pcm_hw_params_set_periods_near(pcm, hw_params, &periods, NULL);
|
2017-11-20 18:07:54 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
if ((err = snd_pcm_hw_params(pcm, hw_params)) < 0)
|
2018-06-16 23:13:21 +00:00
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
printf(" Hardware parameter set failed.\n");
|
2010-09-25 15:46:12 +00:00
|
|
|
goto close_fail;
|
2018-06-16 23:13:21 +00:00
|
|
|
}
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
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);
|
2010-09-25 15:46:12 +00:00
|
|
|
/* Don't start until we're [nearly] full */
|
2019-02-07 01:41:33 +00:00
|
|
|
snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (alsa_buffer_size / 2));
|
|
|
|
err = snd_pcm_sw_params(pcm, sw_params);
|
2017-11-20 18:07:54 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
output_buffer_size = snd_pcm_frames_to_bytes(pcm, alsa_buffer_size);
|
2017-11-20 18:07:54 +00:00
|
|
|
|
2010-09-25 15:46:12 +00:00
|
|
|
if (err < 0)
|
2018-06-16 23:13:21 +00:00
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
printf(" Software parameter set failed.\n");
|
2010-09-25 15:46:12 +00:00
|
|
|
goto close_fail;
|
2018-06-16 23:13:21 +00:00
|
|
|
}
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
printf("OK\n");
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
S9xSetSamplesAvailableCallback(alsa_samples_available, this);
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2018-12-28 22:32:32 +00:00
|
|
|
return true;
|
2010-09-25 15:46:12 +00:00
|
|
|
|
|
|
|
close_fail:
|
2019-02-07 01:41:33 +00:00
|
|
|
snd_pcm_drain(pcm);
|
|
|
|
snd_pcm_close(pcm);
|
2010-09-25 15:46:12 +00:00
|
|
|
pcm = NULL;
|
|
|
|
|
|
|
|
fail:
|
2019-02-07 01:41:33 +00:00
|
|
|
printf("Failed: %s\n", snd_strerror(err));
|
2010-09-25 15:46:12 +00:00
|
|
|
|
2018-12-28 22:32:32 +00:00
|
|
|
return false;
|
2010-09-25 15:46:12 +00:00
|
|
|
}
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
void S9xAlsaSoundDriver::samples_available()
|
2010-09-25 15:46:12 +00:00
|
|
|
{
|
|
|
|
snd_pcm_sframes_t frames_written, frames;
|
|
|
|
int bytes;
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
frames = snd_pcm_avail(pcm);
|
2017-11-20 18:07:54 +00:00
|
|
|
|
2010-09-25 15:46:12 +00:00
|
|
|
if (frames < 0)
|
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
frames = snd_pcm_recover(pcm, frames, 1);
|
2017-11-20 18:07:54 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-10 00:48:17 +00:00
|
|
|
if (Settings.DynamicRateControl)
|
|
|
|
{
|
|
|
|
S9xUpdateDynamicRate(snd_pcm_frames_to_bytes(pcm, frames),
|
|
|
|
output_buffer_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
int snes_frames_available = S9xGetSampleCount() >> 1;
|
2017-11-20 18:07:54 +00:00
|
|
|
|
2019-02-10 00:48:17 +00:00
|
|
|
if (Settings.DynamicRateControl && !Settings.SoundSync)
|
2017-11-20 18:07:54 +00:00
|
|
|
{
|
|
|
|
// Using rate control, we should always keep the emulator's sound buffers empty to
|
|
|
|
// maintain an accurate measurement.
|
2019-02-10 00:48:17 +00:00
|
|
|
if (frames < snes_frames_available)
|
2017-11-20 18:07:54 +00:00
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
S9xClearSamples();
|
2017-11-20 18:07:54 +00:00
|
|
|
return;
|
|
|
|
}
|
2010-09-25 15:46:12 +00:00
|
|
|
}
|
|
|
|
|
2019-02-10 00:48:17 +00:00
|
|
|
if (Settings.SoundSync && !Settings.TurboMode && !Settings.Mute)
|
|
|
|
{
|
2019-02-12 18:00:03 +00:00
|
|
|
snd_pcm_nonblock(pcm, 0);
|
|
|
|
frames = snes_frames_available;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
snd_pcm_nonblock(pcm, 1);
|
|
|
|
frames = MIN(frames, snes_frames_available);
|
2019-02-10 00:48:17 +00:00
|
|
|
}
|
2017-11-20 18:07:54 +00:00
|
|
|
|
2019-02-10 00:48:17 +00:00
|
|
|
bytes = snd_pcm_frames_to_bytes(pcm, frames);
|
2010-09-25 15:46:12 +00:00
|
|
|
if (bytes <= 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (sound_buffer_size < bytes || sound_buffer == NULL)
|
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
sound_buffer = (uint8 *)realloc(sound_buffer, bytes);
|
2010-09-25 15:46:12 +00:00
|
|
|
sound_buffer_size = bytes;
|
|
|
|
}
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
S9xMixSamples(sound_buffer, frames * 2);
|
2010-09-25 15:46:12 +00:00
|
|
|
|
|
|
|
frames_written = 0;
|
|
|
|
|
|
|
|
while (frames_written < frames)
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
2019-02-07 01:41:33 +00:00
|
|
|
result = snd_pcm_writei(pcm,
|
|
|
|
sound_buffer +
|
|
|
|
snd_pcm_frames_to_bytes(pcm, frames_written),
|
|
|
|
frames - frames_written);
|
2010-09-25 15:46:12 +00:00
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
{
|
2019-02-07 01:41:33 +00:00
|
|
|
result = snd_pcm_recover(pcm, result, 1);
|
2010-09-25 15:46:12 +00:00
|
|
|
|
|
|
|
if (result < 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
frames_written += result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|