mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-11-04 13:30:08 -05:00
Change the behavior with hardware volume controls
When a hardware volume control is given, softvol plugin simply passes the slave PCM without any additional changes.
This commit is contained in:
parent
0732cce6f0
commit
199d207423
1 changed files with 70 additions and 42 deletions
|
|
@ -91,6 +91,11 @@ static unsigned short preset_dB_value[PRESET_RESOLUTION] = {
|
||||||
|
|
||||||
#endif /* DOC_HIDDEN */
|
#endif /* DOC_HIDDEN */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* apply volumue attenuation
|
||||||
|
*
|
||||||
|
* TODO: use SIMD operations
|
||||||
|
*/
|
||||||
static void snd_pcm_softvol_convert(snd_pcm_softvol_t *svol,
|
static void snd_pcm_softvol_convert(snd_pcm_softvol_t *svol,
|
||||||
const snd_pcm_channel_area_t *dst_areas,
|
const snd_pcm_channel_area_t *dst_areas,
|
||||||
snd_pcm_uframes_t dst_offset,
|
snd_pcm_uframes_t dst_offset,
|
||||||
|
|
@ -134,6 +139,11 @@ static void snd_pcm_softvol_convert(snd_pcm_softvol_t *svol,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* get the current volume value from driver
|
||||||
|
*
|
||||||
|
* TODO: mmap support?
|
||||||
|
*/
|
||||||
static unsigned int get_current_volume(snd_pcm_softvol_t *svol)
|
static unsigned int get_current_volume(snd_pcm_softvol_t *svol)
|
||||||
{
|
{
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
|
|
@ -145,17 +155,21 @@ static unsigned int get_current_volume(snd_pcm_softvol_t *svol)
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_pcm_softvol_close(snd_pcm_t *pcm)
|
static void softvol_free(snd_pcm_softvol_t *svol)
|
||||||
{
|
{
|
||||||
snd_pcm_softvol_t *svol = pcm->private_data;
|
|
||||||
int err = 0;
|
|
||||||
if (svol->plug.close_slave)
|
if (svol->plug.close_slave)
|
||||||
err = snd_pcm_close(svol->plug.slave);
|
snd_pcm_close(svol->plug.slave);
|
||||||
if (svol->ctl)
|
if (svol->ctl)
|
||||||
snd_ctl_close(svol->ctl);
|
snd_ctl_close(svol->ctl);
|
||||||
if (svol->dB_value && svol->dB_value != preset_dB_value)
|
if (svol->dB_value && svol->dB_value != preset_dB_value)
|
||||||
free(svol->dB_value);
|
free(svol->dB_value);
|
||||||
free(svol);
|
free(svol);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int snd_pcm_softvol_close(snd_pcm_t *pcm)
|
||||||
|
{
|
||||||
|
snd_pcm_softvol_t *svol = pcm->private_data;
|
||||||
|
softvol_free(svol);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -330,40 +344,42 @@ static void snd_pcm_softvol_dump(snd_pcm_t *pcm, snd_output_t *out)
|
||||||
snd_pcm_dump(svol->plug.slave, out);
|
snd_pcm_dump(svol->plug.slave, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
int snd_ctl_elem_add(snd_ctl_t *ctl, snd_ctl_elem_info_t *info);
|
|
||||||
int snd_ctl_elem_replace(snd_ctl_t *ctl, snd_ctl_elem_info_t *info);
|
|
||||||
|
|
||||||
static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
|
static int add_user_ctl(snd_pcm_softvol_t *svol, snd_ctl_elem_info_t *cinfo)
|
||||||
{
|
{
|
||||||
return snd_ctl_elem_add_integer(svol->ctl, &cinfo->id, 1, 0, svol->max_val, 0);
|
return snd_ctl_elem_add_integer(svol->ctl, &cinfo->id, 1, 0, svol->max_val, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* load and set up user-control
|
||||||
|
* returns 0 if the user-control is found or created,
|
||||||
|
* returns 1 if the control is a hw control,
|
||||||
|
* or a negative error code
|
||||||
|
*/
|
||||||
static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
|
static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
|
||||||
char *ctl_name, snd_ctl_elem_id_t *ctl_id,
|
int ctl_card, snd_ctl_elem_id_t *ctl_id,
|
||||||
double min_dB, int resolution)
|
double min_dB, int resolution)
|
||||||
{
|
{
|
||||||
char tmp_name[32];
|
char tmp_name[32];
|
||||||
snd_pcm_info_t *info;
|
snd_pcm_info_t *info;
|
||||||
snd_ctl_elem_info_t *cinfo;
|
snd_ctl_elem_info_t *cinfo;
|
||||||
int err, card;
|
int err;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (! ctl_name) {
|
if (ctl_card < 0) {
|
||||||
snd_pcm_info_alloca(&info);
|
snd_pcm_info_alloca(&info);
|
||||||
err = snd_pcm_info(pcm, info);
|
err = snd_pcm_info(pcm, info);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
card = snd_pcm_info_get_card(info);
|
ctl_card = snd_pcm_info_get_card(info);
|
||||||
if (card < 0) {
|
if (ctl_card < 0) {
|
||||||
SNDERR("No card for this PCM");
|
SNDERR("No card defined for softvol control");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
sprintf(tmp_name, "hw:%d", card);
|
|
||||||
ctl_name = tmp_name;
|
|
||||||
}
|
}
|
||||||
err = snd_ctl_open(&svol->ctl, ctl_name, 0);
|
sprintf(tmp_name, "hw:%d", ctl_card);
|
||||||
|
err = snd_ctl_open(&svol->ctl, tmp_name, 0);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
SNDERR("Cannot open CTL %s", ctl_name);
|
SNDERR("Cannot open CTL %s", tmp_name);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -375,7 +391,7 @@ static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
|
||||||
snd_ctl_elem_info_set_id(cinfo, ctl_id);
|
snd_ctl_elem_info_set_id(cinfo, ctl_id);
|
||||||
if ((err = snd_ctl_elem_info(svol->ctl, cinfo)) < 0) {
|
if ((err = snd_ctl_elem_info(svol->ctl, cinfo)) < 0) {
|
||||||
if (err != -ENOENT) {
|
if (err != -ENOENT) {
|
||||||
SNDERR("Cannot get info for CTL %s", ctl_name);
|
SNDERR("Cannot get info for CTL %s", tmp_name);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = add_user_ctl(svol, cinfo);
|
err = add_user_ctl(svol, cinfo);
|
||||||
|
|
@ -384,14 +400,14 @@ static int softvol_load_control(snd_pcm_t *pcm, snd_pcm_softvol_t *svol,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (cinfo->type != SND_CTL_ELEM_TYPE_INTEGER ||
|
if (! (cinfo->access & SNDRV_CTL_ELEM_ACCESS_USER)) {
|
||||||
|
/* hardware control exists */
|
||||||
|
return 1; /* notify */
|
||||||
|
|
||||||
|
} else if (cinfo->type != SND_CTL_ELEM_TYPE_INTEGER ||
|
||||||
cinfo->count != 1 ||
|
cinfo->count != 1 ||
|
||||||
cinfo->value.integer.min != 0 ||
|
cinfo->value.integer.min != 0 ||
|
||||||
cinfo->value.integer.max != resolution - 1) {
|
cinfo->value.integer.max != resolution - 1) {
|
||||||
if (! (cinfo->access & SNDRV_CTL_ELEM_ACCESS_USER)) {
|
|
||||||
SNDERR("Invalid control");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
snd_ctl_elem_remove(svol->ctl, &cinfo->id);
|
snd_ctl_elem_remove(svol->ctl, &cinfo->id);
|
||||||
err = add_user_ctl(svol, cinfo);
|
err = add_user_ctl(svol, cinfo);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
|
|
@ -446,7 +462,7 @@ static snd_pcm_ops_t snd_pcm_softvol_ops = {
|
||||||
* \param pcmp Returns created PCM handle
|
* \param pcmp Returns created PCM handle
|
||||||
* \param name Name of PCM
|
* \param name Name of PCM
|
||||||
* \param sformat Slave format
|
* \param sformat Slave format
|
||||||
* \param ctl_id Control ID
|
* \param card card index of the control
|
||||||
* \param min_dB minimal dB value
|
* \param min_dB minimal dB value
|
||||||
* \param resolution resolution of control
|
* \param resolution resolution of control
|
||||||
* \param slave Slave PCM handle
|
* \param slave Slave PCM handle
|
||||||
|
|
@ -458,7 +474,7 @@ static snd_pcm_ops_t snd_pcm_softvol_ops = {
|
||||||
*/
|
*/
|
||||||
int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
|
int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_pcm_format_t sformat,
|
snd_pcm_format_t sformat,
|
||||||
char *ctl_name, snd_ctl_elem_id_t *ctl_id,
|
int ctl_card, snd_ctl_elem_id_t *ctl_id,
|
||||||
double min_dB, int resolution,
|
double min_dB, int resolution,
|
||||||
snd_pcm_t *slave, int close_slave)
|
snd_pcm_t *slave, int close_slave)
|
||||||
{
|
{
|
||||||
|
|
@ -471,6 +487,18 @@ int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
|
||||||
svol = calloc(1, sizeof(*svol));
|
svol = calloc(1, sizeof(*svol));
|
||||||
if (! svol)
|
if (! svol)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
err = softvol_load_control(slave, svol, ctl_card, ctl_id, min_dB, resolution);
|
||||||
|
if (err < 0) {
|
||||||
|
softvol_free(svol);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (err > 0) { /* hardware control - no need for softvol! */
|
||||||
|
softvol_free(svol);
|
||||||
|
*pcmp = slave; /* just pass the slave */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* do softvol */
|
||||||
snd_pcm_plugin_init(&svol->plug);
|
snd_pcm_plugin_init(&svol->plug);
|
||||||
svol->sformat = sformat;
|
svol->sformat = sformat;
|
||||||
svol->plug.read = snd_pcm_softvol_read_areas;
|
svol->plug.read = snd_pcm_softvol_read_areas;
|
||||||
|
|
@ -482,7 +510,7 @@ int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
|
||||||
|
|
||||||
err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
|
err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTVOL, name, slave->stream, slave->mode);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
free(svol);
|
softvol_free(svol);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
pcm->ops = &snd_pcm_softvol_ops;
|
pcm->ops = &snd_pcm_softvol_ops;
|
||||||
|
|
@ -492,17 +520,15 @@ int snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
|
||||||
pcm->poll_events = slave->poll_events;
|
pcm->poll_events = slave->poll_events;
|
||||||
snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
|
snd_pcm_set_hw_ptr(pcm, &svol->plug.hw_ptr, -1, 0);
|
||||||
snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
|
snd_pcm_set_appl_ptr(pcm, &svol->plug.appl_ptr, -1, 0);
|
||||||
err = softvol_load_control(pcm, svol, ctl_name, ctl_id, min_dB, resolution);
|
|
||||||
if (err < 0) {
|
|
||||||
snd_pcm_close(pcm);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
*pcmp = pcm;
|
*pcmp = pcm;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, char **ctl_name)
|
/*
|
||||||
|
* parse card index and id for the softvol control
|
||||||
|
*/
|
||||||
|
static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp)
|
||||||
{
|
{
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
int iface = SND_CTL_ELEM_IFACE_MIXER;
|
int iface = SND_CTL_ELEM_IFACE_MIXER;
|
||||||
|
|
@ -512,7 +538,7 @@ static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, char
|
||||||
long subdevice = -1;
|
long subdevice = -1;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
*ctl_name = NULL;
|
*cardp = -1;
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
const char *id;
|
const char *id;
|
||||||
|
|
@ -521,14 +547,12 @@ static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, char
|
||||||
if (strcmp(id, "comment") == 0)
|
if (strcmp(id, "comment") == 0)
|
||||||
continue;
|
continue;
|
||||||
if (strcmp(id, "card") == 0) {
|
if (strcmp(id, "card") == 0) {
|
||||||
const char *ptr;
|
long v;
|
||||||
if ((err = snd_config_get_string(n, &ptr)) < 0) {
|
if ((err = snd_config_get_integer(n, &v)) < 0) {
|
||||||
SNDERR("field %s is not a string", id);
|
SNDERR("field %s is not an integer", id);
|
||||||
goto _err;
|
goto _err;
|
||||||
}
|
}
|
||||||
if (*ctl_name)
|
*cardp = v;
|
||||||
free(*ctl_name);
|
|
||||||
*ctl_name = strdup(ptr);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
|
if (strcmp(id, "iface") == 0 || strcmp(id, "interface") == 0) {
|
||||||
|
|
@ -604,6 +628,10 @@ static int parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, char
|
||||||
This plugin applies the software volume attenuation.
|
This plugin applies the software volume attenuation.
|
||||||
The format, rate and channels must match for both of source and destination.
|
The format, rate and channels must match for both of source and destination.
|
||||||
|
|
||||||
|
If the control already exists and it's a system control (i.e. no
|
||||||
|
user-defined control), the plugin simply passes its slave without
|
||||||
|
any changes.
|
||||||
|
|
||||||
\code
|
\code
|
||||||
pcm.name {
|
pcm.name {
|
||||||
type softvol # Soft Volume conversion PCM
|
type softvol # Soft Volume conversion PCM
|
||||||
|
|
@ -663,7 +691,7 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
|
||||||
snd_ctl_elem_id_t *ctl_id;
|
snd_ctl_elem_id_t *ctl_id;
|
||||||
int resolution = PRESET_RESOLUTION;
|
int resolution = PRESET_RESOLUTION;
|
||||||
double min_dB = PRESET_MIN_DB;
|
double min_dB = PRESET_MIN_DB;
|
||||||
char *ctl_name;
|
int card = -1;
|
||||||
|
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
snd_config_t *n = snd_config_iterator_entry(i);
|
||||||
|
|
@ -731,11 +759,11 @@ int _snd_pcm_softvol_open(snd_pcm_t **pcmp, const char *name,
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
snd_ctl_elem_id_alloca(&ctl_id);
|
snd_ctl_elem_id_alloca(&ctl_id);
|
||||||
if ((err = parse_control_id(control, ctl_id, &ctl_name)) < 0) {
|
if ((err = parse_control_id(control, ctl_id, &card)) < 0) {
|
||||||
snd_pcm_close(spcm);
|
snd_pcm_close(spcm);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
err = snd_pcm_softvol_open(pcmp, name, sformat, ctl_name, ctl_id, min_dB, resolution, spcm, 1);
|
err = snd_pcm_softvol_open(pcmp, name, sformat, card, ctl_id, min_dB, resolution, spcm, 1);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
snd_pcm_close(spcm);
|
snd_pcm_close(spcm);
|
||||||
return err;
|
return err;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue