mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
audioconvert: add volume control
This commit is contained in:
parent
e5c2896fca
commit
8f97e0dd31
4 changed files with 138 additions and 27 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
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 },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue