<commit_before>/*
 *  PCM - Params functions
 *  Copyright (c) 2000 by <NAME> <<EMAIL>>
 *
 *
 *   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 2.1 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 Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */
  
#include "pcm_local.h"

static inline int hw_is_mask(snd_pcm_hw_param_t var)
{
#if SND_PCM_HW_PARAM_FIRST_MASK == 0
	return var <= SND_PCM_HW_PARAM_LAST_MASK;
#else
	return var >= SND_PCM_HW_PARAM_FIRST_MASK &&
		var <= SND_PCM_HW_PARAM_LAST_MASK;
#endif
}

static inline int hw_is_interval(snd_pcm_hw_param_t var)
{
	return var >= SND_PCM_HW_PARAM_FIRST_INTERVAL &&
		var <= SND_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)
{
	assert(hw_is_mask(var));
	return (snd_mask_t*)&params->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
}

static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
						snd_pcm_hw_param_t var)
{
	assert(hw_is_interval(var));
	return &params->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
}

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);
}

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);
}

static 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;
	}
	assert(0);
}

int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
			 snd_pcm_hw_param_t var)
{
	_snd_pcm_hw_param_any(params, var);
	return snd_pcm_hw_refine(pcm, params);
}

void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
{
	unsigned int k;
	memset(params, 0, sizeof(*params));
	for (k = 0; k <= SND_PCM_HW_PARAM_LAST; k++)
		_snd_pcm_hw_param_any(params, k);
	params->info = ~0U;
}

/* 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, 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;
		return snd_mask_value(mask);
	}
	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;
		return snd_interval_value(i);
	}
	assert(0);
	return -EINVAL;
}

/* Return the minimum value for field PAR. */
unsigned int snd_pcm_hw_param_get_min(const snd_pcm_hw_params_t *params,
				      snd_pcm_hw_param_t var, int *dir)
{
	if (hw_is_mask(var)) {
		if (dir)
			*dir = 0;
		return snd_mask_min(hw_param_mask_c(params, var));
	}
	if (hw_is_interval(var)) {
		const snd_interval_t *i = hw_param_interval_c(params, var);
		if (dir)
			*dir = i->openmin;
		return snd_interval_min(i);
	}
	assert(0);
	return 0;
}

/* Return the maximum value for field PAR. */
unsigned int snd_pcm_hw_param_get_max(const snd_pcm_hw_params_t *params,
				      snd_pcm_hw_param_t var, int *dir)
{
	if (hw_is_mask(var)) {
		if (dir)
			*dir = 0;
		return snd_mask_max(hw_param_mask_c(params, var));
	}
	if (hw_is_interval(var)) {
		const snd_interval_t *i = hw_param_interval_c(params, var);
		if (dir)
			*dir = - (int) i->openmax;
		return snd_interval_max(i);
	}
	assert(0);
	return 0;
}

/* 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 snd_mask_t *snd_pcm_hw_param_get_mask(const snd_pcm_hw_params_t *params,
					   snd_pcm_hw_param_t var)
{
	assert(hw_is_mask(var));
	return hw_param_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 snd_interval_t *snd_pcm_hw_param_get_interval(const snd_pcm_hw_params_t *params,
						  snd_pcm_hw_param_t var)
{
	assert(hw_is_interval(var));
	return hw_param_interval_c(params, var);
}

/* --- Refinement functions --- */

int _snd_pcm_hw_param_set_interval(snd_pcm_hw_params_t *params,
				   snd_pcm_hw_param_t var,
				   const snd_interval_t *val)
{
	int changed;
	assert(hw_is_interval(var));
	changed = snd_interval_refine(hw_param_interval(params, var), val);
	if (changed) {
		params->cmask |= 1 << var;
		params->rmask |= 1 << var;
	}
	return changed;
}

void _snd_pcm_hw_param_set_empty(snd_pcm_hw_params_t *params,
				 snd_pcm_hw_param_t var)
{
	if (hw_is_mask(var)) {
	snd_mask_none(hw_param_mask(params, var));
		params->cmask |= 1 << var;
		params->rmask |= 1 << var;
	} else if (hw_is_interval(var)) {
		snd_interval_none(hw_param_interval(params, var));
		params->cmask |= 1 << var;
		params->rmask |= 1 << var;
	} else {
		assert(0);
	}
}

