• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * gstfluiddec - fluiddec plugin for gstreamer
3  *
4  * Copyright 2013 Wim Taymans <wim.taymans@gmail.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:element-fluiddec
24  * @title: fluiddec
25  * @see_also: timidity, wildmidi
26  *
27  * This element renders midi-events as audio streams using
28  * [Fluidsynth](http://fluidsynth.sourceforge.net/).
29  * It offers better sound quality compared to the timidity or wildmidi element.
30  *
31  * ## Example pipeline
32  * |[
33  * gst-launch-1.0 filesrc location=song.mid ! midiparse ! fluiddec ! pulsesink
34  * ]| This example pipeline will parse the midi and render to raw audio which is
35  * played via pulseaudio.
36  *
37  */
38 
39 #ifdef HAVE_CONFIG_H
40 #  include <config.h>
41 #endif
42 
43 #define FLUID_DEC_RATE 44100
44 #define FLUID_DEC_BPS  (4 * 2)
45 
46 #include <gst/gst.h>
47 #include <string.h>
48 #include <glib.h>
49 #include <glib/gstdio.h>
50 #include <stdlib.h>
51 #include <unistd.h>
52 
53 #include <gst/audio/audio.h>
54 
55 #include "gstfluiddec.h"
56 
57 #define GST_HAVE_FLUIDSYNTH_VERSION(major,minor,micro) \
58   (FLUIDSYNTH_VERSION_MAJOR > (major) || \
59   (FLUIDSYNTH_VERSION_MAJOR == (major) && FLUIDSYNTH_VERSION_MINOR > (minor)) || \
60   (FLUIDSYNTH_VERSION_MAJOR == (major) && FLUIDSYNTH_VERSION_MINOR == (minor) && FLUIDSYNTH_VERSION_MICRO >= (micro)))
61 
62 GST_DEBUG_CATEGORY_STATIC (gst_fluid_dec_debug);
63 #define GST_CAT_DEFAULT gst_fluid_dec_debug
64 
65 enum
66 {
67   /* FILL ME */
68   LAST_SIGNAL
69 };
70 
71 #define DEFAULT_SOUNDFONT       NULL
72 #define DEFAULT_SYNTH_CHORUS    TRUE
73 #define DEFAULT_SYNTH_REVERB    TRUE
74 #define DEFAULT_SYNTH_GAIN      0.2
75 #define DEFAULT_SYNTH_POLYPHONY 256
76 
77 enum
78 {
79   PROP_0,
80   PROP_SOUNDFONT,
81   PROP_SYNTH_CHORUS,
82   PROP_SYNTH_REVERB,
83   PROP_SYNTH_GAIN,
84   PROP_SYNTH_POLYPHONY
85 };
86 
87 static void gst_fluid_dec_finalize (GObject * object);
88 
89 static gboolean gst_fluid_dec_sink_event (GstPad * pad, GstObject * parent,
90     GstEvent * event);
91 
92 static GstStateChangeReturn gst_fluid_dec_change_state (GstElement * element,
93     GstStateChange transition);
94 
95 static GstFlowReturn gst_fluid_dec_chain (GstPad * sinkpad, GstObject * parent,
96     GstBuffer * buffer);
97 
98 static void gst_fluid_dec_set_property (GObject * object, guint prop_id,
99     const GValue * value, GParamSpec * pspec);
100 static void gst_fluid_dec_get_property (GObject * object, guint prop_id,
101     GValue * value, GParamSpec * pspec);
102 static gboolean fluiddec_element_init (GstPlugin * plugin);
103 
104 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
105     GST_PAD_SINK,
106     GST_PAD_ALWAYS,
107     GST_STATIC_CAPS ("audio/x-midi-event")
108     );
109 
110 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
111     GST_PAD_SRC,
112     GST_PAD_ALWAYS,
113     GST_STATIC_CAPS ("audio/x-raw, "
114         "format = (string) " GST_AUDIO_NE (F32) ", "
115         "rate = (int) " G_STRINGIFY (FLUID_DEC_RATE) ", "
116         "channels = (int) 2, " "layout = (string) interleaved"));
117 
118 #define parent_class gst_fluid_dec_parent_class
119 G_DEFINE_TYPE (GstFluidDec, gst_fluid_dec, GST_TYPE_ELEMENT);
120 GST_ELEMENT_REGISTER_DEFINE_CUSTOM (fluiddec, fluiddec_element_init);
121 
122 /* initialize the plugin's class */
123 static void
gst_fluid_dec_class_init(GstFluidDecClass * klass)124 gst_fluid_dec_class_init (GstFluidDecClass * klass)
125 {
126   GObjectClass *gobject_class;
127   GstElementClass *gstelement_class;
128 
129   gobject_class = (GObjectClass *) klass;
130   gstelement_class = (GstElementClass *) klass;
131 
132   gobject_class->finalize = gst_fluid_dec_finalize;
133   gobject_class->set_property = gst_fluid_dec_set_property;
134   gobject_class->get_property = gst_fluid_dec_get_property;
135 
136   g_object_class_install_property (gobject_class, PROP_SOUNDFONT,
137       g_param_spec_string ("soundfont",
138           "Soundfont", "the filename of a soundfont (NULL for default)",
139           DEFAULT_SOUNDFONT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
140 
141   g_object_class_install_property (gobject_class, PROP_SYNTH_CHORUS,
142       g_param_spec_boolean ("synth-chorus",
143           "Synth Chorus", "Turn the chorus on or off",
144           DEFAULT_SYNTH_CHORUS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145 
146   g_object_class_install_property (gobject_class, PROP_SYNTH_REVERB,
147       g_param_spec_boolean ("synth-reverb",
148           "Synth Reverb", "Turn the reverb on or off",
149           DEFAULT_SYNTH_REVERB, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
150 
151   g_object_class_install_property (gobject_class, PROP_SYNTH_GAIN,
152       g_param_spec_double ("synth-gain",
153           "Synth Gain", "Set the master gain", 0.0, 10.0,
154           DEFAULT_SYNTH_GAIN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
155 
156   g_object_class_install_property (gobject_class, PROP_SYNTH_POLYPHONY,
157       g_param_spec_int ("synth-polyphony",
158           "Synth Polyphony", "The number of simultaneous voices", 1, 65535,
159           DEFAULT_SYNTH_POLYPHONY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
160 
161   gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
162   gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
163 
164   gst_element_class_set_static_metadata (gstelement_class, "Fluidsynth",
165       "Codec/Decoder/Audio",
166       "Midi Synthesizer Element", "Wim Taymans <wim.taymans@gmail.com>");
167 
168   gstelement_class->change_state = gst_fluid_dec_change_state;
169 }
170 
171 /* initialize the new element
172  * instantiate pads and add them to element
173  * set functions
174  * initialize structure
175  */
176 static void
gst_fluid_dec_init(GstFluidDec * filter)177 gst_fluid_dec_init (GstFluidDec * filter)
178 {
179   filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
180   gst_pad_set_event_function (filter->sinkpad, gst_fluid_dec_sink_event);
181   gst_pad_set_chain_function (filter->sinkpad, gst_fluid_dec_chain);
182   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
183 
184   filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
185   gst_pad_use_fixed_caps (filter->srcpad);
186   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
187 
188   filter->soundfont = g_strdup (DEFAULT_SOUNDFONT);
189   filter->synth_chorus = DEFAULT_SYNTH_CHORUS;
190   filter->synth_reverb = DEFAULT_SYNTH_REVERB;
191   filter->synth_gain = DEFAULT_SYNTH_GAIN;
192   filter->synth_polyphony = DEFAULT_SYNTH_POLYPHONY;
193 
194   filter->settings = new_fluid_settings ();
195 
196   /* http://www.fluidsynth.org/api/fluidsettings.xml */
197   fluid_settings_setnum (filter->settings, "synth.sample-rate", FLUID_DEC_RATE);
198 
199   /* FIXME: Initialize after caps negotiation so we can support more rates */
200   filter->synth = new_fluid_synth (filter->settings);
201   filter->sf = -1;
202 
203   fluid_synth_set_chorus_on (filter->synth, filter->synth_chorus);
204   fluid_synth_set_reverb_on (filter->synth, filter->synth_reverb);
205   fluid_synth_set_gain (filter->synth, filter->synth_gain);
206   fluid_synth_set_polyphony (filter->synth, filter->synth_polyphony);
207 }
208 
209 static void
gst_fluid_dec_finalize(GObject * object)210 gst_fluid_dec_finalize (GObject * object)
211 {
212   GstFluidDec *fluiddec = GST_FLUID_DEC (object);
213 
214   delete_fluid_synth (fluiddec->synth);
215   delete_fluid_settings (fluiddec->settings);
216   g_free (fluiddec->soundfont);
217 
218   G_OBJECT_CLASS (parent_class)->finalize (object);
219 }
220 
221 #if 0
222 static GstBuffer *
223 gst_fluid_dec_clip_buffer (GstFluidDec * fluiddec, GstBuffer * buffer)
224 {
225   guint64 start, stop;
226   guint64 new_start, new_stop;
227   gint64 offset, length;
228 
229   /* clipping disabled for now */
230   return buffer;
231 
232   start = GST_BUFFER_OFFSET (buffer);
233   stop = GST_BUFFER_OFFSET_END (buffer);
234 
235   if (!gst_segment_clip (&fluiddec->segment, GST_FORMAT_DEFAULT,
236           start, stop, &new_start, &new_stop)) {
237     gst_buffer_unref (buffer);
238     return NULL;
239   }
240 
241   if (start == new_start && stop == new_stop)
242     return buffer;
243 
244   offset = new_start - start;
245   length = new_stop - new_start;
246 
247   buffer = gst_buffer_make_writable (buffer);
248   gst_buffer_resize (buffer, offset, length);
249 
250   GST_BUFFER_OFFSET (buffer) = new_start;
251   GST_BUFFER_OFFSET_END (buffer) = new_stop;
252   GST_BUFFER_TIMESTAMP (buffer) =
253       gst_util_uint64_scale_int (new_start, GST_SECOND, FLUID_DEC_RATE);
254   GST_BUFFER_DURATION (buffer) =
255       gst_util_uint64_scale_int (new_stop, GST_SECOND, FLUID_DEC_RATE) -
256       GST_BUFFER_TIMESTAMP (buffer);
257 
258   return buffer;
259 }
260 #endif
261 
262 static void
gst_fluid_dec_reset(GstFluidDec * fluiddec)263 gst_fluid_dec_reset (GstFluidDec * fluiddec)
264 {
265   fluid_synth_system_reset (fluiddec->synth);
266   fluiddec->last_pts = GST_CLOCK_TIME_NONE;
267 }
268 
269 static gboolean
gst_fluid_dec_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)270 gst_fluid_dec_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
271 {
272   gboolean res;
273   GstFluidDec *fluiddec = GST_FLUID_DEC (parent);
274 
275   GST_DEBUG_OBJECT (pad, "%s event received", GST_EVENT_TYPE_NAME (event));
276 
277   switch (GST_EVENT_TYPE (event)) {
278     case GST_EVENT_CAPS:
279     {
280       GstCaps *caps;
281 
282       caps = gst_caps_new_simple ("audio/x-raw",
283           "format", G_TYPE_STRING, GST_AUDIO_NE (F32),
284           "rate", G_TYPE_INT, FLUID_DEC_RATE,
285           "channels", G_TYPE_INT, 2,
286           "layout", G_TYPE_STRING, "interleaved", NULL);
287 
288       res = gst_pad_push_event (fluiddec->srcpad, gst_event_new_caps (caps));
289       gst_caps_unref (caps);
290       gst_event_unref (event);
291       break;
292     }
293     case GST_EVENT_SEGMENT:
294       gst_event_copy_segment (event, &fluiddec->segment);
295       GST_DEBUG_OBJECT (fluiddec, "configured segment %" GST_SEGMENT_FORMAT,
296           &fluiddec->segment);
297       res = gst_pad_event_default (pad, parent, event);
298       break;
299     case GST_EVENT_FLUSH_STOP:
300       gst_fluid_dec_reset (fluiddec);
301       res = gst_pad_event_default (pad, parent, event);
302       break;
303     case GST_EVENT_EOS:
304       /* FIXME, push last samples */
305       res = gst_pad_event_default (pad, parent, event);
306       break;
307     default:
308       res = gst_pad_event_default (pad, parent, event);
309       break;
310   }
311   return res;
312 }
313 
314 static GstFlowReturn
produce_samples(GstFluidDec * fluiddec,GstClockTime pts,guint64 sample)315 produce_samples (GstFluidDec * fluiddec, GstClockTime pts, guint64 sample)
316 {
317   GstClockTime duration, timestamp;
318   guint64 samples, offset;
319   GstMapInfo info;
320   GstBuffer *outbuf;
321 
322   samples = sample - fluiddec->last_sample;
323   duration = pts - fluiddec->last_pts;
324   offset = fluiddec->last_sample;
325   timestamp = fluiddec->last_pts;
326 
327   fluiddec->last_pts = pts;
328   fluiddec->last_sample = sample;
329 
330   if (samples == 0)
331     return GST_FLOW_OK;
332 
333   GST_DEBUG_OBJECT (fluiddec, "duration %" GST_TIME_FORMAT
334       ", samples %" G_GUINT64_FORMAT, GST_TIME_ARGS (duration), samples);
335 
336   outbuf = gst_buffer_new_allocate (NULL, samples * FLUID_DEC_BPS, NULL);
337 
338   gst_buffer_map (outbuf, &info, GST_MAP_WRITE);
339   fluid_synth_write_float (fluiddec->synth, samples, info.data, 0, 2,
340       info.data, 1, 2);
341   gst_buffer_unmap (outbuf, &info);
342 
343   GST_BUFFER_DTS (outbuf) = timestamp;
344   GST_BUFFER_PTS (outbuf) = timestamp;
345   GST_BUFFER_DURATION (outbuf) = duration;
346   GST_BUFFER_OFFSET (outbuf) = offset;
347   GST_BUFFER_OFFSET_END (outbuf) = offset + samples;
348 
349   if (fluiddec->discont) {
350     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
351     fluiddec->discont = FALSE;
352   }
353 
354   return gst_pad_push (fluiddec->srcpad, outbuf);
355 }
356 
357 static void
handle_buffer(GstFluidDec * fluiddec,GstBuffer * buffer)358 handle_buffer (GstFluidDec * fluiddec, GstBuffer * buffer)
359 {
360   GstMapInfo info;
361   guint8 event;
362 
363   gst_buffer_map (buffer, &info, GST_MAP_READ);
364 
365   if (info.size == 0)
366     goto done;
367 
368   event = info.data[0];
369 
370   switch (event & 0xf0) {
371     case 0xf0:
372       switch (event) {
373         case 0xff:
374           GST_DEBUG_OBJECT (fluiddec, "system reset");
375           fluid_synth_system_reset (fluiddec->synth);
376           break;
377         case 0xf0:
378         case 0xf7:
379           GST_DEBUG_OBJECT (fluiddec, "sysex 0x%02x", event);
380           GST_MEMDUMP_OBJECT (fluiddec, "bytes ", info.data + 1, info.size - 1);
381           fluid_synth_sysex (fluiddec->synth, (char *) info.data + 1,
382               info.size - 1, NULL, NULL, NULL, 0);
383 
384           break;
385         case 0xf9:
386           GST_LOG_OBJECT (fluiddec, "midi tick");
387           break;
388         default:
389           GST_WARNING_OBJECT (fluiddec, "unhandled event 0x%02x", event);
390           break;
391       }
392       break;
393     default:
394     {
395       guint8 channel, p1, p2;
396 
397       channel = event & 0x0f;
398 
399       p1 = info.size > 1 ? info.data[1] & 0x7f : 0;
400       p2 = info.size > 2 ? info.data[2] & 0x7f : 0;
401 
402       GST_DEBUG_OBJECT (fluiddec, "event 0x%02x channel %d, 0x%02x 0x%02x",
403           event, channel, p1, p2);
404 
405       switch (event & 0xf0) {
406         case 0x80:
407           fluid_synth_noteoff (fluiddec->synth, channel, p1);
408           break;
409         case 0x90:
410           fluid_synth_noteon (fluiddec->synth, channel, p1, p2);
411           break;
412         case 0xA0:
413           /* aftertouch */
414           break;
415         case 0xB0:
416           fluid_synth_cc (fluiddec->synth, channel, p1, p2);
417           break;
418         case 0xC0:
419           fluid_synth_program_change (fluiddec->synth, channel, p1);
420           break;
421         case 0xD0:
422           fluid_synth_channel_pressure (fluiddec->synth, channel, p1);
423           break;
424         case 0xE0:
425           fluid_synth_pitch_bend (fluiddec->synth, channel, (p2 << 7) | p1);
426           break;
427         default:
428           break;
429       }
430       break;
431     }
432   }
433 
434 done:
435 
436   gst_buffer_unmap (buffer, &info);
437 }
438 
439 static GstFlowReturn
gst_fluid_dec_chain(GstPad * sinkpad,GstObject * parent,GstBuffer * buffer)440 gst_fluid_dec_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * buffer)
441 {
442   GstFlowReturn res = GST_FLOW_OK;
443   GstFluidDec *fluiddec;
444   GstClockTime pts;
445 
446   fluiddec = GST_FLUID_DEC (parent);
447 
448   if (GST_BUFFER_IS_DISCONT (buffer)) {
449     fluiddec->discont = TRUE;
450   }
451 
452   pts = GST_BUFFER_PTS (buffer);
453 
454   if (pts != GST_CLOCK_TIME_NONE) {
455     guint64 sample =
456         gst_util_uint64_scale_int (pts, FLUID_DEC_RATE, GST_SECOND);
457 
458     if (fluiddec->last_pts == GST_CLOCK_TIME_NONE) {
459       fluiddec->last_pts = pts;
460       fluiddec->last_sample = sample;
461     } else if (fluiddec->last_pts < pts) {
462       /* generate samples for the elapsed time */
463       res = produce_samples (fluiddec, pts, sample);
464     }
465   }
466 
467   if (res == GST_FLOW_OK) {
468     handle_buffer (fluiddec, buffer);
469   }
470   gst_buffer_unref (buffer);
471 
472   return res;
473 }
474 
475 static gboolean
gst_fluid_dec_open(GstFluidDec * fluiddec)476 gst_fluid_dec_open (GstFluidDec * fluiddec)
477 {
478   GDir *dir;
479   GError *error = NULL;
480   const gchar *const *sharedirs;
481 
482   if (fluiddec->sf != -1)
483     return TRUE;
484 
485   if (fluiddec->soundfont) {
486     GST_DEBUG_OBJECT (fluiddec, "loading soundfont file %s",
487         fluiddec->soundfont);
488 
489     fluiddec->sf = fluid_synth_sfload (fluiddec->synth, fluiddec->soundfont, 1);
490     if (fluiddec->sf == -1)
491       goto load_failed;
492 
493     GST_DEBUG_OBJECT (fluiddec, "loaded soundfont file %s",
494         fluiddec->soundfont);
495   } else {
496     gint i, j;
497     /* ubuntu/debian in sounds/sf[23], fedora in soundfonts */
498     static const gchar *paths[] =
499         { "sounds/sf3/", "sounds/sf2/", "soundfonts/", NULL };
500 
501     sharedirs = g_get_system_data_dirs ();
502 
503     for (i = 0; sharedirs[i]; i++) {
504       for (j = 0; paths[j]; j++) {
505         gchar *soundfont_path = g_build_path ("/", sharedirs[i], paths[j],
506             NULL);
507         GST_DEBUG_OBJECT (fluiddec, "Trying to list contents of a %s directory",
508             soundfont_path);
509         error = NULL;
510         dir = g_dir_open (soundfont_path, 0, &error);
511         if (dir == NULL) {
512           GST_DEBUG_OBJECT (fluiddec,
513               "Can't open a potential soundfont directory %s: %s",
514               soundfont_path, error->message);
515           g_free (soundfont_path);
516           g_error_free (error);
517           continue;
518         }
519 
520         while (TRUE) {
521           const gchar *name;
522           gchar *filename;
523 
524           if ((name = g_dir_read_name (dir)) == NULL)
525             break;
526 
527           filename = g_build_filename (soundfont_path, name, NULL);
528 
529           GST_DEBUG_OBJECT (fluiddec, "loading soundfont file %s", filename);
530           fluiddec->sf = fluid_synth_sfload (fluiddec->synth, filename, 1);
531           if (fluiddec->sf != -1) {
532             GST_DEBUG_OBJECT (fluiddec, "loaded soundfont file %s", filename);
533             g_free (filename);
534             g_dir_close (dir);
535             g_free (soundfont_path);
536             goto done;
537           }
538           GST_DEBUG_OBJECT (fluiddec, "could not load soundfont file %s",
539               filename);
540           g_free (filename);
541         }
542         g_dir_close (dir);
543         g_free (soundfont_path);
544       }
545     }
546     if (fluiddec->sf == -1) {
547       goto no_soundfont;
548     }
549   }
550 done:
551   return TRUE;
552 
553   /* ERRORS */
554 load_failed:
555   {
556     GST_ELEMENT_ERROR (fluiddec, RESOURCE, OPEN_READ,
557         ("Can't open soundfont %s", fluiddec->soundfont),
558         ("failed to open soundfont file %s for reading", fluiddec->soundfont));
559     return FALSE;
560   }
561 no_soundfont:
562   {
563     GST_ELEMENT_ERROR (fluiddec, RESOURCE, OPEN_READ,
564         ("Can't find a soundfont file in subdirectories of XDG_DATA_DIRS paths"),
565         ("no usable soundfont files found in subdirectories of XDG_DATA_DIRS"));
566     return FALSE;
567   }
568 }
569 
570 static gboolean
gst_fluid_dec_close(GstFluidDec * fluiddec)571 gst_fluid_dec_close (GstFluidDec * fluiddec)
572 {
573   if (fluiddec->sf) {
574     fluid_synth_sfunload (fluiddec->synth, fluiddec->sf, 1);
575     fluiddec->sf = -1;
576   }
577   return TRUE;
578 }
579 
580 static GstStateChangeReturn
gst_fluid_dec_change_state(GstElement * element,GstStateChange transition)581 gst_fluid_dec_change_state (GstElement * element, GstStateChange transition)
582 {
583   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
584   GstFluidDec *fluiddec = GST_FLUID_DEC (element);
585 
586   switch (transition) {
587     case GST_STATE_CHANGE_NULL_TO_READY:
588       if (!gst_fluid_dec_open (fluiddec))
589         goto open_failed;
590       break;
591     case GST_STATE_CHANGE_READY_TO_PAUSED:
592       gst_fluid_dec_reset (fluiddec);
593       break;
594     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
595       break;
596     default:
597       break;
598   }
599 
600   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
601 
602   switch (transition) {
603     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
604       break;
605     case GST_STATE_CHANGE_PAUSED_TO_READY:
606       break;
607     case GST_STATE_CHANGE_READY_TO_NULL:
608       gst_fluid_dec_close (fluiddec);
609       break;
610     default:
611       break;
612   }
613 
614   return ret;
615 
616   /* ERRORS */
617 open_failed:
618   {
619     GST_ERROR_OBJECT (fluiddec, "could not open");
620     return GST_STATE_CHANGE_FAILURE;
621   }
622 }
623 
624 static void
gst_fluid_dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)625 gst_fluid_dec_set_property (GObject * object, guint prop_id,
626     const GValue * value, GParamSpec * pspec)
627 {
628   GstFluidDec *fluiddec = GST_FLUID_DEC (object);
629 
630   switch (prop_id) {
631     case PROP_SOUNDFONT:
632       g_free (fluiddec->soundfont);
633       fluiddec->soundfont = g_value_dup_string (value);
634       break;
635     case PROP_SYNTH_CHORUS:
636       fluiddec->synth_chorus = g_value_get_boolean (value);
637       fluid_synth_set_chorus_on (fluiddec->synth, fluiddec->synth_chorus);
638       break;
639     case PROP_SYNTH_REVERB:
640       fluiddec->synth_reverb = g_value_get_boolean (value);
641       fluid_synth_set_reverb_on (fluiddec->synth, fluiddec->synth_reverb);
642       break;
643     case PROP_SYNTH_GAIN:
644       fluiddec->synth_gain = g_value_get_double (value);
645       fluid_synth_set_gain (fluiddec->synth, fluiddec->synth_gain);
646       break;
647     case PROP_SYNTH_POLYPHONY:
648       fluiddec->synth_polyphony = g_value_get_int (value);
649       fluid_synth_set_polyphony (fluiddec->synth, fluiddec->synth_polyphony);
650       break;
651     default:
652       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
653       break;
654   }
655 }
656 
657 static void
gst_fluid_dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)658 gst_fluid_dec_get_property (GObject * object, guint prop_id,
659     GValue * value, GParamSpec * pspec)
660 {
661   GstFluidDec *fluiddec = GST_FLUID_DEC (object);
662 
663   switch (prop_id) {
664     case PROP_SOUNDFONT:
665       g_value_set_string (value, fluiddec->soundfont);
666       break;
667     case PROP_SYNTH_CHORUS:
668       g_value_set_boolean (value, fluiddec->synth_chorus);
669       break;
670     case PROP_SYNTH_REVERB:
671       g_value_set_boolean (value, fluiddec->synth_reverb);
672       break;
673     case PROP_SYNTH_GAIN:
674       g_value_set_double (value, fluiddec->synth_gain);
675       break;
676     case PROP_SYNTH_POLYPHONY:
677       g_value_set_int (value, fluiddec->synth_polyphony);
678       break;
679     default:
680       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
681       break;
682   }
683 }
684 
685 /* fluid_synth log handler */
686 static void
gst_fluid_synth_error_log_function(int level,const char * message,void * data)687 gst_fluid_synth_error_log_function (int level, const char *message, void *data)
688 {
689   GST_ERROR ("%s", message);
690 }
691 
692 static void
gst_fluid_synth_warning_log_function(int level,const char * message,void * data)693 gst_fluid_synth_warning_log_function (int level, const char *message,
694     void *data)
695 {
696   GST_WARNING ("%s", message);
697 }
698 
699 static void
gst_fluid_synth_info_log_function(int level,const char * message,void * data)700 gst_fluid_synth_info_log_function (int level, const char *message, void *data)
701 {
702   GST_INFO ("%s", message);
703 }
704 
705 static void
gst_fluid_synth_debug_log_function(int level,const char * message,void * data)706 gst_fluid_synth_debug_log_function (int level, const char *message, void *data)
707 {
708   GST_DEBUG ("%s", message);
709 }
710 
711 static gboolean
fluiddec_element_init(GstPlugin * plugin)712 fluiddec_element_init (GstPlugin * plugin)
713 {
714   GST_DEBUG_CATEGORY_INIT (gst_fluid_dec_debug, "fluiddec",
715       0, "Fluidsynth MIDI decoder plugin");
716 
717   /* We modify FluidSynth's global state here; let's hope nobody tries to use
718    * it natively alongside this plugin. */
719 
720 #ifndef GST_DISABLE_GST_DEBUG
721   fluid_set_log_function (FLUID_PANIC,
722       (fluid_log_function_t) gst_fluid_synth_error_log_function, NULL);
723   fluid_set_log_function (FLUID_ERR,
724       (fluid_log_function_t) gst_fluid_synth_warning_log_function, NULL);
725   fluid_set_log_function (FLUID_WARN,
726       (fluid_log_function_t) gst_fluid_synth_warning_log_function, NULL);
727   fluid_set_log_function (FLUID_INFO,
728       (fluid_log_function_t) gst_fluid_synth_info_log_function, NULL);
729   fluid_set_log_function (FLUID_DBG,
730       (fluid_log_function_t) gst_fluid_synth_debug_log_function, NULL);
731 #else
732   fluid_set_log_function (FLUID_PANIC, NULL, NULL);
733   fluid_set_log_function (FLUID_ERR, NULL, NULL);
734   fluid_set_log_function (FLUID_WARN, NULL, NULL);
735   fluid_set_log_function (FLUID_INFO, NULL, NULL);
736   fluid_set_log_function (FLUID_DBG, NULL, NULL);
737 #endif
738 
739 #if GST_HAVE_FLUIDSYNTH_VERSION(1, 1, 9)
740   {
741     /* Disable all audio drivers so new_fluid_settings() does not probe them.
742      * This can crash if FluidSynth is already in use. */
743     const char *empty[] = { NULL };
744     if (fluid_audio_driver_register (empty) != FLUID_OK) {
745       GST_WARNING ("Failed to clear audio drivers");
746     }
747   }
748 #endif
749   return gst_element_register (plugin, "fluiddec",
750       GST_RANK_SECONDARY, GST_TYPE_FLUID_DEC);
751 }
752 
753 static gboolean
plugin_init(GstPlugin * plugin)754 plugin_init (GstPlugin * plugin)
755 {
756   return GST_ELEMENT_REGISTER (fluiddec, plugin);
757 }
758 
759 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
760     GST_VERSION_MINOR,
761     fluidsynthmidi,
762     "Fluidsynth MIDI Plugin",
763     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
764