1 /*
2  * midiparse - midi parser 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-midiparse
24  * @title: midiparse
25  * @see_also: fluiddec
26  *
27  * This element parses midi-files into midi events. You would need a midi
28  * renderer such as fluidsynth to convert the events into raw samples.
29  *
30  * ## Example pipeline
31  * |[
32  * gst-launch-1.0 filesrc location=song.mid ! midiparse ! fluiddec ! pulsesink
33  * ]| This example pipeline will parse the midi and render to raw audio which is
34  * played via pulseaudio.
35  *
36  */
37 
38 #ifdef HAVE_CONFIG_H
39 #  include <config.h>
40 #endif
41 
42 #include <gst/gst.h>
43 #include <string.h>
44 #include <glib.h>
45 
46 #include "midiparse.h"
47 
48 GST_DEBUG_CATEGORY_STATIC (gst_midi_parse_debug);
49 #define GST_CAT_DEFAULT gst_midi_parse_debug
50 
51 enum
52 {
53   /* FILL ME */
54   LAST_SIGNAL
55 };
56 
57 enum
58 {
59   PROP_0,
60   /* FILL ME */
61 };
62 
63 #define DEFAULT_TEMPO   500000  /* 120 BPM is the default */
64 
65 typedef struct
66 {
67   guint8 *data;
68   guint size;
69   guint offset;
70 
71   guint8 running_status;
72   guint64 pulse;
73   gboolean eot;
74 
75 } GstMidiTrack;
76 
77 typedef GstFlowReturn (*GstMidiPushFunc) (GstMidiParse * parse,
78     GstMidiTrack * track, guint8 event, guint8 * data, guint length,
79     gpointer user_data);
80 
81 static void gst_midi_parse_finalize (GObject * object);
82 
83 static gboolean gst_midi_parse_sink_event (GstPad * pad, GstObject * parent,
84     GstEvent * event);
85 static gboolean gst_midi_parse_src_event (GstPad * pad, GstObject * parent,
86     GstEvent * event);
87 
88 static GstStateChangeReturn gst_midi_parse_change_state (GstElement * element,
89     GstStateChange transition);
90 static gboolean gst_midi_parse_activate (GstPad * pad, GstObject * parent);
91 static gboolean gst_midi_parse_activatemode (GstPad * pad, GstObject * parent,
92     GstPadMode mode, gboolean active);
93 
94 static void gst_midi_parse_loop (GstPad * sinkpad);
95 static GstFlowReturn gst_midi_parse_chain (GstPad * sinkpad, GstObject * parent,
96     GstBuffer * buffer);
97 
98 static gboolean gst_midi_parse_src_query (GstPad * pad, GstObject * parent,
99     GstQuery * query);
100 
101 static void gst_midi_parse_set_property (GObject * object, guint prop_id,
102     const GValue * value, GParamSpec * pspec);
103 static void gst_midi_parse_get_property (GObject * object, guint prop_id,
104     GValue * value, GParamSpec * pspec);
105 
106 static void reset_track (GstMidiTrack * track, GstMidiParse * midiparse);
107 
108 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
109     GST_PAD_SINK,
110     GST_PAD_ALWAYS,
111     GST_STATIC_CAPS ("audio/midi; audio/riff-midi")
112     );
113 
114 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
115     GST_PAD_SRC,
116     GST_PAD_ALWAYS,
117     GST_STATIC_CAPS ("audio/x-midi-event"));
118 
119 #define parent_class gst_midi_parse_parent_class
120 G_DEFINE_TYPE (GstMidiParse, gst_midi_parse, GST_TYPE_ELEMENT);
121 
122 /* initialize the plugin's class */
123 static void
gst_midi_parse_class_init(GstMidiParseClass * klass)124 gst_midi_parse_class_init (GstMidiParseClass * 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_midi_parse_finalize;
133   gobject_class->set_property = gst_midi_parse_set_property;
134   gobject_class->get_property = gst_midi_parse_get_property;
135 
136   gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
137   gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
138   gst_element_class_set_static_metadata (gstelement_class, "MidiParse",
139       "Codec/Demuxer/Audio",
140       "Midi Parser Element", "Wim Taymans <wim.taymans@gmail.com>");
141 
142   GST_DEBUG_CATEGORY_INIT (gst_midi_parse_debug, "midiparse",
143       0, "MIDI parser plugin");
144 
145   gstelement_class->change_state = gst_midi_parse_change_state;
146 }
147 
148 /* initialize the new element
149  * instantiate pads and add them to element
150  * set functions
151  * initialize structure
152  */
153 static void
gst_midi_parse_init(GstMidiParse * filter)154 gst_midi_parse_init (GstMidiParse * filter)
155 {
156   filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
157 
158   gst_pad_set_activatemode_function (filter->sinkpad,
159       gst_midi_parse_activatemode);
160   gst_pad_set_activate_function (filter->sinkpad, gst_midi_parse_activate);
161   gst_pad_set_event_function (filter->sinkpad, gst_midi_parse_sink_event);
162   gst_pad_set_chain_function (filter->sinkpad, gst_midi_parse_chain);
163   gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
164 
165   filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
166 
167   gst_pad_set_query_function (filter->srcpad, gst_midi_parse_src_query);
168   gst_pad_set_event_function (filter->srcpad, gst_midi_parse_src_event);
169   gst_pad_use_fixed_caps (filter->srcpad);
170 
171   gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
172 
173   gst_segment_init (&filter->segment, GST_FORMAT_TIME);
174 
175   filter->adapter = gst_adapter_new ();
176 
177   filter->have_group_id = FALSE;
178   filter->group_id = G_MAXUINT;
179 }
180 
181 static void
gst_midi_parse_finalize(GObject * object)182 gst_midi_parse_finalize (GObject * object)
183 {
184   GstMidiParse *midiparse;
185 
186   midiparse = GST_MIDI_PARSE (object);
187 
188   g_object_unref (midiparse->adapter);
189   g_free (midiparse->data);
190 
191   G_OBJECT_CLASS (parent_class)->finalize (object);
192 }
193 
194 static gboolean
gst_midi_parse_src_query(GstPad * pad,GstObject * parent,GstQuery * query)195 gst_midi_parse_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
196 {
197   gboolean res = TRUE;
198   GstMidiParse *midiparse = GST_MIDI_PARSE (parent);
199 
200   switch (GST_QUERY_TYPE (query)) {
201     case GST_QUERY_DURATION:
202       gst_query_set_duration (query, GST_FORMAT_TIME,
203           midiparse->segment.duration);
204       break;
205     case GST_QUERY_POSITION:
206       gst_query_set_position (query, GST_FORMAT_TIME,
207           midiparse->segment.position);
208       break;
209     case GST_QUERY_FORMATS:
210       gst_query_set_formats (query, 1, GST_FORMAT_TIME);
211       break;
212     case GST_QUERY_SEGMENT:{
213       GstFormat format;
214       gint64 start, stop;
215 
216       format = midiparse->segment.format;
217 
218       start =
219           gst_segment_to_stream_time (&midiparse->segment, format,
220           midiparse->segment.start);
221       if ((stop = midiparse->segment.stop) == -1)
222         stop = midiparse->segment.duration;
223       else
224         stop = gst_segment_to_stream_time (&midiparse->segment, format, stop);
225 
226       gst_query_set_segment (query, midiparse->segment.rate, format, start,
227           stop);
228       res = TRUE;
229       break;
230     }
231     case GST_QUERY_SEEKING:
232       gst_query_set_seeking (query, midiparse->segment.format,
233           FALSE, 0, midiparse->segment.duration);
234       break;
235     default:
236       res = gst_pad_query_default (pad, parent, query);
237       break;
238   }
239 
240   return res;
241 }
242 
243 static gboolean
gst_midi_parse_do_seek(GstMidiParse * midiparse,GstSegment * segment)244 gst_midi_parse_do_seek (GstMidiParse * midiparse, GstSegment * segment)
245 {
246   /* if seeking backwards, start from 0 else we just let things run and
247    * have it clip downstream */
248   GST_DEBUG_OBJECT (midiparse, "seeking back to 0");
249   segment->position = 0;
250   g_list_foreach (midiparse->tracks, (GFunc) reset_track, midiparse);
251   midiparse->pulse = 0;
252 
253   return TRUE;
254 }
255 
256 static gboolean
gst_midi_parse_perform_seek(GstMidiParse * midiparse,GstEvent * event)257 gst_midi_parse_perform_seek (GstMidiParse * midiparse, GstEvent * event)
258 {
259   gboolean res = TRUE, tres;
260   gdouble rate;
261   GstFormat seek_format;
262   GstSeekFlags flags;
263   GstSeekType start_type, stop_type;
264   gint64 start, stop;
265   gboolean flush;
266   gboolean update;
267   GstSegment seeksegment;
268   guint32 seqnum;
269   GstEvent *tevent;
270 
271   GST_DEBUG_OBJECT (midiparse, "doing seek: %" GST_PTR_FORMAT, event);
272 
273   if (event) {
274     gst_event_parse_seek (event, &rate, &seek_format, &flags,
275         &start_type, &start, &stop_type, &stop);
276 
277     if (seek_format != GST_FORMAT_TIME)
278       goto invalid_format;
279 
280     flush = flags & GST_SEEK_FLAG_FLUSH;
281     seqnum = gst_event_get_seqnum (event);
282   } else {
283     flush = FALSE;
284     /* get next seqnum */
285     seqnum = gst_util_seqnum_next ();
286   }
287 
288   /* send flush start */
289   if (flush) {
290     tevent = gst_event_new_flush_start ();
291     gst_event_set_seqnum (tevent, seqnum);
292     gst_pad_push_event (midiparse->srcpad, tevent);
293   } else
294     gst_pad_pause_task (midiparse->srcpad);
295 
296   /* grab streaming lock, this should eventually be possible, either
297    * because the task is paused, our streaming thread stopped
298    * or because our peer is flushing. */
299   GST_PAD_STREAM_LOCK (midiparse->sinkpad);
300   if (G_UNLIKELY (midiparse->seqnum == seqnum)) {
301     /* we have seen this event before, issue a warning for now */
302     GST_WARNING_OBJECT (midiparse, "duplicate event found %" G_GUINT32_FORMAT,
303         seqnum);
304   } else {
305     midiparse->seqnum = seqnum;
306     GST_DEBUG_OBJECT (midiparse, "seek with seqnum %" G_GUINT32_FORMAT, seqnum);
307   }
308 
309   /* Copy the current segment info into the temp segment that we can actually
310    * attempt the seek with. We only update the real segment if the seek succeeds. */
311   memcpy (&seeksegment, &midiparse->segment, sizeof (GstSegment));
312 
313   /* now configure the final seek segment */
314   if (event) {
315     gst_segment_do_seek (&seeksegment, rate, seek_format, flags,
316         start_type, start, stop_type, stop, &update);
317   }
318 
319   /* Else, no seek event passed, so we're just (re)starting the
320      current segment. */
321   GST_DEBUG_OBJECT (midiparse, "segment configured from %" G_GINT64_FORMAT
322       " to %" G_GINT64_FORMAT ", position %" G_GINT64_FORMAT,
323       seeksegment.start, seeksegment.stop, seeksegment.position);
324 
325   /* do the seek, segment.position contains the new position. */
326   res = gst_midi_parse_do_seek (midiparse, &seeksegment);
327 
328   /* and prepare to continue streaming */
329   if (flush) {
330     tevent = gst_event_new_flush_stop (TRUE);
331     gst_event_set_seqnum (tevent, seqnum);
332     /* send flush stop, peer will accept data and events again. We
333      * are not yet providing data as we still have the STREAM_LOCK. */
334     gst_pad_push_event (midiparse->srcpad, tevent);
335   }
336 
337   /* if the seek was successful, we update our real segment and push
338    * out the new segment. */
339   if (res) {
340     GST_OBJECT_LOCK (midiparse);
341     memcpy (&midiparse->segment, &seeksegment, sizeof (GstSegment));
342     GST_OBJECT_UNLOCK (midiparse);
343 
344     if (seeksegment.flags & GST_SEGMENT_FLAG_SEGMENT) {
345       GstMessage *message;
346 
347       message = gst_message_new_segment_start (GST_OBJECT (midiparse),
348           seeksegment.format, seeksegment.position);
349       gst_message_set_seqnum (message, seqnum);
350 
351       gst_element_post_message (GST_ELEMENT (midiparse), message);
352     }
353     /* for deriving a stop position for the playback segment from the seek
354      * segment, we must take the duration when the stop is not set */
355     if ((stop = seeksegment.stop) == -1)
356       stop = seeksegment.duration;
357 
358     midiparse->segment_pending = TRUE;
359     midiparse->discont = TRUE;
360   }
361 
362   /* and restart the task in case it got paused explicitly or by
363    * the FLUSH_START event we pushed out. */
364   tres =
365       gst_pad_start_task (midiparse->sinkpad,
366       (GstTaskFunction) gst_midi_parse_loop, midiparse->sinkpad, NULL);
367   if (res && !tres)
368     res = FALSE;
369 
370   /* and release the lock again so we can continue streaming */
371   GST_PAD_STREAM_UNLOCK (midiparse->sinkpad);
372 
373   return res;
374 
375   /* ERROR */
376 invalid_format:
377   {
378     GST_DEBUG_OBJECT (midiparse, "Unsupported seek format %s",
379         gst_format_get_name (seek_format));
380     return FALSE;
381   }
382 }
383 
384 static gboolean
gst_midi_parse_src_event(GstPad * pad,GstObject * parent,GstEvent * event)385 gst_midi_parse_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
386 {
387   gboolean res = FALSE;
388   GstMidiParse *midiparse = GST_MIDI_PARSE (parent);
389 
390   GST_DEBUG_OBJECT (pad, "%s event received", GST_EVENT_TYPE_NAME (event));
391 
392   switch (GST_EVENT_TYPE (event)) {
393     case GST_EVENT_SEEK:
394       res = gst_midi_parse_perform_seek (midiparse, event);
395       break;
396     default:
397       break;
398   }
399   gst_event_unref (event);
400 
401   return res;
402 }
403 
404 static gboolean
gst_midi_parse_activate(GstPad * sinkpad,GstObject * parent)405 gst_midi_parse_activate (GstPad * sinkpad, GstObject * parent)
406 {
407   GstQuery *query;
408   gboolean pull_mode;
409 
410   query = gst_query_new_scheduling ();
411 
412   if (!gst_pad_peer_query (sinkpad, query)) {
413     gst_query_unref (query);
414     goto activate_push;
415   }
416 
417   pull_mode = gst_query_has_scheduling_mode_with_flags (query,
418       GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
419   gst_query_unref (query);
420 
421   if (!pull_mode)
422     goto activate_push;
423 
424   GST_DEBUG_OBJECT (sinkpad, "activating pull");
425   return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
426 
427 activate_push:
428   {
429     GST_DEBUG_OBJECT (sinkpad, "activating push");
430     return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
431   }
432 }
433 
434 static gboolean
gst_midi_parse_activatemode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)435 gst_midi_parse_activatemode (GstPad * pad, GstObject * parent,
436     GstPadMode mode, gboolean active)
437 {
438   gboolean res;
439 
440   switch (mode) {
441     case GST_PAD_MODE_PUSH:
442       res = TRUE;
443       break;
444     case GST_PAD_MODE_PULL:
445       if (active) {
446         res = gst_pad_start_task (pad, (GstTaskFunction) gst_midi_parse_loop,
447             pad, NULL);
448       } else {
449         res = gst_pad_stop_task (pad);
450       }
451       break;
452     default:
453       res = FALSE;
454       break;
455   }
456   return res;
457 }
458 
459 static gboolean
parse_MThd(GstMidiParse * midiparse,guint8 * data,guint size)460 parse_MThd (GstMidiParse * midiparse, guint8 * data, guint size)
461 {
462   guint16 format, ntracks, division;
463   gboolean multitrack;
464 
465   format = GST_READ_UINT16_BE (data);
466   switch (format) {
467     case 0:
468       multitrack = FALSE;
469       break;
470     case 1:
471       multitrack = TRUE;
472       break;
473     default:
474     case 2:
475       goto invalid_format;
476   }
477   ntracks = GST_READ_UINT16_BE (data + 2);
478   if (ntracks > 1 && !multitrack)
479     goto invalid_tracks;
480 
481   division = GST_READ_UINT16_BE (data + 4);
482   if (division & 0x8000)
483     goto invalid_division;
484 
485   GST_DEBUG_OBJECT (midiparse, "format %u, tracks %u, division %u",
486       format, ntracks, division);
487 
488   midiparse->ntracks = ntracks;
489   midiparse->division = division;
490 
491   return TRUE;
492 
493 invalid_format:
494   {
495     GST_ERROR_OBJECT (midiparse, "unsupported midi format %u", format);
496     return FALSE;
497   }
498 invalid_tracks:
499   {
500     GST_ERROR_OBJECT (midiparse, "invalid number of tracks %u for format %u",
501         ntracks, format);
502     return FALSE;
503   }
504 invalid_division:
505   {
506     GST_ERROR_OBJECT (midiparse, "unsupported division");
507     return FALSE;
508   }
509 }
510 
511 static guint
parse_varlen(GstMidiParse * midiparse,guint8 * data,guint size,gint32 * result)512 parse_varlen (GstMidiParse * midiparse, guint8 * data, guint size,
513     gint32 * result)
514 {
515   gint32 res;
516   gint i;
517 
518   res = 0;
519   for (i = 0; i < 4; i++) {
520     if (size == 0)
521       return 0;
522 
523     res = (res << 7) | ((data[i]) & 0x7f);
524     if ((data[i] & 0x80) == 0) {
525       *result = res;
526       return i + 1;
527     }
528   }
529   return 0;
530 }
531 
532 static GstFlowReturn
handle_meta_event(GstMidiParse * midiparse,GstMidiTrack * track,guint8 event)533 handle_meta_event (GstMidiParse * midiparse, GstMidiTrack * track, guint8 event)
534 {
535   guint8 type;
536   guint8 *data;
537   gchar *bytes;
538   guint size, consumed;
539   gint32 length;
540 
541   track->offset += 1;
542 
543   data = track->data + track->offset;
544   size = track->size - track->offset;
545 
546   if (size < 1)
547     goto short_file;
548 
549   type = data[0];
550 
551   consumed = parse_varlen (midiparse, data + 1, size - 1, &length);
552   if (consumed == 0)
553     goto short_file;
554 
555   data += consumed + 1;
556   size -= consumed + 1;
557 
558   if (size < length)
559     goto short_file;
560 
561   GST_DEBUG_OBJECT (midiparse, "handle meta event type 0x%02x, length %u",
562       type, length);
563 
564   bytes = g_strndup ((const gchar *) data, length);
565 
566   switch (type) {
567     case 0x01:
568       GST_DEBUG_OBJECT (midiparse, "Text: %s", bytes);
569       break;
570     case 0x02:
571       GST_DEBUG_OBJECT (midiparse, "Copyright: %s", bytes);
572       break;
573     case 0x03:
574       GST_DEBUG_OBJECT (midiparse, "Track Name: %s", bytes);
575       break;
576     case 0x04:
577       GST_DEBUG_OBJECT (midiparse, "Instrument: %s", bytes);
578       break;
579     case 0x05:
580       GST_DEBUG_OBJECT (midiparse, "Lyric: %s", bytes);
581       break;
582     case 0x06:
583       GST_DEBUG_OBJECT (midiparse, "Marker: %s", bytes);
584       break;
585     case 0x07:
586       GST_DEBUG_OBJECT (midiparse, "Cue point: %s", bytes);
587       break;
588     case 0x08:
589       GST_DEBUG_OBJECT (midiparse, "Patch name: %s", bytes);
590       break;
591     case 0x09:
592       GST_DEBUG_OBJECT (midiparse, "MIDI port: %s", bytes);
593       break;
594     case 0x2f:
595       GST_DEBUG_OBJECT (midiparse, "End of track");
596       break;
597     case 0x51:
598     {
599       guint32 uspqn = (data[0] << 16) | (data[1] << 8) | data[2];
600       midiparse->tempo = (uspqn ? uspqn : DEFAULT_TEMPO);
601       GST_DEBUG_OBJECT (midiparse, "tempo %u", midiparse->tempo);
602       break;
603     }
604     case 0x54:
605       GST_DEBUG_OBJECT (midiparse, "SMPTE offset");
606       break;
607     case 0x58:
608       GST_DEBUG_OBJECT (midiparse, "Time signature");
609       break;
610     case 0x59:
611       GST_DEBUG_OBJECT (midiparse, "Key signature");
612       break;
613     case 0x7f:
614       GST_DEBUG_OBJECT (midiparse, "Proprietary event");
615       break;
616     default:
617       GST_DEBUG_OBJECT (midiparse, "unknown event 0x%02x length %d", type,
618           length);
619       break;
620   }
621   g_free (bytes);
622 
623   track->offset += consumed + length + 1;
624 
625   return GST_FLOW_OK;
626 
627   /* ERRORS */
628 short_file:
629   {
630     GST_DEBUG_OBJECT (midiparse, "not enough data");
631     return GST_FLOW_ERROR;
632   }
633 }
634 
635 static GstFlowReturn
handle_sysex_event(GstMidiParse * midiparse,GstMidiTrack * track,guint8 event,GstMidiPushFunc pushfunc,gpointer user_data)636 handle_sysex_event (GstMidiParse * midiparse, GstMidiTrack * track,
637     guint8 event, GstMidiPushFunc pushfunc, gpointer user_data)
638 {
639   GstFlowReturn ret;
640   guint8 *data;
641   guint size, consumed;
642   gint32 length;
643 
644   track->offset += 1;
645 
646   data = track->data + track->offset;
647   size = track->size - track->offset;
648 
649   consumed = parse_varlen (midiparse, data, size, &length);
650   if (consumed == 0)
651     goto short_file;
652 
653   data += consumed;
654   size -= consumed;
655 
656   if (size < length)
657     goto short_file;
658 
659   GST_DEBUG_OBJECT (midiparse, "handle sysex event 0x%02x, length %u",
660       event, length);
661 
662   if (pushfunc)
663     ret = pushfunc (midiparse, track, event, data, length, user_data);
664   else
665     ret = GST_FLOW_OK;
666 
667   track->offset += consumed + length;
668 
669   return ret;
670 
671   /* ERRORS */
672 short_file:
673   {
674     GST_DEBUG_OBJECT (midiparse, "not enough data");
675     return GST_FLOW_ERROR;
676   }
677 }
678 
679 
680 static guint8
event_from_status(GstMidiParse * midiparse,GstMidiTrack * track,guint8 status)681 event_from_status (GstMidiParse * midiparse, GstMidiTrack * track,
682     guint8 status)
683 {
684   if ((status & 0x80) == 0) {
685     if ((track->running_status & 0x80) == 0)
686       return 0;
687 
688     return track->running_status;
689   } else {
690     return status;
691   }
692 }
693 
694 static gboolean
update_track_position(GstMidiParse * midiparse,GstMidiTrack * track)695 update_track_position (GstMidiParse * midiparse, GstMidiTrack * track)
696 {
697   gint32 delta_time;
698   guint8 *data;
699   guint size, consumed;
700 
701   if (track->offset >= track->size)
702     goto eot;
703 
704   data = track->data + track->offset;
705   size = track->size - track->offset;
706 
707   consumed = parse_varlen (midiparse, data, size, &delta_time);
708   if (consumed == 0)
709     goto eot;
710 
711   track->pulse += delta_time;
712   track->offset += consumed;
713 
714   GST_LOG_OBJECT (midiparse, "updated track to pulse %" G_GUINT64_FORMAT,
715       track->pulse);
716 
717   return TRUE;
718 
719   /* ERRORS */
720 eot:
721   {
722     GST_DEBUG_OBJECT (midiparse, "track ended");
723     track->eot = TRUE;
724     return FALSE;
725   }
726 }
727 
728 static GstFlowReturn
handle_next_event(GstMidiParse * midiparse,GstMidiTrack * track,GstMidiPushFunc pushfunc,gpointer user_data)729 handle_next_event (GstMidiParse * midiparse, GstMidiTrack * track,
730     GstMidiPushFunc pushfunc, gpointer user_data)
731 {
732   GstFlowReturn ret = GST_FLOW_OK;
733   guint8 status, event;
734   guint length;
735   guint8 *data;
736 
737   data = &track->data[track->offset];
738 
739   status = data[0];
740   event = event_from_status (midiparse, track, status);
741 
742   GST_LOG_OBJECT (midiparse, "track %p, status 0x%02x, event 0x%02x", track,
743       status, event);
744 
745   switch (event & 0xf0) {
746     case 0xf0:
747       switch (event) {
748         case 0xff:
749           ret = handle_meta_event (midiparse, track, event);
750           break;
751         case 0xf0:
752         case 0xf7:
753           ret =
754               handle_sysex_event (midiparse, track, event, pushfunc, user_data);
755           break;
756         default:
757           goto unhandled_event;
758       }
759       length = 0;
760       break;
761     case 0xc0:
762     case 0xd0:
763       length = 1;
764       break;
765     case 0x80:
766     case 0x90:
767     case 0xa0:
768     case 0xb0:
769     case 0xe0:
770       length = 2;
771       break;
772     default:
773       goto undefined_status;
774   }
775   if (length > 0) {
776     if (status & 0x80) {
777       if (pushfunc)
778         ret = pushfunc (midiparse, track, event, data + 1, length, user_data);
779       track->offset += length + 1;
780     } else {
781       if (pushfunc)
782         ret = pushfunc (midiparse, track, event, data, length + 1, user_data);
783       track->offset += length;
784     }
785   }
786 
787   if (ret == GST_FLOW_OK) {
788     if (event < 0xF8)
789       track->running_status = event;
790 
791     update_track_position (midiparse, track);
792   }
793   return ret;
794 
795   /* ERRORS */
796 undefined_status:
797   {
798     GST_ERROR_OBJECT (midiparse, "Undefined status and invalid running status");
799     return GST_FLOW_ERROR;
800   }
801 unhandled_event:
802   {
803     /* we don't know the size so we can't continue parsing */
804     GST_ERROR_OBJECT (midiparse, "unhandled event 0x%08x", event);
805     return GST_FLOW_ERROR;
806   }
807 }
808 
809 static void
reset_track(GstMidiTrack * track,GstMidiParse * midiparse)810 reset_track (GstMidiTrack * track, GstMidiParse * midiparse)
811 {
812   GST_DEBUG_OBJECT (midiparse, "reset track");
813   track->offset = 0;
814   track->pulse = 0;
815   track->eot = FALSE;
816   track->running_status = 0xff;
817   update_track_position (midiparse, track);
818 }
819 
820 static gboolean
parse_MTrk(GstMidiParse * midiparse,guint8 * data,guint size)821 parse_MTrk (GstMidiParse * midiparse, guint8 * data, guint size)
822 {
823   GstMidiTrack *track;
824   GstClockTime duration;
825 
826   /* ignore excess tracks */
827   if (midiparse->track_count >= midiparse->ntracks)
828     return TRUE;
829 
830   track = g_slice_new (GstMidiTrack);
831   track->data = data;
832   track->size = size;
833   reset_track (track, midiparse);
834 
835   midiparse->tracks = g_list_append (midiparse->tracks, track);
836   midiparse->track_count++;
837 
838   /* now loop over all events and calculate the duration */
839   while (!track->eot) {
840     handle_next_event (midiparse, track, NULL, NULL);
841   }
842 
843   duration = gst_util_uint64_scale (track->pulse,
844       1000 * midiparse->tempo, midiparse->division);
845 
846   GST_DEBUG_OBJECT (midiparse, "duration %" GST_TIME_FORMAT,
847       GST_TIME_ARGS (duration));
848 
849   if (duration > midiparse->segment.duration)
850     midiparse->segment.duration = duration;
851 
852   reset_track (track, midiparse);
853 
854   return TRUE;
855 }
856 
857 static gboolean
find_midi_chunk(GstMidiParse * midiparse,guint8 * data,guint size,guint * offset,guint * length)858 find_midi_chunk (GstMidiParse * midiparse, guint8 * data, guint size,
859     guint * offset, guint * length)
860 {
861   guint32 type;
862 
863   *length = 0;
864 
865   if (size < 8)
866     goto short_chunk;
867 
868   type = GST_STR_FOURCC (data);
869 
870   if (type == GST_MAKE_FOURCC ('R', 'I', 'F', 'F')) {
871     guint32 riff_len;
872 
873     GST_DEBUG_OBJECT (midiparse, "found RIFF");
874 
875     if (size < 12)
876       goto short_chunk;
877 
878     if (GST_STR_FOURCC (data + 8) != GST_MAKE_FOURCC ('R', 'M', 'I', 'D'))
879       goto invalid_format;
880 
881     riff_len = GST_READ_UINT32_LE (data + 4);
882 
883     if (size < riff_len)
884       goto short_chunk;
885 
886     data += 12;
887     size -= 12;
888     *offset = 12;
889 
890     GST_DEBUG_OBJECT (midiparse, "found RIFF RMID of size %u", riff_len);
891 
892     while (TRUE) {
893       guint32 chunk_type;
894       guint32 chunk_len;
895 
896       if (riff_len < 8)
897         goto short_chunk;
898 
899       chunk_type = GST_STR_FOURCC (data);
900       chunk_len = GST_READ_UINT32_LE (data + 4);
901 
902       riff_len -= 8;
903       if (riff_len < chunk_len)
904         goto short_chunk;
905 
906       data += 8;
907       size -= 8;
908       *offset += 8;
909       riff_len -= chunk_len;
910 
911       if (chunk_type == GST_MAKE_FOURCC ('d', 'a', 't', 'a')) {
912         *length = chunk_len;
913         break;
914       }
915 
916       data += chunk_len;
917       size -= chunk_len;
918     }
919   } else {
920     *offset = 0;
921     *length = size;
922   }
923   return TRUE;
924 
925   /* ERRORS */
926 short_chunk:
927   {
928     GST_LOG_OBJECT (midiparse, "not enough data %u < %u", *length + 8, size);
929     return FALSE;
930   }
931 invalid_format:
932   {
933     GST_ERROR_OBJECT (midiparse, "invalid format");
934     return FALSE;
935   }
936 }
937 
938 static guint
gst_midi_parse_chunk(GstMidiParse * midiparse,guint8 * data,guint size)939 gst_midi_parse_chunk (GstMidiParse * midiparse, guint8 * data, guint size)
940 {
941   guint32 type, length = 0;
942 
943   if (size < 8)
944     goto short_chunk;
945 
946   length = GST_READ_UINT32_BE (data + 4);
947 
948   GST_DEBUG_OBJECT (midiparse, "have type %c%c%c%c, length %u",
949       data[0], data[1], data[2], data[3], length);
950 
951   if (size < length + 8)
952     goto short_chunk;
953 
954   type = GST_STR_FOURCC (data);
955 
956   switch (type) {
957     case GST_MAKE_FOURCC ('M', 'T', 'h', 'd'):
958       if (!parse_MThd (midiparse, data + 8, length))
959         goto invalid_format;
960       break;
961     case GST_MAKE_FOURCC ('M', 'T', 'r', 'k'):
962       if (!parse_MTrk (midiparse, data + 8, length))
963         goto invalid_format;
964       break;
965     default:
966       GST_LOG_OBJECT (midiparse, "ignore chunk");
967       break;
968   }
969 
970   return length + 8;
971 
972   /* ERRORS */
973 short_chunk:
974   {
975     GST_LOG_OBJECT (midiparse, "not enough data %u < %u", size, length + 8);
976     return 0;
977   }
978 invalid_format:
979   {
980     GST_ERROR_OBJECT (midiparse, "invalid format");
981     return 0;
982   }
983 }
984 
985 static GstFlowReturn
gst_midi_parse_parse_song(GstMidiParse * midiparse)986 gst_midi_parse_parse_song (GstMidiParse * midiparse)
987 {
988   GstCaps *outcaps;
989   guint8 *data;
990   guint size, offset, length;
991   GstEvent *event;
992   gchar *stream_id;
993 
994   GST_DEBUG_OBJECT (midiparse, "Parsing song");
995 
996   gst_segment_init (&midiparse->segment, GST_FORMAT_TIME);
997   midiparse->segment.duration = 0;
998   midiparse->pulse = 0;
999 
1000   size = gst_adapter_available (midiparse->adapter);
1001   data = gst_adapter_take (midiparse->adapter, size);
1002 
1003   midiparse->data = data;
1004   midiparse->tempo = DEFAULT_TEMPO;
1005 
1006   if (!find_midi_chunk (midiparse, data, size, &offset, &length))
1007     goto invalid_format;
1008 
1009   while (length) {
1010     guint consumed;
1011 
1012     consumed = gst_midi_parse_chunk (midiparse, &data[offset], length);
1013     if (consumed == 0)
1014       goto short_file;
1015 
1016     offset += consumed;
1017     length -= consumed;
1018   }
1019 
1020   GST_DEBUG_OBJECT (midiparse, "song duration %" GST_TIME_FORMAT,
1021       GST_TIME_ARGS (midiparse->segment.duration));
1022 
1023   stream_id =
1024       gst_pad_create_stream_id (midiparse->srcpad, GST_ELEMENT_CAST (midiparse),
1025       NULL);
1026   event =
1027       gst_pad_get_sticky_event (midiparse->sinkpad, GST_EVENT_STREAM_START, 0);
1028   if (event) {
1029     if (gst_event_parse_group_id (event, &midiparse->group_id))
1030       midiparse->have_group_id = TRUE;
1031     else
1032       midiparse->have_group_id = FALSE;
1033     gst_event_unref (event);
1034   } else if (!midiparse->have_group_id) {
1035     midiparse->have_group_id = TRUE;
1036     midiparse->group_id = gst_util_group_id_next ();
1037   }
1038   event = gst_event_new_stream_start (stream_id);
1039   if (midiparse->have_group_id)
1040     gst_event_set_group_id (event, midiparse->group_id);
1041   gst_pad_push_event (midiparse->srcpad, event);
1042   g_free (stream_id);
1043 
1044   outcaps = gst_pad_get_pad_template_caps (midiparse->srcpad);
1045   gst_pad_set_caps (midiparse->srcpad, outcaps);
1046   gst_caps_unref (outcaps);
1047 
1048   midiparse->segment_pending = TRUE;
1049   midiparse->discont = TRUE;
1050 
1051   GST_DEBUG_OBJECT (midiparse, "Parsing song done");
1052 
1053   return GST_FLOW_OK;
1054 
1055   /* ERRORS */
1056 short_file:
1057   {
1058     GST_ERROR_OBJECT (midiparse, "not enough data");
1059     return GST_FLOW_ERROR;
1060   }
1061 invalid_format:
1062   {
1063     GST_ERROR_OBJECT (midiparse, "invalid format");
1064     return GST_FLOW_ERROR;
1065   }
1066 }
1067 
1068 static GstFlowReturn
play_push_func(GstMidiParse * midiparse,GstMidiTrack * track,guint8 event,guint8 * data,guint length,gpointer user_data)1069 play_push_func (GstMidiParse * midiparse, GstMidiTrack * track,
1070     guint8 event, guint8 * data, guint length, gpointer user_data)
1071 {
1072   GstBuffer *outbuf;
1073   GstMapInfo info;
1074   GstClockTime position;
1075 
1076   outbuf = gst_buffer_new_allocate (NULL, length + 1, NULL);
1077 
1078   gst_buffer_map (outbuf, &info, GST_MAP_WRITE);
1079   info.data[0] = event;
1080   if (length)
1081     memcpy (&info.data[1], data, length);
1082   gst_buffer_unmap (outbuf, &info);
1083 
1084   position = midiparse->segment.position;
1085   GST_BUFFER_PTS (outbuf) = position;
1086   GST_BUFFER_DTS (outbuf) = position;
1087 
1088   GST_DEBUG_OBJECT (midiparse, "pushing %" GST_TIME_FORMAT,
1089       GST_TIME_ARGS (position));
1090 
1091   if (midiparse->discont) {
1092     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
1093     midiparse->discont = FALSE;
1094   }
1095 
1096   return gst_pad_push (midiparse->srcpad, outbuf);
1097 }
1098 
1099 static GstFlowReturn
gst_midi_parse_do_play(GstMidiParse * midiparse)1100 gst_midi_parse_do_play (GstMidiParse * midiparse)
1101 {
1102   GstFlowReturn res;
1103   GList *walk;
1104   guint64 pulse, next_pulse = G_MAXUINT64;
1105   GstClockTime position, next_position;
1106   guint64 tick;
1107 
1108   pulse = midiparse->pulse;
1109   position = midiparse->segment.position;
1110 
1111   if (midiparse->segment_pending) {
1112     gst_pad_push_event (midiparse->srcpad,
1113         gst_event_new_segment (&midiparse->segment));
1114     midiparse->segment_pending = FALSE;
1115   }
1116 
1117   GST_DEBUG_OBJECT (midiparse, "pulse %" G_GUINT64_FORMAT ", position %"
1118       GST_TIME_FORMAT, pulse, GST_TIME_ARGS (position));
1119 
1120   for (walk = midiparse->tracks; walk; walk = g_list_next (walk)) {
1121     GstMidiTrack *track = walk->data;
1122 
1123     while (!track->eot && track->pulse == pulse) {
1124       res = handle_next_event (midiparse, track, play_push_func, NULL);
1125       if (res != GST_FLOW_OK)
1126         goto error;
1127     }
1128 
1129     if (!track->eot && track->pulse < next_pulse)
1130       next_pulse = track->pulse;
1131   }
1132 
1133   if (next_pulse == G_MAXUINT64)
1134     goto eos;
1135 
1136   tick = position / (10 * GST_MSECOND);
1137   GST_DEBUG_OBJECT (midiparse, "current tick %" G_GUINT64_FORMAT, tick);
1138 
1139   next_position = gst_util_uint64_scale (next_pulse,
1140       1000 * midiparse->tempo, midiparse->division);
1141   GST_DEBUG_OBJECT (midiparse, "next position %" GST_TIME_FORMAT,
1142       GST_TIME_ARGS (next_position));
1143 
1144   /* send 10ms ticks to advance the downstream element */
1145   while (TRUE) {
1146     /* get position of next tick */
1147     position = ++tick * (10 * GST_MSECOND);
1148     GST_DEBUG_OBJECT (midiparse, "tick %" G_GUINT64_FORMAT
1149         ", position %" GST_TIME_FORMAT, tick, GST_TIME_ARGS (position));
1150 
1151     if (position >= next_position)
1152       break;
1153 
1154     midiparse->segment.position = position;
1155     res = play_push_func (midiparse, NULL, 0xf9, NULL, 0, NULL);
1156     if (res != GST_FLOW_OK)
1157       goto error;
1158   }
1159 
1160   midiparse->pulse = next_pulse;
1161   midiparse->segment.position = next_position;
1162 
1163   return GST_FLOW_OK;
1164 
1165   /* ERRORS */
1166 eos:
1167   {
1168     GST_DEBUG_OBJECT (midiparse, "we are EOS");
1169     return GST_FLOW_EOS;
1170   }
1171 error:
1172   {
1173     GST_DEBUG_OBJECT (midiparse, "have flow result %s",
1174         gst_flow_get_name (res));
1175     return res;
1176   }
1177 }
1178 
1179 static gboolean
gst_midi_parse_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)1180 gst_midi_parse_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
1181 {
1182   gboolean res;
1183   GstMidiParse *midiparse = GST_MIDI_PARSE (parent);
1184 
1185   GST_DEBUG_OBJECT (pad, "%s event received", GST_EVENT_TYPE_NAME (event));
1186 
1187   switch (GST_EVENT_TYPE (event)) {
1188     case GST_EVENT_EOS:
1189       midiparse->state = GST_MIDI_PARSE_STATE_PARSE;
1190       /* now start the parsing task */
1191       res = gst_pad_start_task (midiparse->sinkpad,
1192           (GstTaskFunction) gst_midi_parse_loop, midiparse->sinkpad, NULL);
1193       /* don't forward the event */
1194       gst_event_unref (event);
1195       break;
1196     case GST_EVENT_CAPS:
1197     case GST_EVENT_STREAM_START:
1198     case GST_EVENT_SEGMENT:
1199       res = TRUE;
1200       gst_event_unref (event);
1201       break;
1202     default:
1203       res = gst_pad_event_default (pad, parent, event);
1204       break;
1205   }
1206   return res;
1207 }
1208 
1209 static GstFlowReturn
gst_midi_parse_chain(GstPad * sinkpad,GstObject * parent,GstBuffer * buffer)1210 gst_midi_parse_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * buffer)
1211 {
1212   GstMidiParse *midiparse;
1213 
1214   midiparse = GST_MIDI_PARSE (parent);
1215 
1216   /* push stuff in the adapter, we will start doing something in the sink event
1217    * handler when we get EOS */
1218   gst_adapter_push (midiparse->adapter, buffer);
1219 
1220   return GST_FLOW_OK;
1221 }
1222 
1223 static void
gst_midi_parse_loop(GstPad * sinkpad)1224 gst_midi_parse_loop (GstPad * sinkpad)
1225 {
1226   GstMidiParse *midiparse = GST_MIDI_PARSE (GST_PAD_PARENT (sinkpad));
1227   GstFlowReturn ret;
1228 
1229   switch (midiparse->state) {
1230     case GST_MIDI_PARSE_STATE_LOAD:
1231     {
1232       GstBuffer *buffer = NULL;
1233 
1234       GST_DEBUG_OBJECT (midiparse, "loading song");
1235 
1236       ret =
1237           gst_pad_pull_range (midiparse->sinkpad, midiparse->offset, -1,
1238           &buffer);
1239 
1240       if (ret == GST_FLOW_EOS) {
1241         GST_DEBUG_OBJECT (midiparse, "Song loaded");
1242         midiparse->state = GST_MIDI_PARSE_STATE_PARSE;
1243       } else if (ret != GST_FLOW_OK) {
1244         GST_ELEMENT_ERROR (midiparse, STREAM, DECODE, (NULL),
1245             ("Unable to read song"));
1246         goto pause;
1247       } else {
1248         GST_DEBUG_OBJECT (midiparse, "pushing buffer");
1249         gst_adapter_push (midiparse->adapter, buffer);
1250         midiparse->offset += gst_buffer_get_size (buffer);
1251       }
1252       break;
1253     }
1254     case GST_MIDI_PARSE_STATE_PARSE:
1255       ret = gst_midi_parse_parse_song (midiparse);
1256       if (ret != GST_FLOW_OK)
1257         goto pause;
1258       midiparse->state = GST_MIDI_PARSE_STATE_PLAY;
1259       break;
1260     case GST_MIDI_PARSE_STATE_PLAY:
1261       ret = gst_midi_parse_do_play (midiparse);
1262       if (ret != GST_FLOW_OK)
1263         goto pause;
1264       break;
1265     default:
1266       break;
1267   }
1268   return;
1269 
1270 pause:
1271   {
1272     const gchar *reason = gst_flow_get_name (ret);
1273     GstEvent *event;
1274 
1275     GST_DEBUG_OBJECT (midiparse, "pausing task, reason %s", reason);
1276     gst_pad_pause_task (sinkpad);
1277     if (ret == GST_FLOW_EOS) {
1278       /* perform EOS logic */
1279       event = gst_event_new_eos ();
1280       gst_pad_push_event (midiparse->srcpad, event);
1281     } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
1282       event = gst_event_new_eos ();
1283       /* for fatal errors we post an error message, post the error
1284        * first so the app knows about the error first. */
1285       GST_ELEMENT_FLOW_ERROR (midiparse, ret);
1286       gst_pad_push_event (midiparse->srcpad, event);
1287     }
1288   }
1289 }
1290 
1291 static void
free_track(GstMidiTrack * track,GstMidiParse * midiparse)1292 free_track (GstMidiTrack * track, GstMidiParse * midiparse)
1293 {
1294   g_slice_free (GstMidiTrack, track);
1295 }
1296 
1297 static void
gst_midi_parse_reset(GstMidiParse * midiparse)1298 gst_midi_parse_reset (GstMidiParse * midiparse)
1299 {
1300   gst_adapter_clear (midiparse->adapter);
1301   g_free (midiparse->data);
1302   midiparse->data = NULL;
1303   g_list_foreach (midiparse->tracks, (GFunc) free_track, midiparse);
1304   g_list_free (midiparse->tracks);
1305   midiparse->tracks = NULL;
1306   midiparse->track_count = 0;
1307   midiparse->have_group_id = FALSE;
1308   midiparse->group_id = G_MAXUINT;
1309 }
1310 
1311 static GstStateChangeReturn
gst_midi_parse_change_state(GstElement * element,GstStateChange transition)1312 gst_midi_parse_change_state (GstElement * element, GstStateChange transition)
1313 {
1314   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1315   GstMidiParse *midiparse = GST_MIDI_PARSE (element);
1316 
1317   switch (transition) {
1318     case GST_STATE_CHANGE_NULL_TO_READY:
1319       break;
1320     case GST_STATE_CHANGE_READY_TO_PAUSED:
1321       midiparse->offset = 0;
1322       midiparse->state = GST_MIDI_PARSE_STATE_LOAD;
1323       break;
1324     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1325       break;
1326     default:
1327       break;
1328   }
1329 
1330   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1331 
1332   switch (transition) {
1333     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1334       break;
1335     case GST_STATE_CHANGE_PAUSED_TO_READY:
1336       gst_midi_parse_reset (midiparse);
1337       break;
1338     case GST_STATE_CHANGE_READY_TO_NULL:
1339       break;
1340     default:
1341       break;
1342   }
1343 
1344   return ret;
1345 }
1346 
1347 static void
gst_midi_parse_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1348 gst_midi_parse_set_property (GObject * object, guint prop_id,
1349     const GValue * value, GParamSpec * pspec)
1350 {
1351   switch (prop_id) {
1352     default:
1353       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1354       break;
1355   }
1356 }
1357 
1358 static void
gst_midi_parse_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1359 gst_midi_parse_get_property (GObject * object, guint prop_id,
1360     GValue * value, GParamSpec * pspec)
1361 {
1362   switch (prop_id) {
1363     default:
1364       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1365       break;
1366   }
1367 }
1368