• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2014  Antonio Ospite <ao2@ao2.it>
3  *
4  * gstalsamidisrc.c: Source element for ALSA MIDI sequencer events
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  * SECTION:element-alsamidisrc
23  * @title: alsamidisrc
24  * @see_also: #GstPushSrc
25  *
26  * The alsamidisrc element is an element that fetches ALSA MIDI sequencer
27  * events and makes them available to elements understanding
28  * audio/x-midi-events in their sink pads.
29  *
30  * It can be used to generate notes from a MIDI input device.
31  *
32  * ## Example launch line
33  * |[
34  * gst-launch -v alsamidisrc ports=129:0 ! fluiddec ! audioconvert ! autoaudiosink
35  * ]|
36  *  This pipeline will listen for events from the sequencer device at port 129:0,
37  * and generate notes using the fluiddec element.
38  *
39  */
40 
41 #ifdef HAVE_CONFIG_H
42 #  include "config.h"
43 #endif
44 
45 #include "gstalsaelements.h"
46 #include "gstalsamidisrc.h"
47 
48 GST_DEBUG_CATEGORY_STATIC (gst_alsa_midi_src_debug);
49 #define GST_CAT_DEFAULT gst_alsa_midi_src_debug
50 
51 /*
52  * The MIDI specification declares some status bytes undefined:
53  *
54  *  - 0xF4 System common - Undefined (Reserved)
55  *  - 0xF5 System common - Undefined (Reserved)
56  *  - 0xF9 System real-time - Undefined (Reserved)
57  *  - 0xFD System real-time - Undefined (Reserved)
58  *
59  * See: http://www.midi.org/techspecs/midimessages.php#2
60  *
61  * Some other documents define status 0xf9 as a tick message with a period of
62  * 10ms:
63  *
64  *  - http://www.blitter.com/~russtopia/MIDI/~jglatt/tech/midispec/tick.htm
65  *  - http://www.sequencer.de/synth/index.php/MIDI_Format#0xf9_-_MIDI_Tick
66  *
67  * Even if non-standard it looks like this convention is quite widespread.
68  *
69  * For instance Fluidsynth uses 0xF9 as a "midi tick" message:
70  * http://sourceforge.net/p/fluidsynth/code-git/ci/master/tree/fluidsynth/src/midi/fluid_midi.h#l62
71  *
72  * And then so does the midiparse element in order to be compatible with
73  * Fluidsynth and the fluiddec element.
74  *
75  * Do the same to behave like midiparse.
76  */
77 #define MIDI_TICK 0xf9
78 #define MIDI_TICK_PERIOD_MS 10
79 
80 /* Functions specific to the Alsa MIDI sequencer API */
81 
82 #define DEFAULT_BUFSIZE 65536
83 #define DEFAULT_CLIENT_NAME "alsamidisrc"
84 
85 static int
init_seq(GstAlsaMidiSrc * alsamidisrc)86 init_seq (GstAlsaMidiSrc * alsamidisrc)
87 {
88   int ret;
89 
90   ret = snd_seq_open (&alsamidisrc->seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
91   if (ret < 0) {
92     GST_ERROR_OBJECT (alsamidisrc, "Cannot open sequencer - %s",
93         snd_strerror (ret));
94     goto error;
95   }
96 
97   /*
98    * Prevent Valgrind from reporting cached configuration as memory leaks, see:
99    * http://git.alsa-project.org/?p=alsa-lib.git;a=blob;f=MEMORY-LEAK;hb=HEAD
100    */
101   snd_config_update_free_global ();
102 
103   ret = snd_seq_set_client_name (alsamidisrc->seq, DEFAULT_CLIENT_NAME);
104   if (ret < 0) {
105     GST_ERROR_OBJECT (alsamidisrc, "Cannot set client name - %s",
106         snd_strerror (ret));
107     goto error_seq_close;
108   }
109 
110   return 0;
111 
112 error_seq_close:
113   snd_seq_close (alsamidisrc->seq);
114 error:
115   return ret;
116 }
117 
118 /* Parses one or more port addresses from the string */
119 static int
parse_ports(const char * arg,GstAlsaMidiSrc * alsamidisrc)120 parse_ports (const char *arg, GstAlsaMidiSrc * alsamidisrc)
121 {
122   gchar **ports_list;
123   guint i;
124   int ret = 0;
125 
126   GST_DEBUG_OBJECT (alsamidisrc, "ports: %s", arg);
127 
128   /*
129    * Assume that ports are separated by commas.
130    *
131    * Commas are used instead of spaces because spaces are valid in client
132    * names.
133    */
134   ports_list = g_strsplit (arg, ",", 0);
135 
136   alsamidisrc->port_count = g_strv_length (ports_list);
137   alsamidisrc->seq_ports = g_try_new (snd_seq_addr_t, alsamidisrc->port_count);
138   if (!alsamidisrc->seq_ports) {
139     GST_ERROR_OBJECT (alsamidisrc, "Out of memory");
140     ret = -ENOMEM;
141     goto out_free_ports_list;
142   }
143 
144   for (i = 0; i < alsamidisrc->port_count; i++) {
145     gchar *port_name = ports_list[i];
146 
147     ret = snd_seq_parse_address (alsamidisrc->seq, &alsamidisrc->seq_ports[i],
148         port_name);
149     if (ret < 0) {
150       GST_ERROR_OBJECT (alsamidisrc, "Invalid port %s - %s", port_name,
151           snd_strerror (ret));
152       goto error_free_seq_ports;
153     }
154   }
155 
156   goto out_free_ports_list;
157 
158 error_free_seq_ports:
159   g_free (alsamidisrc->seq_ports);
160 out_free_ports_list:
161   g_strfreev (ports_list);
162   return ret;
163 }
164 
165 static int
start_queue_timer(GstAlsaMidiSrc * alsamidisrc)166 start_queue_timer (GstAlsaMidiSrc * alsamidisrc)
167 {
168   int ret;
169 
170   ret = snd_seq_start_queue (alsamidisrc->seq, alsamidisrc->queue, NULL);
171   if (ret < 0) {
172     GST_ERROR_OBJECT (alsamidisrc, "Timer event output error: %s",
173         snd_strerror (ret));
174     return ret;
175   }
176 
177   ret = snd_seq_drain_output (alsamidisrc->seq);
178   if (ret < 0)
179     GST_ERROR_OBJECT (alsamidisrc, "Drain output error: %s",
180         snd_strerror (ret));
181 
182   return ret;
183 }
184 
185 static void
schedule_next_tick(GstAlsaMidiSrc * alsamidisrc)186 schedule_next_tick (GstAlsaMidiSrc * alsamidisrc)
187 {
188   snd_seq_event_t ev;
189   snd_seq_real_time_t time;
190   int ret;
191 
192   snd_seq_ev_clear (&ev);
193   snd_seq_ev_set_source (&ev, 0);
194   snd_seq_ev_set_dest (&ev, snd_seq_client_id (alsamidisrc->seq), 0);
195 
196   ev.type = SND_SEQ_EVENT_TICK;
197 
198   alsamidisrc->tick += 1;
199   GST_TIME_TO_TIMESPEC (alsamidisrc->tick * MIDI_TICK_PERIOD_MS * GST_MSECOND,
200       time);
201 
202   snd_seq_ev_schedule_real (&ev, alsamidisrc->queue, 0, &time);
203 
204   ret = snd_seq_event_output (alsamidisrc->seq, &ev);
205   if (ret < 0)
206     GST_ERROR_OBJECT (alsamidisrc, "Event output error: %s",
207         snd_strerror (ret));
208 
209   ret = snd_seq_drain_output (alsamidisrc->seq);
210   if (ret < 0)
211     GST_ERROR_OBJECT (alsamidisrc, "Event drain error: %s", snd_strerror (ret));
212 }
213 
214 static int
create_port(GstAlsaMidiSrc * alsamidisrc)215 create_port (GstAlsaMidiSrc * alsamidisrc)
216 {
217   snd_seq_port_info_t *pinfo;
218   int ret;
219 
220   snd_seq_port_info_alloca (&pinfo);
221   snd_seq_port_info_set_name (pinfo, DEFAULT_CLIENT_NAME);
222   snd_seq_port_info_set_type (pinfo,
223       SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION);
224   snd_seq_port_info_set_capability (pinfo,
225       SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE);
226 
227   ret = snd_seq_alloc_named_queue (alsamidisrc->seq, DEFAULT_CLIENT_NAME);
228   if (ret < 0) {
229     GST_ERROR_OBJECT (alsamidisrc, "Cannot allocate queue: %s",
230         snd_strerror (ret));
231     return ret;
232   }
233 
234   /*
235    * Sequencer queues are "per-system" entities, so it's important to remember
236    * the queue id to make sure alsamidisrc refers to this very one in future
237    * operations, and not to some other port created by another sequencer user.
238    */
239   alsamidisrc->queue = ret;
240 
241   snd_seq_port_info_set_timestamping (pinfo, 1);
242   snd_seq_port_info_set_timestamp_real (pinfo, 1);
243   snd_seq_port_info_set_timestamp_queue (pinfo, alsamidisrc->queue);
244 
245   ret = snd_seq_create_port (alsamidisrc->seq, pinfo);
246   if (ret < 0) {
247     GST_ERROR_OBJECT (alsamidisrc, "Cannot create port - %s",
248         snd_strerror (ret));
249     return ret;
250   }
251 
252   /*
253    * Conversely, it's not strictly necessary to remember the port id because
254    * ports are per-client and alsamidisrc is only creating one port (id = 0).
255    *
256    * If multiple ports were to be created, the ids could be retrieved with
257    * something like:
258    *
259    *   alsamidisrc->port = snd_seq_port_info_get_port(pinfo);
260    */
261 
262   ret = start_queue_timer (alsamidisrc);
263   if (ret < 0)
264     GST_ERROR_OBJECT (alsamidisrc, "Cannot start timer for queue: %d - %s",
265         alsamidisrc->queue, snd_strerror (ret));
266 
267   return ret;
268 }
269 
270 static void
connect_ports(GstAlsaMidiSrc * alsamidisrc)271 connect_ports (GstAlsaMidiSrc * alsamidisrc)
272 {
273   int i;
274   int ret;
275 
276   for (i = 0; i < alsamidisrc->port_count; ++i) {
277     ret =
278         snd_seq_connect_from (alsamidisrc->seq, 0,
279         alsamidisrc->seq_ports[i].client, alsamidisrc->seq_ports[i].port);
280     if (ret < 0)
281       /* Issue a warning and try the other ports */
282       GST_WARNING_OBJECT (alsamidisrc, "Cannot connect from port %d:%d - %s",
283           alsamidisrc->seq_ports[i].client, alsamidisrc->seq_ports[i].port,
284           snd_strerror (ret));
285   }
286 }
287 
288 /* GStreamer specific functions */
289 
290 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
291     GST_PAD_SRC,
292     GST_PAD_ALWAYS,
293     GST_STATIC_CAPS ("audio/x-midi-event"));
294 
295 #define DEFAULT_PORTS           NULL
296 
297 enum
298 {
299   PROP_0,
300   PROP_PORTS,
301   PROP_LAST,
302 };
303 
304 #define gst_alsa_midi_src_parent_class parent_class
305 G_DEFINE_TYPE_WITH_CODE (GstAlsaMidiSrc, gst_alsa_midi_src, GST_TYPE_PUSH_SRC,
306     GST_DEBUG_CATEGORY_INIT (gst_alsa_midi_src_debug, "alsamidisrc", 0,
307         "alsamidisrc element"));
308 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (alsamidisrc, "alsamidisrc",
309     GST_RANK_PRIMARY, GST_TYPE_ALSA_MIDI_SRC, alsa_element_init (plugin));
310 
311 static void gst_alsa_midi_src_set_property (GObject * object, guint prop_id,
312     const GValue * value, GParamSpec * pspec);
313 static void gst_alsa_midi_src_get_property (GObject * object, guint prop_id,
314     GValue * value, GParamSpec * pspec);
315 
316 static gboolean gst_alsa_midi_src_start (GstBaseSrc * basesrc);
317 static gboolean gst_alsa_midi_src_stop (GstBaseSrc * basesrc);
318 static gboolean gst_alsa_midi_src_unlock (GstBaseSrc * basesrc);
319 static gboolean gst_alsa_midi_src_unlock_stop (GstBaseSrc * basesrc);
320 static void gst_alsa_midi_src_state_changed (GstElement * element,
321     GstState oldstate, GstState newstate, GstState pending);
322 
323 static GstFlowReturn
324 gst_alsa_midi_src_create (GstPushSrc * src, GstBuffer ** buf);
325 
326 static void
gst_alsa_midi_src_class_init(GstAlsaMidiSrcClass * klass)327 gst_alsa_midi_src_class_init (GstAlsaMidiSrcClass * klass)
328 {
329   GObjectClass *gobject_class;
330   GstElementClass *gstelement_class;
331   GstBaseSrcClass *gstbase_src_class;
332   GstPushSrcClass *gstpush_src_class;
333 
334   gobject_class = G_OBJECT_CLASS (klass);
335   gstelement_class = GST_ELEMENT_CLASS (klass);
336   gstbase_src_class = GST_BASE_SRC_CLASS (klass);
337   gstpush_src_class = GST_PUSH_SRC_CLASS (klass);
338 
339   gobject_class->set_property = gst_alsa_midi_src_set_property;
340   gobject_class->get_property = gst_alsa_midi_src_get_property;
341 
342   g_object_class_install_property (gobject_class, PROP_PORTS,
343       g_param_spec_string ("ports", "Ports",
344           "Comma separated list of sequencer ports (e.g. client:port,...)",
345           DEFAULT_PORTS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
346 
347   gst_element_class_set_static_metadata (gstelement_class,
348       "AlsaMidi Source",
349       "Source",
350       "Push ALSA MIDI sequencer events around", "Antonio Ospite <ao2@ao2.it>");
351   gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
352 
353   gstbase_src_class->start = GST_DEBUG_FUNCPTR (gst_alsa_midi_src_start);
354   gstbase_src_class->stop = GST_DEBUG_FUNCPTR (gst_alsa_midi_src_stop);
355   gstbase_src_class->unlock = GST_DEBUG_FUNCPTR (gst_alsa_midi_src_unlock);
356   gstbase_src_class->unlock_stop =
357       GST_DEBUG_FUNCPTR (gst_alsa_midi_src_unlock_stop);
358   gstpush_src_class->create = GST_DEBUG_FUNCPTR (gst_alsa_midi_src_create);
359   gstelement_class->state_changed =
360       GST_DEBUG_FUNCPTR (gst_alsa_midi_src_state_changed);
361 }
362 
363 static void
gst_alsa_midi_src_init(GstAlsaMidiSrc * alsamidisrc)364 gst_alsa_midi_src_init (GstAlsaMidiSrc * alsamidisrc)
365 {
366   alsamidisrc->ports = DEFAULT_PORTS;
367 
368   gst_base_src_set_format (GST_BASE_SRC (alsamidisrc), GST_FORMAT_TIME);
369   gst_base_src_set_live (GST_BASE_SRC (alsamidisrc), TRUE);
370 }
371 
372 static void
gst_alsa_midi_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)373 gst_alsa_midi_src_set_property (GObject * object, guint prop_id,
374     const GValue * value, GParamSpec * pspec)
375 {
376   GstAlsaMidiSrc *src;
377 
378   src = GST_ALSA_MIDI_SRC (object);
379 
380   switch (prop_id) {
381     case PROP_PORTS:
382       g_free (src->ports);
383       src->ports = g_value_dup_string (value);
384       break;
385     default:
386       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
387       break;
388   }
389 }
390 
391 static void
gst_alsa_midi_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)392 gst_alsa_midi_src_get_property (GObject * object, guint prop_id, GValue * value,
393     GParamSpec * pspec)
394 {
395   GstAlsaMidiSrc *src;
396 
397   g_return_if_fail (GST_IS_ALSA_MIDI_SRC (object));
398 
399   src = GST_ALSA_MIDI_SRC (object);
400 
401   switch (prop_id) {
402     case PROP_PORTS:
403       g_value_set_string (value, src->ports);
404       break;
405     default:
406       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
407       break;
408   }
409 }
410 
411 static void
push_buffer(GstAlsaMidiSrc * alsamidisrc,gpointer data,guint size,GstClockTime time,GstBufferList * buffer_list)412 push_buffer (GstAlsaMidiSrc * alsamidisrc, gpointer data, guint size,
413     GstClockTime time, GstBufferList * buffer_list)
414 {
415   gpointer local_data;
416   GstBuffer *buffer;
417 
418   buffer = gst_buffer_new ();
419 
420   GST_BUFFER_DTS (buffer) = time;
421   GST_BUFFER_PTS (buffer) = time;
422 
423   local_data = g_memdup2 (data, size);
424 
425   gst_buffer_append_memory (buffer,
426       gst_memory_new_wrapped (0, local_data, size, 0, size, local_data,
427           g_free));
428 
429   GST_MEMDUMP_OBJECT (alsamidisrc, "MIDI data:", local_data, size);
430 
431   gst_buffer_list_add (buffer_list, buffer);
432 }
433 
434 static void
push_tick_buffer(GstAlsaMidiSrc * alsamidisrc,GstClockTime time,GstBufferList * buffer_list)435 push_tick_buffer (GstAlsaMidiSrc * alsamidisrc, GstClockTime time,
436     GstBufferList * buffer_list)
437 {
438   alsamidisrc->buffer[0] = MIDI_TICK;
439   push_buffer (alsamidisrc, alsamidisrc->buffer, 1, time, buffer_list);
440 }
441 
442 static GstFlowReturn
gst_alsa_midi_src_create(GstPushSrc * src,GstBuffer ** buf)443 gst_alsa_midi_src_create (GstPushSrc * src, GstBuffer ** buf)
444 {
445   GstAlsaMidiSrc *alsamidisrc;
446   GstBufferList *buffer_list;
447   GstClockTime time;
448   long size_ev = 0;
449   int err;
450   int ret;
451   guint len;
452 
453   alsamidisrc = GST_ALSA_MIDI_SRC (src);
454 
455   buffer_list = gst_buffer_list_new ();
456 
457 poll:
458   ret = gst_poll_wait (alsamidisrc->poll, GST_CLOCK_TIME_NONE);
459   if (ret <= 0) {
460     if (ret < 0 && errno == EBUSY) {
461       GST_INFO_OBJECT (alsamidisrc, "flushing");
462       gst_buffer_list_unref (buffer_list);
463       return GST_FLOW_FLUSHING;
464     }
465     GST_ERROR_OBJECT (alsamidisrc, "ERROR in poll: %s", strerror (errno));
466   } else {
467     /* There are events available */
468     do {
469       snd_seq_event_t *event;
470       err = snd_seq_event_input (alsamidisrc->seq, &event);
471       if (err < 0)
472         break;                  /* Processed all events */
473 
474       if (event) {
475         time = GST_TIMESPEC_TO_TIME (event->time.time) - alsamidisrc->delay;
476 
477         /*
478          * Special handling is needed because decoding SND_SEQ_EVENT_TICK is
479          * not supported by alsa-lib.
480          */
481         if (event->type == SND_SEQ_EVENT_TICK) {
482           push_tick_buffer (alsamidisrc, time, buffer_list);
483           schedule_next_tick (alsamidisrc);
484           continue;
485         }
486 
487         size_ev =
488             snd_midi_event_decode (alsamidisrc->parser, alsamidisrc->buffer,
489             DEFAULT_BUFSIZE, event);
490         if (size_ev < 0) {
491           /* ENOENT indicates an event that is not a MIDI message, silently skip it */
492           if (-ENOENT == size_ev) {
493             GST_WARNING_OBJECT (alsamidisrc,
494                 "Warning: Received non-MIDI message");
495             goto poll;
496           } else {
497             GST_ERROR_OBJECT (alsamidisrc,
498                 "Error decoding event from ALSA to output: %s",
499                 strerror (-size_ev));
500             goto error;
501           }
502         } else {
503           push_buffer (alsamidisrc, alsamidisrc->buffer, size_ev, time,
504               buffer_list);
505         }
506       }
507     } while (err > 0);
508   }
509 
510   len = gst_buffer_list_length (buffer_list);
511   if (len == 0)
512     goto error;
513 
514   /* Pop the _last_ buffer in the list */
515   *buf = gst_buffer_copy (gst_buffer_list_get (buffer_list, len - 1));
516   gst_buffer_list_remove (buffer_list, len - 1, 1);
517   --len;
518 
519   /*
520    * If there are no more buffers left, free the list, otherwise push all the
521    * _previous_ buffers left in the list.
522    *
523    * The one popped above will be pushed last when this function returns.
524    */
525   if (len == 0)
526     gst_buffer_list_unref (buffer_list);
527   else
528     gst_pad_push_list (GST_BASE_SRC (src)->srcpad, buffer_list);
529 
530   return GST_FLOW_OK;
531 
532 error:
533   gst_buffer_list_unref (buffer_list);
534   return GST_FLOW_ERROR;
535 }
536 
537 static gboolean
gst_alsa_midi_src_start(GstBaseSrc * basesrc)538 gst_alsa_midi_src_start (GstBaseSrc * basesrc)
539 {
540   GstAlsaMidiSrc *alsamidisrc;
541   int ret;
542 
543   alsamidisrc = GST_ALSA_MIDI_SRC (basesrc);
544 
545   alsamidisrc->tick = 0;
546   alsamidisrc->port_count = 0;
547 
548   ret = init_seq (alsamidisrc);
549   if (ret < 0)
550     goto err;
551 
552   if (alsamidisrc->ports) {
553     ret = parse_ports (alsamidisrc->ports, alsamidisrc);
554     if (ret < 0)
555       goto error_seq_close;
556   }
557 
558   ret = create_port (alsamidisrc);
559   if (ret < 0)
560     goto error_free_seq_ports;
561 
562   connect_ports (alsamidisrc);
563 
564   ret = snd_seq_nonblock (alsamidisrc->seq, 1);
565   if (ret < 0) {
566     GST_ERROR_OBJECT (alsamidisrc, "Cannot set nonblock mode - %s",
567         snd_strerror (ret));
568     goto error_free_seq_ports;
569   }
570 
571   snd_midi_event_new (DEFAULT_BUFSIZE, &alsamidisrc->parser);
572   snd_midi_event_init (alsamidisrc->parser);
573   snd_midi_event_reset_decode (alsamidisrc->parser);
574 
575   snd_midi_event_no_status (alsamidisrc->parser, 1);
576 
577   alsamidisrc->buffer = g_try_malloc (DEFAULT_BUFSIZE);
578   if (alsamidisrc->buffer == NULL)
579     goto error_free_parser;
580 
581   {
582     struct pollfd *pfds;
583     int npfds, i;
584 
585     npfds = snd_seq_poll_descriptors_count (alsamidisrc->seq, POLLIN);
586     pfds = g_newa (struct pollfd, npfds);
587 
588     snd_seq_poll_descriptors (alsamidisrc->seq, pfds, npfds, POLLIN);
589 
590     alsamidisrc->poll = gst_poll_new (TRUE);
591     for (i = 0; i < npfds; ++i) {
592       GstPollFD fd = GST_POLL_FD_INIT;
593 
594       fd.fd = pfds[i].fd;
595       gst_poll_add_fd (alsamidisrc->poll, &fd);
596       gst_poll_fd_ctl_read (alsamidisrc->poll, &fd, TRUE);
597       gst_poll_fd_ctl_write (alsamidisrc->poll, &fd, FALSE);
598     }
599   }
600 
601   return TRUE;
602 
603 error_free_parser:
604   snd_midi_event_free (alsamidisrc->parser);
605 error_free_seq_ports:
606   g_free (alsamidisrc->seq_ports);
607 error_seq_close:
608   snd_seq_close (alsamidisrc->seq);
609 err:
610   return FALSE;
611 }
612 
613 static gboolean
gst_alsa_midi_src_stop(GstBaseSrc * basesrc)614 gst_alsa_midi_src_stop (GstBaseSrc * basesrc)
615 {
616   GstAlsaMidiSrc *alsamidisrc;
617 
618   alsamidisrc = GST_ALSA_MIDI_SRC (basesrc);
619 
620   if (alsamidisrc->poll != NULL) {
621     gst_poll_free (alsamidisrc->poll);
622     alsamidisrc->poll = NULL;
623   }
624   g_free (alsamidisrc->ports);
625   g_free (alsamidisrc->buffer);
626   snd_midi_event_free (alsamidisrc->parser);
627   g_free (alsamidisrc->seq_ports);
628   snd_seq_close (alsamidisrc->seq);
629 
630   return TRUE;
631 }
632 
633 static gboolean
gst_alsa_midi_src_unlock(GstBaseSrc * basesrc)634 gst_alsa_midi_src_unlock (GstBaseSrc * basesrc)
635 {
636   GstAlsaMidiSrc *alsamidisrc = GST_ALSA_MIDI_SRC (basesrc);
637 
638   gst_poll_set_flushing (alsamidisrc->poll, TRUE);
639   return TRUE;
640 }
641 
642 static gboolean
gst_alsa_midi_src_unlock_stop(GstBaseSrc * basesrc)643 gst_alsa_midi_src_unlock_stop (GstBaseSrc * basesrc)
644 {
645   GstAlsaMidiSrc *alsamidisrc = GST_ALSA_MIDI_SRC (basesrc);
646 
647   gst_poll_set_flushing (alsamidisrc->poll, FALSE);
648   return TRUE;
649 }
650 
651 static void
gst_alsa_midi_src_state_changed(GstElement * element,GstState oldstate,GstState newstate,GstState pending)652 gst_alsa_midi_src_state_changed (GstElement * element, GstState oldstate,
653     GstState newstate, GstState pending)
654 {
655   GstAlsaMidiSrc *alsamidisrc;
656 
657   alsamidisrc = GST_ALSA_MIDI_SRC (element);
658 
659   if (newstate == GST_STATE_PLAYING) {
660     GstClockTime gst_time;
661     GstClockTime base_time;
662     GstClockTime running_time;
663     GstClockTime queue_time;
664     GstClock *clock;
665     snd_seq_queue_status_t *status;
666 
667     clock = gst_element_get_clock (element);
668     if (clock == NULL) {
669       GST_WARNING_OBJECT (element, "No clock present");
670       return;
671     }
672     gst_time = gst_clock_get_time (clock);
673     gst_object_unref (clock);
674     base_time = gst_element_get_base_time (element);
675     running_time = gst_time - base_time;
676 
677     snd_seq_queue_status_malloc (&status);
678     snd_seq_get_queue_status (alsamidisrc->seq, alsamidisrc->queue, status);
679     queue_time =
680         GST_TIMESPEC_TO_TIME (*snd_seq_queue_status_get_real_time (status));
681     snd_seq_queue_status_free (status);
682 
683     /*
684      * The fact that the ALSA sequencer queue started before the pipeline
685      * transition to the PLAYING state ensures that the pipeline delay is
686      * always positive.
687      */
688     alsamidisrc->delay = queue_time - running_time;
689 
690     if (alsamidisrc->tick == 0) {
691       schedule_next_tick (alsamidisrc);
692     }
693   }
694 }
695