/* * This file is part of uniaud.dll. * * Copyright (c) 2010 Mensys BV * Copyright (c) 2007 Vlad Stelmahovsky aka Vladest * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License and the GNU General Public License along with this library. * If not, see . */ #define INCL_DOS #define INCL_DOSERRORS #include #include #include #include #include #include #include "internal.h" /* * redefine ALSA internals to uniaud */ #define snd_pcm_hw_refine _uniaud_pcm_refine_hw_params #define snd_pcm_t uniaud_pcm typedef enum _snd_set_mode { SND_CHANGE, SND_TRY, SND_TEST, } snd_set_mode_t; MASK_INLINE unsigned int ld2(uint32_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; } void boundary_sub(int a, int adir, int b, int bdir, int *c, int *cdir) { adir = adir < 0 ? -1 : (adir > 0 ? 1 : 0); bdir = bdir < 0 ? -1 : (bdir > 0 ? 1 : 0); *c = a - b; *cdir = adir - bdir; if (*cdir == -2) { assert(*c > INT_MIN); (*c)--; } else if (*cdir == 2) { assert(*c < INT_MAX); (*c)++; } } int boundary_lt(unsigned int a, int adir, unsigned int b, int bdir) { assert(a > 0 || adir >= 0); assert(b > 0 || bdir >= 0); if (adir < 0) { a--; adir = 1; } else if (adir > 0) adir = 1; if (bdir < 0) { b--; bdir = 1; } else if (bdir > 0) bdir = 1; return a < b || (a == b && adir < bdir); } /* Return 1 if min is nearer to best than max */ int boundary_nearer(int min, int mindir, int best, int bestdir, int max, int maxdir) { int dmin, dmindir; int dmax, dmaxdir; boundary_sub(best, bestdir, min, mindir, &dmin, &dmindir); boundary_sub(max, maxdir, best, bestdir, &dmax, &dmaxdir); return boundary_lt(dmin, dmindir, dmax, dmaxdir); } inline int snd_interval_empty(const snd_interval_t *i) { return i->empty; } inline int snd_interval_single(const snd_interval_t *i) { assert(!snd_interval_empty(i)); return (i->min == i->max || (i->min + 1 == i->max && i->openmax)); } INTERVAL_INLINE int snd_interval_value(const snd_interval_t *i) { assert(snd_interval_single(i)); return i->min; } inline void snd_mask_none(snd_mask_t *mask) { memset(mask, 0, sizeof(*mask)); } inline int snd_mask_single(const snd_mask_t *mask) { int i, c = 0; // assert(!snd_mask_empty(mask)); for (i = 0; i < SNDRV_MASK_SIZE; i++) { if (! mask->bits[i]) continue; if (mask->bits[i] & (mask->bits[i] - 1)) return 0; if (c) return 0; c++; } return 1; } inline void snd_mask_leave(snd_mask_t *mask, unsigned int val) { unsigned int v; //assert(val <= SNDRV_MASK_BITS); v = mask->bits[MASK_OFS(val)] & MASK_BIT(val); snd_mask_none(mask); mask->bits[MASK_OFS(val)] = v; } inline int snd_mask_empty(const snd_mask_t *mask) { int i; for (i = 0; i < SNDRV_MASK_SIZE; i++) if (mask->bits[i]) return 0; return 1; } inline unsigned int snd_mask_min(const snd_mask_t *mask) { int i; assert(!snd_mask_empty(mask)); for (i = 0; i < MASK_SIZE; i++) { if (mask->bits[i]) return ffs(mask->bits[i]) - 1 + (i << 5); } return 0; } MASK_INLINE int snd_mask_refine_first(snd_mask_t *mask) { if (snd_mask_empty(mask)) return -ENOENT; if (snd_mask_single(mask)) return 0; snd_mask_leave(mask, snd_mask_min(mask)); return 1; } int snd_interval_refine_first(snd_interval_t *i) { if (snd_interval_empty(i)) return -ENOENT; if (snd_interval_single(i)) return 0; i->max = i->min; i->openmax = i->openmin; if (i->openmax) i->max++; return 1; } INTERVAL_INLINE int snd_interval_max(const snd_interval_t *i) { assert(!snd_interval_empty(i)); return i->max; } inline void snd_mask_any(snd_mask_t *mask) { memset(mask, 0xff, SNDRV_MASK_SIZE * sizeof(uint32_t)); } inline void snd_interval_any(snd_interval_t *i) { i->min = 0; i->openmin = 0; i->max = UINT_MAX; i->openmax = 0; i->integer = 0; i->empty = 0; } int snd_interval_refine_last(snd_interval_t *i) { if (snd_interval_empty(i)) return -ENOENT; if (snd_interval_single(i)) return 0; i->min = i->max; i->openmin = i->openmax; if (i->openmin) i->min--; return 1; } static inline int hw_is_mask(int var) { return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK && var <= SNDRV_PCM_HW_PARAM_LAST_MASK; } static inline int hw_is_interval(int var) { return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; } static inline snd_mask_t *hw_param_mask(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { return ¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; } #if 0 inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } #endif static inline const snd_interval_t *hw_param_interval_c(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { return (const snd_interval_t *)hw_param_interval((snd_pcm_hw_params_t*) params, var); } inline int snd_mask_refine_set(snd_mask_t *mask, unsigned int val) { int changed; // assert(!snd_mask_empty(mask)); changed = !snd_mask_single(mask); snd_mask_leave(mask, val); if (snd_mask_empty(mask)) return -EINVAL; return changed; } MASK_INLINE int snd_mask_value(const snd_mask_t *mask) { assert(!snd_mask_empty(mask)); return snd_mask_min(mask); } inline void snd_interval_none(snd_interval_t *i) { i->empty = 1; } inline int snd_interval_checkempty(const snd_interval_t *i) { return (i->min > i->max || (i->min == i->max && (i->openmin || i->openmax))); } INTERVAL_INLINE int snd_interval_min(const snd_interval_t *i) { assert(!snd_interval_empty(i)); return i->min; } static inline const snd_mask_t *hw_param_mask_c(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { return (const snd_mask_t *)hw_param_mask((snd_pcm_hw_params_t*) params, var); } int snd_pcm_hw_param_empty(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { if (hw_is_mask(var)) return snd_mask_empty(hw_param_mask_c(params, var)); if (hw_is_interval(var)) return snd_interval_empty(hw_param_interval_c(params, var)); assert(0); return -EINVAL; } int snd_interval_refine_min(snd_interval_t *i, unsigned int min, int openmin) { int changed = 0; if (snd_interval_empty(i)) return -ENOENT; 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->integer) { if (i->openmin) { i->min++; i->openmin = 0; } } if (snd_interval_checkempty(i)) { snd_interval_none(i); return -EINVAL; } return changed; } int snd_interval_refine_max(snd_interval_t *i, unsigned int max, int openmax) { int changed = 0; if (snd_interval_empty(i)) return -ENOENT; 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->integer) { if (i->openmax) { i->max--; i->openmax = 0; } } if (snd_interval_checkempty(i)) { snd_interval_none(i); return -EINVAL; } return changed; } /** * snd_interval_refine - refine the interval value of configurator * @i: the interval value to refine * @v: the interval value to refer to * * Refines the interval value with the reference value. * The interval is changed to the range satisfying both intervals. * The interval status (min, max, integer, etc.) are evaluated. * * Returns non-zero if the value is changed, zero if not changed. */ int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v) { int changed = 0; // assert(!snd_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->integer && v->integer) { i->integer = 1; changed = 1; } if (i->integer) { if (i->openmin) { i->min++; i->openmin = 0; } if (i->openmax) { i->max--; i->openmax = 0; } } else if (!i->openmin && !i->openmax && i->min == i->max) i->integer = 1; if (snd_interval_checkempty(i)) { snd_interval_none(i); return -EINVAL; } return changed; } int snd_interval_refine_set(snd_interval_t *i, unsigned int val) { snd_interval_t t; t.empty = 0; t.min = t.max = val; t.openmin = t.openmax = 0; t.integer = 1; return snd_interval_refine(i, &t); } int _snd_pcm_hw_param_set(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int dir) { int changed; if (hw_is_mask(var)) { snd_mask_t *m = hw_param_mask(params, var); if (val == 0 && dir < 0) { changed = -EINVAL; snd_mask_none(m); } else { if (dir > 0) val++; else if (dir < 0) val--; changed = snd_mask_refine_set(hw_param_mask(params, var), val); } } else if (hw_is_interval(var)) { snd_interval_t *i = hw_param_interval(params, var); if (val == 0 && dir < 0) { changed = -EINVAL; snd_interval_none(i); } else if (dir == 0) changed = snd_interval_refine_set(i, val); else { snd_interval_t t; t.openmin = 1; t.openmax = 1; t.empty = 0; t.integer = 0; if (dir < 0) { t.min = val - 1; t.max = val; } else { t.min = val; t.max = val+1; } changed = snd_interval_refine(i, &t); } } else { // assert(0); return -EINVAL; } if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; } return changed; } void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { if (hw_is_mask(var)) { snd_mask_any(hw_param_mask(params, var)); params->cmask |= 1 << var; params->rmask |= 1 << var; return; } if (hw_is_interval(var)) { snd_interval_any(hw_param_interval(params, var)); params->cmask |= 1 << var; params->rmask |= 1 << var; return; } } void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params) { unsigned int k; memset(params, 0, sizeof(*params)); for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK; k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) _snd_pcm_hw_param_any(params, k); for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++) _snd_pcm_hw_param_any(params, k); params->info = ~0U; } inline void snd_mask_reset_range(snd_mask_t *mask, unsigned int from, unsigned int to) { unsigned int i; assert(to <= SND_MASK_MAX && from <= to); for (i = from; i <= to; i++) mask->bits[MASK_OFS(i)] &= ~MASK_BIT(i); } inline int snd_mask_refine_min(snd_mask_t *mask, unsigned int val) { if (snd_mask_empty(mask)) return -ENOENT; if (snd_mask_min(mask) >= val) return 0; snd_mask_reset_range(mask, 0, val - 1); if (snd_mask_empty(mask)) return -EINVAL; return 1; } MASK_INLINE unsigned int snd_mask_max(const snd_mask_t *mask) { int i; assert(!snd_mask_empty(mask)); for (i = MASK_SIZE - 1; i >= 0; i--) { if (mask->bits[i]) return ld2(mask->bits[i]) + (i << 5); } return 0; } MASK_INLINE int snd_mask_refine_max(snd_mask_t *mask, unsigned int val) { if (snd_mask_empty(mask)) return -ENOENT; if (snd_mask_max(mask) <= val) return 0; snd_mask_reset_range(mask, val + 1, SND_MASK_MAX); if (snd_mask_empty(mask)) return -EINVAL; return 1; } MASK_INLINE int snd_mask_refine_last(snd_mask_t *mask) { if (snd_mask_empty(mask)) return -ENOENT; if (snd_mask_single(mask)) return 0; snd_mask_leave(mask, snd_mask_max(mask)); return 1; } int _snd_pcm_hw_param_set_min(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int dir) { int changed; int openmin = 0; if (dir) { if (dir > 0) { openmin = 1; } else if (dir < 0) { if (val > 0) { openmin = 1; val--; } } } if (hw_is_mask(var)) changed = snd_mask_refine_min(hw_param_mask(params, var), val + !!openmin); else if (hw_is_interval(var)) changed = snd_interval_refine_min(hw_param_interval(params, var), val, openmin); else { assert(0); return -EINVAL; } if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; } return changed; } /* Return the minimum value for field PAR. */ int snd_pcm_hw_param_get_min(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int *val, int *dir) { if (hw_is_mask(var)) { const snd_mask_t *m = hw_param_mask_c(params, var); assert(!snd_mask_empty(m)); if (dir) *dir = 0; if (val) *val = snd_mask_min(m); return 0; } else if (hw_is_interval(var)) { const snd_interval_t *i = hw_param_interval_c(params, var); assert(!snd_interval_empty(i)); if (dir) *dir = i->openmin; if (val) *val = snd_interval_min(i); return 0; } assert(0); return 0; } /* 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_param_set_min(uniaud_pcm *pcm, snd_pcm_hw_params_t *params, snd_set_mode_t mode, snd_pcm_hw_param_t var, unsigned int *val, int *dir) { snd_pcm_hw_params_t save; int err; switch (mode) { case SND_CHANGE: break; case SND_TRY: save = *params; break; case SND_TEST: save = *params; params = &save; break; default: assert(0); return -EINVAL; } err = _snd_pcm_hw_param_set_min(params, var, *val, dir ? *dir : 0); if (err < 0) goto _fail; if ((mode != SND_TEST || hw_is_interval(var)) && params->rmask) { err = snd_pcm_hw_refine(pcm, params); if (err < 0) goto _fail; if (snd_pcm_hw_param_empty(params, var)) { err = -ENOENT; goto _fail; } } return snd_pcm_hw_param_get_min(params, var, val, dir); _fail: if (mode == SND_TRY) *params = save; return err; } int _snd_pcm_hw_param_set_max(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int val, int dir) { int changed; int openmax = 0; if (dir) { if (dir < 0) { openmax = 1; } else if (dir > 0) { openmax = 1; val++; } } if (hw_is_mask(var)) { if (val == 0 && openmax) { snd_mask_none(hw_param_mask(params, var)); changed = -EINVAL; } else changed = snd_mask_refine_max(hw_param_mask(params, var), val - !!openmax); } else if (hw_is_interval(var)) changed = snd_interval_refine_max(hw_param_interval(params, var), val, openmax); else { assert(0); return -EINVAL; } if (changed) { params->cmask |= 1 << var; params->rmask |= 1 << var; } return changed; } /* Return the maximum value for field PAR. */ int snd_pcm_hw_param_get_max(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int *val, int *dir) { if (hw_is_mask(var)) { const snd_mask_t *m = hw_param_mask_c(params, var); assert(!snd_mask_empty(m)); if (dir) *dir = 0; if (val) *val = snd_mask_max(m); return 0; } else if (hw_is_interval(var)) { const snd_interval_t *i = hw_param_interval_c(params, var); assert(!snd_interval_empty(i)); if (dir) *dir = - (int) i->openmax; if (val) *val = snd_interval_max(i); return 0; } assert(0); return 0; } /* 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_param_set_max(uniaud_pcm *pcm, snd_pcm_hw_params_t *params, snd_set_mode_t mode, snd_pcm_hw_param_t var, unsigned int *val, int *dir) { snd_pcm_hw_params_t save; int err; switch (mode) { case SND_CHANGE: break; case SND_TRY: save = *params; break; case SND_TEST: save = *params; params = &save; break; default: assert(0); return -EINVAL; } err = _snd_pcm_hw_param_set_max(params, var, *val, dir ? *dir : 0); if (err < 0) goto _fail; if ((mode != SND_TEST || hw_is_interval(var)) && params->rmask) { err = snd_pcm_hw_refine(pcm, params); if (err < 0) goto _fail; if (snd_pcm_hw_param_empty(params, var)) { err = -ENOENT; goto _fail; } } return snd_pcm_hw_param_get_max(params, var, val, dir); _fail: if (mode == SND_TRY) *params = save; return err; } static int _snd_pcm_hw_param_set_last(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { int changed; if (hw_is_mask(var)) changed = snd_mask_refine_last(hw_param_mask(params, var)); else if (hw_is_interval(var)) changed = snd_interval_refine_last(hw_param_interval(params, var)); else { assert(0); return -EINVAL; } if (changed > 0) { params->cmask |= 1 << var; params->rmask |= 1 << var; } return changed; } /* Return the value for field PAR if it's fixed in configuration space defined by PARAMS. Return -EINVAL otherwise */ int snd_pcm_hw_param_get(const snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int *val, int *dir) { if (hw_is_mask(var)) { const snd_mask_t *mask = hw_param_mask_c(params, var); if (snd_mask_empty(mask) || !snd_mask_single(mask)) return -EINVAL; if (dir) *dir = 0; if (val) *val = snd_mask_value(mask); return 0; } else if (hw_is_interval(var)) { const snd_interval_t *i = hw_param_interval_c(params, var); if (snd_interval_empty(i) || !snd_interval_single(i)) return -EINVAL; if (dir) *dir = i->openmin; if (val) *val = snd_interval_value(i); return 0; } assert(0); return -EINVAL; } /* Inside configuration space defined by PARAMS remove from PAR all values < maximum. Reduce configuration space accordingly. Return the maximum. */ int snd_pcm_hw_param_set_last(uniaud_pcm *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int *rval, int *dir) { int err; err = _snd_pcm_hw_param_set_last(params, var); if (err < 0) return err; if (params->rmask) { err = snd_pcm_hw_refine(pcm, params); if (err < 0) return err; } return snd_pcm_hw_param_get(params, var, rval, dir); } static int _snd_pcm_hw_param_set_first(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var) { int changed; if (hw_is_mask(var)) changed = snd_mask_refine_first(hw_param_mask(params, var)); else if (hw_is_interval(var)) changed = snd_interval_refine_first(hw_param_interval(params, var)); else { assert(0); return -EINVAL; } if (changed > 0) { params->cmask |= 1 << var; params->rmask |= 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_param_set_first(uniaud_pcm *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int *rval, int *dir) { int err; err = _snd_pcm_hw_param_set_first(params, var); if (err < 0) return err; if (params->rmask) { err = snd_pcm_hw_refine(pcm, params); if (err < 0) return err; } return snd_pcm_hw_param_get(params, var, rval, dir); } /* 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_param_set_near(uniaud_pcm *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, unsigned int *val, int *dir) { snd_pcm_hw_params_t save; int err; unsigned int best = *val, saved_min; int last = 0; int min, max; int mindir, maxdir; int valdir = dir ? *dir : 0; snd_interval_t *i; /* FIXME */ if (best > INT_MAX) best = INT_MAX; min = max = best; mindir = maxdir = valdir; if (maxdir > 0) maxdir = 0; else if (maxdir == 0) maxdir = -1; else { maxdir = 1; max--; } save = *params; saved_min = min; err = snd_pcm_hw_param_set_min(pcm, params, SND_CHANGE, var, (unsigned int *)&min, &mindir); i = hw_param_interval(params, var); if (!snd_interval_empty(i) && snd_interval_single(i)) return snd_pcm_hw_param_get_min(params, var, val, dir); if (err >= 0) { snd_pcm_hw_params_t params1; if (max < 0) goto _end; if ((unsigned int)min == saved_min && mindir == valdir) goto _end; params1 = save; err = snd_pcm_hw_param_set_max(pcm, ¶ms1, SND_CHANGE, var, (unsigned int *)&max, &maxdir); if (err < 0) goto _end; if (boundary_nearer(max, maxdir, best, valdir, min, mindir)) { *params = params1; last = 1; } } else { *params = save; err = snd_pcm_hw_param_set_max(pcm, params, SND_CHANGE, var, (unsigned int *)&max, &maxdir); if (err < 0) return err; last = 1; } _end: if (last) err = snd_pcm_hw_param_set_last(pcm, params, var, val, dir); else err = snd_pcm_hw_param_set_first(pcm, params, var, val, dir); return err; } /** * \brief Restrict a configuration space to have rate nearest to a target * \param pcm PCM handle * \param params Configuration space * \param val approximate target rate / returned approximate set rate * \return 0 otherwise a negative error code if configuration space is empty * * target/chosen exact value is <,=,> val following dir (-1,0,1) */ int snd_pcm_hw_params_set_rate_near(uniaud_pcm *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { return snd_pcm_hw_param_set_near(pcm, params, SND_PCM_HW_PARAM_RATE, val, dir); } /** * \brief Restrict a configuration space to have buffer time nearest to a target * \param pcm PCM handle * \param params Configuration space * \param val approximate target buffer duration in us / returned chosen approximate target buffer duration * \return 0 otherwise a negative error code if configuration space is empty * * target/chosen exact value is <,=,> val following dir (-1,0,1) */ int snd_pcm_hw_params_set_buffer_time_near(uniaud_pcm *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { return snd_pcm_hw_param_set_near(pcm, params, SND_PCM_HW_PARAM_BUFFER_TIME, val, dir); } /** * \brief Restrict a configuration space to have period time nearest to a target * \param pcm PCM handle * \param params Configuration space * \param val approximate target period duration in us / returned chosen approximate target period duration * \return 0 otherwise a negative error code if configuration space is empty * * target/chosen exact value is <,=,> val following dir (-1,0,1) */ int snd_pcm_hw_params_set_period_time_near(uniaud_pcm *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { return snd_pcm_hw_param_set_near(pcm, params, SND_PCM_HW_PARAM_PERIOD_TIME, val, dir); } /** * \brief Extract buffer size from a configuration space * \param params Configuration space * \param val Returned buffer size in frames * \return 0 otherwise a negative error code if not exactly one is present */ int snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { unsigned int _val; int err = snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &_val, NULL); if (err >= 0) *val = _val; return err; } /** * \brief Extract period size from a configuration space * \param params Configuration space * \param val Returned approximate period size in frames * \param dir Sub unit direction * \return 0 otherwise a negative error code if not exactly one is present * * Actual exact value is <,=,> the approximate one following dir (-1, 0, 1) */ int snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) { unsigned int _val; int err = snd_pcm_hw_param_get(params, SND_PCM_HW_PARAM_PERIOD_SIZE, &_val, dir); if (err >= 0) *val = _val; return err; } /** * \brief Return bytes needed to store a quantity of PCM sample * \param format Sample format * \param samples Samples count * \return bytes needed, a negative error code if not integer or unknown */ ssize_t uniaud_pcm_format_size(snd_pcm_format_t format, size_t samples) { switch (format) { case SNDRV_PCM_FORMAT_S8: case SNDRV_PCM_FORMAT_U8: return samples; case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_BE: case SNDRV_PCM_FORMAT_U16_LE: case SNDRV_PCM_FORMAT_U16_BE: return samples * 2; case SNDRV_PCM_FORMAT_S18_3LE: case SNDRV_PCM_FORMAT_S18_3BE: case SNDRV_PCM_FORMAT_U18_3LE: case SNDRV_PCM_FORMAT_U18_3BE: case SNDRV_PCM_FORMAT_S20_3LE: case SNDRV_PCM_FORMAT_S20_3BE: case SNDRV_PCM_FORMAT_U20_3LE: case SNDRV_PCM_FORMAT_U20_3BE: case SNDRV_PCM_FORMAT_S24_3LE: case SNDRV_PCM_FORMAT_S24_3BE: case SNDRV_PCM_FORMAT_U24_3LE: case SNDRV_PCM_FORMAT_U24_3BE: return samples * 3; case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S24_BE: case SNDRV_PCM_FORMAT_U24_LE: case SNDRV_PCM_FORMAT_U24_BE: case SNDRV_PCM_FORMAT_S32_LE: case SNDRV_PCM_FORMAT_S32_BE: case SNDRV_PCM_FORMAT_U32_LE: case SNDRV_PCM_FORMAT_U32_BE: case SNDRV_PCM_FORMAT_FLOAT_LE: case SNDRV_PCM_FORMAT_FLOAT_BE: return samples * 4; case SNDRV_PCM_FORMAT_FLOAT64_LE: case SNDRV_PCM_FORMAT_FLOAT64_BE: return samples * 8; case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_BE: return samples * 4; case SNDRV_PCM_FORMAT_MU_LAW: case SNDRV_PCM_FORMAT_A_LAW: return samples; case SNDRV_PCM_FORMAT_IMA_ADPCM: if (samples & 1) return -EINVAL; return samples / 2; default: assert(0); return -EINVAL; } } /** * \brief Fill params with a full configuration space for a PCM * \param pcm PCM handle * \param params Configuration space */ 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); } /** * \brief Get delay from a PCM status container (see #snd_pcm_delay) * \return Delay in frames * * Delay is distance between current application frame position and * sound frame position. * It's positive and less than buffer size in normal situation, * negative on playback underrun and greater than buffer size on * capture overrun. */ snd_pcm_sframes_t snd_pcm_status_get_delay(const snd_pcm_status_t *obj) { assert(obj); return obj->delay; } /** * \brief Get number of frames available from a PCM status container (see #snd_pcm_avail_update) * \return Number of frames ready to be read/written */ snd_pcm_uframes_t snd_pcm_status_get_avail(const snd_pcm_status_t *obj) { assert(obj); return obj->avail; } /** * \brief Get maximum number of frames available from a PCM status container after last #snd_pcm_status call * \return Maximum number of frames ready to be read/written */ snd_pcm_uframes_t snd_pcm_status_get_avail_max(const snd_pcm_status_t *obj) { assert(obj); return obj->avail_max; } /** * \brief Get count of ADC overrange detections since last call * \return Count of ADC overrange detections */ snd_pcm_uframes_t snd_pcm_status_get_overrange(const snd_pcm_status_t *obj) { assert(obj); return obj->overrange; } /** * \brief Restrict a configuration space to have periods count nearest to a target * \param pcm PCM handle * \param params Configuration space * \param val approximate target periods per buffer / returned chosen approximate target periods per buffer * \return 0 otherwise a negative error code if configuration space is empty * * target/chosen exact value is <,=,> val following dir (-1,0,1) */ int snd_pcm_hw_params_set_periods_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir) { return snd_pcm_hw_param_set_near(pcm, params, SND_PCM_HW_PARAM_PERIODS, val, dir); } /** * \brief Restrict a configuration space to have buffer size nearest to a target * \param pcm PCM handle * \param params Configuration space * \param val approximate target buffer size in frames / returned chosen approximate target buffer size in frames * \return 0 otherwise a negative error code if configuration space is empty * * target/chosen exact value is <,=,> val following dir (-1,0,1) */ int snd_pcm_hw_params_set_buffer_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { unsigned int _val = *val; int err = snd_pcm_hw_param_set_near(pcm, params, SND_PCM_HW_PARAM_BUFFER_SIZE, &_val, NULL); if (err >= 0) *val = _val; return err; } /** * \brief Restrict a configuration space to have period size nearest to a target * \param pcm PCM handle * \param params Configuration space * \param val approximate target period size in frames / returned chosen approximate target period size * \return 0 otherwise a negative error code if configuration space is empty * * target/chosen exact value is <,=,> val following dir (-1,0,1) */ int snd_pcm_hw_params_set_period_size_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) { unsigned int _val = *val; int err = snd_pcm_hw_param_set_near(pcm, params, SND_PCM_HW_PARAM_PERIOD_SIZE, &_val, dir); if (err >= 0) *val = _val; return err; } /** * \brief Extract minimum buffer size from a configuration space * \param params Configuration space * \param val Returned approximate minimum buffer size in frames * \param dir Sub unit direction * \return 0 otherwise a negative error code * * Exact value is <,=,> the returned one following dir (-1,0,1) */ int snd_pcm_hw_params_get_buffer_size_min(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { unsigned int _val; int err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &_val, NULL); if (err >= 0) *val = _val; return err; } /** * \brief Extract maximum buffer size from a configuration space * \param params Configuration space * \param val Returned approximate maximum buffer size in frames * \param dir Sub unit direction * \return 0 otherwise a negative error code * * Exact value is <,=,> the returned one following dir (-1,0,1) */ int snd_pcm_hw_params_get_buffer_size_max(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val) { unsigned int _val; int err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_BUFFER_SIZE, &_val, NULL); if (err >= 0) *val = _val; return err; } /** * \brief Extract minimum period size from a configuration space * \param params Configuration space * \param val approximate minimum period size in frames * \param dir Sub unit direction * \return 0 otherwise a negative error code * * Exact value is <,=,> the returned one following dir (-1,0,1) */ int snd_pcm_hw_params_get_period_size_min(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) { unsigned int _val = *val; int err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_PERIOD_SIZE, &_val, dir); if (err >= 0) *val = _val; return err; } /** * \brief Extract maximum period size from a configuration space * \param params Configuration space * \param val approximate minimum period size in frames * \param dir Sub unit direction * \return 0 otherwise a negative error code * * Exact value is <,=,> the returned one following dir (-1,0,1) */ int snd_pcm_hw_params_get_period_size_max(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir) { unsigned int _val = *val; int err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_PERIOD_SIZE, &_val, dir); if (err >= 0) *val = _val; return err; }