mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-31 22:25:35 -04:00
Fix buffer size handling of direct plugins
Introduce "max_periods" option to specify the max number of periods per buffer to each plugin. - When max_periods = -1, the fixed buffer size as the slave size is used (old behavior). - When max_periods = 0 (or 1), the number of periods is variable between 2 and the slave buffer size. - When max_periods greater than 2 is given, it specifies the max periods of that pcm explicitly. When no option is given in the PCM defintion, the value "defaults.pcm.dmix_max_periods" is referred as default. The default value is 0, as defined in alsa.conf. You can override this in ~/.asoundrc or /etc/asound.conf as you like.
This commit is contained in:
parent
33d69ef33b
commit
1128efc7d4
7 changed files with 41 additions and 56 deletions
|
|
@ -57,7 +57,7 @@ defaults.pcm.nonblock 1
|
||||||
defaults.pcm.ipc_key 5678293
|
defaults.pcm.ipc_key 5678293
|
||||||
defaults.pcm.ipc_gid audio
|
defaults.pcm.ipc_gid audio
|
||||||
defaults.pcm.ipc_perm 0660
|
defaults.pcm.ipc_perm 0660
|
||||||
defaults.pcm.dmix_variable_buffer true
|
defaults.pcm.dmix_max_periods 0
|
||||||
defaults.pcm.front.card defaults.pcm.card
|
defaults.pcm.front.card defaults.pcm.card
|
||||||
defaults.pcm.front.device defaults.pcm.device
|
defaults.pcm.front.device defaults.pcm.device
|
||||||
defaults.pcm.rear.card defaults.pcm.card
|
defaults.pcm.rear.card defaults.pcm.card
|
||||||
|
|
|
||||||
|
|
@ -92,9 +92,4 @@ pcm.!dmix {
|
||||||
default 16
|
default 16
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# Allow apps different buffer sizes
|
|
||||||
variable_buffer_size {
|
|
||||||
@func refer
|
|
||||||
name defaults.pcm.dmix_variable_buffer
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -697,7 +697,7 @@ int snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
||||||
&dshare->shmptr->hw.period_time);
|
&dshare->shmptr->hw.period_time);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
if (! dshare->variable_buffer_size) {
|
if (dshare->max_periods < 0) {
|
||||||
err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
|
err = hw_param_interval_refine_one(params, SND_PCM_HW_PARAM_BUFFER_SIZE,
|
||||||
&dshare->shmptr->hw.buffer_size);
|
&dshare->shmptr->hw.buffer_size);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
@ -711,13 +711,13 @@ int snd_pcm_direct_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
||||||
(1<<SND_PCM_HW_PARAM_BUFFER_SIZE)|
|
(1<<SND_PCM_HW_PARAM_BUFFER_SIZE)|
|
||||||
(1<<SND_PCM_HW_PARAM_BUFFER_TIME))) {
|
(1<<SND_PCM_HW_PARAM_BUFFER_TIME))) {
|
||||||
int changed;
|
int changed;
|
||||||
|
unsigned int max_periods = dshare->max_periods;
|
||||||
|
if (max_periods < 2)
|
||||||
|
max_periods = dshare->slave_buffer_size / dshare->slave_period_size;
|
||||||
do {
|
do {
|
||||||
changed = 0;
|
changed = 0;
|
||||||
/* Set min/max size to [2:1024] since INT_MAX as the
|
|
||||||
* upper-limit results in a too big buffer on some apps.
|
|
||||||
*/
|
|
||||||
err = hw_param_interval_refine_minmax(params, SND_PCM_HW_PARAM_PERIODS,
|
err = hw_param_interval_refine_minmax(params, SND_PCM_HW_PARAM_PERIODS,
|
||||||
2, 1024);
|
2, max_periods);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
changed |= err;
|
changed |= err;
|
||||||
|
|
@ -1382,17 +1382,19 @@ static int _snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root,
|
||||||
return (direction << 1) + (device << 2) + (subdevice << 8) + (card << 12);
|
return (direction << 1) + (device << 2) + (subdevice << 8) + (card << 12);
|
||||||
}
|
}
|
||||||
|
|
||||||
int snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root,
|
static int snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root,
|
||||||
snd_config_t *sconf,
|
snd_config_t *sconf,
|
||||||
int direction)
|
int direction)
|
||||||
{
|
{
|
||||||
return _snd_pcm_direct_get_slave_ipc_offset(root, sconf, direction, 0);
|
return _snd_pcm_direct_get_slave_ipc_offset(root, sconf, direction, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int snd_pcm_direct_parse_open_conf(snd_config_t *conf, struct snd_pcm_direct_open_conf *rec)
|
int snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf,
|
||||||
|
int stream, struct snd_pcm_direct_open_conf *rec)
|
||||||
{
|
{
|
||||||
snd_config_iterator_t i, next;
|
snd_config_iterator_t i, next;
|
||||||
int ipc_key_add_uid = 0;
|
int ipc_key_add_uid = 0;
|
||||||
|
snd_config_t *n;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
rec->slave = NULL;
|
rec->slave = NULL;
|
||||||
|
|
@ -1401,10 +1403,18 @@ int snd_pcm_direct_parse_open_conf(snd_config_t *conf, struct snd_pcm_direct_ope
|
||||||
rec->ipc_perm = 0600;
|
rec->ipc_perm = 0600;
|
||||||
rec->ipc_gid = -1;
|
rec->ipc_gid = -1;
|
||||||
rec->slowptr = 0;
|
rec->slowptr = 0;
|
||||||
rec->variable_buffer_size = 0;
|
rec->max_periods = 0;
|
||||||
|
|
||||||
|
/* read defaults */
|
||||||
|
if (snd_config_search(root, "defaults.pcm.dmix_max_periods", &n) >= 0) {
|
||||||
|
long val;
|
||||||
|
err = snd_config_get_integer(n, &val);
|
||||||
|
if (err >= 0)
|
||||||
|
rec->max_periods = val;
|
||||||
|
}
|
||||||
|
|
||||||
snd_config_for_each(i, next, conf) {
|
snd_config_for_each(i, next, conf) {
|
||||||
snd_config_t *n = snd_config_iterator_entry(i);
|
n = snd_config_iterator_entry(i);
|
||||||
const char *id;
|
const char *id;
|
||||||
if (snd_config_get_id(n, &id) < 0)
|
if (snd_config_get_id(n, &id) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1488,11 +1498,12 @@ int snd_pcm_direct_parse_open_conf(snd_config_t *conf, struct snd_pcm_direct_ope
|
||||||
rec->slowptr = err;
|
rec->slowptr = err;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strcmp(id, "variable_buffer_size") == 0) {
|
if (strcmp(id, "max_periods") == 0) {
|
||||||
err = snd_config_get_bool(n);
|
long val;
|
||||||
|
err = snd_config_get_integer(n, &val);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
rec->variable_buffer_size = err;
|
rec->max_periods = val;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
SNDERR("Unknown field %s", id);
|
SNDERR("Unknown field %s", id);
|
||||||
|
|
@ -1502,11 +1513,16 @@ int snd_pcm_direct_parse_open_conf(snd_config_t *conf, struct snd_pcm_direct_ope
|
||||||
SNDERR("slave is not defined");
|
SNDERR("slave is not defined");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (ipc_key_add_uid)
|
|
||||||
rec->ipc_key += getuid();
|
|
||||||
if (!rec->ipc_key) {
|
if (!rec->ipc_key) {
|
||||||
SNDERR("Unique IPC key is not defined");
|
SNDERR("Unique IPC key is not defined");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
if (ipc_key_add_uid)
|
||||||
|
rec->ipc_key += getuid();
|
||||||
|
err = snd_pcm_direct_get_slave_ipc_offset(root, rec->slave, stream);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
rec->ipc_key += err;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,7 @@ struct snd_pcm_direct {
|
||||||
snd_timer_t *timer; /* timer used as poll_fd */
|
snd_timer_t *timer; /* timer used as poll_fd */
|
||||||
int interleaved; /* we have interleaved buffer */
|
int interleaved; /* we have interleaved buffer */
|
||||||
int slowptr; /* use slow but more precise ptr updates */
|
int slowptr; /* use slow but more precise ptr updates */
|
||||||
int variable_buffer_size; /* allow the variable buffer size */
|
int max_periods; /* max periods (-1 = fixed periods, 0 = max buffer size) */
|
||||||
unsigned int channels; /* client's channels */
|
unsigned int channels; /* client's channels */
|
||||||
unsigned int *bindings;
|
unsigned int *bindings;
|
||||||
union {
|
union {
|
||||||
|
|
@ -187,7 +187,6 @@ int snd_pcm_direct_timer_stop(snd_pcm_direct_t *dmix);
|
||||||
void snd_pcm_direct_clear_timer_queue(snd_pcm_direct_t *dmix);
|
void snd_pcm_direct_clear_timer_queue(snd_pcm_direct_t *dmix);
|
||||||
int snd_pcm_direct_set_timer_params(snd_pcm_direct_t *dmix);
|
int snd_pcm_direct_set_timer_params(snd_pcm_direct_t *dmix);
|
||||||
int snd_pcm_direct_open_secondary_client(snd_pcm_t **spcmp, snd_pcm_direct_t *dmix, const char *client_name);
|
int snd_pcm_direct_open_secondary_client(snd_pcm_t **spcmp, snd_pcm_direct_t *dmix, const char *client_name);
|
||||||
int snd_pcm_direct_get_slave_ipc_offset(snd_config_t *root, snd_config_t *sconf, int direction);
|
|
||||||
|
|
||||||
int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid);
|
int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid);
|
||||||
struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm);
|
struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm);
|
||||||
|
|
@ -197,9 +196,9 @@ struct snd_pcm_direct_open_conf {
|
||||||
mode_t ipc_perm;
|
mode_t ipc_perm;
|
||||||
int ipc_gid;
|
int ipc_gid;
|
||||||
int slowptr;
|
int slowptr;
|
||||||
int variable_buffer_size;
|
int max_periods;
|
||||||
snd_config_t *slave;
|
snd_config_t *slave;
|
||||||
snd_config_t *bindings;
|
snd_config_t *bindings;
|
||||||
};
|
};
|
||||||
|
|
||||||
int snd_pcm_direct_parse_open_conf(snd_config_t *conf, struct snd_pcm_direct_open_conf *rec);
|
int snd_pcm_direct_parse_open_conf(snd_config_t *root, snd_config_t *conf, int stream, struct snd_pcm_direct_open_conf *rec);
|
||||||
|
|
|
||||||
|
|
@ -487,8 +487,7 @@ static int snd_pcm_dmix_prepare(snd_pcm_t *pcm)
|
||||||
static void reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
|
static void reset_slave_ptr(snd_pcm_t *pcm, snd_pcm_direct_t *dmix)
|
||||||
{
|
{
|
||||||
dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
|
dmix->slave_appl_ptr = dmix->slave_hw_ptr = *dmix->spcm->hw.ptr;
|
||||||
if (! dmix->variable_buffer_size ||
|
if (pcm->buffer_size > pcm->period_size * 2)
|
||||||
pcm->buffer_size > pcm->period_size * 2)
|
|
||||||
return;
|
return;
|
||||||
/* If we have too litte periods, better to align the start position
|
/* If we have too litte periods, better to align the start position
|
||||||
* to the period boundary so that the interrupt can be handled properly
|
* to the period boundary so that the interrupt can be handled properly
|
||||||
|
|
@ -851,7 +850,7 @@ int snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name,
|
||||||
pcm->private_data = dmix;
|
pcm->private_data = dmix;
|
||||||
dmix->state = SND_PCM_STATE_OPEN;
|
dmix->state = SND_PCM_STATE_OPEN;
|
||||||
dmix->slowptr = opts->slowptr;
|
dmix->slowptr = opts->slowptr;
|
||||||
dmix->variable_buffer_size = opts->variable_buffer_size;
|
dmix->max_periods = opts->max_periods;
|
||||||
dmix->sync_ptr = snd_pcm_dmix_sync_ptr;
|
dmix->sync_ptr = snd_pcm_dmix_sync_ptr;
|
||||||
|
|
||||||
if (first_instance) {
|
if (first_instance) {
|
||||||
|
|
@ -1092,10 +1091,9 @@ int _snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name,
|
||||||
struct slave_params params;
|
struct slave_params params;
|
||||||
struct snd_pcm_direct_open_conf dopen;
|
struct snd_pcm_direct_open_conf dopen;
|
||||||
int bsize, psize;
|
int bsize, psize;
|
||||||
int ipc_offset;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = snd_pcm_direct_parse_open_conf(conf, &dopen);
|
err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
@ -1134,13 +1132,6 @@ int _snd_pcm_dmix_open(snd_pcm_t **pcmp, const char *name,
|
||||||
params.period_size = psize;
|
params.period_size = psize;
|
||||||
params.buffer_size = bsize;
|
params.buffer_size = bsize;
|
||||||
|
|
||||||
ipc_offset = snd_pcm_direct_get_slave_ipc_offset(root, sconf, stream);
|
|
||||||
if (ipc_offset < 0) {
|
|
||||||
snd_config_delete(sconf);
|
|
||||||
return ipc_offset;
|
|
||||||
}
|
|
||||||
dopen.ipc_key += ipc_offset;
|
|
||||||
|
|
||||||
err = snd_pcm_dmix_open(pcmp, name, &dopen, ¶ms,
|
err = snd_pcm_dmix_open(pcmp, name, &dopen, ¶ms,
|
||||||
root, sconf, stream, mode);
|
root, sconf, stream, mode);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
|
||||||
|
|
@ -662,7 +662,7 @@ int snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
|
||||||
pcm->private_data = dshare;
|
pcm->private_data = dshare;
|
||||||
dshare->state = SND_PCM_STATE_OPEN;
|
dshare->state = SND_PCM_STATE_OPEN;
|
||||||
dshare->slowptr = opts->slowptr;
|
dshare->slowptr = opts->slowptr;
|
||||||
dshare->variable_buffer_size = opts->variable_buffer_size;
|
dshare->max_periods = opts->max_periods;
|
||||||
dshare->sync_ptr = snd_pcm_dshare_sync_ptr;
|
dshare->sync_ptr = snd_pcm_dshare_sync_ptr;
|
||||||
|
|
||||||
if (first_instance) {
|
if (first_instance) {
|
||||||
|
|
@ -835,10 +835,9 @@ int _snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
|
||||||
struct slave_params params;
|
struct slave_params params;
|
||||||
struct snd_pcm_direct_open_conf dopen;
|
struct snd_pcm_direct_open_conf dopen;
|
||||||
int bsize, psize;
|
int bsize, psize;
|
||||||
int ipc_offset;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = snd_pcm_direct_parse_open_conf(conf, &dopen);
|
err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
@ -869,13 +868,6 @@ int _snd_pcm_dshare_open(snd_pcm_t **pcmp, const char *name,
|
||||||
params.period_size = psize;
|
params.period_size = psize;
|
||||||
params.buffer_size = bsize;
|
params.buffer_size = bsize;
|
||||||
|
|
||||||
ipc_offset = snd_pcm_direct_get_slave_ipc_offset(root, sconf, stream);
|
|
||||||
if (ipc_offset < 0) {
|
|
||||||
snd_config_delete(sconf);
|
|
||||||
return ipc_offset;
|
|
||||||
}
|
|
||||||
dopen.ipc_key += ipc_offset;
|
|
||||||
|
|
||||||
err = snd_pcm_dshare_open(pcmp, name, &dopen, ¶ms,
|
err = snd_pcm_dshare_open(pcmp, name, &dopen, ¶ms,
|
||||||
root, sconf, stream, mode);
|
root, sconf, stream, mode);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
|
||||||
|
|
@ -544,7 +544,7 @@ int snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
|
||||||
pcm->private_data = dsnoop;
|
pcm->private_data = dsnoop;
|
||||||
dsnoop->state = SND_PCM_STATE_OPEN;
|
dsnoop->state = SND_PCM_STATE_OPEN;
|
||||||
dsnoop->slowptr = opts->slowptr;
|
dsnoop->slowptr = opts->slowptr;
|
||||||
dsnoop->variable_buffer_size = opts->variable_buffer_size;
|
dsnoop->max_periods = opts->max_periods;
|
||||||
dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr;
|
dsnoop->sync_ptr = snd_pcm_dsnoop_sync_ptr;
|
||||||
|
|
||||||
if (first_instance) {
|
if (first_instance) {
|
||||||
|
|
@ -707,10 +707,9 @@ int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
|
||||||
struct slave_params params;
|
struct slave_params params;
|
||||||
struct snd_pcm_direct_open_conf dopen;
|
struct snd_pcm_direct_open_conf dopen;
|
||||||
int bsize, psize;
|
int bsize, psize;
|
||||||
int ipc_offset;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = snd_pcm_direct_parse_open_conf(conf, &dopen);
|
err = snd_pcm_direct_parse_open_conf(root, conf, stream, &dopen);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
|
@ -741,13 +740,6 @@ int _snd_pcm_dsnoop_open(snd_pcm_t **pcmp, const char *name,
|
||||||
params.period_size = psize;
|
params.period_size = psize;
|
||||||
params.buffer_size = bsize;
|
params.buffer_size = bsize;
|
||||||
|
|
||||||
ipc_offset = snd_pcm_direct_get_slave_ipc_offset(root, sconf, stream);
|
|
||||||
if (ipc_offset < 0) {
|
|
||||||
snd_config_delete(sconf);
|
|
||||||
return ipc_offset;
|
|
||||||
}
|
|
||||||
dopen.ipc_key += ipc_offset;
|
|
||||||
|
|
||||||
err = snd_pcm_dsnoop_open(pcmp, name, &dopen, ¶ms,
|
err = snd_pcm_dsnoop_open(pcmp, name, &dopen, ¶ms,
|
||||||
root, sconf, stream, mode);
|
root, sconf, stream, mode);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue