1 /* GStreamer
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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 Code based on modplugxmms
22 XMMS plugin:
23 Kenton Varda <temporal@gauge3d.org>
24 Sound Engine:
25 Olivier Lapicque <olivierl@jps.net>
26 */
27
28 /**
29 * SECTION:element-modplug
30 *
31 * Modplug uses the [modplug](http://modplug-xmms.sourceforge.net/) library to
32 * decode tracked music in the MOD/S3M/XM/IT and related formats.
33 *
34 * ## Example pipeline
35 *
36 * |[
37 * gst-launch-1.0 -v filesrc location=1990s-nostalgia.xm ! modplug ! audioconvert ! alsasink
38 * ]| Play a FastTracker xm file.
39 */
40
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44
45 /* Required to not get an undefined warning
46 * https://bugzilla.gnome.org/show_bug.cgi?id=613795
47 */
48 #ifndef WORDS_BIGENDIAN
49 #define WORDS_BIGENDIAN 0
50 #endif
51
52 #include <libmodplug/stdafx.h>
53 #include <libmodplug/sndfile.h>
54
55 #include "gstmodplug.h"
56
57 #include <gst/gst.h>
58 #include <stdlib.h>
59 #include <gst/audio/audio.h>
60
61 GST_DEBUG_CATEGORY_STATIC (modplug_debug);
62 #define GST_CAT_DEFAULT modplug_debug
63
64 enum
65 {
66 ARG_0,
67 ARG_SONGNAME,
68 ARG_REVERB,
69 ARG_REVERB_DEPTH,
70 ARG_REVERB_DELAY,
71 ARG_MEGABASS,
72 ARG_MEGABASS_AMOUNT,
73 ARG_MEGABASS_RANGE,
74 ARG_NOISE_REDUCTION,
75 ARG_SURROUND,
76 ARG_SURROUND_DEPTH,
77 ARG_SURROUND_DELAY,
78 ARG_OVERSAMP
79 };
80
81 #define DEFAULT_REVERB FALSE
82 #define DEFAULT_REVERB_DEPTH 30
83 #define DEFAULT_REVERB_DELAY 100
84 #define DEFAULT_MEGABASS FALSE
85 #define DEFAULT_MEGABASS_AMOUNT 40
86 #define DEFAULT_MEGABASS_RANGE 30
87 #define DEFAULT_SURROUND TRUE
88 #define DEFAULT_SURROUND_DEPTH 20
89 #define DEFAULT_SURROUND_DELAY 20
90 #define DEFAULT_OVERSAMP TRUE
91 #define DEFAULT_NOISE_REDUCTION TRUE
92
93 #define FORMATS "{ " GST_AUDIO_NE (S32) ", " GST_AUDIO_NE (S16) ", U8 }"
94
95 static GstStaticPadTemplate modplug_src_template_factory =
96 GST_STATIC_PAD_TEMPLATE ("src",
97 GST_PAD_SRC,
98 GST_PAD_ALWAYS,
99 GST_STATIC_CAPS ("audio/x-raw,"
100 " format = (string) " FORMATS ", "
101 " layout = (string) interleaved, "
102 " rate = (int) { 8000, 11025, 22050, 44100 },"
103 " channels = (int) [ 1, 2 ]"));
104
105 static GstStaticPadTemplate modplug_sink_template_factory =
106 GST_STATIC_PAD_TEMPLATE ("sink",
107 GST_PAD_SINK,
108 GST_PAD_ALWAYS,
109 GST_STATIC_CAPS ("audio/x-mod; audio/x-xm; audio/x-it; audio/x-s3m; "
110 "audio/x-stm"));
111
112 static void gst_modplug_dispose (GObject * object);
113 static void gst_modplug_set_property (GObject * object,
114 guint id, const GValue * value, GParamSpec * pspec);
115 static void gst_modplug_get_property (GObject * object,
116 guint id, GValue * value, GParamSpec * pspec);
117
118 static gboolean gst_modplug_src_event (GstPad * pad, GstObject * parent,
119 GstEvent * event);
120 static gboolean gst_modplug_src_query (GstPad * pad, GstObject * parent,
121 GstQuery * query);
122 static GstStateChangeReturn gst_modplug_change_state (GstElement * element,
123 GstStateChange transition);
124
125 static gboolean gst_modplug_sinkpad_activate (GstPad * pad, GstObject * parent);
126 static gboolean gst_modplug_sinkpad_activate_mode (GstPad * pad,
127 GstObject * parent, GstPadMode mode, gboolean active);
128 static void gst_modplug_loop (GstModPlug * element);
129
130 #define parent_class gst_modplug_parent_class
131 G_DEFINE_TYPE (GstModPlug, gst_modplug, GST_TYPE_ELEMENT);
132 GST_ELEMENT_REGISTER_DEFINE (modplug, "modplug",
133 GST_RANK_PRIMARY, GST_TYPE_MODPLUG);
134
135 static void
gst_modplug_class_init(GstModPlugClass * klass)136 gst_modplug_class_init (GstModPlugClass * klass)
137 {
138 GObjectClass *gobject_class;
139 GstElementClass *gstelement_class;
140
141 gobject_class = (GObjectClass *) klass;
142 gstelement_class = (GstElementClass *) klass;
143
144 gobject_class->set_property = gst_modplug_set_property;
145 gobject_class->get_property = gst_modplug_get_property;
146 gobject_class->dispose = gst_modplug_dispose;
147
148 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SONGNAME,
149 g_param_spec_string ("songname", "Songname", "The song name",
150 NULL, (GParamFlags) (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)));
151
152 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_REVERB,
153 g_param_spec_boolean ("reverb", "reverb", "Reverb",
154 DEFAULT_REVERB,
155 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
156
157 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_REVERB_DEPTH,
158 g_param_spec_int ("reverb-depth", "reverb depth", "Reverb depth",
159 0, 100, DEFAULT_REVERB_DEPTH,
160 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
161
162 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_REVERB_DELAY,
163 g_param_spec_int ("reverb-delay", "reverb delay", "Reverb delay",
164 0, 200, DEFAULT_REVERB_DELAY,
165 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
166
167 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MEGABASS,
168 g_param_spec_boolean ("megabass", "megabass", "Megabass",
169 DEFAULT_MEGABASS,
170 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
171
172 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MEGABASS_AMOUNT,
173 g_param_spec_int ("megabass-amount", "megabass amount", "Megabass amount",
174 0, 100, DEFAULT_MEGABASS_AMOUNT,
175 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
176
177 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MEGABASS_RANGE,
178 g_param_spec_int ("megabass-range", "megabass range", "Megabass range",
179 0, 100, DEFAULT_MEGABASS_RANGE,
180 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
181
182 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SURROUND,
183 g_param_spec_boolean ("surround", "surround", "Surround",
184 DEFAULT_SURROUND,
185 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
186
187 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SURROUND_DEPTH,
188 g_param_spec_int ("surround-depth", "surround depth", "Surround depth",
189 0, 100, DEFAULT_SURROUND_DEPTH,
190 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
191
192 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SURROUND_DELAY,
193 g_param_spec_int ("surround-delay", "surround delay", "Surround delay",
194 0, 40, DEFAULT_SURROUND_DELAY,
195 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
196
197 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_OVERSAMP,
198 g_param_spec_boolean ("oversamp", "oversamp", "oversamp",
199 DEFAULT_OVERSAMP,
200 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
201
202 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NOISE_REDUCTION,
203 g_param_spec_boolean ("noise-reduction", "noise reduction",
204 "noise reduction", DEFAULT_NOISE_REDUCTION,
205 (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
206
207 gstelement_class->change_state = gst_modplug_change_state;
208
209 gst_element_class_add_static_pad_template (gstelement_class, &modplug_sink_template_factory);
210 gst_element_class_add_static_pad_template (gstelement_class, &modplug_src_template_factory);
211
212 gst_element_class_set_static_metadata (gstelement_class, "ModPlug",
213 "Codec/Decoder/Audio", "Module decoder based on modplug engine",
214 "Jeremy SIMON <jsimon13@yahoo.fr>");
215
216 GST_DEBUG_CATEGORY_INIT (modplug_debug, "modplug", 0, "ModPlug element");
217 }
218
219 static void
gst_modplug_init(GstModPlug * modplug)220 gst_modplug_init (GstModPlug * modplug)
221 {
222 /* create the sink and src pads */
223 modplug->sinkpad =
224 gst_pad_new_from_static_template (&modplug_sink_template_factory, "sink");
225 gst_pad_set_activate_function (modplug->sinkpad,
226 GST_DEBUG_FUNCPTR (gst_modplug_sinkpad_activate));
227 gst_pad_set_activatemode_function (modplug->sinkpad,
228 GST_DEBUG_FUNCPTR (gst_modplug_sinkpad_activate_mode));
229 gst_element_add_pad (GST_ELEMENT (modplug), modplug->sinkpad);
230
231 modplug->srcpad =
232 gst_pad_new_from_static_template (&modplug_src_template_factory, "src");
233 gst_pad_set_event_function (modplug->srcpad,
234 GST_DEBUG_FUNCPTR (gst_modplug_src_event));
235 gst_pad_set_query_function (modplug->srcpad,
236 GST_DEBUG_FUNCPTR (gst_modplug_src_query));
237 gst_element_add_pad (GST_ELEMENT (modplug), modplug->srcpad);
238
239 modplug->reverb = DEFAULT_REVERB;
240 modplug->reverb_depth = DEFAULT_REVERB_DEPTH;
241 modplug->reverb_delay = DEFAULT_REVERB_DELAY;
242 modplug->megabass = DEFAULT_MEGABASS;
243 modplug->megabass_amount = DEFAULT_MEGABASS_AMOUNT;
244 modplug->megabass_range = DEFAULT_MEGABASS_RANGE;
245 modplug->surround = DEFAULT_SURROUND;
246 modplug->surround_depth = DEFAULT_SURROUND_DEPTH;
247 modplug->surround_delay = DEFAULT_SURROUND_DELAY;
248 modplug->oversamp = DEFAULT_OVERSAMP;
249 modplug->noise_reduction = DEFAULT_NOISE_REDUCTION;
250
251 modplug->bits = 16;
252 modplug->channel = 2;
253 modplug->frequency = 44100;
254 }
255
256
257 static void
gst_modplug_dispose(GObject * object)258 gst_modplug_dispose (GObject * object)
259 {
260 GstModPlug *modplug = GST_MODPLUG (object);
261
262 G_OBJECT_CLASS (parent_class)->dispose (object);
263
264 if (modplug->buffer) {
265 gst_buffer_unref (modplug->buffer);
266 modplug->buffer = NULL;
267 }
268 }
269
270 static gboolean
gst_modplug_src_query(GstPad * pad,GstObject * parent,GstQuery * query)271 gst_modplug_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
272 {
273 GstModPlug *modplug;
274 gboolean res = FALSE;
275
276 modplug = GST_MODPLUG (parent);
277
278 switch (GST_QUERY_TYPE (query)) {
279 case GST_QUERY_DURATION:
280 {
281 GstFormat format;
282
283 if (!modplug->mSoundFile)
284 goto done;
285
286 gst_query_parse_duration (query, &format, NULL);
287 if (format == GST_FORMAT_TIME) {
288 gst_query_set_duration (query, format, modplug->song_length);
289 res = TRUE;
290 }
291 }
292 break;
293 case GST_QUERY_POSITION:
294 {
295 GstFormat format;
296
297 if (!modplug->mSoundFile)
298 goto done;
299
300 gst_query_parse_position (query, &format, NULL);
301 if (format == GST_FORMAT_TIME) {
302 gint64 pos;
303 guint32 max;
304
305 max = modplug->mSoundFile->GetMaxPosition();
306 if (max > 0) {
307 pos = (modplug->song_length * modplug->mSoundFile->GetCurrentPos ()) /
308 max;
309 gst_query_set_position (query, format, pos);
310 res = TRUE;
311 }
312 }
313 }
314 break;
315 default:
316 res = gst_pad_query_default (pad, parent, query);
317 break;
318 }
319
320 done:
321 return res;
322 }
323
324 static gboolean
gst_modplug_do_seek(GstModPlug * modplug,GstEvent * event)325 gst_modplug_do_seek (GstModPlug * modplug, GstEvent * event)
326 {
327 gdouble rate;
328 GstFormat format;
329 GstSeekFlags flags;
330 GstSeekType cur_type, stop_type;
331 gboolean flush;
332 gint64 cur, stop;
333 GstSegment seg;
334
335 if (modplug->frequency == 0)
336 goto no_song;
337
338 gst_event_parse_seek (event, &rate, &format, &flags, &cur_type, &cur,
339 &stop_type, &stop);
340
341 if (format != GST_FORMAT_TIME)
342 goto no_time;
343
344 /* FIXME: we should be using GstSegment for all this */
345 if (cur_type != GST_SEEK_TYPE_SET || stop_type != GST_SEEK_TYPE_NONE)
346 goto not_supported;
347
348 if (stop_type == GST_SEEK_TYPE_NONE)
349 stop = GST_CLOCK_TIME_NONE;
350 if (!GST_CLOCK_TIME_IS_VALID (stop) && modplug->song_length > 0)
351 stop = modplug->song_length;
352
353 cur = CLAMP (cur, -1, modplug->song_length);
354
355 GST_DEBUG_OBJECT (modplug, "seek to %" GST_TIME_FORMAT,
356 GST_TIME_ARGS ((guint64) cur));
357
358 modplug->seek_at = cur;
359
360 flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH);
361
362 if (flush) {
363 gst_pad_push_event (modplug->srcpad, gst_event_new_flush_start ());
364 } else {
365 gst_pad_stop_task (modplug->sinkpad);
366 }
367
368 GST_PAD_STREAM_LOCK (modplug->sinkpad);
369
370 if (flags & GST_SEEK_FLAG_SEGMENT) {
371 gst_element_post_message (GST_ELEMENT (modplug),
372 gst_message_new_segment_start (GST_OBJECT (modplug), format, cur));
373 }
374
375 if (flush) {
376 gst_pad_push_event (modplug->srcpad, gst_event_new_flush_stop (TRUE));
377 }
378
379 GST_LOG_OBJECT (modplug, "sending newsegment from %" GST_TIME_FORMAT "-%"
380 GST_TIME_FORMAT ", pos=%" GST_TIME_FORMAT,
381 GST_TIME_ARGS ((guint64) cur), GST_TIME_ARGS ((guint64) stop),
382 GST_TIME_ARGS ((guint64) cur));
383
384 gst_segment_init (&seg, GST_FORMAT_TIME);
385 seg.rate = rate;
386 seg.start = cur;
387 seg.stop = stop;
388 seg.time = cur;
389 gst_pad_push_event (modplug->srcpad, gst_event_new_segment (&seg));
390
391 modplug->offset =
392 gst_util_uint64_scale_int (cur, modplug->frequency, GST_SECOND);
393
394 gst_pad_start_task (modplug->sinkpad,
395 (GstTaskFunction) gst_modplug_loop, modplug, NULL);
396
397 GST_PAD_STREAM_UNLOCK (modplug->sinkpad);
398
399 return TRUE;
400
401 /* ERROR */
402 no_song:
403 {
404 GST_DEBUG_OBJECT (modplug, "no song loaded yet");
405 return FALSE;
406 }
407 no_time:
408 {
409 GST_DEBUG_OBJECT (modplug, "seeking is only supported in TIME format");
410 return FALSE;
411 }
412 not_supported:
413 {
414 GST_DEBUG_OBJECT (modplug, "unsupported seek type");
415 return FALSE;
416 }
417 }
418
419 static gboolean
gst_modplug_src_event(GstPad * pad,GstObject * parent,GstEvent * event)420 gst_modplug_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
421 {
422 GstModPlug *modplug;
423 gboolean res = FALSE;
424
425 modplug = GST_MODPLUG (parent);
426
427 switch (GST_EVENT_TYPE (event)) {
428 case GST_EVENT_SEEK:
429 res = gst_modplug_do_seek (modplug, event);
430 break;
431 default:
432 res = gst_pad_event_default (pad, parent, event);
433 break;
434 }
435 return res;
436 }
437
438 static gboolean
gst_modplug_load_song(GstModPlug * modplug)439 gst_modplug_load_song (GstModPlug * modplug)
440 {
441 GstCaps *newcaps;
442 GstStructure *structure;
443 GstMapInfo map;
444 const gchar *format;
445
446 GST_DEBUG_OBJECT (modplug, "Setting caps");
447
448 /* negotiate srcpad caps */
449 if ((newcaps = gst_pad_get_allowed_caps (modplug->srcpad)) == NULL) {
450 newcaps = gst_pad_get_pad_template_caps (modplug->srcpad);
451 }
452 newcaps = gst_caps_make_writable (newcaps);
453
454 GST_DEBUG_OBJECT (modplug, "allowed caps %" GST_PTR_FORMAT, newcaps);
455
456 structure = gst_caps_get_structure (newcaps, 0);
457
458 if (!gst_structure_fixate_field_string (structure, "format",
459 GST_AUDIO_NE (S16)))
460 GST_WARNING_OBJECT (modplug, "Failed to fixate format to S16NE");
461 if (!gst_structure_fixate_field_nearest_int (structure, "rate", 44100))
462 GST_WARNING_OBJECT (modplug, "Failed to fixate rate to 44100");
463 if (!gst_structure_fixate_field_nearest_int (structure, "channels", 2))
464 GST_WARNING_OBJECT (modplug,
465 "Failed to fixate number of channels to stereo");
466
467 GST_DEBUG_OBJECT (modplug, "normalized caps %" GST_PTR_FORMAT, newcaps);
468
469 newcaps = gst_caps_fixate (newcaps);
470
471 GST_DEBUG_OBJECT (modplug, "fixated caps %" GST_PTR_FORMAT, newcaps);
472
473 /* set up modplug to output the negotiated format */
474 structure = gst_caps_get_structure (newcaps, 0);
475 format = gst_structure_get_string (structure, "format");
476
477 if (g_str_equal (format, GST_AUDIO_NE (S32)))
478 modplug->bits = 32;
479 else if (g_str_equal (format, GST_AUDIO_NE (S16)))
480 modplug->bits = 16;
481 else
482 modplug->bits = 8;
483
484 gst_structure_get_int (structure, "channels", &modplug->channel);
485 gst_structure_get_int (structure, "rate", &modplug->frequency);
486
487 GST_DEBUG_OBJECT (modplug,
488 "Audio settings: %d bits, %d channel(s), %d Hz sampling rate",
489 modplug->bits, modplug->channel, modplug->frequency);
490
491 gst_pad_set_caps (modplug->srcpad, newcaps);
492 gst_caps_unref (newcaps);
493
494 modplug->read_samples = 1152;
495 modplug->read_bytes =
496 modplug->read_samples * modplug->channel * modplug->bits / 8;
497
498 GST_DEBUG_OBJECT (modplug, "Loading song");
499
500 modplug->mSoundFile = new CSoundFile;
501
502 modplug->mSoundFile->SetWaveConfig (modplug->frequency, modplug->bits,
503 modplug->channel);
504
505 modplug->mSoundFile->SetWaveConfigEx (modplug->surround, !modplug->oversamp,
506 modplug->reverb, true, modplug->megabass, modplug->noise_reduction, true);
507 modplug->mSoundFile->SetResamplingMode (SRCMODE_POLYPHASE);
508
509 if (modplug->surround)
510 modplug->mSoundFile->SetSurroundParameters (modplug->surround_depth,
511 modplug->surround_delay);
512
513 if (modplug->megabass)
514 modplug->mSoundFile->SetXBassParameters (modplug->megabass_amount,
515 modplug->megabass_range);
516
517 if (modplug->reverb)
518 modplug->mSoundFile->SetReverbParameters (modplug->reverb_depth,
519 modplug->reverb_delay);
520
521
522 gst_buffer_map (modplug->buffer, &map, GST_MAP_READ);
523 if (!modplug->mSoundFile->Create (map.data, modplug->song_size))
524 goto load_error;
525 gst_buffer_unmap (modplug->buffer, &map);
526
527 modplug->song_length = modplug->mSoundFile->GetSongTime () * GST_SECOND;
528 modplug->seek_at = -1;
529
530 GST_INFO_OBJECT (modplug, "Song length: %" GST_TIME_FORMAT,
531 GST_TIME_ARGS ((guint64) modplug->song_length));
532
533 return TRUE;
534
535 /* ERRORS */
536 load_error:
537 {
538 gst_buffer_unmap (modplug->buffer, &map);
539 GST_ELEMENT_ERROR (modplug, STREAM, DECODE, (NULL),
540 ("Unable to load song"));
541 return FALSE;
542 }
543 }
544
545 static gboolean
gst_modplug_sinkpad_activate(GstPad * sinkpad,GstObject * parent)546 gst_modplug_sinkpad_activate (GstPad * sinkpad, GstObject * parent)
547 {
548 GstQuery *query;
549 gboolean pull_mode;
550
551 query = gst_query_new_scheduling ();
552
553 if (!gst_pad_peer_query (sinkpad, query)) {
554 gst_query_unref (query);
555 goto activate_push;
556 }
557
558 pull_mode = gst_query_has_scheduling_mode_with_flags (query,
559 GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
560 gst_query_unref (query);
561
562 if (!pull_mode)
563 goto activate_push;
564
565 GST_DEBUG_OBJECT (sinkpad, "activating pull");
566 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
567
568 activate_push:
569 {
570 GST_DEBUG_OBJECT (sinkpad, "activating push");
571 return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
572 }
573 }
574
575 static gboolean
gst_modplug_sinkpad_activate_mode(GstPad * pad,GstObject * parent,GstPadMode mode,gboolean active)576 gst_modplug_sinkpad_activate_mode (GstPad * pad, GstObject * parent,
577 GstPadMode mode, gboolean active)
578 {
579 GstModPlug *modplug = GST_MODPLUG (parent);
580 gboolean res;
581
582 switch (mode) {
583 case GST_PAD_MODE_PUSH:
584 res = TRUE;
585 break;
586 case GST_PAD_MODE_PULL:
587 if (active) {
588 res = gst_pad_start_task (pad, (GstTaskFunction) gst_modplug_loop,
589 modplug, NULL);
590 } else {
591 res = gst_pad_stop_task (pad);
592 }
593 break;
594 default:
595 res = FALSE;
596 break;
597 }
598 return res;
599 }
600
601 static gboolean
gst_modplug_get_upstream_size(GstModPlug * modplug,gint64 * length)602 gst_modplug_get_upstream_size (GstModPlug * modplug, gint64 * length)
603 {
604 gboolean res = FALSE;
605 GstPad *peer;
606
607 peer = gst_pad_get_peer (modplug->sinkpad);
608 if (peer == NULL)
609 return FALSE;
610
611 if (gst_pad_query_duration (peer, GST_FORMAT_BYTES, length) && *length >= 0) {
612 res = TRUE;
613 }
614
615 gst_object_unref (peer);
616 return res;
617 }
618
619 static void
gst_modplug_loop(GstModPlug * modplug)620 gst_modplug_loop (GstModPlug * modplug)
621 {
622 GstFlowReturn flow;
623 GstBuffer *out = NULL;
624 GstMapInfo map;
625
626 g_assert (GST_IS_MODPLUG (modplug));
627
628 /* first, get the size of the song */
629 if (!modplug->song_size) {
630 if (!gst_modplug_get_upstream_size (modplug, &modplug->song_size)) {
631 GST_ELEMENT_ERROR (modplug, STREAM, DECODE, (NULL),
632 ("Unable to load song"));
633 goto pause;
634 }
635
636 if (modplug->buffer) {
637 gst_buffer_unref (modplug->buffer);
638 }
639 modplug->buffer = gst_buffer_new_and_alloc (modplug->song_size);
640 modplug->offset = 0;
641 }
642
643 /* read in the song data */
644 if (!modplug->mSoundFile) {
645 GstBuffer *buffer = NULL;
646 guint64 read_size = modplug->song_size - modplug->offset;
647
648 if (read_size > 4096)
649 read_size = 4096;
650
651 flow =
652 gst_pad_pull_range (modplug->sinkpad, modplug->offset, read_size,
653 &buffer);
654 if (flow != GST_FLOW_OK) {
655 GST_ELEMENT_ERROR (modplug, STREAM, DECODE, (NULL),
656 ("Unable to load song"));
657 goto pause;
658 }
659
660 /* GST_LOG_OBJECT (modplug, "Read %u bytes", GST_BUFFER_SIZE (buffer)); */
661 gst_buffer_map (buffer, &map, GST_MAP_READ);
662 gst_buffer_fill (modplug->buffer, modplug->offset, map.data, map.size);
663 gst_buffer_unmap (buffer, &map);
664 gst_buffer_unref (buffer);
665
666 modplug->offset += read_size;
667
668 /* actually load it */
669 if (modplug->offset == modplug->song_size) {
670 GstTagList *tags;
671 gboolean ok;
672 #define COMMENT_SIZE 16384
673 gchar comment[COMMENT_SIZE];
674 GstSegment seg;
675
676 ok = gst_modplug_load_song (modplug);
677 gst_buffer_unref (modplug->buffer);
678 modplug->buffer = NULL;
679 modplug->offset = 0;
680
681 if (!ok) {
682 goto pause;
683 }
684
685 gst_segment_init (&seg, GST_FORMAT_TIME);
686 seg.stop = modplug->song_length;
687 gst_pad_push_event (modplug->srcpad, gst_event_new_segment (&seg));
688
689 /* get and send metadata */
690 tags = gst_tag_list_new_empty ();
691 gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
692 GST_TAG_TITLE, modplug->mSoundFile->GetTitle (),
693 GST_TAG_BEATS_PER_MINUTE,
694 (gdouble) modplug->mSoundFile->GetMusicTempo (), NULL);
695
696 if (modplug->mSoundFile->GetSongComments ((gchar *) & comment,
697 COMMENT_SIZE, 32)) {
698 comment[COMMENT_SIZE - 1] = '\0';
699 gst_tag_list_add (tags, GST_TAG_MERGE_APPEND,
700 GST_TAG_COMMENT, comment, NULL);
701 }
702 gst_pad_push_event (modplug->srcpad, gst_event_new_tag (tags));
703 } else {
704 /* not fully loaded yet */
705 return;
706 }
707 }
708
709 /* could move this to gst_modplug_src_event
710 * if libmodplug was definitely thread safe.. */
711 if (modplug->seek_at != -1) {
712 gint seek_to_pos;
713 gfloat temp;
714
715 temp = (gfloat) modplug->song_length / modplug->seek_at;
716 seek_to_pos = (gint) (modplug->mSoundFile->GetMaxPosition () / temp);
717
718 GST_DEBUG_OBJECT (modplug, "Seeking to row %d", seek_to_pos);
719
720 modplug->mSoundFile->SetCurrentPos (seek_to_pos);
721 modplug->seek_at = -1;
722 }
723
724 /* read and output a buffer */
725 GST_LOG_OBJECT (modplug, "Read %d bytes", (gint) modplug->read_bytes);
726 /* libmodplug 0.8.7 trashes memory */
727 out = gst_buffer_new_allocate (NULL, modplug->read_bytes * 2, NULL);
728
729 gst_buffer_map (out, &map, GST_MAP_WRITE);
730 if (!modplug->mSoundFile->Read (map.data, modplug->read_bytes)) {
731 gst_buffer_unmap (out, &map);
732 goto eos;
733 }
734 gst_buffer_unmap (out, &map);
735 gst_buffer_resize (out, 0, modplug->read_bytes);
736
737 GST_BUFFER_DURATION (out) =
738 gst_util_uint64_scale_int (modplug->read_samples, GST_SECOND,
739 modplug->frequency);
740 GST_BUFFER_OFFSET (out) = modplug->offset;
741 GST_BUFFER_TIMESTAMP (out) =
742 gst_util_uint64_scale_int (modplug->offset, GST_SECOND,
743 modplug->frequency);
744
745 modplug->offset += modplug->read_samples;
746
747 flow = gst_pad_push (modplug->srcpad, out);
748
749 if (flow != GST_FLOW_OK) {
750 GST_LOG_OBJECT (modplug, "pad push flow: %s", gst_flow_get_name (flow));
751 goto pause;
752 }
753
754 return;
755
756 eos:
757 {
758 gst_buffer_unref (out);
759 GST_INFO_OBJECT (modplug, "EOS");
760 gst_pad_push_event (modplug->srcpad, gst_event_new_eos ());
761 goto pause;
762 }
763
764 pause:
765 {
766 GST_INFO_OBJECT (modplug, "Pausing");
767 gst_pad_pause_task (modplug->sinkpad);
768 }
769 }
770
771
772 static GstStateChangeReturn
gst_modplug_change_state(GstElement * element,GstStateChange transition)773 gst_modplug_change_state (GstElement * element, GstStateChange transition)
774 {
775 GstModPlug *modplug;
776 GstStateChangeReturn ret;
777
778 modplug = GST_MODPLUG (element);
779
780 switch (transition) {
781 case GST_STATE_CHANGE_READY_TO_PAUSED:
782 modplug->buffer = NULL;
783 modplug->offset = 0;
784 modplug->song_size = 0;
785 break;
786 default:
787 break;
788 }
789
790 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
791 if (ret == GST_STATE_CHANGE_FAILURE)
792 return ret;
793
794 switch (transition) {
795 case GST_STATE_CHANGE_PAUSED_TO_READY:
796 if (modplug->buffer) {
797 gst_buffer_unref (modplug->buffer);
798 modplug->buffer = NULL;
799 }
800 if (modplug->mSoundFile) {
801 modplug->mSoundFile->Destroy ();
802 delete modplug->mSoundFile;
803 modplug->mSoundFile = NULL;
804 }
805 break;
806 default:
807 break;
808 }
809
810 return GST_STATE_CHANGE_SUCCESS;
811 }
812
813
814 static void
gst_modplug_set_property(GObject * object,guint id,const GValue * value,GParamSpec * pspec)815 gst_modplug_set_property (GObject * object, guint id, const GValue * value,
816 GParamSpec * pspec)
817 {
818 GstModPlug *modplug;
819
820 g_return_if_fail (GST_IS_MODPLUG (object));
821 modplug = GST_MODPLUG (object);
822
823 switch (id) {
824 case ARG_REVERB:
825 modplug->reverb = g_value_get_boolean (value);
826 break;
827 case ARG_REVERB_DEPTH:
828 modplug->reverb_depth = g_value_get_int (value);
829 break;
830 case ARG_REVERB_DELAY:
831 modplug->reverb_delay = g_value_get_int (value);
832 break;
833 case ARG_MEGABASS:
834 modplug->megabass = g_value_get_boolean (value);
835 break;
836 case ARG_MEGABASS_AMOUNT:
837 modplug->megabass_amount = g_value_get_int (value);
838 break;
839 case ARG_MEGABASS_RANGE:
840 modplug->megabass_range = g_value_get_int (value);
841 break;
842 case ARG_NOISE_REDUCTION:
843 modplug->noise_reduction = g_value_get_boolean (value);
844 break;
845 case ARG_SURROUND:
846 modplug->surround = g_value_get_boolean (value);
847 break;
848 case ARG_SURROUND_DEPTH:
849 modplug->surround_depth = g_value_get_int (value);
850 break;
851 case ARG_SURROUND_DELAY:
852 modplug->surround_delay = g_value_get_int (value);
853 break;
854 default:
855 break;
856 }
857 }
858
859 static void
gst_modplug_get_property(GObject * object,guint id,GValue * value,GParamSpec * pspec)860 gst_modplug_get_property (GObject * object, guint id, GValue * value,
861 GParamSpec * pspec)
862 {
863 GstModPlug *modplug;
864
865 g_return_if_fail (GST_IS_MODPLUG (object));
866 modplug = GST_MODPLUG (object);
867
868 switch (id) {
869 case ARG_REVERB:
870 g_value_set_boolean (value, modplug->reverb);
871 break;
872 case ARG_REVERB_DEPTH:
873 g_value_set_int (value, modplug->reverb_depth);
874 break;
875 case ARG_REVERB_DELAY:
876 g_value_set_int (value, modplug->reverb_delay);
877 break;
878 case ARG_MEGABASS:
879 g_value_set_boolean (value, modplug->megabass);
880 break;
881 case ARG_MEGABASS_AMOUNT:
882 g_value_set_int (value, modplug->megabass_amount);
883 break;
884 case ARG_MEGABASS_RANGE:
885 g_value_set_int (value, modplug->megabass_range);
886 break;
887 case ARG_SURROUND:
888 g_value_set_boolean (value, modplug->surround);
889 break;
890 case ARG_SURROUND_DEPTH:
891 g_value_set_int (value, modplug->surround_depth);
892 break;
893 case ARG_SURROUND_DELAY:
894 g_value_set_int (value, modplug->surround_delay);
895 break;
896 case ARG_NOISE_REDUCTION:
897 g_value_set_boolean (value, modplug->noise_reduction);
898 break;
899 default:
900 break;
901 }
902 }
903
904 static gboolean
plugin_init(GstPlugin * plugin)905 plugin_init (GstPlugin * plugin)
906 {
907 return GST_ELEMENT_REGISTER (modplug, plugin);
908 }
909
910 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
911 GST_VERSION_MINOR,
912 modplug,
913 ".MOD audio decoding",
914 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
915