static int _snd_pcm_hw_param_set_integer(snd_pcm_hw_params_t *params,
					 snd_pcm_hw_param_t var)
{
	int changed;
	assert(hw_is_interval(var));
	changed = snd_interval_setinteger(hw_param_interval(params, var));
	if (changed) {
		params->cmask |= 1 << var;
		params->rmask |= 1 << var;
	}
	return changed;
}
	
/* Inside configuration space defined by PARAMS remove from PAR all 
   non integer values. Reduce configuration space accordingly.
   Return -EINVAL if the configuration space is empty
*/
int snd_pcm_hw_param_set_integer(snd_pcm_t *pcm, 
				 snd_pcm_hw_params_t *params,
				 snd_set_mode_t mode,
				 snd_pcm_hw_param_t var)
{
	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_integer(params, var);
	if (err < 0)
		goto _fail;
	if (params->rmask) {
		err = snd_pcm_hw_refine(pcm, params);
		if (err < 0)
			goto _fail;
	}
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

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) {
		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(snd_pcm_t *pcm, 
			       snd_pcm_hw_params_t *params, 
			       snd_pcm_hw_param_t var,
			       int *dir, unsigned int *rval)
{
	int err;
	unsigned int val;

	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;
	}
	val = snd_pcm_hw_param_get(params, var, dir);
	if (rval)
		*rval = val;
	return 0;
}

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) {
		params->cmask |= 1 << var;
		params->rmask |= 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_param_set_last(snd_pcm_t *pcm, 
			      snd_pcm_hw_params_t *params,
			      snd_pcm_hw_param_t var,
			      int *dir, unsigned int *rval)
{
	int err;
	unsigned int val;

	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;
	}
	val = snd_pcm_hw_param_get(params, var, dir);
	if (rval)
		*rval = val;
	return 0;
}

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

/* 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(snd_pcm_t *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;
	}
	*val = snd_pcm_hw_param_get_min(params, var, dir);
	return 0;
 _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;
}

/* 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(snd_pcm_t *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;
	}
	*val = snd_pcm_hw_param_get_max(params, var, dir);
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

int _snd_pcm_hw_param_set_minmax(snd_pcm_hw_params_t *params,
				 snd_pcm_hw_param_t var,
				 unsigned int min, int mindir,
				 unsigned int max, int maxdir)
{
	int changed, c1, c2;
	int openmin = 0, openmax = 0;
	if (mindir) {
		if (mindir > 0) {
			openmin = 1;
		} else if (mindir < 0) {
			if (min > 0) {
				openmin = 1;
				min--;
			}
		}
	}
	if (maxdir) {
		if (maxdir < 0) {
			openmax = 1;
		} else if (maxdir > 0) {
			openmax = 1;
			max++;
		}
	}
	if (hw_is_mask(var)) {
		snd_mask_t *mask = hw_param_mask(params, var);
		if (max == 0 && openmax) {
			snd_mask_none(mask);
			changed = -EINVAL;
		} else {
			c1 = snd_mask_refine_min(mask, min + !!openmin);
			if (c1 < 0)
				changed = c1;
			else {
				c2 = snd_mask_refine_max(mask, max - !!openmax);
				if (c2 < 0)
					changed = c2;
				else
					changed = (c1 || c2);
			}
		}
	}
	else if (hw_is_interval(var)) {
		snd_interval_t *i = hw_param_interval(params, var);
		c1 = snd_interval_refine_min(i, min, openmin);
		if (c1 < 0)
			changed = c1;
		else {
			c2 = snd_interval_refine_max(i, max, openmax);
			if (c2 < 0)
				changed = c2;
			else
				changed = (c1 || c2);
		}
	} else {
		assert(0);
		return -EINVAL;
	}
	if (changed) {
		params->cmask |= 1 << var;
		params->rmask |= 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_param_set_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
				snd_set_mode_t mode,
				snd_pcm_hw_param_t var,
				unsigned int *min, int *mindir,
				unsigned int *max, int *maxdir)
{
	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_minmax(params, var, 
					   *min, mindir ? *mindir : 0,
					   *max, maxdir ? *maxdir : 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;
	}
	*min = snd_pcm_hw_param_get_min(params, var, mindir);
	*max = snd_pcm_hw_param_get_max(params, var, maxdir);
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

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

/* Inside configuration space defined by PARAMS remove from PAR all 
   values != VAL. Reduce configuration space accordingly.
   Return -EINVAL if the configuration space is empty
*/
int snd_pcm_hw_param_set(snd_pcm_t *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(params, var, val, dir);
	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;
	}
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

int _snd_pcm_hw_param_set_mask(snd_pcm_hw_params_t *params,
			       snd_pcm_hw_param_t var, const snd_mask_t *val)
{
	int changed;
	assert(hw_is_mask(var));
	changed = snd_mask_refine(hw_param_mask(params, var), val);
	if (changed) {
		params->cmask |= 1 << var;
		params->rmask |= 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_param_set_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
			      snd_set_mode_t mode,
			      snd_pcm_hw_param_t var, const snd_mask_t *val)
{
	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_mask(params, var, val);
	if (err < 0)
		goto _fail;
	if (mode != SND_TEST && params->rmask) {
		err = snd_pcm_hw_refine(pcm, params);
		if (err < 0)
			goto _fail;
	}
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

/* 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(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
			      snd_pcm_hw_param_t var, unsigned int best,
			      int *dir, unsigned int *val)
{
	snd_pcm_hw_params_t save;
	int err;
	unsigned int saved_min;
	int last = 0;
	int min, max;
	int mindir, maxdir;
	int valdir = dir ? *dir : 0;
	/* 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, &min, &mindir);
	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, &params1, SND_CHANGE, var, &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, &max, &maxdir);
		if (err < 0)
			return err;
		last = 1;
	}
 _end:
	if (last)
		err = snd_pcm_hw_param_set_last(pcm, params, var, dir, val);
	else
		err = snd_pcm_hw_param_set_first(pcm, params, var, dir, val);
	return err;
}

#if 0
/* Inside configuration space defined by PARAMS set PAR to the available value
   nearest to BEST after VAL (on equal difference values less than BEST 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_param_set_next(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
			      snd_pcm_hw_param_t var, 
			      unsigned int best, int bestdir,
			      unsigned int val, int *dir)
{
	snd_pcm_hw_params_t save;
	int v, err;
	int last = 0;
	int min, max;
	int mindir, maxdir;
	int diff, diffdir;
	int valdir = dir ? *dir : 0;
	/* FIXME */
	if (best > INT_MAX)
		best = INT_MAX;
	boundary_sub(val, valdir, best, bestdir, &diff, &diffdir);
	if (diff < 0 || (diff == 0 && diffdir < 0)) {
		min = best - diff;
		mindir = bestdir - diffdir;
		max = val;
		maxdir = bestdir - 1;
	} else {
		min = val;
		mindir = bestdir + 1;
		max = best + diff;
		maxdir = bestdir + diffdir + 1;
	}
	min += mindir / 2;
	mindir %= 2;
	max += maxdir / 2;
	maxdir %= 2;
	save = *params;
	if (min >= 0 &&
	    (err = snd_pcm_hw_param_set_min(pcm, params, SND_CHANGE, var, &min, &mindir)) >= 0) {
		snd_pcm_hw_params_t params1;
		if (max < 0)
			goto _end;
		params1 = save;
		err = snd_pcm_hw_param_set_max(pcm, &params1, SND_CHANGE, var, &max, &maxdir);
		if (err < 0)
			goto _end;
		if (boundary_nearer(max, maxdir, best, bestdir, min, mindir)) {
			*params = params1;
			last = 1;
		}
	} else {
		if (max < 0)
			return -EINVAL;
		*params = save;
		err = snd_pcm_hw_param_set_max(pcm, params, SND_CHANGE, var, &max, &maxdir);
		if (err < 0)
			return max;
		last = 1;
	}
 _end:
	if (last)
		v = snd_pcm_hw_param_set_last(pcm, params, var, dir);
	else
		v = snd_pcm_hw_param_set_first(pcm, params, var, dir);
	assert(v >= 0);
	return v;
}
#endif

static int snd_pcm_hw_param_set_near_minmax(snd_pcm_t *pcm,
					    snd_pcm_hw_params_t *params,
					    snd_pcm_hw_param_t var,
					    unsigned int min, int *mindir,
					    unsigned int max, int *maxdir)
{
	snd_pcm_hw_params_t tmp;
	int err;
	if (!boundary_lt(min, *mindir, max, *maxdir))
		return snd_pcm_hw_param_set_near(pcm, params, var, min, mindir, NULL);
	tmp = *params;
	err = snd_pcm_hw_param_set_near(pcm, &tmp, var, min, mindir, &min);
	if (err < 0)
		return err;
	if (boundary_lt(min, *mindir, max, *maxdir)) {
		tmp = *params;
		err = snd_pcm_hw_param_set_near(pcm, &tmp, var, max, maxdir, &max);
	} else {
		max = min;
		*maxdir = *mindir;
	}
	err = snd_pcm_hw_param_set_minmax(pcm, params, SND_CHANGE, var, &min, mindir,
					  &max, maxdir);
	assert(err >= 0);
	return 0;
}

void snd_pcm_hw_param_refine_near(snd_pcm_t *pcm,
				  snd_pcm_hw_params_t *params,
				  snd_pcm_hw_param_t var,
				  const snd_pcm_hw_params_t *src)
{
	unsigned int min, max;
	int mindir, maxdir;
	min = snd_pcm_hw_param_get_min(src, var, &mindir);
	max = snd_pcm_hw_param_get_max(src, var, &maxdir);
	snd_pcm_hw_param_set_near_minmax(pcm, params, var,
					 min, &mindir, max, &maxdir);
}

/* ---- end of refinement functions ---- */

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_pcm_hw_param_always_eq(const snd_pcm_hw_params_t *params,
			       snd_pcm_hw_param_t var,
			       const snd_pcm_hw_params_t *params1)
{
	if (hw_is_mask(var))
		return snd_mask_always_eq(hw_param_mask_c(params, var),
				      hw_param_mask_c(params1, var));
	if (hw_is_interval(var))
		return snd_interval_always_eq(hw_param_interval_c(params, var),
					  hw_param_interval_c(params1, var));
	assert(0);
	return -EINVAL;
}

int snd_pcm_hw_param_never_eq(const snd_pcm_hw_params_t *params,
			      snd_pcm_hw_param_t var,
			      const snd_pcm_hw_params_t *params1)
{
	if (hw_is_mask(var))
		return snd_mask_never_eq(hw_param_mask_c(params, var),
				      hw_param_mask_c(params1, var));
	if (hw_is_interval(var))
		return snd_interval_never_eq(hw_param_interval_c(params, var),
					 hw_param_interval_c(params1, var));
	assert(0);
	return -EINVAL;
}


/* Choose one configuration from configuration space defined by PARAMS
   The configuration chosen is that obtained fixing in this order:
   first access
   first format
   first subformat
   min channels
   min rate
   min period time
   max buffer size
   min tick time
*/
static int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
	int err;

	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_ACCESS, 0, NULL);
	if (err < 0)
		return err;
	snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_FORMAT, 0, NULL);
	if (err < 0)
		return err;
	snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, 0, NULL);
	if (err < 0)
		return err;
	snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_CHANNELS, 0, NULL);
	if (err < 0)
		return err;
	snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_RATE, 0, NULL);
	if (err < 0)
		return err;
	snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_TIME, 0, NULL);
	if (err < 0)
		return err;
	snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_SIZE, 0, NULL);
	if (err < 0)
		return err;
	snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_BUFFER_SIZE, 0, NULL);
	if (err < 0)
		return err;
	snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_TICK_TIME, 0, NULL);
	if (err < 0)
		return err;
	return 0;
}

#if 0
static unsigned int snd_pcm_hw_param_count(const snd_pcm_hw_params_t *params,
					   snd_pcm_hw_param_t var)
{
	if (hw_is_mask(var)) {
		const snd_mask_t *mask = hw_param_mask_c(params, var);
		return snd_mask_count(mask);
	}
	if (hw_is_interval(var)) {<commit_msg>added missing checks of return values.<commit_after>/*
 *  PCM - Params functions
 *  Copyright (c) 2000 by <NAME> <<EMAIL>>
 *
 *
 *   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 2.1 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 Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 */
  
#include "pcm_local.h"

static inline int hw_is_mask(snd_pcm_hw_param_t var)
{
#if SND_PCM_HW_PARAM_FIRST_MASK == 0
	return var <= SND_PCM_HW_PARAM_LAST_MASK;
#else
	return var >= SND_PCM_HW_PARAM_FIRST_MASK &&
		var <= SND_PCM_HW_PARAM_LAST_MASK;
#endif
}

static inline int hw_is_interval(snd_pcm_hw_param_t var)
{
	return var >= SND_PCM_HW_PARAM_FIRST_INTERVAL &&
		var <= SND_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)
{
	assert(hw_is_mask(var));
	return (snd_mask_t*)&params->masks[var - SND_PCM_HW_PARAM_FIRST_MASK];
}

static inline snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params,
						snd_pcm_hw_param_t var)
{
	assert(hw_is_interval(var));
	return &params->intervals[var - SND_PCM_HW_PARAM_FIRST_INTERVAL];
}

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);
}

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);
}

static 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;
	}
	assert(0);
}

int snd_pcm_hw_param_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
			 snd_pcm_hw_param_t var)
{
	_snd_pcm_hw_param_any(params, var);
	return snd_pcm_hw_refine(pcm, params);
}

void _snd_pcm_hw_params_any(snd_pcm_hw_params_t *params)
{
	unsigned int k;
	memset(params, 0, sizeof(*params));
	for (k = 0; k <= SND_PCM_HW_PARAM_LAST; k++)
		_snd_pcm_hw_param_any(params, k);
	params->info = ~0U;
}

/* 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, 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;
		return snd_mask_value(mask);
	}
	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;
		return snd_interval_value(i);
	}
	assert(0);
	return -EINVAL;
}

/* Return the minimum value for field PAR. */
unsigned int snd_pcm_hw_param_get_min(const snd_pcm_hw_params_t *params,
				      snd_pcm_hw_param_t var, int *dir)
{
	if (hw_is_mask(var)) {
		if (dir)
			*dir = 0;
		return snd_mask_min(hw_param_mask_c(params, var));
	}
	if (hw_is_interval(var)) {
		const snd_interval_t *i = hw_param_interval_c(params, var);
		if (dir)
			*dir = i->openmin;
		return snd_interval_min(i);
	}
	assert(0);
	return 0;
}

/* Return the maximum value for field PAR. */
unsigned int snd_pcm_hw_param_get_max(const snd_pcm_hw_params_t *params,
				      snd_pcm_hw_param_t var, int *dir)
{
	if (hw_is_mask(var)) {
		if (dir)
			*dir = 0;
		return snd_mask_max(hw_param_mask_c(params, var));
	}
	if (hw_is_interval(var)) {
		const snd_interval_t *i = hw_param_interval_c(params, var);
		if (dir)
			*dir = - (int) i->openmax;
		return snd_interval_max(i);
	}
	assert(0);
	return 0;
}

/* 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 snd_mask_t *snd_pcm_hw_param_get_mask(const snd_pcm_hw_params_t *params,
					   snd_pcm_hw_param_t var)
{
	assert(hw_is_mask(var));
	return hw_param_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 snd_interval_t *snd_pcm_hw_param_get_interval(const snd_pcm_hw_params_t *params,
						  snd_pcm_hw_param_t var)
{
	assert(hw_is_interval(var));
	return hw_param_interval_c(params, var);
}

/* --- Refinement functions --- */

int _snd_pcm_hw_param_set_interval(snd_pcm_hw_params_t *params,
				   snd_pcm_hw_param_t var,
				   const snd_interval_t *val)
{
	int changed;
	assert(hw_is_interval(var));
	changed = snd_interval_refine(hw_param_interval(params, var), val);
	if (changed) {
		params->cmask |= 1 << var;
		params->rmask |= 1 << var;
	}
	return changed;
}

void _snd_pcm_hw_param_set_empty(snd_pcm_hw_params_t *params,
				 snd_pcm_hw_param_t var)
{
	if (hw_is_mask(var)) {
	snd_mask_none(hw_param_mask(params, var));
		params->cmask |= 1 << var;
		params->rmask |= 1 << var;
	} else if (hw_is_interval(var)) {
		snd_interval_none(hw_param_interval(params, var));
		params->cmask |= 1 << var;
		params->rmask |= 1 << var;
	} else {
		assert(0);
	}
}

static int _snd_pcm_hw_param_set_integer(snd_pcm_hw_params_t *params,
					 snd_pcm_hw_param_t var)
{
	int changed;
	assert(hw_is_interval(var));
	changed = snd_interval_setinteger(hw_param_interval(params, var));
	if (changed) {
		params->cmask |= 1 << var;
		params->rmask |= 1 << var;
	}
	return changed;
}
	
/* Inside configuration space defined by PARAMS remove from PAR all 
   non integer values. Reduce configuration space accordingly.
   Return -EINVAL if the configuration space is empty
*/
int snd_pcm_hw_param_set_integer(snd_pcm_t *pcm, 
				 snd_pcm_hw_params_t *params,
				 snd_set_mode_t mode,
				 snd_pcm_hw_param_t var)
{
	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_integer(params, var);
	if (err < 0)
		goto _fail;
	if (params->rmask) {
		err = snd_pcm_hw_refine(pcm, params);
		if (err < 0)
			goto _fail;
	}
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

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) {
		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(snd_pcm_t *pcm, 
			       snd_pcm_hw_params_t *params, 
			       snd_pcm_hw_param_t var,
			       int *dir, unsigned int *rval)
{
	int err;
	unsigned int val;

	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;
	}
	val = snd_pcm_hw_param_get(params, var, dir);
	if (rval)
		*rval = val;
	return 0;
}

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) {
		params->cmask |= 1 << var;
		params->rmask |= 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_param_set_last(snd_pcm_t *pcm, 
			      snd_pcm_hw_params_t *params,
			      snd_pcm_hw_param_t var,
			      int *dir, unsigned int *rval)
{
	int err;
	unsigned int val;

	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;
	}
	val = snd_pcm_hw_param_get(params, var, dir);
	if (rval)
		*rval = val;
	return 0;
}

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

/* 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(snd_pcm_t *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;
	}
	*val = snd_pcm_hw_param_get_min(params, var, dir);
	return 0;
 _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;
}

/* 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(snd_pcm_t *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;
	}
	*val = snd_pcm_hw_param_get_max(params, var, dir);
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

int _snd_pcm_hw_param_set_minmax(snd_pcm_hw_params_t *params,
				 snd_pcm_hw_param_t var,
				 unsigned int min, int mindir,
				 unsigned int max, int maxdir)
{
	int changed, c1, c2;
	int openmin = 0, openmax = 0;
	if (mindir) {
		if (mindir > 0) {
			openmin = 1;
		} else if (mindir < 0) {
			if (min > 0) {
				openmin = 1;
				min--;
			}
		}
	}
	if (maxdir) {
		if (maxdir < 0) {
			openmax = 1;
		} else if (maxdir > 0) {
			openmax = 1;
			max++;
		}
	}
	if (hw_is_mask(var)) {
		snd_mask_t *mask = hw_param_mask(params, var);
		if (max == 0 && openmax) {
			snd_mask_none(mask);
			changed = -EINVAL;
		} else {
			c1 = snd_mask_refine_min(mask, min + !!openmin);
			if (c1 < 0)
				changed = c1;
			else {
				c2 = snd_mask_refine_max(mask, max - !!openmax);
				if (c2 < 0)
					changed = c2;
				else
					changed = (c1 || c2);
			}
		}
	}
	else if (hw_is_interval(var)) {
		snd_interval_t *i = hw_param_interval(params, var);
		c1 = snd_interval_refine_min(i, min, openmin);
		if (c1 < 0)
			changed = c1;
		else {
			c2 = snd_interval_refine_max(i, max, openmax);
			if (c2 < 0)
				changed = c2;
			else
				changed = (c1 || c2);
		}
	} else {
		assert(0);
		return -EINVAL;
	}
	if (changed) {
		params->cmask |= 1 << var;
		params->rmask |= 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_param_set_minmax(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
				snd_set_mode_t mode,
				snd_pcm_hw_param_t var,
				unsigned int *min, int *mindir,
				unsigned int *max, int *maxdir)
{
	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_minmax(params, var, 
					   *min, mindir ? *mindir : 0,
					   *max, maxdir ? *maxdir : 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;
	}
	*min = snd_pcm_hw_param_get_min(params, var, mindir);
	*max = snd_pcm_hw_param_get_max(params, var, maxdir);
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

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

/* Inside configuration space defined by PARAMS remove from PAR all 
   values != VAL. Reduce configuration space accordingly.
   Return -EINVAL if the configuration space is empty
*/
int snd_pcm_hw_param_set(snd_pcm_t *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(params, var, val, dir);
	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;
	}
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

int _snd_pcm_hw_param_set_mask(snd_pcm_hw_params_t *params,
			       snd_pcm_hw_param_t var, const snd_mask_t *val)
{
	int changed;
	assert(hw_is_mask(var));
	changed = snd_mask_refine(hw_param_mask(params, var), val);
	if (changed) {
		params->cmask |= 1 << var;
		params->rmask |= 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_param_set_mask(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
			      snd_set_mode_t mode,
			      snd_pcm_hw_param_t var, const snd_mask_t *val)
{
	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_mask(params, var, val);
	if (err < 0)
		goto _fail;
	if (mode != SND_TEST && params->rmask) {
		err = snd_pcm_hw_refine(pcm, params);
		if (err < 0)
			goto _fail;
	}
	return 0;
 _fail:
	if (mode == SND_TRY)
		*params = save;
	return err;
}

/* 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(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
			      snd_pcm_hw_param_t var, unsigned int best,
			      int *dir, unsigned int *val)
{
	snd_pcm_hw_params_t save;
	int err;
	unsigned int saved_min;
	int last = 0;
	int min, max;
	int mindir, maxdir;
	int valdir = dir ? *dir : 0;
	/* 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, &min, &mindir);
	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, &params1, SND_CHANGE, var, &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, &max, &maxdir);
		if (err < 0)
			return err;
		last = 1;
	}
 _end:
	if (last)
		err = snd_pcm_hw_param_set_last(pcm, params, var, dir, val);
	else
		err = snd_pcm_hw_param_set_first(pcm, params, var, dir, val);
	return err;
}

#if 0
/* Inside configuration space defined by PARAMS set PAR to the available value
   nearest to BEST after VAL (on equal difference values less than BEST 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_param_set_next(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
			      snd_pcm_hw_param_t var, 
			      unsigned int best, int bestdir,
			      unsigned int val, int *dir)
{
	snd_pcm_hw_params_t save;
	int v, err;
	int last = 0;
	int min, max;
	int mindir, maxdir;
	int diff, diffdir;
	int valdir = dir ? *dir : 0;
	/* FIXME */
	if (best > INT_MAX)
		best = INT_MAX;
	boundary_sub(val, valdir, best, bestdir, &diff, &diffdir);
	if (diff < 0 || (diff == 0 && diffdir < 0)) {
		min = best - diff;
		mindir = bestdir - diffdir;
		max = val;
		maxdir = bestdir - 1;
	} else {
		min = val;
		mindir = bestdir + 1;
		max = best + diff;
		maxdir = bestdir + diffdir + 1;
	}
	min += mindir / 2;
	mindir %= 2;
	max += maxdir / 2;
	maxdir %= 2;
	save = *params;
	if (min >= 0 &&
	    (err = snd_pcm_hw_param_set_min(pcm, params, SND_CHANGE, var, &min, &mindir)) >= 0) {
		snd_pcm_hw_params_t params1;
		if (max < 0)
			goto _end;
		params1 = save;
		err = snd_pcm_hw_param_set_max(pcm, &params1, SND_CHANGE, var, &max, &maxdir);
		if (err < 0)
			goto _end;
		if (boundary_nearer(max, maxdir, best, bestdir, min, mindir)) {
			*params = params1;
			last = 1;
		}
	} else {
		if (max < 0)
			return -EINVAL;
		*params = save;
		err = snd_pcm_hw_param_set_max(pcm, params, SND_CHANGE, var, &max, &maxdir);
		if (err < 0)
			return max;
		last = 1;
	}
 _end:
	if (last)
		v = snd_pcm_hw_param_set_last(pcm, params, var, dir);
	else
		v = snd_pcm_hw_param_set_first(pcm, params, var, dir);
	assert(v >= 0);
	return v;
}
#endif

static int snd_pcm_hw_param_set_near_minmax(snd_pcm_t *pcm,
					    snd_pcm_hw_params_t *params,
					    snd_pcm_hw_param_t var,
					    unsigned int min, int *mindir,
					    unsigned int max, int *maxdir)
{
	snd_pcm_hw_params_t tmp;
	int err;
	if (!boundary_lt(min, *mindir, max, *maxdir))
		return snd_pcm_hw_param_set_near(pcm, params, var, min, mindir, NULL);
	tmp = *params;
	err = snd_pcm_hw_param_set_near(pcm, &tmp, var, min, mindir, &min);
	if (err < 0)
		return err;
	if (boundary_lt(min, *mindir, max, *maxdir)) {
		tmp = *params;
		err = snd_pcm_hw_param_set_near(pcm, &tmp, var, max, maxdir, &max);
	} else {
		max = min;
		*maxdir = *mindir;
	}
	err = snd_pcm_hw_param_set_minmax(pcm, params, SND_CHANGE, var, &min, mindir,
					  &max, maxdir);
	assert(err >= 0);
	return 0;
}

void snd_pcm_hw_param_refine_near(snd_pcm_t *pcm,
				  snd_pcm_hw_params_t *params,
				  snd_pcm_hw_param_t var,
				  const snd_pcm_hw_params_t *src)
{
	unsigned int min, max;
	int mindir, maxdir;
	min = snd_pcm_hw_param_get_min(src, var, &mindir);
	max = snd_pcm_hw_param_get_max(src, var, &maxdir);
	snd_pcm_hw_param_set_near_minmax(pcm, params, var,
					 min, &mindir, max, &maxdir);
}

/* ---- end of refinement functions ---- */

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_pcm_hw_param_always_eq(const snd_pcm_hw_params_t *params,
			       snd_pcm_hw_param_t var,
			       const snd_pcm_hw_params_t *params1)
{
	if (hw_is_mask(var))
		return snd_mask_always_eq(hw_param_mask_c(params, var),
				      hw_param_mask_c(params1, var));
	if (hw_is_interval(var))
		return snd_interval_always_eq(hw_param_interval_c(params, var),
					  hw_param_interval_c(params1, var));
	assert(0);
	return -EINVAL;
}

int snd_pcm_hw_param_never_eq(const snd_pcm_hw_params_t *params,
			      snd_pcm_hw_param_t var,
			      const snd_pcm_hw_params_t *params1)
{
	if (hw_is_mask(var))
		return snd_mask_never_eq(hw_param_mask_c(params, var),
				      hw_param_mask_c(params1, var));
	if (hw_is_interval(var))
		return snd_interval_never_eq(hw_param_interval_c(params, var),
					 hw_param_interval_c(params1, var));
	assert(0);
	return -EINVAL;
}


/* Choose one configuration from configuration space defined by PARAMS
   The configuration chosen is that obtained fixing in this order:
   first access
   first format
   first subformat
   min channels
   min rate
   min period time
   max buffer size
   min tick time
*/
static int snd_pcm_hw_params_choose(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
	int err;

	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_ACCESS, 0, NULL);
	if (err < 0)
		return err;
	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_FORMAT, 0, NULL);
	if (err < 0)
		return err;
	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, 0, NULL);
	if (err < 0)
		return err;
	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_CHANNELS, 0, NULL);
	if (err < 0)
		return err;
	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_RATE, 0, NULL);
	if (err < 0)
		return err;
	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_TIME, 0, NULL);
	if (err < 0)
		return err;
	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_SIZE, 0, NULL);
	if (err < 0)
		return err;
	err = snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_BUFFER_SIZE, 0, NULL);
	if (err < 0)
		return err;
	err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_TICK_TIME, 0, NULL);
	if (err < 0)
		return err;
	return 0;
}

#if 0
static unsigned int snd_pcm_hw_param_count(const snd_pcm_hw_params_t *params,
					   snd_pcm_hw_param_t var)
{
	if (hw_is_mask(var)) {
		const snd_mask_t *mask = hw_param_mask_c(params, var);
		return snd_mask_count(mask);
	}
	if (hw_is_interval(var)) {
{'fraction_non_alphanumeric': 0.1135685448481657, 'fraction_numerical': 0.00928363856220623, 'mean_word_length': 3.841280332357662, 'pattern_counts': {'":': 0, '<': 221, '<?xml version=': 0, '>': 129, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3669660', 'n_tokens_mistral': 22160, 'n_tokens_neox': 20459, 'n_words': 5041}