• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) <2017> 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-openmptdec
23  * @see_also: #GstOpenMptDec
24  *
25  * openmpdec decodes module music formats, such as S3M, MOD, XM, IT.
26  * It uses the [OpenMPT library](https://lib.openmpt.org) for this purpose.
27  * It can be autoplugged and therefore works with decodebin.
28  *
29  * ## Example launch line
30  *
31  * |[
32  * gst-launch-1.0 filesrc location=media/example.it ! openmptdec ! audioconvert ! audioresample ! autoaudiosink
33  * ]|
34  */
35 
36 
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40 
41 #include <gst/gst.h>
42 
43 #include "gstopenmptdec.h"
44 
45 #ifndef OPENMPT_API_VERSION_AT_LEAST
46 #define OPENMPT_API_VERSION_AT_LEAST(x, y, z) (FALSE)
47 #endif
48 
49 GST_DEBUG_CATEGORY_STATIC (openmptdec_debug);
50 #define GST_CAT_DEFAULT openmptdec_debug
51 
52 
53 enum
54 {
55   PROP_0,
56   PROP_MASTER_GAIN,
57   PROP_STEREO_SEPARATION,
58   PROP_FILTER_LENGTH,
59   PROP_VOLUME_RAMPING,
60   PROP_OUTPUT_BUFFER_SIZE
61 };
62 
63 
64 #define DEFAULT_MASTER_GAIN 0
65 #define DEFAULT_STEREO_SEPARATION 100
66 #define DEFAULT_FILTER_LENGTH 0
67 #define DEFAULT_VOLUME_RAMPING -1
68 #define DEFAULT_OUTPUT_BUFFER_SIZE 1024
69 
70 #define DEFAULT_SAMPLE_FORMAT GST_AUDIO_FORMAT_F32
71 #define DEFAULT_SAMPLE_RATE 48000
72 #define DEFAULT_NUM_CHANNELS 2
73 
74 
75 
76 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
77     GST_PAD_SINK,
78     GST_PAD_ALWAYS,
79     GST_STATIC_CAPS ("audio/x-mod, "
80         "type = (string) { 669, asylum-amf, dsmi-amf, extreme-ams, velvet-ams, "
81         "dbm, digi, dmf, dsm, far, gdm, imf, it, j2b, mdl, med, mod, mt2, mtm, "
82         "okt, psm, ptm, s3m, stm, ult, xm }")
83     );
84 
85 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
86     GST_PAD_SRC,
87     GST_PAD_ALWAYS,
88     GST_STATIC_CAPS ("audio/x-raw, "
89         "format = (string) { " GST_AUDIO_NE (S16) ", " GST_AUDIO_NE (F32) " }, "
90         "layout = (string) interleaved, "
91         "rate = (int) [ 1, 192000 ], " "channels = (int) { 1, 2, 4 } ")
92     );
93 
94 
95 
96 G_DEFINE_TYPE (GstOpenMptDec, gst_openmpt_dec,
97     GST_TYPE_NONSTREAM_AUDIO_DECODER);
98 GST_ELEMENT_REGISTER_DEFINE (openmptdec, "openmptdec", GST_RANK_PRIMARY + 2,
99     gst_openmpt_dec_get_type ());
100 
101 
102 static void gst_openmpt_dec_finalize (GObject * object);
103 
104 static void gst_openmpt_dec_set_property (GObject * object, guint prop_id,
105     const GValue * value, GParamSpec * pspec);
106 static void gst_openmpt_dec_get_property (GObject * object, guint prop_id,
107     GValue * value, GParamSpec * pspec);
108 
109 static gboolean gst_openmpt_dec_seek (GstNonstreamAudioDecoder * dec,
110     GstClockTime * new_position);
111 static GstClockTime gst_openmpt_dec_tell (GstNonstreamAudioDecoder * dec);
112 
113 static void gst_openmpt_dec_log_func (char const *message, void *user);
114 static void gst_openmpt_dec_add_metadata_to_tag_list (GstOpenMptDec *
115     openmpt_dec, GstTagList * tags, char const *key, gchar const *tag);
116 static gboolean gst_openmpt_dec_load_from_buffer (GstNonstreamAudioDecoder *
117     dec, GstBuffer * source_data, guint initial_subsong,
118     GstNonstreamAudioSubsongMode initial_subsong_mode,
119     GstClockTime * initial_position,
120     GstNonstreamAudioOutputMode * initial_output_mode,
121     gint * initial_num_loops);
122 
123 static GstTagList *gst_openmpt_dec_get_main_tags (GstNonstreamAudioDecoder *
124     dec);
125 
126 static gboolean gst_openmpt_dec_set_current_subsong (GstNonstreamAudioDecoder *
127     dec, guint subsong, GstClockTime * initial_position);
128 static guint gst_openmpt_dec_get_current_subsong (GstNonstreamAudioDecoder *
129     dec);
130 
131 static guint gst_openmpt_dec_get_num_subsongs (GstNonstreamAudioDecoder * dec);
132 static GstClockTime
133 gst_openmpt_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
134     guint subsong);
135 static GstTagList *gst_openmpt_dec_get_subsong_tags (GstNonstreamAudioDecoder *
136     dec, guint subsong);
137 static gboolean gst_openmpt_dec_set_subsong_mode (GstNonstreamAudioDecoder *
138     dec, GstNonstreamAudioSubsongMode mode, GstClockTime * initial_position);
139 
140 static gboolean gst_openmpt_dec_set_num_loops (GstNonstreamAudioDecoder * dec,
141     gint num_loops);
142 static gint gst_openmpt_dec_get_num_loops (GstNonstreamAudioDecoder * dec);
143 
144 static guint
145 gst_openmpt_dec_get_supported_output_modes (GstNonstreamAudioDecoder * dec);
146 static gboolean gst_openmpt_dec_decode (GstNonstreamAudioDecoder * dec,
147     GstBuffer ** buffer, guint * num_samples);
148 
149 static gboolean gst_openmpt_dec_select_subsong (GstOpenMptDec *
150     openmpt_dec, GstNonstreamAudioSubsongMode subsong_mode,
151     gint openmpt_subsong);
152 
153 
154 void
gst_openmpt_dec_class_init(GstOpenMptDecClass * klass)155 gst_openmpt_dec_class_init (GstOpenMptDecClass * klass)
156 {
157   GObjectClass *object_class;
158   GstElementClass *element_class;
159   GstNonstreamAudioDecoderClass *dec_class;
160 
161   GST_DEBUG_CATEGORY_INIT (openmptdec_debug, "openmptdec", 0,
162       "OpenMPT-based module music decoder");
163 
164   object_class = G_OBJECT_CLASS (klass);
165   element_class = GST_ELEMENT_CLASS (klass);
166   dec_class = GST_NONSTREAM_AUDIO_DECODER_CLASS (klass);
167 
168   gst_element_class_add_pad_template (element_class,
169       gst_static_pad_template_get (&sink_template));
170   gst_element_class_add_pad_template (element_class,
171       gst_static_pad_template_get (&src_template));
172 
173   object_class->finalize = GST_DEBUG_FUNCPTR (gst_openmpt_dec_finalize);
174   object_class->set_property = GST_DEBUG_FUNCPTR (gst_openmpt_dec_set_property);
175   object_class->get_property = GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_property);
176 
177   dec_class->seek = GST_DEBUG_FUNCPTR (gst_openmpt_dec_seek);
178   dec_class->tell = GST_DEBUG_FUNCPTR (gst_openmpt_dec_tell);
179   dec_class->load_from_buffer =
180       GST_DEBUG_FUNCPTR (gst_openmpt_dec_load_from_buffer);
181   dec_class->get_main_tags = GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_main_tags);
182   dec_class->set_num_loops = GST_DEBUG_FUNCPTR (gst_openmpt_dec_set_num_loops);
183   dec_class->get_num_loops = GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_num_loops);
184   dec_class->get_supported_output_modes =
185       GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_supported_output_modes);
186   dec_class->decode = GST_DEBUG_FUNCPTR (gst_openmpt_dec_decode);
187   dec_class->set_current_subsong =
188       GST_DEBUG_FUNCPTR (gst_openmpt_dec_set_current_subsong);
189   dec_class->get_current_subsong =
190       GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_current_subsong);
191   dec_class->get_num_subsongs =
192       GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_num_subsongs);
193   dec_class->get_subsong_duration =
194       GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_subsong_duration);
195   dec_class->get_subsong_tags =
196       GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_subsong_tags);
197   dec_class->set_subsong_mode =
198       GST_DEBUG_FUNCPTR (gst_openmpt_dec_set_subsong_mode);
199 
200   gst_element_class_set_static_metadata (element_class,
201       "OpenMPT-based module music decoder",
202       "Codec/Decoder/Audio",
203       "Decoders module files (MOD/S3M/XM/IT/MTM/...) using OpenMPT",
204       "Carlos Rafael Giani <dv@pseudoterminal.org>");
205 
206   g_object_class_install_property (object_class,
207       PROP_MASTER_GAIN,
208       g_param_spec_int ("master-gain",
209           "Master gain",
210           "Gain to apply to the playback, in millibel",
211           -G_MAXINT, G_MAXINT,
212           DEFAULT_MASTER_GAIN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
213       );
214   g_object_class_install_property (object_class,
215       PROP_STEREO_SEPARATION,
216       g_param_spec_int ("stereo-separation",
217           "Stereo separation",
218           "Degree of separation for stereo channels, in percent",
219           0, 400,
220           DEFAULT_STEREO_SEPARATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
221       );
222   g_object_class_install_property (object_class,
223       PROP_FILTER_LENGTH,
224       g_param_spec_int ("filter-length",
225           "Filter length",
226           "Length of interpolation filter to use for the samples (0 = internal default)",
227           0, 8,
228           DEFAULT_FILTER_LENGTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
229       );
230   g_object_class_install_property (object_class,
231       PROP_VOLUME_RAMPING,
232       g_param_spec_int ("volume-ramping",
233           "Volume ramping",
234           "Volume ramping strength; higher value -> slower ramping (-1 = internal default)",
235           -1, 10,
236           DEFAULT_VOLUME_RAMPING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
237       );
238   /* 4*4 => quad output with F32 samples; this ensures that no overflow can happen */
239   g_object_class_install_property (object_class,
240       PROP_OUTPUT_BUFFER_SIZE,
241       g_param_spec_uint ("output-buffer-size",
242           "Output buffer size",
243           "Size of each output buffer, in samples (actual size can be smaller "
244           "than this during flush or EOS)",
245           1, G_MAXUINT / (4 * 4),
246           DEFAULT_OUTPUT_BUFFER_SIZE,
247           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
248       );
249 }
250 
251 
252 void
gst_openmpt_dec_init(GstOpenMptDec * openmpt_dec)253 gst_openmpt_dec_init (GstOpenMptDec * openmpt_dec)
254 {
255   openmpt_dec->mod = NULL;
256 
257   openmpt_dec->cur_subsong = 0;
258   openmpt_dec->num_subsongs = 0;
259   openmpt_dec->subsong_durations = NULL;
260 
261   openmpt_dec->num_loops = 0;
262 
263   openmpt_dec->master_gain = DEFAULT_MASTER_GAIN;
264   openmpt_dec->stereo_separation = DEFAULT_STEREO_SEPARATION;
265   openmpt_dec->filter_length = DEFAULT_FILTER_LENGTH;
266   openmpt_dec->volume_ramping = DEFAULT_VOLUME_RAMPING;
267 
268   openmpt_dec->output_buffer_size = DEFAULT_OUTPUT_BUFFER_SIZE;
269 
270   openmpt_dec->main_tags = NULL;
271 
272   openmpt_dec->sample_format = DEFAULT_SAMPLE_FORMAT;
273   openmpt_dec->sample_rate = DEFAULT_SAMPLE_RATE;
274   openmpt_dec->num_channels = DEFAULT_NUM_CHANNELS;
275 }
276 
277 
278 static void
gst_openmpt_dec_finalize(GObject * object)279 gst_openmpt_dec_finalize (GObject * object)
280 {
281   GstOpenMptDec *openmpt_dec;
282 
283   g_return_if_fail (GST_IS_OPENMPT_DEC (object));
284   openmpt_dec = GST_OPENMPT_DEC (object);
285 
286   if (openmpt_dec->main_tags != NULL)
287     gst_tag_list_unref (openmpt_dec->main_tags);
288 
289   if (openmpt_dec->mod != NULL)
290     openmpt_module_destroy (openmpt_dec->mod);
291 
292   g_free (openmpt_dec->subsong_durations);
293 
294   G_OBJECT_CLASS (gst_openmpt_dec_parent_class)->finalize (object);
295 }
296 
297 
298 static void
gst_openmpt_dec_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)299 gst_openmpt_dec_set_property (GObject * object, guint prop_id,
300     const GValue * value, GParamSpec * pspec)
301 {
302   GstNonstreamAudioDecoder *dec;
303   GstOpenMptDec *openmpt_dec;
304 
305   dec = GST_NONSTREAM_AUDIO_DECODER (object);
306   openmpt_dec = GST_OPENMPT_DEC (object);
307 
308   switch (prop_id) {
309     case PROP_MASTER_GAIN:
310     {
311       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
312       openmpt_dec->master_gain = g_value_get_int (value);
313       if (openmpt_dec->mod != NULL)
314         openmpt_module_set_render_param (openmpt_dec->mod,
315             OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,
316             openmpt_dec->master_gain);
317       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
318       break;
319     }
320 
321     case PROP_STEREO_SEPARATION:
322     {
323       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
324       openmpt_dec->stereo_separation = g_value_get_int (value);
325       if (openmpt_dec->mod != NULL)
326         openmpt_module_set_render_param (openmpt_dec->mod,
327             OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,
328             openmpt_dec->stereo_separation);
329       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
330       break;
331     }
332 
333     case PROP_FILTER_LENGTH:
334     {
335       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
336       openmpt_dec->filter_length = g_value_get_int (value);
337       if (openmpt_dec->mod != NULL)
338         openmpt_module_set_render_param (openmpt_dec->mod,
339             OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,
340             openmpt_dec->filter_length);
341       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
342       break;
343     }
344 
345     case PROP_VOLUME_RAMPING:
346     {
347       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
348       openmpt_dec->volume_ramping = g_value_get_int (value);
349       if (openmpt_dec->mod != NULL)
350         openmpt_module_set_render_param (openmpt_dec->mod,
351             OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH,
352             openmpt_dec->volume_ramping);
353       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
354       break;
355     }
356 
357     case PROP_OUTPUT_BUFFER_SIZE:
358     {
359       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
360       openmpt_dec->output_buffer_size = g_value_get_uint (value);
361       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
362       break;
363     }
364 
365     default:
366       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
367       break;
368   }
369 }
370 
371 
372 static void
gst_openmpt_dec_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)373 gst_openmpt_dec_get_property (GObject * object, guint prop_id, GValue * value,
374     GParamSpec * pspec)
375 {
376   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (object);
377 
378   switch (prop_id) {
379     case PROP_MASTER_GAIN:
380     {
381       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
382       g_value_set_int (value, openmpt_dec->master_gain);
383       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
384       break;
385     }
386 
387     case PROP_STEREO_SEPARATION:
388     {
389       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
390       g_value_set_int (value, openmpt_dec->stereo_separation);
391       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
392       break;
393     }
394 
395     case PROP_FILTER_LENGTH:
396     {
397       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
398       g_value_set_int (value, openmpt_dec->filter_length);
399       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
400       break;
401     }
402 
403     case PROP_VOLUME_RAMPING:
404     {
405       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
406       g_value_set_int (value, openmpt_dec->volume_ramping);
407       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
408       break;
409     }
410 
411     case PROP_OUTPUT_BUFFER_SIZE:
412     {
413       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
414       g_value_set_uint (value, openmpt_dec->output_buffer_size);
415       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
416       break;
417     }
418 
419     default:
420       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
421       break;
422   }
423 }
424 
425 
426 static gboolean
gst_openmpt_dec_seek(GstNonstreamAudioDecoder * dec,GstClockTime * new_position)427 gst_openmpt_dec_seek (GstNonstreamAudioDecoder * dec,
428     GstClockTime * new_position)
429 {
430   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
431   g_return_val_if_fail (openmpt_dec->mod != NULL, FALSE);
432 
433   openmpt_module_set_position_seconds (openmpt_dec->mod,
434       (double) (*new_position) / GST_SECOND);
435   *new_position = gst_openmpt_dec_tell (dec);
436 
437   return TRUE;
438 }
439 
440 
441 static GstClockTime
gst_openmpt_dec_tell(GstNonstreamAudioDecoder * dec)442 gst_openmpt_dec_tell (GstNonstreamAudioDecoder * dec)
443 {
444   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
445   g_return_val_if_fail (openmpt_dec->mod != NULL, GST_CLOCK_TIME_NONE);
446 
447   return (GstClockTime) (openmpt_module_get_position_seconds (openmpt_dec->mod)
448       * GST_SECOND);
449 }
450 
451 
452 static void
gst_openmpt_dec_log_func(char const * message,void * user)453 gst_openmpt_dec_log_func (char const *message, void *user)
454 {
455   GST_LOG_OBJECT (GST_OBJECT (user), "%s", message);
456 }
457 
458 
459 static void
gst_openmpt_dec_add_metadata_to_tag_list(GstOpenMptDec * openmpt_dec,GstTagList * tags,char const * key,gchar const * tag)460 gst_openmpt_dec_add_metadata_to_tag_list (GstOpenMptDec * openmpt_dec,
461     GstTagList * tags, char const *key, gchar const *tag)
462 {
463   char const *metadata = openmpt_module_get_metadata (openmpt_dec->mod, key);
464 
465   if (metadata && *metadata) {
466     GST_DEBUG_OBJECT (openmpt_dec,
467         "adding metadata \"%s\" with key \"%s\" to tag list as tag \"%s\"",
468         metadata, key, tag);
469 
470     if (g_strcmp0 (tag, GST_TAG_DATE_TIME) == 0) {
471       /* Special handling for date-time tags - interpret the
472        * metadata string as an iso8601 string and convert it
473        * to a GstDateTime value, since this is the data type
474        * that GST_TAG_DATE_TIME expects. */
475 
476       GstDateTime *date_time = gst_date_time_new_from_iso8601_string (metadata);
477       if (date_time) {
478         GST_DEBUG_OBJECT (openmpt_dec,
479             "successfully created date-time object out of iso8601 string");
480         gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, tag, date_time, NULL);
481         gst_date_time_unref (date_time);
482       } else
483         GST_WARNING_OBJECT (openmpt_dec,
484             "could not create date-time object out of iso8601 string - not adding metadata to tags");
485     } else {
486       /* Default handling - just insert the metadata string as-is */
487       gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, tag, metadata, NULL);
488     }
489   } else
490     GST_DEBUG_OBJECT (openmpt_dec,
491         "attempted to add metadata with key \"%s\" to tag list as tag \"%s\", but none exists",
492         key, tag);
493 
494   if (metadata)
495     openmpt_free_string (metadata);
496 }
497 
498 
499 static gboolean
gst_openmpt_dec_load_from_buffer(GstNonstreamAudioDecoder * dec,GstBuffer * source_data,guint initial_subsong,GstNonstreamAudioSubsongMode initial_subsong_mode,GstClockTime * initial_position,GstNonstreamAudioOutputMode * initial_output_mode,gint * initial_num_loops)500 gst_openmpt_dec_load_from_buffer (GstNonstreamAudioDecoder * dec,
501     GstBuffer * source_data, guint initial_subsong,
502     GstNonstreamAudioSubsongMode initial_subsong_mode,
503     GstClockTime * initial_position,
504     GstNonstreamAudioOutputMode * initial_output_mode, gint * initial_num_loops)
505 {
506   GstMapInfo map;
507   GstOpenMptDec *openmpt_dec;
508 
509   openmpt_dec = GST_OPENMPT_DEC (dec);
510 
511   /* First, determine the sample rate, channel count, and sample format to use */
512   openmpt_dec->sample_format = DEFAULT_SAMPLE_FORMAT;
513   openmpt_dec->sample_rate = DEFAULT_SAMPLE_RATE;
514   openmpt_dec->num_channels = DEFAULT_NUM_CHANNELS;
515   gst_nonstream_audio_decoder_get_downstream_info (dec,
516       &(openmpt_dec->sample_format), &(openmpt_dec->sample_rate),
517       &(openmpt_dec->num_channels));
518 
519   /* Set output format */
520   if (!gst_nonstream_audio_decoder_set_output_format_simple (dec,
521           openmpt_dec->sample_rate,
522           openmpt_dec->sample_format, openmpt_dec->num_channels))
523     return FALSE;
524 
525   /* Pass the module data to OpenMPT for loading */
526   gst_buffer_map (source_data, &map, GST_MAP_READ);
527 #if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
528   openmpt_dec->mod =
529       openmpt_module_create_from_memory2 (map.data, map.size,
530       gst_openmpt_dec_log_func, dec, NULL, NULL, NULL, NULL, NULL);
531 #else
532   openmpt_dec->mod =
533       openmpt_module_create_from_memory (map.data, map.size,
534       gst_openmpt_dec_log_func, dec, NULL);
535 #endif
536   gst_buffer_unmap (source_data, &map);
537 
538   if (openmpt_dec->mod == NULL) {
539     GST_ERROR_OBJECT (dec, "loading module failed");
540     return FALSE;
541   }
542 
543   /* Copy subsong states */
544   openmpt_dec->cur_subsong = initial_subsong;
545   openmpt_dec->cur_subsong_mode = initial_subsong_mode;
546 
547   /* Query the number of subsongs available for logging and for checking
548    * the initial subsong index */
549   openmpt_dec->num_subsongs =
550       openmpt_module_get_num_subsongs (openmpt_dec->mod);
551   if (G_UNLIKELY (initial_subsong >= openmpt_dec->num_subsongs)) {
552     GST_WARNING_OBJECT (openmpt_dec,
553         "initial subsong %u out of bounds (there are %u subsongs) - setting it to 0",
554         initial_subsong, openmpt_dec->num_subsongs);
555     initial_subsong = 0;
556   }
557   GST_INFO_OBJECT (openmpt_dec, "%d subsong(s) available",
558       openmpt_dec->num_subsongs);
559 
560   /* Query the OpenMPT default subsong (can be -1)
561    * The default subsong is the one that is initially selected, so we
562    * need to query it here, *before* any openmpt_module_select_subsong()
563    * calls are done */
564   {
565     gchar const *subsong_cstr =
566         openmpt_module_ctl_get (openmpt_dec->mod, "subsong");
567     gchar *endptr;
568 
569     if (subsong_cstr != NULL) {
570       openmpt_dec->default_openmpt_subsong =
571           g_ascii_strtoll (subsong_cstr, &endptr, 10);
572       if (subsong_cstr == endptr) {
573         GST_WARNING_OBJECT (openmpt_dec,
574             "could not convert ctl string \"%s\" to subsong index - using default OpenMPT index -1 instead",
575             subsong_cstr);
576         openmpt_dec->default_openmpt_subsong = -1;
577       } else
578         GST_DEBUG_OBJECT (openmpt_dec, "default OpenMPT subsong index is %d",
579             openmpt_dec->default_openmpt_subsong);
580 
581       openmpt_free_string (subsong_cstr);
582     } else {
583       GST_INFO_OBJECT (openmpt_dec,
584           "could not get subsong ctl string - using default OpenMPT index -1 instead");
585       openmpt_dec->default_openmpt_subsong = -1;
586     }
587   }
588 
589   /* Seek to initial position */
590   if (*initial_position != 0) {
591     openmpt_module_set_position_seconds (openmpt_dec->mod,
592         (double) (*initial_position) / GST_SECOND);
593     *initial_position =
594         (GstClockTime) (openmpt_module_get_position_seconds (openmpt_dec->mod) *
595         GST_SECOND);
596   }
597 
598   /* LOOPING output mode is not supported */
599   *initial_output_mode = GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
600 
601   /* Query the durations of each subsong (if any exist) */
602   if (openmpt_dec->num_subsongs > 0) {
603     guint i;
604 
605     openmpt_dec->subsong_durations =
606         g_try_malloc (openmpt_dec->num_subsongs * sizeof (double));
607     if (openmpt_dec->subsong_durations == NULL) {
608       GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
609       GST_ELEMENT_ERROR (openmpt_dec, RESOURCE, NO_SPACE_LEFT,
610           ("could not allocate memory for subsong duration array"), (NULL));
611       GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
612       return FALSE;
613     }
614 
615     for (i = 0; i < openmpt_dec->num_subsongs; ++i) {
616       openmpt_module_select_subsong (openmpt_dec->mod, i);
617       openmpt_dec->subsong_durations[i] =
618           openmpt_module_get_duration_seconds (openmpt_dec->mod);
619     }
620   }
621 
622   /* Select the initial subsong */
623   gst_openmpt_dec_select_subsong (openmpt_dec, initial_subsong_mode,
624       initial_subsong);
625 
626   /* Set the number of loops, and query the actual number
627    * that was chosen by OpenMPT */
628   {
629     int32_t actual_repeat_count;
630     openmpt_module_set_repeat_count (openmpt_dec->mod, *initial_num_loops);
631     actual_repeat_count = openmpt_module_get_repeat_count (openmpt_dec->mod);
632 
633     if (actual_repeat_count != *initial_num_loops) {
634       GST_DEBUG_OBJECT (openmpt_dec,
635           "requested num-loops value %d differs from actual value %d",
636           *initial_num_loops, actual_repeat_count);
637       *initial_num_loops = actual_repeat_count;
638     }
639   }
640 
641   /* Set render parameters (adjustable via properties) */
642   openmpt_module_set_render_param (openmpt_dec->mod,
643       OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL, openmpt_dec->master_gain);
644   openmpt_module_set_render_param (openmpt_dec->mod,
645       OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,
646       openmpt_dec->stereo_separation);
647   openmpt_module_set_render_param (openmpt_dec->mod,
648       OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,
649       openmpt_dec->filter_length);
650   openmpt_module_set_render_param (openmpt_dec->mod,
651       OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH,
652       openmpt_dec->volume_ramping);
653 
654   /* Log the available metadata keys, and produce a
655    * tag list if any keys are available */
656   {
657     char const *metadata_keys =
658         openmpt_module_get_metadata_keys (openmpt_dec->mod);
659     if (metadata_keys != NULL) {
660       GstTagList *tags = gst_tag_list_new_empty ();
661 
662       GST_DEBUG_OBJECT (dec, "metadata keys: [%s]", metadata_keys);
663       openmpt_free_string (metadata_keys);
664 
665       gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "title",
666           GST_TAG_TITLE);
667       gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "artist",
668           GST_TAG_ARTIST);
669       gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "message",
670           GST_TAG_COMMENT);
671       gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "tracker",
672           GST_TAG_APPLICATION_NAME);
673       gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "type_long",
674           GST_TAG_CODEC);
675       gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "date",
676           GST_TAG_DATE_TIME);
677       gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags,
678           "container_long", GST_TAG_CONTAINER_FORMAT);
679 
680       openmpt_dec->main_tags = tags;
681     } else {
682       GST_DEBUG_OBJECT (dec,
683           "no metadata keys found - not producing a tag list");
684     }
685   }
686 
687   /* Log any warnings that were produced by OpenMPT while loading */
688   {
689     char const *warnings =
690         openmpt_module_get_metadata (openmpt_dec->mod, "warnings");
691     if (warnings) {
692       if (*warnings)
693         GST_WARNING_OBJECT (openmpt_dec, "reported warnings during loading: %s",
694             warnings);
695       openmpt_free_string (warnings);
696     }
697   }
698 
699   return TRUE;
700 }
701 
702 
703 static GstTagList *
gst_openmpt_dec_get_main_tags(GstNonstreamAudioDecoder * dec)704 gst_openmpt_dec_get_main_tags (GstNonstreamAudioDecoder * dec)
705 {
706   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
707   return gst_tag_list_ref (openmpt_dec->main_tags);
708 }
709 
710 
711 static gboolean
gst_openmpt_dec_set_current_subsong(GstNonstreamAudioDecoder * dec,guint subsong,GstClockTime * initial_position)712 gst_openmpt_dec_set_current_subsong (GstNonstreamAudioDecoder * dec,
713     guint subsong, GstClockTime * initial_position)
714 {
715   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
716   g_return_val_if_fail (openmpt_dec->mod != NULL, FALSE);
717 
718   if (gst_openmpt_dec_select_subsong (openmpt_dec,
719           openmpt_dec->cur_subsong_mode, subsong)) {
720     GST_DEBUG_OBJECT (openmpt_dec,
721         "selected subsong %u and switching subsong mode to SINGLE", subsong);
722     openmpt_dec->cur_subsong_mode = GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE;
723     openmpt_dec->cur_subsong = subsong;
724     *initial_position = 0;
725     return TRUE;
726   } else {
727     GST_ERROR_OBJECT (openmpt_dec, "could not select subsong %u", subsong);
728     return FALSE;
729   }
730 }
731 
732 
733 static guint
gst_openmpt_dec_get_current_subsong(GstNonstreamAudioDecoder * dec)734 gst_openmpt_dec_get_current_subsong (GstNonstreamAudioDecoder * dec)
735 {
736   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
737   return openmpt_dec->cur_subsong;
738 }
739 
740 
741 static guint
gst_openmpt_dec_get_num_subsongs(GstNonstreamAudioDecoder * dec)742 gst_openmpt_dec_get_num_subsongs (GstNonstreamAudioDecoder * dec)
743 {
744   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
745   return openmpt_dec->num_subsongs;
746 }
747 
748 
749 static GstClockTime
gst_openmpt_dec_get_subsong_duration(GstNonstreamAudioDecoder * dec,guint subsong)750 gst_openmpt_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
751     guint subsong)
752 {
753   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
754   return (GstClockTime) (openmpt_dec->subsong_durations[subsong] * GST_SECOND);
755 }
756 
757 
758 static GstTagList *
gst_openmpt_dec_get_subsong_tags(GstNonstreamAudioDecoder * dec,guint subsong)759 gst_openmpt_dec_get_subsong_tags (GstNonstreamAudioDecoder * dec, guint subsong)
760 {
761   GstOpenMptDec *openmpt_dec;
762   char const *name;
763 
764   openmpt_dec = GST_OPENMPT_DEC (dec);
765 
766   name = openmpt_module_get_subsong_name (openmpt_dec->mod, subsong);
767   if (name != NULL) {
768     GstTagList *tags = NULL;
769 
770     if (*name) {
771       tags = gst_tag_list_new_empty ();
772       gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, "title", name, NULL);
773     }
774 
775     openmpt_free_string (name);
776 
777     return tags;
778   } else
779     return NULL;
780 }
781 
782 
783 static gboolean
gst_openmpt_dec_set_subsong_mode(GstNonstreamAudioDecoder * dec,GstNonstreamAudioSubsongMode mode,GstClockTime * initial_position)784 gst_openmpt_dec_set_subsong_mode (GstNonstreamAudioDecoder * dec,
785     GstNonstreamAudioSubsongMode mode, GstClockTime * initial_position)
786 {
787   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
788   g_return_val_if_fail (openmpt_dec->mod != NULL, FALSE);
789 
790   if (gst_openmpt_dec_select_subsong (openmpt_dec, mode,
791           openmpt_dec->cur_subsong)) {
792     GST_DEBUG_OBJECT (openmpt_dec, "set subsong mode");
793     openmpt_dec->cur_subsong_mode = mode;
794     *initial_position = 0;
795     return TRUE;
796   } else {
797     GST_ERROR_OBJECT (openmpt_dec, "could not set subsong mode");
798     return FALSE;
799   }
800 }
801 
802 
803 static gboolean
gst_openmpt_dec_set_num_loops(GstNonstreamAudioDecoder * dec,gint num_loops)804 gst_openmpt_dec_set_num_loops (GstNonstreamAudioDecoder * dec, gint num_loops)
805 {
806   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
807   openmpt_dec->num_loops = num_loops;
808 
809   if (openmpt_dec->mod != NULL) {
810     if (openmpt_module_set_repeat_count (openmpt_dec->mod, num_loops)) {
811       GST_DEBUG_OBJECT (openmpt_dec, "successfully set repeat count %d",
812           num_loops);
813       return TRUE;
814     } else {
815       GST_ERROR_OBJECT (openmpt_dec, "could not set repeat count %d",
816           num_loops);
817       return FALSE;
818     }
819   } else
820     return TRUE;
821 }
822 
823 
824 static gint
gst_openmpt_dec_get_num_loops(GstNonstreamAudioDecoder * dec)825 gst_openmpt_dec_get_num_loops (GstNonstreamAudioDecoder * dec)
826 {
827   GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
828   return openmpt_dec->num_loops;
829 }
830 
831 
832 static guint
gst_openmpt_dec_get_supported_output_modes(G_GNUC_UNUSED GstNonstreamAudioDecoder * dec)833 gst_openmpt_dec_get_supported_output_modes (G_GNUC_UNUSED
834     GstNonstreamAudioDecoder * dec)
835 {
836   return 1u << GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
837 }
838 
839 
840 static gboolean
gst_openmpt_dec_decode(GstNonstreamAudioDecoder * dec,GstBuffer ** buffer,guint * num_samples)841 gst_openmpt_dec_decode (GstNonstreamAudioDecoder * dec, GstBuffer ** buffer,
842     guint * num_samples)
843 {
844   GstOpenMptDec *openmpt_dec;
845   GstBuffer *outbuf;
846   GstMapInfo map;
847   size_t num_read_samples;
848   gsize outbuf_size;
849   GstAudioFormatInfo const *fmt_info;
850 
851   openmpt_dec = GST_OPENMPT_DEC (dec);
852 
853   fmt_info = gst_audio_format_get_info (openmpt_dec->sample_format);
854 
855   /* Allocate output buffer */
856   outbuf_size =
857       openmpt_dec->output_buffer_size * (fmt_info->width / 8) *
858       openmpt_dec->num_channels;
859   outbuf =
860       gst_nonstream_audio_decoder_allocate_output_buffer (dec, outbuf_size);
861   if (G_UNLIKELY (outbuf == NULL))
862     return FALSE;
863 
864   /* Write samples into the output buffer */
865 
866   gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
867 
868   switch (openmpt_dec->sample_format) {
869     case GST_AUDIO_FORMAT_S16:
870     {
871       int16_t *out_samples = (int16_t *) (map.data);
872       switch (openmpt_dec->num_channels) {
873         case 1:
874           num_read_samples =
875               openmpt_module_read_mono (openmpt_dec->mod,
876               openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
877               out_samples);
878           break;
879         case 2:
880           num_read_samples =
881               openmpt_module_read_interleaved_stereo (openmpt_dec->mod,
882               openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
883               out_samples);
884           break;
885         case 4:
886           num_read_samples =
887               openmpt_module_read_interleaved_quad (openmpt_dec->mod,
888               openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
889               out_samples);
890           break;
891         default:
892           g_assert_not_reached ();
893       }
894       break;
895     }
896     case GST_AUDIO_FORMAT_F32:
897     {
898       float *out_samples = (float *) (map.data);
899       switch (openmpt_dec->num_channels) {
900         case 1:
901           num_read_samples =
902               openmpt_module_read_float_mono (openmpt_dec->mod,
903               openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
904               out_samples);
905           break;
906         case 2:
907           num_read_samples =
908               openmpt_module_read_interleaved_float_stereo (openmpt_dec->mod,
909               openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
910               out_samples);
911           break;
912         case 4:
913           num_read_samples =
914               openmpt_module_read_interleaved_float_quad (openmpt_dec->mod,
915               openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
916               out_samples);
917           break;
918         default:
919           g_assert_not_reached ();
920       }
921       break;
922     }
923     default:
924     {
925       GST_ERROR_OBJECT (dec, "using unsupported sample format %s",
926           fmt_info->name);
927       g_assert_not_reached ();
928     }
929   }
930 
931   gst_buffer_unmap (outbuf, &map);
932 
933   if (num_read_samples == 0)
934     return FALSE;
935 
936   *buffer = outbuf;
937   *num_samples = num_read_samples;
938 
939   return TRUE;
940 }
941 
942 
943 static gboolean
gst_openmpt_dec_select_subsong(GstOpenMptDec * openmpt_dec,GstNonstreamAudioSubsongMode subsong_mode,gint openmpt_subsong)944 gst_openmpt_dec_select_subsong (GstOpenMptDec * openmpt_dec,
945     GstNonstreamAudioSubsongMode subsong_mode, gint openmpt_subsong)
946 {
947   switch (subsong_mode) {
948     case GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE:
949       GST_DEBUG_OBJECT (openmpt_dec, "setting subsong mode to SINGLE");
950       return openmpt_module_select_subsong (openmpt_dec->mod, openmpt_subsong);
951 
952     case GST_NONSTREAM_AUDIO_SUBSONG_MODE_ALL:
953       GST_DEBUG_OBJECT (openmpt_dec, "setting subsong mode to ALL");
954       return openmpt_module_select_subsong (openmpt_dec->mod, -1);
955 
956     case GST_NONSTREAM_AUDIO_SUBSONG_MODE_DECODER_DEFAULT:
957       /* NOTE: The OpenMPT documentation recommends to not bother
958        * calling openmpt_module_select_subsong() if the decoder
959        * default shall be used. However, the user might have switched
960        * the subsong mode from SINGLE or ALL to DECODER_DEFAULT,
961        * in which case we *do* have to set the default subsong index.
962        * So, just set the default index here. */
963       GST_DEBUG_OBJECT (openmpt_dec, "setting subsong mode to DECODER_DEFAULT");
964       return openmpt_module_select_subsong (openmpt_dec->mod,
965           openmpt_dec->default_openmpt_subsong);
966 
967     default:
968       g_assert_not_reached ();
969       return TRUE;
970   }
971 }
972