#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_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);
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)
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;
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)
{
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",
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 void
-speed_loop (GstElement * element)
+static inline guint
+speed_chain_int16 (GstSpeed * filter, GstBuffer * in_buf, GstBuffer * out_buf,
+ guint c, guint in_samples)
{
- GstSpeed *filter = GST_SPEED (element);
- GstBuffer *in, *out;
- guint i, j, nin, nout;
- gfloat interp, speed, lower, i_float;
+ gint16 *in_data, *out_data;
+ gfloat interp, lower, i_float;
+ guint i, j;
- g_return_if_fail (filter != NULL);
- g_return_if_fail (GST_IS_SPEED (filter));
+ in_data = ((gint16 *) GST_BUFFER_DATA (in_buf)) + c;
+ out_data = ((gint16 *) GST_BUFFER_DATA (out_buf)) + c;
- i = j = 0;
- speed = filter->speed;
+ lower = in_data[0];
+ i_float = 0.5 * (filter->speed - 1.0);
+ i = (guint) ceil (i_float);
+ j = 0;
- in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
+ while (i < in_samples) {
+ interp = i_float - floor (i_float);
- if (GST_IS_EVENT (in)) {
- gst_pad_event_default (filter->sinkpad, GST_EVENT (in));
- return;
+ 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;
}
- while (GST_IS_EVENT (in)) {
- gst_pad_event_default (filter->srcpad, GST_EVENT (in));
- in = GST_BUFFER (gst_pad_pull (filter->sinkpad));
+ 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;
}
- /* 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);
+ return j;
+}
+
+static void
+speed_chain (GstPad * pad, GstData * data)
+{
+ GstBuffer *in_buf, *out_buf;
+ GstSpeed *filter;
+ guint c, in_samples, out_samples, out_size;
+
+ 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));
+
+ if (GST_IS_EVENT (data)) {
+ switch (GST_EVENT_TYPE (GST_EVENT (data))) {
+ case GST_EVENT_DISCONTINUOUS:
+ {
+ gint64 timestamp, offset;
+
+ if (gst_event_discont_get_value (GST_EVENT (data), GST_FORMAT_BYTES,
+ ×tamp)
+ && 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;
}
+
+ 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);
+ }
+ }
+
+ 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:
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:
}
}
+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)
{