libs: audio-converter: complete code to support non-interleaved audio buffers
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Thu, 1 Feb 2018 15:00:06 +0000 (17:00 +0200)
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>
Wed, 11 Jul 2018 13:26:13 +0000 (16:26 +0300)
https://bugzilla.gnome.org/show_bug.cgi?id=705986

gst-libs/gst/audio/audio-converter.c

index c3bcb82..0812eff 100644 (file)
@@ -131,6 +131,11 @@ struct _GstAudioConverter
   /* quant */
   GstAudioQuantize *quant;
 
+  /* change layout */
+  GstAudioFormat chlayout_format;
+  GstAudioLayout chlayout_target;
+  gint chlayout_channels;
+
   /* pack */
   gboolean out_default;
   AudioChain *chain_end;        /* NULL for empty chain or points to the last element in the chain */
@@ -582,6 +587,110 @@ do_quantize (AudioChain * chain, gpointer user_data)
   return TRUE;
 }
 
+#define MAKE_INTERLEAVE_FUNC(type) \
+static inline void \
+interleave_##type (const type * in[], type * out[], \
+    gsize num_samples, gint channels) \
+{ \
+  gsize s; \
+  gint c; \
+  for (s = 0; s < num_samples; s++) { \
+    for (c = 0; c < channels; c++) { \
+      out[0][s * channels + c] = in[c][s]; \
+    } \
+  } \
+}
+
+#define MAKE_DEINTERLEAVE_FUNC(type) \
+static inline void \
+deinterleave_##type (const type * in[], type * out[], \
+    gsize num_samples, gint channels) \
+{ \
+  gsize s; \
+  gint c; \
+  for (s = 0; s < num_samples; s++) { \
+    for (c = 0; c < channels; c++) { \
+      out[c][s] = in[0][s * channels + c]; \
+    } \
+  } \
+}
+
+MAKE_INTERLEAVE_FUNC (gint16);
+MAKE_INTERLEAVE_FUNC (gint32);
+MAKE_INTERLEAVE_FUNC (gfloat);
+MAKE_INTERLEAVE_FUNC (gdouble);
+MAKE_DEINTERLEAVE_FUNC (gint16);
+MAKE_DEINTERLEAVE_FUNC (gint32);
+MAKE_DEINTERLEAVE_FUNC (gfloat);
+MAKE_DEINTERLEAVE_FUNC (gdouble);
+
+static gboolean
+do_change_layout (AudioChain * chain, gpointer user_data)
+{
+  GstAudioConverter *convert = user_data;
+  GstAudioFormat format = convert->chlayout_format;
+  GstAudioLayout out_layout = convert->chlayout_target;
+  gint channels = convert->chlayout_channels;
+  gsize num_samples;
+  gpointer *in, *out;
+
+  in = audio_chain_get_samples (chain->prev, &num_samples);
+  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
+
+  if (out_layout == GST_AUDIO_LAYOUT_INTERLEAVED) {
+    /* interleave */
+    GST_LOG ("interleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
+    switch (format) {
+      case GST_AUDIO_FORMAT_S16:
+        interleave_gint16 ((const gint16 **) in, (gint16 **) out,
+            num_samples, channels);
+        break;
+      case GST_AUDIO_FORMAT_S32:
+        interleave_gint32 ((const gint32 **) in, (gint32 **) out,
+            num_samples, channels);
+        break;
+      case GST_AUDIO_FORMAT_F32:
+        interleave_gfloat ((const gfloat **) in, (gfloat **) out,
+            num_samples, channels);
+        break;
+      case GST_AUDIO_FORMAT_F64:
+        interleave_gdouble ((const gdouble **) in, (gdouble **) out,
+            num_samples, channels);
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+  } else {
+    /* deinterleave */
+    GST_LOG ("deinterleaving %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
+    switch (format) {
+      case GST_AUDIO_FORMAT_S16:
+        deinterleave_gint16 ((const gint16 **) in, (gint16 **) out,
+            num_samples, channels);
+        break;
+      case GST_AUDIO_FORMAT_S32:
+        deinterleave_gint32 ((const gint32 **) in, (gint32 **) out,
+            num_samples, channels);
+        break;
+      case GST_AUDIO_FORMAT_F32:
+        deinterleave_gfloat ((const gfloat **) in, (gfloat **) out,
+            num_samples, channels);
+        break;
+      case GST_AUDIO_FORMAT_F64:
+        deinterleave_gdouble ((const gdouble **) in, (gdouble **) out,
+            num_samples, channels);
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+  }
+
+  audio_chain_set_samples (chain, out, num_samples);
+  return TRUE;
+}
+
 static gboolean
 is_intermediate_format (GstAudioFormat format)
 {
@@ -719,9 +828,16 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev)
   GstAudioInfo *out = &convert->out;
   GstAudioFormat format = convert->current_format;
   const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert);
+  GstAudioChannelMixerFlags flags = 0;
 
   convert->current_channels = out->channels;
 
+  /* keep the input layout */
+  if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
+    flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_IN;
+    flags |= GST_AUDIO_CHANNEL_MIXER_FLAGS_NON_INTERLEAVED_OUT;
+  }
+
   if (opt_matrix) {
     gfloat **matrix = NULL;
 
@@ -730,12 +846,10 @@ chain_mix (GstAudioConverter * convert, AudioChain * prev)
           mix_matrix_from_g_value (in->channels, out->channels, opt_matrix);
 
     convert->mix =
-        gst_audio_channel_mixer_new_with_matrix (0, format, in->channels,
+        gst_audio_channel_mixer_new_with_matrix (flags, format, in->channels,
         out->channels, matrix);
   } else {
-    GstAudioChannelMixerFlags flags;
-
-    flags =
+    flags |=
         GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
         GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0;
     flags |=
@@ -781,8 +895,13 @@ chain_resample (GstAudioConverter * convert, AudioChain * prev)
     flags = 0;
     if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
       flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN;
+    }
+    /* if the resampler is activated, it is optimal to change layout here */
+    if (out->layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
       flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT;
     }
+    convert->current_layout = out->layout;
+
     if (variable_rate)
       flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE;
 
@@ -876,6 +995,29 @@ chain_quantize (GstAudioConverter * convert, AudioChain * prev)
 }
 
 static AudioChain *
+chain_change_layout (GstAudioConverter * convert, AudioChain * prev)
+{
+  GstAudioInfo *out = &convert->out;
+
+  if (convert->current_layout != out->layout) {
+    convert->current_layout = out->layout;
+
+    /* if there is only 1 channel, layouts are identical */
+    if (convert->current_channels > 1) {
+      convert->chlayout_target = convert->current_layout;
+      convert->chlayout_format = convert->current_format;
+      convert->chlayout_channels = convert->current_channels;
+
+      prev = audio_chain_new (prev, convert);
+      prev->allow_ip = FALSE;
+      prev->pass_alloc = FALSE;
+      audio_chain_set_make_func (prev, do_change_layout, convert, NULL);
+    }
+  }
+  return prev;
+}
+
+static AudioChain *
 chain_pack (GstAudioConverter * convert, AudioChain * prev)
 {
   GstAudioInfo *out = &convert->out;
@@ -1184,8 +1326,6 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
 
   g_return_val_if_fail (in_info != NULL, FALSE);
   g_return_val_if_fail (out_info != NULL, FALSE);
-  g_return_val_if_fail (in_info->layout == GST_AUDIO_LAYOUT_INTERLEAVED, FALSE);
-  g_return_val_if_fail (in_info->layout == out_info->layout, FALSE);
 
   if (config)
     opt_matrix =
@@ -1226,7 +1366,9 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
   prev = chain_convert_out (convert, prev);
   /* step 6, optional quantize */
   prev = chain_quantize (convert, prev);
-  /* step 7, pack */
+  /* step 7, change layout */
+  prev = chain_change_layout (convert, prev);
+  /* step 8, pack */
   convert->chain_end = chain_pack (convert, prev);
 
   convert->convert = converter_generic;
@@ -1236,10 +1378,12 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
   if (convert->mix_passthrough) {
     if (out_info->finfo->format == in_info->finfo->format) {
       if (convert->resampler == NULL) {
-        GST_INFO
-            ("same formats, no resampler and passthrough mixing -> passthrough");
-        convert->convert = converter_passthrough;
-        convert->in_place = TRUE;
+        if (out_info->layout == in_info->layout) {
+          GST_INFO ("same formats, same layout, no resampler and "
+              "passthrough mixing -> passthrough");
+          convert->convert = converter_passthrough;
+          convert->in_place = TRUE;
+        }
       } else {
         if (is_intermediate_format (in_info->finfo->format)) {
           GST_INFO ("same formats, and passthrough mixing -> only resampling");
@@ -1248,7 +1392,7 @@ gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
       }
     } else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo,
             in_info->finfo)) {
-      if (convert->resampler == NULL) {
+      if (convert->resampler == NULL && out_info->layout == in_info->layout) {
         GST_INFO ("no resampler, passthrough mixing -> only endian conversion");
         convert->convert = converter_endian;
         convert->in_place = TRUE;