From 24770e913c062f40a289732d2113ceae1c144a10 Mon Sep 17 00:00:00 2001 From: Dave Robillard Date: Tue, 28 Apr 2009 23:24:45 -0400 Subject: Working LV2 plugin discovery. - Separate gstsignalprocessor into a separate library (not sure if this is in the right place, but it works for now anyway) - Create LV2 element based on LADSPA element, port most discovery functionality --- configure.ac | 4 +- ext/Makefile.am | 7 + ext/ladspa/Makefile.am | 6 +- ext/ladspa/gstladspa.h | 2 +- ext/ladspa/gstsignalprocessor.c | 1024 --------------------- ext/ladspa/gstsignalprocessor.h | 124 --- ext/lv2/Makefile.am | 9 + ext/lv2/gstlv2.c | 588 ++++++++++++ ext/lv2/gstlv2.h | 76 ++ gst-libs/gst/Makefile.am | 4 +- gst-libs/gst/signalprocessor/.gitignore | 7 + gst-libs/gst/signalprocessor/Makefile.am | 8 + gst-libs/gst/signalprocessor/gstsignalprocessor.c | 1024 +++++++++++++++++++++ gst-libs/gst/signalprocessor/gstsignalprocessor.h | 124 +++ 14 files changed, 1852 insertions(+), 1155 deletions(-) delete mode 100644 ext/ladspa/gstsignalprocessor.c delete mode 100644 ext/ladspa/gstsignalprocessor.h create mode 100644 ext/lv2/Makefile.am create mode 100644 ext/lv2/gstlv2.c create mode 100644 ext/lv2/gstlv2.h create mode 100644 gst-libs/gst/signalprocessor/.gitignore create mode 100644 gst-libs/gst/signalprocessor/Makefile.am create mode 100644 gst-libs/gst/signalprocessor/gstsignalprocessor.c create mode 100644 gst-libs/gst/signalprocessor/gstsignalprocessor.h diff --git a/configure.ac b/configure.ac index c6708b5f..9fd46354 100644 --- a/configure.ac +++ b/configure.ac @@ -926,7 +926,7 @@ AG_GST_CHECK_FEATURE(LADSPA, [ladspa], ladspa, [ dnl *** LV2 *** translit(dnm, m, l) AM_CONDITIONAL(USE_LV2, true) AG_GST_CHECK_FEATURE(LV2, [lv2], lv2, [ - PKG_CHECK_MODULES(SLV2, slv2 >= 0.6.0, HAVE_SLV2="yes", HAVE_SLV2="no") + PKG_CHECK_MODULES(SLV2, slv2 >= 0.6.0, HAVE_LV2="yes", HAVE_LV2="no") AC_SUBST(SLV2_CFLAGS) AC_SUBST(SLV2_LIBS) ]) @@ -1588,6 +1588,7 @@ gst-libs/Makefile gst-libs/gst/Makefile gst-libs/gst/dshow/Makefile gst-libs/gst/interfaces/Makefile +gst-libs/gst/signalprocessor/Makefile sys/Makefile sys/dshowdecwrapper/Makefile sys/acmenc/Makefile @@ -1629,6 +1630,7 @@ ext/ivorbis/Makefile ext/jack/Makefile ext/jp2k/Makefile ext/ladspa/Makefile +ext/lv2/Makefile ext/libmms/Makefile ext/Makefile ext/nas/Makefile diff --git a/ext/Makefile.am b/ext/Makefile.am index 5313ae80..4576ec7b 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -154,6 +154,12 @@ else LADSPA_DIR = endif +if USE_LV2 +LV2_DIR = lv2 +else +LV2_DIR = +endif + # if USE_LCS # LCS_DIR=lcs # else @@ -357,6 +363,7 @@ SUBDIRS=\ $(JACK_DIR) \ $(JP2K_DIR) \ $(LADSPA_DIR) \ + $(LV2_DIR) \ $(LCS_DIR) \ $(LIBFAME_DIR) \ $(LIBMMS_DIR) \ diff --git a/ext/ladspa/Makefile.am b/ext/ladspa/Makefile.am index 0ea92746..00582ed7 100644 --- a/ext/ladspa/Makefile.am +++ b/ext/ladspa/Makefile.am @@ -1,9 +1,9 @@ plugin_LTLIBRARIES = libgstladspa.la -libgstladspa_la_SOURCES = gstsignalprocessor.c gstladspa.c +libgstladspa_la_SOURCES = gstladspa.c libgstladspa_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_CFLAGS) $(LRDF_CFLAGS) -libgstladspa_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(LIBM) $(LRDF_LIBS) +libgstladspa_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(LIBM) $(LRDF_LIBS) ../../gst-libs/gst/signalprocessor/libgstsignalprocessor.la libgstladspa_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstladspa_la_LIBTOOLFLAGS = --tag=disable-static -noinst_HEADERS = gstsignalprocessor.h gstladspa.h +noinst_HEADERS = gstladspa.h diff --git a/ext/ladspa/gstladspa.h b/ext/ladspa/gstladspa.h index 7ab94791..f51d6233 100644 --- a/ext/ladspa/gstladspa.h +++ b/ext/ladspa/gstladspa.h @@ -28,7 +28,7 @@ #include -#include "gstsignalprocessor.h" +#include "../../gst-libs/gst/signalprocessor/gstsignalprocessor.h" G_BEGIN_DECLS diff --git a/ext/ladspa/gstsignalprocessor.c b/ext/ladspa/gstsignalprocessor.c deleted file mode 100644 index c46ae7a8..00000000 --- a/ext/ladspa/gstsignalprocessor.c +++ /dev/null @@ -1,1024 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2000 Wim Taymans - * 2005 Wim Taymans - * - * gstsignalprocessor.c: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* - * SECTION:gstsignalprocessor - * - * This baseclass allows to write elements that need data on all pads before - * their processing function can run. - * - * In push mode (gst_signal_processor_chain) it operates as follows: - * 1. store each received buffer on the pad and decrement pending_in - * 2. when pending_in==0, process as much as we can and push outputs - * - * In pull mode (gst_signal_processor_getrange) is operates as follows: - * 1. if there is an output ready, deliver - * 2. otherwise pull from each sink-pad, process requested frames and deliver - * the buffer - */ - -#include - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include "gstsignalprocessor.h" - - -GST_DEBUG_CATEGORY_STATIC (gst_signal_processor_debug); -#define GST_CAT_DEFAULT gst_signal_processor_debug - -/* FIXME: this is mono only */ -static GstStaticCaps template_caps = -GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS); - -#define GST_TYPE_SIGNAL_PROCESSOR_PAD_TEMPLATE \ - (gst_signal_processor_pad_template_get_type ()) -#define GST_SIGNAL_PROCESSOR_PAD_TEMPLATE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIGNAL_PROCESSOR_PAD_TEMPLATE,\ - GstSignalProcessorPadTemplate)) -typedef struct _GstSignalProcessorPadTemplate GstSignalProcessorPadTemplate; -typedef GstPadTemplateClass GstSignalProcessorPadTemplateClass; - -struct _GstSignalProcessorPadTemplate -{ - GstPadTemplate parent; - - guint index; -}; - -static GType -gst_signal_processor_pad_template_get_type (void) -{ - static GType type = 0; - - if (!type) { - static const GTypeInfo info = { - sizeof (GstSignalProcessorPadTemplateClass), NULL, NULL, NULL, NULL, - NULL, sizeof (GstSignalProcessorPadTemplate), 0, NULL - }; - - type = g_type_register_static (GST_TYPE_PAD_TEMPLATE, - "GstSignalProcessorPadTemplate", &info, 0); - } - return type; -} - -/* - * gst_signal_processor_class_add_pad_template: - * @klass: element class - * @name: pad name - * @direction: pad direction (src/sink) - * @index: index for the pad per direction (starting from 0) - * - */ -void -gst_signal_processor_class_add_pad_template (GstSignalProcessorClass * klass, - const gchar * name, GstPadDirection direction, guint index) -{ - GstPadTemplate *new; - GstCaps *caps; - - g_return_if_fail (GST_IS_SIGNAL_PROCESSOR_CLASS (klass)); - g_return_if_fail (name != NULL); - g_return_if_fail (direction == GST_PAD_SRC || direction == GST_PAD_SINK); - - /* FIXME: would be nice to have the template as a parameter, right now this can - * only create mono pads */ - caps = gst_caps_copy (gst_static_caps_get (&template_caps)); - - new = g_object_new (gst_signal_processor_pad_template_get_type (), - "name", name, "name-template", name, - "direction", direction, "presence", GST_PAD_ALWAYS, "caps", caps, NULL); - - GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (new)->index = index; - - gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), new); -} - - -#define GST_TYPE_SIGNAL_PROCESSOR_PAD (gst_signal_processor_pad_get_type ()) -#define GST_SIGNAL_PROCESSOR_PAD(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIGNAL_PROCESSOR_PAD,\ - GstSignalProcessorPad)) -typedef struct _GstSignalProcessorPad GstSignalProcessorPad; -typedef GstPadClass GstSignalProcessorPadClass; - -struct _GstSignalProcessorPad -{ - GstPad parent; - - GstBuffer *pen; - - /* index for the pad per direction (starting from 0) */ - guint index; - - /* these are only used for sink pads */ - guint samples_avail; /* available mono sample frames */ - gfloat *data; /* data pointer to read from / write to */ -}; - -static GType -gst_signal_processor_pad_get_type (void) -{ - static GType type = 0; - - if (!type) { - static const GTypeInfo info = { - sizeof (GstSignalProcessorPadClass), NULL, NULL, NULL, NULL, - NULL, sizeof (GstSignalProcessorPad), 0, NULL - }; - - type = g_type_register_static (GST_TYPE_PAD, - "GstSignalProcessorPad", &info, 0); - } - return type; -} - - -GST_BOILERPLATE (GstSignalProcessor, gst_signal_processor, GstElement, - GST_TYPE_ELEMENT); - - -static void gst_signal_processor_finalize (GObject * object); -static gboolean gst_signal_processor_src_activate_pull (GstPad * pad, - gboolean active); -static gboolean gst_signal_processor_sink_activate_push (GstPad * pad, - gboolean active); -static GstStateChangeReturn gst_signal_processor_change_state (GstElement * - element, GstStateChange transition); - -static gboolean gst_signal_processor_event (GstPad * pad, GstEvent * event); -static GstFlowReturn gst_signal_processor_getrange (GstPad * pad, - guint64 offset, guint length, GstBuffer ** buffer); -static GstFlowReturn gst_signal_processor_chain (GstPad * pad, - GstBuffer * buffer); -static gboolean gst_signal_processor_setcaps (GstPad * pad, GstCaps * caps); - - -static void -gst_signal_processor_base_init (gpointer g_class) -{ - GST_DEBUG_CATEGORY_INIT (gst_signal_processor_debug, "gst-dsp", 0, - "signalprocessor element"); -} - -static void -gst_signal_processor_class_init (GstSignalProcessorClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = G_OBJECT_CLASS (klass); - gstelement_class = GST_ELEMENT_CLASS (klass); - - gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_signal_processor_finalize); - - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_signal_processor_change_state); -} - -static void -gst_signal_processor_add_pad_from_template (GstSignalProcessor * self, - GstPadTemplate * templ) -{ - GstPad *new; - - new = g_object_new (GST_TYPE_SIGNAL_PROCESSOR_PAD, - "name", GST_OBJECT_NAME (templ), "direction", templ->direction, - "template", templ, NULL); - GST_SIGNAL_PROCESSOR_PAD (new)->index = - GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (templ)->index; - - gst_pad_set_setcaps_function (new, - GST_DEBUG_FUNCPTR (gst_signal_processor_setcaps)); - - if (templ->direction == GST_PAD_SINK) { - GST_DEBUG ("added new sink pad"); - - gst_pad_set_event_function (new, - GST_DEBUG_FUNCPTR (gst_signal_processor_event)); - gst_pad_set_chain_function (new, - GST_DEBUG_FUNCPTR (gst_signal_processor_chain)); - gst_pad_set_activatepush_function (new, - GST_DEBUG_FUNCPTR (gst_signal_processor_sink_activate_push)); - } else { - GST_DEBUG ("added new src pad"); - - gst_pad_set_getrange_function (new, - GST_DEBUG_FUNCPTR (gst_signal_processor_getrange)); - gst_pad_set_activatepull_function (new, - GST_DEBUG_FUNCPTR (gst_signal_processor_src_activate_pull)); - } - - gst_element_add_pad (GST_ELEMENT (self), new); -} - -static void -gst_signal_processor_init (GstSignalProcessor * self, - GstSignalProcessorClass * klass) -{ - GList *templates; - - templates = - gst_element_class_get_pad_template_list (GST_ELEMENT_CLASS (klass)); - - while (templates) { - GstPadTemplate *templ = GST_PAD_TEMPLATE (templates->data); - - gst_signal_processor_add_pad_from_template (self, templ); - templates = templates->next; - } - - self->state = GST_SIGNAL_PROCESSOR_STATE_NULL; - - self->audio_in = g_new0 (gfloat *, klass->num_audio_in); - self->control_in = g_new0 (gfloat, klass->num_control_in); - self->audio_out = g_new0 (gfloat *, klass->num_audio_out); - self->control_out = g_new0 (gfloat, klass->num_control_out); - - /* init */ - self->pending_in = klass->num_audio_in; - self->pending_out = 0; - - self->sample_rate = 0; -} - -static void -gst_signal_processor_finalize (GObject * object) -{ - GstSignalProcessor *self = GST_SIGNAL_PROCESSOR (object); - - g_free (self->audio_in); - self->audio_in = NULL; - g_free (self->control_in); - self->control_in = NULL; - g_free (self->audio_out); - self->audio_out = NULL; - g_free (self->control_out); - self->control_out = NULL; - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static gboolean -gst_signal_processor_setup (GstSignalProcessor * self, guint sample_rate) -{ - GstSignalProcessorClass *klass; - gboolean ret = TRUE; - - klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - GST_INFO_OBJECT (self, "setup()"); - - g_return_val_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_NULL, FALSE); - - if (klass->setup) - ret = klass->setup (self, sample_rate); - - if (!ret) - goto setup_failed; - - self->state = GST_SIGNAL_PROCESSOR_STATE_INITIALIZED; - - return ret; - -setup_failed: - { - GST_INFO_OBJECT (self, "setup() failed at %u Hz", sample_rate); - return ret; - } -} - -static gboolean -gst_signal_processor_start (GstSignalProcessor * self) -{ - GstSignalProcessorClass *klass; - gboolean ret = TRUE; - - klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - g_return_val_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_INITIALIZED, - FALSE); - - GST_INFO_OBJECT (self, "start()"); - - if (klass->start) - ret = klass->start (self); - - if (!ret) - goto start_failed; - - self->state = GST_SIGNAL_PROCESSOR_STATE_RUNNING; - - return ret; - -start_failed: - { - GST_INFO_OBJECT (self, "start() failed"); - return ret; - } -} - -static void -gst_signal_processor_stop (GstSignalProcessor * self) -{ - GstSignalProcessorClass *klass; - GstElement *elem; - GList *sinks; - - klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - elem = GST_ELEMENT (self); - - GST_INFO_OBJECT (self, "stop()"); - - g_return_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_RUNNING); - - if (klass->stop) - klass->stop (self); - - for (sinks = elem->sinkpads; sinks; sinks = sinks->next) - /* force set_caps when going to RUNNING, see note in _setcaps () */ - gst_pad_set_caps (GST_PAD (sinks->data), NULL); - - /* should also flush our buffers perhaps? */ - - self->state = GST_SIGNAL_PROCESSOR_STATE_INITIALIZED; -} - -static void -gst_signal_processor_cleanup (GstSignalProcessor * self) -{ - GstSignalProcessorClass *klass; - - klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - GST_INFO_OBJECT (self, "cleanup()"); - - g_return_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_INITIALIZED); - - if (klass->cleanup) - klass->cleanup (self); - - self->state = GST_SIGNAL_PROCESSOR_STATE_NULL; -} - -static gboolean -gst_signal_processor_setcaps_pull (GstSignalProcessor * self, GstPad * pad, - GstCaps * caps) -{ - if (GST_PAD_IS_SRC (pad)) { - GList *l; - - for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) - if (!gst_pad_set_caps (GST_PAD (l->data), caps)) - goto src_setcaps_failed; - } else { - GstPad *peer; - gboolean res; - - peer = gst_pad_get_peer (pad); - if (!peer) - goto unlinked_sink; - - res = gst_pad_set_caps (peer, caps); - gst_object_unref (peer); - - if (!res) - goto peer_setcaps_failed; - } - - return TRUE; - -src_setcaps_failed: - { - /* not logging, presumably the sink pad already logged */ - return FALSE; - } -unlinked_sink: - { - GST_WARNING_OBJECT (self, "unlinked sink pad %" GST_PTR_FORMAT ", I wonder " - "how we passed activate_pull()", pad); - return FALSE; - } -peer_setcaps_failed: - { - GST_INFO_OBJECT (self, "peer of %" GST_PTR_FORMAT " did not accept caps", - pad); - return FALSE; - } -} - -static gboolean -gst_signal_processor_setcaps (GstPad * pad, GstCaps * caps) -{ - GstSignalProcessor *self; - - self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); - - if (self->mode == GST_ACTIVATE_PULL && !gst_caps_is_equal (caps, self->caps) - && !gst_signal_processor_setcaps_pull (self, pad, caps)) - goto setcaps_pull_failed; - - /* the whole processor has one caps; if the sample rate changes, let subclass - implementations know */ - if (!gst_caps_is_equal (caps, self->caps)) { - GstStructure *s; - gint sample_rate; - - GST_DEBUG_OBJECT (pad, "got caps %" GST_PTR_FORMAT, caps); - - s = gst_caps_get_structure (caps, 0); - if (!gst_structure_get_int (s, "rate", &sample_rate)) { - GST_WARNING ("got no sample-rate"); - goto impossible; - } - - GST_DEBUG_OBJECT (self, "Got rate=%d", sample_rate); - - if (GST_SIGNAL_PROCESSOR_IS_RUNNING (self)) - gst_signal_processor_stop (self); - if (GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self)) - gst_signal_processor_cleanup (self); - - if (!gst_signal_processor_setup (self, sample_rate)) - goto start_or_setup_failed; - - self->sample_rate = sample_rate; - gst_caps_replace (&self->caps, caps); - } else { - GST_DEBUG_OBJECT (self, "skipping, have caps already"); - } - - /* we use this method to manage the processor's state, hence the caps clearing - in stop(). so it can be that we enter here just to manage the processor's - state, to take it to RUNNING from already being INITIALIZED with the right - sample rate (e.g., when having gone PLAYING->READY->PLAYING). make sure - when we leave that the processor is RUNNING. */ - if (!GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self) - && !gst_signal_processor_setup (self, self->sample_rate)) - goto start_or_setup_failed; - if (!GST_SIGNAL_PROCESSOR_IS_RUNNING (self) - && !gst_signal_processor_start (self)) - goto start_or_setup_failed; - - gst_object_unref (self); - - return TRUE; - -start_or_setup_failed: - { - gst_object_unref (self); - return FALSE; - } -setcaps_pull_failed: - { - gst_object_unref (self); - return FALSE; - } -impossible: - { - g_critical ("something impossible happened"); - gst_object_unref (self); - return FALSE; - } -} - -static gboolean -gst_signal_processor_event (GstPad * pad, GstEvent * event) -{ - GstSignalProcessor *self; - GstSignalProcessorClass *bclass; - gboolean ret; - - self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); - bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - /* FIXME, this probably isn't the correct interface: what about return values, - * what about overriding event_default - * Sync with GstBaseTransform::gst_base_transform_sink_event */ - if (bclass->event) - bclass->event (self, event); - - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_FLUSH_START: - break; - case GST_EVENT_FLUSH_STOP: - /* clear errors now */ - self->flow_state = GST_FLOW_OK; - break; - default: - break; - } - ret = gst_pad_event_default (pad, event); - - gst_object_unref (self); - - return ret; -} - -static guint -gst_signal_processor_prepare (GstSignalProcessor * self, guint nframes) -{ - GstElement *elem = (GstElement *) self; - GstSignalProcessorClass *klass; - GList *sinks, *srcs; - guint samples_avail = nframes; - - klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - /* first, assign audio_in pointers, and determine the number of samples that - * we can process */ - for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { - GstSignalProcessorPad *sinkpad; - - sinkpad = (GstSignalProcessorPad *) sinks->data; - g_assert (sinkpad->samples_avail > 0); - samples_avail = MIN (samples_avail, sinkpad->samples_avail); - self->audio_in[sinkpad->index] = sinkpad->data; - } - - /* FIXME: return if samples_avail==0 ? */ - - /* now assign output buffers. we can avoid allocation by reusing input - buffers, but only if process() can work in place, and if the input buffer - is the exact size of the number of samples we are processing. */ - sinks = elem->sinkpads; - srcs = elem->srcpads; - if (GST_SIGNAL_PROCESSOR_CLASS_CAN_PROCESS_IN_PLACE (klass)) { - while (sinks && srcs) { - GstSignalProcessorPad *sinkpad, *srcpad; - - sinkpad = (GstSignalProcessorPad *) sinks->data; - srcpad = (GstSignalProcessorPad *) srcs->data; - - if (GST_BUFFER_SIZE (sinkpad->pen) == samples_avail * sizeof (gfloat)) { - /* reusable, yay */ - g_assert (sinkpad->samples_avail == samples_avail); - srcpad->pen = sinkpad->pen; - sinkpad->pen = NULL; - self->audio_out[srcpad->index] = sinkpad->data; - self->pending_out++; - - srcs = srcs->next; - } - - sinks = sinks->next; - } - } - - g_return_val_if_fail (GST_SIGNAL_PROCESSOR_IS_RUNNING (self), 0); - - /* now allocate for any remaining outputs */ - while (srcs) { - GstSignalProcessorPad *srcpad; - GstFlowReturn ret; - - srcpad = (GstSignalProcessorPad *) srcs->data; - - ret = - gst_pad_alloc_buffer_and_set_caps (GST_PAD (srcpad), -1, - samples_avail * sizeof (gfloat), self->caps, &srcpad->pen); - - if (ret != GST_FLOW_OK) { - self->flow_state = ret; - return 0; - } else { - self->audio_out[srcpad->index] = (gfloat *) GST_BUFFER_DATA (srcpad->pen); - self->pending_out++; - } - - srcs = srcs->next; - } - - return samples_avail; -} - -static void -gst_signal_processor_update_inputs (GstSignalProcessor * self, guint nprocessed) -{ - GstElement *elem = (GstElement *) self; - GList *sinks; - - for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { - GstSignalProcessorPad *sinkpad; - - sinkpad = (GstSignalProcessorPad *) sinks->data; - g_assert (sinkpad->samples_avail >= nprocessed); - - if (sinkpad->pen && sinkpad->samples_avail == nprocessed) { - /* used up this buffer, unpen */ - gst_buffer_unref (sinkpad->pen); - sinkpad->pen = NULL; - } - - if (!sinkpad->pen) { - /* this buffer was used up */ - self->pending_in++; - sinkpad->data = NULL; - sinkpad->samples_avail = 0; - } else { - /* advance ->data pointers and decrement ->samples_avail, unreffing buffer - if no samples are left */ - sinkpad->samples_avail -= nprocessed; - sinkpad->data += nprocessed; /* gfloat* arithmetic */ - } - } -} - -static void -gst_signal_processor_process (GstSignalProcessor * self, guint nframes) -{ - GstElement *elem; - GstSignalProcessorClass *klass; - - /* check if we have buffers enqueued */ - g_return_if_fail (self->pending_in == 0); - g_return_if_fail (self->pending_out == 0); - - elem = GST_ELEMENT (self); - - /* check how much input is available and prepare output buffers */ - nframes = gst_signal_processor_prepare (self, nframes); - if (G_UNLIKELY (nframes == 0)) - goto flow_error; - - klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - GST_LOG_OBJECT (self, "process(%u)", nframes); - - klass->process (self, nframes); - - gst_signal_processor_update_inputs (self, nframes); - - return; - -flow_error: - { - GST_WARNING ("gst_pad_alloc_buffer_and_set_caps() returned %d", - self->flow_state); - return; - } -} - -static void -gst_signal_processor_pen_buffer (GstSignalProcessor * self, GstPad * pad, - GstBuffer * buffer) -{ - GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pad; - - if (spad->pen) - goto had_buffer; - - /* keep the reference */ - spad->pen = buffer; - spad->data = (gfloat *) GST_BUFFER_DATA (buffer); - spad->samples_avail = GST_BUFFER_SIZE (buffer) / sizeof (float); - - g_assert (self->pending_in != 0); - - self->pending_in--; - - return; - - /* ERRORS */ -had_buffer: - { - GST_WARNING ("Pad %s:%s already has penned buffer", - GST_DEBUG_PAD_NAME (pad)); - gst_buffer_unref (buffer); - return; - } -} - -static void -gst_signal_processor_flush (GstSignalProcessor * self) -{ - GList *pads; - GstSignalProcessorClass *klass; - - klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - GST_INFO_OBJECT (self, "flush()"); - - /* release enqueued buffers */ - for (pads = GST_ELEMENT (self)->pads; pads; pads = pads->next) { - GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pads->data; - - if (spad->pen) { - gst_buffer_unref (spad->pen); - spad->pen = NULL; - spad->data = NULL; - spad->samples_avail = 0; - } - } - - /* no outputs prepared and inputs for each pad needed */ - self->pending_out = 0; - self->pending_in = klass->num_audio_in; -} - -static void -gst_signal_processor_do_pulls (GstSignalProcessor * self, guint nframes) -{ - GList *sinkpads; - - /* FIXME: not threadsafe atm */ - - sinkpads = GST_ELEMENT (self)->sinkpads; - - for (; sinkpads; sinkpads = sinkpads->next) { - GstSignalProcessorPad *spad = (GstSignalProcessorPad *) sinkpads->data; - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *buf; - - if (spad->pen) { - g_warning ("Unexpectedly full buffer pen for pad %s:%s", - GST_DEBUG_PAD_NAME (spad)); - continue; - } - - ret = - gst_pad_pull_range (GST_PAD (spad), -1, nframes * sizeof (gfloat), - &buf); - - if (ret != GST_FLOW_OK) { - gst_signal_processor_flush (self); - self->flow_state = ret; - return; - } else if (!buf) { - g_critical ("Pull failed to make a buffer!"); - self->flow_state = GST_FLOW_ERROR; - return; - } else { - gst_signal_processor_pen_buffer (self, GST_PAD (spad), buf); - } - } - - if (self->pending_in != 0) { - g_critical ("Something wierd happened..."); - self->flow_state = GST_FLOW_ERROR; - } else { - gst_signal_processor_process (self, nframes); - } -} - -static GstFlowReturn -gst_signal_processor_getrange (GstPad * pad, guint64 offset, - guint length, GstBuffer ** buffer) -{ - GstSignalProcessor *self; - GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pad; - GstFlowReturn ret = GST_FLOW_ERROR; - - self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); - - if (spad->pen) { - *buffer = spad->pen; - spad->pen = NULL; - g_assert (self->pending_out != 0); - self->pending_out--; - ret = GST_FLOW_OK; - } else { - gst_signal_processor_do_pulls (self, length / sizeof (gfloat)); - if (!spad->pen) { - /* this is an error condition */ - *buffer = NULL; - ret = self->flow_state; - } else { - *buffer = spad->pen; - spad->pen = NULL; - self->pending_out--; - ret = GST_FLOW_OK; - } - } - - GST_DEBUG_OBJECT (self, "returns %s", gst_flow_get_name (ret)); - - gst_object_unref (self); - - return ret; -} - -static void -gst_signal_processor_do_pushes (GstSignalProcessor * self) -{ - GList *srcpads; - - /* not threadsafe atm */ - - srcpads = GST_ELEMENT (self)->srcpads; - - for (; srcpads; srcpads = srcpads->next) { - GstSignalProcessorPad *spad = (GstSignalProcessorPad *) srcpads->data; - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *buffer; - - if (!spad->pen) { - g_warning ("Unexpectedly empty buffer pen for pad %s:%s", - GST_DEBUG_PAD_NAME (spad)); - continue; - } - - /* take buffer from pen */ - buffer = spad->pen; - spad->pen = NULL; - - ret = gst_pad_push (GST_PAD (spad), buffer); - - if (ret != GST_FLOW_OK) { - gst_signal_processor_flush (self); - self->flow_state = ret; - return; - } else { - g_assert (self->pending_out > 0); - self->pending_out--; - } - } - - if (self->pending_out != 0) { - g_critical ("Something wierd happened..."); - self->flow_state = GST_FLOW_ERROR; - } -} - -static GstFlowReturn -gst_signal_processor_chain (GstPad * pad, GstBuffer * buffer) -{ - GstSignalProcessor *self; - - self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); - - gst_signal_processor_pen_buffer (self, pad, buffer); - - if (self->pending_in == 0) { - gst_signal_processor_process (self, G_MAXUINT); - - gst_signal_processor_do_pushes (self); - } - - gst_object_unref (self); - - return self->flow_state; -} - -static gboolean -gst_signal_processor_sink_activate_push (GstPad * pad, gboolean active) -{ - gboolean result = TRUE; - GstSignalProcessor *self; - GstSignalProcessorClass *bclass; - - self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); - bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - if (active) { - if (self->mode == GST_ACTIVATE_NONE) { - self->mode = GST_ACTIVATE_PUSH; - result = TRUE; - } else if (self->mode == GST_ACTIVATE_PUSH) { - result = TRUE; - } else { - g_warning ("foo"); - result = FALSE; - } - } else { - if (self->mode == GST_ACTIVATE_NONE) { - result = TRUE; - } else if (self->mode == GST_ACTIVATE_PUSH) { - self->mode = GST_ACTIVATE_NONE; - result = TRUE; - } else { - g_warning ("foo"); - result = FALSE; - } - } - - GST_DEBUG_OBJECT (self, "result : %d", result); - - gst_object_unref (self); - - return result; -} - -static gboolean -gst_signal_processor_src_activate_pull (GstPad * pad, gboolean active) -{ - gboolean result = TRUE; - GstSignalProcessor *self; - GstSignalProcessorClass *bclass; - - self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); - bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); - - if (active) { - if (self->mode == GST_ACTIVATE_NONE) { - GList *l; - - for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) - result &= gst_pad_activate_pull (pad, active); - if (result) - self->mode = GST_ACTIVATE_PULL; - } else if (self->mode == GST_ACTIVATE_PULL) { - result = TRUE; - } else { - g_warning ("foo"); - result = FALSE; - } - } else { - if (self->mode == GST_ACTIVATE_NONE) { - result = TRUE; - } else if (self->mode == GST_ACTIVATE_PULL) { - GList *l; - - for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) - result &= gst_pad_activate_pull (pad, active); - if (result) - self->mode = GST_ACTIVATE_NONE; - result = TRUE; - } else { - g_warning ("foo"); - result = FALSE; - } - } - - GST_DEBUG_OBJECT (self, "result : %d", result); - - gst_object_unref (self); - - return result; -} - -static GstStateChangeReturn -gst_signal_processor_change_state (GstElement * element, - GstStateChange transition) -{ - GstSignalProcessor *self; - GstStateChangeReturn result; - - self = GST_SIGNAL_PROCESSOR (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - self->flow_state = GST_FLOW_OK; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - if ((result = - GST_ELEMENT_CLASS (parent_class)->change_state (element, - transition)) == GST_STATE_CHANGE_FAILURE) - goto failure; - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - if (GST_SIGNAL_PROCESSOR_IS_RUNNING (self)) - gst_signal_processor_stop (self); - gst_signal_processor_flush (self); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - if (GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self)) - gst_signal_processor_cleanup (self); - break; - default: - break; - } - - return result; - - /* ERRORS */ -failure: - { - GST_DEBUG_OBJECT (element, "parent failed state change"); - return result; - } -} diff --git a/ext/ladspa/gstsignalprocessor.h b/ext/ladspa/gstsignalprocessor.h deleted file mode 100644 index d6f0d0b0..00000000 --- a/ext/ladspa/gstsignalprocessor.h +++ /dev/null @@ -1,124 +0,0 @@ -/* GStreamer - * Copyright (C) 1999,2000 Erik Walthinsen - * 2005 Wim Taymans - * - * gstsignalprocessor.h: - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - - -#ifndef __GST_SIGNAL_PROCESSOR_H__ -#define __GST_SIGNAL_PROCESSOR_H__ - -#include - -G_BEGIN_DECLS - - -typedef enum -{ - GST_SIGNAL_PROCESSOR_CLASS_FLAG_CAN_PROCESS_IN_PLACE = 1<<0 -} GstSignalProcessorClassFlags; - -#define GST_SIGNAL_PROCESSOR_CLASS_CAN_PROCESS_IN_PLACE(klass) \ - (GST_SIGNAL_PROCESSOR_CLASS (klass)->flags & \ - GST_SIGNAL_PROCESSOR_CLASS_FLAG_CAN_PROCESS_IN_PLACE) -#define GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE(klass) \ - GST_SIGNAL_PROCESSOR_CLASS (klass)->flags |= \ - GST_SIGNAL_PROCESSOR_CLASS_FLAG_CAN_PROCESS_IN_PLACE - -typedef enum -{ - GST_SIGNAL_PROCESSOR_STATE_NULL, - GST_SIGNAL_PROCESSOR_STATE_INITIALIZED, - GST_SIGNAL_PROCESSOR_STATE_RUNNING -} GstSignalProcessorState; - - -#define GST_TYPE_SIGNAL_PROCESSOR (gst_signal_processor_get_type()) -#define GST_SIGNAL_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIGNAL_PROCESSOR,GstSignalProcessor)) -#define GST_SIGNAL_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SIGNAL_PROCESSOR,GstSignalProcessorClass)) -#define GST_SIGNAL_PROCESSOR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_SIGNAL_PROCESSOR,GstSignalProcessorClass)) -#define GST_IS_SIGNAL_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SIGNAL_PROCESSOR)) -#define GST_IS_SIGNAL_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SIGNAL_PROCESSOR)) - -#define GST_SIGNAL_PROCESSOR_IS_INITIALIZED(obj) \ - (GST_SIGNAL_PROCESSOR (obj)->state >= GST_SIGNAL_PROCESSOR_STATE_INITIALIZED) -#define GST_SIGNAL_PROCESSOR_IS_RUNNING(obj) \ - (GST_SIGNAL_PROCESSOR (obj)->state == GST_SIGNAL_PROCESSOR_STATE_RUNNING) - -typedef struct _GstSignalProcessor GstSignalProcessor; -typedef struct _GstSignalProcessorClass GstSignalProcessorClass; - - -struct _GstSignalProcessor { - GstElement element; - - GstCaps *caps; - - guint sample_rate; - - GstSignalProcessorState state; - - GstFlowReturn flow_state; - - GstActivateMode mode; - - /* pending inputs before processing can take place */ - guint pending_in; - /* panding outputs to be filled */ - guint pending_out; - - gfloat *control_in; - gfloat **audio_in; - gfloat *control_out; - gfloat **audio_out; -}; - -struct _GstSignalProcessorClass { - GstElementClass parent_class; - - /*< public >*/ - guint num_control_in; - guint num_audio_in; - guint num_control_out; - guint num_audio_out; - - guint flags; - - /* virtual methods for subclasses */ - - gboolean (*setup) (GstSignalProcessor *self, guint sample_rate); - gboolean (*start) (GstSignalProcessor *self); - void (*stop) (GstSignalProcessor *self); - void (*cleanup) (GstSignalProcessor *self); - void (*process) (GstSignalProcessor *self, guint num_frames); - gboolean (*event) (GstSignalProcessor *self, GstEvent *event); -}; - - -GType gst_signal_processor_get_type (void); -void gst_signal_processor_class_add_pad_template (GstSignalProcessorClass *klass, - const gchar *name, GstPadDirection direction, guint index); - - - -G_END_DECLS - - -#endif /* __GST_SIGNAL_PROCESSOR_H__ */ diff --git a/ext/lv2/Makefile.am b/ext/lv2/Makefile.am new file mode 100644 index 00000000..f10c7acf --- /dev/null +++ b/ext/lv2/Makefile.am @@ -0,0 +1,9 @@ +plugin_LTLIBRARIES = libgstlv2.la + +libgstlv2_la_SOURCES = gstlv2.c +libgstlv2_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CONTROLLER_CFLAGS) $(GST_CFLAGS) $(SLV2_CFLAGS) +libgstlv2_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_MAJORMINOR) $(LIBM) $(SLV2_LIBS) ../../gst-libs/gst/signalprocessor/libgstsignalprocessor.la +libgstlv2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstlv2_la_LIBTOOLFLAGS = --tag=disable-static + +noinst_HEADERS = gstlv2.h diff --git a/ext/lv2/gstlv2.c b/ext/lv2/gstlv2.c new file mode 100644 index 00000000..5d05f603 --- /dev/null +++ b/ext/lv2/gstlv2.c @@ -0,0 +1,588 @@ +/* GStreamer + * Copyright (C) 1999 Erik Walthinsen + * 2001 Steve Baker + * 2003 Andy Wingo + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include + +#include "gstlv2.h" +#include + +#define GST_SLV2_PLUGIN_QDATA g_quark_from_static_string("slv2-plugin") + +static void gst_lv2_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); + +static void gst_lv2_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static gboolean gst_lv2_setup (GstSignalProcessor * sigproc, guint sample_rate); +static gboolean gst_lv2_start (GstSignalProcessor * sigproc); +static void gst_lv2_stop (GstSignalProcessor * sigproc); +static void gst_lv2_cleanup (GstSignalProcessor * sigproc); +static void gst_lv2_process (GstSignalProcessor * sigproc, guint nframes); + +static SLV2World world; +SLV2Value audio_class; +SLV2Value control_class; +SLV2Value input_class; +SLV2Value output_class; +SLV2Value integer_prop; +SLV2Value toggled_prop; + +static GstSignalProcessorClass *parent_class; + +static GstPlugin *gst_lv2_plugin; + +GST_DEBUG_CATEGORY_STATIC (lv2_debug); +#define GST_CAT_DEFAULT lv2_debug + + +static void +gst_lv2_base_init (gpointer g_class) +{ + GstLV2Class *klass = (GstLV2Class *) g_class; + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + GstSignalProcessorClass *gsp_class = GST_SIGNAL_PROCESSOR_CLASS (g_class); + GstElementDetails *details; + SLV2Plugin lv2plugin; + SLV2Value val; + guint j, audio_in_count, audio_out_count, control_in_count, control_out_count; + gchar *klass_tags; + + GST_DEBUG ("base_init %p", g_class); + + lv2plugin = (SLV2Plugin) g_type_get_qdata (G_OBJECT_CLASS_TYPE (klass), + GST_SLV2_PLUGIN_QDATA); + + g_assert (lv2plugin); + + /* pad templates */ + gsp_class->num_audio_in = 0; + gsp_class->num_audio_out = 0; + /* properties */ + gsp_class->num_control_in = 0; + gsp_class->num_control_out = 0; + + for (j = 0; j < slv2_plugin_get_num_ports (lv2plugin); j++) { + SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j); + if (slv2_port_is_a (lv2plugin, port, audio_class)) { + gchar *name = + g_strdup (slv2_value_as_string (slv2_port_get_symbol (lv2plugin, + port))); + + GST_DEBUG ("LV2 port name: \"%s\"", name); + + if (slv2_port_is_a (lv2plugin, port, input_class)) + gst_signal_processor_class_add_pad_template (gsp_class, name, + GST_PAD_SINK, gsp_class->num_audio_in++); + else if (slv2_port_is_a (lv2plugin, port, output_class)) + gst_signal_processor_class_add_pad_template (gsp_class, name, + GST_PAD_SRC, gsp_class->num_audio_out++); + /* TODO: else ignore plugin */ + + g_free (name); + } else if (slv2_port_is_a (lv2plugin, port, control_class)) { + if (slv2_port_is_a (lv2plugin, port, input_class)) + gsp_class->num_control_in++; + else if (slv2_port_is_a (lv2plugin, port, output_class)) + gsp_class->num_control_out++; + /* TODO: else ignore plugin */ + } + /* TODO: else ignore plugin */ + } + + /* construct the element details struct */ + details = g_new0 (GstElementDetails, 1); + val = slv2_plugin_get_name (lv2plugin); + if (val) { + details->longname = g_strdup (slv2_value_as_string (val)); + slv2_value_free (val); + } else { + details->longname = g_strdup ("no description available"); + } + details->description = details->longname; + val = slv2_plugin_get_author_name (lv2plugin); + if (val) { + details->author = g_strdup (slv2_value_as_string (val)); + slv2_value_free (val); + } else { + details->author = g_strdup ("no author available"); + } + + if (gsp_class->num_audio_in == 0) + klass_tags = "Source/Audio/LV2"; + else if (gsp_class->num_audio_out == 0) { + if (gsp_class->num_control_out == 0) + klass_tags = "Sink/Audio/LV2"; + else + klass_tags = "Sink/Analyzer/Audio/LV2"; + } else + klass_tags = "Filter/Effect/Audio/LV2"; + + details->klass = klass_tags; + GST_INFO ("tags : %s", details->klass); + gst_element_class_set_details (element_class, details); + g_free (details->longname); + g_free (details->author); + g_free (details); + + klass->audio_in_portnums = g_new0 (gint, gsp_class->num_audio_in); + klass->audio_out_portnums = g_new0 (gint, gsp_class->num_audio_out); + klass->control_in_portnums = g_new0 (gint, gsp_class->num_control_in); + klass->control_out_portnums = g_new0 (gint, gsp_class->num_control_out); + + audio_in_count = audio_out_count = control_in_count = control_out_count = 0; + + for (j = 0; j < slv2_plugin_get_num_ports (lv2plugin); j++) { + SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, j); + gboolean is_input = slv2_port_is_a (lv2plugin, port, input_class); + if (slv2_port_is_a (lv2plugin, port, audio_class)) { + if (is_input) + klass->audio_in_portnums[audio_in_count++] = j; + else + klass->audio_out_portnums[audio_out_count++] = j; + } else if (slv2_port_is_a (lv2plugin, port, control_class)) { + if (is_input) + klass->control_in_portnums[control_in_count++] = j; + else + klass->control_out_portnums[control_out_count++] = j; + } + } + + g_assert (audio_in_count == gsp_class->num_audio_in); + g_assert (audio_out_count == gsp_class->num_audio_out); + g_assert (control_in_count == gsp_class->num_control_in); + g_assert (control_out_count == gsp_class->num_control_out); + + /*if (!LV2_IS_INPLACE_BROKEN (desc->Properties)) + GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE (klass); */ + + klass->plugin = lv2plugin; +} + +static gchar * +gst_lv2_class_get_param_name (GstLV2Class * klass, gint portnum) +{ + SLV2Plugin lv2plugin = klass->plugin; + SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, portnum); + return g_strdup (slv2_value_as_string (slv2_port_get_symbol (lv2plugin, + port))); +} + +static GParamSpec * +gst_lv2_class_get_param_spec (GstLV2Class * klass, gint portnum) +{ + SLV2Plugin lv2plugin = klass->plugin; + SLV2Port port = slv2_plugin_get_port_by_index (lv2plugin, portnum); + SLV2Value lv2def, lv2min, lv2max; + GParamSpec *ret; + gchar *name; + gint perms; + gfloat lower = 0.0f, upper = 1.0f, def = 0.0f; + + name = gst_lv2_class_get_param_name (klass, portnum); + perms = G_PARAM_READABLE; + if (slv2_port_is_a (lv2plugin, port, input_class)) + perms |= G_PARAM_WRITABLE | G_PARAM_CONSTRUCT; + if (slv2_port_is_a (lv2plugin, port, control_class)) + perms |= GST_PARAM_CONTROLLABLE; + + if (slv2_port_has_property (lv2plugin, port, toggled_prop)) { + ret = g_param_spec_boolean (name, name, name, FALSE, perms); + g_free (name); + return ret; + } + + slv2_port_get_range (lv2plugin, port, &lv2def, &lv2min, &lv2max); + + if (lv2def) + def = slv2_value_as_float (lv2def); + if (lv2min) + lower = slv2_value_as_float (lv2min); + if (lv2max) + upper = slv2_value_as_float (lv2max); + + if (def < lower) { + fprintf (stderr, "ERROR: %s BAD LOWER %f > %f\n", + slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)), lower, def); + lower = def; + } + + if (def > upper) { + fprintf (stderr, "ERROR: %s BAD UPPER %f < %f\n", + slv2_value_as_string (slv2_plugin_get_uri (lv2plugin)), upper, def); + upper = def; + } + + if (slv2_port_has_property (lv2plugin, port, integer_prop)) + ret = g_param_spec_int (name, name, name, lower, upper, def, perms); + else + ret = g_param_spec_float (name, name, name, lower, upper, def, perms); + + g_free (name); + + return ret; +} + +static void +gst_lv2_class_init (GstLV2Class * klass, SLV2Plugin lv2plugin) +{ + GObjectClass *gobject_class; + GstSignalProcessorClass *gsp_class; + gint i; + + GST_DEBUG ("class_init %p", klass); + + gobject_class = (GObjectClass *) klass; + gobject_class->set_property = gst_lv2_set_property; + gobject_class->get_property = gst_lv2_get_property; + + gsp_class = GST_SIGNAL_PROCESSOR_CLASS (klass); + gsp_class->setup = gst_lv2_setup; + gsp_class->start = gst_lv2_start; + gsp_class->stop = gst_lv2_stop; + gsp_class->cleanup = gst_lv2_cleanup; + gsp_class->process = gst_lv2_process; + + klass->plugin = lv2plugin; + + /* register properties */ + + for (i = 0; i < gsp_class->num_control_in; i++) { + GParamSpec *p; + + p = gst_lv2_class_get_param_spec (klass, klass->control_in_portnums[i]); + + /* properties have an offset of 1 */ + g_object_class_install_property (G_OBJECT_CLASS (klass), i + 1, p); + } + + for (i = 0; i < gsp_class->num_control_out; i++) { + GParamSpec *p; + + p = gst_lv2_class_get_param_spec (klass, klass->control_out_portnums[i]); + + /* properties have an offset of 1, and we already added num_control_in */ + g_object_class_install_property (G_OBJECT_CLASS (klass), + gsp_class->num_control_in + i + 1, p); + } +} + +static void +gst_lv2_init (GstLV2 * lv2, GstLV2Class * klass) +{ +#if 0 + lv2->plugin = klass->plugin; + lv2->instance = NULL; + lv2->activated = FALSE; + lv2->inplace_broken = LV2_IS_INPLACE_BROKEN (lv2->descriptor->Properties); +#endif +} + +static void +gst_lv2_set_property (GObject * object, guint prop_id, const GValue * value, + GParamSpec * pspec) +{ +#if 0 + GstSignalProcessor *gsp; + GstSignalProcessorClass *gsp_class; + + gsp = GST_SIGNAL_PROCESSOR (object); + gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (object); + + /* remember, properties have an offset of 1 */ + prop_id--; + + /* only input ports */ + g_return_if_fail (prop_id < gsp_class->num_control_in); + + /* now see what type it is */ + switch (pspec->value_type) { + case G_TYPE_BOOLEAN: + gsp->control_in[prop_id] = g_value_get_boolean (value) ? 1.f : 0.f; + break; + case G_TYPE_INT: + gsp->control_in[prop_id] = g_value_get_int (value); + break; + case G_TYPE_FLOAT: + gsp->control_in[prop_id] = g_value_get_float (value); + break; + default: + g_assert_not_reached (); + } +#endif +} + +static void +gst_lv2_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ +#if 0 + GstSignalProcessor *gsp; + GstSignalProcessorClass *gsp_class; + gfloat *controls; + + gsp = GST_SIGNAL_PROCESSOR (object); + gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (object); + + /* remember, properties have an offset of 1 */ + prop_id--; + + if (prop_id < gsp_class->num_control_in) { + controls = gsp->control_in; + } else if (prop_id < gsp_class->num_control_in + gsp_class->num_control_out) { + controls = gsp->control_out; + prop_id -= gsp_class->num_control_in; + } else { + g_return_if_reached (); + } + + /* now see what type it is */ + switch (pspec->value_type) { + case G_TYPE_BOOLEAN: + g_value_set_boolean (value, controls[prop_id] > 0.5); + break; + case G_TYPE_INT: + g_value_set_int (value, CLAMP (controls[prop_id], G_MININT, G_MAXINT)); + break; + case G_TYPE_FLOAT: + g_value_set_float (value, controls[prop_id]); + break; + default: + g_return_if_reached (); + } +#endif +} + +static gboolean +gst_lv2_setup (GstSignalProcessor * gsp, guint sample_rate) +{ +#if 0 + GstLV2 *ladspa; + GstLV2Class *oclass; + GstSignalProcessorClass *gsp_class; + LV2_Descriptor *desc; + int i; + + gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp); + ladspa = (GstLV2 *) gsp; + oclass = (GstLV2Class *) gsp_class; + desc = ladspa->descriptor; + + g_return_val_if_fail (ladspa->handle == NULL, FALSE); + g_return_val_if_fail (ladspa->activated == FALSE, FALSE); + + GST_DEBUG_OBJECT (ladspa, "instantiating the plugin at %d Hz", sample_rate); + + ladspa->handle = desc->instantiate (desc, sample_rate); + + g_return_val_if_fail (ladspa->handle != NULL, FALSE); + + /* connect the control ports */ + for (i = 0; i < gsp_class->num_control_in; i++) + desc->connect_port (ladspa->handle, + oclass->control_in_portnums[i], &(gsp->control_in[i])); + for (i = 0; i < gsp_class->num_control_out; i++) + desc->connect_port (ladspa->handle, + oclass->control_out_portnums[i], &(gsp->control_out[i])); +#endif + return TRUE; +} + +static gboolean +gst_lv2_start (GstSignalProcessor * gsp) +{ +#if 0 + GstLV2 *ladspa; + LV2_Descriptor *desc; + + ladspa = (GstLV2 *) gsp; + desc = ladspa->descriptor; + + g_return_val_if_fail (ladspa->activated == FALSE, FALSE); + g_return_val_if_fail (ladspa->handle != NULL, FALSE); + + GST_DEBUG_OBJECT (ladspa, "activating"); + + if (desc->activate) + desc->activate (ladspa->handle); + + ladspa->activated = TRUE; +#endif + + return TRUE; +} + +static void +gst_lv2_stop (GstSignalProcessor * gsp) +{ +#if 0 + GstLV2 *ladspa; + LV2_Descriptor *desc; + + ladspa = (GstLV2 *) gsp; + desc = ladspa->descriptor; + + g_return_if_fail (ladspa->activated == TRUE); + g_return_if_fail (ladspa->handle != NULL); + + GST_DEBUG_OBJECT (ladspa, "deactivating"); + + if (desc->activate) + desc->activate (ladspa->handle); + + ladspa->activated = FALSE; +#endif +} + +static void +gst_lv2_cleanup (GstSignalProcessor * gsp) +{ +#if 0 + GstLV2 *ladspa; + LV2_Descriptor *desc; + + ladspa = (GstLV2 *) gsp; + desc = ladspa->descriptor; + + g_return_if_fail (ladspa->activated == FALSE); + g_return_if_fail (ladspa->handle != NULL); + + GST_DEBUG_OBJECT (ladspa, "cleaning up"); + + if (desc->cleanup) + desc->cleanup (ladspa->handle); + + ladspa->handle = NULL; +#endif +} + +static void +gst_lv2_process (GstSignalProcessor * gsp, guint nframes) +{ +#if 0 + GstSignalProcessorClass *gsp_class; + GstLV2 *ladspa; + GstLV2Class *oclass; + LV2_Descriptor *desc; + guint i; + + gsp_class = GST_SIGNAL_PROCESSOR_GET_CLASS (gsp); + ladspa = (GstLV2 *) gsp; + oclass = (GstLV2Class *) gsp_class; + desc = ladspa->descriptor; + + for (i = 0; i < gsp_class->num_audio_in; i++) + desc->connect_port (ladspa->handle, oclass->audio_in_portnums[i], + gsp->audio_in[i]); + for (i = 0; i < gsp_class->num_audio_out; i++) + desc->connect_port (ladspa->handle, oclass->audio_out_portnums[i], + gsp->audio_out[i]); + + desc->run (ladspa->handle, nframes); +#endif +} + +/* search the plugin path + */ +static gboolean +lv2_plugin_discover (void) +{ + unsigned i; + SLV2Plugins plugins = slv2_world_get_all_plugins (world); + for (i = 0; i < slv2_plugins_size (plugins); ++i) { + SLV2Plugin lv2plugin = slv2_plugins_get_at (plugins, i); + GTypeInfo typeinfo = { + sizeof (GstLV2Class), + (GBaseInitFunc) gst_lv2_base_init, + NULL, + (GClassInitFunc) gst_lv2_class_init, + NULL, + lv2plugin, + sizeof (GstLV2), + 0, + (GInstanceInitFunc) gst_lv2_init, + }; + + GType type; + + /* construct the type name from plugin URI */ + gchar *type_name = g_strdup_printf ("%s", + slv2_value_as_uri (slv2_plugin_get_uri (lv2plugin))); + g_strcanon (type_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-+", '-'); + + /* if it's already registered, drop it */ + if (g_type_from_name (type_name)) + goto next; + + /* create the type */ + type = + g_type_register_static (GST_TYPE_SIGNAL_PROCESSOR, type_name, &typeinfo, + 0); + + /* FIXME: not needed anymore when we can add pad templates, etc in class_init + * as class_data contains the LADSPA_Descriptor too */ + g_type_set_qdata (type, GST_SLV2_PLUGIN_QDATA, (gpointer) lv2plugin); + + if (!gst_element_register (gst_lv2_plugin, type_name, GST_RANK_NONE, type)) + goto next; + + next: + g_free (type_name); + } + return TRUE; +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (lv2_debug, "lv2", + GST_DEBUG_FG_GREEN | GST_DEBUG_BG_BLACK | GST_DEBUG_BOLD, "LV2"); + + world = slv2_world_new (); + slv2_world_load_all (world); + + audio_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_AUDIO); + control_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_CONTROL); + input_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_INPUT); + output_class = slv2_value_new_uri (world, SLV2_PORT_CLASS_OUTPUT); + integer_prop = + slv2_value_new_uri (world, "http://lv2plug.in/ns/lv2core#integer"); + toggled_prop = + slv2_value_new_uri (world, "http://lv2plug.in/ns/lv2core#toggled"); + + parent_class = g_type_class_ref (GST_TYPE_SIGNAL_PROCESSOR); + + gst_lv2_plugin = plugin; + + return lv2_plugin_discover (); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "lv2", + "All LV2 plugins", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/lv2/gstlv2.h b/ext/lv2/gstlv2.h new file mode 100644 index 00000000..f76235b4 --- /dev/null +++ b/ext/lv2/gstlv2.h @@ -0,0 +1,76 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen + * + * gstladspa.h: Header for LV2 plugin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_LV2_H__ +#define __GST_LV2_H__ + + +#include + +#include + +#include "../../gst-libs/gst/signalprocessor/gstsignalprocessor.h" + + +G_BEGIN_DECLS + + +typedef struct _lv2_control_info { + gchar *name; + gchar *param_name; + gfloat lowerbound, upperbound; + gfloat def; + gboolean lower, upper, samplerate; + gboolean toggled, logarithmic, integer, writable; +} lv2_control_info; + + +typedef struct _GstLV2 GstLV2; +typedef struct _GstLV2Class GstLV2Class; + + +struct _GstLV2 { + GstSignalProcessor parent; + + SLV2Plugin *plugin; + SLV2Instance *instance; + + gboolean activated; + gboolean inplace_broken; +}; + +struct _GstLV2Class { + GstSignalProcessorClass parent_class; + + SLV2Plugin plugin; + + gint *audio_in_portnums; + gint *audio_out_portnums; + gint *control_in_portnums; + gint *control_out_portnums; +}; + + +G_END_DECLS + + +#endif /* __GST_LV2_H__ */ diff --git a/gst-libs/gst/Makefile.am b/gst-libs/gst/Makefile.am index c50f8f4b..3471a759 100644 --- a/gst-libs/gst/Makefile.am +++ b/gst-libs/gst/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = interfaces +SUBDIRS = interfaces signalprocessor noinst_HEADERS = gst-i18n-plugin.h gettext.h -DIST_SUBDIRS = dshow interfaces +DIST_SUBDIRS = dshow interfaces signalprocessor diff --git a/gst-libs/gst/signalprocessor/.gitignore b/gst-libs/gst/signalprocessor/.gitignore new file mode 100644 index 00000000..08f5ed37 --- /dev/null +++ b/gst-libs/gst/signalprocessor/.gitignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +*.o +*.lo +*.la +.deps +.libs diff --git a/gst-libs/gst/signalprocessor/Makefile.am b/gst-libs/gst/signalprocessor/Makefile.am new file mode 100644 index 00000000..a8455390 --- /dev/null +++ b/gst-libs/gst/signalprocessor/Makefile.am @@ -0,0 +1,8 @@ +lib_LTLIBRARIES = libgstsignalprocessor.la + +noinst_HEADERS = gstsignalprocessor.h + +libgstsignalprocessor_la_SOURCES = gstsignalprocessor.c +libgstsignalprocessor_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) +libgstsignalprocessor_la_LDFLAGS = $(GST_PLUGINS_BASE_LIBS) + diff --git a/gst-libs/gst/signalprocessor/gstsignalprocessor.c b/gst-libs/gst/signalprocessor/gstsignalprocessor.c new file mode 100644 index 00000000..c46ae7a8 --- /dev/null +++ b/gst-libs/gst/signalprocessor/gstsignalprocessor.c @@ -0,0 +1,1024 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * 2005 Wim Taymans + * + * gstsignalprocessor.c: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * SECTION:gstsignalprocessor + * + * This baseclass allows to write elements that need data on all pads before + * their processing function can run. + * + * In push mode (gst_signal_processor_chain) it operates as follows: + * 1. store each received buffer on the pad and decrement pending_in + * 2. when pending_in==0, process as much as we can and push outputs + * + * In pull mode (gst_signal_processor_getrange) is operates as follows: + * 1. if there is an output ready, deliver + * 2. otherwise pull from each sink-pad, process requested frames and deliver + * the buffer + */ + +#include + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include "gstsignalprocessor.h" + + +GST_DEBUG_CATEGORY_STATIC (gst_signal_processor_debug); +#define GST_CAT_DEFAULT gst_signal_processor_debug + +/* FIXME: this is mono only */ +static GstStaticCaps template_caps = +GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS); + +#define GST_TYPE_SIGNAL_PROCESSOR_PAD_TEMPLATE \ + (gst_signal_processor_pad_template_get_type ()) +#define GST_SIGNAL_PROCESSOR_PAD_TEMPLATE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIGNAL_PROCESSOR_PAD_TEMPLATE,\ + GstSignalProcessorPadTemplate)) +typedef struct _GstSignalProcessorPadTemplate GstSignalProcessorPadTemplate; +typedef GstPadTemplateClass GstSignalProcessorPadTemplateClass; + +struct _GstSignalProcessorPadTemplate +{ + GstPadTemplate parent; + + guint index; +}; + +static GType +gst_signal_processor_pad_template_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof (GstSignalProcessorPadTemplateClass), NULL, NULL, NULL, NULL, + NULL, sizeof (GstSignalProcessorPadTemplate), 0, NULL + }; + + type = g_type_register_static (GST_TYPE_PAD_TEMPLATE, + "GstSignalProcessorPadTemplate", &info, 0); + } + return type; +} + +/* + * gst_signal_processor_class_add_pad_template: + * @klass: element class + * @name: pad name + * @direction: pad direction (src/sink) + * @index: index for the pad per direction (starting from 0) + * + */ +void +gst_signal_processor_class_add_pad_template (GstSignalProcessorClass * klass, + const gchar * name, GstPadDirection direction, guint index) +{ + GstPadTemplate *new; + GstCaps *caps; + + g_return_if_fail (GST_IS_SIGNAL_PROCESSOR_CLASS (klass)); + g_return_if_fail (name != NULL); + g_return_if_fail (direction == GST_PAD_SRC || direction == GST_PAD_SINK); + + /* FIXME: would be nice to have the template as a parameter, right now this can + * only create mono pads */ + caps = gst_caps_copy (gst_static_caps_get (&template_caps)); + + new = g_object_new (gst_signal_processor_pad_template_get_type (), + "name", name, "name-template", name, + "direction", direction, "presence", GST_PAD_ALWAYS, "caps", caps, NULL); + + GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (new)->index = index; + + gst_element_class_add_pad_template (GST_ELEMENT_CLASS (klass), new); +} + + +#define GST_TYPE_SIGNAL_PROCESSOR_PAD (gst_signal_processor_pad_get_type ()) +#define GST_SIGNAL_PROCESSOR_PAD(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIGNAL_PROCESSOR_PAD,\ + GstSignalProcessorPad)) +typedef struct _GstSignalProcessorPad GstSignalProcessorPad; +typedef GstPadClass GstSignalProcessorPadClass; + +struct _GstSignalProcessorPad +{ + GstPad parent; + + GstBuffer *pen; + + /* index for the pad per direction (starting from 0) */ + guint index; + + /* these are only used for sink pads */ + guint samples_avail; /* available mono sample frames */ + gfloat *data; /* data pointer to read from / write to */ +}; + +static GType +gst_signal_processor_pad_get_type (void) +{ + static GType type = 0; + + if (!type) { + static const GTypeInfo info = { + sizeof (GstSignalProcessorPadClass), NULL, NULL, NULL, NULL, + NULL, sizeof (GstSignalProcessorPad), 0, NULL + }; + + type = g_type_register_static (GST_TYPE_PAD, + "GstSignalProcessorPad", &info, 0); + } + return type; +} + + +GST_BOILERPLATE (GstSignalProcessor, gst_signal_processor, GstElement, + GST_TYPE_ELEMENT); + + +static void gst_signal_processor_finalize (GObject * object); +static gboolean gst_signal_processor_src_activate_pull (GstPad * pad, + gboolean active); +static gboolean gst_signal_processor_sink_activate_push (GstPad * pad, + gboolean active); +static GstStateChangeReturn gst_signal_processor_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_signal_processor_event (GstPad * pad, GstEvent * event); +static GstFlowReturn gst_signal_processor_getrange (GstPad * pad, + guint64 offset, guint length, GstBuffer ** buffer); +static GstFlowReturn gst_signal_processor_chain (GstPad * pad, + GstBuffer * buffer); +static gboolean gst_signal_processor_setcaps (GstPad * pad, GstCaps * caps); + + +static void +gst_signal_processor_base_init (gpointer g_class) +{ + GST_DEBUG_CATEGORY_INIT (gst_signal_processor_debug, "gst-dsp", 0, + "signalprocessor element"); +} + +static void +gst_signal_processor_class_init (GstSignalProcessorClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = G_OBJECT_CLASS (klass); + gstelement_class = GST_ELEMENT_CLASS (klass); + + gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_signal_processor_finalize); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_signal_processor_change_state); +} + +static void +gst_signal_processor_add_pad_from_template (GstSignalProcessor * self, + GstPadTemplate * templ) +{ + GstPad *new; + + new = g_object_new (GST_TYPE_SIGNAL_PROCESSOR_PAD, + "name", GST_OBJECT_NAME (templ), "direction", templ->direction, + "template", templ, NULL); + GST_SIGNAL_PROCESSOR_PAD (new)->index = + GST_SIGNAL_PROCESSOR_PAD_TEMPLATE (templ)->index; + + gst_pad_set_setcaps_function (new, + GST_DEBUG_FUNCPTR (gst_signal_processor_setcaps)); + + if (templ->direction == GST_PAD_SINK) { + GST_DEBUG ("added new sink pad"); + + gst_pad_set_event_function (new, + GST_DEBUG_FUNCPTR (gst_signal_processor_event)); + gst_pad_set_chain_function (new, + GST_DEBUG_FUNCPTR (gst_signal_processor_chain)); + gst_pad_set_activatepush_function (new, + GST_DEBUG_FUNCPTR (gst_signal_processor_sink_activate_push)); + } else { + GST_DEBUG ("added new src pad"); + + gst_pad_set_getrange_function (new, + GST_DEBUG_FUNCPTR (gst_signal_processor_getrange)); + gst_pad_set_activatepull_function (new, + GST_DEBUG_FUNCPTR (gst_signal_processor_src_activate_pull)); + } + + gst_element_add_pad (GST_ELEMENT (self), new); +} + +static void +gst_signal_processor_init (GstSignalProcessor * self, + GstSignalProcessorClass * klass) +{ + GList *templates; + + templates = + gst_element_class_get_pad_template_list (GST_ELEMENT_CLASS (klass)); + + while (templates) { + GstPadTemplate *templ = GST_PAD_TEMPLATE (templates->data); + + gst_signal_processor_add_pad_from_template (self, templ); + templates = templates->next; + } + + self->state = GST_SIGNAL_PROCESSOR_STATE_NULL; + + self->audio_in = g_new0 (gfloat *, klass->num_audio_in); + self->control_in = g_new0 (gfloat, klass->num_control_in); + self->audio_out = g_new0 (gfloat *, klass->num_audio_out); + self->control_out = g_new0 (gfloat, klass->num_control_out); + + /* init */ + self->pending_in = klass->num_audio_in; + self->pending_out = 0; + + self->sample_rate = 0; +} + +static void +gst_signal_processor_finalize (GObject * object) +{ + GstSignalProcessor *self = GST_SIGNAL_PROCESSOR (object); + + g_free (self->audio_in); + self->audio_in = NULL; + g_free (self->control_in); + self->control_in = NULL; + g_free (self->audio_out); + self->audio_out = NULL; + g_free (self->control_out); + self->control_out = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_signal_processor_setup (GstSignalProcessor * self, guint sample_rate) +{ + GstSignalProcessorClass *klass; + gboolean ret = TRUE; + + klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + GST_INFO_OBJECT (self, "setup()"); + + g_return_val_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_NULL, FALSE); + + if (klass->setup) + ret = klass->setup (self, sample_rate); + + if (!ret) + goto setup_failed; + + self->state = GST_SIGNAL_PROCESSOR_STATE_INITIALIZED; + + return ret; + +setup_failed: + { + GST_INFO_OBJECT (self, "setup() failed at %u Hz", sample_rate); + return ret; + } +} + +static gboolean +gst_signal_processor_start (GstSignalProcessor * self) +{ + GstSignalProcessorClass *klass; + gboolean ret = TRUE; + + klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + g_return_val_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_INITIALIZED, + FALSE); + + GST_INFO_OBJECT (self, "start()"); + + if (klass->start) + ret = klass->start (self); + + if (!ret) + goto start_failed; + + self->state = GST_SIGNAL_PROCESSOR_STATE_RUNNING; + + return ret; + +start_failed: + { + GST_INFO_OBJECT (self, "start() failed"); + return ret; + } +} + +static void +gst_signal_processor_stop (GstSignalProcessor * self) +{ + GstSignalProcessorClass *klass; + GstElement *elem; + GList *sinks; + + klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + elem = GST_ELEMENT (self); + + GST_INFO_OBJECT (self, "stop()"); + + g_return_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_RUNNING); + + if (klass->stop) + klass->stop (self); + + for (sinks = elem->sinkpads; sinks; sinks = sinks->next) + /* force set_caps when going to RUNNING, see note in _setcaps () */ + gst_pad_set_caps (GST_PAD (sinks->data), NULL); + + /* should also flush our buffers perhaps? */ + + self->state = GST_SIGNAL_PROCESSOR_STATE_INITIALIZED; +} + +static void +gst_signal_processor_cleanup (GstSignalProcessor * self) +{ + GstSignalProcessorClass *klass; + + klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + GST_INFO_OBJECT (self, "cleanup()"); + + g_return_if_fail (self->state == GST_SIGNAL_PROCESSOR_STATE_INITIALIZED); + + if (klass->cleanup) + klass->cleanup (self); + + self->state = GST_SIGNAL_PROCESSOR_STATE_NULL; +} + +static gboolean +gst_signal_processor_setcaps_pull (GstSignalProcessor * self, GstPad * pad, + GstCaps * caps) +{ + if (GST_PAD_IS_SRC (pad)) { + GList *l; + + for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) + if (!gst_pad_set_caps (GST_PAD (l->data), caps)) + goto src_setcaps_failed; + } else { + GstPad *peer; + gboolean res; + + peer = gst_pad_get_peer (pad); + if (!peer) + goto unlinked_sink; + + res = gst_pad_set_caps (peer, caps); + gst_object_unref (peer); + + if (!res) + goto peer_setcaps_failed; + } + + return TRUE; + +src_setcaps_failed: + { + /* not logging, presumably the sink pad already logged */ + return FALSE; + } +unlinked_sink: + { + GST_WARNING_OBJECT (self, "unlinked sink pad %" GST_PTR_FORMAT ", I wonder " + "how we passed activate_pull()", pad); + return FALSE; + } +peer_setcaps_failed: + { + GST_INFO_OBJECT (self, "peer of %" GST_PTR_FORMAT " did not accept caps", + pad); + return FALSE; + } +} + +static gboolean +gst_signal_processor_setcaps (GstPad * pad, GstCaps * caps) +{ + GstSignalProcessor *self; + + self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); + + if (self->mode == GST_ACTIVATE_PULL && !gst_caps_is_equal (caps, self->caps) + && !gst_signal_processor_setcaps_pull (self, pad, caps)) + goto setcaps_pull_failed; + + /* the whole processor has one caps; if the sample rate changes, let subclass + implementations know */ + if (!gst_caps_is_equal (caps, self->caps)) { + GstStructure *s; + gint sample_rate; + + GST_DEBUG_OBJECT (pad, "got caps %" GST_PTR_FORMAT, caps); + + s = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (s, "rate", &sample_rate)) { + GST_WARNING ("got no sample-rate"); + goto impossible; + } + + GST_DEBUG_OBJECT (self, "Got rate=%d", sample_rate); + + if (GST_SIGNAL_PROCESSOR_IS_RUNNING (self)) + gst_signal_processor_stop (self); + if (GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self)) + gst_signal_processor_cleanup (self); + + if (!gst_signal_processor_setup (self, sample_rate)) + goto start_or_setup_failed; + + self->sample_rate = sample_rate; + gst_caps_replace (&self->caps, caps); + } else { + GST_DEBUG_OBJECT (self, "skipping, have caps already"); + } + + /* we use this method to manage the processor's state, hence the caps clearing + in stop(). so it can be that we enter here just to manage the processor's + state, to take it to RUNNING from already being INITIALIZED with the right + sample rate (e.g., when having gone PLAYING->READY->PLAYING). make sure + when we leave that the processor is RUNNING. */ + if (!GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self) + && !gst_signal_processor_setup (self, self->sample_rate)) + goto start_or_setup_failed; + if (!GST_SIGNAL_PROCESSOR_IS_RUNNING (self) + && !gst_signal_processor_start (self)) + goto start_or_setup_failed; + + gst_object_unref (self); + + return TRUE; + +start_or_setup_failed: + { + gst_object_unref (self); + return FALSE; + } +setcaps_pull_failed: + { + gst_object_unref (self); + return FALSE; + } +impossible: + { + g_critical ("something impossible happened"); + gst_object_unref (self); + return FALSE; + } +} + +static gboolean +gst_signal_processor_event (GstPad * pad, GstEvent * event) +{ + GstSignalProcessor *self; + GstSignalProcessorClass *bclass; + gboolean ret; + + self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); + bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + /* FIXME, this probably isn't the correct interface: what about return values, + * what about overriding event_default + * Sync with GstBaseTransform::gst_base_transform_sink_event */ + if (bclass->event) + bclass->event (self, event); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_START: + break; + case GST_EVENT_FLUSH_STOP: + /* clear errors now */ + self->flow_state = GST_FLOW_OK; + break; + default: + break; + } + ret = gst_pad_event_default (pad, event); + + gst_object_unref (self); + + return ret; +} + +static guint +gst_signal_processor_prepare (GstSignalProcessor * self, guint nframes) +{ + GstElement *elem = (GstElement *) self; + GstSignalProcessorClass *klass; + GList *sinks, *srcs; + guint samples_avail = nframes; + + klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + /* first, assign audio_in pointers, and determine the number of samples that + * we can process */ + for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { + GstSignalProcessorPad *sinkpad; + + sinkpad = (GstSignalProcessorPad *) sinks->data; + g_assert (sinkpad->samples_avail > 0); + samples_avail = MIN (samples_avail, sinkpad->samples_avail); + self->audio_in[sinkpad->index] = sinkpad->data; + } + + /* FIXME: return if samples_avail==0 ? */ + + /* now assign output buffers. we can avoid allocation by reusing input + buffers, but only if process() can work in place, and if the input buffer + is the exact size of the number of samples we are processing. */ + sinks = elem->sinkpads; + srcs = elem->srcpads; + if (GST_SIGNAL_PROCESSOR_CLASS_CAN_PROCESS_IN_PLACE (klass)) { + while (sinks && srcs) { + GstSignalProcessorPad *sinkpad, *srcpad; + + sinkpad = (GstSignalProcessorPad *) sinks->data; + srcpad = (GstSignalProcessorPad *) srcs->data; + + if (GST_BUFFER_SIZE (sinkpad->pen) == samples_avail * sizeof (gfloat)) { + /* reusable, yay */ + g_assert (sinkpad->samples_avail == samples_avail); + srcpad->pen = sinkpad->pen; + sinkpad->pen = NULL; + self->audio_out[srcpad->index] = sinkpad->data; + self->pending_out++; + + srcs = srcs->next; + } + + sinks = sinks->next; + } + } + + g_return_val_if_fail (GST_SIGNAL_PROCESSOR_IS_RUNNING (self), 0); + + /* now allocate for any remaining outputs */ + while (srcs) { + GstSignalProcessorPad *srcpad; + GstFlowReturn ret; + + srcpad = (GstSignalProcessorPad *) srcs->data; + + ret = + gst_pad_alloc_buffer_and_set_caps (GST_PAD (srcpad), -1, + samples_avail * sizeof (gfloat), self->caps, &srcpad->pen); + + if (ret != GST_FLOW_OK) { + self->flow_state = ret; + return 0; + } else { + self->audio_out[srcpad->index] = (gfloat *) GST_BUFFER_DATA (srcpad->pen); + self->pending_out++; + } + + srcs = srcs->next; + } + + return samples_avail; +} + +static void +gst_signal_processor_update_inputs (GstSignalProcessor * self, guint nprocessed) +{ + GstElement *elem = (GstElement *) self; + GList *sinks; + + for (sinks = elem->sinkpads; sinks; sinks = sinks->next) { + GstSignalProcessorPad *sinkpad; + + sinkpad = (GstSignalProcessorPad *) sinks->data; + g_assert (sinkpad->samples_avail >= nprocessed); + + if (sinkpad->pen && sinkpad->samples_avail == nprocessed) { + /* used up this buffer, unpen */ + gst_buffer_unref (sinkpad->pen); + sinkpad->pen = NULL; + } + + if (!sinkpad->pen) { + /* this buffer was used up */ + self->pending_in++; + sinkpad->data = NULL; + sinkpad->samples_avail = 0; + } else { + /* advance ->data pointers and decrement ->samples_avail, unreffing buffer + if no samples are left */ + sinkpad->samples_avail -= nprocessed; + sinkpad->data += nprocessed; /* gfloat* arithmetic */ + } + } +} + +static void +gst_signal_processor_process (GstSignalProcessor * self, guint nframes) +{ + GstElement *elem; + GstSignalProcessorClass *klass; + + /* check if we have buffers enqueued */ + g_return_if_fail (self->pending_in == 0); + g_return_if_fail (self->pending_out == 0); + + elem = GST_ELEMENT (self); + + /* check how much input is available and prepare output buffers */ + nframes = gst_signal_processor_prepare (self, nframes); + if (G_UNLIKELY (nframes == 0)) + goto flow_error; + + klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + GST_LOG_OBJECT (self, "process(%u)", nframes); + + klass->process (self, nframes); + + gst_signal_processor_update_inputs (self, nframes); + + return; + +flow_error: + { + GST_WARNING ("gst_pad_alloc_buffer_and_set_caps() returned %d", + self->flow_state); + return; + } +} + +static void +gst_signal_processor_pen_buffer (GstSignalProcessor * self, GstPad * pad, + GstBuffer * buffer) +{ + GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pad; + + if (spad->pen) + goto had_buffer; + + /* keep the reference */ + spad->pen = buffer; + spad->data = (gfloat *) GST_BUFFER_DATA (buffer); + spad->samples_avail = GST_BUFFER_SIZE (buffer) / sizeof (float); + + g_assert (self->pending_in != 0); + + self->pending_in--; + + return; + + /* ERRORS */ +had_buffer: + { + GST_WARNING ("Pad %s:%s already has penned buffer", + GST_DEBUG_PAD_NAME (pad)); + gst_buffer_unref (buffer); + return; + } +} + +static void +gst_signal_processor_flush (GstSignalProcessor * self) +{ + GList *pads; + GstSignalProcessorClass *klass; + + klass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + GST_INFO_OBJECT (self, "flush()"); + + /* release enqueued buffers */ + for (pads = GST_ELEMENT (self)->pads; pads; pads = pads->next) { + GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pads->data; + + if (spad->pen) { + gst_buffer_unref (spad->pen); + spad->pen = NULL; + spad->data = NULL; + spad->samples_avail = 0; + } + } + + /* no outputs prepared and inputs for each pad needed */ + self->pending_out = 0; + self->pending_in = klass->num_audio_in; +} + +static void +gst_signal_processor_do_pulls (GstSignalProcessor * self, guint nframes) +{ + GList *sinkpads; + + /* FIXME: not threadsafe atm */ + + sinkpads = GST_ELEMENT (self)->sinkpads; + + for (; sinkpads; sinkpads = sinkpads->next) { + GstSignalProcessorPad *spad = (GstSignalProcessorPad *) sinkpads->data; + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *buf; + + if (spad->pen) { + g_warning ("Unexpectedly full buffer pen for pad %s:%s", + GST_DEBUG_PAD_NAME (spad)); + continue; + } + + ret = + gst_pad_pull_range (GST_PAD (spad), -1, nframes * sizeof (gfloat), + &buf); + + if (ret != GST_FLOW_OK) { + gst_signal_processor_flush (self); + self->flow_state = ret; + return; + } else if (!buf) { + g_critical ("Pull failed to make a buffer!"); + self->flow_state = GST_FLOW_ERROR; + return; + } else { + gst_signal_processor_pen_buffer (self, GST_PAD (spad), buf); + } + } + + if (self->pending_in != 0) { + g_critical ("Something wierd happened..."); + self->flow_state = GST_FLOW_ERROR; + } else { + gst_signal_processor_process (self, nframes); + } +} + +static GstFlowReturn +gst_signal_processor_getrange (GstPad * pad, guint64 offset, + guint length, GstBuffer ** buffer) +{ + GstSignalProcessor *self; + GstSignalProcessorPad *spad = (GstSignalProcessorPad *) pad; + GstFlowReturn ret = GST_FLOW_ERROR; + + self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); + + if (spad->pen) { + *buffer = spad->pen; + spad->pen = NULL; + g_assert (self->pending_out != 0); + self->pending_out--; + ret = GST_FLOW_OK; + } else { + gst_signal_processor_do_pulls (self, length / sizeof (gfloat)); + if (!spad->pen) { + /* this is an error condition */ + *buffer = NULL; + ret = self->flow_state; + } else { + *buffer = spad->pen; + spad->pen = NULL; + self->pending_out--; + ret = GST_FLOW_OK; + } + } + + GST_DEBUG_OBJECT (self, "returns %s", gst_flow_get_name (ret)); + + gst_object_unref (self); + + return ret; +} + +static void +gst_signal_processor_do_pushes (GstSignalProcessor * self) +{ + GList *srcpads; + + /* not threadsafe atm */ + + srcpads = GST_ELEMENT (self)->srcpads; + + for (; srcpads; srcpads = srcpads->next) { + GstSignalProcessorPad *spad = (GstSignalProcessorPad *) srcpads->data; + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *buffer; + + if (!spad->pen) { + g_warning ("Unexpectedly empty buffer pen for pad %s:%s", + GST_DEBUG_PAD_NAME (spad)); + continue; + } + + /* take buffer from pen */ + buffer = spad->pen; + spad->pen = NULL; + + ret = gst_pad_push (GST_PAD (spad), buffer); + + if (ret != GST_FLOW_OK) { + gst_signal_processor_flush (self); + self->flow_state = ret; + return; + } else { + g_assert (self->pending_out > 0); + self->pending_out--; + } + } + + if (self->pending_out != 0) { + g_critical ("Something wierd happened..."); + self->flow_state = GST_FLOW_ERROR; + } +} + +static GstFlowReturn +gst_signal_processor_chain (GstPad * pad, GstBuffer * buffer) +{ + GstSignalProcessor *self; + + self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); + + gst_signal_processor_pen_buffer (self, pad, buffer); + + if (self->pending_in == 0) { + gst_signal_processor_process (self, G_MAXUINT); + + gst_signal_processor_do_pushes (self); + } + + gst_object_unref (self); + + return self->flow_state; +} + +static gboolean +gst_signal_processor_sink_activate_push (GstPad * pad, gboolean active) +{ + gboolean result = TRUE; + GstSignalProcessor *self; + GstSignalProcessorClass *bclass; + + self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); + bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + if (active) { + if (self->mode == GST_ACTIVATE_NONE) { + self->mode = GST_ACTIVATE_PUSH; + result = TRUE; + } else if (self->mode == GST_ACTIVATE_PUSH) { + result = TRUE; + } else { + g_warning ("foo"); + result = FALSE; + } + } else { + if (self->mode == GST_ACTIVATE_NONE) { + result = TRUE; + } else if (self->mode == GST_ACTIVATE_PUSH) { + self->mode = GST_ACTIVATE_NONE; + result = TRUE; + } else { + g_warning ("foo"); + result = FALSE; + } + } + + GST_DEBUG_OBJECT (self, "result : %d", result); + + gst_object_unref (self); + + return result; +} + +static gboolean +gst_signal_processor_src_activate_pull (GstPad * pad, gboolean active) +{ + gboolean result = TRUE; + GstSignalProcessor *self; + GstSignalProcessorClass *bclass; + + self = GST_SIGNAL_PROCESSOR (gst_pad_get_parent (pad)); + bclass = GST_SIGNAL_PROCESSOR_GET_CLASS (self); + + if (active) { + if (self->mode == GST_ACTIVATE_NONE) { + GList *l; + + for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) + result &= gst_pad_activate_pull (pad, active); + if (result) + self->mode = GST_ACTIVATE_PULL; + } else if (self->mode == GST_ACTIVATE_PULL) { + result = TRUE; + } else { + g_warning ("foo"); + result = FALSE; + } + } else { + if (self->mode == GST_ACTIVATE_NONE) { + result = TRUE; + } else if (self->mode == GST_ACTIVATE_PULL) { + GList *l; + + for (l = GST_ELEMENT (self)->sinkpads; l; l = l->next) + result &= gst_pad_activate_pull (pad, active); + if (result) + self->mode = GST_ACTIVATE_NONE; + result = TRUE; + } else { + g_warning ("foo"); + result = FALSE; + } + } + + GST_DEBUG_OBJECT (self, "result : %d", result); + + gst_object_unref (self); + + return result; +} + +static GstStateChangeReturn +gst_signal_processor_change_state (GstElement * element, + GstStateChange transition) +{ + GstSignalProcessor *self; + GstStateChangeReturn result; + + self = GST_SIGNAL_PROCESSOR (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + self->flow_state = GST_FLOW_OK; + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + if ((result = + GST_ELEMENT_CLASS (parent_class)->change_state (element, + transition)) == GST_STATE_CHANGE_FAILURE) + goto failure; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + if (GST_SIGNAL_PROCESSOR_IS_RUNNING (self)) + gst_signal_processor_stop (self); + gst_signal_processor_flush (self); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + if (GST_SIGNAL_PROCESSOR_IS_INITIALIZED (self)) + gst_signal_processor_cleanup (self); + break; + default: + break; + } + + return result; + + /* ERRORS */ +failure: + { + GST_DEBUG_OBJECT (element, "parent failed state change"); + return result; + } +} diff --git a/gst-libs/gst/signalprocessor/gstsignalprocessor.h b/gst-libs/gst/signalprocessor/gstsignalprocessor.h new file mode 100644 index 00000000..d6f0d0b0 --- /dev/null +++ b/gst-libs/gst/signalprocessor/gstsignalprocessor.h @@ -0,0 +1,124 @@ +/* GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2005 Wim Taymans + * + * gstsignalprocessor.h: + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifndef __GST_SIGNAL_PROCESSOR_H__ +#define __GST_SIGNAL_PROCESSOR_H__ + +#include + +G_BEGIN_DECLS + + +typedef enum +{ + GST_SIGNAL_PROCESSOR_CLASS_FLAG_CAN_PROCESS_IN_PLACE = 1<<0 +} GstSignalProcessorClassFlags; + +#define GST_SIGNAL_PROCESSOR_CLASS_CAN_PROCESS_IN_PLACE(klass) \ + (GST_SIGNAL_PROCESSOR_CLASS (klass)->flags & \ + GST_SIGNAL_PROCESSOR_CLASS_FLAG_CAN_PROCESS_IN_PLACE) +#define GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE(klass) \ + GST_SIGNAL_PROCESSOR_CLASS (klass)->flags |= \ + GST_SIGNAL_PROCESSOR_CLASS_FLAG_CAN_PROCESS_IN_PLACE + +typedef enum +{ + GST_SIGNAL_PROCESSOR_STATE_NULL, + GST_SIGNAL_PROCESSOR_STATE_INITIALIZED, + GST_SIGNAL_PROCESSOR_STATE_RUNNING +} GstSignalProcessorState; + + +#define GST_TYPE_SIGNAL_PROCESSOR (gst_signal_processor_get_type()) +#define GST_SIGNAL_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SIGNAL_PROCESSOR,GstSignalProcessor)) +#define GST_SIGNAL_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SIGNAL_PROCESSOR,GstSignalProcessorClass)) +#define GST_SIGNAL_PROCESSOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_SIGNAL_PROCESSOR,GstSignalProcessorClass)) +#define GST_IS_SIGNAL_PROCESSOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SIGNAL_PROCESSOR)) +#define GST_IS_SIGNAL_PROCESSOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SIGNAL_PROCESSOR)) + +#define GST_SIGNAL_PROCESSOR_IS_INITIALIZED(obj) \ + (GST_SIGNAL_PROCESSOR (obj)->state >= GST_SIGNAL_PROCESSOR_STATE_INITIALIZED) +#define GST_SIGNAL_PROCESSOR_IS_RUNNING(obj) \ + (GST_SIGNAL_PROCESSOR (obj)->state == GST_SIGNAL_PROCESSOR_STATE_RUNNING) + +typedef struct _GstSignalProcessor GstSignalProcessor; +typedef struct _GstSignalProcessorClass GstSignalProcessorClass; + + +struct _GstSignalProcessor { + GstElement element; + + GstCaps *caps; + + guint sample_rate; + + GstSignalProcessorState state; + + GstFlowReturn flow_state; + + GstActivateMode mode; + + /* pending inputs before processing can take place */ + guint pending_in; + /* panding outputs to be filled */ + guint pending_out; + + gfloat *control_in; + gfloat **audio_in; + gfloat *control_out; + gfloat **audio_out; +}; + +struct _GstSignalProcessorClass { + GstElementClass parent_class; + + /*< public >*/ + guint num_control_in; + guint num_audio_in; + guint num_control_out; + guint num_audio_out; + + guint flags; + + /* virtual methods for subclasses */ + + gboolean (*setup) (GstSignalProcessor *self, guint sample_rate); + gboolean (*start) (GstSignalProcessor *self); + void (*stop) (GstSignalProcessor *self); + void (*cleanup) (GstSignalProcessor *self); + void (*process) (GstSignalProcessor *self, guint num_frames); + gboolean (*event) (GstSignalProcessor *self, GstEvent *event); +}; + + +GType gst_signal_processor_get_type (void); +void gst_signal_processor_class_add_pad_template (GstSignalProcessorClass *klass, + const gchar *name, GstPadDirection direction, guint index); + + + +G_END_DECLS + + +#endif /* __GST_SIGNAL_PROCESSOR_H__ */ -- cgit v1.2.1