[retro-gtk] retro-pa-player: Resample audio for non-1 speed rates
- From: Adrien Plazas <aplazas src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [retro-gtk] retro-pa-player: Resample audio for non-1 speed rates
- Date: Thu, 30 Jan 2020 09:29:43 +0000 (UTC)
commit e455ddb63dfe62db80476f73aff525a353c34bd3
Author: Alexander Mikhaylenko <alexm gnome org>
Date: Thu Jan 23 03:59:04 2020 +0500
retro-pa-player: Resample audio for non-1 speed rates
Use libsamplerate to resample audio, so that it doesn't desync with the
video.
Skip audio altogether for speed rates outside [1/256, 256] range, as
that's the hard limit for libsamplerate.
Fixes https://gitlab.gnome.org/GNOME/retro-gtk/issues/32
retro-gtk/retro-pa-player.c | 77 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 76 insertions(+), 1 deletion(-)
---
diff --git a/retro-gtk/retro-pa-player.c b/retro-gtk/retro-pa-player.c
index c96f35f..2d9b510 100644
--- a/retro-gtk/retro-pa-player.c
+++ b/retro-gtk/retro-pa-player.c
@@ -5,6 +5,7 @@
#include "retro-core-private.h"
#include <pulse/simple.h>
#include <pulse/pulseaudio.h>
+#include <samplerate.h>
struct _RetroPaPlayer
{
@@ -15,6 +16,7 @@ struct _RetroPaPlayer
GArray *buffer;
gdouble sample_rate;
pa_simple *simple;
+ SRC_STATE *src;
};
G_DEFINE_TYPE (RetroPaPlayer, retro_pa_player, G_TYPE_OBJECT)
@@ -33,6 +35,7 @@ retro_pa_player_finalize (GObject *object)
self->simple = NULL;
}
+ g_clear_pointer (&self->src, src_delete);
g_array_unref (self->buffer);
G_OBJECT_CLASS (retro_pa_player_parent_class)->finalize (object);
@@ -49,7 +52,13 @@ retro_pa_player_class_init (RetroPaPlayerClass *klass)
static void
retro_pa_player_init (RetroPaPlayer *self)
{
+ gint error;
+
self->buffer = g_array_new (FALSE, FALSE, sizeof (gint16));
+ self->src = src_new (SRC_SINC_BEST_QUALITY, 2, &error);
+
+ if (!self->src)
+ g_error ("Couldn't set up libsamplerate: %s", src_strerror (error));
}
static void
@@ -78,6 +87,57 @@ retro_pa_player_prepare_for_sample_rate (RetroPaPlayer *self,
}
}
+static void
+resample (RetroPaPlayer *self,
+ gdouble ratio)
+{
+ g_autofree gfloat *data_in = NULL;
+ g_autofree gfloat *data_out = NULL;
+ gint error;
+ gsize frames_used_in, frames_used_out, length;
+
+ length = self->buffer->len;
+
+ data_in = g_new (gfloat, length);
+ data_out = g_new (gfloat, length);
+
+ src_short_to_float_array ((gint16 *) self->buffer->data,
+ data_in,
+ length);
+
+ g_array_set_size (self->buffer, 0);
+
+ frames_used_in = 0;
+ frames_used_out = 0;
+ while (frames_used_in < length) {
+ SRC_DATA data;
+
+ data.data_in = data_in + frames_used_in * sizeof (gfloat);
+ data.data_out = data_out;
+ data.input_frames = length / 2 - frames_used_in;
+ data.output_frames = length / 2;
+ data.src_ratio = ratio;
+ data.end_of_input = 0;
+
+ error = src_process (self->src, &data);
+ if (error) {
+ g_critical ("Couldn't resample the audio: %s", src_strerror (error));
+
+ return;
+ }
+
+ g_array_set_size (self->buffer,
+ frames_used_out + data.output_frames_gen * 2);
+ src_float_to_short_array (data_out,
+ (gint16 *) (self->buffer->data +
+ frames_used_out * sizeof (gint16)),
+ data.output_frames_gen * 2);
+
+ frames_used_in += data.input_frames_used * 2;
+ frames_used_out += data.output_frames_gen * 2;
+ }
+}
+
static void
retro_pa_player_on_audio_output (RetroCore *sender,
gint16 *data,
@@ -94,12 +154,23 @@ static void
on_iterated (RetroCore *core,
RetroPaPlayer *self)
{
- gdouble sample_rate;
+ gdouble sample_rate, speed_rate;
if (retro_core_is_running_ahead (self->core))
return;
sample_rate = retro_core_get_sample_rate (self->core);
+ speed_rate = retro_core_get_speed_rate (self->core);
+
+ // Libsamplerate cannot resample audio with rates outside this range
+ // Since audio isn't going to be useful at these rates anyway, just bail.
+ if (speed_rate < 1.0 / 256.0 || speed_rate > 256.0) {
+ g_array_set_size (self->buffer, 0);
+
+ g_debug ("Can’t resample the audio for speed rates lower than 1/256 or greater than 256. The audio won’t
be played.");
+
+ return;
+ }
if (self->simple == NULL || sample_rate != self->sample_rate)
retro_pa_player_prepare_for_sample_rate (self, sample_rate);
@@ -107,6 +178,8 @@ on_iterated (RetroCore *core,
if (self->simple == NULL)
return;
+ resample (self, 1 / speed_rate);
+
pa_simple_write (self->simple,
self->buffer->data,
self->buffer->len * sizeof (gint16),
@@ -161,6 +234,8 @@ retro_pa_player_set_core (RetroPaPlayer *self,
pa_simple_free (self->simple);
self->simple = NULL;
}
+
+ src_reset (self->src);
}
/**
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]