mirror of
https://gitlab.freedesktop.org/pipewire/pipewire
synced 2024-07-21 10:16:14 +00:00
alsa: add option to change the volume-method
Add option to set linear or cubic volume, cubic is now the default.
This commit is contained in:
parent
c7239b1f2a
commit
ee55df9f9e
|
@ -37,26 +37,11 @@
|
|||
PW_LOG_TOPIC_STATIC(alsa_log_topic, "alsa.ctl");
|
||||
#define PW_LOG_TOPIC_DEFAULT alsa_log_topic
|
||||
|
||||
#define DEFAULT_VOLUME_METHOD "cubic"
|
||||
|
||||
#define VOLUME_MIN ((uint32_t) 0U)
|
||||
#define VOLUME_MAX ((uint32_t) 0x10000U)
|
||||
|
||||
static inline uint32_t volume_from_linear(float vol)
|
||||
{
|
||||
uint32_t v;
|
||||
if (vol <= 0.0f)
|
||||
v = VOLUME_MIN;
|
||||
else
|
||||
v = SPA_CLAMP((uint64_t) lround(cbrt(vol) * VOLUME_MAX),
|
||||
VOLUME_MIN, VOLUME_MAX);
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline float volume_to_linear(uint32_t vol)
|
||||
{
|
||||
float v = ((float)vol) / VOLUME_MAX;
|
||||
return v * v * v;
|
||||
}
|
||||
|
||||
struct volume {
|
||||
uint32_t channels;
|
||||
long values[SPA_AUDIO_MAX_CHANNELS];
|
||||
|
@ -94,6 +79,9 @@ typedef struct {
|
|||
struct volume source_volume;
|
||||
|
||||
int subscribed;
|
||||
#define VOLUME_METHOD_LINEAR (0)
|
||||
#define VOLUME_METHOD_CUBIC (1)
|
||||
int volume_method;
|
||||
|
||||
#define UPDATE_SINK_VOL (1<<0)
|
||||
#define UPDATE_SINK_MUTE (1<<1)
|
||||
|
@ -104,6 +92,32 @@ typedef struct {
|
|||
struct spa_list globals;
|
||||
} snd_ctl_pipewire_t;
|
||||
|
||||
static inline uint32_t volume_from_linear(float vol, int method)
|
||||
{
|
||||
if (vol <= 0.0f)
|
||||
vol = 0.0f;
|
||||
|
||||
switch (method) {
|
||||
case VOLUME_METHOD_CUBIC:
|
||||
vol = cbrtf(vol);
|
||||
break;
|
||||
}
|
||||
return SPA_CLAMP((uint64_t)lroundf(vol * VOLUME_MAX),
|
||||
VOLUME_MIN, VOLUME_MAX);
|
||||
}
|
||||
|
||||
static inline float volume_to_linear(uint32_t vol, int method)
|
||||
{
|
||||
float v = ((float)vol) / VOLUME_MAX;
|
||||
|
||||
switch (method) {
|
||||
case VOLUME_METHOD_CUBIC:
|
||||
v = v * v * v;
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
struct global;
|
||||
|
||||
struct global_info {
|
||||
|
@ -496,7 +510,8 @@ finish:
|
|||
return err;
|
||||
}
|
||||
|
||||
static struct spa_pod *build_volume_mute(struct spa_pod_builder *b, struct volume *volume, int *mute)
|
||||
static struct spa_pod *build_volume_mute(struct spa_pod_builder *b, struct volume *volume,
|
||||
int *mute, int volume_method)
|
||||
{
|
||||
struct spa_pod_frame f[1];
|
||||
|
||||
|
@ -508,7 +523,7 @@ static struct spa_pod *build_volume_mute(struct spa_pod_builder *b, struct volum
|
|||
|
||||
n_volumes = volume->channels;
|
||||
for (i = 0; i < n_volumes; i++)
|
||||
volumes[i] = volume_to_linear(volume->values[i]);
|
||||
volumes[i] = volume_to_linear(volume->values[i], volume_method);
|
||||
|
||||
spa_pod_builder_prop(b, SPA_PROP_channelVolumes, 0);
|
||||
spa_pod_builder_array(b, sizeof(float),
|
||||
|
@ -556,7 +571,7 @@ static int set_volume_mute(snd_ctl_pipewire_t *ctl, const char *name, struct vol
|
|||
0);
|
||||
|
||||
spa_pod_builder_prop(&b, SPA_PARAM_ROUTE_props, 0);
|
||||
build_volume_mute(&b, volume, mute);
|
||||
build_volume_mute(&b, volume, mute, ctl->volume_method);
|
||||
param = spa_pod_builder_pop(&b, &f[0]);
|
||||
|
||||
pw_log_debug("set device %d mute/volume for node %d", dg->id, g->id);
|
||||
|
@ -566,7 +581,7 @@ static int set_volume_mute(snd_ctl_pipewire_t *ctl, const char *name, struct vol
|
|||
if (!SPA_FLAG_IS_SET(g->permissions, PW_PERM_W | PW_PERM_X))
|
||||
return -EPERM;
|
||||
|
||||
param = build_volume_mute(&b, volume, mute);
|
||||
param = build_volume_mute(&b, volume, mute, ctl->volume_method);
|
||||
|
||||
pw_log_debug("set node %d mute/volume", g->id);
|
||||
pw_node_set_param((struct pw_node*)g->proxy,
|
||||
|
@ -837,6 +852,7 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi
|
|||
{
|
||||
struct spa_pod_prop *prop;
|
||||
struct spa_pod_object *obj = (struct spa_pod_object *) param;
|
||||
snd_ctl_pipewire_t *ctl = g->ctl;
|
||||
|
||||
SPA_POD_OBJECT_FOREACH(obj, prop) {
|
||||
switch (prop->key) {
|
||||
|
@ -862,7 +878,8 @@ static void parse_props(struct global *g, const struct spa_pod *param, bool devi
|
|||
|
||||
g->node.channel_volume.channels = n_volumes;
|
||||
for (i = 0; i < n_volumes; i++)
|
||||
g->node.channel_volume.values[i] = volume_from_linear(volumes[i]);
|
||||
g->node.channel_volume.values[i] =
|
||||
volume_from_linear(volumes[i], ctl->volume_method);
|
||||
|
||||
SPA_FLAG_UPDATE(g->node.flags, NODE_FLAG_DEVICE_VOLUME, device);
|
||||
pw_log_debug("update node %d channelVolumes", g->id);
|
||||
|
@ -1405,6 +1422,18 @@ SND_CTL_PLUGIN_DEFINE_FUNC(pipewire)
|
|||
if (str != NULL && str[0] != '\0')
|
||||
pw_properties_set(ctl->props, PW_KEY_REMOTE_NAME, str);
|
||||
|
||||
if ((str = pw_properties_get(ctl->props, "alsa.volume-method")) == NULL)
|
||||
str = DEFAULT_VOLUME_METHOD;
|
||||
|
||||
if (spa_streq(str, "cubic"))
|
||||
ctl->volume_method = VOLUME_METHOD_CUBIC;
|
||||
else if (spa_streq(str, "linear"))
|
||||
ctl->volume_method = VOLUME_METHOD_LINEAR;
|
||||
else {
|
||||
ctl->volume_method = VOLUME_METHOD_CUBIC;
|
||||
SNDERR("unknown alsa.volume-method %s, using cubic", str);
|
||||
}
|
||||
|
||||
if ((err = pw_thread_loop_start(ctl->mainloop)) < 0)
|
||||
goto error;
|
||||
|
||||
|
|
|
@ -92,3 +92,12 @@ stream.properties = {
|
|||
#channelmix.hilbert-taps = 0
|
||||
#dither.noise = 0
|
||||
}
|
||||
|
||||
alsa.properties = {
|
||||
#alsa.format = 0
|
||||
#alsa.rate = 0
|
||||
#alsa.channels = 0
|
||||
#alsa.period-bytes = 0
|
||||
#alsa.buffer-bytes = 0
|
||||
#alsa.volume-method = cubic # linear, cubic
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue