documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / ext / wildmidi / gstwildmididec.c
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
115
116
117 static void gst_wildmidi_dec_finalize (GObject * object);
118
119 static void gst_wildmidi_dec_set_property (GObject * object, guint prop_id,
120     const GValue * value, GParamSpec * pspec);
121 static void gst_wildmidi_dec_get_property (GObject * object, guint prop_id,
122     GValue * value, GParamSpec * pspec);
123
124 static gboolean gst_wildmidi_dec_seek (GstNonstreamAudioDecoder * dec,
125     GstClockTime * new_position);
126 static GstClockTime gst_wildmidi_dec_tell (GstNonstreamAudioDecoder * dec);
127
128 static gboolean gst_wildmidi_dec_load_from_buffer (GstNonstreamAudioDecoder *
129     dec, GstBuffer * source_data, guint initial_subsong,
130     GstNonstreamAudioSubsongMode initial_subsong_mode,
131     GstClockTime * initial_position,
132     GstNonstreamAudioOutputMode * initial_output_mode,
133     gint * initial_num_loops);
134
135 static guint gst_wildmidi_dec_get_current_subsong (GstNonstreamAudioDecoder *
136     dec);
137
138 static guint gst_wildmidi_dec_get_num_subsongs (GstNonstreamAudioDecoder * dec);
139 static GstClockTime
140 gst_wildmidi_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
141     guint subsong);
142
143 static guint
144 gst_wildmidi_dec_get_supported_output_modes (GstNonstreamAudioDecoder * dec);
145 static gboolean gst_wildmidi_dec_decode (GstNonstreamAudioDecoder * dec,
146     GstBuffer ** buffer, guint * num_samples);
147
148 static void gst_wildmidi_dec_update_options (GstWildmidiDec * wildmidi_dec);
149
150
151
152 static GMutex load_mutex;
153 static unsigned long init_refcount = 0;
154 static volatile gint wildmidi_initialized = 0;
155
156
157 static gchar *
158 gst_wildmidi_get_config_path (void)
159 {
160   /* This code is adapted from the original wildmidi
161    * gst-plugins-bad decoder element */
162
163   gchar *path = g_strdup (g_getenv ("WILDMIDI_CFG"));
164
165   GST_DEBUG
166       ("trying configuration path \"%s\" from WILDMIDI_CFG environment variable",
167       GST_STR_NULL (path));
168   if (path && (g_access (path, R_OK) == -1)) {
169     g_free (path);
170     path = NULL;
171   }
172
173   if (path == NULL) {
174     path =
175         g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".wildmidirc",
176         NULL);
177     GST_DEBUG ("trying configuration path \"%s\"", path);
178     if (path && (g_access (path, R_OK) == -1)) {
179       g_free (path);
180       path = NULL;
181     }
182   }
183
184   if (path == NULL) {
185     path =
186         g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc",
187         "wildmidi.cfg", NULL);
188     GST_DEBUG ("trying configuration path \"%s\"", path);
189     if (path && (g_access (path, R_OK) == -1)) {
190       g_free (path);
191       path = NULL;
192     }
193   }
194
195   if (path == NULL) {
196     path =
197         g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc", "wildmidi",
198         "wildmidi.cfg", NULL);
199     GST_DEBUG ("trying configuration path \"%s\"", path);
200     if (path && (g_access (path, R_OK) == -1)) {
201       g_free (path);
202       path = NULL;
203     }
204   }
205
206   if (path == NULL) {
207     path = g_strdup (WILDMIDI_CFG);
208     GST_DEBUG ("trying default configuration path \"%s\"", path);
209     if (path && (g_access (path, R_OK) == -1)) {
210       g_free (path);
211       path = NULL;
212     }
213   }
214
215   if (path == NULL) {
216     path =
217         g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc",
218         "timidity.cfg", NULL);
219     GST_DEBUG ("trying TiMidity configuration path \"%s\"", path);
220     if (path && (g_access (path, R_OK) == -1)) {
221       g_free (path);
222       path = NULL;
223     }
224   }
225
226   if (path == NULL) {
227     path =
228         g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc", "timidity",
229         "timidity.cfg", NULL);
230     GST_DEBUG ("trying TiMidity configuration path \"%s\"", path);
231     if (path && (g_access (path, R_OK) == -1)) {
232       g_free (path);
233       path = NULL;
234     }
235   }
236
237   return path;
238 }
239
240
241 static void
242 gst_wildmidi_init_library (void)
243 {
244   GST_DEBUG ("WildMidi init instance counter: %lu", init_refcount);
245
246   g_mutex_lock (&load_mutex);
247
248   if (init_refcount != 0) {
249     ++init_refcount;
250   } else {
251     gchar *config_path = gst_wildmidi_get_config_path ();
252     if (config_path != NULL) {
253       int ret = WildMidi_Init (config_path, WILDMIDI_SAMPLE_RATE, 0);
254       g_free (config_path);
255
256       if (ret == 0) {
257         GST_DEBUG ("WildMidi initialized, version string: %s",
258             WildMidi_GetString (WM_GS_VERSION));
259         ++init_refcount;
260         g_atomic_int_set (&wildmidi_initialized, 1);
261       } else {
262         GST_ERROR ("initializing WildMidi failed");
263         g_atomic_int_set (&wildmidi_initialized, 0);
264       }
265     } else {
266       GST_ERROR ("no config file, can't initialise");
267       g_atomic_int_set (&wildmidi_initialized, 0);
268     }
269   }
270
271   g_mutex_unlock (&load_mutex);
272 }
273
274
275 static void
276 gst_wildmidi_shutdown_library (void)
277 {
278   GST_DEBUG ("WildMidi init instance counter: %lu", init_refcount);
279
280   g_mutex_lock (&load_mutex);
281
282   if (init_refcount != 0) {
283     --init_refcount;
284     if (init_refcount == 0) {
285       WildMidi_Shutdown ();
286       GST_DEBUG ("WildMidi shut down");
287       g_atomic_int_set (&wildmidi_initialized, 0);
288     }
289   }
290
291   g_mutex_unlock (&load_mutex);
292 }
293
294
295
296 void
297 gst_wildmidi_dec_class_init (GstWildmidiDecClass * klass)
298 {
299   GObjectClass *object_class;
300   GstElementClass *element_class;
301   GstNonstreamAudioDecoderClass *dec_class;
302
303   GST_DEBUG_CATEGORY_INIT (wildmididec_debug, "wildmididec", 0,
304       "WildMidi-based MIDI music decoder");
305
306   object_class = G_OBJECT_CLASS (klass);
307   element_class = GST_ELEMENT_CLASS (klass);
308   dec_class = GST_NONSTREAM_AUDIO_DECODER_CLASS (klass);
309
310   gst_element_class_add_pad_template (element_class,
311       gst_static_pad_template_get (&sink_template));
312   gst_element_class_add_pad_template (element_class,
313       gst_static_pad_template_get (&src_template));
314
315   object_class->finalize = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_finalize);
316   object_class->set_property =
317       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_set_property);
318   object_class->get_property =
319       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_property);
320
321   dec_class->tell = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_tell);
322   dec_class->seek = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_seek);
323   dec_class->load_from_buffer =
324       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_load_from_buffer);
325   dec_class->get_current_subsong =
326       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_current_subsong);
327   dec_class->get_num_subsongs =
328       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_num_subsongs);
329   dec_class->get_subsong_duration =
330       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_subsong_duration);
331   dec_class->get_supported_output_modes =
332       GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_supported_output_modes);
333   dec_class->decode = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_decode);
334
335   gst_element_class_set_static_metadata (element_class,
336       "WildMidi-based MIDI music decoder",
337       "Codec/Decoder/Audio",
338       "Decodes MIDI music using WildMidi",
339       "Carlos Rafael Giani <dv@pseudoterminal.org>");
340
341   g_object_class_install_property (object_class,
342       PROP_LOG_VOLUME_SCALE,
343       g_param_spec_boolean ("log-volume-scale",
344           "Logarithmic volume scale",
345           "Use a logarithmic volume scale if set to TRUE, or a linear scale if set to FALSE",
346           DEFAULT_LOG_VOLUME_SCALE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
347       );
348   g_object_class_install_property (object_class,
349       PROP_ENHANCED_RESAMPLING,
350       g_param_spec_boolean ("enhanced-resampling",
351           "Enhanced resampling",
352           "Use enhanced resampling if set to TRUE, or linear interpolation if set to FALSE",
353           DEFAULT_ENHANCED_RESAMPLING,
354           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
355       );
356   g_object_class_install_property (object_class,
357       PROP_REVERB,
358       g_param_spec_boolean ("reverb",
359           "Reverb",
360           "Whether or not to enable the WildMidi 8 reflection reverb engine to add more depth to the sound",
361           DEFAULT_REVERB, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
362       );
363   /* 2*2 => stereo output with S16 samples; the division ensures that no overflow can happen */
364   g_object_class_install_property (object_class,
365       PROP_OUTPUT_BUFFER_SIZE,
366       g_param_spec_uint ("output-buffer-size",
367           "Output buffer size",
368           "Size of each output buffer, in samples (actual size can be smaller than this during flush or EOS)",
369           1, G_MAXUINT / (2 * 2),
370           DEFAULT_OUTPUT_BUFFER_SIZE,
371           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
372       );
373 }
374
375
376 void
377 gst_wildmidi_dec_init (GstWildmidiDec * wildmidi_dec)
378 {
379   wildmidi_dec->song = NULL;
380
381   wildmidi_dec->log_volume_scale = DEFAULT_LOG_VOLUME_SCALE;
382   wildmidi_dec->enhanced_resampling = DEFAULT_ENHANCED_RESAMPLING;
383   wildmidi_dec->reverb = DEFAULT_REVERB;
384   wildmidi_dec->output_buffer_size = DEFAULT_OUTPUT_BUFFER_SIZE;
385
386   gst_wildmidi_init_library ();
387 }
388
389
390 static void
391 gst_wildmidi_dec_finalize (GObject * object)
392 {
393   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (object);
394
395   if (wildmidi_dec->song != NULL)
396     WildMidi_Close (wildmidi_dec->song);
397
398   gst_wildmidi_shutdown_library ();
399
400   G_OBJECT_CLASS (gst_wildmidi_dec_parent_class)->finalize (object);
401 }
402
403
404 static void
405 gst_wildmidi_dec_set_property (GObject * object, guint prop_id,
406     const GValue * value, GParamSpec * pspec)
407 {
408   GstWildmidiDec *wildmidi_dec;
409
410   wildmidi_dec = GST_WILDMIDI_DEC (object);
411
412   switch (prop_id) {
413     case PROP_LOG_VOLUME_SCALE:
414       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
415       wildmidi_dec->log_volume_scale = g_value_get_boolean (value);
416       gst_wildmidi_dec_update_options (wildmidi_dec);
417       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
418       break;
419
420     case PROP_ENHANCED_RESAMPLING:
421       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
422       wildmidi_dec->enhanced_resampling = g_value_get_boolean (value);
423       gst_wildmidi_dec_update_options (wildmidi_dec);
424       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
425       break;
426
427     case PROP_REVERB:
428       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
429       wildmidi_dec->reverb = g_value_get_boolean (value);
430       gst_wildmidi_dec_update_options (wildmidi_dec);
431       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
432       break;
433
434     case PROP_OUTPUT_BUFFER_SIZE:
435       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
436       wildmidi_dec->output_buffer_size = g_value_get_uint (value);
437       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
438       break;
439
440     default:
441       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
442       break;
443   }
444 }
445
446
447 static void
448 gst_wildmidi_dec_get_property (GObject * object, guint prop_id, GValue * value,
449     GParamSpec * pspec)
450 {
451   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (object);
452
453   switch (prop_id) {
454     case PROP_LOG_VOLUME_SCALE:
455       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
456       g_value_set_boolean (value, wildmidi_dec->log_volume_scale);
457       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
458       break;
459
460     case PROP_ENHANCED_RESAMPLING:
461       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
462       g_value_set_boolean (value, wildmidi_dec->enhanced_resampling);
463       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
464       break;
465
466     case PROP_REVERB:
467       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
468       g_value_set_boolean (value, wildmidi_dec->reverb);
469       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
470       break;
471
472     case PROP_OUTPUT_BUFFER_SIZE:
473       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
474       g_value_set_uint (value, wildmidi_dec->output_buffer_size);
475       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
476       break;
477
478     default:
479       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
480       break;
481   }
482 }
483
484
485 static gboolean
486 gst_wildmidi_dec_seek (GstNonstreamAudioDecoder * dec,
487     GstClockTime * new_position)
488 {
489   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
490   unsigned long int sample_pos =
491       gst_util_uint64_scale_int (*new_position, WILDMIDI_SAMPLE_RATE,
492       GST_SECOND);
493
494   if (G_UNLIKELY (wildmidi_dec->song == NULL))
495     return FALSE;
496
497   WildMidi_FastSeek (wildmidi_dec->song, &sample_pos);
498
499   *new_position =
500       gst_util_uint64_scale_int (sample_pos, GST_SECOND, WILDMIDI_SAMPLE_RATE);
501   return TRUE;
502 }
503
504
505 static GstClockTime
506 gst_wildmidi_dec_tell (GstNonstreamAudioDecoder * dec)
507 {
508   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
509   struct _WM_Info *info;
510
511   if (G_UNLIKELY (wildmidi_dec->song == NULL))
512     return GST_CLOCK_TIME_NONE;
513
514   info = WildMidi_GetInfo (wildmidi_dec->song);
515   return gst_util_uint64_scale_int (info->current_sample, GST_SECOND,
516       WILDMIDI_SAMPLE_RATE);
517 }
518
519
520 static gboolean
521 gst_wildmidi_dec_load_from_buffer (GstNonstreamAudioDecoder * dec,
522     GstBuffer * source_data, G_GNUC_UNUSED guint initial_subsong,
523     G_GNUC_UNUSED GstNonstreamAudioSubsongMode initial_subsong_mode,
524     GstClockTime * initial_position,
525     GstNonstreamAudioOutputMode * initial_output_mode,
526     G_GNUC_UNUSED gint * initial_num_loops)
527 {
528   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
529   GstMapInfo buffer_map;
530
531
532   if (g_atomic_int_get (&wildmidi_initialized) == 0) {
533     GST_ERROR_OBJECT (wildmidi_dec,
534         "Could not start loading: WildMidi is not initialized");
535     return FALSE;
536   }
537
538
539   /* Set output format */
540   if (!gst_nonstream_audio_decoder_set_output_format_simple (dec,
541           WILDMIDI_SAMPLE_RATE, GST_AUDIO_FORMAT_S16, WILDMIDI_NUM_CHANNELS))
542     return FALSE;
543
544
545   /* Load MIDI */
546   gst_buffer_map (source_data, &buffer_map, GST_MAP_READ);
547   wildmidi_dec->song = WildMidi_OpenBuffer (buffer_map.data, buffer_map.size);
548   gst_buffer_unmap (source_data, &buffer_map);
549
550   if (wildmidi_dec->song == NULL) {
551     GST_ERROR_OBJECT (wildmidi_dec, "Could not load MIDI tune");
552     return FALSE;
553   }
554
555   gst_wildmidi_dec_update_options (wildmidi_dec);
556
557
558   /* Seek to initial position */
559   if (*initial_position != 0) {
560     unsigned long int sample_pos =
561         gst_util_uint64_scale_int (*initial_position, WILDMIDI_SAMPLE_RATE,
562         GST_SECOND);
563     WildMidi_FastSeek (wildmidi_dec->song, &sample_pos);
564     *initial_position =
565         gst_util_uint64_scale_int (sample_pos, GST_SECOND,
566         WILDMIDI_SAMPLE_RATE);
567   }
568
569
570   /* LOOPING output mode is not supported */
571   *initial_output_mode = GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
572
573
574   return TRUE;
575 }
576
577
578 static guint
579 gst_wildmidi_dec_get_current_subsong (G_GNUC_UNUSED GstNonstreamAudioDecoder *
580     dec)
581 {
582   return 0;
583 }
584
585
586 static guint
587 gst_wildmidi_dec_get_num_subsongs (G_GNUC_UNUSED GstNonstreamAudioDecoder * dec)
588 {
589   return 1;
590 }
591
592
593 static GstClockTime
594 gst_wildmidi_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
595     G_GNUC_UNUSED guint subsong)
596 {
597   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
598   struct _WM_Info *info;
599
600   if (G_UNLIKELY (wildmidi_dec->song == NULL))
601     return GST_CLOCK_TIME_NONE;
602
603   info = WildMidi_GetInfo (wildmidi_dec->song);
604   return gst_util_uint64_scale_int (info->approx_total_samples, GST_SECOND,
605       WILDMIDI_SAMPLE_RATE);
606 }
607
608
609 static guint
610 gst_wildmidi_dec_get_supported_output_modes (G_GNUC_UNUSED
611     GstNonstreamAudioDecoder * dec)
612 {
613   return 1u << GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
614 }
615
616
617 static gboolean
618 gst_wildmidi_dec_decode (GstNonstreamAudioDecoder * dec, GstBuffer ** buffer,
619     guint * num_samples)
620 {
621   GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
622   GstMapInfo info;
623   GstBuffer *outbuf;
624   gsize outbuf_size;
625   int decoded_size_in_bytes;
626
627   if (G_UNLIKELY (wildmidi_dec->song == NULL))
628     return FALSE;
629
630   /* Allocate output buffer
631    * Multiply by 2 to accommodate for the sample size (16 bit = 2 byte) */
632   outbuf_size = wildmidi_dec->output_buffer_size * 2 * WILDMIDI_NUM_CHANNELS;
633   outbuf =
634       gst_nonstream_audio_decoder_allocate_output_buffer (dec, outbuf_size);
635   if (G_UNLIKELY (outbuf == NULL))
636     return FALSE;
637
638   /* The actual decoding */
639   gst_buffer_map (outbuf, &info, GST_MAP_WRITE);
640   decoded_size_in_bytes =
641       WildMidi_GetOutput (wildmidi_dec->song, (int8_t *) (info.data),
642       info.size);
643   gst_buffer_unmap (outbuf, &info);
644
645   if (decoded_size_in_bytes == 0) {
646     gst_buffer_unref (outbuf);
647     return FALSE;
648   }
649
650   *buffer = outbuf;
651   *num_samples = decoded_size_in_bytes / 2 / WILDMIDI_NUM_CHANNELS;
652
653   return TRUE;
654 }
655
656
657 static void
658 gst_wildmidi_dec_update_options (GstWildmidiDec * wildmidi_dec)
659 {
660   unsigned short int options = 0;
661
662   if (wildmidi_dec->song == NULL)
663     return;
664
665   if (wildmidi_dec->log_volume_scale)
666     options |= WM_MO_LOG_VOLUME;
667   if (wildmidi_dec->enhanced_resampling)
668     options |= WM_MO_ENHANCED_RESAMPLING;
669   if (wildmidi_dec->reverb)
670     options |= WM_MO_REVERB;
671
672   WildMidi_SetOption (wildmidi_dec->song,
673       WM_MO_LOG_VOLUME | WM_MO_ENHANCED_RESAMPLING | WM_MO_REVERB, options);
674 }
675
676
677 static gboolean
678 plugin_init (GstPlugin * plugin)
679 {
680   return gst_element_register (plugin, "wildmididec", GST_RANK_MARGINAL,
681       gst_wildmidi_dec_get_type ());
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)