From: Abramo Bagnara Date: Fri, 8 Dec 2000 15:41:14 +0000 (+0000) Subject: Implemented correctly pcm_plug layer X-Git-Tag: v1.0.3~1057 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=d45644fdd4f6be73b899095cec7b6155b2cfa358;p=alsa-lib.git Implemented correctly pcm_plug layer --- diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 82b8f7de..c4c047a8 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -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_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_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) { diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index 28b2af9f..39930a1c 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -31,80 +31,6 @@ typedef struct { unsigned int tt_ssize, tt_cused, tt_sused; } 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) { 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; } +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) { snd_pcm_plug_t *plug = pcm->private; snd_pcm_t *slave = plug->req_slave; snd_pcm_hw_info_t sinfo; + int rate_min, rate_max; + int channels_min, channels_max; + unsigned int format, format_mask; int err; 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; 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->fragment_length_min = sinfo.fragment_length_min; 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_hw_info_t sinfo; 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 format; int err; 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.rate_min = RATE_MIN; sinfo.rate_max = RATE_MAX; - sinfo.fragments_min = params->fragments; - sinfo.fragments_max = params->fragments; - err = snd_pcm_strategy_simple(&strategy, 1000000, 2000000); + + err = snd_pcm_hw_info(slave, &sinfo); 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); + + err = snd_pcm_hw_info_par_nearest_next(&sinfo, + SND_PCM_HW_INFO_RATE, + params->rate, -1, + slave); + if (err < 0) return err; - } - err = snd_pcm_hw_info_strategy(slave, &sinfo, strategy); - snd_pcm_strategy_free(strategy); + 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; - 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; + 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); 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); if (err < 0)