LADI
/
spa
1
Fork 0

module-jack: add volume control

This commit is contained in:
Wim Taymans 2023-05-08 11:34:00 +02:00
parent 685ada05f0
commit d2cd65b6e2
1 changed files with 99 additions and 6 deletions

View File

@ -130,6 +130,12 @@ struct port {
bool latency_changed[2];
};
struct volume {
bool mute;
uint32_t n_volumes;
float volumes[SPA_AUDIO_MAX_CHANNELS];
};
struct impl {
struct pw_context *context;
struct pw_loop *main_loop;
@ -156,12 +162,14 @@ struct impl {
struct spa_hook source_listener;
struct spa_audio_info_raw source_info;
struct port *source_ports[SPA_AUDIO_MAX_CHANNELS];
struct volume sink_volume;
struct pw_properties *sink_props;
struct pw_filter *sink;
struct spa_hook sink_listener;
struct spa_audio_info_raw sink_info;
struct port *sink_ports[SPA_AUDIO_MAX_CHANNELS];
struct volume source_volume;
uint32_t samplerate;
@ -180,6 +188,28 @@ struct impl {
unsigned int sink_connect:1;
};
static void reset_volume(struct volume *vol, uint32_t n_volumes)
{
uint32_t i;
vol->mute = false;
vol->n_volumes = n_volumes;
for (i = 0; i < n_volumes; i++)
vol->volumes[i] = 1.0f;
}
static void do_volume(float *dst, const float *src, struct volume *vol, uint32_t ch, uint32_t n_samples)
{
float v = vol->mute ? 0.0f : vol->volumes[ch];
if (v == 1.0f)
memcpy(dst, src, n_samples * sizeof(float));
else if (v == 0.0f)
memset(dst, 0, n_samples * sizeof(float));
else {
uint32_t i;
for (i = 0; i < n_samples; i++)
dst[i] = src[i] * v;
}
}
static void source_destroy(void *d)
{
struct impl *impl = d;
@ -229,7 +259,8 @@ static void sink_process(void *d, struct spa_io_position *position)
continue;
dst = jack.port_get_buffer(p->jack_port, n_samples);
memcpy(dst, src, n_samples * sizeof(float));
do_volume(dst, src, &impl->sink_volume, i, n_samples);
}
pw_log_trace_fp("done %u", impl->frame_time);
if (impl->mode & MODE_SINK) {
@ -255,7 +286,8 @@ static void source_process(void *d, struct spa_io_position *position)
continue;
src = jack.port_get_buffer (p->jack_port, n_samples);
memcpy(dst, src, n_samples * sizeof(float));
do_volume(dst, src, &impl->source_volume, i, n_samples);
}
pw_log_trace_fp("done %u", impl->frame_time);
if (impl->mode == MODE_SOURCE) {
@ -356,7 +388,6 @@ static void make_sink_ports(struct impl *impl)
port->jack_port = jack.port_register (impl->client, name,
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
impl->sink_ports[i] = port;
if (ports != NULL && ports[i] != NULL) {
@ -368,6 +399,55 @@ static void make_sink_ports(struct impl *impl)
jack.free(ports);
}
static struct spa_pod *make_props_param(struct spa_pod_builder *b, struct impl *impl,
struct volume *vol)
{
return spa_pod_builder_add_object(b, SPA_TYPE_OBJECT_Props, SPA_PARAM_Props,
SPA_PROP_mute, SPA_POD_Bool(vol->mute),
SPA_PROP_channelVolumes, SPA_POD_Array(sizeof(float),
SPA_TYPE_Float, vol->n_volumes, vol->volumes));
}
static void parse_props(struct impl *impl, const struct spa_pod *param,
struct pw_filter *filter, struct volume *vol)
{
struct spa_pod_object *obj = (struct spa_pod_object *) param;
struct spa_pod_prop *prop;
uint8_t buffer[1024];
struct spa_pod_builder b;
const struct spa_pod *params[1];
SPA_POD_OBJECT_FOREACH(obj, prop) {
switch (prop->key) {
case SPA_PROP_mute:
{
bool mute;
if (spa_pod_get_bool(&prop->value, &mute) == 0)
vol->mute = mute;
break;
}
case SPA_PROP_channelVolumes:
{
uint32_t n;
float vols[SPA_AUDIO_MAX_CHANNELS];
if ((n = spa_pod_copy_array(&prop->value, SPA_TYPE_Float,
vols, SPA_AUDIO_MAX_CHANNELS)) > 0) {
vol->n_volumes = n;
for (n = 0; n < vol->n_volumes; n++)
vol->volumes[n] = vols[n];
}
break;
}
default:
break;
}
}
spa_pod_builder_init(&b, buffer, sizeof(buffer));
params[0] = make_props_param(&b, impl, vol);
pw_filter_update_params(filter, NULL, params, 1);
}
static void sink_param_changed(void *data, void *port_data, uint32_t id,
const struct spa_pod *param)
{
@ -381,9 +461,13 @@ static void sink_param_changed(void *data, void *port_data, uint32_t id,
} else {
switch (id) {
case SPA_PARAM_PortConfig:
pw_log_info("PortConfig");
pw_log_debug("PortConfig");
make_sink_ports(impl);
break;
case SPA_PARAM_Props:
pw_log_debug("Props");
parse_props(impl, param, impl->sink, &impl->sink_volume);
break;
}
}
}
@ -428,7 +512,6 @@ static void make_source_ports(struct impl *impl)
port->jack_port = jack.port_register (impl->client, name,
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
impl->source_ports[i] = port;
if (ports != NULL && ports[i] != NULL) {
@ -453,9 +536,13 @@ static void source_param_changed(void *data, void *port_data, uint32_t id,
} else {
switch (id) {
case SPA_PARAM_PortConfig:
pw_log_info("PortConfig");
pw_log_debug("PortConfig");
make_source_ports(impl);
break;
case SPA_PARAM_Props:
pw_log_debug("Props");
parse_props(impl, param, impl->source, &impl->source_volume);
break;
}
}
}
@ -504,11 +591,14 @@ static int create_filters(struct impl *impl)
&impl->sink_listener,
&sink_events, impl);
reset_volume(&impl->sink_volume, impl->sink_info.channels);
n_params = 0;
params[n_params++] = spa_format_audio_raw_build(&b,
SPA_PARAM_EnumFormat, &impl->sink_info);
params[n_params++] = spa_format_audio_raw_build(&b,
SPA_PARAM_Format, &impl->sink_info);
params[n_params++] = make_props_param(&b, impl, &impl->sink_volume);
if ((res = pw_filter_connect(impl->sink,
PW_FILTER_FLAG_DRIVER |
@ -529,11 +619,14 @@ static int create_filters(struct impl *impl)
&impl->source_listener,
&source_events, impl);
reset_volume(&impl->source_volume, impl->source_info.channels);
n_params = 0;
params[n_params++] = spa_format_audio_raw_build(&b,
SPA_PARAM_EnumFormat, &impl->source_info);
params[n_params++] = spa_format_audio_raw_build(&b,
SPA_PARAM_Format, &impl->source_info);
params[n_params++] = make_props_param(&b, impl, &impl->source_volume);
if ((res = pw_filter_connect(impl->source,
PW_FILTER_FLAG_DRIVER |