summaryrefslogtreecommitdiff
path: root/spa/plugins/audioconvert/channelmix.c
diff options
context:
space:
mode:
Diffstat (limited to 'spa/plugins/audioconvert/channelmix.c')
-rw-r--r--spa/plugins/audioconvert/channelmix.c262
1 files changed, 221 insertions, 41 deletions
diff --git a/spa/plugins/audioconvert/channelmix.c b/spa/plugins/audioconvert/channelmix.c
index 9e6d0cd1..73be1bd2 100644
--- a/spa/plugins/audioconvert/channelmix.c
+++ b/spa/plugins/audioconvert/channelmix.c
@@ -31,6 +31,7 @@
#include <spa/support/cpu.h>
#include <spa/utils/list.h>
#include <spa/utils/names.h>
+#include <spa/node/keys.h>
#include <spa/node/node.h>
#include <spa/node/io.h>
#include <spa/node/utils.h>
@@ -118,7 +119,7 @@ struct impl {
struct props props;
struct spa_param_info params[8];
-
+ struct port control_port;
struct port in_port;
struct port out_port;
@@ -128,10 +129,14 @@ struct impl {
uint32_t cpu_flags;
};
-#define CHECK_PORT(this,d,id) (id == 0)
+#define IS_CONTROL_PORT(this,d,id) (id == 1 && d == SPA_DIRECTION_INPUT)
+#define IS_DATA_PORT(this,d,id) (id == 0)
+
+#define CHECK_PORT(this,d,id) (IS_CONTROL_PORT(this,d,id) || IS_DATA_PORT(this,d,id))
+#define GET_CONTROL_PORT(this,id) (&this->control_port)
#define GET_IN_PORT(this,id) (&this->in_port)
#define GET_OUT_PORT(this,id) (&this->out_port)
-#define GET_PORT(this,d,id) (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,id) : GET_OUT_PORT(this,id))
+#define GET_PORT(this,d,id) (IS_CONTROL_PORT(this,d,id) ? GET_CONTROL_PORT(this,id) : (d == SPA_DIRECTION_INPUT ? GET_IN_PORT(this,id) : GET_OUT_PORT(this,id)))
#define _MASK(ch) (1ULL << SPA_AUDIO_CHANNEL_ ## ch)
#define STEREO (_MASK(FL)|_MASK(FR))
@@ -380,6 +385,17 @@ static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
return -ENOTSUP;
}
+static void emit_port_info(struct impl *this, struct port *port, bool full)
+{
+ if (full)
+ port->info.change_mask = port->info_all;
+ if (port->info.change_mask) {
+ spa_node_emit_port_info(&this->hooks,
+ port->direction, port->id, &port->info);
+ port->info.change_mask = 0;
+ }
+}
+
static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
const struct spa_pod *param)
{
@@ -418,17 +434,6 @@ static int impl_node_send_command(void *object, const struct spa_command *comman
return 0;
}
-static void emit_port_info(struct impl *this, struct port *port, bool full)
-{
- if (full)
- port->info.change_mask = port->info_all;
- if (port->info.change_mask) {
- spa_node_emit_port_info(&this->hooks,
- port->direction, port->id, &port->info);
- port->info.change_mask = 0;
- }
-}
-
static int
impl_node_add_listener(void *object,
struct spa_hook *listener,
@@ -445,6 +450,7 @@ impl_node_add_listener(void *object,
emit_info(this, true);
emit_port_info(this, GET_IN_PORT(this, 0), true);
emit_port_info(this, GET_OUT_PORT(this, 0), true);
+ emit_port_info(this, GET_CONTROL_PORT(this, 0), true);
spa_hook_list_join(&this->hooks, &save);
@@ -485,6 +491,14 @@ static int port_enum_formats(void *object,
switch (index) {
case 0:
+ if (IS_CONTROL_PORT(this, direction, port_id)) {
+ *param = spa_pod_builder_add_object(builder,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
+ break;
+ }
+
if (other->have_format) {
*param = spa_pod_builder_add_object(builder,
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
@@ -554,7 +568,13 @@ impl_node_port_enum_params(void *object, int seq,
if (result.index > 0)
return 0;
- param = spa_format_audio_raw_build(&b, id, &port->format.info.raw);
+ if (IS_CONTROL_PORT(this, direction, port_id))
+ param = spa_pod_builder_add_object(&b,
+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_application),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_control));
+ else
+ param = spa_format_audio_raw_build(&b, id, &port->format.info.raw);
break;
case SPA_PARAM_Buffers:
@@ -589,6 +609,9 @@ impl_node_port_enum_params(void *object, int seq,
case SPA_PARAM_Meta:
switch (result.index) {
case 0:
+ if (IS_CONTROL_PORT(this, direction, port_id))
+ return -EINVAL;
+
param = spa_pod_builder_add_object(&b,
SPA_TYPE_OBJECT_ParamMeta, id,
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
@@ -662,22 +685,28 @@ static int port_set_format(void *object,
if ((res = spa_format_parse(format, &info.media_type, &info.media_subtype)) < 0)
return res;
- if (info.media_type != SPA_MEDIA_TYPE_audio ||
- info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
- return -EINVAL;
+ if (IS_CONTROL_PORT(this, direction, port_id)) {
+ if (info.media_type != SPA_MEDIA_TYPE_application ||
+ info.media_subtype != SPA_MEDIA_SUBTYPE_control)
+ return -EINVAL;
+ } else {
+ if (info.media_type != SPA_MEDIA_TYPE_audio ||
+ info.media_subtype != SPA_MEDIA_SUBTYPE_raw)
+ return -EINVAL;
- if (spa_format_audio_raw_parse(format, &info.info.raw) < 0)
- return -EINVAL;
+ if (spa_format_audio_raw_parse(format, &info.info.raw) < 0)
+ return -EINVAL;
- if (info.info.raw.format != SPA_AUDIO_FORMAT_F32P)
- return -EINVAL;
+ if (info.info.raw.format != SPA_AUDIO_FORMAT_F32P)
+ return -EINVAL;
- port->stride = sizeof(float);
- port->blocks = info.info.raw.channels;
+ port->stride = sizeof(float);
+ port->blocks = info.info.raw.channels;
- if (other->have_format) {
- if ((res = setup_convert(this, direction, &info)) < 0)
- return res;
+ if (other->have_format) {
+ if ((res = setup_convert(this, direction, &info)) < 0)
+ return res;
+ }
}
port->format = info;
port->have_format = true;
@@ -704,15 +733,20 @@ impl_node_port_set_param(void *object,
uint32_t id, uint32_t flags,
const struct spa_pod *param)
{
- spa_return_val_if_fail(object != NULL, -EINVAL);
+ struct impl *this = object;
+
+ spa_return_val_if_fail(this != NULL, -EINVAL);
- spa_return_val_if_fail(CHECK_PORT(object, direction, port_id), -EINVAL);
+ spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
- if (id == SPA_PARAM_Format) {
+ switch (id) {
+ case SPA_PARAM_Format:
return port_set_format(object, direction, port_id, flags, param);
+ default:
+ break;
}
- else
- return -ENOENT;
+
+ return -ENOENT;
}
static int
@@ -730,8 +764,6 @@ impl_node_port_use_buffers(void *object,
port = GET_PORT(this, direction, port_id);
- spa_return_val_if_fail(port->have_format, -EIO);
-
spa_log_debug(this->log, NAME " %p: use buffers %d on port %d", this, n_buffers, port_id);
clear_buffers(this, port);
@@ -840,20 +872,136 @@ static int impl_node_port_reuse_buffer(void *object, uint32_t port_id, uint32_t
return 0;
}
+static int update_control_sequence(struct spa_pod_sequence *ctrl_sequence,
+ uint32_t ctrl_size, uint32_t curr_ctrl, uint32_t curr_offset)
+{
+ struct spa_pod_control *c;
+ struct spa_pod_builder b;
+ struct spa_pod_frame f[1];
+ uint32_t i = 0;
+
+ spa_return_val_if_fail(ctrl_sequence != NULL, -EINVAL);
+ spa_return_val_if_fail(ctrl_size > 0, -EINVAL);
+ spa_return_val_if_fail(curr_offset > 0, -EINVAL);
+
+ /* copy the sequence */
+ uint8_t old[ctrl_size];
+ memcpy(old, ctrl_sequence, ctrl_size);
+
+ /* reset the current sequence */
+ spa_pod_builder_init(&b, ctrl_sequence, ctrl_size);
+ spa_pod_builder_push_sequence(&b, &f[0], 0);
+
+ /* add the remaining controls */
+ SPA_POD_SEQUENCE_FOREACH(&((struct spa_io_sequence *)old)->sequence, c) {
+ switch (c->type) {
+ case SPA_CONTROL_Properties:
+ {
+ if (i >= curr_ctrl) {
+ spa_pod_builder_control(&b,
+ i == curr_ctrl ? curr_offset : c->offset,
+ SPA_CONTROL_Properties);
+ spa_pod_builder_primitive(&b, &c->value);
+ }
+
+ i++;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ spa_pod_builder_pop(&b, &f[0]);
+
+ return 0;
+}
+
+static int channelmix_process_control(struct impl *this, struct buffer *cbuf,
+ uint32_t n_dst, void * SPA_RESTRICT dst[n_dst],
+ uint32_t n_src, const void * SPA_RESTRICT src[n_src],
+ uint32_t n_samples)
+{
+ struct spa_pod_sequence *ctrl_sequence;
+ uint32_t ctrl_size;
+ struct spa_pod_control *c;
+ uint32_t avail_samples = n_samples;
+ uint32_t i, j;
+ const float **s = (const float **)src;
+ float **d = (float **)dst;
+
+ /* get the control sequence */
+ spa_return_val_if_fail(cbuf != NULL, -EINVAL);
+ spa_return_val_if_fail(cbuf->outbuf->n_datas == 1, -EINVAL);
+ ctrl_sequence = (struct spa_pod_sequence *)cbuf->outbuf->datas[0].data;
+ ctrl_size = cbuf->outbuf->datas[0].maxsize;
+
+ i = 0;
+ SPA_POD_SEQUENCE_FOREACH(ctrl_sequence, c) {
+ switch (c->type) {
+ case SPA_CONTROL_Properties:
+ {
+ /* update the sequence and return if all samples were processed */
+ if (avail_samples == 0) {
+ update_control_sequence(ctrl_sequence, ctrl_size, i, c->offset);
+ return 0;
+ }
+
+ /* apply the sequence control props */
+ apply_props(this, &c->value);
+
+ if (c->offset > 0) {
+ /* get the minimum offset for this buffer */
+ uint32_t offset = SPA_MIN(avail_samples, c->offset);
+
+ /* process the control sequence */
+ channelmix_process(&this->mix, n_dst, dst, n_src, src, offset);
+ for (j = 0; j < n_src; j++)
+ s[j] += offset;
+ for (j = 0; j < n_dst; j++)
+ d[j] += offset;
+
+ /* update the sequence with the remaining offset samples */
+ if (avail_samples < c->offset) {
+ update_control_sequence(ctrl_sequence, ctrl_size, i, c->offset - avail_samples);
+ return 0;
+ }
+
+ /* decrease available samples */
+ avail_samples -= offset;
+ }
+
+ i++;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ /* process the remaining samples */
+ if (avail_samples > 0)
+ channelmix_process(&this->mix, n_dst, dst, n_src, src, avail_samples);
+
+ return 1;
+}
+
static int impl_node_process(void *object)
{
struct impl *this = object;
- struct port *outport, *inport;
- struct spa_io_buffers *outio, *inio;
- struct buffer *sbuf, *dbuf;
+ struct port *outport, *inport, *ctrlport;
+ struct spa_io_buffers *outio, *inio, *ctrlio;
+ struct buffer *sbuf, *dbuf, *cbuf = NULL;
spa_return_val_if_fail(this != NULL, -EINVAL);
outport = GET_OUT_PORT(this, 0);
inport = GET_IN_PORT(this, 0);
+ ctrlport = GET_CONTROL_PORT(this, 1);
outio = outport->io;
inio = inport->io;
+ ctrlio = ctrlport->io;
spa_return_val_if_fail(outio != NULL, -EIO);
spa_return_val_if_fail(inio != NULL, -EIO);
@@ -880,6 +1028,11 @@ static int impl_node_process(void *object)
sbuf = &inport->buffers[inio->buffer_id];
+ if (ctrlio != NULL &&
+ ctrlio->status == SPA_STATUS_HAVE_DATA &&
+ ctrlio->buffer_id < ctrlport->n_buffers)
+ cbuf = &ctrlport->buffers[ctrlio->buffer_id];
+
{
uint32_t i, n_samples;
struct spa_buffer *sb = sbuf->outbuf, *db = dbuf->outbuf;
@@ -889,7 +1042,7 @@ static int impl_node_process(void *object)
void *dst_datas[n_dst_datas];
bool is_passthrough;
- is_passthrough = this->is_passthrough && this->mix.identity;
+ is_passthrough = this->is_passthrough && this->mix.identity && cbuf == NULL;
n_samples = sb->datas[0].chunk->size / inport->stride;
@@ -901,9 +1054,18 @@ static int impl_node_process(void *object)
db->datas[i].chunk->size = n_samples * outport->stride;
}
- if (!is_passthrough)
- channelmix_process(&this->mix, n_dst_datas, dst_datas,
- n_src_datas, src_datas, n_samples);
+ if (!is_passthrough) {
+ if (cbuf != NULL) {
+ /* if return value is 1, the sequence has been processed */
+ if (channelmix_process_control(this, cbuf, n_dst_datas, dst_datas,
+ n_src_datas, src_datas, n_samples) == 1) {
+ ctrlio->status = SPA_STATUS_OK;
+ }
+ } else {
+ channelmix_process(&this->mix, n_dst_datas, dst_datas,
+ n_src_datas, src_datas, n_samples);
+ }
+ }
}
outio->status = SPA_STATUS_HAVE_DATA;
@@ -996,6 +1158,8 @@ impl_init(const struct spa_handle_factory *factory,
SPA_NODE_CHANGE_MASK_PARAMS;
this->info = SPA_NODE_INFO_INIT();
this->info.flags = SPA_NODE_FLAG_RT;
+ this->info.max_input_ports = 2;
+ this->info.max_output_ports = 1;
this->params[0] = SPA_PARAM_INFO(SPA_PARAM_PropInfo, SPA_PARAM_INFO_READ);
this->params[1] = SPA_PARAM_INFO(SPA_PARAM_Props, SPA_PARAM_INFO_READWRITE);
this->info.params = this->params;
@@ -1035,6 +1199,22 @@ impl_init(const struct spa_handle_factory *factory,
port->info.n_params = 0;
spa_list_init(&port->queue);
+ port = GET_CONTROL_PORT(this, 1);
+ port->direction = SPA_DIRECTION_INPUT;
+ port->id = 1;
+ port->info_all = SPA_PORT_CHANGE_MASK_FLAGS |
+ SPA_PORT_CHANGE_MASK_PARAMS;
+ port->info = SPA_PORT_INFO_INIT();
+ port->info.flags = SPA_PORT_FLAG_NO_REF |
+ SPA_PORT_FLAG_DYNAMIC_DATA;
+ port->params[0] = SPA_PARAM_INFO(SPA_PARAM_EnumFormat, SPA_PARAM_INFO_READ);
+ port->params[1] = SPA_PARAM_INFO(SPA_PARAM_IO, SPA_PARAM_INFO_READ);
+ port->params[2] = SPA_PARAM_INFO(SPA_PARAM_Format, SPA_PARAM_INFO_WRITE);
+ port->params[3] = SPA_PARAM_INFO(SPA_PARAM_Buffers, 0);
+ port->info.params = port->params;
+ port->info.n_params = 4;
+ spa_list_init(&port->queue);
+
return 0;
}