mirror of
https://github.com/libretro/RetroArch
synced 2025-01-27 03:35:22 +00:00
Merge branch 'ratecontrol'
This commit is contained in:
commit
d68658b7fb
23
audio/alsa.c
23
audio/alsa.c
@ -30,6 +30,8 @@ typedef struct alsa
|
|||||||
snd_pcm_t *pcm;
|
snd_pcm_t *pcm;
|
||||||
bool nonblock;
|
bool nonblock;
|
||||||
bool has_float;
|
bool has_float;
|
||||||
|
|
||||||
|
size_t buffer_size;
|
||||||
} alsa_t;
|
} alsa_t;
|
||||||
|
|
||||||
static bool alsa_use_float(void *data)
|
static bool alsa_use_float(void *data)
|
||||||
@ -62,8 +64,8 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency)
|
|||||||
if (device)
|
if (device)
|
||||||
alsa_dev = device;
|
alsa_dev = device;
|
||||||
|
|
||||||
unsigned int latency_usec = latency * 1000;
|
unsigned latency_usec = latency * 1000;
|
||||||
unsigned int channels = 2;
|
unsigned channels = 2;
|
||||||
unsigned periods = 4;
|
unsigned periods = 4;
|
||||||
snd_pcm_uframes_t buffer_size;
|
snd_pcm_uframes_t buffer_size;
|
||||||
snd_pcm_format_t format;
|
snd_pcm_format_t format;
|
||||||
@ -89,6 +91,7 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency)
|
|||||||
SSNES_LOG("ALSA: Period size: %d frames\n", (int)buffer_size);
|
SSNES_LOG("ALSA: Period size: %d frames\n", (int)buffer_size);
|
||||||
snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
|
snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
|
||||||
SSNES_LOG("ALSA: Buffer size: %d frames\n", (int)buffer_size);
|
SSNES_LOG("ALSA: Buffer size: %d frames\n", (int)buffer_size);
|
||||||
|
alsa->buffer_size = snd_pcm_frames_to_bytes(alsa->pcm, buffer_size);
|
||||||
|
|
||||||
TRY_ALSA(snd_pcm_sw_params_malloc(&sw_params));
|
TRY_ALSA(snd_pcm_sw_params_malloc(&sw_params));
|
||||||
TRY_ALSA(snd_pcm_sw_params_current(alsa->pcm, sw_params));
|
TRY_ALSA(snd_pcm_sw_params_current(alsa->pcm, sw_params));
|
||||||
@ -190,6 +193,18 @@ static void alsa_free(void *data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t alsa_write_avail(void *data)
|
||||||
|
{
|
||||||
|
alsa_t *alsa = (alsa_t*)data;
|
||||||
|
return snd_pcm_frames_to_bytes(alsa->pcm, snd_pcm_avail_update(alsa->pcm));
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t alsa_buffer_size(void *data)
|
||||||
|
{
|
||||||
|
alsa_t *alsa = (alsa_t*)data;
|
||||||
|
return alsa->buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
const audio_driver_t audio_alsa = {
|
const audio_driver_t audio_alsa = {
|
||||||
alsa_init,
|
alsa_init,
|
||||||
alsa_write,
|
alsa_write,
|
||||||
@ -198,6 +213,8 @@ const audio_driver_t audio_alsa = {
|
|||||||
alsa_set_nonblock_state,
|
alsa_set_nonblock_state,
|
||||||
alsa_free,
|
alsa_free,
|
||||||
alsa_use_float,
|
alsa_use_float,
|
||||||
"alsa"
|
"alsa",
|
||||||
|
alsa_write_avail,
|
||||||
|
alsa_buffer_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -373,6 +373,20 @@ static ssize_t dsound_write(void *data, const void *buf_, size_t size)
|
|||||||
return written;
|
return written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t dsound_write_avail(void *data)
|
||||||
|
{
|
||||||
|
dsound_t *ds = (dsound_t*)data;
|
||||||
|
EnterCriticalSection(&ds->crit);
|
||||||
|
size_t avail = fifo_write_avail(ds->buffer);
|
||||||
|
LeaveCriticalSection(&ds->crit);
|
||||||
|
return avail;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t dsound_buffer_size(void *data)
|
||||||
|
{
|
||||||
|
return 4 * 1024;
|
||||||
|
}
|
||||||
|
|
||||||
const audio_driver_t audio_dsound = {
|
const audio_driver_t audio_dsound = {
|
||||||
dsound_init,
|
dsound_init,
|
||||||
dsound_write,
|
dsound_write,
|
||||||
@ -381,6 +395,8 @@ const audio_driver_t audio_dsound = {
|
|||||||
dsound_set_nonblock_state,
|
dsound_set_nonblock_state,
|
||||||
dsound_free,
|
dsound_free,
|
||||||
NULL,
|
NULL,
|
||||||
"dsound"
|
"dsound",
|
||||||
|
dsound_write_avail,
|
||||||
|
dsound_buffer_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
19
audio/jack.c
19
audio/jack.c
@ -41,6 +41,7 @@ typedef struct jack
|
|||||||
|
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
pthread_mutex_t cond_lock;
|
pthread_mutex_t cond_lock;
|
||||||
|
size_t buffer_size;
|
||||||
} jack_t;
|
} jack_t;
|
||||||
|
|
||||||
static int process_cb(jack_nframes_t nframes, void *data)
|
static int process_cb(jack_nframes_t nframes, void *data)
|
||||||
@ -163,6 +164,8 @@ static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bufsize = find_buffersize(jd, latency);
|
bufsize = find_buffersize(jd, latency);
|
||||||
|
jd->buffer_size = bufsize;
|
||||||
|
|
||||||
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
|
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
@ -304,6 +307,18 @@ static bool ja_use_float(void *data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t ja_write_avail(void *data)
|
||||||
|
{
|
||||||
|
jack_t *jd = (jack_t*)data;
|
||||||
|
return jack_ringbuffer_write_space(jd->buffer[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t ja_buffer_size(void *data)
|
||||||
|
{
|
||||||
|
jack_t *jd = (jack_t*)data;
|
||||||
|
return jd->buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
const audio_driver_t audio_jack = {
|
const audio_driver_t audio_jack = {
|
||||||
ja_init,
|
ja_init,
|
||||||
ja_write,
|
ja_write,
|
||||||
@ -312,6 +327,8 @@ const audio_driver_t audio_jack = {
|
|||||||
ja_set_nonblock_state,
|
ja_set_nonblock_state,
|
||||||
ja_free,
|
ja_free,
|
||||||
ja_use_float,
|
ja_use_float,
|
||||||
"jack"
|
"jack",
|
||||||
|
ja_write_avail,
|
||||||
|
ja_buffer_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
34
audio/oss.c
34
audio/oss.c
@ -135,7 +135,7 @@ static void oss_set_nonblock_state(void *data, bool state)
|
|||||||
else
|
else
|
||||||
rc = fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) & (~O_NONBLOCK));
|
rc = fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) & (~O_NONBLOCK));
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
fprintf(stderr, "SSNES [ERROR]: Could not set nonblocking on OSS file descriptor. Will not be able to fast-forward.\n");
|
SSNES_WARN("Could not set nonblocking on OSS file descriptor. Will not be able to fast-forward.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void oss_free(void *data)
|
static void oss_free(void *data)
|
||||||
@ -147,6 +147,34 @@ static void oss_free(void *data)
|
|||||||
free(fd);
|
free(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t oss_write_avail(void *data)
|
||||||
|
{
|
||||||
|
int *fd = (int*)data;
|
||||||
|
|
||||||
|
audio_buf_info info;
|
||||||
|
if (ioctl(*fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
|
||||||
|
{
|
||||||
|
SSNES_ERR("SNDCTL_DSP_GETOSPACE failed ...\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t oss_buffer_size(void *data)
|
||||||
|
{
|
||||||
|
int *fd = (int*)data;
|
||||||
|
|
||||||
|
audio_buf_info info;
|
||||||
|
if (ioctl(*fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
|
||||||
|
{
|
||||||
|
SSNES_ERR("SNDCTL_DSP_GETOSPACE failed ...\n");
|
||||||
|
return 1; // Return something non-zero to avoid SIGFPE.
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.fragsize * info.fragstotal;
|
||||||
|
}
|
||||||
|
|
||||||
const audio_driver_t audio_oss = {
|
const audio_driver_t audio_oss = {
|
||||||
oss_init,
|
oss_init,
|
||||||
oss_write,
|
oss_write,
|
||||||
@ -155,6 +183,8 @@ const audio_driver_t audio_oss = {
|
|||||||
oss_set_nonblock_state,
|
oss_set_nonblock_state,
|
||||||
oss_free,
|
oss_free,
|
||||||
NULL,
|
NULL,
|
||||||
"oss"
|
"oss",
|
||||||
|
oss_write_avail,
|
||||||
|
oss_buffer_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ typedef struct
|
|||||||
pa_context *context;
|
pa_context *context;
|
||||||
pa_stream *stream;
|
pa_stream *stream;
|
||||||
bool nonblock;
|
bool nonblock;
|
||||||
|
size_t buffer_size;
|
||||||
} pa_t;
|
} pa_t;
|
||||||
|
|
||||||
static void pulse_free(void *data)
|
static void pulse_free(void *data)
|
||||||
@ -153,6 +154,8 @@ static void *pulse_init(const char *device, unsigned rate, unsigned latency)
|
|||||||
buffer_attr.minreq = -1;
|
buffer_attr.minreq = -1;
|
||||||
buffer_attr.fragsize = -1;
|
buffer_attr.fragsize = -1;
|
||||||
|
|
||||||
|
pa->buffer_size = buffer_attr.tlength;
|
||||||
|
|
||||||
if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
|
if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
@ -177,8 +180,9 @@ static ssize_t pulse_write(void *data, const void *buf, size_t size)
|
|||||||
pa_t *pa = (pa_t*)data;
|
pa_t *pa = (pa_t*)data;
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(pa->mainloop);
|
pa_threaded_mainloop_lock(pa->mainloop);
|
||||||
unsigned length = pa_stream_writable_size(pa->stream);
|
size_t length = pa_stream_writable_size(pa->stream);
|
||||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||||
|
|
||||||
while (length < size)
|
while (length < size)
|
||||||
{
|
{
|
||||||
pa_threaded_mainloop_wait(pa->mainloop);
|
pa_threaded_mainloop_wait(pa->mainloop);
|
||||||
@ -222,6 +226,21 @@ static bool pulse_use_float(void *data)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t pulse_write_avail(void *data)
|
||||||
|
{
|
||||||
|
pa_t *pa = (pa_t*)data;
|
||||||
|
pa_threaded_mainloop_lock(pa->mainloop);
|
||||||
|
size_t length = pa_stream_writable_size(pa->stream);
|
||||||
|
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t pulse_buffer_size(void *data)
|
||||||
|
{
|
||||||
|
pa_t *pa = (pa_t*)data;
|
||||||
|
return pa->buffer_size;
|
||||||
|
}
|
||||||
|
|
||||||
const audio_driver_t audio_pulse = {
|
const audio_driver_t audio_pulse = {
|
||||||
pulse_init,
|
pulse_init,
|
||||||
pulse_write,
|
pulse_write,
|
||||||
@ -230,6 +249,8 @@ const audio_driver_t audio_pulse = {
|
|||||||
pulse_set_nonblock_state,
|
pulse_set_nonblock_state,
|
||||||
pulse_free,
|
pulse_free,
|
||||||
pulse_use_float,
|
pulse_use_float,
|
||||||
"pulse"
|
"pulse",
|
||||||
|
pulse_write_avail,
|
||||||
|
pulse_buffer_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -181,6 +181,24 @@ static void rs_free(void *data)
|
|||||||
free(rsd);
|
free(rsd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t rs_write_avail(void *data)
|
||||||
|
{
|
||||||
|
rsd_t *rsd = (rsd_t*)data;
|
||||||
|
|
||||||
|
if (rsd->has_error)
|
||||||
|
return 0;
|
||||||
|
rsd_callback_lock(rsd->rd);
|
||||||
|
size_t val = fifo_write_avail(rsd->buffer);
|
||||||
|
rsd_callback_unlock(rsd->rd);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t rs_buffer_size(void *data)
|
||||||
|
{
|
||||||
|
(void)data;
|
||||||
|
return 1024 * 4;
|
||||||
|
}
|
||||||
|
|
||||||
const audio_driver_t audio_rsound = {
|
const audio_driver_t audio_rsound = {
|
||||||
rs_init,
|
rs_init,
|
||||||
rs_write,
|
rs_write,
|
||||||
@ -189,6 +207,8 @@ const audio_driver_t audio_rsound = {
|
|||||||
rs_set_nonblock_state,
|
rs_set_nonblock_state,
|
||||||
rs_free,
|
rs_free,
|
||||||
NULL,
|
NULL,
|
||||||
"rsound"
|
"rsound",
|
||||||
|
rs_write_avail,
|
||||||
|
rs_buffer_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ typedef struct
|
|||||||
{
|
{
|
||||||
xaudio2_t *xa;
|
xaudio2_t *xa;
|
||||||
bool nonblock;
|
bool nonblock;
|
||||||
|
size_t bufsize;
|
||||||
} xa_t;
|
} xa_t;
|
||||||
|
|
||||||
static void *xa_init(const char *device, unsigned rate, unsigned latency)
|
static void *xa_init(const char *device, unsigned rate, unsigned latency)
|
||||||
@ -39,7 +40,9 @@ static void *xa_init(const char *device, unsigned rate, unsigned latency)
|
|||||||
|
|
||||||
SSNES_LOG("XAudio2: Requesting %d ms latency, using %d ms latency.\n", latency, (int)bufsize * 1000 / rate);
|
SSNES_LOG("XAudio2: Requesting %d ms latency, using %d ms latency.\n", latency, (int)bufsize * 1000 / rate);
|
||||||
|
|
||||||
xa->xa = xaudio2_new(rate, 2, bufsize * 2 * sizeof(float));
|
xa->bufsize = bufsize * 2 * sizeof(float);
|
||||||
|
|
||||||
|
xa->xa = xaudio2_new(rate, 2, xa->bufsize);
|
||||||
if (!xa->xa)
|
if (!xa->xa)
|
||||||
{
|
{
|
||||||
SSNES_ERR("Failed to init XAudio2.\n");
|
SSNES_ERR("Failed to init XAudio2.\n");
|
||||||
@ -103,6 +106,18 @@ static void xa_free(void *data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t xa_write_avail(void *data)
|
||||||
|
{
|
||||||
|
xa_t *xa = (xa_t*)data;
|
||||||
|
return xaudio2_write_avail(xa->xa);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t xa_buffer_size(void *data)
|
||||||
|
{
|
||||||
|
xa_t *xa = (xa_t*)data;
|
||||||
|
return xa->bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
const audio_driver_t audio_xa = {
|
const audio_driver_t audio_xa = {
|
||||||
xa_init,
|
xa_init,
|
||||||
xa_write,
|
xa_write,
|
||||||
@ -111,5 +126,7 @@ const audio_driver_t audio_xa = {
|
|||||||
xa_set_nonblock_state,
|
xa_set_nonblock_state,
|
||||||
xa_free,
|
xa_free,
|
||||||
xa_use_float,
|
xa_use_float,
|
||||||
"xaudio"
|
"xaudio",
|
||||||
|
xa_write_avail,
|
||||||
|
xa_buffer_size,
|
||||||
};
|
};
|
||||||
|
10
config.def.h
10
config.def.h
@ -231,6 +231,16 @@ static const int out_latency = 64;
|
|||||||
// Will sync audio. (recommended)
|
// Will sync audio. (recommended)
|
||||||
static const bool audio_sync = true;
|
static const bool audio_sync = true;
|
||||||
|
|
||||||
|
// Experimental rate control
|
||||||
|
static const bool rate_control = false;
|
||||||
|
|
||||||
|
// Rate control delta. Defines how much rate_control is allowed to adjust input rate.
|
||||||
|
static const float rate_control_delta = 0.005;
|
||||||
|
|
||||||
|
//////////////
|
||||||
|
// Misc
|
||||||
|
//////////////
|
||||||
|
|
||||||
// Enables use of rewind. This will incur some memory footprint depending on the save state buffer.
|
// Enables use of rewind. This will incur some memory footprint depending on the save state buffer.
|
||||||
// This rewind only works when using bSNES core atm.
|
// This rewind only works when using bSNES core atm.
|
||||||
static const bool rewind_enable = false;
|
static const bool rewind_enable = false;
|
||||||
|
14
driver.c
14
driver.c
@ -335,9 +335,21 @@ void init_audio(void)
|
|||||||
ssnes_assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
|
ssnes_assert(g_settings.audio.out_rate < g_settings.audio.in_rate * AUDIO_MAX_RATIO);
|
||||||
ssnes_assert(g_extern.audio_data.outsamples = (float*)malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO));
|
ssnes_assert(g_extern.audio_data.outsamples = (float*)malloc(max_bufsamples * sizeof(float) * AUDIO_MAX_RATIO));
|
||||||
|
|
||||||
g_extern.audio_data.src_ratio =
|
g_extern.audio_data.orig_src_ratio =
|
||||||
|
g_extern.audio_data.src_ratio =
|
||||||
(double)g_settings.audio.out_rate / g_settings.audio.in_rate;
|
(double)g_settings.audio.out_rate / g_settings.audio.in_rate;
|
||||||
|
|
||||||
|
if (g_settings.audio.rate_control)
|
||||||
|
{
|
||||||
|
if (driver.audio->buffer_size && driver.audio->write_avail)
|
||||||
|
{
|
||||||
|
g_extern.audio_data.driver_buffer_size = driver.audio->buffer_size(driver.audio_data);
|
||||||
|
g_extern.audio_data.rate_control = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SSNES_WARN("Audio rate control was desired, but driver does not support needed features.\n");
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DYLIB
|
#ifdef HAVE_DYLIB
|
||||||
init_dsp_plugin();
|
init_dsp_plugin();
|
||||||
#endif
|
#endif
|
||||||
|
3
driver.h
3
driver.h
@ -98,6 +98,9 @@ typedef struct audio_driver
|
|||||||
void (*free)(void *data);
|
void (*free)(void *data);
|
||||||
bool (*use_float)(void *data); // Defines if driver will take standard floating point samples, or int16_t samples.
|
bool (*use_float)(void *data); // Defines if driver will take standard floating point samples, or int16_t samples.
|
||||||
const char *ident;
|
const char *ident;
|
||||||
|
|
||||||
|
size_t (*write_avail)(void *data); // Optional
|
||||||
|
size_t (*buffer_size)(void *data); // Optional
|
||||||
} audio_driver_t;
|
} audio_driver_t;
|
||||||
|
|
||||||
#define AXIS_NEG(x) (((uint32_t)(x) << 16) | UINT16_C(0xFFFF))
|
#define AXIS_NEG(x) (((uint32_t)(x) << 16) | UINT16_C(0xFFFF))
|
||||||
|
@ -138,6 +138,9 @@ struct settings
|
|||||||
|
|
||||||
char dsp_plugin[PATH_MAX];
|
char dsp_plugin[PATH_MAX];
|
||||||
char external_driver[PATH_MAX];
|
char external_driver[PATH_MAX];
|
||||||
|
|
||||||
|
bool rate_control;
|
||||||
|
float rate_control_delta;
|
||||||
} audio;
|
} audio;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
@ -300,6 +303,10 @@ struct global
|
|||||||
dylib_t dsp_lib;
|
dylib_t dsp_lib;
|
||||||
const ssnes_dsp_plugin_t *dsp_plugin;
|
const ssnes_dsp_plugin_t *dsp_plugin;
|
||||||
void *dsp_handle;
|
void *dsp_handle;
|
||||||
|
|
||||||
|
bool rate_control;
|
||||||
|
double orig_src_ratio;
|
||||||
|
size_t driver_buffer_size;
|
||||||
} audio_data;
|
} audio_data;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|
@ -195,6 +195,8 @@ void config_set_defaults(void)
|
|||||||
strlcpy(g_settings.audio.device, audio_device, sizeof(g_settings.audio.device));
|
strlcpy(g_settings.audio.device, audio_device, sizeof(g_settings.audio.device));
|
||||||
g_settings.audio.latency = out_latency;
|
g_settings.audio.latency = out_latency;
|
||||||
g_settings.audio.sync = audio_sync;
|
g_settings.audio.sync = audio_sync;
|
||||||
|
g_settings.audio.rate_control = rate_control;
|
||||||
|
g_settings.audio.rate_control_delta = rate_control_delta;
|
||||||
|
|
||||||
g_settings.rewind_enable = rewind_enable;
|
g_settings.rewind_enable = rewind_enable;
|
||||||
g_settings.rewind_buffer_size = rewind_buffer_size;
|
g_settings.rewind_buffer_size = rewind_buffer_size;
|
||||||
@ -429,6 +431,8 @@ bool config_load_file(const char *path)
|
|||||||
CONFIG_GET_STRING(audio.device, "audio_device");
|
CONFIG_GET_STRING(audio.device, "audio_device");
|
||||||
CONFIG_GET_INT(audio.latency, "audio_latency");
|
CONFIG_GET_INT(audio.latency, "audio_latency");
|
||||||
CONFIG_GET_BOOL(audio.sync, "audio_sync");
|
CONFIG_GET_BOOL(audio.sync, "audio_sync");
|
||||||
|
CONFIG_GET_BOOL(audio.rate_control, "audio_rate_control");
|
||||||
|
CONFIG_GET_FLOAT(audio.rate_control_delta, "audio_rate_control_delta");
|
||||||
|
|
||||||
CONFIG_GET_STRING(video.driver, "video_driver");
|
CONFIG_GET_STRING(video.driver, "video_driver");
|
||||||
CONFIG_GET_STRING(audio.driver, "audio_driver");
|
CONFIG_GET_STRING(audio.driver, "audio_driver");
|
||||||
|
22
ssnes.c
22
ssnes.c
@ -246,6 +246,24 @@ void ssnes_render_cached_frame(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void readjust_audio_input_rate(void)
|
||||||
|
{
|
||||||
|
int avail = driver.audio->write_avail(driver.audio_data);
|
||||||
|
//fprintf(stderr, "Audio buffer is %u%% full\n",
|
||||||
|
// (unsigned)(100 - (avail * 100) / g_extern.audio_data.driver_buffer_size));
|
||||||
|
|
||||||
|
int half_size = g_extern.audio_data.driver_buffer_size / 2;
|
||||||
|
int delta_mid = avail - half_size;
|
||||||
|
double direction = (double)delta_mid / half_size;
|
||||||
|
|
||||||
|
double adjust = 1.0 + g_settings.audio.rate_control_delta * direction;
|
||||||
|
|
||||||
|
g_extern.audio_data.src_ratio = g_extern.audio_data.orig_src_ratio * adjust;
|
||||||
|
|
||||||
|
//fprintf(stderr, "New rate: %lf, Orig rate: %lf\n",
|
||||||
|
// g_extern.audio_data.src_ratio, g_extern.audio_data.orig_src_ratio);
|
||||||
|
}
|
||||||
|
|
||||||
static bool audio_flush(const int16_t *data, size_t samples)
|
static bool audio_flush(const int16_t *data, size_t samples)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_FFMPEG
|
#ifdef HAVE_FFMPEG
|
||||||
@ -284,6 +302,10 @@ static bool audio_flush(const int16_t *data, size_t samples)
|
|||||||
src_data.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data;
|
src_data.data_in = dsp_output.samples ? dsp_output.samples : g_extern.audio_data.data;
|
||||||
src_data.data_out = g_extern.audio_data.outsamples;
|
src_data.data_out = g_extern.audio_data.outsamples;
|
||||||
src_data.input_frames = dsp_output.samples ? dsp_output.frames : (samples / 2);
|
src_data.input_frames = dsp_output.samples ? dsp_output.frames : (samples / 2);
|
||||||
|
|
||||||
|
if (g_extern.audio_data.rate_control)
|
||||||
|
readjust_audio_input_rate();
|
||||||
|
|
||||||
src_data.ratio = g_extern.audio_data.src_ratio;
|
src_data.ratio = g_extern.audio_data.src_ratio;
|
||||||
|
|
||||||
hermite_process(g_extern.audio_data.source, &src_data);
|
hermite_process(g_extern.audio_data.source, &src_data);
|
||||||
|
@ -146,6 +146,12 @@
|
|||||||
# Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
|
# Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
|
||||||
# audio_latency = 64
|
# audio_latency = 64
|
||||||
|
|
||||||
|
# Enable experimental audio rate control.
|
||||||
|
# audio_rate_control = false
|
||||||
|
|
||||||
|
# Controls audio rate control delta. Defines how much input rate can be adjusted dynamically.
|
||||||
|
# Input rate = in_rate * (1.0 +/- audio_rate_control_delta)
|
||||||
|
# audio_rate_control_delta = 0.005
|
||||||
|
|
||||||
#### Input
|
#### Input
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user