summaryrefslogtreecommitdiff
path: root/ext/wildmidi
diff options
context:
space:
mode:
authorCarlos Rafael Giani <dv@pseudoterminal.org>2017-03-08 22:17:41 +0100
committerJan Schmidt <jan@centricular.com>2017-05-22 16:34:05 +0200
commitff32a4297f2d50cd5288b2cac930f61ce783bc00 (patch)
treed9047725b5c3482f0ad127e798e7fe91540c15de /ext/wildmidi
parent2e4c6d6a49a2c26e19cf8c1159231b316b9082a3 (diff)
downloadgstreamer-plugins-bad-ff32a4297f2d50cd5288b2cac930f61ce783bc00.tar.gz
wildmidi: Port to 1.0 on top of the nonstreamaudiodecoder base class
https://bugzilla.gnome.org/show_bug.cgi?id=768576
Diffstat (limited to 'ext/wildmidi')
-rw-r--r--ext/wildmidi/Makefile.am16
-rw-r--r--ext/wildmidi/gstwildmidi.c1038
-rw-r--r--ext/wildmidi/gstwildmidi.h90
-rw-r--r--ext/wildmidi/gstwildmididec.c683
-rw-r--r--ext/wildmidi/gstwildmididec.h69
5 files changed, 764 insertions, 1132 deletions
diff --git a/ext/wildmidi/Makefile.am b/ext/wildmidi/Makefile.am
index 42f88c030..63fa690eb 100644
--- a/ext/wildmidi/Makefile.am
+++ b/ext/wildmidi/Makefile.am
@@ -5,14 +5,22 @@ if USE_WILDMIDI
plugin_LTLIBRARIES += libgstwildmidi.la
# sources used to compile this plug-in
-libgstwildmidi_la_SOURCES = gstwildmidi.c
+libgstwildmidi_la_SOURCES = gstwildmididec.c
# flags used to compile this plugin
# add other _CFLAGS and _LIBS as needed
-libgstwildmidi_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(WILDMIDI_CFLAGS)
-libgstwildmidi_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(WILDMIDI_LIBS)
+libgstwildmidi_la_CFLAGS = \
+ -I$(top_srcdir)/gst-libs \
+ -I$(top_builddir)/gst-libs \
+ $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \
+ $(GST_CFLAGS) $(WILDMIDI_CFLAGS)
+libgstwildmidi_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_LIBS) $(GST_BASE_LIBS) $(WILDMIDI_LIBS)
libgstwildmidi_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
endif
-noinst_HEADERS = gstwildmidi.h
+noinst_HEADERS = gstwildmididec.h
diff --git a/ext/wildmidi/gstwildmidi.c b/ext/wildmidi/gstwildmidi.c
deleted file mode 100644
index 89bdcebb3..000000000
--- a/ext/wildmidi/gstwildmidi.c
+++ /dev/null
@@ -1,1038 +0,0 @@
-/*
- * gstwildmidi - wildmidi plugin for gstreamer
- *
- * Copyright 2007 Wouter Paesen <wouter@blue-gate.be>
- *
- * 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-wildmidi
- * @title: wildmidi
- * @see_also: timidity
- *
- * This element renders midi-files as audio streams using
- * <ulink url="http://wildmidi.sourceforge.net//">Wildmidi</ulink>.
- * It offers better sound quality compared to the timidity element. Wildmidi
- * uses the same sound-patches as timidity (it tries the path in $WILDMIDI_CFG,
- * $HOME/.wildmidirc and /etc/wildmidi.cfg)
- *
- * ## Example pipeline
- * |[
- * gst-launch-1.0 filesrc location=song.mid ! wildmidi ! alsasink
- * ]| This example pipeline will parse the midi and render to raw audio which is
- * played via alsa.
- *
- */
-
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#define WILDMIDI_RATE 44100
-#define WILDMIDI_BPS (2 * 2)
-
-#include <gst/gst.h>
-#include <string.h>
-#include <glib.h>
-#include <glib/gstdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "gstwildmidi.h"
-
-#ifndef WILDMIDI_CFG
-#define WILDMIDI_CFG "/etc/timidity.cfg"
-#endif
-
-GST_DEBUG_CATEGORY_STATIC (gst_wildmidi_debug);
-#define GST_CAT_DEFAULT gst_wildmidi_debug
-
-enum
-{
- /* FILL ME */
- LAST_SIGNAL
-};
-
-enum
-{
- PROP_0,
- PROP_LINEAR_VOLUME,
- PROP_HIGH_QUALITY,
- /* FILL ME */
-};
-
-#define DEFAULT_LINEAR_VOLUME TRUE
-#define DEFAULT_HIGH_QUALITY TRUE
-
-static void gst_wildmidi_finalize (GObject * object);
-
-static gboolean gst_wildmidi_sink_event (GstPad * pad, GstObject * parent,
- GstEvent * event);
-static gboolean gst_wildmidi_src_event (GstPad * pad, GstObject * parent,
- GstEvent * event);
-
-static GstStateChangeReturn gst_wildmidi_change_state (GstElement * element,
- GstStateChange transition);
-static gboolean gst_wildmidi_activate (GstPad * pad, GstObject * parent);
-static gboolean gst_wildmidi_activatemode (GstPad * pad, GstObject * parent,
- GstPadMode mode, gboolean active);
-
-static void gst_wildmidi_loop (GstPad * sinkpad);
-static GstFlowReturn gst_wildmidi_chain (GstPad * sinkpad, GstObject * parent,
- GstBuffer * buffer);
-
-static gboolean gst_wildmidi_src_query (GstPad * pad, GstObject * parent,
- GstQuery * query);
-
-static void gst_wildmidi_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec);
-static void gst_wildmidi_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec);
-
-static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/midi; audio/riff-midi")
- );
-
-static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-raw, "
- "format = (string) S16LE, "
- "rate = (int) 44100, "
- "channels = (int) 2, " "layout = (string) interleaved"));
-
-#define parent_class gst_wildmidi_parent_class
-G_DEFINE_TYPE (GstWildmidi, gst_wildmidi, GST_TYPE_ELEMENT);
-
-static gboolean
-wildmidi_open_config (void)
-{
- gchar *path = g_strdup (g_getenv ("WILDMIDI_CFG"));
- gint ret;
-
- GST_DEBUG ("trying %s", GST_STR_NULL (path));
- if (path && (g_access (path, R_OK) == -1)) {
- g_free (path);
- path = NULL;
- }
-
- if (path == NULL) {
- path =
- g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".wildmidirc",
- NULL);
- GST_DEBUG ("trying %s", path);
- if (path && (g_access (path, R_OK) == -1)) {
- g_free (path);
- path = NULL;
- }
- }
-
- if (path == NULL) {
- path =
- g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc",
- "wildmidi.cfg", NULL);
- GST_DEBUG ("trying %s", path);
- if (path && (g_access (path, R_OK) == -1)) {
- g_free (path);
- path = NULL;
- }
- }
-
- if (path == NULL) {
- path =
- g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc", "wildmidi",
- "wildmidi.cfg", NULL);
- GST_DEBUG ("trying %s", path);
- if (path && (g_access (path, R_OK) == -1)) {
- g_free (path);
- path = NULL;
- }
- }
-
- if (path == NULL) {
- path = g_strdup (WILDMIDI_CFG);
- GST_DEBUG ("trying %s", path);
- if (path && (g_access (path, R_OK) == -1)) {
- g_free (path);
- path = NULL;
- }
- }
-
- if (path == NULL) {
- path =
- g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc",
- "timidity.cfg", NULL);
- GST_DEBUG ("trying %s", path);
- if (path && (g_access (path, R_OK) == -1)) {
- g_free (path);
- path = NULL;
- }
- }
-
- if (path == NULL) {
- path =
- g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc", "timidity",
- "timidity.cfg", NULL);
- GST_DEBUG ("trying %s", path);
- if (path && (g_access (path, R_OK) == -1)) {
- g_free (path);
- path = NULL;
- }
- }
-
- if (path == NULL) {
- /* I've created a symlink to get it playing
- * ln -s /usr/share/timidity/timidity.cfg /etc/wildmidi.cfg
- * we could make it use : WILDMIDI_CFG
- * but unfortunately it fails to create a proper filename if the config
- * has a redirect
- * http://sourceforge.net/tracker/index.php?func=detail&aid=1657358&group_id=42635&atid=433744
- */
- GST_WARNING ("no config file, can't initialise");
- return FALSE;
- }
-
- /* this also initializes a some filter and stuff and thus is slow */
- ret = WildMidi_Init (path, WILDMIDI_RATE, 0);
- g_free (path);
-
- return (ret == 0);
-}
-
-/* initialize the plugin's class */
-static void
-gst_wildmidi_class_init (GstWildmidiClass * klass)
-{
- GObjectClass *gobject_class;
- GstElementClass *gstelement_class;
-
- gobject_class = (GObjectClass *) klass;
- gstelement_class = (GstElementClass *) klass;
-
- gobject_class->finalize = gst_wildmidi_finalize;
- gobject_class->set_property = gst_wildmidi_set_property;
- gobject_class->get_property = gst_wildmidi_get_property;
-
- g_object_class_install_property (gobject_class, PROP_LINEAR_VOLUME,
- g_param_spec_boolean ("linear-volume", "Linear volume",
- "Linear volume", DEFAULT_LINEAR_VOLUME,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (gobject_class, PROP_HIGH_QUALITY,
- g_param_spec_boolean ("high-quality", "High Quality",
- "High Quality", DEFAULT_HIGH_QUALITY,
- G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
- gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
- gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
- gst_element_class_set_static_metadata (gstelement_class, "WildMidi",
- "Codec/Decoder/Audio",
- "Midi Synthesizer Element", "Wouter Paesen <wouter@blue-gate.be>");
-
- gstelement_class->change_state = gst_wildmidi_change_state;
-}
-
-/* initialize the new element
- * instantiate pads and add them to element
- * set functions
- * initialize structure
- */
-static void
-gst_wildmidi_init (GstWildmidi * filter)
-{
- filter->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
-
- gst_pad_set_activatemode_function (filter->sinkpad,
- gst_wildmidi_activatemode);
- gst_pad_set_activate_function (filter->sinkpad, gst_wildmidi_activate);
- gst_pad_set_event_function (filter->sinkpad, gst_wildmidi_sink_event);
- gst_pad_set_chain_function (filter->sinkpad, gst_wildmidi_chain);
- gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
-
- filter->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
-
- gst_pad_set_query_function (filter->srcpad, gst_wildmidi_src_query);
- gst_pad_set_event_function (filter->srcpad, gst_wildmidi_src_event);
- gst_pad_use_fixed_caps (filter->srcpad);
-
- gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
-
- gst_segment_init (filter->o_segment, GST_FORMAT_DEFAULT);
-
- filter->adapter = gst_adapter_new ();
-
- filter->bytes_per_frame = WILDMIDI_BPS;
-
- filter->high_quality = DEFAULT_HIGH_QUALITY;
- filter->linear_volume = DEFAULT_LINEAR_VOLUME;
-}
-
-static void
-gst_wildmidi_finalize (GObject * object)
-{
- GstWildmidi *wildmidi;
-
- wildmidi = GST_WILDMIDI (object);
-
- g_object_unref (wildmidi->adapter);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
-static gboolean
-gst_wildmidi_src_convert (GstWildmidi * wildmidi,
- GstFormat src_format, gint64 src_value,
- GstFormat * dest_format, gint64 * dest_value)
-{
- gboolean res = TRUE;
- gint64 frames;
-
- if (src_format == *dest_format || src_value == -1) {
- *dest_value = src_value;
- goto done;
- }
-
- switch (src_format) {
- case GST_FORMAT_TIME:
- frames = gst_util_uint64_scale_int (src_value, WILDMIDI_RATE, GST_SECOND);
- break;
- case GST_FORMAT_BYTES:
- frames = src_value / (wildmidi->bytes_per_frame);
- break;
- case GST_FORMAT_DEFAULT:
- frames = src_value;
- break;
- default:
- res = FALSE;
- goto done;
- }
-
- switch (*dest_format) {
- case GST_FORMAT_TIME:
- *dest_value =
- gst_util_uint64_scale_int (frames, GST_SECOND, WILDMIDI_RATE);
- break;
- case GST_FORMAT_BYTES:
- *dest_value = frames * wildmidi->bytes_per_frame;
- break;
- case GST_FORMAT_DEFAULT:
- *dest_value = frames;
- break;
- default:
- res = FALSE;
- break;
- }
-
-done:
- return res;
-}
-
-static gboolean
-gst_wildmidi_src_query (GstPad * pad, GstObject * parent, GstQuery * query)
-{
- gboolean res = TRUE;
- GstWildmidi *wildmidi = GST_WILDMIDI (parent);
- GstFormat src_format, dst_format;
- gint64 src_value, dst_value;
-
- if (!wildmidi->song)
- return FALSE;
-
- switch (GST_QUERY_TYPE (query)) {
- case GST_QUERY_DURATION:
- gst_query_set_duration (query, GST_FORMAT_TIME,
- gst_util_uint64_scale_int (wildmidi->o_len, GST_SECOND,
- WILDMIDI_RATE));
- break;
- case GST_QUERY_POSITION:
- gst_query_set_position (query, GST_FORMAT_TIME,
- gst_util_uint64_scale_int (wildmidi->o_segment->position, GST_SECOND,
- WILDMIDI_RATE));
- break;
- case GST_QUERY_CONVERT:
- gst_query_parse_convert (query, &src_format, &src_value,
- &dst_format, NULL);
-
- res =
- gst_wildmidi_src_convert (wildmidi, src_format, src_value,
- &dst_format, &dst_value);
- if (res)
- gst_query_set_convert (query, src_format, src_value, dst_format,
- dst_value);
-
- break;
- case GST_QUERY_FORMATS:
- gst_query_set_formats (query, 3,
- GST_FORMAT_TIME, GST_FORMAT_BYTES, GST_FORMAT_DEFAULT);
- break;
- case GST_QUERY_SEGMENT:{
- GstFormat format;
- gint64 start, stop;
-
- format = wildmidi->o_segment->format;
-
- start =
- gst_segment_to_stream_time (wildmidi->o_segment, format,
- wildmidi->o_segment->start);
- if ((stop = wildmidi->o_segment->stop) == -1)
- stop = wildmidi->o_segment->duration;
- else
- stop = gst_segment_to_stream_time (wildmidi->o_segment, format, stop);
-
- gst_query_set_segment (query, wildmidi->o_segment->rate, format, start,
- stop);
- res = TRUE;
- break;
- }
- case GST_QUERY_SEEKING:
- gst_query_set_seeking (query, wildmidi->o_segment->format,
- TRUE, 0, wildmidi->o_len);
- break;
- default:
- res = gst_pad_query_default (pad, parent, query);
- break;
- }
-
- return res;
-}
-
-static GstEvent *
-gst_wildmidi_get_new_segment_event (GstWildmidi * wildmidi, GstFormat format)
-{
- gint64 start = 0, stop = -1, time = 0;
- GstSegment *segment, newseg;
- GstEvent *event;
- GstFormat src_format;
-
- segment = wildmidi->o_segment;
- src_format = segment->format;
- newseg = *segment;
-
- /* convert the segment values to the target format */
- gst_wildmidi_src_convert (wildmidi, src_format, segment->start, &format,
- &start);
- gst_wildmidi_src_convert (wildmidi, src_format, segment->stop, &format,
- &stop);
- gst_wildmidi_src_convert (wildmidi, src_format, segment->time, &format,
- &time);
-
- newseg.format = format;
- newseg.start = start;
- newseg.stop = stop;
- newseg.time = time;
-
- event = gst_event_new_segment (&newseg);
-
- return event;
-}
-
-static gboolean
-gst_wildmidi_do_seek (GstWildmidi * wildmidi, GstEvent * event)
-{
- gdouble rate;
- GstFormat src_format, dst_format;
- GstSeekFlags flags;
- GstSeekType start_type, stop_type;
- gint64 start, stop;
- gboolean flush, update;
-#ifdef HAVE_WILDMIDI_0_2_2
- gboolean accurate;
-#endif
- gboolean res;
- unsigned long int sample;
- GstSegment *segment;
-
- if (!wildmidi->song)
- return FALSE;
-
- gst_event_parse_seek (event, &rate, &src_format, &flags,
- &start_type, &start, &stop_type, &stop);
-
- /* convert the input format to samples */
- dst_format = GST_FORMAT_DEFAULT;
- res = TRUE;
- if (start_type != GST_SEEK_TYPE_NONE) {
- res =
- gst_wildmidi_src_convert (wildmidi, src_format, start, &dst_format,
- &start);
- }
- if (res && stop_type != GST_SEEK_TYPE_NONE) {
- res =
- gst_wildmidi_src_convert (wildmidi, src_format, stop, &dst_format,
- &stop);
- }
- /* unsupported format */
- if (!res)
- return res;
-
- flush = ((flags & GST_SEEK_FLAG_FLUSH) == GST_SEEK_FLAG_FLUSH);
-#ifdef HAVE_WILDMIDI_0_2_2
- accurate = ((flags & GST_SEEK_FLAG_ACCURATE) == GST_SEEK_FLAG_ACCURATE);
-#endif
-
- if (flush) {
- GST_DEBUG ("performing flush");
- gst_pad_push_event (wildmidi->srcpad, gst_event_new_flush_start ());
- } else {
- gst_pad_stop_task (wildmidi->sinkpad);
- }
-
- segment = wildmidi->o_segment;
-
- GST_PAD_STREAM_LOCK (wildmidi->sinkpad);
-
- if (flush) {
- gst_pad_push_event (wildmidi->srcpad, gst_event_new_flush_stop (TRUE));
- }
-
- /* update the segment now */
- gst_segment_do_seek (segment, rate, dst_format, flags,
- start_type, start, stop_type, stop, &update);
-
- /* we need to seek to position in the segment now, sample will be updated */
- sample = segment->position;
-
- GST_OBJECT_LOCK (wildmidi);
-#ifdef HAVE_WILDMIDI_0_2_2
- if (accurate) {
- WildMidi_SampledSeek (wildmidi->song, &sample);
- } else {
- WildMidi_FastSeek (wildmidi->song, &sample);
- }
-#else
- WildMidi_FastSeek (wildmidi->song, &sample);
-#endif
-
- GST_OBJECT_UNLOCK (wildmidi);
-
- segment->start = segment->time = segment->position = sample;
-
- gst_pad_push_event (wildmidi->srcpad,
- gst_wildmidi_get_new_segment_event (wildmidi, GST_FORMAT_TIME));
-
- gst_pad_start_task (wildmidi->sinkpad,
- (GstTaskFunction) gst_wildmidi_loop, wildmidi->sinkpad, NULL);
-
- wildmidi->discont = TRUE;
- GST_PAD_STREAM_UNLOCK (wildmidi->sinkpad);
- GST_DEBUG ("seek done");
-
- return TRUE;
-}
-
-static gboolean
-gst_wildmidi_src_event (GstPad * pad, GstObject * parent, GstEvent * event)
-{
- gboolean res = FALSE;
- GstWildmidi *wildmidi = GST_WILDMIDI (parent);
-
- GST_DEBUG_OBJECT (pad, "%s event received", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_SEEK:
- res = gst_wildmidi_do_seek (wildmidi, event);
- break;
- default:
- break;
- }
- gst_event_unref (event);
-
- return res;
-}
-
-
-static gboolean
-gst_wildmidi_activate (GstPad * sinkpad, GstObject * parent)
-{
- GstQuery *query;
- gboolean pull_mode;
-
- query = gst_query_new_scheduling ();
-
- if (!gst_pad_peer_query (sinkpad, query)) {
- gst_query_unref (query);
- goto activate_push;
- }
-
- pull_mode = gst_query_has_scheduling_mode_with_flags (query,
- GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE);
- gst_query_unref (query);
-
- if (!pull_mode)
- goto activate_push;
-
- GST_DEBUG_OBJECT (sinkpad, "activating pull");
- return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE);
-
-activate_push:
- {
- GST_DEBUG_OBJECT (sinkpad, "activating push");
- return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE);
- }
-}
-
-static gboolean
-gst_wildmidi_activatemode (GstPad * pad, GstObject * parent,
- GstPadMode mode, gboolean active)
-{
- gboolean res;
-
- switch (mode) {
- case GST_PAD_MODE_PUSH:
- res = TRUE;
- break;
- case GST_PAD_MODE_PULL:
- if (active) {
- res = gst_pad_start_task (pad, (GstTaskFunction) gst_wildmidi_loop,
- pad, NULL);
- } else {
- res = gst_pad_stop_task (pad);
- }
- break;
- default:
- res = FALSE;
- break;
- }
- return res;
-}
-
-static GstBuffer *
-gst_wildmidi_clip_buffer (GstWildmidi * wildmidi, GstBuffer * buffer)
-{
- guint64 start, stop;
- guint64 new_start, new_stop;
- gint64 offset, length;
- guint64 bpf;
-
- /* clipping disabled for now */
- return buffer;
-
- start = GST_BUFFER_OFFSET (buffer);
- stop = GST_BUFFER_OFFSET_END (buffer);
-
- if (!gst_segment_clip (wildmidi->o_segment, GST_FORMAT_DEFAULT,
- start, stop, &new_start, &new_stop)) {
- gst_buffer_unref (buffer);
- return NULL;
- }
-
- if (start == new_start && stop == new_stop)
- return buffer;
-
-
- offset = new_start - start;
- length = new_stop - new_start;
-
- bpf = wildmidi->bytes_per_frame;
- buffer = gst_buffer_make_writable (buffer);
- gst_buffer_resize (buffer, offset * bpf, length * bpf);
-
- GST_BUFFER_OFFSET (buffer) = new_start;
- GST_BUFFER_OFFSET_END (buffer) = new_stop;
- GST_BUFFER_TIMESTAMP (buffer) =
- gst_util_uint64_scale_int (new_start, GST_SECOND, WILDMIDI_RATE);
- GST_BUFFER_DURATION (buffer) =
- gst_util_uint64_scale_int (new_stop, GST_SECOND, WILDMIDI_RATE) -
- GST_BUFFER_TIMESTAMP (buffer);
-
- return buffer;
-}
-
-/* generate audio data and advance internal timers */
-static GstBuffer *
-gst_wildmidi_get_buffer (GstWildmidi * wildmidi)
-{
- size_t size;
- gint64 samples;
- GstBuffer *buffer;
- GstSegment *segment;
- GstMapInfo info;
- guint bpf;
-
- bpf = wildmidi->bytes_per_frame;
-
- buffer = gst_buffer_new_and_alloc (256 * bpf);
-
- gst_buffer_map (buffer, &info, GST_MAP_READWRITE);
-
- GST_OBJECT_LOCK (wildmidi);
- size = WildMidi_GetOutput (wildmidi->song, (gpointer) info.data,
- (unsigned long int) info.size);
- GST_OBJECT_UNLOCK (wildmidi);
-
- gst_buffer_unmap (buffer, &info);
-
- if (size == 0) {
- gst_buffer_unref (buffer);
- return NULL;
- }
-
- /* adjust buffer size */
- gst_buffer_resize (buffer, 0, size);
-
- segment = wildmidi->o_segment;
-
- GST_BUFFER_OFFSET (buffer) = segment->position;
- GST_BUFFER_TIMESTAMP (buffer) =
- gst_util_uint64_scale_int (segment->position, GST_SECOND, WILDMIDI_RATE);
-
- samples = size / bpf;
- segment->position += samples;
-
- GST_BUFFER_OFFSET_END (buffer) = segment->position;
- GST_BUFFER_DURATION (buffer) =
- gst_util_uint64_scale_int (segment->position, GST_SECOND,
- WILDMIDI_RATE) - GST_BUFFER_TIMESTAMP (buffer);
-
- GST_DEBUG_OBJECT (wildmidi, "buffer ts: %" GST_TIME_FORMAT ", "
- "duration: %" GST_TIME_FORMAT " (%" G_GINT64_FORMAT " samples)",
- GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer)),
- GST_TIME_ARGS (GST_BUFFER_DURATION (buffer)), samples);
-
- return gst_wildmidi_clip_buffer (wildmidi, buffer);
-}
-
-static GstFlowReturn
-gst_wildmidi_parse_song (GstWildmidi * wildmidi)
-{
- struct _WM_Info *info;
- GstCaps *outcaps;
- guint8 *data;
- guint size;
-
- GST_DEBUG_OBJECT (wildmidi, "Parsing song");
-
- size = gst_adapter_available (wildmidi->adapter);
- data = gst_adapter_take (wildmidi->adapter, size);
-
- /* this method takes our memory block */
- GST_OBJECT_LOCK (wildmidi);
- wildmidi->song = WildMidi_OpenBuffer (data, size);
-
- if (!wildmidi->song)
- goto open_failed;
-
-#ifdef HAVE_WILDMIDI_0_2_2
- WildMidi_LoadSamples (wildmidi->song);
-#endif
-
-#ifdef HAVE_WILDMIDI_0_2_2
- WildMidi_SetOption (wildmidi->song, WM_MO_LINEAR_VOLUME,
- wildmidi->linear_volume);
- WildMidi_SetOption (wildmidi->song, WM_MO_EXPENSIVE_INTERPOLATION,
- wildmidi->high_quality);
-#else
- WildMidi_SetOption (wildmidi->song, WM_MO_LOG_VOLUME,
- !wildmidi->linear_volume);
- WildMidi_SetOption (wildmidi->song, WM_MO_ENHANCED_RESAMPLING,
- wildmidi->high_quality);
-#endif
-
- info = WildMidi_GetInfo (wildmidi->song);
- GST_OBJECT_UNLOCK (wildmidi);
-
- wildmidi->o_len = info->approx_total_samples;
-
- outcaps = gst_caps_copy (gst_pad_get_pad_template_caps (wildmidi->srcpad));
- gst_pad_set_caps (wildmidi->srcpad, outcaps);
- gst_caps_unref (outcaps);
-
- /* we keep an internal segment in samples */
- gst_segment_init (wildmidi->o_segment, GST_FORMAT_DEFAULT);
-
- gst_pad_push_event (wildmidi->srcpad,
- gst_wildmidi_get_new_segment_event (wildmidi, GST_FORMAT_TIME));
-
- GST_DEBUG_OBJECT (wildmidi, "Parsing song done");
-
- return GST_FLOW_OK;
-
- /* ERRORS */
-open_failed:
- {
- GST_OBJECT_UNLOCK (wildmidi);
- GST_ELEMENT_ERROR (wildmidi, STREAM, DECODE, (NULL),
- ("Unable to parse midi data"));
- return GST_FLOW_ERROR;
- }
-}
-
-static GstFlowReturn
-gst_wildmidi_do_play (GstWildmidi * wildmidi)
-{
- GstBuffer *out;
- GstFlowReturn ret;
-
- if (!(out = gst_wildmidi_get_buffer (wildmidi)))
- goto eos;
-
- if (wildmidi->discont) {
- GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DISCONT);
- wildmidi->discont = FALSE;
- }
-
- ret = gst_pad_push (wildmidi->srcpad, out);
-
- return ret;
-
- /* ERRORS */
-eos:
- {
- GST_LOG_OBJECT (wildmidi, "Song ended");
- return GST_FLOW_EOS;
- }
-}
-
-static gboolean
-gst_wildmidi_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
-{
- gboolean res;
- GstWildmidi *wildmidi = GST_WILDMIDI (parent);
-
- GST_DEBUG_OBJECT (pad, "%s event received", GST_EVENT_TYPE_NAME (event));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_EOS:
- wildmidi->state = GST_WILDMIDI_STATE_PARSE;
- /* now start the parsing task */
- res = gst_pad_start_task (wildmidi->sinkpad,
- (GstTaskFunction) gst_wildmidi_loop, wildmidi->sinkpad, NULL);
- /* don't forward the event */
- gst_event_unref (event);
- break;
- default:
- res = gst_pad_event_default (pad, parent, event);
- break;
- }
- return res;
-}
-
-static GstFlowReturn
-gst_wildmidi_chain (GstPad * sinkpad, GstObject * parent, GstBuffer * buffer)
-{
- GstWildmidi *wildmidi;
-
- wildmidi = GST_WILDMIDI (parent);
-
- /* push stuff in the adapter, we will start doing something in the sink event
- * handler when we get EOS */
- gst_adapter_push (wildmidi->adapter, buffer);
-
- return GST_FLOW_OK;
-}
-
-static void
-gst_wildmidi_loop (GstPad * sinkpad)
-{
- GstWildmidi *wildmidi = GST_WILDMIDI (GST_PAD_PARENT (sinkpad));
- GstFlowReturn ret;
-
- switch (wildmidi->state) {
- case GST_WILDMIDI_STATE_LOAD:
- {
- GstBuffer *buffer = NULL;
-
- GST_DEBUG_OBJECT (wildmidi, "loading song");
-
- ret =
- gst_pad_pull_range (wildmidi->sinkpad, wildmidi->offset, -1, &buffer);
-
- if (ret == GST_FLOW_EOS) {
- GST_DEBUG_OBJECT (wildmidi, "Song loaded");
- wildmidi->state = GST_WILDMIDI_STATE_PARSE;
- } else if (ret != GST_FLOW_OK) {
- GST_ELEMENT_ERROR (wildmidi, STREAM, DECODE, (NULL),
- ("Unable to read song"));
- goto pause;
- } else {
- GST_DEBUG_OBJECT (wildmidi, "pushing buffer");
- gst_adapter_push (wildmidi->adapter, buffer);
- wildmidi->offset += gst_buffer_get_size (buffer);
- }
- break;
- }
- case GST_WILDMIDI_STATE_PARSE:
- ret = gst_wildmidi_parse_song (wildmidi);
- if (ret != GST_FLOW_OK)
- goto pause;
- wildmidi->state = GST_WILDMIDI_STATE_PLAY;
- break;
- case GST_WILDMIDI_STATE_PLAY:
- ret = gst_wildmidi_do_play (wildmidi);
- if (ret != GST_FLOW_OK)
- goto pause;
- break;
- default:
- break;
- }
- return;
-
-pause:
- {
- const gchar *reason = gst_flow_get_name (ret);
- GstEvent *event;
-
- GST_DEBUG_OBJECT (wildmidi, "pausing task, reason %s", reason);
- gst_pad_pause_task (sinkpad);
- if (ret == GST_FLOW_EOS) {
- /* perform EOS logic */
- event = gst_event_new_eos ();
- gst_pad_push_event (wildmidi->srcpad, event);
- } else if (ret == GST_FLOW_NOT_LINKED || ret < GST_FLOW_EOS) {
- event = gst_event_new_eos ();
- /* for fatal errors we post an error message, post the error
- * first so the app knows about the error first. */
- GST_ELEMENT_FLOW_ERROR (wildmidi, ret);
- gst_pad_push_event (wildmidi->srcpad, event);
- }
- }
-}
-
-static GstStateChangeReturn
-gst_wildmidi_change_state (GstElement * element, GstStateChange transition)
-{
- GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
- GstWildmidi *wildmidi = GST_WILDMIDI (element);
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- wildmidi->offset = 0;
- wildmidi->state = GST_WILDMIDI_STATE_LOAD;
- wildmidi->discont = FALSE;
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- GST_OBJECT_LOCK (wildmidi);
- if (wildmidi->song)
- WildMidi_Close (wildmidi->song);
- wildmidi->song = NULL;
- GST_OBJECT_UNLOCK (wildmidi);
- gst_adapter_clear (wildmidi->adapter);
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-static void
-gst_wildmidi_set_property (GObject * object, guint prop_id,
- const GValue * value, GParamSpec * pspec)
-{
- GstWildmidi *wildmidi;
-
- g_return_if_fail (GST_IS_WILDMIDI (object));
-
- wildmidi = GST_WILDMIDI (object);
-
- switch (prop_id) {
- case PROP_LINEAR_VOLUME:
- GST_OBJECT_LOCK (object);
- wildmidi->linear_volume = g_value_get_boolean (value);
- if (wildmidi->song)
-#ifdef HAVE_WILDMIDI_0_2_2
- WildMidi_SetOption (wildmidi->song, WM_MO_LINEAR_VOLUME,
- wildmidi->linear_volume);
-#else
- WildMidi_SetOption (wildmidi->song, WM_MO_LOG_VOLUME,
- !wildmidi->linear_volume);
-#endif
- GST_OBJECT_UNLOCK (object);
- break;
- case PROP_HIGH_QUALITY:
- GST_OBJECT_LOCK (object);
- wildmidi->high_quality = g_value_get_boolean (value);
- if (wildmidi->song)
-#ifdef HAVE_WILDMIDI_0_2_2
- WildMidi_SetOption (wildmidi->song, WM_MO_EXPENSIVE_INTERPOLATION,
- wildmidi->high_quality);
-#else
- WildMidi_SetOption (wildmidi->song, WM_MO_ENHANCED_RESAMPLING,
- wildmidi->high_quality);
-#endif
- GST_OBJECT_UNLOCK (object);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-gst_wildmidi_get_property (GObject * object, guint prop_id,
- GValue * value, GParamSpec * pspec)
-{
- GstWildmidi *wildmidi;
-
- g_return_if_fail (GST_IS_WILDMIDI (object));
-
- wildmidi = GST_WILDMIDI (object);
-
- switch (prop_id) {
- case PROP_LINEAR_VOLUME:
- GST_OBJECT_LOCK (object);
- g_value_set_boolean (value, wildmidi->linear_volume);
- GST_OBJECT_UNLOCK (object);
- break;
- case PROP_HIGH_QUALITY:
- GST_OBJECT_LOCK (object);
- g_value_set_boolean (value, wildmidi->high_quality);
- GST_OBJECT_UNLOCK (object);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- GST_DEBUG_CATEGORY_INIT (gst_wildmidi_debug, "wildmidi",
- 0, "Wildmidi plugin");
-
- if (!wildmidi_open_config ()) {
- GST_WARNING ("Can't initialize wildmidi");
- return FALSE;
- }
-
- return gst_element_register (plugin, "wildmidi",
- GST_RANK_SECONDARY, GST_TYPE_WILDMIDI);
-}
-
-GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
- GST_VERSION_MINOR,
- wildmidi,
- "Wildmidi Plugin",
- plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/ext/wildmidi/gstwildmidi.h b/ext/wildmidi/gstwildmidi.h
deleted file mode 100644
index 54a915b30..000000000
--- a/ext/wildmidi/gstwildmidi.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * gstwildmidi - wildmidi plugin for gstreamer
- *
- * Copyright 2007 Wouter Paesen <wouter@blue-gate.be>
- *
- * 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.
- *
- * Wrapper element for libtimidity. This element works in pull
- * based mode because that's essentially how libwildmidi works.
- * We create a libwildmidi stream that operates on the srcpad.
- * The sinkpad is in pull mode.
- */
-
-#ifndef __GST_WILDMIDI_H__
-#define __GST_WILDMIDI_H__
-
-#include <gst/gst.h>
-#include <gst/base/gstadapter.h>
-#include <wildmidi_lib.h>
-
-G_BEGIN_DECLS
-
-#define GST_TYPE_WILDMIDI \
- (gst_wildmidi_get_type())
-#define GST_WILDMIDI(obj) \
- (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_WILDMIDI,GstWildmidi))
-#define GST_WILDMIDI_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_WILDMIDI,GstWildmidiClass))
-#define GST_IS_WILDMIDI(obj) \
- (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_WILDMIDI))
-#define GST_IS_WILDMIDI_CLASS(klass) \
- (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_WILDMIDI))
-
-typedef struct _GstWildmidi GstWildmidi;
-typedef struct _GstWildmidiClass GstWildmidiClass;
-
-typedef enum {
- GST_WILDMIDI_STATE_LOAD,
- GST_WILDMIDI_STATE_PARSE,
- GST_WILDMIDI_STATE_PLAY
-} GstWildmidiState;
-
-struct _GstWildmidi
-{
- GstElement element;
-
- GstPad *sinkpad, *srcpad;
-
- /* input stream properties */
- GstWildmidiState state;
- GstAdapter *adapter;
- midi *song;
- guint64 offset;
-
- /* output data */
- gboolean discont;
- GstSegment o_segment[1];
- gint64 o_len;
-
- /* format of the stream */
- guint bytes_per_frame;
-
- /* wildmidi settings */
- gboolean high_quality;
- gboolean linear_volume;
-};
-
-struct _GstWildmidiClass
-{
- GstElementClass parent_class;
-};
-
-GType gst_wildmidi_get_type (void);
-
-G_END_DECLS
-
-#endif /* __GST_WILDMIDI_H__ */
diff --git a/ext/wildmidi/gstwildmididec.c b/ext/wildmidi/gstwildmididec.c
new file mode 100644
index 000000000..69aa75c32
--- /dev/null
+++ b/ext/wildmidi/gstwildmididec.c
@@ -0,0 +1,683 @@
+/* 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.
+ */
+
+
+/**
+ * SECTION:element-wildmididec
+ * @see_also: #GstWildmidiDec
+ *
+ * wildmididec decodes MIDI files.
+ * It uses <ulink url="https://www.mindwerks.net/projects/wildmidi/">WildMidi</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.mid ! wildmididec ! audioconvert ! audioresample ! autoaudiosink
+ * ]|
+ * </refsect2>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gst/gst.h>
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+
+#ifndef R_OK
+#define R_OK 4 /* Test for read permission */
+#endif
+
+#else
+#include <unistd.h>
+#endif
+
+#include "gstwildmididec.h"
+
+
+GST_DEBUG_CATEGORY_STATIC (wildmididec_debug);
+#define GST_CAT_DEFAULT wildmididec_debug
+
+
+/* This is hardcoded because the sample rate is set once,
+ * globally, in WildMidi_Init() */
+#define WILDMIDI_SAMPLE_RATE 44100
+/* WildMidi always outputs stereo data */
+#define WILDMIDI_NUM_CHANNELS 2
+
+#ifndef WILDMIDI_CFG
+#define WILDMIDI_CFG "/etc/timidity.cfg"
+#endif
+
+#define DEFAULT_LOG_VOLUME_SCALE TRUE
+#define DEFAULT_ENHANCED_RESAMPLING TRUE
+#define DEFAULT_REVERB FALSE
+#define DEFAULT_OUTPUT_BUFFER_SIZE 1024
+
+
+enum
+{
+ PROP_0,
+ PROP_LOG_VOLUME_SCALE,
+ PROP_ENHANCED_RESAMPLING,
+ PROP_REVERB,
+ PROP_OUTPUT_BUFFER_SIZE
+};
+
+
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/midi; audio/riff-midi")
+ );
+
+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) ", "
+ "layout = (string) interleaved, "
+ "rate = (int) " G_STRINGIFY (WILDMIDI_SAMPLE_RATE) ", "
+ "channels = (int) " G_STRINGIFY (WILDMIDI_NUM_CHANNELS)
+ )
+ );
+
+
+
+G_DEFINE_TYPE (GstWildmidiDec, gst_wildmidi_dec,
+ GST_TYPE_NONSTREAM_AUDIO_DECODER);
+
+
+
+static void gst_wildmidi_dec_finalize (GObject * object);
+
+static void gst_wildmidi_dec_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_wildmidi_dec_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+static gboolean gst_wildmidi_dec_seek (GstNonstreamAudioDecoder * dec,
+ GstClockTime * new_position);
+static GstClockTime gst_wildmidi_dec_tell (GstNonstreamAudioDecoder * dec);
+
+static gboolean gst_wildmidi_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 guint gst_wildmidi_dec_get_current_subsong (GstNonstreamAudioDecoder *
+ dec);
+
+static guint gst_wildmidi_dec_get_num_subsongs (GstNonstreamAudioDecoder * dec);
+static GstClockTime
+gst_wildmidi_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
+ guint subsong);
+
+static guint
+gst_wildmidi_dec_get_supported_output_modes (GstNonstreamAudioDecoder * dec);
+static gboolean gst_wildmidi_dec_decode (GstNonstreamAudioDecoder * dec,
+ GstBuffer ** buffer, guint * num_samples);
+
+static void gst_wildmidi_dec_update_options (GstWildmidiDec * wildmidi_dec);
+
+
+
+static GMutex load_mutex;
+static unsigned long init_refcount = 0;
+static volatile gint wildmidi_initialized = 0;
+
+
+static gchar *
+gst_wildmidi_get_config_path (void)
+{
+ /* This code is adapted from the original wildmidi
+ * gst-plugins-bad decoder element */
+
+ gchar *path = g_strdup (g_getenv ("WILDMIDI_CFG"));
+
+ GST_DEBUG
+ ("trying configuration path \"%s\" from WILDMIDI_CFG environment variable",
+ GST_STR_NULL (path));
+ if (path && (g_access (path, R_OK) == -1)) {
+ g_free (path);
+ path = NULL;
+ }
+
+ if (path == NULL) {
+ path =
+ g_build_path (G_DIR_SEPARATOR_S, g_get_home_dir (), ".wildmidirc",
+ NULL);
+ GST_DEBUG ("trying configuration path \"%s\"", path);
+ if (path && (g_access (path, R_OK) == -1)) {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ if (path == NULL) {
+ path =
+ g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc",
+ "wildmidi.cfg", NULL);
+ GST_DEBUG ("trying configuration path \"%s\"", path);
+ if (path && (g_access (path, R_OK) == -1)) {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ if (path == NULL) {
+ path =
+ g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc", "wildmidi",
+ "wildmidi.cfg", NULL);
+ GST_DEBUG ("trying configuration path \"%s\"", path);
+ if (path && (g_access (path, R_OK) == -1)) {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ if (path == NULL) {
+ path = g_strdup (WILDMIDI_CFG);
+ GST_DEBUG ("trying default configuration path \"%s\"", path);
+ if (path && (g_access (path, R_OK) == -1)) {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ if (path == NULL) {
+ path =
+ g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc",
+ "timidity.cfg", NULL);
+ GST_DEBUG ("trying TiMidity configuration path \"%s\"", path);
+ if (path && (g_access (path, R_OK) == -1)) {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ if (path == NULL) {
+ path =
+ g_build_path (G_DIR_SEPARATOR_S, G_DIR_SEPARATOR_S "etc", "timidity",
+ "timidity.cfg", NULL);
+ GST_DEBUG ("trying TiMidity configuration path \"%s\"", path);
+ if (path && (g_access (path, R_OK) == -1)) {
+ g_free (path);
+ path = NULL;
+ }
+ }
+
+ return path;
+}
+
+
+static void
+gst_wildmidi_init_library (void)
+{
+ GST_DEBUG ("WildMidi init instance counter: %lu", init_refcount);
+
+ g_mutex_lock (&load_mutex);
+
+ if (init_refcount != 0) {
+ ++init_refcount;
+ } else {
+ gchar *config_path = gst_wildmidi_get_config_path ();
+ if (config_path != NULL) {
+ int ret = WildMidi_Init (config_path, WILDMIDI_SAMPLE_RATE, 0);
+ g_free (config_path);
+
+ if (ret == 0) {
+ GST_DEBUG ("WildMidi initialized, version string: %s",
+ WildMidi_GetString (WM_GS_VERSION));
+ ++init_refcount;
+ g_atomic_int_set (&wildmidi_initialized, 1);
+ } else {
+ GST_ERROR ("initializing WildMidi failed");
+ g_atomic_int_set (&wildmidi_initialized, 0);
+ }
+ } else {
+ GST_ERROR ("no config file, can't initialise");
+ g_atomic_int_set (&wildmidi_initialized, 0);
+ }
+ }
+
+ g_mutex_unlock (&load_mutex);
+}
+
+
+static void
+gst_wildmidi_shutdown_library (void)
+{
+ GST_DEBUG ("WildMidi init instance counter: %lu", init_refcount);
+
+ g_mutex_lock (&load_mutex);
+
+ if (init_refcount != 0) {
+ --init_refcount;
+ if (init_refcount == 0) {
+ WildMidi_Shutdown ();
+ GST_DEBUG ("WildMidi shut down");
+ g_atomic_int_set (&wildmidi_initialized, 0);
+ }
+ }
+
+ g_mutex_unlock (&load_mutex);
+}
+
+
+
+void
+gst_wildmidi_dec_class_init (GstWildmidiDecClass * klass)
+{
+ GObjectClass *object_class;
+ GstElementClass *element_class;
+ GstNonstreamAudioDecoderClass *dec_class;
+
+ GST_DEBUG_CATEGORY_INIT (wildmididec_debug, "wildmididec", 0,
+ "WildMidi-based MIDI 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_wildmidi_dec_finalize);
+ object_class->set_property =
+ GST_DEBUG_FUNCPTR (gst_wildmidi_dec_set_property);
+ object_class->get_property =
+ GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_property);
+
+ dec_class->tell = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_tell);
+ dec_class->seek = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_seek);
+ dec_class->load_from_buffer =
+ GST_DEBUG_FUNCPTR (gst_wildmidi_dec_load_from_buffer);
+ dec_class->get_current_subsong =
+ GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_current_subsong);
+ dec_class->get_num_subsongs =
+ GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_num_subsongs);
+ dec_class->get_subsong_duration =
+ GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_subsong_duration);
+ dec_class->get_supported_output_modes =
+ GST_DEBUG_FUNCPTR (gst_wildmidi_dec_get_supported_output_modes);
+ dec_class->decode = GST_DEBUG_FUNCPTR (gst_wildmidi_dec_decode);
+
+ gst_element_class_set_static_metadata (element_class,
+ "WildMidi-based MIDI music decoder",
+ "Codec/Decoder/Audio",
+ "Decodes MIDI music using WildMidi",
+ "Carlos Rafael Giani <dv@pseudoterminal.org>");
+
+ g_object_class_install_property (object_class,
+ PROP_LOG_VOLUME_SCALE,
+ g_param_spec_boolean ("log-volume-scale",
+ "Logarithmic volume scale",
+ "Use a logarithmic volume scale if set to TRUE, or a linear scale if set to FALSE",
+ DEFAULT_LOG_VOLUME_SCALE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+ g_object_class_install_property (object_class,
+ PROP_ENHANCED_RESAMPLING,
+ g_param_spec_boolean ("enhanced-resampling",
+ "Enhanced resampling",
+ "Use enhanced resampling if set to TRUE, or linear interpolation if set to FALSE",
+ DEFAULT_ENHANCED_RESAMPLING,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+ g_object_class_install_property (object_class,
+ PROP_REVERB,
+ g_param_spec_boolean ("reverb",
+ "Reverb",
+ "Whether or not to enable the WildMidi 8 reflection reverb engine to add more depth to the sound",
+ DEFAULT_REVERB, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+ /* 2*2 => stereo output with S16 samples; the division 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 / (2 * 2),
+ DEFAULT_OUTPUT_BUFFER_SIZE,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)
+ );
+}
+
+
+void
+gst_wildmidi_dec_init (GstWildmidiDec * wildmidi_dec)
+{
+ wildmidi_dec->song = NULL;
+
+ wildmidi_dec->log_volume_scale = DEFAULT_LOG_VOLUME_SCALE;
+ wildmidi_dec->enhanced_resampling = DEFAULT_ENHANCED_RESAMPLING;
+ wildmidi_dec->reverb = DEFAULT_REVERB;
+ wildmidi_dec->output_buffer_size = DEFAULT_OUTPUT_BUFFER_SIZE;
+
+ gst_wildmidi_init_library ();
+}
+
+
+static void
+gst_wildmidi_dec_finalize (GObject * object)
+{
+ GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (object);
+
+ if (wildmidi_dec->song != NULL)
+ WildMidi_Close (wildmidi_dec->song);
+
+ gst_wildmidi_shutdown_library ();
+
+ G_OBJECT_CLASS (gst_wildmidi_dec_parent_class)->finalize (object);
+}
+
+
+static void
+gst_wildmidi_dec_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstWildmidiDec *wildmidi_dec;
+
+ wildmidi_dec = GST_WILDMIDI_DEC (object);
+
+ switch (prop_id) {
+ case PROP_LOG_VOLUME_SCALE:
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ wildmidi_dec->log_volume_scale = g_value_get_boolean (value);
+ gst_wildmidi_dec_update_options (wildmidi_dec);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+
+ case PROP_ENHANCED_RESAMPLING:
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ wildmidi_dec->enhanced_resampling = g_value_get_boolean (value);
+ gst_wildmidi_dec_update_options (wildmidi_dec);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+
+ case PROP_REVERB:
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ wildmidi_dec->reverb = g_value_get_boolean (value);
+ gst_wildmidi_dec_update_options (wildmidi_dec);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+
+ case PROP_OUTPUT_BUFFER_SIZE:
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ wildmidi_dec->output_buffer_size = g_value_get_uint (value);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_wildmidi_dec_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (object);
+
+ switch (prop_id) {
+ case PROP_LOG_VOLUME_SCALE:
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ g_value_set_boolean (value, wildmidi_dec->log_volume_scale);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+
+ case PROP_ENHANCED_RESAMPLING:
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ g_value_set_boolean (value, wildmidi_dec->enhanced_resampling);
+ GST_NONSTREAM_AUDIO_DECODER_UNLOCK_MUTEX (object);
+ break;
+
+ case PROP_REVERB:
+ GST_NONSTREAM_AUDIO_DECODER_LOCK_MUTEX (object);
+ g_value_set_boolean (value, wildmidi_dec->reverb);
+ 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, wildmidi_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_wildmidi_dec_seek (GstNonstreamAudioDecoder * dec,
+ GstClockTime * new_position)
+{
+ GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
+ unsigned long int sample_pos =
+ gst_util_uint64_scale_int (*new_position, WILDMIDI_SAMPLE_RATE,
+ GST_SECOND);
+
+ if (G_UNLIKELY (wildmidi_dec->song == NULL))
+ return FALSE;
+
+ WildMidi_FastSeek (wildmidi_dec->song, &sample_pos);
+
+ *new_position =
+ gst_util_uint64_scale_int (sample_pos, GST_SECOND, WILDMIDI_SAMPLE_RATE);
+ return TRUE;
+}
+
+
+static GstClockTime
+gst_wildmidi_dec_tell (GstNonstreamAudioDecoder * dec)
+{
+ GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
+ struct _WM_Info *info;
+
+ if (G_UNLIKELY (wildmidi_dec->song == NULL))
+ return GST_CLOCK_TIME_NONE;
+
+ info = WildMidi_GetInfo (wildmidi_dec->song);
+ return gst_util_uint64_scale_int (info->current_sample, GST_SECOND,
+ WILDMIDI_SAMPLE_RATE);
+}
+
+
+static gboolean
+gst_wildmidi_dec_load_from_buffer (GstNonstreamAudioDecoder * dec,
+ GstBuffer * source_data, G_GNUC_UNUSED guint initial_subsong,
+ G_GNUC_UNUSED GstNonstreamAudioSubsongMode initial_subsong_mode,
+ GstClockTime * initial_position,
+ GstNonstreamAudioOutputMode * initial_output_mode,
+ G_GNUC_UNUSED gint * initial_num_loops)
+{
+ GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
+ GstMapInfo buffer_map;
+
+
+ if (g_atomic_int_get (&wildmidi_initialized) == 0) {
+ GST_ERROR_OBJECT (wildmidi_dec,
+ "Could not start loading: WildMidi is not initialized");
+ return FALSE;
+ }
+
+
+ /* Set output format */
+ if (!gst_nonstream_audio_decoder_set_output_format_simple (dec,
+ WILDMIDI_SAMPLE_RATE, GST_AUDIO_FORMAT_S16, WILDMIDI_NUM_CHANNELS))
+ return FALSE;
+
+
+ /* Load MIDI */
+ gst_buffer_map (source_data, &buffer_map, GST_MAP_READ);
+ wildmidi_dec->song = WildMidi_OpenBuffer (buffer_map.data, buffer_map.size);
+ gst_buffer_unmap (source_data, &buffer_map);
+
+ if (wildmidi_dec->song == NULL) {
+ GST_ERROR_OBJECT (wildmidi_dec, "Could not load MIDI tune");
+ return FALSE;
+ }
+
+ gst_wildmidi_dec_update_options (wildmidi_dec);
+
+
+ /* Seek to initial position */
+ if (*initial_position != 0) {
+ unsigned long int sample_pos =
+ gst_util_uint64_scale_int (*initial_position, WILDMIDI_SAMPLE_RATE,
+ GST_SECOND);
+ WildMidi_FastSeek (wildmidi_dec->song, &sample_pos);
+ *initial_position =
+ gst_util_uint64_scale_int (sample_pos, GST_SECOND,
+ WILDMIDI_SAMPLE_RATE);
+ }
+
+
+ /* LOOPING output mode is not supported */
+ *initial_output_mode = GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
+
+
+ return TRUE;
+}
+
+
+static guint
+gst_wildmidi_dec_get_current_subsong (G_GNUC_UNUSED GstNonstreamAudioDecoder *
+ dec)
+{
+ return 0;
+}
+
+
+static guint
+gst_wildmidi_dec_get_num_subsongs (G_GNUC_UNUSED GstNonstreamAudioDecoder * dec)
+{
+ return 1;
+}
+
+
+static GstClockTime
+gst_wildmidi_dec_get_subsong_duration (GstNonstreamAudioDecoder * dec,
+ G_GNUC_UNUSED guint subsong)
+{
+ GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
+ struct _WM_Info *info;
+
+ if (G_UNLIKELY (wildmidi_dec->song == NULL))
+ return GST_CLOCK_TIME_NONE;
+
+ info = WildMidi_GetInfo (wildmidi_dec->song);
+ return gst_util_uint64_scale_int (info->approx_total_samples, GST_SECOND,
+ WILDMIDI_SAMPLE_RATE);
+}
+
+
+static guint
+gst_wildmidi_dec_get_supported_output_modes (G_GNUC_UNUSED
+ GstNonstreamAudioDecoder * dec)
+{
+ return 1u << GST_NONSTREAM_AUDIO_OUTPUT_MODE_STEADY;
+}
+
+
+static gboolean
+gst_wildmidi_dec_decode (GstNonstreamAudioDecoder * dec, GstBuffer ** buffer,
+ guint * num_samples)
+{
+ GstWildmidiDec *wildmidi_dec = GST_WILDMIDI_DEC (dec);
+ GstMapInfo info;
+ GstBuffer *outbuf;
+ gsize outbuf_size;
+ int decoded_size_in_bytes;
+
+ if (G_UNLIKELY (wildmidi_dec->song == NULL))
+ return FALSE;
+
+ /* Allocate output buffer
+ * Multiply by 2 to accomodate for the sample size (16 bit = 2 byte) */
+ outbuf_size = wildmidi_dec->output_buffer_size * 2 * WILDMIDI_NUM_CHANNELS;
+ outbuf =
+ gst_nonstream_audio_decoder_allocate_output_buffer (dec, outbuf_size);
+ if (G_UNLIKELY (outbuf == NULL))
+ return FALSE;
+
+ /* The actual decoding */
+ gst_buffer_map (outbuf, &info, GST_MAP_WRITE);
+ decoded_size_in_bytes =
+ WildMidi_GetOutput (wildmidi_dec->song, (char *) (info.data), info.size);
+ gst_buffer_unmap (outbuf, &info);
+
+ if (decoded_size_in_bytes == 0) {
+ gst_buffer_unref (outbuf);
+ return FALSE;
+ }
+
+ *buffer = outbuf;
+ *num_samples = decoded_size_in_bytes / 2 / WILDMIDI_NUM_CHANNELS;
+
+ return TRUE;
+}
+
+
+static void
+gst_wildmidi_dec_update_options (GstWildmidiDec * wildmidi_dec)
+{
+ unsigned short int options = 0;
+
+ if (wildmidi_dec->song == NULL)
+ return;
+
+ if (wildmidi_dec->log_volume_scale)
+ options |= WM_MO_LOG_VOLUME;
+ if (wildmidi_dec->enhanced_resampling)
+ options |= WM_MO_ENHANCED_RESAMPLING;
+ if (wildmidi_dec->reverb)
+ options |= WM_MO_REVERB;
+
+ WildMidi_SetOption (wildmidi_dec->song,
+ WM_MO_LOG_VOLUME | WM_MO_ENHANCED_RESAMPLING | WM_MO_REVERB, options);
+}
+
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ return gst_element_register (plugin, "wildmididec", GST_RANK_MARGINAL,
+ gst_wildmidi_dec_get_type ());
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ wildmidi,
+ "WildMidi-based MIDI playback plugin",
+ plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/ext/wildmidi/gstwildmididec.h b/ext/wildmidi/gstwildmididec.h
new file mode 100644
index 000000000..cc5aea08b
--- /dev/null
+++ b/ext/wildmidi/gstwildmididec.h
@@ -0,0 +1,69 @@
+/* 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_WILDMIDI_DEC_H__
+#define __GST_WILDMIDI_DEC_H__
+
+
+#include <gst/gst.h>
+#include "gst/audio/gstnonstreamaudiodecoder.h"
+#include <wildmidi_lib.h>
+
+
+G_BEGIN_DECLS
+
+
+typedef struct _GstWildmidiDec GstWildmidiDec;
+typedef struct _GstWildmidiDecClass GstWildmidiDecClass;
+
+
+#define GST_TYPE_WILDMIDI_DEC (gst_wildmidi_dec_get_type())
+#define GST_WILDMIDI_DEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_WILDMIDI_DEC, GstWildmidiDec))
+#define GST_WILDMIDI_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_WILDMIDI_DEC, GstWildmidiDecClass))
+#define GST_IS_WILDMIDI_DEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_WILDMIDI_DEC))
+#define GST_IS_WILDMIDI_DEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_WILDMIDI_DEC))
+
+
+struct _GstWildmidiDec
+{
+ GstNonstreamAudioDecoder parent;
+
+ midi *song;
+
+ gboolean log_volume_scale;
+ gboolean enhanced_resampling;
+ gboolean reverb;
+ guint output_buffer_size;
+};
+
+
+struct _GstWildmidiDecClass
+{
+ GstNonstreamAudioDecoderClass parent_class;
+};
+
+
+GType gst_wildmidi_dec_get_type (void);
+
+
+G_END_DECLS
+
+
+#endif /* __GST_WILDMIDI_DEC_H__ */