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) * - (channel mix F64)
* - pack from 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 gboolean
audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in, audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
GstAudioInfo * out, GstAudioConvertDithering dither, GstAudioInfo * out, GstAudioDitherMethod dither,
GstAudioConvertNoiseShaping ns) GstAudioNoiseShapingMethod ns)
{ {
gint in_depth, out_depth; gint in_depth, out_depth;
GstChannelMixFlags flags; GstChannelMixFlags flags;
@ -104,19 +89,19 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
* source depth. */ * source depth. */
if (out_depth <= 20 && (!GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo) if (out_depth <= 20 && (!GST_AUDIO_FORMAT_INFO_IS_INTEGER (in->finfo)
|| in_depth >= out_depth)) { || in_depth >= out_depth)) {
ctx->dither = dither; dither = dither;
ctx->ns = ns; ns = ns;
GST_INFO ("using dither %d and noise shaping %d", dither, ns); GST_INFO ("using dither %d and noise shaping %d", dither, ns);
} else { } else {
ctx->dither = DITHER_NONE; dither = GST_AUDIO_DITHER_NONE;
ctx->ns = NOISE_SHAPING_NONE; ns = GST_AUDIO_NOISE_SHAPING_NONE;
GST_INFO ("using no dither and noise shaping"); GST_INFO ("using no dither and noise shaping");
} }
/* Use simple error feedback when output sample rate is smaller than /* Use simple error feedback when output sample rate is smaller than
* 32000 as the other methods might move the noise to audible ranges */ * 32000 as the other methods might move the noise to audible ranges */
if (ctx->ns > NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000) if (ns > GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK && out->rate < 32000)
ctx->ns = NOISE_SHAPING_ERROR_FEEDBACK; ns = GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK;
flags = flags =
GST_AUDIO_INFO_IS_UNPOSITIONED (in) ? 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, ctx->mix = gst_channel_mix_new (flags, in->channels, in->position,
out->channels, out->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 /* if one formats is float/double or we use noise shaping use double as
* intermediate format and switch mixing */ * intermediate format and switch mixing */
if (DOUBLE_INTERMEDIATE_FORMAT (ctx)) { if (ctx->mix_format == GST_AUDIO_FORMAT_F64) {
GST_INFO ("use float mixing"); GST_INFO ("use float mixing");
ctx->mix_format = GST_AUDIO_FORMAT_F64;
if (ctx->in.finfo->unpack_format != GST_AUDIO_FORMAT_F64) { if (ctx->in.finfo->unpack_format != GST_AUDIO_FORMAT_F64) {
ctx->convert = audio_convert_orc_s32_to_double; ctx->convert = audio_convert_orc_s32_to_double;
GST_INFO ("convert input to F64"); GST_INFO ("convert input to F64");
@ -151,7 +142,6 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
} }
} else { } else {
GST_INFO ("use int mixing"); GST_INFO ("use int mixing");
ctx->mix_format = GST_AUDIO_FORMAT_S32;
/* check if input needs to be unpacked to intermediate format */ /* check if input needs to be unpacked to intermediate format */
ctx->in_default = ctx->in_default =
GST_AUDIO_FORMAT_INFO_FORMAT (in->finfo) == GST_AUDIO_FORMAT_S32; 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 */ /* check if channel mixer is passthrough */
ctx->mix_passthrough = gst_channel_mix_is_passthrough (ctx->mix); 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", GST_INFO ("in default %d, mix passthrough %d, out default %d",
ctx->in_default, ctx->mix_passthrough, ctx->out_default); 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_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; return TRUE;
@ -191,7 +183,8 @@ audio_convert_clean_context (AudioConvertCtx * ctx)
{ {
g_return_val_if_fail (ctx != NULL, FALSE); 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) if (ctx->mix)
gst_channel_mix_free (ctx->mix); gst_channel_mix_free (ctx->mix);
ctx->mix = NULL; ctx->mix = NULL;
@ -244,7 +237,7 @@ audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
out_width = GST_AUDIO_FORMAT_INFO_WIDTH (ctx->out.finfo); out_width = GST_AUDIO_FORMAT_INFO_WIDTH (ctx->out.finfo);
/* find biggest temp buffer size */ /* find biggest temp buffer size */
size = (DOUBLE_INTERMEDIATE_FORMAT (ctx)) ? sizeof (gdouble) size = (ctx->mix_format == GST_AUDIO_FORMAT_F64) ? sizeof (gdouble)
: sizeof (gint32); : sizeof (gint32);
if (!ctx->in_default) if (!ctx->in_default)
@ -314,7 +307,9 @@ audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
else else
outbuf = tmpbuf; outbuf = tmpbuf;
ctx->quantize (ctx, src, outbuf, samples); gst_audio_quantize_samples (ctx->quant, src, samples);
outbuf = src;
} }
if (!ctx->out_default) { if (!ctx->out_default) {

View file

@ -26,71 +26,13 @@
#include <gst/audio/audio.h> #include <gst/audio/audio.h>
#include "gstchannelmix.h" #include "gstchannelmix.h"
#include "gstaudioquantize.h"
GST_DEBUG_CATEGORY_EXTERN (audio_convert_debug); GST_DEBUG_CATEGORY_EXTERN (audio_convert_debug);
#define GST_CAT_DEFAULT (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; 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); typedef void (*AudioConvertToF64) (gdouble *dst, const gint32 *src, gint count);
struct _AudioConvertCtx struct _AudioConvertCtx
@ -98,7 +40,9 @@ struct _AudioConvertCtx
GstAudioInfo in; GstAudioInfo in;
GstAudioInfo out; GstAudioInfo out;
GstAudioFormat mix_format;
GstChannelMix *mix; GstChannelMix *mix;
GstAudioQuantize *quant;
gboolean in_default; gboolean in_default;
gboolean mix_passthrough; gboolean mix_passthrough;
@ -109,22 +53,13 @@ struct _AudioConvertCtx
gint tmpbufsize; gint tmpbufsize;
gint out_scale; gint out_scale;
GstAudioFormat mix_format;
AudioConvertToF64 convert; 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, gboolean audio_convert_prepare_context (AudioConvertCtx * ctx,
GstAudioInfo * in, GstAudioInfo * out, GstAudioInfo * in, GstAudioInfo * out,
GstAudioConvertDithering dither, GstAudioConvertNoiseShaping ns); GstAudioDitherMethod dither, GstAudioNoiseShapingMethod ns);
gboolean audio_convert_get_sizes (AudioConvertCtx * ctx, gint samples, gboolean audio_convert_get_sizes (AudioConvertCtx * ctx, gint samples,
gint * srcsize, gint * dstsize); gint * srcsize, gint * dstsize);

View file

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

View file

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

View file

@ -34,12 +34,31 @@
#include <gst/gst.h> #include <gst/gst.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include "audioconvert.h"
#include "gstaudioconvertorc.h" #include "gstaudioconvertorc.h"
#include "gstaudioquantize.h" #include "gstaudioquantize.h"
#include "gstfastrandom.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) \ #define MAKE_QUANTIZE_FUNC_NAME(name) \
gst_audio_quantize_quantize_##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, \ #define MAKE_QUANTIZE_FUNC_I(name, DITHER_INIT_FUNC, ADD_DITHER_FUNC, \
ROUND_FUNC) \ ROUND_FUNC) \
static void \ static void \
MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gint32 *src, \ MAKE_QUANTIZE_FUNC_NAME (name) (GstAudioQuantize *quant, gint32 *src, \
gint32 *dst, gint count) \ gint32 *dst, gint count) \
{ \ { \
gint scale = ctx->out_scale; \ gint scale = quant->quantizer; \
gint channels = ctx->out.channels; \ gint channels = quant->channels; \
gint chan_pos; \ gint chan_pos; \
\ \
if (scale > 0) { \ if (scale > 0) { \
@ -83,11 +102,11 @@ MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gint32 *src, \
ADD_NS_FUNC, ADD_DITHER_FUNC, \ ADD_NS_FUNC, ADD_DITHER_FUNC, \
UPDATE_ERROR_FUNC) \ UPDATE_ERROR_FUNC) \
static void \ static void \
MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gdouble *src, \ MAKE_QUANTIZE_FUNC_NAME (name) (GstAudioQuantize *quant, gdouble *src, \
gint32 *dst, gint count) \ gint32 *dst, gint count) \
{ \ { \
gint scale = ctx->out_scale; \ gint scale = quant->quantizer; \
gint channels = ctx->out.channels; \ gint channels = quant->channels; \
gint chan_pos; \ gint chan_pos; \
gdouble tmp, d, factor = (1U<<(32-scale-1)); \ 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() \ #define INIT_DITHER_TPDF_HF_I() \
gint32 rand; \ gint32 rand; \
gint32 dither = (1<<(scale-1)); \ 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() \ #define ADD_DITHER_TPDF_HF_I() \
tmp_rand = RANDOM_INT_DITHER(dither); \ 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() \ #define INIT_DITHER_TPDF_HF_F() \
gdouble rand; \ gdouble rand; \
gdouble dither = 1.0/(1U<<(32 - scale)); \ 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() \ #define ADD_DITHER_TPDF_HF_F() \
tmp_rand = gst_fast_random_double_range (- dither, dither); \ 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() \ #define INIT_NS_ERROR_FEEDBACK() \
gdouble orig; \ gdouble orig; \
gdouble *errors = ctx->error_buf; gdouble *errors = quant->error_buf;
#define ADD_NS_ERROR_FEEDBACK() \ #define ADD_NS_ERROR_FEEDBACK() \
orig = tmp; \ orig = tmp; \
@ -230,7 +249,7 @@ MAKE_QUANTIZE_FUNC_NAME (name) (AudioConvertCtx *ctx, gdouble *src, \
#define INIT_NS_SIMPLE() \ #define INIT_NS_SIMPLE() \
gdouble orig; \ gdouble orig; \
gdouble *errors = ctx->error_buf, cur_error; gdouble *errors = quant->error_buf, cur_error;
#define ADD_NS_SIMPLE() \ #define ADD_NS_SIMPLE() \
cur_error = errors[chan_pos*2] - 0.5 * errors[chan_pos*2 + 1]; \ 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() \ #define INIT_NS_MEDIUM() \
gdouble orig; \ gdouble orig; \
gdouble *errors = ctx->error_buf, cur_error; \ gdouble *errors = quant->error_buf, cur_error; \
int j; int j;
#define ADD_NS_MEDIUM() \ #define ADD_NS_MEDIUM() \
@ -280,7 +299,7 @@ static const gdouble ns_high_coeffs[] = {
#define INIT_NS_HIGH() \ #define INIT_NS_HIGH() \
gdouble orig; \ gdouble orig; \
gdouble *errors = ctx->error_buf, cur_error; \ gdouble *errors = quant->error_buf, cur_error; \
int j; int j;
#define ADD_NS_HIGH() \ #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, 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); ADD_NS_HIGH, ADD_DITHER_TPDF_HF_F, UPDATE_ERROR_HIGH);
static const AudioConvertQuantize quantize_funcs[] = { static const QuantizeFunc quantize_funcs[] = {
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (int_none_none), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_none_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (int_rpdf_none), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_rpdf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (int_tpdf_none), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_tpdf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (int_tpdf_hf_none), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (int_tpdf_hf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_none), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_error_feedback), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_error_feedback),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_simple), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_simple),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_medium), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_medium),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_none_high), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_none_high),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_none), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_error_feedback), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_error_feedback),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_simple), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_simple),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_medium), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_medium),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_high), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_rpdf_high),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_none), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_error_feedback), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_error_feedback),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_simple), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_simple),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_medium), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_medium),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_high), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_high),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_none), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_none),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_error_feedback), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_error_feedback),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_simple), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_simple),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_medium), (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_medium),
(AudioConvertQuantize) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_high) (QuantizeFunc) MAKE_QUANTIZE_FUNC_NAME (float_tpdf_hf_high)
}; };
static void static void
gst_audio_quantize_setup_noise_shaping (AudioConvertCtx * ctx) gst_audio_quantize_setup_noise_shaping (GstAudioQuantize * quant)
{ {
switch (ctx->ns) { switch (quant->ns) {
case NOISE_SHAPING_HIGH:{ case GST_AUDIO_NOISE_SHAPING_HIGH:{
ctx->error_buf = g_new0 (gdouble, ctx->out.channels * 8); quant->error_buf = g_new0 (gdouble, quant->channels * 8);
break; break;
} }
case NOISE_SHAPING_MEDIUM:{ case GST_AUDIO_NOISE_SHAPING_MEDIUM:{
ctx->error_buf = g_new0 (gdouble, ctx->out.channels * 5); quant->error_buf = g_new0 (gdouble, quant->channels * 5);
break; break;
} }
case NOISE_SHAPING_SIMPLE:{ case GST_AUDIO_NOISE_SHAPING_SIMPLE:{
ctx->error_buf = g_new0 (gdouble, ctx->out.channels * 2); quant->error_buf = g_new0 (gdouble, quant->channels * 2);
break; break;
} }
case NOISE_SHAPING_ERROR_FEEDBACK: case GST_AUDIO_NOISE_SHAPING_ERROR_FEEDBACK:
ctx->error_buf = g_new0 (gdouble, ctx->out.channels); quant->error_buf = g_new0 (gdouble, quant->channels);
break; break;
case NOISE_SHAPING_NONE: case GST_AUDIO_NOISE_SHAPING_NONE:
default: default:
ctx->error_buf = NULL; quant->error_buf = NULL;
break; break;
} }
return; return;
} }
static void static void
gst_audio_quantize_free_noise_shaping (AudioConvertCtx * ctx) gst_audio_quantize_setup_dither (GstAudioQuantize * quant)
{ {
switch (ctx->ns) { switch (quant->dither) {
case NOISE_SHAPING_HIGH: case GST_AUDIO_DITHER_TPDF_HF:
case NOISE_SHAPING_MEDIUM: quant->last_random = g_new0 (gdouble, quant->channels);
case NOISE_SHAPING_SIMPLE: break;
case NOISE_SHAPING_ERROR_FEEDBACK: case GST_AUDIO_DITHER_RPDF:
case NOISE_SHAPING_NONE: case GST_AUDIO_DITHER_TPDF:
quant->last_random = NULL;
break;
case GST_AUDIO_DITHER_NONE:
default: default:
break; quant->last_random = NULL;
}
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;
break; break;
} }
return; return;
} }
static void static void
gst_audio_quantize_free_dither (AudioConvertCtx * ctx) gst_audio_quantize_setup_quantize_func (GstAudioQuantize * quant)
{
g_free (ctx->last_random);
return;
}
static void
gst_audio_quantize_setup_quantize_func (AudioConvertCtx * ctx)
{ {
gint index = 0; gint index = 0;
if (!GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->out.finfo)) { if (quant->ns == GST_AUDIO_NOISE_SHAPING_NONE
ctx->quantize = NULL; && quant->format == GST_AUDIO_FORMAT_S32) {
return; index += quant->dither;
}
if (ctx->ns == NOISE_SHAPING_NONE
&& GST_AUDIO_FORMAT_INFO_IS_INTEGER (ctx->in.finfo)) {
index += ctx->dither;
} else { } else {
index += 4 + (5 * ctx->dither); g_assert (quant->format == GST_AUDIO_FORMAT_F64);
index += ctx->ns;
}
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); GstAudioQuantize *quant;
gst_audio_quantize_setup_noise_shaping (ctx);
gst_audio_quantize_setup_quantize_func (ctx);
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 void
gst_audio_quantize_free (AudioConvertCtx * ctx) gst_audio_quantize_samples (GstAudioQuantize * quant,
gpointer data, guint samples)
{ {
gst_audio_quantize_free_dither (ctx); quant->quantize (quant, data, data, samples);
gst_audio_quantize_free_noise_shaping (ctx);
} }

View file

@ -1,5 +1,6 @@
/* GStreamer /* GStreamer
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org> * 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 * gstaudioquantize.h: quantizes audio to the target format and optionally
* applies dithering and noise shaping. * applies dithering and noise shaping.
@ -21,14 +22,68 @@
*/ */
#include <gst/gst.h> #include <gst/gst.h>
#include "audioconvert.h"
#include <gst/audio/audio.h>
#ifndef __GST_AUDIO_QUANTIZE_H__ #ifndef __GST_AUDIO_QUANTIZE_H__
#define __GST_AUDIO_QUANTIZE_H__ #define __GST_AUDIO_QUANTIZE_H__
gboolean gst_audio_quantize_setup (AudioConvertCtx * ctx); /**
void gst_audio_quantize_reset (AudioConvertCtx * ctx); * GstAudioDitherMethod:
void gst_audio_quantize_free (AudioConvertCtx * ctx); * @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__ */ #endif /* __GST_AUDIO_QUANTIZE_H__ */