From b9916fd7fe3162835fbd591fd3fab5455d47f32c Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Wed, 29 Nov 2000 08:32:36 +0000 Subject: [PATCH] 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 --- include/pcm.h | 34 +- src/pcm/pcm.c | 2458 +++++++++++++----------------------------- src/pcm/pcm_adpcm.c | 32 +- src/pcm/pcm_alaw.c | 32 +- src/pcm/pcm_linear.c | 32 +- src/pcm/pcm_local.h | 3 +- src/pcm/pcm_misc.c | 15 +- src/pcm/pcm_mulaw.c | 32 +- src/pcm/pcm_multi.c | 90 +- src/pcm/pcm_plug.c | 343 +++--- src/pcm/pcm_plugin.h | 22 +- src/pcm/pcm_rate.c | 120 +-- src/pcm/pcm_route.c | 51 +- src/pcm/pcm_share.c | 50 +- src/pcm/pcm_shm.c | 21 +- 15 files changed, 1163 insertions(+), 2172 deletions(-) diff --git a/include/pcm.h b/include/pcm.h index 872e12ec..89b6d175 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -102,17 +102,8 @@ int snd_pcm_unlink(snd_pcm_t *pcm); int snd_pcm_wait(snd_pcm_t *pcm, int timeout); ssize_t snd_pcm_avail_update(snd_pcm_t *pcm); 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); -int snd_pcm_hw_params_rules(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, - 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, ...); +void snd_pcm_hw_info_any(snd_pcm_hw_info_t *info); +int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info); 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; int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, - const snd_pcm_strategy_t *strategy, - unsigned int min_badness, unsigned int max_badness); + const 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_near(snd_pcm_strategy_t *strategy, +int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp, + 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 long best, 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 count, 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 */ 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_readn(snd_pcm_t *pcm, void **bufs, size_t size); -const char *snd_pcm_format_name(int format); -const char *snd_pcm_format_description(int format); +const char *snd_pcm_format_name(unsigned int format); +const char *snd_pcm_format_description(unsigned int format); int snd_pcm_format_value(const char* name); 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_little_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_physical_width(int format); /* in bits */ int snd_pcm_build_linear_format(int width, int unsignd, int big_endian); diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index cbf447e1..c0fca4be 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -112,7 +112,7 @@ int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) return err; } -void snd_pcm_hw_info_all(snd_pcm_hw_info_t *info) +void snd_pcm_hw_info_any(snd_pcm_hw_info_t *info) { assert(info); info->access_mask = ~0; @@ -143,119 +143,67 @@ void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *i info->buffer_size_min = info->buffer_size_max = params->fragment_size * params->fragments; } -void snd_pcm_hw_info_to_params(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params) -{ - assert(info->access_mask && - !(info->access_mask & (info->access_mask - 1))); - params->access = ffs(info->access_mask) - 1; - assert(info->format_mask && - !(info->format_mask & (info->format_mask - 1))); - params->format = ffs(info->format_mask) - 1; - assert(info->subformat_mask && - !(info->subformat_mask & (info->subformat_mask - 1))); - params->subformat = ffs(info->subformat_mask) - 1; - assert(info->channels_min == info->channels_max); - params->channels = info->channels_min; - assert(info->rate_min == info->rate_max); - params->rate = info->rate_min; - assert(info->fragment_size_min == info->fragment_size_max); - params->fragment_size = info->fragment_size_min; - assert(info->fragments_min == info->fragments_max); - params->fragments = info->fragments_min; -} - -void snd_pcm_hw_info_to_params_fail(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params) -{ - unsigned int f = 0; - if (info->access_mask == 0) - f |= SND_PCM_HW_PARBIT_ACCESS; - if (info->format_mask == 0) - f |= SND_PCM_HW_PARBIT_FORMAT; - if (info->subformat_mask == 0) - f |= SND_PCM_HW_PARBIT_SUBFORMAT; - if (info->channels_min > info->channels_max) - f |= SND_PCM_HW_PARBIT_CHANNELS; - if (info->rate_min > info->rate_max) - f |= SND_PCM_HW_PARBIT_RATE; - if (info->fragment_size_min > info->fragment_size_max) - f |= SND_PCM_HW_PARBIT_FRAGMENT_SIZE; - if (info->fragments_min > info->fragments_max) - f |= SND_PCM_HW_PARBIT_FRAGMENTS; - assert(f); - params->fail_mask = f; -} - -int _snd_pcm_hw_params(snd_pcm_t *pcm, 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) { + snd_pcm_hw_info_t i = *info; int err; - snd_pcm_hw_info_t info; - - snd_pcm_hw_params_to_info(params, &info); - err = snd_pcm_hw_info(pcm, &info); - if (err < 0) { - snd_pcm_hw_info_to_params_fail(&info, params); - return err; - } - snd_pcm_hw_info_to_params(&info, params); - if ((err = pcm->ops->hw_params(pcm->op_arg, params)) < 0) - return err; - pcm->setup = 1; - pcm->access = params->access; - pcm->format = params->format; - pcm->subformat = params->subformat; - pcm->rate = params->rate; - pcm->channels = params->channels; - pcm->fragment_size = params->fragment_size; - pcm->fragments = params->fragments; - pcm->bits_per_sample = snd_pcm_format_physical_width(params->format); - pcm->bits_per_frame = pcm->bits_per_sample * params->channels; - pcm->buffer_size = params->fragment_size * params->fragments; - pcm->info = info.info; - pcm->msbits = info.msbits; - pcm->rate_master = info.rate_master; - pcm->rate_divisor = info.rate_divisor; - pcm->fifo_size = info.fifo_size; + err = snd_pcm_hw_info(pcm, &i); + if (err < 0) + return err; - /* Default sw params */ - pcm->start_mode = SND_PCM_START_DATA; - pcm->ready_mode = SND_PCM_READY_FRAGMENT; - pcm->xrun_mode = SND_PCM_XRUN_FRAGMENT; - pcm->avail_min = pcm->fragment_size; - pcm->xfer_min = pcm->fragment_size; - pcm->xfer_align = pcm->fragment_size; - pcm->time = 0; - pcm->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size; + assert(i.access_mask); + if (i.access_mask & (i.access_mask - 1)) { + i.access_mask = 1 << (ffs(i.access_mask - 1)); + err = snd_pcm_hw_info(pcm, info); + assert(err >= 0); + } + assert(i.format_mask); + if (i.format_mask & (i.format_mask - 1)) { + i.format_mask = 1 << (ffs(i.format_mask - 1)); + err = snd_pcm_hw_info(pcm, info); + assert(err >= 0); + } + assert(i.subformat_mask); + if (i.subformat_mask & (i.subformat_mask - 1)) { + i.subformat_mask = 1 << (ffs(i.subformat_mask - 1)); + err = snd_pcm_hw_info(pcm, info); + assert(err >= 0); + } + assert(i.channels_min <= i.channels_max); + if (i.channels_min < i.channels_max) { + i.channels_max = i.channels_min; + err = snd_pcm_hw_info(pcm, info); + assert(err >= 0); + } + assert(i.rate_min <= i.rate_max); + if (i.rate_min < i.rate_max) { + i.rate_max = i.rate_min; + err = snd_pcm_hw_info(pcm, info); + assert(err >= 0); + } + assert(i.fragment_size_min <= i.fragment_size_max); + if (i.fragment_size_min < i.fragment_size_max) { + i.fragment_size_max = i.fragment_size_min; + err = snd_pcm_hw_info(pcm, info); + assert(err >= 0); + } + assert(i.fragments_min <= i.fragments_max); + if (i.fragments_min < i.fragments_max) { + i.fragments_max = i.fragments_min; + err = snd_pcm_hw_info(pcm, info); + assert(err >= 0); + } + params->access = ffs(i.access_mask) - 1; + params->format = ffs(i.format_mask) - 1; + params->subformat = ffs(i.subformat_mask) - 1; + params->channels = i.channels_min; + params->rate = i.rate_min; + params->fragment_size = i.fragment_size_min; + params->fragments = i.fragments_min; return 0; } -int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) -{ - int err; - assert(pcm && params); - if (pcm->setup && pcm->mmap_channels && - (pcm->mmap_rw || - (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED || - pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED || - pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) { - err = snd_pcm_munmap(pcm); - if (err < 0) - return err; - } - err = _snd_pcm_hw_params(pcm, params); - if (pcm->setup && - (pcm->mmap_rw || - (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED || - pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED || - pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) { - int err; - err = snd_pcm_mmap(pcm); - if (err < 0) - return err; - } - return err; -} - int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) { int err; @@ -475,73 +423,35 @@ int snd_pcm_poll_descriptor(snd_pcm_t *pcm) return pcm->poll_fd; } -typedef struct { - int value; - const char* name; - const char* desc; -} assoc_t; - -static assoc_t *assoc_value(int value, assoc_t *alist) -{ - while (alist->name) { - if (value == alist->value) - return alist; - alist++; - } - return 0; -} - -static assoc_t *assoc_name(const char *name, assoc_t *alist) -{ - while (alist->name) { - if (strcasecmp(name, alist->name) == 0) - return alist; - alist++; - } - return 0; -} - -static const char *assoc(int value, assoc_t *alist) -{ - assoc_t *a; - a = assoc_value(value, alist); - if (a) - return a->name; - return "UNKNOWN"; -} +#define STATE(v) [SND_PCM_STATE_##v] = #v +#define STREAM(v) [SND_PCM_STREAM_##v] = #v +#define READY(v) [SND_PCM_READY_##v] = #v +#define XRUN(v) [SND_PCM_XRUN_##v] = #v +#define ACCESS(v) [SND_PCM_ACCESS_##v] = #v +#define START(v) [SND_PCM_START_##v] = #v +#define HW_PARAM(v) [SND_PCM_HW_PARAM_##v] = #v +#define SW_PARAM(v) [SND_PCM_SW_PARAM_##v] = #v +#define FORMAT(v) [SND_PCM_FORMAT_##v] = #v +#define SUBFORMAT(v) [SND_PCM_SUBFORMAT_##v] = #v -#define STATE(v) { SND_PCM_STATE_##v, #v, #v } -#define STREAM(v) { SND_PCM_STREAM_##v, #v, #v } -#define READY(v) { SND_PCM_READY_##v, #v, #v } -#define XRUN(v) { SND_PCM_XRUN_##v, #v, #v } -#define ACCESS(v) { SND_PCM_ACCESS_##v, #v, #v } -#define FORMAT(v, d) { SND_PCM_FORMAT_##v, #v, d } -#define SUBFORMAT(v, d) { SND_PCM_SUBFORMAT_##v, #v, d } -#define XRUN_ACT(v) { SND_PCM_XRUN_ACT_##v, #v, #v } -#define START(v) { SND_PCM_START_##v, #v, #v } -#define FILL(v) { SND_PCM_FILL_##v, #v, #v } -#define HW_PARAM(v) { SND_PCM_HW_PARAM_##v, #v, #v } -#define SW_PARAM(v) { SND_PCM_SW_PARAM_##v, #v, #v } -#define END { 0, NULL, NULL } +#define FORMATD(v, d) [SND_PCM_FORMAT_##v] = d +#define SUBFORMATD(v, d) [SND_PCM_SUBFORMAT_##v] = d -static assoc_t streams[] = { +char *snd_pcm_stream_names[] = { STREAM(PLAYBACK), STREAM(CAPTURE), - END }; -static assoc_t states[] = { +char *snd_pcm_state_names[] = { STATE(OPEN), STATE(SETUP), STATE(PREPARED), STATE(RUNNING), STATE(XRUN), STATE(PAUSED), - END }; -#if 0 -static assoc_t hw_params[] = { +char *snd_pcm_hw_param_names[] = { HW_PARAM(ACCESS), HW_PARAM(FORMAT), HW_PARAM(SUBFORMAT), @@ -550,10 +460,9 @@ static assoc_t hw_params[] = { HW_PARAM(FRAGMENT_SIZE), HW_PARAM(FRAGMENTS), HW_PARAM(BUFFER_SIZE), - END }; -static assoc_t sw_params[] = { +char *snd_pcm_sw_param_names[] = { SW_PARAM(START_MODE), SW_PARAM(READY_MODE), SW_PARAM(AVAIL_MIN), @@ -561,91 +470,122 @@ static assoc_t sw_params[] = { SW_PARAM(XFER_ALIGN), SW_PARAM(XRUN_MODE), SW_PARAM(TIME), - END }; -#endif -static assoc_t accesses[] = { +char *snd_pcm_access_names[] = { ACCESS(MMAP_INTERLEAVED), ACCESS(MMAP_NONINTERLEAVED), ACCESS(MMAP_COMPLEX), ACCESS(RW_INTERLEAVED), ACCESS(RW_NONINTERLEAVED), - END }; -static assoc_t formats[] = { - FORMAT(S8, "Signed 8-bit"), - FORMAT(U8, "Unsigned 8-bit"), - FORMAT(S16_LE, "Signed 16-bit Little Endian"), - FORMAT(S16_BE, "Signed 16-bit Big Endian"), - FORMAT(U16_LE, "Unsigned 16-bit Little Endian"), - FORMAT(U16_BE, "Unsigned 16-bit Big Endian"), - FORMAT(S24_LE, "Signed 24-bit Little Endian"), - FORMAT(S24_BE, "Signed 24-bit Big Endian"), - FORMAT(U24_LE, "Unsigned 24-bit Little Endian"), - FORMAT(U24_BE, "Unsigned 24-bit Big Endian"), - FORMAT(S32_LE, "Signed 32-bit Little Endian"), - FORMAT(S32_BE, "Signed 32-bit Big Endian"), - FORMAT(U32_LE, "Unsigned 32-bit Little Endian"), - FORMAT(U32_BE, "Unsigned 32-bit Big Endian"), - FORMAT(FLOAT_LE, "Float Little Endian"), - FORMAT(FLOAT_BE, "Float Big Endian"), - FORMAT(FLOAT64_LE, "Float64 Little Endian"), - FORMAT(FLOAT64_BE, "Float64 Big Endian"), - FORMAT(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"), - FORMAT(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"), - FORMAT(MU_LAW, "Mu-Law"), - FORMAT(A_LAW, "A-Law"), - FORMAT(IMA_ADPCM, "Ima-ADPCM"), - FORMAT(MPEG, "MPEG"), - FORMAT(GSM, "GSM"), - FORMAT(SPECIAL, "Special"), - END +char *snd_pcm_format_names[] = { + FORMAT(S8), + FORMAT(U8), + FORMAT(S16_LE), + FORMAT(S16_BE), + FORMAT(U16_LE), + FORMAT(U16_BE), + FORMAT(S24_LE), + FORMAT(S24_BE), + FORMAT(U24_LE), + FORMAT(U24_BE), + FORMAT(S32_LE), + FORMAT(S32_BE), + FORMAT(U32_LE), + FORMAT(U32_BE), + FORMAT(FLOAT_LE), + FORMAT(FLOAT_BE), + FORMAT(FLOAT64_LE), + FORMAT(FLOAT64_BE), + FORMAT(IEC958_SUBFRAME_LE), + FORMAT(IEC958_SUBFRAME_BE), + FORMAT(MU_LAW), + FORMAT(A_LAW), + FORMAT(IMA_ADPCM), + FORMAT(MPEG), + FORMAT(GSM), + FORMAT(SPECIAL), +}; + +char *snd_pcm_format_descriptions[] = { + FORMATD(S8, "Signed 8-bit"), + FORMATD(U8, "Unsigned 8-bit"), + FORMATD(S16_LE, "Signed 16-bit Little Endian"), + FORMATD(S16_BE, "Signed 16-bit Big Endian"), + FORMATD(U16_LE, "Unsigned 16-bit Little Endian"), + FORMATD(U16_BE, "Unsigned 16-bit Big Endian"), + FORMATD(S24_LE, "Signed 24-bit Little Endian"), + FORMATD(S24_BE, "Signed 24-bit Big Endian"), + FORMATD(U24_LE, "Unsigned 24-bit Little Endian"), + FORMATD(U24_BE, "Unsigned 24-bit Big Endian"), + FORMATD(S32_LE, "Signed 32-bit Little Endian"), + FORMATD(S32_BE, "Signed 32-bit Big Endian"), + FORMATD(U32_LE, "Unsigned 32-bit Little Endian"), + FORMATD(U32_BE, "Unsigned 32-bit Big Endian"), + FORMATD(FLOAT_LE, "Float Little Endian"), + FORMATD(FLOAT_BE, "Float Big Endian"), + FORMATD(FLOAT64_LE, "Float64 Little Endian"), + FORMATD(FLOAT64_BE, "Float64 Big Endian"), + FORMATD(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"), + FORMATD(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"), + FORMATD(MU_LAW, "Mu-Law"), + FORMATD(A_LAW, "A-Law"), + FORMATD(IMA_ADPCM, "Ima-ADPCM"), + FORMATD(MPEG, "MPEG"), + FORMATD(GSM, "GSM"), + FORMATD(SPECIAL, "Special"), +}; + +char *snd_pcm_subformat_names[] = { + SUBFORMAT(STD), }; -static assoc_t subformats[] = { - SUBFORMAT(STD, "Standard"), - END +char *snd_pcm_subformat_descriptions[] = { + SUBFORMATD(STD, "Standard"), }; -static assoc_t starts[] = { +char *snd_pcm_start_mode_names[] = { START(EXPLICIT), START(DATA), - END }; -static assoc_t readys[] = { + +char *snd_pcm_ready_mode_names[] = { READY(FRAGMENT), READY(ASAP), - END }; -static assoc_t xruns[] = { +char *snd_pcm_xrun_mode_names[] = { XRUN(ASAP), XRUN(FRAGMENT), XRUN(NONE), - END }; -static assoc_t onoff[] = { - {0, "OFF", NULL}, - {1, "ON", NULL}, - {-1, "ON", NULL}, - END +static char *onoff[] = { + [0] = "OFF", + [1] = "ON", }; +#define assoc(value, names) ({ \ + unsigned int __v = value; \ + assert(__v < sizeof(names) / sizeof(names[0])); \ + names[__v]; \ +}) + + int snd_pcm_dump_hw_setup(snd_pcm_t *pcm, FILE *fp) { assert(pcm); assert(fp); assert(pcm->setup); - fprintf(fp, "stream : %s\n", assoc(pcm->stream, streams)); - fprintf(fp, "access : %s\n", assoc(pcm->access, accesses)); - fprintf(fp, "format : %s\n", assoc(pcm->format, formats)); - fprintf(fp, "subformat : %s\n", assoc(pcm->subformat, subformats)); + fprintf(fp, "stream : %s\n", assoc(pcm->stream, snd_pcm_stream_names)); + fprintf(fp, "access : %s\n", assoc(pcm->access, snd_pcm_access_names)); + fprintf(fp, "format : %s\n", assoc(pcm->format, snd_pcm_format_names)); + fprintf(fp, "subformat : %s\n", assoc(pcm->subformat, snd_pcm_subformat_names)); fprintf(fp, "channels : %d\n", pcm->channels); fprintf(fp, "rate : %d\n", pcm->rate); - fprintf(fp, "rate : %g (%d/%d)\n", (double) pcm->rate_master / pcm->rate_divisor, pcm->rate_master, pcm->rate_divisor); + fprintf(fp, "exact rate : %g (%d/%d)\n", (double) pcm->rate_master / pcm->rate_divisor, pcm->rate_master, pcm->rate_divisor); fprintf(fp, "msbits : %d\n", pcm->msbits); fprintf(fp, "fragment_size: %ld\n", (long)pcm->fragment_size); fprintf(fp, "fragments : %d\n", pcm->fragments); @@ -657,9 +597,9 @@ int snd_pcm_dump_sw_setup(snd_pcm_t *pcm, FILE *fp) assert(pcm); assert(fp); assert(pcm->setup); - fprintf(fp, "start_mode : %s\n", assoc(pcm->start_mode, starts)); - fprintf(fp, "ready_mode : %s\n", assoc(pcm->ready_mode, readys)); - fprintf(fp, "xrun_mode : %s\n", assoc(pcm->xrun_mode, xruns)); + fprintf(fp, "start_mode : %s\n", assoc(pcm->start_mode, snd_pcm_start_mode_names)); + fprintf(fp, "ready_mode : %s\n", assoc(pcm->ready_mode, snd_pcm_ready_mode_names)); + fprintf(fp, "xrun_mode : %s\n", assoc(pcm->xrun_mode, snd_pcm_xrun_mode_names)); fprintf(fp, "avail_min : %ld\n", (long)pcm->avail_min); fprintf(fp, "xfer_min : %ld\n", (long)pcm->xfer_min); fprintf(fp, "xfer_align : %ld\n", (long)pcm->xfer_align); @@ -675,131 +615,26 @@ int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *fp) return 0; } -int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp) -{ - unsigned int k; - fputs("access:", fp); - if (info->access_mask == ~0U) - fputs(" ALL", fp); - else if (info->access_mask) { - for (k = 0; k <= SND_PCM_ACCESS_LAST; ++k) - if (info->access_mask & (1U << k)) { - putc(' ', fp); - fputs(assoc(k, accesses), fp); - } - } else - fputs(" NONE", fp); - putc('\n', fp); - - fputs("format:", fp); - if (info->format_mask == ~0U) - fputs(" ALL", fp); - else if (info->format_mask) { - for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) - if (info->format_mask & (1U << k)) { - putc(' ', fp); - fputs(assoc(k, formats), fp); - } - } else - fputs(" NONE", fp); - putc('\n', fp); - - fputs("subformat:", fp); - if (info->subformat_mask == ~0U) - fputs(" ALL", fp); - else if (info->subformat_mask) { - for (k = 0; k <= SND_PCM_SUBFORMAT_LAST; ++k) - if (info->subformat_mask & (1U << k)) { - putc(' ', fp); - fputs(assoc(k, subformats), fp); - } - } else - fputs(" NONE", fp); - putc('\n', fp); - - fputs("channels: ", fp); - if (info->channels_min <= 1 && info->channels_max == UINT_MAX) - fputs("ALL", fp); - else if (info->channels_min > info->channels_max) - fputs("NONE", fp); - else { - fprintf(fp, "%u", info->channels_min); - if (info->channels_min < info->channels_max) - fprintf(fp, " - %u", info->channels_max); - } - putc('\n', fp); - - fputs("rate: ", fp); - if (info->rate_min <= 1 && info->rate_max == UINT_MAX) - fputs("ALL", fp); - else if (info->rate_min > info->rate_max) - fputs("NONE", fp); - else { - fprintf(fp, "%u", info->rate_min); - if (info->rate_min < info->rate_max) - fprintf(fp, " - %u", info->rate_max); - } - putc('\n', fp); - - fputs("fragment_size: ", fp); - if (info->fragment_size_min <= 1 && - info->fragment_size_max == ULONG_MAX) - fputs("ALL", fp); - else if (info->fragment_size_min > info->fragment_size_max) - fputs("NONE", fp); - else { - fprintf(fp, "%lu", (unsigned long)info->fragment_size_min); - if (info->fragment_size_min < info->fragment_size_max) - fprintf(fp, " - %lu", (unsigned long)info->fragment_size_max); - } - putc('\n', fp); - - fputs("fragments: ", fp); - if (info->fragments_min <= 1 && info->fragments_max == UINT_MAX) - fputs("ALL", fp); - else if (info->fragments_min > info->fragments_max) - fputs("NONE", fp); - else { - fprintf(fp, "%u", info->fragments_min); - if (info->fragments_min < info->fragments_max) - fprintf(fp, " - %u", info->fragments_max); - } - putc('\n', fp); - - fputs("buffer_size: ", fp); - if (info->buffer_size_min <= 1 && - info->buffer_size_max == ULONG_MAX) - fputs("ALL", fp); - else if (info->buffer_size_min > info->buffer_size_max) - fputs("NONE", fp); - else { - fprintf(fp, "%lu", (unsigned long)info->buffer_size_min); - if (info->buffer_size_min < info->buffer_size_max) - fprintf(fp, " - %lu", (unsigned long)info->buffer_size_max); - } - putc('\n', fp); - - return 0; -} - int snd_pcm_dump_hw_params_fail(snd_pcm_hw_params_t *params, FILE *fp) { int k; - if (params->fail_mask == 0) + if (params->fail_mask == 0) { + fprintf(fp, "unknown hw_params failure reason\n"); return 0; + } fprintf(fp, "hw_params failed on the following field value(s):\n"); for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { if (!(params->fail_mask & (1U << k))) continue; switch (k) { case SND_PCM_HW_PARAM_ACCESS: - fprintf(fp, "access: %s\n", assoc(params->access, accesses)); + fprintf(fp, "access: %s\n", assoc(params->access, snd_pcm_access_names)); break; case SND_PCM_HW_PARAM_FORMAT: - fprintf(fp, "format: %s\n", assoc(params->format, formats)); + fprintf(fp, "format: %s\n", assoc(params->format, snd_pcm_format_names)); break; case SND_PCM_HW_PARAM_SUBFORMAT: - fprintf(fp, "subformat: %s\n", assoc(params->subformat, subformats)); + fprintf(fp, "subformat: %s\n", assoc(params->subformat, snd_pcm_subformat_names)); break; case SND_PCM_HW_PARAM_CHANNELS: fprintf(fp, "channels: %d\n", params->channels); @@ -824,21 +659,23 @@ int snd_pcm_dump_hw_params_fail(snd_pcm_hw_params_t *params, FILE *fp) int snd_pcm_dump_sw_params_fail(snd_pcm_sw_params_t *params, FILE *fp) { int k; - if (params->fail_mask == 0) + if (params->fail_mask == 0) { + fprintf(fp, "unknown sw_params failure reason\n"); return 0; + } fprintf(fp, "sw_params failed on the following field value(s):\n"); for (k = 0; k <= SND_PCM_SW_PARAM_LAST; ++k) { if (!(params->fail_mask & (1U << k))) continue; switch (k) { case SND_PCM_SW_PARAM_START_MODE: - fprintf(fp, "start_mode: %s\n", assoc(params->start_mode, starts)); + fprintf(fp, "start_mode: %s\n", assoc(params->start_mode, snd_pcm_start_mode_names)); break; case SND_PCM_SW_PARAM_READY_MODE: - fprintf(fp, "ready_mode: %s\n", assoc(params->ready_mode, readys)); + fprintf(fp, "ready_mode: %s\n", assoc(params->ready_mode, snd_pcm_ready_mode_names)); break; case SND_PCM_SW_PARAM_XRUN_MODE: - fprintf(fp, "xrun_mode: %s\n", assoc(params->xrun_mode, xruns)); + fprintf(fp, "xrun_mode: %s\n", assoc(params->xrun_mode, snd_pcm_xrun_mode_names)); break; case SND_PCM_SW_PARAM_AVAIL_MIN: fprintf(fp, "avail_min: %ld\n", (long)params->avail_min); @@ -863,7 +700,7 @@ int snd_pcm_dump_sw_params_fail(snd_pcm_sw_params_t *params, FILE *fp) int snd_pcm_dump_status(snd_pcm_status_t *status, FILE *fp) { assert(status); - fprintf(fp, "state : %s\n", assoc(status->state, states)); + fprintf(fp, "state : %s\n", assoc(status->state, snd_pcm_state_names)); fprintf(fp, "trigger_time: %ld.%06ld\n", status->trigger_time.tv_sec, status->trigger_time.tv_usec); fprintf(fp, "tstamp : %ld.%06ld\n", @@ -882,27 +719,25 @@ int snd_pcm_dump(snd_pcm_t *pcm, FILE *fp) return 0; } -const char *snd_pcm_format_name(int format) +const char *snd_pcm_format_name(unsigned int format) { - assoc_t *a = assoc_value(format, formats); - if (a) - return a->name; - return 0; + assert(format <= SND_PCM_FORMAT_LAST); + return snd_pcm_format_names[format]; } -const char *snd_pcm_format_description(int format) +const char *snd_pcm_format_description(unsigned int format) { - assoc_t *a = assoc_value(format, formats); - if (a) - return a->desc; - return "Unknown"; + assert(format <= SND_PCM_FORMAT_LAST); + return snd_pcm_format_descriptions[format]; } int snd_pcm_format_value(const char* name) { - assoc_t *a = assoc_name(name, formats); - if (a) - return a->value; + unsigned int format; + for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) + if (snd_pcm_format_names[format] && + strcasecmp(name, snd_pcm_format_names[format]) == 0) + return format; return -1; } @@ -1621,1352 +1456,304 @@ struct { #define SND_PCM_RATES (sizeof(snd_pcm_rates) / sizeof(snd_pcm_rates[0])) -int snd_pcm_hw_info_rules_access(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_params_info(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_info_t *info) { - int k; - unsigned int rel, mask; - snd_pcm_hw_info_t i; - rel = *rules & SND_PCM_RULE_REL_MASK; - switch (rel) { - case SND_PCM_RULE_REL_LT: - mask = (1U << params->access) - 1; + int err = snd_pcm_hw_info_to_params(pcm, info, params); + assert(err >= 0); + return snd_pcm_hw_params(pcm, params); +} + +struct _snd_pcm_strategy { + unsigned int badness_min, badness_max; + int (*choose_param)(const snd_pcm_hw_info_t *info, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy); + long (*next_value)(const snd_pcm_hw_info_t *info, + unsigned int param, + long value, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy); + int (*min_badness)(const snd_pcm_hw_info_t *info, + unsigned int max_badness, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy); + void *private; + void (*free)(snd_pcm_strategy_t *strategy); +}; + +/* Independent badness */ +typedef struct _snd_pcm_strategy_simple snd_pcm_strategy_simple_t; + +struct _snd_pcm_strategy_simple { + int valid; + unsigned int order; + long (*next_value)(const snd_pcm_hw_info_t *info, + unsigned int param, + long value, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par); + unsigned int (*min_badness)(const snd_pcm_hw_info_t *info, + unsigned int param, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par); + void *private; + void (*free)(snd_pcm_strategy_simple_t *strategy); +}; + +typedef struct _snd_pcm_strategy_simple_near { + long best; + unsigned int mul; +} snd_pcm_strategy_simple_near_t; + +typedef struct _snd_pcm_strategy_simple_choices { + unsigned int count; + /* choices need to be sorted on ascending badness */ + snd_pcm_strategy_simple_choices_list_t *choices; +} snd_pcm_strategy_simple_choices_t; + +static inline unsigned int hweight32(u_int32_t v) +{ + v = (v & 0x55555555) + ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + v = (v & 0x0F0F0F0F) + ((v >> 4) & 0x0F0F0F0F); + v = (v & 0x00FF00FF) + ((v >> 8) & 0x00FF00FF); + return (v & 0x0000FFFF) + ((v >> 16) & 0x0000FFFF); +} + +static inline unsigned int ld2(u_int32_t v) +{ + unsigned r = 0; + + if (v >= 0x10000) { + v >>= 16; + r += 16; + } + if (v >= 0x100) { + v >>= 8; + r += 8; + } + if (v >= 0x10) { + v >>= 4; + r += 4; + } + if (v >= 4) { + v >>= 2; + r += 2; + } + if (v >= 2) + r++; + return r; +} + +typedef struct { + enum { MASK, MINMAX } type; + char **names; + unsigned int last; +} par_desc_t; + +par_desc_t hw_pars[SND_PCM_HW_PARAM_LAST + 1] = { + [SND_PCM_HW_PARAM_ACCESS] = { + type: MASK, + names: snd_pcm_access_names, + last: SND_PCM_ACCESS_LAST, + }, + [SND_PCM_HW_PARAM_FORMAT] = { + type: MASK, + names: snd_pcm_format_names, + last: SND_PCM_FORMAT_LAST, + }, + [SND_PCM_HW_PARAM_SUBFORMAT] = { + type: MASK, + names: snd_pcm_subformat_names, + last: SND_PCM_SUBFORMAT_LAST, + }, + [SND_PCM_HW_PARAM_CHANNELS] = { + type: MINMAX, + names: 0, + last: 0, + }, + [SND_PCM_HW_PARAM_RATE] = { + type: MINMAX, + names: 0, + last: 0, + }, + [SND_PCM_HW_PARAM_FRAGMENT_SIZE] = { + type: MINMAX, + names: 0, + last: 0, + }, + [SND_PCM_HW_PARAM_FRAGMENTS] = { + type: MINMAX, + names: 0, + last: 0, + }, + [SND_PCM_HW_PARAM_BUFFER_SIZE] = { + type: MINMAX, + names: 0, + last: 0, + }, +}; + +unsigned int snd_pcm_hw_info_par_get_mask(const snd_pcm_hw_info_t *info, + unsigned int param) +{ + switch (param) { + case SND_PCM_HW_PARAM_ACCESS: + return info->access_mask; + case SND_PCM_HW_PARAM_FORMAT: + return info->format_mask; + case SND_PCM_HW_PARAM_SUBFORMAT: + return info->subformat_mask; + default: + assert(0); + return 0; + } +} + +void snd_pcm_hw_info_par_get_minmax(const snd_pcm_hw_info_t *info, + unsigned int param, + unsigned long *min, unsigned long *max) +{ + switch (param) { + case SND_PCM_HW_PARAM_ACCESS: + case SND_PCM_HW_PARAM_FORMAT: + case SND_PCM_HW_PARAM_SUBFORMAT: + { + unsigned int mask = snd_pcm_hw_info_par_get_mask(info, param); + if (!mask) { + *min = 32; + *max = 0; + } else { + *min = ffs(mask) - 1; + *max = ld2(mask); + } break; - case SND_PCM_RULE_REL_LE: - mask = (1U << (params->access + 1)) - 1; + } + case SND_PCM_HW_PARAM_CHANNELS: + *min = info->channels_min; + *max = info->channels_max; break; - case SND_PCM_RULE_REL_GT: - mask = ~((1U << (params->access + 1)) - 1); + case SND_PCM_HW_PARAM_RATE: + *min = info->rate_min; + *max = info->rate_max; break; - case SND_PCM_RULE_REL_GE: - mask = ~((1U << params->access) - 1); + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + *min = info->fragment_size_min; + *max = info->fragment_size_max; break; - case SND_PCM_RULE_REL_EQ: - mask = 1U << params->access; + case SND_PCM_HW_PARAM_FRAGMENTS: + *min = info->fragments_min; + *max = info->fragments_max; break; - case SND_PCM_RULE_REL_NEAR: - { - unsigned int diff = 0; - int n; - for (diff = 0; diff < 32; ++diff) { - n = (int)params->access - (int)diff; - if (n >= 0) { - unsigned int bit = 1U << n; - if (info->access_mask & bit) { - i = *info; - i.access_mask = bit; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - } else if (params->access + diff > SND_PCM_ACCESS_LAST) - break; - if (diff == 0) - continue; - n = params->access + diff; - if (n <= SND_PCM_ACCESS_LAST) { - unsigned int bit = 1U << n; - if (info->access_mask & bit) { - i = *info; - i.access_mask = bit; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - } - } - info->access_mask = 0; - return -EINVAL; - } - case SND_PCM_RULE_REL_BITS: - mask = params->access; + case SND_PCM_HW_PARAM_BUFFER_SIZE: + *min = info->buffer_size_min; + *max = info->buffer_size_max; break; default: assert(0); - return -EINVAL; } - info->access_mask &= mask; - if (info->access_mask == 0) - return -EINVAL; - switch (rel) { - case SND_PCM_RULE_REL_LE: - case SND_PCM_RULE_REL_LT: - for (k = SND_PCM_ACCESS_LAST; k >= 0; --k) { - if (!(info->access_mask & (1U << k))) - continue; - i = *info; - i.access_mask = 1U << k; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - info->access_mask = 0; - return -EINVAL; - default: - for (k = 0; k <= SND_PCM_ACCESS_LAST; ++k) { - if (!(info->access_mask & (1U << k))) - continue; - i = *info; - i.access_mask = 1U << k; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - info->access_mask = 0; - return -EINVAL; - } - return 0; } -int snd_pcm_hw_info_rules_format(snd_pcm_t *pcm, - snd_pcm_hw_info_t *info, - snd_pcm_hw_params_t *params, - unsigned int count, int *rules) +void snd_pcm_hw_info_par_set_mask(snd_pcm_hw_info_t *info, unsigned int param, + unsigned int v) { - int k; - unsigned int rel, mask; - snd_pcm_hw_info_t i; - rel = *rules & SND_PCM_RULE_REL_MASK; - switch (rel) { - case SND_PCM_RULE_REL_LT: - mask = (1U << params->format) - 1; - break; - case SND_PCM_RULE_REL_LE: - mask = (1U << (params->format + 1)) - 1; - break; - case SND_PCM_RULE_REL_GT: - mask = ~((1U << (params->format + 1)) - 1); + switch (param) { + case SND_PCM_HW_PARAM_ACCESS: + info->access_mask = v; break; - case SND_PCM_RULE_REL_GE: - mask = ~((1U << params->format) - 1); + case SND_PCM_HW_PARAM_FORMAT: + info->format_mask = v; break; - case SND_PCM_RULE_REL_EQ: - mask = 1U << params->format; + case SND_PCM_HW_PARAM_SUBFORMAT: + info->subformat_mask = v; break; - case SND_PCM_RULE_REL_NEAR: - { - unsigned int diff = 0; - int n; - for (diff = 0; diff < 32; ++diff) { - n = (int)params->format - (int)diff; - if (n >= 0) { - unsigned int bit = 1U << n; - if (info->format_mask & bit) { - i = *info; - i.format_mask = bit; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - } else if (params->format + diff > SND_PCM_FORMAT_LAST) - break; - if (diff == 0) - continue; - n = params->format + diff; - if (n <= SND_PCM_FORMAT_LAST) { - unsigned int bit = 1U << n; - if (info->format_mask & bit) { - i = *info; - i.format_mask = bit; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - } - } - info->format_mask = 0; - return -EINVAL; - } - case SND_PCM_RULE_REL_BITS: - mask = params->format; - break; - default: - assert(0); - return -EINVAL; - } - info->format_mask &= mask; - if (info->format_mask == 0) - return -EINVAL; - switch (rel) { - case SND_PCM_RULE_REL_LE: - case SND_PCM_RULE_REL_LT: - for (k = SND_PCM_FORMAT_LAST; k >= 0; --k) { - if (!(info->format_mask & (1U << k))) - continue; - i = *info; - i.format_mask = 1U << k; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - info->format_mask = 0; - return -EINVAL; - default: - for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) { - if (!(info->format_mask & (1U << k))) - continue; - i = *info; - i.format_mask = 1U << k; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - info->format_mask = 0; - return -EINVAL; - } - return 0; -} - -int snd_pcm_hw_info_rules_subformat(snd_pcm_t *pcm, - snd_pcm_hw_info_t *info, - snd_pcm_hw_params_t *params, - unsigned int count, int *rules) -{ - int k; - unsigned int rel, mask; - snd_pcm_hw_info_t i; - rel = *rules & SND_PCM_RULE_REL_MASK; - switch (rel) { - case SND_PCM_RULE_REL_LT: - mask = (1U << params->subformat) - 1; - break; - case SND_PCM_RULE_REL_LE: - mask = (1U << (params->subformat + 1)) - 1; - break; - case SND_PCM_RULE_REL_GT: - mask = ~((1U << (params->subformat + 1)) - 1); - break; - case SND_PCM_RULE_REL_GE: - mask = ~((1U << params->subformat) - 1); - break; - case SND_PCM_RULE_REL_EQ: - mask = 1U << params->subformat; - break; - case SND_PCM_RULE_REL_NEAR: - { - unsigned int diff = 0; - int n; - for (diff = 0; diff < 32; ++diff) { - n = (int)params->subformat - (int)diff; - if (n >= 0) { - unsigned int bit = 1U << n; - if (info->subformat_mask & bit) { - i = *info; - i.subformat_mask = bit; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - } else if (params->subformat + diff > SND_PCM_SUBFORMAT_LAST) - break; - if (diff == 0) - continue; - n = params->subformat + diff; - if (n <= SND_PCM_SUBFORMAT_LAST) { - unsigned int bit = 1U << n; - if (info->subformat_mask & bit) { - i = *info; - i.subformat_mask = bit; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - } - } - info->subformat_mask = 0; - return -EINVAL; - } - case SND_PCM_RULE_REL_BITS: - mask = params->subformat; - break; - default: - assert(0); - return -EINVAL; - } - info->subformat_mask &= mask; - if (info->subformat_mask == 0) - return -EINVAL; - switch (rel) { - case SND_PCM_RULE_REL_LE: - case SND_PCM_RULE_REL_LT: - for (k = SND_PCM_SUBFORMAT_LAST; k >= 0; --k) { - if (!(info->subformat_mask & (1U << k))) - continue; - i = *info; - i.subformat_mask = 1U << k; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - info->subformat_mask = 0; - return -EINVAL; - default: - for (k = 0; k <= SND_PCM_SUBFORMAT_LAST; ++k) { - if (!(info->subformat_mask & (1U << k))) - continue; - i = *info; - i.subformat_mask = 1U << k; - if (snd_pcm_hw_info(pcm, &i) >= 0 && - snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - info->subformat_mask = 0; - return -EINVAL; - } - return 0; -} - -int snd_pcm_hw_info_rules_channels(snd_pcm_t *pcm, - snd_pcm_hw_info_t *info, - snd_pcm_hw_params_t *params, - unsigned int count, int *rules) -{ - int err; - unsigned int rel; - snd_pcm_hw_info_t i; - rel = *rules & SND_PCM_RULE_REL_MASK; - switch (rel) { - case SND_PCM_RULE_REL_LT: - if (info->channels_max > params->channels - 1) - info->channels_max = params->channels - 1; - goto _le; - case SND_PCM_RULE_REL_LE: - if (info->channels_max > params->channels) - info->channels_max = params->channels; - _le: - while (1) { - if (info->channels_min > info->channels_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->channels_min = i.channels_min; - info->channels_max = i.channels_max; - return err; - } - i.channels_min = i.channels_max; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - info->channels_max--; - } - break; - case SND_PCM_RULE_REL_GT: - if (info->channels_min < params->channels + 1) - info->channels_min = params->channels + 1; - goto _ge; - case SND_PCM_RULE_REL_GE: - if (info->channels_min < params->channels) - info->channels_min = params->channels; - _ge: - while (1) { - if (info->channels_min > info->channels_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->channels_min = i.channels_min; - info->channels_max = i.channels_max; - return err; - } - i.channels_max = i.channels_min; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - info->channels_min++; - } - break; - case SND_PCM_RULE_REL_EQ: - info->channels_min = params->channels; - info->channels_max = params->channels; - return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1); - break; - case SND_PCM_RULE_REL_NEAR: - { - unsigned int max1, min2; - int err1 = -EINVAL, err2 = -EINVAL; - max1 = params->channels; - min2 = params->channels+1; - if (info->channels_min <= max1) { - i = *info; - i.channels_max = max1; - err1 = snd_pcm_hw_info(pcm, &i); - /* shortcut for common case */ - if (err1 >= 0 && max1 == i.channels_max) { - i.channels_min = max1; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - i.channels_max = max1 - 1; - err1 = snd_pcm_hw_info(pcm, &i); - } - max1 = i.channels_max; - } - if (min2 <= info->channels_max) { - i = *info; - i.channels_min = min2; - err2 = snd_pcm_hw_info(pcm, &i); - min2 = i.channels_min; - } - while (1) { - unsigned int channels; - if (err1 >= 0) { - if (err2 >= 0) { - if (params->channels - max1 < - min2 - params->channels) - channels = max1; - else - channels = min2; - } else - channels = max1; - } else if (err2 >= 0) - channels = min2; - else { - info->channels_min = UINT_MAX; - info->channels_max = 0; - return -EINVAL; - } - i = *info; - i.channels_min = i.channels_max = channels; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - if (channels == max1) { - max1--; - i = *info; - i.channels_max = max1; - err1 = snd_pcm_hw_info(pcm, &i); - max1 = i.channels_max; - } else { - min2++; - i = *info; - i.channels_min = min2; - err2 = snd_pcm_hw_info(pcm, &i); - min2 = i.channels_min; - } - - } - break; - } - case SND_PCM_RULE_REL_BITS: - { - unsigned int k; - for (k = info->channels_min; k < 32; ++k) { - if (!(params->channels & (1U << k))) - continue; - info->channels_min = k; - if (info->channels_min > info->channels_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->channels_min = i.channels_min; - info->channels_max = i.channels_max; - return err; - } - i.channels_max = i.channels_min; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - break; - } - default: - assert(0); - return -EINVAL; - } - return 0; -} - -int snd_pcm_hw_info_rules_rate(snd_pcm_t *pcm, - snd_pcm_hw_info_t *info, - snd_pcm_hw_params_t *params, - unsigned int count, int *rules) -{ - int err; - unsigned int rel; - snd_pcm_hw_info_t i; - rel = *rules & SND_PCM_RULE_REL_MASK; - switch (rel) { - case SND_PCM_RULE_REL_LT: - if (info->rate_max > params->rate - 1) - info->rate_max = params->rate - 1; - goto _le; - case SND_PCM_RULE_REL_LE: - if (info->rate_max > params->rate) - info->rate_max = params->rate; - _le: - while (1) { - if (info->rate_min > info->rate_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->rate_min = i.rate_min; - info->rate_max = i.rate_max; - return err; - } - i.rate_min = i.rate_max; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - info->rate_max--; - } - break; - case SND_PCM_RULE_REL_GT: - if (info->rate_min < params->rate + 1) - info->rate_min = params->rate + 1; - goto _ge; - case SND_PCM_RULE_REL_GE: - if (info->rate_min < params->rate) - info->rate_min = params->rate; - _ge: - while (1) { - if (info->rate_min > info->rate_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->rate_min = i.rate_min; - info->rate_max = i.rate_max; - return err; - } - i.rate_max = i.rate_min; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - info->rate_min++; - } - break; - case SND_PCM_RULE_REL_EQ: - info->rate_min = params->rate; - info->rate_max = params->rate; - return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1); - break; - case SND_PCM_RULE_REL_NEAR: - { - unsigned int max1, min2; - int err1 = -EINVAL, err2 = -EINVAL; - max1 = params->rate; - min2 = params->rate+1; - if (info->rate_min <= max1) { - i = *info; - i.rate_max = max1; - err1 = snd_pcm_hw_info(pcm, &i); - /* shortcut for common case */ - if (err1 >= 0 && max1 == i.rate_max) { - i.rate_min = max1; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - i.rate_max = max1 - 1; - err1 = snd_pcm_hw_info(pcm, &i); - } - max1 = i.rate_max; - } - if (min2 <= info->rate_max) { - i = *info; - i.rate_min = min2; - err2 = snd_pcm_hw_info(pcm, &i); - min2 = i.rate_min; - } - while (1) { - unsigned int rate; - if (err1 >= 0) { - if (err2 >= 0) { - if (params->rate - max1 < - min2 - params->rate) - rate = max1; - else - rate = min2; - } else - rate = max1; - } else if (err2 >= 0) - rate = min2; - else { - info->rate_min = UINT_MAX; - info->rate_max = 0; - return -EINVAL; - } - i = *info; - i.rate_min = i.rate_max = rate; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - if (rate == max1) { - max1--; - i = *info; - i.rate_max = max1; - err1 = snd_pcm_hw_info(pcm, &i); - max1 = i.rate_max; - } else { - min2++; - i = *info; - i.rate_min = min2; - err2 = snd_pcm_hw_info(pcm, &i); - min2 = i.rate_min; - } - - } - break; - } - case SND_PCM_RULE_REL_BITS: - { - unsigned int k; - for (k = 0; k < SND_PCM_RATES; ++k) { - if (snd_pcm_rates[k].rate < info->rate_min) - continue; - if (!(params->rate & snd_pcm_rates[k].flag)) - continue; - info->rate_min = snd_pcm_rates[k].rate; - if (info->rate_min > info->rate_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->rate_min = i.rate_min; - info->rate_max = i.rate_max; - return err; - } - i.rate_max = i.rate_min; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - break; - } - default: - assert(0); - return -EINVAL; - } - return 0; -} - -int snd_pcm_hw_info_rules_fragment_size(snd_pcm_t *pcm, - snd_pcm_hw_info_t *info, - snd_pcm_hw_params_t *params, - unsigned int count, int *rules) -{ - int err; - unsigned int rel; - snd_pcm_hw_info_t i; - rel = *rules & SND_PCM_RULE_REL_MASK; - switch (rel) { - case SND_PCM_RULE_REL_LT: - if (info->fragment_size_max > params->fragment_size - 1) - info->fragment_size_max = params->fragment_size - 1; - goto _le; - case SND_PCM_RULE_REL_LE: - if (info->fragment_size_max > params->fragment_size) - info->fragment_size_max = params->fragment_size; - _le: - while (1) { - if (info->fragment_size_min > info->fragment_size_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->fragment_size_min = i.fragment_size_min; - info->fragment_size_max = i.fragment_size_max; - return err; - } - i.fragment_size_min = i.fragment_size_max; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - info->fragment_size_max--; - } - break; - case SND_PCM_RULE_REL_GT: - if (info->fragment_size_min < params->fragment_size + 1) - info->fragment_size_min = params->fragment_size + 1; - goto _ge; - case SND_PCM_RULE_REL_GE: - if (info->fragment_size_min < params->fragment_size) - info->fragment_size_min = params->fragment_size; - _ge: - while (1) { - if (info->fragment_size_min > info->fragment_size_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->fragment_size_min = i.fragment_size_min; - info->fragment_size_max = i.fragment_size_max; - return err; - } - i.fragment_size_max = i.fragment_size_min; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - info->fragment_size_min++; - } - break; - case SND_PCM_RULE_REL_EQ: - info->fragment_size_min = params->fragment_size; - info->fragment_size_max = params->fragment_size; - return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1); - break; - case SND_PCM_RULE_REL_NEAR: - { - size_t max1, min2; - int err1 = -EINVAL, err2 = -EINVAL; - max1 = params->fragment_size; - min2 = params->fragment_size+1; - if (info->fragment_size_min <= max1) { - i = *info; - i.fragment_size_max = max1; - err1 = snd_pcm_hw_info(pcm, &i); - /* shortcut for common case */ - if (err1 >= 0 && max1 == i.fragment_size_max) { - i.fragment_size_min = max1; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - i.fragment_size_max = max1 - 1; - err1 = snd_pcm_hw_info(pcm, &i); - } - max1 = i.fragment_size_max; - } - if (min2 <= info->fragment_size_max) { - i = *info; - i.fragment_size_min = min2; - err2 = snd_pcm_hw_info(pcm, &i); - min2 = i.fragment_size_min; - } - while (1) { - size_t fragment_size; - if (err1 >= 0) { - if (err2 >= 0) { - if (params->fragment_size - max1 < - min2 - params->fragment_size) - fragment_size = max1; - else - fragment_size = min2; - } else - fragment_size = max1; - } else if (err2 >= 0) - fragment_size = min2; - else { - info->fragment_size_min = ULONG_MAX; - info->fragment_size_max = 0; - return -EINVAL; - } - i = *info; - i.fragment_size_min = i.fragment_size_max = fragment_size; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - if (fragment_size == max1) { - max1--; - i = *info; - i.fragment_size_max = max1; - err1 = snd_pcm_hw_info(pcm, &i); - max1 = i.fragment_size_max; - } else { - min2++; - i = *info; - i.fragment_size_min = min2; - err2 = snd_pcm_hw_info(pcm, &i); - min2 = i.fragment_size_min; - } - - } - break; - } - case SND_PCM_RULE_REL_BITS: - { - unsigned int k; - for (k = info->fragment_size_min; k < 32; ++k) { - if (!(params->fragment_size & (1U << k))) - continue; - info->fragment_size_min = 1U << k; - if (info->fragment_size_min > info->fragment_size_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->fragment_size_min = i.fragment_size_min; - info->fragment_size_max = i.fragment_size_max; - return err; - } - i.fragment_size_max = i.fragment_size_min; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - break; - } - default: - assert(0); - return -EINVAL; - } - return 0; -} - -int snd_pcm_hw_info_rules_fragments(snd_pcm_t *pcm, - snd_pcm_hw_info_t *info, - snd_pcm_hw_params_t *params, - unsigned int count, int *rules) -{ - int err; - unsigned int rel; - snd_pcm_hw_info_t i; - rel = *rules & SND_PCM_RULE_REL_MASK; - switch (rel) { - case SND_PCM_RULE_REL_LT: - if (info->fragments_max > params->fragments - 1) - info->fragments_max = params->fragments - 1; - goto _le; - case SND_PCM_RULE_REL_LE: - if (info->fragments_max > params->fragments) - info->fragments_max = params->fragments; - _le: - while (1) { - if (info->fragments_min > info->fragments_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->fragments_min = i.fragments_min; - info->fragments_max = i.fragments_max; - return err; - } - i.fragments_min = i.fragments_max; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - info->fragments_max--; - } - break; - case SND_PCM_RULE_REL_GT: - if (info->fragments_min < params->fragments + 1) - info->fragments_min = params->fragments + 1; - goto _ge; - case SND_PCM_RULE_REL_GE: - if (info->fragments_min < params->fragments) - info->fragments_min = params->fragments; - _ge: - while (1) { - if (info->fragments_min > info->fragments_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->fragments_min = i.fragments_min; - info->fragments_max = i.fragments_max; - return err; - } - i.fragments_max = i.fragments_min; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - info->fragments_min++; - } - break; - case SND_PCM_RULE_REL_EQ: - info->fragments_min = params->fragments; - info->fragments_max = params->fragments; - return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1); - break; - case SND_PCM_RULE_REL_NEAR: - { - unsigned int max1, min2; - int err1 = -EINVAL, err2 = -EINVAL; - max1 = params->fragments; - min2 = params->fragments+1; - if (info->fragments_min <= max1) { - i = *info; - i.fragments_max = max1; - err1 = snd_pcm_hw_info(pcm, &i); - /* shortcut for common case */ - if (err1 >= 0 && max1 == i.fragments_max) { - i.fragments_min = max1; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - i.fragments_max = max1 - 1; - err1 = snd_pcm_hw_info(pcm, &i); - } - max1 = i.fragments_max; - } - if (min2 <= info->fragments_max) { - i = *info; - i.fragments_min = min2; - err2 = snd_pcm_hw_info(pcm, &i); - min2 = i.fragments_min; - } - while (1) { - unsigned int fragments; - if (err1 >= 0) { - if (err2 >= 0) { - if (params->fragments - max1 < - min2 - params->fragments) - fragments = max1; - else - fragments = min2; - } else - fragments = max1; - } else if (err2 >= 0) - fragments = min2; - else { - info->fragments_min = UINT_MAX; - info->fragments_max = 0; - return -EINVAL; - } - i = *info; - i.fragments_min = i.fragments_max = fragments; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - if (fragments == max1) { - max1--; - i = *info; - i.fragments_max = max1; - err1 = snd_pcm_hw_info(pcm, &i); - max1 = i.fragments_max; - } else { - min2++; - i = *info; - i.fragments_min = min2; - err2 = snd_pcm_hw_info(pcm, &i); - min2 = i.fragments_min; - } - - } - break; - } - case SND_PCM_RULE_REL_BITS: - { - unsigned int k; - for (k = info->fragments_min; k < 32; ++k) { - if (!(params->fragments & (1U << k))) - continue; - info->fragments_min = k; - if (info->fragments_min > info->fragments_max) - return -EINVAL; - i = *info; - err = snd_pcm_hw_info(pcm, &i); - if (err < 0) { - info->fragments_min = i.fragments_min; - info->fragments_max = i.fragments_max; - return err; - } - i.fragments_max = i.fragments_min; - if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) { - *info = i; - return 0; - } - } - break; - } - default: - assert(0); - return -EINVAL; - } - return 0; -} - -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) -{ - unsigned int par; - if (count == 0) - return snd_pcm_hw_info(pcm, info); - par = rules[0] & SND_PCM_RULE_PAR_MASK; - switch (par) { - case SND_PCM_HW_PARAM_ACCESS: - return snd_pcm_hw_info_rules_access(pcm, info, params, count, rules); - case SND_PCM_HW_PARAM_FORMAT: - return snd_pcm_hw_info_rules_format(pcm, info, params, count, rules); - case SND_PCM_HW_PARAM_SUBFORMAT: - return snd_pcm_hw_info_rules_subformat(pcm, info, params, count, rules); - case SND_PCM_HW_PARAM_CHANNELS: - return snd_pcm_hw_info_rules_channels(pcm, info, params, count, rules); - case SND_PCM_HW_PARAM_RATE: - return snd_pcm_hw_info_rules_rate(pcm, info, params, count, rules); - case SND_PCM_HW_PARAM_FRAGMENT_SIZE: - return snd_pcm_hw_info_rules_fragment_size(pcm, info, params, count, rules); - case SND_PCM_HW_PARAM_FRAGMENTS: - return snd_pcm_hw_info_rules_fragments(pcm, info, params, count, rules); - default: - assert(0); - return -EINVAL; - } -} - -int snd_pcm_hw_info_rulesv(snd_pcm_t *pcm, - snd_pcm_hw_info_t *info, - snd_pcm_hw_params_t *params, ...) -{ - va_list arg; - unsigned int count = 0; - int rules[32]; - va_start(arg, params); - while (1) { - int i = va_arg(arg, int); - if (i == -1) - break; - rules[count++] = i; - } - va_end(arg); - return snd_pcm_hw_info_rules(pcm, info, params, count, rules); -} - -int snd_pcm_hw_params_rules(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, - unsigned int count, int *rules) -{ - int err; - snd_pcm_hw_info_t info; - unsigned int k; - snd_pcm_hw_params_to_info(params, &info); - for (k = 0; k < count; ++k) { - switch (rules[k] & SND_PCM_RULE_PAR_MASK) { - case SND_PCM_HW_PARAM_ACCESS: - info.access_mask = ~0; - break; - case SND_PCM_HW_PARAM_FORMAT: - info.format_mask = ~0; - break; - case SND_PCM_HW_PARAM_SUBFORMAT: - info.subformat_mask = ~0; - break; - case SND_PCM_HW_PARAM_CHANNELS: - info.channels_min = 1; - info.channels_max = UINT_MAX; - break; - case SND_PCM_HW_PARAM_RATE: - info.rate_min = 0; - info.rate_max = UINT_MAX; - break; - case SND_PCM_HW_PARAM_FRAGMENT_SIZE: - info.fragment_size_min = 1; - info.fragment_size_max = ULONG_MAX; - info.buffer_size_min = 1; - info.buffer_size_max = ULONG_MAX; - break; - case SND_PCM_HW_PARAM_FRAGMENTS: - info.fragments_min = 1; - info.fragments_max = UINT_MAX; - info.buffer_size_min = 1; - info.buffer_size_max = ULONG_MAX; - break; - default: - assert(0); - break; - } - } - err = snd_pcm_hw_info_rules(pcm, &info, params, count, rules); - if (err < 0) { - snd_pcm_hw_info_to_params_fail(&info, params); - return err; - } - snd_pcm_hw_info_to_params(&info, params); - return snd_pcm_hw_params(pcm, params); -} - -int snd_pcm_hw_params_rulesv(snd_pcm_t *pcm, - snd_pcm_hw_params_t *params, ...) -{ - va_list arg; - unsigned int count = 0; - int rules[32]; - va_start(arg, params); - while (1) { - int i = va_arg(arg, int); - if (i == -1) - break; - rules[count++] = i; - } - va_end(arg); - return snd_pcm_hw_params_rules(pcm, params, count, rules); -} - -struct _snd_pcm_strategy { - int (*choose_param)(const snd_pcm_hw_info_t *info, - snd_pcm_t *pcm, - const snd_pcm_strategy_t *strategy); - long (*next_value)(const snd_pcm_hw_info_t *info, - unsigned int param, - long value, - snd_pcm_t *pcm, - const snd_pcm_strategy_t *strategy); - int (*min_badness)(const snd_pcm_hw_info_t *info, - unsigned int max_badness, - snd_pcm_t *pcm, - const snd_pcm_strategy_t *strategy); - void *private; - void (*free)(snd_pcm_strategy_t *strategy); -}; - -/* Independent badness */ -typedef struct _snd_pcm_strategy_simple snd_pcm_strategy_simple_t; - -struct _snd_pcm_strategy_simple { - int valid; - long (*next_value)(const snd_pcm_hw_info_t *info, - unsigned int param, - long value, - snd_pcm_t *pcm, - const snd_pcm_strategy_simple_t *par); - unsigned int (*min_badness)(const snd_pcm_hw_info_t *info, - unsigned int param, - snd_pcm_t *pcm, - const snd_pcm_strategy_simple_t *par); - void *private; - void (*free)(snd_pcm_strategy_simple_t *strategy); -}; - -typedef struct _snd_pcm_strategy_simple_near { - long best; - unsigned int mul; -} snd_pcm_strategy_simple_near_t; - -typedef struct _snd_pcm_strategy_simple_choices { - unsigned int count; - /* choices need to be sorted on ascending badness */ - snd_pcm_strategy_simple_choices_list_t *choices; -} snd_pcm_strategy_simple_choices_t; - -static inline unsigned int hweight32(u_int32_t v) -{ - v = (v & 0x55555555) + ((v >> 1) & 0x55555555); - v = (v & 0x33333333) + ((v >> 2) & 0x33333333); - v = (v & 0x0F0F0F0F) + ((v >> 4) & 0x0F0F0F0F); - v = (v & 0x00FF00FF) + ((v >> 8) & 0x00FF00FF); - return (v & 0x0000FFFF) + ((v >> 16) & 0x0000FFFF); -} - -static inline unsigned int ld2(u_int32_t v) -{ - unsigned r = 0; - - if (v >= 0x10000) { - v >>= 16; - r += 16; - } - if (v >= 0x100) { - v >>= 8; - r += 8; - } - if (v >= 0x10) { - v >>= 4; - r += 4; - } - if (v >= 4) { - v >>= 2; - r += 2; - } - if (v >= 2) - r++; - return r; -} - - -static unsigned long par_choices(const snd_pcm_hw_info_t *info, unsigned int param) -{ - switch (param) { - case SND_PCM_HW_PARAM_ACCESS: - return hweight32(info->access_mask); - case SND_PCM_HW_PARAM_FORMAT: - return hweight32(info->format_mask); - case SND_PCM_HW_PARAM_SUBFORMAT: - return hweight32(info->subformat_mask); - case SND_PCM_HW_PARAM_CHANNELS: - return info->channels_max - info->channels_min + 1; - case SND_PCM_HW_PARAM_RATE: - return info->rate_max - info->rate_min + 1; - case SND_PCM_HW_PARAM_FRAGMENT_SIZE: - return info->fragment_size_max - info->fragment_size_min + 1; - case SND_PCM_HW_PARAM_FRAGMENTS: - return info->fragments_max - info->fragments_min + 1; - case SND_PCM_HW_PARAM_BUFFER_SIZE: - return info->buffer_size_max - info->buffer_size_min + 1; default: assert(0); - return 0; } } -static unsigned long par_refine_min(snd_pcm_hw_info_t *info, +void snd_pcm_hw_info_par_set_minmax(snd_pcm_hw_info_t *info, unsigned int param, - unsigned long value) + unsigned long min, unsigned long max) { - int i; switch (param) { case SND_PCM_HW_PARAM_ACCESS: - if (value >= 32) { - info->access_mask = 0; - return 32; - } else - info->access_mask &= ~((1 << value) - 1); - i = ffs(info->access_mask); - if (i == 0) - return 32; - return i - 1; case SND_PCM_HW_PARAM_FORMAT: - if (value >= 32) { - info->format_mask = 0; - return 32; - } else - info->format_mask &= ~((1 << value) - 1); - i = ffs(info->format_mask); - if (i == 0) - return 32; - return i - 1; case SND_PCM_HW_PARAM_SUBFORMAT: - if (value >= 32) { - info->subformat_mask = 0; - return 32; - } else - info->subformat_mask &= ~((1 << value) - 1); - i = ffs(info->subformat_mask); - if (i == 0) - return 32; - return i - 1; - case SND_PCM_HW_PARAM_CHANNELS: - if (value > info->channels_min) - info->channels_min = value; - return info->channels_min; - case SND_PCM_HW_PARAM_RATE: - if (value > info->rate_min) - info->rate_min = value; - return info->rate_min; - case SND_PCM_HW_PARAM_FRAGMENT_SIZE: - if (value > info->fragment_size_min) - info->fragment_size_min = value; - return info->fragment_size_min; - case SND_PCM_HW_PARAM_FRAGMENTS: - if (value > info->fragments_min) - info->fragments_min = value; - return info->fragments_min; - case SND_PCM_HW_PARAM_BUFFER_SIZE: - if (value > info->buffer_size_min) - info->buffer_size_min = value; - return info->buffer_size_min; - default: - assert(0); - return 0; + { + unsigned int mask; + if (min >= 32 || max <= 0 || min > max) { + snd_pcm_hw_info_par_set_mask(info, param, 0); + break; + } + if (max >= 31) { + max = 31; + if (min <= 0) + break; + } + mask = snd_pcm_hw_info_par_get_mask(info, param); + mask &= ((1U << (max - min + 1)) - 1) << min; + snd_pcm_hw_info_par_set_mask(info, param, mask); + break; } -} - -static unsigned long par_refine_max(snd_pcm_hw_info_t *info, - unsigned int param, - unsigned long value) -{ - switch (param) { - case SND_PCM_HW_PARAM_ACCESS: - if (value < 31) - info->access_mask &= (1 << (value + 1)) - 1; - return ld2(info->access_mask); - case SND_PCM_HW_PARAM_FORMAT: - if (value < 31) - info->format_mask &= (1 << (value + 1)) - 1; - return ld2(info->format_mask); - case SND_PCM_HW_PARAM_SUBFORMAT: - if (value < 31) - info->subformat_mask &= (1 << (value + 1)) - 1; - return ld2(info->subformat_mask); case SND_PCM_HW_PARAM_CHANNELS: - if (value < info->channels_max) - info->channels_max = value; - return info->channels_max; + info->channels_min = min; + info->channels_max = max; + break; case SND_PCM_HW_PARAM_RATE: - if (value < info->rate_max) - info->rate_max = value; - return info->rate_max; + info->rate_min = min; + info->rate_max = max; + break; case SND_PCM_HW_PARAM_FRAGMENT_SIZE: - if (value < info->fragment_size_max) - info->fragment_size_max = value; - return info->fragment_size_max; + info->fragment_size_min = min; + info->fragment_size_max = max; + break; case SND_PCM_HW_PARAM_FRAGMENTS: - if (value < info->fragments_max) - info->fragments_max = value; - return info->fragments_max; + info->fragments_min = min; + info->fragments_max = max; + break; case SND_PCM_HW_PARAM_BUFFER_SIZE: - if (value < info->buffer_size_max) - info->buffer_size_max = value; - return info->buffer_size_max; + info->buffer_size_min = min; + info->buffer_size_max = max; + break; default: assert(0); - return 0; } } -static void par_set(snd_pcm_hw_info_t *info, unsigned int param, - unsigned long value) +void snd_pcm_hw_info_par_copy(snd_pcm_hw_info_t *info, unsigned int param, + snd_pcm_hw_info_t *src) { switch (param) { case SND_PCM_HW_PARAM_ACCESS: - info->access_mask = 1 << value; + info->access_mask = src->access_mask; break; case SND_PCM_HW_PARAM_FORMAT: - info->format_mask = 1 << value; + info->format_mask = src->format_mask; break; case SND_PCM_HW_PARAM_SUBFORMAT: - info->subformat_mask = 1 << value; + info->subformat_mask = src->subformat_mask; break; case SND_PCM_HW_PARAM_CHANNELS: - info->channels_min = info->channels_max = value; + info->channels_min = src->channels_min; + info->channels_max = src->channels_max; break; case SND_PCM_HW_PARAM_RATE: - info->rate_min = info->rate_max = value; + info->rate_min = src->rate_min; + info->rate_max = src->rate_max; break; case SND_PCM_HW_PARAM_FRAGMENT_SIZE: - info->fragment_size_min = info->fragment_size_max = value; + info->fragment_size_min = src->fragment_size_min; + info->fragment_size_max = src->fragment_size_max; break; case SND_PCM_HW_PARAM_FRAGMENTS: - info->fragments_min = info->fragments_max = value; + info->fragments_min = src->fragments_min; + info->fragments_max = src->fragments_max; break; case SND_PCM_HW_PARAM_BUFFER_SIZE: - info->buffer_size_min = info->buffer_size_max = value; + info->buffer_size_min = src->buffer_size_min; + info->buffer_size_max = src->buffer_size_max; break; default: assert(0); @@ -2974,39 +1761,79 @@ static void par_set(snd_pcm_hw_info_t *info, unsigned int param, } } -static int par_check(const snd_pcm_hw_info_t *info, unsigned int param, - unsigned long value) +unsigned long snd_pcm_hw_info_par_choices(const snd_pcm_hw_info_t *info, + unsigned int param) { - switch (param) { - case SND_PCM_HW_PARAM_ACCESS: - return info->access_mask & (1 << value); - case SND_PCM_HW_PARAM_FORMAT: - return info->format_mask & (1 << value); - case SND_PCM_HW_PARAM_SUBFORMAT: - return info->subformat_mask & (1 << value); - case SND_PCM_HW_PARAM_CHANNELS: - return value >= info->channels_min && - value <= info->channels_max; - case SND_PCM_HW_PARAM_RATE: - return value >= info->rate_min && - value <= info->rate_max; - case SND_PCM_HW_PARAM_FRAGMENT_SIZE: - return value >= info->fragment_size_min && - value <= info->fragment_size_max; - case SND_PCM_HW_PARAM_FRAGMENTS: - return value >= info->fragments_min && - value <= info->fragments_max; - case SND_PCM_HW_PARAM_BUFFER_SIZE: - return value >= info->buffer_size_min && - value <= info->buffer_size_max; + par_desc_t *p; + assert(param <= SND_PCM_HW_PARAM_LAST); + p = &hw_pars[param]; + switch (p->type) { + case MASK: + return hweight32(snd_pcm_hw_info_par_get_mask(info, param)); + case MINMAX: + { + unsigned long min, max; + snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); + return max - min + 1; + } + default: + assert(0); + return 0; + } +} + +unsigned long snd_pcm_hw_info_par_refine_min(snd_pcm_hw_info_t *info, + unsigned int param, + unsigned long value) +{ + unsigned long min, max; + snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); + if (min < value) { + min = value; + snd_pcm_hw_info_par_set_minmax(info, param, min, max); + } + return min; +} + +unsigned long snd_pcm_hw_info_par_refine_max(snd_pcm_hw_info_t *info, + unsigned int param, + unsigned long value) +{ + unsigned long min, max; + snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); + if (max > value) { + max = value; + snd_pcm_hw_info_par_set_minmax(info, param, min, max); + } + return max; +} + +int snd_pcm_hw_info_par_check(const snd_pcm_hw_info_t *info, + unsigned int param, + unsigned long value) +{ + par_desc_t *p; + assert(param <= SND_PCM_HW_PARAM_LAST); + p = &hw_pars[param]; + switch (p->type) { + case MASK: + return snd_pcm_hw_info_par_get_mask(info, param) & (1 << value); + case MINMAX: + { + unsigned long min, max; + snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); + return value >= min && value <= max; + } default: assert(0); return 0; } } -static long par_nearest_next(const snd_pcm_hw_info_t *info, unsigned int param, - unsigned long best, long value, snd_pcm_t *pcm) +long snd_pcm_hw_info_par_nearest_next(const snd_pcm_hw_info_t *info, + unsigned int param, + unsigned long best, long value, + snd_pcm_t *pcm) { unsigned long min, max; unsigned long d1, d2; @@ -3015,10 +1842,9 @@ static long par_nearest_next(const snd_pcm_hw_info_t *info, unsigned int param, int err1 = -EINVAL; int err2 = -EINVAL; + snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); i1 = *info; i2 = *info; - max = par_refine_max(&i1, param, ULONG_MAX); - min = par_refine_min(&i2, param, 0); if (value < 0) { d1 = 0; d2 = 0; @@ -3037,17 +1863,17 @@ static long par_nearest_next(const snd_pcm_hw_info_t *info, unsigned int param, else max1 = 0; min2 = best + d2; - max1 = par_refine_max(&i1, param, max1); - min2 = par_refine_min(&i2, param, min2); + max1 = snd_pcm_hw_info_par_refine_max(&i1, param, max1); + min2 = snd_pcm_hw_info_par_refine_min(&i2, param, min2); if (min <= max1) { err1 = snd_pcm_hw_info(pcm, &i1); if (err1 >= 0) - max1 = par_refine_max(&i1, param, max1); + max1 = snd_pcm_hw_info_par_refine_max(&i1, param, max1); } if (min2 <= max && (err1 < 0 || best - max1 > min2 - best)) { err2 = snd_pcm_hw_info(pcm, &i2); if (err2 >= 0) - min2 = par_refine_min(&i2, param, min2); + min2 = snd_pcm_hw_info_par_refine_min(&i2, param, min2); } if (err1 < 0) { if (err2 < 0) @@ -3060,66 +1886,213 @@ static long par_nearest_next(const snd_pcm_hw_info_t *info, unsigned int param, return min2; } +void snd_pcm_hw_info_par_dump(snd_pcm_hw_info_t *info, unsigned int param, FILE *fp) +{ + par_desc_t *p; + assert(param <= SND_PCM_HW_PARAM_LAST); + p = &hw_pars[param]; + switch (p->type) { + case MASK: + { + unsigned int mask = snd_pcm_hw_info_par_get_mask(info, param); + if (mask == ~0U) + fputs(" ALL", fp); + else if (mask) { + unsigned int k; + for (k = 0; k <= p->last; ++k) + if (mask & (1U << k)) { + putc(' ', fp); + fputs(p->names[k], fp); + } + } else + fputs(" NONE", fp); + break; + } + case MINMAX: + { + unsigned long min, max; + snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); + printf("%ld - %ld", min, max); + } + default: + assert(0); + break; + } +} + +int snd_pcm_hw_info_par_empty(snd_pcm_hw_info_t *info, unsigned int param) +{ + par_desc_t *p; + assert(param <= SND_PCM_HW_PARAM_LAST); + p = &hw_pars[param]; + switch (p->type) { + case MASK: + return !snd_pcm_hw_info_par_get_mask(info, param); + case MINMAX: + { + unsigned long min, max; + snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); + return min > max; + } + default: + assert(0); + return 0;; + } +} + +unsigned int snd_pcm_hw_info_fail_mask(snd_pcm_hw_info_t *info) +{ + unsigned int k, mask = 0; + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { + if (snd_pcm_hw_info_par_empty(info, k)) + mask |= 1 << k; + } + return mask; +} + +int _snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + int err; + snd_pcm_hw_info_t info; + + snd_pcm_hw_params_to_info(params, &info); + err = snd_pcm_hw_info(pcm, &info); + if (err < 0) { + params->fail_mask = snd_pcm_hw_info_fail_mask(&info); + return err; + } + + if ((err = pcm->ops->hw_params(pcm->op_arg, params)) < 0) + return err; + pcm->setup = 1; + pcm->access = params->access; + pcm->format = params->format; + pcm->subformat = params->subformat; + pcm->rate = params->rate; + pcm->channels = params->channels; + pcm->fragment_size = params->fragment_size; + pcm->fragments = params->fragments; + pcm->bits_per_sample = snd_pcm_format_physical_width(params->format); + pcm->bits_per_frame = pcm->bits_per_sample * params->channels; + pcm->buffer_size = params->fragment_size * params->fragments; + + pcm->info = info.info; + pcm->msbits = info.msbits; + pcm->rate_master = info.rate_master; + pcm->rate_divisor = info.rate_divisor; + pcm->fifo_size = info.fifo_size; + + /* Default sw params */ + pcm->start_mode = SND_PCM_START_DATA; + pcm->ready_mode = SND_PCM_READY_FRAGMENT; + pcm->xrun_mode = SND_PCM_XRUN_FRAGMENT; + pcm->avail_min = pcm->fragment_size; + pcm->xfer_min = pcm->fragment_size; + pcm->xfer_align = pcm->fragment_size; + pcm->time = 0; + pcm->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size; + return 0; +} + +int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + int err; + assert(pcm && params); + if (pcm->setup && pcm->mmap_channels && + (pcm->mmap_rw || + (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED || + pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED || + pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) { + err = snd_pcm_munmap(pcm); + if (err < 0) + return err; + } + err = _snd_pcm_hw_params(pcm, params); + if (pcm->setup && + (pcm->mmap_rw || + (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED || + pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED || + pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) { + int err; + err = snd_pcm_mmap(pcm); + if (err < 0) + return err; + } + return err; +} + int snd_pcm_hw_info_strategy1(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, const snd_pcm_strategy_t *strategy, - unsigned int min_badness, unsigned int max_badness) + unsigned int badness_min, unsigned int badness_max) { snd_pcm_hw_info_t best_info; int param; long value; unsigned int best_badness; - int badness; - badness = strategy->min_badness(info, max_badness, pcm, strategy); + unsigned int mask = ~0; + int badness = strategy->min_badness(info, badness_max, pcm, strategy); + snd_pcm_hw_info_t info1; #if 0 printf("\nBadness: %d\n", badness); snd_pcm_dump_hw_info(info, stdout); #endif if (badness < 0) - return -EINVAL; - if ((unsigned int)badness > min_badness) - min_badness = badness; + return badness; + if ((unsigned int)badness > badness_min) + badness_min = badness_min; param = strategy->choose_param(info, pcm, strategy); if (param < 0) return badness; best_badness = UINT_MAX; value = -1; while (1) { - snd_pcm_hw_info_t info1; int err; value = strategy->next_value(info, param, value, pcm, strategy); if (value < 0) break; info1 = *info; - par_set(&info1, param, value); + snd_pcm_hw_info_par_set_minmax(&info1, param, value, value); err = snd_pcm_hw_info(pcm, &info1); - if (err < 0) - continue; - badness = snd_pcm_hw_info_strategy1(pcm, &info1, strategy, min_badness, max_badness); - if (badness < 0) - continue; - if ((unsigned int) badness <= min_badness) { - *info = info1; - return badness; + if (err >= 0) { + badness = snd_pcm_hw_info_strategy1(pcm, &info1, strategy, badness_min, badness_max); + if (badness >= 0) { + + if ((unsigned int) badness <= badness_min) { + *info = info1; + return badness; + } + best_badness = badness; + best_info = info1; + badness_max = badness - 1; + continue; + } + if (badness != -EINVAL) + continue; } - best_badness = badness; - best_info = info1; - max_badness = badness - 1; + mask &= snd_pcm_hw_info_fail_mask(&info1); } - if (best_badness == UINT_MAX) + if (best_badness == UINT_MAX) { + for (param = 0; param <= SND_PCM_HW_PARAM_LAST; param++) { + if (!(mask & (1 << param))) + continue; + snd_pcm_hw_info_par_copy(info, param, &info1); + } return -EINVAL; + } *info = best_info; return best_badness; } int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, - const snd_pcm_strategy_t *strategy, - unsigned int min_badness, unsigned int max_badness) + const snd_pcm_strategy_t *strategy) { int err; err = snd_pcm_hw_info(pcm, info); if (err < 0) return err; - return snd_pcm_hw_info_strategy1(pcm, info, strategy, min_badness, max_badness); + return snd_pcm_hw_info_strategy1(pcm, info, strategy, + strategy->badness_min, + strategy->badness_max); } @@ -3142,15 +2115,20 @@ int snd_pcm_strategy_simple_choose_param(const snd_pcm_hw_info_t *info, int best_param = -1; const snd_pcm_strategy_simple_t *pars = strategy->private; unsigned long min_choices = ULONG_MAX; + unsigned int min_order = UINT_MAX; for (param = 0; param <= SND_PCM_HW_PARAM_LAST; ++param) { + const snd_pcm_strategy_simple_t *p = &pars[param]; unsigned int choices; - if (!pars[param].valid) + if (!p->valid) continue; - choices = par_choices(info, param); + choices = snd_pcm_hw_info_par_choices(info, param); if (choices == 1) continue; assert(choices != 0); - if (choices < min_choices) { + if (p->order < min_order || + (p->order == min_order && + choices < min_choices)) { + min_order = p->order; min_choices = choices; best_param = param; } @@ -3184,7 +2162,7 @@ int snd_pcm_strategy_simple_min_badness(const snd_pcm_hw_info_t *info, continue; b = pars[param].min_badness(info, param, pcm, &pars[param]); if (b > max_badness || max_badness - b < badness) - return -EINVAL; + return -E2BIG; badness += b; } return badness; @@ -3203,7 +2181,7 @@ unsigned int snd_pcm_strategy_simple_near_min_badness(const snd_pcm_hw_info_t *i const snd_pcm_strategy_simple_t *par) { const snd_pcm_strategy_simple_near_t *p = par->private; - long value = par_nearest_next(info, param, p->best, -1, pcm); + long value = snd_pcm_hw_info_par_nearest_next(info, param, p->best, -1, pcm); long diff; assert(value >= 0); diff = p->best - value; @@ -3219,7 +2197,7 @@ long snd_pcm_strategy_simple_near_next_value(const snd_pcm_hw_info_t *info, const snd_pcm_strategy_simple_t *par) { const snd_pcm_strategy_simple_near_t *p = par->private; - return par_nearest_next(info, param, p->best, value, pcm); + return snd_pcm_hw_info_par_nearest_next(info, param, p->best, value, pcm); } void snd_pcm_strategy_simple_choices_free(snd_pcm_strategy_simple_t *par) @@ -3237,7 +2215,7 @@ unsigned int snd_pcm_strategy_simple_choices_min_badness(const snd_pcm_hw_info_t const snd_pcm_strategy_simple_choices_t *p = par->private; unsigned int k; for (k = 0; k < p->count; ++k) { - if (par_check(info, param, p->choices[k].value)) + if (snd_pcm_hw_info_par_check(info, param, p->choices[k].value)) return p->choices[k].badness; } assert(0); @@ -3262,7 +2240,7 @@ long snd_pcm_strategy_simple_choices_next_value(const snd_pcm_hw_info_t *info, } for (; k < p->count; ++k) { unsigned long v = p->choices[k].value; - if (par_check(info, param, v)) + if (snd_pcm_hw_info_par_check(info, param, v)) return v; } return -1; @@ -3276,7 +2254,9 @@ int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy) return 0; } -int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp) +int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp, + unsigned int badness_min, + unsigned int badness_max) { snd_pcm_strategy_simple_t *data; snd_pcm_strategy_t *s; @@ -3292,6 +2272,8 @@ int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp) s->choose_param = snd_pcm_strategy_simple_choose_param; s->next_value = snd_pcm_strategy_simple_next_value; s->min_badness = snd_pcm_strategy_simple_min_badness; + s->badness_min = badness_min; + s->badness_max = badness_max; s->private = data; s->free = snd_pcm_strategy_simple_free; *strategyp = s; @@ -3299,6 +2281,7 @@ int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp) } int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, + int order, unsigned int param, unsigned long best, unsigned int mul) @@ -3314,6 +2297,7 @@ int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, data->best = best; data->mul = mul; s += param; + s->order = order; s->valid = 1; s->next_value = snd_pcm_strategy_simple_near_next_value; s->min_badness = snd_pcm_strategy_simple_near_min_badness; @@ -3323,6 +2307,7 @@ int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, } int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, + int order, unsigned int param, unsigned int count, snd_pcm_strategy_simple_choices_list_t *choices) @@ -3339,6 +2324,7 @@ int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, data->choices = choices; s += param; s->valid = 1; + s->order = order; s->next_value = snd_pcm_strategy_simple_choices_next_value; s->min_badness = snd_pcm_strategy_simple_choices_min_badness; s->private = data; @@ -3346,6 +2332,78 @@ int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, return 0; } +int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp) +{ + unsigned int param; + for (param = 0; param <= SND_PCM_HW_PARAM_LAST; param++) { + fprintf(fp, "%s: ", snd_pcm_hw_param_names[param]); + snd_pcm_hw_info_par_dump(info, param, fp); + putc('\n', fp); + } + return 0; +} + +int snd_pcm_hw_info_try_explain_failure1(snd_pcm_t *pcm, + snd_pcm_hw_info_t *fail, + snd_pcm_hw_info_t *success, + unsigned int depth, + FILE *fp) +{ + unsigned int param; + snd_pcm_hw_info_t i; + if (depth < 1) + return -ENOENT; + for (param = 0; param <= SND_PCM_HW_PARAM_LAST; param++) { + int err; + i = *success; + snd_pcm_hw_info_par_copy(&i, param, fail); + err = snd_pcm_hw_info(pcm, &i); + if (err == 0 && + snd_pcm_hw_info_try_explain_failure1(pcm, fail, &i, depth - 1, fp) < 0) + continue; + fprintf(fp, "%s: ", snd_pcm_hw_param_names[param]); + snd_pcm_hw_info_par_dump(fail, param, fp); + putc('\n', fp); + return 0; + } + return -ENOENT; +} + +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) +{ + snd_pcm_hw_info_t i, any; + int err; + unsigned int fail_mask; + assert(pcm && fail); + fail_mask = snd_pcm_hw_info_fail_mask(fail); + if (fail_mask) { + unsigned int param; + for (param = 0; param <= SND_PCM_HW_PARAM_LAST; param++) { + if (!(fail_mask & (1 << param))) + continue; + fprintf(fp, "%s: ", snd_pcm_hw_param_names[param]); + snd_pcm_hw_info_par_dump(fail, param, fp); + putc('\n', fp); + } + return 0; + } + i = *fail; + err = snd_pcm_hw_info(pcm, &i); + if (err == 0) { + fprintf(fp, "Too low max badness or configuration temporarily unavailable\n"); + return 0; + } + if (!success) { + snd_pcm_hw_info_any(&any); + success = &any; + } + return snd_pcm_hw_info_try_explain_failure1(pcm, fail, success, depth, fp); +} + size_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm) { return *pcm->hw_ptr; diff --git a/src/pcm/pcm_adpcm.c b/src/pcm/pcm_adpcm.c index d6be0e57..70882a03 100644 --- a/src/pcm/pcm_adpcm.c +++ b/src/pcm/pcm_adpcm.c @@ -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->access_mask = SND_PCM_ACCBIT_MMAP; err = snd_pcm_hw_info(adpcm->plug.slave, info); - if (info->format_mask) - info->format_mask = format_mask; - if (info->access_mask) { - adpcm->plug.saccess_mask = info->access_mask; - info->access_mask = access_mask; - } + info->format_mask = format_mask; + info->access_mask = access_mask; if (err < 0) return err; 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_t *slave = adpcm->plug.slave; - unsigned int format, access; + snd_pcm_hw_info_t sinfo; + snd_pcm_hw_params_t sparams; int err; - format = params->format; - access = params->access; - params->format = adpcm->sformat; - if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) - params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; - 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; + snd_pcm_hw_params_to_info(params, &sinfo); + sinfo.format_mask = 1 << adpcm->sformat; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); + params->fail_mask = sparams.fail_mask; if (err < 0) return err; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 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; } else { 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 { 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; } else { adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_FORMAT_S16); diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c index fd5f619c..16dd7399 100644 --- a/src/pcm/pcm_alaw.c +++ b/src/pcm/pcm_alaw.c @@ -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->access_mask = SND_PCM_ACCBIT_MMAP; err = snd_pcm_hw_info(alaw->plug.slave, info); - if (info->format_mask) - info->format_mask = format_mask; - if (info->access_mask) { - alaw->plug.saccess_mask = info->access_mask; - info->access_mask = access_mask; - } + info->format_mask = format_mask; + info->access_mask = access_mask; if (err < 0) return err; 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_t *slave = alaw->plug.slave; - unsigned int format, access; + snd_pcm_hw_info_t sinfo; + snd_pcm_hw_params_t sparams; int err; - format = params->format; - access = params->access; - params->format = alaw->sformat; - if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) - params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; - 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; + snd_pcm_hw_params_to_info(params, &sinfo); + sinfo.format_mask = 1 << alaw->sformat; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); + params->fail_mask = sparams.fail_mask; if (err < 0) return err; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 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; } else { 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 { 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; } else { alaw->getput_idx = get_index(alaw->sformat, SND_PCM_FORMAT_S16); diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c index 74ada49c..ba577ff9 100644 --- a/src/pcm/pcm_linear.c +++ b/src/pcm/pcm_linear.c @@ -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->access_mask = SND_PCM_ACCBIT_MMAP; err = snd_pcm_hw_info(linear->plug.slave, info); - if (info->format_mask) - info->format_mask = format_mask; - if (info->access_mask) { - linear->plug.saccess_mask = info->access_mask; - info->access_mask = access_mask; - } + info->format_mask = format_mask; + info->access_mask = access_mask; if (err < 0) return err; 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_t *slave = linear->plug.slave; - unsigned int format, access; + snd_pcm_hw_info_t sinfo; + snd_pcm_hw_params_t sparams; int err; - format = params->format; - access = params->access; - params->format = linear->sformat; - if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) - params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; - 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; + snd_pcm_hw_params_to_info(params, &sinfo); + sinfo.format_mask = 1 << linear->sformat; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); + params->fail_mask = sparams.fail_mask; if (err < 0) return err; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - linear->conv_idx = conv_index(format, + linear->conv_idx = conv_index(params->format, linear->sformat); else linear->conv_idx = conv_index(linear->sformat, - format); + params->format); return 0; } diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index 9065a1c0..df8af730 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -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); 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_info_to_params(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_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); diff --git a/src/pcm/pcm_misc.c b/src/pcm/pcm_misc.c index a6790215..5d0f0c20 100644 --- a/src/pcm/pcm_misc.c +++ b/src/pcm/pcm_misc.c @@ -65,9 +65,9 @@ int snd_pcm_format_unsigned(int format) int val; val = snd_pcm_format_signed(format); - if (val >= 0) - val ^= 1; - return val; + if (val < 0) + return val; + return !val; } int snd_pcm_format_linear(int format) @@ -113,6 +113,15 @@ int snd_pcm_format_big_endian(int format) 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) { switch (format) { diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c index 01cd202c..90aa36a0 100644 --- a/src/pcm/pcm_mulaw.c +++ b/src/pcm/pcm_mulaw.c @@ -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->access_mask = SND_PCM_ACCBIT_MMAP; err = snd_pcm_hw_info(mulaw->plug.slave, info); - if (info->format_mask) - info->format_mask = format_mask; - if (info->access_mask) { - mulaw->plug.saccess_mask = info->access_mask; - info->access_mask = access_mask; - } + info->format_mask = format_mask; + info->access_mask = access_mask; if (err < 0) return err; 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_t *slave = mulaw->plug.slave; - unsigned int format, access; + snd_pcm_hw_info_t sinfo; + snd_pcm_hw_params_t sparams; int err; - format = params->format; - access = params->access; - params->format = mulaw->sformat; - if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) - params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; - 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; + snd_pcm_hw_params_to_info(params, &sinfo); + sinfo.format_mask = 1 << mulaw->sformat; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); + params->fail_mask = sparams.fail_mask; if (err < 0) return err; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { 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; } else { 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 { 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; } else { mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_FORMAT_S16); diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index a2016102..95678004 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -31,7 +31,6 @@ typedef struct { snd_pcm_t *pcm; unsigned int channels_count; int close_slave; - unsigned int access_mask; } snd_pcm_multi_slave_t; 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; unsigned int k; snd_pcm_hw_info_t i; - unsigned int access_mask = ~0; + unsigned int access_mask, saccess_mask; + int changed = 0; int err = 0; if (info->channels_min < multi->channels_count) info->channels_min = multi->channels_count; if (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; i = *info; - for (k = 0; k < multi->slaves_count; ++k) { - snd_pcm_t *slave = multi->slaves[k].pcm; - i.access_mask = SND_PCM_ACCBIT_MMAP; - i.channels_min = i.channels_max = multi->slaves[k].channels_count; - err = snd_pcm_hw_info(slave, &i); - access_mask &= i.access_mask; - if (err < 0) - break; - multi->slaves[k].access_mask = i.access_mask; - } + saccess_mask = ~0; + changed = 0; + do { + for (k = 0; k < multi->slaves_count; ++k) { + snd_pcm_t *slave = multi->slaves[k].pcm; + snd_pcm_hw_info_t sinfo = i; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + sinfo.channels_min = sinfo.channels_max = multi->slaves[k].channels_count; + 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; - if (i.channels_min <= i.channels_max) - info->channels_min = info->channels_max = multi->channels_count; - if (i.access_mask) { - if (!(access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) || - multi->slaves_count > 1) - info->access_mask &= ~SND_PCM_ACCBIT_MMAP_INTERLEAVED; - if (!(access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)) - info->access_mask &= ~SND_PCM_ACCBIT_MMAP_NONINTERLEAVED; - } - return err; + info->access_mask = access_mask; + if (!(saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) || + multi->slaves_count > 1) + info->access_mask &= ~SND_PCM_ACCBIT_MMAP_INTERLEAVED; + if (!(saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)) + info->access_mask &= ~SND_PCM_ACCBIT_MMAP_NONINTERLEAVED; + if (!info->access_mask) + return -EINVAL; + info->channels_min = info->channels_max = multi->channels_count; + return 0; } static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_multi_t *multi = pcm->private; unsigned int i; - snd_pcm_hw_params_t p; 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) { snd_pcm_t *slave = multi->slaves[i].pcm; - if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) - p.access = SND_PCM_ACCESS_MMAP_INTERLEAVED; - else if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) - p.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - else - assert(0); - p.channels = multi->slaves[i].channels_count; - err = snd_pcm_hw_params(slave, &p); + snd_pcm_hw_info_t sinfo; + snd_pcm_hw_params_t sparams; + snd_pcm_hw_params_to_info(params, &sinfo); + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + sinfo.channels_min = sinfo.channels_max = multi->slaves[i].channels_count; + err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); if (err < 0) { - params->fail_mask = p.fail_mask; + params->fail_mask = sparams.fail_mask; return err; } err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format); diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index cafe1970..123a6d51 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -32,67 +32,77 @@ typedef struct { } snd_pcm_plug_t; -static int preferred_formats[] = { - 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) +static int format_badness(unsigned int src, unsigned int dst) { - if (snd_pcm_format_linear(format)) { - int width = snd_pcm_format_width(format); - int unsignd = snd_pcm_format_unsigned(format); - int big = snd_pcm_format_big_endian(format); - int format1; - int wid, width1=width; - int dwidth1 = 8; - for (wid = 0; wid < 4; ++wid) { - int end, big1 = big; - for (end = 0; end < 2; ++end) { - int sgn, unsignd1 = unsignd; - for (sgn = 0; sgn < 2; ++sgn) { - format1 = snd_pcm_build_linear_format(width1, unsignd1, big1); - if (format1 >= 0 && - format_mask & (1U << format1)) - return format1; - unsignd1 = !unsignd1; - } - big1 = !big1; - } - if (width1 == 32) { - dwidth1 = -dwidth1; - width1 = width; - } - width1 += dwidth1; - } - return ffs(format_mask) - 1; + 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 { - 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(preferred_formats) / sizeof(preferred_formats[0]); ++i) { - int format1 = preferred_formats[i]; - if (format_mask & (1U << format1)) - return format1; - } - default: - return ffs(format_mask) - 1; + 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) @@ -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) { - int err; snd_pcm_plug_t *plug = pcm->private; snd_pcm_t *slave = plug->req_slave; - snd_pcm_hw_info_t sinfo, i; - snd_pcm_hw_params_t sparams; - unsigned int rate_min, rate_max; - unsigned int channels_min, channels_max; - unsigned int format, format_mask; - size_t fragment_size_min, fragment_size_max; + snd_pcm_hw_info_t sinfo; + int err; + size_t size; + unsigned int n; info->access_mask &= (SND_PCM_ACCBIT_MMAP_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) return -EINVAL; - if (info->rate_min < 4000) - info->rate_min = 4000; - if (info->rate_max > 192000) - info->rate_max = 192000; + info->subformat_mask &= SND_PCM_SUBFMTBIT_STD; + if (info->subformat_mask == 0) + return -EINVAL; + + 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) return -EINVAL; if (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; - 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.access_mask = SND_PCM_ACCBIT_MMAP; sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW | SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM); - sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD; sinfo.channels_min = 1; - sinfo.channels_max = 1024; - sinfo.rate_min = 4000; - sinfo.rate_max = 192000; - 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.channels_max = UINT_MAX; + sinfo.rate_min = RATE_MIN; + sinfo.rate_max = 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); - err = snd_pcm_hw_info(slave, &sinfo); - if (err < 0) { - *info = sinfo; - return err; - } + 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); + err = snd_pcm_hw_info(slave, &sinfo); info->subformat_mask = sinfo.subformat_mask; info->fragments_min = sinfo.fragments_min; 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; + + size = muldiv_down(sinfo.buffer_size_min, info->rate_min, sinfo.rate_max); + if (info->buffer_size_min < size) + info->buffer_size_min = size; + size = muldiv_up(sinfo.buffer_size_max, info->rate_max, sinfo.rate_min); + if (info->buffer_size_max > size) + 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; - fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max); - 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; + if (err < 0) + return err; info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); snd_pcm_hw_info_complete(info); 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_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 err; + + params->fail_mask = 0; + sparams = *params; snd_pcm_hw_params_to_info(&sparams, &sinfo); 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); sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD; sinfo.channels_min = 1; - sinfo.channels_max = 1024; - sinfo.rate_min = 4000; - sinfo.rate_max = 192000; + 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; sinfo.fragment_size_min = 1; sinfo.fragment_size_max = ULONG_MAX; - err = snd_pcm_hw_info_rulesv(slave, &sinfo, params, - SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE, - SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS, - -1); + sinfo.buffer_size_min = 1; + sinfo.buffer_size_max = ULONG_MAX; + 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_hw_info_to_params_fail(&sinfo, params); + snd_pcm_strategy_free(strategy); return err; } - err = snd_pcm_plug_slave_fmt(sparams.format, sinfo.format_mask); - if (err < 0) + 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; - sparams.format = err; - sinfo.format_mask = 1U << err; - 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); + } + 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_hw_info_to_params_fail(&sinfo, params); + snd_pcm_strategy_free(strategy); return err; } - if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) - sparams.access = SND_PCM_ACCESS_MMAP_INTERLEAVED; - else if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) - sparams.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - else - assert(0); + 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); if (err < 0) return err; diff --git a/src/pcm/pcm_plugin.h b/src/pcm/pcm_plugin.h index 4047ffb7..fc449bf2 100644 --- a/src/pcm/pcm_plugin.h +++ b/src/pcm/pcm_plugin.h @@ -28,7 +28,6 @@ typedef struct { int (*init)(snd_pcm_t *pcm); int shmid; size_t appl_ptr, hw_ptr; - unsigned int saccess_mask; } snd_pcm_plugin_t; 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 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_U16_LE|SND_PCM_FMTBIT_U16_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; +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) { - 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) { - 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) { - 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_RESOLUTION 16 diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index 8684ccec..94a9a9f8 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -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_hw_info_t sinfo; unsigned int access_mask; - size_t fragment_size_min, fragment_size_max; + size_t size; int err; info->access_mask &= (SND_PCM_ACCBIT_MMAP_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; if (info->format_mask == 0) return -EINVAL; - if (info->rate_min < 4000) - info->rate_min = 4000; - if (info->rate_max > 192000) - info->rate_max = 192000; - if (info->rate_max < info->rate_min) + info->subformat_mask &= SND_PCM_SUBFMTBIT_STD; + if (info->subformat_mask == 0) return -EINVAL; - if (info->fragment_size_max > 1024 * 1024) - info->fragment_size_max = 1024 * 1024; - if (info->fragment_size_max < info->fragment_size_min) + 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) return -EINVAL; - sinfo = *info; - sinfo.rate_min = rate->srate; - sinfo.rate_max = rate->srate; + sinfo = *info; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; if (rate->sformat >= 0) 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_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); - info->subformat_mask = sinfo.subformat_mask; + if (rate->sformat < 0) + info->format_mask = sinfo.format_mask; info->channels_min = sinfo.channels_min; info->channels_max = sinfo.channels_max; info->fragments_min = sinfo.fragments_min; info->fragments_max = sinfo.fragments_max; - if (!sinfo.access_mask) { - info->access_mask = 0; - } - if (!sinfo.format_mask) { - info->format_mask = 0; - } - if (sinfo.rate_min > sinfo.rate_max) { - info->rate_min = UINT_MAX; - info->rate_max = 0; - } - if (sinfo.fragment_size_min > sinfo.fragment_size_max) { - info->fragment_size_min = ULONG_MAX; - info->fragment_size_max = 0; - } + 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; + if (info->fragment_size_min > info->fragment_size_max) + return -EINVAL; + + size = muldiv_down(sinfo.buffer_size_min, info->rate_min, sinfo.rate_max); + if (info->buffer_size_min < size) + info->buffer_size_min = size; + 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) return err; - - fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max); - 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); + + info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); snd_pcm_hw_info_complete(info); 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_t *slave = rate->plug.slave; 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_rate, dst_rate; - size_t fragment_size; int mul, div; 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); - sinfo.fragment_size_min = 0; - sinfo.fragment_size_max = ULONG_MAX; - err = snd_pcm_hw_info_rulesv(slave, &sinfo, params, - SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE, - -1); - snd_pcm_hw_info_to_params(&sinfo, params); - if (err >= 0) - err = snd_pcm_hw_params(slave, params); - params->format = format; - params->rate = crate; - params->access = access; - params->fragment_size = fragment_size; + if (rate->sformat >= 0) + sinfo.format_mask = 1 << rate->sformat; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + sinfo.rate_min = rate->srate; + sinfo.rate_max = rate->srate; + sinfo.fragment_size_min = muldiv_down(params->fragment_size, rate->srate, params->rate); + sinfo.fragment_size_max = muldiv_up(params->fragment_size, rate->srate, params->rate); + sinfo.buffer_size_min = muldiv_down(params->fragment_size * params->fragments, rate->srate, params->rate); + sinfo.buffer_size_max = muldiv_up(params->fragment_size * params->fragments, rate->srate, params->rate); + err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); + params->fail_mask = sparams.fail_mask; if (err < 0) return err; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - src_format = format; + src_format = params->format; dst_format = slave->format; - src_rate = crate; + src_rate = params->rate; dst_rate = slave->rate; } else { src_format = slave->format; - dst_format = format; + dst_format = params->format; src_rate = slave->rate; - dst_rate = crate; + dst_rate = params->rate; } rate->get_idx = get_index(src_format, SND_PCM_FORMAT_S16); rate->put_idx = put_index(SND_PCM_FORMAT_S16, dst_format); diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index 75a4b106..da35baab 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -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) { 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; info->access_mask &= (SND_PCM_ACCBIT_MMAP_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; if (access_mask == 0) return -EINVAL; + info->format_mask &= SND_PCM_FMTBIT_LINEAR; format_mask = info->format_mask; if (format_mask == 0) return -EINVAL; + if (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_max = info->channels_max; + if (channels_min > channels_max) + return -EINVAL; + if (route->sformat >= 0) info->format_mask = 1U << route->sformat; 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; 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; - if (info->channels_min <= info->channels_max) { + if (route->schannels >= 0) { info->channels_min = channels_min; 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) return err; 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_t *slave = route->plug.slave; - unsigned int format, access, channels; unsigned int src_format, dst_format; + snd_pcm_hw_info_t sinfo; + snd_pcm_hw_params_t sparams; int err; - format = params->format; - channels = params->channels; - access = params->access; + snd_pcm_hw_params_to_info(params, &sinfo); if (route->sformat >= 0) - params->format = route->sformat; + sinfo.format_mask = 1 << route->sformat; if (route->schannels >= 0) - params->channels = route->schannels; - if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) - params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED; - else if (route->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->channels = channels; - params->access = access; + sinfo.channels_min = sinfo.channels_max = route->schannels; + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); + params->fail_mask = sparams.fail_mask; if (err < 0) return err; if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - src_format = format; + src_format = params->format; dst_format = slave->format; } else { 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.put_idx = put_index(SND_PCM_FORMAT_U32, dst_format); diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index 148353c6..d116b2e8 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -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; if (access_mask == 0) 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) { info->format_mask &= 1U << slave->format; if (!info->format_mask) 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 (info->rate_min < (unsigned)slave->rate) info->rate_min = slave->rate; if (info->rate_max > (unsigned)slave->rate) info->rate_max = slave->rate; - if (info->rate_max > info->rate_max) + if (info->rate_min > info->rate_max) return -EINVAL; } + info->access_mask = SND_PCM_ACCBIT_MMAP; info->channels_min = info->channels_max = slave->channels_count; err = snd_pcm_hw_info(slave->pcm, info); - if (info->channels_min <= info->channels_max) - info->channels_min = info->channels_max = share->channels_count; - if (info->access_mask) - info->access_mask = access_mask; + info->access_mask = access_mask; + info->channels_min = info->channels_max = share->channels_count; + if (err < 0) + return err; 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) @@ -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); if (slave->setup_count > 1 || (slave->setup_count == 1 && !pcm->setup)) { - if (params->access != spcm->access || - 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 = 0; + if (params->format != spcm->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 { snd_pcm_hw_params_t sparams = *params; diff --git a/src/pcm/pcm_shm.c b/src/pcm/pcm_shm.c index d543f22f..cc6bfa4c 100644 --- a/src/pcm/pcm_shm.c +++ b/src/pcm/pcm_shm.c @@ -41,8 +41,8 @@ typedef struct { int socket; - unsigned int access_mask; volatile snd_pcm_shm_ctrl_t *ctrl; + unsigned int access_mask; } snd_pcm_shm_t; 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; ctrl->cmd = SND_PCM_IOCTL_HW_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); - *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) 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) -- 2.47.3