audioconvert: add volume control

This commit is contained in:
Wim Taymans 2018-07-03 21:34:22 +02:00
parent e5c2896fca
commit 8f97e0dd31
4 changed files with 138 additions and 27 deletions

View file

@ -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,11 +613,42 @@ 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;
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)

View file

@ -22,13 +22,34 @@
#include <spa/utils/defs.h>
#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;
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);
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 },
};

View file

@ -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);
}
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];
{

View file

@ -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)