[retro-gtk] retro-pa-player: Resample audio for non-1 speed rates



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]