From 5cf367ae576450ddab9c3a750b525d5393a5e614 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 30 Oct 2015 17:36:48 +0100 Subject: [PATCH] audioconvert: rework audioconvert Rewrite audioconvert to try to make it more clear what steps are executed during conversion. Add passthrough step that just does a memcpy when possible. Add ORC optimized dither and quantization functions. Implement noise-shaping on S32 samples only and allow for arbitrary noise shaping coefficients if we want this later. --- gst/audioconvert/audioconvert.c | 278 +++++++-------- gst/audioconvert/audioconvert.h | 20 +- gst/audioconvert/gstaudioconvertorc.orc | 21 ++ gst/audioconvert/gstaudioquantize.c | 585 +++++++++++++++----------------- gst/audioconvert/gstaudioquantize.h | 3 +- 5 files changed, 432 insertions(+), 475 deletions(-) diff --git a/gst/audioconvert/audioconvert.c b/gst/audioconvert/audioconvert.c index 247c2e4..a76e366 100644 --- a/gst/audioconvert/audioconvert.c +++ b/gst/audioconvert/audioconvert.c @@ -32,29 +32,14 @@ #include "gstaudioconvertorc.h" /** - * int -> int - * - unpack S32 - * - convert F64 - * - (channel mix S32) (channel mix F64) - * - (quantize+dither S32) quantize+dither+ns F64->S32 - * - pack from S32 + * int/int int/float float/int float/float * - * int -> float - * - unpack S32 - * - convert F64 - * - (channel mix F64) - * - pack from F64 - * - * float -> int - * - unpack F64 - * - (channel mix F64) - * - quantize+dither+ns F64->S32 - * - pack from S32 - * - * float -> float - * - unpack F64 - * - (channel mix F64) - * - pack from F64 + * unpack S32 S32 F64 F64 + * convert S32->F64 + * channel mix S32 F64 F64 F64 + * convert F64->S32 + * quantize S32 S32 + * pack S32 F64 S32 F64 */ gboolean audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in, @@ -63,6 +48,8 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in, { gint in_depth, out_depth; GstChannelMixFlags flags; + gboolean in_int, out_int; + GstAudioFormat format; g_return_val_if_fail (ctx != NULL, FALSE); g_return_val_if_fail (in != NULL, FALSE); @@ -78,30 +65,15 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in, ctx->in = *in; ctx->out = *out; + GST_INFO ("unitsizes: %d -> %d", in->bpf, out->bpf); + 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); - /* 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 - * source depth. */ - if (out_depth <= 20 && (!GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo) - || in_depth >= out_depth)) { - dither = dither; - ns = ns; - GST_INFO ("using dither %d and noise shaping %d", dither, ns); - } else { - dither = GST_AUDIO_DITHER_NONE; - ns = GST_AUDIO_NOISE_SHAPING_NONE; - GST_INFO ("using no dither and noise shaping"); - } - - /* Use simple error feedback when output sample rate is smaller than - * 32000 as the other methods might move the noise to audible ranges */ - if (ns > GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000) - ns = GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK; + in_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo); + out_int = GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo); flags = GST_AUDIO_INFO_IS_UNPOSITIONED (in) ? @@ -110,63 +82,70 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in, GST_AUDIO_INFO_IS_UNPOSITIONED (out) ? GST_CHANNEL_MIX_FLAGS_UNPOSITIONED_OUT : 0; - ctx->mix = gst_channel_mix_new (flags, in->channels, in->position, - out->channels, out->position); - if (!GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->in.finfo) || - !GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->out.finfo) || - (ns != GST_AUDIO_NOISE_SHAPING_NONE)) - ctx->mix_format = GST_AUDIO_FORMAT_F64; - else - ctx->mix_format = GST_AUDIO_FORMAT_S32; - - /* if one formats is float/double or we use noise shaping use double as - * intermediate format and switch mixing */ - if (ctx->mix_format == GST_AUDIO_FORMAT_F64) { - GST_INFO ("use float mixing"); - if (ctx->in.finfo->unpack_format != GST_AUDIO_FORMAT_F64) { - ctx->convert = audio_convert_orc_s32_to_double; - GST_INFO ("convert input to F64"); - } - /* check if input needs to be unpacked to intermediate format */ - ctx->in_default = - GST_AUDIO_FORMAT_INFO_FORMAT (in->finfo) == GST_AUDIO_FORMAT_F64; - - if (GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo)) { - /* quantization will convert to s32, check if this is our final output format */ - ctx->out_default = - GST_AUDIO_FORMAT_INFO_FORMAT (out->finfo) == GST_AUDIO_FORMAT_S32; - } else { - ctx->out_default = - GST_AUDIO_FORMAT_INFO_FORMAT (out->finfo) == GST_AUDIO_FORMAT_F64; - } - } else { - GST_INFO ("use int mixing"); - /* check if input needs to be unpacked to intermediate format */ - ctx->in_default = - GST_AUDIO_FORMAT_INFO_FORMAT (in->finfo) == GST_AUDIO_FORMAT_S32; - /* check if output is in default format */ - ctx->out_default = - GST_AUDIO_FORMAT_INFO_FORMAT (out->finfo) == GST_AUDIO_FORMAT_S32; - } + /* step 1, unpack */ + format = in->finfo->unpack_format; + ctx->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_INFO ("unitsizes: %d -> %d", in->bpf, out->bpf); + /* step 2, optional convert from S32 to F64 for channel mix */ + if (in_int && !out_int) { + GST_INFO ("convert S32 to F64"); + ctx->convert_in = (AudioConvertFunc) audio_convert_orc_s32_to_double; + format = GST_AUDIO_FORMAT_F64; + } - /* check if channel mixer is passthrough */ + /* step 3, channel mix */ + ctx->mix_format = format; + ctx->mix = gst_channel_mix_new (flags, in->channels, in->position, + out->channels, out->position); ctx->mix_passthrough = gst_channel_mix_is_passthrough (ctx->mix); - ctx->quant_default = - GST_AUDIO_FORMAT_INFO_FORMAT (out->finfo) == ctx->mix_format; - - GST_INFO ("in default %d, mix passthrough %d, out default %d", - ctx->in_default, ctx->mix_passthrough, ctx->out_default); - - ctx->out_scale = - GST_AUDIO_FORMAT_INFO_IS_INTEGER (out->finfo) ? (32 - out_depth) : 0; - - GST_INFO ("scale out %d", ctx->out_scale); - - ctx->quant = gst_audio_quantize_new (dither, ns, 0, ctx->mix_format, - out->channels, ctx->out_scale); + GST_INFO ("mix format %s, passthrough %d, in_channels %d, out_channels %d", + gst_audio_format_to_string (format), ctx->mix_passthrough, + in->channels, out->channels); + + /* step 4, optional convert for quantize */ + if (!in_int && out_int) { + GST_INFO ("convert F64 to S32"); + ctx->convert_out = (AudioConvertFunc) audio_convert_orc_double_to_s32; + format = GST_AUDIO_FORMAT_S32; + } + /* step 5, optional quantize */ + /* 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 + * source depth. */ + if (out_depth > 20 || (in_int && out_depth >= in_depth)) { + dither = GST_AUDIO_DITHER_NONE; + ns = GST_AUDIO_NOISE_SHAPING_NONE; + GST_INFO ("using no dither and noise shaping"); + } else { + GST_INFO ("using dither %d and noise shaping %d", dither, ns); + /* Use simple error feedback when output sample rate is smaller than + * 32000 as the other methods might move the noise to audible ranges */ + if (ns > GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000) + ns = GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK; + } + /* we still want to run the quantization step when reducing bits to get + * the rounding correct */ + if (out_int && out_depth < 32) { + GST_INFO ("quantize to %d bits, dither %d, ns %d", out_depth, dither, ns); + ctx->quant = gst_audio_quantize_new (dither, ns, 0, format, + out->channels, 1U << (32 - out_depth)); + } + /* step 6, pack */ + g_assert (out->finfo->unpack_format == format); + ctx->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)); + + /* optimize */ + if (out->finfo->format == in->finfo->format && ctx->mix_passthrough) { + GST_INFO ("same formats and passthrough mixing -> passthrough"); + ctx->passthrough = TRUE; + } return TRUE; @@ -185,15 +164,19 @@ audio_convert_clean_context (AudioConvertCtx * ctx) if (ctx->quant) gst_audio_quantize_free (ctx->quant); + ctx->quant = NULL; if (ctx->mix) gst_channel_mix_free (ctx->mix); ctx->mix = NULL; gst_audio_info_init (&ctx->in); gst_audio_info_init (&ctx->out); - ctx->convert = NULL; + ctx->convert_in = NULL; + ctx->convert_out = NULL; g_free (ctx->tmpbuf); + g_free (ctx->tmpbuf2); ctx->tmpbuf = NULL; + ctx->tmpbuf2 = NULL; ctx->tmpbufsize = 0; return TRUE; @@ -217,10 +200,8 @@ gboolean audio_convert_convert (AudioConvertCtx * ctx, gpointer src, gpointer dst, gint samples, gboolean src_writable) { - guint insize, outsize, size; - gpointer outbuf, tmpbuf; - guint intemp = 0, outtemp = 0, biggest; - gint in_width, out_width; + guint size; + gpointer outbuf, tmpbuf, tmpbuf2; g_return_val_if_fail (ctx != NULL, FALSE); g_return_val_if_fail (src != NULL, FALSE); @@ -230,90 +211,83 @@ audio_convert_convert (AudioConvertCtx * ctx, gpointer src, if (samples == 0) return TRUE; - insize = ctx->in.bpf * samples; - outsize = ctx->out.bpf * samples; - - in_width = GST_AUDIO_FORMAT_INFO_WIDTH (ctx->in.finfo); - out_width = GST_AUDIO_FORMAT_INFO_WIDTH (ctx->out.finfo); - - /* find biggest temp buffer size */ - size = (ctx->mix_format == GST_AUDIO_FORMAT_F64) ? sizeof (gdouble) - : sizeof (gint32); - - if (!ctx->in_default) - intemp = gst_util_uint64_scale (insize, size * 8, in_width); - if (!ctx->mix_passthrough || !ctx->quant_default) - outtemp = gst_util_uint64_scale (outsize, size * 8, out_width); - biggest = MAX (intemp, outtemp); - - /* see if one of the buffers can be used as temp */ - if ((outsize >= biggest) && (ctx->out.bpf <= size)) - tmpbuf = dst; - else if ((insize >= biggest) && src_writable && (ctx->in.bpf >= size)) - tmpbuf = src; - else { - if (biggest > ctx->tmpbufsize) { - ctx->tmpbuf = g_realloc (ctx->tmpbuf, biggest); - ctx->tmpbufsize = biggest; - } - tmpbuf = ctx->tmpbuf; + if (ctx->passthrough) { + memcpy (dst, src, samples * ctx->in.bpf); + return TRUE; } - /* start conversion */ - if (!ctx->in_default) { - gpointer t; + size = sizeof (gdouble) * samples * MAX (ctx->in.channels, ctx->out.channels); - /* check if final conversion */ - if (!(ctx->quant_default && ctx->mix_passthrough)) - outbuf = tmpbuf; - else - outbuf = dst; + if (size > ctx->tmpbufsize) { + ctx->tmpbuf = g_realloc (ctx->tmpbuf, size); + ctx->tmpbuf2 = g_realloc (ctx->tmpbuf2, size); + ctx->tmpbufsize = size; + } + tmpbuf = ctx->tmpbuf; + tmpbuf2 = ctx->tmpbuf2; - /* move samples to the middle of the array so that we can - * convert them in-place */ - if (ctx->convert) - t = ((gint32 *) outbuf) + (samples * ctx->in.channels); + /* 1. unpack */ + if (!ctx->in_default) { + if (!ctx->convert_in && ctx->mix_passthrough && !ctx->convert_out + && !ctx->quant && ctx->out_default) + outbuf = dst; else - t = outbuf; + outbuf = tmpbuf; - /* unpack to default format */ - ctx->in.finfo->unpack_func (ctx->in.finfo, 0, t, src, + ctx->in.finfo->unpack_func (ctx->in.finfo, 0, outbuf, src, samples * ctx->in.channels); + src = outbuf; + } - if (ctx->convert) - ctx->convert (outbuf, t, samples * ctx->in.channels); + /* 2. optionally convert for mixing */ + if (ctx->convert_in) { + if (ctx->mix_passthrough && !ctx->convert_out && !ctx->quant + && ctx->out_default) + outbuf = dst; + else if (src == tmpbuf) + outbuf = tmpbuf2; + else + outbuf = tmpbuf; + ctx->convert_in (outbuf, src, samples * ctx->in.channels); src = outbuf; } + /* step 3, channel mix if not passthrough */ if (!ctx->mix_passthrough) { - /* check if final conversion */ - if (ctx->quant_default) + if (!ctx->convert_out && !ctx->quant && ctx->out_default) outbuf = dst; else outbuf = tmpbuf; - /* convert channels */ gst_channel_mix_mix (ctx->mix, ctx->mix_format, ctx->in.layout, src, outbuf, samples); + src = outbuf; + } + /* step 4, optional convert F64 -> S32 for quantize */ + if (ctx->convert_out) { + if (!ctx->quant && ctx->out_default) + outbuf = dst; + else + outbuf = tmpbuf; + ctx->convert_out (outbuf, src, samples * ctx->out.channels); src = outbuf; } - /* we only need to quantize if output format is int */ - if (GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->out.finfo)) { + /* step 5, optional quantize */ + if (ctx->quant) { if (ctx->out_default) outbuf = dst; else outbuf = tmpbuf; - gst_audio_quantize_samples (ctx->quant, src, samples); - - outbuf = src; + gst_audio_quantize_samples (ctx->quant, outbuf, src, samples); + src = outbuf; } + /* step 6, pack */ if (!ctx->out_default) { - /* pack default format into dst */ ctx->out.finfo->pack_func (ctx->out.finfo, 0, src, dst, samples * ctx->out.channels); } diff --git a/gst/audioconvert/audioconvert.h b/gst/audioconvert/audioconvert.h index 6b5e712..c34e2aa 100644 --- a/gst/audioconvert/audioconvert.h +++ b/gst/audioconvert/audioconvert.h @@ -33,28 +33,32 @@ GST_DEBUG_CATEGORY_EXTERN (audio_convert_debug); typedef struct _AudioConvertCtx AudioConvertCtx; -typedef void (*AudioConvertToF64) (gdouble *dst, const gint32 *src, gint count); +typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count); struct _AudioConvertCtx { GstAudioInfo in; GstAudioInfo out; + gboolean in_default; + + AudioConvertFunc convert_in; + GstAudioFormat mix_format; + gboolean mix_passthrough; GstChannelMix *mix; + + AudioConvertFunc convert_out; + GstAudioQuantize *quant; - gboolean in_default; - gboolean mix_passthrough; - gboolean quant_default; gboolean out_default; + gboolean passthrough; + gpointer tmpbuf; + gpointer tmpbuf2; gint tmpbufsize; - - gint out_scale; - - AudioConvertToF64 convert; }; gboolean audio_convert_prepare_context (AudioConvertCtx * ctx, diff --git a/gst/audioconvert/gstaudioconvertorc.orc b/gst/audioconvert/gstaudioconvertorc.orc index dee6b61..24fdf02 100644 --- a/gst/audioconvert/gstaudioconvertorc.orc +++ b/gst/audioconvert/gstaudioconvertorc.orc @@ -13,3 +13,24 @@ divd d1, t1, 0x41DFFFFFFFC00000L muld t1, s1, 0x41DFFFFFFFC00000L convdl d1, t1 + +.function audio_convert_orc_int_bias +.dest 4 d1 gint32 +.source 4 s1 gint32 +.param 4 bias gint32 +.param 4 mask gint32 +.temp 4 t1 + +addssl t1, s1, bias +andl d1, t1, mask + +.function audio_convert_orc_int_dither +.dest 4 d1 gint32 +.source 4 s1 gint32 +.source 4 dither gint32 +.param 4 mask gint32 +.temp 4 t1 + +addssl t1, s1, dither +andl d1, t1, mask + diff --git a/gst/audioconvert/gstaudioquantize.c b/gst/audioconvert/gstaudioquantize.c index ac40659..33e2780 100644 --- a/gst/audioconvert/gstaudioquantize.c +++ b/gst/audioconvert/gstaudioquantize.c @@ -1,5 +1,6 @@ /* GStreamer * Copyright (C) 2007 Sebastian Dröge + * (C) 2015 Wim Taymans * * gstaudioquantize.c: quantizes audio to the target format and optionally * applies dithering and noise shaping. @@ -20,12 +21,6 @@ * Boston, MA 02110-1301, USA. */ -/* - * FIXME: When doing dithering with int as intermediate format - * one gets audible harmonics while the noise floor is - * constant for double as intermediate format! - */ - /* TODO: - Maybe drop 5-pole noise shaping and use coefficients * generated by dmaker * http://shibatch.sf.net @@ -39,7 +34,7 @@ #include "gstfastrandom.h" -typedef void (*QuantizeFunc) (GstAudioQuantize * quant, gpointer src, +typedef void (*QuantizeFunc) (GstAudioQuantize * quant, const gpointer src, gpointer dst, gint count); struct _GstAudioQuantize @@ -51,208 +46,247 @@ struct _GstAudioQuantize guint channels; guint quantizer; + guint shift; + guint32 mask, bias; + /* last random number generated per channel for hifreq TPDF dither */ gpointer last_random; /* contains the past quantization errors, error[out_channels][count] */ - gdouble *error_buf; + guint error_size; + gpointer error_buf; + /* buffer with dither values */ + guint dither_size; + gpointer dither_buf; + /* noise shaping coefficients */ + gpointer coeffs; + gint n_coeffs; QuantizeFunc quantize; }; -#define MAKE_QUANTIZE_FUNC_NAME(name) \ -gst_audio_quantize_quantize_##name - #define ADDSS(res,val) \ - if (val > 0 && res > 0 && G_MAXINT32 - res <= val) \ + if (val > 0 && res > 0 && G_MAXINT32 - res <= val){ \ res = G_MAXINT32; \ - else if (val < 0 && res < 0 && G_MININT32 - res >= val) \ + } else if (val < 0 && res < 0 && G_MININT32 - res >= val){ \ res = G_MININT32; \ - else \ + } else \ res += val; -/* Quantize functions for gint32 as intermediate format */ - -#define MAKE_QUANTIZE_FUNC_I(name, DITHER_INIT_FUNC, ADD_DITHER_FUNC, \ - ROUND_FUNC) \ -static void \ -MAKE_QUANTIZE_FUNC_NAME (name) (GstAudioQuantize *quant, gint32 *src, \ - gint32 *dst, gint count) \ -{ \ - gint scale = quant->quantizer; \ - gint channels = quant->channels; \ - gint chan_pos; \ - \ - if (scale > 0) { \ - gint32 tmp; \ - guint32 mask = 0xffffffff & (0xffffffff << scale); \ - guint32 bias = 1U << (scale - 1); \ - DITHER_INIT_FUNC() \ - \ - for (;count;count--) { \ - for (chan_pos = 0; chan_pos < channels; chan_pos++) { \ - tmp = *src++; \ - ADD_DITHER_FUNC() \ - ROUND_FUNC() \ - *dst = tmp & mask; \ - dst++; \ - } \ - } \ - } else { \ - memcpy (dst, src, count * channels * 4); \ - } \ +static void +gst_audio_quantize_quantize_memcpy (GstAudioQuantize * quant, + const gpointer src, gpointer dst, gint samples) +{ + if (src != dst) + memcpy (dst, src, samples * sizeof (gint32) * quant->channels); } - -/* Quantize functions for gdouble as intermediate format with - * int as target */ - -#define MAKE_QUANTIZE_FUNC_F(name, DITHER_INIT_FUNC, NS_INIT_FUNC, \ - ADD_NS_FUNC, ADD_DITHER_FUNC, \ - UPDATE_ERROR_FUNC) \ -static void \ -MAKE_QUANTIZE_FUNC_NAME (name) (GstAudioQuantize *quant, gdouble *src, \ - gint32 *dst, gint count) \ -{ \ - gint scale = quant->quantizer; \ - gint channels = quant->channels; \ - gint chan_pos; \ - gdouble tmp, d, factor = (1U<<(32-scale-1)); \ - \ - if (scale > 0) { \ - DITHER_INIT_FUNC() \ - NS_INIT_FUNC() \ - \ - for (;count;count--) { \ - for (chan_pos = 0; chan_pos < channels; chan_pos++) { \ - tmp = *src++; \ - ADD_NS_FUNC() \ - ADD_DITHER_FUNC() \ - tmp = floor(tmp * factor); \ - d = CLAMP (tmp, -factor, factor - 1); \ - UPDATE_ERROR_FUNC() \ - *dst++ = ((gint32)d) << scale; \ - } \ - } \ - } else { \ - audio_convert_orc_double_to_s32 (dst, src, count * channels); \ - } \ +/* Quantize functions for gint32 as intermediate format */ +static void +gst_audio_quantize_quantize_int_none_none (GstAudioQuantize * quant, + const gpointer src, gpointer dst, gint samples) +{ + audio_convert_orc_int_bias (dst, src, quant->bias, ~quant->mask, + samples * quant->channels); } -/* Rounding functions for int as intermediate format, only used when - * not using dithering. With dithering we include this offset in our - * dither noise instead. */ - -#define ROUND() \ - if (tmp > 0 && G_MAXINT32 - tmp <= bias) \ - tmp = G_MAXINT32; \ - else \ - tmp += bias; - - -#define NONE_FUNC() - -/* Dithering definitions - * See http://en.wikipedia.org/wiki/Dithering or - * http://www.users.qwest.net/~volt42/cadenzarecording/DitherExplained.pdf for explainations. - * - * We already add the rounding offset to the dither noise here - * to have only one overflow check instead of two. */ - -#define INIT_DITHER_RPDF_I() \ - gint32 rand; \ - gint32 dither = (1<<(scale)); - /* Assuming dither == 2^n, * returns one of 2^(n+1) possible random values: * -dither <= retval < dither */ #define RANDOM_INT_DITHER(dither) \ (- dither + (gst_fast_random_int32 () & ((dither << 1) - 1))) -#define ADD_DITHER_RPDF_I() \ - rand = bias + RANDOM_INT_DITHER(dither); \ - ADDSS (tmp, rand); - -#define INIT_DITHER_RPDF_F() \ - gdouble dither = 1.0/(1U<<(32 - scale - 1)); - -#define ADD_DITHER_RPDF_F() \ - tmp += gst_fast_random_double_range (- dither, dither); - -#define INIT_DITHER_TPDF_I() \ - gint32 rand; \ - gint32 dither = (1<<(scale - 1)); - -#define ADD_DITHER_TPDF_I() \ - rand = bias + RANDOM_INT_DITHER(dither) \ - + RANDOM_INT_DITHER(dither); \ - ADDSS (tmp, rand); +static void +setup_dither_buf (GstAudioQuantize * quant, gint samples) +{ + gboolean need_init = FALSE; + gint channels = quant->channels; + gint i, len = samples * channels; + guint shift = quant->shift; + guint32 bias; + gint32 dither, *d; + + if (quant->dither_size < len) { + quant->dither_size = len; + quant->dither_buf = g_realloc (quant->dither_buf, len * sizeof (gint32)); + need_init = TRUE; + } -#define INIT_DITHER_TPDF_F() \ - gdouble dither = 1.0/(1U<<(32 - scale)); + bias = quant->bias; + d = quant->dither_buf; -#define ADD_DITHER_TPDF_F() \ - tmp += gst_fast_random_double_range (- dither, dither) \ - + gst_fast_random_double_range (- dither, dither); + switch (quant->dither) { + case GST_AUDIO_DITHER_NONE: + if (need_init) { + for (i = 0; i < len; i++) + d[i] = 0; + } + break; -#define INIT_DITHER_TPDF_HF_I() \ - gint32 rand; \ - gint32 dither = (1<<(scale-1)); \ - gint32 *last_random = (gint32 *) quant->last_random, tmp_rand; + case GST_AUDIO_DITHER_RPDF: + dither = 1 << (shift); + for (i = 0; i < len; i++) + d[i] = bias + RANDOM_INT_DITHER (dither); + break; -#define ADD_DITHER_TPDF_HF_I() \ - tmp_rand = RANDOM_INT_DITHER(dither); \ - rand = bias + tmp_rand - last_random[chan_pos]; \ - last_random[chan_pos] = tmp_rand; \ - ADDSS (tmp, rand); + case GST_AUDIO_DITHER_TPDF: + dither = 1 << (shift - 1); + for (i = 0; i < len; i++) + d[i] = bias + RANDOM_INT_DITHER (dither) + RANDOM_INT_DITHER (dither); + break; -/* Like TPDF dither but the dither noise is oriented more to the - * higher frequencies */ + case GST_AUDIO_DITHER_TPDF_HF: + { + gint32 tmp, *last_random = quant->last_random; + + dither = 1 << (shift - 1); + for (i = 0; i < len; i++) { + tmp = RANDOM_INT_DITHER (dither); + d[i] = bias + tmp - last_random[i % channels]; + last_random[i % channels] = tmp; + } + break; + } + } +} -#define INIT_DITHER_TPDF_HF_F() \ - gdouble rand; \ - gdouble dither = 1.0/(1U<<(32 - scale)); \ - gdouble *last_random = (gdouble *) quant->last_random, tmp_rand; +static void +gst_audio_quantize_quantize_int_dither_none (GstAudioQuantize * quant, + const gpointer src, gpointer dst, gint samples) +{ + setup_dither_buf (quant, samples); -#define ADD_DITHER_TPDF_HF_F() \ - tmp_rand = gst_fast_random_double_range (- dither, dither); \ - rand = tmp_rand - last_random[chan_pos]; \ - last_random[chan_pos] = tmp_rand; \ - tmp += rand; + audio_convert_orc_int_dither (dst, src, quant->dither_buf, ~quant->mask, + samples * quant->channels); +} -/* Noise shaping definitions. - * See http://en.wikipedia.org/wiki/Noise_shaping for explanations. */ +static void +setup_error_buf (GstAudioQuantize * quant, gint samples) +{ + gint channels = quant->channels; + gint len = (samples + quant->n_coeffs) * channels; + + if (quant->error_size < len) { + quant->error_buf = g_realloc (quant->error_buf, len * sizeof (gint32)); + if (quant->error_size == 0) + memset ((gint32 *) quant->error_buf, 0, + channels * quant->n_coeffs * sizeof (gint32)); + quant->error_size = len; + } +} +static void +gst_audio_quantize_quantize_int_dither_feedback (GstAudioQuantize * quant, + const gpointer src, gpointer dst, gint samples) +{ + guint32 mask; + gint i, len, channels; + const gint32 *s = src; + gint32 *dith, *d = dst, v, o, *e, err; + + setup_dither_buf (quant, samples); + setup_error_buf (quant, samples); + + channels = quant->channels; + len = samples * channels; + dith = quant->dither_buf; + e = quant->error_buf; + mask = ~quant->mask; + + for (i = 0; i < len; i++) { + o = v = s[i]; + /* add dither */ + err = dith[i]; + /* remove error */ + err -= e[i]; + ADDSS (v, err); + v &= mask; + /* store new error */ + e[i + channels] = e[i] + (v - o); + /* store result */ + d[i] = v; + } + memmove (e, &e[len], sizeof (gint32) * channels); +} -/* Simple error feedback: Just accumulate the dithering and quantization - * error and remove it from each sample. */ +#define SHIFT 10 +#define REDUCE 8 +#define RROUND (1<<(REDUCE-1)) +#define SREDUCE 2 +#define SROUND (1<<(SREDUCE-1)) -#define INIT_NS_ERROR_FEEDBACK() \ - gdouble orig; \ - gdouble *errors = quant->error_buf; +static void +gst_audio_quantize_quantize_int_dither_noise_shape (GstAudioQuantize * quant, + const gpointer src, gpointer dst, gint samples) +{ + guint32 mask; + gint i, j, k, len, channels, nc; + const gint32 *s = src; + gint32 *c, *dith, *d = dst, v, o, *e, err; + + setup_dither_buf (quant, samples); + setup_error_buf (quant, samples); + + channels = quant->channels; + len = samples * channels; + dith = quant->dither_buf; + e = quant->error_buf; + c = quant->coeffs; + nc = quant->n_coeffs; + mask = ~quant->mask; + + for (i = 0; i < len; i++) { + v = s[i]; + /* combine and remove error */ + err = 0; + for (j = 0, k = i; j < nc; j++, k += channels) + err -= e[k] * c[j]; + err = (err + SROUND) >> (SREDUCE); + ADDSS (v, err); + o = v; + /* add dither */ + err = dith[i]; + ADDSS (v, err); + /* quantize */ + v &= mask; + /* store new error with reduced precision */ + e[k] = (v - o + RROUND) >> REDUCE; + /* store result */ + d[i] = v; + } + memmove (e, &e[len], sizeof (gint32) * channels * nc); +} -#define ADD_NS_ERROR_FEEDBACK() \ - orig = tmp; \ - tmp -= errors[chan_pos]; +#define MAKE_QUANTIZE_FUNC_NAME(name) \ +gst_audio_quantize_quantize_##name -#define UPDATE_ERROR_ERROR_FEEDBACK() \ - errors[chan_pos] += (d)/factor - orig; +static const QuantizeFunc quantize_funcs[] = { + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_none_none), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_feedback), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_none), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_feedback), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_none), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_feedback), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_none), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_feedback), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), + (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_dither_noise_shape), +}; /* Same as error feedback but also add 1/2 of the previous error value. * This moves the noise a bit more into the higher frequencies. */ - -#define INIT_NS_SIMPLE() \ - gdouble orig; \ - gdouble *errors = quant->error_buf, cur_error; - -#define ADD_NS_SIMPLE() \ - cur_error = errors[chan_pos*2] - 0.5 * errors[chan_pos*2 + 1]; \ - tmp -= cur_error; \ - orig = tmp; - -#define UPDATE_ERROR_SIMPLE() \ - errors[chan_pos*2 + 1] = errors[chan_pos*2]; \ - errors[chan_pos*2] = (d)/factor - orig; - +static const gdouble ns_simple_coeffs[] = { + -0.5, 1.0 +}; /* Noise shaping coefficients from[1], moves most power of the * error noise into inaudible frequency ranges. @@ -263,158 +297,53 @@ MAKE_QUANTIZE_FUNC_NAME (name) (GstAudioQuantize *quant, gdouble *src, \ * J. Audio Eng. Soc., Vol. 39, No. 11, November 1991. */ static const gdouble ns_medium_coeffs[] = { - 2.033, -2.165, 1.959, -1.590, 0.6149 + 0.6149, -1.590, 1.959, -2.165, 2.033 }; -#define INIT_NS_MEDIUM() \ - gdouble orig; \ - gdouble *errors = quant->error_buf, cur_error; \ - int j; - -#define ADD_NS_MEDIUM() \ - cur_error = 0.0; \ - for (j = 0; j < 5; j++) \ - cur_error += errors[chan_pos*5 + j] * ns_medium_coeffs[j]; \ - tmp -= cur_error; \ - orig = tmp; - -#define UPDATE_ERROR_MEDIUM() \ - for (j = 4; j > 0; j--) \ - errors[chan_pos*5 + j] = errors[chan_pos*5 + j-1]; \ - errors[chan_pos*5] = (d)/factor - orig; - /* Noise shaping coefficients by David Schleef, moves most power of the * error noise into inaudible frequency ranges */ - static const gdouble ns_high_coeffs[] = { - 2.08484, -2.92975, 3.27918, -3.31399, 2.61339, -1.72008, 0.876066, -0.340122 + -0.340122, 0.876066, -1.72008, 2.61339, -3.31399, 3.27918, -2.92975, 2.08484, }; -#define INIT_NS_HIGH() \ - gdouble orig; \ - gdouble *errors = quant->error_buf, cur_error; \ - int j; - -#define ADD_NS_HIGH() \ - cur_error = 0.0; \ - for (j = 0; j < 8; j++) \ - cur_error += errors[chan_pos*8 + j] * ns_high_coeffs[j]; \ - tmp -= cur_error; \ - orig = tmp; - -#define UPDATE_ERROR_HIGH() \ - for (j = 7; j > 0; j--) \ - errors[chan_pos*8 + j] = errors[chan_pos*8 + j-1]; \ - errors[chan_pos*8] = (d)/factor - orig; - - -MAKE_QUANTIZE_FUNC_I (int_none_none, NONE_FUNC, NONE_FUNC, ROUND); -MAKE_QUANTIZE_FUNC_I (int_rpdf_none, INIT_DITHER_RPDF_I, ADD_DITHER_RPDF_I, - NONE_FUNC); -MAKE_QUANTIZE_FUNC_I (int_tpdf_none, INIT_DITHER_TPDF_I, ADD_DITHER_TPDF_I, - NONE_FUNC); -MAKE_QUANTIZE_FUNC_I (int_tpdf_hf_none, INIT_DITHER_TPDF_HF_I, - ADD_DITHER_TPDF_HF_I, NONE_FUNC); - -MAKE_QUANTIZE_FUNC_F (float_none_none, NONE_FUNC, - NONE_FUNC, NONE_FUNC, NONE_FUNC, NONE_FUNC); -MAKE_QUANTIZE_FUNC_F (float_none_error_feedback, NONE_FUNC, - INIT_NS_ERROR_FEEDBACK, ADD_NS_ERROR_FEEDBACK, NONE_FUNC, - UPDATE_ERROR_ERROR_FEEDBACK); -MAKE_QUANTIZE_FUNC_F (float_none_simple, NONE_FUNC, INIT_NS_SIMPLE, - ADD_NS_SIMPLE, NONE_FUNC, UPDATE_ERROR_SIMPLE); -MAKE_QUANTIZE_FUNC_F (float_none_medium, NONE_FUNC, INIT_NS_MEDIUM, - ADD_NS_MEDIUM, NONE_FUNC, UPDATE_ERROR_MEDIUM); -MAKE_QUANTIZE_FUNC_F (float_none_high, NONE_FUNC, INIT_NS_HIGH, ADD_NS_HIGH, - NONE_FUNC, UPDATE_ERROR_HIGH); - -MAKE_QUANTIZE_FUNC_F (float_rpdf_none, INIT_DITHER_RPDF_F, - NONE_FUNC, NONE_FUNC, ADD_DITHER_RPDF_F, NONE_FUNC); -MAKE_QUANTIZE_FUNC_F (float_rpdf_error_feedback, INIT_DITHER_RPDF_F, - INIT_NS_ERROR_FEEDBACK, ADD_NS_ERROR_FEEDBACK, ADD_DITHER_RPDF_F, - UPDATE_ERROR_ERROR_FEEDBACK); -MAKE_QUANTIZE_FUNC_F (float_rpdf_simple, INIT_DITHER_RPDF_F, INIT_NS_SIMPLE, - ADD_NS_SIMPLE, ADD_DITHER_RPDF_F, UPDATE_ERROR_SIMPLE); -MAKE_QUANTIZE_FUNC_F (float_rpdf_medium, INIT_DITHER_RPDF_F, INIT_NS_MEDIUM, - ADD_NS_MEDIUM, ADD_DITHER_RPDF_F, UPDATE_ERROR_MEDIUM); -MAKE_QUANTIZE_FUNC_F (float_rpdf_high, INIT_DITHER_RPDF_F, INIT_NS_HIGH, - ADD_NS_HIGH, ADD_DITHER_RPDF_F, UPDATE_ERROR_HIGH); - -MAKE_QUANTIZE_FUNC_F (float_tpdf_none, INIT_DITHER_TPDF_F, - NONE_FUNC, NONE_FUNC, ADD_DITHER_TPDF_F, NONE_FUNC); -MAKE_QUANTIZE_FUNC_F (float_tpdf_error_feedback, INIT_DITHER_TPDF_F, - INIT_NS_ERROR_FEEDBACK, ADD_NS_ERROR_FEEDBACK, ADD_DITHER_TPDF_F, - UPDATE_ERROR_ERROR_FEEDBACK); -MAKE_QUANTIZE_FUNC_F (float_tpdf_simple, INIT_DITHER_TPDF_F, INIT_NS_SIMPLE, - ADD_NS_SIMPLE, ADD_DITHER_TPDF_F, UPDATE_ERROR_SIMPLE); -MAKE_QUANTIZE_FUNC_F (float_tpdf_medium, INIT_DITHER_TPDF_F, INIT_NS_MEDIUM, - ADD_NS_MEDIUM, ADD_DITHER_TPDF_F, UPDATE_ERROR_MEDIUM); -MAKE_QUANTIZE_FUNC_F (float_tpdf_high, INIT_DITHER_TPDF_F, INIT_NS_HIGH, - ADD_NS_HIGH, ADD_DITHER_TPDF_F, UPDATE_ERROR_HIGH); - -MAKE_QUANTIZE_FUNC_F (float_tpdf_hf_none, INIT_DITHER_TPDF_HF_F, - NONE_FUNC, NONE_FUNC, ADD_DITHER_TPDF_HF_F, NONE_FUNC); -MAKE_QUANTIZE_FUNC_F (float_tpdf_hf_error_feedback, INIT_DITHER_TPDF_HF_F, - INIT_NS_ERROR_FEEDBACK, ADD_NS_ERROR_FEEDBACK, ADD_DITHER_TPDF_HF_F, - UPDATE_ERROR_ERROR_FEEDBACK); -MAKE_QUANTIZE_FUNC_F (float_tpdf_hf_simple, INIT_DITHER_TPDF_HF_F, - INIT_NS_SIMPLE, ADD_NS_SIMPLE, ADD_DITHER_TPDF_HF_F, UPDATE_ERROR_SIMPLE); -MAKE_QUANTIZE_FUNC_F (float_tpdf_hf_medium, INIT_DITHER_TPDF_HF_F, - INIT_NS_MEDIUM, ADD_NS_MEDIUM, ADD_DITHER_TPDF_HF_F, UPDATE_ERROR_MEDIUM); -MAKE_QUANTIZE_FUNC_F (float_tpdf_hf_high, INIT_DITHER_TPDF_HF_F, INIT_NS_HIGH, - ADD_NS_HIGH, ADD_DITHER_TPDF_HF_F, UPDATE_ERROR_HIGH); - -static const QuantizeFunc quantize_funcs[] = { - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_none_none), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_rpdf_none), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_tpdf_none), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_tpdf_hf_none), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_none), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_error_feedback), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_simple), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_medium), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_high), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_none), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_error_feedback), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_simple), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_medium), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_high), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_none), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_error_feedback), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_simple), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_medium), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_high), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_none), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_error_feedback), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_simple), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_medium), - (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_high) -}; static void gst_audio_quantize_setup_noise_shaping (GstAudioQuantize * quant) { + gint i, n_coeffs = 0; + gint32 *q; + const gdouble *coeffs; + switch (quant->ns) { - case GST_AUDIO_NOISE_SHAPING_HIGH:{ - quant->error_buf = g_new0 (gdouble, quant->channels * 8); + case GST_AUDIO_NOISE_SHAPING_HIGH: + n_coeffs = 8; + coeffs = ns_high_coeffs; break; - } - case GST_AUDIO_NOISE_SHAPING_MEDIUM:{ - quant->error_buf = g_new0 (gdouble, quant->channels * 5); + + case GST_AUDIO_NOISE_SHAPING_MEDIUM: + n_coeffs = 5; + coeffs = ns_medium_coeffs; break; - } - case GST_AUDIO_NOISE_SHAPING_SIMPLE:{ - quant->error_buf = g_new0 (gdouble, quant->channels * 2); + + case GST_AUDIO_NOISE_SHAPING_SIMPLE: + n_coeffs = 2; + coeffs = ns_simple_coeffs; break; - } + case GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK: - quant->error_buf = g_new0 (gdouble, quant->channels); break; + case GST_AUDIO_NOISE_SHAPING_NONE: default: - quant->error_buf = NULL; break; } + + if (n_coeffs) { + quant->n_coeffs = n_coeffs; + q = quant->coeffs = g_new0 (gint32, quant->channels * n_coeffs); + for (i = 0; i < n_coeffs; i++) + q[i] = floor (coeffs[i] * (1 << SHIFT) + 0.5); + } return; } @@ -423,7 +352,7 @@ gst_audio_quantize_setup_dither (GstAudioQuantize * quant) { switch (quant->dither) { case GST_AUDIO_DITHER_TPDF_HF: - quant->last_random = g_new0 (gdouble, quant->channels); + quant->last_random = g_new0 (gint32, quant->channels); break; case GST_AUDIO_DITHER_RPDF: case GST_AUDIO_DITHER_TPDF: @@ -440,20 +369,28 @@ gst_audio_quantize_setup_dither (GstAudioQuantize * quant) static void gst_audio_quantize_setup_quantize_func (GstAudioQuantize * quant) { - gint index = 0; + gint index; - if (quant->ns == GST_AUDIO_NOISE_SHAPING_NONE - && quant->format == GST_AUDIO_FORMAT_S32) { - index += quant->dither; - } else { - g_assert (quant->format == GST_AUDIO_FORMAT_F64); - - index += 4 + (5 * quant->dither); - index += quant->ns; + if (quant->shift == 0) { + quant->quantize = (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (memcpy); + return; } + + index = 5 * quant->dither + quant->ns; quant->quantize = quantize_funcs[index]; } +static gint +count_power (guint v) +{ + gint res = 0; + while (v > 1) { + res++; + v >>= 1; + } + return res; +} + /** * gst_audio_quantize_new: * @dither: a #GstAudioDitherMethod @@ -465,6 +402,9 @@ gst_audio_quantize_setup_quantize_func (GstAudioQuantize * quant) * * Create a new quantizer object with the given parameters. * + * Output samples will be quantized to a multiple of @quantizer. Better + * performance is achieved when @quantizer is a power of 2. + * * Returns: a new #GstAudioQuantize. Free with gst_audio_quantize_free(). */ GstAudioQuantize * @@ -474,6 +414,9 @@ gst_audio_quantize_new (GstAudioDitherMethod dither, { GstAudioQuantize *quant; + g_return_val_if_fail (format == GST_AUDIO_FORMAT_S32, NULL); + g_return_val_if_fail (channels > 0, NULL); + quant = g_slice_new0 (GstAudioQuantize); quant->dither = dither; quant->ns = ns; @@ -482,6 +425,13 @@ gst_audio_quantize_new (GstAudioDitherMethod dither, quant->channels = channels; quant->quantizer = quantizer; + quant->shift = count_power (quantizer); + if (quant->shift > 0) + quant->bias = (1U << (quant->shift - 1)); + else + quant->bias = 0; + quant->mask = (1U << quant->shift) - 1; + gst_audio_quantize_setup_dither (quant); gst_audio_quantize_setup_noise_shaping (quant); gst_audio_quantize_setup_quantize_func (quant); @@ -498,7 +448,10 @@ gst_audio_quantize_new (GstAudioDitherMethod dither, void gst_audio_quantize_free (GstAudioQuantize * quant) { + g_return_if_fail (quant != NULL); + g_free (quant->error_buf); + g_free (quant->coeffs); g_free (quant->last_random); g_slice_free (GstAudioQuantize, quant); @@ -506,7 +459,11 @@ gst_audio_quantize_free (GstAudioQuantize * quant) void gst_audio_quantize_samples (GstAudioQuantize * quant, - gpointer data, guint samples) + const gpointer src, gpointer dst, guint samples) { - quant->quantize (quant, data, data, samples); + g_return_if_fail (quant != NULL); + g_return_if_fail (dst != NULL || samples == 0); + g_return_if_fail (src != NULL || samples == 0); + + quant->quantize (quant, dst, src, samples); } diff --git a/gst/audioconvert/gstaudioquantize.h b/gst/audioconvert/gstaudioquantize.h index bb5ccf4..513867d 100644 --- a/gst/audioconvert/gstaudioquantize.h +++ b/gst/audioconvert/gstaudioquantize.h @@ -84,6 +84,7 @@ GstAudioQuantize * gst_audio_quantize_new (GstAudioDitherMethod dither, void gst_audio_quantize_free (GstAudioQuantize * quant); void gst_audio_quantize_samples (GstAudioQuantize * quant, - gpointer data, guint samples); + const gpointer src, + gpointer dst, guint samples); #endif /* __GST_AUDIO_QUANTIZE_H__ */ -- 2.7.4