From 8f97e0dd318eaee37c63c60dd1526e7affa77c2b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 3 Jul 2018 21:34:22 +0200 Subject: [PATCH] audioconvert: add volume control --- spa/plugins/audioconvert/audioconvert.c | 60 +++++++++++++++++++-- spa/plugins/audioconvert/channelmix-ops.c | 65 ++++++++++++++++++----- spa/plugins/audioconvert/channelmix.c | 38 +++++++++---- spa/plugins/audioconvert/fmtconvert.c | 2 +- 4 files changed, 138 insertions(+), 27 deletions(-) diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index 6eeaeb7ee..a0ec29094 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -48,6 +48,10 @@ struct type { uint32_t format; uint32_t prop_truncate; uint32_t prop_dither; + + uint32_t prop_volume; + uint32_t io_prop_volume; + struct spa_type_io io; struct spa_type_param param; struct spa_type_media_type media_type; @@ -68,6 +72,10 @@ static inline void init_type(struct type *type, struct spa_type_map *map) type->format = spa_type_map_get_id(map, SPA_TYPE__Format); type->prop_truncate = spa_type_map_get_id(map, SPA_TYPE_PROPS__truncate); type->prop_dither = spa_type_map_get_id(map, SPA_TYPE_PROPS__ditherType); + + type->prop_volume = spa_type_map_get_id(map, SPA_TYPE_PROPS__volume); + type->io_prop_volume = spa_type_map_get_id(map, SPA_TYPE_IO_PROP_BASE "volume"); + spa_type_io_map(map, &type->io); spa_type_param_map(map, &type->param); spa_type_media_type_map(map, &type->media_type); @@ -113,6 +121,10 @@ struct link { struct spa_buffer **buffers; }; +struct control { + struct spa_pod_float *volume; +}; + struct impl { struct spa_handle handle; struct spa_node node; @@ -122,6 +134,7 @@ struct impl { struct spa_log *log; struct props props; + struct control control; const struct spa_node_callbacks *callbacks; void *user_data; @@ -264,7 +277,6 @@ static int negotiate_link_format(struct impl *this, struct link *link) filter = format; spa_pod_fixate(filter); - spa_debug_pod(filter, SPA_DEBUG_FLAG_FORMAT); if ((res = spa_node_port_set_param(link->out_node, SPA_DIRECTION_OUTPUT, link->out_port, @@ -601,12 +613,43 @@ impl_node_port_enum_params(struct spa_node *node, struct spa_pod_builder *builder) { struct impl *this; + struct spa_pod *param; + struct spa_pod_builder b = { 0 }; + uint8_t buffer[1024]; + struct type *t; spa_return_val_if_fail(node != NULL, -EINVAL); this = SPA_CONTAINER_OF(node, struct impl, node); + t = &this->type; - return spa_node_port_enum_params(this->fmt[direction], direction, port_id, + if (id == t->param_io.idPropsIn) { + next: + spa_pod_builder_init(&b, buffer, sizeof(buffer)); + + switch (*index) { + case 0: + param = spa_pod_builder_object(builder, + id, t->param_io.Prop, + ":", t->param_io.id, "I", this->type.io_prop_volume, + ":", t->param_io.size, "i", sizeof(struct spa_pod_float), + ":", t->param.propId, "I", this->type.prop_volume, + ":", t->param.propType, "fru", 1.0, + SPA_POD_PROP_MIN_MAX(0.0, 10.0)); + break; + default: + return 0; + } + + (*index)++; + + if (spa_pod_filter(builder, result, param, filter) < 0) + goto next; + + return 1; + } + else + return spa_node_port_enum_params(this->fmt[direction], direction, port_id, id, index, filter, result, builder); } @@ -667,16 +710,23 @@ impl_node_port_set_io(struct spa_node *node, { struct impl *this; struct type *t; + int res; spa_return_val_if_fail(node != NULL, -EINVAL); this = SPA_CONTAINER_OF(node, struct impl, node); t = &this->type; - if (id == t->io.ControlRange) - spa_node_port_set_io(this->resample, direction, 0, id, data, size); + spa_log_debug(this->log, "set io %d %d %d", id, direction, port_id); - return spa_node_port_set_io(this->fmt[direction], direction, port_id, id, data, size); + if (id == t->io.ControlRange) + res = spa_node_port_set_io(this->resample, direction, 0, id, data, size); + else if (id == t->io_prop_volume) + res = spa_node_port_set_io(this->channelmix, direction, 0, id, data, size); + else + res = spa_node_port_set_io(this->fmt[direction], direction, port_id, id, data, size); + + return res; } static int impl_node_port_reuse_buffer(struct spa_node *node, uint32_t port_id, uint32_t buffer_id) diff --git a/spa/plugins/audioconvert/channelmix-ops.c b/spa/plugins/audioconvert/channelmix-ops.c index 5afc5152c..7ea45b135 100644 --- a/spa/plugins/audioconvert/channelmix-ops.c +++ b/spa/plugins/audioconvert/channelmix-ops.c @@ -22,13 +22,34 @@ #include +#define VOLUME_MIN 0.0f +#define VOLUME_NORM 1.0f + static void channelmix_copy(void *data, int n_dst, void *dst[n_dst], int n_src, const void *src[n_src], void *matrix, int n_bytes) { - int i; - for (i = 0; i < n_src; i++) - memcpy(dst[i], src[i], n_bytes); + int i, j; + float *m = matrix; + float v = m[0]; + + if (v <= VOLUME_MIN) { + for (i = 0; i < n_src; i++) + memset(dst[i], 0, n_bytes); + } + else if (v == VOLUME_NORM) { + for (i = 0; i < n_src; i++) + memcpy(dst[i], src[i], n_bytes); + } + else { + float **d = (float **) dst; + float **s = (float **) src; + int n_samples = n_bytes / sizeof(float); + + for (i = 0; i < n_src; i++) + for (j = 0; j < n_samples; j++) + d[i][j] = s[i][j] * v; + } } static void @@ -60,11 +81,20 @@ channelmix_f32_1_2(void *data, int n_dst, void *dst[n_dst], int n, n_samples; float **d = (float **) dst; const float *s = src[0]; + float *m = matrix; + float v = m[0]; n_samples = n_bytes / sizeof(float); - for (n = 0; n < n_samples; n++) { - d[0][n] = s[n]; - d[1][n] = s[n]; + if (v <= VOLUME_MIN) { + memset(d[0], 0, n_bytes); + } + else if (v == VOLUME_NORM) { + for (n = 0; n < n_samples; n++) + d[0][n] = d[1][n] = s[n]; + } + else { + for (n = 0; n < n_samples; n++) + d[0][n] = d[1][n] = s[n] * v; } } @@ -75,10 +105,22 @@ channelmix_f32_2_1(void *data, int n_dst, void *dst[n_dst], int n, n_samples; float *d = dst[0]; float **s = (float **) src; + float *m = matrix; + float v = m[0]; n_samples = n_bytes / sizeof(float); - for (n = 0; n < n_samples; n++) - d[n] = (s[0][n] + s[1][n]) * 0.5f; + if (v <= VOLUME_MIN) { + memset(d, 0, n_bytes); + } + else if (v == VOLUME_NORM) { + for (n = 0; n < n_samples; n++) + d[n] = (s[0][n] + s[1][n]) * 0.5f; + } + else { + v *= 0.5f; + for (n = 0; n < n_samples; n++) + d[n] = (s[0][n] + s[1][n]) * v; + } } typedef void (*channelmix_func_t) (void *data, int n_dst, void *dst[n_dst], @@ -91,13 +133,12 @@ static const struct channelmix_info { uint32_t dst_chan; channelmix_func_t func; -#define CHANNELMIX_INFO_FLAG_NO_MATRIX (1 << 0) uint32_t flags; } channelmix_table[] = { - { -2, -2, channelmix_copy, CHANNELMIX_INFO_FLAG_NO_MATRIX }, - { 1, 2, channelmix_f32_1_2, CHANNELMIX_INFO_FLAG_NO_MATRIX }, - { 2, 1, channelmix_f32_2_1, CHANNELMIX_INFO_FLAG_NO_MATRIX }, + { -2, -2, channelmix_copy, 0 }, + { 1, 2, channelmix_f32_1_2, 0 }, + { 2, 1, channelmix_f32_2_1, 0 }, { -1, -1, channelmix_f32_n_m, 0 }, }; diff --git a/spa/plugins/audioconvert/channelmix.c b/spa/plugins/audioconvert/channelmix.c index bae828b23..a59072e87 100644 --- a/spa/plugins/audioconvert/channelmix.c +++ b/spa/plugins/audioconvert/channelmix.c @@ -42,12 +42,15 @@ struct impl; +#define DEFAULT_VOLUME 1.0 + struct props { - int dummy; + float volume; }; static void props_reset(struct props *props) { + props->volume = DEFAULT_VOLUME; } struct buffer { @@ -58,12 +61,18 @@ struct buffer { struct spa_meta_header *h; }; +struct control { + struct spa_pod_float *volume; +}; + struct port { uint32_t id; struct spa_io_buffers *io; struct spa_port_info info; + struct control control; + bool have_format; struct spa_audio_info format; uint32_t stride; @@ -79,8 +88,10 @@ struct port { struct type { uint32_t node; uint32_t format; - uint32_t prop_truncate; - uint32_t prop_dither; + + uint32_t prop_volume; + uint32_t io_prop_volume; + struct spa_type_io io; struct spa_type_param param; struct spa_type_media_type media_type; @@ -99,8 +110,10 @@ static inline void init_type(struct type *type, struct spa_type_map *map) { type->node = spa_type_map_get_id(map, SPA_TYPE__Node); type->format = spa_type_map_get_id(map, SPA_TYPE__Format); - type->prop_truncate = spa_type_map_get_id(map, SPA_TYPE_PROPS__truncate); - type->prop_dither = spa_type_map_get_id(map, SPA_TYPE_PROPS__ditherType); + + type->prop_volume = spa_type_map_get_id(map, SPA_TYPE_PROPS__volume); + type->io_prop_volume = spa_type_map_get_id(map, SPA_TYPE_IO_PROP_BASE "volume"); + spa_type_io_map(map, &type->io); spa_type_param_map(map, &type->param); spa_type_media_type_map(map, &type->media_type); @@ -158,7 +171,7 @@ static void setup_matrix(struct impl *this, for (i = 0; i < dst_chan; i++) { for (j = 0; j < src_chan; j++) { if (i == j) - this->matrix[i * src_chan + j] = 1.0f; + this->matrix[i * src_chan + j] = this->props.volume; else this->matrix[i * src_chan + j] = 0.0f; } @@ -204,9 +217,7 @@ static int setup_convert(struct impl *this, this->convert = chanmix_info->func; /* set up the matrix if needed */ - if (!SPA_FLAG_CHECK(chanmix_info->flags, CHANNELMIX_INFO_FLAG_NO_MATRIX)) { - setup_matrix(this, src_info, dst_info); - } + setup_matrix(this, src_info, dst_info); return 0; } @@ -698,6 +709,8 @@ impl_node_port_set_io(struct spa_node *node, if (id == t->io.Buffers) port->io = data; + else if (id == t->io_prop_volume) + port->control.volume = data; else return -ENOENT; @@ -795,6 +808,13 @@ static int impl_node_process(struct spa_node *node) if ((dbuf = dequeue_buffer(this, outport)) == NULL) return outio->status = -EPIPE; + if (outport->control.volume && outport->control.volume->value != this->props.volume) { + this->props.volume = outport->control.volume->value; + setup_matrix(this, + &GET_IN_PORT(this, 0)->format, + &GET_OUT_PORT(this, 0)->format); + } + sbuf = &inport->buffers[inio->buffer_id]; { diff --git a/spa/plugins/audioconvert/fmtconvert.c b/spa/plugins/audioconvert/fmtconvert.c index 8fcfca5d6..1810bf1ac 100644 --- a/spa/plugins/audioconvert/fmtconvert.c +++ b/spa/plugins/audioconvert/fmtconvert.c @@ -863,7 +863,7 @@ impl_node_port_set_io(struct spa_node *node, port = GET_PORT(this, direction, port_id); - spa_log_trace(this->log, NAME " %p: port %d:%d update io %d %p", + spa_log_debug(this->log, NAME " %p: port %d:%d update io %d %p", this, direction, port_id, id, data); if (id == t->io.Buffers)