• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2016> Carlos Rafael Giani <dv at pseudoterminal dot org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 
21 /**
22  * SECTION:element-wildmididec
23  * @see_also: #GstWildmidiDec
24  *
25  * wildmididec decodes MIDI files.
26  *
27  * It uses [WildMidi](https://www.mindwerks.net/projects/wildmidi/) for this
28  * purpose. It can be autoplugged and therefore works with decodebin.
29  *
30  * ## Example launch line
31  *
32  * |[
33  * gst-launch-1.0 filesrc location=media/example.mid ! wildmididec ! audioconvert ! audioresample ! autoaudiosink
34  * ]|
35  */
36 
37 
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41 
42 #ifdef HAVE_STDINT_H
43 #include <stdint.h>
44 #endif
45 
46 #include <gst/gst.h>
47 #include <glib/gstdio.h>
48 
49 #ifdef G_OS_WIN32
50 
51 #ifndef R_OK
52 #define R_OK 4                  /* Test for read permission */
53 #endif
54 
55 #else
56 #include <unistd.h>
57 #endif
58 
59 #include "gstwildmididec.h"
60 
61 
62 GST_DEBUG_CATEGORY_STATIC (wildmididec_debug);
63 #define GST_CAT_DEFAULT wildmididec_debug
64 
65 
66 /* This is hardcoded because the sample rate is set once,
67  * globally, in WildMidi_Init() */
68 #define WILDMIDI_SAMPLE_RATE 44100
69 /* WildMidi always outputs stereo data */
70 #define WILDMIDI_NUM_CHANNELS 2
71 
72 #ifndef WILDMIDI_CFG
73 #define WILDMIDI_CFG "/etc/timidity.cfg"
74 #endif
75 
76 #define DEFAULT_LOG_VOLUME_SCALE     TRUE
77 #define DEFAULT_ENHANCED_RESAMPLING  TRUE
78 #define DEFAULT_REVERB               FALSE
79 #define DEFAULT_OUTPUT_BUFFER_SIZE   1024
80 
81 
82 enum
83 {
84   PROP_0,
85   PROP_LOG_VOLUME_SCALE,
86   PROP_ENHANCED_RESAMPLING,
87   PROP_REVERB,
88   PROP_OUTPUT_BUFFER_SIZE
89 };
90 
91 
92 
93 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
94     GST_PAD_SINK,
95     GST_PAD_ALWAYS,
96     GST_STATIC_CAPS ("audio/midi; audio/riff-midi")
97     );
98 
99 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
100     GST_PAD_SRC,
101     GST_PAD_ALWAYS,
102     GST_STATIC_CAPS ("audio/x-raw, "
103         "format = (string) " GST_AUDIO_NE (S16) ", "
104         "layout = (string) interleaved, "
105         "rate = (int) " G_STRINGIFY (WILDMIDI_SAMPLE_RATE) ", "
106         "channels = (int) " G_STRINGIFY (WILDMIDI_NUM_CHANNELS)
107     )
108     );
109 
110 
111 
112 G_DEFINE_TYPE (GstWildmidiDec, gst_wildmidi_dec,
113     GST_TYPE_NONSTREAM_AUDIO_DECODER);
114 GST_ELEMENT_REGISTER_DEFINE (wildmididec, "wildmididec", GST_RANK_MARGINAL,
115     gst_wildmidi_dec_get_type ());
116 
117 
118 static void gst_wildmidi_dec_finalize (GObject * object);
119 
120 static void gst_wildmidi_dec_set_property (GObject * object, guint prop_id,
121     const GValue * value, GParamSpec * pspec);
122 static void gst_wildmidi_dec_get_property (GObject * object, guint prop_id,
123     GValue * value, GParamSpec * pspec);
124 
125 static gboolean gst_wildmidi_dec_seek (GstNonstreamAudioDecoder * dec,
126     GstClockTime * new_position);
127 static GstClockTime gst_wildmidi_dec_tell (GstNonstreamAudioDecoder * dec);
128 
129 static gboolean gst_wildmidi_dec_load_from_buffer (GstNonstreamAudioDecoder *
130     dec, GstBuffer * source_data, guint initial_subsong,
131     GstNonstreamAudioSubsongMode initial_subsong_mode,
132     GstClockTime * initial_position,
133     GstNonstreamAudioOutputMode * initial_output_mode,
134     gint * initial_num_loops);
135 
136 static guint gst_wildmidi_dec_get_current_subsong (GstNonstreamAudioDecoder *
137     dec);
138 
139 static guint gst_wildmidi_dec_get_num_subsongs (GstNonstreamAudioDecoder * dec);
140 static GstClockTime
141 gst_wildmidi_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
142     guint subsong);
143 
144 static guint
145 gst_wildmidi_dec_get_supported_output_modes (GstNonstreamAudioDecoder * dec);
146 static gboolean gst_wildmidi_dec_decode (GstNonstreamAudioDecoder * dec,
147     GstBuffer ** buffer, guint * num_samples);
148 
149 static void gst_wildmidi_dec_update_options (GstWildmidiDec * wildmidi_dec);
150 
151 
152 
153 static GMutex load_mutex;
154 static unsigned long init_refcount = 0;
155 static gint wildmidi_initialized = 0;
156 
157 
158 static gchar *
gst_wildmidi_get_config_path(void)159 gst_wildmidi_get_config_path (void)
160 {
161   /* This code is adapted from the original wildmidi
162    * gst-plugins-bad decoder element */
163 
164   gchar *path = g_strdup (g_getenv ("WILDMIDI_CFG"));
165 
166   GST_DEBUG
167       ("trying configuration path \"%s\" from WILDMIDI_CFG environment variable",
168       GST_STR_NULL (path));
169   if (path && (g_access (path, R_OK) == -1)) {
170     g_free (path);
171     path = NULL;
172   }
173 
174   if (path == NULL) {
175     path =
176         g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".wildmidirc",
177         NULL);
178     GST_DEBUG ("trying configuration path \"%s\"", path);
179     if (path && (g_access (path, R_OK) == -1)) {
180       g_free (path);
181       path = NULL;
182     }
183   }
184 
185   if (path == NULL) {
186     path =
187         g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc",
188         "wildmidi.cfg", NULL);
189     GST_DEBUG ("trying configuration path \"%s\"", path);
190     if (path && (g_access (path, R_OK) == -1)) {
191       g_free (path);
192       path = NULL;
193     }
194   }
195 
196   if (path == NULL) {
197     path =
198         g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc", "wildmidi",
199         "wildmidi.cfg", NULL);
200     GST_DEBUG ("trying configuration path \"%s\"", path);
201     if (path && (g_access (path, R_OK) == -1)) {
202       g_free (path);
203       path = NULL;
204     }
205   }
206 
207   if (path == NULL) {
208     path = g_strdup (WILDMIDI_CFG);
209     GST_DEBUG ("trying default configuration path \"%s\"", path);
210     if (path && (g_access (path, R_OK) == -1)) {
211       g_free (path);
212       path = NULL;
213     }
214   }
215 
216   if (path == NULL) {
217     path =
218         g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc",
219         "timidity.cfg", NULL);
220     GST_DEBUG ("trying TiMidity configuration path \"%s\"", path);
221     if (path && (g_access (path, R_OK) == -1)) {
222       g_free (path);
223       path = NULL;
224     }
225   }
226 
227   if (path == NULL) {
228     path =
229         g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc", "timidity",
230         "timidity.cfg", NULL);
231     GST_DEBUG ("trying TiMidity configuration path \"%s\"", path);
232     if (path && (g_access (path, R_OK) == -1)) {
233       g_free (path);
234       path = NULL;
235     }
236   }
237 
238   return path;
239 }
240 
241 
242 static void
gst_wildmidi_init_library(void)243 gst_wildmidi_init_library (void)
244 {
245   GST_DEBUG ("WildMidi init instance counter: %lu", init_refcount);
246 
247   g_mutex_lock (&load_mutex);
248 
249   if (init_refcount != 0) {
250     ++init_refcount;
251   } else {
252     gchar *config_path = gst_wildmidi_get_config_path ();
253     if (config_path != NULL) {
254       int ret = WildMidi_Init (config_path, WILDMIDI_SAMPLE_RATE, 0);
255       g_free (config_path);
256 
257       if (ret == 0) {
258         GST_DEBUG ("WildMidi initialized, version string: %s",
259             WildMidi_GetString (WM_GS_VERSION));
260         ++init_refcount;
261         g_atomic_int_set (&wildmidi_initialized, 1);
262       } else {
263         GST_ERROR ("initializing WildMidi failed");
264         g_atomic_int_set (&wildmidi_initialized, 0);
265       }
266     } else {
267       GST_ERROR ("no config file, can't initialise");
268       g_atomic_int_set (&wildmidi_initialized, 0);
269     }
270   }
271 
272   g_mutex_unlock (&load_mutex);
273 }
274 
275 
276 static void
gst_wildmidi_shutdown_library(void)277 gst_wildmidi_shutdown_library (void)
278 {
279   GST_DEBUG ("WildMidi init instance counter: %lu", init_refcount);
280 
281   g_mutex_lock (&load_mutex);
282 
283   if (init_refcount != 0) {
284     --init_refcount;
285     if (init_refcount == 0) {
286       WildMidi_Shutdown ();
287       GST_DEBUG ("WildMidi shut down");
288       g_atomic_int_set (&wildmidi_initialized, 0);
289     }
290   }
291 
292   g_mutex_unlock (&load_mutex);
293 }
294 
295 
296 
297 void
gst_wildmidi_dec_class_init(GstWildmidiDecClass * klass)298 gst_wildmidi_dec_class_init (GstWildmidiDecClass * klass)
299 {
300   GObjectClass *object_class;
301   GstElementClass *element_class;
302   GstNonstreamAudioDecoderClass *dec_class;
303 
304   GST_DEBUG_CATEGORY_INIT (wildmididec_debug, "wildmididec", 0,
305       "WildMidi-based MIDI music decoder");
306 
307   object_class = G_OBJECT_CLASS (klass);
308   element_class = GST_ELEMENT_CLASS (klass);
309   dec_class = GST_NONSTREAM_AUDIO_DECODER_CLASS (klass);
310 
311   gst_element_class_add_pad_template (element_class,
312       gst_static_pad_template_get (&sink_template));
313   gst_element_class_add_pad_template (element_class,
314       gst_static_pad_template_get (&src_template));
315 
316   object_class->finalize = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_finalize);
317   object_class->set_property =
318       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_set_property);
319   object_class->get_property =
320       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_property);
321 
322   dec_class->tell = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_tell);
323   dec_class->seek = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_seek);
324   dec_class->load_from_buffer =
325       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_load_from_buffer);
326   dec_class->get_current_subsong =
327       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_current_subsong);
328   dec_class->get_num_subsongs =
329       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_num_subsongs);
330   dec_class->get_subsong_duration =
331       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_subsong_duration);
332   dec_class->get_supported_output_modes =
333       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_supported_output_modes);
334   dec_class->decode = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_decode);
335 
336   gst_element_class_set_static_metadata (element_class,
337       "WildMidi-based MIDI music decoder",
338       "Codec/Decoder/Audio",
339       "Decodes MIDI music using WildMidi",
340       "Carlos Rafael Giani <dv@pseudoterminal.org>");
341 
342   g_object_class_install_property (object_class,
343       PROP_LOG_VOLUME_SCALE,
344       g_param_spec_boolean ("log-volume-scale",
345           "Logarithmic volume scale",
346           "Use a logarithmic volume scale if set to TRUE, or a linear scale if set to FALSE",
347           DEFAULT_LOG_VOLUME_SCALE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
348       );
349   g_object_class_install_property (object_class,
350       PROP_ENHANCED_RESAMPLING,
351       g_param_spec_boolean ("enhanced-resampling",
352           "Enhanced resampling",
353           "Use enhanced resampling if set to TRUE, or linear interpolation if set to FALSE",
354           DEFAULT_ENHANCED_RESAMPLING,
355           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
356       );
357   g_object_class_install_property (object_class,
358       PROP_REVERB,
359       g_param_spec_boolean ("reverb",
360           "Reverb",
361           "Whether or not to enable the WildMidi 8 reflection reverb engine to add more depth to the sound",
362           DEFAULT_REVERB, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
363       );
364   /* 2*2 => stereo output with S16 samples; the division ensures that no overflow can happen */
365   g_object_class_install_property (object_class,
366       PROP_OUTPUT_BUFFER_SIZE,
367       g_param_spec_uint ("output-buffer-size",
368           "Output buffer size",
369           "Size of each output buffer, in samples (actual size can be smaller than this during flush or EOS)",
370           1, G_MAXUINT / (2 * 2),
371           DEFAULT_OUTPUT_BUFFER_SIZE,
372           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
373       );
374 }
375 
376 
377 void
gst_wildmidi_dec_init(GstWildmidiDec * wildmidi_dec)378 gst_wildmidi_dec_init (GstWildmidiDec * wildmidi_dec)
379 {
380   wildmidi_dec->song = NULL;
381 
382   wildmidi_dec->log_volume_scale = DEFAULT_LOG_VOLUME_SCALE;
383   wildmidi_dec->enhanced_resampling = DEFAULT_ENHANCED_RESAMPLING;
384   wildmidi_dec->reverb = DEFAULT_REVERB;
385   wildmidi_dec->output_buffer_size = DEFAULT_OUTPUT_BUFFER_SIZE;
386 
387   gst_wildmidi_init_library ();
388 }
389 
390 
391 static void
gst_wildmidi_dec_finalize(GObject * object)392 gst_wildmidi_dec_finalize (GObject * object)
393 {
394   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (object);
395 
396   if (wildmidi_dec->song != NULL)
397     WildMidi_Close (wildmidi_dec->song);
398 
399   gst_wildmidi_shutdown_library ();
400 
401   G_OBJECT_CLASS (gst_wildmidi_dec_parent_class)->finalize (object);
402 }
403 
404 
405 static void
gst_wildmidi_dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)406 gst_wildmidi_dec_set_property (GObject * object, guint prop_id,
407     const GValue * value, GParamSpec * pspec)
408 {
409   GstWildmidiDec *wildmidi_dec;
410 
411   wildmidi_dec = GST_WILDMIDI_DEC (object);
412 
413   switch (prop_id) {
414     case PROP_LOG_VOLUME_SCALE:
415       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
416       wildmidi_dec->log_volume_scale = g_value_get_boolean (value);
417       gst_wildmidi_dec_update_options (wildmidi_dec);
418       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
419       break;
420 
421     case PROP_ENHANCED_RESAMPLING:
422       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
423       wildmidi_dec->enhanced_resampling = g_value_get_boolean (value);
424       gst_wildmidi_dec_update_options (wildmidi_dec);
425       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
426       break;
427 
428     case PROP_REVERB:
429       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
430       wildmidi_dec->reverb = g_value_get_boolean (value);
431       gst_wildmidi_dec_update_options (wildmidi_dec);
432       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
433       break;
434 
435     case PROP_OUTPUT_BUFFER_SIZE:
436       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
437       wildmidi_dec->output_buffer_size = g_value_get_uint (value);
438       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
439       break;
440 
441     default:
442       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
443       break;
444   }
445 }
446 
447 
448 static void
gst_wildmidi_dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)449 gst_wildmidi_dec_get_property (GObject * object, guint prop_id, GValue * value,
450     GParamSpec * pspec)
451 {
452   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (object);
453 
454   switch (prop_id) {
455     case PROP_LOG_VOLUME_SCALE:
456       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
457       g_value_set_boolean (value, wildmidi_dec->log_volume_scale);
458       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
459       break;
460 
461     case PROP_ENHANCED_RESAMPLING:
462       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
463       g_value_set_boolean (value, wildmidi_dec->enhanced_resampling);
464       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
465       break;
466 
467     case PROP_REVERB:
468       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
469       g_value_set_boolean (value, wildmidi_dec->reverb);
470       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
471       break;
472 
473     case PROP_OUTPUT_BUFFER_SIZE:
474       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
475       g_value_set_uint (value, wildmidi_dec->output_buffer_size);
476       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
477       break;
478 
479     default:
480       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
481       break;
482   }
483 }
484 
485 
486 static gboolean
gst_wildmidi_dec_seek(GstNonstreamAudioDecoder * dec,GstClockTime * new_position)487 gst_wildmidi_dec_seek (GstNonstreamAudioDecoder * dec,
488     GstClockTime * new_position)
489 {
490   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
491   unsigned long int sample_pos =
492       gst_util_uint64_scale_int (*new_position, WILDMIDI_SAMPLE_RATE,
493       GST_SECOND);
494 
495   if (G_UNLIKELY (wildmidi_dec->song == NULL))
496     return FALSE;
497 
498   WildMidi_FastSeek (wildmidi_dec->song, &sample_pos);
499 
500   *new_position =
501       gst_util_uint64_scale_int (sample_pos, GST_SECOND, WILDMIDI_SAMPLE_RATE);
502   return TRUE;
503 }
504 
505 
506 static GstClockTime
gst_wildmidi_dec_tell(GstNonstreamAudioDecoder * dec)507 gst_wildmidi_dec_tell (GstNonstreamAudioDecoder * dec)
508 {
509   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
510   struct _WM_Info *info;
511 
512   if (G_UNLIKELY (wildmidi_dec->song == NULL))
513     return GST_CLOCK_TIME_NONE;
514 
515   info = WildMidi_GetInfo (wildmidi_dec->song);
516   return gst_util_uint64_scale_int (info->current_sample, GST_SECOND,
517       WILDMIDI_SAMPLE_RATE);
518 }
519 
520 
521 static gboolean
gst_wildmidi_dec_load_from_buffer(GstNonstreamAudioDecoder * dec,GstBuffer * source_data,G_GNUC_UNUSED guint initial_subsong,G_GNUC_UNUSED GstNonstreamAudioSubsongMode initial_subsong_mode,GstClockTime * initial_position,GstNonstreamAudioOutputMode * initial_output_mode,G_GNUC_UNUSED gint * initial_num_loops)522 gst_wildmidi_dec_load_from_buffer (GstNonstreamAudioDecoder * dec,
523     GstBuffer * source_data, G_GNUC_UNUSED guint initial_subsong,
524     G_GNUC_UNUSED GstNonstreamAudioSubsongMode initial_subsong_mode,
525     GstClockTime * initial_position,
526     GstNonstreamAudioOutputMode * initial_output_mode,
527     G_GNUC_UNUSED gint * initial_num_loops)
528 {
529   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
530   GstMapInfo buffer_map;
531 
532 
533   if (g_atomic_int_get (&wildmidi_initialized) == 0) {
534     GST_ERROR_OBJECT (wildmidi_dec,
535         "Could not start loading: WildMidi is not initialized");
536     return FALSE;
537   }
538 
539 
540   /* Set output format */
541   if (!gst_nonstream_audio_decoder_set_output_format_simple (dec,
542           WILDMIDI_SAMPLE_RATE, GST_AUDIO_FORMAT_S16, WILDMIDI_NUM_CHANNELS))
543     return FALSE;
544 
545 
546   /* Load MIDI */
547   gst_buffer_map (source_data, &buffer_map, GST_MAP_READ);
548   wildmidi_dec->song = WildMidi_OpenBuffer (buffer_map.data, buffer_map.size);
549   gst_buffer_unmap (source_data, &buffer_map);
550 
551   if (wildmidi_dec->song == NULL) {
552     GST_ERROR_OBJECT (wildmidi_dec, "Could not load MIDI tune");
553     return FALSE;
554   }
555 
556   gst_wildmidi_dec_update_options (wildmidi_dec);
557 
558 
559   /* Seek to initial position */
560   if (*initial_position != 0) {
561     unsigned long int sample_pos =
562         gst_util_uint64_scale_int (*initial_position, WILDMIDI_SAMPLE_RATE,
563         GST_SECOND);
564     WildMidi_FastSeek (wildmidi_dec->song, &sample_pos);
565     *initial_position =
566         gst_util_uint64_scale_int (sample_pos, GST_SECOND,
567         WILDMIDI_SAMPLE_RATE);
568   }
569 
570 
571   /* LOOPING output mode is not supported */
572   *initial_output_mode = GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
573 
574 
575   return TRUE;
576 }
577 
578 
579 static guint
gst_wildmidi_dec_get_current_subsong(G_GNUC_UNUSED GstNonstreamAudioDecoder * dec)580 gst_wildmidi_dec_get_current_subsong (G_GNUC_UNUSED GstNonstreamAudioDecoder *
581     dec)
582 {
583   return 0;
584 }
585 
586 
587 static guint
gst_wildmidi_dec_get_num_subsongs(G_GNUC_UNUSED GstNonstreamAudioDecoder * dec)588 gst_wildmidi_dec_get_num_subsongs (G_GNUC_UNUSED GstNonstreamAudioDecoder * dec)
589 {
590   return 1;
591 }
592 
593 
594 static GstClockTime
gst_wildmidi_dec_get_subsong_duration(GstNonstreamAudioDecoder * dec,G_GNUC_UNUSED guint subsong)595 gst_wildmidi_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
596     G_GNUC_UNUSED guint subsong)
597 {
598   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
599   struct _WM_Info *info;
600 
601   if (G_UNLIKELY (wildmidi_dec->song == NULL))
602     return GST_CLOCK_TIME_NONE;
603 
604   info = WildMidi_GetInfo (wildmidi_dec->song);
605   return gst_util_uint64_scale_int (info->approx_total_samples, GST_SECOND,
606       WILDMIDI_SAMPLE_RATE);
607 }
608 
609 
610 static guint
gst_wildmidi_dec_get_supported_output_modes(G_GNUC_UNUSED GstNonstreamAudioDecoder * dec)611 gst_wildmidi_dec_get_supported_output_modes (G_GNUC_UNUSED
612     GstNonstreamAudioDecoder * dec)
613 {
614   return 1u << GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
615 }
616 
617 
618 static gboolean
gst_wildmidi_dec_decode(GstNonstreamAudioDecoder * dec,GstBuffer ** buffer,guint * num_samples)619 gst_wildmidi_dec_decode (GstNonstreamAudioDecoder * dec, GstBuffer ** buffer,
620     guint * num_samples)
621 {
622   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
623   GstMapInfo info;
624   GstBuffer *outbuf;
625   gsize outbuf_size;
626   int decoded_size_in_bytes;
627 
628   if (G_UNLIKELY (wildmidi_dec->song == NULL))
629     return FALSE;
630 
631   /* Allocate output buffer
632    * Multiply by 2 to accommodate for the sample size (16 bit = 2 byte) */
633   outbuf_size = wildmidi_dec->output_buffer_size * 2 * WILDMIDI_NUM_CHANNELS;
634   outbuf =
635       gst_nonstream_audio_decoder_allocate_output_buffer (dec, outbuf_size);
636   if (G_UNLIKELY (outbuf == NULL))
637     return FALSE;
638 
639   /* The actual decoding */
640   gst_buffer_map (outbuf, &info, GST_MAP_WRITE);
641   decoded_size_in_bytes =
642       WildMidi_GetOutput (wildmidi_dec->song, (int8_t *) (info.data),
643       info.size);
644   gst_buffer_unmap (outbuf, &info);
645 
646   if (decoded_size_in_bytes == 0) {
647     gst_buffer_unref (outbuf);
648     return FALSE;
649   }
650 
651   *buffer = outbuf;
652   *num_samples = decoded_size_in_bytes / 2 / WILDMIDI_NUM_CHANNELS;
653 
654   return TRUE;
655 }
656 
657 
658 static void
gst_wildmidi_dec_update_options(GstWildmidiDec * wildmidi_dec)659 gst_wildmidi_dec_update_options (GstWildmidiDec * wildmidi_dec)
660 {
661   unsigned short int options = 0;
662 
663   if (wildmidi_dec->song == NULL)
664     return;
665 
666   if (wildmidi_dec->log_volume_scale)
667     options |= WM_MO_LOG_VOLUME;
668   if (wildmidi_dec->enhanced_resampling)
669     options |= WM_MO_ENHANCED_RESAMPLING;
670   if (wildmidi_dec->reverb)
671     options |= WM_MO_REVERB;
672 
673   WildMidi_SetOption (wildmidi_dec->song,
674       WM_MO_LOG_VOLUME | WM_MO_ENHANCED_RESAMPLING | WM_MO_REVERB, options);
675 }
676 
677 
678 static gboolean
plugin_init(GstPlugin * plugin)679 plugin_init (GstPlugin * plugin)
680 {
681   return GST_ELEMENT_REGISTER (wildmididec, plugin);
682 }
683 
684 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
685     GST_VERSION_MINOR,
686     wildmidi,
687     "WildMidi-based MIDI playback plugin",
688     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
689