summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorCarlos Rafael Giani <dv@pseudoterminal.org>2016-07-27 02:22:26 +0200
committerJan Schmidt <jan@centricular.com>2017-05-22 16:34:05 +0200
commite7a5fdfde429b04d240211523c536438627a2a52 (patch)
tree8cfb69d8933d765637ebc3ef32221b6db344106f /ext
parentff32a4297f2d50cd5288b2cac930f61ce783bc00 (diff)
downloadgstreamer-plugins-bad-e7a5fdfde429b04d240211523c536438627a2a52.tar.gz
openmpt: Add openmptdec element
https://bugzilla.gnome.org/show_bug.cgi?id=768576
Diffstat (limited to 'ext')
-rw-r--r--ext/Makefile.am8
-rw-r--r--ext/openmpt/Makefile.am18
-rw-r--r--ext/openmpt/gstopenmptdec.c968
-rw-r--r--ext/openmpt/gstopenmptdec.h81
-rw-r--r--ext/openmpt/plugin.c44
5 files changed, 1119 insertions, 0 deletions
diff --git a/ext/Makefile.am b/ext/Makefile.am
index 534b9ac7b..bc2abed31 100644
--- a/ext/Makefile.am
+++ b/ext/Makefile.am
@@ -232,6 +232,12 @@ else
OPENH264_DIR =
endif
+if USE_OPENMPT
+OPENMPT_DIR=openmpt
+else
+OPENMPT_DIR=
+endif
+
if USE_OPENNI2
OPENNI2_DIR=openni2
else
@@ -431,6 +437,7 @@ SUBDIRS=\
$(OPENEXR_DIR) \
$(OPENH264_DIR) \
$(OPENJPEG_DIR) \
+ $(OPENMPT_DIR) \
$(OPENNI2_DIR) \
$(OPUS_DIR) \
$(RSVG_DIR) \
@@ -494,6 +501,7 @@ DIST_SUBDIRS = \
opencv \
openexr \
openh264 \
+ openmpt \
openni2 \
openjpeg \
opus \
diff --git a/ext/openmpt/Makefile.am b/ext/openmpt/Makefile.am
new file mode 100644
index 000000000..a699a11e6
--- /dev/null
+++ b/ext/openmpt/Makefile.am
@@ -0,0 +1,18 @@
+plugin_LTLIBRARIES = libgstopenmpt.la
+
+libgstopenmpt_la_SOURCES = gstopenmptdec.c plugin.c
+
+libgstopenmpt_la_CFLAGS = \
+ -I$(top_srcdir)/gst-libs \
+ -I$(top_builddir)/gst-libs \
+ $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \
+ $(GST_CFLAGS) $(OPENMPT_CFLAGS)
+libgstopenmpt_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstopenmpt_la_LIBADD = \
+ $(top_builddir)/gst-libs/gst/base/libgstbadbase-$(GST_API_VERSION).la \
+ $(top_builddir)/gst-libs/gst/audio/libgstbadaudio-$(GST_API_VERSION).la \
+ $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_API_VERSION@ \
+ $(GST_BASE_LIBS) $(GST_LIBS) $(OPENMPT_LIBS)
+libgstopenmpt_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
+
+noinst_HEADERS = gstopenmptdec.h
diff --git a/ext/openmpt/gstopenmptdec.c b/ext/openmpt/gstopenmptdec.c
new file mode 100644
index 000000000..c4830f6f2
--- /dev/null
+++ b/ext/openmpt/gstopenmptdec.c
@@ -0,0 +1,968 @@
+/* GStreamer
+ * Copyright (C) <2017> Carlos Rafael Giani <dv at pseudoterminal dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+/**
+ * SECTION:element-openmptdec
+ * @see_also: #GstOpenMptDec
+ *
+ * openmpdec decodes module music formats, such as S3M, MOD, XM, IT.
+ * It uses the <ulink url="https://lib.openmpt.org">OpenMPT library</ulink>
+ * for this purpose. It can be autoplugged and therefore works with decodebin.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch-1.0 filesrc location=media/example.it ! openmptdec ! audioconvert ! audioresample ! autoaudiosink
+ * ]|
+ * </refsect2>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gst/gst.h>
+
+#include "gstopenmptdec.h"
+
+
+GST_DEBUG_CATEGORY_STATIC (openmptdec_debug);
+#define GST_CAT_DEFAULT openmptdec_debug
+
+
+enum
+{
+ PROP_0,
+ PROP_MASTER_GAIN,
+ PROP_STEREO_SEPARATION,
+ PROP_FILTER_LENGTH,
+ PROP_VOLUME_RAMPING,
+ PROP_OUTPUT_BUFFER_SIZE
+};
+
+
+#define DEFAULT_MASTER_GAIN 0
+#define DEFAULT_STEREO_SEPARATION 100
+#define DEFAULT_FILTER_LENGTH 0
+#define DEFAULT_VOLUME_RAMPING -1
+#define DEFAULT_OUTPUT_BUFFER_SIZE 1024
+
+#define DEFAULT_SAMPLE_FORMAT GST_AUDIO_FORMAT_F32
+#define DEFAULT_SAMPLE_RATE 48000
+#define DEFAULT_NUM_CHANNELS 2
+
+
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-mod, "
+ "type = (string) { 669, asylum-amf, dsmi-amf, extreme-ams, velvet-ams, "
+ "dbm, digi, dmf, dsm, far, gdm, imf, it, j2b, mdl, med, mod, mt2, mtm, "
+ "okt, psm, ptm, s3m, stm, ult, xm }")
+ );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) { " GST_AUDIO_NE (S16) ", " GST_AUDIO_NE (F32) " }, "
+ "layout = (string) interleaved, "
+ "rate = (int) [ 1, 192000 ], " "channels = (int) { 1, 2, 4 } ")
+ );
+
+
+
+G_DEFINE_TYPE (GstOpenMptDec, gst_openmpt_dec,
+ GST_TYPE_NONSTREAM_AUDIO_DECODER);
+
+
+
+static void gst_openmpt_dec_finalize (GObject * object);
+
+static void gst_openmpt_dec_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_openmpt_dec_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static gboolean gst_openmpt_dec_seek (GstNonstreamAudioDecoder * dec,
+ GstClockTime * new_position);
+static GstClockTime gst_openmpt_dec_tell (GstNonstreamAudioDecoder * dec);
+
+static void gst_openmpt_dec_log_func (char const *message, void *user);
+static void gst_openmpt_dec_add_metadata_to_tag_list (GstOpenMptDec *
+ openmpt_dec, GstTagList * tags, char const *key, gchar const *tag);
+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);
+
+static GstTagList *gst_openmpt_dec_get_main_tags (GstNonstreamAudioDecoder *
+ dec);
+
+static gboolean gst_openmpt_dec_set_current_subsong (GstNonstreamAudioDecoder *
+ dec, guint subsong, GstClockTime * initial_position);
+static guint gst_openmpt_dec_get_current_subsong (GstNonstreamAudioDecoder *
+ dec);
+
+static guint gst_openmpt_dec_get_num_subsongs (GstNonstreamAudioDecoder * dec);
+static GstClockTime
+gst_openmpt_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
+ guint subsong);
+static GstTagList *gst_openmpt_dec_get_subsong_tags (GstNonstreamAudioDecoder *
+ dec, guint subsong);
+static gboolean gst_openmpt_dec_set_subsong_mode (GstNonstreamAudioDecoder *
+ dec, GstNonstreamAudioSubsongMode mode, GstClockTime * initial_position);
+
+static gboolean gst_openmpt_dec_set_num_loops (GstNonstreamAudioDecoder * dec,
+ gint num_loops);
+static gint gst_openmpt_dec_get_num_loops (GstNonstreamAudioDecoder * dec);
+
+static guint
+gst_openmpt_dec_get_supported_output_modes (GstNonstreamAudioDecoder * dec);
+static gboolean gst_openmpt_dec_decode (GstNonstreamAudioDecoder * dec,
+ GstBuffer ** buffer, guint * num_samples);
+
+static gboolean gst_openmpt_dec_select_subsong (GstOpenMptDec *
+ openmpt_dec, GstNonstreamAudioSubsongMode subsong_mode,
+ gint openmpt_subsong);
+
+
+void
+gst_openmpt_dec_class_init (GstOpenMptDecClass * klass)
+{
+ GObjectClass *object_class;
+ GstElementClass *element_class;
+ GstNonstreamAudioDecoderClass *dec_class;
+
+ GST_DEBUG_CATEGORY_INIT (openmptdec_debug, "openmptdec", 0,
+ "OpenMPT-based module music decoder");
+
+ object_class = G_OBJECT_CLASS (klass);
+ element_class = GST_ELEMENT_CLASS (klass);
+ dec_class = GST_NONSTREAM_AUDIO_DECODER_CLASS (klass);
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_template));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_template));
+
+ object_class->finalize = GST_DEBUG_FUNCPTR (gst_openmpt_dec_finalize);
+ object_class->set_property = GST_DEBUG_FUNCPTR (gst_openmpt_dec_set_property);
+ object_class->get_property = GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_property);
+
+ dec_class->seek = GST_DEBUG_FUNCPTR (gst_openmpt_dec_seek);
+ dec_class->tell = GST_DEBUG_FUNCPTR (gst_openmpt_dec_tell);
+ dec_class->load_from_buffer =
+ GST_DEBUG_FUNCPTR (gst_openmpt_dec_load_from_buffer);
+ dec_class->get_main_tags = GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_main_tags);
+ dec_class->set_num_loops = GST_DEBUG_FUNCPTR (gst_openmpt_dec_set_num_loops);
+ dec_class->get_num_loops = GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_num_loops);
+ dec_class->get_supported_output_modes =
+ GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_supported_output_modes);
+ dec_class->decode = GST_DEBUG_FUNCPTR (gst_openmpt_dec_decode);
+ dec_class->set_current_subsong =
+ GST_DEBUG_FUNCPTR (gst_openmpt_dec_set_current_subsong);
+ dec_class->get_current_subsong =
+ GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_current_subsong);
+ dec_class->get_num_subsongs =
+ GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_num_subsongs);
+ dec_class->get_subsong_duration =
+ GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_subsong_duration);
+ dec_class->get_subsong_tags =
+ GST_DEBUG_FUNCPTR (gst_openmpt_dec_get_subsong_tags);
+ dec_class->set_subsong_mode =
+ GST_DEBUG_FUNCPTR (gst_openmpt_dec_set_subsong_mode);
+
+ gst_element_class_set_static_metadata (element_class,
+ "OpenMPT-based module music decoder",
+ "Codec/Decoder/Audio",
+ "Decoders module files (MOD/S3M/XM/IT/MTM/...) using OpenMPT",
+ "Carlos Rafael Giani <dv@pseudoterminal.org>");
+
+ g_object_class_install_property (object_class,
+ PROP_MASTER_GAIN,
+ g_param_spec_int ("master-gain",
+ "Master gain",
+ "Gain to apply to the playback, in millibel",
+ -G_MAXINT, G_MAXINT,
+ DEFAULT_MASTER_GAIN, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+ g_object_class_install_property (object_class,
+ PROP_STEREO_SEPARATION,
+ g_param_spec_int ("stereo-separation",
+ "Stereo separation",
+ "Degree of separation for stereo channels, in percent",
+ 0, 400,
+ DEFAULT_STEREO_SEPARATION, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+ g_object_class_install_property (object_class,
+ PROP_FILTER_LENGTH,
+ g_param_spec_int ("filter-length",
+ "Filter length",
+ "Length of interpolation filter to use for the samples (0 = internal default)",
+ 0, 8,
+ DEFAULT_FILTER_LENGTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+ g_object_class_install_property (object_class,
+ PROP_VOLUME_RAMPING,
+ g_param_spec_int ("volume-ramping",
+ "Volume ramping",
+ "Volume ramping strength; higher value -> slower ramping (-1 = internal default)",
+ -1, 10,
+ DEFAULT_VOLUME_RAMPING, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+ /* 4*4 => quad output with F32 samples; this ensures that no overflow can happen */
+ g_object_class_install_property (object_class,
+ PROP_OUTPUT_BUFFER_SIZE,
+ g_param_spec_uint ("output-buffer-size",
+ "Output buffer size",
+ "Size of each output buffer, in samples (actual size can be smaller "
+ "than this during flush or EOS)",
+ 1, G_MAXUINT / (4 * 4),
+ DEFAULT_OUTPUT_BUFFER_SIZE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+}
+
+
+void
+gst_openmpt_dec_init (GstOpenMptDec * openmpt_dec)
+{
+ openmpt_dec->mod = NULL;
+
+ openmpt_dec->cur_subsong = 0;
+ openmpt_dec->num_subsongs = 0;
+ openmpt_dec->subsong_durations = NULL;
+
+ openmpt_dec->num_loops = 0;
+
+ openmpt_dec->master_gain = DEFAULT_MASTER_GAIN;
+ openmpt_dec->stereo_separation = DEFAULT_STEREO_SEPARATION;
+ openmpt_dec->filter_length = DEFAULT_FILTER_LENGTH;
+ openmpt_dec->volume_ramping = DEFAULT_VOLUME_RAMPING;
+
+ openmpt_dec->output_buffer_size = DEFAULT_OUTPUT_BUFFER_SIZE;
+
+ openmpt_dec->main_tags = NULL;
+
+ openmpt_dec->sample_format = DEFAULT_SAMPLE_FORMAT;
+ openmpt_dec->sample_rate = DEFAULT_SAMPLE_RATE;
+ openmpt_dec->num_channels = DEFAULT_NUM_CHANNELS;
+}
+
+
+static void
+gst_openmpt_dec_finalize (GObject * object)
+{
+ GstOpenMptDec *openmpt_dec;
+
+ g_return_if_fail (GST_IS_OPENMPT_DEC (object));
+ openmpt_dec = GST_OPENMPT_DEC (object);
+
+ if (openmpt_dec->main_tags != NULL)
+ gst_tag_list_unref (openmpt_dec->main_tags);
+
+ if (openmpt_dec->mod != NULL)
+ openmpt_module_destroy (openmpt_dec->mod);
+
+ g_free (openmpt_dec->subsong_durations);
+
+ G_OBJECT_CLASS (gst_openmpt_dec_parent_class)->finalize (object);
+}
+
+
+static void
+gst_openmpt_dec_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstNonstreamAudioDecoder *dec;
+ GstOpenMptDec *openmpt_dec;
+
+ dec = GST_NONSTREAM_AUDIO_DECODER (object);
+ openmpt_dec = GST_OPENMPT_DEC (object);
+
+ switch (prop_id) {
+ case PROP_MASTER_GAIN:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
+ openmpt_dec->master_gain = g_value_get_int (value);
+ if (openmpt_dec->mod != NULL)
+ openmpt_module_set_render_param (openmpt_dec->mod,
+ OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,
+ openmpt_dec->master_gain);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
+ break;
+ }
+
+ case PROP_STEREO_SEPARATION:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
+ openmpt_dec->stereo_separation = g_value_get_int (value);
+ if (openmpt_dec->mod != NULL)
+ openmpt_module_set_render_param (openmpt_dec->mod,
+ OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,
+ openmpt_dec->stereo_separation);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
+ break;
+ }
+
+ case PROP_FILTER_LENGTH:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
+ openmpt_dec->filter_length = g_value_get_int (value);
+ if (openmpt_dec->mod != NULL)
+ openmpt_module_set_render_param (openmpt_dec->mod,
+ OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,
+ openmpt_dec->filter_length);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
+ break;
+ }
+
+ case PROP_VOLUME_RAMPING:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
+ openmpt_dec->volume_ramping = g_value_get_int (value);
+ if (openmpt_dec->mod != NULL)
+ openmpt_module_set_render_param (openmpt_dec->mod,
+ OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH,
+ openmpt_dec->volume_ramping);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
+ break;
+ }
+
+ case PROP_OUTPUT_BUFFER_SIZE:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
+ openmpt_dec->output_buffer_size = g_value_get_uint (value);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
+ break;
+ }
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_openmpt_dec_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (object);
+
+ switch (prop_id) {
+ case PROP_MASTER_GAIN:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ g_value_set_int (value, openmpt_dec->master_gain);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+ }
+
+ case PROP_STEREO_SEPARATION:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ g_value_set_int (value, openmpt_dec->stereo_separation);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+ }
+
+ case PROP_FILTER_LENGTH:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ g_value_set_int (value, openmpt_dec->filter_length);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+ }
+
+ case PROP_VOLUME_RAMPING:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ g_value_set_int (value, openmpt_dec->volume_ramping);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+ }
+
+ case PROP_OUTPUT_BUFFER_SIZE:
+ {
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ g_value_set_uint (value, openmpt_dec->output_buffer_size);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+ }
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static gboolean
+gst_openmpt_dec_seek (GstNonstreamAudioDecoder * dec,
+ GstClockTime * new_position)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ g_return_val_if_fail (openmpt_dec->mod != NULL, FALSE);
+
+ openmpt_module_set_position_seconds (openmpt_dec->mod,
+ (double) (*new_position) / GST_SECOND);
+ *new_position = gst_openmpt_dec_tell (dec);
+
+ return TRUE;
+}
+
+
+static GstClockTime
+gst_openmpt_dec_tell (GstNonstreamAudioDecoder * dec)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ g_return_val_if_fail (openmpt_dec->mod != NULL, GST_CLOCK_TIME_NONE);
+
+ return (GstClockTime) (openmpt_module_get_position_seconds (openmpt_dec->mod)
+ * GST_SECOND);
+}
+
+
+static void
+gst_openmpt_dec_log_func (char const *message, void *user)
+{
+ GST_LOG_OBJECT (GST_OBJECT (user), "%s", message);
+}
+
+
+static void
+gst_openmpt_dec_add_metadata_to_tag_list (GstOpenMptDec * openmpt_dec,
+ GstTagList * tags, char const *key, gchar const *tag)
+{
+ char const *metadata = openmpt_module_get_metadata (openmpt_dec->mod, key);
+
+ if (metadata && *metadata) {
+ GST_DEBUG_OBJECT (openmpt_dec,
+ "adding metadata \"%s\" with key \"%s\" to tag list as tag \"%s\"",
+ metadata, key, tag);
+
+ if (g_strcmp0 (tag, GST_TAG_DATE_TIME) == 0) {
+ /* Special handling for date-time tags - interpret the
+ * metadata string as an iso8601 string and convert it
+ * to a GstDateTime value, since this is the data type
+ * that GST_TAG_DATE_TIME expects. */
+
+ GstDateTime *date_time = gst_date_time_new_from_iso8601_string (metadata);
+ if (date_time) {
+ GST_DEBUG_OBJECT (openmpt_dec,
+ "successfully created date-time object out of iso8601 string");
+ gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, tag, date_time, NULL);
+ gst_date_time_unref (date_time);
+ } else
+ GST_WARNING_OBJECT (openmpt_dec,
+ "could not create date-time object out of iso8601 string - not adding metadata to tags");
+ } else {
+ /* Default handling - just insert the metadata string as-is */
+ gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, tag, metadata, NULL);
+ }
+ } else
+ GST_DEBUG_OBJECT (openmpt_dec,
+ "attempted to add metadata with key \"%s\" to tag list as tag \"%s\", but none exists",
+ key, tag);
+
+ if (metadata)
+ openmpt_free_string (metadata);
+}
+
+
+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)
+{
+ GstMapInfo map;
+ GstOpenMptDec *openmpt_dec;
+
+ openmpt_dec = GST_OPENMPT_DEC (dec);
+
+ /* First, determine the sample rate, channel count, and sample format to use */
+ openmpt_dec->sample_format = DEFAULT_SAMPLE_FORMAT;
+ openmpt_dec->sample_rate = DEFAULT_SAMPLE_RATE;
+ openmpt_dec->num_channels = DEFAULT_NUM_CHANNELS;
+ gst_nonstream_audio_decoder_get_downstream_info (dec,
+ &(openmpt_dec->sample_format), &(openmpt_dec->sample_rate),
+ &(openmpt_dec->num_channels));
+
+ /* Set output format */
+ if (!gst_nonstream_audio_decoder_set_output_format_simple (dec,
+ openmpt_dec->sample_rate,
+ openmpt_dec->sample_format, openmpt_dec->num_channels))
+ return FALSE;
+
+ /* Pass the module data to OpenMPT for loading */
+ gst_buffer_map (source_data, &map, GST_MAP_READ);
+#if OPENMPT_API_VERSION_AT_LEAST(0,3,0)
+ openmpt_dec->mod =
+ openmpt_module_create_from_memory2 (map.data, map.size,
+ gst_openmpt_dec_log_func, dec, NULL, NULL, NULL, NULL, NULL);
+#else
+ openmpt_dec->mod =
+ openmpt_module_create_from_memory (map.data, map.size,
+ gst_openmpt_dec_log_func, dec, NULL);
+#endif
+ gst_buffer_unmap (source_data, &map);
+
+ if (openmpt_dec->mod == NULL) {
+ GST_ERROR_OBJECT (dec, "loading module failed");
+ return FALSE;
+ }
+
+ /* Copy subsong states */
+ openmpt_dec->cur_subsong = initial_subsong;
+ openmpt_dec->cur_subsong_mode = initial_subsong_mode;
+
+ /* Query the number of subsongs available for logging and for checking
+ * the initial subsong index */
+ openmpt_dec->num_subsongs =
+ openmpt_module_get_num_subsongs (openmpt_dec->mod);
+ if (G_UNLIKELY (initial_subsong >= openmpt_dec->num_subsongs)) {
+ GST_WARNING_OBJECT (openmpt_dec,
+ "initial subsong %u out of bounds (there are %u subsongs) - setting it to 0",
+ initial_subsong, openmpt_dec->num_subsongs);
+ initial_subsong = 0;
+ }
+ GST_INFO_OBJECT (openmpt_dec, "%d subsong(s) available",
+ openmpt_dec->num_subsongs);
+
+ /* Query the OpenMPT default subsong (can be -1)
+ * The default subsong is the one that is initially selected, so we
+ * need to query it here, *before* any openmpt_module_select_subsong()
+ * calls are done */
+ {
+ gchar const *subsong_cstr =
+ openmpt_module_ctl_get (openmpt_dec->mod, "subsong");
+ gchar *endptr;
+
+ if (subsong_cstr != NULL) {
+ openmpt_dec->default_openmpt_subsong =
+ g_ascii_strtoll (subsong_cstr, &endptr, 10);
+ if (subsong_cstr == endptr) {
+ GST_WARNING_OBJECT (openmpt_dec,
+ "could not convert ctl string \"%s\" to subsong index - using default OpenMPT index -1 instead",
+ subsong_cstr);
+ openmpt_dec->default_openmpt_subsong = -1;
+ } else
+ GST_DEBUG_OBJECT (openmpt_dec, "default OpenMPT subsong index is %d",
+ openmpt_dec->default_openmpt_subsong);
+
+ openmpt_free_string (subsong_cstr);
+ } else {
+ GST_INFO_OBJECT (openmpt_dec,
+ "could not get subsong ctl string - using default OpenMPT index -1 instead");
+ openmpt_dec->default_openmpt_subsong = -1;
+ }
+ }
+
+ /* Seek to initial position */
+ if (*initial_position != 0) {
+ openmpt_module_set_position_seconds (openmpt_dec->mod,
+ (double) (*initial_position) / GST_SECOND);
+ *initial_position =
+ (GstClockTime) (openmpt_module_get_position_seconds (openmpt_dec->mod) *
+ GST_SECOND);
+ }
+
+ /* LOOPING output mode is not supported */
+ *initial_output_mode = GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
+
+ /* Query the durations of each subsong (if any exist) */
+ if (openmpt_dec->num_subsongs > 0) {
+ guint i;
+
+ openmpt_dec->subsong_durations =
+ g_try_malloc (openmpt_dec->num_subsongs * sizeof (double));
+ if (openmpt_dec->subsong_durations == NULL) {
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (dec);
+ GST_ELEMENT_ERROR (openmpt_dec, RESOURCE, NO_SPACE_LEFT,
+ ("could not allocate memory for subsong duration array"), (NULL));
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (dec);
+ return FALSE;
+ }
+
+ for (i = 0; i < openmpt_dec->num_subsongs; ++i) {
+ openmpt_module_select_subsong (openmpt_dec->mod, i);
+ openmpt_dec->subsong_durations[i] =
+ openmpt_module_get_duration_seconds (openmpt_dec->mod);
+ }
+ }
+
+ /* Select the initial subsong */
+ gst_openmpt_dec_select_subsong (openmpt_dec, initial_subsong_mode,
+ initial_subsong);
+
+ /* Set the number of loops, and query the actual number
+ * that was chosen by OpenMPT */
+ {
+ int32_t actual_repeat_count;
+ openmpt_module_set_repeat_count (openmpt_dec->mod, *initial_num_loops);
+ actual_repeat_count = openmpt_module_get_repeat_count (openmpt_dec->mod);
+
+ if (actual_repeat_count != *initial_num_loops) {
+ GST_DEBUG_OBJECT (openmpt_dec,
+ "requested num-loops value %d differs from actual value %d",
+ *initial_num_loops, actual_repeat_count);
+ *initial_num_loops = actual_repeat_count;
+ }
+ }
+
+ /* Set render parameters (adjustable via properties) */
+ openmpt_module_set_render_param (openmpt_dec->mod,
+ OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL, openmpt_dec->master_gain);
+ openmpt_module_set_render_param (openmpt_dec->mod,
+ OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,
+ openmpt_dec->stereo_separation);
+ openmpt_module_set_render_param (openmpt_dec->mod,
+ OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,
+ openmpt_dec->filter_length);
+ openmpt_module_set_render_param (openmpt_dec->mod,
+ OPENMPT_MODULE_RENDER_VOLUMERAMPING_STRENGTH,
+ openmpt_dec->volume_ramping);
+
+ /* Log the available metadata keys, and produce a
+ * tag list if any keys are available */
+ {
+ char const *metadata_keys =
+ openmpt_module_get_metadata_keys (openmpt_dec->mod);
+ if (metadata_keys != NULL) {
+ GstTagList *tags = gst_tag_list_new_empty ();
+
+ GST_DEBUG_OBJECT (dec, "metadata keys: [%s]", metadata_keys);
+ openmpt_free_string (metadata_keys);
+
+ gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "title",
+ GST_TAG_TITLE);
+ gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "artist",
+ GST_TAG_ARTIST);
+ gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "message",
+ GST_TAG_COMMENT);
+ gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "tracker",
+ GST_TAG_APPLICATION_NAME);
+ gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "type_long",
+ GST_TAG_CODEC);
+ gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags, "date",
+ GST_TAG_DATE_TIME);
+ gst_openmpt_dec_add_metadata_to_tag_list (openmpt_dec, tags,
+ "container_long", GST_TAG_CONTAINER_FORMAT);
+
+ openmpt_dec->main_tags = tags;
+ } else {
+ GST_DEBUG_OBJECT (dec,
+ "no metadata keys found - not producing a tag list");
+ }
+ }
+
+ /* Log any warnings that were produced by OpenMPT while loading */
+ {
+ char const *warnings =
+ openmpt_module_get_metadata (openmpt_dec->mod, "warnings");
+ if (warnings) {
+ if (*warnings)
+ GST_WARNING_OBJECT (openmpt_dec, "reported warnings during loading: %s",
+ warnings);
+ openmpt_free_string (warnings);
+ }
+ }
+
+ return TRUE;
+}
+
+
+static GstTagList *
+gst_openmpt_dec_get_main_tags (GstNonstreamAudioDecoder * dec)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ return gst_tag_list_ref (openmpt_dec->main_tags);
+}
+
+
+static gboolean
+gst_openmpt_dec_set_current_subsong (GstNonstreamAudioDecoder * dec,
+ guint subsong, GstClockTime * initial_position)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ g_return_val_if_fail (openmpt_dec->mod != NULL, FALSE);
+
+ if (gst_openmpt_dec_select_subsong (openmpt_dec,
+ openmpt_dec->cur_subsong_mode, subsong)) {
+ GST_DEBUG_OBJECT (openmpt_dec,
+ "selected subsong %u and switching subsong mode to SINGLE", subsong);
+ openmpt_dec->cur_subsong_mode = GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE;
+ openmpt_dec->cur_subsong = subsong;
+ *initial_position = 0;
+ return TRUE;
+ } else {
+ GST_ERROR_OBJECT (openmpt_dec, "could not select subsong %u", subsong);
+ return FALSE;
+ }
+}
+
+
+static guint
+gst_openmpt_dec_get_current_subsong (GstNonstreamAudioDecoder * dec)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ return openmpt_dec->cur_subsong;
+}
+
+
+static guint
+gst_openmpt_dec_get_num_subsongs (GstNonstreamAudioDecoder * dec)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ return openmpt_dec->num_subsongs;
+}
+
+
+static GstClockTime
+gst_openmpt_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
+ guint subsong)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ return (GstClockTime) (openmpt_dec->subsong_durations[subsong] * GST_SECOND);
+}
+
+
+static GstTagList *
+gst_openmpt_dec_get_subsong_tags (GstNonstreamAudioDecoder * dec, guint subsong)
+{
+ GstOpenMptDec *openmpt_dec;
+ char const *name;
+
+ openmpt_dec = GST_OPENMPT_DEC (dec);
+
+ name = openmpt_module_get_subsong_name (openmpt_dec->mod, subsong);
+ if (name != NULL) {
+ GstTagList *tags = NULL;
+
+ if (*name) {
+ tags = gst_tag_list_new_empty ();
+ gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, "title", name, NULL);
+ }
+
+ openmpt_free_string (name);
+
+ return tags;
+ } else
+ return NULL;
+}
+
+
+static gboolean
+gst_openmpt_dec_set_subsong_mode (GstNonstreamAudioDecoder * dec,
+ GstNonstreamAudioSubsongMode mode, GstClockTime * initial_position)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ g_return_val_if_fail (openmpt_dec->mod != NULL, FALSE);
+
+ if (gst_openmpt_dec_select_subsong (openmpt_dec, mode,
+ openmpt_dec->cur_subsong)) {
+ GST_DEBUG_OBJECT (openmpt_dec, "set subsong mode");
+ openmpt_dec->cur_subsong_mode = mode;
+ *initial_position = 0;
+ return TRUE;
+ } else {
+ GST_ERROR_OBJECT (openmpt_dec, "could not set subsong mode");
+ return FALSE;
+ }
+}
+
+
+static gboolean
+gst_openmpt_dec_set_num_loops (GstNonstreamAudioDecoder * dec, gint num_loops)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ openmpt_dec->num_loops = num_loops;
+
+ if (openmpt_dec->mod != NULL) {
+ if (openmpt_module_set_repeat_count (openmpt_dec->mod, num_loops)) {
+ GST_DEBUG_OBJECT (openmpt_dec, "successfully set repeat count %d",
+ num_loops);
+ return TRUE;
+ } else {
+ GST_ERROR_OBJECT (openmpt_dec, "could not set repeat count %d",
+ num_loops);
+ return FALSE;
+ }
+ } else
+ return TRUE;
+}
+
+
+static gint
+gst_openmpt_dec_get_num_loops (GstNonstreamAudioDecoder * dec)
+{
+ GstOpenMptDec *openmpt_dec = GST_OPENMPT_DEC (dec);
+ return openmpt_dec->num_loops;
+}
+
+
+static guint
+gst_openmpt_dec_get_supported_output_modes (G_GNUC_UNUSED
+ GstNonstreamAudioDecoder * dec)
+{
+ return 1u << GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
+}
+
+
+static gboolean
+gst_openmpt_dec_decode (GstNonstreamAudioDecoder * dec, GstBuffer ** buffer,
+ guint * num_samples)
+{
+ GstOpenMptDec *openmpt_dec;
+ GstBuffer *outbuf;
+ GstMapInfo map;
+ size_t num_read_samples;
+ gsize outbuf_size;
+ GstAudioFormatInfo const *fmt_info;
+
+ openmpt_dec = GST_OPENMPT_DEC (dec);
+
+ fmt_info = gst_audio_format_get_info (openmpt_dec->sample_format);
+
+ /* Allocate output buffer */
+ outbuf_size =
+ openmpt_dec->output_buffer_size * (fmt_info->width / 8) *
+ openmpt_dec->num_channels;
+ outbuf =
+ gst_nonstream_audio_decoder_allocate_output_buffer (dec, outbuf_size);
+ if (G_UNLIKELY (outbuf == NULL))
+ return FALSE;
+
+ /* Write samples into the output buffer */
+
+ gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
+
+ switch (openmpt_dec->sample_format) {
+ case GST_AUDIO_FORMAT_S16:
+ {
+ int16_t *out_samples = (int16_t *) (map.data);
+ switch (openmpt_dec->num_channels) {
+ case 1:
+ num_read_samples =
+ openmpt_module_read_mono (openmpt_dec->mod,
+ openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
+ out_samples);
+ break;
+ case 2:
+ num_read_samples =
+ openmpt_module_read_interleaved_stereo (openmpt_dec->mod,
+ openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
+ out_samples);
+ break;
+ case 4:
+ num_read_samples =
+ openmpt_module_read_interleaved_quad (openmpt_dec->mod,
+ openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
+ out_samples);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ break;
+ }
+ case GST_AUDIO_FORMAT_F32:
+ {
+ float *out_samples = (float *) (map.data);
+ switch (openmpt_dec->num_channels) {
+ case 1:
+ num_read_samples =
+ openmpt_module_read_float_mono (openmpt_dec->mod,
+ openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
+ out_samples);
+ break;
+ case 2:
+ num_read_samples =
+ openmpt_module_read_interleaved_float_stereo (openmpt_dec->mod,
+ openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
+ out_samples);
+ break;
+ case 4:
+ num_read_samples =
+ openmpt_module_read_interleaved_float_quad (openmpt_dec->mod,
+ openmpt_dec->sample_rate, openmpt_dec->output_buffer_size,
+ out_samples);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ break;
+ }
+ default:
+ {
+ GST_ERROR_OBJECT (dec, "using unsupported sample format %s",
+ fmt_info->name);
+ g_assert_not_reached ();
+ }
+ }
+
+ gst_buffer_unmap (outbuf, &map);
+
+ if (num_read_samples == 0)
+ return FALSE;
+
+ *buffer = outbuf;
+ *num_samples = num_read_samples;
+
+ return TRUE;
+}
+
+
+static gboolean
+gst_openmpt_dec_select_subsong (GstOpenMptDec * openmpt_dec,
+ GstNonstreamAudioSubsongMode subsong_mode, gint openmpt_subsong)
+{
+ switch (subsong_mode) {
+ case GST_NONSTREAM_AUDIO_SUBSONG_MODE_SINGLE:
+ GST_DEBUG_OBJECT (openmpt_dec, "setting subsong mode to SINGLE");
+ return openmpt_module_select_subsong (openmpt_dec->mod, openmpt_subsong);
+
+ case GST_NONSTREAM_AUDIO_SUBSONG_MODE_ALL:
+ GST_DEBUG_OBJECT (openmpt_dec, "setting subsong mode to ALL");
+ return openmpt_module_select_subsong (openmpt_dec->mod, -1);
+
+ case GST_NONSTREAM_AUDIO_SUBSONG_MODE_DECODER_DEFAULT:
+ /* NOTE: The OpenMPT documentation recommends to not bother
+ * calling openmpt_module_select_subsong() if the decoder
+ * default shall be used. However, the user might have switched
+ * the subsong mode from SINGLE or ALL to DECODER_DEFAULT,
+ * in which case we *do* have to set the default subsong index.
+ * So, just set the default index here. */
+ GST_DEBUG_OBJECT (openmpt_dec, "setting subsong mode to DECODER_DEFAULT");
+ return openmpt_module_select_subsong (openmpt_dec->mod,
+ openmpt_dec->default_openmpt_subsong);
+
+ default:
+ g_assert_not_reached ();
+ return TRUE;
+ }
+}
diff --git a/ext/openmpt/gstopenmptdec.h b/ext/openmpt/gstopenmptdec.h
new file mode 100644
index 000000000..93e283f4f
--- /dev/null
+++ b/ext/openmpt/gstopenmptdec.h
@@ -0,0 +1,81 @@
+/* GStreamer
+ * Copyright (C) <2017> Carlos Rafael Giani <dv at pseudoterminal dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef __GST_OPENMPT_DEC_H__
+#define __GST_OPENMPT_DEC_H__
+
+
+#include <gst/gst.h>
+#include "gst/audio/gstnonstreamaudiodecoder.h"
+#include <libopenmpt/libopenmpt.h>
+
+
+G_BEGIN_DECLS
+
+
+typedef struct _GstOpenMptDec GstOpenMptDec;
+typedef struct _GstOpenMptDecClass GstOpenMptDecClass;
+
+
+#define GST_TYPE_OPENMPT_DEC (gst_openmpt_dec_get_type())
+#define GST_OPENMPT_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_OPENMPT_DEC, GstOpenMptDec))
+#define GST_OPENMPT_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_OPENMPT_DEC, GstOpenMptDecClass))
+#define GST_IS_OPENMPT_DEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_OPENMPT_DEC))
+#define GST_IS_OPENMPT_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_OPENMPT_DEC))
+
+
+struct _GstOpenMptDec
+{
+ GstNonstreamAudioDecoder parent;
+ openmpt_module *mod;
+
+ guint cur_subsong, num_subsongs;
+ double *subsong_durations;
+ /* NOTE: this is of type int, not guint, because the value
+ * is defined by OpenMPT, and can be -1 (= "all subsongs") */
+ int default_openmpt_subsong;
+ GstNonstreamAudioSubsongMode cur_subsong_mode;
+
+ gint num_loops;
+
+ gint master_gain, stereo_separation, filter_length, volume_ramping;
+
+ GstAudioFormat sample_format;
+ gint sample_rate, num_channels;
+
+ guint output_buffer_size;
+
+ GstTagList *main_tags;
+};
+
+
+struct _GstOpenMptDecClass
+{
+ GstNonstreamAudioDecoderClass parent_class;
+};
+
+
+GType gst_openmpt_dec_get_type (void);
+
+
+G_END_DECLS
+
+
+#endif /* __GST_OPENMPT_DEC_H__ */
diff --git a/ext/openmpt/plugin.c b/ext/openmpt/plugin.c
new file mode 100644
index 000000000..6487fa2b6
--- /dev/null
+++ b/ext/openmpt/plugin.c
@@ -0,0 +1,44 @@
+/* GStreamer
+ * Copyright (C) <2016> Carlos Rafael Giani <dv at pseudoterminal dot org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include "gstopenmptdec.h"
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gboolean ret = TRUE;
+ ret = ret
+ && gst_element_register (plugin, "openmptdec", GST_RANK_PRIMARY + 2,
+ gst_openmpt_dec_get_type ());
+ return ret;
+}
+
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ openmpt,
+ "OpenMPT module player",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)