Commit dbb857b9 authored by Sebastian Dröge's avatar Sebastian Dröge
Browse files

gst/audioconvert/: Implement dithering and noise shaping in audioconvert. By default now

Original commit message from CVS:
* gst/audioconvert/Makefile.am:
* gst/audioconvert/audioconvert.c: (audio_convert_get_func_index),
(check_default), (audio_convert_prepare_context),
(audio_convert_clean_context), (audio_convert_convert):
* gst/audioconvert/audioconvert.h:
* gst/audioconvert/gstaudioconvert.c:
(gst_audio_convert_dithering_get_type),
(gst_audio_convert_ns_get_type), (gst_audio_convert_class_init),
(gst_audio_convert_init), (gst_audio_convert_set_caps),
(gst_audio_convert_set_property), (gst_audio_convert_get_property):
* gst/audioconvert/gstaudioconvert.h:
* gst/audioconvert/gstaudioquantize.c:
(gst_audio_quantize_setup_noise_shaping),
(gst_audio_quantize_free_noise_shaping),
(gst_audio_quantize_setup_dither),
(gst_audio_quantize_free_dither),
(gst_audio_quantize_setup_quantize_func),
(gst_audio_quantize_setup), (gst_audio_quantize_free):
* gst/audioconvert/gstaudioquantize.h:
Implement dithering and noise shaping in audioconvert. By default now
TPDF dithering (and no noise shaping) will be used when converting
from a higher bit depth to 20 bit depth or smaller, otherwise
everything will be as it is now.
For the last audioconvert in a pipeline it would make sense to
use some kind of noise shaping, enabling it by default for all
conversions would give undesired results though. Fixes #360246.
* tests/check/elements/audioconvert.c: (setup_audioconvert),
(GST_START_TEST):
Adjust unit test for the new audioconvert.
parent 8c05f2eb
2007-06-28 Sebastian Dröge <slomo@circular-chaos.org>
* gst/audioconvert/Makefile.am:
* gst/audioconvert/audioconvert.c: (audio_convert_get_func_index),
(check_default), (audio_convert_prepare_context),
(audio_convert_clean_context), (audio_convert_convert):
* gst/audioconvert/audioconvert.h:
* gst/audioconvert/gstaudioconvert.c:
(gst_audio_convert_dithering_get_type),
(gst_audio_convert_ns_get_type), (gst_audio_convert_class_init),
(gst_audio_convert_init), (gst_audio_convert_set_caps),
(gst_audio_convert_set_property), (gst_audio_convert_get_property):
* gst/audioconvert/gstaudioconvert.h:
* gst/audioconvert/gstaudioquantize.c:
(gst_audio_quantize_setup_noise_shaping),
(gst_audio_quantize_free_noise_shaping),
(gst_audio_quantize_setup_dither),
(gst_audio_quantize_free_dither),
(gst_audio_quantize_setup_quantize_func),
(gst_audio_quantize_setup), (gst_audio_quantize_free):
* gst/audioconvert/gstaudioquantize.h:
Implement dithering and noise shaping in audioconvert. By default now
TPDF dithering (and no noise shaping) will be used when converting
from a higher bit depth to 20 bit depth or smaller, otherwise
everything will be as it is now.
For the last audioconvert in a pipeline it would make sense to
use some kind of noise shaping, enabling it by default for all
conversions would give undesired results though. Fixes #360246.
* tests/check/elements/audioconvert.c: (setup_audioconvert),
(GST_START_TEST):
Adjust unit test for the new audioconvert.
2007-06-28 Wim Taymans <wim@fluendo.com>
 
