Cleaned all hw_info. Removed snd_pcm_hw_{info,params}_rules* and changed strategy to allow the same functionality. Cleaned names tables. Added informative failure

This commit is contained in:
Abramo Bagnara 2000-11-29 08:32:36 +00:00
parent ae549cf739
commit b9916fd7fe
15 changed files with 1098 additions and 2107 deletions

View file

@ -102,17 +102,8 @@ int snd_pcm_unlink(snd_pcm_t *pcm);
int snd_pcm_wait(snd_pcm_t *pcm, int timeout); int snd_pcm_wait(snd_pcm_t *pcm, int timeout);
ssize_t snd_pcm_avail_update(snd_pcm_t *pcm); ssize_t snd_pcm_avail_update(snd_pcm_t *pcm);
int snd_pcm_set_avail_min(snd_pcm_t *pcm, size_t size); int snd_pcm_set_avail_min(snd_pcm_t *pcm, size_t size);
void snd_pcm_hw_info_all(snd_pcm_hw_info_t *info); void snd_pcm_hw_info_any(snd_pcm_hw_info_t *info);
int snd_pcm_hw_params_rules(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info);
unsigned int count, int *rules);
int snd_pcm_hw_params_rulesv(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ...);
int snd_pcm_hw_info_rules(snd_pcm_t *pcm,
snd_pcm_hw_info_t *info,
snd_pcm_hw_params_t *params,
unsigned int count, int *rules);
int snd_pcm_hw_info_rulesv(snd_pcm_t *pcm,
snd_pcm_hw_info_t *info,
snd_pcm_hw_params_t *params, ...);
typedef struct _snd_pcm_strategy snd_pcm_strategy_t; typedef struct _snd_pcm_strategy snd_pcm_strategy_t;
@ -123,19 +114,25 @@ typedef struct _snd_pcm_strategy_simple_choices_list {
} snd_pcm_strategy_simple_choices_list_t; } snd_pcm_strategy_simple_choices_list_t;
int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_info_t *info,
const snd_pcm_strategy_t *strategy, const snd_pcm_strategy_t *strategy);
unsigned int min_badness, unsigned int max_badness);
int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy); int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy);
int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp); int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp,
int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, unsigned int badness_min,
unsigned int badness_max);
int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, int order,
unsigned int param, unsigned int param,
unsigned long best, unsigned long best,
unsigned int mul); unsigned int mul);
int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, int order,
unsigned int param, unsigned int param,
unsigned int count, unsigned int count,
snd_pcm_strategy_simple_choices_list_t *choices); snd_pcm_strategy_simple_choices_list_t *choices);
int snd_pcm_hw_info_try_explain_failure(snd_pcm_t *pcm,
snd_pcm_hw_info_t *fail,
snd_pcm_hw_info_t *success,
unsigned int depth,
FILE *fp);
/* mmap */ /* mmap */
snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm); snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm);
@ -149,8 +146,8 @@ ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size);
ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size); ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size);
ssize_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, size_t size); ssize_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, size_t size);
const char *snd_pcm_format_name(int format); const char *snd_pcm_format_name(unsigned int format);
const char *snd_pcm_format_description(int format); const char *snd_pcm_format_description(unsigned int format);
int snd_pcm_format_value(const char* name); int snd_pcm_format_value(const char* name);
int snd_pcm_area_silence(snd_pcm_channel_area_t *dst_channel, size_t dst_offset, int snd_pcm_area_silence(snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
@ -177,6 +174,7 @@ int snd_pcm_format_unsigned(int format);
int snd_pcm_format_linear(int format); int snd_pcm_format_linear(int format);
int snd_pcm_format_little_endian(int format); int snd_pcm_format_little_endian(int format);
int snd_pcm_format_big_endian(int format); int snd_pcm_format_big_endian(int format);
int snd_pcm_format_cpu_endian(int format);
int snd_pcm_format_width(int format); /* in bits */ int snd_pcm_format_width(int format); /* in bits */
int snd_pcm_format_physical_width(int format); /* in bits */ int snd_pcm_format_physical_width(int format); /* in bits */
int snd_pcm_build_linear_format(int width, int unsignd, int big_endian); int snd_pcm_build_linear_format(int width, int unsignd, int big_endian);

File diff suppressed because it is too large Load diff

View file

@ -352,12 +352,8 @@ static int snd_pcm_adpcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
info->format_mask = 1U << adpcm->sformat; info->format_mask = 1U << adpcm->sformat;
info->access_mask = SND_PCM_ACCBIT_MMAP; info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(adpcm->plug.slave, info); err = snd_pcm_hw_info(adpcm->plug.slave, info);
if (info->format_mask) info->format_mask = format_mask;
info->format_mask = format_mask; info->access_mask = access_mask;
if (info->access_mask) {
adpcm->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
if (err < 0) if (err < 0)
return err; return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@ -369,25 +365,19 @@ static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{ {
snd_pcm_adpcm_t *adpcm = pcm->private; snd_pcm_adpcm_t *adpcm = pcm->private;
snd_pcm_t *slave = adpcm->plug.slave; snd_pcm_t *slave = adpcm->plug.slave;
unsigned int format, access; snd_pcm_hw_info_t sinfo;
snd_pcm_hw_params_t sparams;
int err; int err;
format = params->format; snd_pcm_hw_params_to_info(params, &sinfo);
access = params->access; sinfo.format_mask = 1 << adpcm->sformat;
params->format = adpcm->sformat; sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; params->fail_mask = sparams.fail_mask;
else if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->access = access;
if (err < 0) if (err < 0)
return err; return err;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) { if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
adpcm->getput_idx = get_index(format, SND_PCM_FORMAT_S16); adpcm->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16);
adpcm->func = adpcm_encode; adpcm->func = adpcm_encode;
} else { } else {
adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, adpcm->sformat); adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, adpcm->sformat);
@ -395,7 +385,7 @@ static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
} }
} else { } else {
if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) { if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, format); adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, params->format);
adpcm->func = adpcm_decode; adpcm->func = adpcm_decode;
} else { } else {
adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_FORMAT_S16); adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_FORMAT_S16);

View file

@ -234,12 +234,8 @@ static int snd_pcm_alaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
info->format_mask = 1U << alaw->sformat; info->format_mask = 1U << alaw->sformat;
info->access_mask = SND_PCM_ACCBIT_MMAP; info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(alaw->plug.slave, info); err = snd_pcm_hw_info(alaw->plug.slave, info);
if (info->format_mask) info->format_mask = format_mask;
info->format_mask = format_mask; info->access_mask = access_mask;
if (info->access_mask) {
alaw->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
if (err < 0) if (err < 0)
return err; return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@ -251,25 +247,19 @@ static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{ {
snd_pcm_alaw_t *alaw = pcm->private; snd_pcm_alaw_t *alaw = pcm->private;
snd_pcm_t *slave = alaw->plug.slave; snd_pcm_t *slave = alaw->plug.slave;
unsigned int format, access; snd_pcm_hw_info_t sinfo;
snd_pcm_hw_params_t sparams;
int err; int err;
format = params->format; snd_pcm_hw_params_to_info(params, &sinfo);
access = params->access; sinfo.format_mask = 1 << alaw->sformat;
params->format = alaw->sformat; sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; params->fail_mask = sparams.fail_mask;
else if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->access = access;
if (err < 0) if (err < 0)
return err; return err;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) { if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) {
alaw->getput_idx = get_index(format, SND_PCM_FORMAT_S16); alaw->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16);
alaw->func = alaw_encode; alaw->func = alaw_encode;
} else { } else {
alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, alaw->sformat); alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, alaw->sformat);
@ -277,7 +267,7 @@ static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
} }
} else { } else {
if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) { if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) {
alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, format); alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, params->format);
alaw->func = alaw_decode; alaw->func = alaw_decode;
} else { } else {
alaw->getput_idx = get_index(alaw->sformat, SND_PCM_FORMAT_S16); alaw->getput_idx = get_index(alaw->sformat, SND_PCM_FORMAT_S16);

View file

@ -92,12 +92,8 @@ static int snd_pcm_linear_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
info->format_mask = 1U << linear->sformat; info->format_mask = 1U << linear->sformat;
info->access_mask = SND_PCM_ACCBIT_MMAP; info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(linear->plug.slave, info); err = snd_pcm_hw_info(linear->plug.slave, info);
if (info->format_mask) info->format_mask = format_mask;
info->format_mask = format_mask; info->access_mask = access_mask;
if (info->access_mask) {
linear->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
if (err < 0) if (err < 0)
return err; return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@ -109,28 +105,22 @@ static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{ {
snd_pcm_linear_t *linear = pcm->private; snd_pcm_linear_t *linear = pcm->private;
snd_pcm_t *slave = linear->plug.slave; snd_pcm_t *slave = linear->plug.slave;
unsigned int format, access; snd_pcm_hw_info_t sinfo;
snd_pcm_hw_params_t sparams;
int err; int err;
format = params->format; snd_pcm_hw_params_to_info(params, &sinfo);
access = params->access; sinfo.format_mask = 1 << linear->sformat;
params->format = linear->sformat; sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; params->fail_mask = sparams.fail_mask;
else if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->access = access;
if (err < 0) if (err < 0)
return err; return err;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
linear->conv_idx = conv_index(format, linear->conv_idx = conv_index(params->format,
linear->sformat); linear->sformat);
else else
linear->conv_idx = conv_index(linear->sformat, linear->conv_idx = conv_index(linear->sformat,
format); params->format);
return 0; return 0;
} }

View file

@ -170,8 +170,7 @@ ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size);
ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size); ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size);
int snd_pcm_hw_info_complete(snd_pcm_hw_info_t *info); int snd_pcm_hw_info_complete(snd_pcm_hw_info_t *info);
void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info); void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info);
void snd_pcm_hw_info_to_params(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params); int snd_pcm_hw_info_to_params(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params);
void snd_pcm_hw_info_to_params_fail(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params);
int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info); int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info);
int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid); int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid);

View file

@ -65,9 +65,9 @@ int snd_pcm_format_unsigned(int format)
int val; int val;
val = snd_pcm_format_signed(format); val = snd_pcm_format_signed(format);
if (val >= 0) if (val < 0)
val ^= 1; return val;
return val; return !val;
} }
int snd_pcm_format_linear(int format) int snd_pcm_format_linear(int format)
@ -113,6 +113,15 @@ int snd_pcm_format_big_endian(int format)
return !val; return !val;
} }
int snd_pcm_format_cpu_endian(int format)
{
#ifdef SND_LITTLE_ENDIAN
return snd_pcm_format_little_endian(format);
#else
return snd_pcm_format_big_endian(format);
#endif
}
int snd_pcm_format_width(int format) int snd_pcm_format_width(int format)
{ {
switch (format) { switch (format) {

View file

@ -251,12 +251,8 @@ static int snd_pcm_mulaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
info->format_mask = 1U << mulaw->sformat; info->format_mask = 1U << mulaw->sformat;
info->access_mask = SND_PCM_ACCBIT_MMAP; info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(mulaw->plug.slave, info); err = snd_pcm_hw_info(mulaw->plug.slave, info);
if (info->format_mask) info->format_mask = format_mask;
info->format_mask = format_mask; info->access_mask = access_mask;
if (info->access_mask) {
mulaw->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
if (err < 0) if (err < 0)
return err; return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@ -268,25 +264,19 @@ static int snd_pcm_mulaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{ {
snd_pcm_mulaw_t *mulaw = pcm->private; snd_pcm_mulaw_t *mulaw = pcm->private;
snd_pcm_t *slave = mulaw->plug.slave; snd_pcm_t *slave = mulaw->plug.slave;
unsigned int format, access; snd_pcm_hw_info_t sinfo;
snd_pcm_hw_params_t sparams;
int err; int err;
format = params->format; snd_pcm_hw_params_to_info(params, &sinfo);
access = params->access; sinfo.format_mask = 1 << mulaw->sformat;
params->format = mulaw->sformat; sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; params->fail_mask = sparams.fail_mask;
else if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->access = access;
if (err < 0) if (err < 0)
return err; return err;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) { if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) {
mulaw->getput_idx = get_index(format, SND_PCM_FORMAT_S16); mulaw->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16);
mulaw->func = mulaw_encode; mulaw->func = mulaw_encode;
} else { } else {
mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, mulaw->sformat); mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, mulaw->sformat);
@ -294,7 +284,7 @@ static int snd_pcm_mulaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
} }
} else { } else {
if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) { if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) {
mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, format); mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, params->format);
mulaw->func = mulaw_decode; mulaw->func = mulaw_decode;
} else { } else {
mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_FORMAT_S16); mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_FORMAT_S16);

View file

@ -31,7 +31,6 @@ typedef struct {
snd_pcm_t *pcm; snd_pcm_t *pcm;
unsigned int channels_count; unsigned int channels_count;
int close_slave; int close_slave;
unsigned int access_mask;
} snd_pcm_multi_slave_t; } snd_pcm_multi_slave_t;
typedef struct { typedef struct {
@ -96,61 +95,76 @@ static int snd_pcm_multi_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int k; unsigned int k;
snd_pcm_hw_info_t i; snd_pcm_hw_info_t i;
unsigned int access_mask = ~0; unsigned int access_mask, saccess_mask;
int changed = 0;
int err = 0; int err = 0;
if (info->channels_min < multi->channels_count) if (info->channels_min < multi->channels_count)
info->channels_min = multi->channels_count; info->channels_min = multi->channels_count;
if (info->channels_max > multi->channels_count) if (info->channels_max > multi->channels_count)
info->channels_max = multi->channels_count; info->channels_max = multi->channels_count;
if (info->channels_max > info->channels_max) if (info->channels_min > info->channels_max)
return -EINVAL; return -EINVAL;
i = *info; i = *info;
for (k = 0; k < multi->slaves_count; ++k) { saccess_mask = ~0;
snd_pcm_t *slave = multi->slaves[k].pcm; changed = 0;
i.access_mask = SND_PCM_ACCBIT_MMAP; do {
i.channels_min = i.channels_max = multi->slaves[k].channels_count; for (k = 0; k < multi->slaves_count; ++k) {
err = snd_pcm_hw_info(slave, &i); snd_pcm_t *slave = multi->slaves[k].pcm;
access_mask &= i.access_mask; snd_pcm_hw_info_t sinfo = i;
if (err < 0) sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
break; sinfo.channels_min = sinfo.channels_max = multi->slaves[k].channels_count;
multi->slaves[k].access_mask = i.access_mask; err = snd_pcm_hw_info(slave, &sinfo);
} if (err < 0) {
sinfo.access_mask = info->access_mask;
sinfo.channels_min = multi->channels_count;
sinfo.channels_max = multi->channels_count;
*info = sinfo;
return err;
}
if (i.format_mask != sinfo.format_mask ||
i.subformat_mask != sinfo.subformat_mask ||
i.rate_min != sinfo.rate_min ||
i.rate_max != sinfo.rate_max ||
i.fragment_size_min != sinfo.fragment_size_min ||
i.fragment_size_max != sinfo.fragment_size_max ||
i.fragments_min != sinfo.fragments_min ||
i.fragments_max != sinfo.fragments_max ||
i.buffer_size_min != sinfo.buffer_size_min ||
i.buffer_size_max != sinfo.buffer_size_max)
changed++;
saccess_mask &= sinfo.access_mask;
i = sinfo;
}
} while (changed && multi->slaves_count > 1);
access_mask = info->access_mask;
*info = i; *info = i;
if (i.channels_min <= i.channels_max) info->access_mask = access_mask;
info->channels_min = info->channels_max = multi->channels_count; if (!(saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) ||
if (i.access_mask) { multi->slaves_count > 1)
if (!(access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) || info->access_mask &= ~SND_PCM_ACCBIT_MMAP_INTERLEAVED;
multi->slaves_count > 1) if (!(saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED))
info->access_mask &= ~SND_PCM_ACCBIT_MMAP_INTERLEAVED; info->access_mask &= ~SND_PCM_ACCBIT_MMAP_NONINTERLEAVED;
if (!(access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)) if (!info->access_mask)
info->access_mask &= ~SND_PCM_ACCBIT_MMAP_NONINTERLEAVED; return -EINVAL;
} info->channels_min = info->channels_max = multi->channels_count;
return err; return 0;
} }
static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int i; unsigned int i;
snd_pcm_hw_params_t p;
int err; int err;
if (params->channels != multi->channels_count) {
params->fail_mask = SND_PCM_HW_PARBIT_CHANNELS;
return -EINVAL;
}
p = *params;
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm; snd_pcm_t *slave = multi->slaves[i].pcm;
if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) snd_pcm_hw_info_t sinfo;
p.access = SND_PCM_ACCESS_MMAP_INTERLEAVED; snd_pcm_hw_params_t sparams;
else if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) snd_pcm_hw_params_to_info(params, &sinfo);
p.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
else sinfo.channels_min = sinfo.channels_max = multi->slaves[i].channels_count;
assert(0); err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
p.channels = multi->slaves[i].channels_count;
err = snd_pcm_hw_params(slave, &p);
if (err < 0) { if (err < 0) {
params->fail_mask = p.fail_mask; params->fail_mask = sparams.fail_mask;
return err; return err;
} }
err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format); err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);

View file

@ -32,67 +32,77 @@ typedef struct {
} snd_pcm_plug_t; } snd_pcm_plug_t;
static int preferred_formats[] = { static int format_badness(unsigned int src, unsigned int dst)
SND_PCM_FORMAT_S16_LE,
SND_PCM_FORMAT_S16_BE,
SND_PCM_FORMAT_U16_LE,
SND_PCM_FORMAT_U16_BE,
SND_PCM_FORMAT_S24_LE,
SND_PCM_FORMAT_S24_BE,
SND_PCM_FORMAT_U24_LE,
SND_PCM_FORMAT_U24_BE,
SND_PCM_FORMAT_S32_LE,
SND_PCM_FORMAT_S32_BE,
SND_PCM_FORMAT_U32_LE,
SND_PCM_FORMAT_U32_BE,
SND_PCM_FORMAT_S8,
SND_PCM_FORMAT_U8
};
static int snd_pcm_plug_slave_fmt(int format, unsigned int format_mask)
{ {
if (snd_pcm_format_linear(format)) { unsigned int badness = 0;
int width = snd_pcm_format_width(format); if (!snd_pcm_format_linear(src)) {
int unsignd = snd_pcm_format_unsigned(format); int w;
int big = snd_pcm_format_big_endian(format); if (!snd_pcm_format_linear(dst))
int format1; return format_badness(src, SND_PCM_FORMAT_S16) +
int wid, width1=width; format_badness(SND_PCM_FORMAT_S16, dst);
int dwidth1 = 8; w = snd_pcm_format_width(dst);
for (wid = 0; wid < 4; ++wid) { if (w < 16) {
int end, big1 = big; /* Resolution loss */
for (end = 0; end < 2; ++end) { badness += ((16 - w) / 8) * 32;
int sgn, unsignd1 = unsignd; badness += 16;
for (sgn = 0; sgn < 2; ++sgn) { } else
format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); badness += ((w - 16) / 8) * 32;
if (format1 >= 0 && if (!snd_pcm_format_cpu_endian(dst))
format_mask & (1U << format1)) badness += 2;
return format1; if (!snd_pcm_format_signed(dst))
unsignd1 = !unsignd1; badness += 1;
} } else if (!snd_pcm_format_linear(dst)) {
big1 = !big1; int w;
} w = snd_pcm_format_width(src);
if (width1 == 32) { if (w < 16)
dwidth1 = -dwidth1; badness += ((16 - w) / 8) * 32;
width1 = width; else
} badness += ((w - 16) / 8) * 32;
width1 += dwidth1; if (!snd_pcm_format_cpu_endian(src))
} badness += 2;
return ffs(format_mask) - 1; if (!snd_pcm_format_signed(src))
badness += 1;
} else { } else {
unsigned int i; int sw = snd_pcm_format_width(src);
switch (format) { int dw = snd_pcm_format_width(dst);
case SND_PCM_FORMAT_MU_LAW: if (sw < dw) {
case SND_PCM_FORMAT_A_LAW: badness += ((dw - sw) / 8) * 4;
case SND_PCM_FORMAT_IMA_ADPCM: } else if (sw > dw) {
for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) { /* Resolution loss */
int format1 = preferred_formats[i]; badness += ((sw - dw) / 8 * 4);
if (format_mask & (1U << format1)) badness += 16;
return format1;
}
default:
return ffs(format_mask) - 1;
} }
if (snd_pcm_format_little_endian(src) != snd_pcm_format_little_endian(dst))
badness += 2;
if (snd_pcm_format_signed(src) != snd_pcm_format_signed(dst))
badness += 1;
} }
return badness;
}
static int compare(const void * _a, const void * _b) {
const snd_pcm_strategy_simple_choices_list_t *a = _a, *b = _b;
return a->badness - b->badness;
}
static int snd_pcm_plug_format_choices(unsigned int stream,
snd_pcm_strategy_simple_choices_list_t *table,
unsigned int cformat, unsigned int sformat_mask)
{
unsigned int k;
unsigned int c = 0;
for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) {
if (!(sformat_mask & (1 << k)))
continue;
table[c].value = k;
if (stream == SND_PCM_STREAM_PLAYBACK)
table[c].badness = format_badness(cformat, k);
else
table[c].badness = format_badness(k, cformat);
c++;
}
qsort(table, c, sizeof(*table), compare);
return c;
} }
static int snd_pcm_plug_close(snd_pcm_t *pcm) static int snd_pcm_plug_close(snd_pcm_t *pcm)
@ -140,15 +150,12 @@ static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
{ {
int err;
snd_pcm_plug_t *plug = pcm->private; snd_pcm_plug_t *plug = pcm->private;
snd_pcm_t *slave = plug->req_slave; snd_pcm_t *slave = plug->req_slave;
snd_pcm_hw_info_t sinfo, i; snd_pcm_hw_info_t sinfo;
snd_pcm_hw_params_t sparams; int err;
unsigned int rate_min, rate_max; size_t size;
unsigned int channels_min, channels_max; unsigned int n;
unsigned int format, format_mask;
size_t fragment_size_min, fragment_size_max;
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED | SND_PCM_ACCBIT_RW_INTERLEAVED |
@ -162,130 +169,63 @@ static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
if (info->format_mask == 0) if (info->format_mask == 0)
return -EINVAL; return -EINVAL;
if (info->rate_min < 4000) info->subformat_mask &= SND_PCM_SUBFMTBIT_STD;
info->rate_min = 4000; if (info->subformat_mask == 0)
if (info->rate_max > 192000) return -EINVAL;
info->rate_max = 192000;
if (info->rate_min < RATE_MIN)
info->rate_min = RATE_MIN;
if (info->rate_max > RATE_MAX)
info->rate_max = RATE_MAX;
if (info->rate_max < info->rate_min) if (info->rate_max < info->rate_min)
return -EINVAL; return -EINVAL;
if (info->channels_min < 1) if (info->channels_min < 1)
info->channels_min = 1; info->channels_min = 1;
if (info->channels_max > 1024)
info->channels_max = 1024;
if (info->channels_max < info->channels_min) if (info->channels_max < info->channels_min)
return -EINVAL; return -EINVAL;
if (info->fragment_size_max > 1024 * 1024) sinfo = *info;
info->fragment_size_max = 1024 * 1024;
if (info->fragment_size_max < info->fragment_size_min)
return -EINVAL;
sinfo.access_mask = SND_PCM_ACCBIT_MMAP; sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW | sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW |
SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM); SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM);
sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD;
sinfo.channels_min = 1; sinfo.channels_min = 1;
sinfo.channels_max = 1024; sinfo.channels_max = UINT_MAX;
sinfo.rate_min = 4000; sinfo.rate_min = RATE_MIN;
sinfo.rate_max = 192000; sinfo.rate_max = RATE_MAX;
sinfo.fragments_min = 1;
sinfo.fragments_max = UINT_MAX;
sinfo.fragment_size_min = 1;
sinfo.fragment_size_max = ULONG_MAX;
/* Formats */
err = snd_pcm_hw_info(slave, &sinfo);
if (err < 0) {
*info = i;
return err;
}
format_mask = 0;
for (format = 0; format < SND_PCM_FORMAT_LAST; ++format) {
if (!(info->format_mask & (1 << format)))
continue;
err = snd_pcm_plug_slave_fmt(format, sinfo.format_mask);
if (err < 0)
info->format_mask &= ~(1 << format);
else
format_mask |= (1 << err);
}
sinfo.format_mask = format_mask;
/* Rate (and fragment_size) */
i = sinfo;
sparams.rate = info->rate_min;
err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE,
-1);
if (err < 0) {
*info = i;
return err;
}
rate_min = i.rate_min;
if (info->rate_max != info->rate_min) {
i = sinfo;
sparams.rate = info->rate_max;
err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE,
-1);
if (err < 0) {
*info = i;
return err;
}
rate_max = i.rate_min;
} else
rate_max = rate_min;
sinfo.rate_min = rate_min;
sinfo.rate_max = rate_max;
/* Channels */
i = sinfo;
sparams.channels = info->channels_min;
err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS,
-1);
if (err < 0) {
*info = i;
return err;
}
channels_min = i.channels_min;
if (info->channels_max != info->channels_min) {
i = sinfo;
sparams.channels = info->channels_max;
err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS,
-1);
if (err < 0) {
*info = i;
return err;
}
channels_max = i.channels_min;
} else
channels_max = channels_min;
sinfo.channels_min = channels_min;
sinfo.channels_max = channels_max;
sinfo.fragments_min = info->fragments_min;
sinfo.fragments_max = info->fragments_max;
sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max); sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max);
sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min); sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min);
err = snd_pcm_hw_info(slave, &sinfo); sinfo.buffer_size_min = muldiv_down(info->buffer_size_min, sinfo.rate_min, info->rate_max);
if (err < 0) { sinfo.buffer_size_max = muldiv_up(info->buffer_size_max, sinfo.rate_max, info->rate_min);
*info = sinfo;
return err;
}
err = snd_pcm_hw_info(slave, &sinfo);
info->subformat_mask = sinfo.subformat_mask; info->subformat_mask = sinfo.subformat_mask;
info->fragments_min = sinfo.fragments_min; info->fragments_min = sinfo.fragments_min;
info->fragments_max = sinfo.fragments_max; info->fragments_max = sinfo.fragments_max;
size = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max);
if (info->fragment_size_min < size)
info->fragment_size_min = size;
size = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
if (info->fragment_size_max > size)
info->fragment_size_max = size;
fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max); size = muldiv_down(sinfo.buffer_size_min, info->rate_min, sinfo.rate_max);
fragment_size_max = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min); if (info->buffer_size_min < size)
if (fragment_size_min > info->fragment_size_min) info->buffer_size_min = size;
info->fragment_size_min = fragment_size_min; size = muldiv_up(sinfo.buffer_size_max, info->rate_max, sinfo.rate_min);
if (fragment_size_max < info->fragment_size_max) if (info->buffer_size_max > size)
info->fragment_size_max = fragment_size_max; info->buffer_size_max = size;
n = info->buffer_size_min / info->fragment_size_max;
if (info->fragments_min < n)
info->fragments_min = n;
n = info->buffer_size_max / info->fragment_size_min;
if (info->fragments_max > n)
info->fragments_max = n;
if (err < 0)
return err;
info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info); snd_pcm_hw_info_complete(info);
return 0; return 0;
@ -490,7 +430,13 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
snd_pcm_t *slave = plug->req_slave; snd_pcm_t *slave = plug->req_slave;
snd_pcm_hw_info_t sinfo; snd_pcm_hw_info_t sinfo;
snd_pcm_hw_params_t sparams; snd_pcm_hw_params_t sparams;
snd_pcm_strategy_t *strategy;
snd_pcm_strategy_simple_choices_list_t formats[SND_PCM_FORMAT_LAST + 1];
unsigned int nformats;
int err; int err;
params->fail_mask = 0;
sparams = *params; sparams = *params;
snd_pcm_hw_params_to_info(&sparams, &sinfo); snd_pcm_hw_params_to_info(&sparams, &sinfo);
sinfo.access_mask = SND_PCM_ACCBIT_MMAP; sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
@ -498,39 +444,52 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM); SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM);
sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD; sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD;
sinfo.channels_min = 1; sinfo.channels_min = 1;
sinfo.channels_max = 1024; sinfo.channels_max = UINT_MAX;
sinfo.rate_min = 4000; sinfo.rate_min = RATE_MIN;
sinfo.rate_max = 192000; sinfo.rate_max = RATE_MAX;
sinfo.fragments_min = params->fragments;
sinfo.fragments_max = params->fragments;
sinfo.fragment_size_min = 1; sinfo.fragment_size_min = 1;
sinfo.fragment_size_max = ULONG_MAX; sinfo.fragment_size_max = ULONG_MAX;
err = snd_pcm_hw_info_rulesv(slave, &sinfo, params, sinfo.buffer_size_min = 1;
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE, sinfo.buffer_size_max = ULONG_MAX;
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS, err = snd_pcm_strategy_simple(&strategy, 1000000, 2000000);
-1);
if (err < 0) {
snd_pcm_hw_info_to_params_fail(&sinfo, params);
return err;
}
err = snd_pcm_plug_slave_fmt(sparams.format, sinfo.format_mask);
if (err < 0) if (err < 0)
return err; return err;
sparams.format = err; err = snd_pcm_strategy_simple_near(strategy, 0, SND_PCM_HW_PARAM_RATE,
sinfo.format_mask = 1U << err; params->rate, 1);
sparams.fragment_size = muldiv_near(params->fragment_size, sparams.rate, params->rate);
err = snd_pcm_hw_info_rulesv(slave, &sinfo, &sparams,
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE,
-1);
if (err < 0) { if (err < 0) {
snd_pcm_hw_info_to_params_fail(&sinfo, params); snd_pcm_strategy_free(strategy);
return err; return err;
} }
if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) err = snd_pcm_strategy_simple_near(strategy, 1, SND_PCM_HW_PARAM_CHANNELS,
sparams.access = SND_PCM_ACCESS_MMAP_INTERLEAVED; params->channels, 1);
else if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) if (err < 0) {
sparams.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; snd_pcm_strategy_free(strategy);
else return err;
assert(0); }
nformats = snd_pcm_plug_format_choices(pcm->stream, formats,
params->format,
sinfo.access_mask);
err = snd_pcm_strategy_simple_choices(strategy, 2, SND_PCM_HW_PARAM_FORMAT,
nformats, formats);
if (err < 0) {
snd_pcm_strategy_free(strategy);
return err;
}
err = snd_pcm_hw_info_strategy(slave, &sinfo, strategy);
snd_pcm_strategy_free(strategy);
if (err < 0)
return err;
assert(sinfo.format_mask &&
(sinfo.format_mask & (sinfo.format_mask - 1)) == 0);
sparams.format = ffs(sinfo.format_mask) - 1;
assert(sinfo.channels_min == sinfo.channels_max);
sparams.channels = sinfo.channels_min;
assert(sinfo.rate_min == sinfo.rate_max);
sparams.rate = sinfo.rate_min;
err = snd_pcm_plug_insert_plugins(pcm, params, &sparams); err = snd_pcm_plug_insert_plugins(pcm, params, &sparams);
if (err < 0) if (err < 0)
return err; return err;

View file

@ -28,7 +28,6 @@ typedef struct {
int (*init)(snd_pcm_t *pcm); int (*init)(snd_pcm_t *pcm);
int shmid; int shmid;
size_t appl_ptr, hw_ptr; size_t appl_ptr, hw_ptr;
unsigned int saccess_mask;
} snd_pcm_plugin_t; } snd_pcm_plugin_t;
int snd_pcm_plugin_close(snd_pcm_t *pcm); int snd_pcm_plugin_close(snd_pcm_t *pcm);
@ -65,7 +64,7 @@ int get_index(int src_format, int dst_format);
int put_index(int src_format, int dst_format); int put_index(int src_format, int dst_format);
int conv_index(int src_format, int dst_format); int conv_index(int src_format, int dst_format);
#define SND_PCM_FMTBIT_LINEAR (SND_PCM_FMTBIT_S8 |SND_PCM_FMTBIT_U8 | \ #define SND_PCM_FMTBIT_LINEAR (SND_PCM_FMTBIT_S8 |SND_PCM_FMTBIT_U8 | \
SND_PCM_FMTBIT_S16_LE|SND_PCM_FMTBIT_S16_BE | \ SND_PCM_FMTBIT_S16_LE|SND_PCM_FMTBIT_S16_BE | \
SND_PCM_FMTBIT_U16_LE|SND_PCM_FMTBIT_U16_BE | \ SND_PCM_FMTBIT_U16_LE|SND_PCM_FMTBIT_U16_BE | \
SND_PCM_FMTBIT_S24_LE|SND_PCM_FMTBIT_S24_BE | \ SND_PCM_FMTBIT_S24_LE|SND_PCM_FMTBIT_S24_BE | \
@ -75,21 +74,34 @@ int conv_index(int src_format, int dst_format);
extern snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops; extern snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops;
static inline ssize_t muldiv(ssize_t a, ssize_t b, ssize_t d, ssize_t corr)
{
double v = ((double) a * b + corr) / d;
if (v > LONG_MAX)
return LONG_MAX;
if (v < LONG_MIN)
return LONG_MIN;
return v;
}
static inline ssize_t muldiv_down(ssize_t a, ssize_t b, ssize_t d) static inline ssize_t muldiv_down(ssize_t a, ssize_t b, ssize_t d)
{ {
return (int64_t) (a * b) / d; return muldiv(a, b, d, 0);
} }
static inline ssize_t muldiv_up(ssize_t a, ssize_t b, ssize_t d) static inline ssize_t muldiv_up(ssize_t a, ssize_t b, ssize_t d)
{ {
return (int64_t) (a * b + (d - 1)) / d; return muldiv(a, b, d, d - 1);
} }
static inline ssize_t muldiv_near(ssize_t a, ssize_t b, ssize_t d) static inline ssize_t muldiv_near(ssize_t a, ssize_t b, ssize_t d)
{ {
return (int64_t) (a * b + (d / 2)) / d; return muldiv(a, b, d, d / 2);
} }
#define RATE_MIN 4000
#define RATE_MAX 192000
#define ROUTE_PLUGIN_FLOAT 1 #define ROUTE_PLUGIN_FLOAT 1
#define ROUTE_PLUGIN_RESOLUTION 16 #define ROUTE_PLUGIN_RESOLUTION 16

View file

@ -238,7 +238,7 @@ static int snd_pcm_rate_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
snd_pcm_rate_t *rate = pcm->private; snd_pcm_rate_t *rate = pcm->private;
snd_pcm_hw_info_t sinfo; snd_pcm_hw_info_t sinfo;
unsigned int access_mask; unsigned int access_mask;
size_t fragment_size_min, fragment_size_max; size_t size;
int err; int err;
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED | SND_PCM_ACCBIT_RW_INTERLEAVED |
@ -250,58 +250,56 @@ static int snd_pcm_rate_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
info->format_mask &= SND_PCM_FMTBIT_LINEAR; info->format_mask &= SND_PCM_FMTBIT_LINEAR;
if (info->format_mask == 0) if (info->format_mask == 0)
return -EINVAL; return -EINVAL;
if (info->rate_min < 4000) info->subformat_mask &= SND_PCM_SUBFMTBIT_STD;
info->rate_min = 4000; if (info->subformat_mask == 0)
if (info->rate_max > 192000) return -EINVAL;
info->rate_max = 192000; if (info->rate_min < RATE_MIN)
info->rate_min = RATE_MIN;
if (info->rate_max > RATE_MAX)
info->rate_max = RATE_MAX;
if (info->rate_max < info->rate_min) if (info->rate_max < info->rate_min)
return -EINVAL; return -EINVAL;
if (info->fragment_size_max > 1024 * 1024)
info->fragment_size_max = 1024 * 1024;
if (info->fragment_size_max < info->fragment_size_min)
return -EINVAL;
sinfo = *info;
sinfo.rate_min = rate->srate; sinfo = *info;
sinfo.rate_max = rate->srate; sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
if (rate->sformat >= 0) if (rate->sformat >= 0)
sinfo.format_mask = 1U << rate->sformat; sinfo.format_mask = 1U << rate->sformat;
sinfo.rate_min = rate->srate;
sinfo.rate_max = rate->srate;
sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max); sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max);
sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min); sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min);
sinfo.buffer_size_min = muldiv_down(info->buffer_size_min, sinfo.rate_min, info->rate_max);
sinfo.buffer_size_max = muldiv_up(info->buffer_size_max, sinfo.rate_max, info->rate_min);
sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(rate->plug.slave, &sinfo); err = snd_pcm_hw_info(rate->plug.slave, &sinfo);
info->subformat_mask = sinfo.subformat_mask; if (rate->sformat < 0)
info->format_mask = sinfo.format_mask;
info->channels_min = sinfo.channels_min; info->channels_min = sinfo.channels_min;
info->channels_max = sinfo.channels_max; info->channels_max = sinfo.channels_max;
info->fragments_min = sinfo.fragments_min; info->fragments_min = sinfo.fragments_min;
info->fragments_max = sinfo.fragments_max; info->fragments_max = sinfo.fragments_max;
if (!sinfo.access_mask) { size = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max);
info->access_mask = 0; if (info->fragment_size_min < size)
} info->fragment_size_min = size;
if (!sinfo.format_mask) { size = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
info->format_mask = 0; if (info->fragment_size_max > size)
} info->fragment_size_max = size;
if (sinfo.rate_min > sinfo.rate_max) { if (info->fragment_size_min > info->fragment_size_max)
info->rate_min = UINT_MAX; return -EINVAL;
info->rate_max = 0;
} size = muldiv_down(sinfo.buffer_size_min, info->rate_min, sinfo.rate_max);
if (sinfo.fragment_size_min > sinfo.fragment_size_max) { if (info->buffer_size_min < size)
info->fragment_size_min = ULONG_MAX; info->buffer_size_min = size;
info->fragment_size_max = 0; size = muldiv_up(sinfo.buffer_size_max, info->rate_max, sinfo.rate_min);
} if (info->buffer_size_max > size)
info->buffer_size_max = size;
if (info->buffer_size_min > info->buffer_size_max)
return -EINVAL;
if (err < 0) if (err < 0)
return err; return err;
fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max); info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
fragment_size_max = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
if (fragment_size_min > info->fragment_size_min)
info->fragment_size_min = fragment_size_min;
if (fragment_size_max < info->fragment_size_max)
info->fragment_size_max = fragment_size_max;
rate->plug.saccess_mask = sinfo.access_mask;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
snd_pcm_hw_info_complete(info); snd_pcm_hw_info_complete(info);
return 0; return 0;
} }
@ -311,51 +309,35 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
snd_pcm_rate_t *rate = pcm->private; snd_pcm_rate_t *rate = pcm->private;
snd_pcm_t *slave = rate->plug.slave; snd_pcm_t *slave = rate->plug.slave;
snd_pcm_hw_info_t sinfo; snd_pcm_hw_info_t sinfo;
unsigned int format, access, crate; snd_pcm_hw_params_t sparams;
unsigned int src_format, dst_format; unsigned int src_format, dst_format;
unsigned int src_rate, dst_rate; unsigned int src_rate, dst_rate;
size_t fragment_size;
int mul, div; int mul, div;
int err; int err;
crate = params->rate;
format = params->format;
fragment_size = params->fragment_size;
access = params->access;
params->rate = rate->srate;
if (rate->sformat >= 0)
params->format = rate->sformat;
if (rate->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
else if (rate->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
params->fragment_size = muldiv_near(params->fragment_size, params->rate, crate);
snd_pcm_hw_params_to_info(params, &sinfo); snd_pcm_hw_params_to_info(params, &sinfo);
sinfo.fragment_size_min = 0; if (rate->sformat >= 0)
sinfo.fragment_size_max = ULONG_MAX; sinfo.format_mask = 1 << rate->sformat;
err = snd_pcm_hw_info_rulesv(slave, &sinfo, params, sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE, sinfo.rate_min = rate->srate;
-1); sinfo.rate_max = rate->srate;
snd_pcm_hw_info_to_params(&sinfo, params); sinfo.fragment_size_min = muldiv_down(params->fragment_size, rate->srate, params->rate);
if (err >= 0) sinfo.fragment_size_max = muldiv_up(params->fragment_size, rate->srate, params->rate);
err = snd_pcm_hw_params(slave, params); sinfo.buffer_size_min = muldiv_down(params->fragment_size * params->fragments, rate->srate, params->rate);
params->format = format; sinfo.buffer_size_max = muldiv_up(params->fragment_size * params->fragments, rate->srate, params->rate);
params->rate = crate; err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
params->access = access; params->fail_mask = sparams.fail_mask;
params->fragment_size = fragment_size;
if (err < 0) if (err < 0)
return err; return err;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
src_format = format; src_format = params->format;
dst_format = slave->format; dst_format = slave->format;
src_rate = crate; src_rate = params->rate;
dst_rate = slave->rate; dst_rate = slave->rate;
} else { } else {
src_format = slave->format; src_format = slave->format;
dst_format = format; dst_format = params->format;
src_rate = slave->rate; src_rate = slave->rate;
dst_rate = crate; dst_rate = params->rate;
} }
rate->get_idx = get_index(src_format, SND_PCM_FORMAT_S16); rate->get_idx = get_index(src_format, SND_PCM_FORMAT_S16);
rate->put_idx = put_index(SND_PCM_FORMAT_S16, dst_format); rate->put_idx = put_index(SND_PCM_FORMAT_S16, dst_format);

View file

@ -428,7 +428,8 @@ static int snd_pcm_route_close(snd_pcm_t *pcm)
static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
{ {
snd_pcm_route_t *route = pcm->private; snd_pcm_route_t *route = pcm->private;
unsigned int format_mask, access_mask, channels_min, channels_max; unsigned int format_mask, access_mask;
unsigned int channels_min, channels_max;
int err; int err;
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
SND_PCM_ACCBIT_RW_INTERLEAVED | SND_PCM_ACCBIT_RW_INTERLEAVED |
@ -437,18 +438,19 @@ static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
access_mask = info->access_mask; access_mask = info->access_mask;
if (access_mask == 0) if (access_mask == 0)
return -EINVAL; return -EINVAL;
info->format_mask &= SND_PCM_FMTBIT_LINEAR; info->format_mask &= SND_PCM_FMTBIT_LINEAR;
format_mask = info->format_mask; format_mask = info->format_mask;
if (format_mask == 0) if (format_mask == 0)
return -EINVAL; return -EINVAL;
if (info->channels_min < 1) if (info->channels_min < 1)
info->channels_min = 1; info->channels_min = 1;
if (info->channels_max > 1024)
info->channels_max = 1024;
if (info->channels_max < info->channels_min)
return -EINVAL;
channels_min = info->channels_min; channels_min = info->channels_min;
channels_max = info->channels_max; channels_max = info->channels_max;
if (channels_min > channels_max)
return -EINVAL;
if (route->sformat >= 0) if (route->sformat >= 0)
info->format_mask = 1U << route->sformat; info->format_mask = 1U << route->sformat;
if (route->schannels >= 0) if (route->schannels >= 0)
@ -456,18 +458,13 @@ static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
info->access_mask = SND_PCM_ACCBIT_MMAP; info->access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_hw_info(route->plug.slave, info); err = snd_pcm_hw_info(route->plug.slave, info);
if (info->format_mask) info->access_mask = access_mask;
if (route->sformat >= 0)
info->format_mask = format_mask; info->format_mask = format_mask;
if (info->channels_min <= info->channels_max) { if (route->schannels >= 0) {
info->channels_min = channels_min; info->channels_min = channels_min;
info->channels_max = channels_max; info->channels_max = channels_max;
} }
if (info->access_mask) {
route->plug.saccess_mask = info->access_mask;
info->access_mask = access_mask;
}
if (info->format_mask)
info->format_mask = format_mask;
if (err < 0) if (err < 0)
return err; return err;
info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@ -479,34 +476,26 @@ static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
{ {
snd_pcm_route_t *route = pcm->private; snd_pcm_route_t *route = pcm->private;
snd_pcm_t *slave = route->plug.slave; snd_pcm_t *slave = route->plug.slave;
unsigned int format, access, channels;
unsigned int src_format, dst_format; unsigned int src_format, dst_format;
snd_pcm_hw_info_t sinfo;
snd_pcm_hw_params_t sparams;
int err; int err;
format = params->format; snd_pcm_hw_params_to_info(params, &sinfo);
channels = params->channels;
access = params->access;
if (route->sformat >= 0) if (route->sformat >= 0)
params->format = route->sformat; sinfo.format_mask = 1 << route->sformat;
if (route->schannels >= 0) if (route->schannels >= 0)
params->channels = route->schannels; sinfo.channels_min = sinfo.channels_max = route->schannels;
if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
else if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) params->fail_mask = sparams.fail_mask;
params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
else
assert(0);
err = snd_pcm_hw_params(slave, params);
params->format = format;
params->channels = channels;
params->access = access;
if (err < 0) if (err < 0)
return err; return err;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
src_format = format; src_format = params->format;
dst_format = slave->format; dst_format = slave->format;
} else { } else {
src_format = slave->format; src_format = slave->format;
dst_format = format; dst_format = params->format;
} }
route->params.get_idx = get_index(src_format, SND_PCM_FORMAT_U16); route->params.get_idx = get_index(src_format, SND_PCM_FORMAT_U16);
route->params.put_idx = put_index(SND_PCM_FORMAT_U32, dst_format); route->params.put_idx = put_index(SND_PCM_FORMAT_U32, dst_format);

View file

@ -455,34 +455,38 @@ static int snd_pcm_share_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
access_mask = info->access_mask; access_mask = info->access_mask;
if (access_mask == 0) if (access_mask == 0)
return -EINVAL; return -EINVAL;
if (info->channels_min < share->channels_count)
info->channels_min = share->channels_count;
if (info->channels_max > share->channels_count)
info->channels_max = share->channels_count;
if (info->channels_max > info->channels_max)
return -EINVAL;
if (slave->format >= 0) { if (slave->format >= 0) {
info->format_mask &= 1U << slave->format; info->format_mask &= 1U << slave->format;
if (!info->format_mask) if (!info->format_mask)
return -EINVAL; return -EINVAL;
} }
if (info->channels_min < share->channels_count)
info->channels_min = share->channels_count;
if (info->channels_max > share->channels_count)
info->channels_max = share->channels_count;
if (info->channels_min > info->channels_max)
return -EINVAL;
if (slave->rate >= 0) { if (slave->rate >= 0) {
if (info->rate_min < (unsigned)slave->rate) if (info->rate_min < (unsigned)slave->rate)
info->rate_min = slave->rate; info->rate_min = slave->rate;
if (info->rate_max > (unsigned)slave->rate) if (info->rate_max > (unsigned)slave->rate)
info->rate_max = slave->rate; info->rate_max = slave->rate;
if (info->rate_max > info->rate_max) if (info->rate_min > info->rate_max)
return -EINVAL; return -EINVAL;
} }
info->access_mask = SND_PCM_ACCBIT_MMAP; info->access_mask = SND_PCM_ACCBIT_MMAP;
info->channels_min = info->channels_max = slave->channels_count; info->channels_min = info->channels_max = slave->channels_count;
err = snd_pcm_hw_info(slave->pcm, info); err = snd_pcm_hw_info(slave->pcm, info);
if (info->channels_min <= info->channels_max) info->access_mask = access_mask;
info->channels_min = info->channels_max = share->channels_count; info->channels_min = info->channels_max = share->channels_count;
if (info->access_mask) if (err < 0)
info->access_mask = access_mask; return err;
info->info |= SND_PCM_INFO_DOUBLE; info->info |= SND_PCM_INFO_DOUBLE;
return err; return 0;
} }
static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
@ -494,15 +498,21 @@ static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Pthread_mutex_lock(&slave->mutex); Pthread_mutex_lock(&slave->mutex);
if (slave->setup_count > 1 || if (slave->setup_count > 1 ||
(slave->setup_count == 1 && !pcm->setup)) { (slave->setup_count == 1 && !pcm->setup)) {
if (params->access != spcm->access || params->fail_mask = 0;
params->format != spcm->format || if (params->format != spcm->format)
params->subformat != spcm->subformat ||
params->rate != spcm->rate ||
params->fragments != spcm->fragments ||
params->fragment_size != spcm->fragment_size) {
ERR("slave is already running with different setup");
params->fail_mask |= SND_PCM_HW_PARBIT_FORMAT; params->fail_mask |= SND_PCM_HW_PARBIT_FORMAT;
return -EBUSY; if (params->subformat != spcm->subformat)
params->fail_mask |= SND_PCM_HW_PARBIT_SUBFORMAT;
if (params->rate != spcm->rate)
params->fail_mask |= SND_PCM_HW_PARBIT_RATE;
if (params->fragments != spcm->fragments)
params->fail_mask |= SND_PCM_HW_PARBIT_FRAGMENTS;
if (params->fragment_size != spcm->fragment_size)
params->fail_mask |= SND_PCM_HW_PARBIT_FRAGMENT_SIZE;
if (params->fail_mask) {
ERR("slave is already running with different setup");
err = -EBUSY;
goto _end;
} }
} else { } else {
snd_pcm_hw_params_t sparams = *params; snd_pcm_hw_params_t sparams = *params;

View file

@ -41,8 +41,8 @@
typedef struct { typedef struct {
int socket; int socket;
unsigned int access_mask;
volatile snd_pcm_shm_ctrl_t *ctrl; volatile snd_pcm_shm_ctrl_t *ctrl;
unsigned int access_mask;
} snd_pcm_shm_t; } snd_pcm_shm_t;
int receive_fd(int socket, void *data, size_t len, int *fd) int receive_fd(int socket, void *data, size_t len, int *fd)
@ -156,18 +156,19 @@ static int snd_pcm_shm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
unsigned int access_mask = info->access_mask; unsigned int access_mask = info->access_mask;
ctrl->cmd = SND_PCM_IOCTL_HW_INFO; ctrl->cmd = SND_PCM_IOCTL_HW_INFO;
ctrl->u.hw_info = *info; ctrl->u.hw_info = *info;
ctrl->u.hw_info.access_mask |= SND_PCM_ACCBIT_MMAP; ctrl->u.hw_info.access_mask = SND_PCM_ACCBIT_MMAP;
err = snd_pcm_shm_action(pcm); err = snd_pcm_shm_action(pcm);
*info = ctrl->u.hw_info;
if (info->access_mask) {
shm->access_mask = info->access_mask;
info->access_mask |= (SND_PCM_ACCESS_RW_INTERLEAVED |
SND_PCM_ACCESS_RW_NONINTERLEAVED);
info->access_mask &= access_mask;
}
if (err < 0) if (err < 0)
return err; return err;
return err; access_mask &= (SND_PCM_ACCESS_RW_INTERLEAVED |
SND_PCM_ACCESS_RW_NONINTERLEAVED |
ctrl->u.hw_info.access_mask);
if (!access_mask)
return -EINVAL;
*info = ctrl->u.hw_info;
shm->access_mask = info->access_mask;
info->access_mask = access_mask;
return 0;
} }
static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)