mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-12-16 08:56:45 -05:00
acp: add support for soft volume
Add a set_soft_volume/mute event. If we can't configure the hardware volume completely, notify the remainder with the soft_volume/mute events.
This commit is contained in:
parent
4db6e0e2b3
commit
d80d593271
4 changed files with 87 additions and 10 deletions
|
|
@ -30,6 +30,8 @@ int _acp_log_level = 1;
|
||||||
acp_log_func _acp_log_func;
|
acp_log_func _acp_log_func;
|
||||||
void *_acp_log_data;
|
void *_acp_log_data;
|
||||||
|
|
||||||
|
#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */
|
||||||
|
|
||||||
static void profile_free(void *data)
|
static void profile_free(void *data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -53,6 +55,7 @@ static void init_device(pa_card *impl, pa_alsa_device *dev, pa_alsa_direction_t
|
||||||
dev->device.format.format_mask = m->sample_spec.format;
|
dev->device.format.format_mask = m->sample_spec.format;
|
||||||
dev->device.format.rate_mask = m->sample_spec.rate;
|
dev->device.format.rate_mask = m->sample_spec.rate;
|
||||||
dev->device.format.channels = m->channel_map.channels;
|
dev->device.format.channels = m->channel_map.channels;
|
||||||
|
pa_cvolume_set(&dev->real_volume, m->channel_map.channels, PA_VOLUME_NORM);
|
||||||
for (i = 0; i < m->channel_map.channels; i++)
|
for (i = 0; i < m->channel_map.channels; i++)
|
||||||
dev->device.format.map[i]= m->channel_map.map[i];
|
dev->device.format.map[i]= m->channel_map.map[i];
|
||||||
dev->direction = direction;
|
dev->direction = direction;
|
||||||
|
|
@ -724,6 +727,7 @@ static int read_volume(pa_alsa_device *dev)
|
||||||
|
|
||||||
static void set_volume(pa_alsa_device *dev, const pa_cvolume *v)
|
static void set_volume(pa_alsa_device *dev, const pa_cvolume *v)
|
||||||
{
|
{
|
||||||
|
pa_card *impl = dev->card;
|
||||||
pa_cvolume r;
|
pa_cvolume r;
|
||||||
|
|
||||||
dev->real_volume = *v;
|
dev->real_volume = *v;
|
||||||
|
|
@ -734,6 +738,46 @@ static void set_volume(pa_alsa_device *dev, const pa_cvolume *v)
|
||||||
if (pa_alsa_path_set_volume(dev->mixer_path, dev->mixer_handle, &dev->mapping->channel_map,
|
if (pa_alsa_path_set_volume(dev->mixer_path, dev->mixer_handle, &dev->mapping->channel_map,
|
||||||
&r, false, true) < 0)
|
&r, false, true) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* Shift down by the base volume, so that 0dB becomes maximum volume */
|
||||||
|
pa_sw_cvolume_multiply_scalar(&r, &r, dev->base_volume);
|
||||||
|
|
||||||
|
dev->hardware_volume = r;
|
||||||
|
|
||||||
|
if (dev->mixer_path->has_dB) {
|
||||||
|
pa_cvolume new_soft_volume;
|
||||||
|
bool accurate_enough;
|
||||||
|
|
||||||
|
/* Match exactly what the user requested by software */
|
||||||
|
pa_sw_cvolume_divide(&new_soft_volume, &dev->real_volume, &dev->hardware_volume);
|
||||||
|
|
||||||
|
/* If the adjustment to do in software is only minimal we
|
||||||
|
* can skip it. That saves us CPU at the expense of a bit of
|
||||||
|
* accuracy */
|
||||||
|
accurate_enough =
|
||||||
|
(pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) &&
|
||||||
|
(pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY));
|
||||||
|
|
||||||
|
pa_log_debug("Requested volume: %d", pa_cvolume_max(&dev->real_volume));
|
||||||
|
pa_log_debug("Got hardware volume: %d", pa_cvolume_max(&dev->hardware_volume));
|
||||||
|
pa_log_debug("Calculated software volume: %d (accurate-enough=%s)",
|
||||||
|
pa_cvolume_max(&new_soft_volume),
|
||||||
|
pa_yes_no(accurate_enough));
|
||||||
|
|
||||||
|
if (!accurate_enough && impl->events && impl->events->set_soft_volume) {
|
||||||
|
uint32_t i, n_volumes = new_soft_volume.channels;
|
||||||
|
float volumes[n_volumes];
|
||||||
|
for (i = 0; i < n_volumes; i++)
|
||||||
|
volumes[i] = ((float)new_soft_volume.values[i]) / PA_VOLUME_NORM;
|
||||||
|
impl->events->set_soft_volume(impl->user_data, &dev->device, volumes, n_volumes);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pa_log_debug("Wrote hardware volume: %d", pa_cvolume_max(&r));
|
||||||
|
/* We can't match exactly what the user requested, hence let's
|
||||||
|
* at least tell the user about it */
|
||||||
|
dev->real_volume = r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int read_mute(pa_alsa_device *dev)
|
static int read_mute(pa_alsa_device *dev)
|
||||||
|
|
@ -1302,15 +1346,23 @@ int acp_device_set_port(struct acp_device *dev, uint32_t port_index)
|
||||||
int acp_device_set_volume(struct acp_device *dev, const float *volume, uint32_t n_volume)
|
int acp_device_set_volume(struct acp_device *dev, const float *volume, uint32_t n_volume)
|
||||||
{
|
{
|
||||||
pa_alsa_device *d = (pa_alsa_device*)dev;
|
pa_alsa_device *d = (pa_alsa_device*)dev;
|
||||||
pa_cvolume v;
|
pa_card *impl = d->card;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
v.channels = d->mapping->channel_map.channels;
|
pa_cvolume v;
|
||||||
if (n_volume == 0)
|
if (n_volume == 0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
v.channels = d->mapping->channel_map.channels;
|
||||||
for (i = 0; i < v.channels; i++)
|
for (i = 0; i < v.channels; i++)
|
||||||
v.values[i] = volume[i % n_volume] * PA_VOLUME_NORM;
|
v.values[i] = volume[i % n_volume] * PA_VOLUME_NORM;
|
||||||
if (d->set_volume)
|
|
||||||
|
if (d->set_volume) {
|
||||||
d->set_volume(d, &v);
|
d->set_volume(d, &v);
|
||||||
|
} else {
|
||||||
|
d->real_volume = v;
|
||||||
|
if (impl->events && impl->events->set_soft_volume)
|
||||||
|
impl->events->set_soft_volume(impl->user_data, dev, volume, n_volume);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1330,8 +1382,16 @@ int acp_device_get_volume(struct acp_device *dev, float *volume, uint32_t n_volu
|
||||||
int acp_device_set_mute(struct acp_device *dev, bool mute)
|
int acp_device_set_mute(struct acp_device *dev, bool mute)
|
||||||
{
|
{
|
||||||
pa_alsa_device *d = (pa_alsa_device*)dev;
|
pa_alsa_device *d = (pa_alsa_device*)dev;
|
||||||
if (d->set_mute)
|
pa_card *impl = d->card;
|
||||||
|
if (d->muted == mute)
|
||||||
|
return 0;
|
||||||
|
if (d->set_mute) {
|
||||||
d->set_mute(d, mute);
|
d->set_mute(d, mute);
|
||||||
|
} else {
|
||||||
|
d->muted = mute;
|
||||||
|
if (impl->events && impl->events->set_soft_mute)
|
||||||
|
impl->events->set_soft_mute(impl->user_data, dev, mute);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1341,6 +1401,7 @@ int acp_device_get_mute(struct acp_device *dev, bool *mute)
|
||||||
*mute = d->muted;
|
*mute = d->muted;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void acp_set_log_func(acp_log_func func, void *data)
|
void acp_set_log_func(acp_log_func func, void *data)
|
||||||
{
|
{
|
||||||
_acp_log_func = func;
|
_acp_log_func = func;
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,10 @@ struct acp_card_events {
|
||||||
|
|
||||||
void (*volume_changed) (void *data, struct acp_device *dev);
|
void (*volume_changed) (void *data, struct acp_device *dev);
|
||||||
void (*mute_changed) (void *data, struct acp_device *dev);
|
void (*mute_changed) (void *data, struct acp_device *dev);
|
||||||
|
|
||||||
|
void (*set_soft_volume) (void *data, struct acp_device *dev,
|
||||||
|
const float *volume, uint32_t n_volume);
|
||||||
|
void (*set_soft_mute) (void *data, struct acp_device *dev, bool mute);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct acp_port {
|
struct acp_port {
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,7 @@ struct pa_alsa_device {
|
||||||
unsigned muted:1;
|
unsigned muted:1;
|
||||||
unsigned decibel_volume:1;
|
unsigned decibel_volume:1;
|
||||||
pa_cvolume real_volume;
|
pa_cvolume real_volume;
|
||||||
|
pa_cvolume hardware_volume;
|
||||||
|
|
||||||
pa_volume_t base_volume;
|
pa_volume_t base_volume;
|
||||||
unsigned n_volume_steps;
|
unsigned n_volume_steps;
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,17 @@ static inline pa_volume_t pa_cvolume_max(const pa_cvolume *a)
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline pa_volume_t pa_cvolume_min(const pa_cvolume *a)
|
||||||
|
{
|
||||||
|
pa_volume_t m = PA_VOLUME_MAX;
|
||||||
|
unsigned c;
|
||||||
|
for (c = 0; c < a->channels; c++)
|
||||||
|
if (a->values[c] < m)
|
||||||
|
m = a->values[c];
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue