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