audio-converter: rework the main processing loop
authorWim Taymans <wtaymans@redhat.com>
Wed, 16 Dec 2015 09:45:48 +0000 (10:45 +0100)
committerWim Taymans <wtaymans@redhat.com>
Wed, 16 Dec 2015 10:13:15 +0000 (11:13 +0100)
Rework the main processing loop. We now create an audio processing
chain from small core functions. This is very similar to how the
video-converter core works and allows us to statically calculate an
optimal allocation strategy for all possible combinations of operations.
Make sure we support non-interleaved data everywhere.
Add functions to calculate in and out frames and latency.

gst-libs/gst/audio/audio-converter.c
gst-libs/gst/audio/audio-converter.h
win32/common/libgstaudio.def

index 9136732..b518dcc 100644 (file)
@@ -74,8 +74,9 @@ ensure_debug_category (void)
 #define ensure_debug_category() /* NOOP */
 #endif /* GST_DISABLE_GST_DEBUG */
 
-typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count);
+typedef struct _AudioChain AudioChain;
 
+typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count);
 /*                           int/int    int/float  float/int float/float
  *
  *  unpack                     S32          S32         F64       F64
@@ -84,6 +85,11 @@ typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count);
  *  convert                                           F64->S32
  *  quantize                   S32                      S32
  *  pack                       S32          F64         S32       F64
+ *
+ *
+ *  interleave
+ *  deinterleave
+ *  resample
  */
 struct _GstAudioConverter
 {
@@ -92,26 +98,136 @@ struct _GstAudioConverter
 
   GstStructure *config;
 
+  GstAudioConverterFlags flags;
+  GstAudioFormat current_format;
+  GstAudioLayout current_layout;
+  gint current_channels;
+
+  gpointer *in_data;
+  gpointer *out_data;
+
+  /* unpack */
   gboolean in_default;
+  gboolean unpack_ip;
+  AudioChain *unpack_chain;
 
+  /* convert in */
   AudioConvertFunc convert_in;
+  AudioChain *convert_in_chain;
 
+  /* channel mix */
   gboolean mix_passthrough;
   GstAudioChannelMix *mix;
+  AudioChain *mix_chain;
 
+  /* convert out */
   AudioConvertFunc convert_out;
+  AudioChain *convert_out_chain;
 
+  /* quant */
   GstAudioQuantize *quant;
+  AudioChain *quant_chain;
 
+  /* pack */
   gboolean out_default;
+  AudioChain *pack_chain;
 
   gboolean passthrough;
+};
+
+typedef gboolean (*AudioChainFunc) (AudioChain * chain, gsize samples,
+    gpointer user_data);
+typedef gpointer *(*AudioChainAllocFunc) (AudioChain * chain, gsize samples,
+    gpointer user_data);
+
+static gpointer *get_output_samples (AudioChain * chain, gsize samples,
+    gpointer user_data);
+
+struct _AudioChain
+{
+  AudioChain *prev;
 
-  gpointer tmpbuf;
-  gpointer tmpbuf2;
-  gint tmpbufsize;
+  AudioChainFunc make_func;
+  gpointer make_func_data;
+  GDestroyNotify make_func_notify;
+
+  gint stride;
+  gint inc;
+  gint blocks;
+
+  gboolean pass_alloc;
+  gboolean allow_ip;
+
+  AudioChainAllocFunc alloc_func;
+  gpointer alloc_data;
+
+  gpointer *tmp;
+  gsize tmpsize;
+
+  gpointer *samples;
 };
 
+static AudioChain *
+audio_chain_new (AudioChain * prev, GstAudioConverter * convert)
+{
+  AudioChain *chain;
+  const GstAudioFormatInfo *finfo;
+
+  chain = g_slice_new0 (AudioChain);
+  chain->prev = prev;
+
+  if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
+    chain->inc = 1;
+    chain->blocks = convert->current_channels;
+  } else {
+    chain->inc = convert->current_channels;
+    chain->blocks = 1;
+  }
+  finfo = gst_audio_format_get_info (convert->current_format);
+  chain->stride = (finfo->width * chain->inc) / 8;
+
+  return chain;
+}
+
+static void
+audio_chain_set_make_func (AudioChain * chain,
+    AudioChainFunc make_func, gpointer user_data, GDestroyNotify notify)
+{
+  chain->make_func = make_func;
+  chain->make_func_data = user_data;
+  chain->make_func_notify = notify;
+}
+
+static void
+audio_chain_free (AudioChain * chain)
+{
+  GST_LOG ("free chain %p", chain);
+  if (chain->make_func_notify)
+    chain->make_func_notify (chain->make_func_data);
+  g_free (chain->tmp);
+  g_slice_free (AudioChain, chain);
+}
+
+static gpointer *
+audio_chain_alloc_samples (AudioChain * chain, guint samples)
+{
+  return chain->alloc_func (chain, samples, chain->alloc_data);
+}
+
+static gpointer *
+audio_chain_get_samples (AudioChain * chain, guint samples)
+{
+  gpointer *res;
+
+  while (!chain->samples)
+    chain->make_func (chain, samples, chain->make_func_data);
+
+  res = chain->samples;
+  chain->samples = NULL;
+
+  return res;
+}
+
 /*
 static guint
 get_opt_uint (GstAudioConverter * convert, const gchar * opt, guint def)
@@ -202,89 +318,176 @@ gst_audio_converter_get_config (GstAudioConverter * convert)
   return convert->config;
 }
 
-/**
- * gst_audio_converter_new: (skip)
- * @in: a source #GstAudioInfo
- * @out: a destination #GstAudioInfo
- * @config: (transfer full): a #GstStructure with configuration options
- *
- * Create a new #GstAudioConverter that is able to convert between @in and @out
- * audio formats.
- *
- * @config contains extra configuration options, see #GST_VIDEO_CONVERTER_OPT_*
- * parameters for details about the options and values.
- *
- * Returns: a #GstAudioConverter or %NULL if conversion is not possible.
- */
-GstAudioConverter *
-gst_audio_converter_new (GstAudioInfo * in, GstAudioInfo * out,
-    GstStructure * config)
+static gboolean
+do_unpack (AudioChain * chain, gsize samples, gpointer user_data)
 {
-  GstAudioConverter *convert;
-  gint in_depth, out_depth;
-  GstAudioChannelMixFlags flags;
-  gboolean in_int, out_int;
-  GstAudioFormat format;
-  GstAudioDitherMethod dither;
-  GstAudioNoiseShapingMethod ns;
+  GstAudioConverter *convert = user_data;
+  gpointer *tmp;
+  gboolean src_writable;
 
-  g_return_val_if_fail (in != NULL, FALSE);
-  g_return_val_if_fail (out != NULL, FALSE);
-  g_return_val_if_fail (in->rate == out->rate, FALSE);
-  g_return_val_if_fail (in->layout == GST_AUDIO_LAYOUT_INTERLEAVED, FALSE);
-  g_return_val_if_fail (in->layout == out->layout, FALSE);
+  src_writable = (convert->flags & GST_AUDIO_CONVERTER_FLAG_SOURCE_WRITABLE);
 
-  if ((GST_AUDIO_INFO_CHANNELS (in) != GST_AUDIO_INFO_CHANNELS (out)) &&
-      (GST_AUDIO_INFO_IS_UNPOSITIONED (in)
-          || GST_AUDIO_INFO_IS_UNPOSITIONED (out)))
-    goto unpositioned;
+  if (!chain->allow_ip || !src_writable || !convert->in_default) {
+    gint i;
 
-  convert = g_slice_new0 (GstAudioConverter);
+    if (src_writable && chain->allow_ip)
+      tmp = convert->in_data;
+    else
+      tmp = audio_chain_alloc_samples (chain, samples);
+    GST_LOG ("unpack %p %p, %" G_GSIZE_FORMAT, tmp, convert->in_data, samples);
+
+    for (i = 0; i < chain->blocks; i++) {
+      convert->in.finfo->unpack_func (convert->in.finfo,
+          GST_AUDIO_PACK_FLAG_TRUNCATE_RANGE, tmp[i], convert->in_data[i],
+          samples * chain->inc);
+    }
+  } else {
+    tmp = convert->in_data;
+    GST_LOG ("get in samples %p", tmp);
+  }
+  chain->samples = tmp;
 
-  convert->in = *in;
-  convert->out = *out;
+  return TRUE;
+}
 
-  /* default config */
-  convert->config = gst_structure_new_empty ("GstAudioConverter");
-  if (config)
-    gst_audio_converter_set_config (convert, config);
+static gboolean
+do_convert_in (AudioChain * chain, gsize samples, gpointer user_data)
+{
+  GstAudioConverter *convert = user_data;
+  gpointer *in, *out;
+  gint i;
 
-  dither = GET_OPT_DITHER_METHOD (convert);
-  ns = GET_OPT_NOISE_SHAPING_METHOD (convert);
+  in = audio_chain_get_samples (chain->prev, samples);
+  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, samples));
+  GST_LOG ("convert in %p, %p %" G_GSIZE_FORMAT, in, out, samples);
 
-  GST_INFO ("unitsizes: %d -> %d", in->bpf, out->bpf);
+  for (i = 0; i < chain->blocks; i++)
+    convert->convert_in (out[i], in[i], samples * chain->inc);
 
-  in_depth = GST_AUDIO_FORMAT_INFO_DEPTH (in->finfo);
-  out_depth = GST_AUDIO_FORMAT_INFO_DEPTH (out->finfo);
+  chain->samples = out;
 
-  GST_INFO ("depth in %d, out %d", in_depth, out_depth);
+  return TRUE;
+}
 
-  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
-  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
+static gboolean
+do_mix (AudioChain * chain, gsize samples, gpointer user_data)
+{
+  GstAudioConverter *convert = user_data;
+  gpointer *in, *out;
 
-  flags =
-      GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
-      GST_AUDIO_CHANNEL_MIX_FLAGS_UNPOSITIONED_IN : 0;
-  flags |=
-      GST_AUDIO_INFO_IS_UNPOSITIONED (out) ?
-      GST_AUDIO_CHANNEL_MIX_FLAGS_UNPOSITIONED_OUT : 0;
+  in = audio_chain_get_samples (chain->prev, samples);
+  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, samples));
+  GST_LOG ("mix %p %p,%" G_GSIZE_FORMAT, in, out, samples);
 
+  gst_audio_channel_mix_samples (convert->mix, in, out, samples);
 
-  /* step 1, unpack */
-  format = in->finfo->unpack_format;
+  chain->samples = out;
+
+  return TRUE;
+}
+
+static gboolean
+do_convert_out (AudioChain * chain, gsize samples, gpointer user_data)
+{
+  GstAudioConverter *convert = user_data;
+  gpointer *in, *out;
+  gint i;
+
+  in = audio_chain_get_samples (chain->prev, samples);
+  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, samples));
+  GST_LOG ("convert out %p, %p %" G_GSIZE_FORMAT, in, out, samples);
+
+  for (i = 0; i < chain->blocks; i++)
+    convert->convert_out (out[i], in[i], samples * chain->inc);
+
+  chain->samples = out;
+
+  return TRUE;
+}
+
+static gboolean
+do_quantize (AudioChain * chain, gsize samples, gpointer user_data)
+{
+  GstAudioConverter *convert = user_data;
+  gpointer *in, *out;
+
+  in = audio_chain_get_samples (chain->prev, samples);
+  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, samples));
+  GST_LOG ("quantize %p, %p %" G_GSIZE_FORMAT, in, out, samples);
+
+  gst_audio_quantize_samples (convert->quant, in, out, samples);
+
+  chain->samples = out;
+
+  return TRUE;
+}
+
+static AudioChain *
+chain_unpack (GstAudioConverter * convert)
+{
+  AudioChain *prev;
+  GstAudioInfo *in = &convert->in;
+  const GstAudioFormatInfo *fup;
+
+  convert->current_format = in->finfo->unpack_format;
+  convert->current_layout = in->layout;
+  convert->current_channels = in->channels;
   convert->in_default = in->finfo->unpack_format == in->finfo->format;
+
   GST_INFO ("unpack format %s to %s",
       gst_audio_format_to_string (in->finfo->format),
-      gst_audio_format_to_string (format));
+      gst_audio_format_to_string (convert->current_format));
+
+  fup = gst_audio_format_get_info (in->finfo->unpack_format);
+
+  prev = convert->unpack_chain = audio_chain_new (NULL, convert);
+  prev->allow_ip = fup->width <= in->finfo->width;
+  prev->pass_alloc = FALSE;
+  audio_chain_set_make_func (prev, do_unpack, convert, NULL);
+
+  return prev;
+}
+
+static AudioChain *
+chain_convert_in (GstAudioConverter * convert, AudioChain * prev)
+{
+  gboolean in_int, out_int;
+  GstAudioInfo *in = &convert->in;
+  GstAudioInfo *out = &convert->out;
+
+  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
+  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
 
-  /* step 2, optional convert from S32 to F64 for channel mix */
   if (in_int && !out_int) {
     GST_INFO ("convert S32 to F64");
     convert->convert_in = (AudioConvertFunc) audio_orc_s32_to_double;
-    format = GST_AUDIO_FORMAT_F64;
+    convert->current_format = GST_AUDIO_FORMAT_F64;
+
+    prev = convert->convert_in_chain = audio_chain_new (prev, convert);
+    prev->allow_ip = FALSE;
+    prev->pass_alloc = FALSE;
+    audio_chain_set_make_func (prev, do_convert_in, convert, NULL);
   }
+  return prev;
+}
+
+static AudioChain *
+chain_mix (GstAudioConverter * convert, AudioChain * prev)
+{
+  GstAudioChannelMixFlags flags;
+  GstAudioInfo *in = &convert->in;
+  GstAudioInfo *out = &convert->out;
+  GstAudioFormat format = convert->current_format;
+
+  flags =
+      GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
+      GST_AUDIO_CHANNEL_MIX_FLAGS_UNPOSITIONED_IN : 0;
+  flags |=
+      GST_AUDIO_INFO_IS_UNPOSITIONED (out) ?
+      GST_AUDIO_CHANNEL_MIX_FLAGS_UNPOSITIONED_OUT : 0;
+
+  convert->current_channels = out->channels;
 
-  /* step 3, channel mix */
   convert->mix =
       gst_audio_channel_mix_new (flags, format, in->channels, in->position,
       out->channels, out->position);
@@ -294,13 +497,60 @@ gst_audio_converter_new (GstAudioInfo * in, GstAudioInfo * out,
       gst_audio_format_to_string (format), convert->mix_passthrough,
       in->channels, out->channels);
 
-  /* step 4, optional convert for quantize */
+  if (!convert->mix_passthrough) {
+    prev = convert->mix_chain = audio_chain_new (prev, convert);
+    /* we can only do in-place when in >= out, else we don't have enough
+     * memory. */
+    prev->allow_ip = in->channels >= out->channels;
+    prev->pass_alloc = in->channels <= out->channels;
+    audio_chain_set_make_func (prev, do_mix, convert, NULL);
+  }
+  return prev;
+}
+
+static AudioChain *
+chain_convert_out (GstAudioConverter * convert, AudioChain * prev)
+{
+  gboolean in_int, out_int;
+  GstAudioInfo *in = &convert->in;
+  GstAudioInfo *out = &convert->out;
+
+  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
+  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
+
   if (!in_int && out_int) {
-    GST_INFO ("convert F64 to S32");
     convert->convert_out = (AudioConvertFunc) audio_orc_double_to_s32;
-    format = GST_AUDIO_FORMAT_S32;
+    convert->current_format = GST_AUDIO_FORMAT_S32;
+
+    GST_INFO ("convert F64 to S32");
+    prev = convert->convert_out_chain = audio_chain_new (prev, convert);
+    prev->allow_ip = TRUE;
+    prev->pass_alloc = FALSE;
+    audio_chain_set_make_func (prev, do_convert_out, convert, NULL);
   }
-  /* step 5, optional quantize */
+  return prev;
+}
+
+static AudioChain *
+chain_quantize (GstAudioConverter * convert, AudioChain * prev)
+{
+  GstAudioInfo *in = &convert->in;
+  GstAudioInfo *out = &convert->out;
+  gint in_depth, out_depth;
+  gboolean in_int, out_int;
+  GstAudioDitherMethod dither;
+  GstAudioNoiseShapingMethod ns;
+
+  dither = GET_OPT_DITHER_METHOD (convert);
+  ns = GET_OPT_NOISE_SHAPING_METHOD (convert);
+
+  in_depth = GST_AUDIO_FORMAT_INFO_DEPTH (in->finfo);
+  out_depth = GST_AUDIO_FORMAT_INFO_DEPTH (out->finfo);
+  GST_INFO ("depth in %d, out %d", in_depth, out_depth);
+
+  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
+  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
+
   /* Don't dither or apply noise shaping if target depth is bigger than 20 bits
    * as DA converters only can do a SNR up to 20 bits in reality.
    * Also don't dither or apply noise shaping if target depth is larger than
@@ -320,21 +570,165 @@ gst_audio_converter_new (GstAudioInfo * in, GstAudioInfo * out,
    * the rounding correct */
   if (out_int && out_depth < 32) {
     GST_INFO ("quantize to %d bits, dither %d, ns %d", out_depth, dither, ns);
-    convert->quant = gst_audio_quantize_new (dither, ns, 0, format,
+    convert->quant =
+        gst_audio_quantize_new (dither, ns, 0, convert->current_format,
         out->channels, 1U << (32 - out_depth));
+
+    prev = convert->quant_chain = audio_chain_new (prev, convert);
+    prev->allow_ip = TRUE;
+    prev->pass_alloc = TRUE;
+    audio_chain_set_make_func (prev, do_quantize, convert, NULL);
   }
-  /* step 6, pack */
+  return prev;
+}
+
+static AudioChain *
+chain_pack (GstAudioConverter * convert, AudioChain * prev)
+{
+  GstAudioInfo *out = &convert->out;
+  GstAudioFormat format = convert->current_format;
+
+  convert->current_format = out->finfo->format;
+
   g_assert (out->finfo->unpack_format == format);
   convert->out_default = format == out->finfo->format;
   GST_INFO ("pack format %s to %s", gst_audio_format_to_string (format),
       gst_audio_format_to_string (out->finfo->format));
 
+  return prev;
+}
+
+static gpointer *
+get_output_samples (AudioChain * chain, gsize samples, gpointer user_data)
+{
+  GstAudioConverter *convert = user_data;
+
+  GST_LOG ("output samples %" G_GSIZE_FORMAT, samples);
+  return convert->out_data;
+}
+
+static gpointer *
+get_temp_samples (AudioChain * chain, gsize samples, gpointer user_data)
+{
+  gsize needed;
+
+  /* first part contains the pointers, second part the data */
+  needed = (samples * chain->stride + sizeof (gpointer)) * chain->blocks;
+
+  if (needed > chain->tmpsize) {
+    gint i;
+    guint8 *s;
+
+    GST_DEBUG ("alloc samples %" G_GSIZE_FORMAT, needed);
+    chain->tmp = g_realloc (chain->tmp, needed);
+    chain->tmpsize = needed;
+
+    /* jump to the data */
+    s = (guint8 *) & chain->tmp[chain->blocks];
+
+    /* set up the pointers */
+    for (i = 0; i < chain->blocks; i++)
+      chain->tmp[i] = s + (i * samples * chain->stride);
+  }
+  return chain->tmp;
+}
+
+static void
+setup_allocators (GstAudioConverter * convert)
+{
+  AudioChain *chain;
+  AudioChainAllocFunc alloc_func;
+  gboolean allow_ip;
+
+  /* start with using dest if we can directly write into it */
+  if (convert->out_default) {
+    alloc_func = get_output_samples;
+    allow_ip = FALSE;
+  } else {
+    alloc_func = get_temp_samples;
+    allow_ip = TRUE;
+  }
+  /* now walk backwards, we try to write into the dest samples directly
+   * and keep track if the source needs to be writable */
+  for (chain = convert->pack_chain; chain; chain = chain->prev) {
+    chain->alloc_func = alloc_func;
+    chain->alloc_data = convert;
+    chain->allow_ip = allow_ip && chain->allow_ip;
+
+    if (!chain->pass_alloc) {
+      /* can't pass allocator, make new temp line allocator */
+      alloc_func = get_temp_samples;
+      allow_ip = TRUE;
+    }
+  }
+}
+
+/**
+ * gst_audio_converter_new: (skip)
+ * @in: a source #GstAudioInfo
+ * @out: a destination #GstAudioInfo
+ * @config: (transfer full): a #GstStructure with configuration options
+ *
+ * Create a new #GstAudioConverter that is able to convert between @in and @out
+ * audio formats.
+ *
+ * @config contains extra configuration options, see #GST_VIDEO_CONVERTER_OPT_*
+ * parameters for details about the options and values.
+ *
+ * Returns: a #GstAudioConverter or %NULL if conversion is not possible.
+ */
+GstAudioConverter *
+gst_audio_converter_new (GstAudioInfo * in, GstAudioInfo * out,
+    GstStructure * config)
+{
+  GstAudioConverter *convert;
+  AudioChain *prev;
+
+  g_return_val_if_fail (in != NULL, FALSE);
+  g_return_val_if_fail (out != NULL, FALSE);
+  g_return_val_if_fail (in->rate == out->rate, FALSE);
+  g_return_val_if_fail (in->layout == GST_AUDIO_LAYOUT_INTERLEAVED, FALSE);
+  g_return_val_if_fail (in->layout == out->layout, FALSE);
+
+  if ((GST_AUDIO_INFO_CHANNELS (in) != GST_AUDIO_INFO_CHANNELS (out)) &&
+      (GST_AUDIO_INFO_IS_UNPOSITIONED (in)
+          || GST_AUDIO_INFO_IS_UNPOSITIONED (out)))
+    goto unpositioned;
+
+  convert = g_slice_new0 (GstAudioConverter);
+
+  convert->in = *in;
+  convert->out = *out;
+
+  /* default config */
+  convert->config = gst_structure_new_empty ("GstAudioConverter");
+  if (config)
+    gst_audio_converter_set_config (convert, config);
+
+  GST_INFO ("unitsizes: %d -> %d", in->bpf, out->bpf);
+
+  /* step 1, unpack */
+  prev = chain_unpack (convert);
+  /* step 2, optional convert from S32 to F64 for channel mix */
+  prev = chain_convert_in (convert, prev);
+  /* step 3, channel mix */
+  prev = chain_mix (convert, prev);
+  /* step 4, optional convert for quantize */
+  prev = chain_convert_out (convert, prev);
+  /* step 5, optional quantize */
+  prev = chain_quantize (convert, prev);
+  /* step 6, pack */
+  convert->pack_chain = chain_pack (convert, prev);
+
   /* optimize */
   if (out->finfo->format == in->finfo->format && convert->mix_passthrough) {
     GST_INFO ("same formats and passthrough mixing -> passthrough");
     convert->passthrough = TRUE;
   }
 
+  setup_allocators (convert);
+
+
   return convert;
 
   /* ERRORS */
@@ -356,6 +750,17 @@ gst_audio_converter_free (GstAudioConverter * convert)
 {
   g_return_if_fail (convert != NULL);
 
+  if (convert->unpack_chain)
+    audio_chain_free (convert->unpack_chain);
+  if (convert->convert_in_chain)
+    audio_chain_free (convert->convert_in_chain);
+  if (convert->mix_chain)
+    audio_chain_free (convert->mix_chain);
+  if (convert->convert_out_chain)
+    audio_chain_free (convert->convert_out_chain);
+  if (convert->quant_chain)
+    audio_chain_free (convert->quant_chain);
+
   if (convert->quant)
     gst_audio_quantize_free (convert->quant);
   if (convert->mix)
@@ -363,14 +768,62 @@ gst_audio_converter_free (GstAudioConverter * convert)
   gst_audio_info_init (&convert->in);
   gst_audio_info_init (&convert->out);
 
-  g_free (convert->tmpbuf);
-  g_free (convert->tmpbuf2);
   gst_structure_free (convert->config);
 
   g_slice_free (GstAudioConverter, convert);
 }
 
 /**
+ * gst_audio_converter_get_out_frames:
+ * @convert: a #GstAudioConverter
+ * @in_frames: number of input frames
+ *
+ * Calculate how many output frames can be produced when @in_frames input
+ * frames are given to @convert.
+ *
+ * Returns: the number of output frames
+ */
+gsize
+gst_audio_converter_get_out_frames (GstAudioConverter * convert,
+    gsize in_frames)
+{
+  return in_frames;
+}
+
+/**
+ * gst_audio_converter_get_in_frames:
+ * @convert: a #GstAudioConverter
+ * @out_frames: number of output frames
+ *
+ * Calculate how many input frames are currently needed by @convert to produce
+ * @out_frames of output frames.
+ *
+ * Returns: the number of input frames
+ */
+gsize
+gst_audio_converter_get_in_frames (GstAudioConverter * convert,
+    gsize out_frames)
+{
+  return out_frames;
+}
+
+/**
+ * gst_audio_converter_get_max_latency:
+ * @convert: a #GstAudioConverter
+ *
+ * Get the maximum number of input frames that the converter would
+ * need before producing output.
+ *
+ * Returns: the latency of @convert as expressed in the number of
+ * frames.
+ */
+gsize
+gst_audio_converter_get_max_latency (GstAudioConverter * convert)
+{
+  return 0;
+}
+
+/**
  * gst_audio_converter_samples:
  * @convert: a #GstAudioConverter
  * @flags: extra #GstAudioConverterFlags
@@ -402,8 +855,9 @@ gst_audio_converter_samples (GstAudioConverter * convert,
     gpointer out[], gsize out_samples, gsize * in_consumed,
     gsize * out_produced)
 {
-  guint size;
-  gpointer outbuf, tmpbuf, tmpbuf2, inp, outp;
+  AudioChain *chain;
+  gpointer *tmp;
+  gint i;
 
   g_return_val_if_fail (convert != NULL, FALSE);
   g_return_val_if_fail (in != NULL, FALSE);
@@ -419,93 +873,31 @@ gst_audio_converter_samples (GstAudioConverter * convert,
     return TRUE;
   }
 
-  inp = in[0];
-  outp = out[0];
+  chain = convert->pack_chain;
 
   if (convert->passthrough) {
-    memcpy (outp, inp, in_samples * convert->in.bpf);
+    for (i = 0; i < chain->blocks; i++)
+      memcpy (out[i], in[i], in_samples * chain->inc);
     *out_produced = in_samples;
     *in_consumed = in_samples;
     return TRUE;
   }
 
-  size =
-      sizeof (gdouble) * in_samples * MAX (convert->in.channels,
-      convert->out.channels);
-
-  if (size > convert->tmpbufsize) {
-    convert->tmpbuf = g_realloc (convert->tmpbuf, size);
-    convert->tmpbuf2 = g_realloc (convert->tmpbuf2, size);
-    convert->tmpbufsize = size;
-  }
-  tmpbuf = convert->tmpbuf;
-  tmpbuf2 = convert->tmpbuf2;
-
-  /* 1. unpack */
-  if (!convert->in_default) {
-    if (!convert->convert_in && convert->mix_passthrough
-        && !convert->convert_out && !convert->quant && convert->out_default)
-      outbuf = outp;
-    else
-      outbuf = tmpbuf;
-
-    convert->in.finfo->unpack_func (convert->in.finfo,
-        GST_AUDIO_PACK_FLAG_TRUNCATE_RANGE, outbuf, inp,
-        in_samples * convert->in.channels);
-    inp = outbuf;
-  }
-
-  /* 2. optionally convert for mixing */
-  if (convert->convert_in) {
-    if (convert->mix_passthrough && !convert->convert_out && !convert->quant
-        && convert->out_default)
-      outbuf = outp;
-    else if (inp == tmpbuf)
-      outbuf = tmpbuf2;
-    else
-      outbuf = tmpbuf;
-
-    convert->convert_in (outbuf, inp, in_samples * convert->in.channels);
-    inp = outbuf;
-  }
-
-  /* step 3, channel mix if not passthrough */
-  if (!convert->mix_passthrough) {
-    if (!convert->convert_out && !convert->quant && convert->out_default)
-      outbuf = outp;
-    else
-      outbuf = tmpbuf;
-
-    gst_audio_channel_mix_samples (convert->mix, &inp, &outbuf, in_samples);
-    inp = outbuf;
-  }
-  /* step 4, optional convert F64 -> S32 for quantize */
-  if (convert->convert_out) {
-    if (!convert->quant && convert->out_default)
-      outbuf = outp;
-    else
-      outbuf = tmpbuf;
-
-    convert->convert_out (outbuf, inp, in_samples * convert->out.channels);
-    inp = outbuf;
-  }
-
-  /* step 5, optional quantize */
-  if (convert->quant) {
-    if (convert->out_default)
-      outbuf = outp;
-    else
-      outbuf = tmpbuf;
+  convert->flags = flags;
+  convert->in_data = in;
+  convert->out_data = out;
 
-    gst_audio_quantize_samples (convert->quant, &inp, &outbuf, in_samples);
-    inp = outbuf;
-  }
+  /* get samples to pack */
+  tmp = audio_chain_get_samples (chain, in_samples);
 
-  /* step 6, pack */
   if (!convert->out_default) {
-    convert->out.finfo->pack_func (convert->out.finfo, 0, inp, outp,
-        in_samples * convert->out.channels);
+    GST_LOG ("pack %p, %p %" G_GSIZE_FORMAT, tmp, out, in_samples);
+    /* and pack if needed */
+    for (i = 0; i < chain->blocks; i++)
+      convert->out.finfo->pack_func (convert->out.finfo, 0, tmp[i], out[i],
+          in_samples * chain->inc);
   }
+
   *out_produced = in_samples;
   *in_consumed = in_samples;
 
index 616f247..1810293 100644 (file)
@@ -69,20 +69,27 @@ typedef enum {
   GST_AUDIO_CONVERTER_FLAG_SOURCE_WRITABLE = (1 << 0)
 } GstAudioConverterFlags;
 
-GstAudioConverter *  gst_audio_converter_new            (GstAudioInfo *in_info,
-                                                         GstAudioInfo *out_info,
-                                                         GstStructure *config);
+GstAudioConverter *  gst_audio_converter_new             (GstAudioInfo *in_info,
+                                                          GstAudioInfo *out_info,
+                                                          GstStructure *config);
 
-void                 gst_audio_converter_free           (GstAudioConverter * convert);
+void                 gst_audio_converter_free            (GstAudioConverter * convert);
 
-gboolean             gst_audio_converter_set_config     (GstAudioConverter * convert, GstStructure *config);
-const GstStructure * gst_audio_converter_get_config     (GstAudioConverter * convert);
+gboolean             gst_audio_converter_set_config      (GstAudioConverter * convert, GstStructure *config);
+const GstStructure * gst_audio_converter_get_config      (GstAudioConverter * convert);
 
+gsize                gst_audio_converter_get_out_frames  (GstAudioConverter *convert,
+                                                          gsize in_frames);
+gsize                gst_audio_converter_get_in_frames   (GstAudioConverter *convert,
+                                                          gsize out_frames);
 
-gboolean             gst_audio_converter_samples        (GstAudioConverter * convert,
-                                                         GstAudioConverterFlags flags,
-                                                         gpointer in[], gsize in_samples,
-                                                         gpointer out[], gsize out_samples,
-                                                         gsize *in_consumed, gsize *out_produced);
+gsize                gst_audio_converter_get_max_latency (GstAudioConverter *convert);
+
+
+gboolean             gst_audio_converter_samples         (GstAudioConverter * convert,
+                                                          GstAudioConverterFlags flags,
+                                                          gpointer in[], gsize in_samples,
+                                                          gpointer out[], gsize out_samples,
+                                                          gsize *in_consumed, gsize *out_produced);
 
 #endif /* __GST_AUDIO_CONVERTER_H__ */
index fa9cc91..aa71f67 100644 (file)
@@ -49,6 +49,9 @@ EXPORTS
        gst_audio_converter_flags_get_type
        gst_audio_converter_free
        gst_audio_converter_get_config
+       gst_audio_converter_get_in_frames
+       gst_audio_converter_get_max_latency
+       gst_audio_converter_get_out_frames
        gst_audio_converter_new
        gst_audio_converter_samples
        gst_audio_converter_set_config