and make it handle more than one channel.

Original commit message from CVS:
Fix speed element and make it chain-based (fixes #156467), and make it handle more than one channel.
This commit is contained in:
Tim-Philipp Müller 2005-02-10 15:06:13 +00:00
parent d7af68b7c8
commit 909da9fb04
6 changed files with 228 additions and 156 deletions

View file

@ -1,3 +1,16 @@
2005-02-10 Tim-Philipp Müller <tim at centricular dot net>
* gst/speed/Makefile.am:
* gst/speed/demo-mp3.c: (main):
* gst/speed/filter.func:
* gst/speed/gstspeed.c: (speed_link), (speed_parse_caps),
(speed_class_init), (speed_init), (speed_chain_int16),
(speed_chain_float32), (speed_chain), (speed_set_property),
(speed_get_property), (speed_change_state):
* gst/speed/gstspeed.h:
Fix speed element and make it chain-based (fixes #156467),
and make it handle more than one channel.
2005-02-10 Jan Schmidt <thaytan@mad.scientist.com>
* ext/dts/gstdtsdec.c: (gst_dtsdec_init), (gst_dtsdec_channels),

View file

@ -6,7 +6,7 @@ libgstspeed_la_CFLAGS = $(GST_CFLAGS)
libgstspeed_la_LIBADD =
libgstspeed_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
noinst_HEADERS = gstspeed.h filter.func
noinst_HEADERS = gstspeed.h
if HAVE_GTK
noinst_PROGRAMS = demo-mp3

View file

@ -37,7 +37,7 @@ int
main (int argc, char **argv)
{
GtkWidget *window, *vbox, *hscale, *button;
GstElement *filesrc, *mad, *stereo2mono, *speed, *audiosink, *pipeline;
GstElement *filesrc, *mad, *audioconvert, *speed, *audiosink, *pipeline;
gst_init (&argc, &argv);
gtk_init (&argc, &argv);
@ -65,18 +65,17 @@ main (int argc, char **argv)
filesrc = gst_element_factory_make ("filesrc", "filesrc");
mad = gst_element_factory_make ("mad", "mad");
stereo2mono = gst_element_factory_make ("stereo2mono", "stereo2mono");
audioconvert = gst_element_factory_make ("audioconvert", "audioconvert0");
speed = gst_element_factory_make ("speed", "speed");
audiosink = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
g_object_set (audiosink, "fragment", 0x00180008, NULL);
gtk_signal_connect (GTK_OBJECT (gtk_range_get_adjustment (GTK_RANGE
(hscale))), "value_changed", G_CALLBACK (set_speed), speed);
pipeline = gst_pipeline_new ("app");
gst_bin_add_many (GST_BIN (pipeline), filesrc, mad, stereo2mono, speed,
gst_bin_add_many (GST_BIN (pipeline), filesrc, mad, audioconvert, speed,
audiosink, NULL);
gst_element_link_many (filesrc, mad, stereo2mono, speed, audiosink, NULL);
gst_element_link_many (filesrc, mad, audioconvert, speed, audiosink, NULL);
g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL);
gst_element_set_state (pipeline, GST_STATE_PLAYING);

View file

@ -1,60 +0,0 @@
/* -*- Mode: c; c-basic-offset: 2 -*- */
_FORMAT *in_data, *out_data;
static gint64 offset = 0, timestamp = 0;
/* get a buffer here so that we can have something to interpolate
* against for the first few samples if speed < 0.5 */
in_data = (_FORMAT*) GST_BUFFER_DATA(in);
nin = GST_BUFFER_SIZE(in)/sizeof(_FORMAT);
lower = in_data[0];
i_float = 0.5 * (speed - 1.0);
i = i_float + 1.0; /* ciel(i_float) for ints */
do {
speed = filter->speed; /* update this, it might have changed */
out = gst_buffer_new();
GST_BUFFER_DATA(out) = (gchar*) g_new(_FORMAT,SPEED_BUFSIZE/sizeof(_FORMAT));
GST_BUFFER_SIZE(out) = SPEED_BUFSIZE;
out_data = (_FORMAT*) GST_BUFFER_DATA(out);
nout = GST_BUFFER_SIZE(out) / sizeof(_FORMAT);
GST_BUFFER_TIMESTAMP (out) = timestamp;
offset += nout;
timestamp = offset * GST_SECOND / filter->rate;
for (j=0; j<nout; j++) {
/* index of upper bounds of interpolation for
* new sample, got it by trial&error on the chalkboard */
i_float += speed;
i = i_float + 1.0; /* ciel(i_float) for ints */
while (i >= nin) {
i = i % nin;
i_float = i_float - nin;
lower = in_data[nin-1];
gst_buffer_unref(in);
in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
while (GST_IS_EVENT (in)) {
gst_pad_event_default (filter->srcpad, GST_EVENT (in));
in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
}
in_data = (_FORMAT*) GST_BUFFER_DATA(in);
nin = GST_BUFFER_SIZE(in) / sizeof(_FORMAT);
}
if (i>0)
lower = in_data[i-1];
interp = i_float - floor(i_float);
out_data[j] = lower*(1-interp) + in_data[i]*interp;
lower = in_data[i];
}
gst_pad_push(filter->srcpad, GST_DATA (out));
gst_element_yield (element);
} while (TRUE);

View file

@ -29,46 +29,49 @@
#include "gstspeed.h"
/* buffer size to make if no bufferpool is available, must be divisible by
* sizeof(gfloat) */
#define SPEED_BUFSIZE 4096
/* number of buffers to allocate per chunk in sink buffer pool */
#define SPEED_NUMBUF 6
/* elementfactory information */
static GstElementDetails speed_details = GST_ELEMENT_DETAILS ("Speed",
"Filter/Effect/Audio",
"Set speed/pitch on audio/raw streams (resampler)",
"Andy Wingo <apwingo@eos.ncsu.edu>");
"Andy Wingo <apwingo@eos.ncsu.edu>, "
"Tim-Philipp Müller <tim@centricular.net>");
/* Filter signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_0,
ARG_SPEED
};
/* assumption here: sizeof (gfloat) = 4 */
#define GST_SPEED_AUDIO_CAPS \
"audio/x-raw-float, " \
"rate = (int) [ 1, MAX ], " \
"channels = (int) [ 1, MAX ], " \
"endianness = (int) BYTE_ORDER, " \
"width = (int) 32, " \
"buffer-frames = (int) 0; " \
\
"audio/x-raw-int, " \
"rate = (int) [ 1, MAX ], " \
"channels = (int) [ 1, MAX ], " \
"endianness = (int) BYTE_ORDER, " \
"width = (int) 16, " \
"depth = (int) 16, " \
"signed = (boolean) true"
static GstStaticPadTemplate gst_speed_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
GST_STATIC_CAPS (GST_SPEED_AUDIO_CAPS)
);
static GstStaticPadTemplate gst_speed_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_AUDIO_INT_PAD_TEMPLATE_CAPS "; "
GST_AUDIO_FLOAT_STANDARD_PAD_TEMPLATE_CAPS)
GST_STATIC_CAPS (GST_SPEED_AUDIO_CAPS)
);
static void speed_base_init (gpointer g_class);
@ -82,11 +85,11 @@ static void speed_get_property (GObject * object, guint prop_id, GValue * value,
static gboolean speed_parse_caps (GstSpeed * filter, const GstCaps * caps);
static void speed_loop (GstElement * element);
static void speed_chain (GstPad * pad, GstData * data);
static GstElementClass *parent_class = NULL;
static GstElementStateReturn speed_change_state (GstElement * element);
/*static guint gst_filter_signals[LAST_SIGNAL] = { 0 }; */
static GstElementClass *parent_class; /* NULL */
static GstPadLinkReturn
speed_link (GstPad * pad, const GstCaps * caps)
@ -97,7 +100,7 @@ speed_link (GstPad * pad, const GstCaps * caps)
filter = GST_SPEED (gst_pad_get_parent (pad));
g_return_val_if_fail (filter != NULL, GST_PAD_LINK_REFUSED);
g_return_val_if_fail (GST_IS_SPEED (filter), GST_PAD_LINK_REFUSED);
otherpad = (pad == filter->srcpad ? filter->sinkpad : filter->srcpad);
otherpad = (pad == filter->srcpad) ? filter->sinkpad : filter->srcpad;
if (!speed_parse_caps (filter, caps))
return GST_PAD_LINK_REFUSED;
@ -117,29 +120,31 @@ speed_parse_caps (GstSpeed * filter, const GstCaps * caps)
structure = gst_caps_get_structure (caps, 0);
mimetype = gst_structure_get_name (structure);
if (strcmp (mimetype, "audio/x-raw-float") == 0)
filter->format = GST_SPEED_FORMAT_FLOAT;
else if (strcmp (mimetype, "audio/x-raw-int") == 0)
filter->format = GST_SPEED_FORMAT_INT;
else
return FALSE;
ret = gst_structure_get_int (structure, "rate", &filter->rate);
ret &= gst_structure_get_int (structure, "channels", &filter->channels);
ret &= gst_structure_get_int (structure, "width", &filter->width);
ret &= gst_structure_get_int (structure, "endianness", &filter->endianness);
filter->buffer_frames = 0;
gst_structure_get_int (structure, "buffer-frames", &filter->buffer_frames);
mimetype = gst_structure_get_name (structure);
if (strcmp (mimetype, "audio/x-raw-int") == 0) {
filter->format = GST_SPEED_FORMAT_INT;
ret &= gst_structure_get_int (structure, "depth", &filter->depth);
ret &= gst_structure_get_boolean (structure, "signed", &filter->is_signed);
} else if (strcmp (mimetype, "audio/x-raw-float") == 0) {
filter->format = GST_SPEED_FORMAT_FLOAT;
if (filter->format == GST_SPEED_FORMAT_FLOAT) {
filter->sample_size = filter->channels * filter->width / 8;
} else {
return FALSE;
/* our caps only allow width == depth for now */
filter->sample_size = filter->channels * filter->width / 8;
}
return ret;
}
GType
gst_speed_get_type (void)
{
@ -180,11 +185,13 @@ static void
speed_class_init (GstSpeedClass * klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GstElementClass *gstelement_class = (GstElementClass *) klass;
gobject_class->set_property = speed_set_property;
gobject_class->get_property = speed_get_property;
gstelement_class->change_state = speed_change_state;
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
parent_class = g_type_class_peek_parent (klass);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SPEED,
g_param_spec_float ("speed", "speed", "speed",
@ -198,73 +205,165 @@ speed_init (GstSpeed * filter)
gst_pad_new_from_template (gst_static_pad_template_get
(&gst_speed_sink_template), "sink");
gst_pad_set_link_function (filter->sinkpad, speed_link);
gst_pad_set_chain_function (filter->sinkpad, speed_chain);
gst_pad_set_getcaps_function (filter->sinkpad, gst_pad_proxy_getcaps);
gst_element_add_pad (GST_ELEMENT (filter), filter->sinkpad);
filter->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get
(&gst_speed_src_template), "src");
gst_pad_set_link_function (filter->srcpad, speed_link);
gst_pad_set_getcaps_function (filter->srcpad, gst_pad_proxy_getcaps);
gst_element_add_pad (GST_ELEMENT (filter), filter->srcpad);
gst_element_set_loop_function (GST_ELEMENT (filter), speed_loop);
filter->offset = 0;
filter->timestamp = 0;
filter->sample_size = 0;
}
static inline guint
speed_chain_int16 (GstSpeed * filter, GstBuffer * in_buf, GstBuffer * out_buf,
guint c, guint in_samples)
{
gint16 *in_data, *out_data;
gfloat interp, lower, i_float;
guint i, j;
in_data = ((gint16 *) GST_BUFFER_DATA (in_buf)) + c;
out_data = ((gint16 *) GST_BUFFER_DATA (out_buf)) + c;
lower = in_data[0];
i_float = 0.5 * (filter->speed - 1.0);
i = (guint) ceil (i_float);
j = 0;
while (i < in_samples) {
interp = i_float - floor (i_float);
out_data[j * filter->channels] =
lower * (1 - interp) + in_data[i * filter->channels] * interp;
lower = in_data[i * filter->channels];
i_float += filter->speed;
i = (guint) ceil (i_float);
++j;
}
return j;
}
static inline guint
speed_chain_float32 (GstSpeed * filter, GstBuffer * in_buf, GstBuffer * out_buf,
guint c, guint in_samples)
{
gfloat *in_data, *out_data;
gfloat interp, lower, i_float;
guint i, j;
in_data = ((gfloat *) GST_BUFFER_DATA (in_buf)) + c;
out_data = ((gfloat *) GST_BUFFER_DATA (out_buf)) + c;
lower = in_data[0];
i_float = 0.5 * (filter->speed - 1.0);
i = (guint) ceil (i_float);
j = 0;
while (i < in_samples) {
interp = i_float - floor (i_float);
out_data[j * filter->channels] =
lower * (1 - interp) + in_data[i * filter->channels] * interp;
lower = in_data[i * filter->channels];
i_float += filter->speed;
i = (guint) ceil (i_float);
++j;
}
return j;
}
static void
speed_loop (GstElement * element)
speed_chain (GstPad * pad, GstData * data)
{
GstSpeed *filter = GST_SPEED (element);
GstBuffer *in, *out;
guint i, j, nin, nout;
gfloat interp, speed, lower, i_float;
GstBuffer *in_buf, *out_buf;
GstSpeed *filter;
guint c, in_samples, out_samples, out_size;
g_return_if_fail (filter != NULL);
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (data != NULL);
filter = GST_SPEED (GST_OBJECT_PARENT (pad));
g_return_if_fail (GST_IS_SPEED (filter));
i = j = 0;
speed = filter->speed;
if (GST_IS_EVENT (data)) {
switch (GST_EVENT_TYPE (GST_EVENT (data))) {
case GST_EVENT_DISCONTINUOUS:
{
gint64 timestamp, offset;
in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
if (GST_IS_EVENT (in)) {
gst_pad_event_default (filter->sinkpad, GST_EVENT (in));
if (gst_event_discont_get_value (GST_EVENT (data), GST_FORMAT_BYTES,
&timestamp)
&& gst_event_discont_get_value (GST_EVENT (data), GST_FORMAT_BYTES,
&offset)) {
filter->offset = offset;
filter->timestamp = timestamp;
}
break;
}
default:
break;
}
gst_pad_event_default (pad, GST_EVENT (data));
return;
}
while (GST_IS_EVENT (in)) {
gst_pad_event_default (filter->srcpad, GST_EVENT (in));
in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
in_buf = GST_BUFFER (data);
out_size = ceil ((gfloat) GST_BUFFER_SIZE (in_buf) / filter->speed);
out_buf = gst_pad_alloc_buffer (filter->srcpad, -1, out_size);
in_samples = GST_BUFFER_SIZE (in_buf) / filter->sample_size;
out_samples = 0;
for (c = 0; c < filter->channels; ++c) {
if (filter->format == GST_SPEED_FORMAT_INT) {
out_samples = speed_chain_int16 (filter, in_buf, out_buf, c, in_samples);
} else {
out_samples =
speed_chain_float32 (filter, in_buf, out_buf, c, in_samples);
}
}
/* this is a bit nasty, but hey, it's what you've got to do to keep the same
* algorithm and multiple data types in c. */
if (filter->format == GST_SPEED_FORMAT_FLOAT) {
#define _FORMAT gfloat
#include "filter.func"
#undef _FORMAT
} else if (filter->format == GST_SPEED_FORMAT_INT && filter->width == 16) {
#define _FORMAT gint16
#include "filter.func"
#undef _FORMAT
} else if (filter->format == GST_SPEED_FORMAT_INT && filter->width == 8) {
#define _FORMAT gint8
#include "filter.func"
#undef _FORMAT
} else {
GST_ELEMENT_ERROR (filter, CORE, NEGOTIATION, (NULL),
("format wasn't negotiated before chain function"));
gst_element_yield (element);
}
GST_BUFFER_SIZE (out_buf) = out_samples * filter->sample_size;
GST_BUFFER_OFFSET (out_buf) = filter->offset;
GST_BUFFER_TIMESTAMP (out_buf) = filter->timestamp;
filter->offset += GST_BUFFER_SIZE (out_buf) / filter->sample_size;
filter->timestamp = filter->offset * GST_SECOND / filter->rate;
GST_BUFFER_DURATION (out_buf) =
filter->timestamp - GST_BUFFER_TIMESTAMP (out_buf);
gst_pad_push (filter->srcpad, GST_DATA (out_buf));
gst_buffer_unref (in_buf);
}
static void
speed_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
GstSpeed *filter;
GstSpeed *filter = (GstSpeed *) object;
/* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_SPEED (object));
filter = GST_SPEED (object);
switch (prop_id) {
case ARG_SPEED:
@ -279,11 +378,9 @@ static void
speed_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstSpeed *filter;
GstSpeed *filter = (GstSpeed *) object;
/* it's not null if we got it, but it might not be ours */
g_return_if_fail (GST_IS_SPEED (object));
filter = GST_SPEED (object);
switch (prop_id) {
case ARG_SPEED:
@ -295,6 +392,29 @@ speed_get_property (GObject * object, guint prop_id, GValue * value,
}
}
static GstElementStateReturn
speed_change_state (GstElement * element)
{
GstSpeed *speed = GST_SPEED (element);
switch (GST_STATE_TRANSITION (element)) {
case GST_STATE_PAUSED_TO_READY:
break;
case GST_STATE_READY_TO_PAUSED:
speed->offset = 0;
speed->timestamp = 0;
speed->sample_size = 0;
break;
default:
break;
}
if (parent_class->change_state)
return parent_class->change_state (element);
return GST_STATE_SUCCESS;
}
static gboolean
plugin_init (GstPlugin * plugin)
{

View file

@ -47,23 +47,23 @@ enum _GstSpeedFormat {
};
struct _GstSpeed {
GstElement element;
GstElement element;
GstPad *sinkpad, *srcpad;
GstPad *sinkpad;
GstPad *srcpad;
gfloat speed;
gfloat speed;
/* valid for both int and float */
gint64 offset;
gint64 timestamp;
guint rate;
guint channels;
guint width;
guint buffer_frames;
guint sample_size;
GstSpeedFormat format;
guint rate;
guint channels;
guint width;
guint endianness;
guint buffer_frames;
/* valid only for format==GST_SPEED_FORMAT_INT */
guint depth;
gboolean is_signed;
};
struct _GstSpeedClass {