mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-12-17 08:56:45 -05:00
Implemented correctly pcm_plug layer
This commit is contained in:
parent
714ac32b5e
commit
d45644fdd4
2 changed files with 197 additions and 110 deletions
|
|
@ -173,6 +173,10 @@ void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *i
|
||||||
int snd_pcm_hw_info_to_params(snd_pcm_t *pcm, 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);
|
||||||
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);
|
||||||
|
int snd_pcm_hw_info_par_nearest_next(const snd_pcm_hw_info_t *info,
|
||||||
|
unsigned int param,
|
||||||
|
unsigned int best, int value,
|
||||||
|
snd_pcm_t *pcm);
|
||||||
|
|
||||||
static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
|
static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -31,80 +31,6 @@ typedef struct {
|
||||||
unsigned int tt_ssize, tt_cused, tt_sused;
|
unsigned int tt_ssize, tt_cused, tt_sused;
|
||||||
} snd_pcm_plug_t;
|
} snd_pcm_plug_t;
|
||||||
|
|
||||||
|
|
||||||
static int format_badness(unsigned int src, unsigned int dst)
|
|
||||||
{
|
|
||||||
unsigned int badness = 0;
|
|
||||||
if (!snd_pcm_format_linear(src)) {
|
|
||||||
int w;
|
|
||||||
if (!snd_pcm_format_linear(dst))
|
|
||||||
return format_badness(src, SND_PCM_FORMAT_S16) +
|
|
||||||
format_badness(SND_PCM_FORMAT_S16, dst);
|
|
||||||
w = snd_pcm_format_width(dst);
|
|
||||||
if (w < 16) {
|
|
||||||
/* Resolution loss */
|
|
||||||
badness += ((16 - w) / 8) * 32;
|
|
||||||
badness += 16;
|
|
||||||
} else
|
|
||||||
badness += ((w - 16) / 8) * 32;
|
|
||||||
if (!snd_pcm_format_cpu_endian(dst))
|
|
||||||
badness += 2;
|
|
||||||
if (!snd_pcm_format_signed(dst))
|
|
||||||
badness += 1;
|
|
||||||
} else if (!snd_pcm_format_linear(dst)) {
|
|
||||||
int w;
|
|
||||||
w = snd_pcm_format_width(src);
|
|
||||||
if (w < 16)
|
|
||||||
badness += ((16 - w) / 8) * 32;
|
|
||||||
else
|
|
||||||
badness += ((w - 16) / 8) * 32;
|
|
||||||
if (!snd_pcm_format_cpu_endian(src))
|
|
||||||
badness += 2;
|
|
||||||
if (!snd_pcm_format_signed(src))
|
|
||||||
badness += 1;
|
|
||||||
} else {
|
|
||||||
int sw = snd_pcm_format_width(src);
|
|
||||||
int dw = snd_pcm_format_width(dst);
|
|
||||||
if (sw < dw) {
|
|
||||||
badness += ((dw - sw) / 8) * 4;
|
|
||||||
} else if (sw > dw) {
|
|
||||||
/* Resolution loss */
|
|
||||||
badness += ((sw - dw) / 8 * 4);
|
|
||||||
badness += 16;
|
|
||||||
}
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
snd_pcm_plug_t *plug = pcm->private;
|
snd_pcm_plug_t *plug = pcm->private;
|
||||||
|
|
@ -148,11 +74,117 @@ static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int linear_preferred_formats[] = {
|
||||||
|
#ifdef SND_LITTLE_ENDIAN
|
||||||
|
SND_PCM_FORMAT_S16_LE,
|
||||||
|
SND_PCM_FORMAT_U16_LE,
|
||||||
|
SND_PCM_FORMAT_S16_BE,
|
||||||
|
SND_PCM_FORMAT_U16_BE,
|
||||||
|
#else
|
||||||
|
SND_PCM_FORMAT_S16_BE,
|
||||||
|
SND_PCM_FORMAT_U16_BE,
|
||||||
|
SND_PCM_FORMAT_S16_LE,
|
||||||
|
SND_PCM_FORMAT_U16_LE,
|
||||||
|
#endif
|
||||||
|
#ifdef SND_LITTLE_ENDIAN
|
||||||
|
SND_PCM_FORMAT_S24_LE,
|
||||||
|
SND_PCM_FORMAT_U24_LE,
|
||||||
|
SND_PCM_FORMAT_S24_BE,
|
||||||
|
SND_PCM_FORMAT_U24_BE,
|
||||||
|
#else
|
||||||
|
SND_PCM_FORMAT_S24_BE,
|
||||||
|
SND_PCM_FORMAT_U24_BE,
|
||||||
|
SND_PCM_FORMAT_S24_LE,
|
||||||
|
SND_PCM_FORMAT_U24_LE,
|
||||||
|
#endif
|
||||||
|
#ifdef SND_LITTLE_ENDIAN
|
||||||
|
SND_PCM_FORMAT_S32_LE,
|
||||||
|
SND_PCM_FORMAT_U32_LE,
|
||||||
|
SND_PCM_FORMAT_S32_BE,
|
||||||
|
SND_PCM_FORMAT_U32_BE,
|
||||||
|
#else
|
||||||
|
SND_PCM_FORMAT_S32_BE,
|
||||||
|
SND_PCM_FORMAT_U32_BE,
|
||||||
|
SND_PCM_FORMAT_S32_LE,
|
||||||
|
SND_PCM_FORMAT_U32_LE,
|
||||||
|
#endif
|
||||||
|
SND_PCM_FORMAT_S8,
|
||||||
|
SND_PCM_FORMAT_U8
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned int nonlinear_preferred_formats[] = {
|
||||||
|
SND_PCM_FORMAT_MU_LAW,
|
||||||
|
SND_PCM_FORMAT_A_LAW,
|
||||||
|
SND_PCM_FORMAT_IMA_ADPCM,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int snd_pcm_plug_slave_format(int format, unsigned int format_mask)
|
||||||
|
{
|
||||||
|
int w, u, e, wid, w1, dw;
|
||||||
|
if (format_mask & (1 << format))
|
||||||
|
return format;
|
||||||
|
if (!((1 << format) & SND_PCM_FMTBIT_LINEAR)) {
|
||||||
|
unsigned int i;
|
||||||
|
switch (format) {
|
||||||
|
case SND_PCM_FORMAT_MU_LAW:
|
||||||
|
case SND_PCM_FORMAT_A_LAW:
|
||||||
|
case SND_PCM_FORMAT_IMA_ADPCM:
|
||||||
|
for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
|
||||||
|
unsigned int f = linear_preferred_formats[i];
|
||||||
|
if (format_mask & (1 << f))
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
/* Fall through */
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (!(format_mask & SND_PCM_FMTBIT_LINEAR)) {
|
||||||
|
unsigned int i;
|
||||||
|
for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
|
||||||
|
unsigned int f = nonlinear_preferred_formats[i];
|
||||||
|
if (format_mask & (1 << f))
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
w = snd_pcm_format_width(format);
|
||||||
|
u = snd_pcm_format_unsigned(format);
|
||||||
|
e = snd_pcm_format_big_endian(format);
|
||||||
|
w1 = w;
|
||||||
|
dw = 8;
|
||||||
|
for (wid = 0; wid < 4; ++wid) {
|
||||||
|
int end, e1 = e;
|
||||||
|
for (end = 0; end < 2; ++end) {
|
||||||
|
int sgn, u1 = u;
|
||||||
|
for (sgn = 0; sgn < 2; ++sgn) {
|
||||||
|
int f;
|
||||||
|
f = snd_pcm_build_linear_format(w1, u1, e1);
|
||||||
|
if (f >= 0 && format_mask & (1 << f))
|
||||||
|
return f;
|
||||||
|
u1 = !u1;
|
||||||
|
}
|
||||||
|
e1 = !e1;
|
||||||
|
}
|
||||||
|
if (w1 < 32)
|
||||||
|
w1 += dw;
|
||||||
|
else {
|
||||||
|
w1 = w - 8;
|
||||||
|
dw = -8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
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;
|
snd_pcm_hw_info_t sinfo;
|
||||||
|
int rate_min, rate_max;
|
||||||
|
int channels_min, channels_max;
|
||||||
|
unsigned int format, format_mask;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
|
info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED |
|
||||||
|
|
@ -193,6 +225,62 @@ static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
|
||||||
sinfo.rate_max = RATE_MAX;
|
sinfo.rate_max = RATE_MAX;
|
||||||
|
|
||||||
err = snd_pcm_hw_info(slave, &sinfo);
|
err = snd_pcm_hw_info(slave, &sinfo);
|
||||||
|
if (err < 0)
|
||||||
|
goto _err;
|
||||||
|
|
||||||
|
rate_min = snd_pcm_hw_info_par_nearest_next(&sinfo,
|
||||||
|
SND_PCM_HW_INFO_RATE,
|
||||||
|
info->rate_min, -1,
|
||||||
|
slave);
|
||||||
|
assert(rate_min >= 0);
|
||||||
|
if ((int)info->rate_max - rate_min > 1) {
|
||||||
|
rate_max = snd_pcm_hw_info_par_nearest_next(&sinfo,
|
||||||
|
SND_PCM_HW_INFO_RATE,
|
||||||
|
info->rate_max, -1,
|
||||||
|
slave);
|
||||||
|
assert(rate_max >= 0);
|
||||||
|
} else
|
||||||
|
rate_max = rate_min;
|
||||||
|
sinfo.rate_min = rate_min;
|
||||||
|
sinfo.rate_max = rate_max;
|
||||||
|
|
||||||
|
err = snd_pcm_hw_info(slave, &sinfo);
|
||||||
|
assert(err >= 0);
|
||||||
|
|
||||||
|
channels_min = snd_pcm_hw_info_par_nearest_next(&sinfo,
|
||||||
|
SND_PCM_HW_INFO_CHANNELS,
|
||||||
|
info->channels_min, -1,
|
||||||
|
slave);
|
||||||
|
assert(channels_min >= 0);
|
||||||
|
if ((int)info->channels_max - channels_min > 1) {
|
||||||
|
channels_max = snd_pcm_hw_info_par_nearest_next(&sinfo,
|
||||||
|
SND_PCM_HW_INFO_CHANNELS,
|
||||||
|
info->channels_max, -1,
|
||||||
|
slave);
|
||||||
|
assert(channels_max >= 0);
|
||||||
|
} else
|
||||||
|
channels_max = channels_min;
|
||||||
|
sinfo.channels_min = channels_min;
|
||||||
|
sinfo.channels_max = channels_max;
|
||||||
|
|
||||||
|
err = snd_pcm_hw_info(slave, &sinfo);
|
||||||
|
assert(err >= 0);
|
||||||
|
|
||||||
|
format_mask = 0;
|
||||||
|
for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
|
||||||
|
int f;
|
||||||
|
if (!(info->format_mask & (1 << format)))
|
||||||
|
continue;
|
||||||
|
f = snd_pcm_plug_slave_format(format, sinfo.format_mask);
|
||||||
|
assert(f >= 0);
|
||||||
|
format_mask |= (1 << f);
|
||||||
|
}
|
||||||
|
sinfo.format_mask = format_mask;
|
||||||
|
|
||||||
|
err = snd_pcm_hw_info(slave, &sinfo);
|
||||||
|
assert(err >= 0);
|
||||||
|
|
||||||
|
_err:
|
||||||
info->subformat_mask = sinfo.subformat_mask;
|
info->subformat_mask = sinfo.subformat_mask;
|
||||||
info->fragment_length_min = sinfo.fragment_length_min;
|
info->fragment_length_min = sinfo.fragment_length_min;
|
||||||
info->fragment_length_max = sinfo.fragment_length_max;
|
info->fragment_length_max = sinfo.fragment_length_max;
|
||||||
|
|
@ -407,9 +495,7 @@ 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;
|
int format;
|
||||||
snd_pcm_strategy_simple_choices_list_t formats[SND_PCM_FORMAT_LAST + 1];
|
|
||||||
unsigned int nformats;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
sparams = *params;
|
sparams = *params;
|
||||||
|
|
@ -422,44 +508,41 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
||||||
sinfo.channels_max = UINT_MAX;
|
sinfo.channels_max = UINT_MAX;
|
||||||
sinfo.rate_min = RATE_MIN;
|
sinfo.rate_min = RATE_MIN;
|
||||||
sinfo.rate_max = RATE_MAX;
|
sinfo.rate_max = RATE_MAX;
|
||||||
sinfo.fragments_min = params->fragments;
|
|
||||||
sinfo.fragments_max = params->fragments;
|
err = snd_pcm_hw_info(slave, &sinfo);
|
||||||
err = snd_pcm_strategy_simple(&strategy, 1000000, 2000000);
|
|
||||||
if (err < 0)
|
|
||||||
return err;
|
|
||||||
err = snd_pcm_strategy_simple_near(strategy, 0, SND_PCM_HW_PARAM_RATE,
|
|
||||||
params->rate, 1);
|
|
||||||
if (err < 0) {
|
|
||||||
snd_pcm_strategy_free(strategy);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
err = snd_pcm_strategy_simple_near(strategy, 1, SND_PCM_HW_PARAM_CHANNELS,
|
|
||||||
params->channels, 1);
|
|
||||||
if (err < 0) {
|
|
||||||
snd_pcm_strategy_free(strategy);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
nformats = snd_pcm_plug_format_choices(pcm->stream, formats,
|
|
||||||
params->format,
|
|
||||||
sinfo.format_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)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
assert(sinfo.format_mask &&
|
err = snd_pcm_hw_info_par_nearest_next(&sinfo,
|
||||||
(sinfo.format_mask & (sinfo.format_mask - 1)) == 0);
|
SND_PCM_HW_INFO_RATE,
|
||||||
sparams.format = ffs(sinfo.format_mask) - 1;
|
params->rate, -1,
|
||||||
assert(sinfo.channels_min == sinfo.channels_max);
|
slave);
|
||||||
sparams.channels = sinfo.channels_min;
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
sinfo.rate_min = sinfo.rate_max = err;
|
||||||
|
|
||||||
|
err = snd_pcm_hw_info(slave, &sinfo);
|
||||||
|
assert(err >= 0);
|
||||||
|
|
||||||
|
err = snd_pcm_hw_info_par_nearest_next(&sinfo,
|
||||||
|
SND_PCM_HW_INFO_CHANNELS,
|
||||||
|
params->channels, -1,
|
||||||
|
slave);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
sinfo.channels_min = sinfo.channels_max = err;
|
||||||
|
|
||||||
|
err = snd_pcm_hw_info(slave, &sinfo);
|
||||||
|
assert(err >= 0);
|
||||||
|
|
||||||
|
format = snd_pcm_plug_slave_format(params->format, sinfo.format_mask);
|
||||||
|
assert(format >= 0);
|
||||||
|
|
||||||
|
sparams.format = format;
|
||||||
assert(sinfo.rate_min == sinfo.rate_max);
|
assert(sinfo.rate_min == sinfo.rate_max);
|
||||||
sparams.rate = sinfo.rate_min;
|
sparams.rate = sinfo.rate_min;
|
||||||
|
assert(sinfo.channels_min == sinfo.channels_max);
|
||||||
|
sparams.channels = sinfo.channels_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)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue