audioconvert: make the quantizer a reusable object

Turn the quantizer into a reusable object.
This commit is contained in:
Wim Taymans 2015-10-27 17:28:06 +01:00
parent 8fc2569328
commit cd6c29e071
6 changed files with 244 additions and 231 deletions

View file

@ -56,25 +56,10 @@
* - (channel mix F64)
* - pack from F64
*/
#define DOUBLE_INTERMEDIATE_FORMAT(ctx) \
(!GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->in.finfo) || \
!GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->out.finfo) || \
(ctx->ns != NOISE_SHAPING_NONE))
static inline gboolean
check_default (AudioConvertCtx * ctx, const GstAudioFormatInfo * fmt)
{
if (!DOUBLE_INTERMEDIATE_FORMAT (ctx)) {
return GST_AUDIO_FORMAT_INFO_FORMAT (fmt) == GST_AUDIO_FORMAT_S32;
} else {
return GST_AUDIO_FORMAT_INFO_FORMAT (fmt) == GST_AUDIO_FORMAT_F64;
}
}
gboolean
audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
GstAudioInfo * out, GstAudioConvertDithering dither,
GstAudioConvertNoiseShaping ns)
GstAudioInfo * out, GstAudioDitherMethod dither,
GstAudioNoiseShapingMethod ns)
{
gint in_depth, out_depth;
GstChannelMixFlags flags;
@ -104,19 +89,19 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
* source depth. */
if (out_depth <= 20 && (!GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo)
|| in_depth >= out_depth)) {
ctx->dither = dither;
ctx->ns = ns;
dither = dither;
ns = ns;
GST_INFO ("using dither %d and noise shaping %d", dither, ns);
} else {
ctx->dither = DITHER_NONE;
ctx->ns = NOISE_SHAPING_NONE;
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 (ctx->ns > NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000)
ctx->ns = NOISE_SHAPING_ERROR_FEEDBACK;
if (ns > GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000)
ns = GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK;
flags =
GST_AUDIO_INFO_IS_UNPOSITIONED (in) ?
@ -128,11 +113,17 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
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 (DOUBLE_INTERMEDIATE_FORMAT (ctx)) {
if (ctx->mix_format == GST_AUDIO_FORMAT_F64) {
GST_INFO ("use float mixing");
ctx->mix_format = GST_AUDIO_FORMAT_F64;
if (ctx->in.finfo->unpack_format != GST_AUDIO_FORMAT_F64) {
ctx->convert = audio_convert_orc_s32_to_double;
GST_INFO ("convert input to F64");
@ -151,7 +142,6 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
}
} else {
GST_INFO ("use int mixing");
ctx->mix_format = GST_AUDIO_FORMAT_S32;
/* check if input needs to be unpacked to intermediate format */
ctx->in_default =
GST_AUDIO_FORMAT_INFO_FORMAT (in->finfo) == GST_AUDIO_FORMAT_S32;
@ -164,7 +154,8 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
/* check if channel mixer is passthrough */
ctx->mix_passthrough = gst_channel_mix_is_passthrough (ctx->mix);
ctx->quant_default = check_default (ctx, out->finfo);
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);
@ -174,7 +165,8 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
GST_INFO ("scale out %d", ctx->out_scale);
gst_audio_quantize_setup (ctx);
ctx->quant = gst_audio_quantize_new (dither, ns, 0, ctx->mix_format,
out->channels, ctx->out_scale);
return TRUE;
@ -191,7 +183,8 @@ audio_convert_clean_context (AudioConvertCtx * ctx)
{
g_return_val_if_fail (ctx != NULL, FALSE);
gst_audio_quantize_free (ctx);
if (ctx->quant)
gst_audio_quantize_free (ctx->quant);
if (ctx->mix)
gst_channel_mix_free (ctx->mix);
ctx->mix = NULL;
@ -244,7 +237,7 @@ audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
out_width = GST_AUDIO_FORMAT_INFO_WIDTH (ctx->out.finfo);
/* find biggest temp buffer size */
size = (DOUBLE_INTERMEDIATE_FORMAT (ctx)) ? sizeof (gdouble)
size = (ctx->mix_format == GST_AUDIO_FORMAT_F64) ? sizeof (gdouble)
: sizeof (gint32);
if (!ctx->in_default)
@ -314,7 +307,9 @@ audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
else
outbuf = tmpbuf;
ctx->quantize (ctx, src, outbuf, samples);
gst_audio_quantize_samples (ctx->quant, src, samples);
outbuf = src;
}
if (!ctx->out_default) {

View file

@ -26,71 +26,13 @@
#include <gst/audio/audio.h>
#include "gstchannelmix.h"
#include "gstaudioquantize.h"
GST_DEBUG_CATEGORY_EXTERN (audio_convert_debug);
#define GST_CAT_DEFAULT (audio_convert_debug)
/**
* GstAudioConvertDithering:
* @DITHER_NONE: No dithering
* @DITHER_RPDF: Rectangular dithering
* @DITHER_TPDF: Triangular dithering (default)
* @DITHER_TPDF_HF: High frequency triangular dithering
*
* Set of available dithering methods when converting audio.
*/
typedef enum
{
DITHER_NONE = 0,
DITHER_RPDF,
DITHER_TPDF,
DITHER_TPDF_HF
} GstAudioConvertDithering;
/**
* GstAudioConvertNoiseShaping:
* @NOISE_SHAPING_NONE: No noise shaping (default)
* @NOISE_SHAPING_ERROR_FEEDBACK: Error feedback
* @NOISE_SHAPING_SIMPLE: Simple 2-pole noise shaping
* @NOISE_SHAPING_MEDIUM: Medium 5-pole noise shaping
* @NOISE_SHAPING_HIGH: High 8-pole noise shaping
*
* Set of available noise shaping methods
*/
typedef enum
{
NOISE_SHAPING_NONE = 0,
NOISE_SHAPING_ERROR_FEEDBACK,
NOISE_SHAPING_SIMPLE,
NOISE_SHAPING_MEDIUM,
NOISE_SHAPING_HIGH
} GstAudioConvertNoiseShaping;
typedef struct _AudioConvertCtx AudioConvertCtx;
#if 0
typedef struct _AudioConvertFmt AudioConvertFmt;
struct _AudioConvertFmt
{
/* general caps */
gboolean is_int;
gint endianness;
gint width;
gint rate;
gint channels;
GstAudioChannelPosition *pos;
gboolean unpositioned_layout;
/* int audio caps */
gboolean sign;
gint depth;
gint unit_size;
};
#endif
typedef void (*AudioConvertQuantize) (AudioConvertCtx * ctx, gpointer src,
gpointer dst, gint count);
typedef void (*AudioConvertToF64) (gdouble *dst, const gint32 *src, gint count);
struct _AudioConvertCtx
@ -98,7 +40,9 @@ struct _AudioConvertCtx
GstAudioInfo in;
GstAudioInfo out;
GstAudioFormat mix_format;
GstChannelMix *mix;
GstAudioQuantize *quant;
gboolean in_default;
gboolean mix_passthrough;
@ -109,22 +53,13 @@ struct _AudioConvertCtx
gint tmpbufsize;
gint out_scale;
GstAudioFormat mix_format;
AudioConvertToF64 convert;
AudioConvertQuantize quantize;
GstAudioConvertDithering dither;
GstAudioConvertNoiseShaping ns;
/* 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;
};
gboolean audio_convert_prepare_context (AudioConvertCtx * ctx,
GstAudioInfo * in, GstAudioInfo * out,
GstAudioConvertDithering dither, GstAudioConvertNoiseShaping ns);
GstAudioDitherMethod dither, GstAudioNoiseShapingMethod ns);
gboolean audio_convert_get_sizes (AudioConvertCtx * ctx, gint samples,
gint * srcsize, gint * dstsize);

View file

@ -140,11 +140,12 @@ gst_audio_convert_dithering_get_type (void)
if (gtype == 0) {
static const GEnumValue values[] = {
{DITHER_NONE, "No dithering",
{GST_AUDIO_DITHER_NONE, "No dithering",
"none"},
{DITHER_RPDF, "Rectangular dithering", "rpdf"},
{DITHER_TPDF, "Triangular dithering (default)", "tpdf"},
{DITHER_TPDF_HF, "High frequency triangular dithering", "tpdf-hf"},
{GST_AUDIO_DITHER_RPDF, "Rectangular dithering", "rpdf"},
{GST_AUDIO_DITHER_TPDF, "Triangular dithering (default)", "tpdf"},
{GST_AUDIO_DITHER_TPDF_HF, "High frequency triangular dithering",
"tpdf-hf"},
{0, NULL, NULL}
};
@ -161,12 +162,13 @@ gst_audio_convert_ns_get_type (void)
if (gtype == 0) {
static const GEnumValue values[] = {
{NOISE_SHAPING_NONE, "No noise shaping (default)",
{GST_AUDIO_NOISE_SHAPING_NONE, "No noise shaping (default)",
"none"},
{NOISE_SHAPING_ERROR_FEEDBACK, "Error feedback", "error-feedback"},
{NOISE_SHAPING_SIMPLE, "Simple 2-pole noise shaping", "simple"},
{NOISE_SHAPING_MEDIUM, "Medium 5-pole noise shaping", "medium"},
{NOISE_SHAPING_HIGH, "High 8-pole noise shaping", "high"},
{GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK, "Error feedback",
"error-feedback"},
{GST_AUDIO_NOISE_SHAPING_SIMPLE, "Simple 2-pole noise shaping", "simple"},
{GST_AUDIO_NOISE_SHAPING_MEDIUM, "Medium 5-pole noise shaping", "medium"},
{GST_AUDIO_NOISE_SHAPING_HIGH, "High 8-pole noise shaping", "high"},
{0, NULL, NULL}
};
@ -191,13 +193,13 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass)
g_object_class_install_property (gobject_class, PROP_DITHERING,
g_param_spec_enum ("dithering", "Dithering",
"Selects between different dithering methods.",
GST_TYPE_AUDIO_CONVERT_DITHERING, DITHER_TPDF,
GST_TYPE_AUDIO_CONVERT_DITHERING, GST_AUDIO_DITHER_TPDF,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_NOISE_SHAPING,
g_param_spec_enum ("noise-shaping", "Noise shaping",
"Selects between different noise shaping methods.",
GST_TYPE_AUDIO_CONVERT_NOISE_SHAPING, NOISE_SHAPING_NONE,
GST_TYPE_AUDIO_CONVERT_NOISE_SHAPING, GST_AUDIO_NOISE_SHAPING_NONE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_pad_template (element_class,
@ -227,8 +229,8 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass)
static void
gst_audio_convert_init (GstAudioConvert * this)
{
this->dither = DITHER_TPDF;
this->ns = NOISE_SHAPING_NONE;
this->dither = GST_AUDIO_DITHER_TPDF;
this->ns = GST_AUDIO_NOISE_SHAPING_NONE;
memset (&this->ctx, 0, sizeof (AudioConvertCtx));
gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (this), TRUE);

View file

@ -48,8 +48,8 @@ struct _GstAudioConvert
AudioConvertCtx ctx;
GstAudioConvertDithering dither;
GstAudioConvertNoiseShaping ns;
GstAudioDitherMethod dither;
GstAudioNoiseShapingMethod ns;
};
struct _GstAudioConvertClass

View file

@ -34,12 +34,31 @@
#include <gst/gst.h>
#include <string.h>
#include <math.h>
#include "audioconvert.h"
#include "gstaudioconvertorc.h"
#include "gstaudioquantize.h"
#include "gstfastrandom.h"
typedef void (*QuantizeFunc) (GstAudioQuantize * quant, gpointer src,
gpointer dst, gint count);
struct _GstAudioQuantize
{
GstAudioDitherMethod dither;
GstAudioNoiseShapingMethod ns;
GstAudioQuantizeFlags flags;
GstAudioFormat format;
guint channels;
guint quantizer;
/* 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;
QuantizeFunc quantize;
};
#define MAKE_QUANTIZE_FUNC_NAME(name) \
gst_audio_quantize_quantize_##name
@ -48,11 +67,11 @@ gst_audio_quantize_quantize_##name
#define MAKE_QUANTIZE_FUNC_I(name, DITHER_INIT_FUNC, ADD_DITHER_FUNC, \
ROUND_FUNC) \
static void \
MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gint32 *src, \
MAKE_QUANTIZE_FUNC_NAME (name) (GstAudioQuantize *quant, gint32 *src, \
gint32 *dst, gint count) \
{ \
gint scale = ctx->out_scale; \
gint channels = ctx->out.channels; \
gint scale = quant->quantizer; \
gint channels = quant->channels; \
gint chan_pos; \
\
if (scale > 0) { \
@ -83,11 +102,11 @@ MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gint32 *src, \
ADD_NS_FUNC, ADD_DITHER_FUNC, \
UPDATE_ERROR_FUNC) \
static void \
MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gdouble *src, \
MAKE_QUANTIZE_FUNC_NAME (name) (GstAudioQuantize *quant, gdouble *src, \
gint32 *dst, gint count) \
{ \
gint scale = ctx->out_scale; \
gint channels = ctx->out.channels; \
gint scale = quant->quantizer; \
gint channels = quant->channels; \
gint chan_pos; \
gdouble tmp, d, factor = (1U<<(32-scale-1)); \
\
@ -180,7 +199,7 @@ MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gdouble *src, \
#define INIT_DITHER_TPDF_HF_I() \
gint32 rand; \
gint32 dither = (1<<(scale-1)); \
gint32 *last_random = (gint32 *) ctx->last_random, tmp_rand;
gint32 *last_random = (gint32 *) quant->last_random, tmp_rand;
#define ADD_DITHER_TPDF_HF_I() \
tmp_rand = RANDOM_INT_DITHER(dither); \
@ -199,7 +218,7 @@ MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gdouble *src, \
#define INIT_DITHER_TPDF_HF_F() \
gdouble rand; \
gdouble dither = 1.0/(1U<<(32 - scale)); \
gdouble *last_random = (gdouble *) ctx->last_random, tmp_rand;
gdouble *last_random = (gdouble *) quant->last_random, tmp_rand;
#define ADD_DITHER_TPDF_HF_F() \
tmp_rand = gst_fast_random_double_range (- dither, dither); \
@ -216,7 +235,7 @@ MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gdouble *src, \
#define INIT_NS_ERROR_FEEDBACK() \
gdouble orig; \
gdouble *errors = ctx->error_buf;
gdouble *errors = quant->error_buf;
#define ADD_NS_ERROR_FEEDBACK() \
orig = tmp; \
@ -230,7 +249,7 @@ MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gdouble *src, \
#define INIT_NS_SIMPLE() \
gdouble orig; \
gdouble *errors = ctx->error_buf, cur_error;
gdouble *errors = quant->error_buf, cur_error;
#define ADD_NS_SIMPLE() \
cur_error = errors[chan_pos*2] - 0.5 * errors[chan_pos*2 + 1]; \
@ -256,7 +275,7 @@ static const gdouble ns_medium_coeffs[] = {
#define INIT_NS_MEDIUM() \
gdouble orig; \
gdouble *errors = ctx->error_buf, cur_error; \
gdouble *errors = quant->error_buf, cur_error; \
int j;
#define ADD_NS_MEDIUM() \
@ -280,7 +299,7 @@ static const gdouble ns_high_coeffs[] = {
#define INIT_NS_HIGH() \
gdouble orig; \
gdouble *errors = ctx->error_buf, cur_error; \
gdouble *errors = quant->error_buf, cur_error; \
int j;
#define ADD_NS_HIGH() \
@ -352,142 +371,149 @@ MAKE_QUANTIZE_FUNC_F (float_tpdf_hf_medium, INIT_DITHER_TPDF_HF_F,
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 AudioConvertQuantize quantize_funcs[] = {
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (int_none_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (int_rpdf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (int_tpdf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (int_tpdf_hf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_error_feedback),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_simple),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_medium),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_high),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_error_feedback),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_simple),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_medium),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_high),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_error_feedback),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_simple),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_medium),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_high),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_error_feedback),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_simple),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_medium),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_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 (AudioConvertCtx * ctx)
gst_audio_quantize_setup_noise_shaping (GstAudioQuantize * quant)
{
switch (ctx->ns) {
case NOISE_SHAPING_HIGH:{
ctx->error_buf = g_new0 (gdouble, ctx->out.channels * 8);
switch (quant->ns) {
case GST_AUDIO_NOISE_SHAPING_HIGH:{
quant->error_buf = g_new0 (gdouble, quant->channels * 8);
break;
}
case NOISE_SHAPING_MEDIUM:{
ctx->error_buf = g_new0 (gdouble, ctx->out.channels * 5);
case GST_AUDIO_NOISE_SHAPING_MEDIUM:{
quant->error_buf = g_new0 (gdouble, quant->channels * 5);
break;
}
case NOISE_SHAPING_SIMPLE:{
ctx->error_buf = g_new0 (gdouble, ctx->out.channels * 2);
case GST_AUDIO_NOISE_SHAPING_SIMPLE:{
quant->error_buf = g_new0 (gdouble, quant->channels * 2);
break;
}
case NOISE_SHAPING_ERROR_FEEDBACK:
ctx->error_buf = g_new0 (gdouble, ctx->out.channels);
case GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK:
quant->error_buf = g_new0 (gdouble, quant->channels);
break;
case NOISE_SHAPING_NONE:
case GST_AUDIO_NOISE_SHAPING_NONE:
default:
ctx->error_buf = NULL;
quant->error_buf = NULL;
break;
}
return;
}
static void
gst_audio_quantize_free_noise_shaping (AudioConvertCtx * ctx)
gst_audio_quantize_setup_dither (GstAudioQuantize * quant)
{
switch (ctx->ns) {
case NOISE_SHAPING_HIGH:
case NOISE_SHAPING_MEDIUM:
case NOISE_SHAPING_SIMPLE:
case NOISE_SHAPING_ERROR_FEEDBACK:
case NOISE_SHAPING_NONE:
switch (quant->dither) {
case GST_AUDIO_DITHER_TPDF_HF:
quant->last_random = g_new0 (gdouble, quant->channels);
break;
case GST_AUDIO_DITHER_RPDF:
case GST_AUDIO_DITHER_TPDF:
quant->last_random = NULL;
break;
case GST_AUDIO_DITHER_NONE:
default:
break;
}
g_free (ctx->error_buf);
ctx->error_buf = NULL;
return;
}
static void
gst_audio_quantize_setup_dither (AudioConvertCtx * ctx)
{
switch (ctx->dither) {
case DITHER_TPDF_HF:
if (GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->out.finfo))
ctx->last_random = g_new0 (gint32, ctx->out.channels);
else
ctx->last_random = g_new0 (gdouble, ctx->out.channels);
break;
case DITHER_RPDF:
case DITHER_TPDF:
ctx->last_random = NULL;
break;
case DITHER_NONE:
default:
ctx->last_random = NULL;
quant->last_random = NULL;
break;
}
return;
}
static void
gst_audio_quantize_free_dither (AudioConvertCtx * ctx)
{
g_free (ctx->last_random);
return;
}
static void
gst_audio_quantize_setup_quantize_func (AudioConvertCtx * ctx)
gst_audio_quantize_setup_quantize_func (GstAudioQuantize * quant)
{
gint index = 0;
if (!GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->out.finfo)) {
ctx->quantize = NULL;
return;
}
if (ctx->ns == NOISE_SHAPING_NONE
&& GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->in.finfo)) {
index += ctx->dither;
if (quant->ns == GST_AUDIO_NOISE_SHAPING_NONE
&& quant->format == GST_AUDIO_FORMAT_S32) {
index += quant->dither;
} else {
index += 4 + (5 * ctx->dither);
index += ctx->ns;
}
g_assert (quant->format == GST_AUDIO_FORMAT_F64);
ctx->quantize = quantize_funcs[index];
index += 4 + (5 * quant->dither);
index += quant->ns;
}
quant->quantize = quantize_funcs[index];
}
gboolean
gst_audio_quantize_setup (AudioConvertCtx * ctx)
/**
* gst_audio_quantize_new:
* @dither: a #GstAudioDitherMethod
* @ns: a #GstAudioNoiseShapingMethod
* @flags: #GstAudioQuantizeFlags
* @format: the #GstAudioFormat of the samples
* @channels: the amount of channels in the samples
* @quantizer: the quantizer to use
*
* Create a new quantizer object with the given parameters.
*
* Returns: a new #GstAudioQuantize. Free with gst_audio_quantize_free().
*/
GstAudioQuantize *
gst_audio_quantize_new (GstAudioDitherMethod dither,
GstAudioNoiseShapingMethod ns, GstAudioQuantizeFlags flags,
GstAudioFormat format, guint channels, guint quantizer)
{
gst_audio_quantize_setup_dither (ctx);
gst_audio_quantize_setup_noise_shaping (ctx);
gst_audio_quantize_setup_quantize_func (ctx);
GstAudioQuantize *quant;
return TRUE;
quant = g_slice_new0 (GstAudioQuantize);
quant->dither = dither;
quant->ns = ns;
quant->flags = flags;
quant->format = format;
quant->channels = channels;
quant->quantizer = quantizer;
gst_audio_quantize_setup_dither (quant);
gst_audio_quantize_setup_noise_shaping (quant);
gst_audio_quantize_setup_quantize_func (quant);
return quant;
}
/**
* gst_audio_quantize_free:
* @quant: a #GstAudioQuantize
*
* Free a #GstAudioQuantize.
*/
void
gst_audio_quantize_free (GstAudioQuantize * quant)
{
g_free (quant->error_buf);
g_free (quant->last_random);
g_slice_free (GstAudioQuantize, quant);
}
void
gst_audio_quantize_free (AudioConvertCtx * ctx)
gst_audio_quantize_samples (GstAudioQuantize * quant,
gpointer data, guint samples)
{
gst_audio_quantize_free_dither (ctx);
gst_audio_quantize_free_noise_shaping (ctx);
quant->quantize (quant, data, data, samples);
}

View file

@ -1,5 +1,6 @@
/* GStreamer
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
* (C) 2015 Wim Taymans <wim.taymans@gmail.com>
*
* gstaudioquantize.h: quantizes audio to the target format and optionally
* applies dithering and noise shaping.
@ -21,14 +22,68 @@
*/
#include <gst/gst.h>
#include "audioconvert.h"
#include <gst/audio/audio.h>
#ifndef __GST_AUDIO_QUANTIZE_H__
#define __GST_AUDIO_QUANTIZE_H__
gboolean gst_audio_quantize_setup (AudioConvertCtx * ctx);
void gst_audio_quantize_reset (AudioConvertCtx * ctx);
void gst_audio_quantize_free (AudioConvertCtx * ctx);
/**
* GstAudioDitherMethod:
* @GST_AUDIO_DITHER_NONE: No dithering
* @GST_AUDIO_DITHER_RPDF: Rectangular dithering
* @GST_AUDIO_DITHER_TPDF: Triangular dithering (default)
* @GST_AUDIO_DITHER_TPDF_HF: High frequency triangular dithering
*
* Set of available dithering methods.
*/
typedef enum
{
GST_AUDIO_DITHER_NONE = 0,
GST_AUDIO_DITHER_RPDF,
GST_AUDIO_DITHER_TPDF,
GST_AUDIO_DITHER_TPDF_HF
} GstAudioDitherMethod;
/**
* GstAudioNoiseShapingMethod:
* @GST_AUDIO_NOISE_SHAPING_NONE: No noise shaping (default)
* @GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK: Error feedback
* @GST_AUDIO_NOISE_SHAPING_SIMPLE: Simple 2-pole noise shaping
* @GST_AUDIO_NOISE_SHAPING_MEDIUM: Medium 5-pole noise shaping
* @GST_AUDIO_NOISE_SHAPING_HIGH: High 8-pole noise shaping
*
* Set of available noise shaping methods
*/
typedef enum
{
GST_AUDIO_NOISE_SHAPING_NONE = 0,
GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK,
GST_AUDIO_NOISE_SHAPING_SIMPLE,
GST_AUDIO_NOISE_SHAPING_MEDIUM,
GST_AUDIO_NOISE_SHAPING_HIGH
} GstAudioNoiseShapingMethod;
typedef enum
{
GST_AUDIO_QUANTIZE_FLAG_NONE = 0
} GstAudioQuantizeFlags;
typedef struct _GstAudioQuantize GstAudioQuantize;
GstAudioQuantize * gst_audio_quantize_new (GstAudioDitherMethod dither,
GstAudioNoiseShapingMethod ns,
GstAudioQuantizeFlags flags,
GstAudioFormat format,
guint channels,
guint quantizer);
void gst_audio_quantize_free (GstAudioQuantize * quant);
void gst_audio_quantize_samples (GstAudioQuantize * quant,
gpointer data, guint samples);
#endif /* __GST_AUDIO_QUANTIZE_H__ */