Define GstElementDetails as const and also static (when defined as global)
[platform/upstream/gstreamer.git] / ext / sndfile / gstsf.c
1 /* GStreamer libsndfile plugin
2  * Copyright (C) 2003 Andy Wingo <wingo at pobox dot com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gst/gst-i18n-plugin.h"
26 #include <string.h>
27 #include <gst/gst.h>
28
29 #include <gst/audio/audio.h>
30
31 #include "gstsf.h"
32
33
34 static const GstElementDetails sfsrc_details =
35 GST_ELEMENT_DETAILS ("Sndfile source",
36     "Source/Audio",
37     "Read audio streams from disk using libsndfile",
38     "Andy Wingo <wingo at pobox dot com>");
39
40 static const GstElementDetails sfsink_details =
41 GST_ELEMENT_DETAILS ("Sndfile sink",
42     "Sink/Audio",
43     "Write audio streams to disk using libsndfile",
44     "Andy Wingo <wingo at pobox dot com>");
45
46 enum
47 {
48   ARG_0,
49   ARG_LOCATION,
50   ARG_MAJOR_TYPE,
51   ARG_MINOR_TYPE,
52   ARG_LOOP,
53   ARG_CREATE_PADS
54 };
55
56 static GstStaticPadTemplate sf_src_factory = GST_STATIC_PAD_TEMPLATE ("src%d",
57     GST_PAD_SRC,
58     GST_PAD_REQUEST,
59     GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
60     );
61
62 static GstStaticPadTemplate sf_sink_factory = GST_STATIC_PAD_TEMPLATE ("sink%d",
63     GST_PAD_SINK,
64     GST_PAD_REQUEST,
65     GST_STATIC_CAPS (GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
66     );
67
68 #define GST_TYPE_SF_MAJOR_TYPES (gst_sf_major_types_get_type())
69 static GType
70 gst_sf_major_types_get_type (void)
71 {
72   static GType sf_major_types_type = 0;
73   static GEnumValue *sf_major_types = NULL;
74
75   if (!sf_major_types_type) {
76     SF_FORMAT_INFO format_info;
77     int k, count;
78
79     sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int));
80
81     sf_major_types = g_new0 (GEnumValue, count + 1);
82
83     for (k = 0; k < count; k++) {
84       format_info.format = k;
85       sf_command (NULL, SFC_GET_FORMAT_MAJOR, &format_info,
86           sizeof (format_info));
87       sf_major_types[k].value = format_info.format;
88       sf_major_types[k].value_name = g_strdup (format_info.name);
89       sf_major_types[k].value_nick = g_strdup (format_info.extension);
90
91       /* Irritatingly enough, there exist major_types with the same extension. Let's
92          just hope that sndfile gives us the list in alphabetical order, as it
93          currently does. */
94       if (k > 0
95           && strcmp (sf_major_types[k].value_nick,
96               sf_major_types[k - 1].value_nick) == 0) {
97         g_free (sf_major_types[k].value_nick);
98         sf_major_types[k].value_nick =
99             g_strconcat (sf_major_types[k - 1].value_nick, "-",
100             sf_major_types[k].value_name, NULL);
101         g_strcanon (sf_major_types[k].value_nick,
102             G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
103       }
104     }
105
106     sf_major_types_type =
107         g_enum_register_static ("GstSndfileMajorTypes", sf_major_types);
108   }
109   return sf_major_types_type;
110 }
111
112 #define GST_TYPE_SF_MINOR_TYPES (gst_sf_minor_types_get_type())
113 static GType
114 gst_sf_minor_types_get_type (void)
115 {
116   static GType sf_minor_types_type = 0;
117   static GEnumValue *sf_minor_types = NULL;
118
119   if (!sf_minor_types_type) {
120     SF_FORMAT_INFO format_info;
121     int k, count;
122
123     sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int));
124
125     sf_minor_types = g_new0 (GEnumValue, count + 1);
126
127     for (k = 0; k < count; k++) {
128       format_info.format = k;
129       sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &format_info,
130           sizeof (format_info));
131       sf_minor_types[k].value = format_info.format;
132       sf_minor_types[k].value_name = g_strdup (format_info.name);
133       sf_minor_types[k].value_nick = g_ascii_strdown (format_info.name, -1);
134       g_strcanon (sf_minor_types[k].value_nick, G_CSET_a_2_z G_CSET_DIGITS "-",
135           '-');
136     }
137
138     sf_minor_types_type =
139         g_enum_register_static ("GstSndfileMinorTypes", sf_minor_types);
140   }
141   return sf_minor_types_type;
142 }
143
144 static void gst_sfsrc_base_init (gpointer g_class);
145 static void gst_sfsink_base_init (gpointer g_class);
146 static void gst_sf_class_init (GstSFClass * klass);
147 static void gst_sf_init (GstSF * this);
148 static void gst_sf_dispose (GObject * object);
149 static void gst_sf_set_property (GObject * object, guint prop_id,
150     const GValue * value, GParamSpec * pspec);
151 static void gst_sf_get_property (GObject * object, guint prop_id,
152     GValue * value, GParamSpec * pspec);
153
154 static GstClock *gst_sf_get_clock (GstElement * element);
155 static void gst_sf_set_clock (GstElement * element, GstClock * clock);
156 static GstPad *gst_sf_request_new_pad (GstElement * element,
157     GstPadTemplate * templ, const gchar * unused);
158 static void gst_sf_release_request_pad (GstElement * element, GstPad * pad);
159 static GstStateChangeReturn gst_sf_change_state (GstElement * element,
160     GstStateChange transition);
161
162 static GstPadLinkReturn gst_sf_link (GstPad * pad, const GstCaps * caps);
163
164 static void gst_sf_loop (GstElement * element);
165
166 static GstClockTime gst_sf_get_time (GstClock * clock, gpointer data);
167
168 static gboolean gst_sf_open_file (GstSF * this);
169 static void gst_sf_close_file (GstSF * this);
170
171 static GstElementClass *parent_class = NULL;
172
173 GST_DEBUG_CATEGORY_STATIC (gstsf_debug);
174 #define INFO(...) \
175     GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, NULL, __VA_ARGS__)
176 #define INFO_OBJ(obj,...) \
177     GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, obj, __VA_ARGS__)
178
179 GType
180 gst_sf_get_type (void)
181 {
182   static GType sf_type = 0;
183
184   if (!sf_type) {
185     static const GTypeInfo sf_info = {
186       sizeof (GstSFClass), NULL,
187       NULL,
188       (GClassInitFunc) NULL,    /* don't even initialize the class */
189       NULL,
190       NULL,
191       sizeof (GstSF),
192       0,
193       (GInstanceInitFunc) NULL  /* abstract base class */
194     };
195
196     sf_type = g_type_register_static (GST_TYPE_ELEMENT, "GstSF", &sf_info, 0);
197   }
198   return sf_type;
199 }
200
201 GType
202 gst_sfsrc_get_type (void)
203 {
204   static GType sfsrc_type = 0;
205
206   if (!sfsrc_type) {
207     static const GTypeInfo sfsrc_info = {
208       sizeof (GstSFClass),
209       gst_sfsrc_base_init,
210       NULL,
211       (GClassInitFunc) gst_sf_class_init,
212       NULL,
213       NULL,
214       sizeof (GstSF),
215       0,
216       (GInstanceInitFunc) gst_sf_init,
217     };
218
219     sfsrc_type =
220         g_type_register_static (GST_TYPE_SF, "GstSFSrc", &sfsrc_info, 0);
221   }
222   return sfsrc_type;
223 }
224
225 GType
226 gst_sfsink_get_type (void)
227 {
228   static GType sfsink_type = 0;
229
230   if (!sfsink_type) {
231     static const GTypeInfo sfsink_info = {
232       sizeof (GstSFClass),
233       gst_sfsink_base_init,
234       NULL,
235       (GClassInitFunc) gst_sf_class_init,
236       NULL,
237       NULL,
238       sizeof (GstSF),
239       0,
240       (GInstanceInitFunc) gst_sf_init,
241     };
242
243     sfsink_type =
244         g_type_register_static (GST_TYPE_SF, "GstSFSink", &sfsink_info, 0);
245   }
246   return sfsink_type;
247 }
248
249 static void
250 gst_sfsrc_base_init (gpointer g_class)
251 {
252   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
253
254   gst_element_class_add_pad_template (element_class,
255       gst_static_pad_template_get (&sf_src_factory));
256   gst_element_class_set_details (element_class, &sfsrc_details);
257 }
258
259 static void
260 gst_sfsink_base_init (gpointer g_class)
261 {
262   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
263
264   gst_element_class_add_pad_template (element_class,
265       gst_static_pad_template_get (&sf_sink_factory));
266   gst_element_class_set_details (element_class, &sfsink_details);
267 }
268
269 static void
270 gst_sf_class_init (GstSFClass * klass)
271 {
272   GObjectClass *gobject_class;
273   GstElementClass *gstelement_class;
274   GParamSpec *pspec;
275
276   gobject_class = (GObjectClass *) klass;
277   gstelement_class = (GstElementClass *) klass;
278
279   parent_class = g_type_class_peek_parent (klass);
280
281   gst_element_class_install_std_props (gstelement_class, "location",
282       ARG_LOCATION, G_PARAM_READWRITE, NULL);
283   pspec = g_param_spec_enum
284       ("major-type", "Major type", "Major output type", GST_TYPE_SF_MAJOR_TYPES,
285       SF_FORMAT_WAV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
286   g_object_class_install_property (gobject_class, ARG_MAJOR_TYPE, pspec);
287   pspec = g_param_spec_enum
288       ("minor-type", "Minor type", "Minor output type", GST_TYPE_SF_MINOR_TYPES,
289       SF_FORMAT_FLOAT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
290   g_object_class_install_property (gobject_class, ARG_MINOR_TYPE, pspec);
291
292   if (G_TYPE_FROM_CLASS (klass) == GST_TYPE_SFSRC) {
293     pspec = g_param_spec_boolean ("loop", "Loop?", "Loop the output?",
294         FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
295     g_object_class_install_property (gobject_class, ARG_LOOP, pspec);
296     pspec =
297         g_param_spec_boolean ("create-pads", "Create pads?",
298         "Create one pad for each channel in the sound file?", TRUE,
299         G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
300     g_object_class_install_property (gobject_class, ARG_CREATE_PADS, pspec);
301   }
302
303   gobject_class->dispose = gst_sf_dispose;
304   gobject_class->set_property = gst_sf_set_property;
305   gobject_class->get_property = gst_sf_get_property;
306
307   gstelement_class->get_clock = gst_sf_get_clock;
308   gstelement_class->set_clock = gst_sf_set_clock;
309   gstelement_class->change_state = gst_sf_change_state;
310   gstelement_class->request_new_pad = gst_sf_request_new_pad;
311   gstelement_class->release_pad = gst_sf_release_request_pad;
312 }
313
314 static void
315 gst_sf_init (GstSF * this)
316 {
317   gst_element_set_loop_function (GST_ELEMENT (this), gst_sf_loop);
318   this->provided_clock = gst_audio_clock_new ("sfclock", gst_sf_get_time, this);
319   gst_object_set_parent (GST_OBJECT (this->provided_clock), GST_OBJECT (this));
320 }
321
322 static void
323 gst_sf_dispose (GObject * object)
324 {
325   GstSF *this = (GstSF *) object;
326
327   if (this->provided_clock) {
328     gst_object_unparent (GST_OBJECT (this->provided_clock));
329     this->provided_clock = NULL;
330   }
331
332   G_OBJECT_CLASS (parent_class)->dispose (object);
333 }
334
335 static void
336 gst_sf_set_property (GObject * object, guint prop_id, const GValue * value,
337     GParamSpec * pspec)
338 {
339   GstSF *this = GST_SF (object);
340
341   switch (prop_id) {
342     case ARG_LOCATION:
343       if (GST_OBJECT_FLAG_IS_SET (object, GST_SF_OPEN))
344         gst_sf_close_file (this);
345       if (this->filename)
346         g_free (this->filename);
347
348       if (g_value_get_string (value))
349         this->filename = g_strdup (g_value_get_string (value));
350       else
351         this->filename = NULL;
352
353       if (this->filename)
354         gst_sf_open_file (this);
355       break;
356
357     case ARG_MAJOR_TYPE:
358       this->format_major = g_value_get_enum (value);
359       break;
360
361     case ARG_MINOR_TYPE:
362       this->format_subtype = g_value_get_enum (value);
363       break;
364
365     case ARG_LOOP:
366       this->loop = g_value_get_boolean (value);
367       break;
368
369     case ARG_CREATE_PADS:
370       this->create_pads = g_value_get_boolean (value);
371       if (this->file && this->create_pads) {
372         int i;
373
374         for (i = g_list_length (this->channels); i < this->numchannels; i++)
375           gst_element_get_request_pad ((GstElement *) this, "src%d");
376       }
377       break;
378
379     default:
380       break;
381   }
382 }
383
384 static void
385 gst_sf_get_property (GObject * object, guint prop_id, GValue * value,
386     GParamSpec * pspec)
387 {
388   GstSF *this = GST_SF (object);
389
390   switch (prop_id) {
391     case ARG_LOCATION:
392       g_value_set_string (value, this->filename);
393       break;
394
395     case ARG_MAJOR_TYPE:
396       g_value_set_enum (value, this->format_major);
397       break;
398
399     case ARG_MINOR_TYPE:
400       g_value_set_enum (value, this->format_subtype);
401       break;
402
403     case ARG_LOOP:
404       g_value_set_boolean (value, this->loop);
405       break;
406
407     case ARG_CREATE_PADS:
408       g_value_set_boolean (value, this->create_pads);
409       break;
410
411     default:
412       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
413       break;
414   }
415 }
416
417 static GstClock *
418 gst_sf_get_clock (GstElement * element)
419 {
420   GstSF *this = GST_SF (element);
421
422   return this->provided_clock;
423 }
424
425 static void
426 gst_sf_set_clock (GstElement * element, GstClock * clock)
427 {
428   GstSF *this = GST_SF (element);
429
430   this->clock = clock;
431 }
432
433 static GstClockTime
434 gst_sf_get_time (GstClock * clock, gpointer data)
435 {
436   GstSF *this = GST_SF (data);
437
438   return this->time;
439 }
440
441 static GstStateChangeReturn
442 gst_sf_change_state (GstElement * element, GstStateChange transition)
443 {
444   GstSF *this = GST_SF (element);
445
446   switch (transition) {
447     case GST_STATE_CHANGE_NULL_TO_READY:
448       break;
449     case GST_STATE_CHANGE_READY_TO_PAUSED:
450       break;
451     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
452       gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock), TRUE);
453       break;
454     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
455       gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock),
456           FALSE);
457       break;
458     case GST_STATE_CHANGE_PAUSED_TO_READY:
459       break;
460     case GST_STATE_CHANGE_READY_TO_NULL:
461       if (GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN))
462         gst_sf_close_file (this);
463       break;
464   }
465
466   if (GST_ELEMENT_CLASS (parent_class)->change_state)
467     return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
468
469   return GST_STATE_CHANGE_SUCCESS;
470 }
471
472 static GstPad *
473 gst_sf_request_new_pad (GstElement * element, GstPadTemplate * templ,
474     const gchar * unused)
475 {
476   gchar *name;
477   GstSF *this;
478   GstSFChannel *channel;
479
480   this = GST_SF (element);
481   channel = g_new0 (GstSFChannel, 1);
482
483   if (templ->direction == GST_PAD_SINK) {
484     /* we have an SFSink */
485     name = g_strdup_printf ("sink%d", this->channelcount);
486     this->numchannels++;
487     if (this->file) {
488       gst_sf_close_file (this);
489       gst_sf_open_file (this);
490     }
491   } else {
492     /* we have an SFSrc */
493     name = g_strdup_printf ("src%d", this->channelcount);
494   }
495
496   channel->pad = gst_pad_new_from_template (templ, name);
497   gst_element_add_pad (GST_ELEMENT (this), channel->pad);
498   gst_pad_set_link_function (channel->pad, gst_sf_link);
499
500   this->channels = g_list_append (this->channels, channel);
501   this->channelcount++;
502
503   INFO_OBJ (element, "added pad %s\n", name);
504
505   g_free (name);
506   return channel->pad;
507 }
508
509 static void
510 gst_sf_release_request_pad (GstElement * element, GstPad * pad)
511 {
512   GstSF *this;
513   GstSFChannel *channel = NULL;
514   GList *l;
515
516   this = GST_SF (element);
517
518   if (GST_STATE (element) == GST_STATE_PLAYING) {
519     g_warning
520         ("You can't release a request pad if the element is PLAYING, sorry.");
521     return;
522   }
523
524   for (l = this->channels; l; l = l->next) {
525     if (GST_SF_CHANNEL (l)->pad == pad) {
526       channel = GST_SF_CHANNEL (l);
527       break;
528     }
529   }
530
531   g_return_if_fail (channel != NULL);
532
533   INFO_OBJ (element, "Releasing request pad %s", GST_PAD_NAME (channel->pad));
534
535   if (GST_OBJECT_FLAG_IS_SET (element, GST_SF_OPEN))
536     gst_sf_close_file (this);
537
538   gst_element_remove_pad (element, channel->pad);
539   this->channels = g_list_remove (this->channels, channel);
540   this->numchannels--;
541   g_free (channel);
542 }
543
544 static GstPadLinkReturn
545 gst_sf_link (GstPad * pad, const GstCaps * caps)
546 {
547   GstSF *this = (GstSF *) GST_OBJECT_PARENT (pad);
548   GstStructure *structure;
549
550   structure = gst_caps_get_structure (caps, 0);
551
552   gst_structure_get_int (structure, "rate", &this->rate);
553   gst_structure_get_int (structure, "buffer-frames", &this->buffer_frames);
554
555   INFO_OBJ (this, "linked pad %s:%s with fixed caps, rate=%d, frames=%d",
556       GST_DEBUG_PAD_NAME (pad), this->rate, this->buffer_frames);
557
558   if (this->numchannels) {
559     /* we can go ahead and allocate our buffer */
560     if (this->buffer)
561       g_free (this->buffer);
562     this->buffer =
563         g_malloc (this->numchannels * this->buffer_frames * sizeof (float));
564     memset (this->buffer, 0,
565         this->numchannels * this->buffer_frames * sizeof (float));
566   }
567   return GST_PAD_LINK_OK;
568 }
569
570 static gboolean
571 gst_sf_open_file (GstSF * this)
572 {
573   int mode;
574   SF_INFO info;
575
576   g_return_val_if_fail (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN), FALSE);
577
578   this->time = 0;
579
580   if (!this->filename) {
581     GST_ELEMENT_ERROR (this, RESOURCE, NOT_FOUND,
582         (_("No filename specified.")), (NULL));
583     return FALSE;
584   }
585
586   if (GST_IS_SFSRC (this)) {
587     mode = SFM_READ;
588     info.format = 0;
589   } else {
590     if (!this->rate) {
591       INFO_OBJ (this, "Not opening %s yet because caps are not set",
592           this->filename);
593       return FALSE;
594     } else if (!this->numchannels) {
595       INFO_OBJ (this, "Not opening %s yet because we have no input channels",
596           this->filename);
597       return FALSE;
598     }
599
600     mode = SFM_WRITE;
601     this->format = this->format_major | this->format_subtype;
602     info.samplerate = this->rate;
603     info.channels = this->numchannels;
604     info.format = this->format;
605
606     INFO_OBJ (this, "Opening %s with rate %d, %d channels, format 0x%x",
607         this->filename, info.samplerate, info.channels, info.format);
608
609     if (!sf_format_check (&info)) {
610       GST_ELEMENT_ERROR (this, STREAM, ENCODE, (NULL),
611           ("Input parameters (rate:%d, channels:%d, format:0x%x) invalid",
612               info.samplerate, info.channels, info.format));
613       return FALSE;
614     }
615   }
616
617   this->file = sf_open (this->filename, mode, &info);
618
619   if (!this->file) {
620     GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE,
621         (_("Could not open file \"%s\" for writing."), this->filename),
622         ("soundfile error: %s", sf_strerror (NULL)));
623     return FALSE;
624   }
625
626   if (GST_IS_SFSRC (this)) {
627     GList *l = NULL;
628
629     /* the number of channels in the file can be different than the number of
630      * pads */
631     this->numchannels = info.channels;
632     this->rate = info.samplerate;
633
634     if (this->create_pads) {
635       int i;
636
637       for (i = g_list_length (this->channels); i < this->numchannels; i++)
638         gst_element_get_request_pad ((GstElement *) this, "src%d");
639     }
640
641     for (l = this->channels; l; l = l->next)
642       /* queue the need to set caps */
643       GST_SF_CHANNEL (l)->caps_set = FALSE;
644   }
645
646   GST_OBJECT_FLAG_SET (this, GST_SF_OPEN);
647
648   return TRUE;
649 }
650
651 static void
652 gst_sf_close_file (GstSF * this)
653 {
654   int err = 0;
655
656   g_return_if_fail (GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN));
657
658   INFO_OBJ (this, "Closing file %s", this->filename);
659
660   if ((err = sf_close (this->file)))
661     GST_ELEMENT_ERROR (this, RESOURCE, CLOSE,
662         ("Could not close file file \"%s\".", this->filename),
663         ("soundfile error: %s", strerror (err)));
664   else
665     GST_OBJECT_FLAG_UNSET (this, GST_SF_OPEN);
666
667   this->file = NULL;
668   if (this->buffer)
669     g_free (this->buffer);
670   this->buffer = NULL;
671 }
672
673 static void
674 gst_sf_loop (GstElement * element)
675 {
676   GstSF *this;
677   GList *l = NULL;
678
679   this = (GstSF *) element;
680
681   if (this->channels == NULL) {
682     GST_ELEMENT_ERROR (element, CORE, PAD, (NULL),
683         ("You must connect at least one pad to sndfile elements."));
684     return;
685   }
686
687   if (GST_IS_SFSRC (this)) {
688     sf_count_t read;
689     gint i, j;
690     int eos = 0;
691     int buffer_frames = this->buffer_frames;
692     int nchannels = this->numchannels;
693     GstSFChannel *channel = NULL;
694     gfloat *data;
695     gfloat *buf = this->buffer;
696     GstBuffer *out;
697
698     if (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN))
699       if (!gst_sf_open_file (this))
700         return;                 /* we've already set gst_element_error */
701
702     if (buffer_frames == 0) {
703       /* we have to set the caps later */
704       buffer_frames = this->buffer_frames = 1024;
705     }
706     if (buf == NULL) {
707       buf = this->buffer =
708           g_malloc (this->numchannels * this->buffer_frames * sizeof (float));
709       memset (this->buffer, 0,
710           this->numchannels * this->buffer_frames * sizeof (float));
711     }
712
713     read = sf_readf_float (this->file, buf, buffer_frames);
714     if (read < buffer_frames)
715       eos = 1;
716
717     if (read)
718       for (i = 0, l = this->channels; l; l = l->next, i++) {
719         channel = GST_SF_CHANNEL (l);
720
721         /* don't push on disconnected pads -- useful for ::create-pads=TRUE */
722         if (!GST_PAD_PEER (channel->pad))
723           continue;
724
725         if (!channel->caps_set) {
726           GstCaps *caps =
727               gst_caps_copy (GST_PAD_CAPS (GST_SF_CHANNEL (l)->pad));
728           if (!caps)
729             caps = gst_caps_copy
730                 (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (GST_SF_CHANNEL
731                         (l)->pad)));
732           gst_caps_set_simple (caps, "rate", G_TYPE_INT, this->rate,
733               "buffer-frames", G_TYPE_INT, this->buffer_frames, NULL);
734           if (!gst_pad_try_set_caps (GST_SF_CHANNEL (l)->pad, caps)) {
735             GST_ELEMENT_ERROR (this, CORE, NEGOTIATION, (NULL),
736                 ("Opened file with sample rate %d, but could not set caps",
737                     this->rate));
738             gst_sf_close_file (this);
739             return;
740           }
741           channel->caps_set = TRUE;
742         }
743
744         out = gst_buffer_new_and_alloc (read * sizeof (float));
745         data = (gfloat *) GST_BUFFER_DATA (out);
746         for (j = 0; j < read; j++)
747           data[j] = buf[j * nchannels + i % nchannels];
748         gst_pad_push (channel->pad, GST_DATA (out));
749       }
750
751     this->time += read * (GST_SECOND / this->rate);
752     gst_audio_clock_update_time ((GstAudioClock *) this->provided_clock,
753         this->time);
754
755     if (eos) {
756       if (this->loop) {
757         sf_seek (this->file, (sf_count_t) 0, SEEK_SET);
758         eos = 0;
759       } else {
760         for (l = this->channels; l; l = l->next)
761           gst_pad_push (GST_SF_CHANNEL (l)->pad,
762               GST_DATA (gst_event_new (GST_EVENT_EOS)));
763         gst_element_set_eos (element);
764       }
765     }
766   } else {
767     sf_count_t written, num_to_write;
768     gint i, j;
769     int buffer_frames = this->buffer_frames;
770     int nchannels = this->numchannels;
771     GstSFChannel *channel = NULL;
772     gfloat *data;
773     gfloat *buf = this->buffer;
774     GstBuffer *in;
775
776     /* the problem: we can't allocate a buffer for pulled data before caps is
777      * set, and we can't open the file without the sample rate from the
778      * caps... */
779
780     num_to_write = buffer_frames;
781
782     INFO_OBJ (this, "looping, buffer_frames=%d, nchannels=%d", buffer_frames,
783         nchannels);
784
785     for (i = 0, l = this->channels; l; l = l->next, i++) {
786       channel = GST_SF_CHANNEL (l);
787
788     pull_again:
789       in = GST_BUFFER (gst_pad_pull (channel->pad));
790
791       if (buffer_frames == 0) {
792         /* pulling a buffer from the pad should have caused capsnego to occur,
793            which then would set this->buffer_frames to a new value */
794         buffer_frames = this->buffer_frames;
795         if (buffer_frames == 0) {
796           GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL),
797               ("format wasn't negotiated before chain function"));
798           return;
799         }
800         buf = this->buffer;
801         num_to_write = buffer_frames;
802       }
803
804       if (!GST_OBJECT_FLAG_IS_SET (this, GST_SF_OPEN))
805         if (!gst_sf_open_file (this))
806           return;               /* we've already set gst_element_error */
807
808       if (GST_IS_EVENT (in)) {
809         switch (GST_EVENT_TYPE (in)) {
810           case GST_EVENT_EOS:
811           case GST_EVENT_INTERRUPT:
812             num_to_write = 0;
813             break;
814           default:
815             goto pull_again;
816             break;
817         }
818       }
819
820       if (num_to_write) {
821         data = (gfloat *) GST_BUFFER_DATA (in);
822         num_to_write =
823             MIN (num_to_write, GST_BUFFER_SIZE (in) / sizeof (gfloat));
824         for (j = 0; j < num_to_write; j++)
825           buf[j * nchannels + i % nchannels] = data[j];
826       }
827
828       gst_data_unref ((GstData *) in);
829     }
830
831     if (num_to_write) {
832       written = sf_writef_float (this->file, buf, num_to_write);
833       if (written != num_to_write)
834         GST_ELEMENT_ERROR (element, RESOURCE, WRITE,
835             (_("Could not write to file \"%s\"."), this->filename),
836             ("soundfile error: %s", sf_strerror (this->file)));
837     }
838
839     this->time += num_to_write * (GST_SECOND / this->rate);
840     gst_audio_clock_update_time ((GstAudioClock *) this->provided_clock,
841         this->time);
842
843     if (num_to_write != buffer_frames)
844       gst_element_set_eos (element);
845   }
846 }
847
848 static gboolean
849 plugin_init (GstPlugin * plugin)
850 {
851   if (!gst_library_load ("gstaudio"))
852     return FALSE;
853
854   GST_DEBUG_CATEGORY_INIT (gstsf_debug, "sf",
855       GST_DEBUG_FG_WHITE | GST_DEBUG_BG_GREEN | GST_DEBUG_BOLD,
856       "libsndfile plugin");
857
858   if (!gst_element_register (plugin, "sfsrc", GST_RANK_NONE, GST_TYPE_SFSRC))
859     return FALSE;
860
861   if (!gst_element_register (plugin, "sfsink", GST_RANK_NONE, GST_TYPE_SFSINK))
862     return FALSE;
863
864 #ifdef ENABLE_NLS
865   setlocale (LC_ALL, "");
866   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
867 #endif /* ENABLE_NLS */
868
869   return TRUE;
870 }
871
872 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
873     GST_VERSION_MINOR,
874     "sndfile",
875     "use libsndfile to read and write audio from and to files",
876     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)