From 39459b2f302e954094e8720db1c5deb42d5edeb0 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sebastian=20Dr=C3=B6ge?= Date: Wed, 6 Nov 2013 15:18:50 +0100 Subject: [PATCH] audiomixer: Add new element based on adder that does synchronized audio mixing --- gst/audiomixer/Makefile.am | 18 + gst/audiomixer/gstaudiomixer.c | 1963 +++++++++++++++++++++++ gst/audiomixer/gstaudiomixer.h | 126 ++ gst/audiomixer/gstaudiomixerorc-dist.c | 2661 ++++++++++++++++++++++++++++++++ gst/audiomixer/gstaudiomixerorc-dist.h | 106 ++ gst/audiomixer/gstaudiomixerorc.orc | 176 +++ tests/check/elements/audiomixer.c | 1296 ++++++++++++++++ 7 files changed, 6346 insertions(+) create mode 100644 gst/audiomixer/Makefile.am create mode 100644 gst/audiomixer/gstaudiomixer.c create mode 100644 gst/audiomixer/gstaudiomixer.h create mode 100644 gst/audiomixer/gstaudiomixerorc-dist.c create mode 100644 gst/audiomixer/gstaudiomixerorc-dist.h create mode 100644 gst/audiomixer/gstaudiomixerorc.orc create mode 100644 tests/check/elements/audiomixer.c diff --git a/gst/audiomixer/Makefile.am b/gst/audiomixer/Makefile.am new file mode 100644 index 0000000..90328bc --- /dev/null +++ b/gst/audiomixer/Makefile.am @@ -0,0 +1,18 @@ +plugin_LTLIBRARIES = libgstaudiomixer.la + +ORC_SOURCE=gstaudiomixerorc +include $(top_srcdir)/common/orc.mak + + +libgstaudiomixer_la_SOURCES = gstaudiomixer.c +nodist_libgstaudiomixer_la_SOURCES = $(ORC_NODIST_SOURCES) +libgstaudiomixer_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(ORC_CFLAGS) +libgstaudiomixer_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstaudiomixer_la_LIBADD = \ + $(GST_PLUGINS_BASE_LIBS) \ + -lgstaudio-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) $(ORC_LIBS) +libgstaudiomixer_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = gstaudiomixer.h + diff --git a/gst/audiomixer/gstaudiomixer.c b/gst/audiomixer/gstaudiomixer.c new file mode 100644 index 0000000..6073732 --- /dev/null +++ b/gst/audiomixer/gstaudiomixer.c @@ -0,0 +1,1963 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2001 Thomas + * 2005,2006 Wim Taymans + * 2013 Sebastian Dröge + * + * audiomixer.c: AudioMixer element, N in, one out, samples are added + * + * 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. + */ +/** + * SECTION:element-audiomixer + * + * The audiomixer allows to mix several streams into one by adding the data. + * Mixed data is clamped to the min/max values of the data format. + * + * The audiomixer currently mixes all data received on the sinkpads as soon as + * possible without trying to synchronize the streams. + * + * + * Example launch line + * |[ + * gst-launch audiotestsrc freq=100 ! audiomixer name=mix ! audioconvert ! alsasink audiotestsrc freq=500 ! mix. + * ]| This pipeline produces two sine waves mixed together. + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstaudiomixer.h" +#include +#include /* strcmp */ +#include "gstaudiomixerorc.h" + +#define GST_CAT_DEFAULT gst_audiomixer_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +typedef struct _GstAudioMixerCollect GstAudioMixerCollect; +struct _GstAudioMixerCollect +{ + GstCollectData collect; /* we extend the CollectData */ + + GstBuffer *buffer; /* current buffer we're mixing, + for comparison with collect.buffer + to see if we need to update our + cached values. */ + guint position, size; + + guint64 output_offset; /* Offset in output segment that + collect.pos refers to in the + current buffer. */ + + guint64 next_offset; /* Next expected offset in the input segment */ +}; + +#define DEFAULT_PAD_VOLUME (1.0) +#define DEFAULT_PAD_MUTE (FALSE) + +/* some defines for audio processing */ +/* the volume factor is a range from 0.0 to (arbitrary) VOLUME_MAX_DOUBLE = 10.0 + * we map 1.0 to VOLUME_UNITY_INT* + */ +#define VOLUME_UNITY_INT8 8 /* internal int for unity 2^(8-5) */ +#define VOLUME_UNITY_INT8_BIT_SHIFT 3 /* number of bits to shift for unity */ +#define VOLUME_UNITY_INT16 2048 /* internal int for unity 2^(16-5) */ +#define VOLUME_UNITY_INT16_BIT_SHIFT 11 /* number of bits to shift for unity */ +#define VOLUME_UNITY_INT24 524288 /* internal int for unity 2^(24-5) */ +#define VOLUME_UNITY_INT24_BIT_SHIFT 19 /* number of bits to shift for unity */ +#define VOLUME_UNITY_INT32 134217728 /* internal int for unity 2^(32-5) */ +#define VOLUME_UNITY_INT32_BIT_SHIFT 27 + +enum +{ + PROP_PAD_0, + PROP_PAD_VOLUME, + PROP_PAD_MUTE +}; + +G_DEFINE_TYPE (GstAudioMixerPad, gst_audiomixer_pad, GST_TYPE_PAD); + +static void +gst_audiomixer_pad_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (object); + + switch (prop_id) { + case PROP_PAD_VOLUME: + g_value_set_double (value, pad->volume); + break; + case PROP_PAD_MUTE: + g_value_set_boolean (value, pad->mute); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audiomixer_pad_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (object); + + switch (prop_id) { + case PROP_PAD_VOLUME: + GST_OBJECT_LOCK (pad); + pad->volume = g_value_get_double (value); + pad->volume_i8 = pad->volume * VOLUME_UNITY_INT8; + pad->volume_i16 = pad->volume * VOLUME_UNITY_INT16; + pad->volume_i32 = pad->volume * VOLUME_UNITY_INT32; + GST_OBJECT_UNLOCK (pad); + break; + case PROP_PAD_MUTE: + GST_OBJECT_LOCK (pad); + pad->mute = g_value_get_boolean (value); + GST_OBJECT_UNLOCK (pad); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audiomixer_pad_class_init (GstAudioMixerPadClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->set_property = gst_audiomixer_pad_set_property; + gobject_class->get_property = gst_audiomixer_pad_get_property; + + g_object_class_install_property (gobject_class, PROP_PAD_VOLUME, + g_param_spec_double ("volume", "Volume", "Volume of this pad", + 0.0, 10.0, DEFAULT_PAD_VOLUME, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_PAD_MUTE, + g_param_spec_boolean ("mute", "Mute", "Mute this pad", + DEFAULT_PAD_MUTE, + G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS)); +} + +static void +gst_audiomixer_pad_init (GstAudioMixerPad * pad) +{ + pad->volume = DEFAULT_PAD_VOLUME; + pad->mute = DEFAULT_PAD_MUTE; +} + +#define DEFAULT_ALIGNMENT_THRESHOLD (40 * GST_MSECOND) +#define DEFAULT_DISCONT_WAIT (1 * GST_SECOND) +#define DEFAULT_BLOCKSIZE (1024) + +enum +{ + PROP_0, + PROP_FILTER_CAPS, + PROP_ALIGNMENT_THRESHOLD, + PROP_DISCONT_WAIT, + PROP_BLOCKSIZE +}; + +/* elementfactory information */ + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN +#define CAPS \ + GST_AUDIO_CAPS_MAKE ("{ S32LE, U32LE, S16LE, U16LE, S8, U8, F32LE, F64LE }") \ + ", layout = (string) { interleaved, non-interleaved }" +#else +#define CAPS \ + GST_AUDIO_CAPS_MAKE ("{ S32BE, U32BE, S16BE, U16BE, S8, U8, F32BE, F64BE }") \ + ", layout = (string) { interleaved, non-interleaved }" +#endif + +static GstStaticPadTemplate gst_audiomixer_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (CAPS) + ); + +static GstStaticPadTemplate gst_audiomixer_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink_%u", + GST_PAD_SINK, + GST_PAD_REQUEST, + GST_STATIC_CAPS (CAPS) + ); + +static void gst_audiomixer_child_proxy_init (gpointer g_iface, + gpointer iface_data); + +#define gst_audiomixer_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE (GstAudioMixer, gst_audiomixer, GST_TYPE_ELEMENT, + G_IMPLEMENT_INTERFACE (GST_TYPE_CHILD_PROXY, + gst_audiomixer_child_proxy_init)); + +static void gst_audiomixer_dispose (GObject * object); +static void gst_audiomixer_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_audiomixer_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_audiomixer_setcaps (GstAudioMixer * audiomixer, + GstPad * pad, GstCaps * caps); +static gboolean gst_audiomixer_src_query (GstPad * pad, GstObject * parent, + GstQuery * query); +static gboolean gst_audiomixer_sink_query (GstCollectPads * pads, + GstCollectData * pad, GstQuery * query, gpointer user_data); +static gboolean gst_audiomixer_src_event (GstPad * pad, GstObject * parent, + GstEvent * event); +static gboolean gst_audiomixer_sink_event (GstCollectPads * pads, + GstCollectData * pad, GstEvent * event, gpointer user_data); + +static GstPad *gst_audiomixer_request_new_pad (GstElement * element, + GstPadTemplate * temp, const gchar * unused, const GstCaps * caps); +static void gst_audiomixer_release_pad (GstElement * element, GstPad * pad); + +static GstStateChangeReturn gst_audiomixer_change_state (GstElement * element, + GstStateChange transition); + +static GstFlowReturn gst_audiomixer_do_clip (GstCollectPads * pads, + GstCollectData * data, GstBuffer * buffer, GstBuffer ** out, + gpointer user_data); +static GstFlowReturn gst_audiomixer_collected (GstCollectPads * pads, + gpointer user_data); + +/* we can only accept caps that we and downstream can handle. + * if we have filtercaps set, use those to constrain the target caps. + */ +static GstCaps * +gst_audiomixer_sink_getcaps (GstPad * pad, GstCaps * filter) +{ + GstAudioMixer *audiomixer; + GstCaps *result, *peercaps, *current_caps, *filter_caps; + + audiomixer = GST_AUDIO_MIXER (GST_PAD_PARENT (pad)); + + GST_OBJECT_LOCK (audiomixer); + /* take filter */ + if ((filter_caps = audiomixer->filter_caps)) { + if (filter) + filter_caps = + gst_caps_intersect_full (filter, filter_caps, + GST_CAPS_INTERSECT_FIRST); + else + gst_caps_ref (filter_caps); + } else { + filter_caps = filter ? gst_caps_ref (filter) : NULL; + } + GST_OBJECT_UNLOCK (audiomixer); + + if (filter_caps && gst_caps_is_empty (filter_caps)) { + GST_WARNING_OBJECT (pad, "Empty filter caps"); + return filter_caps; + } + + /* get the downstream possible caps */ + peercaps = gst_pad_peer_query_caps (audiomixer->srcpad, filter_caps); + + /* get the allowed caps on this sinkpad */ + GST_OBJECT_LOCK (audiomixer); + current_caps = + audiomixer->current_caps ? gst_caps_ref (audiomixer->current_caps) : NULL; + if (current_caps == NULL) { + current_caps = gst_pad_get_pad_template_caps (pad); + if (!current_caps) + current_caps = gst_caps_new_any (); + } + GST_OBJECT_UNLOCK (audiomixer); + + if (peercaps) { + /* if the peer has caps, intersect */ + GST_DEBUG_OBJECT (audiomixer, "intersecting peer and our caps"); + result = + gst_caps_intersect_full (peercaps, current_caps, + GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (peercaps); + gst_caps_unref (current_caps); + } else { + /* the peer has no caps (or there is no peer), just use the allowed caps + * of this sinkpad. */ + /* restrict with filter-caps if any */ + if (filter_caps) { + GST_DEBUG_OBJECT (audiomixer, "no peer caps, using filtered caps"); + result = + gst_caps_intersect_full (filter_caps, current_caps, + GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (current_caps); + } else { + GST_DEBUG_OBJECT (audiomixer, "no peer caps, using our caps"); + result = current_caps; + } + } + + if (filter_caps) + gst_caps_unref (filter_caps); + + GST_LOG_OBJECT (audiomixer, "getting caps on pad %p,%s to %" GST_PTR_FORMAT, + pad, GST_PAD_NAME (pad), result); + + return result; +} + +static gboolean +gst_audiomixer_sink_query (GstCollectPads * pads, GstCollectData * pad, + GstQuery * query, gpointer user_data) +{ + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS: + { + GstCaps *filter, *caps; + + gst_query_parse_caps (query, &filter); + caps = gst_audiomixer_sink_getcaps (pad->pad, filter); + gst_query_set_caps_result (query, caps); + gst_caps_unref (caps); + res = TRUE; + break; + } + default: + res = gst_collect_pads_query_default (pads, pad, query, FALSE); + break; + } + + return res; +} + +/* the first caps we receive on any of the sinkpads will define the caps for all + * the other sinkpads because we can only mix streams with the same caps. + */ +static gboolean +gst_audiomixer_setcaps (GstAudioMixer * audiomixer, GstPad * pad, + GstCaps * caps) +{ + GstAudioInfo info; + + if (!gst_audio_info_from_caps (&info, caps)) + goto invalid_format; + + GST_OBJECT_LOCK (audiomixer); + /* don't allow reconfiguration for now; there's still a race between the + * different upstream threads doing query_caps + accept_caps + sending + * (possibly different) CAPS events, but there's not much we can do about + * that, upstream needs to deal with it. */ + if (audiomixer->current_caps != NULL) { + if (gst_audio_info_is_equal (&info, &audiomixer->info)) { + GST_OBJECT_UNLOCK (audiomixer); + return TRUE; + } else { + GST_DEBUG_OBJECT (pad, "got input caps %" GST_PTR_FORMAT ", but " + "current caps are %" GST_PTR_FORMAT, caps, audiomixer->current_caps); + GST_OBJECT_UNLOCK (audiomixer); + gst_pad_push_event (pad, gst_event_new_reconfigure ()); + return FALSE; + } + } + + GST_INFO_OBJECT (pad, "setting caps to %" GST_PTR_FORMAT, caps); + audiomixer->current_caps = gst_caps_ref (caps); + + memcpy (&audiomixer->info, &info, sizeof (info)); + GST_OBJECT_UNLOCK (audiomixer); + /* send caps event later, after stream-start event */ + + GST_INFO_OBJECT (pad, "handle caps change to %" GST_PTR_FORMAT, caps); + + return TRUE; + + /* ERRORS */ +invalid_format: + { + GST_WARNING_OBJECT (audiomixer, "invalid format set as caps"); + return FALSE; + } +} + +/* FIXME, the duration query should reflect how long you will produce + * data, that is the amount of stream time until you will emit EOS. + * + * For synchronized mixing this is always the max of all the durations + * of upstream since we emit EOS when all of them finished. + * + * We don't do synchronized mixing so this really depends on where the + * streams where punched in and what their relative offsets are against + * eachother which we can get from the first timestamps we see. + * + * When we add a new stream (or remove a stream) the duration might + * also become invalid again and we need to post a new DURATION + * message to notify this fact to the parent. + * For now we take the max of all the upstream elements so the simple + * cases work at least somewhat. + */ +static gboolean +gst_audiomixer_query_duration (GstAudioMixer * audiomixer, GstQuery * query) +{ + gint64 max; + gboolean res; + GstFormat format; + GstIterator *it; + gboolean done; + GValue item = { 0, }; + + /* parse format */ + gst_query_parse_duration (query, &format, NULL); + + max = -1; + res = TRUE; + done = FALSE; + + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); + while (!done) { + GstIteratorResult ires; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&item); + gint64 duration; + + /* ask sink peer for duration */ + res &= gst_pad_peer_query_duration (pad, format, &duration); + /* take max from all valid return values */ + if (res) { + /* valid unknown length, stop searching */ + if (duration == -1) { + max = duration; + done = TRUE; + } + /* else see if bigger than current max */ + else if (duration > max) + max = duration; + } + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + max = -1; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + g_value_unset (&item); + gst_iterator_free (it); + + if (res) { + /* and store the max */ + GST_DEBUG_OBJECT (audiomixer, "Total duration in format %s: %" + GST_TIME_FORMAT, gst_format_get_name (format), GST_TIME_ARGS (max)); + gst_query_set_duration (query, format, max); + } + + return res; +} + +static gboolean +gst_audiomixer_query_latency (GstAudioMixer * audiomixer, GstQuery * query) +{ + GstClockTime min, max; + gboolean live; + gboolean res; + GstIterator *it; + gboolean done; + GValue item = { 0, }; + + res = TRUE; + done = FALSE; + + live = FALSE; + min = 0; + max = GST_CLOCK_TIME_NONE; + + /* Take maximum of all latency values */ + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); + while (!done) { + GstIteratorResult ires; + + ires = gst_iterator_next (it, &item); + switch (ires) { + case GST_ITERATOR_DONE: + done = TRUE; + break; + case GST_ITERATOR_OK: + { + GstPad *pad = g_value_get_object (&item); + GstQuery *peerquery; + GstClockTime min_cur, max_cur; + gboolean live_cur; + + peerquery = gst_query_new_latency (); + + /* Ask peer for latency */ + res &= gst_pad_peer_query (pad, peerquery); + + /* take max from all valid return values */ + if (res) { + gst_query_parse_latency (peerquery, &live_cur, &min_cur, &max_cur); + + if (min_cur > min) + min = min_cur; + + if (max_cur != GST_CLOCK_TIME_NONE && + ((max != GST_CLOCK_TIME_NONE && max_cur > max) || + (max == GST_CLOCK_TIME_NONE))) + max = max_cur; + + live = live || live_cur; + } + + gst_query_unref (peerquery); + g_value_reset (&item); + break; + } + case GST_ITERATOR_RESYNC: + live = FALSE; + min = 0; + max = GST_CLOCK_TIME_NONE; + res = TRUE; + gst_iterator_resync (it); + break; + default: + res = FALSE; + done = TRUE; + break; + } + } + g_value_unset (&item); + gst_iterator_free (it); + + if (res) { + /* store the results */ + GST_DEBUG_OBJECT (audiomixer, "Calculated total latency: live %s, min %" + GST_TIME_FORMAT ", max %" GST_TIME_FORMAT, + (live ? "yes" : "no"), GST_TIME_ARGS (min), GST_TIME_ARGS (max)); + gst_query_set_latency (query, live, min, max); + } + + return res; +} + +static gboolean +gst_audiomixer_src_query (GstPad * pad, GstObject * parent, GstQuery * query) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (parent); + gboolean res = FALSE; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_POSITION: + { + GstFormat format; + + gst_query_parse_position (query, &format, NULL); + + switch (format) { + case GST_FORMAT_TIME: + /* FIXME, bring to stream time, might be tricky */ + gst_query_set_position (query, format, audiomixer->segment.position); + res = TRUE; + break; + case GST_FORMAT_DEFAULT: + gst_query_set_position (query, format, audiomixer->offset); + res = TRUE; + break; + default: + break; + } + break; + } + case GST_QUERY_DURATION: + res = gst_audiomixer_query_duration (audiomixer, query); + break; + case GST_QUERY_LATENCY: + res = gst_audiomixer_query_latency (audiomixer, query); + break; + default: + /* FIXME, needs a custom query handler because we have multiple + * sinkpads */ + res = gst_pad_query_default (pad, parent, query); + break; + } + + return res; +} + +/* event handling */ + +typedef struct +{ + GstEvent *event; + gboolean flush; +} EventData; + +/* FIXME: What is this supposed to solve? */ +static gboolean +forward_event_func (const GValue * val, GValue * ret, EventData * data) +{ + GstPad *pad = g_value_get_object (val); + GstEvent *event = data->event; + GstPad *peer; + + gst_event_ref (event); + GST_LOG_OBJECT (pad, "About to send event %s", GST_EVENT_TYPE_NAME (event)); + peer = gst_pad_get_peer (pad); + /* collect pad might have been set flushing, + * so bypass core checking that and send directly to peer */ + if (!peer || !gst_pad_send_event (peer, event)) { + if (!peer) + gst_event_unref (event); + GST_WARNING_OBJECT (pad, "Sending event %p (%s) failed.", + event, GST_EVENT_TYPE_NAME (event)); + /* quick hack to unflush the pads, ideally we need a way to just unflush + * this single collect pad */ + if (data->flush) + gst_pad_send_event (pad, gst_event_new_flush_stop (TRUE)); + } else { + g_value_set_boolean (ret, TRUE); + GST_LOG_OBJECT (pad, "Sent event %p (%s).", + event, GST_EVENT_TYPE_NAME (event)); + } + if (peer) + gst_object_unref (peer); + + /* continue on other pads, even if one failed */ + return TRUE; +} + +/* forwards the event to all sinkpads, takes ownership of the + * event + * + * Returns: TRUE if the event could be forwarded on all + * sinkpads. + */ +static gboolean +forward_event (GstAudioMixer * audiomixer, GstEvent * event, gboolean flush) +{ + gboolean ret; + GstIterator *it; + GstIteratorResult ires; + GValue vret = { 0 }; + EventData data; + + GST_LOG_OBJECT (audiomixer, "Forwarding event %p (%s)", event, + GST_EVENT_TYPE_NAME (event)); + + data.event = event; + data.flush = flush; + + g_value_init (&vret, G_TYPE_BOOLEAN); + g_value_set_boolean (&vret, FALSE); + it = gst_element_iterate_sink_pads (GST_ELEMENT_CAST (audiomixer)); + while (TRUE) { + ires = + gst_iterator_fold (it, (GstIteratorFoldFunction) forward_event_func, + &vret, &data); + switch (ires) { + case GST_ITERATOR_RESYNC: + GST_WARNING ("resync"); + gst_iterator_resync (it); + g_value_set_boolean (&vret, TRUE); + break; + case GST_ITERATOR_OK: + case GST_ITERATOR_DONE: + ret = g_value_get_boolean (&vret); + goto done; + default: + ret = FALSE; + goto done; + } + } +done: + gst_iterator_free (it); + GST_LOG_OBJECT (audiomixer, "Forwarded event %p (%s), ret=%d", event, + GST_EVENT_TYPE_NAME (event), ret); + gst_event_unref (event); + + return ret; +} + +static gboolean +gst_audiomixer_src_event (GstPad * pad, GstObject * parent, GstEvent * event) +{ + GstAudioMixer *audiomixer; + gboolean result; + + audiomixer = GST_AUDIO_MIXER (parent); + + GST_DEBUG_OBJECT (pad, "Got %s event on src pad", + GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + /* TODO: Update from videomixer */ + case GST_EVENT_SEEK: + { + GstSeekFlags flags; + gdouble rate; + GstSeekType start_type, stop_type; + gint64 start, stop; + GstFormat seek_format, dest_format; + gboolean flush; + + /* parse the seek parameters */ + gst_event_parse_seek (event, &rate, &seek_format, &flags, &start_type, + &start, &stop_type, &stop); + + if ((start_type != GST_SEEK_TYPE_NONE) + && (start_type != GST_SEEK_TYPE_SET)) { + result = FALSE; + GST_DEBUG_OBJECT (audiomixer, + "seeking failed, unhandled seek type for start: %d", start_type); + goto done; + } + if ((stop_type != GST_SEEK_TYPE_NONE) && (stop_type != GST_SEEK_TYPE_SET)) { + result = FALSE; + GST_DEBUG_OBJECT (audiomixer, + "seeking failed, unhandled seek type for end: %d", stop_type); + goto done; + } + + dest_format = audiomixer->segment.format; + if (seek_format != dest_format) { + result = FALSE; + GST_DEBUG_OBJECT (audiomixer, + "seeking failed, unhandled seek format: %d", seek_format); + goto done; + } + + flush = (flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH; + + /* check if we are flushing */ + if (flush) { + /* flushing seek, start flush downstream, the flush will be done + * when all pads received a FLUSH_STOP. + * Make sure we accept nothing anymore and return WRONG_STATE. + * We send a flush-start before, to ensure no streaming is done + * as we need to take the stream lock. + */ + gst_pad_push_event (audiomixer->srcpad, gst_event_new_flush_start ()); + gst_collect_pads_set_flushing (audiomixer->collect, TRUE); + + /* We can't send FLUSH_STOP here since upstream could start pushing data + * after we unlock audiomixer->collect. + * We set flush_stop_pending to TRUE instead and send FLUSH_STOP after + * forwarding the seek upstream or from gst_audiomixer_collected, + * whichever happens first. + */ + GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); + audiomixer->flush_stop_pending = TRUE; + GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); + GST_DEBUG_OBJECT (audiomixer, "mark pending flush stop event"); + } + GST_DEBUG_OBJECT (audiomixer, "handling seek event: %" GST_PTR_FORMAT, + event); + + /* now wait for the collected to be finished and mark a new + * segment. After we have the lock, no collect function is running and no + * new collect function will be called for as long as we're flushing. */ + GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); + /* clip position and update our segment */ + if (audiomixer->segment.stop != -1) { + audiomixer->segment.position = audiomixer->segment.stop; + } + gst_segment_do_seek (&audiomixer->segment, rate, seek_format, flags, + start_type, start, stop_type, stop, NULL); + + if (flush) { + /* Yes, we need to call _set_flushing again *WHEN* the streaming threads + * have stopped so that the cookie gets properly updated. */ + gst_collect_pads_set_flushing (audiomixer->collect, TRUE); + } + GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); + GST_DEBUG_OBJECT (audiomixer, "forwarding seek event: %" GST_PTR_FORMAT, + event); + GST_DEBUG_OBJECT (audiomixer, "updated segment: %" GST_SEGMENT_FORMAT, + &audiomixer->segment); + + /* we're forwarding seek to all upstream peers and wait for one to reply + * with a newsegment-event before we send a newsegment-event downstream */ + g_atomic_int_set (&audiomixer->segment_pending, TRUE); + result = forward_event (audiomixer, event, flush); + /* FIXME: We should use the seek segment and forward that downstream next time + * not any upstream segment event */ + if (!result) { + /* seek failed. maybe source is a live source. */ + GST_DEBUG_OBJECT (audiomixer, "seeking failed"); + } + if (g_atomic_int_compare_and_exchange (&audiomixer->flush_stop_pending, + TRUE, FALSE)) { + GST_DEBUG_OBJECT (audiomixer, "pending flush stop"); + if (!gst_pad_push_event (audiomixer->srcpad, + gst_event_new_flush_stop (TRUE))) { + GST_WARNING_OBJECT (audiomixer, "Sending flush stop event failed"); + } + } + break; + } + case GST_EVENT_QOS: + /* QoS might be tricky */ + result = FALSE; + gst_event_unref (event); + break; + case GST_EVENT_NAVIGATION: + /* navigation is rather pointless. */ + result = FALSE; + gst_event_unref (event); + break; + default: + /* just forward the rest for now */ + GST_DEBUG_OBJECT (audiomixer, "forward unhandled event: %s", + GST_EVENT_TYPE_NAME (event)); + result = forward_event (audiomixer, event, FALSE); + break; + } + +done: + + return result; +} + +static gboolean +gst_audiomixer_sink_event (GstCollectPads * pads, GstCollectData * pad, + GstEvent * event, gpointer user_data) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (user_data); + GstAudioMixerCollect *adata = (GstAudioMixerCollect *) pad; + gboolean res = TRUE, discard = FALSE; + + GST_DEBUG_OBJECT (pad->pad, "Got %s event on sink pad", + GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + res = gst_audiomixer_setcaps (audiomixer, pad->pad, caps); + gst_event_unref (event); + event = NULL; + break; + } + /* FIXME: Who cares about flushes from upstream? We should + * not forward them at all */ + case GST_EVENT_FLUSH_START: + /* ensure that we will send a flush stop */ + GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); + audiomixer->flush_stop_pending = TRUE; + res = gst_collect_pads_event_default (pads, pad, event, discard); + event = NULL; + GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); + break; + case GST_EVENT_FLUSH_STOP: + /* we received a flush-stop. We will only forward it when + * flush_stop_pending is set, and we will unset it then. + */ + g_atomic_int_set (&audiomixer->segment_pending, TRUE); + GST_COLLECT_PADS_STREAM_LOCK (audiomixer->collect); + if (audiomixer->flush_stop_pending) { + GST_DEBUG_OBJECT (pad->pad, "forwarding flush stop"); + res = gst_collect_pads_event_default (pads, pad, event, discard); + audiomixer->flush_stop_pending = FALSE; + event = NULL; + gst_buffer_replace (&audiomixer->current_buffer, NULL); + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } else { + discard = TRUE; + GST_DEBUG_OBJECT (pad->pad, "eating flush stop"); + } + GST_COLLECT_PADS_STREAM_UNLOCK (audiomixer->collect); + /* Clear pending tags */ + if (audiomixer->pending_events) { + g_list_foreach (audiomixer->pending_events, (GFunc) gst_event_unref, + NULL); + g_list_free (audiomixer->pending_events); + audiomixer->pending_events = NULL; + } + adata->position = adata->size = 0; + adata->output_offset = adata->next_offset = -1; + gst_buffer_replace (&adata->buffer, NULL); + break; + case GST_EVENT_TAG: + /* collect tags here so we can push them out when we collect data */ + audiomixer->pending_events = + g_list_append (audiomixer->pending_events, event); + event = NULL; + break; + case GST_EVENT_SEGMENT:{ + const GstSegment *segment; + gst_event_parse_segment (event, &segment); + if (segment->rate != audiomixer->segment.rate) { + GST_ERROR_OBJECT (pad->pad, + "Got segment event with wrong rate %lf, expected %lf", + segment->rate, audiomixer->segment.rate); + res = FALSE; + gst_event_unref (event); + event = NULL; + } else if (segment->rate < 0.0) { + GST_ERROR_OBJECT (pad->pad, "Negative rates not supported yet"); + res = FALSE; + gst_event_unref (event); + event = NULL; + } + discard = TRUE; + break; + } + default: + break; + } + + if (G_LIKELY (event)) + return gst_collect_pads_event_default (pads, pad, event, discard); + else + return res; +} + +static void +gst_audiomixer_class_init (GstAudioMixerClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + + gobject_class->set_property = gst_audiomixer_set_property; + gobject_class->get_property = gst_audiomixer_get_property; + gobject_class->dispose = gst_audiomixer_dispose; + + g_object_class_install_property (gobject_class, PROP_FILTER_CAPS, + g_param_spec_boxed ("caps", "Target caps", + "Set target format for mixing (NULL means ANY). " + "Setting this property takes a reference to the supplied GstCaps " + "object", GST_TYPE_CAPS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ALIGNMENT_THRESHOLD, + g_param_spec_uint64 ("alignment-threshold", "Alignment Threshold", + "Timestamp alignment threshold in nanoseconds", 0, + G_MAXUINT64 - 1, DEFAULT_ALIGNMENT_THRESHOLD, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_DISCONT_WAIT, + g_param_spec_uint64 ("discont-wait", "Discont Wait", + "Window of time in nanoseconds to wait before " + "creating a discontinuity", 0, + G_MAXUINT64 - 1, DEFAULT_DISCONT_WAIT, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_BLOCKSIZE, + g_param_spec_uint ("blocksize", "Block Size", + "Output block size in number of samples", 0, + G_MAXUINT, DEFAULT_BLOCKSIZE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_audiomixer_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_audiomixer_sink_template)); + gst_element_class_set_static_metadata (gstelement_class, "AudioMixer", + "Generic/Audio", + "Mixes multiple audio streams", + "Sebastian Dröge "); + + gstelement_class->request_new_pad = + GST_DEBUG_FUNCPTR (gst_audiomixer_request_new_pad); + gstelement_class->release_pad = + GST_DEBUG_FUNCPTR (gst_audiomixer_release_pad); + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_audiomixer_change_state); +} + +static void +gst_audiomixer_init (GstAudioMixer * audiomixer) +{ + GstPadTemplate *template; + + template = gst_static_pad_template_get (&gst_audiomixer_src_template); + audiomixer->srcpad = gst_pad_new_from_template (template, "src"); + gst_object_unref (template); + + gst_pad_set_query_function (audiomixer->srcpad, + GST_DEBUG_FUNCPTR (gst_audiomixer_src_query)); + gst_pad_set_event_function (audiomixer->srcpad, + GST_DEBUG_FUNCPTR (gst_audiomixer_src_event)); + GST_PAD_SET_PROXY_CAPS (audiomixer->srcpad); + gst_element_add_pad (GST_ELEMENT (audiomixer), audiomixer->srcpad); + + audiomixer->current_caps = NULL; + gst_audio_info_init (&audiomixer->info); + audiomixer->padcount = 0; + + audiomixer->filter_caps = NULL; + audiomixer->alignment_threshold = DEFAULT_ALIGNMENT_THRESHOLD; + audiomixer->discont_wait = DEFAULT_DISCONT_WAIT; + audiomixer->blocksize = DEFAULT_BLOCKSIZE; + + /* keep track of the sinkpads requested */ + audiomixer->collect = gst_collect_pads_new (); + gst_collect_pads_set_function (audiomixer->collect, + GST_DEBUG_FUNCPTR (gst_audiomixer_collected), audiomixer); + gst_collect_pads_set_clip_function (audiomixer->collect, + GST_DEBUG_FUNCPTR (gst_audiomixer_do_clip), audiomixer); + gst_collect_pads_set_event_function (audiomixer->collect, + GST_DEBUG_FUNCPTR (gst_audiomixer_sink_event), audiomixer); + gst_collect_pads_set_query_function (audiomixer->collect, + GST_DEBUG_FUNCPTR (gst_audiomixer_sink_query), audiomixer); +} + +static void +gst_audiomixer_dispose (GObject * object) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); + + if (audiomixer->collect) { + gst_object_unref (audiomixer->collect); + audiomixer->collect = NULL; + } + gst_caps_replace (&audiomixer->filter_caps, NULL); + gst_caps_replace (&audiomixer->current_caps, NULL); + + if (audiomixer->pending_events) { + g_list_foreach (audiomixer->pending_events, (GFunc) gst_event_unref, NULL); + g_list_free (audiomixer->pending_events); + audiomixer->pending_events = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_audiomixer_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); + + switch (prop_id) { + case PROP_FILTER_CAPS:{ + GstCaps *new_caps = NULL; + GstCaps *old_caps; + const GstCaps *new_caps_val = gst_value_get_caps (value); + + if (new_caps_val != NULL) { + new_caps = (GstCaps *) new_caps_val; + gst_caps_ref (new_caps); + } + + GST_OBJECT_LOCK (audiomixer); + old_caps = audiomixer->filter_caps; + audiomixer->filter_caps = new_caps; + GST_OBJECT_UNLOCK (audiomixer); + + if (old_caps) + gst_caps_unref (old_caps); + + GST_DEBUG_OBJECT (audiomixer, "set new caps %" GST_PTR_FORMAT, new_caps); + break; + } + case PROP_ALIGNMENT_THRESHOLD: + audiomixer->alignment_threshold = g_value_get_uint64 (value); + break; + case PROP_DISCONT_WAIT: + audiomixer->discont_wait = g_value_get_uint64 (value); + break; + case PROP_BLOCKSIZE: + audiomixer->blocksize = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_audiomixer_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (object); + + switch (prop_id) { + case PROP_FILTER_CAPS: + GST_OBJECT_LOCK (audiomixer); + gst_value_set_caps (value, audiomixer->filter_caps); + GST_OBJECT_UNLOCK (audiomixer); + break; + case PROP_ALIGNMENT_THRESHOLD: + g_value_set_uint64 (value, audiomixer->alignment_threshold); + break; + case PROP_DISCONT_WAIT: + g_value_set_uint64 (value, audiomixer->discont_wait); + break; + case PROP_BLOCKSIZE: + g_value_set_uint (value, audiomixer->blocksize); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +free_pad (GstCollectData * data) +{ + GstAudioMixerCollect *adata = (GstAudioMixerCollect *) data; + + gst_buffer_replace (&adata->buffer, NULL); +} + +static GstPad * +gst_audiomixer_request_new_pad (GstElement * element, GstPadTemplate * templ, + const gchar * unused, const GstCaps * caps) +{ + gchar *name; + GstAudioMixer *audiomixer; + GstPad *newpad; + gint padcount; + GstCollectData *cdata; + GstAudioMixerCollect *adata; + + if (templ->direction != GST_PAD_SINK) + goto not_sink; + + audiomixer = GST_AUDIO_MIXER (element); + + /* increment pad counter */ + padcount = g_atomic_int_add (&audiomixer->padcount, 1); + + name = g_strdup_printf ("sink_%u", padcount); + newpad = g_object_new (GST_TYPE_AUDIO_MIXER_PAD, "name", name, "direction", + templ->direction, "template", templ, NULL); + GST_DEBUG_OBJECT (audiomixer, "request new pad %s", name); + g_free (name); + + cdata = + gst_collect_pads_add_pad (audiomixer->collect, newpad, + sizeof (GstAudioMixerCollect), free_pad, TRUE); + adata = (GstAudioMixerCollect *) cdata; + adata->buffer = NULL; + adata->position = 0; + adata->size = 0; + adata->output_offset = -1; + adata->next_offset = -1; + + /* takes ownership of the pad */ + if (!gst_element_add_pad (GST_ELEMENT (audiomixer), newpad)) + goto could_not_add; + + gst_child_proxy_child_added (GST_CHILD_PROXY (audiomixer), G_OBJECT (newpad), + GST_OBJECT_NAME (newpad)); + + return newpad; + + /* errors */ +not_sink: + { + g_warning ("gstaudiomixer: request new pad that is not a SINK pad\n"); + return NULL; + } +could_not_add: + { + GST_DEBUG_OBJECT (audiomixer, "could not add pad"); + gst_collect_pads_remove_pad (audiomixer->collect, newpad); + gst_object_unref (newpad); + return NULL; + } +} + +static void +gst_audiomixer_release_pad (GstElement * element, GstPad * pad) +{ + GstAudioMixer *audiomixer; + + audiomixer = GST_AUDIO_MIXER (element); + + GST_DEBUG_OBJECT (audiomixer, "release pad %s:%s", GST_DEBUG_PAD_NAME (pad)); + + gst_child_proxy_child_removed (GST_CHILD_PROXY (audiomixer), G_OBJECT (pad), + GST_OBJECT_NAME (pad)); + if (audiomixer->collect) + gst_collect_pads_remove_pad (audiomixer->collect, pad); + gst_element_remove_pad (element, pad); +} + +static GstFlowReturn +gst_audiomixer_do_clip (GstCollectPads * pads, GstCollectData * data, + GstBuffer * buffer, GstBuffer ** out, gpointer user_data) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (user_data); + gint rate, bpf; + + rate = GST_AUDIO_INFO_RATE (&audiomixer->info); + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + buffer = gst_audio_buffer_clip (buffer, &data->segment, rate, bpf); + + *out = buffer; + return GST_FLOW_OK; +} + +static gboolean +gst_audio_mixer_fill_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, + GstCollectData * collect_data, GstAudioMixerCollect * adata, + GstBuffer * inbuf) +{ + GstClockTime start_time, end_time; + gboolean discont = FALSE; + guint64 start_offset, end_offset; + GstClockTime timestamp, stream_time; + gint rate, bpf; + + g_assert (adata->buffer == NULL); + + rate = GST_AUDIO_INFO_RATE (&audiomixer->info); + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + timestamp = GST_BUFFER_TIMESTAMP (inbuf); + stream_time = + gst_segment_to_stream_time (&collect_data->segment, GST_FORMAT_TIME, + timestamp); + + /* sync object properties on stream time */ + /* TODO: Ideally we would want to do that on every sample */ + if (GST_CLOCK_TIME_IS_VALID (stream_time)) + gst_object_sync_values (GST_OBJECT (collect_data->pad), stream_time); + + adata->position = 0; + adata->size = gst_buffer_get_size (inbuf); + + start_time = GST_BUFFER_TIMESTAMP (inbuf); + end_time = + start_time + gst_util_uint64_scale_ceil (adata->size / bpf, + GST_SECOND, rate); + + start_offset = gst_util_uint64_scale (start_time, rate, GST_SECOND); + end_offset = start_offset + adata->size / bpf; + + if (GST_BUFFER_IS_DISCONT (inbuf) || adata->next_offset == -1) { + discont = TRUE; + } else { + guint64 diff, max_sample_diff; + + /* Check discont, based on audiobasesink */ + if (start_offset <= adata->next_offset) + diff = adata->next_offset - start_offset; + else + diff = start_offset - adata->next_offset; + + max_sample_diff = + gst_util_uint64_scale_int (audiomixer->alignment_threshold, rate, + GST_SECOND); + + /* Discont! */ + if (G_UNLIKELY (diff >= max_sample_diff)) { + if (audiomixer->discont_wait > 0) { + if (audiomixer->discont_time == GST_CLOCK_TIME_NONE) { + audiomixer->discont_time = start_time; + } else if (start_time - audiomixer->discont_time >= + audiomixer->discont_wait) { + discont = TRUE; + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } + } else { + discont = TRUE; + } + } else if (G_UNLIKELY (audiomixer->discont_time != GST_CLOCK_TIME_NONE)) { + /* we have had a discont, but are now back on track! */ + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } + } + + if (discont) { + /* Have discont, need resync */ + if (adata->next_offset != -1) + GST_INFO_OBJECT (collect_data->pad, "Have discont. Expected %" + G_GUINT64_FORMAT ", got %" G_GUINT64_FORMAT, + adata->next_offset, start_offset); + adata->output_offset = -1; + } else { + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } + + adata->next_offset = end_offset; + + if (adata->output_offset == -1) { + GstClockTime start_running_time; + GstClockTime end_running_time; + guint64 start_running_time_offset; + guint64 end_running_time_offset; + + start_running_time = + gst_segment_to_running_time (&collect_data->segment, + GST_FORMAT_TIME, start_time); + end_running_time = + gst_segment_to_running_time (&collect_data->segment, + GST_FORMAT_TIME, end_time); + start_running_time_offset = + gst_util_uint64_scale (start_running_time, rate, GST_SECOND); + end_running_time_offset = + gst_util_uint64_scale (end_running_time, rate, GST_SECOND); + + if (end_running_time_offset < audiomixer->offset) { + /* Before output segment, drop */ + gst_buffer_unref (inbuf); + adata->buffer = NULL; + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + adata->position = 0; + adata->size = 0; + adata->output_offset = -1; + GST_DEBUG_OBJECT (collect_data->pad, + "Buffer before segment or current position: %" G_GUINT64_FORMAT " < %" + G_GUINT64_FORMAT, end_running_time_offset, audiomixer->offset); + return FALSE; + } + + if (start_running_time_offset < audiomixer->offset) { + guint diff = (audiomixer->offset - start_running_time_offset) * bpf; + adata->position += diff; + adata->size -= diff; + /* FIXME: This could only happen due to rounding errors */ + if (adata->size == 0) { + /* Empty buffer, drop */ + gst_buffer_unref (inbuf); + adata->buffer = NULL; + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + adata->position = 0; + adata->size = 0; + adata->output_offset = -1; + GST_DEBUG_OBJECT (collect_data->pad, + "Buffer before segment or current position: %" G_GUINT64_FORMAT + " < %" G_GUINT64_FORMAT, end_running_time_offset, + audiomixer->offset); + return FALSE; + } + } + + adata->output_offset = MAX (start_running_time_offset, audiomixer->offset); + GST_DEBUG_OBJECT (collect_data->pad, + "Buffer resynced: Pad offset %" G_GUINT64_FORMAT + ", current mixer offset %" G_GUINT64_FORMAT, adata->output_offset, + audiomixer->offset); + } + + GST_LOG_OBJECT (collect_data->pad, + "Queued new buffer at offset %" G_GUINT64_FORMAT, adata->output_offset); + adata->buffer = inbuf; + + return TRUE; +} + +static void +gst_audio_mixer_mix_buffer (GstAudioMixer * audiomixer, GstCollectPads * pads, + GstCollectData * collect_data, GstAudioMixerCollect * adata, + GstMapInfo * outmap) +{ + GstAudioMixerPad *pad = GST_AUDIO_MIXER_PAD (adata->collect.pad); + guint overlap; + guint out_start; + GstBuffer *inbuf; + GstMapInfo inmap; + gint bpf; + + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + /* Overlap => mix */ + if (audiomixer->offset < adata->output_offset) + out_start = adata->output_offset - audiomixer->offset; + else + out_start = 0; + + if (audiomixer->offset + audiomixer->blocksize + adata->position / bpf < + adata->output_offset + adata->size / bpf + out_start) + overlap = audiomixer->blocksize - out_start; + else + overlap = adata->size / bpf - adata->position / bpf; + + inbuf = gst_collect_pads_peek (pads, collect_data); + g_assert (inbuf != NULL && inbuf == adata->buffer); + + GST_OBJECT_LOCK (pad); + if (pad->mute || pad->volume < G_MINDOUBLE) { + GST_DEBUG_OBJECT (pad, "Skipping muted pad"); + gst_buffer_unref (inbuf); + adata->position += adata->size; + adata->output_offset += adata->size / bpf; + if (adata->position >= adata->size) { + /* Buffer done, drop it */ + gst_buffer_replace (&adata->buffer, NULL); + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + } + GST_OBJECT_UNLOCK (pad); + return; + } + + if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_GAP)) { + /* skip gap buffer */ + GST_LOG_OBJECT (pad, "skipping GAP buffer"); + gst_buffer_unref (inbuf); + adata->position += adata->size; + adata->output_offset += adata->size / bpf; + /* Buffer done, drop it */ + gst_buffer_replace (&adata->buffer, NULL); + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + GST_OBJECT_UNLOCK (pad); + return; + } + + gst_buffer_map (inbuf, &inmap, GST_MAP_READ); + GST_LOG_OBJECT (pad, "mixing %u bytes at offset %u from offset %u", + overlap * bpf, out_start * bpf, adata->position); + /* further buffers, need to add them */ + if (pad->volume == 1.0) { + switch (audiomixer->info.finfo->format) { + case GST_AUDIO_FORMAT_U8: + audiomixer_orc_add_u8 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S8: + audiomixer_orc_add_s8 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_U16: + audiomixer_orc_add_u16 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S16: + audiomixer_orc_add_s16 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_U32: + audiomixer_orc_add_u32 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S32: + audiomixer_orc_add_s32 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_F32: + audiomixer_orc_add_f32 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_F64: + audiomixer_orc_add_f64 ((gpointer) (outmap->data + out_start * bpf), + (gpointer) (inmap.data + adata->position), + overlap * audiomixer->info.channels); + break; + default: + g_assert_not_reached (); + break; + } + } else { + switch (audiomixer->info.finfo->format) { + case GST_AUDIO_FORMAT_U8: + audiomixer_orc_add_volume_u8 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i8, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S8: + audiomixer_orc_add_volume_s8 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i8, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_U16: + audiomixer_orc_add_volume_u16 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i16, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S16: + audiomixer_orc_add_volume_s16 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i16, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_U32: + audiomixer_orc_add_volume_u32 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i32, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_S32: + audiomixer_orc_add_volume_s32 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume_i32, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_F32: + audiomixer_orc_add_volume_f32 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume, overlap * audiomixer->info.channels); + break; + case GST_AUDIO_FORMAT_F64: + audiomixer_orc_add_volume_f64 ((gpointer) (outmap->data + + out_start * bpf), (gpointer) (inmap.data + adata->position), + pad->volume, overlap * audiomixer->info.channels); + break; + default: + g_assert_not_reached (); + break; + } + } + gst_buffer_unmap (inbuf, &inmap); + gst_buffer_unref (inbuf); + + adata->position += overlap * bpf; + adata->output_offset += overlap; + + if (adata->position == adata->size) { + /* Buffer done, drop it */ + gst_buffer_replace (&adata->buffer, NULL); + gst_buffer_unref (gst_collect_pads_pop (pads, collect_data)); + GST_DEBUG_OBJECT (pad, "Finished mixing buffer, waiting for next"); + } + + GST_OBJECT_UNLOCK (pad); +} + +static GstFlowReturn +gst_audiomixer_collected (GstCollectPads * pads, gpointer user_data) +{ + /* Get all pads that have data for us and store them in a + * new list. + * + * Calculate the current output offset/timestamp and + * offset_end/timestamp_end. Allocate a silence buffer + * for this and store it. + * + * For all pads: + * 1) Once per input buffer (cached) + * 1) Check discont (flag and timestamp with tolerance) + * 2) If discont or new, resync. That means: + * 1) Drop all start data of the buffer that comes before + * the current position/offset. + * 2) Calculate the offset (output segment!) that the first + * frame of the input buffer corresponds to. Base this on + * the running time. + * + * 2) If the current pad's offset/offset_end overlaps with the output + * offset/offset_end, mix it at the appropiate position in the output + * buffer and advance the pad's position. Remember if this pad needs + * a new buffer to advance behind the output offset_end. + * + * 3) If we had no pad with a buffer, go EOS. + * + * 4) If we had at least one pad that did not advance behind output + * offset_end, let collected be called again for the current + * output offset/offset_end. + */ + GstAudioMixer *audiomixer; + GSList *collected; + GstFlowReturn ret; + GstBuffer *outbuf = NULL; + GstMapInfo outmap; + gint64 next_offset; + gint64 next_timestamp; + gint rate, bpf; + gboolean dropped = FALSE; + gboolean is_eos = TRUE; + gboolean is_done = TRUE; + gboolean handled_buffer = FALSE; + + audiomixer = GST_AUDIO_MIXER (user_data); + + /* this is fatal */ + if (G_UNLIKELY (audiomixer->info.finfo->format == GST_AUDIO_FORMAT_UNKNOWN)) + goto not_negotiated; + + if (audiomixer->flush_stop_pending == TRUE) { + GST_INFO_OBJECT (audiomixer->srcpad, "send pending flush stop event"); + if (!gst_pad_push_event (audiomixer->srcpad, + gst_event_new_flush_stop (TRUE))) { + GST_WARNING_OBJECT (audiomixer->srcpad, + "Sending flush stop event failed"); + } + + audiomixer->flush_stop_pending = FALSE; + gst_buffer_replace (&audiomixer->current_buffer, NULL); + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + } + + if (audiomixer->send_stream_start) { + gchar s_id[32]; + + GST_INFO_OBJECT (audiomixer->srcpad, "send pending stream start event"); + /* stream-start (FIXME: create id based on input ids) */ + g_snprintf (s_id, sizeof (s_id), "audiomixer-%08x", g_random_int ()); + if (!gst_pad_push_event (audiomixer->srcpad, + gst_event_new_stream_start (s_id))) { + GST_WARNING_OBJECT (audiomixer->srcpad, + "Sending stream start event failed"); + } + audiomixer->send_stream_start = FALSE; + } + + if (audiomixer->send_caps) { + GstEvent *caps_event; + + caps_event = gst_event_new_caps (audiomixer->current_caps); + GST_INFO_OBJECT (audiomixer->srcpad, + "send pending caps event %" GST_PTR_FORMAT, caps_event); + if (!gst_pad_push_event (audiomixer->srcpad, caps_event)) { + GST_WARNING_OBJECT (audiomixer->srcpad, "Sending caps event failed"); + } + audiomixer->send_caps = FALSE; + } + + rate = GST_AUDIO_INFO_RATE (&audiomixer->info); + bpf = GST_AUDIO_INFO_BPF (&audiomixer->info); + + if (g_atomic_int_compare_and_exchange (&audiomixer->segment_pending, TRUE, + FALSE)) { + GstEvent *event; + + /* + * When seeking we set the start and stop positions as given in the seek + * event. We also adjust offset & timestamp accordingly. + * This basically ignores all newsegments sent by upstream. + * + * FIXME: We require that all inputs have the same rate currently + * as we do no rate conversion! + */ + event = gst_event_new_segment (&audiomixer->segment); + if (audiomixer->segment.rate > 0.0) { + audiomixer->segment.position = audiomixer->segment.start; + } else { + audiomixer->segment.position = audiomixer->segment.stop; + } + audiomixer->offset = gst_util_uint64_scale (audiomixer->segment.position, + rate, GST_SECOND); + + GST_INFO_OBJECT (audiomixer->srcpad, "sending pending new segment event %" + GST_SEGMENT_FORMAT, &audiomixer->segment); + if (event) { + if (!gst_pad_push_event (audiomixer->srcpad, event)) { + GST_WARNING_OBJECT (audiomixer->srcpad, + "Sending new segment event failed"); + } + } else { + GST_WARNING_OBJECT (audiomixer->srcpad, "Creating new segment event for " + "start:%" G_GINT64_FORMAT " end:%" G_GINT64_FORMAT " failed", + audiomixer->segment.start, audiomixer->segment.stop); + } + } + + if (G_UNLIKELY (audiomixer->pending_events)) { + GList *tmp = audiomixer->pending_events; + + while (tmp) { + GstEvent *ev = (GstEvent *) tmp->data; + + gst_pad_push_event (audiomixer->srcpad, ev); + tmp = g_list_next (tmp); + } + g_list_free (audiomixer->pending_events); + audiomixer->pending_events = NULL; + } + + /* for the next timestamp, use the sample counter, which will + * never accumulate rounding errors */ + + /* FIXME: Reverse mixing does not work at all yet */ + if (audiomixer->segment.rate > 0.0) { + next_offset = audiomixer->offset + audiomixer->blocksize; + } else { + next_offset = audiomixer->offset - audiomixer->blocksize; + } + + next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); + + if (audiomixer->current_buffer) { + outbuf = audiomixer->current_buffer; + } else { + outbuf = gst_buffer_new_and_alloc (audiomixer->blocksize * bpf); + gst_buffer_map (outbuf, &outmap, GST_MAP_WRITE); + gst_audio_format_fill_silence (audiomixer->info.finfo, outmap.data, + outmap.size); + gst_buffer_unmap (outbuf, &outmap); + audiomixer->current_buffer = outbuf; + } + + GST_LOG_OBJECT (audiomixer, + "Starting to mix %u samples for offset %" G_GUINT64_FORMAT + " with timestamp %" GST_TIME_FORMAT, audiomixer->blocksize, + audiomixer->offset, GST_TIME_ARGS (audiomixer->segment.position)); + + gst_buffer_map (outbuf, &outmap, GST_MAP_READWRITE); + + for (collected = pads->data; collected; collected = collected->next) { + GstCollectData *collect_data; + GstAudioMixerCollect *adata; + GstBuffer *inbuf; + + collect_data = (GstCollectData *) collected->data; + adata = (GstAudioMixerCollect *) collect_data; + + inbuf = gst_collect_pads_peek (pads, collect_data); + if (!inbuf) + continue; + + /* New buffer? */ + if (!adata->buffer || adata->buffer != inbuf) { + /* Takes ownership of buffer */ + if (!gst_audio_mixer_fill_buffer (audiomixer, pads, collect_data, adata, + inbuf)) { + dropped = TRUE; + continue; + } + } else { + gst_buffer_unref (inbuf); + } + + if (!adata->buffer && !dropped + && GST_COLLECT_PADS_STATE_IS_SET (&adata->collect, + GST_COLLECT_PADS_STATE_EOS)) { + GST_DEBUG_OBJECT (collect_data->pad, "Pad is in EOS state"); + } else { + is_eos = FALSE; + } + + /* At this point adata->output_offset >= audiomixer->offset or we have no buffer anymore */ + if (adata->output_offset >= audiomixer->offset + && adata->output_offset < + audiomixer->offset + audiomixer->blocksize && adata->buffer) { + GST_LOG_OBJECT (collect_data->pad, "Mixing buffer for current offset"); + gst_audio_mixer_mix_buffer (audiomixer, pads, collect_data, adata, + &outmap); + if (adata->output_offset >= next_offset) { + GST_DEBUG_OBJECT (collect_data->pad, + "Pad is after current offset: %" G_GUINT64_FORMAT " >= %" + G_GUINT64_FORMAT, adata->output_offset, next_offset); + } else { + is_done = FALSE; + } + handled_buffer = TRUE; + } + } + + gst_buffer_unmap (outbuf, &outmap); + + if (dropped) { + /* We dropped a buffer, retry */ + GST_DEBUG_OBJECT (audiomixer, + "A pad dropped a buffer, wait for the next one"); + return GST_FLOW_OK; + } + + if (!is_done && !is_eos) { + /* Get more buffers */ + GST_DEBUG_OBJECT (audiomixer, + "We're not done yet for the current offset," " waiting for more data"); + return GST_FLOW_OK; + } + + if (is_eos) { + gint64 max_offset = 0; + + GST_DEBUG_OBJECT (audiomixer, "We're EOS"); + + /* This means EOS or no pads at all */ + if (!handled_buffer) { + gst_buffer_replace (&audiomixer->current_buffer, NULL); + goto eos; + } + + for (collected = pads->data; collected; collected = collected->next) { + GstCollectData *collect_data; + GstAudioMixerCollect *adata; + + collect_data = (GstCollectData *) collected->data; + adata = (GstAudioMixerCollect *) collect_data; + + max_offset = MAX (max_offset, adata->output_offset); + } + + if (max_offset <= next_offset) { + GST_DEBUG_OBJECT (audiomixer, + "Last buffer is incomplete: %" G_GUINT64_FORMAT " <= %" + G_GUINT64_FORMAT, max_offset, next_offset); + next_offset = max_offset; + + gst_buffer_resize (outbuf, 0, (next_offset - audiomixer->offset) * bpf); + next_timestamp = gst_util_uint64_scale (next_offset, GST_SECOND, rate); + } + } + + /* set timestamps on the output buffer */ + if (audiomixer->segment.rate > 0.0) { + GST_BUFFER_TIMESTAMP (outbuf) = audiomixer->segment.position; + GST_BUFFER_OFFSET (outbuf) = audiomixer->offset; + GST_BUFFER_OFFSET_END (outbuf) = next_offset; + GST_BUFFER_DURATION (outbuf) = + next_timestamp - audiomixer->segment.position; + } else { + GST_BUFFER_TIMESTAMP (outbuf) = next_timestamp; + GST_BUFFER_OFFSET (outbuf) = next_offset; + GST_BUFFER_OFFSET_END (outbuf) = audiomixer->offset; + GST_BUFFER_DURATION (outbuf) = + audiomixer->segment.position - next_timestamp; + } + + audiomixer->offset = next_offset; + audiomixer->segment.position = next_timestamp; + + /* send it out */ + GST_LOG_OBJECT (audiomixer, + "pushing outbuf %p, timestamp %" GST_TIME_FORMAT " offset %" + G_GINT64_FORMAT, outbuf, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), + GST_BUFFER_OFFSET (outbuf)); + + ret = gst_pad_push (audiomixer->srcpad, outbuf); + audiomixer->current_buffer = NULL; + + GST_LOG_OBJECT (audiomixer, "pushed outbuf, result = %s", + gst_flow_get_name (ret)); + + if (ret == GST_FLOW_OK && is_eos) + goto eos; + + return ret; + /* ERRORS */ +not_negotiated: + { + GST_ELEMENT_ERROR (audiomixer, STREAM, FORMAT, (NULL), + ("Unknown data received, not negotiated")); + return GST_FLOW_NOT_NEGOTIATED; + } + +eos: + { + GST_DEBUG_OBJECT (audiomixer, "EOS"); + gst_pad_push_event (audiomixer->srcpad, gst_event_new_eos ()); + return GST_FLOW_EOS; + } +} + +static GstStateChangeReturn +gst_audiomixer_change_state (GstElement * element, GstStateChange transition) +{ + GstAudioMixer *audiomixer; + GstStateChangeReturn ret; + + audiomixer = GST_AUDIO_MIXER (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + audiomixer->offset = 0; + audiomixer->flush_stop_pending = FALSE; + audiomixer->segment_pending = TRUE; + audiomixer->send_stream_start = TRUE; + audiomixer->send_caps = TRUE; + gst_caps_replace (&audiomixer->current_caps, NULL); + gst_segment_init (&audiomixer->segment, GST_FORMAT_TIME); + gst_collect_pads_start (audiomixer->collect); + audiomixer->discont_time = GST_CLOCK_TIME_NONE; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + /* need to unblock the collectpads before calling the + * parent change_state so that streaming can finish */ + gst_collect_pads_stop (audiomixer->collect); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_buffer_replace (&audiomixer->current_buffer, NULL); + break; + default: + break; + } + + return ret; +} + +/* GstChildProxy implementation */ +static GObject * +gst_audiomixer_child_proxy_get_child_by_index (GstChildProxy * child_proxy, + guint index) +{ + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (child_proxy); + GObject *obj = NULL; + + GST_OBJECT_LOCK (audiomixer); + obj = g_list_nth_data (GST_ELEMENT_CAST (audiomixer)->sinkpads, index); + if (obj) + gst_object_ref (obj); + GST_OBJECT_UNLOCK (audiomixer); + + return obj; +} + +static guint +gst_audiomixer_child_proxy_get_children_count (GstChildProxy * child_proxy) +{ + guint count = 0; + GstAudioMixer *audiomixer = GST_AUDIO_MIXER (child_proxy); + + GST_OBJECT_LOCK (audiomixer); + count = GST_ELEMENT_CAST (audiomixer)->numsinkpads; + GST_OBJECT_UNLOCK (audiomixer); + GST_INFO_OBJECT (audiomixer, "Children Count: %d", count); + + return count; +} + +static void +gst_audiomixer_child_proxy_init (gpointer g_iface, gpointer iface_data) +{ + GstChildProxyInterface *iface = g_iface; + + GST_INFO ("intializing child proxy interface"); + iface->get_child_by_index = gst_audiomixer_child_proxy_get_child_by_index; + iface->get_children_count = gst_audiomixer_child_proxy_get_children_count; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "audiomixer", 0, + "audio mixing element"); + + if (!gst_element_register (plugin, "audiomixer", GST_RANK_NONE, + GST_TYPE_AUDIO_MIXER)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + audiomixer, + "Mixes multiple audio streams", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/gst/audiomixer/gstaudiomixer.h b/gst/audiomixer/gstaudiomixer.h new file mode 100644 index 0000000..40a25c9 --- /dev/null +++ b/gst/audiomixer/gstaudiomixer.h @@ -0,0 +1,126 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * Copyright (C) 2013 Sebastian Dröge + * + * gstaudiomixer.h: Header for GstAudioMixer element + * + * 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. + */ + +#ifndef __GST_AUDIO_MIXER_H__ +#define __GST_AUDIO_MIXER_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_AUDIO_MIXER (gst_audiomixer_get_type()) +#define GST_AUDIO_MIXER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_MIXER,GstAudioMixer)) +#define GST_IS_AUDIO_MIXER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_MIXER)) +#define GST_AUDIO_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_MIXER,GstAudioMixerClass)) +#define GST_IS_AUDIO_MIXER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_MIXER)) +#define GST_AUDIO_MIXER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_MIXER,GstAudioMixerClass)) + +typedef struct _GstAudioMixer GstAudioMixer; +typedef struct _GstAudioMixerClass GstAudioMixerClass; + +typedef struct _GstAudioMixerPad GstAudioMixerPad; +typedef struct _GstAudioMixerPadClass GstAudioMixerPadClass; + +/** + * GstAudioMixer: + * + * The audiomixer object structure. + */ +struct _GstAudioMixer { + GstElement element; + + GstPad *srcpad; + GstCollectPads *collect; + /* pad counter, used for creating unique request pads */ + gint padcount; + + /* the next are valid for both int and float */ + GstAudioInfo info; + + /* counters to keep track of timestamps */ + gint64 offset; + /* Buffer starting at offset containing block_size samples */ + GstBuffer *current_buffer; + + /* sink event handling */ + GstSegment segment; + volatile gboolean segment_pending; + volatile gboolean flush_stop_pending; + + /* current caps */ + GstCaps *current_caps; + + /* target caps (set via property) */ + GstCaps *filter_caps; + + GstClockTime alignment_threshold; + GstClockTime discont_wait; + + /* Last time we noticed a discont */ + GstClockTime discont_time; + + /* Size in samples that is output per buffer */ + guint blocksize; + + /* Pending inline events */ + GList *pending_events; + + gboolean send_stream_start; + gboolean send_caps; +}; + +struct _GstAudioMixerClass { + GstElementClass parent_class; +}; + +GType gst_audiomixer_get_type (void); + +#define GST_TYPE_AUDIO_MIXER_PAD (gst_audiomixer_pad_get_type()) +#define GST_AUDIO_MIXER_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPad)) +#define GST_IS_AUDIO_MIXER_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AUDIO_MIXER_PAD)) +#define GST_AUDIO_MIXER_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPadClass)) +#define GST_IS_AUDIO_MIXER_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_AUDIO_MIXER_PAD)) +#define GST_AUDIO_MIXER_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_AUDIO_MIXER_PAD,GstAudioMixerPadClass)) + +struct _GstAudioMixerPad { + GstPad parent; + + gdouble volume; + gint volume_i32; + gint volume_i16; + gint volume_i8; + gboolean mute; +}; + +struct _GstAudioMixerPadClass { + GstPadClass parent_class; +}; + +GType gst_audiomixer_pad_get_type (void); + +G_END_DECLS + + +#endif /* __GST_AUDIO_MIXER_H__ */ diff --git a/gst/audiomixer/gstaudiomixerorc-dist.c b/gst/audiomixer/gstaudiomixerorc-dist.c new file mode 100644 index 0000000..1a54e14 --- /dev/null +++ b/gst/audiomixer/gstaudiomixerorc-dist.c @@ -0,0 +1,2661 @@ + +/* autogenerated from gstaudiomixerorc.orc */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +typedef int8_t orc_int8; +typedef int16_t orc_int16; +typedef int32_t orc_int32; +typedef int64_t orc_int64; +typedef uint8_t orc_uint8; +typedef uint16_t orc_uint16; +typedef uint32_t orc_uint32; +typedef uint64_t orc_uint64; +#define ORC_UINT64_C(x) UINT64_C(x) +#elif defined(_MSC_VER) +typedef signed __int8 orc_int8; +typedef signed __int16 orc_int16; +typedef signed __int32 orc_int32; +typedef signed __int64 orc_int64; +typedef unsigned __int8 orc_uint8; +typedef unsigned __int16 orc_uint16; +typedef unsigned __int32 orc_uint32; +typedef unsigned __int64 orc_uint64; +#define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline +#else +#include +typedef signed char orc_int8; +typedef short orc_int16; +typedef int orc_int32; +typedef unsigned char orc_uint8; +typedef unsigned short orc_uint16; +typedef unsigned int orc_uint32; +#if INT_MAX == LONG_MAX +typedef long long orc_int64; +typedef unsigned long long orc_uint64; +#define ORC_UINT64_C(x) (x##ULL) +#else +typedef long orc_int64; +typedef unsigned long orc_uint64; +#define ORC_UINT64_C(x) (x##UL) +#endif +#endif +typedef union +{ + orc_int16 i; + orc_int8 x2[2]; +} orc_union16; +typedef union +{ + orc_int32 i; + float f; + orc_int16 x2[2]; + orc_int8 x4[4]; +} orc_union32; +typedef union +{ + orc_int64 i; + double f; + orc_int32 x2[2]; + float x2f[2]; + orc_int16 x4[4]; +} orc_union64; +#endif +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif + +#ifndef ORC_INTERNAL +#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define ORC_INTERNAL __hidden +#elif defined (__GNUC__) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#else +#define ORC_INTERNAL +#endif +#endif + + +#ifndef DISABLE_ORC +#include +#endif +void audiomixer_orc_add_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_s8 (gint8 * ORC_RESTRICT d1, + const gint8 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u8 (guint8 * ORC_RESTRICT d1, + const guint8 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_f32 (float *ORC_RESTRICT d1, + const float *ORC_RESTRICT s1, int n); +void audiomixer_orc_add_f64 (double *ORC_RESTRICT d1, + const double *ORC_RESTRICT s1, int n); +void audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n); +void audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, + const guint8 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, + const gint8 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_f32 (float *ORC_RESTRICT d1, + const float *ORC_RESTRICT s1, float p1, int n); +void audiomixer_orc_add_volume_f64 (double *ORC_RESTRICT d1, + const double *ORC_RESTRICT s1, double p1, int n); + + +/* begin Orc C target preamble */ +#define ORC_CLAMP(x,a,b) ((x)<(a) ? (a) : ((x)>(b) ? (b) : (x))) +#define ORC_ABS(a) ((a)<0 ? -(a) : (a)) +#define ORC_MIN(a,b) ((a)<(b) ? (a) : (b)) +#define ORC_MAX(a,b) ((a)>(b) ? (a) : (b)) +#define ORC_SB_MAX 127 +#define ORC_SB_MIN (-1-ORC_SB_MAX) +#define ORC_UB_MAX 255 +#define ORC_UB_MIN 0 +#define ORC_SW_MAX 32767 +#define ORC_SW_MIN (-1-ORC_SW_MAX) +#define ORC_UW_MAX 65535 +#define ORC_UW_MIN 0 +#define ORC_SL_MAX 2147483647 +#define ORC_SL_MIN (-1-ORC_SL_MAX) +#define ORC_UL_MAX 4294967295U +#define ORC_UL_MIN 0 +#define ORC_CLAMP_SB(x) ORC_CLAMP(x,ORC_SB_MIN,ORC_SB_MAX) +#define ORC_CLAMP_UB(x) ORC_CLAMP(x,ORC_UB_MIN,ORC_UB_MAX) +#define ORC_CLAMP_SW(x) ORC_CLAMP(x,ORC_SW_MIN,ORC_SW_MAX) +#define ORC_CLAMP_UW(x) ORC_CLAMP(x,ORC_UW_MIN,ORC_UW_MAX) +#define ORC_CLAMP_SL(x) ORC_CLAMP(x,ORC_SL_MIN,ORC_SL_MAX) +#define ORC_CLAMP_UL(x) ORC_CLAMP(x,ORC_UL_MIN,ORC_UL_MAX) +#define ORC_SWAP_W(x) ((((x)&0xff)<<8) | (((x)&0xff00)>>8)) +#define ORC_SWAP_L(x) ((((x)&0xff)<<24) | (((x)&0xff00)<<8) | (((x)&0xff0000)>>8) | (((x)&0xff000000)>>24)) +#define ORC_SWAP_Q(x) ((((x)&ORC_UINT64_C(0xff))<<56) | (((x)&ORC_UINT64_C(0xff00))<<40) | (((x)&ORC_UINT64_C(0xff0000))<<24) | (((x)&ORC_UINT64_C(0xff000000))<<8) | (((x)&ORC_UINT64_C(0xff00000000))>>8) | (((x)&ORC_UINT64_C(0xff0000000000))>>24) | (((x)&ORC_UINT64_C(0xff000000000000))>>40) | (((x)&ORC_UINT64_C(0xff00000000000000))>>56)) +#define ORC_PTR_OFFSET(ptr,offset) ((void *)(((unsigned char *)(ptr)) + (offset))) +#define ORC_DENORMAL(x) ((x) & ((((x)&0x7f800000) == 0) ? 0xff800000 : 0xffffffff)) +#define ORC_ISNAN(x) ((((x)&0x7f800000) == 0x7f800000) && (((x)&0x007fffff) != 0)) +#define ORC_DENORMAL_DOUBLE(x) ((x) & ((((x)&ORC_UINT64_C(0x7ff0000000000000)) == 0) ? ORC_UINT64_C(0xfff0000000000000) : ORC_UINT64_C(0xffffffffffffffff))) +#define ORC_ISNAN_DOUBLE(x) ((((x)&ORC_UINT64_C(0x7ff0000000000000)) == ORC_UINT64_C(0x7ff0000000000000)) && (((x)&ORC_UINT64_C(0x000fffffffffffff)) != 0)) +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif +/* end Orc C target preamble */ + + + +/* audiomixer_orc_add_s32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addssl */ + var34.i = ORC_CLAMP_SL ((orc_int64) var32.i + (orc_int64) var33.i); + /* 3: storel */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_s32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addssl */ + var34.i = ORC_CLAMP_SL ((orc_int64) var32.i + (orc_int64) var33.i); + /* 3: storel */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 115, 51, 50, 11, 4, 4, 12, 4, 4, 104, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_s32"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + + orc_program_append_2 (p, "addssl", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_s16 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var32; + orc_union16 var33; + orc_union16 var34; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var32 = ptr0[i]; + /* 1: loadw */ + var33 = ptr4[i]; + /* 2: addssw */ + var34.i = ORC_CLAMP_SW (var32.i + var33.i); + /* 3: storew */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_s16 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var32; + orc_union16 var33; + orc_union16 var34; + + ptr0 = (orc_union16 *) ex->arrays[0]; + ptr4 = (orc_union16 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var32 = ptr0[i]; + /* 1: loadw */ + var33 = ptr4[i]; + /* 2: addssw */ + var34.i = ORC_CLAMP_SW (var32.i + var33.i); + /* 3: storew */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 115, 49, 54, 11, 2, 2, 12, 2, 2, 71, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s16); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_s16"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s16); + orc_program_add_destination (p, 2, "d1"); + orc_program_add_source (p, 2, "s1"); + + orc_program_append_2 (p, "addssw", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_s8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_s8 (gint8 * ORC_RESTRICT d1, const gint8 * ORC_RESTRICT s1, + int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var32; + orc_int8 var33; + orc_int8 var34; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var32 = ptr0[i]; + /* 1: loadb */ + var33 = ptr4[i]; + /* 2: addssb */ + var34 = ORC_CLAMP_SB (var32 + var33); + /* 3: storeb */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_s8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var32; + orc_int8 var33; + orc_int8 var34; + + ptr0 = (orc_int8 *) ex->arrays[0]; + ptr4 = (orc_int8 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var32 = ptr0[i]; + /* 1: loadb */ + var33 = ptr4[i]; + /* 2: addssb */ + var34 = ORC_CLAMP_SB (var32 + var33); + /* 3: storeb */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_s8 (gint8 * ORC_RESTRICT d1, const gint8 * ORC_RESTRICT s1, + int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 21, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 115, 56, 11, 1, 1, 12, 1, 1, 34, 0, + 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_s8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_s8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + + orc_program_append_2 (p, "addssb", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_u32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addusl */ + var34.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var32.i + + (orc_int64) (orc_uint32) var33.i); + /* 3: storel */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_u32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addusl */ + var34.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var32.i + + (orc_int64) (orc_uint32) var33.i); + /* 3: storel */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 117, 51, 50, 11, 4, 4, 12, 4, 4, 105, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_u32"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + + orc_program_append_2 (p, "addusl", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_u16 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int n) +{ + int i; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var32; + orc_union16 var33; + orc_union16 var34; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var32 = ptr0[i]; + /* 1: loadw */ + var33 = ptr4[i]; + /* 2: addusw */ + var34.i = ORC_CLAMP_UW ((orc_uint16) var32.i + (orc_uint16) var33.i); + /* 3: storew */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_u16 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var32; + orc_union16 var33; + orc_union16 var34; + + ptr0 = (orc_union16 *) ex->arrays[0]; + ptr4 = (orc_union16 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var32 = ptr0[i]; + /* 1: loadw */ + var33 = ptr4[i]; + /* 2: addusw */ + var34.i = ORC_CLAMP_UW ((orc_uint16) var32.i + (orc_uint16) var33.i); + /* 3: storew */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 117, 49, 54, 11, 2, 2, 12, 2, 2, 72, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u16); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_u16"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u16); + orc_program_add_destination (p, 2, "d1"); + orc_program_add_source (p, 2, "s1"); + + orc_program_append_2 (p, "addusw", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_u8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_u8 (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, + int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var32; + orc_int8 var33; + orc_int8 var34; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var32 = ptr0[i]; + /* 1: loadb */ + var33 = ptr4[i]; + /* 2: addusb */ + var34 = ORC_CLAMP_UB ((orc_uint8) var32 + (orc_uint8) var33); + /* 3: storeb */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_u8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var32; + orc_int8 var33; + orc_int8 var34; + + ptr0 = (orc_int8 *) ex->arrays[0]; + ptr4 = (orc_int8 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var32 = ptr0[i]; + /* 1: loadb */ + var33 = ptr4[i]; + /* 2: addusb */ + var34 = ORC_CLAMP_UB ((orc_uint8) var32 + (orc_uint8) var33); + /* 3: storeb */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_u8 (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, + int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 21, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 117, 56, 11, 1, 1, 12, 1, 1, 35, 0, + 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_u8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_u8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + + orc_program_append_2 (p, "addusb", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_f32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_f32 (float *ORC_RESTRICT d1, const float *ORC_RESTRICT s1, + int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var32.i); + _src2.i = ORC_DENORMAL (var33.i); + _dest1.f = _src1.f + _src2.f; + var34.i = ORC_DENORMAL (_dest1.i); + } + /* 3: storel */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_f32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var32; + orc_union32 var33; + orc_union32 var34; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var32 = ptr0[i]; + /* 1: loadl */ + var33 = ptr4[i]; + /* 2: addf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var32.i); + _src2.i = ORC_DENORMAL (var33.i); + _dest1.f = _src1.f + _src2.f; + var34.i = ORC_DENORMAL (_dest1.i); + } + /* 3: storel */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_f32 (float *ORC_RESTRICT d1, const float *ORC_RESTRICT s1, + int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 102, 51, 50, 11, 4, 4, 12, 4, 4, 200, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_f32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_f32"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_f32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + + orc_program_append_2 (p, "addf", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_f64 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_f64 (double *ORC_RESTRICT d1, const double *ORC_RESTRICT s1, + int n) +{ + int i; + orc_union64 *ORC_RESTRICT ptr0; + const orc_union64 *ORC_RESTRICT ptr4; + orc_union64 var32; + orc_union64 var33; + orc_union64 var34; + + ptr0 = (orc_union64 *) d1; + ptr4 = (orc_union64 *) s1; + + + for (i = 0; i < n; i++) { + /* 0: loadq */ + var32 = ptr0[i]; + /* 1: loadq */ + var33 = ptr4[i]; + /* 2: addd */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var32.i); + _src2.i = ORC_DENORMAL_DOUBLE (var33.i); + _dest1.f = _src1.f + _src2.f; + var34.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 3: storeq */ + ptr0[i] = var34; + } + +} + +#else +static void +_backup_audiomixer_orc_add_f64 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union64 *ORC_RESTRICT ptr0; + const orc_union64 *ORC_RESTRICT ptr4; + orc_union64 var32; + orc_union64 var33; + orc_union64 var34; + + ptr0 = (orc_union64 *) ex->arrays[0]; + ptr4 = (orc_union64 *) ex->arrays[4]; + + + for (i = 0; i < n; i++) { + /* 0: loadq */ + var32 = ptr0[i]; + /* 1: loadq */ + var33 = ptr4[i]; + /* 2: addd */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var32.i); + _src2.i = ORC_DENORMAL_DOUBLE (var33.i); + _dest1.f = _src1.f + _src2.f; + var34.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 3: storeq */ + ptr0[i] = var34; + } + +} + +void +audiomixer_orc_add_f64 (double *ORC_RESTRICT d1, const double *ORC_RESTRICT s1, + int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 22, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 102, 54, 52, 11, 8, 8, 12, 8, 8, 212, + 0, 0, 4, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_f64); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_f64"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_f64); + orc_program_add_destination (p, 8, "d1"); + orc_program_add_source (p, 8, "s1"); + + orc_program_append_2 (p, "addd", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_S1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_volume_u8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + orc_int8 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var35; +#else + orc_int8 var35; +#endif + orc_int8 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var37; +#else + orc_int8 var37; +#endif + orc_int8 var38; + orc_int8 var39; + orc_union16 var40; + orc_union16 var41; + orc_int8 var42; + + ptr0 = (orc_int8 *) d1; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = p1; + /* 7: loadpb */ + var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr0[i]; + /* 2: xorb */ + var39 = var34 ^ var35; + /* 4: mulsbw */ + var40.i = var39 * var36; + /* 5: shrsw */ + var41.i = var40.i >> 3; + /* 6: convssswb */ + var42 = ORC_CLAMP_SB (var41.i); + /* 8: xorb */ + var38 = var42 ^ var37; + /* 9: storeb */ + ptr0[i] = var38; + } + +} + +#else +static void +_backup_audiomixer_orc_volume_u8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + orc_int8 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var35; +#else + orc_int8 var35; +#endif + orc_int8 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var37; +#else + orc_int8 var37; +#endif + orc_int8 var38; + orc_int8 var39; + orc_union16 var40; + orc_union16 var41; + orc_int8 var42; + + ptr0 = (orc_int8 *) ex->arrays[0]; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = ex->params[24]; + /* 7: loadpb */ + var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr0[i]; + /* 2: xorb */ + var39 = var34 ^ var35; + /* 4: mulsbw */ + var40.i = var39 * var36; + /* 5: shrsw */ + var41.i = var40.i >> 3; + /* 6: convssswb */ + var42 = ORC_CLAMP_SB (var41.i); + /* 8: xorb */ + var38 = var42 ^ var37; + /* 9: storeb */ + ptr0[i] = var38; + } + +} + +void +audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 24, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 118, 111, 108, 117, 109, 101, 95, 117, 56, 11, 1, 1, 14, 1, + 128, 0, 0, 0, 14, 4, 3, 0, 0, 0, 16, 1, 20, 2, 20, 1, + 68, 33, 0, 16, 174, 32, 33, 24, 94, 32, 32, 17, 159, 33, 32, 68, + 0, 33, 16, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_volume_u8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_volume_u8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_volume_u8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_constant (p, 1, 0x00000080, "c1"); + orc_program_add_constant (p, 4, 0x00000003, "c2"); + orc_program_add_parameter (p, 1, "p1"); + orc_program_add_temporary (p, 2, "t1"); + orc_program_add_temporary (p, 1, "t2"); + + orc_program_append_2 (p, "xorb", 0, ORC_VAR_T2, ORC_VAR_D1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "mulsbw", 0, ORC_VAR_T1, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsw", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convssswb", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "xorb", 0, ORC_VAR_D1, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_u8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, + const guint8 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var35; +#else + orc_int8 var35; +#endif + orc_int8 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var37; +#else + orc_int8 var37; +#endif + orc_int8 var38; + orc_int8 var39; + orc_int8 var40; + orc_union16 var41; + orc_union16 var42; + orc_int8 var43; + orc_int8 var44; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = p1; + /* 7: loadpb */ + var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[i]; + /* 2: xorb */ + var40 = var34 ^ var35; + /* 4: mulsbw */ + var41.i = var40 * var36; + /* 5: shrsw */ + var42.i = var41.i >> 3; + /* 6: convssswb */ + var43 = ORC_CLAMP_SB (var42.i); + /* 8: xorb */ + var44 = var43 ^ var37; + /* 9: loadb */ + var38 = ptr0[i]; + /* 10: addusb */ + var39 = ORC_CLAMP_UB ((orc_uint8) var38 + (orc_uint8) var44); + /* 11: storeb */ + ptr0[i] = var39; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_u8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var35; +#else + orc_int8 var35; +#endif + orc_int8 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_int8 var37; +#else + orc_int8 var37; +#endif + orc_int8 var38; + orc_int8 var39; + orc_int8 var40; + orc_union16 var41; + orc_union16 var42; + orc_int8 var43; + orc_int8 var44; + + ptr0 = (orc_int8 *) ex->arrays[0]; + ptr4 = (orc_int8 *) ex->arrays[4]; + + /* 1: loadpb */ + var35 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + /* 3: loadpb */ + var36 = ex->params[24]; + /* 7: loadpb */ + var37 = (int) 0x00000080; /* 128 or 6.32404e-322f */ + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[i]; + /* 2: xorb */ + var40 = var34 ^ var35; + /* 4: mulsbw */ + var41.i = var40 * var36; + /* 5: shrsw */ + var42.i = var41.i >> 3; + /* 6: convssswb */ + var43 = ORC_CLAMP_SB (var42.i); + /* 8: xorb */ + var44 = var43 ^ var37; + /* 9: loadb */ + var38 = ptr0[i]; + /* 10: addusb */ + var39 = ORC_CLAMP_UB ((orc_uint8) var38 + (orc_uint8) var44); + /* 11: storeb */ + ptr0[i] = var39; + } + +} + +void +audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, + const guint8 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 28, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 117, 56, 11, + 1, 1, 12, 1, 1, 14, 1, 128, 0, 0, 0, 14, 4, 3, 0, 0, + 0, 16, 1, 20, 2, 20, 1, 68, 33, 4, 16, 174, 32, 33, 24, 94, + 32, 32, 17, 159, 33, 32, 68, 33, 33, 16, 35, 0, 0, 33, 2, 0, + + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_u8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_u8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_u8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + orc_program_add_constant (p, 1, 0x00000080, "c1"); + orc_program_add_constant (p, 4, 0x00000003, "c2"); + orc_program_add_parameter (p, 1, "p1"); + orc_program_add_temporary (p, 2, "t1"); + orc_program_add_temporary (p, 1, "t2"); + + orc_program_append_2 (p, "xorb", 0, ORC_VAR_T2, ORC_VAR_S1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "mulsbw", 0, ORC_VAR_T1, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsw", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convssswb", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "xorb", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "addusb", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_s8 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, + const gint8 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; + orc_int8 var35; + orc_int8 var36; + orc_int8 var37; + orc_union16 var38; + orc_union16 var39; + orc_int8 var40; + + ptr0 = (orc_int8 *) d1; + ptr4 = (orc_int8 *) s1; + + /* 1: loadpb */ + var35 = p1; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[i]; + /* 2: mulsbw */ + var38.i = var34 * var35; + /* 3: shrsw */ + var39.i = var38.i >> 3; + /* 4: convssswb */ + var40 = ORC_CLAMP_SB (var39.i); + /* 5: loadb */ + var36 = ptr0[i]; + /* 6: addssb */ + var37 = ORC_CLAMP_SB (var36 + var40); + /* 7: storeb */ + ptr0[i] = var37; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_s8 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_int8 *ORC_RESTRICT ptr0; + const orc_int8 *ORC_RESTRICT ptr4; + orc_int8 var34; + orc_int8 var35; + orc_int8 var36; + orc_int8 var37; + orc_union16 var38; + orc_union16 var39; + orc_int8 var40; + + ptr0 = (orc_int8 *) ex->arrays[0]; + ptr4 = (orc_int8 *) ex->arrays[4]; + + /* 1: loadpb */ + var35 = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadb */ + var34 = ptr4[i]; + /* 2: mulsbw */ + var38.i = var34 * var35; + /* 3: shrsw */ + var39.i = var38.i >> 3; + /* 4: convssswb */ + var40 = ORC_CLAMP_SB (var39.i); + /* 5: loadb */ + var36 = ptr0[i]; + /* 6: addssb */ + var37 = ORC_CLAMP_SB (var36 + var40); + /* 7: storeb */ + ptr0[i] = var37; + } + +} + +void +audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, + const gint8 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 28, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 115, 56, 11, + 1, 1, 12, 1, 1, 14, 4, 3, 0, 0, 0, 16, 1, 20, 2, 20, + 1, 174, 32, 4, 24, 94, 32, 32, 16, 159, 33, 32, 34, 0, 0, 33, + 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_s8); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_s8"); + orc_program_set_backup_function (p, _backup_audiomixer_orc_add_volume_s8); + orc_program_add_destination (p, 1, "d1"); + orc_program_add_source (p, 1, "s1"); + orc_program_add_constant (p, 4, 0x00000003, "c1"); + orc_program_add_parameter (p, 1, "p1"); + orc_program_add_temporary (p, 2, "t1"); + orc_program_add_temporary (p, 1, "t2"); + + orc_program_append_2 (p, "mulsbw", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsw", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "convssswb", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "addssb", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_u16 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union16 var35; +#else + orc_union16 var35; +#endif + orc_union16 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union16 var37; +#else + orc_union16 var37; +#endif + orc_union16 var38; + orc_union16 var39; + orc_union16 var40; + orc_union32 var41; + orc_union32 var42; + orc_union16 var43; + orc_union16 var44; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + /* 1: loadpw */ + var35.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + /* 3: loadpw */ + var36.i = p1; + /* 7: loadpw */ + var37.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: xorw */ + var40.i = var34.i ^ var35.i; + /* 4: mulswl */ + var41.i = var40.i * var36.i; + /* 5: shrsl */ + var42.i = var41.i >> 11; + /* 6: convssslw */ + var43.i = ORC_CLAMP_SW (var42.i); + /* 8: xorw */ + var44.i = var43.i ^ var37.i; + /* 9: loadw */ + var38 = ptr0[i]; + /* 10: addusw */ + var39.i = ORC_CLAMP_UW ((orc_uint16) var38.i + (orc_uint16) var44.i); + /* 11: storew */ + ptr0[i] = var39; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_u16 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union16 var35; +#else + orc_union16 var35; +#endif + orc_union16 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union16 var37; +#else + orc_union16 var37; +#endif + orc_union16 var38; + orc_union16 var39; + orc_union16 var40; + orc_union32 var41; + orc_union32 var42; + orc_union16 var43; + orc_union16 var44; + + ptr0 = (orc_union16 *) ex->arrays[0]; + ptr4 = (orc_union16 *) ex->arrays[4]; + + /* 1: loadpw */ + var35.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + /* 3: loadpw */ + var36.i = ex->params[24]; + /* 7: loadpw */ + var37.i = (int) 0x00008000; /* 32768 or 1.61895e-319f */ + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: xorw */ + var40.i = var34.i ^ var35.i; + /* 4: mulswl */ + var41.i = var40.i * var36.i; + /* 5: shrsl */ + var42.i = var41.i >> 11; + /* 6: convssslw */ + var43.i = ORC_CLAMP_SW (var42.i); + /* 8: xorw */ + var44.i = var43.i ^ var37.i; + /* 9: loadw */ + var38 = ptr0[i]; + /* 10: addusw */ + var39.i = ORC_CLAMP_UW ((orc_uint16) var38.i + (orc_uint16) var44.i); + /* 11: storew */ + ptr0[i] = var39; + } + +} + +void +audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, + const guint16 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 117, 49, 54, + 11, 2, 2, 12, 2, 2, 14, 2, 0, 128, 0, 0, 14, 4, 11, 0, + 0, 0, 16, 2, 20, 4, 20, 2, 101, 33, 4, 16, 176, 32, 33, 24, + 125, 32, 32, 17, 165, 33, 32, 101, 33, 33, 16, 72, 0, 0, 33, 2, + 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_u16); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_u16"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_u16); + orc_program_add_destination (p, 2, "d1"); + orc_program_add_source (p, 2, "s1"); + orc_program_add_constant (p, 2, 0x00008000, "c1"); + orc_program_add_constant (p, 4, 0x0000000b, "c2"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 2, "t2"); + + orc_program_append_2 (p, "xorw", 0, ORC_VAR_T2, ORC_VAR_S1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "mulswl", 0, ORC_VAR_T1, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convssslw", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "xorw", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "addusw", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_s16 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var34; + orc_union16 var35; + orc_union16 var36; + orc_union16 var37; + orc_union32 var38; + orc_union32 var39; + orc_union16 var40; + + ptr0 = (orc_union16 *) d1; + ptr4 = (orc_union16 *) s1; + + /* 1: loadpw */ + var35.i = p1; + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: mulswl */ + var38.i = var34.i * var35.i; + /* 3: shrsl */ + var39.i = var38.i >> 11; + /* 4: convssslw */ + var40.i = ORC_CLAMP_SW (var39.i); + /* 5: loadw */ + var36 = ptr0[i]; + /* 6: addssw */ + var37.i = ORC_CLAMP_SW (var36.i + var40.i); + /* 7: storew */ + ptr0[i] = var37; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_s16 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union16 *ORC_RESTRICT ptr0; + const orc_union16 *ORC_RESTRICT ptr4; + orc_union16 var34; + orc_union16 var35; + orc_union16 var36; + orc_union16 var37; + orc_union32 var38; + orc_union32 var39; + orc_union16 var40; + + ptr0 = (orc_union16 *) ex->arrays[0]; + ptr4 = (orc_union16 *) ex->arrays[4]; + + /* 1: loadpw */ + var35.i = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadw */ + var34 = ptr4[i]; + /* 2: mulswl */ + var38.i = var34.i * var35.i; + /* 3: shrsl */ + var39.i = var38.i >> 11; + /* 4: convssslw */ + var40.i = ORC_CLAMP_SW (var39.i); + /* 5: loadw */ + var36 = ptr0[i]; + /* 6: addssw */ + var37.i = ORC_CLAMP_SW (var36.i + var40.i); + /* 7: storew */ + ptr0[i] = var37; + } + +} + +void +audiomixer_orc_add_volume_s16 (gint16 * ORC_RESTRICT d1, + const gint16 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 115, 49, 54, + 11, 2, 2, 12, 2, 2, 14, 4, 11, 0, 0, 0, 16, 2, 20, 4, + 20, 2, 176, 32, 4, 24, 125, 32, 32, 16, 165, 33, 32, 71, 0, 0, + 33, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_s16); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_s16"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_s16); + orc_program_add_destination (p, 2, "d1"); + orc_program_add_source (p, 2, "s1"); + orc_program_add_constant (p, 4, 0x0000000b, "c1"); + orc_program_add_parameter (p, 2, "p1"); + orc_program_add_temporary (p, 4, "t1"); + orc_program_add_temporary (p, 2, "t2"); + + orc_program_append_2 (p, "mulswl", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsl", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "convssslw", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "addssw", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_u32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var35; +#else + orc_union32 var35; +#endif + orc_union32 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var37; +#else + orc_union32 var37; +#endif + orc_union32 var38; + orc_union32 var39; + orc_union32 var40; + orc_union64 var41; + orc_union64 var42; + orc_union32 var43; + orc_union32 var44; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + /* 1: loadpl */ + var35.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + /* 3: loadpl */ + var36.i = p1; + /* 7: loadpl */ + var37.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: xorl */ + var40.i = var34.i ^ var35.i; + /* 4: mulslq */ + var41.i = ((orc_int64) var40.i) * ((orc_int64) var36.i); + /* 5: shrsq */ + var42.i = var41.i >> 27; + /* 6: convsssql */ + var43.i = ORC_CLAMP_SL (var42.i); + /* 8: xorl */ + var44.i = var43.i ^ var37.i; + /* 9: loadl */ + var38 = ptr0[i]; + /* 10: addusl */ + var39.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var38.i + + (orc_int64) (orc_uint32) var44.i); + /* 11: storel */ + ptr0[i] = var39; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_u32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var34; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var35; +#else + orc_union32 var35; +#endif + orc_union32 var36; +#if defined(__APPLE__) && __GNUC__ == 4 && __GNUC_MINOR__ == 2 && defined (__i386__) + volatile orc_union32 var37; +#else + orc_union32 var37; +#endif + orc_union32 var38; + orc_union32 var39; + orc_union32 var40; + orc_union64 var41; + orc_union64 var42; + orc_union32 var43; + orc_union32 var44; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + /* 1: loadpl */ + var35.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + /* 3: loadpl */ + var36.i = ex->params[24]; + /* 7: loadpl */ + var37.i = (int) 0x80000000; /* -2147483648 or 1.061e-314f */ + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: xorl */ + var40.i = var34.i ^ var35.i; + /* 4: mulslq */ + var41.i = ((orc_int64) var40.i) * ((orc_int64) var36.i); + /* 5: shrsq */ + var42.i = var41.i >> 27; + /* 6: convsssql */ + var43.i = ORC_CLAMP_SL (var42.i); + /* 8: xorl */ + var44.i = var43.i ^ var37.i; + /* 9: loadl */ + var38 = ptr0[i]; + /* 10: addusl */ + var39.i = + ORC_CLAMP_UL ((orc_int64) (orc_uint32) var38.i + + (orc_int64) (orc_uint32) var44.i); + /* 11: storel */ + ptr0[i] = var39; + } + +} + +void +audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, + const guint32 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 117, 51, 50, + 11, 4, 4, 12, 4, 4, 14, 4, 0, 0, 0, 128, 14, 4, 27, 0, + 0, 0, 16, 4, 20, 8, 20, 4, 132, 33, 4, 16, 178, 32, 33, 24, + 147, 32, 32, 17, 170, 33, 32, 132, 33, 33, 16, 105, 0, 0, 33, 2, + 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_u32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_u32"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_u32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0x80000000, "c1"); + orc_program_add_constant (p, 4, 0x0000001b, "c2"); + orc_program_add_parameter (p, 4, "p1"); + orc_program_add_temporary (p, 8, "t1"); + orc_program_add_temporary (p, 4, "t2"); + + orc_program_append_2 (p, "xorl", 0, ORC_VAR_T2, ORC_VAR_S1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "mulslq", 0, ORC_VAR_T1, ORC_VAR_T2, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsq", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C2, + ORC_VAR_D1); + orc_program_append_2 (p, "convsssql", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "xorl", 0, ORC_VAR_T2, ORC_VAR_T2, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "addusl", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_s32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int p1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var34; + orc_union32 var35; + orc_union32 var36; + orc_union32 var37; + orc_union64 var38; + orc_union64 var39; + orc_union32 var40; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + /* 1: loadpl */ + var35.i = p1; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: mulslq */ + var38.i = ((orc_int64) var34.i) * ((orc_int64) var35.i); + /* 3: shrsq */ + var39.i = var38.i >> 27; + /* 4: convsssql */ + var40.i = ORC_CLAMP_SL (var39.i); + /* 5: loadl */ + var36 = ptr0[i]; + /* 6: addssl */ + var37.i = ORC_CLAMP_SL ((orc_int64) var36.i + (orc_int64) var40.i); + /* 7: storel */ + ptr0[i] = var37; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_s32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var34; + orc_union32 var35; + orc_union32 var36; + orc_union32 var37; + orc_union64 var38; + orc_union64 var39; + orc_union32 var40; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + /* 1: loadpl */ + var35.i = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var34 = ptr4[i]; + /* 2: mulslq */ + var38.i = ((orc_int64) var34.i) * ((orc_int64) var35.i); + /* 3: shrsq */ + var39.i = var38.i >> 27; + /* 4: convsssql */ + var40.i = ORC_CLAMP_SL (var39.i); + /* 5: loadl */ + var36 = ptr0[i]; + /* 6: addssl */ + var37.i = ORC_CLAMP_SL ((orc_int64) var36.i + (orc_int64) var40.i); + /* 7: storel */ + ptr0[i] = var37; + } + +} + +void +audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, + const gint32 * ORC_RESTRICT s1, int p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 115, 51, 50, + 11, 4, 4, 12, 4, 4, 14, 4, 27, 0, 0, 0, 16, 4, 20, 8, + 20, 4, 178, 32, 4, 24, 147, 32, 32, 16, 170, 33, 32, 104, 0, 0, + 33, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_s32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_s32"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_s32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_constant (p, 4, 0x0000001b, "c1"); + orc_program_add_parameter (p, 4, "p1"); + orc_program_add_temporary (p, 8, "t1"); + orc_program_add_temporary (p, 4, "t2"); + + orc_program_append_2 (p, "mulslq", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "shrsq", 0, ORC_VAR_T1, ORC_VAR_T1, ORC_VAR_C1, + ORC_VAR_D1); + orc_program_append_2 (p, "convsssql", 0, ORC_VAR_T2, ORC_VAR_T1, + ORC_VAR_D1, ORC_VAR_D1); + orc_program_append_2 (p, "addssl", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T2, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + ex->params[ORC_VAR_P1] = p1; + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_f32 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_f32 (float *ORC_RESTRICT d1, + const float *ORC_RESTRICT s1, float p1, int n) +{ + int i; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var33; + orc_union32 var34; + orc_union32 var35; + orc_union32 var36; + orc_union32 var37; + + ptr0 = (orc_union32 *) d1; + ptr4 = (orc_union32 *) s1; + + /* 1: loadpl */ + var34.f = p1; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var33 = ptr4[i]; + /* 2: mulf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var33.i); + _src2.i = ORC_DENORMAL (var34.i); + _dest1.f = _src1.f * _src2.f; + var37.i = ORC_DENORMAL (_dest1.i); + } + /* 3: loadl */ + var35 = ptr0[i]; + /* 4: addf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var35.i); + _src2.i = ORC_DENORMAL (var37.i); + _dest1.f = _src1.f + _src2.f; + var36.i = ORC_DENORMAL (_dest1.i); + } + /* 5: storel */ + ptr0[i] = var36; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_f32 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union32 *ORC_RESTRICT ptr0; + const orc_union32 *ORC_RESTRICT ptr4; + orc_union32 var33; + orc_union32 var34; + orc_union32 var35; + orc_union32 var36; + orc_union32 var37; + + ptr0 = (orc_union32 *) ex->arrays[0]; + ptr4 = (orc_union32 *) ex->arrays[4]; + + /* 1: loadpl */ + var34.i = ex->params[24]; + + for (i = 0; i < n; i++) { + /* 0: loadl */ + var33 = ptr4[i]; + /* 2: mulf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var33.i); + _src2.i = ORC_DENORMAL (var34.i); + _dest1.f = _src1.f * _src2.f; + var37.i = ORC_DENORMAL (_dest1.i); + } + /* 3: loadl */ + var35 = ptr0[i]; + /* 4: addf */ + { + orc_union32 _src1; + orc_union32 _src2; + orc_union32 _dest1; + _src1.i = ORC_DENORMAL (var35.i); + _src2.i = ORC_DENORMAL (var37.i); + _dest1.f = _src1.f + _src2.f; + var36.i = ORC_DENORMAL (_dest1.i); + } + /* 5: storel */ + ptr0[i] = var36; + } + +} + +void +audiomixer_orc_add_volume_f32 (float *ORC_RESTRICT d1, + const float *ORC_RESTRICT s1, float p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 102, 51, 50, + 11, 4, 4, 12, 4, 4, 17, 4, 20, 4, 202, 32, 4, 24, 200, 0, + 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_f32); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_f32"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_f32); + orc_program_add_destination (p, 4, "d1"); + orc_program_add_source (p, 4, "s1"); + orc_program_add_parameter_float (p, 4, "p1"); + orc_program_add_temporary (p, 4, "t1"); + + orc_program_append_2 (p, "mulf", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "addf", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + { + orc_union32 tmp; + tmp.f = p1; + ex->params[ORC_VAR_P1] = tmp.i; + } + + func = c->exec; + func (ex); +} +#endif + + +/* audiomixer_orc_add_volume_f64 */ +#ifdef DISABLE_ORC +void +audiomixer_orc_add_volume_f64 (double *ORC_RESTRICT d1, + const double *ORC_RESTRICT s1, double p1, int n) +{ + int i; + orc_union64 *ORC_RESTRICT ptr0; + const orc_union64 *ORC_RESTRICT ptr4; + orc_union64 var33; + orc_union64 var34; + orc_union64 var35; + orc_union64 var36; + orc_union64 var37; + + ptr0 = (orc_union64 *) d1; + ptr4 = (orc_union64 *) s1; + + /* 1: loadpq */ + var34.f = p1; + + for (i = 0; i < n; i++) { + /* 0: loadq */ + var33 = ptr4[i]; + /* 2: muld */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var33.i); + _src2.i = ORC_DENORMAL_DOUBLE (var34.i); + _dest1.f = _src1.f * _src2.f; + var37.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 3: loadq */ + var35 = ptr0[i]; + /* 4: addd */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var35.i); + _src2.i = ORC_DENORMAL_DOUBLE (var37.i); + _dest1.f = _src1.f + _src2.f; + var36.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 5: storeq */ + ptr0[i] = var36; + } + +} + +#else +static void +_backup_audiomixer_orc_add_volume_f64 (OrcExecutor * ORC_RESTRICT ex) +{ + int i; + int n = ex->n; + orc_union64 *ORC_RESTRICT ptr0; + const orc_union64 *ORC_RESTRICT ptr4; + orc_union64 var33; + orc_union64 var34; + orc_union64 var35; + orc_union64 var36; + orc_union64 var37; + + ptr0 = (orc_union64 *) ex->arrays[0]; + ptr4 = (orc_union64 *) ex->arrays[4]; + + /* 1: loadpq */ + var34.i = + (ex->params[24] & 0xffffffff) | ((orc_uint64) (ex->params[24 + + (ORC_VAR_T1 - ORC_VAR_P1)]) << 32); + + for (i = 0; i < n; i++) { + /* 0: loadq */ + var33 = ptr4[i]; + /* 2: muld */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var33.i); + _src2.i = ORC_DENORMAL_DOUBLE (var34.i); + _dest1.f = _src1.f * _src2.f; + var37.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 3: loadq */ + var35 = ptr0[i]; + /* 4: addd */ + { + orc_union64 _src1; + orc_union64 _src2; + orc_union64 _dest1; + _src1.i = ORC_DENORMAL_DOUBLE (var35.i); + _src2.i = ORC_DENORMAL_DOUBLE (var37.i); + _dest1.f = _src1.f + _src2.f; + var36.i = ORC_DENORMAL_DOUBLE (_dest1.i); + } + /* 5: storeq */ + ptr0[i] = var36; + } + +} + +void +audiomixer_orc_add_volume_f64 (double *ORC_RESTRICT d1, + const double *ORC_RESTRICT s1, double p1, int n) +{ + OrcExecutor _ex, *ex = &_ex; + static volatile int p_inited = 0; + static OrcCode *c = 0; + void (*func) (OrcExecutor *); + + if (!p_inited) { + orc_once_mutex_lock (); + if (!p_inited) { + OrcProgram *p; + +#if 1 + static const orc_uint8 bc[] = { + 1, 9, 29, 97, 117, 100, 105, 111, 109, 105, 120, 101, 114, 95, 111, 114, + 99, 95, 97, 100, 100, 95, 118, 111, 108, 117, 109, 101, 95, 102, 54, 52, + 11, 8, 8, 12, 8, 8, 18, 8, 20, 8, 214, 32, 4, 24, 212, 0, + 0, 32, 2, 0, + }; + p = orc_program_new_from_static_bytecode (bc); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_f64); +#else + p = orc_program_new (); + orc_program_set_name (p, "audiomixer_orc_add_volume_f64"); + orc_program_set_backup_function (p, + _backup_audiomixer_orc_add_volume_f64); + orc_program_add_destination (p, 8, "d1"); + orc_program_add_source (p, 8, "s1"); + orc_program_add_parameter_double (p, 8, "p1"); + orc_program_add_temporary (p, 8, "t1"); + + orc_program_append_2 (p, "muld", 0, ORC_VAR_T1, ORC_VAR_S1, ORC_VAR_P1, + ORC_VAR_D1); + orc_program_append_2 (p, "addd", 0, ORC_VAR_D1, ORC_VAR_D1, ORC_VAR_T1, + ORC_VAR_D1); +#endif + + orc_program_compile (p); + c = orc_program_take_code (p); + orc_program_free (p); + } + p_inited = TRUE; + orc_once_mutex_unlock (); + } + ex->arrays[ORC_VAR_A2] = c; + ex->program = 0; + + ex->n = n; + ex->arrays[ORC_VAR_D1] = d1; + ex->arrays[ORC_VAR_S1] = (void *) s1; + { + orc_union64 tmp; + tmp.f = p1; + ex->params[ORC_VAR_P1] = tmp.x2[0]; + ex->params[ORC_VAR_T1] = tmp.x2[1]; + } + + func = c->exec; + func (ex); +} +#endif diff --git a/gst/audiomixer/gstaudiomixerorc-dist.h b/gst/audiomixer/gstaudiomixerorc-dist.h new file mode 100644 index 0000000..af0de01 --- /dev/null +++ b/gst/audiomixer/gstaudiomixerorc-dist.h @@ -0,0 +1,106 @@ + +/* autogenerated from gstaudiomixerorc.orc */ + +#ifndef _GSTAUDIOMIXERORC_H_ +#define _GSTAUDIOMIXERORC_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifndef _ORC_INTEGER_TYPEDEFS_ +#define _ORC_INTEGER_TYPEDEFS_ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +typedef int8_t orc_int8; +typedef int16_t orc_int16; +typedef int32_t orc_int32; +typedef int64_t orc_int64; +typedef uint8_t orc_uint8; +typedef uint16_t orc_uint16; +typedef uint32_t orc_uint32; +typedef uint64_t orc_uint64; +#define ORC_UINT64_C(x) UINT64_C(x) +#elif defined(_MSC_VER) +typedef signed __int8 orc_int8; +typedef signed __int16 orc_int16; +typedef signed __int32 orc_int32; +typedef signed __int64 orc_int64; +typedef unsigned __int8 orc_uint8; +typedef unsigned __int16 orc_uint16; +typedef unsigned __int32 orc_uint32; +typedef unsigned __int64 orc_uint64; +#define ORC_UINT64_C(x) (x##Ui64) +#define inline __inline +#else +#include +typedef signed char orc_int8; +typedef short orc_int16; +typedef int orc_int32; +typedef unsigned char orc_uint8; +typedef unsigned short orc_uint16; +typedef unsigned int orc_uint32; +#if INT_MAX == LONG_MAX +typedef long long orc_int64; +typedef unsigned long long orc_uint64; +#define ORC_UINT64_C(x) (x##ULL) +#else +typedef long orc_int64; +typedef unsigned long orc_uint64; +#define ORC_UINT64_C(x) (x##UL) +#endif +#endif +typedef union { orc_int16 i; orc_int8 x2[2]; } orc_union16; +typedef union { orc_int32 i; float f; orc_int16 x2[2]; orc_int8 x4[4]; } orc_union32; +typedef union { orc_int64 i; double f; orc_int32 x2[2]; float x2f[2]; orc_int16 x4[4]; } orc_union64; +#endif +#ifndef ORC_RESTRICT +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#define ORC_RESTRICT restrict +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define ORC_RESTRICT __restrict__ +#else +#define ORC_RESTRICT +#endif +#endif + +#ifndef ORC_INTERNAL +#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550) +#define ORC_INTERNAL __hidden +#elif defined (__GNUC__) +#define ORC_INTERNAL __attribute__((visibility("hidden"))) +#else +#define ORC_INTERNAL +#endif +#endif + +void audiomixer_orc_add_s32 (gint32 * ORC_RESTRICT d1, const gint32 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_s16 (gint16 * ORC_RESTRICT d1, const gint16 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_s8 (gint8 * ORC_RESTRICT d1, const gint8 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u32 (guint32 * ORC_RESTRICT d1, const guint32 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u16 (guint16 * ORC_RESTRICT d1, const guint16 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_u8 (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_f32 (float * ORC_RESTRICT d1, const float * ORC_RESTRICT s1, int n); +void audiomixer_orc_add_f64 (double * ORC_RESTRICT d1, const double * ORC_RESTRICT s1, int n); +void audiomixer_orc_volume_u8 (guint8 * ORC_RESTRICT d1, int p1, int n); +void audiomixer_orc_add_volume_u8 (guint8 * ORC_RESTRICT d1, const guint8 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s8 (gint8 * ORC_RESTRICT d1, const gint8 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_u16 (guint16 * ORC_RESTRICT d1, const guint16 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s16 (gint16 * ORC_RESTRICT d1, const gint16 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_u32 (guint32 * ORC_RESTRICT d1, const guint32 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_s32 (gint32 * ORC_RESTRICT d1, const gint32 * ORC_RESTRICT s1, int p1, int n); +void audiomixer_orc_add_volume_f32 (float * ORC_RESTRICT d1, const float * ORC_RESTRICT s1, float p1, int n); +void audiomixer_orc_add_volume_f64 (double * ORC_RESTRICT d1, const double * ORC_RESTRICT s1, double p1, int n); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/gst/audiomixer/gstaudiomixerorc.orc b/gst/audiomixer/gstaudiomixerorc.orc new file mode 100644 index 0000000..5eaff23 --- /dev/null +++ b/gst/audiomixer/gstaudiomixerorc.orc @@ -0,0 +1,176 @@ +.function audiomixer_orc_add_s32 +.dest 4 d1 gint32 +.source 4 s1 gint32 + +addssl d1, d1, s1 + + +.function audiomixer_orc_add_s16 +.dest 2 d1 gint16 +.source 2 s1 gint16 + +addssw d1, d1, s1 + + +.function audiomixer_orc_add_s8 +.dest 1 d1 gint8 +.source 1 s1 gint8 + +addssb d1, d1, s1 + + +.function audiomixer_orc_add_u32 +.dest 4 d1 guint32 +.source 4 s1 guint32 + +addusl d1, d1, s1 + + +.function audiomixer_orc_add_u16 +.dest 2 d1 guint16 +.source 2 s1 guint16 + +addusw d1, d1, s1 + + +.function audiomixer_orc_add_u8 +.dest 1 d1 guint8 +.source 1 s1 guint8 + +addusb d1, d1, s1 + + +.function audiomixer_orc_add_f32 +.dest 4 d1 float +.source 4 s1 float + +addf d1, d1, s1 + +.function audiomixer_orc_add_f64 +.dest 8 d1 double +.source 8 s1 double + +addd d1, d1, s1 + + +.function audiomixer_orc_volume_u8 +.dest 1 d1 guint8 +.param 1 p1 +.const 1 c1 0x80 +.temp 2 t1 +.temp 1 t2 + +xorb t2, d1, c1 +mulsbw t1, t2, p1 +shrsw t1, t1, 3 +convssswb t2, t1 +xorb d1, t2, c1 + + +.function audiomixer_orc_add_volume_u8 +.dest 1 d1 guint8 +.source 1 s1 guint8 +.param 1 p1 +.const 1 c1 0x80 +.temp 2 t1 +.temp 1 t2 + +xorb t2, s1, c1 +mulsbw t1, t2, p1 +shrsw t1, t1, 3 +convssswb t2, t1 +xorb t2, t2, c1 +addusb d1, d1, t2 + + +.function audiomixer_orc_add_volume_s8 +.dest 1 d1 gint8 +.source 1 s1 gint8 +.param 1 p1 +.temp 2 t1 +.temp 1 t2 + +mulsbw t1, s1, p1 +shrsw t1, t1, 3 +convssswb t2, t1 +addssb d1, d1, t2 + + +.function audiomixer_orc_add_volume_u16 +.dest 2 d1 guint16 +.source 2 s1 guint16 +.param 2 p1 +.const 2 c1 0x8000 +.temp 4 t1 +.temp 2 t2 + +xorw t2, s1, c1 +mulswl t1, t2, p1 +shrsl t1, t1, 11 +convssslw t2, t1 +xorw t2, t2, c1 +addusw d1, d1, t2 + + +.function audiomixer_orc_add_volume_s16 +.dest 2 d1 gint16 +.source 2 s1 gint16 +.param 2 p1 +.temp 4 t1 +.temp 2 t2 + +mulswl t1, s1, p1 +shrsl t1, t1, 11 +convssslw t2, t1 +addssw d1, d1, t2 + + +.function audiomixer_orc_add_volume_u32 +.dest 4 d1 guint32 +.source 4 s1 guint32 +.param 4 p1 +.const 4 c1 0x80000000 +.temp 8 t1 +.temp 4 t2 + +xorl t2, s1, c1 +mulslq t1, t2, p1 +shrsq t1, t1, 27 +convsssql t2, t1 +xorl t2, t2, c1 +addusl d1, d1, t2 + + +.function audiomixer_orc_add_volume_s32 +.dest 4 d1 gint32 +.source 4 s1 gint32 +.param 4 p1 +.temp 8 t1 +.temp 4 t2 + +mulslq t1, s1, p1 +shrsq t1, t1, 27 +convsssql t2, t1 +addssl d1, d1, t2 + + +.function audiomixer_orc_add_volume_f32 +.dest 4 d1 float +.source 4 s1 float +.floatparam 4 p1 +.temp 4 t1 + +mulf t1, s1, p1 +addf d1, d1, t1 + + +.function audiomixer_orc_add_volume_f64 +.dest 8 d1 double +.source 8 s1 double +.doubleparam 8 p1 +.temp 8 t1 + +muld t1, s1, p1 +addd d1, d1, t1 + + diff --git a/tests/check/elements/audiomixer.c b/tests/check/elements/audiomixer.c new file mode 100644 index 0000000..313cc49 --- /dev/null +++ b/tests/check/elements/audiomixer.c @@ -0,0 +1,1296 @@ +/* GStreamer + * + * unit test for audiomixer + * + * Copyright (C) <2005> Thomas Vander Stichele + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_VALGRIND +# include +#endif + +#include + +#include +#include +#include + +static GMainLoop *main_loop; + +/* make sure downstream gets a CAPS event before buffers are sent */ +GST_START_TEST (test_caps) +{ + GstElement *pipeline, *src, *audiomixer, *sink; + GstStateChangeReturn state_res; + GstCaps *caps; + GstPad *pad; + + /* build pipeline */ + pipeline = gst_pipeline_new ("pipeline"); + + src = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (pipeline), src, audiomixer, sink, NULL); + + fail_unless (gst_element_link_many (src, audiomixer, sink, NULL)); + + /* prepare playing */ + state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_ASYNC); + + /* wait for preroll */ + state_res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_SUCCESS); + + /* check caps on fakesink */ + pad = gst_element_get_static_pad (sink, "sink"); + caps = gst_pad_get_current_caps (pad); + fail_unless (caps != NULL); + gst_caps_unref (caps); + gst_object_unref (pad); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + +/* check that caps set on the property are honoured */ +GST_START_TEST (test_filter_caps) +{ + GstElement *pipeline, *src, *audiomixer, *sink; + GstStateChangeReturn state_res; + GstCaps *filter_caps, *caps; + GstPad *pad; + + filter_caps = gst_caps_new_simple ("audio/x-raw", + "format", G_TYPE_STRING, "F32LE", + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 1, NULL); + + /* build pipeline */ + pipeline = gst_pipeline_new ("pipeline"); + + src = gst_element_factory_make ("audiotestsrc", NULL); + g_object_set (src, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", NULL); + g_object_set (audiomixer, "caps", filter_caps, NULL); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (pipeline), src, audiomixer, sink, NULL); + + fail_unless (gst_element_link_many (src, audiomixer, sink, NULL)); + + /* prepare playing */ + state_res = gst_element_set_state (pipeline, GST_STATE_PAUSED); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_ASYNC); + + /* wait for preroll */ + state_res = gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + fail_unless_equals_int (state_res, GST_STATE_CHANGE_SUCCESS); + + /* check caps on fakesink */ + pad = gst_element_get_static_pad (sink, "sink"); + caps = gst_pad_get_current_caps (pad); + fail_unless (caps != NULL); + GST_INFO_OBJECT (pipeline, "received caps: %" GST_PTR_FORMAT, caps); + fail_unless (gst_caps_is_equal_fixed (caps, filter_caps)); + gst_caps_unref (caps); + gst_object_unref (pad); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + + gst_caps_unref (filter_caps); +} + +GST_END_TEST; + +static void +message_received (GstBus * bus, GstMessage * message, GstPipeline * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_EOS: + g_main_loop_quit (main_loop); + break; + case GST_MESSAGE_WARNING:{ + GError *gerror; + gchar *debug; + + gst_message_parse_warning (message, &gerror, &debug); + gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); + g_error_free (gerror); + g_free (debug); + break; + } + case GST_MESSAGE_ERROR:{ + GError *gerror; + gchar *debug; + + gst_message_parse_error (message, &gerror, &debug); + gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); + g_error_free (gerror); + g_free (debug); + g_main_loop_quit (main_loop); + break; + } + default: + break; + } +} + + +static GstFormat format = GST_FORMAT_UNDEFINED; +static gint64 position = -1; + +static void +test_event_message_received (GstBus * bus, GstMessage * message, + GstPipeline * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_SEGMENT_DONE: + gst_message_parse_segment_done (message, &format, &position); + GST_INFO ("received segment_done : %" G_GINT64_FORMAT, position); + g_main_loop_quit (main_loop); + break; + default: + g_assert_not_reached (); + break; + } +} + + +GST_START_TEST (test_event) +{ + GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstBus *bus; + GstEvent *seek_event; + GstStateChangeReturn state_res; + gboolean res; + GstPad *srcpad, *sinkpad; + GstStreamConsistency *chk_1, *chk_2, *chk_3; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + chk_3 = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + /* create consistency checkers for the pads */ + srcpad = gst_element_get_static_pad (src1, "src"); + chk_1 = gst_consistency_checker_new (srcpad); + sinkpad = gst_pad_get_peer (srcpad); + gst_consistency_checker_add_pad (chk_3, sinkpad); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + srcpad = gst_element_get_static_pad (src2, "src"); + chk_2 = gst_consistency_checker_new (srcpad); + sinkpad = gst_pad_get_peer (srcpad); + gst_consistency_checker_add_pad (chk_3, sinkpad); + gst_object_unref (sinkpad); + gst_object_unref (srcpad); + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + format = GST_FORMAT_UNDEFINED; + position = -1; + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_event_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + GST_INFO ("running main loop"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (position, 2 * GST_SECOND); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_consistency_checker_free (chk_1); + gst_consistency_checker_free (chk_2); + gst_consistency_checker_free (chk_3); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +static guint play_count = 0; +static GstEvent *play_seek_event = NULL; + +static void +test_play_twice_message_received (GstBus * bus, GstMessage * message, + GstPipeline * bin) +{ + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_SEGMENT_DONE: + play_count++; + if (play_count == 1) { + state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_READY); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* prepare playing again */ + state_res = gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (GST_ELEMENT (bin), + gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + state_res = + gst_element_set_state (GST_ELEMENT (bin), GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + } else { + g_main_loop_quit (main_loop); + } + break; + default: + g_assert_not_reached (); + break; + } +} + + +GST_START_TEST (test_play_twice) +{ + GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstBus *bus; + gboolean res; + GstStateChangeReturn state_res; + GstPad *srcpad; + GstStreamConsistency *consist; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + consist = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + play_count = 0; + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_play_twice_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + GST_INFO ("seeked"); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (play_count, 2); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_consistency_checker_free (consist); + gst_event_ref (play_seek_event); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_play_twice_then_add_and_play_again) +{ + GstElement *bin, *src1, *src2, *src3, *audiomixer, *sink; + GstBus *bus; + gboolean res; + GstStateChangeReturn state_res; + gint i; + GstPad *srcpad; + GstStreamConsistency *consist; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + consist = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) test_play_twice_message_received, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + /* run it twice */ + for (i = 0; i < 2; i++) { + play_count = 0; + + GST_INFO ("starting test-loop %d", i); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + GST_INFO ("seeked"); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_READY); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + ck_assert_int_eq (play_count, 2); + + /* plug another source */ + if (i == 0) { + src3 = gst_element_factory_make ("audiotestsrc", "src3"); + g_object_set (src3, "wave", 4, NULL); /* silence */ + gst_bin_add (GST_BIN (bin), src3); + + res = gst_element_link (src3, audiomixer); + fail_unless (res == TRUE, NULL); + } + + gst_consistency_checker_reset (consist); + } + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_event_ref (play_seek_event); + gst_consistency_checker_free (consist); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + + +static void +test_live_seeking_eos_message_received (GstBus * bus, GstMessage * message, + GstPipeline * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + switch (message->type) { + case GST_MESSAGE_EOS: + g_main_loop_quit (main_loop); + break; + default: + g_assert_not_reached (); + break; + } +} + +static GstElement * +test_live_seeking_try_audiosrc (const gchar * factory_name) +{ + GstElement *src; + GstStateChangeReturn state_res; + + if (!(src = gst_element_factory_make (factory_name, NULL))) { + GST_INFO ("can't make '%s', skipping", factory_name); + return NULL; + } + + /* Test that the audio source can get to ready, else skip */ + state_res = gst_element_set_state (src, GST_STATE_READY); + gst_element_set_state (src, GST_STATE_NULL); + + if (state_res == GST_STATE_CHANGE_FAILURE) { + GST_INFO_OBJECT (src, "can't go to ready, skipping"); + gst_object_unref (src); + return NULL; + } + + return src; +} + +/* test failing seeks on live-sources */ +GST_START_TEST (test_live_seeking) +{ + GstElement *bin, *src1 = NULL, *src2, *ac1, *ac2, *audiomixer, *sink; + GstBus *bus; + gboolean res; + GstPad *srcpad; + gint i; + GstStateChangeReturn state_res; + GstStreamConsistency *consist; + /* don't use autoaudiosrc, as then we can't set anything here */ + const gchar *audio_src_factories[] = { + "alsasrc", + "pulseaudiosrc" + }; + + GST_INFO ("preparing test"); + main_loop = NULL; + play_seek_event = NULL; + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + for (i = 0; (i < G_N_ELEMENTS (audio_src_factories) && src1 == NULL); i++) { + src1 = test_live_seeking_try_audiosrc (audio_src_factories[i]); + } + if (!src1) { + /* normal audiosources behave differently than audiotestsrc */ + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, "is-live", TRUE, NULL); /* silence */ + } else { + /* live sources ignore seeks, force eos after 2 sec (4 buffers half second + * each) + */ + g_object_set (src1, "num-buffers", 4, "blocksize", 44100, NULL); + } + + ac1 = gst_element_factory_make ("audioconvert", "ac1"); + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + ac2 = gst_element_factory_make ("audioconvert", "ac2"); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, ac1, src2, ac2, audiomixer, sink, + NULL); + + res = gst_element_link (src1, ac1); + fail_unless (res == TRUE, NULL); + res = gst_element_link (ac1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, ac2); + fail_unless (res == TRUE, NULL); + res = gst_element_link (ac2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + play_seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 2 * GST_SECOND); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", + (GCallback) test_live_seeking_eos_message_received, bin); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + consist = gst_consistency_checker_new (srcpad); + gst_object_unref (srcpad); + + GST_INFO ("starting test"); + + /* run it twice */ + for (i = 0; i < 2; i++) { + + GST_INFO ("starting test-loop %d", i); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, gst_event_ref (play_seek_event)); + fail_unless (res == TRUE, NULL); + + GST_INFO ("seeked"); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + GST_INFO ("playing"); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + gst_consistency_checker_reset (consist); + } + + /* cleanup */ + GST_INFO ("cleaning up"); + gst_consistency_checker_free (consist); + if (main_loop) + g_main_loop_unref (main_loop); + if (play_seek_event) + gst_event_unref (play_seek_event); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +/* check if adding pads work as expected */ +GST_START_TEST (test_add_pad) +{ + GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstBus *bus; + GstPad *srcpad; + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "num-buffers", 4, NULL); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + /* one buffer less, we connect with 1 buffer of delay */ + g_object_set (src2, "num-buffers", 3, NULL); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, audiomixer, sink, NULL); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + gst_object_unref (srcpad); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", (GCallback) message_received, + bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* add other element */ + gst_bin_add_many (GST_BIN (bin), src2, NULL); + + /* now link the second element */ + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + + /* set to PAUSED as well */ + state_res = gst_element_set_state (src2, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* now play all */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +/* check if removing pads work as expected */ +GST_START_TEST (test_remove_pad) +{ + GstElement *bin, *src, *audiomixer, *sink; + GstBus *bus; + GstPad *pad, *srcpad; + gboolean res; + GstStateChangeReturn state_res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src = gst_element_factory_make ("audiotestsrc", "src"); + g_object_set (src, "num-buffers", 4, NULL); + g_object_set (src, "wave", 4, NULL); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src, audiomixer, sink, NULL); + + res = gst_element_link (src, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + /* create an unconnected sinkpad in audiomixer */ + pad = gst_element_get_request_pad (audiomixer, "sink_%u"); + fail_if (pad == NULL, NULL); + + srcpad = gst_element_get_static_pad (audiomixer, "src"); + gst_object_unref (srcpad); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", (GCallback) message_received, + bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing, this will not preroll as audiomixer is waiting + * on the unconnected sinkpad. */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion for one second, will return ASYNC */ + state_res = gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, GST_SECOND); + ck_assert_int_eq (state_res, GST_STATE_CHANGE_ASYNC); + + /* get rid of the pad now, audiomixer should stop waiting on it and + * continue the preroll */ + gst_element_release_request_pad (audiomixer, pad); + gst_object_unref (pad); + + /* wait for completion, should work now */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* now play all */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (G_OBJECT (bus)); + gst_object_unref (G_OBJECT (bin)); +} + +GST_END_TEST; + + +static GstBuffer *handoff_buffer = NULL; +static void +handoff_buffer_cb (GstElement * fakesink, GstBuffer * buffer, GstPad * pad, + gpointer user_data) +{ + GST_DEBUG ("got buffer %p", buffer); + gst_buffer_replace (&handoff_buffer, buffer); +} + +/* check if clipping works as expected */ +GST_START_TEST (test_clip) +{ + GstSegment segment; + GstElement *bin, *audiomixer, *sink; + GstBus *bus; + GstPad *sinkpad; + gboolean res; + GstStateChangeReturn state_res; + GstFlowReturn ret; + GstEvent *event; + GstBuffer *buffer; + GstCaps *caps; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + /* just an audiomixer and a fakesink */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + g_object_set (sink, "signal-handoffs", TRUE, NULL); + g_signal_connect (sink, "handoff", (GCallback) handoff_buffer_cb, NULL); + gst_bin_add_many (GST_BIN (bin), audiomixer, sink, NULL); + + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* create an unconnected sinkpad in audiomixer, should also automatically activate + * the pad */ + sinkpad = gst_element_get_request_pad (audiomixer, "sink_%u"); + fail_if (sinkpad == NULL, NULL); + + gst_pad_send_event (sinkpad, gst_event_new_stream_start ("test")); + + caps = gst_caps_new_simple ("audio/x-raw", +#if G_BYTE_ORDER == G_BIG_ENDIAN + "format", G_TYPE_STRING, "S16BE", +#else + "format", G_TYPE_STRING, "S16LE", +#endif + "layout", G_TYPE_STRING, "interleaved", + "rate", G_TYPE_INT, 44100, "channels", G_TYPE_INT, 2, NULL); + + gst_pad_set_caps (sinkpad, caps); + gst_caps_unref (caps); + + /* send segment to audiomixer */ + gst_segment_init (&segment, GST_FORMAT_TIME); + segment.start = GST_SECOND; + segment.stop = 2 * GST_SECOND; + segment.time = 0; + event = gst_event_new_segment (&segment); + gst_pad_send_event (sinkpad, event); + + /* should be clipped and ok */ + buffer = gst_buffer_new_and_alloc (44100); + GST_BUFFER_TIMESTAMP (buffer) = 0; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer == NULL); + + /* should be partially clipped */ + buffer = gst_buffer_new_and_alloc (44100); + GST_BUFFER_TIMESTAMP (buffer) = 900 * GST_MSECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer != NULL); + gst_buffer_replace (&handoff_buffer, NULL); + + /* should not be clipped */ + buffer = gst_buffer_new_and_alloc (44100); + GST_BUFFER_TIMESTAMP (buffer) = 1 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer != NULL); + gst_buffer_replace (&handoff_buffer, NULL); + + /* should be clipped and ok */ + buffer = gst_buffer_new_and_alloc (44100); + GST_BUFFER_TIMESTAMP (buffer) = 2 * GST_SECOND; + GST_BUFFER_DURATION (buffer) = 250 * GST_MSECOND; + GST_DEBUG ("pushing buffer %p", buffer); + ret = gst_pad_chain (sinkpad, buffer); + ck_assert_int_eq (ret, GST_FLOW_OK); + fail_unless (handoff_buffer == NULL); + + gst_element_release_request_pad (audiomixer, sinkpad); + gst_object_unref (sinkpad); + gst_element_set_state (bin, GST_STATE_NULL); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_duration_is_max) +{ + GstElement *bin, *src[3], *audiomixer, *sink; + GstStateChangeReturn state_res; + GstFormat format = GST_FORMAT_TIME; + gboolean res; + gint64 duration; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + + /* 3 sources, an audiomixer and a fakesink */ + src[0] = gst_element_factory_make ("audiotestsrc", NULL); + src[1] = gst_element_factory_make ("audiotestsrc", NULL); + src[2] = gst_element_factory_make ("audiotestsrc", NULL); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src[0], src[1], src[2], audiomixer, sink, + NULL); + + gst_element_link (src[0], audiomixer); + gst_element_link (src[1], audiomixer); + gst_element_link (src[2], audiomixer); + gst_element_link (audiomixer, sink); + + /* irks, duration is reset on basesrc */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* set durations on src */ + GST_BASE_SRC (src[0])->segment.duration = 1000; + GST_BASE_SRC (src[1])->segment.duration = 3000; + GST_BASE_SRC (src[2])->segment.duration = 2000; + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration); + fail_unless (res, NULL); + + ck_assert_int_eq (duration, 3000); + + gst_element_set_state (bin, GST_STATE_NULL); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_duration_unknown_overrides) +{ + GstElement *bin, *src[3], *audiomixer, *sink; + GstStateChangeReturn state_res; + GstFormat format = GST_FORMAT_TIME; + gboolean res; + gint64 duration; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + + /* 3 sources, an audiomixer and a fakesink */ + src[0] = gst_element_factory_make ("audiotestsrc", NULL); + src[1] = gst_element_factory_make ("audiotestsrc", NULL); + src[2] = gst_element_factory_make ("audiotestsrc", NULL); + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src[0], src[1], src[2], audiomixer, sink, + NULL); + + gst_element_link (src[0], audiomixer); + gst_element_link (src[1], audiomixer); + gst_element_link (src[2], audiomixer); + gst_element_link (audiomixer, sink); + + /* irks, duration is reset on basesrc */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* set durations on src */ + GST_BASE_SRC (src[0])->segment.duration = GST_CLOCK_TIME_NONE; + GST_BASE_SRC (src[1])->segment.duration = 3000; + GST_BASE_SRC (src[2])->segment.duration = 2000; + + /* set to playing */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + /* wait for completion */ + state_res = + gst_element_get_state (GST_ELEMENT (bin), NULL, NULL, + GST_CLOCK_TIME_NONE); + fail_unless (state_res != GST_STATE_CHANGE_FAILURE, NULL); + + res = gst_element_query_duration (GST_ELEMENT (bin), format, &duration); + fail_unless (res, NULL); + + ck_assert_int_eq (duration, GST_CLOCK_TIME_NONE); + + gst_element_set_state (bin, GST_STATE_NULL); + gst_object_unref (bin); +} + +GST_END_TEST; + + +static gboolean looped = FALSE; + +static void +loop_segment_done (GstBus * bus, GstMessage * message, GstElement * bin) +{ + GST_INFO ("bus message from \"%" GST_PTR_FORMAT "\": %" GST_PTR_FORMAT, + GST_MESSAGE_SRC (message), message); + + if (looped) { + g_main_loop_quit (main_loop); + } else { + GstEvent *seek_event; + gboolean res; + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 1 * GST_SECOND); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + looped = TRUE; + } +} + +GST_START_TEST (test_loop) +{ + GstElement *bin, *src1, *src2, *audiomixer, *sink; + GstBus *bus; + GstEvent *seek_event; + GstStateChangeReturn state_res; + gboolean res; + + GST_INFO ("preparing test"); + + /* build pipeline */ + bin = gst_pipeline_new ("pipeline"); + bus = gst_element_get_bus (bin); + gst_bus_add_signal_watch_full (bus, G_PRIORITY_HIGH); + + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (bin), src1, src2, audiomixer, sink, NULL); + + res = gst_element_link (src1, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (src2, audiomixer); + fail_unless (res == TRUE, NULL); + res = gst_element_link (audiomixer, sink); + fail_unless (res == TRUE, NULL); + + seek_event = gst_event_new_seek (1.0, GST_FORMAT_TIME, + GST_SEEK_FLAG_SEGMENT | GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, (GstClockTime) 0, + GST_SEEK_TYPE_SET, (GstClockTime) 1 * GST_SECOND); + + main_loop = g_main_loop_new (NULL, FALSE); + g_signal_connect (bus, "message::segment-done", + (GCallback) loop_segment_done, bin); + g_signal_connect (bus, "message::error", (GCallback) message_received, bin); + g_signal_connect (bus, "message::warning", (GCallback) message_received, bin); + g_signal_connect (bus, "message::eos", (GCallback) message_received, bin); + + GST_INFO ("starting test"); + + /* prepare playing */ + state_res = gst_element_set_state (bin, GST_STATE_PAUSED); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + /* wait for completion */ + state_res = gst_element_get_state (bin, NULL, NULL, GST_CLOCK_TIME_NONE); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + res = gst_element_send_event (bin, seek_event); + fail_unless (res == TRUE, NULL); + + /* run pipeline */ + state_res = gst_element_set_state (bin, GST_STATE_PLAYING); + ck_assert_int_ne (state_res, GST_STATE_CHANGE_FAILURE); + + GST_INFO ("running main loop"); + g_main_loop_run (main_loop); + + state_res = gst_element_set_state (bin, GST_STATE_NULL); + + /* cleanup */ + g_main_loop_unref (main_loop); + gst_bus_remove_signal_watch (bus); + gst_object_unref (bus); + gst_object_unref (bin); +} + +GST_END_TEST; + +GST_START_TEST (test_flush_start_flush_stop) +{ + GstPadTemplate *sink_template; + GstPad *tmppad, *sinkpad1, *sinkpad2, *audiomixer_src; + GstElement *pipeline, *src1, *src2, *audiomixer, *sink; + + GST_INFO ("preparing test"); + + /* build pipeline */ + pipeline = gst_pipeline_new ("pipeline"); + src1 = gst_element_factory_make ("audiotestsrc", "src1"); + g_object_set (src1, "wave", 4, NULL); /* silence */ + src2 = gst_element_factory_make ("audiotestsrc", "src2"); + g_object_set (src2, "wave", 4, NULL); /* silence */ + audiomixer = gst_element_factory_make ("audiomixer", "audiomixer"); + sink = gst_element_factory_make ("fakesink", "sink"); + gst_bin_add_many (GST_BIN (pipeline), src1, src2, audiomixer, sink, NULL); + + sink_template = + gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (audiomixer), + "sink_%u"); + fail_unless (GST_IS_PAD_TEMPLATE (sink_template)); + sinkpad1 = gst_element_request_pad (audiomixer, sink_template, NULL, NULL); + tmppad = gst_element_get_static_pad (src1, "src"); + gst_pad_link (tmppad, sinkpad1); + gst_object_unref (tmppad); + + sinkpad2 = gst_element_request_pad (audiomixer, sink_template, NULL, NULL); + tmppad = gst_element_get_static_pad (src2, "src"); + gst_pad_link (tmppad, sinkpad2); + gst_object_unref (tmppad); + + gst_element_link (audiomixer, sink); + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + fail_unless (gst_element_get_state (pipeline, NULL, NULL, + GST_CLOCK_TIME_NONE) == GST_STATE_CHANGE_SUCCESS); + + audiomixer_src = gst_element_get_static_pad (audiomixer, "src"); + fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); + gst_pad_send_event (sinkpad1, gst_event_new_flush_start ()); + fail_unless (GST_PAD_IS_FLUSHING (audiomixer_src)); + gst_pad_send_event (sinkpad1, gst_event_new_flush_stop (TRUE)); + fail_if (GST_PAD_IS_FLUSHING (audiomixer_src)); + gst_object_unref (audiomixer_src); + + gst_element_release_request_pad (audiomixer, sinkpad1); + gst_object_unref (sinkpad1); + gst_element_release_request_pad (audiomixer, sinkpad2); + gst_object_unref (sinkpad2); + + /* cleanup */ + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); +} + +GST_END_TEST; + + +static Suite * +audiomixer_suite (void) +{ + Suite *s = suite_create ("audiomixer"); + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_caps); + tcase_add_test (tc_chain, test_filter_caps); + tcase_add_test (tc_chain, test_event); + tcase_add_test (tc_chain, test_play_twice); + tcase_add_test (tc_chain, test_play_twice_then_add_and_play_again); + tcase_add_test (tc_chain, test_live_seeking); + tcase_add_test (tc_chain, test_add_pad); + tcase_add_test (tc_chain, test_remove_pad); + tcase_add_test (tc_chain, test_clip); + tcase_add_test (tc_chain, test_duration_is_max); + tcase_add_test (tc_chain, test_duration_unknown_overrides); + tcase_add_test (tc_chain, test_loop); + tcase_add_test (tc_chain, test_flush_start_flush_stop); + + /* Use a longer timeout */ +#ifdef HAVE_VALGRIND + if (RUNNING_ON_VALGRIND) { + tcase_set_timeout (tc_chain, 5 * 60); + } else +#endif + { + /* this is shorter than the default 60 seconds?! (tpm) */ + /* tcase_set_timeout (tc_chain, 6); */ + } + + return s; +} + +GST_CHECK_MAIN (audiomixer); -- 2.7.4