• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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