mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-11 09:55:36 +00:00
wildmidi: Port to 1.0 on top of the nonstreamaudiodecoder base class
https://bugzilla.gnome.org/show_bug.cgi?id=768576
This commit is contained in:
parent
2e4c6d6a49
commit
ff32a4297f
6 changed files with 765 additions and 1133 deletions
|
@ -103,7 +103,7 @@ EXTRA_HFILES = \
|
|||
$(top_srcdir)/ext/rtmp/gstrtmpsink.h \
|
||||
$(top_srcdir)/ext/spandsp/gstspanplc.h \
|
||||
$(top_srcdir)/ext/spandsp/gstdtmfdetect.h \
|
||||
$(top_srcdir)/ext/wildmidi/gstwildmidi.h \
|
||||
$(top_srcdir)/ext/wildmidi/gstwildmididec.h \
|
||||
$(top_srcdir)/ext/voaacenc/gstvoaacenc.h \
|
||||
$(top_srcdir)/ext/voamrwbenc/gstvoamrwbenc.h \
|
||||
$(top_srcdir)/ext/webrtcdsp/gstwebrtcdsp.h \
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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__ */
|
683
ext/wildmidi/gstwildmididec.c
Normal file
683
ext/wildmidi/gstwildmididec.c
Normal file
|
@ -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)
|
69
ext/wildmidi/gstwildmididec.h
Normal file
69
ext/wildmidi/gstwildmididec.h
Normal file
|
@ -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__ */
|
Loading…
Reference in a new issue