gstreamer/gst/modplug/gstmodplug.cc

830 lines
24 KiB
C++
Raw Normal View History

/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
Code based on modplugxmms
XMMS plugin:
Kenton Varda <temporal@gauge3d.org>
Sound Engine:
Olivier Lapicque <olivierl@jps.net>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "libmodplug/stdafx.h"
#include "libmodplug/sndfile.h"
#include "gstmodplug.h"
#include <gst/gst.h>
#include <stdlib.h>
#include <gst/audio/audio.h>
/* elementfactory information */
GstElementDetails modplug_details = {
"ModPlug",
"Codec/Decoder/Audio",
"Module decoder based on modplug engine",
"Jeremy SIMON <jsimon13@yahoo.fr>"
};
/* Filter signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_0,
ARG_SONGNAME,
ARG_REVERB,
ARG_REVERB_DEPTH,
ARG_REVERB_DELAY,
ARG_MEGABASS,
ARG_MEGABASS_AMOUNT,
ARG_MEGABASS_RANGE,
ARG_NOISE_REDUCTION,
ARG_SURROUND,
ARG_SURROUND_DEPTH,
ARG_SURROUND_DELAY,
ARG_OVERSAMP
};
static GstStaticPadTemplate modplug_src_template_factory =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw-int, " "endianness = (int) BYTE_ORDER, " "signed = (boolean) TRUE, " "width = (int) 16, " "depth = (int) 16, " "rate = (int) { 8000, 11025, 22050, 44100 }, " /* FIXME? */
"channels = (int) [ 1, 2 ]; " "audio/x-raw-int, " "endianness = (int) BYTE_ORDER, " "signed = (boolean) FALSE, " "width = (int) 8, " "depth = (int) 8, " "rate = (int) { 8000, 11025, 22050, 44100 }, " /* FIXME? */
"channels = (int) [ 1, 2 ]")
);
static GstStaticPadTemplate modplug_sink_template_factory =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-mod")
);
enum
{
MODPLUG_STATE_NEED_TUNE = 1,
MODPLUG_STATE_LOAD_TUNE = 2,
MODPLUG_STATE_PLAY_TUNE = 3
};
static void gst_modplug_base_init (GstModPlugClass * klass);
static void gst_modplug_class_init (GstModPlugClass * klass);
static void gst_modplug_init (GstModPlug * filter);
static void gst_modplug_set_property (GObject * object,
guint id, const GValue * value, GParamSpec * pspec);
static void gst_modplug_get_property (GObject * object,
guint id, GValue * value, GParamSpec * pspec);
static GstPadLinkReturn
gst_modplug_srclink (GstPad * pad, const GstCaps * caps);
static GstCaps *gst_modplug_fixate (GstPad * pad, const GstCaps * caps);
static void gst_modplug_loop (GstElement * element);
static void gst_modplug_setup (GstModPlug * modplug);
static const GstFormat *gst_modplug_get_formats (GstPad * pad);
static const GstQueryType *gst_modplug_get_query_types (GstPad * pad);
static gboolean gst_modplug_src_event (GstPad * pad, GstEvent * event);
static gboolean gst_modplug_src_query (GstPad * pad,
GstQueryType type, GstFormat * format, gint64 * value);
static GstStateChangeReturn gst_modplug_change_state (GstElement * element,
GstStateChange transition);
static GstElementClass *parent_class = NULL;
GType
gst_modplug_get_type (void)
{
static GType modplug_type = 0;
if (!modplug_type) {
static const GTypeInfo modplug_info = {
sizeof (GstModPlugClass),
(GBaseInitFunc) gst_modplug_base_init,
NULL,
(GClassInitFunc) gst_modplug_class_init,
NULL,
NULL,
sizeof (GstModPlug),
0,
(GInstanceInitFunc) gst_modplug_init,
NULL
};
modplug_type =
g_type_register_static (GST_TYPE_ELEMENT, "GstModPlug", &modplug_info,
(GTypeFlags) 0);
}
return modplug_type;
}
static void
gst_modplug_base_init (GstModPlugClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&modplug_sink_template_factory));
gst_element_class_add_pad_template (element_class,
gst_static_pad_template_get (&modplug_src_template_factory));
gst_element_class_set_details (element_class, &modplug_details);
}
static void
gst_modplug_class_init (GstModPlugClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = GST_ELEMENT_CLASS (g_type_class_ref (GST_TYPE_ELEMENT));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SONGNAME,
g_param_spec_string ("songname", "Songname", "The song name",
"", G_PARAM_READABLE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_REVERB,
g_param_spec_boolean ("reverb", "reverb", "reverb",
FALSE, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_REVERB_DEPTH,
g_param_spec_int ("reverb_depth", "reverb_depth", "reverb_depth",
0, 100, 30, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_REVERB_DELAY,
g_param_spec_int ("reverb_delay", "reverb_delay", "reverb_delay",
0, 200, 100, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MEGABASS,
g_param_spec_boolean ("megabass", "megabass", "megabass",
FALSE, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MEGABASS_AMOUNT,
g_param_spec_int ("megabass_amount", "megabass_amount", "megabass_amount",
0, 100, 40, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MEGABASS_RANGE,
g_param_spec_int ("megabass_range", "megabass_range", "megabass_range",
0, 100, 30, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SURROUND,
g_param_spec_boolean ("surround", "surround", "surround",
TRUE, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SURROUND_DEPTH,
g_param_spec_int ("surround_depth", "surround_depth", "surround_depth",
0, 100, 20, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SURROUND_DELAY,
g_param_spec_int ("surround_delay", "surround_delay", "surround_delay",
0, 40, 20, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_OVERSAMP,
g_param_spec_boolean ("oversamp", "oversamp", "oversamp",
TRUE, (GParamFlags) G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NOISE_REDUCTION,
g_param_spec_boolean ("noise_reduction", "noise_reduction",
"noise_reduction", TRUE, (GParamFlags) G_PARAM_READWRITE));
gobject_class->set_property = gst_modplug_set_property;
gobject_class->get_property = gst_modplug_get_property;
gstelement_class->change_state = gst_modplug_change_state;
}
static void
gst_modplug_init (GstModPlug * modplug)
{
modplug->sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get
(&modplug_sink_template_factory), "sink");
gst_element_add_pad (GST_ELEMENT (modplug), modplug->sinkpad);
modplug->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get
(&modplug_src_template_factory), "src");
gst_pad_set_link_function (modplug->srcpad, gst_modplug_srclink);
gst_pad_set_fixate_function (modplug->srcpad, gst_modplug_fixate);
gst_pad_set_event_function (modplug->srcpad,
(GstPadEventFunction) GST_DEBUG_FUNCPTR (gst_modplug_src_event));
gst_pad_set_query_function (modplug->srcpad, gst_modplug_src_query);
gst_pad_set_query_type_function (modplug->srcpad, (GstPadQueryTypeFunction)
GST_DEBUG_FUNCPTR (gst_modplug_get_query_types));
gst_pad_set_formats_function (modplug->srcpad,
(GstPadFormatsFunction) GST_DEBUG_FUNCPTR (gst_modplug_get_formats));
gst_element_add_pad (GST_ELEMENT (modplug), modplug->srcpad);
gst_element_set_loop_function (GST_ELEMENT (modplug), gst_modplug_loop);
modplug->reverb = FALSE;
modplug->reverb_depth = 30;
modplug->reverb_delay = 100;
modplug->megabass = FALSE;
modplug->megabass_amount = 40;
modplug->megabass_range = 30;
modplug->surround = TRUE;
modplug->surround_depth = 20;
modplug->surround_delay = 20;
modplug->oversamp = TRUE;
modplug->noise_reduction = TRUE;
modplug->_16bit = TRUE;
modplug->channel = 2;
modplug->frequency = 44100;
modplug->audiobuffer = NULL;
modplug->buffer_in = NULL;
modplug->state = MODPLUG_STATE_NEED_TUNE;
GST_FLAG_SET (modplug, GST_ELEMENT_EVENT_AWARE);
}
static void
gst_modplug_setup (GstModPlug * modplug)
{
if (modplug->_16bit)
modplug->mSoundFile->SetWaveConfig (modplug->frequency, 16,
modplug->channel);
else
modplug->mSoundFile->SetWaveConfig (modplug->frequency, 8,
modplug->channel);
modplug->mSoundFile->SetWaveConfigEx (modplug->surround, !modplug->oversamp,
modplug->reverb, true, modplug->megabass, modplug->noise_reduction, true);
modplug->mSoundFile->SetResamplingMode (SRCMODE_POLYPHASE);
if (modplug->surround)
modplug->mSoundFile->SetSurroundParameters (modplug->surround_depth,
modplug->surround_delay);
if (modplug->megabass)
modplug->mSoundFile->SetXBassParameters (modplug->megabass_amount,
modplug->megabass_range);
if (modplug->reverb)
modplug->mSoundFile->SetReverbParameters (modplug->reverb_depth,
modplug->reverb_delay);
}
static const GstFormat *
gst_modplug_get_formats (GstPad * pad)
{
static const GstFormat src_formats[] = {
/* GST_FORMAT_BYTES,
GST_FORMAT_DEFAULT,*/
GST_FORMAT_TIME,
(GstFormat) 0
};
static const GstFormat sink_formats[] = {
/*GST_FORMAT_BYTES, */
GST_FORMAT_TIME,
(GstFormat) 0
};
return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
}
static const GstQueryType *
gst_modplug_get_query_types (GstPad * pad)
{
static const GstQueryType gst_modplug_src_query_types[] = {
GST_QUERY_TOTAL,
GST_QUERY_POSITION,
(GstQueryType) 0
};
return gst_modplug_src_query_types;
}
static gboolean
gst_modplug_src_query (GstPad * pad, GstQueryType type,
GstFormat * format, gint64 * value)
{
gboolean res = TRUE;
GstModPlug *modplug;
gfloat tmp;
modplug = GST_MODPLUG (gst_pad_get_parent (pad));
switch (type) {
case GST_QUERY_TOTAL:
switch (*format) {
case GST_FORMAT_TIME:
*value = (gint64) modplug->mSoundFile->GetSongTime () * GST_SECOND;
break;
default:
res = FALSE;
break;
}
break;
case GST_QUERY_POSITION:
switch (*format) {
ext/flac/gstflacdec.c: Only return true if we actually filled something in. Prevents player applications from showing... Original commit message from CVS: * ext/flac/gstflacdec.c: (gst_flacdec_src_query): Only return true if we actually filled something in. Prevents player applications from showing a random length for flac files. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_class_init), (gst_riff_read_use_event), (gst_riff_read_handle_event), (gst_riff_read_seek), (gst_riff_read_skip), (gst_riff_read_strh), (gst_riff_read_strf_vids_with_data), (gst_riff_read_strf_auds_with_data), (gst_riff_read_strf_iavs): OK, ok, so I implemented event handling. Apparently it's normal that we receive random events at random points without asking for it. * gst/avi/gstavidemux.c: (gst_avi_demux_reset), (gst_avi_demux_src_convert), (gst_avi_demux_handle_src_query), (gst_avi_demux_handle_src_event), (gst_avi_demux_stream_index), (gst_avi_demux_sync), (gst_avi_demux_stream_scan), (gst_avi_demux_massage_index), (gst_avi_demux_stream_header), (gst_avi_demux_handle_seek), (gst_avi_demux_process_next_entry), (gst_avi_demux_stream_data), (gst_avi_demux_loop): * gst/avi/gstavidemux.h: Implement non-lineair chunk handling and subchunk processing. The first solves playback of AVI files where the audio and video data of individual buffers that we read are not synchronized. This should not happen according to the wonderful AVI specs, but of course it does happen in reality. It is also a prerequisite for the second. Subchunk processing allows us to cut chunks in small pieces and process each of these pieces separately. This is required because I've seen several AVI files with incredibly large audio chunks, even some files with only one audio chunk for the whole file. This allows for proper playback including seeking. This patch is supposed to fix all AVI A/V sync issues. * gst/flx/gstflxdec.c: (gst_flxdec_class_init), (flx_decode_chunks), (flx_decode_color), (gst_flxdec_loop): Work. * gst/modplug/gstmodplug.cc: Proper return value setting for the query() function. * gst/playback/gstplaybasebin.c: (setup_source): Being in non-playing state (after, e.g., EOS) is not necessarily a bad thing. Allow for that. This fixes playback of short files. They don't actually playback fully now, because the clock already runs. This means that small files (<500kB) with a small length (<2sec) will still not or barely play. Other files, such as mod or flx, will work correctly, however.
2004-09-29 09:45:40 +00:00
case GST_FORMAT_TIME:
tmp =
((float) (modplug->mSoundFile->GetSongTime () *
modplug->mSoundFile->GetCurrentPos ()) /
(float) modplug->mSoundFile->GetMaxPosition ());
*value = (gint64) (tmp * GST_SECOND);
break;
ext/flac/gstflacdec.c: Only return true if we actually filled something in. Prevents player applications from showing... Original commit message from CVS: * ext/flac/gstflacdec.c: (gst_flacdec_src_query): Only return true if we actually filled something in. Prevents player applications from showing a random length for flac files. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_class_init), (gst_riff_read_use_event), (gst_riff_read_handle_event), (gst_riff_read_seek), (gst_riff_read_skip), (gst_riff_read_strh), (gst_riff_read_strf_vids_with_data), (gst_riff_read_strf_auds_with_data), (gst_riff_read_strf_iavs): OK, ok, so I implemented event handling. Apparently it's normal that we receive random events at random points without asking for it. * gst/avi/gstavidemux.c: (gst_avi_demux_reset), (gst_avi_demux_src_convert), (gst_avi_demux_handle_src_query), (gst_avi_demux_handle_src_event), (gst_avi_demux_stream_index), (gst_avi_demux_sync), (gst_avi_demux_stream_scan), (gst_avi_demux_massage_index), (gst_avi_demux_stream_header), (gst_avi_demux_handle_seek), (gst_avi_demux_process_next_entry), (gst_avi_demux_stream_data), (gst_avi_demux_loop): * gst/avi/gstavidemux.h: Implement non-lineair chunk handling and subchunk processing. The first solves playback of AVI files where the audio and video data of individual buffers that we read are not synchronized. This should not happen according to the wonderful AVI specs, but of course it does happen in reality. It is also a prerequisite for the second. Subchunk processing allows us to cut chunks in small pieces and process each of these pieces separately. This is required because I've seen several AVI files with incredibly large audio chunks, even some files with only one audio chunk for the whole file. This allows for proper playback including seeking. This patch is supposed to fix all AVI A/V sync issues. * gst/flx/gstflxdec.c: (gst_flxdec_class_init), (flx_decode_chunks), (flx_decode_color), (gst_flxdec_loop): Work. * gst/modplug/gstmodplug.cc: Proper return value setting for the query() function. * gst/playback/gstplaybasebin.c: (setup_source): Being in non-playing state (after, e.g., EOS) is not necessarily a bad thing. Allow for that. This fixes playback of short files. They don't actually playback fully now, because the clock already runs. This means that small files (<500kB) with a small length (<2sec) will still not or barely play. Other files, such as mod or flx, will work correctly, however.
2004-09-29 09:45:40 +00:00
default:
res = FALSE;
break;
}
break;
default:
ext/flac/gstflacdec.c: Only return true if we actually filled something in. Prevents player applications from showing... Original commit message from CVS: * ext/flac/gstflacdec.c: (gst_flacdec_src_query): Only return true if we actually filled something in. Prevents player applications from showing a random length for flac files. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_class_init), (gst_riff_read_use_event), (gst_riff_read_handle_event), (gst_riff_read_seek), (gst_riff_read_skip), (gst_riff_read_strh), (gst_riff_read_strf_vids_with_data), (gst_riff_read_strf_auds_with_data), (gst_riff_read_strf_iavs): OK, ok, so I implemented event handling. Apparently it's normal that we receive random events at random points without asking for it. * gst/avi/gstavidemux.c: (gst_avi_demux_reset), (gst_avi_demux_src_convert), (gst_avi_demux_handle_src_query), (gst_avi_demux_handle_src_event), (gst_avi_demux_stream_index), (gst_avi_demux_sync), (gst_avi_demux_stream_scan), (gst_avi_demux_massage_index), (gst_avi_demux_stream_header), (gst_avi_demux_handle_seek), (gst_avi_demux_process_next_entry), (gst_avi_demux_stream_data), (gst_avi_demux_loop): * gst/avi/gstavidemux.h: Implement non-lineair chunk handling and subchunk processing. The first solves playback of AVI files where the audio and video data of individual buffers that we read are not synchronized. This should not happen according to the wonderful AVI specs, but of course it does happen in reality. It is also a prerequisite for the second. Subchunk processing allows us to cut chunks in small pieces and process each of these pieces separately. This is required because I've seen several AVI files with incredibly large audio chunks, even some files with only one audio chunk for the whole file. This allows for proper playback including seeking. This patch is supposed to fix all AVI A/V sync issues. * gst/flx/gstflxdec.c: (gst_flxdec_class_init), (flx_decode_chunks), (flx_decode_color), (gst_flxdec_loop): Work. * gst/modplug/gstmodplug.cc: Proper return value setting for the query() function. * gst/playback/gstplaybasebin.c: (setup_source): Being in non-playing state (after, e.g., EOS) is not necessarily a bad thing. Allow for that. This fixes playback of short files. They don't actually playback fully now, because the clock already runs. This means that small files (<500kB) with a small length (<2sec) will still not or barely play. Other files, such as mod or flx, will work correctly, however.
2004-09-29 09:45:40 +00:00
res = FALSE;
break;
}
return res;
}
static gboolean
gst_modplug_src_event (GstPad * pad, GstEvent * event)
{
gboolean res = TRUE;
GstModPlug *modplug;
modplug = GST_MODPLUG (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
/* the all-formats seek logic */
case GST_EVENT_SEEK:
{
gboolean flush;
GstFormat format;
format = GST_FORMAT_TIME;
/* shave off the flush flag, we'll need it later */
flush = GST_EVENT_SEEK_FLAGS (event) & GST_SEEK_FLAG_FLUSH;
modplug->seek_at = GST_EVENT_SEEK_OFFSET (event);
break;
}
default:
res = FALSE;
break;
}
gst_event_unref (event);
return res;
}
#if 0
static GstCaps *
gst_modplug_get_streaminfo (GstModPlug * modplug)
{
GstCaps *caps;
props = gst_props_empty_new ();
entry =
gst_props_entry_new ("Patterns",
G_TYPE_INT ((gint) modplug->mSoundFile->GetNumPatterns ()));
gst_props_add_entry (props, (GstPropsEntry *) entry);
caps = gst_caps_new_simple ("application/x-gst-streaminfo", NULL);
return caps;
}
static void
gst_modplug_update_info (GstModPlug * modplug)
{
if (modplug->streaminfo) {
gst_caps_unref (modplug->streaminfo);
}
modplug->streaminfo = gst_modplug_get_streaminfo (modplug);
g_object_notify (G_OBJECT (modplug), "streaminfo");
}
static void
gst_modplug_update_metadata (GstModPlug * modplug)
{
GstProps *props;
GstPropsEntry *entry;
const gchar *title;
props = gst_props_empty_new ();
title = modplug->mSoundFile->GetTitle ();
entry = gst_props_entry_new ("Title", G_TYPE_STRING (title));
gst_props_add_entry (props, entry);
modplug->metadata = gst_caps_new_simple ("application/x-gst-metadata", NULL);
g_object_notify (G_OBJECT (modplug), "metadata");
}
#endif
static GstPadLinkReturn
gst_modplug_srclink (GstPad * pad, const GstCaps * caps)
{
GstModPlug *modplug;
GstStructure *structure;
gint depth;
modplug = GST_MODPLUG (gst_pad_get_parent (pad));
structure = gst_caps_get_structure (caps, 0);
gst_structure_get_int (structure, "depth", &depth);
modplug->_16bit = (depth == 16);
gst_structure_get_int (structure, "channels", &modplug->channel);
gst_structure_get_int (structure, "rate", &modplug->frequency);
modplug->length = 1152 * modplug->channel * depth / 8;
gst_modplug_setup (modplug);
return GST_PAD_LINK_OK;
}
static GstCaps *
gst_modplug_fixate (GstPad * pad, const GstCaps * caps)
{
if (gst_caps_is_simple (caps)) {
GstCaps *copy;
GstStructure *structure;
copy = gst_caps_copy (caps);
structure = gst_caps_get_structure (copy, 0);
if (gst_caps_structure_fixate_field_nearest_int (structure, "rate", 44100))
return copy;
if (gst_caps_structure_fixate_field_nearest_int (structure, "channels", 2))
return copy;
gst_caps_free (copy);
}
return NULL;
}
static void
gst_modplug_handle_event (GstModPlug * modplug)
{
gint64 value;
guint32 remaining;
GstEvent *event;
gst_bytestream_get_status (modplug->bs, &remaining, &event);
if (!event) {
g_warning ("modplug: no bytestream event");
return;
}
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
gst_event_unref (event);
break;
case GST_EVENT_DISCONTINUOUS:
if (gst_event_discont_get_value (event, GST_FORMAT_BYTES, &value)) {
if (remaining == value) {
gst_event_unref (event);
break;
}
}
gst_bytestream_flush_fast (modplug->bs, remaining);
default:
gst_pad_event_default (modplug->sinkpad, event);
break;
}
}
static void
gst_modplug_loop (GstElement * element)
{
GstModPlug *modplug;
GstEvent *event;
g_return_if_fail (element != NULL);
g_return_if_fail (GST_IS_MODPLUG (element));
modplug = GST_MODPLUG (element);
if (modplug->state == MODPLUG_STATE_NEED_TUNE) {
/* GstBuffer *buf;*/
modplug->seek_at = -1;
modplug->need_discont = TRUE;
modplug->eos = FALSE;
/*
buf = gst_pad_pull (modplug->sinkpad);
g_assert (buf != NULL);
if (GST_IS_EVENT (buf)) {
GstEvent *event = GST_EVENT (buf);
switch (GST_EVENT_TYPE (buf)) {
case GST_EVENT_EOS:
modplug->state = MODPLUG_STATE_LOAD_TUNE;
break;
case GST_EVENT_DISCONTINUOUS:
break;
default:
bail out, we're not going to do anything
gst_event_unref (event);
gst_pad_send_event (modplug->srcpad, gst_event_new (GST_EVENT_EOS));
gst_element_set_eos (element);
return;
}
gst_event_unref (event);
}
else {
memcpy (modplug->buffer_in + modplug->song_size, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
modplug->song_size += GST_BUFFER_SIZE (buf);
gst_buffer_unref (buf);
}
*/
if (modplug->bs) {
guint64 got;
modplug->song_size = gst_bytestream_length (modplug->bs);
got =
gst_bytestream_peek_bytes (modplug->bs, &modplug->buffer_in,
modplug->song_size);
if (got < modplug->song_size) {
gst_modplug_handle_event (modplug);
return;
}
modplug->state = MODPLUG_STATE_LOAD_TUNE;
}
}
if (modplug->state == MODPLUG_STATE_LOAD_TUNE) {
modplug->mSoundFile = new CSoundFile;
if (!GST_PAD_CAPS (modplug->srcpad) &&
GST_PAD_LINK_FAILED (gst_pad_renegotiate (modplug->srcpad))) {
GST_ELEMENT_ERROR (modplug, CORE, NEGOTIATION, (NULL), (NULL));
return;
}
modplug->mSoundFile->Create (modplug->buffer_in, modplug->song_size);
modplug->opened = TRUE;
gst_bytestream_flush (modplug->bs, modplug->song_size);
modplug->buffer_in = NULL;
modplug->audiobuffer = (guchar *) g_malloc (modplug->length);
//gst_modplug_update_metadata (modplug);
//gst_modplug_update_info (modplug);
modplug->state = MODPLUG_STATE_PLAY_TUNE;
}
if (modplug->state == MODPLUG_STATE_PLAY_TUNE && !modplug->eos) {
if (modplug->seek_at != -1) {
gint seek_to_pos;
gint64 total;
gfloat temp;
total = modplug->mSoundFile->GetSongTime () * GST_SECOND;
temp = (gfloat) total / modplug->seek_at;
seek_to_pos = (int) (modplug->mSoundFile->GetMaxPosition () / temp);
modplug->mSoundFile->SetCurrentPos (seek_to_pos);
modplug->need_discont = TRUE;
modplug->seek_at = -1;
}
if (modplug->need_discont && GST_PAD_IS_USABLE (modplug->srcpad)) {
GstEvent *discont;
gint64 value;
GstFormat format = GST_FORMAT_TIME;
if (gst_modplug_src_query (modplug->srcpad, GST_QUERY_POSITION, &format,
&value)) {
discont =
gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, value,
GST_FORMAT_UNDEFINED);
modplug->timestamp = value;
} else {
modplug->timestamp = GST_CLOCK_TIME_NONE;
discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_UNDEFINED);
}
gst_pad_push (modplug->srcpad, GST_DATA (discont));
modplug->need_discont = FALSE;
}
if (modplug->mSoundFile->Read (modplug->audiobuffer, modplug->length) != 0) {
GstBuffer *buffer_out;
buffer_out = gst_buffer_new ();
GST_BUFFER_DATA (buffer_out) =
(guchar *) g_memdup (modplug->audiobuffer, modplug->length);
GST_BUFFER_SIZE (buffer_out) = modplug->length;
GST_BUFFER_TIMESTAMP (buffer_out) = modplug->timestamp;
if (GST_CLOCK_TIME_IS_VALID (modplug->timestamp)) {
GST_BUFFER_DURATION (buffer_out) =
modplug->length * GST_SECOND / modplug->frequency /
modplug->channel / (modplug->_16bit ? 2 : 1);
modplug->timestamp += GST_BUFFER_DURATION (buffer_out);
}
if (GST_PAD_IS_USABLE (modplug->srcpad))
gst_pad_push (modplug->srcpad, GST_DATA (buffer_out));
} else if (GST_PAD_IS_LINKED (modplug->srcpad)) {
/* FIXME, hack, pull final EOS from peer */
gst_bytestream_flush (modplug->bs, 1);
event = gst_event_new (GST_EVENT_EOS);
gst_pad_push (modplug->srcpad, GST_DATA (event));
gst_element_set_eos (element);
modplug->eos = TRUE;
}
}
}
static GstStateChangeReturn
gst_modplug_change_state (GstElement * element, GstStateChange transition)
{
GstModPlug *modplug;
modplug = GST_MODPLUG (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
modplug->bs = gst_bytestream_new (modplug->sinkpad);
modplug->song_size = 0;
modplug->state = MODPLUG_STATE_NEED_TUNE;
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_bytestream_destroy (modplug->bs);
modplug->bs = NULL;
if (modplug->opened) {
modplug->mSoundFile->Destroy ();
modplug->opened = FALSE;
}
if (modplug->audiobuffer)
g_free (modplug->audiobuffer);
modplug->buffer_in = NULL;
modplug->audiobuffer = NULL;
modplug->state = MODPLUG_STATE_NEED_TUNE;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
default:
break;
}
if (GST_ELEMENT_CLASS (parent_class)->change_state)
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
return GST_STATE_CHANGE_SUCCESS;
}
static void
gst_modplug_set_property (GObject * object, guint id, const GValue * value,
GParamSpec * pspec)
{
GstModPlug *modplug;
/* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_MODPLUG (object));
modplug = GST_MODPLUG (object);
switch (id) {
case ARG_REVERB:
modplug->reverb = g_value_get_boolean (value);
break;
case ARG_REVERB_DEPTH:
modplug->reverb_depth = g_value_get_int (value);
break;
case ARG_REVERB_DELAY:
modplug->reverb_delay = g_value_get_int (value);
break;
case ARG_MEGABASS:
modplug->megabass = g_value_get_boolean (value);
break;
case ARG_MEGABASS_AMOUNT:
modplug->megabass_amount = g_value_get_int (value);
break;
case ARG_MEGABASS_RANGE:
modplug->megabass_range = g_value_get_int (value);
break;
case ARG_NOISE_REDUCTION:
modplug->noise_reduction = g_value_get_boolean (value);
break;
case ARG_SURROUND:
modplug->surround = g_value_get_boolean (value);
break;
case ARG_SURROUND_DEPTH:
modplug->surround_depth = g_value_get_int (value);
break;
case ARG_SURROUND_DELAY:
modplug->surround_delay = g_value_get_int (value);
break;
default:
break;
}
}
static void
gst_modplug_get_property (GObject * object, guint id, GValue * value,
GParamSpec * pspec)
{
GstModPlug *modplug;
/* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_MODPLUG (object));
modplug = GST_MODPLUG (object);
switch (id) {
case ARG_REVERB:
g_value_set_boolean (value, modplug->reverb);
break;
case ARG_REVERB_DEPTH:
g_value_set_int (value, modplug->reverb_depth);
break;
case ARG_REVERB_DELAY:
g_value_set_int (value, modplug->reverb_delay);
break;
case ARG_MEGABASS:
g_value_set_boolean (value, modplug->megabass);
break;
case ARG_MEGABASS_AMOUNT:
g_value_set_int (value, modplug->megabass_amount);
break;
case ARG_MEGABASS_RANGE:
g_value_set_int (value, modplug->megabass_range);
break;
case ARG_SURROUND:
g_value_set_boolean (value, modplug->surround);
break;
case ARG_SURROUND_DEPTH:
g_value_set_int (value, modplug->surround_depth);
break;
case ARG_SURROUND_DELAY:
g_value_set_int (value, modplug->surround_delay);
break;
case ARG_NOISE_REDUCTION:
g_value_set_boolean (value, modplug->noise_reduction);
break;
default:
break;
}
}
static gboolean
plugin_init (GstPlugin * plugin)
{
/* this filter needs the bytestream package */
if (!gst_library_load ("gstbytestream"))
return FALSE;
return gst_element_register (plugin, "modplug",
GST_RANK_PRIMARY, GST_TYPE_MODPLUG);
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
"modplug",
".MOD audio decoding",
plugin_init, VERSION, "LGPL", GST_PACKAGE, GST_ORIGIN)