A fairly large reworking of the ladspa _get, _chain and _loop funcs. highlights are:

Original commit message from CVS:
A fairly large reworking of the ladspa _get, _chain and _loop funcs.
highlights are:
- pad names are now g_strcanonised
- in all cases possible, src and sink buffers are shared
- all ladspa properties are exposed as dparams and element properties
- the buffers are now processed in dparam driven loops
- timestamps on buffers should be correct
- misc fixes

It has been minimally tested - the following pipeline works for me:
gst-launch ladspa_sine_fcac Frequency=440.0 Amplitude=1.0 name=fcac1 ladspa_sine_fcac Frequency=450.0 Amplitude=1.0 name=fcac2 ladspa_freeverb3 Wet-Level=0.5 name=free float2int name=f2i fcac1.Output!free.Input--Left- free.Output--Left-!f2i.sink%d fcac2.Output!free.Input--Right- free.Output--Right-!f2i.sink%d f2i.src!sink osssink
This commit is contained in:
Steve Baker 2002-04-20 13:57:00 +00:00
parent 05489e640d
commit 71b95f0145
2 changed files with 413 additions and 253 deletions

View file

@ -20,59 +20,49 @@
#include <string.h>
#include <math.h>
#include <gst/control/control.h>
#include "gstladspa.h"
#include "ladspa.h" /* main ladspa sdk include file */
#include "utils.h" /* ladspa sdk utility functions */
static GstPadTemplate*
ladspa_src_factory (void)
{
return
gst_pad_template_new (
"src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
gst_caps_new (
"ladspa_src",
"audio/raw",
gst_props_new (
"format", GST_PROPS_STRING ("float"),
"layout", GST_PROPS_STRING ("gfloat"),
"intercept", GST_PROPS_FLOAT(0.0),
"slope", GST_PROPS_FLOAT(1.0),
"channels", GST_PROPS_INT (1),
"rate", GST_PROPS_INT_RANGE (0,G_MAXINT),
NULL)),
NULL);
}
GST_PAD_TEMPLATE_FACTORY (ladspa_sink_factory,
"sink",
GST_PAD_SINK,
GST_PAD_REQUEST,
GST_CAPS_NEW (
"ladspa_sink",
"audio/raw",
"rate", GST_PROPS_INT_RANGE (4000, 96000),
"format", GST_PROPS_STRING ("float"),
"layout", GST_PROPS_STRING ("gfloat"),
"intercept", GST_PROPS_FLOAT(0.0),
"slope", GST_PROPS_FLOAT(1.0),
"channels", GST_PROPS_INT (1)
)
);
static GstPadTemplate*
ladspa_sink_factory (void)
{
return
gst_pad_template_new (
"sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
gst_caps_new (
"float2int_sink",
"audio/raw",
gst_props_new (
"format", GST_PROPS_STRING ("float"),
"layout", GST_PROPS_STRING ("gfloat"),
"intercept", GST_PROPS_FLOAT(0.0),
"slope", GST_PROPS_FLOAT(1.0),
"channels", GST_PROPS_INT (1),
"rate", GST_PROPS_INT_RANGE (0,G_MAXINT),
NULL)),
NULL);
}
GST_PAD_TEMPLATE_FACTORY (ladspa_src_factory,
"src",
GST_PAD_SRC,
GST_PAD_REQUEST,
GST_CAPS_NEW (
"ladspa_src",
"audio/raw",
"rate", GST_PROPS_INT_RANGE (4000, 96000),
"format", GST_PROPS_STRING ("float"),
"layout", GST_PROPS_STRING ("gfloat"),
"intercept", GST_PROPS_FLOAT (0.0),
"slope", GST_PROPS_FLOAT (1.0),
"channels", GST_PROPS_INT (1)
)
);
static GstPadTemplate *srctempl, *sinktempl;
enum {
ARG_0,
ARG_LOOP_BASED,
ARG_SAMPLERATE,
ARG_BUFFERSIZE,
ARG_LAST,
@ -81,6 +71,7 @@ enum {
static void gst_ladspa_class_init (GstLADSPAClass *klass);
static void gst_ladspa_init (GstLADSPA *ladspa);
static void gst_ladspa_update_int(const GValue *value, gpointer data);
static GstPadConnectReturn gst_ladspa_connect (GstPad *pad, GstCaps *caps);
static GstPadConnectReturn gst_ladspa_connect_get (GstPad *pad, GstCaps *caps);
static void gst_ladspa_force_src_caps (GstLADSPA *ladspa, GstPad *pad);
@ -94,11 +85,10 @@ static void gst_ladspa_deactivate (GstLADSPA *ladspa);
static GstElementStateReturn gst_ladspa_change_state (GstElement *element);
static void gst_ladspa_loop (GstElement *element);
static void gst_ladspa_chain_mono (GstPad *pad,GstBuffer *buf);
static void gst_ladspa_chain (GstPad *pad,GstBuffer *buf);
static GstBuffer * gst_ladspa_get (GstPad *pad);
static GstElementClass *parent_class = NULL;
static GstPadTemplate *srctempl, *sinktempl;
/* static guint gst_ladspa_signals[LAST_SIGNAL] = { 0 }; */
static GstPlugin *ladspa_plugin;
@ -114,7 +104,7 @@ gst_ladspa_get_bufferpool (GstPad *pad)
if (oclass->numsrcpads > 0)
for (i=0;i<oclass->numsrcpads;i++)
if ((bp = gst_pad_get_bufferpool(ladspa->srcpads[0])) != NULL)
if ((bp = gst_pad_get_bufferpool(ladspa->srcpads[i])) != NULL)
return bp;
return NULL;
@ -140,10 +130,6 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
gstelement_class->change_state = gst_ladspa_change_state;
g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_LOOP_BASED,
g_param_spec_boolean("loop-based","loop-based","loop-based",
FALSE,G_PARAM_READWRITE));
/* look up and store the ladspa descriptor */
klass->descriptor = g_hash_table_lookup(ladspa_descriptors,GINT_TO_POINTER(G_TYPE_FROM_CLASS(klass)));
desc = klass->descriptor;
@ -242,7 +228,7 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
desc->PortRangeHints[current_portnum].LowerBound;
} else {
if (argtype==G_TYPE_INT) klass->control_info[i].lowerbound = (gfloat)G_MININT;
if (argtype==G_TYPE_FLOAT) klass->control_info[i].lowerbound = G_MINFLOAT;
if (argtype==G_TYPE_FLOAT) klass->control_info[i].lowerbound = -G_MINFLOAT;
}
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hintdesc)) {
@ -292,17 +278,25 @@ gst_ladspa_class_init (GstLADSPAClass *klass)
argname = numargname;
}
g_print("adding arg %s from %s\n",argname, klass->control_info[i].name);
klass->control_info[i].param_name = argname;
GST_DEBUG (0, "adding arg %s from %s",argname, klass->control_info[i].name);
if (argtype==G_TYPE_BOOLEAN){
paramspec = g_param_spec_boolean(argname,argname,argname, FALSE, argperms);
} else if (argtype==G_TYPE_INT){
paramspec = g_param_spec_int(argname,argname,argname,
(gint)klass->control_info[i].lowerbound, (gint)klass->control_info[i].upperbound, 0, argperms);
(gint)klass->control_info[i].lowerbound,
(gint)klass->control_info[i].upperbound,
(gint)klass->control_info[i].lowerbound, argperms);
} else if (klass->control_info[i].samplerate){
paramspec = g_param_spec_float(argname,argname,argname,
0.0, G_MAXFLOAT,
0.0, argperms);
} else {
paramspec = g_param_spec_float(argname,argname,argname,
klass->control_info[i].lowerbound, klass->control_info[i].upperbound,
(klass->control_info[i].lowerbound + klass->control_info[i].upperbound) / 2.0f, argperms);
klass->control_info[i].lowerbound, argperms);
}
g_object_class_install_property(G_OBJECT_CLASS(klass), i+ARG_LAST, paramspec);
@ -313,6 +307,7 @@ static void
gst_ladspa_init (GstLADSPA *ladspa)
{
GstLADSPAClass *oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS(ladspa));
ladspa_control_info cinfo;
LADSPA_Descriptor *desc;
gint i,sinkcount,srccount,controlcount;
@ -323,35 +318,76 @@ gst_ladspa_init (GstLADSPA *ladspa)
/* allocate the various arrays */
ladspa->srcpads = g_new0(GstPad*,oclass->numsrcpads);
ladspa->sinkpads = g_new0(GstPad*,oclass->numsinkpads);
ladspa->bytestreams = g_new0(GstByteStream*,oclass->numsinkpads);
ladspa->controls = g_new(gfloat,oclass->numcontrols);
ladspa->dpman = gst_dpman_new ("ladspa_dpman", GST_ELEMENT(ladspa));
/* walk through the ports and add all the pads */
sinkcount = 0;
srccount = 0;
controlcount = 0;
for (i=0;i<desc->PortCount;i++) {
if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) &&
LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])) {
ladspa->sinkpads[sinkcount] = gst_pad_new_from_template (sinktempl, (gchar *)desc->PortNames[i]);
ladspa->bytestreams[sinkcount] = gst_bytestream_new (ladspa->sinkpads[sinkcount]);
gst_element_add_pad(GST_ELEMENT(ladspa),ladspa->sinkpads[sinkcount]);
sinkcount++;
}
if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i]) &&
LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[i])) {
ladspa->srcpads[srccount] = gst_pad_new_from_template (srctempl, (gchar *)desc->PortNames[i]);
gst_element_add_pad(GST_ELEMENT(ladspa),ladspa->srcpads[srccount]);
srccount++;
if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[i])){
gchar *canon_port_name = g_strdup((gchar *)desc->PortNames[i]);
g_strcanon (canon_port_name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
if (LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])) {
ladspa->sinkpads[sinkcount] = gst_pad_new_from_template (sinktempl, canon_port_name);
gst_element_add_pad(GST_ELEMENT(ladspa),ladspa->sinkpads[sinkcount]);
sinkcount++;
}
if (LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[i])) {
ladspa->srcpads[srccount] = gst_pad_new_from_template (srctempl, canon_port_name);
gst_element_add_pad(GST_ELEMENT(ladspa),ladspa->srcpads[srccount]);
srccount++;
}
}
if (LADSPA_IS_PORT_CONTROL(desc->PortDescriptors[i]) &&
LADSPA_IS_PORT_INPUT(desc->PortDescriptors[i])) {
cinfo = oclass->control_info[controlcount];
/* use the lowerbound as the default value if it exists */
if (oclass->control_info[controlcount].lower){
ladspa->controls[controlcount]=oclass->control_info[controlcount].lowerbound;
if (cinfo.lower){
ladspa->controls[controlcount]=cinfo.lowerbound;
} else {
ladspa->controls[controlcount] = 0.0;
}
/* set up dparams for this instance */
if (cinfo.toggled){
gst_dpman_add_required_dparam_callback (
ladspa->dpman,
g_param_spec_int(cinfo.param_name, cinfo.name, cinfo.name,
0, 1, (gint)(ladspa->controls[controlcount]), G_PARAM_READWRITE),
"int", gst_ladspa_update_int, &(ladspa->controls[controlcount])
);
}
else if (cinfo.integer){
gst_dpman_add_required_dparam_callback (
ladspa->dpman,
g_param_spec_int(cinfo.param_name, cinfo.name, cinfo.name,
(gint)cinfo.lowerbound, (gint)cinfo.upperbound,
(gint)ladspa->controls[controlcount], G_PARAM_READWRITE),
"int", gst_ladspa_update_int, &(ladspa->controls[controlcount])
);
}
else if (cinfo.samplerate){
gst_dpman_add_required_dparam_direct (
ladspa->dpman,
g_param_spec_float(cinfo.param_name, cinfo.name, cinfo.name,
cinfo.lowerbound, cinfo.upperbound,
ladspa->controls[controlcount], G_PARAM_READWRITE),
"hertz-rate-bound", &(ladspa->controls[controlcount])
);
}
else {
gst_dpman_add_required_dparam_direct (
ladspa->dpman,
g_param_spec_float(cinfo.param_name, cinfo.name, cinfo.name,
cinfo.lowerbound, cinfo.upperbound,
ladspa->controls[controlcount], G_PARAM_READWRITE),
"float", &(ladspa->controls[controlcount])
);
}
controlcount++;
}
}
@ -363,43 +399,50 @@ gst_ladspa_init (GstLADSPA *ladspa)
ladspa->activated = FALSE;
ladspa->bufpool = NULL;
/* we assume srccount > 0 because there are no LADSPA sink elements. */
if (sinkcount==0) {
if (sinkcount==0 && srccount == 1) {
/* get mode (no sink pads) */
GST_INFO (0, "get mode with %d src pads\n", srccount);
GST_DEBUG (0, "mono get mode with 1 src pad");
ladspa->newcaps = TRUE;
ladspa->samplerate = 44100;
ladspa->buffersize = 64;
for (i=0;i<srccount;i++) {
gst_pad_set_connect_function (ladspa->srcpads[i], gst_ladspa_connect_get);
gst_pad_set_get_function (ladspa->srcpads[i], gst_ladspa_get);
}
} else if (sinkcount==1 && srccount==1){
/* mono chain */
GST_INFO (0, "inplace mono chain mode\n");
gst_pad_set_connect_function (ladspa->srcpads[0], gst_ladspa_connect_get);
gst_pad_set_get_function (ladspa->srcpads[0], gst_ladspa_get);
} else if (sinkcount==1){
/* with one sink we can use the chain function */
GST_DEBUG (0, "chain mode");
gst_pad_set_connect_function (ladspa->sinkpads[0], gst_ladspa_connect);
gst_pad_set_chain_function (ladspa->sinkpads[0], gst_ladspa_chain_mono);
gst_pad_set_chain_function (ladspa->sinkpads[0], gst_ladspa_chain);
gst_pad_set_bufferpool_function (ladspa->sinkpads[0], gst_ladspa_get_bufferpool);
} else {
/* sinkcount>0 and srccount>0 : N sink pads and M src pads */
GST_INFO (0, "loop mode with %d sink pads and %d src pads\n", sinkcount, srccount);
} else if (sinkcount > 1){
/* more than one sink pad needs loop mode */
GST_DEBUG (0, "loop mode with %d sink pads and %d src pads", sinkcount, srccount);
for (i=0;i<sinkcount;i++) {
gst_pad_set_connect_function (ladspa->sinkpads[i], gst_ladspa_connect);
gst_pad_set_bufferpool_function (ladspa->sinkpads[i], gst_ladspa_get_bufferpool);
}
ladspa->loopbased = TRUE;
gst_element_set_loop_function (GST_ELEMENT (ladspa), gst_ladspa_loop);
}
else if (sinkcount==0 && srccount == 0){
/* for some reason these plugins exist - we'll just ignore them */
} else {
g_warning("%d sink pads, %d src pads not yet supported", sinkcount, srccount);
}
gst_ladspa_instantiate(ladspa);
}
static void
gst_ladspa_update_int(const GValue *value, gpointer data)
{
gfloat *target = (gfloat*) data;
*target = (gfloat)g_value_get_int(value);
}
static GstPadConnectReturn
gst_ladspa_connect (GstPad *pad, GstCaps *caps)
{
@ -411,12 +454,14 @@ gst_ladspa_connect (GstPad *pad, GstCaps *caps)
g_return_val_if_fail (caps != NULL, GST_PAD_CONNECT_DELAYED);
g_return_val_if_fail (pad != NULL, GST_PAD_CONNECT_DELAYED);
gst_caps_get_int (caps, "rate", &rate);
/* have to instantiate ladspa plugin when samplerate changes (groan) */
if (ladspa->samplerate != rate){
ladspa->samplerate = rate;
if (! gst_ladspa_instantiate(ladspa))
return GST_PAD_CONNECT_REFUSED;
if (gst_caps_get_int (caps, "rate", &rate)){
/* have to instantiate ladspa plugin when samplerate changes (groan) */
if (ladspa->samplerate != rate){
ladspa->samplerate = rate;
if (! gst_ladspa_instantiate(ladspa))
return GST_PAD_CONNECT_REFUSED;
}
}
/* if the caps are fixed, we are going to try to set all srcpads using this
@ -429,7 +474,7 @@ gst_ladspa_connect (GstPad *pad, GstCaps *caps)
}
}
return GST_PAD_CONNECT_DELAYED;
return GST_PAD_CONNECT_OK;
}
static GstPadConnectReturn
@ -441,21 +486,21 @@ gst_ladspa_connect_get (GstPad *pad, GstCaps *caps)
g_return_val_if_fail (caps != NULL, GST_PAD_CONNECT_DELAYED);
g_return_val_if_fail (pad != NULL, GST_PAD_CONNECT_DELAYED);
gst_caps_get_int (caps, "rate", &rate);
if (ladspa->samplerate != rate) {
ladspa->samplerate = rate;
if (! gst_ladspa_instantiate(ladspa))
return GST_PAD_CONNECT_REFUSED;
if (gst_caps_get_int (caps, "rate", &rate)){
if (ladspa->samplerate != rate) {
ladspa->samplerate = rate;
if (! gst_ladspa_instantiate(ladspa))
return GST_PAD_CONNECT_REFUSED;
}
}
return GST_PAD_CONNECT_DELAYED;
return GST_PAD_CONNECT_OK;
}
static void
gst_ladspa_force_src_caps(GstLADSPA *ladspa, GstPad *pad)
{
GST_DEBUG (0, "forcing caps");
GST_DEBUG (0, "forcing caps with rate %d", ladspa->samplerate);
gst_pad_try_set_caps (pad, gst_caps_new (
"ladspa_src_caps",
"audio/raw",
@ -495,19 +540,6 @@ gst_ladspa_set_property (GObject *object, guint prop_id, const GValue *value, GP
/* is it a ladspa plugin arg? */
if (cid < 0) return;
/*
if (id == ARG_LOOP_BASED) {
* we can only do this in NULL state *
g_return_if_fail (GST_STATE(object) != GST_STATE_NULL);
ladspa->loopbased = g_value_get_boolean (value);
if (ladspa->loopbased) {
gst_element_set_loop_function (GST_ELEMENT (ladspa), gst_ladspa_loop);
} else {
gst_element_set_loop_function (GST_ELEMENT (ladspa), NULL);
}
}
*/
oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (object));
/* verify it exists and is a control (not a port) */
@ -677,114 +709,174 @@ gst_ladspa_deactivate(GstLADSPA *ladspa)
}
static void
gst_ladspa_loop (GstElement *element)
gst_ladspa_loop(GstElement *element)
{
gint8 *raw_in, *zero_out, i;
GstBuffer **buffers_out;
guint bufferbytesize, i, numsrcpads, numsinkpads, num_empty_pads;
guint num_processed, num_to_process;
GstEvent *event = NULL;
guint32 waiting;
LADSPA_Data **data_in, *data_out;
unsigned long size;
LADSPA_Data **data_in, **data_out;
GstBuffer **buffers_in, **buffers_out;
GstBufferPool *bufpool;
GstByteStream **bytestreams;
gboolean inplace_broken;
GstLADSPA *ladspa = (GstLADSPA *)element;
GstLADSPAClass *oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS (ladspa));
LADSPA_Descriptor *desc = ladspa->descriptor;
data_in = g_new0(LADSPA_Data *, oclass->numsinkpads);
buffers_out = g_new0(GstBuffer *, oclass->numsrcpads);
numsinkpads = oclass->numsinkpads;
numsrcpads = oclass->numsrcpads;
/* set up the bufferpool first if necessary */
i = 0;
while ((ladspa->bufpool == NULL) && (i < oclass->numsrcpads)) {
ladspa->bufpool = gst_pad_get_bufferpool (ladspa->srcpads[i++]);
}
if (ladspa->bufpool == NULL) {
ladspa->bufpool = gst_buffer_pool_get_default (sizeof (LADSPA_Data) * ladspa->buffersize,
ladspa->numbuffers);
data_in = g_new0(LADSPA_Data*, numsinkpads);
data_out = g_new0(LADSPA_Data*, numsrcpads);
buffers_in = g_new0(GstBuffer*, numsinkpads);
buffers_out = g_new0(GstBuffer*, numsrcpads);
bytestreams = g_new0(GstByteStream*, numsinkpads);
bufferbytesize = sizeof (LADSPA_Data) * ladspa->buffersize;
/* find a bufferpool */
bufpool = gst_buffer_pool_get_default (bufferbytesize, ladspa->numbuffers);
/* get the bytestreams for each pad */
for (i=0 ; i<numsinkpads ; i++){
bytestreams[i] = gst_bytestream_new (ladspa->sinkpads[i]);
}
/* since this is a loop element, we just loop here til things fall apart. */
do {
num_empty_pads = 0;
/* first get all the necessary data from the input ports */
for (i=0;i<oclass->numsinkpads;i++){
GST_DEBUG (0, "pulling %d bytes through channel %d'sbytestream\n",
ladspa->buffersize * sizeof (LADSPA_Data), i);
raw_in = gst_bytestream_peek_bytes (ladspa->bytestreams[i], ladspa->buffersize * sizeof (LADSPA_Data));
for (i=0 ; i<numsinkpads ; i++){
GST_DEBUG (0, "pulling %u bytes through channel %d'sbytestream", bufferbytesize, i);
buffers_in[i] = gst_bytestream_read (bytestreams[i], bufferbytesize);
if (raw_in == NULL) {
if (buffers_in[i] == NULL) {
/* we need to check for an event. */
gst_bytestream_get_status (ladspa->bytestreams[i], &waiting, &event);
gst_bytestream_get_status (bytestreams[i], &waiting, &event);
if (event) {
if (GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
/* if we get an EOS event from one of our sink pads, we assume that
pad's finished handling data. delete the bytestream, free up the
pad, and free up the memory associated with the input channel. */
GST_DEBUG (0, "got an EOS event on sinkpad %d", i);
if (event && GST_EVENT_TYPE(event) == GST_EVENT_EOS) {
/* if we get an EOS event from one of our sink pads, we assume that
pad's finished handling data. delete the bytestream, free up the
pad, and free up the memory associated with the input channel. */
GST_DEBUG (0, "got an EOS event on sinkpad %d", i);
}
/* CHECKME should maybe check for other events and try to pull more data here */
num_empty_pads++;
} else {
data_in[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_in[i]);
GST_BUFFER_TIMESTAMP(buffers_in[i]) = ladspa->timestamp;
}
}
if (num_empty_pads > 0){
if (num_empty_pads < numsinkpads){
/* only some pads have EOS, need to create some empty buffers */
for (i=0 ; i<numsinkpads ; i++){
if (buffers_in[i] == NULL){
int x;
LADSPA_Data *data;
buffers_in[i] = gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
GST_BUFFER_TIMESTAMP(buffers_in[i]) = ladspa->timestamp;
data_in[i] = data = (LADSPA_Data *) GST_BUFFER_DATA(buffers_in[i]);
for (x=0 ; x < ladspa->buffersize ; x++)
data[x] = 0.0F;
data_in[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_in[i]);
GST_BUFFER_TIMESTAMP(buffers_in[i]) = ladspa->timestamp;
}
/* we need to create some zeroed out data to feed to this port of the
ladspa filter. */
data_in[i] = g_new0 (LADSPA_Data, ladspa->buffersize * sizeof (LADSPA_Data));
} else {
/* copy the retrieved data and bind the ladspa src port to the data */
data_in[i] = (LADSPA_Data *) raw_in;
}
}
raw_in = NULL;
desc->connect_port (ladspa->handle, oclass->sinkpad_portnums[i], data_in[i]);
gst_bytestream_flush (ladspa->bytestreams[i], ladspa->buffersize * sizeof (LADSPA_Data));
else {
/* all pads have EOS, time to quit */
/* CHECKME do I have to push EOS events here? */
GST_DEBUG (0, "All sink pads have EOS, finishing.");
break;
}
}
/* now set up the output ports */
for (i=0;i<oclass->numsrcpads;i++) {
buffers_out[i] = (GstBuffer *) gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
if (buffers_out[i] == NULL)
GST_ERROR (0, "could not get new output buffer for srcpad %d !\n", i);
data_out = (LADSPA_Data *) GST_BUFFER_DATA (buffers_out[i]);
size = GST_BUFFER_SIZE (buffers_out[i]);
GST_BUFFER_TIMESTAMP (buffers_out[i]) = ladspa->timestamp;
/* initialize the output data to 0 */
zero_out = (gint8 *) GST_BUFFER_DATA (buffers_out[i]);
for (i = 0; i < size; i++)
zero_out[i] = 0;
desc->connect_port (ladspa->handle, oclass->srcpad_portnums[i], data_out);
/* we now have a full set of buffers_in.
* now share or create the buffers_out */
for (i=0 ; i<numsrcpads ; i++){
if (i <= numsinkpads && !inplace_broken){
/* we can share buffers */
buffers_out[i] = buffers_in[i];
data_out[i] = data_in[i];
} else {
buffers_out[i] = gst_buffer_new_from_pool (bufpool, 0, 0);
GST_BUFFER_TIMESTAMP(buffers_out[i]) = ladspa->timestamp;
data_out[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_out[i]);
}
}
/* i'm not sure if this will work ; the number of samples might be
nonconstant across sources and sinks ... */
desc->run (ladspa->handle, ladspa->buffersize);
num_to_process = GST_DPMAN_PREPROCESS(ladspa->dpman, ladspa->buffersize, ladspa->timestamp);
num_processed = 0;
/* now go through and reset the ladspa ports, pushing out the output buffers
at the same time. */
for (i=0;i<oclass->numsinkpads;i++) {
desc->connect_port (ladspa->handle, oclass->sinkpad_portnums[i], NULL);
/* split up processing of the buffer into chunks so that dparams can
* be updated when required.
* In many cases the buffer will be processed in one chunk anyway.
*/
while(GST_DPMAN_PROCESS_CHUNK(ladspa->dpman, num_to_process, num_processed)) {
for (i=0 ; i<numsinkpads ; i++){
desc->connect_port (ladspa->handle, oclass->sinkpad_portnums[i], data_in[i]);
}
for (i=0 ; i<numsrcpads ; i++){
desc->connect_port (ladspa->handle, oclass->srcpad_portnums[i], data_out[i]);
}
desc->run(ladspa->handle, num_to_process);
for (i=0 ; i<numsinkpads ; i++){
data_in[i] += num_to_process;
}
for (i=0 ; i<numsrcpads ; i++){
data_out[i] += num_to_process;
}
num_processed = num_to_process;
num_to_process = 0;
}
for (i=0;i<oclass->numsrcpads;i++) {
for (i=0 ; i<numsrcpads ; i++) {
GST_DEBUG (0, "pushing buffer (%p) on src pad %d", buffers_out[i], i);
gst_pad_push (ladspa->srcpads[i], buffers_out[i]);
data_out[i] = NULL;
buffers_out[i] = NULL;
desc->connect_port (ladspa->handle, oclass->srcpad_portnums[i], NULL);
}
for (i=0 ; i<numsinkpads ; i++) {
if (i > numsrcpads || inplace_broken){
/* we have some buffers to unref */
gst_buffer_unref(buffers_in[i]);
}
data_in[i] = NULL;
buffers_in[i] = NULL;
}
ladspa->timestamp += ladspa->buffersize * ladspa->samplerate * 10^9;
ladspa->timestamp += ladspa->buffersize * 10^9 / ladspa->samplerate;
} while (TRUE);
for (i=0 ; i<numsinkpads ; i++){
gst_bytestream_destroy (bytestreams[i]);
}
g_free (buffers_out);
g_free (buffers_in);
g_free (data_out);
g_free (data_in);
g_free (bytestreams);
}
static void
gst_ladspa_chain_mono (GstPad *pad, GstBuffer *buf)
gst_ladspa_chain (GstPad *pad, GstBuffer *buf)
{
LADSPA_Descriptor *desc;
LADSPA_Data *data;
LADSPA_Data *data_in, **data_out;
GstBuffer **buffers_out;
unsigned long num_samples;
gboolean inplace_broken;
guint num_to_process, num_processed, i, numsrcpads;
GstLADSPA *ladspa;
GstLADSPAClass *oclass;
@ -799,39 +891,96 @@ gst_ladspa_chain_mono (GstPad *pad, GstBuffer *buf)
/* this might happen if caps nego hasn't happened */
g_return_if_fail(ladspa->handle != NULL);
if (! GST_IS_EVENT (buf)) {
oclass = (GstLADSPAClass *) (G_OBJECT_GET_CLASS (ladspa));
data = (LADSPA_Data *) GST_BUFFER_DATA(buf);
num_samples = GST_BUFFER_SIZE(buf) / sizeof(gfloat);
oclass = (GstLADSPAClass *) (G_OBJECT_GET_CLASS (ladspa));
desc = ladspa->descriptor;
/* we know that we're dealing here with a filter that has one sink and one
src pad. */
desc->connect_port(ladspa->handle,oclass->sinkpad_portnums[0],data);
desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],data);
desc->run(ladspa->handle,num_samples);
desc->connect_port(ladspa->handle,oclass->sinkpad_portnums[0],NULL);
desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],NULL);
if (GST_IS_EVENT (buf)) {
/* push the event out all the src pads */
for (i=0 ; i<oclass->numsrcpads ; i++){
gst_pad_push (ladspa->srcpads[0], buf);
}
return;
}
gst_pad_push (ladspa->srcpads[0], buf);
data_in = (LADSPA_Data *) GST_BUFFER_DATA(buf);
num_samples = GST_BUFFER_SIZE(buf) / sizeof(gfloat);
numsrcpads = oclass->numsrcpads;
desc = ladspa->descriptor;
inplace_broken = LADSPA_IS_INPLACE_BROKEN(desc->Properties);
if (numsrcpads > 0){
guint num_created_buffers = 0;
buffers_out = g_new(GstBuffer*, numsrcpads);
data_out = g_new(LADSPA_Data*, numsrcpads);
if (inplace_broken){
num_created_buffers = numsrcpads;
}
else {
/* we can share the buffer for input and output */
buffers_out[0] = buf;
data_out[0] = (LADSPA_Data *)GST_BUFFER_DATA(buf);
num_created_buffers = numsrcpads - 1;
}
if (num_created_buffers > 0){
GstBufferPool *bufpool;
bufpool = gst_buffer_pool_get_default (sizeof (LADSPA_Data) * GST_BUFFER_SIZE(buf), ladspa->numbuffers);
for (i = numsrcpads - num_created_buffers ; i<numsrcpads ; i++){
buffers_out[i] = gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
GST_BUFFER_TIMESTAMP(buffers_out[i]) = GST_BUFFER_TIMESTAMP(buf);
data_out[i] = (LADSPA_Data *) GST_BUFFER_DATA(buffers_out[i]);
}
}
}
num_to_process = GST_DPMAN_PREPROCESS(ladspa->dpman, num_samples, GST_BUFFER_TIMESTAMP(buf));
num_processed = 0;
/* split up processing of the buffer into chunks so that dparams can
* be updated when required.
* In many cases the buffer will be processed in one chunk anyway.
*/
while(GST_DPMAN_PROCESS_CHUNK(ladspa->dpman, num_to_process, num_processed)) {
desc->connect_port(ladspa->handle,oclass->sinkpad_portnums[0],data_in);
for (i=0 ; i<numsrcpads ; i++){
desc->connect_port(ladspa->handle,oclass->srcpad_portnums[i],data_out[i]);
}
desc->run(ladspa->handle, num_to_process);
data_in += num_to_process;
for (i=0 ; i<numsrcpads ; i++){
data_out[i] += num_to_process;
}
num_processed += num_to_process;
num_to_process = 0;
}
if (numsrcpads > 0){
for (i=0 ; i<numsrcpads ; i++){
gst_pad_push (ladspa->srcpads[i], buffers_out[i]);
}
g_free(buffers_out);
g_free(data_out);
return;
}
/* if we have reached here, there are no src pads */
gst_buffer_unref(buf);
}
static GstBuffer *
gst_ladspa_get(GstPad *pad)
{
LADSPA_Descriptor *desc;
LADSPA_Data *data;
GstLADSPA *ladspa;
GstLADSPAClass *oclass;
GstBuffer *buf;
gint8 i, *zero_out;
unsigned long size;
GstBuffer *buf;
LADSPA_Data *data;
LADSPA_Descriptor *desc;
guint num_to_process, num_processed;
g_return_val_if_fail(pad != NULL, NULL);
g_return_val_if_fail(GST_IS_PAD(pad), NULL);
@ -844,45 +993,42 @@ gst_ladspa_get(GstPad *pad)
oclass = (GstLADSPAClass*)(G_OBJECT_GET_CLASS(ladspa));
/* force all src pads to set their caps */
/* force src pad to set caps */
if (ladspa->newcaps) {
for (i=0;i<oclass->numsrcpads;i++) {
gst_ladspa_force_src_caps(ladspa, ladspa->srcpads[0]);
}
gst_ladspa_force_src_caps(ladspa, ladspa->srcpads[0]);
}
/* get a bufferpool */
i = 0;
while ((ladspa->bufpool == NULL) && (i < oclass->numsrcpads)) {
ladspa->bufpool = gst_pad_get_bufferpool (ladspa->srcpads[i++]);
}
if (ladspa->bufpool == NULL) {
ladspa->bufpool = gst_buffer_pool_get_default (sizeof (LADSPA_Data) * ladspa->buffersize,
ladspa->numbuffers);
ladspa->bufpool = gst_pad_get_bufferpool (ladspa->srcpads[0]);
if (ladspa->bufpool == NULL) {
ladspa->bufpool = gst_buffer_pool_get_default (sizeof (LADSPA_Data) * ladspa->buffersize, ladspa->numbuffers);
}
}
buf = (GstBuffer *) gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
g_return_val_if_fail (buf, NULL);
/* initialize the output data to 0 */
zero_out = (gint8 *) GST_BUFFER_DATA (buf);
for (i = 0; i < GST_BUFFER_SIZE (buf); i++)
zero_out[i] = 0;
data = (LADSPA_Data *) GST_BUFFER_DATA(buf);
size = GST_BUFFER_SIZE(buf);
buf = gst_buffer_new_from_pool (ladspa->bufpool, 0, 0);
GST_BUFFER_TIMESTAMP(buf) = ladspa->timestamp;
ladspa->timestamp += size * ladspa->samplerate * 10^9;
data = (LADSPA_Data *) GST_BUFFER_DATA(buf);
desc = ladspa->descriptor;
num_to_process = GST_DPMAN_PREPROCESS(ladspa->dpman, ladspa->buffersize, ladspa->timestamp);
num_processed = 0;
for (i=0;i<oclass->numsrcpads;i++)
desc->connect_port(ladspa->handle,oclass->srcpad_portnums[i],data);
/* update timestamp */
ladspa->timestamp += num_to_process * 10^9 / ladspa->samplerate;
desc->run(ladspa->handle, size);
/* split up processing of the buffer into chunks so that dparams can
* be updated when required.
* In many cases the buffer will be processed in one chunk anyway.
*/
while(GST_DPMAN_PROCESS_CHUNK(ladspa->dpman, num_to_process, num_processed)) {
desc->connect_port(ladspa->handle,oclass->srcpad_portnums[0],data);
desc->run(ladspa->handle, num_to_process);
for (i=0;i<oclass->numsrcpads;i++)
desc->connect_port(ladspa->handle,oclass->srcpad_portnums[i],NULL);
data += num_to_process;
num_processed = num_to_process;
num_to_process = 0;
}
return buf;
}
@ -956,8 +1102,15 @@ ladspa_describe_plugin(const char *pcFullFilename,
}
}
srctempl = ladspa_src_factory();
gst_element_factory_add_pad_template (factory, srctempl);
/* only add src padtemplate if there are srcpads */
for (j=0;j<desc->PortCount;j++) {
if (LADSPA_IS_PORT_AUDIO(desc->PortDescriptors[j]) &&
LADSPA_IS_PORT_OUTPUT(desc->PortDescriptors[j])) {
srctempl = ladspa_src_factory();
gst_element_factory_add_pad_template (factory, srctempl);
break;
}
}
}
}
@ -977,6 +1130,11 @@ plugin_init (GModule *module, GstPlugin *plugin)
return FALSE;
}
if (! gst_library_load ("gstcontrol")) {
gst_info ("gstladspa: could not load support library: 'gstcontrol'\n");
return FALSE;
}
return TRUE;
}

View file

@ -37,6 +37,7 @@ extern "C" {
typedef struct _ladspa_control_info {
gchar *name;
gchar *param_name;
gfloat lowerbound, upperbound;
gboolean lower,upper,samplerate;
gboolean toggled, logarithmic, integer, writable;
@ -51,19 +52,20 @@ struct _GstLADSPA {
LADSPA_Descriptor *descriptor;
LADSPA_Handle *handle;
GstDParamManager *dpman;
gfloat *controls;
GstPad **sinkpads,
**srcpads;
GstByteStream **bytestreams;
GstBufferPool *bufpool;
gboolean loopbased, newcaps, activated;
gboolean newcaps, activated;
gint samplerate, buffersize, numbuffers;
gulong timestamp;
gint64 timestamp;
};