2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000 Wim Taymans <wim.taymans@chello.be>
4 * 2001 Thomas <thomas@apestaart.org>
6 * adder.c: Adder element, N in, one out, samples are added
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
25 #include <string.h> /* strcmp */
27 #define GST_ADDER_BUFFER_SIZE 4096
28 #define GST_ADDER_NUM_BUFFERS 8
30 GstElementDetails adder_details = {
33 "2-to-1 audio adder/mixer",
35 "Thomas <thomas@apestaart.org>",
39 /* Adder signals and args */
51 GST_PAD_TEMPLATE_FACTORY (gst_adder_src_template_factory,
58 "format", GST_PROPS_STRING ("int"),
59 "law", GST_PROPS_INT (0),
60 "endianness", GST_PROPS_INT (G_BYTE_ORDER),
61 "signed", GST_PROPS_BOOLEAN (TRUE),
62 "width", GST_PROPS_LIST (GST_PROPS_INT (8), GST_PROPS_INT (16)),
63 "depth", GST_PROPS_LIST (GST_PROPS_INT (8), GST_PROPS_INT (16)),
64 "rate", GST_PROPS_INT_RANGE (4000, 48000), /* FIXME */
65 "channels", GST_PROPS_INT_RANGE (1, 2)
70 "format", GST_PROPS_STRING("float"),
71 "layout", GST_PROPS_STRING ("gfloat"),
72 "intercept", GST_PROPS_FLOAT (0.0),
73 "slope", GST_PROPS_FLOAT (1.0),
74 "rate", GST_PROPS_INT_RANGE (4000, 96000),
75 "channels", GST_PROPS_INT_RANGE (1, 2)
79 GST_PAD_TEMPLATE_FACTORY (gst_adder_sink_template_factory,
86 "format", GST_PROPS_STRING ("int"),
87 "law", GST_PROPS_INT (0),
88 "endianness", GST_PROPS_INT (G_BYTE_ORDER),
89 "signed", GST_PROPS_BOOLEAN (TRUE),
90 "width", GST_PROPS_LIST (GST_PROPS_INT (8), GST_PROPS_INT (16)),
91 "depth", GST_PROPS_LIST (GST_PROPS_INT (8), GST_PROPS_INT (16)),
92 "rate", GST_PROPS_INT_RANGE (4000, 48000), /* FIXME */
93 "channels", GST_PROPS_INT_RANGE (1, 2)
98 "format", GST_PROPS_STRING("float"),
99 "layout", GST_PROPS_STRING ("gfloat"),
100 "intercept", GST_PROPS_FLOAT (0.0),
101 "slope", GST_PROPS_FLOAT (1.0),
102 "rate", GST_PROPS_INT_RANGE (4000, 96000),
103 "channels", GST_PROPS_INT_RANGE (1, 2)
107 static void gst_adder_class_init (GstAdderClass *klass);
108 static void gst_adder_init (GstAdder *adder);
110 static void gst_adder_get_property (GObject *object, guint prop_id,
111 GValue *value, GParamSpec *pspec);
113 static GstPad* gst_adder_request_new_pad (GstElement *element, GstPadTemplate *temp,
114 const gchar *unused);
116 /* we do need a loop function */
117 static void gst_adder_loop (GstElement *element);
119 static GstElementClass *parent_class = NULL;
120 /* static guint gst_adder_signals[LAST_SIGNAL] = { 0 }; */
123 gst_adder_get_type(void) {
124 static GType adder_type = 0;
127 static const GTypeInfo adder_info = {
128 sizeof(GstAdderClass), NULL,
130 (GClassInitFunc)gst_adder_class_init,
135 (GInstanceInitFunc)gst_adder_init,
137 adder_type = g_type_register_static (GST_TYPE_ELEMENT, "GstAdder", &adder_info, 0);
143 gst_adder_parse_caps (GstAdder *adder, GstCaps *caps)
147 gst_caps_get_string (caps, "format", &format);
149 if (adder->format == GST_ADDER_FORMAT_UNSET) {
150 /* the caps haven't been set yet at all, so we need to go ahead and set all
151 the relevant values. */
152 if (strcmp (format, "int") == 0) {
153 adder->format = GST_ADDER_FORMAT_INT;
154 gst_caps_get_int (caps, "width", &adder->width);
155 gst_caps_get_int (caps, "depth", &adder->depth);
156 gst_caps_get_int (caps, "law", &adder->law);
157 gst_caps_get_int (caps, "endianness", &adder->endianness);
158 gst_caps_get_boolean (caps, "signed", &adder->is_signed);
159 gst_caps_get_int (caps, "channels", &adder->channels);
160 } else if (strcmp (format, "float") == 0) {
161 adder->format = GST_ADDER_FORMAT_FLOAT;
162 gst_caps_get_string (caps, "layout", &adder->layout);
163 gst_caps_get_float (caps, "intercept", &adder->intercept);
164 gst_caps_get_float (caps, "slope", &adder->slope);
165 gst_caps_get_int (caps, "channels", &adder->channels);
168 /* otherwise, a previously-connected pad has set all the values. we should
169 barf if some of the attempted new values don't match. */
170 if (strcmp (format, "int") == 0) {
171 gint width, channels;
174 gst_caps_get_int (caps, "width", &width);
175 gst_caps_get_int (caps, "channels", &channels);
176 gst_caps_get_boolean (caps, "signed", &is_signed);
178 if ((adder->format != GST_ADDER_FORMAT_INT) ||
179 (adder->width != width) ||
180 (adder->channels != channels) ||
181 (adder->is_signed != is_signed)) {
184 } else if (strcmp (format, "float") == 0) {
187 gst_caps_get_int (caps, "channels", &channels);
189 if ((adder->format != GST_ADDER_FORMAT_FLOAT) ||
190 (adder->channels != channels)) {
194 /* whoa, we don't know what's trying to connect with us ! barf ! */
201 static GstPadConnectReturn
202 gst_adder_connect (GstPad *pad, GstCaps *caps)
205 GList *sinkpads, *remove = NULL;
209 g_return_val_if_fail (caps != NULL, GST_PAD_CONNECT_REFUSED);
210 g_return_val_if_fail (pad != NULL, GST_PAD_CONNECT_REFUSED);
212 adder = GST_ADDER (GST_PAD_PARENT (pad));
214 if (GST_CAPS_IS_FIXED (caps)) {
215 if (!gst_adder_parse_caps (adder, caps))
216 return GST_PAD_CONNECT_REFUSED;
218 if (pad == adder->srcpad || gst_pad_try_set_caps (adder->srcpad, caps)) {
219 sinkpads = gst_element_get_pad_list ((GstElement*) adder);
221 p = (GstPad*) sinkpads->data;
222 if (p != pad && p != adder->srcpad) {
223 if (!gst_pad_try_set_caps (p, caps)) {
224 GST_DEBUG (0, "caps mismatch; disconnecting and removing pad %s:%s (peer %s:%s)",
225 GST_DEBUG_PAD_NAME (p), GST_DEBUG_PAD_NAME (GST_PAD_PEER (p)));
226 gst_pad_disconnect (GST_PAD (GST_PAD_PEER (p)), p);
227 remove = g_list_prepend (remove, p);
230 sinkpads = g_list_next (sinkpads);
233 gst_element_remove_pad (GST_ELEMENT (adder), GST_PAD_CAST (remove->data));
235 channels = adder->input_channels;
237 GstAdderInputChannel *channel = (GstAdderInputChannel*) channels->data;
238 if (channel->sinkpad == GST_PAD_CAST (remove->data)) {
239 gst_bytestream_destroy (channel->bytestream);
240 adder->input_channels = g_slist_remove_link (adder->input_channels, channels);
241 adder->numsinkpads--;
244 channels = g_slist_next (channels);
246 remove = g_list_next (remove);
248 return GST_PAD_CONNECT_OK;
250 return GST_PAD_CONNECT_REFUSED;
253 return GST_PAD_CONNECT_DELAYED;
258 gst_adder_class_init (GstAdderClass *klass)
260 GObjectClass *gobject_class;
261 GstElementClass *gstelement_class;
263 gobject_class = (GObjectClass*)klass;
264 gstelement_class = (GstElementClass*)klass;
266 parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
268 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NUM_PADS,
269 g_param_spec_int("num_pads","num_pads","num_pads",
270 G_MININT, G_MAXINT, 0, G_PARAM_READABLE));
272 gobject_class->get_property = gst_adder_get_property;
274 gstelement_class->request_new_pad = gst_adder_request_new_pad;
278 gst_adder_init (GstAdder *adder)
280 adder->srcpad = gst_pad_new_from_template (gst_adder_src_template_factory (), "src");
281 gst_element_add_pad (GST_ELEMENT (adder), adder->srcpad);
282 gst_element_set_loop_function (GST_ELEMENT (adder), gst_adder_loop);
283 gst_pad_set_connect_function (adder->srcpad, gst_adder_connect);
285 adder->format = GST_ADDER_FORMAT_UNSET;
287 /* keep track of the sinkpads requested */
289 adder->numsinkpads = 0;
290 adder->input_channels = NULL;
294 gst_adder_request_new_pad (GstElement *element, GstPadTemplate *templ, const gchar *unused)
298 GstAdderInputChannel *input;
300 g_return_val_if_fail (GST_IS_ADDER (element), NULL);
302 if (templ->direction != GST_PAD_SINK) {
303 g_warning ("gstadder: request new pad that is not a SINK pad\n");
307 /* allocate space for the input_channel */
309 input = (GstAdderInputChannel *) g_malloc (sizeof (GstAdderInputChannel));
311 g_warning ("gstadder: could not allocate memory for adder input channel !\n");
315 adder = GST_ADDER (element);
317 /* fill in input_channel structure */
319 name = g_strdup_printf ("sink%d", adder->numsinkpads);
320 input->sinkpad = gst_pad_new_from_template (templ, name);
321 input->bytestream = gst_bytestream_new (input->sinkpad);
323 gst_element_add_pad (GST_ELEMENT (adder), input->sinkpad);
324 gst_pad_set_connect_function(input->sinkpad, gst_adder_connect);
326 /* add the input_channel to the list of input channels */
328 adder->input_channels = g_slist_append (adder->input_channels, input);
329 adder->numsinkpads++;
331 return input->sinkpad;
335 gst_adder_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
339 /* it's not null if we got it, but it might not be ours */
340 g_return_if_fail (GST_IS_ADDER (object));
342 adder = GST_ADDER (object);
346 g_value_set_int (value, adder->numsinkpads);
349 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
356 gst_adder_loop (GstElement *element)
359 * combine channels by adding sample values
361 * - request an output buffer from the pool
362 * - repeat for each input pipe :
363 * - get number of bytes from the channel's bytestream to fill output buffer
364 * - if there's an EOS event, remove the input channel
365 * - otherwise add the gotten bytes to the output buffer
366 * - push out the output buffer
371 GstEvent *event = NULL;
374 GstAdderInputChannel *input;
376 gint8 *raw_in, *zero_out;
380 g_return_if_fail (element != NULL);
381 g_return_if_fail (GST_IS_ADDER (element));
383 adder = GST_ADDER (element);
384 adder->bufpool = gst_pad_get_bufferpool (adder->srcpad);
385 if (adder->bufpool == NULL) {
386 adder->bufpool = gst_buffer_pool_get_default(GST_ADDER_BUFFER_SIZE, GST_ADDER_NUM_BUFFERS);
390 /* get new output buffer */
391 buf_out = gst_buffer_new_from_pool (adder->bufpool, 0, 0);
394 GST_ERROR (0, "could not get new output buffer !\n");
396 /* initialize the output data to 0 */
397 zero_out = (gint8 *) GST_BUFFER_DATA (buf_out);
398 for (i = 0; i < GST_BUFFER_SIZE (buf_out); i++)
401 /* get data from all of the sinks */
402 inputs = adder->input_channels;
404 GST_DEBUG (0, "starting to cycle through channels");
407 input = (GstAdderInputChannel *) inputs->data;
409 GST_DEBUG (0, "looking into channel %p", input);
411 /* get data from the bytestream of each input channel. we need to check for
412 events before passing on the data to the output buffer. */
413 raw_in = gst_bytestream_peek_bytes (input->bytestream, GST_BUFFER_SIZE (buf_out));
415 if (raw_in == NULL) {
416 /* we need to check for an event. */
417 gst_bytestream_get_status (input->bytestream, &waiting, &event);
420 if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
421 /* if we get an EOS event from one of our sink pads, we assume that
422 pad's finished handling data. delete the bytestream, free up the
423 pad, and free up the memory associated with the input channel. */
424 GST_DEBUG (0, "got an EOS event");
426 gst_bytestream_destroy (input->bytestream);
427 /* gst_object_unref (GST_OBJECT (input->sinkpad)); this causes problems */
430 adder->input_channels = g_slist_delete_link (inputs, inputs);
431 inputs = adder->input_channels;
437 /* here's where the data gets copied. this is a little nasty looking
438 because it's the same code pretty much 3 times, except each time uses
439 different data types and clamp limits. */
440 GST_DEBUG (0, "copying %d bytes from channel %p to output data %p in buffer %p",
441 GST_BUFFER_SIZE (buf_out), input, GST_BUFFER_DATA (buf_out), buf_out);
443 if (adder->format == GST_ADDER_FORMAT_INT) {
444 if (adder->width == 16) {
445 gint16 *in = (gint16 *) raw_in;
446 gint16 *out = (gint16 *) GST_BUFFER_DATA (buf_out);
447 for (i = 0; i < GST_BUFFER_SIZE (buf_out) / 2; i++)
448 out[i] = CLAMP(out[i] + in[i], -32768, 32767);
449 } else if (adder->width == 8) {
450 gint8 *in = (gint8 *) raw_in;
451 gint8 *out = (gint8 *) GST_BUFFER_DATA (buf_out);
452 for (i = 0; i < GST_BUFFER_SIZE (buf_out); i++)
453 out[i] = CLAMP(out[i] + in[i], -128, 127);
455 GST_ERROR (0, "invalid width (%d) for int format in gstadder\n", adder->width);
457 } else if (adder->format == GST_ADDER_FORMAT_FLOAT) {
458 gfloat *in = (gfloat *) raw_in;
459 gfloat *out = (gfloat *) GST_BUFFER_DATA (buf_out);
460 for (i = 0; i < GST_BUFFER_SIZE (buf_out) / sizeof (gfloat); i++)
463 GST_ERROR (0, "invalid audio format (%d) in gstadder\n", adder->format);
466 gst_bytestream_flush (input->bytestream, GST_BUFFER_SIZE (buf_out));
468 GST_DEBUG (0, "done copying data");
471 inputs = g_slist_next (inputs);
476 GST_DEBUG (0, "pushing buf_out");
477 gst_pad_push (adder->srcpad, buf_out);
483 plugin_init (GModule *module, GstPlugin *plugin)
485 GstElementFactory *factory;
487 factory = gst_element_factory_new("adder",GST_TYPE_ADDER,
489 g_return_val_if_fail(factory != NULL, FALSE);
491 if (! gst_library_load ("gstbytestream")) {
492 gst_info ("gstadder: could not load support library: 'gstbytestream'\n");
496 gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (gst_adder_src_template_factory));
497 gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (gst_adder_sink_template_factory));
499 gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
504 GstPluginDesc plugin_desc = {