mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-03-30 12:49:40 +00:00
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.
This commit is contained in:
parent
e1569ce76a
commit
5cf367ae57
5 changed files with 428 additions and 471 deletions
|
@ -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;
|
||||
/* 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));
|
||||
|
||||
/* 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 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;
|
||||
}
|
||||
|
||||
GST_INFO ("unitsizes: %d -> %d", in->bpf, out->bpf);
|
||||
|
||||
/* 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 ("mix format %s, passthrough %d, in_channels %d, out_channels %d",
|
||||
gst_audio_format_to_string (format), ctx->mix_passthrough,
|
||||
in->channels, out->channels);
|
||||
|
||||
GST_INFO ("in default %d, mix passthrough %d, out default %d",
|
||||
ctx->in_default, ctx->mix_passthrough, ctx->out_default);
|
||||
/* 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));
|
||||
|
||||
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);
|
||||
/* 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 */
|
||||
size = sizeof (gdouble) * samples * MAX (ctx->in.channels, ctx->out.channels);
|
||||
|
||||
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;
|
||||
|
||||
/* 1. unpack */
|
||||
if (!ctx->in_default) {
|
||||
gpointer t;
|
||||
|
||||
/* check if final conversion */
|
||||
if (!(ctx->quant_default && ctx->mix_passthrough))
|
||||
outbuf = tmpbuf;
|
||||
else
|
||||
if (!ctx->convert_in && ctx->mix_passthrough && !ctx->convert_out
|
||||
&& !ctx->quant && ctx->out_default)
|
||||
outbuf = dst;
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
if (ctx->convert)
|
||||
ctx->convert (outbuf, t, samples * ctx->in.channels);
|
||||
|
||||
src = outbuf;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
|
||||
* (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* 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;
|
||||
|
||||
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 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_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);
|
||||
}
|
||||
|
||||
|
||||
/* 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); \
|
||||
} \
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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;
|
||||
|
||||
#define INIT_DITHER_RPDF_F() \
|
||||
gdouble dither = 1.0/(1U<<(32 - scale - 1));
|
||||
if (quant->dither_size < len) {
|
||||
quant->dither_size = len;
|
||||
quant->dither_buf = g_realloc (quant->dither_buf, len * sizeof (gint32));
|
||||
need_init = TRUE;
|
||||
}
|
||||
|
||||
#define ADD_DITHER_RPDF_F() \
|
||||
tmp += gst_fast_random_double_range (- dither, dither);
|
||||
bias = quant->bias;
|
||||
d = quant->dither_buf;
|
||||
|
||||
#define INIT_DITHER_TPDF_I() \
|
||||
gint32 rand; \
|
||||
gint32 dither = (1<<(scale - 1));
|
||||
switch (quant->dither) {
|
||||
case GST_AUDIO_DITHER_NONE:
|
||||
if (need_init) {
|
||||
for (i = 0; i < len; i++)
|
||||
d[i] = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
#define ADD_DITHER_TPDF_I() \
|
||||
rand = bias + RANDOM_INT_DITHER(dither) \
|
||||
+ RANDOM_INT_DITHER(dither); \
|
||||
ADDSS (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 INIT_DITHER_TPDF_F() \
|
||||
gdouble dither = 1.0/(1U<<(32 - scale));
|
||||
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;
|
||||
|
||||
#define ADD_DITHER_TPDF_F() \
|
||||
tmp += gst_fast_random_double_range (- dither, dither) \
|
||||
+ gst_fast_random_double_range (- dither, dither);
|
||||
case GST_AUDIO_DITHER_TPDF_HF:
|
||||
{
|
||||
gint32 tmp, *last_random = quant->last_random;
|
||||
|
||||
#define INIT_DITHER_TPDF_HF_I() \
|
||||
gint32 rand; \
|
||||
gint32 dither = (1<<(scale-1)); \
|
||||
gint32 *last_random = (gint32 *) quant->last_random, tmp_rand;
|
||||
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 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);
|
||||
static void
|
||||
gst_audio_quantize_quantize_int_dither_none (GstAudioQuantize * quant,
|
||||
const gpointer src, gpointer dst, gint samples)
|
||||
{
|
||||
setup_dither_buf (quant, samples);
|
||||
|
||||
/* Like TPDF dither but the dither noise is oriented more to the
|
||||
* higher frequencies */
|
||||
audio_convert_orc_int_dither (dst, src, quant->dither_buf, ~quant->mask,
|
||||
samples * quant->channels);
|
||||
}
|
||||
|
||||
#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
|
||||
setup_error_buf (GstAudioQuantize * quant, gint samples)
|
||||
{
|
||||
gint channels = quant->channels;
|
||||
gint len = (samples + quant->n_coeffs) * channels;
|
||||
|
||||
#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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Noise shaping definitions.
|
||||
* See http://en.wikipedia.org/wiki/Noise_shaping for explanations. */
|
||||
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);
|
||||
|
||||
/* Simple error feedback: Just accumulate the dithering and quantization
|
||||
* error and remove it from each sample. */
|
||||
channels = quant->channels;
|
||||
len = samples * channels;
|
||||
dith = quant->dither_buf;
|
||||
e = quant->error_buf;
|
||||
mask = ~quant->mask;
|
||||
|
||||
#define INIT_NS_ERROR_FEEDBACK() \
|
||||
gdouble orig; \
|
||||
gdouble *errors = quant->error_buf;
|
||||
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);
|
||||
}
|
||||
|
||||
#define ADD_NS_ERROR_FEEDBACK() \
|
||||
orig = tmp; \
|
||||
tmp -= errors[chan_pos];
|
||||
#define SHIFT 10
|
||||
#define REDUCE 8
|
||||
#define RROUND (1<<(REDUCE-1))
|
||||
#define SREDUCE 2
|
||||
#define SROUND (1<<(SREDUCE-1))
|
||||
|
||||
#define UPDATE_ERROR_ERROR_FEEDBACK() \
|
||||
errors[chan_pos] += (d)/factor - orig;
|
||||
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 MAKE_QUANTIZE_FUNC_NAME(name) \
|
||||
gst_audio_quantize_quantize_##name
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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__ */
|
||||
|
|
Loading…
Reference in a new issue