From ceb84de91677d60d4cb42084c300a6cd104936d5 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 26 Aug 2005 15:43:56 +0000 Subject: [PATCH] gst/audioconvert/: Cleanups, librarify a bit, optimize, better negotiation and more. Original commit message from CVS: * gst/audioconvert/Makefile.am: * gst/audioconvert/audioconvert.c: (if), (float), (audio_convert_get_func_index), (check_default), (audio_convert_clean_fmt), (audio_convert_prepare_context), (audio_convert_clean_context), (audio_convert_get_sizes), (get_temp_buffer), (audio_convert_convert): * gst/audioconvert/audioconvert.h: * gst/audioconvert/gstaudioconvert.c: (gst_audio_convert_class_init), (gst_audio_convert_init), (gst_audio_convert_dispose), (gst_audio_convert_parse_caps), (gst_audio_convert_get_unit_size), (gst_audio_convert_transform_caps), (gst_audio_convert_fixate_caps), (gst_audio_convert_set_caps), (gst_audio_convert_transform_ip), (gst_audio_convert_transform): * gst/audioconvert/gstaudioconvert.h: * gst/audioconvert/gstchannelmix.c: (gst_channel_mix_unset_matrix), (gst_channel_mix_fill_identical), (gst_channel_mix_fill_compatible), (gst_channel_mix_detect_pos), (gst_channel_mix_fill_one_other), (gst_channel_mix_fill_others), (gst_channel_mix_fill_normalize), (gst_channel_mix_fill_matrix), (gst_channel_mix_setup_matrix), (gst_channel_mix_passthrough), (gst_channel_mix_mix): * gst/audioconvert/gstchannelmix.h: Cleanups, librarify a bit, optimize, better negotiation and more. --- ChangeLog | 27 ++ gst/audioconvert/Makefile.am | 3 + gst/audioconvert/audioconvert.c | 393 ++++++++++++++++++ gst/audioconvert/audioconvert.h | 89 ++++ gst/audioconvert/gstaudioconvert.c | 645 +++++++++-------------------- gst/audioconvert/gstaudioconvert.h | 52 +++ gst/audioconvert/gstchannelmix.c | 173 ++++---- gst/audioconvert/gstchannelmix.h | 63 +-- 8 files changed, 842 insertions(+), 603 deletions(-) create mode 100644 gst/audioconvert/audioconvert.c create mode 100644 gst/audioconvert/audioconvert.h create mode 100644 gst/audioconvert/gstaudioconvert.h diff --git a/ChangeLog b/ChangeLog index 873e101c97..60123204dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,30 @@ +2005-08-26 Wim Taymans + + * gst/audioconvert/Makefile.am: + * gst/audioconvert/audioconvert.c: (if), (float), + (audio_convert_get_func_index), (check_default), + (audio_convert_clean_fmt), (audio_convert_prepare_context), + (audio_convert_clean_context), (audio_convert_get_sizes), + (get_temp_buffer), (audio_convert_convert): + * gst/audioconvert/audioconvert.h: + * gst/audioconvert/gstaudioconvert.c: + (gst_audio_convert_class_init), (gst_audio_convert_init), + (gst_audio_convert_dispose), (gst_audio_convert_parse_caps), + (gst_audio_convert_get_unit_size), + (gst_audio_convert_transform_caps), + (gst_audio_convert_fixate_caps), (gst_audio_convert_set_caps), + (gst_audio_convert_transform_ip), (gst_audio_convert_transform): + * gst/audioconvert/gstaudioconvert.h: + * gst/audioconvert/gstchannelmix.c: (gst_channel_mix_unset_matrix), + (gst_channel_mix_fill_identical), + (gst_channel_mix_fill_compatible), (gst_channel_mix_detect_pos), + (gst_channel_mix_fill_one_other), (gst_channel_mix_fill_others), + (gst_channel_mix_fill_normalize), (gst_channel_mix_fill_matrix), + (gst_channel_mix_setup_matrix), (gst_channel_mix_passthrough), + (gst_channel_mix_mix): + * gst/audioconvert/gstchannelmix.h: + Cleanups, librarify a bit, optimize, better negotiation and more. + 2005-08-26 Jan Schmidt * ext/ogg/gstoggdemux.c: (ogg_find_peek): diff --git a/gst/audioconvert/Makefile.am b/gst/audioconvert/Makefile.am index 86ab480478..a13d175e80 100644 --- a/gst/audioconvert/Makefile.am +++ b/gst/audioconvert/Makefile.am @@ -2,6 +2,7 @@ plugin_LTLIBRARIES = libgstaudioconvert.la libgstaudioconvert_la_SOURCES = \ gstaudioconvert.c \ + audioconvert.c \ gstchannelmix.c \ bufferframesconvert.c \ plugin.c @@ -13,6 +14,8 @@ libgstaudioconvert_la_LIBADD = \ $(GST_LIBS) noinst_HEADERS = \ + gstaudioconvert.h \ + audioconvert.h \ gstchannelmix.h \ plugin.h diff --git a/gst/audioconvert/audioconvert.c b/gst/audioconvert/audioconvert.c new file mode 100644 index 0000000000..8ec67dd74e --- /dev/null +++ b/gst/audioconvert/audioconvert.c @@ -0,0 +1,393 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * audioconvert.c: Convert audio to different audio formats automatically + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "gstchannelmix.h" +#include "audioconvert.h" + +/* int to float conversion: int2float(i) = 1 / (2^31-1) * i */ +#define INT2FLOAT(i) (4.6566128752457969e-10 * ((gfloat)i)) + +#define UNPACK_CODE(type, corr, E_FUNC) \ + type* p = (type *) src; \ + gint64 tmp; \ + for (;count; count--) { \ + tmp = ((gint64) E_FUNC (*p) - corr) * scale; \ + *dst++ = CLAMP (tmp, -((gint64) 1 << 32), (gint64) 0x7FFFFFFF); \ + p++; \ + } + +#define MAKE_UNPACK_FUNC_NAME(name) \ +audio_convert_unpack_##name + +/* unsigned case */ +#define MAKE_UNPACK_FUNC_U(name, type, E_FUNC) \ +static void \ +MAKE_UNPACK_FUNC_NAME (name) (gpointer src, gint32 *dst, \ + gint64 scale, gint count) \ +{ \ + UNPACK_CODE(type, (1 << (sizeof (type) * 8 - 1)), E_FUNC); \ +} + +/* signed case */ +#define MAKE_UNPACK_FUNC_S(name, type, E_FUNC) \ +static void \ +MAKE_UNPACK_FUNC_NAME (name) (gpointer src, gint32 *dst, \ + gint64 scale, gint count) \ +{ \ + UNPACK_CODE(type, 0, E_FUNC); \ +} + +MAKE_UNPACK_FUNC_U (u8, guint8, /* nothing */ ) + MAKE_UNPACK_FUNC_S (s8, gint8, /* nothing */ ) + MAKE_UNPACK_FUNC_U (u16_le, guint16, GUINT16_FROM_LE) + MAKE_UNPACK_FUNC_S (s16_le, gint16, GINT16_FROM_LE) + MAKE_UNPACK_FUNC_U (u16_be, guint16, GUINT16_FROM_BE) + MAKE_UNPACK_FUNC_S (s16_be, gint16, GINT16_FROM_BE) + MAKE_UNPACK_FUNC_U (u32_le, guint32, GUINT32_FROM_LE) + MAKE_UNPACK_FUNC_S (s32_le, gint32, GINT32_FROM_LE) + MAKE_UNPACK_FUNC_U (u32_be, guint32, GUINT32_FROM_BE) + MAKE_UNPACK_FUNC_S (s32_be, gint32, GINT32_FROM_BE) + +/* FIXME 24 bits */ +#if 0 + gint64 cur = 0; + + /* FIXME */ + + /* Read 24-bits LE/BE into signed 64 host-endian */ +if (this->sinkcaps.endianness == G_LITTLE_ENDIAN) +{ +cur = src[0] | (src[1] << 8) | (src[2] << 16); +} else { + cur = src[2] | (src[1] << 8) | (src[0] << 16); +} + + /* Sign extend */ +if ((this->sinkcaps.sign) + && (cur & (1 << (this->sinkcaps.depth - 1)))) + cur |= ((gint64) (-1)) ^ ((1 << this->sinkcaps.depth) - 1); + +src -= 3; +#endif + +static void +MAKE_UNPACK_FUNC_NAME (float) (gpointer src, gint32 * dst, + gint64 scale, gint count) +{ + gfloat *p = (gfloat *) src; + gfloat temp; + + for (; count; count--) { + temp = *p++ * 2147483647.0f + .5; + *dst++ = (gint32) CLAMP (temp, -2147483648ll, 2147483647ll); + } +} + +#define PACK_CODE(type, corr, E_FUNC) \ + type* p = (type *) dst; \ + gint32 scale = (32 - depth); \ + for (;count; count--) { \ + *p = E_FUNC ((type)((*src) >> scale) + corr); \ + p++; src++; \ + } + +#define MAKE_PACK_FUNC_NAME(name) \ +audio_convert_pack_##name + +#define MAKE_PACK_FUNC_U(name, type, E_FUNC) \ +static void \ +MAKE_PACK_FUNC_NAME (name) (gint32 *src, gpointer dst, \ + gint depth, gint count) \ +{ \ + PACK_CODE (type, (1 << (depth - 1)), E_FUNC); \ +} + +#define MAKE_PACK_FUNC_S(name, type, E_FUNC) \ +static void \ +MAKE_PACK_FUNC_NAME (name) (gint32 *src, gpointer dst, \ + gint depth, gint count) \ +{ \ + PACK_CODE (type, 0, E_FUNC); \ +} + +MAKE_PACK_FUNC_U (u8, guint8, /* nothing */ ); +MAKE_PACK_FUNC_S (s8, gint8, /* nothing */ ); +MAKE_PACK_FUNC_U (u16_le, guint16, GUINT16_TO_LE); +MAKE_PACK_FUNC_S (s16_le, gint16, GINT16_TO_LE); +MAKE_PACK_FUNC_U (u16_be, guint16, GUINT16_TO_BE); +MAKE_PACK_FUNC_S (s16_be, gint16, GINT16_TO_BE); +MAKE_PACK_FUNC_U (u32_le, guint32, GUINT32_TO_LE); +MAKE_PACK_FUNC_S (s32_le, gint32, GINT32_TO_LE); +MAKE_PACK_FUNC_U (u32_be, guint32, GUINT32_TO_BE); +MAKE_PACK_FUNC_S (s32_be, gint32, GINT32_TO_BE); + +static void +MAKE_PACK_FUNC_NAME (float) (gint32 * src, gpointer dst, gint depth, gint count) +{ + gfloat *p = (gfloat *) dst; + + for (; count; count--) { + *p++ = INT2FLOAT (*src++); + } +} + +static AudioConvertUnpack unpack_funcs[] = { + MAKE_UNPACK_FUNC_NAME (u8), + MAKE_UNPACK_FUNC_NAME (s8), + MAKE_UNPACK_FUNC_NAME (u8), + MAKE_UNPACK_FUNC_NAME (s8), + MAKE_UNPACK_FUNC_NAME (u16_le), + MAKE_UNPACK_FUNC_NAME (s16_le), + MAKE_UNPACK_FUNC_NAME (u16_be), + MAKE_UNPACK_FUNC_NAME (s16_be), + NULL, + NULL, + NULL, + NULL, + MAKE_UNPACK_FUNC_NAME (u32_le), + MAKE_UNPACK_FUNC_NAME (s32_le), + MAKE_UNPACK_FUNC_NAME (u32_be), + MAKE_UNPACK_FUNC_NAME (s32_be), + MAKE_UNPACK_FUNC_NAME (float), +}; + +static AudioConvertPack pack_funcs[] = { + MAKE_PACK_FUNC_NAME (u8), + MAKE_PACK_FUNC_NAME (s8), + MAKE_PACK_FUNC_NAME (u8), + MAKE_PACK_FUNC_NAME (s8), + MAKE_PACK_FUNC_NAME (u16_le), + MAKE_PACK_FUNC_NAME (s16_le), + MAKE_PACK_FUNC_NAME (u16_be), + MAKE_PACK_FUNC_NAME (s16_be), + NULL, + NULL, + NULL, + NULL, + MAKE_PACK_FUNC_NAME (u32_le), + MAKE_PACK_FUNC_NAME (s32_le), + MAKE_PACK_FUNC_NAME (u32_be), + MAKE_PACK_FUNC_NAME (s32_be), + MAKE_PACK_FUNC_NAME (float), +}; + +static gint +audio_convert_get_func_index (AudioConvertFmt * fmt) +{ + gint index = 0; + + if (fmt->is_int) { + index += (fmt->width / 8 - 1) * 4; + index += fmt->endianness == G_LITTLE_ENDIAN ? 0 : 2; + index += fmt->sign ? 1 : 0; + } else { + index = 16; + } + return index; +} + +static gboolean +check_default (AudioConvertFmt * fmt) +{ + return (fmt->width == 32 && fmt->depth == 32 && + fmt->endianness == G_BYTE_ORDER && fmt->sign == TRUE); +} + +gboolean +audio_convert_clean_fmt (AudioConvertFmt * fmt) +{ + g_return_val_if_fail (fmt != NULL, FALSE); + + g_free (fmt->pos); + fmt->pos = NULL; + + return TRUE; +} + + +gboolean +audio_convert_prepare_context (AudioConvertCtx * ctx, AudioConvertFmt * in, + AudioConvertFmt * out) +{ + gint idx; + + 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); + + ctx->in = *in; + ctx->out = *out; + + gst_channel_mix_setup_matrix (ctx); + + idx = audio_convert_get_func_index (in); + if (!(ctx->unpack = unpack_funcs[idx])) + goto not_supported; + + idx = audio_convert_get_func_index (out); + if (!(ctx->pack = pack_funcs[idx])) + goto not_supported; + + /* check if input is in default format */ + ctx->in_default = check_default (in); + /* check if channel mixer is passthrough */ + ctx->mix_passthrough = gst_channel_mix_passthrough (ctx); + /* check if output is in default format */ + ctx->out_default = check_default (out); + + ctx->scale = ((gint64) 1 << (32 - in->depth)); + ctx->depth = out->depth; + + return TRUE; + +not_supported: + { + return FALSE; + } +} + +gboolean +audio_convert_clean_context (AudioConvertCtx * ctx) +{ + g_return_val_if_fail (ctx != NULL, FALSE); + + audio_convert_clean_fmt (&ctx->in); + audio_convert_clean_fmt (&ctx->out); + gst_channel_mix_unset_matrix (ctx); + + g_free (ctx->tmpbuf); + ctx->tmpbuf = NULL; + + return TRUE; +} + +gboolean +audio_convert_get_sizes (AudioConvertCtx * ctx, gint samples, gint * srcsize, + gint * dstsize) +{ + g_return_val_if_fail (ctx != NULL, FALSE); + + if (srcsize) + *srcsize = samples * ctx->in.unit_size; + if (dstsize) + *dstsize = samples * ctx->out.unit_size; + + return TRUE; +} + +static gpointer +get_temp_buffer (AudioConvertCtx * ctx, gpointer src, gint srcsize, + gboolean writable, gint tmpsize) +{ + gpointer result; + + if (srcsize >= tmpsize && writable) { + result = src; + } else { + if (ctx->tmpbufsize < tmpsize) { + ctx->tmpbuf = g_realloc (ctx->tmpbuf, tmpsize); + ctx->tmpbufsize = tmpsize; + } + result = ctx->tmpbuf; + } + return result; +} + +gboolean +audio_convert_convert (AudioConvertCtx * ctx, gpointer src, + gpointer dst, gint samples, gboolean src_writable) +{ + gint insize; + gboolean final; + gpointer buf; + gint bufsize; + gboolean bufwritable; + gpointer tmpdst; + gint tmpsize; + + g_return_val_if_fail (ctx != 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); + + if (samples == 0) + return TRUE; + + insize = ctx->in.unit_size * samples; + tmpsize = insize * 32 / ctx->in.width; + + /* this is our source data, we start with the input src data. */ + buf = src; + bufsize = insize; + bufwritable = src_writable; + + if (!ctx->in_default) { + /* check if final conversion */ + final = (ctx->out_default && ctx->mix_passthrough); + + if (final) + tmpdst = dst; + else + tmpdst = get_temp_buffer (ctx, buf, bufsize, bufwritable, tmpsize); + + ctx->unpack (buf, tmpdst, ctx->scale, samples * ctx->in.channels); + + if (!final) { + /* new source data, it is writable */ + buf = tmpdst; + bufsize = tmpsize; + bufwritable = TRUE; + } + } + + if (!ctx->mix_passthrough) { + /* see if we need an intermediate step */ + final = ctx->out_default; + + if (final) + tmpdst = dst; + else + tmpdst = get_temp_buffer (ctx, buf, bufsize, bufwritable, tmpsize); + + /* convert */ + gst_channel_mix_mix (ctx, buf, tmpdst, samples); + + if (!final) { + buf = tmpdst; + } + } + + if (!ctx->out_default) { + /* output always to dst buffer */ + ctx->pack (buf, dst, ctx->depth, samples * ctx->out.channels); + } + + return TRUE; +} diff --git a/gst/audioconvert/audioconvert.h b/gst/audioconvert/audioconvert.h new file mode 100644 index 0000000000..736745e01e --- /dev/null +++ b/gst/audioconvert/audioconvert.h @@ -0,0 +1,89 @@ +/* GStreamer + * Copyright (C) 2004 Ronald Bultje + * + * audioconvert.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 + * 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. + */ + +#ifndef __AUDIO_CONVERT_H__ +#define __AUDIO_CONVERT_H__ + +#include +#include + +typedef struct _AudioConvertCtx AudioConvertCtx; +typedef struct _AudioConvertFmt AudioConvertFmt; + +struct _AudioConvertFmt +{ + /* general caps */ + gboolean is_int; + gint endianness; + gint width; + gint rate; + gint channels; + GstAudioChannelPosition *pos; + + /* int audio caps */ + gboolean sign; + gint depth; + + /* float audio caps */ + gint buffer_frames; + + gint unit_size; +}; + +typedef void (*AudioConvertUnpack) (gpointer src, gint32 *dst, gint64 scale, gint count); +typedef void (*AudioConvertPack) (gint32 *src, gpointer dst, gint depth, gint count); + +struct _AudioConvertCtx +{ + AudioConvertFmt in; + AudioConvertFmt out; + + AudioConvertUnpack unpack; + AudioConvertPack pack; + + /* channel conversion matrix, m[in_channels][out_channels]. + * If identity matrix, passthrough applies. */ + gfloat **matrix; + + gboolean in_default; + gboolean mix_passthrough; + gboolean out_default; + + gpointer tmpbuf; + gint tmpbufsize; + + gint64 scale; + gint depth; +}; + +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_clean_context (AudioConvertCtx *ctx); + +gboolean audio_convert_convert (AudioConvertCtx *ctx, gpointer src, + gpointer dst, gint samples, gboolean src_writable); + +#endif /* __AUDIO_CONVERT_H__ */ diff --git a/gst/audioconvert/gstaudioconvert.c b/gst/audioconvert/gstaudioconvert.c index 1f8420cf60..7c91aec019 100644 --- a/gst/audioconvert/gstaudioconvert.c +++ b/gst/audioconvert/gstaudioconvert.c @@ -1,6 +1,7 @@ /* GStreamer * Copyright (C) 2003 Benjamin Otte * Copyright (C) 2005 Thomas Vander Stichele + * Copyright (C) 2005 Wim Taymans * * gstaudioconvert.c: Convert audio to different audio formats automatically * @@ -36,10 +37,9 @@ #include "config.h" #endif -#include -#include -#include #include + +#include "gstaudioconvert.h" #include "gstchannelmix.h" #include "plugin.h" @@ -65,28 +65,18 @@ static void gst_audio_convert_init (GstAudioConvert * audio_convert); static void gst_audio_convert_dispose (GObject * obj); /* gstreamer functions */ -static GstBuffer *gst_audio_convert_buffer_to_default_format (GstAudioConvert * - this, GstBuffer * buf); -static GstBuffer *gst_audio_convert_buffer_from_default_format (GstAudioConvert - * this, GstBuffer * buf); - -static GstBuffer *gst_audio_convert_channels (GstAudioConvert * this, - GstBuffer * buf); - -static gboolean gst_audio_convert_parse_caps (const GstCaps * gst_caps, - GstAudioConvertCaps * caps); - -gboolean audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps, - guint * size); -GstCaps *audio_convert_transform_caps (GstBaseTransform * base, +static gboolean gst_audio_convert_get_unit_size (GstBaseTransform * base, + GstCaps * caps, guint * size); +static GstCaps *gst_audio_convert_transform_caps (GstBaseTransform * base, GstPadDirection direction, GstCaps * caps); -void audio_convert_fixate_caps (GstBaseTransform * base, +static void gst_audio_convert_fixate_caps (GstBaseTransform * base, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); -gboolean audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps, - GstCaps * outcaps); -static GstFlowReturn -audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf, - GstBuffer * outbuf); +static gboolean gst_audio_convert_set_caps (GstBaseTransform * base, + GstCaps * incaps, GstCaps * outcaps); +static GstFlowReturn gst_audio_convert_transform (GstBaseTransform * base, + GstBuffer * inbuf, GstBuffer * outbuf); +static GstFlowReturn gst_audio_convert_transform_ip (GstBaseTransform * base, + GstBuffer * buf); /* AudioConvert signals and args */ enum @@ -191,25 +181,22 @@ gst_audio_convert_class_init (GstAudioConvertClass * klass) supported_positions[i] = i; GST_BASE_TRANSFORM_CLASS (klass)->get_unit_size = - GST_DEBUG_FUNCPTR (audio_convert_get_unit_size); + GST_DEBUG_FUNCPTR (gst_audio_convert_get_unit_size); GST_BASE_TRANSFORM_CLASS (klass)->transform_caps = - GST_DEBUG_FUNCPTR (audio_convert_transform_caps); + GST_DEBUG_FUNCPTR (gst_audio_convert_transform_caps); GST_BASE_TRANSFORM_CLASS (klass)->fixate_caps = - GST_DEBUG_FUNCPTR (audio_convert_fixate_caps); + GST_DEBUG_FUNCPTR (gst_audio_convert_fixate_caps); GST_BASE_TRANSFORM_CLASS (klass)->set_caps = - GST_DEBUG_FUNCPTR (audio_convert_set_caps); + GST_DEBUG_FUNCPTR (gst_audio_convert_set_caps); + GST_BASE_TRANSFORM_CLASS (klass)->transform_ip = + GST_DEBUG_FUNCPTR (gst_audio_convert_transform_ip); GST_BASE_TRANSFORM_CLASS (klass)->transform = - GST_DEBUG_FUNCPTR (audio_convert_transform); + GST_DEBUG_FUNCPTR (gst_audio_convert_transform); } static void gst_audio_convert_init (GstAudioConvert * this) { - /* clear important variables */ - this->convert_internal = NULL; - this->sinkcaps.pos = NULL; - this->srccaps.pos = NULL; - this->matrix = NULL; } static void @@ -217,42 +204,105 @@ gst_audio_convert_dispose (GObject * obj) { GstAudioConvert *this = GST_AUDIO_CONVERT (obj); - if (this->sinkcaps.pos) { - g_free (this->sinkcaps.pos); - this->sinkcaps.pos = NULL; - } - - if (this->srccaps.pos) { - g_free (this->srccaps.pos); - this->srccaps.pos = NULL; - } - - gst_audio_convert_unset_matrix (this); + audio_convert_clean_context (&this->ctx); G_OBJECT_CLASS (parent_class)->dispose (obj); } /*** GSTREAMER FUNCTIONS ******************************************************/ +/* convert the given GstCaps to our format */ +static gboolean +gst_audio_convert_parse_caps (const GstCaps * caps, AudioConvertFmt * fmt) +{ + GstStructure *structure = gst_caps_get_structure (caps, 0); + + GST_DEBUG ("parse caps %p and %" GST_PTR_FORMAT, caps, caps); + + g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); + g_return_val_if_fail (fmt != NULL, FALSE); + + /* cleanup old */ + audio_convert_clean_fmt (fmt); + + fmt->endianness = G_BYTE_ORDER; + fmt->is_int = + (strcmp (gst_structure_get_name (structure), "audio/x-raw-int") == 0); + + /* parse common fields */ + if (!gst_structure_get_int (structure, "channels", &fmt->channels)) + goto no_values; + if (!(fmt->pos = gst_audio_get_channel_positions (structure))) + goto no_values; + if (!gst_structure_get_int (structure, "width", &fmt->width)) + goto no_values; + if (!gst_structure_get_int (structure, "rate", &fmt->rate)) + goto no_values; + + if (fmt->is_int) { + /* int specific fields */ + if (!gst_structure_get_boolean (structure, "signed", &fmt->sign)) + goto no_values; + if (!gst_structure_get_int (structure, "depth", &fmt->depth)) + goto no_values; + + /* width != 8 can have an endianness field */ + if (fmt->width != 8) { + if (!gst_structure_get_int (structure, "endianness", &fmt->endianness)) + goto no_values; + } + /* depth cannot be bigger than the width */ + if (fmt->depth > fmt->width) + goto not_allowed; + } else { + /* float specific fields */ + if (!gst_structure_get_int (structure, "buffer-frames", + &fmt->buffer_frames)) + goto no_values; + } + + fmt->unit_size = (fmt->width * fmt->channels) / 8; + + return TRUE; + + /* ERRORS */ +no_values: + { + GST_DEBUG ("could not get some values from structure"); + audio_convert_clean_fmt (fmt); + return FALSE; + } +not_allowed: + { + GST_DEBUG ("width > depth, not allowed - make us advertise correct fmt"); + audio_convert_clean_fmt (fmt); + return FALSE; + } +} + /* BaseTransform vmethods */ -gboolean -audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps, +static gboolean +gst_audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps, guint * size) { - GstAudioConvertCaps ac_caps; + AudioConvertFmt ac_caps = { 0 }; g_return_val_if_fail (size, FALSE); - memset (&ac_caps, 0, sizeof (ac_caps)); + if (!gst_audio_convert_parse_caps (caps, &ac_caps)) + goto parse_error; - if (!gst_audio_convert_parse_caps (caps, &ac_caps)) { + g_free (ac_caps.pos); + + *size = ac_caps.unit_size; + + return TRUE; + +parse_error: + { g_free (ac_caps.pos); return FALSE; } - g_free (ac_caps.pos); - - *size = ac_caps.width * ac_caps.channels / 8; - return TRUE; } /* audioconvert can convert anything except sample rate; so return template @@ -260,25 +310,27 @@ audio_convert_get_unit_size (GstBaseTransform * base, GstCaps * caps, /* FIXME: * it would be smart here to return the caps with the same width as the first */ -GstCaps * -audio_convert_transform_caps (GstBaseTransform * base, +static GstCaps * +gst_audio_convert_transform_caps (GstBaseTransform * base, GstPadDirection direction, GstCaps * caps) { int i; const GValue *rate; + GstCaps *ret; + GstStructure *structure; g_return_val_if_fail (GST_CAPS_IS_SIMPLE (caps), NULL); - GstStructure *structure = gst_caps_get_structure (caps, 0); + structure = gst_caps_get_structure (caps, 0); - GstCaps *ret = gst_static_caps_get (&gst_audio_convert_static_caps); + ret = gst_static_caps_get (&gst_audio_convert_static_caps); - ret = gst_caps_make_writable (ret); - - rate = gst_structure_get_value (structure, "rate"); - if (!rate) { + /* if rate not set, we return the template */ + if (!(rate = gst_structure_get_value (structure, "rate"))) return ret; - } + + /* else, write rate in the template caps */ + ret = gst_caps_make_writable (ret); for (i = 0; i < gst_caps_get_size (ret); ++i) { structure = gst_caps_get_structure (ret, i); @@ -290,12 +342,12 @@ audio_convert_transform_caps (GstBaseTransform * base, /* try to keep as many of the structure members the same by fixating the * possible ranges; this way we convert the least amount of things as possible */ -void -audio_convert_fixate_caps (GstBaseTransform * base, +static void +gst_audio_convert_fixate_caps (GstBaseTransform * base, GstPadDirection direction, GstCaps * caps, GstCaps * othercaps) { GstStructure *ins, *outs; - gint rate, endianness, depth; + gint rate, endianness, depth, width, channels; gboolean signedness; g_return_if_fail (gst_caps_is_fixed (caps)); @@ -306,6 +358,11 @@ audio_convert_fixate_caps (GstBaseTransform * base, ins = gst_caps_get_structure (caps, 0); outs = gst_caps_get_structure (othercaps, 0); + if (gst_structure_get_int (ins, "channels", &channels)) { + if (gst_structure_has_field (outs, "channels")) { + gst_caps_structure_fixate_field_nearest_int (outs, "channels", channels); + } + } if (gst_structure_get_int (ins, "rate", &rate)) { if (gst_structure_has_field (outs, "rate")) { gst_caps_structure_fixate_field_nearest_int (outs, "rate", rate); @@ -317,11 +374,25 @@ audio_convert_fixate_caps (GstBaseTransform * base, endianness); } } + if (gst_structure_get_int (ins, "width", &width)) { + if (gst_structure_has_field (outs, "width")) { + gst_caps_structure_fixate_field_nearest_int (outs, "width", width); + } + } else { + /* this is not allowed */ + } + if (gst_structure_get_int (ins, "depth", &depth)) { if (gst_structure_has_field (outs, "depth")) { gst_caps_structure_fixate_field_nearest_int (outs, "depth", depth); } + } else { + /* set depth as width */ + if (gst_structure_has_field (outs, "depth")) { + gst_caps_structure_fixate_field_nearest_int (outs, "depth", width); + } } + if (gst_structure_get_boolean (ins, "signed", &signedness)) { if (gst_structure_has_field (outs, "signed")) { gst_caps_structure_fixate_field_boolean (outs, "signed", signedness); @@ -331,424 +402,82 @@ audio_convert_fixate_caps (GstBaseTransform * base, GST_DEBUG_OBJECT (base, "fixated othercaps to %" GST_PTR_FORMAT, othercaps); } -gboolean -audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps, +static gboolean +gst_audio_convert_set_caps (GstBaseTransform * base, GstCaps * incaps, GstCaps * outcaps) { - GstAudioConvertCaps in_ac_caps = { 0 }; - GstAudioConvertCaps out_ac_caps = { 0 }; + AudioConvertFmt in_ac_caps = { 0 }; + AudioConvertFmt out_ac_caps = { 0 }; GstAudioConvert *this = GST_AUDIO_CONVERT (base); GST_DEBUG_OBJECT (base, "incaps %" GST_PTR_FORMAT ", outcaps %" GST_PTR_FORMAT, incaps, outcaps); - in_ac_caps.pos = NULL; if (!gst_audio_convert_parse_caps (incaps, &in_ac_caps)) return FALSE; - - out_ac_caps.pos = NULL; if (!gst_audio_convert_parse_caps (outcaps, &out_ac_caps)) return FALSE; - this->sinkcaps = in_ac_caps; - this->srccaps = out_ac_caps; - - GST_DEBUG ("setting up matrix"); - gst_audio_convert_setup_matrix (this); - GST_DEBUG ("set up matrix, %p", this->matrix); + if (!audio_convert_prepare_context (&this->ctx, &in_ac_caps, &out_ac_caps)) + goto no_converter; return TRUE; + +no_converter: + { + return FALSE; + } } static GstFlowReturn -audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf, - GstBuffer * outbuf) +gst_audio_convert_transform_ip (GstBaseTransform * base, GstBuffer * buf) { - GstAudioConvert *this = GST_AUDIO_CONVERT (base); - GstBuffer *buf; - - /* - * Theory of operation: - * - convert the format (endianness, signedness, width, depth) to - * (G_BYTE_ORDER, TRUE, 32, 32) - * - convert rate and channels - * - convert back to output format - */ - - /* FIXME: optimize for copying */ - buf = gst_buffer_copy (inbuf); - buf = gst_audio_convert_buffer_to_default_format (this, buf); - buf = gst_audio_convert_channels (this, buf); - buf = gst_audio_convert_buffer_from_default_format (this, buf); - memcpy (GST_BUFFER_DATA (outbuf), GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (outbuf)); - gst_buffer_unref (buf); - + /* nothing to do here */ return GST_FLOW_OK; } -/* convert the given GstCaps to our ghetto format */ -static gboolean -gst_audio_convert_parse_caps (const GstCaps * gst_caps, - GstAudioConvertCaps * caps) +static GstFlowReturn +gst_audio_convert_transform (GstBaseTransform * base, GstBuffer * inbuf, + GstBuffer * outbuf) { - GstStructure *structure = gst_caps_get_structure (gst_caps, 0); + GstAudioConvert *this = GST_AUDIO_CONVERT (base); + gboolean res; + gint insize, outsize; + gint samples; + gpointer src, dst; - GST_DEBUG ("parse caps %p and %" GST_PTR_FORMAT, gst_caps, gst_caps); + /* get amount of samples to convert. */ + samples = GST_BUFFER_SIZE (inbuf) / this->ctx.in.unit_size; - g_return_val_if_fail (gst_caps_is_fixed (gst_caps), FALSE); - g_return_val_if_fail (caps != NULL, FALSE); + /* get in/output sizes, to see if the buffers we got are of correct + * sizes */ + if (!(res = audio_convert_get_sizes (&this->ctx, samples, &insize, &outsize))) + goto error; - /* cleanup old */ - if (caps->pos) { - g_free (caps->pos); - caps->pos = NULL; + /* check in and outsize */ + if (GST_BUFFER_SIZE (inbuf) < insize) + goto wrong_size; + if (GST_BUFFER_SIZE (outbuf) < outsize) + goto wrong_size; + + /* get src and dst data */ + src = GST_BUFFER_DATA (inbuf); + dst = GST_BUFFER_DATA (outbuf); + + /* and convert the samples */ + if (!(res = audio_convert_convert (&this->ctx, src, dst, + samples, gst_buffer_is_writable (inbuf)))) + goto error; + + return GST_FLOW_OK; + + /* ERRORS */ +error: + { + return GST_FLOW_ERROR; } - - caps->endianness = G_BYTE_ORDER; - caps->is_int = - (strcmp (gst_structure_get_name (structure), "audio/x-raw-int") == 0); - if (!gst_structure_get_int (structure, "channels", &caps->channels) - || !(caps->pos = gst_audio_get_channel_positions (structure)) - || !gst_structure_get_int (structure, "width", &caps->width) - || !gst_structure_get_int (structure, "rate", &caps->rate) - || (caps->is_int - && (!gst_structure_get_boolean (structure, "signed", &caps->sign) - || !gst_structure_get_int (structure, "depth", &caps->depth) - || (caps->width != 8 - && !gst_structure_get_int (structure, "endianness", - &caps->endianness)))) || (!caps->is_int - && !gst_structure_get_int (structure, "buffer-frames", - &caps->buffer_frames))) { - GST_DEBUG ("could not get some values from structure"); - g_free (caps->pos); - caps->pos = NULL; - return FALSE; - } - if (caps->is_int && caps->depth > caps->width) { - GST_DEBUG ("width > depth, not allowed - make us advertise correct caps"); - g_free (caps->pos); - caps->pos = NULL; - return FALSE; - } - return TRUE; -} - -/* return a writable buffer of size which ideally is the same as before - - You must unref the new buffer - - The size of the old buffer is undefined after this operation */ -static GstBuffer * -gst_audio_convert_get_buffer (GstBuffer * buf, guint size) -{ - GstBuffer *ret; - - g_assert (GST_IS_BUFFER (buf)); - - GST_LOG - ("new buffer of size %u requested. Current is: data: %p - size: %u", - size, buf->data, buf->size); - if (buf->size >= size && gst_buffer_is_writable (buf)) { - gst_buffer_ref (buf); - buf->size = size; - GST_LOG - ("returning same buffer with adjusted values. data: %p - size: %u", - buf->data, buf->size); - return buf; - } else { - ret = gst_buffer_new_and_alloc (size); - g_assert (ret); - gst_buffer_stamp (ret, buf); - GST_LOG ("returning new buffer. data: %p - size: %u", ret->data, ret->size); - return ret; +wrong_size: + { + return GST_FLOW_ERROR; } } - -static inline guint8 -GUINT8_IDENTITY (guint8 x) -{ - return x; -} -static inline guint8 -GINT8_IDENTITY (gint8 x) -{ - return x; -} - -#define CONVERT_TO(to, from, type, sign, endianness, LE_FUNC, BE_FUNC) \ -G_STMT_START { \ - type value; \ - memcpy (&value, from, sizeof (type)); \ - from -= sizeof (type); \ - value = (endianness == G_LITTLE_ENDIAN) ? \ - LE_FUNC (value) : BE_FUNC (value); \ - if (sign) { \ - to = value; \ - } else { \ - to = (gint64) value - (1 << (sizeof (type) * 8 - 1)); \ - } \ -} G_STMT_END; - -static GstBuffer * -gst_audio_convert_buffer_to_default_format (GstAudioConvert * this, - GstBuffer * buf) -{ - GstBaseTransform *base = GST_BASE_TRANSFORM (this); - GstBuffer *ret; - gint i, count; - gint64 cur = 0; - gint32 write; - gint32 *dest; - guint8 *src; - - GST_LOG_OBJECT (base, "converting buffer of size %d to default format", - GST_BUFFER_SIZE (buf)); - if (this->sinkcaps.is_int) { - if (this->sinkcaps.width == 32 && this->sinkcaps.depth == 32 && - this->sinkcaps.endianness == G_BYTE_ORDER - && this->sinkcaps.sign == TRUE) - return buf; - - ret = - gst_audio_convert_get_buffer (buf, - buf->size * 32 / this->sinkcaps.width); - gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad)); - - count = ret->size / 4; - src = buf->data + (count - 1) * (this->sinkcaps.width / 8); - dest = (gint32 *) ret->data; - for (i = count - 1; i >= 0; i--) { - switch (this->sinkcaps.width) { - case 8: - if (this->sinkcaps.sign) { - CONVERT_TO (cur, src, gint8, this->sinkcaps.sign, - this->sinkcaps.endianness, GINT8_IDENTITY, GINT8_IDENTITY); - } else { - CONVERT_TO (cur, src, guint8, this->sinkcaps.sign, - this->sinkcaps.endianness, GUINT8_IDENTITY, GUINT8_IDENTITY); - } - break; - case 16: - if (this->sinkcaps.sign) { - CONVERT_TO (cur, src, gint16, this->sinkcaps.sign, - this->sinkcaps.endianness, GINT16_FROM_LE, GINT16_FROM_BE); - } else { - CONVERT_TO (cur, src, guint16, this->sinkcaps.sign, - this->sinkcaps.endianness, GUINT16_FROM_LE, GUINT16_FROM_BE); - } - break; - case 24: - { - /* Read 24-bits LE/BE into signed 64 host-endian */ - if (this->sinkcaps.endianness == G_LITTLE_ENDIAN) { - cur = src[0] | (src[1] << 8) | (src[2] << 16); - } else { - cur = src[2] | (src[1] << 8) | (src[0] << 16); - } - - /* Sign extend */ - if ((this->sinkcaps.sign) - && (cur & (1 << (this->sinkcaps.depth - 1)))) - cur |= ((gint64) (-1)) ^ ((1 << this->sinkcaps.depth) - 1); - - src -= 3; - } - break; - case 32: - if (this->sinkcaps.sign) { - CONVERT_TO (cur, src, gint32, this->sinkcaps.sign, - this->sinkcaps.endianness, GINT32_FROM_LE, GINT32_FROM_BE); - } else { - CONVERT_TO (cur, src, guint32, this->sinkcaps.sign, - this->sinkcaps.endianness, GUINT32_FROM_LE, GUINT32_FROM_BE); - } - break; - default: - g_assert_not_reached (); - } - cur = cur * ((gint64) 1 << (32 - this->sinkcaps.depth)); - cur = CLAMP (cur, -((gint64) 1 << 32), (gint64) 0x7FFFFFFF); - write = cur; - memcpy (&dest[i], &write, 4); - } - } else { - /* float2int */ - gfloat *in; - gint32 *out; - float temp; - - /* should just give the same buffer, unless it's not writable -- float is - * already 32 bits */ - ret = gst_audio_convert_get_buffer (buf, buf->size); - gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad)); - - in = (gfloat *) GST_BUFFER_DATA (buf); - out = (gint32 *) GST_BUFFER_DATA (ret); - for (i = buf->size / sizeof (float); i > 0; i--) { - temp = *in * 2147483647.0f + .5; - *out = (gint32) CLAMP ((gint64) temp, -2147483648ll, 2147483647ll); - out++; - in++; - } - } - - gst_buffer_unref (buf); - return ret; -} - -#define POPULATE(out, format, be_func, le_func) G_STMT_START { \ - format val; \ - format* p = (format *) out; \ - int_value >>= (32 - this->srccaps.depth); \ - if (this->srccaps.sign) { \ - val = (format) int_value; \ - } else { \ - val = (format) int_value + (1 << (this->srccaps.depth - 1)); \ - } \ - switch (this->srccaps.endianness) { \ - case G_LITTLE_ENDIAN: \ - val = le_func (val); \ - break; \ - case G_BIG_ENDIAN: \ - val = be_func (val); \ - break; \ - default: \ - g_assert_not_reached (); \ - }; \ - *p = val; \ - p ++; \ - out = (guint8 *) p; \ -}G_STMT_END - -static GstBuffer * -gst_audio_convert_buffer_from_default_format (GstAudioConvert * this, - GstBuffer * buf) -{ - GstBaseTransform *base; - GstBuffer *ret; - guint count, i; - gint32 *src; - - base = GST_BASE_TRANSFORM (this); - - GST_LOG_OBJECT (base, "converting buffer of size %d from default format", - GST_BUFFER_SIZE (buf)); - - if (this->srccaps.is_int && this->srccaps.width == 32 - && this->srccaps.depth == 32 && this->srccaps.endianness == G_BYTE_ORDER - && this->srccaps.sign == TRUE) - return buf; - - if (this->srccaps.is_int) { - guint8 *dest; - - count = buf->size / 4; /* size is undefined after gst_audio_convert_get_buffer! */ - ret = - gst_audio_convert_get_buffer (buf, - buf->size * this->srccaps.width / 32); - gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad)); - - dest = ret->data; - src = (gint32 *) buf->data; - - for (i = 0; i < count; i++) { - gint32 int_value = *src; - - src++; - switch (this->srccaps.width) { - case 8: - if (this->srccaps.sign) { - POPULATE (dest, gint8, GINT8_IDENTITY, GINT8_IDENTITY); - } else { - POPULATE (dest, guint8, GUINT8_IDENTITY, GUINT8_IDENTITY); - } - break; - case 16: - if (this->srccaps.sign) { - POPULATE (dest, gint16, GINT16_TO_BE, GINT16_TO_LE); - } else { - POPULATE (dest, guint16, GUINT16_TO_BE, GUINT16_TO_LE); - } - break; - case 24: - { - guint8 tmp[4]; - guint8 *tmpp = tmp; - - /* Write out big endian array */ - if (this->srccaps.sign) { - POPULATE (tmpp, gint32, GINT32_TO_BE, GINT32_TO_BE); - } else { - POPULATE (tmpp, guint32, GUINT32_TO_BE, GUINT32_TO_BE); - } - - if (this->srccaps.endianness == G_LITTLE_ENDIAN) { - dest[2] = tmp[1]; - dest[1] = tmp[2]; - dest[0] = tmp[3]; - } else { - memcpy (dest, tmp + 1, 3); - } - dest += 3; - } - break; - case 32: - if (this->srccaps.sign) { - POPULATE (dest, gint32, GINT32_TO_BE, GINT32_TO_LE); - } else { - POPULATE (dest, guint32, GUINT32_TO_BE, GUINT32_TO_LE); - } - break; - default: - g_assert_not_reached (); - } - } - } else { - gfloat *dest; - - count = buf->size / 4; /* size is undefined after gst_audio_convert_get_buffer! */ - ret = - gst_audio_convert_get_buffer (buf, - buf->size * this->srccaps.width / 32); - gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad)); - - dest = (gfloat *) ret->data; - src = (gint32 *) buf->data; - for (i = 0; i < count; i++) { - *dest = INT2FLOAT (*src); - dest++; - src++; - } - } - - gst_buffer_unref (buf); - return ret; -} - -static GstBuffer * -gst_audio_convert_channels (GstAudioConvert * this, GstBuffer * buf) -{ - GstBaseTransform *base = GST_BASE_TRANSFORM (this); - GstBuffer *ret; - gint units; /* one unit is one sample of audio for each channel, combined */ - - g_assert (this->matrix != NULL); - - GST_LOG_OBJECT (base, "converting buffer of size %d for different channels", - GST_BUFFER_SIZE (buf)); - - /* check for passthrough */ - if (gst_audio_convert_passthrough (this)) - return buf; - - /* convert */ - GST_LOG_OBJECT (base, "%d sinkpad channels, %d srcpad channels", - this->sinkcaps.channels, this->srccaps.channels); - units = GST_BUFFER_SIZE (buf) / 4 / this->sinkcaps.channels; - ret = gst_audio_convert_get_buffer (buf, units * 4 * this->srccaps.channels); - gst_buffer_set_caps (ret, GST_PAD_CAPS (base->srcpad)); - gst_audio_convert_mix (this, (gint32 *) GST_BUFFER_DATA (buf), - (gint32 *) GST_BUFFER_DATA (ret), units); - gst_buffer_unref (buf); - - return ret; -} diff --git a/gst/audioconvert/gstaudioconvert.h b/gst/audioconvert/gstaudioconvert.h new file mode 100644 index 0000000000..44b73657fc --- /dev/null +++ b/gst/audioconvert/gstaudioconvert.h @@ -0,0 +1,52 @@ +/* GStreamer + * Copyright (C) 2005 Wim Taymans + * + * gstaudioconvert.h: Convert audio to different audio formats automatically + * + * 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. + */ + +#ifndef __GST_AUDIO_CONVERT_H__ +#define __GST_AUDIO_CONVERT_H__ + +#include +#include +#include + +#include "audioconvert.h" + +#define GST_TYPE_AUDIO_CONVERT (gst_audio_convert_get_type()) +#define GST_AUDIO_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_CONVERT,GstAudioConvert)) +#define GST_AUDIO_CONVERT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_CONVERT,GstAudioConvert)) +#define GST_IS_AUDIO_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_CONVERT)) +#define GST_IS_AUDIO_CONVERT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_CONVERT)) + +typedef struct _GstAudioConvert GstAudioConvert; +typedef struct _GstAudioConvertClass GstAudioConvertClass; + +struct _GstAudioConvert +{ + GstBaseTransform element; + + AudioConvertCtx ctx; +}; + +struct _GstAudioConvertClass +{ + GstBaseTransformClass parent_class; +}; + +#endif /* __GST_AUDIO_CONVERT_H__ */ diff --git a/gst/audioconvert/gstchannelmix.c b/gst/audioconvert/gstchannelmix.c index 23bdcbea03..570a854a7d 100644 --- a/gst/audioconvert/gstchannelmix.c +++ b/gst/audioconvert/gstchannelmix.c @@ -43,7 +43,7 @@ */ void -gst_audio_convert_unset_matrix (GstAudioConvert * this) +gst_channel_mix_unset_matrix (AudioConvertCtx * this) { gint i; @@ -52,7 +52,7 @@ gst_audio_convert_unset_matrix (GstAudioConvert * this) return; /* free */ - for (i = 0; i < this->sinkcaps.channels; i++) + for (i = 0; i < this->in.channels; i++) g_free (this->matrix[i]); g_free (this->matrix); @@ -66,17 +66,17 @@ gst_audio_convert_unset_matrix (GstAudioConvert * this) */ static void -gst_audio_convert_fill_identical (GstAudioConvert * this) +gst_channel_mix_fill_identical (AudioConvertCtx * this) { gint ci, co; /* Apart from the compatible channel assignments, we can also have * same channel assignments. This is much simpler, we simply copy * the value from source to dest! */ - for (co = 0; co < this->srccaps.channels; co++) { + for (co = 0; co < this->out.channels; co++) { /* find a channel in input with same position */ - for (ci = 0; ci < this->sinkcaps.channels; ci++) { - if (this->sinkcaps.pos[ci] == this->srccaps.pos[co]) { + for (ci = 0; ci < this->in.channels; ci++) { + if (this->in.pos[ci] == this->out.pos[co]) { this->matrix[ci][co] = 1.0; } } @@ -90,7 +90,7 @@ gst_audio_convert_fill_identical (GstAudioConvert * this) */ static void -gst_audio_convert_fill_compatible (GstAudioConvert * this) +gst_channel_mix_fill_compatible (AudioConvertCtx * this) { /* Conversions from one-channel to compatible two-channel configs */ struct @@ -128,14 +128,14 @@ gst_audio_convert_fill_compatible (GstAudioConvert * this) gint pos1_0 = -1, pos1_1 = -1, pos2_0 = -1, n; /* Try to go from the given 2 channels to the given 1 channel */ - for (n = 0; n < this->sinkcaps.channels; n++) { - if (this->sinkcaps.pos[n] == conv[c].pos1[0]) + for (n = 0; n < this->in.channels; n++) { + if (this->in.pos[n] == conv[c].pos1[0]) pos1_0 = n; - else if (this->sinkcaps.pos[n] == conv[c].pos1[1]) + else if (this->in.pos[n] == conv[c].pos1[1]) pos1_1 = n; } - for (n = 0; n < this->srccaps.channels; n++) { - if (this->srccaps.pos[n] == conv[c].pos2[0]) + for (n = 0; n < this->out.channels; n++) { + if (this->out.pos[n] == conv[c].pos2[0]) pos2_0 = n; } @@ -149,14 +149,14 @@ gst_audio_convert_fill_compatible (GstAudioConvert * this) pos1_1 = -1; pos2_0 = -1; - for (n = 0; n < this->srccaps.channels; n++) { - if (this->srccaps.pos[n] == conv[c].pos1[0]) + for (n = 0; n < this->out.channels; n++) { + if (this->out.pos[n] == conv[c].pos1[0]) pos1_0 = n; - else if (this->srccaps.pos[n] == conv[c].pos1[1]) + else if (this->out.pos[n] == conv[c].pos1[1]) pos1_1 = n; } - for (n = 0; n < this->sinkcaps.channels; n++) { - if (this->sinkcaps.pos[n] == conv[c].pos2[0]) + for (n = 0; n < this->in.channels; n++) { + if (this->in.pos[n] == conv[c].pos2[0]) pos2_0 = n; } @@ -177,7 +177,7 @@ gst_audio_convert_fill_compatible (GstAudioConvert * this) */ static void -gst_audio_convert_detect_pos (GstAudioConvertCaps * caps, +gst_channel_mix_detect_pos (AudioConvertFmt * caps, gint * f, gboolean * has_f, gint * c, gboolean * has_c, gint * r, gboolean * has_r, gint * s, gboolean * has_s, gint * b, gboolean * has_b) @@ -232,12 +232,12 @@ gst_audio_convert_detect_pos (GstAudioConvertCaps * caps, } static void -gst_audio_convert_fill_one_other (gfloat ** matrix, - GstAudioConvertCaps * from_caps, gint * from_idx, +gst_channel_mix_fill_one_other (gfloat ** matrix, + AudioConvertFmt * from_caps, gint * from_idx, GstAudioChannelPosition from_pos_l, GstAudioChannelPosition from_pos_r, GstAudioChannelPosition from_pos_c, - GstAudioConvertCaps * to_caps, gint * to_idx, + AudioConvertFmt * to_caps, gint * to_idx, GstAudioChannelPosition to_pos_l, GstAudioChannelPosition to_pos_r, GstAudioChannelPosition to_pos_c, gfloat ratio) @@ -285,7 +285,7 @@ gst_audio_convert_fill_one_other (gfloat ** matrix, #define RATIO_CENTER_BASS (1.0 / sqrt (2.0)) static void -gst_audio_convert_fill_others (GstAudioConvert * this) +gst_channel_mix_fill_others (AudioConvertCtx * this) { gboolean in_has_front = FALSE, out_has_front = FALSE, in_has_center = FALSE, out_has_center = FALSE, @@ -305,33 +305,33 @@ gst_audio_convert_fill_others (GstAudioConvert * this) /* First see where (if at all) the various channels from/to * which we want to convert are located in our matrix/array. */ - gst_audio_convert_detect_pos (&this->sinkcaps, + gst_channel_mix_detect_pos (&this->in, in_f, &in_has_front, in_c, &in_has_center, in_r, &in_has_rear, in_s, &in_has_side, in_b, &in_has_bass); - gst_audio_convert_detect_pos (&this->srccaps, + gst_channel_mix_detect_pos (&this->out, out_f, &out_has_front, out_c, &out_has_center, out_r, &out_has_rear, out_s, &out_has_side, out_b, &out_has_bass); /* center/front */ if (!in_has_center && in_has_front && out_has_center) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_f, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, - &this->srccaps, out_c, + &this->out, out_c, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_FRONT_CENTER); } else if (in_has_center && !out_has_center && out_has_front) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_c, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_c, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - &this->srccaps, out_f, + &this->out, out_f, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_CENTER); @@ -339,22 +339,22 @@ gst_audio_convert_fill_others (GstAudioConvert * this) /* rear/front */ if (!in_has_rear && in_has_front && out_has_rear) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_f, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, - &this->srccaps, out_r, + &this->out, out_r, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_FRONT_REAR); } else if (in_has_center && !out_has_center && out_has_front) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_r, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_r, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - &this->srccaps, out_f, + &this->out, out_f, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_REAR); @@ -363,68 +363,68 @@ gst_audio_convert_fill_others (GstAudioConvert * this) /* bass/any */ if (in_has_bass && !out_has_bass) { if (out_has_front) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_b, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_b, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_LFE, - &this->srccaps, out_f, + &this->out, out_f, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, RATIO_FRONT_BASS); } if (out_has_center) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_b, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_b, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_LFE, - &this->srccaps, out_c, + &this->out, out_c, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, RATIO_CENTER_BASS); } if (out_has_rear) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_b, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_b, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_LFE, - &this->srccaps, out_r, + &this->out, out_r, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, RATIO_REAR_BASS); } } else if (!in_has_bass && out_has_bass) { if (in_has_front) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_f, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_f, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_MONO, - &this->srccaps, out_b, + &this->out, out_b, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_FRONT_BASS); } if (in_has_center) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_c, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_c, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, - &this->srccaps, out_b, + &this->out, out_b, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_CENTER_BASS); } if (in_has_rear) { - gst_audio_convert_fill_one_other (this->matrix, - &this->sinkcaps, in_r, + gst_channel_mix_fill_one_other (this->matrix, + &this->in, in_r, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER, - &this->srccaps, out_b, + &this->out, out_b, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_INVALID, GST_AUDIO_CHANNEL_POSITION_LFE, RATIO_REAR_BASS); @@ -439,15 +439,15 @@ gst_audio_convert_fill_others (GstAudioConvert * this) */ static void -gst_audio_convert_fill_normalize (GstAudioConvert * this) +gst_channel_mix_fill_normalize (AudioConvertCtx * this) { gfloat sum, top = 0; gint i, j; - for (j = 0; j < this->srccaps.channels; j++) { + for (j = 0; j < this->out.channels; j++) { /* calculate sum */ sum = 0.0; - for (i = 0; i < this->sinkcaps.channels; i++) { + for (i = 0; i < this->in.channels; i++) { sum += fabs (this->matrix[i][j]); } if (sum > top) { @@ -456,8 +456,8 @@ gst_audio_convert_fill_normalize (GstAudioConvert * this) } /* normalize to this */ - for (j = 0; j < this->srccaps.channels; j++) { - for (i = 0; i < this->sinkcaps.channels; i++) { + for (j = 0; j < this->out.channels; j++) { + for (i = 0; i < this->in.channels; i++) { this->matrix[i][j] /= top; } } @@ -468,45 +468,45 @@ gst_audio_convert_fill_normalize (GstAudioConvert * this) */ static void -gst_audio_convert_fill_matrix (GstAudioConvert * this) +gst_channel_mix_fill_matrix (AudioConvertCtx * this) { - gst_audio_convert_fill_identical (this); - gst_audio_convert_fill_compatible (this); - gst_audio_convert_fill_others (this); - gst_audio_convert_fill_normalize (this); + gst_channel_mix_fill_identical (this); + gst_channel_mix_fill_compatible (this); + gst_channel_mix_fill_others (this); + gst_channel_mix_fill_normalize (this); } -/* only call after this->srccaps and this->sinkcaps are filled in */ +/* only call after this->out and this->in are filled in */ void -gst_audio_convert_setup_matrix (GstAudioConvert * this) +gst_channel_mix_setup_matrix (AudioConvertCtx * this) { gint i, j; GString *s; /* don't lose memory */ - gst_audio_convert_unset_matrix (this); + gst_channel_mix_unset_matrix (this); /* allocate */ - this->matrix = g_new0 (gfloat *, this->sinkcaps.channels); - for (i = 0; i < this->sinkcaps.channels; i++) { - this->matrix[i] = g_new (gfloat, this->srccaps.channels); - for (j = 0; j < this->srccaps.channels; j++) + this->matrix = g_new0 (gfloat *, this->in.channels); + for (i = 0; i < this->in.channels; i++) { + this->matrix[i] = g_new (gfloat, this->out.channels); + for (j = 0; j < this->out.channels; j++) this->matrix[i][j] = 0.; } /* setup the matrix' internal values */ - gst_audio_convert_fill_matrix (this); + gst_channel_mix_fill_matrix (this); /* debug */ s = g_string_new ("Matrix for"); g_string_append_printf (s, " %d -> %d: ", - this->sinkcaps.channels, this->srccaps.channels); + this->in.channels, this->out.channels); g_string_append (s, "{"); - for (i = 0; i < this->sinkcaps.channels; i++) { + for (i = 0; i < this->in.channels; i++) { if (i != 0) g_string_append (s, ","); g_string_append (s, " {"); - for (j = 0; j < this->srccaps.channels; j++) { + for (j = 0; j < this->out.channels; j++) { if (j != 0) g_string_append (s, ","); g_string_append_printf (s, " %f", this->matrix[i][j]); @@ -519,16 +519,16 @@ gst_audio_convert_setup_matrix (GstAudioConvert * this) } gboolean -gst_audio_convert_passthrough (GstAudioConvert * this) +gst_channel_mix_passthrough (AudioConvertCtx * this) { gint i; /* only NxN matrices can be identities */ - if (this->sinkcaps.channels != this->srccaps.channels) + if (this->in.channels != this->out.channels) return FALSE; /* this assumes a normalized matrix */ - for (i = 0; i < this->sinkcaps.channels; i++) + for (i = 0; i < this->in.channels; i++) if (this->matrix[i][i] != 1.) return FALSE; @@ -538,23 +538,22 @@ gst_audio_convert_passthrough (GstAudioConvert * this) /* IMPORTANT: out_data == in_data is possible, make sure to not overwrite data * you might need later on! */ void -gst_audio_convert_mix (GstAudioConvert * this, +gst_channel_mix_mix (AudioConvertCtx * this, gint32 * in_data, gint32 * out_data, gint samples) { gint in, out, n; gint64 res; - gint32 tmp[this->srccaps.channels]; - gboolean backwards = this->srccaps.channels > this->sinkcaps.channels; + gint32 tmp[this->out.channels]; + gboolean backwards = this->out.channels > this->in.channels; /* FIXME: use liboil here? */ for (n = (backwards ? samples - 1 : 0); n < samples && n >= 0; backwards ? n-- : n++) { - for (out = 0; out < this->srccaps.channels; out++) { + for (out = 0; out < this->out.channels; out++) { /* convert */ res = 0; - for (in = 0; in < this->sinkcaps.channels; in++) { - res += in_data[n * this->sinkcaps.channels + in] * - this->matrix[in][out]; + for (in = 0; in < this->in.channels; in++) { + res += in_data[n * this->in.channels + in] * this->matrix[in][out]; } /* clip (shouldn't we use doubles instead as intermediate format?) */ @@ -564,7 +563,7 @@ gst_audio_convert_mix (GstAudioConvert * this, res = G_MAXINT32; tmp[out] = res; } - memcpy (&out_data[n * this->srccaps.channels], tmp, - sizeof (gint32) * this->srccaps.channels); + memcpy (&out_data[n * this->out.channels], tmp, + sizeof (gint32) * this->out.channels); } } diff --git a/gst/audioconvert/gstchannelmix.h b/gst/audioconvert/gstchannelmix.h index ae0361e9ed..9aec9cee8e 100644 --- a/gst/audioconvert/gstchannelmix.h +++ b/gst/audioconvert/gstchannelmix.h @@ -23,83 +23,30 @@ #define __GST_CHANNEL_MIX_H__ #include -#include -#include - -#define GST_TYPE_AUDIO_CONVERT (gst_audio_convert_get_type()) -#define GST_AUDIO_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_CONVERT,GstAudioConvert)) -#define GST_AUDIO_CONVERT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AUDIO_CONVERT,GstAudioConvert)) -#define GST_IS_AUDIO_CONVERT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_CONVERT)) -#define GST_IS_AUDIO_CONVERT_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AUDIO_CONVERT)) +#include "audioconvert.h" GST_DEBUG_CATEGORY_EXTERN (audio_convert_debug); #define GST_CAT_DEFAULT (audio_convert_debug) -typedef struct _GstAudioConvert GstAudioConvert; -typedef struct _GstAudioConvertCaps GstAudioConvertCaps; -typedef struct _GstAudioConvertClass GstAudioConvertClass; - -/* this struct is a handy way of passing around all the caps info ... */ -struct _GstAudioConvertCaps -{ - /* general caps */ - gboolean is_int; - gint endianness; - gint width; - gint rate; - gint channels; - GstAudioChannelPosition *pos; - - /* int audio caps */ - gboolean sign; - gint depth; - - /* float audio caps */ - gint buffer_frames; -}; - -struct _GstAudioConvert -{ - GstBaseTransform element; - - GstAudioConvertCaps srccaps; - GstAudioConvertCaps sinkcaps; - - GstCaps *src_prefered; - GstCaps *sink_prefered; - - /* channel conversion matrix, m[in_channels][out_channels]. - * If identity matrix, passthrough applies. */ - gfloat **matrix; - - /* conversion functions */ - GstBuffer *(*convert_internal) (GstAudioConvert * this, GstBuffer * buf); -}; - -struct _GstAudioConvertClass -{ - GstBaseTransformClass parent_class; -}; - /* * Delete channel mixer matrix. */ -void gst_audio_convert_unset_matrix (GstAudioConvert * this); +void gst_channel_mix_unset_matrix (AudioConvertCtx * this); /* * Setup channel mixer matrix. */ -void gst_audio_convert_setup_matrix (GstAudioConvert * this); +void gst_channel_mix_setup_matrix (AudioConvertCtx * this); /* * Checks for passthrough (= identity matrix). */ -gboolean gst_audio_convert_passthrough (GstAudioConvert * this); +gboolean gst_channel_mix_passthrough (AudioConvertCtx * this); /* * Do actual mixing. */ -void gst_audio_convert_mix (GstAudioConvert * this, +void gst_channel_mix_mix (AudioConvertCtx * this, gint32 * in_data, gint32 * out_data, gint samples);