* gst/playback/gstqueue2.c: (apply_segment), (update_buffering):
......@@ -4,6 +4,7 @@ libgstaudioconvert_la_SOURCES = \
gstaudioconvert.c \
audioconvert.c \
gstchannelmix.c \
gstaudioquantize.c \
plugin.c
libgstaudioconvert_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
......@@ -16,6 +17,7 @@ noinst_HEADERS = \
gstaudioconvert.h \
audioconvert.h \
gstchannelmix.h \
gstaudioquantize.h \
plugin.h
#TESTS = channelmixtest
......
This diff is collapsed.
......@@ -25,6 +25,23 @@
#include <gst/gst.h>
#include <gst/audio/multichannel.h>
typedef enum
{
DITHER_NONE = 0,
DITHER_RPDF,
DITHER_TPDF,
DITHER_TPDF_HF
} DitherType;
typedef enum
{
NOISE_SHAPING_NONE = 0,
NOISE_SHAPING_ERROR_FEEDBACK,
NOISE_SHAPING_SIMPLE,
NOISE_SHAPING_MEDIUM,
NOISE_SHAPING_HIGH
} NoiseShapingType;
typedef struct _AudioConvertCtx AudioConvertCtx;
typedef struct _AudioConvertFmt AudioConvertFmt;
......@@ -45,10 +62,14 @@ struct _AudioConvertFmt
gint unit_size;
};
typedef void (*AudioConvertUnpack) (gpointer src, gpointer dst, gint scale, gint count);
typedef void (*AudioConvertPack) (gpointer src, gpointer dst, gint scale, gint count);
typedef void (*AudioConvertUnpack) (gpointer src, gpointer dst, gint scale,
gint count);
typedef void (*AudioConvertPack) (gpointer src, gpointer dst, gint scale,
gint count);
typedef void (*AudioConvertMix) (AudioConvertCtx *, gpointer, gpointer, gint);
typedef void (*AudioConvertQuantize) (AudioConvertCtx * ctx, gpointer src,
gpointer dst, gint count);
struct _AudioConvertCtx
{
......@@ -73,20 +94,31 @@ struct _AudioConvertCtx
gint in_scale;
gint out_scale;
AudioConvertMix channel_mix;
AudioConvertQuantize quantize;
DitherType dither;
NoiseShapingType ns;
/* random number generate for dither noise */
GRand *dither_random;
/* 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_clean_fmt (AudioConvertFmt *fmt);
gboolean audio_convert_clean_fmt (AudioConvertFmt * fmt);
gboolean audio_convert_prepare_context (AudioConvertCtx *ctx, AudioConvertFmt *in,
AudioConvertFmt *out);
gboolean audio_convert_get_sizes (AudioConvertCtx *ctx, gint samples, gint *srcsize,
gint *dstsize);
gboolean audio_convert_prepare_context (AudioConvertCtx * ctx,
AudioConvertFmt * in, AudioConvertFmt * out, DitherType dither,
NoiseShapingType ns);
gboolean audio_convert_get_sizes (AudioConvertCtx * ctx, gint samples,
gint * srcsize, gint * dstsize);
gboolean audio_convert_clean_context (AudioConvertCtx *ctx);
gboolean audio_convert_clean_context (AudioConvertCtx * ctx);
gboolean audio_convert_convert (AudioConvertCtx *ctx, gpointer src,
gpointer dst, gint samples, gboolean src_writable);
gboolean audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
gpointer dst, gint samples, gboolean src_writable);
#endif /* __AUDIO_CONVERT_H__ */
......@@ -74,6 +74,7 @@
#include "gstaudioconvert.h"
#include "gstchannelmix.h"
#include "gstaudioquantize.h"
#include "plugin.h"
GST_DEBUG_CATEGORY (audio_convert_debug);
......@@ -102,6 +103,11 @@ static GstFlowReturn gst_audio_convert_transform (GstBaseTransform * base,
GstBuffer * inbuf, GstBuffer * outbuf);
static GstFlowReturn gst_audio_convert_transform_ip (GstBaseTransform * base,
GstBuffer * buf);
static void gst_audio_convert_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_audio_convert_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
/* AudioConvert signals and args */
enum
......@@ -113,7 +119,8 @@ enum
enum
{
ARG_0,
ARG_AGGRESSIVE
ARG_DITHERING,
ARG_NOISE_SHAPING,
};
#define DEBUG_INIT(bla) \
......@@ -179,6 +186,50 @@ GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_ALWAYS,
STATIC_CAPS);
#define GST_TYPE_AUDIO_CONVERT_DITHERING (gst_audio_convert_dithering_get_type ())
static GType
gst_audio_convert_dithering_get_type (void)
{
static GType gtype = 0;
if (gtype == 0) {
static const GEnumValue values[] = {
{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"},
{0, NULL, NULL}
};
gtype = g_enum_register_static ("GstAudioConvertDithering", values);
}
return gtype;
}
#define GST_TYPE_AUDIO_CONVERT_NOISE_SHAPING (gst_audio_convert_ns_get_type ())
static GType
gst_audio_convert_ns_get_type (void)
{
static GType gtype = 0;
if (gtype == 0) {
static const GEnumValue values[] = {
{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"},
{0, NULL, NULL}
};
gtype = g_enum_register_static ("GstAudioConvertNoiseShaping", values);
}
return gtype;
}
/*** TYPE FUNCTIONS ***********************************************************/
static void
......@@ -201,12 +252,25 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass)
gint i;
gobject_class->dispose = gst_audio_convert_dispose;
gobject_class->set_property = gst_audio_convert_set_property;
gobject_class->get_property = gst_audio_convert_get_property;
supported_positions = g_new0 (GstAudioChannelPosition,
GST_AUDIO_CHANNEL_POSITION_NUM);
for (i = 0; i < GST_AUDIO_CHANNEL_POSITION_NUM; i++)
supported_positions[i] = i;
g_object_class_install_property (gobject_class, ARG_DITHERING,
g_param_spec_enum ("dithering", "Dithering",
"Selects between different dithering methods.",
GST_TYPE_AUDIO_CONVERT_DITHERING, DITHER_TPDF, G_PARAM_READWRITE));
g_object_class_install_property (gobject_class, ARG_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,
G_PARAM_READWRITE));
basetransform_class->get_unit_size =
GST_DEBUG_FUNCPTR (gst_audio_convert_get_unit_size);
basetransform_class->transform_caps =
......@@ -226,6 +290,8 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass)
static void
gst_audio_convert_init (GstAudioConvert * this, GstAudioConvertClass * g_class)
{
this->dither = DITHER_TPDF;
this->ns = NOISE_SHAPING_NONE;
memset (&this->ctx, 0, sizeof (AudioConvertCtx));
}
......@@ -672,7 +738,8 @@ gst_audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps,
if (!gst_audio_convert_parse_caps (outcaps, &out_ac_caps))
return FALSE;
if (!audio_convert_prepare_context (&this->ctx, &in_ac_caps, &out_ac_caps))
if (!audio_convert_prepare_context (&this->ctx, &in_ac_caps, &out_ac_caps,
this->dither, this->ns))
goto no_converter;
return TRUE;
......@@ -753,3 +820,41 @@ convert_error:
return GST_FLOW_ERROR;
}
}
static void
gst_audio_convert_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstAudioConvert *this = GST_AUDIO_CONVERT (object);
switch (prop_id) {
case ARG_DITHERING:
this->dither = g_value_get_enum (value);
break;
case ARG_NOISE_SHAPING:
this->ns = g_value_get_enum (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_audio_convert_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstAudioConvert *this = GST_AUDIO_CONVERT (object);
switch (prop_id) {
case ARG_DITHERING:
g_value_set_enum (value, this->dither);
break;
case ARG_NOISE_SHAPING:
g_value_set_enum (value, this->ns);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
......@@ -47,6 +47,9 @@ struct _GstAudioConvert
GstBaseTransform element;
AudioConvertCtx ctx;
DitherType dither;
NoiseShapingType ns;
};
struct _GstAudioConvertClass
......
This diff is collapsed.
/* GStreamer
* Copyright (C) 2007 Sebastian Dröge <slomo@circular-chaos.org>
*
* gstaudioquantize.h: quantizes audio to the target format and optionally
* applies dithering and noise shaping.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <gst/gst.h>
#include "audioconvert.h"
GST_DEBUG_CATEGORY_EXTERN (audio_convert_debug);
#define GST_CAT_DEFAULT (audio_convert_debug)
#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);
#endif /* __GST_AUDIO_QUANTIZE_H__ */
......@@ -87,6 +87,8 @@ setup_audioconvert (GstCaps * outcaps)
GST_DEBUG ("setup_audioconvert with caps %" GST_PTR_FORMAT, outcaps);
audioconvert = gst_check_setup_element ("audioconvert");
g_object_set (G_OBJECT (audioconvert), "dithering", 0, NULL);
g_object_set (G_OBJECT (audioconvert), "noise-shaping", 0, NULL);
mysrcpad = gst_check_setup_src_pad (audioconvert, &srctemplate, NULL);
mysinkpad = gst_check_setup_sink_pad (audioconvert, &sinktemplate, NULL);
/* this installs a getcaps func that will always return the caps we set
......@@ -532,7 +534,7 @@ GST_START_TEST (test_int_conversion)
gint16 out[] = { 0, G_MININT16, G_MAXINT16,
32, 33, 32,
33, 31,
-32, -33,
-31, -32,
-31, -33,
-32
};
......@@ -633,9 +635,9 @@ GST_START_TEST (test_float_conversion)
{
gint16 in[] = { 0, -32768, 16384, -16384 };
gdouble out[] = { 0.0,
4.6566128752457969e-10 * (gdouble) (-32768L << 16), /* ~ -1.0 */
4.6566128752457969e-10 * (gdouble) (16384L << 16), /* ~ 0.5 */
4.6566128752457969e-10 * (gdouble) (-16384L << 16), /* ~ -0.5 */
(gdouble) (-32768L << 16) / 2147483647.0, /* ~ -1.0 */
(gdouble) (16384L << 16) / 2147483647.0, /* ~ 0.5 */
(gdouble) (-16384L << 16) / 2147483647.0, /* ~ -0.5 */
};
RUN_CONVERSION ("16 signed to 64 float",
......@@ -645,9 +647,9 @@ GST_START_TEST (test_float_conversion)
{
gint32 in[] = { 0, (-1L << 31), (1L << 30), (-1L << 30) };
gdouble out[] = { 0.0,
4.6566128752457969e-10 * (gdouble) (-1L << 31), /* ~ -1.0 */
4.6566128752457969e-10 * (gdouble) (1L << 30), /* ~ 0.5 */
4.6566128752457969e-10 * (gdouble) (-1L << 30), /* ~ -0.5 */
(gdouble) (-1L << 31) / 2147483647.0, /* ~ -1.0 */
(gdouble) (1L << 30) / 2147483647.0, /* ~ 0.5 */
(gdouble) (-1L << 30) / 2147483647.0, /* ~ -0.5 */
};
RUN_CONVERSION ("32 signed to 64 float",
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment