/*
* 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;
}