mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
openmpt: Add openmptdec element
https://bugzilla.gnome.org/show_bug.cgi?id=768576
This commit is contained in:
parent
ff32a4297f
commit
e7a5fdfde4
6 changed files with 1129 additions and 0 deletions
10
configure.ac
10
configure.ac
|
@ -2828,6 +2828,14 @@ AG_GST_CHECK_FEATURE(OPENJPEG, [openjpeg library], openjpeg, [
|
|||
AC_SUBST(OPENJPEG_LIBS)
|
||||
])
|
||||
|
||||
dnl *** OpenMPT ***
|
||||
translit(dnm, m, l) AM_CONDITIONAL(USE_OPENMPT, true)
|
||||
AG_GST_CHECK_FEATURE(OPENMPT, openmpt, openmpt, [
|
||||
PKG_CHECK_MODULES(OPENMPT, libopenmpt, HAVE_OPENMPT="yes", HAVE_OPENMPT="no")
|
||||
AC_SUBST(OPENMPT_CFLAGS)
|
||||
AC_SUBST(OPENMPT_LIBS)
|
||||
])
|
||||
|
||||
dnl *** OpenNI2 ***
|
||||
translit(dnm, m, l) AM_CONDITIONAL(USE_OPENNI2, true)
|
||||
AG_GST_CHECK_FEATURE(OPENNI2, [openni2 library], openni2, [
|
||||
|
@ -3384,6 +3392,7 @@ AM_CONDITIONAL(USE_OPENAL, false)
|
|||
AM_CONDITIONAL(USE_OPENCV, false)
|
||||
AM_CONDITIONAL(USE_OPENEXR, false)
|
||||
AM_CONDITIONAL(USE_OPENJPEG, false)
|
||||
AM_CONDITIONAL(USE_OPENMPT, false)
|
||||
AM_CONDITIONAL(USE_OPENNI2, false)
|
||||
AM_CONDITIONAL(USE_OPUS, false)
|
||||
AM_CONDITIONAL(USE_QT, false)
|
||||
|
@ -3685,6 +3694,7 @@ ext/opencv/Makefile
|
|||
ext/openexr/Makefile
|
||||
ext/openh264/Makefile
|
||||
ext/openjpeg/Makefile
|
||||
ext/openmpt/Makefile
|
||||
ext/openni2/Makefile
|
||||
ext/opus/Makefile
|
||||
ext/qt/Makefile
|
||||
|
|
|
@ -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 \
|
||||
|
|
18
ext/openmpt/Makefile.am
Normal file
18
ext/openmpt/Makefile.am
Normal file
|
@ -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
|
968
ext/openmpt/gstopenmptdec.c
Normal file
968
ext/openmpt/gstopenmptdec.c
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
81
ext/openmpt/gstopenmptdec.h
Normal file
81
ext/openmpt/gstopenmptdec.h
Normal file
|
@ -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__ */
|
44
ext/openmpt/plugin.c
Normal file
44
ext/openmpt/plugin.c
Normal file
|
@ -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)
|
Loading…
Reference in a new issue