From: Abramo Bagnara Date: Thu, 21 Dec 2000 20:44:10 +0000 (+0000) Subject: New hw_params implementation X-Git-Tag: v1.0.3~1049 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=8dd927e97fdaddf3eb9f733299130041c7d73e3d;p=alsa-lib.git New hw_params implementation --- diff --git a/aserver/aserver.c b/aserver/aserver.c index 8c07a8dc..cf8119ce 100644 --- a/aserver/aserver.c +++ b/aserver/aserver.c @@ -379,8 +379,8 @@ int pcm_shm_cmd(client_t *client) case SND_PCM_IOCTL_INFO: ctrl->result = snd_pcm_info(pcm, (snd_pcm_info_t *) &ctrl->u.info); break; - case SND_PCM_IOCTL_HW_INFO: - ctrl->result = snd_pcm_hw_info(pcm, (snd_pcm_hw_info_t *) &ctrl->u.hw_info); + case SND_PCM_IOCTL_HW_REFINE: + ctrl->result = snd_pcm_hw_refine(pcm, (snd_pcm_hw_params_t *) &ctrl->u.hw_refine); break; case SND_PCM_IOCTL_HW_PARAMS: ctrl->result = snd_pcm_hw_params(pcm, (snd_pcm_hw_params_t *) &ctrl->u.hw_params); diff --git a/include/aserver.h b/include/aserver.h index db8439e6..f66944c7 100644 --- a/include/aserver.h +++ b/include/aserver.h @@ -42,7 +42,7 @@ typedef struct { pid_t pid; } async; snd_pcm_info_t info; - snd_pcm_hw_info_t hw_info; + snd_pcm_hw_params_t hw_refine; snd_pcm_hw_params_t hw_params; snd_pcm_sw_params_t sw_params; snd_pcm_dig_params_t dig_params; diff --git a/include/pcm.h b/include/pcm.h index 9203e4e5..01ba3149 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -35,18 +35,6 @@ typedef enum _snd_pcm_type { SND_PCM_TYPE_LBSERVER, } snd_pcm_type_t; -enum { - SND_PCM_RULE_PAR_MASK = 0x00ff, - SND_PCM_RULE_REL_LT = 0x100, - SND_PCM_RULE_REL_GT = 0x200, - SND_PCM_RULE_REL_EQ = 0x300, - SND_PCM_RULE_REL_LE = 0x400, - SND_PCM_RULE_REL_GE = 0x500, - SND_PCM_RULE_REL_NEAR = 0x600, - SND_PCM_RULE_REL_BITS = 0x700, - SND_PCM_RULE_REL_MASK = 0xff00 -}; - typedef struct _snd_pcm_channel_area { void *addr; /* base address of channel samples */ unsigned int first; /* offset to first sample in bits */ @@ -69,8 +57,7 @@ int snd_pcm_poll_descriptor(snd_pcm_t *pcm); int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock); int snd_pcm_async(snd_pcm_t *pcm, int sig, pid_t pid); int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info); -int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info); -int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +int snd_pcm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); int snd_pcm_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info); int snd_pcm_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params); @@ -92,7 +79,7 @@ ssize_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, size_t size); int snd_pcm_dump_hw_setup(snd_pcm_t *pcm, FILE *fp); int snd_pcm_dump_sw_setup(snd_pcm_t *pcm, FILE *fp); int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *fp); -int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp); +int snd_pcm_dump_hw_params(snd_pcm_hw_params_t *params, FILE *fp); 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 snd_pcm_dump(snd_pcm_t *pcm, FILE *fp); @@ -103,8 +90,58 @@ 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_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 _mask mask_t; +size_t mask_sizeof(); +void mask_none(mask_t *mask); +void mask_all(mask_t *mask); +void mask_load(mask_t *mask, unsigned int msk); +int mask_empty(const mask_t *mask); +void mask_set(mask_t *mask, unsigned int val); +void mask_reset(mask_t *mask, unsigned int val); +void mask_copy(mask_t *mask, const mask_t *v); +int mask_test(const mask_t *mask, unsigned int val); +void mask_intersect(mask_t *mask, const mask_t *v); +int mask_eq(const mask_t *a, const mask_t *b); +int mask_single(const mask_t *mask); + +int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int par, unsigned int val); +int snd_pcm_hw_params_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int par, unsigned int val); +int snd_pcm_hw_params_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int par, unsigned int val); +int snd_pcm_hw_params_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int par, unsigned int min, unsigned int max); +int snd_pcm_hw_params_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int par); +int snd_pcm_hw_params_last(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int par); +int snd_pcm_hw_params_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int par, unsigned int val); +int snd_pcm_hw_params_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int par, const mask_t *mask); +int snd_pcm_hw_params_info_rate(const snd_pcm_hw_params_t *params, + unsigned int *rate_num, + unsigned int *rate_den); +int snd_pcm_hw_params_info_msbits(const snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_info_flags(const snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_info_fifo_size(const snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_info_dig_groups(const snd_pcm_hw_params_t *params); +int snd_pcm_hw_params_value(const snd_pcm_hw_params_t *params, + unsigned int var); +const mask_t *snd_pcm_hw_params_value_mask(const snd_pcm_hw_params_t *params, + unsigned int var); +const interval_t *snd_pcm_hw_params_value_interval(const snd_pcm_hw_params_t *params, + unsigned int var); +unsigned int snd_pcm_hw_params_value_min(const snd_pcm_hw_params_t *params, + unsigned int var); +unsigned int snd_pcm_hw_params_value_max(const snd_pcm_hw_params_t *params, + unsigned int var); +int snd_pcm_hw_params_test(const snd_pcm_hw_params_t *params, + unsigned int var, unsigned int val); +int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); typedef struct _snd_pcm_strategy snd_pcm_strategy_t; @@ -114,7 +151,7 @@ typedef struct _snd_pcm_strategy_simple_choices_list { unsigned int badness; } snd_pcm_strategy_simple_choices_list_t; -int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, +int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_params_t *info, const snd_pcm_strategy_t *strategy); int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy); @@ -129,9 +166,9 @@ 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, +int snd_pcm_hw_params_try_explain_failure(snd_pcm_t *pcm, + snd_pcm_hw_params_t *fail, + snd_pcm_hw_params_t *success, unsigned int depth, FILE *fp); @@ -147,7 +184,12 @@ 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_access_name(unsigned int access); const char *snd_pcm_format_name(unsigned int format); +const char *snd_pcm_subformat_name(unsigned int subformat); +const char *snd_pcm_hw_param_name(unsigned int params); +const char *snd_pcm_sw_param_name(unsigned int params); + const char *snd_pcm_format_description(unsigned int format); int snd_pcm_format_value(const char* name); diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am index 351a3783..38e3892f 100644 --- a/src/pcm/Makefile.am +++ b/src/pcm/Makefile.am @@ -1,11 +1,13 @@ EXTRA_LTLIBRARIES = libpcm.la -libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plugin.c pcm_copy.c pcm_linear.c \ +libpcm_la_SOURCES = mask.c interval.c \ + pcm.c pcm_hw.c pcm_plugin.c pcm_copy.c pcm_linear.c \ pcm_route.c pcm_mulaw.c pcm_alaw.c pcm_adpcm.c \ pcm_rate.c pcm_plug.c pcm_misc.c pcm_mmap.c pcm_multi.c \ - pcm_shm.c pcm_file.c pcm_share.c pcm_null.c -noinst_HEADERS = pcm_local.h pcm_plugin.h + pcm_shm.c pcm_file.c pcm_share.c pcm_null.c pcm_params.c +noinst_HEADERS = pcm_local.h pcm_plugin.h mask.h mask_inline.h \ + interval.h interval_inline.h all: libpcm.la diff --git a/src/pcm/interval.c b/src/pcm/interval.c new file mode 100644 index 00000000..bd3982c8 --- /dev/null +++ b/src/pcm/interval.c @@ -0,0 +1,297 @@ +/* + * Interval functions + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define INTERVAL_C + +#include +#include +#include +#include +#include +#include "interval.h" + +static inline void div64_32(u_int64_t *n, u_int32_t div, u_int32_t *rem) +{ + *rem = *n % div; + *n /= div; +} + +static inline unsigned int div32(unsigned int a, unsigned int b, + unsigned int *r) +{ + *r = a % b; + return a / b; +} + +static inline unsigned int div_down(unsigned int a, unsigned int b) +{ + return a / b; +} + +static inline unsigned int div_up(unsigned int a, unsigned int b) +{ + unsigned int r; + unsigned int q = div32(a, b, &r); + if (r) + ++q; + return q; +} + +static inline unsigned int mul(unsigned int a, unsigned int b) +{ + if (div_down(UINT_MAX, a) < b) + return UINT_MAX; + return a * b; +} + +static inline unsigned int add(unsigned int a, unsigned int b) +{ + if (a >= UINT_MAX - b) + return UINT_MAX; + return a + b; +} + +static inline unsigned int sub(unsigned int a, unsigned int b) +{ + if (a > b) + return a - b; + return 0; +} + +static inline unsigned int muldiv32(unsigned int a, unsigned int b, + unsigned int c, unsigned int *r) +{ + u_int64_t n = (u_int64_t) a * b; + div64_32(&n, c, r); + if (n >= UINT_MAX) { + *r = 0; + return UINT_MAX; + } + return n; +} + +int interval_refine_min(interval_t *i, unsigned int min) +{ + int changed = 0; + int openmin = 0; + assert(!interval_empty(i)); + if (i->min < min) { + i->min = min; + i->openmin = openmin; + changed = 1; + } else if (i->min == min && !i->openmin && openmin) { + i->openmin = 1; + changed = 1; + } + if (!i->real) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + } + if (i->min > i->max || + (i->min == i->max && (i->openmin || i->openmax))) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +int interval_refine_max(interval_t *i, unsigned int max) +{ + int changed = 0; + int openmax = 1; + max = add(max, 1); + assert(!interval_empty(i)); + if (i->max > max) { + i->max = max; + i->openmax = openmax; + changed = 1; + } else if (i->max == max && !i->openmax && openmax) { + i->openmax = 1; + changed = 1; + } + if (!i->real) { + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } + if (i->min > i->max || + (i->min == i->max && (i->openmin || i->openmax))) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +/* r <- v */ +int interval_refine(interval_t *i, const interval_t *v) +{ + int changed = 0; + assert(!interval_empty(i)); + if (i->min < v->min) { + i->min = v->min; + i->openmin = v->openmin; + changed = 1; + } else if (i->min == v->min && !i->openmin && v->openmin) { + i->openmin = 1; + changed = 1; + } + if (i->max > v->max) { + i->max = v->max; + i->openmax = v->openmax; + changed = 1; + } else if (i->max == v->max && !i->openmax && v->openmax) { + i->openmax = 1; + changed = 1; + } + if (!i->real) { + if (i->openmin) { + i->min++; + i->openmin = 0; + } + if (i->openmax) { + i->max--; + i->openmax = 0; + } + } + if (i->min > i->max || + (i->min == i->max && (i->openmin || i->openmax))) { + i->empty = 1; + return -EINVAL; + } + return changed; +} + +int interval_refine_first(interval_t *i) +{ + assert(!interval_empty(i)); + if (i->min == i->max || + (i->min + 1 == i->max && i->openmin && i->openmax)) + return 0; + i->max = i->min; + i->openmax = i->openmin; + if (i->openmax) + i->max++; + return 1; +} + +int interval_refine_last(interval_t *i) +{ + assert(!interval_empty(i)); + if (i->min == i->max || + (i->min + 1 == i->max && i->openmin && i->openmax)) + return 0; + i->min = i->max; + i->openmin = i->openmax; + if (i->openmin) + i->min--; + return 1; +} + +int interval_refine_set(interval_t *i, unsigned int val) +{ + interval_t t; + t.min = val; + t.openmin = 0; + t.max = add(val, 1); + t.openmax = 1; + return interval_refine(i, &t); +} + +/* a <- b * c */ +int interval_mul(interval_t *a, const interval_t *b, const interval_t *c) +{ + interval_t t; + assert(!a->empty && !b->empty && !c->empty); + t.min = mul(b->min, c->min); + t.openmin = (b->openmin || c->openmin); + t.max = mul(b->max, c->max); + t.openmax = (b->openmax || c->openmax); + return interval_refine(a, &t); +} + +/* a <- b / c */ +int interval_div(interval_t *a, const interval_t *b, const interval_t *c) +{ + interval_t t; + unsigned int r; + assert(!a->empty && !b->empty && !c->empty); + t.min = div32(b->min, c->max, &r); + t.openmin = (r || b->openmin || c->openmax); + t.max = div32(b->max, c->min, &r); + if (r) { + t.max++; + t.openmax = 1; + } else + t.openmax = (b->openmax || c->openmin); + return interval_refine(a, &t); +} + + +/* a <- b * c / k */ +int interval_muldivk(interval_t *a, unsigned int k, + const interval_t *b, const interval_t *c) +{ + interval_t t; + unsigned int r; + assert(!a->empty && !b->empty && !c->empty); + t.min = muldiv32(b->min, c->min, k, &r); + t.openmin = (r || b->openmin || c->openmin); + t.max = muldiv32(b->max, c->max, k, &r); + if (r) { + t.max++; + t.openmax = 1; + } else + t.openmax = (b->openmax || c->openmax); + return interval_refine(a, &t); +} + +/* a <- b * k / c */ +int interval_mulkdiv(interval_t *a, unsigned int k, + const interval_t *b, const interval_t *c) +{ + interval_t t; + unsigned int r; + assert(!a->empty && !b->empty && !c->empty); + t.min = muldiv32(b->min, k, c->max, &r); + t.openmin = (r || b->openmin || c->openmax); + t.max = muldiv32(b->max, k, c->min, &r); + if (r) { + t.max++; + t.openmax = 1; + } else + t.openmax = (b->openmax || c->openmin); + return interval_refine(a, &t); +} + +void interval_print(const interval_t *i, FILE *fp) +{ + if (interval_single(i)) { + fprintf(fp, "%u", interval_value(i)); + } else { + fprintf(fp, "%c%u %u%c", + i->openmin ? '(' : '[', + i->min, i->max, + i->openmax ? ')' : ']'); + } +} diff --git a/src/pcm/interval.h b/src/pcm/interval.h new file mode 100644 index 00000000..6fd91f77 --- /dev/null +++ b/src/pcm/interval.h @@ -0,0 +1,50 @@ +/* + * Interval header + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include + +#ifdef INTERVAL_C +#include "interval_inline.h" +#endif + +void interval_all(interval_t *i); +void interval_setreal(interval_t *i); +int interval_empty(const interval_t *i); +int interval_single(const interval_t *i); +int interval_value(const interval_t *i); +int interval_min(const interval_t *i); +int interval_max(const interval_t *i); +int interval_test(const interval_t *i, unsigned int val); +int interval_refine_min(interval_t *i, unsigned int min); +int interval_refine_max(interval_t *i, unsigned int max); +int interval_refine(interval_t *i, const interval_t *v); +int interval_refine_first(interval_t *i); +int interval_refine_last(interval_t *i); +int interval_refine_set(interval_t *i, unsigned int val); +int interval_mul(interval_t *a, const interval_t *b, const interval_t *c); +int interval_div(interval_t *a, const interval_t *b, const interval_t *c); +int interval_muldivk(interval_t *a, unsigned int k, + const interval_t *b, const interval_t *c); +int interval_mulkdiv(interval_t *a, unsigned int k, + const interval_t *b, const interval_t *c); +void interval_copy(interval_t *dst, const interval_t *src); +void interval_print(const interval_t *i, FILE *fp); +int interval_eq(const interval_t *i1, const interval_t *i2); diff --git a/src/pcm/interval_inline.h b/src/pcm/interval_inline.h new file mode 100644 index 00000000..845fec8a --- /dev/null +++ b/src/pcm/interval_inline.h @@ -0,0 +1,93 @@ +/* + * Interval inlines + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef INTERVAL_C +#define INLINE inline +#else +#define INLINE static inline +#endif + +INLINE void interval_all(interval_t *i) +{ + i->min = 1; + i->max = UINT_MAX; +} + +INLINE int interval_empty(const interval_t *i) +{ + return i->empty; +} + +INLINE int interval_single(const interval_t *i) +{ + assert(!interval_empty(i)); + return (i->min == i->max || + (i->min + 1 == i->max && i->openmax)); +} + +INLINE int interval_value(const interval_t *i) +{ + assert(interval_single(i)); + return i->min; +} + +INLINE int interval_min(const interval_t *i) +{ + assert(!interval_empty(i)); + return i->min; +} + +INLINE int interval_max(const interval_t *i) +{ + unsigned int v; + assert(!interval_empty(i)); + v = i->max; + if (i->openmax) + v--; + return v; +} + +INLINE int interval_test(const interval_t *i, unsigned int val) +{ + return !((i->min > val || (i->min == val && i->openmin) || + i->max < val || (i->max == val && i->openmax))); +} + +INLINE void interval_copy(interval_t *d, const interval_t *s) +{ + *d = *s; +} + +INLINE void interval_setreal(interval_t *i) +{ + i->real = 1; +} + +INLINE int interval_eq(const interval_t *i1, const interval_t *i2) +{ + if (i1->empty) + return i2->empty; + if (i2->empty) + return i1->empty; + return i1->min == i2->min && i1->openmin == i2->openmin && + i1->max == i2->max && i1->openmax == i2->openmax; +} + diff --git a/src/pcm/mask.c b/src/pcm/mask.c new file mode 100644 index 00000000..124ea7e9 --- /dev/null +++ b/src/pcm/mask.c @@ -0,0 +1,30 @@ +/* + * Mask functions + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define MASK_C + +#include +#include +#include +#include +#include "asoundlib.h" +#include "mask.h" + diff --git a/src/pcm/mask.h b/src/pcm/mask.h new file mode 100644 index 00000000..8324bbdd --- /dev/null +++ b/src/pcm/mask.h @@ -0,0 +1,57 @@ +/* + * Mask header + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include "asoundlib.h" + +#define MASK_MAX 32 + +#ifdef MASK_C +#include "mask_inline.h" +#endif + +void mask_none(mask_t *mask); +void mask_all(mask_t *mask); +void mask_load(mask_t *mask, unsigned int msk); +int mask_empty(const mask_t *mask); +void mask_set(mask_t *mask, unsigned int val); +void mask_reset(mask_t *mask, unsigned int val); +void mask_copy(mask_t *mask, const mask_t *v); +int mask_test(const mask_t *mask, unsigned int val); +void mask_intersect(mask_t *mask, const mask_t *v); +unsigned int mask_count(const mask_t *mask); +unsigned int mask_min(const mask_t *mask); +unsigned int mask_max(const mask_t *mask); +void mask_set_range(mask_t *mask, unsigned int from, unsigned int to); +void mask_reset_range(mask_t *mask, unsigned int from, unsigned int to); +void mask_leave(mask_t *mask, unsigned int val); +int mask_eq(const mask_t *mask, const mask_t *v); +int mask_single(const mask_t *mask); +int mask_refine(mask_t *mask, const mask_t *v); +int mask_refine_first(mask_t *mask); +int mask_refine_last(mask_t *mask); +int mask_refine_min(mask_t *mask, unsigned int val); +int mask_refine_max(mask_t *mask, unsigned int val); +int mask_refine_set(mask_t *mask, unsigned int val); +int mask_value(const mask_t *mask); diff --git a/src/pcm/mask_inline.h b/src/pcm/mask_inline.h new file mode 100644 index 00000000..abfbfa46 --- /dev/null +++ b/src/pcm/mask_inline.h @@ -0,0 +1,233 @@ +/* + * Mask inlines + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef MASK_C +#define INLINE inline +#else +#define INLINE static inline +#endif + +struct _mask { + unsigned int bits; +}; + +#define mask_bits(mask) ((mask)->bits) + +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; +} + +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); +} + +INLINE size_t mask_sizeof(void) +{ + return sizeof(mask_t); +} + +INLINE void mask_none(mask_t *mask) +{ + mask_bits(mask) = 0; +} + +INLINE void mask_all(mask_t *mask) +{ + mask_bits(mask) = ~0U; +} + +INLINE void mask_load(mask_t *mask, unsigned int msk) +{ + mask_bits(mask) = msk; +} + +INLINE int mask_empty(const mask_t *mask) +{ + return mask_bits(mask) == 0; +} + +INLINE unsigned int mask_count(const mask_t *mask) +{ + return hweight32(mask_bits(mask)); +} + +INLINE unsigned int mask_min(const mask_t *mask) +{ + assert(!mask_empty(mask)); + return ffs(mask_bits(mask)) - 1; +} + +INLINE unsigned int mask_max(const mask_t *mask) +{ + assert(!mask_empty(mask)); + return ld2(mask_bits(mask)); +} + +INLINE void mask_set(mask_t *mask, unsigned int val) +{ + assert(val <= MASK_MAX); + mask_bits(mask) |= (1U << val); +} + +INLINE void mask_reset(mask_t *mask, unsigned int val) +{ + assert(val <= MASK_MAX); + mask_bits(mask) &= ~(1U << val); +} + +INLINE void mask_set_range(mask_t *mask, unsigned int from, unsigned int to) +{ + assert(to <= MASK_MAX && from <= to); + mask_bits(mask) |= ((1U << (from - to + 1)) - 1) << from; +} + +INLINE void mask_reset_range(mask_t *mask, unsigned int from, unsigned int to) +{ + assert(to <= MASK_MAX && from <= to); + mask_bits(mask) &= ~(((1U << (from - to + 1)) - 1) << from); +} + +INLINE void mask_leave(mask_t *mask, unsigned int val) +{ + assert(val <= MASK_MAX); + mask_bits(mask) &= 1U << val; +} + +INLINE void mask_intersect(mask_t *mask, const mask_t *v) +{ + mask_bits(mask) &= mask_bits(v); +} + +INLINE int mask_eq(const mask_t *mask, const mask_t *v) +{ + return mask_bits(mask) == mask_bits(v); +} + +INLINE void mask_copy(mask_t *mask, const mask_t *v) +{ + mask_bits(mask) = mask_bits(v); +} + +INLINE int mask_test(const mask_t *mask, unsigned int val) +{ + assert(val <= MASK_MAX); + return mask_bits(mask) & (1U << val); +} + +INLINE int mask_single(const mask_t *mask) +{ + assert(!mask_empty(mask)); + return !(mask_bits(mask) & (mask_bits(mask) - 1)); +} + +INLINE int mask_refine(mask_t *mask, const mask_t *v) +{ + mask_t old; + assert(!mask_empty(mask)); + mask_copy(&old, mask); + mask_intersect(mask, v); + if (mask_empty(mask)) + return -EINVAL; + return !mask_eq(mask, &old); +} + +INLINE int mask_refine_first(mask_t *mask) +{ + assert(!mask_empty(mask)); + if (mask_single(mask)) + return 0; + mask_leave(mask, mask_min(mask)); + return 1; +} + +INLINE int mask_refine_last(mask_t *mask) +{ + assert(!mask_empty(mask)); + if (mask_single(mask)) + return 0; + mask_leave(mask, mask_max(mask)); + return 1; +} + +INLINE int mask_refine_min(mask_t *mask, unsigned int val) +{ + assert(!mask_empty(mask)); + if (mask_min(mask) >= val) + return 0; + mask_reset_range(mask, 0, val - 1); + if (mask_empty(mask)) + return -EINVAL; + return 1; +} + +INLINE int mask_refine_max(mask_t *mask, unsigned int val) +{ + assert(!mask_empty(mask)); + if (mask_max(mask) <= val) + return 0; + mask_reset_range(mask, val + 1, MASK_MAX); + if (mask_empty(mask)) + return -EINVAL; + return 1; +} + +INLINE int mask_refine_set(mask_t *mask, unsigned int val) +{ + int changed; + assert(!mask_empty(mask)); + changed = !mask_single(mask); + mask_leave(mask, val); + if (mask_empty(mask)) + return -EINVAL; + return changed; +} + +INLINE int mask_value(const mask_t *mask) +{ + assert(!mask_empty(mask)); + return mask_min(mask); +} diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 2b873593..7de083cb 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -94,64 +94,6 @@ int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info) return pcm->ops->info(pcm->op_arg, info); } -int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) -{ - int err; - assert(pcm && info); -#if 0 - fprintf(stderr, "hw_info entered:\n"); - snd_pcm_dump_hw_info(info, stderr); - fprintf(stderr, "\n"); -#endif - err = pcm->ops->hw_info(pcm->op_arg, info); -#if 0 - fprintf(stderr, "hw_info return %d:\n", err); - snd_pcm_dump_hw_info(info, stderr); - fprintf(stderr, "\n"); -#endif - return err; -} - -void snd_pcm_hw_info_any(snd_pcm_hw_info_t *info) -{ - assert(info); - info->flags = 0; - info->access_mask = ~0; - info->format_mask = ~0; - info->subformat_mask = ~0; - info->channels_min = 1; - info->channels_max = UINT_MAX; - info->rate_min = 1; - info->rate_max = UINT_MAX; - info->fragment_length_min = 0; - info->fragment_length_max = UINT_MAX; - info->fragments_min = 1; - info->fragments_max = UINT_MAX; - info->buffer_length_min = 1; - info->buffer_length_max = UINT_MAX; -} - -void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info) -{ - int r; - assert(info && params); - info->flags = 0; - info->access_mask = 1U << params->access; - info->format_mask = 1U << params->format; - info->subformat_mask = 1U << params->subformat; - info->channels_min = info->channels_max = params->channels; - info->rate_min = info->rate_max = params->rate; - info->fragment_length_min = muldiv_down(params->fragment_size, 1000000, params->rate); - info->fragment_length_max = muldiv(params->fragment_size + 1, 1000000, params->rate, &r); - if (r == 0) - info->fragment_length_max--; - info->fragments_min = info->fragments_max = params->fragments; - info->buffer_length_min = muldiv_down(params->fragment_size * params->fragments, 1000000, params->rate); - info->buffer_length_max = muldiv((params->fragment_size + 1) * params->fragments, 1000000, params->rate, &r); - if (r == 0) - info->buffer_length_max--; -} - int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) { int err; @@ -377,7 +319,6 @@ int snd_pcm_poll_descriptor(snd_pcm_t *pcm) #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_INFO(v) [SND_PCM_HW_INFO_##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 @@ -400,25 +341,21 @@ char *snd_pcm_state_names[] = { STATE(PAUSED), }; -char *snd_pcm_hw_info_names[] = { - HW_INFO(ACCESS), - HW_INFO(FORMAT), - HW_INFO(SUBFORMAT), - HW_INFO(CHANNELS), - HW_INFO(RATE), - HW_INFO(FRAGMENT_LENGTH), - HW_INFO(FRAGMENTS), - HW_INFO(BUFFER_LENGTH), -}; - char *snd_pcm_hw_param_names[] = { HW_PARAM(ACCESS), HW_PARAM(FORMAT), HW_PARAM(SUBFORMAT), HW_PARAM(CHANNELS), HW_PARAM(RATE), + HW_PARAM(FRAGMENT_LENGTH), HW_PARAM(FRAGMENT_SIZE), HW_PARAM(FRAGMENTS), + HW_PARAM(BUFFER_LENGTH), + HW_PARAM(BUFFER_SIZE), + HW_PARAM(SAMPLE_BITS), + HW_PARAM(FRAME_BITS), + HW_PARAM(FRAGMENT_BYTES), + HW_PARAM(BUFFER_BYTES), }; char *snd_pcm_sw_param_names[] = { @@ -574,47 +511,6 @@ int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *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) { - 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, snd_pcm_access_names)); - break; - case SND_PCM_HW_PARAM_FORMAT: - 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, snd_pcm_subformat_names)); - break; - case SND_PCM_HW_PARAM_CHANNELS: - fprintf(fp, "channels: %u\n", params->channels); - break; - case SND_PCM_HW_PARAM_RATE: - fprintf(fp, "rate: %u\n", params->rate); - break; - case SND_PCM_HW_PARAM_FRAGMENT_SIZE: - fprintf(fp, "fragment_size: %lu\n", (long)params->fragment_size); - break; - case SND_PCM_HW_PARAM_FRAGMENTS: - fprintf(fp, "fragments: %u\n", params->fragments); - break; - default: - assert(0); - break; - } - } - return 0; -} - int snd_pcm_dump_sw_params_fail(snd_pcm_sw_params_t *params, FILE *fp) { int k; @@ -678,6 +574,12 @@ int snd_pcm_dump(snd_pcm_t *pcm, FILE *fp) return 0; } +const char *snd_pcm_access_name(unsigned int access) +{ + assert(access <= SND_PCM_ACCESS_LAST); + return snd_pcm_access_names[access]; +} + const char *snd_pcm_format_name(unsigned int format) { assert(format <= SND_PCM_FORMAT_LAST); @@ -700,6 +602,18 @@ int snd_pcm_format_value(const char* name) return -1; } +const char *snd_pcm_subformat_name(unsigned int subformat) +{ + assert(subformat <= SND_PCM_SUBFORMAT_LAST); + return snd_pcm_subformat_names[subformat]; +} + +const char *snd_pcm_hw_param_name(unsigned int param) +{ + assert(param <= SND_PCM_HW_PARAM_LAST); + return snd_pcm_hw_param_names[param]; +} + ssize_t snd_pcm_bytes_to_frames(snd_pcm_t *pcm, ssize_t bytes) { assert(pcm); @@ -1291,1124 +1205,6 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas, return err; } -#if 0 -int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i) -{ - i->type = SND_PCM_MMAP_USER; - i->size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size); - i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666); - if (i->u.user.shmid < 0) { - SYSERR("shmget failed"); - return -errno; - } - i->addr = shmat(i->u.user.shmid, 0, 0); - if (i->addr == (void*) -1) { - SYSERR("shmat failed"); - return -errno; - } - return 0; -} - -int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd) -{ - i->type = SND_PCM_MMAP_KERNEL; - /* FIXME */ - i->size = PAGE_ALIGN(snd_pcm_frames_to_bytes(pcm, pcm->buffer_size)); - i->addr = mmap(NULL, i->size, - PROT_WRITE | PROT_READ, - MAP_FILE|MAP_SHARED, - fd, SND_PCM_MMAP_OFFSET_DATA); - if (i->addr == MAP_FAILED || - i->addr == NULL) { - SYSERR("data mmap failed"); - return -errno; - } - i->u.kernel.fd = fd; - return 0; -} - -int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i) -{ - if (i->type == SND_PCM_MMAP_USER) { - if (shmdt(i->addr) < 0) { - SYSERR("shmdt failed"); - return -errno; - } - if (shmctl(i->u.user.shmid, IPC_RMID, 0) < 0) { - SYSERR("shmctl IPC_RMID failed"); - return -errno; - } - } else { - if (munmap(pcm->mmap_info->addr, pcm->mmap_info->size) < 0) { - SYSERR("data munmap failed"); - return -errno; - } - } - return 0; -} -#endif - -int snd_pcm_hw_info_bits_per_sample(snd_pcm_hw_info_t *info, - unsigned int *min, unsigned int *max) -{ - int k; - unsigned int bits_min = UINT_MAX, bits_max = 0; - int changed = 0; - for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) { - int bits; - if (!(info->format_mask & (1U << k))) - continue; - bits = snd_pcm_format_physical_width(k); - assert(bits > 0); - if ((unsigned) bits < *min || (unsigned)bits > *max) { - info->format_mask &= ~(1U << k); - changed++; - continue; - } - if ((unsigned)bits < bits_min) - bits_min = bits; - if ((unsigned)bits > bits_max) - bits_max = bits; - } - *min = bits_min; - *max = bits_max; - if (info->format_mask == 0) - return -EINVAL; - return changed; -} - - -int snd_pcm_hw_info_complete(snd_pcm_hw_info_t *info) -{ - if (info->msbits == 0) { - unsigned int bits_min = 0, bits_max = UINT_MAX; - snd_pcm_hw_info_bits_per_sample(info, &bits_min, &bits_max); - if (bits_min == bits_max) - info->msbits = bits_min; - } - if (info->rate_den == 0 && - info->rate_min == info->rate_max) { - info->rate_num = info->rate_min; - info->rate_den = 1; - } - return 0; -} - -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); - int (*next_value)(const snd_pcm_hw_info_t *info, - unsigned int param, - int 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; - int (*next_value)(const snd_pcm_hw_info_t *info, - unsigned int param, - int 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 { - int 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_infos[SND_PCM_HW_INFO_LAST + 1] = { - [SND_PCM_HW_INFO_ACCESS] = { - type: MASK, - names: snd_pcm_access_names, - last: SND_PCM_ACCESS_LAST, - }, - [SND_PCM_HW_INFO_FORMAT] = { - type: MASK, - names: snd_pcm_format_names, - last: SND_PCM_FORMAT_LAST, - }, - [SND_PCM_HW_INFO_SUBFORMAT] = { - type: MASK, - names: snd_pcm_subformat_names, - last: SND_PCM_SUBFORMAT_LAST, - }, - [SND_PCM_HW_INFO_CHANNELS] = { - type: MINMAX, - names: 0, - last: 0, - }, - [SND_PCM_HW_INFO_RATE] = { - type: MINMAX, - names: 0, - last: 0, - }, - [SND_PCM_HW_INFO_FRAGMENT_LENGTH] = { - type: MINMAX, - names: 0, - last: 0, - }, - [SND_PCM_HW_INFO_FRAGMENTS] = { - type: MINMAX, - names: 0, - last: 0, - }, - [SND_PCM_HW_INFO_BUFFER_LENGTH] = { - 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_INFO_ACCESS: - return info->access_mask; - case SND_PCM_HW_INFO_FORMAT: - return info->format_mask; - case SND_PCM_HW_INFO_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 int *min, unsigned int *max) -{ - switch (param) { - case SND_PCM_HW_INFO_ACCESS: - case SND_PCM_HW_INFO_FORMAT: - case SND_PCM_HW_INFO_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_HW_INFO_CHANNELS: - *min = info->channels_min; - *max = info->channels_max; - break; - case SND_PCM_HW_INFO_RATE: - *min = info->rate_min; - *max = info->rate_max; - break; - case SND_PCM_HW_INFO_FRAGMENT_LENGTH: - *min = info->fragment_length_min; - *max = info->fragment_length_max; - break; - case SND_PCM_HW_INFO_FRAGMENTS: - *min = info->fragments_min; - *max = info->fragments_max; - break; - case SND_PCM_HW_INFO_BUFFER_LENGTH: - *min = info->buffer_length_min; - *max = info->buffer_length_max; - break; - default: - assert(0); - } -} - -void snd_pcm_hw_info_par_set_mask(snd_pcm_hw_info_t *info, unsigned int param, - unsigned int v) -{ - switch (param) { - case SND_PCM_HW_INFO_ACCESS: - info->access_mask = v; - break; - case SND_PCM_HW_INFO_FORMAT: - info->format_mask = v; - break; - case SND_PCM_HW_INFO_SUBFORMAT: - info->subformat_mask = v; - break; - default: - assert(0); - } -} - -void snd_pcm_hw_info_par_set_minmax(snd_pcm_hw_info_t *info, - unsigned int param, - unsigned int min, unsigned int max) -{ - switch (param) { - case SND_PCM_HW_INFO_ACCESS: - case SND_PCM_HW_INFO_FORMAT: - case SND_PCM_HW_INFO_SUBFORMAT: - { - 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; - } - case SND_PCM_HW_INFO_CHANNELS: - info->channels_min = min; - info->channels_max = max; - break; - case SND_PCM_HW_INFO_RATE: - info->rate_min = min; - info->rate_max = max; - break; - case SND_PCM_HW_INFO_FRAGMENT_LENGTH: - info->fragment_length_min = min; - info->fragment_length_max = max; - break; - case SND_PCM_HW_INFO_FRAGMENTS: - info->fragments_min = min; - info->fragments_max = max; - break; - case SND_PCM_HW_INFO_BUFFER_LENGTH: - info->buffer_length_min = min; - info->buffer_length_max = max; - break; - default: - assert(0); - } -} - -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_INFO_ACCESS: - info->access_mask = src->access_mask; - break; - case SND_PCM_HW_INFO_FORMAT: - info->format_mask = src->format_mask; - break; - case SND_PCM_HW_INFO_SUBFORMAT: - info->subformat_mask = src->subformat_mask; - break; - case SND_PCM_HW_INFO_CHANNELS: - info->channels_min = src->channels_min; - info->channels_max = src->channels_max; - break; - case SND_PCM_HW_INFO_RATE: - info->rate_min = src->rate_min; - info->rate_max = src->rate_max; - break; - case SND_PCM_HW_INFO_FRAGMENT_LENGTH: - info->fragment_length_min = src->fragment_length_min; - info->fragment_length_max = src->fragment_length_max; - break; - case SND_PCM_HW_INFO_FRAGMENTS: - info->fragments_min = src->fragments_min; - info->fragments_max = src->fragments_max; - break; - case SND_PCM_HW_INFO_BUFFER_LENGTH: - info->buffer_length_min = src->buffer_length_min; - info->buffer_length_max = src->buffer_length_max; - break; - default: - assert(0); - break; - } -} - -unsigned int snd_pcm_hw_info_par_choices(const snd_pcm_hw_info_t *info, - unsigned int param) -{ - par_desc_t *p; - assert(param <= SND_PCM_HW_INFO_LAST); - p = &hw_infos[param]; - switch (p->type) { - case MASK: - return hweight32(snd_pcm_hw_info_par_get_mask(info, param)); - case MINMAX: - { - unsigned int min, max; - snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); - return max - min + 1; - } - default: - assert(0); - return 0; - } -} - -unsigned int snd_pcm_hw_info_par_refine_min(snd_pcm_hw_info_t *info, - unsigned int param, - unsigned int value) -{ - unsigned int 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 int snd_pcm_hw_info_par_refine_max(snd_pcm_hw_info_t *info, - unsigned int param, - unsigned int value) -{ - unsigned int 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 int value) -{ - par_desc_t *p; - assert(param <= SND_PCM_HW_INFO_LAST); - p = &hw_infos[param]; - switch (p->type) { - case MASK: - return snd_pcm_hw_info_par_get_mask(info, param) & (1 << value); - case MINMAX: - { - unsigned int min, max; - snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); - return value >= min && value <= max; - } - default: - assert(0); - return 0; - } -} - -int snd_pcm_hw_info_par_nearest_next(const snd_pcm_hw_info_t *info, - unsigned int param, - unsigned int best, int value, - snd_pcm_t *pcm) -{ - unsigned int min, max; - unsigned int max1, min2; - snd_pcm_hw_info_t i1, i2; - int err1 = -EINVAL; - int err2 = -EINVAL; - - snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); - i1 = *info; - i2 = *info; - if (value < 0) { - max1 = best; - min2 = best + 1; - } else { - int diff = value - best; - if (diff < 0) { - if (value > 1) - max1 = value - 1; - else - max1 = 0; - min2 = best - diff; - } else { - if (best > (unsigned int) diff) - max1 = best - diff - 1; - else - max1 = 0; - min2 = value + 1; - } - } - 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 = 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 = snd_pcm_hw_info_par_refine_min(&i2, param, min2); - } - if (err1 < 0) { - if (err2 < 0) - return -1; - return min2; - } else if (err2 < 0) - return max1; - if (best - max1 <= min2 - best) - return max1; - 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_INFO_LAST); - p = &hw_infos[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 int min, max; - snd_pcm_hw_info_par_get_minmax(info, param, &min, &max); - if (min == max) - printf("%u", min); - else - printf("%u - %u", min, max); - break; - } - 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_INFO_LAST); - p = &hw_infos[param]; - switch (p->type) { - case MASK: - return !snd_pcm_hw_info_par_get_mask(info, param); - case MINMAX: - { - unsigned int 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_INFO_LAST; ++k) { - if (snd_pcm_hw_info_par_empty(info, k)) - mask |= 1 << k; - } - return mask; -} - -int snd_pcm_hw_info_to_params(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params) -{ - int err; - err = snd_pcm_hw_info(pcm, info); - if (err < 0) { - params->fail_mask = snd_pcm_hw_info_fail_mask(info); - return err; - } - - assert(info->access_mask); - if (info->access_mask & (info->access_mask - 1)) { - info->access_mask = 1 << (ffs(info->access_mask) - 1); - err = snd_pcm_hw_info(pcm, info); - assert(err >= 0); - } - assert(info->format_mask); - if (info->format_mask & (info->format_mask - 1)) { - info->format_mask = 1 << (ffs(info->format_mask) - 1); - err = snd_pcm_hw_info(pcm, info); - assert(err >= 0); - } - assert(info->subformat_mask); - if (info->subformat_mask & (info->subformat_mask - 1)) { - info->subformat_mask = 1 << (ffs(info->subformat_mask) - 1); - err = snd_pcm_hw_info(pcm, info); - assert(err >= 0); - } - assert(info->channels_min <= info->channels_max); - if (info->channels_min < info->channels_max) { - info->channels_max = info->channels_min; - err = snd_pcm_hw_info(pcm, info); - assert(err >= 0); - } - assert(info->rate_min <= info->rate_max); - if (info->rate_min < info->rate_max) { - info->rate_max = info->rate_min; - err = snd_pcm_hw_info(pcm, info); - assert(err >= 0); - } - assert(info->fragment_length_min <= info->fragment_length_max); - if (info->fragment_length_min < info->fragment_length_max) { - info->fragment_length_max = info->fragment_length_min; - err = snd_pcm_hw_info(pcm, info); - assert(err >= 0); - } - assert(info->fragments_min <= info->fragments_max); - if (info->fragments_min < info->fragments_max) { - /* Defaults to maximum use of buffer */ - info->fragments_min = info->fragments_max; - err = snd_pcm_hw_info(pcm, info); - assert(err >= 0); - } - params->access = ffs(info->access_mask) - 1; - params->format = ffs(info->format_mask) - 1; - params->subformat = ffs(info->subformat_mask) - 1; - params->channels = info->channels_min; - params->rate = info->rate_min; - params->fragment_size = muldiv_near(info->fragment_length_min, info->rate_min, 1000000); - params->fragments = info->fragments_min; - return 0; -} - -int _snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info) -{ - int err; - params->fail_mask = 0; - - if (pcm->mmap_channels) { - err = snd_pcm_munmap(pcm); - if (err < 0) - return err; - } - err = pcm->ops->hw_params(pcm->op_arg, params); - if (err < 0) - goto _mmap; - - 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_num = info->rate_num; - pcm->rate_den = info->rate_den; - 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; - - _mmap: - 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_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) -{ - snd_pcm_hw_info_t info; - int err; - assert(pcm && params); - 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; - } - return _snd_pcm_hw_params_info(pcm, params, &info); -} - -int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, - snd_pcm_hw_info_t *info) -{ - int err = snd_pcm_hw_info_to_params(pcm, info, params); - if (err < 0) - return err; - return _snd_pcm_hw_params_info(pcm, params, info); -} - - -int snd_pcm_hw_info_strategy1(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, - const snd_pcm_strategy_t *strategy, - unsigned int badness_min, unsigned int badness_max) -{ - snd_pcm_hw_info_t best_info; - int param; - int value; - unsigned int best_badness; - 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 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) { - int err; - value = strategy->next_value(info, param, value, pcm, strategy); - if (value < 0) - break; - info1 = *info; - snd_pcm_hw_info_par_set_minmax(&info1, param, value, value); - err = snd_pcm_hw_info(pcm, &info1); - 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; - } - mask &= snd_pcm_hw_info_fail_mask(&info1); - } - if (best_badness == UINT_MAX) { - for (param = 0; param <= SND_PCM_HW_INFO_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) -{ - int err; - err = snd_pcm_hw_info(pcm, info); - if (err < 0) - return err; - return snd_pcm_hw_info_strategy1(pcm, info, strategy, - strategy->badness_min, - strategy->badness_max); -} - - -void snd_pcm_strategy_simple_free(snd_pcm_strategy_t *strategy) -{ - snd_pcm_strategy_simple_t *pars = strategy->private; - int k; - for (k = 0; k <= SND_PCM_HW_INFO_LAST; ++k) { - if (pars[k].valid && pars[k].free) - pars[k].free(&pars[k]); - } - free(pars); -} - -int snd_pcm_strategy_simple_choose_param(const snd_pcm_hw_info_t *info, - snd_pcm_t *pcm ATTRIBUTE_UNUSED, - const snd_pcm_strategy_t *strategy) -{ - unsigned int param; - int best_param = -1; - const snd_pcm_strategy_simple_t *pars = strategy->private; - unsigned int min_choices = UINT_MAX; - unsigned int min_order = UINT_MAX; - for (param = 0; param <= SND_PCM_HW_INFO_LAST; ++param) { - const snd_pcm_strategy_simple_t *p = &pars[param]; - unsigned int choices; - if (!p->valid) - continue; - choices = snd_pcm_hw_info_par_choices(info, param); - if (choices == 1) - continue; - assert(choices != 0); - if (p->order < min_order || - (p->order == min_order && - choices < min_choices)) { - min_order = p->order; - min_choices = choices; - best_param = param; - } - } - return best_param; -} - -int snd_pcm_strategy_simple_next_value(const snd_pcm_hw_info_t *info, - unsigned int param, - int value, - snd_pcm_t *pcm, - const snd_pcm_strategy_t *strategy) -{ - const snd_pcm_strategy_simple_t *pars = strategy->private; - assert(pars[param].valid); - return pars[param].next_value(info, param, value, pcm, &pars[param]); -} - - -int snd_pcm_strategy_simple_min_badness(const snd_pcm_hw_info_t *info, - unsigned int max_badness, - snd_pcm_t *pcm, - const snd_pcm_strategy_t *strategy) -{ - unsigned int param; - unsigned int badness = 0; - const snd_pcm_strategy_simple_t *pars = strategy->private; - for (param = 0; param <= SND_PCM_HW_INFO_LAST; ++param) { - unsigned int b; - if (!pars[param].valid) - continue; - b = pars[param].min_badness(info, param, pcm, &pars[param]); - if (b > max_badness || max_badness - b < badness) - return -E2BIG; - badness += b; - } - return badness; -} - - -void snd_pcm_strategy_simple_near_free(snd_pcm_strategy_simple_t *par) -{ - snd_pcm_strategy_simple_near_t *p = par->private; - free(p); -} - -unsigned int snd_pcm_strategy_simple_near_min_badness(const snd_pcm_hw_info_t *info, - unsigned int param, - snd_pcm_t *pcm, - const snd_pcm_strategy_simple_t *par) -{ - const snd_pcm_strategy_simple_near_t *p = par->private; - int value = snd_pcm_hw_info_par_nearest_next(info, param, p->best, -1, pcm); - int diff; - assert(value >= 0); - diff = p->best - value; - if (diff < 0) - diff = -diff; - return diff * p->mul; -} - -int snd_pcm_strategy_simple_near_next_value(const snd_pcm_hw_info_t *info, - unsigned int param, - int value, - snd_pcm_t *pcm, - const snd_pcm_strategy_simple_t *par) -{ - const snd_pcm_strategy_simple_near_t *p = par->private; - 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) -{ - snd_pcm_strategy_simple_choices_t *p = par->private; -// free(p->choices); - free(p); -} - -unsigned int snd_pcm_strategy_simple_choices_min_badness(const snd_pcm_hw_info_t *info, - unsigned int param, - snd_pcm_t *pcm ATTRIBUTE_UNUSED, - const snd_pcm_strategy_simple_t *par) -{ - const snd_pcm_strategy_simple_choices_t *p = par->private; - unsigned int k; - for (k = 0; k < p->count; ++k) { - if (snd_pcm_hw_info_par_check(info, param, p->choices[k].value)) - return p->choices[k].badness; - } - assert(0); - return UINT_MAX; -} - -int snd_pcm_strategy_simple_choices_next_value(const snd_pcm_hw_info_t *info, - unsigned int param, - int value, - snd_pcm_t *pcm ATTRIBUTE_UNUSED, - const snd_pcm_strategy_simple_t *par) -{ - const snd_pcm_strategy_simple_choices_t *p = par->private; - unsigned int k = 0; - if (value >= 0) { - for (; k < p->count; ++k) { - if (p->choices[k].value == (unsigned int) value) { - k++; - break; - } - } - } - for (; k < p->count; ++k) { - unsigned int v = p->choices[k].value; - if (snd_pcm_hw_info_par_check(info, param, v)) - return v; - } - return -1; -} - -int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy) -{ - if (strategy->free) - strategy->free(strategy); - free(strategy); - return 0; -} - -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; - assert(strategyp); - data = calloc(SND_PCM_HW_INFO_LAST + 1, sizeof(*data)); - if (!data) - return -ENOMEM; - s = calloc(1, sizeof(*s)); - if (!s) { - free(data); - return -ENOMEM; - } - 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; - return 0; -} - -int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, - int order, - unsigned int param, - unsigned int best, - unsigned int mul) -{ - snd_pcm_strategy_simple_t *s = strategy->private; - snd_pcm_strategy_simple_near_t *data; - assert(strategy); - assert(param <= SND_PCM_HW_INFO_LAST); - assert(!s->valid); - data = calloc(1, sizeof(*data)); - if (!data) - return -ENOMEM; - 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; - s->private = data; - s->free = snd_pcm_strategy_simple_near_free; - return 0; -} - -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) -{ - snd_pcm_strategy_simple_t *s = strategy->private; - snd_pcm_strategy_simple_choices_t *data; - assert(strategy); - assert(param <= SND_PCM_HW_INFO_LAST); - assert(!s->valid); - data = calloc(1, sizeof(*data)); - if (!data) - return -ENOMEM; - data->count = count; - 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; - s->free = snd_pcm_strategy_simple_choices_free; - 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_INFO_LAST; param++) { - fprintf(fp, "%s: ", snd_pcm_hw_info_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_INFO_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_info_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_INFO_LAST; param++) { - if (!(fail_mask & (1 << param))) - continue; - fprintf(fp, "%s: ", snd_pcm_hw_info_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 dd32c5ad..2946b48e 100644 --- a/src/pcm/pcm_adpcm.c +++ b/src/pcm/pcm_adpcm.c @@ -329,35 +329,59 @@ static int snd_pcm_adpcm_close(snd_pcm_t *pcm) return 0; } -static int snd_pcm_adpcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int snd_pcm_adpcm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_adpcm_t *adpcm = pcm->private; - unsigned int format_mask, access_mask; + snd_pcm_t *slave = adpcm->plug.slave; int err; - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - access_mask = info->access_mask; - if (access_mask == 0) - return -EINVAL; - if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) - info->format_mask &= SND_PCM_FMTBIT_LINEAR; - else - info->format_mask &= SND_PCM_FMTBIT_IMA_ADPCM; - format_mask = info->format_mask; - if (format_mask == 0) - return -EINVAL; + snd_pcm_hw_params_t sparams; + mask_t *access_mask = alloca(mask_sizeof()); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(access_mask, SND_PCM_ACCBIT_PLUGIN); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_ACCESS, + access_mask); + if (err < 0) + return err; + if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) { + mask_t *format_mask = alloca(mask_sizeof()); + mask_load(format_mask, SND_PCM_FMTBIT_LINEAR); + err = _snd_pcm_hw_params_mask(params, 1, + SND_PCM_HW_PARAM_FORMAT, + format_mask); + if (err < 0) + return err; + } else { + err = _snd_pcm_hw_params_set(params, 1, + SND_PCM_HW_PARAM_FORMAT, + SND_PCM_FORMAT_IMA_ADPCM); + if (err < 0) + return err; + } + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + if (err < 0) + return err; - info->format_mask = 1U << adpcm->sformat; - info->access_mask = SND_PCM_ACCBIT_MMAP; - err = snd_pcm_hw_info(adpcm->plug.slave, info); - info->format_mask = format_mask; - info->access_mask = access_mask; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + adpcm->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave, + SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); if (err < 0) return err; - info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - snd_pcm_hw_info_complete(info); + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } @@ -365,19 +389,33 @@ 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; - snd_pcm_hw_info_t sinfo; - snd_pcm_hw_params_t sparams; int err; - 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; + snd_pcm_hw_params_t sparams; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + adpcm->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave, + SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); if (err < 0) return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) { - adpcm->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16); + adpcm->getput_idx = get_index(snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT), SND_PCM_FORMAT_S16); adpcm->func = adpcm_encode; } else { adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, adpcm->sformat); @@ -385,7 +423,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, params->format); + adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT)); adpcm->func = adpcm_decode; } else { adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_FORMAT_S16); @@ -394,7 +432,7 @@ static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) } if (adpcm->states) free(adpcm->states); - adpcm->states = malloc(params->channels * sizeof(*adpcm->states)); + adpcm->states = malloc(snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_CHANNELS) * sizeof(*adpcm->states)); return 0; } @@ -495,7 +533,7 @@ static void snd_pcm_adpcm_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_adpcm_ops = { close: snd_pcm_adpcm_close, info: snd_pcm_plugin_info, - hw_info: snd_pcm_adpcm_hw_info, + hw_refine: snd_pcm_adpcm_hw_refine, hw_params: snd_pcm_adpcm_hw_params, sw_params: snd_pcm_plugin_sw_params, dig_info: snd_pcm_plugin_dig_info, diff --git a/src/pcm/pcm_alaw.c b/src/pcm/pcm_alaw.c index a6428775..d29dab8f 100644 --- a/src/pcm/pcm_alaw.c +++ b/src/pcm/pcm_alaw.c @@ -211,35 +211,59 @@ static void alaw_encode(const snd_pcm_channel_area_t *src_areas, } } -static int snd_pcm_alaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int snd_pcm_alaw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_alaw_t *alaw = pcm->private; - unsigned int format_mask, access_mask; + snd_pcm_t *slave = alaw->plug.slave; int err; - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - access_mask = info->access_mask; - if (access_mask == 0) - return -EINVAL; - if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) - info->format_mask &= SND_PCM_FMTBIT_LINEAR; - else - info->format_mask &= SND_PCM_FMTBIT_MU_LAW; - format_mask = info->format_mask; - if (format_mask == 0) - return -EINVAL; + snd_pcm_hw_params_t sparams; + mask_t *access_mask = alloca(mask_sizeof()); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(access_mask, SND_PCM_ACCBIT_PLUGIN); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_ACCESS, + access_mask); + if (err < 0) + return err; + if (alaw->sformat == SND_PCM_FORMAT_A_LAW) { + mask_t *format_mask = alloca(mask_sizeof()); + mask_load(format_mask, SND_PCM_FMTBIT_LINEAR); + err = _snd_pcm_hw_params_mask(params, 1, + SND_PCM_HW_PARAM_FORMAT, + format_mask); + if (err < 0) + return err; + } else { + err = _snd_pcm_hw_params_set(params, 1, + SND_PCM_HW_PARAM_FORMAT, + SND_PCM_FORMAT_A_LAW); + if (err < 0) + return err; + } + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + if (err < 0) + return err; - info->format_mask = 1U << alaw->sformat; - info->access_mask = SND_PCM_ACCBIT_MMAP; - err = snd_pcm_hw_info(alaw->plug.slave, info); - info->format_mask = format_mask; - info->access_mask = access_mask; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + alaw->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave, + SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); if (err < 0) return err; - info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - snd_pcm_hw_info_complete(info); + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } @@ -247,27 +271,41 @@ 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; - snd_pcm_hw_info_t sinfo; - snd_pcm_hw_params_t sparams; int err; - 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; + snd_pcm_hw_params_t sparams; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + alaw->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave, + SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); if (err < 0) return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) { - alaw->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16); + if (alaw->sformat == SND_PCM_FORMAT_A_LAW) { + alaw->getput_idx = get_index(snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT), SND_PCM_FORMAT_S16); alaw->func = alaw_encode; } else { alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, alaw->sformat); alaw->func = alaw_decode; } } else { - if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) { - alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, params->format); + if (alaw->sformat == SND_PCM_FORMAT_A_LAW) { + alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT)); alaw->func = alaw_decode; } else { alaw->getput_idx = get_index(alaw->sformat, SND_PCM_FORMAT_S16); @@ -363,7 +401,7 @@ static void snd_pcm_alaw_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_alaw_ops = { close: snd_pcm_plugin_close, info: snd_pcm_plugin_info, - hw_info: snd_pcm_alaw_hw_info, + hw_refine: snd_pcm_alaw_hw_refine, hw_params: snd_pcm_alaw_hw_params, sw_params: snd_pcm_plugin_sw_params, dig_info: snd_pcm_plugin_dig_info, diff --git a/src/pcm/pcm_copy.c b/src/pcm/pcm_copy.c index b699883d..4a9ea503 100644 --- a/src/pcm/pcm_copy.c +++ b/src/pcm/pcm_copy.c @@ -28,40 +28,50 @@ typedef struct { snd_pcm_plugin_t plug; } snd_pcm_copy_t; -static int snd_pcm_copy_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int snd_pcm_copy_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_copy_t *copy = pcm->private; - unsigned int access_mask; + snd_pcm_t *slave = copy->plug.slave; int err; - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - access_mask = info->access_mask; - if (access_mask == 0) - return -EINVAL; - - info->access_mask = SND_PCM_ACCBIT_MMAP; - err = snd_pcm_hw_info(copy->plug.slave, info); - info->access_mask = access_mask; + snd_pcm_hw_params_t sparams; + mask_t *access_mask = alloca(mask_sizeof()); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(access_mask, SND_PCM_ACCBIT_PLUGIN); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_ACCESS, + access_mask); if (err < 0) return err; - info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - snd_pcm_hw_info_complete(info); - return 0; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave, + ~SND_PCM_HW_PARBIT_ACCESS); + if (err < 0) + return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + return err; } static int snd_pcm_copy_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_copy_t *copy = pcm->private; snd_pcm_t *slave = copy->plug.slave; - snd_pcm_hw_info_t sinfo; - snd_pcm_hw_params_t sparams; int err; - snd_pcm_hw_params_to_info(params, &sinfo); - sinfo.access_mask = SND_PCM_ACCBIT_MMAP; - err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); - params->fail_mask = sparams.fail_mask; + snd_pcm_hw_params_t sparams; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave, + ~SND_PCM_HW_PARBIT_ACCESS); + if (err < 0) + return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return err; } @@ -149,7 +159,7 @@ static void snd_pcm_copy_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_copy_ops = { close: snd_pcm_plugin_close, info: snd_pcm_plugin_info, - hw_info: snd_pcm_copy_hw_info, + hw_refine: snd_pcm_copy_hw_refine, hw_params: snd_pcm_copy_hw_params, sw_params: snd_pcm_plugin_sw_params, dig_info: snd_pcm_plugin_dig_info, diff --git a/src/pcm/pcm_file.c b/src/pcm/pcm_file.c index 2b563c39..bd464cb7 100644 --- a/src/pcm/pcm_file.c +++ b/src/pcm/pcm_file.c @@ -254,10 +254,10 @@ static int snd_pcm_file_set_avail_min(snd_pcm_t *pcm, size_t frames) return snd_pcm_set_avail_min(file->slave, frames); } -static int snd_pcm_file_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int snd_pcm_file_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_file_t *file = pcm->private; - return snd_pcm_hw_info(file->slave, info); + return snd_pcm_hw_refine(file->slave, params); } static int snd_pcm_file_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) @@ -314,7 +314,7 @@ static void snd_pcm_file_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_file_ops = { close: snd_pcm_file_close, info: snd_pcm_file_info, - hw_info: snd_pcm_file_hw_info, + hw_refine: snd_pcm_file_hw_refine, hw_params: snd_pcm_file_hw_params, sw_params: snd_pcm_file_sw_params, dig_info: snd_pcm_file_dig_info, diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c index c874634e..78e1ecf3 100644 --- a/src/pcm/pcm_hw.c +++ b/src/pcm/pcm_hw.c @@ -109,7 +109,7 @@ static int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid) return 0; } -static int _snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) +static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; @@ -120,12 +120,12 @@ static int _snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info) return 0; } -static int snd_pcm_hw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_hw_t *hw = pcm->private; int fd = hw->fd; - if (ioctl(fd, SND_PCM_IOCTL_HW_INFO, info) < 0) { - // SYSERR("SND_PCM_IOCTL_HW_INFO failed"); + if (ioctl(fd, SND_PCM_IOCTL_HW_REFINE, params) < 0) { + // SYSERR("SND_PCM_IOCTL_HW_REFINE failed"); return -errno; } return 0; @@ -521,8 +521,8 @@ static void snd_pcm_hw_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_hw_ops = { close: snd_pcm_hw_close, - info: _snd_pcm_hw_info, - hw_info: snd_pcm_hw_hw_info, + info: snd_pcm_hw_info, + hw_refine: snd_pcm_hw_hw_refine, hw_params: snd_pcm_hw_hw_params, sw_params: snd_pcm_hw_sw_params, dig_info: snd_pcm_hw_dig_info, diff --git a/src/pcm/pcm_linear.c b/src/pcm/pcm_linear.c index 50c3e603..0647ee07 100644 --- a/src/pcm/pcm_linear.c +++ b/src/pcm/pcm_linear.c @@ -72,32 +72,50 @@ static void linear_transfer(const snd_pcm_channel_area_t *src_areas, size_t src_ } } -static int snd_pcm_linear_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int snd_pcm_linear_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_linear_t *linear = pcm->private; - unsigned int format_mask, access_mask; + snd_pcm_t *slave = linear->plug.slave; int err; - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - 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; + snd_pcm_hw_params_t sparams; + mask_t *access_mask = alloca(mask_sizeof()); + mask_t *format_mask = alloca(mask_sizeof()); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(access_mask, SND_PCM_ACCBIT_PLUGIN); + mask_load(format_mask, SND_PCM_FMTBIT_LINEAR); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_ACCESS, + access_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_FORMAT, + format_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + if (err < 0) + return err; - info->format_mask = 1U << linear->sformat; - info->access_mask = SND_PCM_ACCBIT_MMAP; - err = snd_pcm_hw_info(linear->plug.slave, info); - info->format_mask = format_mask; - info->access_mask = access_mask; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + linear->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave, + SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); if (err < 0) return err; - info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - snd_pcm_hw_info_complete(info); + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } @@ -105,22 +123,36 @@ 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; - snd_pcm_hw_info_t sinfo; - snd_pcm_hw_params_t sparams; int err; - 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; + snd_pcm_hw_params_t sparams; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + linear->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave, + SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); if (err < 0) return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) - linear->conv_idx = conv_index(params->format, + linear->conv_idx = conv_index(snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT), linear->sformat); else linear->conv_idx = conv_index(linear->sformat, - params->format); + snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT)); return 0; } @@ -208,7 +240,7 @@ static void snd_pcm_linear_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_linear_ops = { close: snd_pcm_plugin_close, info: snd_pcm_plugin_info, - hw_info: snd_pcm_linear_hw_info, + hw_refine: snd_pcm_linear_hw_refine, hw_params: snd_pcm_linear_hw_params, sw_params: snd_pcm_plugin_sw_params, dig_info: snd_pcm_plugin_dig_info, diff --git a/src/pcm/pcm_local.h b/src/pcm/pcm_local.h index ecb671b6..0bf9796b 100644 --- a/src/pcm/pcm_local.h +++ b/src/pcm/pcm_local.h @@ -1,6 +1,7 @@ /* * PCM Interface - local header file - * Copyright (c) 1999 by Jaroslav Kysela + * Copyright (c) 2000 by Jaroslav Kysela + * Abramo Bagnara * * * This library is free software; you can redistribute it and/or modify @@ -58,7 +59,7 @@ typedef struct { int (*nonblock)(snd_pcm_t *pcm, int nonblock); int (*async)(snd_pcm_t *pcm, int sig, pid_t pid); int (*info)(snd_pcm_t *pcm, snd_pcm_info_t *info); - int (*hw_info)(snd_pcm_t *pcm, snd_pcm_hw_info_t *info); + int (*hw_refine)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); int (*hw_params)(snd_pcm_t *pcm, snd_pcm_hw_params_t *params); int (*sw_params)(snd_pcm_t *pcm, snd_pcm_sw_params_t *params); int (*dig_info)(snd_pcm_t *pcm, snd_pcm_dig_info_t *info); @@ -168,15 +169,9 @@ ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas, snd_pcm_xfer_areas_func_t func); 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); -int snd_pcm_hw_info_to_params(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params); +int snd_pcm_hw_info_complete(snd_pcm_hw_params_t *info); int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info); int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid); -int snd_pcm_hw_info_par_nearest_next(const snd_pcm_hw_info_t *info, - unsigned int param, - unsigned int best, int value, - snd_pcm_t *pcm); static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm) { @@ -316,3 +311,50 @@ static inline int muldiv_near(int a, int b, int c) n++; return n; } + +int _snd_pcm_hw_refine(snd_pcm_hw_params_t *params); +void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params); +int _snd_pcm_hw_params_mask(snd_pcm_hw_params_t *params, int hw, + unsigned int var, const mask_t *mask); +int _snd_pcm_hw_params_first(snd_pcm_hw_params_t *params, int hw, + unsigned int var); +int _snd_pcm_hw_params_last(snd_pcm_hw_params_t *params, int hw, + unsigned int var); +int _snd_pcm_hw_params_set(snd_pcm_hw_params_t *params, int hw, + unsigned int var, unsigned int val); +int _snd_pcm_hw_params_min(snd_pcm_hw_params_t *params, int hw, + unsigned int var, unsigned int val); +int _snd_pcm_hw_params_max(snd_pcm_hw_params_t *params, int hw, + unsigned int var, unsigned int val); +int snd_pcm_hw_refine2(snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *sparams, + int (*func)(snd_pcm_t *slave, + snd_pcm_hw_params_t *params), + snd_pcm_t *slave, + unsigned int links); +int snd_pcm_hw_params2(snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *sparams, + int (*func)(snd_pcm_t *slave, + snd_pcm_hw_params_t *sparams), + snd_pcm_t *slave, + unsigned int links); + +#define SND_PCM_HW_PARBIT_ACCESS (1 << SND_PCM_HW_PARAM_ACCESS) +#define SND_PCM_HW_PARBIT_FORMAT (1 << SND_PCM_HW_PARAM_FORMAT) +#define SND_PCM_HW_PARBIT_SUBFORMAT (1 << SND_PCM_HW_PARAM_SUBFORMAT) +#define SND_PCM_HW_PARBIT_CHANNELS (1 << SND_PCM_HW_PARAM_CHANNELS) +#define SND_PCM_HW_PARBIT_RATE (1 << SND_PCM_HW_PARAM_RATE) +#define SND_PCM_HW_PARBIT_FRAGMENT_LENGTH (1 << SND_PCM_HW_PARAM_FRAGMENT_LENGTH) +#define SND_PCM_HW_PARBIT_FRAGMENT_SIZE (1 << SND_PCM_HW_PARAM_FRAGMENT_SIZE) +#define SND_PCM_HW_PARBIT_FRAGMENTS (1 << SND_PCM_HW_PARAM_FRAGMENTS) +#define SND_PCM_HW_PARBIT_BUFFER_LENGTH (1 << SND_PCM_HW_PARAM_BUFFER_LENGTH) +#define SND_PCM_HW_PARBIT_BUFFER_SIZE (1 << SND_PCM_HW_PARAM_BUFFER_SIZE) +#define SND_PCM_HW_PARBIT_SAMPLE_BITS (1 << SND_PCM_HW_PARAM_SAMPLE_BITS) +#define SND_PCM_HW_PARBIT_FRAME_BITS (1 << SND_PCM_HW_PARAM_FRAME_BITS) +#define SND_PCM_HW_PARBIT_FRAGMENT_BYTES (1 << SND_PCM_HW_PARAM_FRAGMENT_BYTES) +#define SND_PCM_HW_PARBIT_BUFFER_BYTES (1 << SND_PCM_HW_PARAM_BUFFER_BYTES) + + +#define SND_PCM_ACCBIT_MMAP ((1 << SND_PCM_ACCESS_MMAP_INTERLEAVED) | \ + (1 << SND_PCM_ACCESS_MMAP_NONINTERLEAVED) | \ + (1 << SND_PCM_ACCESS_MMAP_COMPLEX)) diff --git a/src/pcm/pcm_mulaw.c b/src/pcm/pcm_mulaw.c index abefeacb..4b937911 100644 --- a/src/pcm/pcm_mulaw.c +++ b/src/pcm/pcm_mulaw.c @@ -228,35 +228,59 @@ static void mulaw_encode(const snd_pcm_channel_area_t *src_areas, } } -static int snd_pcm_mulaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int snd_pcm_mulaw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_mulaw_t *mulaw = pcm->private; - unsigned int format_mask, access_mask; + snd_pcm_t *slave = mulaw->plug.slave; int err; - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - access_mask = info->access_mask; - if (access_mask == 0) - return -EINVAL; - if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) - info->format_mask &= SND_PCM_FMTBIT_LINEAR; - else - info->format_mask &= SND_PCM_FMTBIT_MU_LAW; - format_mask = info->format_mask; - if (format_mask == 0) - return -EINVAL; + snd_pcm_hw_params_t sparams; + mask_t *access_mask = alloca(mask_sizeof()); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(access_mask, SND_PCM_ACCBIT_PLUGIN); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_ACCESS, + access_mask); + if (err < 0) + return err; + if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) { + mask_t *format_mask = alloca(mask_sizeof()); + mask_load(format_mask, SND_PCM_FMTBIT_LINEAR); + err = _snd_pcm_hw_params_mask(params, 1, + SND_PCM_HW_PARAM_FORMAT, + format_mask); + if (err < 0) + return err; + } else { + err = _snd_pcm_hw_params_set(params, 1, + SND_PCM_HW_PARAM_FORMAT, + SND_PCM_FORMAT_MU_LAW); + if (err < 0) + return err; + } + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + if (err < 0) + return err; - info->format_mask = 1U << mulaw->sformat; - info->access_mask = SND_PCM_ACCBIT_MMAP; - err = snd_pcm_hw_info(mulaw->plug.slave, info); - info->format_mask = format_mask; - info->access_mask = access_mask; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + mulaw->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave, + SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); if (err < 0) return err; - info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - snd_pcm_hw_info_complete(info); + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } @@ -264,19 +288,33 @@ 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; - snd_pcm_hw_info_t sinfo; - snd_pcm_hw_params_t sparams; int err; - 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; + snd_pcm_hw_params_t sparams; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + mulaw->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave, + SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); if (err < 0) return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) { - mulaw->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16); + mulaw->getput_idx = get_index(snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT), SND_PCM_FORMAT_S16); mulaw->func = mulaw_encode; } else { mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, mulaw->sformat); @@ -284,7 +322,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, params->format); + mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT)); mulaw->func = mulaw_decode; } else { mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_FORMAT_S16); @@ -380,7 +418,7 @@ static void snd_pcm_mulaw_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_mulaw_ops = { close: snd_pcm_plugin_close, info: snd_pcm_plugin_info, - hw_info: snd_pcm_mulaw_hw_info, + hw_refine: snd_pcm_mulaw_hw_refine, hw_params: snd_pcm_mulaw_hw_params, sw_params: snd_pcm_plugin_sw_params, dig_info: snd_pcm_plugin_dig_info, diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c index 82c50cc0..57578a3e 100644 --- a/src/pcm/pcm_multi.c +++ b/src/pcm/pcm_multi.c @@ -26,6 +26,7 @@ #include #include #include "pcm_local.h" +#include "interval.h" typedef struct { snd_pcm_t *pcm; @@ -90,83 +91,109 @@ static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info) return 0; } -static int snd_pcm_multi_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) +static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_multi_t *multi = pcm->private; unsigned int k; - snd_pcm_hw_info_t i; - unsigned int access_mask, saccess_mask; + snd_pcm_hw_params_t sparams; 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_min > info->channels_max) - return -EINVAL; - i = *info; - saccess_mask = ~0; + int err; + const mask_t *access_mask = snd_pcm_hw_params_value_mask(params, SND_PCM_HW_PARAM_ACCESS); + mask_t *saccess_mask = alloca(mask_sizeof()); + if (mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) || + mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + else { + mask_none(saccess_mask); + if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED) && + multi->slaves_count == 1) + mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); + if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) + mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); + if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_COMPLEX)) { + mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX); + if (multi->slaves_count > 1) + mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); + } + } + + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_CHANNELS, + multi->channels_count); + if (err < 0) + return err; 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; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, + SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, + SND_PCM_HW_PARAM_CHANNELS, + multi->slaves[k].channels_count); + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave, + SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_BUFFER_LENGTH | + SND_PCM_HW_PARBIT_FRAGMENTS); + if (err < 0) 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_length_min != sinfo.fragment_length_min || - i.fragment_length_max != sinfo.fragment_length_max || - i.fragments_min != sinfo.fragments_min || - i.fragments_max != sinfo.fragments_max || - i.buffer_length_min != sinfo.buffer_length_min || - i.buffer_length_max != sinfo.buffer_length_max) + if (params->hw_cmask) changed++; - saccess_mask &= sinfo.access_mask; - i = sinfo; } } while (changed && multi->slaves_count > 1); - access_mask = info->access_mask; - *info = i; - 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; + unsigned int k; int err; - for (i = 0; i < multi->slaves_count; ++i) { - snd_pcm_t *slave = multi->slaves[i].pcm; - snd_pcm_hw_info_t sinfo; + const mask_t *access_mask = snd_pcm_hw_params_value_mask(params, SND_PCM_HW_PARAM_ACCESS); + mask_t *saccess_mask = alloca(mask_sizeof()); + if (mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) || + mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + else { + mask_none(saccess_mask); + if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED) && + multi->slaves_count == 1) + mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); + if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) + mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); + if (mask_test(access_mask, SND_PCM_ACCESS_MMAP_COMPLEX)) { + mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX); + if (multi->slaves_count > 1) + mask_set(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); + } + } + for (k = 0; k < multi->slaves_count; ++k) { + snd_pcm_t *slave = multi->slaves[k].pcm; 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 = sparams.fail_mask; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_CHANNELS, + multi->slaves[k].channels_count); + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave, + SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_BUFFER_LENGTH | + SND_PCM_HW_PARBIT_FRAGMENTS); + if (err < 0) return err; - } err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format); if (err < 0) return err; @@ -376,7 +403,7 @@ static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_multi_ops = { close: snd_pcm_multi_close, info: snd_pcm_multi_info, - hw_info: snd_pcm_multi_hw_info, + hw_refine: snd_pcm_multi_hw_refine, hw_params: snd_pcm_multi_hw_params, sw_params: snd_pcm_multi_sw_params, dig_info: snd_pcm_multi_dig_info, diff --git a/src/pcm/pcm_null.c b/src/pcm/pcm_null.c index abaa06ad..bdedd22f 100644 --- a/src/pcm/pcm_null.c +++ b/src/pcm/pcm_null.c @@ -230,10 +230,11 @@ static int snd_pcm_null_set_avail_min(snd_pcm_t *pcm, size_t frames) return 0; } -static int snd_pcm_null_hw_info(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_info_t * info) +static int snd_pcm_null_hw_refine(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params) { - snd_pcm_hw_info_complete(info); - info->fifo_size = 0; + _snd_pcm_hw_refine(params); + params->fifo_size = 0; + params->dig_groups = 0; return 0; } @@ -245,15 +246,15 @@ static int snd_pcm_null_hw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_pa static int snd_pcm_null_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t * params) { if (params->start_mode > SND_PCM_START_LAST) { - params->fail_mask = SND_PCM_SW_PARBIT_START_MODE; + params->fail_mask = 1 << SND_PCM_SW_PARAM_START_MODE; return -EINVAL; } if (params->ready_mode > SND_PCM_READY_LAST) { - params->fail_mask = SND_PCM_SW_PARBIT_READY_MODE; + params->fail_mask = 1 << SND_PCM_SW_PARAM_READY_MODE; return -EINVAL; } if (params->xrun_mode > SND_PCM_XRUN_LAST) { - params->fail_mask = SND_PCM_SW_PARBIT_XRUN_MODE; + params->fail_mask = 1 << SND_PCM_SW_PARAM_XRUN_MODE; return -EINVAL; } return 0; @@ -306,7 +307,7 @@ static void snd_pcm_null_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_null_ops = { close: snd_pcm_null_close, info: snd_pcm_null_info, - hw_info: snd_pcm_null_hw_info, + hw_refine: snd_pcm_null_hw_refine, hw_params: snd_pcm_null_hw_params, sw_params: snd_pcm_null_sw_params, dig_params: snd_pcm_null_dig_params, diff --git a/src/pcm/pcm_params.c b/src/pcm/pcm_params.c new file mode 100644 index 00000000..b944ed9c --- /dev/null +++ b/src/pcm/pcm_params.c @@ -0,0 +1,1689 @@ +/* + * PCM - Params functions + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "pcm_local.h" +#include "interval_inline.h" +#include "interval.h" +#include "mask.h" + +static inline unsigned int add(unsigned int a, unsigned int b) +{ + if (a >= UINT_MAX - b) + return UINT_MAX; + return a + b; +} + +static inline unsigned int sub(unsigned int a, unsigned int b) +{ + if (a > b) + return a - b; + return 0; +} + +static inline int is_mask(int var) +{ + return var >= SND_PCM_HW_PARAM_FIRST_MASK && + var <= SND_PCM_HW_PARAM_LAST_MASK; +} + +static inline int is_interval(int var) +{ + return var >= SND_PCM_HW_PARAM_FIRST_INTERVAL && + var <= SND_PCM_HW_PARAM_LAST_INTERVAL; +} + +static inline mask_t *params_mask(snd_pcm_hw_params_t *params, + unsigned int var) +{ + assert(is_mask(var)); + return (mask_t*)¶ms->masks[var - SND_PCM_HW_PARAM_FIRST_MASK]; +} + +static inline interval_t *params_interval(snd_pcm_hw_params_t *params, + unsigned int var) +{ + assert(is_interval(var)); + return ¶ms->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL]; +} + +static inline const mask_t *params_mask_c(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + return (const mask_t *)params_mask((snd_pcm_hw_params_t*) params, var); +} + +static inline const interval_t *params_interval_c(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + return (const interval_t *)params_interval((snd_pcm_hw_params_t*) params, var); +} + +void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) +{ + unsigned int k; + memset(params, 0, sizeof(*params)); + for (k = SND_PCM_HW_PARAM_FIRST_MASK; k <= SND_PCM_HW_PARAM_LAST_MASK; k++) { + mask_all(params_mask(params, k)); + params->appl_cmask |= 1 << k; + } + + for (k = SND_PCM_HW_PARAM_FIRST_INTERVAL; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; k++) { + interval_all(params_interval(params, k)); + params->appl_cmask |= 1 << k; + } + interval_setreal(params_interval(params, SND_PCM_HW_PARAM_RATE)); + interval_setreal(params_interval(params, SND_PCM_HW_PARAM_FRAGMENT_LENGTH)); + interval_setreal(params_interval(params, SND_PCM_HW_PARAM_BUFFER_LENGTH)); + params->info = ~0U; + params->dig_groups = UINT_MAX; +} + +/* Fill PARAMS with full configuration space boundaries */ +int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + _snd_pcm_hw_params_any(params); + return snd_pcm_hw_refine(pcm, params); +} + +/* Return the value for field PAR if it's fixed in configuration space + defined by PARAMS. Return -EINVAL otherwise +*/ +int snd_pcm_hw_params_value(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + if (is_mask(var)) { + const mask_t *mask = params_mask_c(params, var); + if (!mask_single(mask)) + return -EINVAL; + return mask_value(mask); + } + if (is_interval(var)) { + const interval_t *i = params_interval_c(params, var); + if (!interval_single(i)) + return -EINVAL; + return interval_value(i); + } + assert(0); + return -EINVAL; +} + +/* Return the minimum value for field PAR. */ +unsigned int snd_pcm_hw_params_value_min(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + if (is_mask(var)) { + return mask_min(params_mask_c(params, var)); + } + if (is_interval(var)) { + return interval_min(params_interval_c(params, var)); + } + assert(0); + return -EINVAL; +} + +/* Return the maximum value for field PAR. */ +unsigned int snd_pcm_hw_params_value_max(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + if (is_mask(var)) { + return mask_max(params_mask_c(params, var)); + } + if (is_interval(var)) { + return interval_max(params_interval_c(params, var)); + } + assert(0); + return -EINVAL; +} + +/* Return the mask for field PAR. + This function can be called only for SND_PCM_HW_PARAM_ACCESS, + SND_PCM_HW_PARAM_FORMAT, SND_PCM_HW_PARAM_SUBFORMAT. */ +const mask_t *snd_pcm_hw_params_value_mask(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + assert(is_mask(var)); + return params_mask_c(params, var); +} + +/* Return the interval for field PAR. + This function cannot be called for SND_PCM_HW_PARAM_ACCESS, + SND_PCM_HW_PARAM_FORMAT, SND_PCM_HW_PARAM_SUBFORMAT. */ +const interval_t *snd_pcm_hw_params_value_interval(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + assert(is_interval(var)); + return params_interval_c(params, var); +} + + +/* --- Refinement functions --- */ + +int _snd_pcm_hw_params_first(snd_pcm_hw_params_t *params, int hw, + unsigned int var) +{ + int changed; + if (is_mask(var)) + changed = mask_refine_first(params_mask(params, var)); + else if (is_interval(var)) + changed = interval_refine_first(params_interval(params, var)); + else { + assert(0); + return -EINVAL; + } + if (changed) { + if (hw) + params->hw_cmask |= 1 << var; + else + params->appl_cmask |= 1 << var; + } + return changed; +} + + +/* Inside configuration space defined by PARAMS remove from PAR all + values > minimum. Reduce configuration space accordingly. + Return the minimum. +*/ +int snd_pcm_hw_params_first(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, unsigned int var) +{ + int changed = _snd_pcm_hw_params_first(params, 0, var); + if (changed < 0) + return changed; + if (changed) { + int err = snd_pcm_hw_refine(pcm, params); + assert(err >= 0); + } + return snd_pcm_hw_params_value(params, var); +} + +int _snd_pcm_hw_params_last(snd_pcm_hw_params_t *params, int hw, + unsigned int var) +{ + int changed; + if (is_mask(var)) + changed = mask_refine_last(params_mask(params, var)); + else if (is_interval(var)) + changed = interval_refine_last(params_interval(params, var)); + else { + assert(0); + return -EINVAL; + } + if (changed) { + if (hw) + params->hw_cmask |= 1 << var; + else + params->appl_cmask |= 1 << var; + } + return changed; +} + + +/* Inside configuration space defined by PARAMS remove from PAR all + values < maximum. Reduce configuration space accordingly. + Return the maximum. +*/ +int snd_pcm_hw_params_last(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params, unsigned int var) +{ + int changed = _snd_pcm_hw_params_last(params, 0, var); + if (changed < 0) + return changed; + if (changed) { + int err = snd_pcm_hw_refine(pcm, params); + assert(err >= 0); + } + return snd_pcm_hw_params_value(params, var); +} + +int _snd_pcm_hw_params_min(snd_pcm_hw_params_t *params, int hw, + unsigned int var, unsigned int val) +{ + int changed; + if (is_mask(var)) + changed = mask_refine_min(params_mask(params, var), val); + else if (is_interval(var)) + changed = interval_refine_min(params_interval(params, var), val); + else { + assert(0); + return -EINVAL; + } + if (changed) { + if (hw) + params->hw_cmask |= 1 << var; + else + params->appl_cmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values < VAL. Reduce configuration space accordingly. + Return new minimum or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_params_min(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int var, unsigned int val) +{ + int changed = _snd_pcm_hw_params_min(params, 0, var, val); + if (changed < 0) + return changed; + if (changed) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_params_value_min(params, var); +} + +int _snd_pcm_hw_params_max(snd_pcm_hw_params_t *params, int hw, + unsigned int var, unsigned int val) +{ + int changed; + if (is_mask(var)) + changed = mask_refine_max(params_mask(params, var), val); + else if (is_interval(var)) + changed = interval_refine_max(params_interval(params, var), val); + else { + assert(0); + return -EINVAL; + } + if (changed) { + if (hw) + params->hw_cmask |= 1 << var; + else + params->appl_cmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values >= VAL + 1. Reduce configuration space accordingly. + Return new maximum or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_params_max(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int var, unsigned int val) +{ + int changed = _snd_pcm_hw_params_max(params, 0, var, val); + if (changed < 0) + return changed; + if (changed) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_params_value_max(params, var); +} + +int _snd_pcm_hw_params_minmax(snd_pcm_hw_params_t *params, int hw, + unsigned int var, + unsigned int min, unsigned int max) +{ + int changed, c1, c2; + if (is_mask(var)) { + mask_t *mask = params_mask(params, var); + c1 = mask_refine_min(mask, min); + if (c1 < 0) + changed = c1; + else { + c2 = mask_refine_max(mask, max); + if (c2 < 0) + changed = c2; + else + changed = (c1 || c2); + } + } + else if (is_interval(var)) { + interval_t *i = params_interval(params, var); + c1 = interval_refine_min(i, min); + if (c1 < 0) + changed = c1; + else { + c2 = interval_refine_max(i, max); + if (c2 < 0) + changed = c2; + else + changed = (c1 || c2); + } + } else { + assert(0); + return -EINVAL; + } + if (changed) { + if (hw) + params->hw_cmask |= 1 << var; + else + params->appl_cmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values < MIN and all values > MAX. Reduce configuration space accordingly. + Return 0 or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_params_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int var, + unsigned int min, unsigned int max) +{ + int changed = _snd_pcm_hw_params_minmax(params, 0, var, min, max); + if (changed < 0) + return changed; + if (changed) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +int _snd_pcm_hw_params_set(snd_pcm_hw_params_t *params, int hw, + unsigned int var, unsigned int val) +{ + int changed; + if (is_mask(var)) + changed = mask_refine_set(params_mask(params, var), val); + else if (is_interval(var)) + changed = interval_refine_set(params_interval(params, var), val); + else { + assert(0); + return -EINVAL; + } + if (changed) { + if (hw) + params->hw_cmask |= 1 << var; + else + params->appl_cmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all + values < VAL and >= VAL +1. Reduce configuration space accordingly. + Return VAL or -EINVAL if the configuration space is empty +*/ +int snd_pcm_hw_params_set(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int var, unsigned int val) +{ + int changed = _snd_pcm_hw_params_set(params, 0, var, val); + if (changed < 0) + return changed; + if (changed) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return snd_pcm_hw_params_value(params, var); +} + +int _snd_pcm_hw_params_mask(snd_pcm_hw_params_t *params, int hw, + unsigned int var, const mask_t *val) +{ + int changed; + assert(is_mask(var)); + changed = mask_refine(params_mask(params, var), val); + if (changed) { + if (hw) + params->hw_cmask |= 1 << var; + else + params->appl_cmask |= 1 << var; + } + return changed; +} + +/* Inside configuration space defined by PARAMS remove from PAR all values + not contained in MASK. Reduce configuration space accordingly. + This function can be called only for SND_PCM_HW_PARAM_ACCESS, + SND_PCM_HW_PARAM_FORMAT, SND_PCM_HW_PARAM_SUBFORMAT. + Return 0 on success or -EINVAL + if the configuration space is empty +*/ +int snd_pcm_hw_params_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int var, const mask_t *val) +{ + int changed = _snd_pcm_hw_params_mask(params, 0, var, val); + if (changed < 0) + return changed; + if (changed) { + int err = snd_pcm_hw_refine(pcm, params); + if (err < 0) + return err; + } + return 0; +} + +/* Inside configuration space defined by PARAMS set PAR to the available value + nearest to VAL. Reduce configuration space accordingly. + This function cannot be called for SND_PCM_HW_PARAM_ACCESS, + SND_PCM_HW_PARAM_FORMAT, SND_PCM_HW_PARAM_SUBFORMAT. + Return the value found. + */ +int snd_pcm_hw_params_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int var, unsigned int val) +{ + snd_pcm_hw_params_t save; + int v; + unsigned int max1 = val, min2 = add(val, 1); + save = *params; + v = snd_pcm_hw_params_max(pcm, params, var, max1); + if (v >= 0) { + int v1; + snd_pcm_hw_params_t params1; + if (val == (unsigned int)v) + goto _end; + params1 = save; + v1 = snd_pcm_hw_params_min(pcm, ¶ms1, var, min2); + if (v1 < 0) + goto _end; + if (val - v > v1 - val) { + *params = params1; + v = v1; + } + } else { + *params = save; + v = snd_pcm_hw_params_min(pcm, params, var, min2); + assert(v >= 0); + } + _end: + v = snd_pcm_hw_params_set(pcm, params, var, v); + assert(v >= 0); + return v; +} + +/* Inside configuration space defined by PARAMS set PAR to the available value + nearest to VAL after OLD (values less than VAL are returned first). + Reduce configuration space accordingly. + This function cannot be called for SND_PCM_HW_PARAM_ACCESS, + SND_PCM_HW_PARAM_FORMAT, SND_PCM_HW_PARAM_SUBFORMAT. + Return the value found. + */ +int snd_pcm_hw_params_next(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + unsigned int var, unsigned int val, + unsigned int old) +{ + snd_pcm_hw_params_t save; + int v; + unsigned int max1, min2; + int diff = old - val; + if (diff < 0) { + max1 = sub(old, 1); + min2 = add(val, -diff); + } else { + max1 = sub(val, diff + 1); + min2 = add(old, 1); + } + save = *params; + v = snd_pcm_hw_params_max(pcm, params, var, max1); + if (v >= 0) { + int v1; + snd_pcm_hw_params_t params1; + if (val == (unsigned int)v) + goto _end; + params1 = save; + v1 = snd_pcm_hw_params_min(pcm, ¶ms1, var, min2); + if (v1 < 0) + goto _end; + if (val - v > v1 - val) { + *params = params1; + v = v1; + } + } else { + *params = save; + v = snd_pcm_hw_params_min(pcm, params, var, min2); + if (v < 0) + return v; + } + _end: + v = snd_pcm_hw_params_set(pcm, params, var, v); + return v; +} + + +/* ---- end of refinement functions ---- */ + +int snd_pcm_hw_params_empty(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + if (is_mask(var)) + return mask_empty(params_mask_c(params, var)); + if (is_interval(var)) + return interval_empty(params_interval_c(params, var)); + assert(0); + return -EINVAL; +} + +/* Return rate numerator/denumerator obtainable for configuration space defined + by PARAMS */ +int snd_pcm_hw_params_info_rate(const snd_pcm_hw_params_t *params, + unsigned int *rate_num, unsigned int *rate_den) +{ + if (params->rate_den == 0) + return -EINVAL; + *rate_num = params->rate_num; + *rate_den = params->rate_den; + return 0; +} + +/* Return significative bits in sample for configuration space defined + by PARAMS */ +int snd_pcm_hw_params_info_msbits(const snd_pcm_hw_params_t *params) +{ + if (params->msbits == 0) + return -EINVAL; + return params->msbits; +} + +/* Return info for configuration space defined by PARAMS */ +int snd_pcm_hw_params_info_flags(const snd_pcm_hw_params_t *params) +{ + if (params->info == ~0U) + return -EINVAL; + return params->info; +} + +/* Return fifo size for configuration space defined by PARAMS */ +int snd_pcm_hw_params_info_fifo_size(const snd_pcm_hw_params_t *params) +{ + if (params->fifo_size == 0) + return -EINVAL; + return params->fifo_size; +} + +/* Return count of digital groups for configuration space defined by PARAMS */ +int snd_pcm_hw_params_info_dig_groups(const snd_pcm_hw_params_t *params) +{ + if (params->dig_groups == UINT_MAX) + return -EINVAL; + return params->dig_groups; +} + +/* Choose one configuration from configuration space defined by PARAMS + The configuration choosen is that obtained fixing in this order: + first access + first format + first subformat + min channels + min rate + min fragment size + max fragments +*/ +void snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + int err; + unsigned int hw_cmask = 0; + + err = snd_pcm_hw_params_first(pcm, params, SND_PCM_HW_PARAM_ACCESS); + assert(err >= 0); + hw_cmask |= params->hw_cmask; + + err = snd_pcm_hw_params_first(pcm, params, SND_PCM_HW_PARAM_FORMAT); + assert(err >= 0); + hw_cmask |= params->hw_cmask; + + err = snd_pcm_hw_params_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT); + assert(err >= 0); + hw_cmask |= params->hw_cmask; + + err = snd_pcm_hw_params_first(pcm, params, SND_PCM_HW_PARAM_CHANNELS); + assert(err >= 0); + hw_cmask |= params->hw_cmask; + + err = snd_pcm_hw_params_first(pcm, params, SND_PCM_HW_PARAM_RATE); + assert(err >= 0); + hw_cmask |= params->hw_cmask; + + err = snd_pcm_hw_params_first(pcm, params, SND_PCM_HW_PARAM_FRAGMENT_SIZE); + assert(err >= 0); + hw_cmask |= params->hw_cmask; + + err = snd_pcm_hw_params_last(pcm, params, SND_PCM_HW_PARAM_FRAGMENTS); + assert(err >= 0); + hw_cmask |= params->hw_cmask; + + params->hw_cmask = hw_cmask; +} + +int snd_pcm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + int err; + assert(pcm && params); + params->hw_cmask = 0; + err = pcm->ops->hw_refine(pcm->op_arg, params); + params->appl_cmask = 0; + return err; +} + +/* Install one of the configurations present in configuration + space defined by PARAMS. + The configuration choosen is that obtained fixing in this order: + first access + first format + first subformat + min channels + min rate + min fragment_size + max fragments + Return 0 on success or a negative number expressing the error. +*/ +int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + int err; + params->hw_cmask = 0; + snd_pcm_hw_params_choose(pcm, params); + if (pcm->mmap_channels) { + err = snd_pcm_munmap(pcm); + if (err < 0) + return err; + } + err = pcm->ops->hw_params(pcm->op_arg, params); + if (err < 0) + goto _mmap; + + pcm->setup = 1; + pcm->access = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_ACCESS); + pcm->format = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT); + pcm->subformat = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_SUBFORMAT); + pcm->channels = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_CHANNELS); + pcm->rate = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_RATE); + pcm->fragment_size = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FRAGMENT_SIZE); + pcm->fragments = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FRAGMENTS); + pcm->bits_per_sample = snd_pcm_format_physical_width(pcm->format); + pcm->bits_per_frame = pcm->bits_per_sample * pcm->channels; + pcm->buffer_size = pcm->fragment_size * pcm->fragments; + + pcm->info = params->info; + pcm->msbits = params->msbits; + pcm->rate_num = params->rate_num; + pcm->rate_den = params->rate_den; + pcm->fifo_size = params->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; + + _mmap: + 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; + } + if (err >= 0) + snd_pcm_prepare(pcm); + return err; +} + +/* Strategies */ + +struct _snd_pcm_strategy { + unsigned int badness_min, badness_max; + int (*choose_param)(const snd_pcm_hw_params_t *params, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy); + int (*next_value)(snd_pcm_hw_params_t *params, + unsigned int param, + int value, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy); + int (*min_badness)(const snd_pcm_hw_params_t *params, + 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; + int (*next_value)(snd_pcm_hw_params_t *params, + unsigned int param, + int value, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par); + unsigned int (*min_badness)(const snd_pcm_hw_params_t *params, + 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 { + int 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; + +int snd_pcm_hw_params_test(const snd_pcm_hw_params_t *params, + unsigned int var, unsigned int val) +{ + if (is_mask(var)) { + const mask_t *mask = params_mask_c(params, var); + return mask_test(mask, val); + } + if (is_interval(var)) { + const interval_t *i = params_interval_c(params, var); + return interval_test(i, val); + } + assert(0); + return -EINVAL; +} + +unsigned int snd_pcm_hw_params_count(const snd_pcm_hw_params_t *params, + unsigned int var) +{ + if (is_mask(var)) { + const mask_t *mask = params_mask_c(params, var); + return mask_count(mask); + } + if (is_interval(var)) { + const interval_t *i = params_interval_c(params, var); + return interval_max(i) - interval_min(i) + 1; + } + assert(0); + return 0; +} + +void snd_pcm_hw_params_copy(snd_pcm_hw_params_t *params, unsigned int var, + const snd_pcm_hw_params_t *src) +{ + if (is_mask(var)) { + mask_t *d = params_mask(params, var); + const mask_t *s = params_mask_c(src, var); + mask_copy(d, s); + } + if (is_interval(var)) { + interval_t *d = params_interval(params, var); + const interval_t *s = params_interval_c(src, var); + interval_copy(d, s); + } + assert(0); +} + +void snd_pcm_hw_params_dump(const snd_pcm_hw_params_t *params, + unsigned int var, FILE *fp) +{ + if (is_mask(var)) { + const mask_t *mask = params_mask_c(params, var); + if (mask_empty(mask)) + fputs(" NONE", fp); + else { + unsigned int k; + for (k = 0; k <= MASK_MAX; ++k) { + if (mask_test(mask, k)) { + putc(' ', fp); + switch (var) { + case SND_PCM_HW_PARAM_ACCESS: + fputs(snd_pcm_access_name(k), fp); + break; + case SND_PCM_HW_PARAM_FORMAT: + fputs(snd_pcm_format_name(k), fp); + break; + case SND_PCM_HW_PARAM_SUBFORMAT: + fputs(snd_pcm_subformat_name(k), fp); + break; + default: + assert(0); + } + } + } + } + return; + } + if (is_interval(var)) { + interval_print(params_interval_c(params, var), fp); + return; + } + assert(0); +} + +int snd_pcm_dump_hw_params(snd_pcm_hw_params_t *params, FILE *fp) +{ + unsigned int k; + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; k++) { + fprintf(fp, "%s: ", snd_pcm_hw_param_name(k)); + snd_pcm_hw_params_dump(params, k, fp); + putc('\n', fp); + } + return 0; +} + +int snd_pcm_hw_params_strategy(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + const snd_pcm_strategy_t *strategy, + unsigned int badness_min, + unsigned int badness_max) +{ + snd_pcm_hw_params_t best_params; + int var; + int value; + unsigned int best_badness; + int badness = strategy->min_badness(params, badness_max, pcm, strategy); + snd_pcm_hw_params_t params1; +#if 0 + printf("\nBadness: %d\n", badness); + snd_pcm_dump_hw_params(params, stdout); +#endif + if (badness < 0) + return badness; + if ((unsigned int)badness > badness_min) + badness_min = badness_min; + var = strategy->choose_param(params, pcm, strategy); + if (var < 0) + return badness; + best_badness = UINT_MAX; + value = -1; + while (1) { + unsigned int hw_cmask; + params1 = *params; + value = strategy->next_value(¶ms1, var, value, pcm, strategy); + if (value < 0) + break; + hw_cmask = params1.hw_cmask; + badness = snd_pcm_hw_params_strategy(pcm, ¶ms1, strategy, badness_min, badness_max); + params1.hw_cmask |= hw_cmask; + if (badness >= 0) { + if ((unsigned int) badness <= badness_min) { + *params = params1; + return badness; + } + best_badness = badness; + best_params = params1; + badness_max = badness - 1; + } + } + if (best_badness == UINT_MAX) { + return -EINVAL; + } + *params = best_params; + return best_badness; +} + +void snd_pcm_strategy_simple_free(snd_pcm_strategy_t *strategy) +{ + snd_pcm_strategy_simple_t *pars = strategy->private; + int k; + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { + if (pars[k].valid && pars[k].free) + pars[k].free(&pars[k]); + } + free(pars); +} + +int snd_pcm_strategy_simple_choose_param(const snd_pcm_hw_params_t *params, + snd_pcm_t *pcm ATTRIBUTE_UNUSED, + const snd_pcm_strategy_t *strategy) +{ + unsigned int var; + int best_var = -1; + const snd_pcm_strategy_simple_t *pars = strategy->private; + unsigned int min_choices = UINT_MAX; + unsigned int min_order = UINT_MAX; + for (var = 0; var <= SND_PCM_HW_PARAM_LAST; ++var) { + const snd_pcm_strategy_simple_t *p = &pars[var]; + unsigned int choices; + if (!p->valid) + continue; + choices = snd_pcm_hw_params_count(params, var); + if (choices == 1) + continue; + assert(choices != 0); + if (p->order < min_order || + (p->order == min_order && + choices < min_choices)) { + min_order = p->order; + min_choices = choices; + best_var = var; + } + } + return best_var; +} + +int snd_pcm_strategy_simple_next_value(snd_pcm_hw_params_t *params, + unsigned int var, + int value, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy) +{ + const snd_pcm_strategy_simple_t *pars = strategy->private; + assert(pars[var].valid); + return pars[var].next_value(params, var, value, pcm, &pars[var]); +} + + +int snd_pcm_strategy_simple_min_badness(const snd_pcm_hw_params_t *params, + unsigned int max_badness, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy) +{ + unsigned int var; + unsigned int badness = 0; + const snd_pcm_strategy_simple_t *pars = strategy->private; + for (var = 0; var <= SND_PCM_HW_PARAM_LAST; ++var) { + unsigned int b; + if (!pars[var].valid) + continue; + b = pars[var].min_badness(params, var, pcm, &pars[var]); + if (b > max_badness || max_badness - b < badness) + return -E2BIG; + badness += b; + } + return badness; +} + + +void snd_pcm_strategy_simple_near_free(snd_pcm_strategy_simple_t *par) +{ + snd_pcm_strategy_simple_near_t *p = par->private; + free(p); +} + +unsigned int snd_pcm_strategy_simple_near_min_badness(const snd_pcm_hw_params_t *params, + unsigned int var, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par) +{ + const snd_pcm_strategy_simple_near_t *p = par->private; + snd_pcm_hw_params_t params1 = *params; + int value = snd_pcm_hw_params_near(pcm, ¶ms1, var, p->best); + int diff; + assert(value >= 0); + diff = p->best - value; + if (diff < 0) + diff = -diff; + return diff * p->mul; +} + +int snd_pcm_strategy_simple_near_next_value(snd_pcm_hw_params_t *params, + unsigned int var, + int value, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par) +{ + const snd_pcm_strategy_simple_near_t *p = par->private; + if (value < 0) + return snd_pcm_hw_params_near(pcm, params, var, p->best); + else + return snd_pcm_hw_params_next(pcm, params, var, p->best, value); +} + +void snd_pcm_strategy_simple_choices_free(snd_pcm_strategy_simple_t *par) +{ + snd_pcm_strategy_simple_choices_t *p = par->private; +// free(p->choices); + free(p); +} + +unsigned int snd_pcm_strategy_simple_choices_min_badness(const snd_pcm_hw_params_t *params, + unsigned int var, + snd_pcm_t *pcm ATTRIBUTE_UNUSED, + const snd_pcm_strategy_simple_t *par) +{ + const snd_pcm_strategy_simple_choices_t *p = par->private; + unsigned int k; + for (k = 0; k < p->count; ++k) { + if (snd_pcm_hw_params_test(params, var, p->choices[k].value)) + return p->choices[k].badness; + } + assert(0); + return UINT_MAX; +} + +int snd_pcm_strategy_simple_choices_next_value(snd_pcm_hw_params_t *params, + unsigned int var, + int value, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par) +{ + const snd_pcm_strategy_simple_choices_t *p = par->private; + unsigned int k = 0; + if (value >= 0) { + for (; k < p->count; ++k) { + if (p->choices[k].value == (unsigned int) value) { + k++; + break; + } + } + } + for (; k < p->count; ++k) { + unsigned int v = p->choices[k].value; + if (snd_pcm_hw_params_test(params, var, v)) { + snd_pcm_hw_params_t save = *params; + int err = snd_pcm_hw_params_set(pcm, params, var, v); + if (err < 0) { + *params = save; + continue; + } + return v; + } + } + return -1; +} + +int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy) +{ + if (strategy->free) + strategy->free(strategy); + free(strategy); + return 0; +} + +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; + assert(strategyp); + data = calloc(SND_PCM_HW_PARAM_LAST + 1, sizeof(*data)); + if (!data) + return -ENOMEM; + s = calloc(1, sizeof(*s)); + if (!s) { + free(data); + return -ENOMEM; + } + 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; + return 0; +} + +int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, + int order, + unsigned int var, + unsigned int best, + unsigned int mul) +{ + snd_pcm_strategy_simple_t *s = strategy->private; + snd_pcm_strategy_simple_near_t *data; + assert(strategy); + assert(var <= SND_PCM_HW_PARAM_LAST); + assert(!s->valid); + data = calloc(1, sizeof(*data)); + if (!data) + return -ENOMEM; + data->best = best; + data->mul = mul; + s += var; + 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; + s->private = data; + s->free = snd_pcm_strategy_simple_near_free; + return 0; +} + +int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, + int order, + unsigned int var, + unsigned int count, + snd_pcm_strategy_simple_choices_list_t *choices) +{ + snd_pcm_strategy_simple_t *s = strategy->private; + snd_pcm_strategy_simple_choices_t *data; + assert(strategy); + assert(var <= SND_PCM_HW_PARAM_LAST); + assert(!s->valid); + data = calloc(1, sizeof(*data)); + if (!data) + return -ENOMEM; + data->count = count; + data->choices = choices; + s += var; + 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; + s->free = snd_pcm_strategy_simple_choices_free; + return 0; +} + +int snd_pcm_hw_params_try_explain_failure1(snd_pcm_t *pcm, + snd_pcm_hw_params_t *fail, + snd_pcm_hw_params_t *success, + unsigned int depth, + FILE *fp) +{ + unsigned int var; + snd_pcm_hw_params_t i; + if (depth < 1) + return -ENOENT; + for (var = 0; var <= SND_PCM_HW_PARAM_LAST; var++) { + int err; + i = *success; + snd_pcm_hw_params_copy(&i, var, fail); + err = snd_pcm_hw_refine(pcm, &i); + if (err == 0 && + snd_pcm_hw_params_try_explain_failure1(pcm, fail, &i, depth - 1, fp) < 0) + continue; + fprintf(fp, "%s: ", snd_pcm_hw_param_name(var)); + snd_pcm_hw_params_dump(fail, var, fp); + putc('\n', fp); + return 0; + } + return -ENOENT; +} + +int snd_pcm_hw_params_try_explain_failure(snd_pcm_t *pcm, + snd_pcm_hw_params_t *fail, + snd_pcm_hw_params_t *success, + unsigned int depth, + FILE *fp) +{ + snd_pcm_hw_params_t i, any; + int err; + unsigned int var; + int done = 0; + assert(pcm && fail); + for (var = 0; var <= SND_PCM_HW_PARAM_LAST; var++) { + if (!snd_pcm_hw_params_empty(fail, var)) + continue; + fprintf(fp, "%s is empty\n", snd_pcm_hw_param_name(var)); + done = 1; + } + if (done) + return 0; + i = *fail; + err = snd_pcm_hw_refine(pcm, &i); + if (err == 0) { + fprintf(fp, "Configuration is virtually correct\n"); + return 0; + } + if (!success) { + snd_pcm_hw_params_any(pcm, &any); + success = &any; + } + return snd_pcm_hw_params_try_explain_failure1(pcm, fail, success, depth, fp); +} + +typedef struct _snd_pcm_hw_rule snd_pcm_hw_rule_t; + +typedef int (*snd_pcm_hw_rule_func_t)(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule); + +struct _snd_pcm_hw_rule { + int var; + snd_pcm_hw_rule_func_t func; + int deps[4]; + void *private; +}; + +int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + return interval_mul(params_interval(params, rule->var), + params_interval(params, rule->deps[0]), + params_interval(params, rule->deps[1])); +} + +int snd_pcm_hw_rule_div(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + return interval_div(params_interval(params, rule->var), + params_interval(params, rule->deps[0]), + params_interval(params, rule->deps[1])); +} + +int snd_pcm_hw_rule_muldivk(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + return interval_muldivk(params_interval(params, rule->var), + (unsigned long) rule->private, + params_interval(params, rule->deps[0]), + params_interval(params, rule->deps[1])); +} + +int snd_pcm_hw_rule_mulkdiv(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + return interval_mulkdiv(params_interval(params, rule->var), + (unsigned long) rule->private, + params_interval(params, rule->deps[0]), + params_interval(params, rule->deps[1])); +} + +int snd_pcm_hw_rule_format(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + int changed = 0; + unsigned int k; + mask_t *mask = params_mask(params, rule->var); + interval_t *i = params_interval(params, rule->deps[0]); + for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) { + int bits; + if (!mask_test(mask, k)) + continue; + bits = snd_pcm_format_physical_width(k); + if (bits < 0) + continue; + if (!interval_test(i, bits)) { + mask_reset(mask, k); + if (mask_empty(mask)) + return -EINVAL; + changed = 1; + } + } + return changed; +} + + +int snd_pcm_hw_rule_sample_bits(snd_pcm_hw_params_t *params, + snd_pcm_hw_rule_t *rule) +{ + unsigned int min, max; + unsigned int k; + interval_t *i = params_interval(params, rule->var); + mask_t *mask = params_mask(params, rule->deps[0]); + int c, changed = 0; + min = UINT_MAX; + max = 0; + for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) { + int bits; + if (!mask_test(mask, k)) + continue; + bits = snd_pcm_format_physical_width(k); + if (bits < 0) + continue; + if (min > (unsigned)bits) + min = bits; + if (max < (unsigned)bits) + max = bits; + } + c = interval_refine_min(i, min); + if (c < 0) + return c; + if (c) + changed = 1; + c = interval_refine_max(i, max); + if (c < 0) + return c; + if (c) + changed = 1; + return changed; +} + +snd_pcm_hw_rule_t snd_pcm_hw_rules[] = { + { + var: SND_PCM_HW_PARAM_FORMAT, + func: snd_pcm_hw_rule_format, + deps: { SND_PCM_HW_PARAM_SAMPLE_BITS, -1 }, + private: 0, + }, + { + var: SND_PCM_HW_PARAM_SAMPLE_BITS, + func: snd_pcm_hw_rule_sample_bits, + deps: { SND_PCM_HW_PARAM_FORMAT, -1 }, + private: 0, + }, + { + var: SND_PCM_HW_PARAM_SAMPLE_BITS, + func: snd_pcm_hw_rule_div, + deps: { SND_PCM_HW_PARAM_FRAME_BITS, + SND_PCM_HW_PARAM_CHANNELS, -1 }, + private: 0, + }, + { + var: SND_PCM_HW_PARAM_FRAME_BITS, + func: snd_pcm_hw_rule_mul, + deps: { SND_PCM_HW_PARAM_SAMPLE_BITS, + SND_PCM_HW_PARAM_CHANNELS, -1 }, + private: 0, + }, + { + var: SND_PCM_HW_PARAM_FRAME_BITS, + func: snd_pcm_hw_rule_mulkdiv, + deps: { SND_PCM_HW_PARAM_FRAGMENT_BYTES, + SND_PCM_HW_PARAM_FRAGMENT_SIZE, -1 }, + private: (void*) 8, + }, + { + var: SND_PCM_HW_PARAM_FRAME_BITS, + func: snd_pcm_hw_rule_mulkdiv, + deps: { SND_PCM_HW_PARAM_BUFFER_BYTES, + SND_PCM_HW_PARAM_BUFFER_SIZE, -1 }, + private: (void*) 8, + }, + { + var: SND_PCM_HW_PARAM_CHANNELS, + func: snd_pcm_hw_rule_div, + deps: { SND_PCM_HW_PARAM_FRAME_BITS, + SND_PCM_HW_PARAM_SAMPLE_BITS, -1 }, + private: 0, + }, + { + var: SND_PCM_HW_PARAM_RATE, + func: snd_pcm_hw_rule_mulkdiv, + deps: { SND_PCM_HW_PARAM_FRAGMENT_SIZE, + SND_PCM_HW_PARAM_FRAGMENT_LENGTH, -1 }, + private: (void*) 1000000, + }, + { + var: SND_PCM_HW_PARAM_RATE, + func: snd_pcm_hw_rule_mulkdiv, + deps: { SND_PCM_HW_PARAM_BUFFER_SIZE, + SND_PCM_HW_PARAM_BUFFER_LENGTH, -1 }, + private: (void*) 1000000, + }, + { + var: SND_PCM_HW_PARAM_FRAGMENTS, + func: snd_pcm_hw_rule_div, + deps: { SND_PCM_HW_PARAM_BUFFER_SIZE, + SND_PCM_HW_PARAM_FRAGMENT_SIZE, -1 }, + private: 0, + }, + { + var: SND_PCM_HW_PARAM_FRAGMENT_SIZE, + func: snd_pcm_hw_rule_div, + deps: { SND_PCM_HW_PARAM_BUFFER_SIZE, + SND_PCM_HW_PARAM_FRAGMENTS, -1 }, + private: 0, + }, + { + var: SND_PCM_HW_PARAM_FRAGMENT_SIZE, + func: snd_pcm_hw_rule_mulkdiv, + deps: { SND_PCM_HW_PARAM_FRAGMENT_BYTES, + SND_PCM_HW_PARAM_FRAME_BITS, -1 }, + private: (void*) 8, + }, + { + var: SND_PCM_HW_PARAM_FRAGMENT_SIZE, + func: snd_pcm_hw_rule_muldivk, + deps: { SND_PCM_HW_PARAM_FRAGMENT_LENGTH, + SND_PCM_HW_PARAM_RATE, -1 }, + private: (void*) 1000000, + }, + { + var: SND_PCM_HW_PARAM_BUFFER_SIZE, + func: snd_pcm_hw_rule_mul, + deps: { SND_PCM_HW_PARAM_FRAGMENT_SIZE, + SND_PCM_HW_PARAM_FRAGMENTS, -1 }, + private: 0, + }, + { + var: SND_PCM_HW_PARAM_BUFFER_SIZE, + func: snd_pcm_hw_rule_mulkdiv, + deps: { SND_PCM_HW_PARAM_BUFFER_BYTES, + SND_PCM_HW_PARAM_FRAME_BITS, -1 }, + private: (void*) 8, + }, + { + var: SND_PCM_HW_PARAM_BUFFER_SIZE, + func: snd_pcm_hw_rule_muldivk, + deps: { SND_PCM_HW_PARAM_BUFFER_LENGTH, + SND_PCM_HW_PARAM_RATE, -1 }, + private: (void*) 1000000, + }, + { + var: SND_PCM_HW_PARAM_FRAGMENT_BYTES, + func: snd_pcm_hw_rule_muldivk, + deps: { SND_PCM_HW_PARAM_FRAGMENT_SIZE, + SND_PCM_HW_PARAM_FRAME_BITS, -1 }, + private: (void*) 8, + }, + { + var: SND_PCM_HW_PARAM_BUFFER_BYTES, + func: snd_pcm_hw_rule_muldivk, + deps: { SND_PCM_HW_PARAM_BUFFER_SIZE, + SND_PCM_HW_PARAM_FRAME_BITS, -1 }, + private: (void*) 8, + }, + { + var: SND_PCM_HW_PARAM_FRAGMENT_LENGTH, + func: snd_pcm_hw_rule_mulkdiv, + deps: { SND_PCM_HW_PARAM_FRAGMENT_SIZE, + SND_PCM_HW_PARAM_RATE, -1 }, + private: (void*) 1000000, + }, + { + var: SND_PCM_HW_PARAM_BUFFER_LENGTH, + func: snd_pcm_hw_rule_mulkdiv, + deps: { SND_PCM_HW_PARAM_BUFFER_SIZE, + SND_PCM_HW_PARAM_RATE, -1 }, + private: (void*) 1000000, + }, +}; + +#define RULES (sizeof(snd_pcm_hw_rules) / sizeof(snd_pcm_hw_rules[0])) + +int _snd_pcm_hw_refine(snd_pcm_hw_params_t *params) +{ + unsigned int k; + interval_t *i; + snd_pcm_hw_rule_t *rules = snd_pcm_hw_rules; + unsigned int rstamps[RULES]; + unsigned int vstamps[SND_PCM_HW_PARAM_LAST + 1]; + unsigned int stamp = 2; + int err, changed; + + for (k = 0; k < RULES; k++) + rstamps[k] = 0; + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; k++) + vstamps[k] = (params->appl_cmask & (1 << k)) ? 1 : 0; + params->appl_cmask = 0; + params->hw_cmask = 0; + changed = 1; + while (changed) { + changed = 0; + for (k = 0; k < RULES; k++) { + snd_pcm_hw_rule_t *r = &rules[k]; + unsigned int d; + int doit = 0; +#ifdef RULES_DEBUG + interval_t *i; +#endif + for (d = 0; r->deps[d] >= 0; d++) { + if (vstamps[r->deps[d]] > rstamps[k]) { + doit = 1; + break; + } + } + if (!doit) + continue; +#ifdef RULES_DEBUG + i = params_interval(params, r->var); + fprintf(stderr, "Rule %d: %u ", k, r->var); + interval_print(i, stderr); +#endif + err = r->func(params, r); +#ifdef RULES_DEBUG + interval_print(i, stderr); + putc('\n', stderr); +#endif + rstamps[k] = stamp; + if (err && r->var >= 0) { + params->hw_cmask |= 1 << r->var; + vstamps[r->var] = stamp; + changed = 1; + } + if (err < 0) + return err; + stamp++; + } + } + if (!params->msbits) { + i = params_interval(params, SND_PCM_HW_PARAM_SAMPLE_BITS); + if (interval_single(i)) + params->msbits = interval_value(i); + } + + if (!params->rate_den) { + i = params_interval(params, SND_PCM_HW_PARAM_RATE); + if (interval_single(i)) { + params->rate_num = interval_value(i); + params->rate_den = 1; + } + } + return 0; +} + +int snd_pcm_hw_params_refine(snd_pcm_hw_params_t *params, unsigned int var, + const snd_pcm_hw_params_t *src) +{ + if (is_mask(var)) { + mask_t *d = params_mask(params, var); + const mask_t *s = params_mask_c(src, var); + return mask_refine(d, s); + } + if (is_interval(var)) { + interval_t *d = params_interval(params, var); + const interval_t *s = params_interval_c(src, var); + return interval_refine(d, s); + } + assert(0); + return -EINVAL; +} + +int snd_pcm_hw_refine2(snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *sparams, + int (*func)(snd_pcm_t *slave, + snd_pcm_hw_params_t *sparams), + snd_pcm_t *slave, + unsigned int links) +{ + unsigned int k; + int err, error = 0; + unsigned int client_from_slave = ~0U; + unsigned int client_refine = ~0U; + unsigned int slave_from_client = ~0U; + unsigned int slave_refine = ~0U; + unsigned int hw_cmask = params->hw_cmask; + + while (client_from_slave || client_refine) { + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { + int changed; + if (!(links & (1 << k))) + continue; + if (!(client_from_slave & (1 << k))) + continue; + changed = snd_pcm_hw_params_refine(params, k, sparams); + if (changed) { + hw_cmask |= 1 << k; + slave_from_client |= 1 << k; + client_refine |= 1 << k; + } + if (changed < 0) + error = changed; + } + client_from_slave = 0; + if (error) + break; + if (client_refine) { + params->appl_cmask = client_refine; + client_refine = 0; + err = _snd_pcm_hw_refine(params); + hw_cmask |= params->hw_cmask; + slave_from_client |= params->hw_cmask; + if (err < 0) { + error = err; + break; + } + } + + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { + int changed; + if (!(links & (1 << k))) + continue; + if (!(slave_from_client & (1 << k))) + continue; + changed = snd_pcm_hw_params_refine(sparams, k, params); + if (changed) { + slave_refine |= 1 << k; + client_from_slave |= 1 << k; + } + if (changed < 0) + error = changed; + } + slave_from_client = 0; + if (error) + continue; + if (slave_refine) { + sparams->appl_cmask = slave_refine; + slave_refine = 0; + error = func(slave, sparams); + client_from_slave |= sparams->hw_cmask; + } + } + params->appl_cmask = 0; + params->hw_cmask = hw_cmask; + return error; +} + +int snd_pcm_hw_params2(snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *sparams, + int (*func)(snd_pcm_t *slave, + snd_pcm_hw_params_t *sparams), + snd_pcm_t *slave, + unsigned int links) +{ + unsigned int k; + int err; + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { + int changed; + if (!(links & (1 << k))) + continue; + changed = snd_pcm_hw_params_refine(sparams, k, params); + if (changed < 0) + return changed; + } + sparams->appl_cmask = ~0U; + err = func(slave, sparams); + if (err >= 0) + return err; + + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { + int changed; + if (!(links & (1 << k))) + continue; + if (!(sparams->hw_cmask & (1 << k))) + continue; + changed = snd_pcm_hw_params_refine(params, k, sparams); + if (changed) + params->hw_cmask |= 1 << k; + + } + return err; +} + diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index 9c6c1426..1a833f5b 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -118,12 +118,14 @@ static unsigned int nonlinear_preferred_formats[] = { SND_PCM_FORMAT_IMA_ADPCM, }; -static int snd_pcm_plug_slave_format(int format, unsigned int format_mask) +static int snd_pcm_plug_slave_format(int format, const mask_t *format_mask) { int w, u, e, wid, w1, dw; - if (format_mask & (1 << format)) + mask_t *lin = alloca(mask_sizeof()); + if (mask_test(format_mask, format)) return format; - if (!((1 << format) & SND_PCM_FMTBIT_LINEAR)) { + mask_load(lin, SND_PCM_FMTBIT_LINEAR); + if (!mask_test(lin, format)) { unsigned int i; switch (format) { case SND_PCM_FORMAT_MU_LAW: @@ -131,7 +133,7 @@ static int snd_pcm_plug_slave_format(int format, unsigned int format_mask) case SND_PCM_FORMAT_IMA_ADPCM: for (i = 0; i < sizeof(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) { unsigned int f = linear_preferred_formats[i]; - if (format_mask & (1 << f)) + if (mask_test(format_mask, f)) return f; } /* Fall through */ @@ -140,11 +142,12 @@ static int snd_pcm_plug_slave_format(int format, unsigned int format_mask) } } - if (!(format_mask & SND_PCM_FMTBIT_LINEAR)) { + mask_intersect(lin, format_mask); + if (mask_empty(lin)) { unsigned int i; for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) { unsigned int f = nonlinear_preferred_formats[i]; - if (format_mask & (1 << f)) + if (mask_test(format_mask, f)) return f; } return -EINVAL; @@ -161,7 +164,7 @@ static int snd_pcm_plug_slave_format(int format, unsigned int format_mask) for (sgn = 0; sgn < 2; ++sgn) { int f; f = snd_pcm_build_linear_format(w1, u1, e1); - if (f >= 0 && format_mask & (1 << f)) + if (f >= 0 && mask_test(format_mask, f)) return f; u1 = !u1; } @@ -177,125 +180,181 @@ static int snd_pcm_plug_slave_format(int format, unsigned int format_mask) return -EINVAL; } -static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) +#define SND_PCM_FMTBIT_PLUG (SND_PCM_FMTBIT_LINEAR | \ + (1 << SND_PCM_FORMAT_MU_LAW) | \ + (1 << SND_PCM_FORMAT_A_LAW) | \ + (1 << SND_PCM_FORMAT_IMA_ADPCM)) + +static int snd_pcm_plug_hw_refine1(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *sparams) { snd_pcm_plug_t *plug = pcm->private; snd_pcm_t *slave = plug->req_slave; - snd_pcm_hw_info_t sinfo; - int rate_min, rate_max; - int channels_min, channels_max; - unsigned int format, format_mask; int err; + const mask_t *format_mask, *sformat_mask; + unsigned int rate_min, rate_max, srate_min, srate_max; + unsigned int channels_min, channels_max, schannels_min, schannels_max; + unsigned int format; + int same_rate, same_channels, same_format; + snd_pcm_hw_params_t tmp; + unsigned int links = (SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); + mask_t *tmp_mask = alloca(mask_sizeof()); + mask_t *accplug_mask = alloca(mask_sizeof()); + mask_t *mmap_mask = alloca(mask_sizeof()); + mask_t *fmtplug_mask = alloca(mask_sizeof()); + mask_load(accplug_mask, SND_PCM_ACCBIT_PLUGIN); + mask_load(mmap_mask, SND_PCM_ACCBIT_MMAP); + mask_load(fmtplug_mask, SND_PCM_FMTBIT_PLUG); - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - if (info->access_mask == 0) - return -EINVAL; - - info->format_mask &= (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW | - SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM); - if (info->format_mask == 0) - return -EINVAL; - - 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 < info->channels_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.channels_min = 1; - sinfo.channels_max = UINT_MAX; - sinfo.rate_min = RATE_MIN; - sinfo.rate_max = RATE_MAX; + err = _snd_pcm_hw_params_min(params, 1, SND_PCM_HW_PARAM_CHANNELS, 1); + if (err < 0) + return err; + err = _snd_pcm_hw_params_min(params, 1, SND_PCM_HW_PARAM_RATE, RATE_MIN); + if (err < 0) + return err; + err = _snd_pcm_hw_params_max(params, 1, SND_PCM_HW_PARAM_RATE, RATE_MAX); + if (err < 0) + return err; - err = snd_pcm_hw_info(slave, &sinfo); + _snd_pcm_hw_params_any(sparams); + err = snd_pcm_hw_refine2(params, sparams, + snd_pcm_hw_refine, slave, links); if (err < 0) - goto _err; + return err; - rate_min = snd_pcm_hw_info_par_nearest_next(&sinfo, - SND_PCM_HW_INFO_RATE, - info->rate_min, -1, - slave); - assert(rate_min >= 0); - if ((int)info->rate_max - rate_min > 1) { - rate_max = snd_pcm_hw_info_par_nearest_next(&sinfo, - SND_PCM_HW_INFO_RATE, - info->rate_max, -1, - slave); - assert(rate_max >= 0); - } else - rate_max = rate_min; - sinfo.rate_min = rate_min; - sinfo.rate_max = rate_max; - - err = snd_pcm_hw_info(slave, &sinfo); + rate_min = snd_pcm_hw_params_value_min(params, SND_PCM_HW_PARAM_RATE); + rate_max = snd_pcm_hw_params_value_max(params, SND_PCM_HW_PARAM_RATE); + tmp = *sparams; + srate_min = snd_pcm_hw_params_near(slave, &tmp, + SND_PCM_HW_PARAM_RATE, rate_min); + if (srate_min < rate_max) { + tmp = *sparams; + srate_max = snd_pcm_hw_params_near(slave, &tmp, + SND_PCM_HW_PARAM_RATE, rate_max); + } else + srate_max = srate_min; + err = snd_pcm_hw_params_minmax(slave, sparams, + SND_PCM_HW_PARAM_RATE, + srate_min, srate_max); assert(err >= 0); - channels_min = snd_pcm_hw_info_par_nearest_next(&sinfo, - SND_PCM_HW_INFO_CHANNELS, - info->channels_min, -1, - slave); - assert(channels_min >= 0); - if ((int)info->channels_max - channels_min > 1) { - channels_max = snd_pcm_hw_info_par_nearest_next(&sinfo, - SND_PCM_HW_INFO_CHANNELS, - info->channels_max, -1, - slave); - assert(channels_max >= 0); - } else - channels_max = channels_min; - sinfo.channels_min = channels_min; - sinfo.channels_max = channels_max; - - err = snd_pcm_hw_info(slave, &sinfo); + channels_min = snd_pcm_hw_params_value_min(params, SND_PCM_HW_PARAM_CHANNELS); + channels_max = snd_pcm_hw_params_value_max(params, SND_PCM_HW_PARAM_CHANNELS); + tmp = *sparams; + schannels_min = snd_pcm_hw_params_near(slave, &tmp, + SND_PCM_HW_PARAM_CHANNELS, channels_min); + if (schannels_min < channels_max) { + tmp = *sparams; + schannels_max = snd_pcm_hw_params_near(slave, &tmp, + SND_PCM_HW_PARAM_CHANNELS, channels_max); + } else + schannels_max = schannels_min; + err = snd_pcm_hw_params_minmax(slave, sparams, + SND_PCM_HW_PARAM_CHANNELS, + schannels_min, schannels_max); assert(err >= 0); - format_mask = 0; + format_mask = snd_pcm_hw_params_value_mask(params, + SND_PCM_HW_PARAM_FORMAT); + sformat_mask = snd_pcm_hw_params_value_mask(sparams, + SND_PCM_HW_PARAM_FORMAT); + mask_none(tmp_mask); for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) { int f; - if (!(info->format_mask & (1 << format))) + if (!mask_test(format_mask, format)) continue; - f = snd_pcm_plug_slave_format(format, sinfo.format_mask); - assert(f >= 0); - format_mask |= (1 << f); + if (mask_test(sformat_mask, format)) + f = format; + else { + f = snd_pcm_plug_slave_format(format, sformat_mask); + if (f < 0) { + mask_t *m = alloca(mask_sizeof()); + mask_all(m); + mask_reset(m, format); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_FORMAT, m); + if (err < 0) + return err; + continue; + } + } + mask_set(tmp_mask, f); } - sinfo.format_mask = format_mask; - err = snd_pcm_hw_info(slave, &sinfo); + err = _snd_pcm_hw_params_mask(sparams, 0, + SND_PCM_HW_PARAM_FORMAT, tmp_mask); assert(err >= 0); - - _err: - info->subformat_mask = sinfo.subformat_mask; - info->fragment_length_min = sinfo.fragment_length_min; - info->fragment_length_max = sinfo.fragment_length_max; - info->fragments_min = sinfo.fragments_min; - info->fragments_max = sinfo.fragments_max; - info->buffer_length_min = sinfo.buffer_length_min; - info->buffer_length_max = sinfo.buffer_length_max; - + sformat_mask = snd_pcm_hw_params_value_mask(sparams, + SND_PCM_HW_PARAM_FORMAT); + + same_rate = (rate_min == rate_max && + rate_min == srate_min && + rate_min == srate_max); + same_channels = (channels_min == channels_max && + channels_min == schannels_min && + channels_min == schannels_max); + same_format = (mask_single(format_mask) && + mask_eq(format_mask, sformat_mask)); + if (same_rate) + links |= (SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_BUFFER_SIZE); + if (same_channels) { + links |= SND_PCM_HW_PARBIT_CHANNELS; + if (same_format) + links |= (SND_PCM_HW_PARBIT_FRAME_BITS | + SND_PCM_HW_PARBIT_FRAGMENT_BYTES | + SND_PCM_HW_PARBIT_BUFFER_BYTES); + } + if (same_format) + links |= (SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_SAMPLE_BITS); + + if (same_rate && same_channels && same_format) { + const mask_t *access_mask = snd_pcm_hw_params_value_mask(params, SND_PCM_HW_PARAM_ACCESS); + mask_copy(tmp_mask, snd_pcm_hw_params_value_mask(sparams, SND_PCM_HW_PARAM_ACCESS)); + mask_intersect(tmp_mask, access_mask); + if (!mask_empty(tmp_mask)) + _snd_pcm_hw_params_mask(sparams, 0, + SND_PCM_HW_PARAM_ACCESS, + access_mask); + else + _snd_pcm_hw_params_mask(sparams, 0, SND_PCM_HW_PARAM_ACCESS, + mmap_mask); + } else { + err = _snd_pcm_hw_params_mask(params, 1, + SND_PCM_HW_PARAM_ACCESS, + accplug_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_params_mask(params, 1, + SND_PCM_HW_PARAM_FORMAT, + fmtplug_mask); + if (err < 0) + return err; + _snd_pcm_hw_params_mask(sparams, 0, SND_PCM_HW_PARAM_ACCESS, + mmap_mask); + _snd_pcm_hw_params_mask(sparams, 0, SND_PCM_HW_PARAM_FORMAT, + fmtplug_mask); + } + err = snd_pcm_hw_refine2(params, sparams, + snd_pcm_hw_refine, slave, links); if (err < 0) return err; - info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - snd_pcm_hw_info_complete(info); + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } + + +static int snd_pcm_plug_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + snd_pcm_hw_params_t sparams; + return snd_pcm_plug_hw_refine1(pcm, params, &sparams); +} + static void snd_pcm_plug_clear(snd_pcm_t *pcm) { snd_pcm_plug_t *plug = pcm->private; @@ -309,7 +368,14 @@ static void snd_pcm_plug_clear(snd_pcm_t *pcm) } } -static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv) +typedef struct { + unsigned int access; + unsigned int format; + unsigned int channels; + unsigned int rate; +} snd_pcm_plug_params_t; + +static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv) { snd_pcm_plug_t *plug = pcm->private; int err; @@ -326,7 +392,7 @@ static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_ return 1; } -static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv) +static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv) { snd_pcm_plug_t *plug = pcm->private; unsigned int tt_ssize, tt_cused, tt_sused; @@ -391,7 +457,7 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm return 1; } -static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv) +static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv) { snd_pcm_plug_t *plug = pcm->private; int err, cfmt; @@ -451,7 +517,7 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_h return 1; } -static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv) +static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *clt, snd_pcm_plug_params_t *slv) { snd_pcm_plug_t *plug = pcm->private; int err; @@ -465,11 +531,11 @@ static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_h } static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, - snd_pcm_hw_params_t *client, - snd_pcm_hw_params_t *slave) + snd_pcm_plug_params_t *client, + snd_pcm_plug_params_t *slave) { snd_pcm_plug_t *plug = pcm->private; - int (*funcs[])(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *s, snd_pcm_hw_params_t *d) = { + int (*funcs[])(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_plug_params_t *s, snd_pcm_plug_params_t *d) = { snd_pcm_plug_change_format, snd_pcm_plug_change_channels, snd_pcm_plug_change_rate, @@ -477,7 +543,7 @@ static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, snd_pcm_plug_change_format, snd_pcm_plug_change_access }; - snd_pcm_hw_params_t p = *slave; + snd_pcm_plug_params_t p = *slave; unsigned int k = 0; while (client->format != p.format || client->channels != p.channels || @@ -505,70 +571,25 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_plug_t *plug = pcm->private; snd_pcm_t *slave = plug->req_slave; - snd_pcm_hw_info_t sinfo; + snd_pcm_plug_params_t clt_params, slv_params; snd_pcm_hw_params_t sparams; - int format; - int err; - - sparams = *params; - snd_pcm_hw_params_to_info(&sparams, &sinfo); - sinfo.access_mask = ~0U; - 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 = UINT_MAX; - sinfo.rate_min = RATE_MIN; - sinfo.rate_max = RATE_MAX; - - err = snd_pcm_hw_info(slave, &sinfo); - if (err < 0) - return err; - - err = snd_pcm_hw_info_par_nearest_next(&sinfo, - SND_PCM_HW_INFO_RATE, - params->rate, -1, - slave); - if (err < 0) - return err; - sinfo.rate_min = sinfo.rate_max = err; - - err = snd_pcm_hw_info(slave, &sinfo); - assert(err >= 0); - - err = snd_pcm_hw_info_par_nearest_next(&sinfo, - SND_PCM_HW_INFO_CHANNELS, - params->channels, -1, - slave); - if (err < 0) - return err; - sinfo.channels_min = sinfo.channels_max = err; - - err = snd_pcm_hw_info(slave, &sinfo); + int err = snd_pcm_plug_hw_refine1(pcm, params, &sparams); assert(err >= 0); - format = snd_pcm_plug_slave_format(params->format, sinfo.format_mask); - assert(format >= 0); + clt_params.access = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_ACCESS); + clt_params.format = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT); + clt_params.channels = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_CHANNELS); + clt_params.rate = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_RATE); - sparams.format = format; - assert(sinfo.rate_min == sinfo.rate_max); - sparams.rate = sinfo.rate_min; - assert(sinfo.channels_min == sinfo.channels_max); - sparams.channels = sinfo.channels_min; + slv_params.access = snd_pcm_hw_params_first(slave, &sparams, SND_PCM_HW_PARAM_ACCESS); + slv_params.format = snd_pcm_hw_params_value(&sparams, SND_PCM_HW_PARAM_FORMAT); + slv_params.channels = snd_pcm_hw_params_value(&sparams, SND_PCM_HW_PARAM_CHANNELS); + slv_params.rate = snd_pcm_hw_params_value(&sparams, SND_PCM_HW_PARAM_RATE); snd_pcm_plug_clear(pcm); - if (params->format != sparams.format || - params->channels != sparams.channels || - params->rate != sparams.rate || - !(sinfo.access_mask & (1 << params->access))) { - sinfo.access_mask &= SND_PCM_ACCBIT_MMAP; - assert(sinfo.access_mask); - sparams.access = ffs(sinfo.access_mask) - 1; - err = snd_pcm_plug_insert_plugins(pcm, params, &sparams); - if (err < 0) - return err; - } - + err = snd_pcm_plug_insert_plugins(pcm, &clt_params, &slv_params); + if (err < 0) + return err; err = snd_pcm_hw_params(plug->slave, params); if (err < 0) { snd_pcm_plug_clear(pcm); @@ -638,7 +659,7 @@ static void snd_pcm_plug_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_plug_ops = { close: snd_pcm_plug_close, info: snd_pcm_plug_info, - hw_info: snd_pcm_plug_hw_info, + hw_refine: snd_pcm_plug_hw_refine, hw_params: snd_pcm_plug_hw_params, sw_params: snd_pcm_plug_sw_params, dig_info: snd_pcm_plug_dig_info, diff --git a/src/pcm/pcm_plugin.h b/src/pcm/pcm_plugin.h index e5029f2a..1cab1044 100644 --- a/src/pcm/pcm_plugin.h +++ b/src/pcm/pcm_plugin.h @@ -64,13 +64,14 @@ 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 | \ - 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 | \ - SND_PCM_FMTBIT_U24_LE|SND_PCM_FMTBIT_U24_BE | \ - SND_PCM_FMTBIT_S32_LE|SND_PCM_FMTBIT_S32_BE | \ - SND_PCM_FMTBIT_U32_LE|SND_PCM_FMTBIT_U32_BE) +#define SND_PCM_FMTBIT_LINEAR \ + ((1 << SND_PCM_FORMAT_S8 ) | (1 << SND_PCM_FORMAT_U8) | \ + (1 << SND_PCM_FORMAT_S16_LE) | (1 << SND_PCM_FORMAT_S16_BE) | \ + (1 << SND_PCM_FORMAT_U16_LE) | (1 << SND_PCM_FORMAT_U16_BE) | \ + (1 << SND_PCM_FORMAT_S24_LE) | (1 << SND_PCM_FORMAT_S24_BE) | \ + (1 << SND_PCM_FORMAT_U24_LE) | (1 << SND_PCM_FORMAT_U24_BE) | \ + (1 << SND_PCM_FORMAT_S32_LE) | (1 << SND_PCM_FORMAT_S32_BE) | \ + (1 << SND_PCM_FORMAT_U32_LE) | (1 << SND_PCM_FORMAT_U32_BE)) extern snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops; @@ -107,3 +108,9 @@ int snd_pcm_route_open(snd_pcm_t **pcmp, char *name, snd_pcm_t *slave, int close_slave); int snd_pcm_rate_open(snd_pcm_t **pcmp, char *name, int sformat, int srate, snd_pcm_t *slave, int close_slave); + +#define SND_PCM_ACCBIT_PLUGIN ((1 << SND_PCM_ACCESS_MMAP_INTERLEAVED) | \ + (1 << SND_PCM_ACCESS_RW_INTERLEAVED) | \ + (1 << SND_PCM_ACCESS_MMAP_NONINTERLEAVED) | \ + (1 << SND_PCM_ACCESS_RW_NONINTERLEAVED)) + diff --git a/src/pcm/pcm_rate.c b/src/pcm/pcm_rate.c index 231c83c8..74dfbdb3 100644 --- a/src/pcm/pcm_rate.c +++ b/src/pcm/pcm_rate.c @@ -233,50 +233,60 @@ static int snd_pcm_rate_close(snd_pcm_t *pcm) return 0; } -static int snd_pcm_rate_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int snd_pcm_rate_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_rate_t *rate = pcm->private; - unsigned int access_mask, format_mask; - unsigned int rate_min, rate_max; + snd_pcm_t *slave = rate->plug.slave; int err; - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - 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; + snd_pcm_hw_params_t sparams; + unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); + mask_t *access_mask = alloca(mask_sizeof()); + mask_t *format_mask = alloca(mask_sizeof()); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(access_mask, SND_PCM_ACCBIT_PLUGIN); + mask_load(format_mask, SND_PCM_FMTBIT_LINEAR); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_ACCESS, + access_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_FORMAT, + format_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_params_min(params, 1, + SND_PCM_HW_PARAM_RATE, RATE_MIN); + if (err < 0) + return err; + err = _snd_pcm_hw_params_max(params, 1, + SND_PCM_HW_PARAM_RATE, RATE_MAX); + if (err < 0) + return err; - if (info->rate_min < RATE_MIN) - info->rate_min = RATE_MIN; - if (info->rate_max > RATE_MAX) - info->rate_max = RATE_MAX; - rate_min = info->rate_min; - rate_max = info->rate_max; - if (rate_max < rate_min) - return -EINVAL; - - info->access_mask = SND_PCM_ACCBIT_MMAP; - if (rate->sformat >= 0) - info->format_mask = 1U << rate->sformat; - info->rate_min = rate->srate; - info->rate_max = rate->srate; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + if (rate->sformat >= 0) { + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + rate->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + } else + links |= (SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_SAMPLE_BITS | + SND_PCM_HW_PARBIT_FRAME_BITS); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_RATE, + rate->srate); - err = snd_pcm_hw_info(rate->plug.slave, info); - if (rate->sformat >= 0) - info->format_mask = format_mask; - info->rate_min = rate_min; - info->rate_max = rate_max; - + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave, links); if (err < 0) return err; - info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - snd_pcm_hw_info_complete(info); + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } @@ -284,32 +294,48 @@ 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; + int err; snd_pcm_hw_params_t sparams; + unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); unsigned int src_format, dst_format; unsigned int src_rate, dst_rate; - int mul, div; - int err; - snd_pcm_hw_params_to_info(params, &sinfo); - 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; - err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); - params->fail_mask = sparams.fail_mask; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + if (rate->sformat >= 0) { + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + rate->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + } else + links |= (SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_SAMPLE_BITS | + SND_PCM_HW_PARBIT_FRAME_BITS); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_RATE, + rate->srate); + + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave, links); if (err < 0) return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - src_format = params->format; + src_format = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT); dst_format = slave->format; - src_rate = params->rate; + src_rate = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_RATE); dst_rate = slave->rate; } else { src_format = slave->format; - dst_format = params->format; + dst_format = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT); src_rate = slave->rate; - dst_rate = params->rate; + dst_rate = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_RATE); } rate->get_idx = get_index(src_format, SND_PCM_FORMAT_S16); rate->put_idx = put_index(SND_PCM_FORMAT_S16, dst_format); @@ -321,16 +347,9 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) /* pitch is get_increment */ } rate->pitch = (((u_int64_t)dst_rate * DIV) + src_rate / 2) / src_rate; - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - mul = DIV; - div = rate->pitch; - } else { - mul = rate->pitch; - div = DIV; - } if (rate->states) free(rate->states); - rate->states = malloc(params->channels * sizeof(*rate->states)); + rate->states = malloc(snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_CHANNELS) * sizeof(*rate->states)); return 0; } @@ -493,7 +512,7 @@ static void snd_pcm_rate_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_rate_ops = { close: snd_pcm_rate_close, info: snd_pcm_plugin_info, - hw_info: snd_pcm_rate_hw_info, + hw_refine: snd_pcm_rate_hw_refine, hw_params: snd_pcm_rate_hw_params, sw_params: snd_pcm_rate_sw_params, dig_info: snd_pcm_plugin_dig_info, diff --git a/src/pcm/pcm_route.c b/src/pcm/pcm_route.c index d9eb379c..cbebad8b 100644 --- a/src/pcm/pcm_route.c +++ b/src/pcm/pcm_route.c @@ -425,50 +425,64 @@ static int snd_pcm_route_close(snd_pcm_t *pcm) return 0; } -static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) +static int snd_pcm_route_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_route_t *route = pcm->private; - unsigned int format_mask, access_mask; - unsigned int channels_min, channels_max; + snd_pcm_t *slave = route->plug.slave; int err; - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - 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; - channels_min = info->channels_min; - channels_max = info->channels_max; - if (channels_min > channels_max) - return -EINVAL; + snd_pcm_hw_params_t sparams; + unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); + mask_t *access_mask = alloca(mask_sizeof()); + mask_t *format_mask = alloca(mask_sizeof()); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(access_mask, SND_PCM_ACCBIT_PLUGIN); + mask_load(format_mask, SND_PCM_FMTBIT_LINEAR); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_ACCESS, + access_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_FORMAT, + format_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_params_min(params, 1, SND_PCM_HW_PARAM_CHANNELS, 1); + if (err < 0) + return err; - if (route->sformat >= 0) - info->format_mask = 1U << route->sformat; - if (route->schannels >= 0) - info->channels_min = info->channels_max = route->schannels; - - info->access_mask = SND_PCM_ACCBIT_MMAP; - err = snd_pcm_hw_info(route->plug.slave, info); - info->access_mask = access_mask; - if (route->sformat >= 0) - info->format_mask = format_mask; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + if (route->sformat >= 0) { + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + route->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + } else + links |= (SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_SAMPLE_BITS); if (route->schannels >= 0) { - info->channels_min = channels_min; - info->channels_max = channels_max; + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_CHANNELS, + route->schannels); + } else { + links |= SND_PCM_HW_PARBIT_CHANNELS; + if (route->sformat < 0) + links |= (SND_PCM_HW_PARBIT_FRAME_BITS | + SND_PCM_HW_PARBIT_FRAGMENT_BYTES | + SND_PCM_HW_PARBIT_BUFFER_BYTES); } + + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave, links); if (err < 0) return err; - info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); - snd_pcm_hw_info_complete(info); + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); return 0; } @@ -476,26 +490,51 @@ 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 src_format, dst_format; - snd_pcm_hw_info_t sinfo; - snd_pcm_hw_params_t sparams; int err; - snd_pcm_hw_params_to_info(params, &sinfo); - if (route->sformat >= 0) - sinfo.format_mask = 1 << route->sformat; - if (route->schannels >= 0) - 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; + snd_pcm_hw_params_t sparams; + unsigned int links = (SND_PCM_HW_PARBIT_FRAGMENTS | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_BUFFER_LENGTH); + unsigned int src_format, dst_format; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + if (route->sformat >= 0) { + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_FORMAT, + route->sformat); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_SUBFORMAT, + SND_PCM_SUBFORMAT_STD); + } else + links |= (SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_SAMPLE_BITS); + if (route->schannels >= 0) { + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_CHANNELS, + route->schannels); + } else { + links |= SND_PCM_HW_PARBIT_CHANNELS; + if (route->sformat < 0) + links |= (SND_PCM_HW_PARBIT_FRAME_BITS | + SND_PCM_HW_PARBIT_FRAGMENT_BYTES | + SND_PCM_HW_PARBIT_BUFFER_BYTES); + } + + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave, links); if (err < 0) return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - src_format = params->format; + src_format = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_FORMAT); dst_format = slave->format; } else { src_format = slave->format; - dst_format = params->format; + dst_format = snd_pcm_hw_params_value(params, SND_PCM_HW_PARAM_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); @@ -622,7 +661,7 @@ static void snd_pcm_route_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_route_ops = { close: snd_pcm_route_close, info: snd_pcm_plugin_info, - hw_info: snd_pcm_route_hw_info, + hw_refine: snd_pcm_route_hw_refine, hw_params: snd_pcm_route_hw_params, sw_params: snd_pcm_plugin_sw_params, dig_info: snd_pcm_plugin_dig_info, diff --git a/src/pcm/pcm_share.c b/src/pcm/pcm_share.c index 8c55c9ad..0bf9d664 100644 --- a/src/pcm/pcm_share.c +++ b/src/pcm/pcm_share.c @@ -442,50 +442,66 @@ static int snd_pcm_share_info(snd_pcm_t *pcm, snd_pcm_info_t *info) return snd_pcm_info(share->slave->pcm, info); } -static int snd_pcm_share_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info) +static int snd_pcm_share_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) { snd_pcm_share_t *share = pcm->private; snd_pcm_share_slave_t *slave = share->slave; - unsigned int access_mask; - int err = 0; - info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | - SND_PCM_ACCBIT_RW_INTERLEAVED | - SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | - SND_PCM_ACCBIT_RW_NONINTERLEAVED); - access_mask = info->access_mask; - if (access_mask == 0) - return -EINVAL; + snd_pcm_hw_params_t sparams; + int err; + mask_t *access_mask = alloca(mask_sizeof()); + const mask_t *mmap_mask; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_CHANNELS, + share->channels_count); + if (err < 0) + return err; if (slave->format >= 0) { - info->format_mask &= 1U << slave->format; - if (!info->format_mask) - return -EINVAL; + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_FORMAT, + slave->format); + if (err < 0) + return err; } - 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_min > info->rate_max) - return -EINVAL; + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_RATE, + slave->rate); + if (err < 0) + return err; } - info->access_mask = SND_PCM_ACCBIT_MMAP; - info->channels_min = info->channels_max = slave->channels_count; - err = snd_pcm_hw_info(slave->pcm, info); - info->access_mask = access_mask; - info->channels_min = info->channels_max = share->channels_count; + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_CHANNELS, + slave->channels_count); + err = snd_pcm_hw_refine2(params, &sparams, + snd_pcm_hw_refine, slave->pcm, + SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_BUFFER_LENGTH | + SND_PCM_HW_PARBIT_FRAGMENTS); + if (err < 0) + return err; + mmap_mask = snd_pcm_hw_params_value_mask(&sparams, SND_PCM_HW_PARAM_ACCESS); + mask_all(access_mask); + mask_reset(access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED); + if (!mask_test(mmap_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) + mask_reset(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); + if (!mask_test(mmap_mask, SND_PCM_ACCESS_MMAP_COMPLEX) && + !mask_test(mmap_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) + mask_reset(access_mask, SND_PCM_ACCESS_MMAP_COMPLEX); + err = _snd_pcm_hw_params_mask(params, 1, SND_PCM_HW_PARAM_ACCESS, + access_mask); if (err < 0) return err; - info->info |= SND_PCM_INFO_DOUBLE; + params->info |= SND_PCM_INFO_DOUBLE; return 0; } @@ -498,31 +514,55 @@ 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->format != spcm->format) - params->fail_mask |= SND_PCM_HW_PARBIT_FORMAT; - 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 = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_FORMAT, + spcm->format); + if (err < 0) + goto _err; + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_SUBFORMAT, + spcm->subformat); + if (err < 0) + goto _err; + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_RATE, + spcm->rate); + if (err < 0) + goto _err; + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_FRAGMENT_SIZE, + spcm->fragment_size); + if (err < 0) + goto _err; + err = _snd_pcm_hw_params_set(params, 1, SND_PCM_HW_PARAM_FRAGMENTS, + spcm->fragments); + _err: + if (err < 0) { ERR("slave is already running with different setup"); err = -EBUSY; goto _end; } } else { - snd_pcm_hw_params_t sparams = *params; - sparams.channels = slave->channels_count; - err = snd_pcm_hw_params(slave->pcm, &sparams); + snd_pcm_hw_params_t sparams; + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + _snd_pcm_hw_params_any(&sparams); + _snd_pcm_hw_params_mask(&sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + _snd_pcm_hw_params_set(&sparams, 0, SND_PCM_HW_PARAM_CHANNELS, + share->channels_count); + err = snd_pcm_hw_params2(params, &sparams, + snd_pcm_hw_params, slave->pcm, + SND_PCM_HW_PARBIT_FORMAT | + SND_PCM_HW_PARBIT_SUBFORMAT | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_FRAGMENT_SIZE | + SND_PCM_HW_PARBIT_FRAGMENT_LENGTH | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_BUFFER_LENGTH | + SND_PCM_HW_PARBIT_FRAGMENTS); if (err < 0) goto _end; /* >= 30 ms */ - slave->safety_threshold = sparams.rate * 30 / 1000; - slave->safety_threshold += sparams.fragment_size - 1; - slave->safety_threshold -= slave->safety_threshold % sparams.fragment_size; + slave->safety_threshold = slave->pcm->rate * 30 / 1000; + slave->safety_threshold += slave->pcm->fragment_size - 1; + slave->safety_threshold -= slave->safety_threshold % slave->pcm->fragment_size; slave->silence_frames = slave->safety_threshold; if (slave->pcm->stream == SND_PCM_STREAM_PLAYBACK) snd_pcm_areas_silence(slave->pcm->running_areas, 0, slave->pcm->channels, slave->pcm->buffer_size, slave->pcm->format); @@ -537,15 +577,15 @@ static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) static int snd_pcm_share_sw_params(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_sw_params_t *params) { if (params->start_mode > SND_PCM_START_LAST) { - params->fail_mask = SND_PCM_SW_PARBIT_START_MODE; + params->fail_mask = 1 << SND_PCM_SW_PARAM_START_MODE; return -EINVAL; } if (params->ready_mode > SND_PCM_READY_LAST) { - params->fail_mask = SND_PCM_SW_PARBIT_READY_MODE; + params->fail_mask = 1 << SND_PCM_SW_PARAM_READY_MODE; return -EINVAL; } if (params->xrun_mode > SND_PCM_XRUN_LAST) { - params->fail_mask = SND_PCM_SW_PARBIT_XRUN_MODE; + params->fail_mask = 1 << SND_PCM_SW_PARAM_XRUN_MODE; return -EINVAL; } return 0; @@ -1067,7 +1107,7 @@ static void snd_pcm_share_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_share_ops = { close: snd_pcm_share_close, info: snd_pcm_share_info, - hw_info: snd_pcm_share_hw_info, + hw_refine: snd_pcm_share_hw_refine, hw_params: snd_pcm_share_hw_params, sw_params: snd_pcm_share_sw_params, dig_info: snd_pcm_share_dig_info, diff --git a/src/pcm/pcm_shm.c b/src/pcm/pcm_shm.c index 885d72e0..cac1108d 100644 --- a/src/pcm/pcm_shm.c +++ b/src/pcm/pcm_shm.c @@ -42,7 +42,6 @@ typedef struct { int socket; 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) @@ -148,49 +147,60 @@ static int snd_pcm_shm_info(snd_pcm_t *pcm, snd_pcm_info_t * info) return err; } -static int snd_pcm_shm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +static int _snd_pcm_shm_hw_refine(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED) { snd_pcm_shm_t *shm = pcm->private; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; - int err; - 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; - err = snd_pcm_shm_action(pcm); - if (err < 0) - 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; + ctrl->cmd = SND_PCM_IOCTL_HW_REFINE; + return snd_pcm_shm_action(pcm); } -static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) +static int snd_pcm_shm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + snd_pcm_shm_t *shm = pcm->private; + volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; + snd_pcm_hw_params_t *sparams = (snd_pcm_hw_params_t *) &ctrl->u.hw_refine; + const mask_t *access_mask = snd_pcm_hw_params_value_mask(params, SND_PCM_HW_PARAM_ACCESS); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + if (!mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) && + !mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) + mask_intersect(saccess_mask, access_mask); + _snd_pcm_hw_params_any(sparams); + _snd_pcm_hw_params_mask(sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + return snd_pcm_hw_refine2(params, sparams, + _snd_pcm_shm_hw_refine, pcm, + ~SND_PCM_HW_PARBIT_ACCESS); +} + +static int _snd_pcm_shm_hw_params(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params ATTRIBUTE_UNUSED) { snd_pcm_shm_t *shm = pcm->private; volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; - unsigned int access = params->access; - int err; ctrl->cmd = SND_PCM_IOCTL_HW_PARAMS; - ctrl->u.hw_params = *params; - if (shm->access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) - ctrl->u.hw_params.access = SND_PCM_ACCESS_MMAP_INTERLEAVED; - else if (shm->access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED) - ctrl->u.hw_params.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; - else - assert(0); - err = snd_pcm_shm_action(pcm); - params->access = access; - *params = ctrl->u.hw_params; - if (err < 0) - return err; - return err; + return snd_pcm_shm_action(pcm); +} + +static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) +{ + snd_pcm_shm_t *shm = pcm->private; + volatile snd_pcm_shm_ctrl_t *ctrl = shm->ctrl; + snd_pcm_hw_params_t *sparams = (snd_pcm_hw_params_t *) &ctrl->u.hw_refine; + const mask_t *access_mask = snd_pcm_hw_params_value_mask(params, SND_PCM_HW_PARAM_ACCESS); + mask_t *saccess_mask = alloca(mask_sizeof()); + mask_load(saccess_mask, SND_PCM_ACCBIT_MMAP); + if (!mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) && + !mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) + mask_intersect(saccess_mask, access_mask); + _snd_pcm_hw_params_any(sparams); + _snd_pcm_hw_params_mask(sparams, 0, SND_PCM_HW_PARAM_ACCESS, + saccess_mask); + return snd_pcm_hw_params2(params, sparams, + _snd_pcm_shm_hw_params, pcm, + ~SND_PCM_HW_PARBIT_ACCESS); } static int snd_pcm_shm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params) @@ -461,7 +471,7 @@ static void snd_pcm_shm_dump(snd_pcm_t *pcm, FILE *fp) snd_pcm_ops_t snd_pcm_shm_ops = { close: snd_pcm_shm_close, info: snd_pcm_shm_info, - hw_info: snd_pcm_shm_hw_info, + hw_refine: snd_pcm_shm_hw_refine, hw_params: snd_pcm_shm_hw_params, sw_params: snd_pcm_shm_sw_params, dig_info: snd_pcm_shm_dig_info,