mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-03-29 20:35:40 +00:00
audioconvert: move audio quantize code to libs
Move the audio quantize code from audioconvert to the audio library. work on making an audio converter helper function similar to the video converter. Fold fastrandom directly into the quantizer, add some ORC code to optimize this later.
This commit is contained in:
parent
a7789854d5
commit
c36ac3ce45
11 changed files with 353 additions and 258 deletions
|
@ -7,6 +7,7 @@ glib_enum_headers= \
|
|||
audio-format.h \
|
||||
audio-channels.h \
|
||||
audio-info.h \
|
||||
audio-quantize.h \
|
||||
gstaudioringbuffer.h
|
||||
|
||||
glib_enum_define = GST_AUDIO
|
||||
|
@ -27,6 +28,7 @@ libgstaudio_@GST_API_VERSION@_la_SOURCES = \
|
|||
audio-format.c \
|
||||
audio-channels.c \
|
||||
audio-info.c \
|
||||
audio-quantize.c \
|
||||
gstaudioringbuffer.c \
|
||||
gstaudioclock.c \
|
||||
gstaudiocdsrc.c \
|
||||
|
@ -50,6 +52,7 @@ libgstaudio_@GST_API_VERSION@include_HEADERS = \
|
|||
audio-format.h \
|
||||
audio-channels.h \
|
||||
audio-info.h \
|
||||
audio-quantize.h \
|
||||
gstaudioringbuffer.h \
|
||||
gstaudioclock.h \
|
||||
gstaudiofilter.h \
|
||||
|
|
|
@ -29,10 +29,9 @@
|
|||
#include <gst/gst.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "gstaudioconvertorc.h"
|
||||
#include "gstaudioquantize.h"
|
||||
|
||||
#include "gstfastrandom.h"
|
||||
#include "gstaudiopack.h"
|
||||
#include "audio-quantize.h"
|
||||
|
||||
typedef void (*QuantizeFunc) (GstAudioQuantize * quant, const gpointer src,
|
||||
gpointer dst, gint count);
|
||||
|
@ -85,10 +84,26 @@ 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,
|
||||
audio_orc_int_bias (dst, src, quant->bias, ~quant->mask,
|
||||
samples * quant->channels);
|
||||
}
|
||||
|
||||
/* This is the base function, implementing a linear congruential generator
|
||||
* and returning a pseudo random number between 0 and 2^32 - 1.
|
||||
*/
|
||||
static inline guint32
|
||||
gst_fast_random_uint32 (void)
|
||||
{
|
||||
static guint32 state = 0xdeadbeef;
|
||||
return (state = state * 1103515245 + 12345);
|
||||
}
|
||||
|
||||
static inline gint32
|
||||
gst_fast_random_int32 (void)
|
||||
{
|
||||
return (gint32) gst_fast_random_uint32 ();
|
||||
}
|
||||
|
||||
/* Assuming dither == 2^n,
|
||||
* returns one of 2^(n+1) possible random values:
|
||||
* -dither <= retval < dither */
|
||||
|
@ -155,7 +170,7 @@ gst_audio_quantize_quantize_int_dither_none (GstAudioQuantize * quant,
|
|||
{
|
||||
setup_dither_buf (quant, samples);
|
||||
|
||||
audio_convert_orc_int_dither (dst, src, quant->dither_buf, ~quant->mask,
|
||||
audio_orc_int_dither (dst, src, quant->dither_buf, ~quant->mask,
|
||||
samples * quant->channels);
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
#include <gst/audio/audio-format.h>
|
||||
#include <gst/audio/audio-channels.h>
|
||||
#include <gst/audio/audio-info.h>
|
||||
#include <gst/audio/audio-quantize.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
|
|
@ -380,3 +380,31 @@ copyl d1, p1
|
|||
|
||||
copyq d1, p1
|
||||
|
||||
.function audio_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_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
|
||||
|
||||
.function audio_orc_update_rand
|
||||
.dest 4 r guint32
|
||||
.temp 4 t
|
||||
|
||||
mulll t, r, 1103515245
|
||||
addl r, t, 12345
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ libgstaudioconvert_la_SOURCES = \
|
|||
gstaudioconvert.c \
|
||||
audioconvert.c \
|
||||
gstchannelmix.c \
|
||||
gstaudioquantize.c \
|
||||
plugin.c
|
||||
nodist_libgstaudioconvert_la_SOURCES = $(ORC_NODIST_SOURCES)
|
||||
|
||||
|
@ -22,6 +21,4 @@ noinst_HEADERS = \
|
|||
gstaudioconvert.h \
|
||||
audioconvert.h \
|
||||
gstchannelmix.h \
|
||||
gstaudioquantize.h \
|
||||
gstfastrandom.h \
|
||||
plugin.h
|
||||
|
|
|
@ -27,10 +27,11 @@
|
|||
#include <string.h>
|
||||
|
||||
#include "gstchannelmix.h"
|
||||
#include "gstaudioquantize.h"
|
||||
#include "audioconvert.h"
|
||||
#include "gstaudioconvertorc.h"
|
||||
|
||||
typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count);
|
||||
|
||||
/**
|
||||
* int/int int/float float/int float/float
|
||||
*
|
||||
|
@ -41,29 +42,160 @@
|
|||
* quantize S32 S32
|
||||
* pack S32 F64 S32 F64
|
||||
*/
|
||||
gboolean
|
||||
audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
|
||||
GstAudioInfo * out, GstAudioDitherMethod dither,
|
||||
GstAudioNoiseShapingMethod ns)
|
||||
struct _GstAudioConverter
|
||||
{
|
||||
GstAudioInfo in;
|
||||
GstAudioInfo out;
|
||||
|
||||
GstStructure *config;
|
||||
|
||||
gboolean in_default;
|
||||
|
||||
AudioConvertFunc convert_in;
|
||||
|
||||
GstAudioFormat mix_format;
|
||||
gboolean mix_passthrough;
|
||||
GstChannelMix *mix;
|
||||
|
||||
AudioConvertFunc convert_out;
|
||||
|
||||
GstAudioQuantize *quant;
|
||||
|
||||
gboolean out_default;
|
||||
|
||||
gboolean passthrough;
|
||||
|
||||
gpointer tmpbuf;
|
||||
gpointer tmpbuf2;
|
||||
gint tmpbufsize;
|
||||
};
|
||||
|
||||
/*
|
||||
static guint
|
||||
get_opt_uint (GstAudioConverter * convert, const gchar * opt, guint def)
|
||||
{
|
||||
guint res;
|
||||
if (!gst_structure_get_uint (convert->config, opt, &res))
|
||||
res = def;
|
||||
return res;
|
||||
}
|
||||
*/
|
||||
|
||||
static gint
|
||||
get_opt_enum (GstAudioConverter * convert, const gchar * opt, GType type,
|
||||
gint def)
|
||||
{
|
||||
gint res;
|
||||
if (!gst_structure_get_enum (convert->config, opt, type, &res))
|
||||
res = def;
|
||||
return res;
|
||||
}
|
||||
|
||||
#define DEFAULT_OPT_DITHER_METHOD GST_AUDIO_DITHER_NONE
|
||||
#define DEFAULT_OPT_NOISE_SHAPING_METHOD GST_AUDIO_NOISE_SHAPING_NONE
|
||||
#define DEFAULT_OPT_QUANTIZATION 1
|
||||
|
||||
#define GET_OPT_DITHER_METHOD(c) get_opt_enum(c, \
|
||||
GST_AUDIO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_AUDIO_DITHER_METHOD, \
|
||||
DEFAULT_OPT_DITHER_METHOD)
|
||||
#define GET_OPT_NOISE_SHAPING_METHOD(c) get_opt_enum(c, \
|
||||
GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD, GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, \
|
||||
DEFAULT_OPT_NOISE_SHAPING_METHOD)
|
||||
#define GET_OPT_QUANTIZATION(c) get_opt_uint(c, \
|
||||
GST_AUDIO_CONVERTER_OPT_QUANTIZATION, DEFAULT_OPT_QUANTIZATION)
|
||||
|
||||
static gboolean
|
||||
copy_config (GQuark field_id, const GValue * value, gpointer user_data)
|
||||
{
|
||||
GstAudioConverter *convert = user_data;
|
||||
|
||||
gst_structure_id_set_value (convert->config, field_id, value);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_audio_converter_set_config:
|
||||
* @convert: a #GstAudioConverter
|
||||
* @config: (transfer full): a #GstStructure
|
||||
*
|
||||
* Set @config as extra configuraion for @convert.
|
||||
*
|
||||
* If the parameters in @config can not be set exactly, this function returns
|
||||
* %FALSE and will try to update as much state as possible. The new state can
|
||||
* then be retrieved and refined with gst_audio_converter_get_config().
|
||||
*
|
||||
* Look at the #GST_AUDIO_CONVERTER_OPT_* fields to check valid configuration
|
||||
* option and values.
|
||||
*
|
||||
* Returns: %TRUE when @config could be set.
|
||||
*/
|
||||
gboolean
|
||||
gst_audio_converter_set_config (GstAudioConverter * convert,
|
||||
GstStructure * config)
|
||||
{
|
||||
g_return_val_if_fail (convert != NULL, FALSE);
|
||||
g_return_val_if_fail (config != NULL, FALSE);
|
||||
|
||||
gst_structure_foreach (config, copy_config, convert);
|
||||
gst_structure_free (config);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_audio_converter_get_config:
|
||||
* @convert: a #GstAudioConverter
|
||||
*
|
||||
* Get the current configuration of @convert.
|
||||
*
|
||||
* Returns: a #GstStructure that remains valid for as long as @convert is valid
|
||||
* or until gst_audio_converter_set_config() is called.
|
||||
*/
|
||||
const GstStructure *
|
||||
gst_audio_converter_get_config (GstAudioConverter * convert)
|
||||
{
|
||||
g_return_val_if_fail (convert != NULL, NULL);
|
||||
|
||||
return convert->config;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
GstAudioConverter *
|
||||
gst_audio_converter_new (GstAudioInfo * in, GstAudioInfo * out,
|
||||
GstStructure * config)
|
||||
{
|
||||
GstAudioConverter *convert;
|
||||
gint in_depth, out_depth;
|
||||
GstChannelMixFlags flags;
|
||||
gboolean in_int, out_int;
|
||||
GstAudioFormat format;
|
||||
GstAudioDitherMethod dither;
|
||||
GstAudioNoiseShapingMethod ns;
|
||||
|
||||
g_return_val_if_fail (ctx != NULL, FALSE);
|
||||
g_return_val_if_fail (in != NULL, FALSE);
|
||||
g_return_val_if_fail (out != NULL, FALSE);
|
||||
|
||||
/* first clean the existing context */
|
||||
audio_convert_clean_context (ctx);
|
||||
if ((GST_AUDIO_INFO_CHANNELS (in) != GST_AUDIO_INFO_CHANNELS (out)) &&
|
||||
(GST_AUDIO_INFO_IS_UNPOSITIONED (in)
|
||||
|| GST_AUDIO_INFO_IS_UNPOSITIONED (out)))
|
||||
goto unpositioned;
|
||||
|
||||
ctx->in = *in;
|
||||
ctx->out = *out;
|
||||
convert = g_slice_new0 (GstAudioConverter);
|
||||
|
||||
convert->in = *in;
|
||||
convert->out = *out;
|
||||
|
||||
/* default config */
|
||||
convert->config = gst_structure_new_empty ("GstAudioConverter");
|
||||
if (config)
|
||||
gst_audio_converter_set_config (convert, config);
|
||||
|
||||
dither = GET_OPT_DITHER_METHOD (convert);
|
||||
ns = GET_OPT_NOISE_SHAPING_METHOD (convert);
|
||||
|
||||
GST_INFO ("unitsizes: %d -> %d", in->bpf, out->bpf);
|
||||
|
||||
|
@ -85,7 +217,7 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
|
|||
|
||||
/* step 1, unpack */
|
||||
format = in->finfo->unpack_format;
|
||||
ctx->in_default = in->finfo->unpack_format == in->finfo->format;
|
||||
convert->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));
|
||||
|
@ -93,23 +225,23 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
|
|||
/* 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;
|
||||
convert->convert_in = (AudioConvertFunc) audio_convert_orc_s32_to_double;
|
||||
format = GST_AUDIO_FORMAT_F64;
|
||||
}
|
||||
|
||||
/* step 3, channel mix */
|
||||
ctx->mix_format = format;
|
||||
ctx->mix = gst_channel_mix_new (flags, in->channels, in->position,
|
||||
convert->mix_format = format;
|
||||
convert->mix = gst_channel_mix_new (flags, in->channels, in->position,
|
||||
out->channels, out->position);
|
||||
ctx->mix_passthrough = gst_channel_mix_is_passthrough (ctx->mix);
|
||||
convert->mix_passthrough = gst_channel_mix_is_passthrough (convert->mix);
|
||||
GST_INFO ("mix format %s, passthrough %d, in_channels %d, out_channels %d",
|
||||
gst_audio_format_to_string (format), ctx->mix_passthrough,
|
||||
gst_audio_format_to_string (format), convert->mix_passthrough,
|
||||
in->channels, out->channels);
|
||||
|
||||
/* step 4, optional convert for quantize */
|
||||
if (!in_int && out_int) {
|
||||
GST_INFO ("convert F64 to S32");
|
||||
ctx->convert_out = (AudioConvertFunc) audio_convert_orc_double_to_s32;
|
||||
convert->convert_out = (AudioConvertFunc) audio_convert_orc_double_to_s32;
|
||||
format = GST_AUDIO_FORMAT_S32;
|
||||
}
|
||||
/* step 5, optional quantize */
|
||||
|
@ -132,78 +264,72 @@ audio_convert_prepare_context (AudioConvertCtx * ctx, GstAudioInfo * in,
|
|||
* 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,
|
||||
convert->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;
|
||||
convert->out_default = format == out->finfo->format;
|
||||
GST_INFO ("pack format %s to %s", gst_audio_format_to_string (format),
|
||||
gst_audio_format_to_string (out->finfo->format));
|
||||
|
||||
/* optimize */
|
||||
if (out->finfo->format == in->finfo->format && ctx->mix_passthrough) {
|
||||
if (out->finfo->format == in->finfo->format && convert->mix_passthrough) {
|
||||
GST_INFO ("same formats and passthrough mixing -> passthrough");
|
||||
ctx->passthrough = TRUE;
|
||||
convert->passthrough = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return convert;
|
||||
|
||||
/* ERRORS */
|
||||
unpositioned:
|
||||
{
|
||||
GST_WARNING ("unpositioned channels");
|
||||
return FALSE;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
audio_convert_clean_context (AudioConvertCtx * ctx)
|
||||
void
|
||||
gst_audio_converter_free (GstAudioConverter * convert)
|
||||
{
|
||||
g_return_val_if_fail (ctx != NULL, FALSE);
|
||||
g_return_if_fail (convert != NULL);
|
||||
|
||||
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_in = NULL;
|
||||
ctx->convert_out = NULL;
|
||||
if (convert->quant)
|
||||
gst_audio_quantize_free (convert->quant);
|
||||
if (convert->mix)
|
||||
gst_channel_mix_free (convert->mix);
|
||||
gst_audio_info_init (&convert->in);
|
||||
gst_audio_info_init (&convert->out);
|
||||
|
||||
g_free (ctx->tmpbuf);
|
||||
g_free (ctx->tmpbuf2);
|
||||
ctx->tmpbuf = NULL;
|
||||
ctx->tmpbuf2 = NULL;
|
||||
ctx->tmpbufsize = 0;
|
||||
g_free (convert->tmpbuf);
|
||||
g_free (convert->tmpbuf2);
|
||||
gst_structure_free (convert->config);
|
||||
|
||||
return TRUE;
|
||||
g_slice_free (GstAudioConverter, convert);
|
||||
}
|
||||
|
||||
gboolean
|
||||
audio_convert_get_sizes (AudioConvertCtx * ctx, gint samples, gint * srcsize,
|
||||
gint * dstsize)
|
||||
gst_audio_converter_get_sizes (GstAudioConverter * convert, gint samples,
|
||||
gint * srcsize, gint * dstsize)
|
||||
{
|
||||
g_return_val_if_fail (ctx != NULL, FALSE);
|
||||
g_return_val_if_fail (convert != NULL, FALSE);
|
||||
|
||||
if (srcsize)
|
||||
*srcsize = samples * ctx->in.bpf;
|
||||
*srcsize = samples * convert->in.bpf;
|
||||
if (dstsize)
|
||||
*dstsize = samples * ctx->out.bpf;
|
||||
*dstsize = samples * convert->out.bpf;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
|
||||
gpointer dst, gint samples, gboolean src_writable)
|
||||
gst_audio_converter_samples (GstAudioConverter * convert,
|
||||
GstAudioConverterFlags flags, gpointer src, gpointer dst, gint samples)
|
||||
{
|
||||
guint size;
|
||||
gpointer outbuf, tmpbuf, tmpbuf2;
|
||||
|
||||
g_return_val_if_fail (ctx != NULL, FALSE);
|
||||
g_return_val_if_fail (convert != NULL, FALSE);
|
||||
g_return_val_if_fail (src != NULL, FALSE);
|
||||
g_return_val_if_fail (dst != NULL, FALSE);
|
||||
g_return_val_if_fail (samples >= 0, FALSE);
|
||||
|
@ -211,86 +337,88 @@ audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
|
|||
if (samples == 0)
|
||||
return TRUE;
|
||||
|
||||
if (ctx->passthrough) {
|
||||
memcpy (dst, src, samples * ctx->in.bpf);
|
||||
if (convert->passthrough) {
|
||||
memcpy (dst, src, samples * convert->in.bpf);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
size = sizeof (gdouble) * samples * MAX (ctx->in.channels, ctx->out.channels);
|
||||
size =
|
||||
sizeof (gdouble) * samples * MAX (convert->in.channels,
|
||||
convert->out.channels);
|
||||
|
||||
if (size > ctx->tmpbufsize) {
|
||||
ctx->tmpbuf = g_realloc (ctx->tmpbuf, size);
|
||||
ctx->tmpbuf2 = g_realloc (ctx->tmpbuf2, size);
|
||||
ctx->tmpbufsize = size;
|
||||
if (size > convert->tmpbufsize) {
|
||||
convert->tmpbuf = g_realloc (convert->tmpbuf, size);
|
||||
convert->tmpbuf2 = g_realloc (convert->tmpbuf2, size);
|
||||
convert->tmpbufsize = size;
|
||||
}
|
||||
tmpbuf = ctx->tmpbuf;
|
||||
tmpbuf2 = ctx->tmpbuf2;
|
||||
tmpbuf = convert->tmpbuf;
|
||||
tmpbuf2 = convert->tmpbuf2;
|
||||
|
||||
/* 1. unpack */
|
||||
if (!ctx->in_default) {
|
||||
if (!ctx->convert_in && ctx->mix_passthrough && !ctx->convert_out
|
||||
&& !ctx->quant && ctx->out_default)
|
||||
if (!convert->in_default) {
|
||||
if (!convert->convert_in && convert->mix_passthrough
|
||||
&& !convert->convert_out && !convert->quant && convert->out_default)
|
||||
outbuf = dst;
|
||||
else
|
||||
outbuf = tmpbuf;
|
||||
|
||||
ctx->in.finfo->unpack_func (ctx->in.finfo,
|
||||
convert->in.finfo->unpack_func (convert->in.finfo,
|
||||
GST_AUDIO_PACK_FLAG_TRUNCATE_RANGE, outbuf, src,
|
||||
samples * ctx->in.channels);
|
||||
samples * convert->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)
|
||||
if (convert->convert_in) {
|
||||
if (convert->mix_passthrough && !convert->convert_out && !convert->quant
|
||||
&& convert->out_default)
|
||||
outbuf = dst;
|
||||
else if (src == tmpbuf)
|
||||
outbuf = tmpbuf2;
|
||||
else
|
||||
outbuf = tmpbuf;
|
||||
|
||||
ctx->convert_in (outbuf, src, samples * ctx->in.channels);
|
||||
convert->convert_in (outbuf, src, samples * convert->in.channels);
|
||||
src = outbuf;
|
||||
}
|
||||
|
||||
/* step 3, channel mix if not passthrough */
|
||||
if (!ctx->mix_passthrough) {
|
||||
if (!ctx->convert_out && !ctx->quant && ctx->out_default)
|
||||
if (!convert->mix_passthrough) {
|
||||
if (!convert->convert_out && !convert->quant && convert->out_default)
|
||||
outbuf = dst;
|
||||
else
|
||||
outbuf = tmpbuf;
|
||||
|
||||
gst_channel_mix_mix (ctx->mix, ctx->mix_format, ctx->in.layout, src, outbuf,
|
||||
samples);
|
||||
gst_channel_mix_mix (convert->mix, convert->mix_format, convert->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)
|
||||
if (convert->convert_out) {
|
||||
if (!convert->quant && convert->out_default)
|
||||
outbuf = dst;
|
||||
else
|
||||
outbuf = tmpbuf;
|
||||
|
||||
ctx->convert_out (outbuf, src, samples * ctx->out.channels);
|
||||
convert->convert_out (outbuf, src, samples * convert->out.channels);
|
||||
src = outbuf;
|
||||
}
|
||||
|
||||
/* step 5, optional quantize */
|
||||
if (ctx->quant) {
|
||||
if (ctx->out_default)
|
||||
if (convert->quant) {
|
||||
if (convert->out_default)
|
||||
outbuf = dst;
|
||||
else
|
||||
outbuf = tmpbuf;
|
||||
|
||||
gst_audio_quantize_samples (ctx->quant, outbuf, src, samples);
|
||||
gst_audio_quantize_samples (convert->quant, outbuf, src, samples);
|
||||
src = outbuf;
|
||||
}
|
||||
|
||||
/* step 6, pack */
|
||||
if (!ctx->out_default) {
|
||||
ctx->out.finfo->pack_func (ctx->out.finfo, 0, src, dst,
|
||||
samples * ctx->out.channels);
|
||||
if (!convert->out_default) {
|
||||
convert->out.finfo->pack_func (convert->out.finfo, 0, src, dst,
|
||||
samples * convert->out.channels);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2004 Ronald Bultje <rbultje@ronald.bitfreak.net>
|
||||
* (C) 2015 Wim Taymans <wim.taymans@gmail.com>
|
||||
*
|
||||
* audioconvert.h: audio format conversion library
|
||||
* audioconverter.h: audio format conversion library
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
|
@ -19,57 +20,73 @@
|
|||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_CONVERT_H__
|
||||
#define __AUDIO_CONVERT_H__
|
||||
#ifndef __GST_AUDIO_CONVERTER_H__
|
||||
#define __GST_AUDIO_CONVERTER_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#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)
|
||||
typedef struct _GstAudioConverter GstAudioConverter;
|
||||
|
||||
typedef struct _AudioConvertCtx AudioConvertCtx;
|
||||
/**
|
||||
* GST_AUDIO_CONVERTER_OPT_DITHER_METHOD:
|
||||
*
|
||||
* #GST_TYPE_AUDIO_DITHER_METHOD, The dither method to use when
|
||||
* changing bit depth.
|
||||
* Default is #GST_AUDIO_DITHER_NONE.
|
||||
*/
|
||||
#define GST_AUDIO_CONVERTER_OPT_DITHER_METHOD "GstAudioConverter.dither-method"
|
||||
|
||||
typedef void (*AudioConvertFunc) (gpointer dst, const gpointer src, gint count);
|
||||
/**
|
||||
* GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD:
|
||||
*
|
||||
* #GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, The noise shaping method to use
|
||||
* to mask noise from quantization errors.
|
||||
* Default is #GST_AUDIO_NOISE_SHAPING_NONE.
|
||||
*/
|
||||
#define GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD "GstAudioConverter.noise-shaping-method"
|
||||
|
||||
struct _AudioConvertCtx
|
||||
{
|
||||
GstAudioInfo in;
|
||||
GstAudioInfo out;
|
||||
/**
|
||||
* GST_AUDIO_CONVERTER_OPT_QUANTIZATION:
|
||||
*
|
||||
* #G_TYPE_UINT, The quantization amount. Components will be
|
||||
* quantized to multiples of this value.
|
||||
* Default is 1
|
||||
*/
|
||||
#define GST_AUDIO_CONVERTER_OPT_QUANTIZATION "GstAudioConverter.quantization"
|
||||
|
||||
gboolean in_default;
|
||||
|
||||
AudioConvertFunc convert_in;
|
||||
/**
|
||||
* @GST_AUDIO_CONVERTER_FLAG_NONE: no flag
|
||||
* @GST_AUDIO_CONVERTER_FLAG_SOURCE_WRITABLE: the source is writable and can be
|
||||
* used as temporary storage during conversion.
|
||||
*
|
||||
* Extra flags passed to gst_audio_converter_samples().
|
||||
*/
|
||||
typedef enum {
|
||||
GST_AUDIO_CONVERTER_FLAG_NONE = 0,
|
||||
GST_AUDIO_CONVERTER_FLAG_SOURCE_WRITABLE = (1 << 0)
|
||||
} GstAudioConverterFlags;
|
||||
|
||||
GstAudioFormat mix_format;
|
||||
gboolean mix_passthrough;
|
||||
GstChannelMix *mix;
|
||||
GstAudioConverter * gst_audio_converter_new (GstAudioInfo *in_info,
|
||||
GstAudioInfo *out_info,
|
||||
GstStructure *config);
|
||||
|
||||
AudioConvertFunc convert_out;
|
||||
void gst_audio_converter_free (GstAudioConverter * convert);
|
||||
|
||||
GstAudioQuantize *quant;
|
||||
gboolean gst_audio_converter_set_config (GstAudioConverter * convert, GstStructure *config);
|
||||
const GstStructure * gst_audio_converter_get_config (GstAudioConverter * convert);
|
||||
|
||||
gboolean out_default;
|
||||
|
||||
gboolean passthrough;
|
||||
gboolean gst_audio_converter_get_sizes (GstAudioConverter * convert,
|
||||
gint samples,
|
||||
gint * srcsize, gint * dstsize);
|
||||
|
||||
gpointer tmpbuf;
|
||||
gpointer tmpbuf2;
|
||||
gint tmpbufsize;
|
||||
};
|
||||
gboolean gst_audio_converter_samples (GstAudioConverter * convert,
|
||||
GstAudioConverterFlags flags,
|
||||
gpointer src, gpointer dst,
|
||||
gint samples);
|
||||
|
||||
gboolean audio_convert_prepare_context (AudioConvertCtx * ctx,
|
||||
GstAudioInfo * in, GstAudioInfo * out,
|
||||
GstAudioDitherMethod dither, GstAudioNoiseShapingMethod ns);
|
||||
gboolean audio_convert_get_sizes (AudioConvertCtx * ctx, gint samples,
|
||||
gint * srcsize, gint * dstsize);
|
||||
|
||||
gboolean audio_convert_clean_context (AudioConvertCtx * ctx);
|
||||
|
||||
gboolean audio_convert_convert (AudioConvertCtx * ctx, gpointer src,
|
||||
gpointer dst, gint samples, gboolean src_writable);
|
||||
|
||||
#endif /* __AUDIO_CONVERT_H__ */
|
||||
#endif /* __GST_AUDIO_CONVERTER_H__ */
|
||||
|
|
|
@ -64,7 +64,6 @@
|
|||
|
||||
#include "gstaudioconvert.h"
|
||||
#include "gstchannelmix.h"
|
||||
#include "gstaudioquantize.h"
|
||||
#include "plugin.h"
|
||||
|
||||
GST_DEBUG_CATEGORY (audio_convert_debug);
|
||||
|
@ -134,51 +133,6 @@ 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[] = {
|
||||
{GST_AUDIO_DITHER_NONE, "No dithering",
|
||||
"none"},
|
||||
{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}
|
||||
};
|
||||
|
||||
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[] = {
|
||||
{GST_AUDIO_NOISE_SHAPING_NONE, "No noise shaping (default)",
|
||||
"none"},
|
||||
{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}
|
||||
};
|
||||
|
||||
gtype = g_enum_register_static ("GstAudioConvertNoiseShaping", values);
|
||||
}
|
||||
return gtype;
|
||||
}
|
||||
|
||||
|
||||
/*** TYPE FUNCTIONS ***********************************************************/
|
||||
static void
|
||||
|
@ -195,13 +149,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, GST_AUDIO_DITHER_TPDF,
|
||||
GST_TYPE_AUDIO_DITHER_METHOD, 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, GST_AUDIO_NOISE_SHAPING_NONE,
|
||||
GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, GST_AUDIO_NOISE_SHAPING_NONE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
|
@ -235,7 +189,6 @@ gst_audio_convert_init (GstAudioConvert * this)
|
|||
{
|
||||
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);
|
||||
}
|
||||
|
@ -245,7 +198,7 @@ gst_audio_convert_dispose (GObject * obj)
|
|||
{
|
||||
GstAudioConvert *this = GST_AUDIO_CONVERT (obj);
|
||||
|
||||
audio_convert_clean_context (&this->ctx);
|
||||
gst_audio_converter_free (this->convert);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (obj);
|
||||
}
|
||||
|
@ -694,15 +647,29 @@ gst_audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps,
|
|||
GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %"
|
||||
GST_PTR_FORMAT, incaps, outcaps);
|
||||
|
||||
if (this->convert) {
|
||||
gst_audio_converter_free (this->convert);
|
||||
this->convert = NULL;
|
||||
}
|
||||
|
||||
if (!gst_audio_info_from_caps (&in_info, incaps))
|
||||
goto invalid_in;
|
||||
if (!gst_audio_info_from_caps (&out_info, outcaps))
|
||||
goto invalid_out;
|
||||
|
||||
if (!audio_convert_prepare_context (&this->ctx, &in_info, &out_info,
|
||||
this->dither, this->ns))
|
||||
this->convert = gst_audio_converter_new (&in_info, &out_info,
|
||||
gst_structure_new ("GstAudioConverterConfig",
|
||||
GST_AUDIO_CONVERTER_OPT_DITHER_METHOD, GST_TYPE_AUDIO_DITHER_METHOD,
|
||||
this->dither,
|
||||
GST_AUDIO_CONVERTER_OPT_NOISE_SHAPING_METHOD,
|
||||
GST_TYPE_AUDIO_NOISE_SHAPING_METHOD, this->ns, NULL));
|
||||
|
||||
if (this->convert == NULL)
|
||||
goto no_converter;
|
||||
|
||||
this->in_info = in_info;
|
||||
this->out_info = out_info;
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
|
@ -718,7 +685,7 @@ invalid_out:
|
|||
}
|
||||
no_converter:
|
||||
{
|
||||
GST_ERROR_OBJECT (base, "could not find converter");
|
||||
GST_ERROR_OBJECT (base, "could not make converter");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -732,15 +699,17 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
|
|||
GstMapInfo srcmap, dstmap;
|
||||
gint insize, outsize;
|
||||
gboolean inbuf_writable;
|
||||
GstAudioConverterFlags flags;
|
||||
|
||||
gint samples;
|
||||
|
||||
/* get amount of samples to convert. */
|
||||
samples = gst_buffer_get_size (inbuf) / this->ctx.in.bpf;
|
||||
samples = gst_buffer_get_size (inbuf) / this->in_info.bpf;
|
||||
|
||||
/* get in/output sizes, to see if the buffers we got are of correct
|
||||
* sizes */
|
||||
if (!audio_convert_get_sizes (&this->ctx, samples, &insize, &outsize))
|
||||
if (!gst_audio_converter_get_sizes (this->convert, samples, &insize,
|
||||
&outsize))
|
||||
goto error;
|
||||
|
||||
if (insize == 0 || outsize == 0)
|
||||
|
@ -762,13 +731,17 @@ gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf,
|
|||
goto wrong_size;
|
||||
|
||||
/* and convert the samples */
|
||||
flags = 0;
|
||||
if (inbuf_writable)
|
||||
flags |= GST_AUDIO_CONVERTER_FLAG_SOURCE_WRITABLE;
|
||||
|
||||
if (!GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) {
|
||||
if (!audio_convert_convert (&this->ctx, srcmap.data, dstmap.data,
|
||||
samples, inbuf_writable))
|
||||
if (!gst_audio_converter_samples (this->convert, flags, srcmap.data,
|
||||
dstmap.data, samples))
|
||||
goto convert_error;
|
||||
} else {
|
||||
/* Create silence buffer */
|
||||
gst_audio_format_fill_silence (this->ctx.out.finfo, dstmap.data, outsize);
|
||||
gst_audio_format_fill_silence (this->out_info.finfo, dstmap.data, outsize);
|
||||
}
|
||||
ret = GST_FLOW_OK;
|
||||
|
||||
|
@ -829,8 +802,8 @@ gst_audio_convert_submit_input_buffer (GstBaseTransform * base,
|
|||
|
||||
if (base->segment.format == GST_FORMAT_TIME) {
|
||||
input =
|
||||
gst_audio_buffer_clip (input, &base->segment, this->ctx.in.rate,
|
||||
this->ctx.in.bpf);
|
||||
gst_audio_buffer_clip (input, &base->segment, this->in_info.rate,
|
||||
this->in_info.bpf);
|
||||
|
||||
if (!input)
|
||||
return GST_FLOW_OK;
|
||||
|
|
|
@ -46,10 +46,13 @@ struct _GstAudioConvert
|
|||
{
|
||||
GstBaseTransform element;
|
||||
|
||||
AudioConvertCtx ctx;
|
||||
|
||||
/* properties */
|
||||
GstAudioDitherMethod dither;
|
||||
GstAudioNoiseShapingMethod ns;
|
||||
|
||||
GstAudioInfo in_info;
|
||||
GstAudioInfo out_info;
|
||||
GstAudioConverter *convert;
|
||||
};
|
||||
|
||||
struct _GstAudioConvertClass
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
|
||||
*
|
||||
* gstfastrandom.h: Fast, bad PNRG
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#ifndef __GST_FAST_RANDOM__
|
||||
#define __GST_FAST_RANDOM__
|
||||
|
||||
/* transform [0..2^32] -> [0..1] */
|
||||
#define GST_RAND_DOUBLE_TRANSFORM 2.3283064365386962890625e-10
|
||||
|
||||
/* This is the base function, implementing a linear congruential generator
|
||||
* and returning a pseudo random number between 0 and 2^32 - 1.
|
||||
*/
|
||||
static inline guint32
|
||||
gst_fast_random_uint32 (void)
|
||||
{
|
||||
static guint32 state = 0xdeadbeef;
|
||||
|
||||
return (state = state * 1103515245 + 12345);
|
||||
}
|
||||
|
||||
static inline gint32
|
||||
gst_fast_random_int32 (void)
|
||||
{
|
||||
return (gint32) gst_fast_random_uint32 ();
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
gst_fast_random_double (void)
|
||||
{
|
||||
gdouble ret;
|
||||
|
||||
ret = gst_fast_random_uint32 () * GST_RAND_DOUBLE_TRANSFORM;
|
||||
ret = (ret + gst_fast_random_uint32 ()) * GST_RAND_DOUBLE_TRANSFORM;
|
||||
|
||||
if (ret >= 1.0)
|
||||
return gst_fast_random_double ();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline gdouble
|
||||
gst_fast_random_double_range (gdouble start, gdouble end)
|
||||
{
|
||||
return gst_fast_random_double () * (end - start) + start;
|
||||
}
|
||||
|
||||
#undef GST_RAND_DOUBLE_TRANSFORM
|
||||
|
||||
#endif /* __GST_FAST_RANDOM__ */
|
||||
|
Loading…
Reference in a new issue