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

Packit 971217
/* GStreamer
Packit 971217
 * Copyright (C) 2005 Wim Taymans <wim at fluendo dot com>
Packit 971217
 *           (C) 2015 Wim Taymans <wim.taymans@gmail.com>
Packit 971217
 *
Packit 971217
 * audioconverter.c: Convert audio to different audio formats automatically
Packit 971217
 *
Packit 971217
 * This library is free software; you can redistribute it and/or
Packit 971217
 * modify it under the terms of the GNU Library General Public
Packit 971217
 * License as published by the Free Software Foundation; either
Packit 971217
 * version 2 of the License, or (at your option) any later version.
Packit 971217
 *
Packit 971217
 * This library is distributed in the hope that it will be useful,
Packit 971217
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 971217
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 971217
 * Library General Public License for more details.
Packit 971217
 *
Packit 971217
 * You should have received a copy of the GNU Library General Public
Packit 971217
 * License along with this library; if not, write to the
Packit 971217
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 971217
 * Boston, MA 02110-1301, USA.
Packit 971217
 */
Packit 971217
Packit 971217
#ifdef HAVE_CONFIG_H
Packit 971217
#include "config.h"
Packit 971217
#endif
Packit 971217
Packit 971217
#include <math.h>
Packit 971217
#include <string.h>
Packit 971217
Packit 971217
#include "audio-converter.h"
Packit 971217
#include "gstaudiopack.h"
Packit 971217
Packit 971217
/**
Packit 971217
 * SECTION:audioconverter
Packit 971217
 * @title: GstAudioConverter
Packit 971217
 * @short_description: Generic audio conversion
Packit 971217
 *
Packit 971217
 * This object is used to convert audio samples from one format to another.
Packit 971217
 * The object can perform conversion of:
Packit 971217
 *
Packit 971217
 *  * audio format with optional dithering and noise shaping
Packit 971217
 *
Packit 971217
 *  * audio samplerate
Packit 971217
 *
Packit 971217
 *  * audio channels and channel layout
Packit 971217
 *
Packit 971217
 */
Packit 971217
Packit 971217
#ifndef GST_DISABLE_GST_DEBUG
Packit 971217
#define GST_CAT_DEFAULT ensure_debug_category()
Packit 971217
static GstDebugCategory *
Packit 971217
ensure_debug_category (void)
Packit 971217
{
Packit 971217
  static gsize cat_gonce = 0;
Packit 971217
Packit 971217
  if (g_once_init_enter (&cat_gonce)) {
Packit 971217
    gsize cat_done;
Packit 971217
Packit 971217
    cat_done = (gsize) _gst_debug_category_new ("audio-converter", 0,
Packit 971217
        "audio-converter object");
Packit 971217
Packit 971217
    g_once_init_leave (&cat_gonce, cat_done);
Packit 971217
  }
Packit 971217
Packit 971217
  return (GstDebugCategory *) cat_gonce;
Packit 971217
}
Packit 971217
#else
Packit 971217
#define ensure_debug_category() /* NOOP */
Packit 971217
#endif /* GST_DISABLE_GST_DEBUG */
Packit 971217
Packit 971217
typedef struct _AudioChain AudioChain;
Packit 971217
Packit 971217
typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count);
Packit 971217
typedef gboolean (*AudioConvertSamplesFunc) (GstAudioConverter * convert,
Packit 971217
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
Packit 971217
    gpointer out[], gsize out_frames);
Packit 971217
typedef void (*AudioConvertEndianFunc) (gpointer dst, const gpointer src,
Packit 971217
    gint count);
Packit 971217
Packit 971217
/*                           int/int    int/float  float/int float/float
Packit 971217
 *
Packit 971217
 *  unpack                     S32          S32         F64       F64
Packit 971217
 *  convert                               S32->F64
Packit 971217
 *  channel mix                S32          F64         F64       F64
Packit 971217
 *  convert                                           F64->S32
Packit 971217
 *  quantize                   S32                      S32
Packit 971217
 *  pack                       S32          F64         S32       F64
Packit 971217
 *
Packit 971217
 *
Packit 971217
 *  interleave
Packit 971217
 *  deinterleave
Packit 971217
 *  resample
Packit 971217
 */
Packit 971217
struct _GstAudioConverter
Packit 971217
{
Packit 971217
  GstAudioInfo in;
Packit 971217
  GstAudioInfo out;
Packit 971217
Packit 971217
  GstStructure *config;
Packit 971217
Packit 971217
  GstAudioConverterFlags flags;
Packit 971217
  GstAudioFormat current_format;
Packit 971217
  GstAudioLayout current_layout;
Packit 971217
  gint current_channels;
Packit 971217
Packit 971217
  gboolean in_writable;
Packit 971217
  gpointer *in_data;
Packit 971217
  gsize in_frames;
Packit 971217
  gpointer *out_data;
Packit 971217
  gsize out_frames;
Packit 971217
Packit 971217
  gboolean in_place;            /* the conversion can be done in place; returned by gst_audio_converter_supports_inplace() */
Packit 971217
Packit 971217
  /* unpack */
Packit 971217
  gboolean in_default;
Packit 971217
  gboolean unpack_ip;
Packit 971217
Packit 971217
  /* convert in */
Packit 971217
  AudioConvertFunc convert_in;
Packit 971217
Packit 971217
  /* channel mix */
Packit 971217
  gboolean mix_passthrough;
Packit 971217
  GstAudioChannelMixer *mix;
Packit 971217
Packit 971217
  /* resample */
Packit 971217
  GstAudioResampler *resampler;
Packit 971217
Packit 971217
  /* convert out */
Packit 971217
  AudioConvertFunc convert_out;
Packit 971217
Packit 971217
  /* quant */
Packit 971217
  GstAudioQuantize *quant;
Packit 971217
Packit 971217
  /* pack */
Packit 971217
  gboolean out_default;
Packit 971217
  AudioChain *chain_end;        /* NULL for empty chain or points to the last element in the chain */
Packit 971217
Packit 971217
  /* endian swap */
Packit 971217
  AudioConvertEndianFunc swap_endian;
Packit 971217
Packit 971217
  AudioConvertSamplesFunc convert;
Packit 971217
};
Packit 971217
Packit 971217
static GstAudioConverter *
Packit 971217
gst_audio_converter_copy (GstAudioConverter * convert)
Packit 971217
{
Packit 971217
  GstAudioConverter *res =
Packit 971217
      gst_audio_converter_new (convert->flags, &convert->in, &convert->out,
Packit 971217
      convert->config);
Packit 971217
Packit 971217
  return res;
Packit 971217
}
Packit 971217
Packit 971217
G_DEFINE_BOXED_TYPE (GstAudioConverter, gst_audio_converter,
Packit 971217
    (GBoxedCopyFunc) gst_audio_converter_copy,
Packit 971217
    (GBoxedFreeFunc) gst_audio_converter_free);
Packit 971217
Packit 971217
typedef gboolean (*AudioChainFunc) (AudioChain * chain, gpointer user_data);
Packit 971217
typedef gpointer *(*AudioChainAllocFunc) (AudioChain * chain, gsize num_samples,
Packit 971217
    gpointer user_data);
Packit 971217
Packit 971217
struct _AudioChain
Packit 971217
{
Packit 971217
  AudioChain *prev;
Packit 971217
Packit 971217
  AudioChainFunc make_func;
Packit 971217
  gpointer make_func_data;
Packit 971217
  GDestroyNotify make_func_notify;
Packit 971217
Packit 971217
  const GstAudioFormatInfo *finfo;
Packit 971217
  gint stride;
Packit 971217
  gint inc;
Packit 971217
  gint blocks;
Packit 971217
Packit 971217
  gboolean pass_alloc;
Packit 971217
  gboolean allow_ip;
Packit 971217
Packit 971217
  AudioChainAllocFunc alloc_func;
Packit 971217
  gpointer alloc_data;
Packit 971217
Packit 971217
  gpointer *tmp;
Packit 971217
  gsize allocated_samples;
Packit 971217
Packit 971217
  gpointer *samples;
Packit 971217
  gsize num_samples;
Packit 971217
};
Packit 971217
Packit 971217
static AudioChain *
Packit 971217
audio_chain_new (AudioChain * prev, GstAudioConverter * convert)
Packit 971217
{
Packit 971217
  AudioChain *chain;
Packit 971217
Packit 971217
  chain = g_slice_new0 (AudioChain);
Packit 971217
  chain->prev = prev;
Packit 971217
Packit 971217
  if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
Packit 971217
    chain->inc = 1;
Packit 971217
    chain->blocks = convert->current_channels;
Packit 971217
  } else {
Packit 971217
    chain->inc = convert->current_channels;
Packit 971217
    chain->blocks = 1;
Packit 971217
  }
Packit 971217
  chain->finfo = gst_audio_format_get_info (convert->current_format);
Packit 971217
  chain->stride = (chain->finfo->width * chain->inc) / 8;
Packit 971217
Packit 971217
  return chain;
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
audio_chain_set_make_func (AudioChain * chain,
Packit 971217
    AudioChainFunc make_func, gpointer user_data, GDestroyNotify notify)
Packit 971217
{
Packit 971217
  chain->make_func = make_func;
Packit 971217
  chain->make_func_data = user_data;
Packit 971217
  chain->make_func_notify = notify;
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
audio_chain_free (AudioChain * chain)
Packit 971217
{
Packit 971217
  GST_LOG ("free chain %p", chain);
Packit 971217
  if (chain->make_func_notify)
Packit 971217
    chain->make_func_notify (chain->make_func_data);
Packit 971217
  g_free (chain->tmp);
Packit 971217
  g_slice_free (AudioChain, chain);
Packit 971217
}
Packit 971217
Packit 971217
static gpointer *
Packit 971217
audio_chain_alloc_samples (AudioChain * chain, gsize num_samples)
Packit 971217
{
Packit 971217
  return chain->alloc_func (chain, num_samples, chain->alloc_data);
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
audio_chain_set_samples (AudioChain * chain, gpointer * samples,
Packit 971217
    gsize num_samples)
Packit 971217
{
Packit 971217
  GST_LOG ("set samples %p %" G_GSIZE_FORMAT, samples, num_samples);
Packit 971217
Packit 971217
  chain->samples = samples;
Packit 971217
  chain->num_samples = num_samples;
Packit 971217
}
Packit 971217
Packit 971217
static gpointer *
Packit 971217
audio_chain_get_samples (AudioChain * chain, gsize * avail)
Packit 971217
{
Packit 971217
  gpointer *res;
Packit 971217
Packit 971217
  while (!chain->samples)
Packit 971217
    chain->make_func (chain, chain->make_func_data);
Packit 971217
Packit 971217
  res = chain->samples;
Packit 971217
  *avail = chain->num_samples;
Packit 971217
  chain->samples = NULL;
Packit 971217
Packit 971217
  return res;
Packit 971217
}
Packit 971217
Packit 971217
/*
Packit 971217
static guint
Packit 971217
get_opt_uint (GstAudioConverter * convert, const gchar * opt, guint def)
Packit 971217
{
Packit 971217
  guint res;
Packit 971217
  if (!gst_structure_get_uint (convert->config, opt, &res))
Packit 971217
    res = def;
Packit 971217
  return res;
Packit 971217
}
Packit 971217
*/
Packit 971217
Packit 971217
static gint
Packit 971217
get_opt_enum (GstAudioConverter * convert, const gchar * opt, GType type,
Packit 971217
    gint def)
Packit 971217
{
Packit 971217
  gint res;
Packit 971217
  if (!gst_structure_get_enum (convert->config, opt, type, &res))
Packit 971217
    res = def;
Packit 971217
  return res;
Packit 971217
}
Packit 971217
Packit 971217
static const GValue *
Packit 971217
get_opt_value (GstAudioConverter * convert, const gchar * opt)
Packit 971217
{
Packit 971217
  return gst_structure_get_value (convert->config, opt);
Packit 971217
}
Packit 971217
Packit 971217
#define DEFAULT_OPT_RESAMPLER_METHOD GST_AUDIO_RESAMPLER_METHOD_BLACKMAN_NUTTALL
Packit 971217
#define DEFAULT_OPT_DITHER_METHOD GST_AUDIO_DITHER_NONE
Packit 971217
#define DEFAULT_OPT_NOISE_SHAPING_METHOD GST_AUDIO_NOISE_SHAPING_NONE
Packit 971217
#define DEFAULT_OPT_QUANTIZATION 1
Packit 971217
Packit 971217
#define GET_OPT_RESAMPLER_METHOD(c) get_opt_enum(c, \
Packit 971217
    GST_AUDIO_CONVERTER_OPT_RESAMPLER_METHOD, GST_TYPE_AUDIO_RESAMPLER_METHOD, \
Packit 971217
    DEFAULT_OPT_RESAMPLER_METHOD)
Packit 971217
#define GET_OPT_DITHER_METHOD(c) get_opt_enum(c, \
Packit 971217
    GST_AUDIO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_AUDIO_DITHER_METHOD, \
Packit 971217
    DEFAULT_OPT_DITHER_METHOD)
Packit 971217
#define GET_OPT_NOISE_SHAPING_METHOD(c) get_opt_enum(c, \
Packit 971217
    GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD, GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, \
Packit 971217
    DEFAULT_OPT_NOISE_SHAPING_METHOD)
Packit 971217
#define GET_OPT_QUANTIZATION(c) get_opt_uint(c, \
Packit 971217
    GST_AUDIO_CONVERTER_OPT_QUANTIZATION, DEFAULT_OPT_QUANTIZATION)
Packit 971217
#define GET_OPT_MIX_MATRIX(c) get_opt_value(c, \
Packit 971217
    GST_AUDIO_CONVERTER_OPT_MIX_MATRIX)
Packit 971217
Packit 971217
static gboolean
Packit 971217
copy_config (GQuark field_id, const GValue * value, gpointer user_data)
Packit 971217
{
Packit 971217
  GstAudioConverter *convert = user_data;
Packit 971217
Packit 971217
  gst_structure_id_set_value (convert->config, field_id, value);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_update_config:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 * @in_rate: input rate
Packit 971217
 * @out_rate: output rate
Packit 971217
 * @config: (transfer full) (allow-none): a #GstStructure or %NULL
Packit 971217
 *
Packit 971217
 * Set @in_rate, @out_rate and @config as extra configuration for @convert.
Packit 971217
 *
Packit 971217
 * @in_rate and @out_rate specify the new sample rates of input and output
Packit 971217
 * formats. A value of 0 leaves the sample rate unchanged.
Packit 971217
 *
Packit 971217
 * @config can be %NULL, in which case, the current configuration is not
Packit 971217
 * changed.
Packit 971217
 *
Packit 971217
 * If the parameters in @config can not be set exactly, this function returns
Packit 971217
 * %FALSE and will try to update as much state as possible. The new state can
Packit 971217
 * then be retrieved and refined with gst_audio_converter_get_config().
Packit 971217
 *
Packit 971217
 * Look at the #GST_AUDIO_CONVERTER_OPT_* fields to check valid configuration
Packit 971217
 * option and values.
Packit 971217
 *
Packit 971217
 * Returns: %TRUE when the new parameters could be set
Packit 971217
 */
Packit 971217
gboolean
Packit 971217
gst_audio_converter_update_config (GstAudioConverter * convert,
Packit 971217
    gint in_rate, gint out_rate, GstStructure * config)
Packit 971217
{
Packit 971217
  g_return_val_if_fail (convert != NULL, FALSE);
Packit 971217
  g_return_val_if_fail ((in_rate == 0 && out_rate == 0) ||
Packit 971217
      convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE, FALSE);
Packit 971217
Packit 971217
  GST_LOG ("new rate %d -> %d", in_rate, out_rate);
Packit 971217
Packit 971217
  if (in_rate <= 0)
Packit 971217
    in_rate = convert->in.rate;
Packit 971217
  if (out_rate <= 0)
Packit 971217
    out_rate = convert->out.rate;
Packit 971217
Packit 971217
  convert->in.rate = in_rate;
Packit 971217
  convert->out.rate = out_rate;
Packit 971217
Packit 971217
  if (convert->resampler)
Packit 971217
    gst_audio_resampler_update (convert->resampler, in_rate, out_rate, config);
Packit 971217
Packit 971217
  if (config) {
Packit 971217
    gst_structure_foreach (config, copy_config, convert);
Packit 971217
    gst_structure_free (config);
Packit 971217
  }
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_get_config:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 * @in_rate: (out) (optional): result input rate
Packit 971217
 * @out_rate: (out) (optional): result output rate
Packit 971217
 *
Packit 971217
 * Get the current configuration of @convert.
Packit 971217
 *
Packit 971217
 * Returns: (transfer none):
Packit 971217
 *   a #GstStructure that remains valid for as long as @convert is valid
Packit 971217
 *   or until gst_audio_converter_update_config() is called.
Packit 971217
 */
Packit 971217
const GstStructure *
Packit 971217
gst_audio_converter_get_config (GstAudioConverter * convert,
Packit 971217
    gint * in_rate, gint * out_rate)
Packit 971217
{
Packit 971217
  g_return_val_if_fail (convert != NULL, NULL);
Packit 971217
Packit 971217
  if (in_rate)
Packit 971217
    *in_rate = convert->in.rate;
Packit 971217
  if (out_rate)
Packit 971217
    *out_rate = convert->out.rate;
Packit 971217
Packit 971217
  return convert->config;
Packit 971217
}
Packit 971217
Packit 971217
static gpointer *
Packit 971217
get_output_samples (AudioChain * chain, gsize num_samples, gpointer user_data)
Packit 971217
{
Packit 971217
  GstAudioConverter *convert = user_data;
Packit 971217
Packit 971217
  GST_LOG ("output samples %p %" G_GSIZE_FORMAT, convert->out_data,
Packit 971217
      num_samples);
Packit 971217
Packit 971217
  return convert->out_data;
Packit 971217
}
Packit 971217
Packit 971217
#define MEM_ALIGN(m,a) ((gint8 *)((guintptr)((gint8 *)(m) + ((a)-1)) & ~((a)-1)))
Packit 971217
#define ALIGN 16
Packit 971217
Packit 971217
static gpointer *
Packit 971217
get_temp_samples (AudioChain * chain, gsize num_samples, gpointer user_data)
Packit 971217
{
Packit 971217
  if (num_samples > chain->allocated_samples) {
Packit 971217
    gint i;
Packit 971217
    gint8 *s;
Packit 971217
    gsize stride = GST_ROUND_UP_N (num_samples * chain->stride, ALIGN);
Packit 971217
    /* first part contains the pointers, second part the data, add some extra bytes
Packit 971217
     * for alignement */
Packit 971217
    gsize needed = (stride + sizeof (gpointer)) * chain->blocks + ALIGN - 1;
Packit 971217
Packit 971217
    GST_DEBUG ("alloc samples %d %" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT,
Packit 971217
        chain->stride, num_samples, needed);
Packit 971217
    chain->tmp = g_realloc (chain->tmp, needed);
Packit 971217
    chain->allocated_samples = num_samples;
Packit 971217
Packit 971217
    /* pointer to the data, make sure it's 16 bytes aligned */
Packit 971217
    s = MEM_ALIGN (&chain->tmp[chain->blocks], ALIGN);
Packit 971217
Packit 971217
    /* set up the pointers */
Packit 971217
    for (i = 0; i < chain->blocks; i++)
Packit 971217
      chain->tmp[i] = s + i * stride;
Packit 971217
  }
Packit 971217
  GST_LOG ("temp samples %p %" G_GSIZE_FORMAT, chain->tmp, num_samples);
Packit 971217
Packit 971217
  return chain->tmp;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
do_unpack (AudioChain * chain, gpointer user_data)
Packit 971217
{
Packit 971217
  GstAudioConverter *convert = user_data;
Packit 971217
  gsize num_samples;
Packit 971217
  gpointer *tmp;
Packit 971217
  gboolean in_writable;
Packit 971217
Packit 971217
  in_writable = convert->in_writable;
Packit 971217
  num_samples = convert->in_frames;
Packit 971217
Packit 971217
  if (!chain->allow_ip || !in_writable || !convert->in_default) {
Packit 971217
    gint i;
Packit 971217
Packit 971217
    if (in_writable && chain->allow_ip) {
Packit 971217
      tmp = convert->in_data;
Packit 971217
      GST_LOG ("unpack in-place %p, %" G_GSIZE_FORMAT, tmp, num_samples);
Packit 971217
    } else {
Packit 971217
      tmp = audio_chain_alloc_samples (chain, num_samples);
Packit 971217
      GST_LOG ("unpack to tmp %p, %" G_GSIZE_FORMAT, tmp, num_samples);
Packit 971217
    }
Packit 971217
Packit 971217
    if (convert->in_data) {
Packit 971217
      for (i = 0; i < chain->blocks; i++) {
Packit 971217
        if (convert->in_default) {
Packit 971217
          GST_LOG ("copy %p, %p, %" G_GSIZE_FORMAT, tmp[i], convert->in_data[i],
Packit 971217
              num_samples);
Packit 971217
          memcpy (tmp[i], convert->in_data[i], num_samples * chain->stride);
Packit 971217
        } else {
Packit 971217
          GST_LOG ("unpack %p, %p, %" G_GSIZE_FORMAT, tmp[i],
Packit 971217
              convert->in_data[i], num_samples);
Packit 971217
          convert->in.finfo->unpack_func (convert->in.finfo,
Packit 971217
              GST_AUDIO_PACK_FLAG_TRUNCATE_RANGE, tmp[i], convert->in_data[i],
Packit 971217
              num_samples * chain->inc);
Packit 971217
        }
Packit 971217
      }
Packit 971217
    } else {
Packit 971217
      for (i = 0; i < chain->blocks; i++) {
Packit 971217
        gst_audio_format_fill_silence (chain->finfo, tmp[i],
Packit 971217
            num_samples * chain->inc);
Packit 971217
      }
Packit 971217
    }
Packit 971217
  } else {
Packit 971217
    tmp = convert->in_data;
Packit 971217
    GST_LOG ("get in samples %p", tmp);
Packit 971217
  }
Packit 971217
  audio_chain_set_samples (chain, tmp, num_samples);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
do_convert_in (AudioChain * chain, gpointer user_data)
Packit 971217
{
Packit 971217
  gsize num_samples;
Packit 971217
  GstAudioConverter *convert = user_data;
Packit 971217
  gpointer *in, *out;
Packit 971217
  gint i;
Packit 971217
Packit 971217
  in = audio_chain_get_samples (chain->prev, &num_samples);
Packit 971217
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
Packit 971217
  GST_LOG ("convert in %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples);
Packit 971217
Packit 971217
  for (i = 0; i < chain->blocks; i++)
Packit 971217
    convert->convert_in (out[i], in[i], num_samples * chain->inc);
Packit 971217
Packit 971217
  audio_chain_set_samples (chain, out, num_samples);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
do_mix (AudioChain * chain, gpointer user_data)
Packit 971217
{
Packit 971217
  gsize num_samples;
Packit 971217
  GstAudioConverter *convert = user_data;
Packit 971217
  gpointer *in, *out;
Packit 971217
Packit 971217
  in = audio_chain_get_samples (chain->prev, &num_samples);
Packit 971217
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
Packit 971217
  GST_LOG ("mix %p, %p, %" G_GSIZE_FORMAT, in, out, num_samples);
Packit 971217
Packit 971217
  gst_audio_channel_mixer_samples (convert->mix, in, out, num_samples);
Packit 971217
Packit 971217
  audio_chain_set_samples (chain, out, num_samples);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
do_resample (AudioChain * chain, gpointer user_data)
Packit 971217
{
Packit 971217
  GstAudioConverter *convert = user_data;
Packit 971217
  gpointer *in, *out;
Packit 971217
  gsize in_frames, out_frames;
Packit 971217
Packit 971217
  in = audio_chain_get_samples (chain->prev, &in_frames);
Packit 971217
  out_frames = convert->out_frames;
Packit 971217
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, out_frames));
Packit 971217
Packit 971217
  GST_LOG ("resample %p %p,%" G_GSIZE_FORMAT " %" G_GSIZE_FORMAT, in,
Packit 971217
      out, in_frames, out_frames);
Packit 971217
Packit 971217
  gst_audio_resampler_resample (convert->resampler, in, in_frames, out,
Packit 971217
      out_frames);
Packit 971217
Packit 971217
  audio_chain_set_samples (chain, out, out_frames);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
do_convert_out (AudioChain * chain, gpointer user_data)
Packit 971217
{
Packit 971217
  GstAudioConverter *convert = user_data;
Packit 971217
  gsize num_samples;
Packit 971217
  gpointer *in, *out;
Packit 971217
  gint i;
Packit 971217
Packit 971217
  in = audio_chain_get_samples (chain->prev, &num_samples);
Packit 971217
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
Packit 971217
  GST_LOG ("convert out %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
Packit 971217
Packit 971217
  for (i = 0; i < chain->blocks; i++)
Packit 971217
    convert->convert_out (out[i], in[i], num_samples * chain->inc);
Packit 971217
Packit 971217
  audio_chain_set_samples (chain, out, num_samples);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
do_quantize (AudioChain * chain, gpointer user_data)
Packit 971217
{
Packit 971217
  GstAudioConverter *convert = user_data;
Packit 971217
  gsize num_samples;
Packit 971217
  gpointer *in, *out;
Packit 971217
Packit 971217
  in = audio_chain_get_samples (chain->prev, &num_samples);
Packit 971217
  out = (chain->allow_ip ? in : audio_chain_alloc_samples (chain, num_samples));
Packit 971217
  GST_LOG ("quantize %p, %p %" G_GSIZE_FORMAT, in, out, num_samples);
Packit 971217
Packit 971217
  gst_audio_quantize_samples (convert->quant, in, out, num_samples);
Packit 971217
Packit 971217
  audio_chain_set_samples (chain, out, num_samples);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
is_intermediate_format (GstAudioFormat format)
Packit 971217
{
Packit 971217
  return (format == GST_AUDIO_FORMAT_S16 ||
Packit 971217
      format == GST_AUDIO_FORMAT_S32 ||
Packit 971217
      format == GST_AUDIO_FORMAT_F32 || format == GST_AUDIO_FORMAT_F64);
Packit 971217
}
Packit 971217
Packit 971217
static AudioChain *
Packit 971217
chain_unpack (GstAudioConverter * convert)
Packit 971217
{
Packit 971217
  AudioChain *prev;
Packit 971217
  GstAudioInfo *in = &convert->in;
Packit 971217
  GstAudioInfo *out = &convert->out;
Packit 971217
  gboolean same_format;
Packit 971217
Packit 971217
  same_format = in->finfo->format == out->finfo->format;
Packit 971217
Packit 971217
  /* do not unpack if we have the same input format as the output format
Packit 971217
   * and it is a possible intermediate format */
Packit 971217
  if (same_format && is_intermediate_format (in->finfo->format)) {
Packit 971217
    convert->current_format = in->finfo->format;
Packit 971217
  } else {
Packit 971217
    convert->current_format = in->finfo->unpack_format;
Packit 971217
  }
Packit 971217
  convert->current_layout = in->layout;
Packit 971217
  convert->current_channels = in->channels;
Packit 971217
Packit 971217
  convert->in_default = convert->current_format == in->finfo->format;
Packit 971217
Packit 971217
  GST_INFO ("unpack format %s to %s",
Packit 971217
      gst_audio_format_to_string (in->finfo->format),
Packit 971217
      gst_audio_format_to_string (convert->current_format));
Packit 971217
Packit 971217
  prev = audio_chain_new (NULL, convert);
Packit 971217
  prev->allow_ip = prev->finfo->width <= in->finfo->width;
Packit 971217
  prev->pass_alloc = FALSE;
Packit 971217
  audio_chain_set_make_func (prev, do_unpack, convert, NULL);
Packit 971217
Packit 971217
  return prev;
Packit 971217
}
Packit 971217
Packit 971217
static AudioChain *
Packit 971217
chain_convert_in (GstAudioConverter * convert, AudioChain * prev)
Packit 971217
{
Packit 971217
  gboolean in_int, out_int;
Packit 971217
  GstAudioInfo *in = &convert->in;
Packit 971217
  GstAudioInfo *out = &convert->out;
Packit 971217
Packit 971217
  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
Packit 971217
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
Packit 971217
Packit 971217
  if (in_int && !out_int) {
Packit 971217
    GST_INFO ("convert S32 to F64");
Packit 971217
    convert->convert_in = (AudioConvertFunc) audio_orc_s32_to_double;
Packit 971217
    convert->current_format = GST_AUDIO_FORMAT_F64;
Packit 971217
Packit 971217
    prev = audio_chain_new (prev, convert);
Packit 971217
    prev->allow_ip = FALSE;
Packit 971217
    prev->pass_alloc = FALSE;
Packit 971217
    audio_chain_set_make_func (prev, do_convert_in, convert, NULL);
Packit 971217
  }
Packit 971217
  return prev;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
check_mix_matrix (guint in_channels, guint out_channels, const GValue * value)
Packit 971217
{
Packit 971217
  guint i, j;
Packit 971217
Packit 971217
  /* audio-channel-mixer will generate an identity matrix */
Packit 971217
  if (gst_value_array_get_size (value) == 0)
Packit 971217
    return TRUE;
Packit 971217
Packit 971217
  if (gst_value_array_get_size (value) != out_channels) {
Packit 971217
    GST_ERROR ("Invalid mix matrix size, should be %d", out_channels);
Packit 971217
    goto fail;
Packit 971217
  }
Packit 971217
Packit 971217
  for (j = 0; j < out_channels; j++) {
Packit 971217
    const GValue *row = gst_value_array_get_value (value, j);
Packit 971217
Packit 971217
    if (gst_value_array_get_size (row) != in_channels) {
Packit 971217
      GST_ERROR ("Invalid mix matrix row size, should be %d", in_channels);
Packit 971217
      goto fail;
Packit 971217
    }
Packit 971217
Packit 971217
    for (i = 0; i < in_channels; i++) {
Packit 971217
      const GValue *itm;
Packit 971217
Packit 971217
      itm = gst_value_array_get_value (row, i);
Packit 971217
      if (!G_VALUE_HOLDS_FLOAT (itm)) {
Packit 971217
        GST_ERROR ("Invalid mix matrix element type, should be float");
Packit 971217
        goto fail;
Packit 971217
      }
Packit 971217
    }
Packit 971217
  }
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
Packit 971217
fail:
Packit 971217
  return FALSE;
Packit 971217
}
Packit 971217
Packit 971217
static gfloat **
Packit 971217
mix_matrix_from_g_value (guint in_channels, guint out_channels,
Packit 971217
    const GValue * value)
Packit 971217
{
Packit 971217
  guint i, j;
Packit 971217
  gfloat **matrix = g_new (gfloat *, in_channels);
Packit 971217
Packit 971217
  for (i = 0; i < in_channels; i++)
Packit 971217
    matrix[i] = g_new (gfloat, out_channels);
Packit 971217
Packit 971217
  for (j = 0; j < out_channels; j++) {
Packit 971217
    const GValue *row = gst_value_array_get_value (value, j);
Packit 971217
Packit 971217
    for (i = 0; i < in_channels; i++) {
Packit 971217
      const GValue *itm;
Packit 971217
      gfloat coefficient;
Packit 971217
Packit 971217
      itm = gst_value_array_get_value (row, i);
Packit 971217
      coefficient = g_value_get_float (itm);
Packit 971217
      matrix[i][j] = coefficient;
Packit 971217
    }
Packit 971217
  }
Packit 971217
Packit 971217
  return matrix;
Packit 971217
}
Packit 971217
Packit 971217
static AudioChain *
Packit 971217
chain_mix (GstAudioConverter * convert, AudioChain * prev)
Packit 971217
{
Packit 971217
  GstAudioInfo *in = &convert->in;
Packit 971217
  GstAudioInfo *out = &convert->out;
Packit 971217
  GstAudioFormat format = convert->current_format;
Packit 971217
  const GValue *opt_matrix = GET_OPT_MIX_MATRIX (convert);
Packit 971217
Packit 971217
  convert->current_channels = out->channels;
Packit 971217
Packit 971217
  if (opt_matrix) {
Packit 971217
    gfloat **matrix = NULL;
Packit 971217
Packit 971217
    if (gst_value_array_get_size (opt_matrix))
Packit 971217
      matrix =
Packit 971217
          mix_matrix_from_g_value (in->channels, out->channels, opt_matrix);
Packit 971217
Packit 971217
    convert->mix =
Packit 971217
        gst_audio_channel_mixer_new_with_matrix (0, format, in->channels,
Packit 971217
        out->channels, matrix);
Packit 971217
  } else {
Packit 971217
    GstAudioChannelMixerFlags flags;
Packit 971217
Packit 971217
    flags =
Packit 971217
        GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
Packit 971217
        GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_IN : 0;
Packit 971217
    flags |=
Packit 971217
        GST_AUDIO_INFO_IS_UNPOSITIONED (out) ?
Packit 971217
        GST_AUDIO_CHANNEL_MIXER_FLAGS_UNPOSITIONED_OUT : 0;
Packit 971217
Packit 971217
    convert->mix =
Packit 971217
        gst_audio_channel_mixer_new (flags, format, in->channels, in->position,
Packit 971217
        out->channels, out->position);
Packit 971217
  }
Packit 971217
Packit 971217
  convert->mix_passthrough =
Packit 971217
      gst_audio_channel_mixer_is_passthrough (convert->mix);
Packit 971217
  GST_INFO ("mix format %s, passthrough %d, in_channels %d, out_channels %d",
Packit 971217
      gst_audio_format_to_string (format), convert->mix_passthrough,
Packit 971217
      in->channels, out->channels);
Packit 971217
Packit 971217
  if (!convert->mix_passthrough) {
Packit 971217
    prev = audio_chain_new (prev, convert);
Packit 971217
    prev->allow_ip = FALSE;
Packit 971217
    prev->pass_alloc = FALSE;
Packit 971217
    audio_chain_set_make_func (prev, do_mix, convert, NULL);
Packit 971217
  }
Packit 971217
  return prev;
Packit 971217
}
Packit 971217
Packit 971217
static AudioChain *
Packit 971217
chain_resample (GstAudioConverter * convert, AudioChain * prev)
Packit 971217
{
Packit 971217
  GstAudioInfo *in = &convert->in;
Packit 971217
  GstAudioInfo *out = &convert->out;
Packit 971217
  GstAudioResamplerMethod method;
Packit 971217
  GstAudioResamplerFlags flags;
Packit 971217
  GstAudioFormat format = convert->current_format;
Packit 971217
  gint channels = convert->current_channels;
Packit 971217
  gboolean variable_rate;
Packit 971217
Packit 971217
  variable_rate = convert->flags & GST_AUDIO_CONVERTER_FLAG_VARIABLE_RATE;
Packit 971217
Packit 971217
  if (in->rate != out->rate || variable_rate) {
Packit 971217
    method = GET_OPT_RESAMPLER_METHOD (convert);
Packit 971217
Packit 971217
    flags = 0;
Packit 971217
    if (convert->current_layout == GST_AUDIO_LAYOUT_NON_INTERLEAVED) {
Packit 971217
      flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_IN;
Packit 971217
      flags |= GST_AUDIO_RESAMPLER_FLAG_NON_INTERLEAVED_OUT;
Packit 971217
    }
Packit 971217
    if (variable_rate)
Packit 971217
      flags |= GST_AUDIO_RESAMPLER_FLAG_VARIABLE_RATE;
Packit 971217
Packit 971217
    convert->resampler =
Packit 971217
        gst_audio_resampler_new (method, flags, format, channels, in->rate,
Packit 971217
        out->rate, convert->config);
Packit 971217
Packit 971217
    prev = audio_chain_new (prev, convert);
Packit 971217
    prev->allow_ip = FALSE;
Packit 971217
    prev->pass_alloc = FALSE;
Packit 971217
    audio_chain_set_make_func (prev, do_resample, convert, NULL);
Packit 971217
  }
Packit 971217
  return prev;
Packit 971217
}
Packit 971217
Packit 971217
static AudioChain *
Packit 971217
chain_convert_out (GstAudioConverter * convert, AudioChain * prev)
Packit 971217
{
Packit 971217
  gboolean in_int, out_int;
Packit 971217
  GstAudioInfo *in = &convert->in;
Packit 971217
  GstAudioInfo *out = &convert->out;
Packit 971217
Packit 971217
  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo);
Packit 971217
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
Packit 971217
Packit 971217
  if (!in_int && out_int) {
Packit 971217
    convert->convert_out = (AudioConvertFunc) audio_orc_double_to_s32;
Packit 971217
    convert->current_format = GST_AUDIO_FORMAT_S32;
Packit 971217
Packit 971217
    GST_INFO ("convert F64 to S32");
Packit 971217
    prev = audio_chain_new (prev, convert);
Packit 971217
    prev->allow_ip = TRUE;
Packit 971217
    prev->pass_alloc = FALSE;
Packit 971217
    audio_chain_set_make_func (prev, do_convert_out, convert, NULL);
Packit 971217
  }
Packit 971217
  return prev;
Packit 971217
}
Packit 971217
Packit 971217
static AudioChain *
Packit 971217
chain_quantize (GstAudioConverter * convert, AudioChain * prev)
Packit 971217
{
Packit 971217
  const GstAudioFormatInfo *cur_finfo;
Packit 971217
  GstAudioInfo *out = &convert->out;
Packit 971217
  gint in_depth, out_depth;
Packit 971217
  gboolean in_int, out_int;
Packit 971217
  GstAudioDitherMethod dither;
Packit 971217
  GstAudioNoiseShapingMethod ns;
Packit 971217
Packit 971217
  dither = GET_OPT_DITHER_METHOD (convert);
Packit 971217
  ns = GET_OPT_NOISE_SHAPING_METHOD (convert);
Packit 971217
Packit 971217
  cur_finfo = gst_audio_format_get_info (convert->current_format);
Packit 971217
Packit 971217
  in_depth = GST_AUDIO_FORMAT_INFO_DEPTH (cur_finfo);
Packit 971217
  out_depth = GST_AUDIO_FORMAT_INFO_DEPTH (out->finfo);
Packit 971217
  GST_INFO ("depth in %d, out %d", in_depth, out_depth);
Packit 971217
Packit 971217
  in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (cur_finfo);
Packit 971217
  out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo);
Packit 971217
Packit 971217
  /* Don't dither or apply noise shaping if target depth is bigger than 20 bits
Packit 971217
   * as DA converters only can do a SNR up to 20 bits in reality.
Packit 971217
   * Also don't dither or apply noise shaping if target depth is larger than
Packit 971217
   * source depth. */
Packit 971217
  if (out_depth > 20 || (in_int && out_depth >= in_depth)) {
Packit 971217
    dither = GST_AUDIO_DITHER_NONE;
Packit 971217
    ns = GST_AUDIO_NOISE_SHAPING_NONE;
Packit 971217
    GST_INFO ("using no dither and noise shaping");
Packit 971217
  } else {
Packit 971217
    GST_INFO ("using dither %d and noise shaping %d", dither, ns);
Packit 971217
    /* Use simple error feedback when output sample rate is smaller than
Packit 971217
     * 32000 as the other methods might move the noise to audible ranges */
Packit 971217
    if (ns > GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000)
Packit 971217
      ns = GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK;
Packit 971217
  }
Packit 971217
  /* we still want to run the quantization step when reducing bits to get
Packit 971217
   * the rounding correct */
Packit 971217
  if (out_int && out_depth < 32
Packit 971217
      && convert->current_format == GST_AUDIO_FORMAT_S32) {
Packit 971217
    GST_INFO ("quantize to %d bits, dither %d, ns %d", out_depth, dither, ns);
Packit 971217
    convert->quant =
Packit 971217
        gst_audio_quantize_new (dither, ns, 0, convert->current_format,
Packit 971217
        out->channels, 1U << (32 - out_depth));
Packit 971217
Packit 971217
    prev = audio_chain_new (prev, convert);
Packit 971217
    prev->allow_ip = TRUE;
Packit 971217
    prev->pass_alloc = TRUE;
Packit 971217
    audio_chain_set_make_func (prev, do_quantize, convert, NULL);
Packit 971217
  }
Packit 971217
  return prev;
Packit 971217
}
Packit 971217
Packit 971217
static AudioChain *
Packit 971217
chain_pack (GstAudioConverter * convert, AudioChain * prev)
Packit 971217
{
Packit 971217
  GstAudioInfo *out = &convert->out;
Packit 971217
  GstAudioFormat format = convert->current_format;
Packit 971217
Packit 971217
  convert->current_format = out->finfo->format;
Packit 971217
Packit 971217
  convert->out_default = format == out->finfo->format;
Packit 971217
  GST_INFO ("pack format %s to %s", gst_audio_format_to_string (format),
Packit 971217
      gst_audio_format_to_string (out->finfo->format));
Packit 971217
Packit 971217
  return prev;
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
setup_allocators (GstAudioConverter * convert)
Packit 971217
{
Packit 971217
  AudioChain *chain;
Packit 971217
  AudioChainAllocFunc alloc_func;
Packit 971217
  gboolean allow_ip;
Packit 971217
Packit 971217
  /* start with using dest if we can directly write into it */
Packit 971217
  if (convert->out_default) {
Packit 971217
    alloc_func = get_output_samples;
Packit 971217
    allow_ip = FALSE;
Packit 971217
  } else {
Packit 971217
    alloc_func = get_temp_samples;
Packit 971217
    allow_ip = TRUE;
Packit 971217
  }
Packit 971217
  /* now walk backwards, we try to write into the dest samples directly
Packit 971217
   * and keep track if the source needs to be writable */
Packit 971217
  for (chain = convert->chain_end; chain; chain = chain->prev) {
Packit 971217
    chain->alloc_func = alloc_func;
Packit 971217
    chain->alloc_data = convert;
Packit 971217
    chain->allow_ip = allow_ip && chain->allow_ip;
Packit 971217
    GST_LOG ("chain %p: %d %d", chain, allow_ip, chain->allow_ip);
Packit 971217
Packit 971217
    if (!chain->pass_alloc) {
Packit 971217
      /* can't pass allocator, make new temp line allocator */
Packit 971217
      alloc_func = get_temp_samples;
Packit 971217
      allow_ip = TRUE;
Packit 971217
    }
Packit 971217
  }
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
converter_passthrough (GstAudioConverter * convert,
Packit 971217
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
Packit 971217
    gpointer out[], gsize out_frames)
Packit 971217
{
Packit 971217
  gint i;
Packit 971217
  AudioChain *chain;
Packit 971217
  gsize samples;
Packit 971217
Packit 971217
  /* in-place passthrough -> do nothing */
Packit 971217
  if (in == out) {
Packit 971217
    g_assert (convert->in_place);
Packit 971217
    return TRUE;
Packit 971217
  }
Packit 971217
Packit 971217
  chain = convert->chain_end;
Packit 971217
Packit 971217
  samples = in_frames * chain->inc;
Packit 971217
Packit 971217
  GST_LOG ("passthrough: %" G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT " samples",
Packit 971217
      in_frames, samples);
Packit 971217
Packit 971217
  if (in) {
Packit 971217
    gsize bytes;
Packit 971217
Packit 971217
    bytes = samples * (convert->in.bpf / convert->in.channels);
Packit 971217
Packit 971217
    for (i = 0; i < chain->blocks; i++) {
Packit 971217
      if (out[i] == in[i]) {
Packit 971217
        g_assert (convert->in_place);
Packit 971217
        continue;
Packit 971217
      }
Packit 971217
Packit 971217
      memcpy (out[i], in[i], bytes);
Packit 971217
    }
Packit 971217
  } else {
Packit 971217
    for (i = 0; i < chain->blocks; i++)
Packit 971217
      gst_audio_format_fill_silence (convert->in.finfo, out[i], samples);
Packit 971217
  }
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
/* perform LE<->BE conversion on a block of @count 16-bit samples
Packit 971217
 * dst may equal src for in-place conversion
Packit 971217
 */
Packit 971217
static void
Packit 971217
converter_swap_endian_16 (gpointer dst, const gpointer src, gint count)
Packit 971217
{
Packit 971217
  guint16 *out = dst;
Packit 971217
  const guint16 *in = src;
Packit 971217
  gint i;
Packit 971217
Packit 971217
  for (i = 0; i < count; i++)
Packit 971217
    out[i] = GUINT16_SWAP_LE_BE (in[i]);
Packit 971217
}
Packit 971217
Packit 971217
/* perform LE<->BE conversion on a block of @count 24-bit samples
Packit 971217
 * dst may equal src for in-place conversion
Packit 971217
 *
Packit 971217
 * naive algorithm, which performs better with -O3 and worse with -O2
Packit 971217
 * than the commented out optimized algorithm below
Packit 971217
 */
Packit 971217
static void
Packit 971217
converter_swap_endian_24 (gpointer dst, const gpointer src, gint count)
Packit 971217
{
Packit 971217
  guint8 *out = dst;
Packit 971217
  const guint8 *in = src;
Packit 971217
  gint i;
Packit 971217
Packit 971217
  count *= 3;
Packit 971217
Packit 971217
  for (i = 0; i < count; i += 3) {
Packit 971217
    guint8 x = in[i + 0];
Packit 971217
    out[i + 0] = in[i + 2];
Packit 971217
    out[i + 1] = in[i + 1];
Packit 971217
    out[i + 2] = x;
Packit 971217
  }
Packit 971217
}
Packit 971217
Packit 971217
/* the below code performs better with -O2 but worse with -O3 */
Packit 971217
#if 0
Packit 971217
/* perform LE<->BE conversion on a block of @count 24-bit samples
Packit 971217
 * dst may equal src for in-place conversion
Packit 971217
 *
Packit 971217
 * assumes that dst and src are 32-bit aligned
Packit 971217
 */
Packit 971217
static void
Packit 971217
converter_swap_endian_24 (gpointer dst, const gpointer src, gint count)
Packit 971217
{
Packit 971217
  guint32 *out = dst;
Packit 971217
  const guint32 *in = src;
Packit 971217
  guint8 *out8;
Packit 971217
  const guint8 *in8;
Packit 971217
  gint i;
Packit 971217
Packit 971217
  /* first convert 24-bit samples in multiples of 4 reading 3x 32-bits in one cycle
Packit 971217
   *
Packit 971217
   * input:               A1 B1 C1 A2 , B2 C2 A3 B3 , C3 A4 B4 C4
Packit 971217
   * 32-bit endian swap:  A2 C1 B1 A1 , B3 A3 C2 B2 , C4 B4 A4 C3
Packit 971217
   *                      <--  x  -->   <--  y  --> , <--  z  -->
Packit 971217
   *
Packit 971217
   * desired output:      C1 B1 A1 C2 , B2 A2 C3 B3 , A3 C4 B4 A4
Packit 971217
   */
Packit 971217
  for (i = 0; i < count / 4; i++, in += 3, out += 3) {
Packit 971217
    guint32 x, y, z;
Packit 971217
Packit 971217
    x = GUINT32_SWAP_LE_BE (in[0]);
Packit 971217
    y = GUINT32_SWAP_LE_BE (in[1]);
Packit 971217
    z = GUINT32_SWAP_LE_BE (in[2]);
Packit 971217
Packit 971217
#if G_BYTE_ORDER == G_BIG_ENDIAN
Packit 971217
    out[0] = (x << 8) + ((y >> 8) & 0xff);
Packit 971217
    out[1] = (in[1] & 0xff0000ff) + ((x >> 8) & 0xff0000) + ((z << 8) & 0xff00);
Packit 971217
    out[2] = (z >> 8) + ((y << 8) & 0xff000000);
Packit 971217
#else
Packit 971217
    out[0] = (x >> 8) + ((y << 8) & 0xff000000);
Packit 971217
    out[1] = (in[1] & 0xff0000ff) + ((x << 8) & 0xff00) + ((z >> 8) & 0xff0000);
Packit 971217
    out[2] = (z << 8) + ((y >> 8) & 0xff);
Packit 971217
#endif
Packit 971217
  }
Packit 971217
Packit 971217
  /* convert the remainder less efficiently */
Packit 971217
  for (out8 = (guint8 *) out, in8 = (const guint8 *) in, i = 0; i < (count & 3);
Packit 971217
      i++) {
Packit 971217
    guint8 x = in8[i + 0];
Packit 971217
    out8[i + 0] = in8[i + 2];
Packit 971217
    out8[i + 1] = in8[i + 1];
Packit 971217
    out8[i + 2] = x;
Packit 971217
  }
Packit 971217
}
Packit 971217
#endif
Packit 971217
Packit 971217
/* perform LE<->BE conversion on a block of @count 32-bit samples
Packit 971217
 * dst may equal src for in-place conversion
Packit 971217
 */
Packit 971217
static void
Packit 971217
converter_swap_endian_32 (gpointer dst, const gpointer src, gint count)
Packit 971217
{
Packit 971217
  guint32 *out = dst;
Packit 971217
  const guint32 *in = src;
Packit 971217
  gint i;
Packit 971217
Packit 971217
  for (i = 0; i < count; i++)
Packit 971217
    out[i] = GUINT32_SWAP_LE_BE (in[i]);
Packit 971217
}
Packit 971217
Packit 971217
/* perform LE<->BE conversion on a block of @count 64-bit samples
Packit 971217
 * dst may equal src for in-place conversion
Packit 971217
 */
Packit 971217
static void
Packit 971217
converter_swap_endian_64 (gpointer dst, const gpointer src, gint count)
Packit 971217
{
Packit 971217
  guint64 *out = dst;
Packit 971217
  const guint64 *in = src;
Packit 971217
  gint i;
Packit 971217
Packit 971217
  for (i = 0; i < count; i++)
Packit 971217
    out[i] = GUINT64_SWAP_LE_BE (in[i]);
Packit 971217
}
Packit 971217
Packit 971217
/* the worker function to perform endian-conversion only
Packit 971217
 * assuming finfo and foutinfo have the same depth
Packit 971217
 */
Packit 971217
static gboolean
Packit 971217
converter_endian (GstAudioConverter * convert,
Packit 971217
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
Packit 971217
    gpointer out[], gsize out_frames)
Packit 971217
{
Packit 971217
  gint i;
Packit 971217
  AudioChain *chain;
Packit 971217
  gsize samples;
Packit 971217
Packit 971217
  chain = convert->chain_end;
Packit 971217
  samples = in_frames * chain->inc;
Packit 971217
Packit 971217
  GST_LOG ("convert endian: %" G_GSIZE_FORMAT " / %" G_GSIZE_FORMAT " samples",
Packit 971217
      in_frames, samples);
Packit 971217
Packit 971217
  if (in) {
Packit 971217
    for (i = 0; i < chain->blocks; i++)
Packit 971217
      convert->swap_endian (out[i], in[i], samples);
Packit 971217
  } else {
Packit 971217
    for (i = 0; i < chain->blocks; i++)
Packit 971217
      gst_audio_format_fill_silence (convert->in.finfo, out[i], samples);
Packit 971217
  }
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
converter_generic (GstAudioConverter * convert,
Packit 971217
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
Packit 971217
    gpointer out[], gsize out_frames)
Packit 971217
{
Packit 971217
  AudioChain *chain;
Packit 971217
  gpointer *tmp;
Packit 971217
  gint i;
Packit 971217
  gsize produced;
Packit 971217
Packit 971217
  chain = convert->chain_end;
Packit 971217
Packit 971217
  convert->in_writable = flags & GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE;
Packit 971217
  convert->in_data = in;
Packit 971217
  convert->in_frames = in_frames;
Packit 971217
  convert->out_data = out;
Packit 971217
  convert->out_frames = out_frames;
Packit 971217
Packit 971217
  /* get frames to pack */
Packit 971217
  tmp = audio_chain_get_samples (chain, &produced);
Packit 971217
Packit 971217
  if (!convert->out_default) {
Packit 971217
    GST_LOG ("pack %p, %p %" G_GSIZE_FORMAT, tmp, out, produced);
Packit 971217
    /* and pack if needed */
Packit 971217
    for (i = 0; i < chain->blocks; i++)
Packit 971217
      convert->out.finfo->pack_func (convert->out.finfo, 0, tmp[i], out[i],
Packit 971217
          produced * chain->inc);
Packit 971217
  }
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
static gboolean
Packit 971217
converter_resample (GstAudioConverter * convert,
Packit 971217
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
Packit 971217
    gpointer out[], gsize out_frames)
Packit 971217
{
Packit 971217
  gst_audio_resampler_resample (convert->resampler, in, in_frames, out,
Packit 971217
      out_frames);
Packit 971217
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
#define GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION(info1, info2) \
Packit 971217
		( \
Packit 971217
			!(((info1)->flags ^ (info2)->flags) & (~GST_AUDIO_FORMAT_FLAG_UNPACK)) && \
Packit 971217
			(info1)->endianness != (info2)->endianness && \
Packit 971217
			(info1)->width == (info2)->width && \
Packit 971217
			(info1)->depth == (info2)->depth \
Packit 971217
		)
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_new:
Packit 971217
 * @flags: extra #GstAudioConverterFlags
Packit 971217
 * @in_info: a source #GstAudioInfo
Packit 971217
 * @out_info: a destination #GstAudioInfo
Packit 971217
 * @config: (transfer full) (nullable): a #GstStructure with configuration options
Packit 971217
 *
Packit 971217
 * Create a new #GstAudioConverter that is able to convert between @in and @out
Packit 971217
 * audio formats.
Packit 971217
 *
Packit 971217
 * @config contains extra configuration options, see #GST_VIDEO_CONVERTER_OPT_*
Packit 971217
 * parameters for details about the options and values.
Packit 971217
 *
Packit 971217
 * Returns: a #GstAudioConverter or %NULL if conversion is not possible.
Packit 971217
 */
Packit 971217
GstAudioConverter *
Packit 971217
gst_audio_converter_new (GstAudioConverterFlags flags, GstAudioInfo * in_info,
Packit 971217
    GstAudioInfo * out_info, GstStructure * config)
Packit 971217
{
Packit 971217
  GstAudioConverter *convert;
Packit 971217
  AudioChain *prev;
Packit 971217
  const GValue *opt_matrix = NULL;
Packit 971217
Packit 971217
  g_return_val_if_fail (in_info != NULL, FALSE);
Packit 971217
  g_return_val_if_fail (out_info != NULL, FALSE);
Packit 971217
  g_return_val_if_fail (in_info->layout == GST_AUDIO_LAYOUT_INTERLEAVED, FALSE);
Packit 971217
  g_return_val_if_fail (in_info->layout == out_info->layout, FALSE);
Packit 971217
Packit 971217
  if (config)
Packit 971217
    opt_matrix =
Packit 971217
        gst_structure_get_value (config, GST_AUDIO_CONVERTER_OPT_MIX_MATRIX);
Packit 971217
Packit 971217
  if (opt_matrix
Packit 971217
      && !check_mix_matrix (in_info->channels, out_info->channels, opt_matrix))
Packit 971217
    goto invalid_mix_matrix;
Packit 971217
Packit 971217
  if ((GST_AUDIO_INFO_CHANNELS (in_info) != GST_AUDIO_INFO_CHANNELS (out_info))
Packit 971217
      && (GST_AUDIO_INFO_IS_UNPOSITIONED (in_info)
Packit 971217
          || GST_AUDIO_INFO_IS_UNPOSITIONED (out_info))
Packit 971217
      && !opt_matrix)
Packit 971217
    goto unpositioned;
Packit 971217
Packit 971217
  convert = g_slice_new0 (GstAudioConverter);
Packit 971217
Packit 971217
  convert->flags = flags;
Packit 971217
  convert->in = *in_info;
Packit 971217
  convert->out = *out_info;
Packit 971217
Packit 971217
  /* default config */
Packit 971217
  convert->config = gst_structure_new_empty ("GstAudioConverter");
Packit 971217
  if (config)
Packit 971217
    gst_audio_converter_update_config (convert, 0, 0, config);
Packit 971217
Packit 971217
  GST_INFO ("unitsizes: %d -> %d", in_info->bpf, out_info->bpf);
Packit 971217
Packit 971217
  /* step 1, unpack */
Packit 971217
  prev = chain_unpack (convert);
Packit 971217
  /* step 2, optional convert from S32 to F64 for channel mix */
Packit 971217
  prev = chain_convert_in (convert, prev);
Packit 971217
  /* step 3, channel mix */
Packit 971217
  prev = chain_mix (convert, prev);
Packit 971217
  /* step 4, resample */
Packit 971217
  prev = chain_resample (convert, prev);
Packit 971217
  /* step 5, optional convert for quantize */
Packit 971217
  prev = chain_convert_out (convert, prev);
Packit 971217
  /* step 6, optional quantize */
Packit 971217
  prev = chain_quantize (convert, prev);
Packit 971217
  /* step 7, pack */
Packit 971217
  convert->chain_end = chain_pack (convert, prev);
Packit 971217
Packit 971217
  convert->convert = converter_generic;
Packit 971217
  convert->in_place = FALSE;
Packit 971217
Packit 971217
  /* optimize */
Packit 971217
  if (convert->mix_passthrough) {
Packit 971217
    if (out_info->finfo->format == in_info->finfo->format) {
Packit 971217
      if (convert->resampler == NULL) {
Packit 971217
        GST_INFO
Packit 971217
            ("same formats, no resampler and passthrough mixing -> passthrough");
Packit 971217
        convert->convert = converter_passthrough;
Packit 971217
        convert->in_place = TRUE;
Packit 971217
      } else {
Packit 971217
        if (is_intermediate_format (in_info->finfo->format)) {
Packit 971217
          GST_INFO ("same formats, and passthrough mixing -> only resampling");
Packit 971217
          convert->convert = converter_resample;
Packit 971217
        }
Packit 971217
      }
Packit 971217
    } else if (GST_AUDIO_FORMAT_IS_ENDIAN_CONVERSION (out_info->finfo,
Packit 971217
            in_info->finfo)) {
Packit 971217
      if (convert->resampler == NULL) {
Packit 971217
        GST_INFO ("no resampler, passthrough mixing -> only endian conversion");
Packit 971217
        convert->convert = converter_endian;
Packit 971217
        convert->in_place = TRUE;
Packit 971217
Packit 971217
        switch (GST_AUDIO_INFO_BPS (in_info)) {
Packit 971217
          case 2:
Packit 971217
            GST_DEBUG ("initializing 16-bit endian conversion");
Packit 971217
            convert->swap_endian = converter_swap_endian_16;
Packit 971217
            break;
Packit 971217
          case 3:
Packit 971217
            GST_DEBUG ("initializing 24-bit endian conversion");
Packit 971217
            convert->swap_endian = converter_swap_endian_24;
Packit 971217
            break;
Packit 971217
          case 4:
Packit 971217
            GST_DEBUG ("initializing 32-bit endian conversion");
Packit 971217
            convert->swap_endian = converter_swap_endian_32;
Packit 971217
            break;
Packit 971217
          case 8:
Packit 971217
            GST_DEBUG ("initializing 64-bit endian conversion");
Packit 971217
            convert->swap_endian = converter_swap_endian_64;
Packit 971217
            break;
Packit 971217
          default:
Packit 971217
            GST_ERROR ("unsupported sample width for endian conversion");
Packit 971217
            g_assert_not_reached ();
Packit 971217
        }
Packit 971217
      }
Packit 971217
    }
Packit 971217
  }
Packit 971217
Packit 971217
  setup_allocators (convert);
Packit 971217
Packit 971217
  return convert;
Packit 971217
Packit 971217
  /* ERRORS */
Packit 971217
unpositioned:
Packit 971217
  {
Packit 971217
    GST_WARNING ("unpositioned channels");
Packit 971217
    return NULL;
Packit 971217
  }
Packit 971217
Packit 971217
invalid_mix_matrix:
Packit 971217
  {
Packit 971217
    GST_WARNING ("Invalid mix matrix");
Packit 971217
    return NULL;
Packit 971217
  }
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_free:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 *
Packit 971217
 * Free a previously allocated @convert instance.
Packit 971217
 */
Packit 971217
void
Packit 971217
gst_audio_converter_free (GstAudioConverter * convert)
Packit 971217
{
Packit 971217
  AudioChain *chain;
Packit 971217
Packit 971217
  g_return_if_fail (convert != NULL);
Packit 971217
Packit 971217
  /* walk the chain backwards and free all elements */
Packit 971217
  for (chain = convert->chain_end; chain;) {
Packit 971217
    AudioChain *prev = chain->prev;
Packit 971217
    audio_chain_free (chain);
Packit 971217
    chain = prev;
Packit 971217
  }
Packit 971217
Packit 971217
  if (convert->quant)
Packit 971217
    gst_audio_quantize_free (convert->quant);
Packit 971217
  if (convert->mix)
Packit 971217
    gst_audio_channel_mixer_free (convert->mix);
Packit 971217
  if (convert->resampler)
Packit 971217
    gst_audio_resampler_free (convert->resampler);
Packit 971217
  gst_audio_info_init (&convert->in);
Packit 971217
  gst_audio_info_init (&convert->out);
Packit 971217
Packit 971217
  gst_structure_free (convert->config);
Packit 971217
Packit 971217
  g_slice_free (GstAudioConverter, convert);
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_get_out_frames:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 * @in_frames: number of input frames
Packit 971217
 *
Packit 971217
 * Calculate how many output frames can be produced when @in_frames input
Packit 971217
 * frames are given to @convert.
Packit 971217
 *
Packit 971217
 * Returns: the number of output frames
Packit 971217
 */
Packit 971217
gsize
Packit 971217
gst_audio_converter_get_out_frames (GstAudioConverter * convert,
Packit 971217
    gsize in_frames)
Packit 971217
{
Packit 971217
  if (convert->resampler)
Packit 971217
    return gst_audio_resampler_get_out_frames (convert->resampler, in_frames);
Packit 971217
  else
Packit 971217
    return in_frames;
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_get_in_frames:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 * @out_frames: number of output frames
Packit 971217
 *
Packit 971217
 * Calculate how many input frames are currently needed by @convert to produce
Packit 971217
 * @out_frames of output frames.
Packit 971217
 *
Packit 971217
 * Returns: the number of input frames
Packit 971217
 */
Packit 971217
gsize
Packit 971217
gst_audio_converter_get_in_frames (GstAudioConverter * convert,
Packit 971217
    gsize out_frames)
Packit 971217
{
Packit 971217
  if (convert->resampler)
Packit 971217
    return gst_audio_resampler_get_in_frames (convert->resampler, out_frames);
Packit 971217
  else
Packit 971217
    return out_frames;
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_get_max_latency:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 *
Packit 971217
 * Get the maximum number of input frames that the converter would
Packit 971217
 * need before producing output.
Packit 971217
 *
Packit 971217
 * Returns: the latency of @convert as expressed in the number of
Packit 971217
 * frames.
Packit 971217
 */
Packit 971217
gsize
Packit 971217
gst_audio_converter_get_max_latency (GstAudioConverter * convert)
Packit 971217
{
Packit 971217
  if (convert->resampler)
Packit 971217
    return gst_audio_resampler_get_max_latency (convert->resampler);
Packit 971217
  else
Packit 971217
    return 0;
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_reset:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 *
Packit 971217
 * Reset @convert to the state it was when it was first created, clearing
Packit 971217
 * any history it might currently have.
Packit 971217
 */
Packit 971217
void
Packit 971217
gst_audio_converter_reset (GstAudioConverter * convert)
Packit 971217
{
Packit 971217
  if (convert->resampler)
Packit 971217
    gst_audio_resampler_reset (convert->resampler);
Packit 971217
  if (convert->quant)
Packit 971217
    gst_audio_quantize_reset (convert->quant);
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_samples:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 * @flags: extra #GstAudioConverterFlags
Packit 971217
 * @in: input frames
Packit 971217
 * @in_frames: number of input frames
Packit 971217
 * @out: output frames
Packit 971217
 * @out_frames: number of output frames
Packit 971217
 *
Packit 971217
 * Perform the conversion with @in_frames in @in to @out_frames in @out
Packit 971217
 * using @convert.
Packit 971217
 *
Packit 971217
 * In case the samples are interleaved, @in and @out must point to an
Packit 971217
 * array with a single element pointing to a block of interleaved samples.
Packit 971217
 *
Packit 971217
 * If non-interleaved samples are used, @in and @out must point to an
Packit 971217
 * array with pointers to memory blocks, one for each channel.
Packit 971217
 *
Packit 971217
 * @in may be %NULL, in which case @in_frames of silence samples are processed
Packit 971217
 * by the converter.
Packit 971217
 *
Packit 971217
 * This function always produces @out_frames of output and consumes @in_frames of
Packit 971217
 * input. Use gst_audio_converter_get_out_frames() and
Packit 971217
 * gst_audio_converter_get_in_frames() to make sure @in_frames and @out_frames
Packit 971217
 * are matching and @in and @out point to enough memory.
Packit 971217
 *
Packit 971217
 * Returns: %TRUE is the conversion could be performed.
Packit 971217
 */
Packit 971217
gboolean
Packit 971217
gst_audio_converter_samples (GstAudioConverter * convert,
Packit 971217
    GstAudioConverterFlags flags, gpointer in[], gsize in_frames,
Packit 971217
    gpointer out[], gsize out_frames)
Packit 971217
{
Packit 971217
  g_return_val_if_fail (convert != NULL, FALSE);
Packit 971217
  g_return_val_if_fail (out != NULL, FALSE);
Packit 971217
Packit 971217
  if (in_frames == 0) {
Packit 971217
    GST_LOG ("skipping empty buffer");
Packit 971217
    return TRUE;
Packit 971217
  }
Packit 971217
  return convert->convert (convert, flags, in, in_frames, out, out_frames);
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_convert:
Packit 971217
 * @flags: extra #GstAudioConverterFlags
Packit 971217
 * @in: (array length=in_size) (element-type guint8): input data
Packit 971217
 * @in_size: size of @in
Packit 971217
 * @out: (out) (array length=out_size) (element-type guint8): a pointer where
Packit 971217
 *  the output data will be written
Packit 971217
 * @out_size: (out): a pointer where the size of @out will be written
Packit 971217
 *
Packit 971217
 * Convenience wrapper around gst_audio_converter_samples(), which will
Packit 971217
 * perform allocation of the output buffer based on the result from
Packit 971217
 * gst_audio_converter_get_out_frames().
Packit 971217
 *
Packit 971217
 * Returns: %TRUE is the conversion could be performed.
Packit 971217
 *
Packit 971217
 * Since: 1.14
Packit 971217
 */
Packit 971217
gboolean
Packit 971217
gst_audio_converter_convert (GstAudioConverter * convert,
Packit 971217
    GstAudioConverterFlags flags, gpointer in, gsize in_size,
Packit 971217
    gpointer * out, gsize * out_size)
Packit 971217
{
Packit 971217
  gsize in_frames;
Packit 971217
  gsize out_frames;
Packit 971217
Packit 971217
  g_return_val_if_fail (convert != NULL, FALSE);
Packit 971217
  g_return_val_if_fail (flags ^ GST_AUDIO_CONVERTER_FLAG_IN_WRITABLE, FALSE);
Packit 971217
Packit 971217
  in_frames = in_size / convert->in.bpf;
Packit 971217
  out_frames = gst_audio_converter_get_out_frames (convert, in_frames);
Packit 971217
Packit 971217
  *out_size = out_frames * convert->out.bpf;
Packit 971217
  *out = g_malloc0 (*out_size);
Packit 971217
Packit 971217
  return gst_audio_converter_samples (convert, flags, &in, in_frames, out,
Packit 971217
      out_frames);
Packit 971217
}
Packit 971217
Packit 971217
/**
Packit 971217
 * gst_audio_converter_supports_inplace:
Packit 971217
 * @convert: a #GstAudioConverter
Packit 971217
 *
Packit 971217
 * Returns whether the audio converter can perform the conversion in-place.
Packit 971217
 * The return value would be typically input to gst_base_transform_set_in_place()
Packit 971217
 *
Packit 971217
 * Returns: %TRUE when the conversion can be done in place.
Packit 971217
 */
Packit 971217
gboolean
Packit 971217
gst_audio_converter_supports_inplace (GstAudioConverter * convert)
Packit 971217
{
Packit 971217
  return convert->in_place;
Packit 971217
}