1 /* GStreamer libsndfile plugin
2 * Copyright (C) 2003 Andy Wingo <wingo at pobox dot com>
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.
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.
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.
28 #include <gst/audio/audio.h>
33 static GstElementDetails sfsrc_details = {
36 "Read audio streams from disk using libsndfile",
37 "Andy Wingo <wingo at pobox dot com>",
40 static GstElementDetails sfsink_details = {
43 "Write audio streams to disk using libsndfile",
44 "Andy Wingo <wingo at pobox dot com>",
56 GST_PAD_TEMPLATE_FACTORY (sf_src_factory,
63 GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_PROPS
67 GST_PAD_TEMPLATE_FACTORY (sf_sink_factory,
74 GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_PROPS
78 #define GST_TYPE_SF_MAJOR_TYPES (gst_sf_major_types_get_type())
80 gst_sf_major_types_get_type (void)
82 static GType sf_major_types_type = 0;
83 static GEnumValue *sf_major_types = NULL;
85 if (!sf_major_types_type)
87 SF_FORMAT_INFO format_info;
90 sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int)) ;
92 sf_major_types = g_new0 (GEnumValue, count + 1);
94 for (k = 0 ; k < count ; k++) {
95 format_info.format = k ;
96 sf_command (NULL, SFC_GET_FORMAT_MAJOR, &format_info, sizeof (format_info));
97 sf_major_types[k].value = format_info.format;
98 sf_major_types[k].value_name = g_strdup (format_info.name);
99 sf_major_types[k].value_nick = g_strdup (format_info.extension);
101 /* Irritatingly enough, there exist major_types with the same extension. Let's
102 just hope that sndfile gives us the list in alphabetical order, as it
104 if (k > 0 && strcmp (sf_major_types[k].value_nick, sf_major_types[k-1].value_nick) == 0) {
105 g_free (sf_major_types[k].value_nick);
106 sf_major_types[k].value_nick = g_strconcat (sf_major_types[k-1].value_nick, "-",
107 sf_major_types[k].value_name, NULL);
108 g_strcanon (sf_major_types[k].value_nick,
109 G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
113 sf_major_types_type = g_enum_register_static ("GstSndfileMajorTypes", sf_major_types);
115 return sf_major_types_type;
118 #define GST_TYPE_SF_MINOR_TYPES (gst_sf_minor_types_get_type())
120 gst_sf_minor_types_get_type (void)
122 static GType sf_minor_types_type = 0;
123 static GEnumValue *sf_minor_types = NULL;
125 if (!sf_minor_types_type)
127 SF_FORMAT_INFO format_info;
130 sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int)) ;
132 sf_minor_types = g_new0 (GEnumValue, count + 1);
134 for (k = 0 ; k < count ; k++) {
135 format_info.format = k ;
136 sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &format_info, sizeof (format_info));
137 sf_minor_types[k].value = format_info.format;
138 sf_minor_types[k].value_name = g_strdup (format_info.name);
139 sf_minor_types[k].value_nick = g_ascii_strdown (format_info.name, -1);
140 g_strcanon (sf_minor_types[k].value_nick, G_CSET_a_2_z G_CSET_DIGITS "-", '-');
143 sf_minor_types_type = g_enum_register_static ("GstSndfileMinorTypes", sf_minor_types);
145 return sf_minor_types_type;
148 static void gst_sfsrc_base_init (gpointer g_class);
149 static void gst_sfsink_base_init (gpointer g_class);
150 static void gst_sf_class_init (GstSFClass *klass);
151 static void gst_sf_init (GstSF *this);
152 static void gst_sf_dispose (GObject *object);
153 static void gst_sf_set_property (GObject *object, guint prop_id,
154 const GValue *value, GParamSpec *pspec);
155 static void gst_sf_get_property (GObject *object, guint prop_id,
156 GValue *value, GParamSpec *pspec);
158 static GstClock* gst_sf_get_clock (GstElement *element);
159 static void gst_sf_set_clock (GstElement *element, GstClock *clock);
160 static GstPad* gst_sf_request_new_pad (GstElement *element, GstPadTemplate *templ,
161 const gchar *unused);
162 static void gst_sf_release_request_pad (GstElement *element, GstPad *pad);
163 static GstElementStateReturn gst_sf_change_state (GstElement *element);
165 static GstPadLinkReturn gst_sf_link (GstPad *pad, GstCaps *caps);
167 static void gst_sf_loop (GstElement *element);
169 static GstClockTime gst_sf_get_time (GstClock *clock, gpointer data);
171 static gboolean gst_sf_open_file (GstSF *this);
172 static void gst_sf_close_file (GstSF *this);
174 static GstElementClass *parent_class = NULL;
176 GST_DEBUG_CATEGORY_STATIC (gstsf_debug);
178 GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, NULL, __VA_ARGS__)
179 #define INFO_OBJ(obj,...) \
180 GST_CAT_LEVEL_LOG (gstsf_debug, GST_LEVEL_INFO, obj, __VA_ARGS__)
183 gst_sf_get_type (void)
185 static GType sf_type = 0;
188 static const GTypeInfo sf_info = {
189 sizeof (GstSFClass), NULL,
191 (GClassInitFunc)NULL, /* don't even initialize the class */
196 (GInstanceInitFunc)NULL /* abstract base class */
198 sf_type = g_type_register_static (GST_TYPE_ELEMENT, "GstSF", &sf_info, 0);
204 gst_sfsrc_get_type (void)
206 static GType sfsrc_type = 0;
209 static const GTypeInfo sfsrc_info = {
213 (GClassInitFunc) gst_sf_class_init,
218 (GInstanceInitFunc) gst_sf_init,
220 sfsrc_type = g_type_register_static (GST_TYPE_SF, "GstSFSrc", &sfsrc_info, 0);
226 gst_sfsink_get_type (void)
228 static GType sfsink_type = 0;
231 static const GTypeInfo sfsink_info = {
233 gst_sfsink_base_init,
235 (GClassInitFunc) gst_sf_class_init,
240 (GInstanceInitFunc) gst_sf_init,
242 sfsink_type = g_type_register_static (GST_TYPE_SF, "GstSFSink", &sfsink_info, 0);
248 gst_sfsrc_base_init (gpointer g_class)
250 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
252 gst_element_class_add_pad_template (element_class, GST_PAD_TEMPLATE_GET (sf_src_factory));
253 gst_element_class_set_details (element_class, &sfsrc_details);
257 gst_sfsink_base_init (gpointer g_class)
259 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
261 gst_element_class_add_pad_template (element_class, GST_PAD_TEMPLATE_GET (sf_sink_factory));
262 gst_element_class_set_details (element_class, &sfsink_details);
266 gst_sf_class_init (GstSFClass *klass)
268 GObjectClass *gobject_class;
269 GstElementClass *gstelement_class;
272 gobject_class = (GObjectClass*)klass;
273 gstelement_class = (GstElementClass*)klass;
275 /* although this isn't really the parent class, that's ok; GstSF doesn't
276 override any methods */
277 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
279 gst_element_class_install_std_props (gstelement_class, "location",
280 ARG_LOCATION, G_PARAM_READWRITE, NULL);
281 pspec = g_param_spec_enum
282 ("major-type", "Major type", "Major output type", GST_TYPE_SF_MAJOR_TYPES,
283 SF_FORMAT_WAV, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
284 g_object_class_install_property (gobject_class, ARG_MAJOR_TYPE, pspec);
285 pspec = g_param_spec_enum
286 ("minor-type", "Minor type", "Minor output type", GST_TYPE_SF_MINOR_TYPES,
287 SF_FORMAT_FLOAT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
288 g_object_class_install_property (gobject_class, ARG_MINOR_TYPE, pspec);
290 if (G_TYPE_FROM_CLASS (klass) == GST_TYPE_SFSRC) {
291 pspec = g_param_spec_boolean ("loop", "Loop?", "Loop the output?",
292 FALSE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
293 g_object_class_install_property (gobject_class, ARG_LOOP, pspec);
294 pspec = g_param_spec_boolean ("create-pads", "Create pads?", "Create one pad for each channel in the sound file?",
295 TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
296 g_object_class_install_property (gobject_class, ARG_CREATE_PADS, pspec);
299 gobject_class->dispose = gst_sf_dispose;
300 gobject_class->set_property = gst_sf_set_property;
301 gobject_class->get_property = gst_sf_get_property;
303 gstelement_class->get_clock = gst_sf_get_clock;
304 gstelement_class->set_clock = gst_sf_set_clock;
305 gstelement_class->change_state = gst_sf_change_state;
306 gstelement_class->request_new_pad = gst_sf_request_new_pad;
307 gstelement_class->release_pad = gst_sf_release_request_pad;
311 gst_sf_init (GstSF *this)
313 gst_element_set_loop_function (GST_ELEMENT (this), gst_sf_loop);
314 this->provided_clock = gst_audio_clock_new ("sfclock", gst_sf_get_time, this);
315 gst_object_set_parent (GST_OBJECT (this->provided_clock), GST_OBJECT (this));
319 gst_sf_dispose (GObject *object)
321 GstSF *this = (GstSF*)object;
323 gst_object_unparent (GST_OBJECT (this->provided_clock));
325 G_OBJECT_CLASS (parent_class)->dispose (object);
329 gst_sf_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
331 GstSF *this = GST_SF (object);
335 if (GST_FLAG_IS_SET (object, GST_SF_OPEN))
336 gst_sf_close_file (this);
338 g_free (this->filename);
340 if (g_value_get_string (value))
341 this->filename = g_strdup (g_value_get_string (value));
343 this->filename = NULL;
346 gst_sf_open_file (this);
350 this->format_major = g_value_get_enum (value);
354 this->format_subtype = g_value_get_enum (value);
358 this->loop = g_value_get_boolean (value);
361 case ARG_CREATE_PADS:
362 this->create_pads = g_value_get_boolean (value);
363 if (this->file && this->create_pads) {
365 for (i=g_list_length (this->channels); i<this->numchannels; i++)
366 gst_element_get_request_pad ((GstElement*)this, "src%d");
376 gst_sf_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
378 GstSF *this = GST_SF (object);
382 g_value_set_string (value, this->filename);
386 g_value_set_enum (value, this->format_major);
390 g_value_set_enum (value, this->format_subtype);
394 g_value_set_boolean (value, this->loop);
397 case ARG_CREATE_PADS:
398 g_value_set_boolean (value, this->create_pads);
402 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
408 gst_sf_get_clock (GstElement *element)
410 GstSF *this = GST_SF (element);
412 return this->provided_clock;
416 gst_sf_set_clock (GstElement *element, GstClock *clock)
418 GstSF *this = GST_SF (element);
424 gst_sf_get_time (GstClock *clock, gpointer data)
426 GstSF *this = GST_SF (data);
431 static GstElementStateReturn
432 gst_sf_change_state (GstElement *element)
434 GstSF *this = GST_SF (element);
436 switch (GST_STATE_TRANSITION (element)) {
437 case GST_STATE_NULL_TO_READY:
439 case GST_STATE_READY_TO_PAUSED:
441 case GST_STATE_PAUSED_TO_PLAYING:
442 gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock), TRUE);
444 case GST_STATE_PLAYING_TO_PAUSED:
445 gst_audio_clock_set_active (GST_AUDIO_CLOCK (this->provided_clock), FALSE);
447 case GST_STATE_PAUSED_TO_READY:
449 case GST_STATE_READY_TO_NULL:
450 if (GST_FLAG_IS_SET (this, GST_SF_OPEN))
451 gst_sf_close_file (this);
455 if (GST_ELEMENT_CLASS (parent_class)->change_state)
456 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
458 return GST_STATE_SUCCESS;
462 gst_sf_request_new_pad (GstElement *element, GstPadTemplate *templ,
467 GstSFChannel *channel;
469 this = GST_SF (element);
470 channel = g_new0 (GstSFChannel, 1);
472 if (templ->direction == GST_PAD_SINK) {
473 /* we have an SFSink */
474 name = g_strdup_printf ("sink%d", this->channelcount);
477 gst_sf_close_file (this);
478 gst_sf_open_file (this);
481 /* we have an SFSrc */
482 name = g_strdup_printf ("src%d", this->channelcount);
485 channel->pad = gst_pad_new_from_template (templ, name);
486 gst_element_add_pad (GST_ELEMENT (this), channel->pad);
487 gst_pad_set_link_function (channel->pad, gst_sf_link);
489 this->channels = g_list_append (this->channels, channel);
490 this->channelcount++;
492 INFO_OBJ (element, "added pad %s\n", name);
499 gst_sf_release_request_pad (GstElement *element, GstPad *pad)
502 GstSFChannel *channel = NULL;
505 this = GST_SF (element);
507 if (GST_STATE (element) == GST_STATE_PLAYING) {
508 g_warning ("You can't release a request pad if the element is PLAYING, sorry.");
512 for (l=this->channels; l; l=l->next) {
513 if (GST_SF_CHANNEL (l)->pad == pad) {
514 channel = GST_SF_CHANNEL (l);
519 g_return_if_fail (channel != NULL);
521 INFO_OBJ (element, "Releasing request pad %s", GST_PAD_NAME (channel->pad));
523 if (GST_FLAG_IS_SET (element, GST_SF_OPEN))
524 gst_sf_close_file (this);
526 gst_element_remove_pad (element, channel->pad);
527 this->channels = g_list_remove (this->channels, channel);
532 static GstPadLinkReturn
533 gst_sf_link (GstPad *pad, GstCaps *caps)
535 GstSF *this = (GstSF*)GST_OBJECT_PARENT (pad);
537 if (GST_CAPS_IS_FIXED (caps)) {
538 gst_caps_get_int (caps, "rate", &this->rate);
539 gst_caps_get_int (caps, "buffer-frames", &this->buffer_frames);
541 INFO_OBJ (this, "linked pad %s:%s with fixed caps, frames=%d, rate=%d",
542 GST_DEBUG_PAD_NAME (pad), this->rate, this->buffer_frames);
544 if (this->numchannels) {
545 /* we can go ahead and allocate our buffer */
547 g_free (this->buffer);
548 this->buffer = g_malloc (this->numchannels * this->buffer_frames * sizeof (float));
549 memset (this->buffer, 0, this->numchannels * this->buffer_frames * sizeof (float));
551 return GST_PAD_LINK_OK;
554 return GST_PAD_LINK_DELAYED;
558 gst_sf_open_file (GstSF *this)
563 g_return_val_if_fail (!GST_FLAG_IS_SET (this, GST_SF_OPEN), FALSE);
567 if (!this->filename) {
568 gst_element_error (GST_ELEMENT (this), "sndfile: 'location' was not set");
572 if (GST_IS_SFSRC (this)) {
577 INFO_OBJ (this, "Not opening %s yet because caps are not set", this->filename);
579 } else if (!this->numchannels) {
580 INFO_OBJ (this, "Not opening %s yet because we have no input channels", this->filename);
585 this->format = this->format_major | this->format_subtype;
586 info.samplerate = this->rate;
587 info.channels = this->numchannels;
588 info.format = this->format;
590 INFO_OBJ (this, "Opening %s with rate %d, %d channels, format 0x%x",
591 this->filename, info.samplerate, info.channels, info.format);
593 if (!sf_format_check (&info)) {
594 gst_element_error (GST_ELEMENT (this),
595 g_strdup_printf ("Input parameters (rate:%d, channels:%d, format:0x%x) invalid",
596 info.samplerate, info.channels, info.format));
601 this->file = sf_open (this->filename, mode, &info);
604 gst_element_error (GST_ELEMENT (this),
605 g_strdup_printf ("could not open file \"%s\": %s",
606 this->filename, sf_strerror (NULL)));
610 if (GST_IS_SFSRC (this)) {
612 /* the number of channels in the file can be different than the number of
614 this->numchannels = info.channels;
615 this->rate = info.samplerate;
617 if (this->create_pads) {
619 for (i=g_list_length (this->channels); i<this->numchannels; i++)
620 gst_element_get_request_pad ((GstElement*)this, "src%d");
623 for (l=this->channels; l; l=l->next)
624 /* queue the need to set caps */
625 GST_SF_CHANNEL (l)->caps_set = FALSE;
628 GST_FLAG_SET (this, GST_SF_OPEN);
634 gst_sf_close_file (GstSF *this)
638 g_return_if_fail (GST_FLAG_IS_SET (this, GST_SF_OPEN));
640 INFO_OBJ (this, "Closing file %s", this->filename);
642 if ((err = sf_close (this->file)))
643 gst_element_error (GST_ELEMENT (this),
644 g_strdup_printf ("sndfile: could not close file \"%s\": %s",
645 this->filename, sf_error_number (err)));
647 GST_FLAG_UNSET (this, GST_SF_OPEN);
651 g_free (this->buffer);
656 gst_sf_loop (GstElement *element)
661 this = (GstSF*)element;
663 if (this->channels == NULL) {
664 gst_element_error (element, "You must connect at least one pad to sndfile elements.");
668 if (GST_IS_SFSRC (this)) {
672 int buffer_frames = this->buffer_frames;
673 int nchannels = this->numchannels;
674 GstSFChannel *channel = NULL;
676 gfloat *buf = this->buffer;
679 if (!GST_FLAG_IS_SET (this, GST_SF_OPEN))
680 if (!gst_sf_open_file (this))
681 return; /* we've already set gst_element_error */
683 if (buffer_frames == 0) {
684 /* we have to set the caps later */
685 buffer_frames = this->buffer_frames = 1024;
688 buf = this->buffer = g_malloc (this->numchannels * this->buffer_frames * sizeof (float));
689 memset (this->buffer, 0, this->numchannels * this->buffer_frames * sizeof (float));
692 read = sf_readf_float (this->file, buf, buffer_frames);
693 if (read < buffer_frames)
697 for (i=0,l=this->channels; l; l=l->next,i++) {
698 channel = GST_SF_CHANNEL (l);
700 /* don't push on disconnected pads -- useful for ::create-pads=TRUE*/
701 if (!GST_PAD_PEER (channel->pad))
704 if (!channel->caps_set) {
705 GstCaps *caps = GST_PAD_CAPS (GST_SF_CHANNEL (l)->pad);
708 (GST_PAD_TEMPLATE_CAPS (GST_PAD_PAD_TEMPLATE (GST_SF_CHANNEL (l)->pad)));
709 gst_caps_set (caps, "rate", GST_PROPS_INT (this->rate), NULL);
710 gst_caps_set (caps, "buffer-frames", GST_PROPS_INT (this->buffer_frames), NULL);
711 if (!gst_pad_try_set_caps (GST_SF_CHANNEL (l)->pad, caps)) {
712 gst_element_error (GST_ELEMENT (this),
713 g_strdup_printf ("Opened file with sample rate %d, but could not set caps",
715 gst_sf_close_file (this);
718 channel->caps_set = TRUE;
721 out = gst_buffer_new_and_alloc (read * sizeof(float));
722 data = (gfloat*)GST_BUFFER_DATA (out);
723 for (j=0; j<read; j++)
724 data[j] = buf[j * nchannels + i % nchannels];
725 gst_pad_push (channel->pad, GST_DATA (out));
728 this->time += read * (GST_SECOND / this->rate);
729 gst_audio_clock_update_time ((GstAudioClock*)this->provided_clock, this->time);
733 sf_seek (this->file, (sf_count_t)0, SEEK_SET);
736 for (l=this->channels; l; l=l->next)
737 gst_pad_push (GST_SF_CHANNEL (l)->pad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
738 gst_element_set_eos (element);
742 sf_count_t written, num_to_write;
744 int buffer_frames = this->buffer_frames;
745 int nchannels = this->numchannels;
746 GstSFChannel *channel = NULL;
748 gfloat *buf = this->buffer;
751 /* the problem: we can't allocate a buffer for pulled data before caps is
752 * set, and we can't open the file without the sample rate from the
755 num_to_write = buffer_frames;
757 INFO_OBJ (this, "looping, buffer_frames=%d, nchannels=%d", buffer_frames, nchannels);
759 for (i=0,l=this->channels; l; l=l->next,i++) {
760 channel = GST_SF_CHANNEL (l);
762 in = GST_BUFFER (gst_pad_pull (channel->pad));
764 if (buffer_frames == 0) {
765 /* pulling a buffer from the pad should have caused capsnego to occur,
766 which then would set this->buffer_frames to a new value */
767 buffer_frames = this->buffer_frames;
768 if (buffer_frames == 0) {
769 gst_element_error (element, "Caps were never set, bailing...");
773 num_to_write = buffer_frames;
776 if (!GST_FLAG_IS_SET (this, GST_SF_OPEN))
777 if (!gst_sf_open_file (this))
778 return; /* we've already set gst_element_error */
780 if (GST_IS_EVENT (in)) {
783 data = (gfloat*)GST_BUFFER_DATA (in);
784 num_to_write = MIN (num_to_write, GST_BUFFER_SIZE (in) / sizeof (gfloat));
785 for (j=0; j<num_to_write; j++)
786 buf[j * nchannels + i % nchannels] = data[j];
788 gst_data_unref ((GstData*)in);
792 written = sf_writef_float (this->file, buf, num_to_write);
793 if (written != num_to_write)
794 gst_element_error (element, "Error writing file: %s", sf_strerror (this->file));
797 this->time += num_to_write * (GST_SECOND / this->rate);
798 gst_audio_clock_update_time ((GstAudioClock*)this->provided_clock, this->time);
800 if (num_to_write != buffer_frames)
801 gst_element_set_eos (element);
806 plugin_init (GstPlugin *plugin)
808 if (!gst_library_load ("gstaudio"))
811 GST_DEBUG_CATEGORY_INIT (gstsf_debug, "sf",
812 GST_DEBUG_FG_WHITE | GST_DEBUG_BG_GREEN | GST_DEBUG_BOLD,
813 "libsndfile plugin");
815 if (!gst_element_register (plugin, "sfsrc", GST_RANK_NONE, GST_TYPE_SFSRC))
818 if (!gst_element_register (plugin, "sfsink", GST_RANK_NONE, GST_TYPE_SFSINK))
828 "Sndfile plugin library",