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