2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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.
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.
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., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 Code based on modplugxmms
23 Kenton Varda <temporal@gauge3d.org>
25 Olivier Lapicque <olivierl@jps.net>
32 #include "libmodplug/stdafx.h"
33 #include "libmodplug/sndfile.h"
35 #include "gstmodplug.h"
39 #include <gst/audio/audio.h>
41 /* elementfactory information */
42 GstElementDetails modplug_details = {
44 "Codec/Decoder/Audio",
45 "Module decoder based on modplug engine",
46 "Jeremy SIMON <jsimon13@yahoo.fr>"
50 /* Filter signals and args */
72 static GstStaticPadTemplate modplug_src_template_factory =
73 GST_STATIC_PAD_TEMPLATE (
79 "endianness = (int) BYTE_ORDER, "
80 "signed = (boolean) TRUE, "
83 "rate = (int) { 8000, 11025, 22050, 44100 }, " /* FIXME? */
84 "channels = (int) [ 1, 2 ]; "
86 "signed = (boolean) FALSE, "
89 "rate = (int) { 8000, 11025, 22050, 44100 }, " /* FIXME? */
90 "channels = (int) [ 1, 2 ]"
94 static GstStaticPadTemplate modplug_sink_template_factory =
95 GST_STATIC_PAD_TEMPLATE (
99 GST_STATIC_CAPS ("audio/x-mod")
103 MODPLUG_STATE_NEED_TUNE = 1,
104 MODPLUG_STATE_LOAD_TUNE = 2,
105 MODPLUG_STATE_PLAY_TUNE = 3,
108 static void gst_modplug_base_init (GstModPlugClass *klass);
109 static void gst_modplug_class_init (GstModPlugClass *klass);
110 static void gst_modplug_init (GstModPlug *filter);
111 static void gst_modplug_set_property (GObject *object,
115 static void gst_modplug_get_property (GObject *object,
119 static GstPadLinkReturn
120 gst_modplug_srclink (GstPad *pad, const GstCaps *caps);
121 static void gst_modplug_loop (GstElement *element);
122 static void gst_modplug_setup (GstModPlug *modplug);
123 static const GstFormat *
124 gst_modplug_get_formats (GstPad *pad);
125 static const GstQueryType *
126 gst_modplug_get_query_types (GstPad *pad);
127 static gboolean gst_modplug_src_event (GstPad *pad, GstEvent *event);
128 static gboolean gst_modplug_src_query (GstPad *pad,
132 static GstElementStateReturn
133 gst_modplug_change_state (GstElement *element);
135 static GstElementClass *parent_class = NULL;
138 gst_modplug_get_type(void) {
139 static GType modplug_type = 0;
142 static const GTypeInfo modplug_info = {
143 sizeof(GstModPlugClass),
144 (GBaseInitFunc)gst_modplug_base_init,
146 (GClassInitFunc)gst_modplug_class_init,
151 (GInstanceInitFunc)gst_modplug_init,
154 modplug_type = g_type_register_static(GST_TYPE_ELEMENT, "GstModPlug", &modplug_info, (GTypeFlags)0);
160 gst_modplug_base_init (GstModPlugClass *klass)
162 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
164 gst_element_class_add_pad_template (element_class,
165 gst_static_pad_template_get (&modplug_sink_template_factory));
166 gst_element_class_add_pad_template (element_class,
167 gst_static_pad_template_get (&modplug_src_template_factory));
168 gst_element_class_set_details (element_class, &modplug_details);
172 gst_modplug_class_init (GstModPlugClass *klass)
174 GObjectClass *gobject_class;
175 GstElementClass *gstelement_class;
177 gobject_class = (GObjectClass*)klass;
178 gstelement_class = (GstElementClass*)klass;
180 parent_class = GST_ELEMENT_CLASS( g_type_class_ref(GST_TYPE_ELEMENT));
182 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SONGNAME,
183 g_param_spec_string("songname","Songname","The song name",
184 "", G_PARAM_READABLE));
186 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_REVERB,
187 g_param_spec_boolean("reverb", "reverb", "reverb",
188 FALSE, (GParamFlags)G_PARAM_READWRITE ));
190 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_REVERB_DEPTH,
191 g_param_spec_int("reverb_depth", "reverb_depth", "reverb_depth",
192 0, 100, 30, (GParamFlags)G_PARAM_READWRITE ));
194 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_REVERB_DELAY,
195 g_param_spec_int("reverb_delay", "reverb_delay", "reverb_delay",
196 0, 200, 100, (GParamFlags)G_PARAM_READWRITE ));
198 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MEGABASS,
199 g_param_spec_boolean("megabass", "megabass", "megabass",
200 FALSE, (GParamFlags)G_PARAM_READWRITE ));
202 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MEGABASS_AMOUNT,
203 g_param_spec_int("megabass_amount", "megabass_amount", "megabass_amount",
204 0, 100, 40, (GParamFlags)G_PARAM_READWRITE ));
206 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MEGABASS_RANGE,
207 g_param_spec_int("megabass_range", "megabass_range", "megabass_range",
208 0, 100, 30, (GParamFlags)G_PARAM_READWRITE ));
210 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SURROUND,
211 g_param_spec_boolean("surround", "surround", "surround",
212 TRUE, (GParamFlags)G_PARAM_READWRITE ));
214 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SURROUND_DEPTH,
215 g_param_spec_int("surround_depth", "surround_depth", "surround_depth",
216 0, 100, 20, (GParamFlags)G_PARAM_READWRITE ));
218 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SURROUND_DELAY,
219 g_param_spec_int("surround_delay", "surround_delay", "surround_delay",
220 0, 40, 20, (GParamFlags)G_PARAM_READWRITE ));
222 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_OVERSAMP,
223 g_param_spec_boolean("oversamp", "oversamp", "oversamp",
224 TRUE, (GParamFlags)G_PARAM_READWRITE ));
226 g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NOISE_REDUCTION,
227 g_param_spec_boolean("noise_reduction", "noise_reduction", "noise_reduction",
228 TRUE, (GParamFlags)G_PARAM_READWRITE ));
230 gobject_class->set_property = gst_modplug_set_property;
231 gobject_class->get_property = gst_modplug_get_property;
233 gstelement_class->change_state = gst_modplug_change_state;
237 gst_modplug_init (GstModPlug *modplug)
239 modplug->sinkpad = gst_pad_new_from_template (gst_static_pad_template_get (&modplug_sink_template_factory), "sink");
240 gst_element_add_pad (GST_ELEMENT(modplug), modplug->sinkpad);
242 modplug->srcpad = gst_pad_new_from_template (gst_static_pad_template_get (&modplug_src_template_factory), "src");
243 gst_element_add_pad (GST_ELEMENT(modplug), modplug->srcpad);
244 gst_pad_set_link_function (modplug->srcpad, gst_modplug_srclink);
246 gst_pad_set_event_function (modplug->srcpad, (GstPadEventFunction)GST_DEBUG_FUNCPTR(gst_modplug_src_event));
247 gst_pad_set_query_function (modplug->srcpad, gst_modplug_src_query);
248 gst_pad_set_query_type_function (modplug->srcpad, (GstPadQueryTypeFunction) GST_DEBUG_FUNCPTR (gst_modplug_get_query_types));
249 gst_pad_set_formats_function (modplug->srcpad, (GstPadFormatsFunction)GST_DEBUG_FUNCPTR (gst_modplug_get_formats));
251 gst_element_set_loop_function (GST_ELEMENT (modplug), gst_modplug_loop);
253 modplug->reverb = FALSE;
254 modplug->reverb_depth = 30;
255 modplug->reverb_delay = 100;
256 modplug->megabass = FALSE;
257 modplug->megabass_amount = 40;
258 modplug->megabass_range = 30;
259 modplug->surround = TRUE;
260 modplug->surround_depth = 20;
261 modplug->surround_delay = 20;
262 modplug->oversamp = TRUE;
263 modplug->noise_reduction = TRUE;
265 modplug->_16bit = TRUE;
266 modplug->channel = 2;
267 modplug->frequency = 44100;
268 modplug->audiobuffer = NULL;
269 modplug->buffer_in = NULL;
271 modplug->state = MODPLUG_STATE_NEED_TUNE;
275 gst_modplug_setup (GstModPlug *modplug)
278 modplug->mSoundFile->SetWaveConfig (modplug->frequency, 16, modplug->channel);
280 modplug->mSoundFile->SetWaveConfig (modplug->frequency, 8, modplug->channel);
282 modplug->mSoundFile->SetWaveConfigEx (modplug->surround, !modplug->oversamp, modplug->reverb, true, modplug->megabass, modplug->noise_reduction, true);
283 modplug->mSoundFile->SetResamplingMode (SRCMODE_POLYPHASE);
285 if (modplug->surround)
286 modplug->mSoundFile->SetSurroundParameters (modplug->surround_depth, modplug->surround_delay);
288 if (modplug->megabass)
289 modplug->mSoundFile->SetXBassParameters (modplug->megabass_amount, modplug->megabass_range);
292 modplug->mSoundFile->SetReverbParameters (modplug->reverb_depth, modplug->reverb_delay);
296 static const GstFormat*
297 gst_modplug_get_formats (GstPad *pad)
299 static const GstFormat src_formats[] = {
301 GST_FORMAT_DEFAULT,*/
305 static const GstFormat sink_formats[] = {
306 /*GST_FORMAT_BYTES,*/
311 return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
314 static const GstQueryType*
315 gst_modplug_get_query_types (GstPad *pad)
317 static const GstQueryType gst_modplug_src_query_types[] = {
323 return gst_modplug_src_query_types;
328 gst_modplug_src_query (GstPad *pad, GstQueryType type,
329 GstFormat *format, gint64 *value)
335 modplug = GST_MODPLUG (gst_pad_get_parent (pad));
338 case GST_QUERY_TOTAL:
340 case GST_FORMAT_TIME:
341 *value=(gint64)modplug->mSoundFile->GetSongTime() * GST_SECOND;
348 case GST_QUERY_POSITION:
351 tmp = ((float)( modplug->mSoundFile->GetSongTime() * modplug->mSoundFile->GetCurrentPos() ) / (float)modplug->mSoundFile->GetMaxPosition() );
352 *value=(gint64)(tmp * GST_SECOND);
364 gst_modplug_src_event (GstPad *pad, GstEvent *event)
369 modplug = GST_MODPLUG (gst_pad_get_parent (pad));
371 switch (GST_EVENT_TYPE (event)) {
372 /* the all-formats seek logic */
378 format = GST_FORMAT_TIME;
380 /* shave off the flush flag, we'll need it later */
381 flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
383 modplug->seek_at = GST_EVENT_SEEK_OFFSET (event);
391 gst_event_unref (event);
398 gst_modplug_get_streaminfo (GstModPlug *modplug)
402 props = gst_props_empty_new ();
404 entry = gst_props_entry_new ("Patterns", G_TYPE_INT ((gint)modplug->mSoundFile->GetNumPatterns()));
405 gst_props_add_entry (props, (GstPropsEntry *) entry);
407 caps = gst_caps_new_simple ("application/x-gst-streaminfo", NULL);
413 gst_modplug_update_info (GstModPlug *modplug)
415 if (modplug->streaminfo) {
416 gst_caps_unref (modplug->streaminfo);
419 modplug->streaminfo = gst_modplug_get_streaminfo (modplug);
420 g_object_notify (G_OBJECT (modplug), "streaminfo");
424 gst_modplug_update_metadata (GstModPlug *modplug)
427 GstPropsEntry *entry;
430 props = gst_props_empty_new ();
432 title = modplug->mSoundFile->GetTitle();
433 entry = gst_props_entry_new ("Title", G_TYPE_STRING (title));
434 gst_props_add_entry (props, entry);
436 modplug->metadata = gst_caps_new_simple ("application/x-gst-metadata",
439 g_object_notify (G_OBJECT (modplug), "metadata");
443 static GstPadLinkReturn
444 gst_modplug_srclink (GstPad *pad, const GstCaps *caps)
447 GstStructure *structure;
450 modplug = GST_MODPLUG (gst_pad_get_parent (pad));
452 structure = gst_caps_get_structure (caps, 0);
454 gst_structure_get_int (structure, "depth", &depth);
455 modplug->_16bit = (depth == 16);
456 gst_structure_get_int (structure, "channels", &modplug->channel);
457 gst_structure_get_int (structure, "rate", &modplug->frequency);
459 modplug->length = 1152 * modplug->channel * depth / 8;
460 gst_modplug_setup (modplug);
462 return GST_PAD_LINK_OK;
466 gst_modplug_handle_event (GstModPlug *modplug)
471 gst_bytestream_get_status (modplug->bs, &remaining, &event);
474 g_warning ("modplug: no bytestream event");
478 switch (GST_EVENT_TYPE (event)) {
479 case GST_EVENT_DISCONTINUOUS:
480 gst_bytestream_flush_fast (modplug->bs, remaining);
482 gst_pad_event_default (modplug->sinkpad, event);
488 gst_modplug_loop (GstElement *element)
493 g_return_if_fail (element != NULL);
494 g_return_if_fail (GST_IS_MODPLUG (element));
496 modplug = GST_MODPLUG (element);
498 if (modplug->state == MODPLUG_STATE_NEED_TUNE)
502 modplug->seek_at = -1;
503 modplug->need_discont = FALSE;
504 modplug->eos = FALSE;
506 buf = gst_pad_pull (modplug->sinkpad);
507 g_assert (buf != NULL);
509 if (GST_IS_EVENT (buf)) {
510 GstEvent *event = GST_EVENT (buf);
512 switch (GST_EVENT_TYPE (buf)) {
514 modplug->state = MODPLUG_STATE_LOAD_TUNE;
516 case GST_EVENT_DISCONTINUOUS:
519 bail out, we're not going to do anything
520 gst_event_unref (event);
521 gst_pad_send_event (modplug->srcpad, gst_event_new (GST_EVENT_EOS));
522 gst_element_set_eos (element);
525 gst_event_unref (event);
528 memcpy (modplug->buffer_in + modplug->song_size, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
529 modplug->song_size += GST_BUFFER_SIZE (buf);
531 gst_buffer_unref (buf);
539 modplug->song_size = gst_bytestream_length (modplug->bs);
541 got = gst_bytestream_peek_bytes (modplug->bs, &modplug->buffer_in, modplug->song_size);
543 if ( got < modplug->song_size )
545 gst_modplug_handle_event (modplug);
548 modplug->state = MODPLUG_STATE_LOAD_TUNE;
552 if (modplug->state == MODPLUG_STATE_LOAD_TUNE)
554 modplug->mSoundFile = new CSoundFile;
556 if (!GST_PAD_CAPS (modplug->srcpad) &&
557 GST_PAD_LINK_FAILED (gst_pad_renegotiate (modplug->srcpad))) {
558 GST_ELEMENT_ERROR (modplug, CORE, NEGOTIATION, (NULL), (NULL));
562 modplug->mSoundFile->Create (modplug->buffer_in, modplug->song_size);
563 modplug->opened = TRUE;
565 gst_bytestream_flush (modplug->bs, modplug->song_size);
566 modplug->buffer_in = NULL;
568 modplug->audiobuffer = (guchar *) g_malloc (modplug->length);
570 //gst_modplug_update_metadata (modplug);
571 //gst_modplug_update_info (modplug);
573 modplug->state = MODPLUG_STATE_PLAY_TUNE;
576 if (modplug->state == MODPLUG_STATE_PLAY_TUNE && !modplug->eos)
578 if (modplug->seek_at != -1)
584 total = modplug->mSoundFile->GetSongTime () * GST_SECOND;
586 temp = (gfloat) total / modplug->seek_at;
587 seek_to_pos = (int) (modplug->mSoundFile->GetMaxPosition () / temp);
589 modplug->mSoundFile->SetCurrentPos (seek_to_pos);
590 modplug->need_discont = TRUE;
591 modplug->seek_at = -1;
594 if (modplug->mSoundFile->Read (modplug->audiobuffer, modplug->length) != 0)
596 GstBuffer *buffer_out;
600 format = GST_FORMAT_TIME;
601 gst_modplug_src_query (modplug->srcpad, GST_QUERY_POSITION, &format, &value);
603 if (modplug->need_discont && GST_PAD_IS_USABLE (modplug->srcpad))
607 discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, value, NULL);
608 gst_pad_push (modplug->srcpad, GST_DATA (discont));
610 modplug->need_discont= FALSE;
613 buffer_out = gst_buffer_new ();
614 GST_BUFFER_DATA (buffer_out) = (guchar *) g_memdup (modplug->audiobuffer, modplug->length);
615 GST_BUFFER_SIZE (buffer_out) = modplug->length;
616 GST_BUFFER_TIMESTAMP (buffer_out) = value;
618 if (GST_PAD_IS_USABLE (modplug->srcpad))
619 gst_pad_push (modplug->srcpad, GST_DATA (buffer_out));
622 if (GST_PAD_IS_LINKED (modplug->srcpad))
624 /* FIXME, hack, pull final EOS from peer */
625 gst_bytestream_flush (modplug->bs, 1);
627 event = gst_event_new (GST_EVENT_EOS);
628 gst_pad_push (modplug->srcpad, GST_DATA (event));
629 gst_element_set_eos (element);
636 static GstElementStateReturn
637 gst_modplug_change_state (GstElement *element)
641 modplug = GST_MODPLUG (element);
643 switch (GST_STATE_TRANSITION (element)) {
644 case GST_STATE_NULL_TO_READY:
646 case GST_STATE_READY_TO_PAUSED:
647 modplug->bs = gst_bytestream_new (modplug->sinkpad);
648 modplug->song_size = 0;
649 modplug->state = MODPLUG_STATE_NEED_TUNE;
651 case GST_STATE_PAUSED_TO_PLAYING:
653 case GST_STATE_PLAYING_TO_PAUSED:
655 case GST_STATE_PAUSED_TO_READY:
656 gst_bytestream_destroy (modplug->bs);
660 modplug->mSoundFile->Destroy ();
661 modplug->opened = FALSE;
663 if (modplug->audiobuffer) g_free (modplug->audiobuffer);
664 modplug->buffer_in = NULL;
665 modplug->audiobuffer = NULL;
666 modplug->state = MODPLUG_STATE_NEED_TUNE;
668 case GST_STATE_READY_TO_NULL:
674 if (GST_ELEMENT_CLASS (parent_class)->change_state)
675 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
677 return GST_STATE_SUCCESS;
682 gst_modplug_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec )
686 /* it's not null if we got it, but it might not be ours */
687 g_return_if_fail (GST_IS_MODPLUG(object));
688 modplug = GST_MODPLUG (object);
692 modplug->reverb = g_value_get_boolean (value);
694 case ARG_REVERB_DEPTH:
695 modplug->reverb_depth = g_value_get_int (value);
697 case ARG_REVERB_DELAY:
698 modplug->reverb_delay = g_value_get_int (value);
701 modplug->megabass = g_value_get_boolean (value);
703 case ARG_MEGABASS_AMOUNT:
704 modplug->megabass_amount = g_value_get_int (value);
706 case ARG_MEGABASS_RANGE:
707 modplug->megabass_range = g_value_get_int (value);
709 case ARG_NOISE_REDUCTION:
710 modplug->noise_reduction = g_value_get_boolean (value);
713 modplug->surround = g_value_get_boolean (value);
715 case ARG_SURROUND_DEPTH:
716 modplug->surround_depth = g_value_get_int (value);
718 case ARG_SURROUND_DELAY:
719 modplug->surround_delay = g_value_get_int (value);
727 gst_modplug_get_property (GObject *object, guint id, GValue *value, GParamSpec *pspec )
731 /* it's not null if we got it, but it might not be ours */
732 g_return_if_fail (GST_IS_MODPLUG(object));
733 modplug = GST_MODPLUG (object);
737 g_value_set_boolean (value, modplug->reverb);
739 case ARG_REVERB_DEPTH:
740 g_value_set_int (value, modplug->reverb_depth);
742 case ARG_REVERB_DELAY:
743 g_value_set_int (value, modplug->reverb_delay);
746 g_value_set_boolean (value, modplug->megabass);
748 case ARG_MEGABASS_AMOUNT:
749 g_value_set_int (value, modplug->megabass_amount);
751 case ARG_MEGABASS_RANGE:
752 g_value_set_int (value, modplug->megabass_range);
755 g_value_set_boolean (value, modplug->surround);
757 case ARG_SURROUND_DEPTH:
758 g_value_set_int (value, modplug->surround_depth);
760 case ARG_SURROUND_DELAY:
761 g_value_set_int (value, modplug->surround_delay);
763 case ARG_NOISE_REDUCTION:
764 g_value_set_boolean (value, modplug->noise_reduction);
772 plugin_init (GstPlugin *plugin)
774 /* this filter needs the bytestream package */
775 if (!gst_library_load ("gstbytestream"))
778 return gst_element_register (plugin, "modplug",
779 GST_RANK_PRIMARY, GST_TYPE_MODPLUG);
786 ".MOD audio decoding",