| /* | |
| * PCM - Params functions | |
| * Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org> | |
| * | |
| * | |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| * | |
| */ | |
| #include "pcm_local.h" | |
| #ifndef NDEBUG | |
| /* | |
| * dump hw_params when $LIBASOUND_DEBUG is set to >= 1 | |
| */ | |
| static void dump_hw_params(snd_pcm_hw_params_t *params, const char *type, | |
| snd_pcm_hw_param_t var, unsigned int val, int err) | |
| { | |
| const char *verbose = getenv("LIBASOUND_DEBUG"); | |
| snd_output_t *out; | |
| if (! verbose || ! *verbose || atoi(verbose) < 1) | |
| return; | |
| if (snd_output_stdio_attach(&out, stderr, 0) < 0) | |
| return; | |
| fprintf(stderr, "ALSA ERROR hw_params: %s (%s)\n", | |
| type, snd_pcm_hw_param_name(var)); | |
| fprintf(stderr, " value = "); | |
| switch (var) { | |
| case SND_PCM_HW_PARAM_ACCESS: | |
| fprintf(stderr, "%s", snd_pcm_access_name(val)); | |
| break; | |
| case SND_PCM_HW_PARAM_FORMAT: | |
| fprintf(stderr, "%s", snd_pcm_format_name(val)); | |
| break; | |
| case SND_PCM_HW_PARAM_SUBFORMAT: | |
| fprintf(stderr, "%s", snd_pcm_subformat_name(val)); | |
| break; | |
| default: | |
| fprintf(stderr, "%u", val); | |
| } | |
| fprintf(stderr, " : %s\n", snd_strerror(err)); | |
| snd_pcm_hw_params_dump(params, out); | |
| snd_output_close(out); | |
| } | |
| #else | |
| static inline void dump_hw_params(snd_pcm_hw_params_t *params, const char *type, | |
| snd_pcm_hw_param_t var, unsigned int val, int err) | |
| { | |
| } | |
| #endif | |
| 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; | |
| } | |
| #define hw_param_mask(params,var) \ | |
| &((params)->masks[(var) - SND_PCM_HW_PARAM_FIRST_MASK]) | |
| #define hw_param_interval(params,var) \ | |
| &((params)->intervals[(var) - SND_PCM_HW_PARAM_FIRST_INTERVAL]) | |
| #define hw_param_mask_c hw_param_mask | |
| #define hw_param_interval_c hw_param_interval | |
| 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 = SND_PCM_HW_PARAM_FIRST_MASK; k <= SND_PCM_HW_PARAM_LAST_MASK; k++) | |
| _snd_pcm_hw_param_any(params, k); | |
| for (k = SND_PCM_HW_PARAM_FIRST_INTERVAL; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; k++) | |
| _snd_pcm_hw_param_any(params, k); | |
| params->rmask = ~0U; | |
| params->cmask = 0; | |
| 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, | |
| 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; | |
| } | |
| /* 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; | |
| } | |
| /* 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; | |
| } | |
| /* 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 > 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(snd_pcm_t *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); | |
| } | |
| 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; | |
| } | |
| /* 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, | |
| 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); | |
| } | |
| 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; | |
| 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; | |
| if (err < 0 && mode == SND_TRY) | |
| dump_hw_params(params, "set_min", var, *val, err); | |
| 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; | |
| 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; | |
| if (err < 0 && mode == SND_TRY) | |
| dump_hw_params(params, "set_max", var, *val, err); | |
| 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; | |
| } | |
| err = snd_pcm_hw_param_get_min(params, var, min, mindir); | |
| if (err < 0) | |
| return err; | |
| return snd_pcm_hw_param_get_max(params, var, max, maxdir); | |
| _fail: | |
| if (mode == SND_TRY) | |
| *params = save; | |
| if (err < 0) | |
| dump_hw_params(params, "set_minmax", var, *min, err); | |
| 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; | |
| if (err < 0 && mode == SND_TRY) | |
| dump_hw_params(params, "set", var, val, err); | |
| 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 *val, int *dir) | |
| { | |
| snd_pcm_hw_params_t save; | |
| int err; | |
| unsigned int best = *val, saved_min; | |
| int last = 0; | |
| unsigned 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, &min, &mindir); | |
| i = hw_param_interval(params, var); | |
| if (!snd_interval_empty(i) && snd_interval_single(i)) { | |
| err = snd_pcm_hw_param_get_min(params, var, val, dir); | |
| if (err < 0) | |
| dump_hw_params(params, "set_near", var, *val, err); | |
| return err; | |
| } | |
| if (err >= 0) { | |
| snd_pcm_hw_params_t params1; | |
| if (min == saved_min && mindir == valdir) | |
| goto _end; | |
| params1 = save; | |
| err = snd_pcm_hw_param_set_max(pcm, ¶ms1, 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) { | |
| dump_hw_params(params, "set_near", var, *val, err); | |
| 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); | |
| if (err < 0) | |
| dump_hw_params(params, "set_near", var, *val, err); | |
| 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, ¶ms1, 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); | |
| tmp = *params; | |
| err = snd_pcm_hw_param_set_near(pcm, &tmp, var, &min, mindir); | |
| 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); | |
| } else { | |
| max = min; | |
| *maxdir = *mindir; | |
| } | |
| err = snd_pcm_hw_param_set_minmax(pcm, params, SND_CHANGE, var, &min, mindir, | |
| &max, maxdir); | |
| if (err < 0) | |
| return err; | |
| return 0; | |
| } | |
| int 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, err; | |
| if ((err = snd_pcm_hw_param_get_min(src, var, &min, &mindir)) < 0) | |
| return err; | |
| if ((err = snd_pcm_hw_param_get_max(src, var, &max, &maxdir)) < 0) | |
| return err; | |
| if ((err = snd_pcm_hw_param_set_near_minmax(pcm, params, var, | |
| min, &mindir, max, &maxdir)) < 0) | |
| return err; | |
| return 0; | |
| } | |
| int snd_pcm_hw_param_refine_multiple(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_param_t var, | |
| const snd_pcm_hw_params_t *src) | |
| { | |
| const snd_interval_t *it = hw_param_interval_c(src, var); | |
| const snd_interval_t *st = hw_param_interval_c(params, var); | |
| if (snd_interval_single(it)) { | |
| unsigned int best = snd_interval_min(it), cur, prev; | |
| cur = best; | |
| for (;;) { | |
| if (st->max < cur || (st->max == cur && st->openmax)) | |
| break; | |
| if (it->min <= cur && ! (it->min == cur && st->openmin)) { | |
| if (! snd_pcm_hw_param_set(pcm, params, SND_TRY, var, cur, 0)) | |
| return 0; /* ok */ | |
| } | |
| prev = cur; | |
| cur += best; | |
| if (cur <= prev) | |
| break; | |
| } | |
| } | |
| return snd_pcm_hw_param_refine_near(pcm, params, var, src); | |
| } | |
| /* ---- 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; | |
| } | |
| #if 0 | |
| #define CHOOSE_DEBUG | |
| #endif | |
| /* 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; | |
| #ifdef CHOOSE_DEBUG | |
| snd_output_t *log; | |
| snd_output_stdio_attach(&log, stderr, 0); | |
| snd_output_printf(log, "CHOOSE called:\n"); | |
| snd_pcm_hw_params_dump(params, log); | |
| #endif | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_ACCESS, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_FORMAT, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_SUBFORMAT, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_CHANNELS, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_RATE, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| if (pcm->minperiodtime > 0) { | |
| unsigned int min, max; | |
| int dir = 1; | |
| err = snd_pcm_hw_param_get_min(params, SND_PCM_HW_PARAM_PERIOD_TIME, &min, &dir); | |
| if (err >= 0) | |
| err = snd_pcm_hw_param_get_max(params, SND_PCM_HW_PARAM_PERIOD_TIME, &max, &dir); | |
| if (err >= 0 && (long)min < pcm->minperiodtime && | |
| (long)max > pcm->minperiodtime) { | |
| min = pcm->minperiodtime; dir = 1; | |
| snd_pcm_hw_param_set_min(pcm, params, SND_CHANGE, SND_PCM_HW_PARAM_PERIOD_TIME, &min, &dir); | |
| } | |
| } | |
| if (pcm->compat) { | |
| /* old mode */ | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_TIME, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_SIZE, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_BUFFER_SIZE, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| } else { | |
| /* determine buffer size first */ | |
| err = snd_pcm_hw_param_set_last(pcm, params, SND_PCM_HW_PARAM_BUFFER_SIZE, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_SIZE, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_PERIOD_TIME, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| } | |
| err = snd_pcm_hw_param_set_first(pcm, params, SND_PCM_HW_PARAM_TICK_TIME, NULL, 0); | |
| if (err < 0) | |
| return err; | |
| #ifdef CHOOSE_DEBUG | |
| snd_output_printf(log, "choose done\n"); | |
| snd_pcm_hw_params_dump(params, log); | |
| snd_output_close(log); | |
| #endif | |
| 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)) { | |
| const snd_interval_t *i = hw_param_interval_c(params, var); | |
| return snd_interval_max(i) - snd_interval_min(i) + 1; | |
| } | |
| assert(0); | |
| return 0; | |
| } | |
| #endif | |
| int _snd_pcm_hw_param_refine(snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_param_t var, | |
| const snd_pcm_hw_params_t *src) | |
| { | |
| int changed = 0; | |
| if (hw_is_mask(var)) { | |
| snd_mask_t *d = hw_param_mask(params, var); | |
| const snd_mask_t *s = hw_param_mask_c(src, var); | |
| changed = snd_mask_refine(d, s); | |
| } else if (hw_is_interval(var)) { | |
| snd_interval_t *d = hw_param_interval(params, var); | |
| const snd_interval_t *s = hw_param_interval_c(src, var); | |
| changed = snd_interval_refine(d, s); | |
| } else | |
| return 0; /* NOP / reserved */ | |
| if (changed) { | |
| params->cmask |= 1 << var; | |
| params->rmask |= 1 << var; | |
| } | |
| return changed; | |
| } | |
| #if 0 | |
| static void _snd_pcm_hw_param_copy(snd_pcm_hw_params_t *params, snd_pcm_hw_param_t var, | |
| const snd_pcm_hw_params_t *src) | |
| { | |
| if (hw_is_mask(var)) { | |
| snd_mask_t *d = hw_param_mask(params, var); | |
| const snd_mask_t *s = hw_param_mask_c(src, var); | |
| snd_mask_copy(d, s); | |
| params->cmask |= 1 << var; | |
| params->rmask |= 1 << var; | |
| return; | |
| } | |
| if (hw_is_interval(var)) { | |
| snd_interval_t *d = hw_param_interval(params, var); | |
| const snd_interval_t *s = hw_param_interval_c(src, var); | |
| snd_interval_copy(d, s); | |
| params->cmask |= 1 << var; | |
| params->rmask |= 1 << var; | |
| return; | |
| } | |
| assert(0); | |
| } | |
| #endif | |
| void snd_pcm_hw_param_dump(const snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_param_t var, snd_output_t *out) | |
| { | |
| if (hw_is_mask(var)) { | |
| const snd_mask_t *mask = hw_param_mask_c(params, var); | |
| if (snd_mask_empty(mask)) | |
| snd_output_puts(out, " NONE"); | |
| else if (snd_mask_full(mask)) | |
| snd_output_puts(out, " ALL"); | |
| else { | |
| unsigned int k; | |
| for (k = 0; k <= SND_MASK_MAX; ++k) { | |
| if (snd_mask_test(mask, k)) { | |
| const char *s; | |
| switch (var) { | |
| case SND_PCM_HW_PARAM_ACCESS: | |
| s = snd_pcm_access_name(k); | |
| break; | |
| case SND_PCM_HW_PARAM_FORMAT: | |
| s = snd_pcm_format_name(k); | |
| break; | |
| case SND_PCM_HW_PARAM_SUBFORMAT: | |
| s = snd_pcm_subformat_name(k); | |
| break; | |
| default: | |
| assert(0); | |
| s = NULL; | |
| } | |
| if (s) { | |
| snd_output_putc(out, ' '); | |
| snd_output_puts(out, s); | |
| } | |
| } | |
| } | |
| } | |
| return; | |
| } | |
| if (hw_is_interval(var)) { | |
| snd_interval_print(hw_param_interval_c(params, var), out); | |
| return; | |
| } | |
| assert(0); | |
| } | |
| #define HW_PARAM(v) [SND_PCM_HW_PARAM_##v] = #v | |
| static const char *const snd_pcm_hw_param_names[] = { | |
| HW_PARAM(ACCESS), | |
| HW_PARAM(FORMAT), | |
| HW_PARAM(SUBFORMAT), | |
| HW_PARAM(SAMPLE_BITS), | |
| HW_PARAM(FRAME_BITS), | |
| HW_PARAM(CHANNELS), | |
| HW_PARAM(RATE), | |
| HW_PARAM(PERIOD_TIME), | |
| HW_PARAM(PERIOD_SIZE), | |
| HW_PARAM(PERIOD_BYTES), | |
| HW_PARAM(PERIODS), | |
| HW_PARAM(BUFFER_TIME), | |
| HW_PARAM(BUFFER_SIZE), | |
| HW_PARAM(BUFFER_BYTES), | |
| HW_PARAM(TICK_TIME), | |
| }; | |
| const char *snd_pcm_hw_param_name(snd_pcm_hw_param_t param) | |
| { | |
| assert(param <= SND_PCM_HW_PARAM_LAST_INTERVAL); | |
| return snd_pcm_hw_param_names[param]; | |
| } | |
| #if 0 | |
| /* Strategies */ | |
| struct _snd_pcm_hw_strategy { | |
| unsigned int badness_min, badness_max; | |
| int (*choose_param)(const snd_pcm_hw_params_t *params, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_t *strategy); | |
| int (*next_value)(snd_pcm_hw_params_t *params, | |
| unsigned int param, | |
| int value, int *dir, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_t *strategy); | |
| int (*min_badness)(const snd_pcm_hw_params_t *params, | |
| unsigned int max_badness, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_t *strategy); | |
| void *private_data; | |
| void (*free)(snd_pcm_hw_strategy_t *strategy); | |
| }; | |
| /* Independent badness */ | |
| typedef struct _snd_pcm_hw_strategy_simple snd_pcm_hw_strategy_simple_t; | |
| struct _snd_pcm_hw_strategy_simple { | |
| int valid; | |
| unsigned int order; | |
| int (*next_value)(snd_pcm_hw_params_t *params, | |
| unsigned int param, | |
| int value, int *dir, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_simple_t *par); | |
| unsigned int (*min_badness)(const snd_pcm_hw_params_t *params, | |
| unsigned int param, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_simple_t *par); | |
| void *private_data; | |
| void (*free)(snd_pcm_hw_strategy_simple_t *strategy); | |
| }; | |
| typedef struct _snd_pcm_hw_strategy_simple_near { | |
| int best; | |
| unsigned int mul; | |
| } snd_pcm_hw_strategy_simple_near_t; | |
| typedef struct _snd_pcm_hw_strategy_simple_choices { | |
| unsigned int count; | |
| /* choices need to be sorted on ascending badness */ | |
| snd_pcm_hw_strategy_simple_choices_list_t *choices; | |
| } snd_pcm_hw_strategy_simple_choices_t; | |
| int snd_pcm_hw_params_strategy(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | |
| const snd_pcm_hw_strategy_t *strategy, | |
| unsigned int badness_min, | |
| unsigned int badness_max) | |
| { | |
| snd_pcm_hw_params_t best_params; | |
| int var; | |
| int value, dir; | |
| unsigned int best_badness; | |
| int badness = strategy->min_badness(params, badness_max, pcm, strategy); | |
| snd_pcm_hw_params_t params1; | |
| #if 0 | |
| printf("\nBadness: %d\n", badness); | |
| snd_pcm_hw_params_dump(params, stdout); | |
| #endif | |
| if (badness < 0) | |
| return badness; | |
| if ((unsigned int)badness > badness_min) | |
| badness_min = badness_min; | |
| var = strategy->choose_param(params, pcm, strategy); | |
| if (var < 0) | |
| return badness; | |
| best_badness = UINT_MAX; | |
| value = -1; | |
| while (1) { | |
| params1 = *params; | |
| value = strategy->next_value(¶ms1, var, value, &dir, pcm, strategy); | |
| if (value < 0) | |
| break; | |
| badness = snd_pcm_hw_params_strategy(pcm, ¶ms1, strategy, badness_min, badness_max); | |
| if (badness >= 0) { | |
| if ((unsigned int) badness <= badness_min) { | |
| *params = params1; | |
| return badness; | |
| } | |
| best_badness = badness; | |
| best_params = params1; | |
| badness_max = badness - 1; | |
| } | |
| } | |
| if (best_badness == UINT_MAX) { | |
| return -EINVAL; | |
| } | |
| *params = best_params; | |
| return best_badness; | |
| } | |
| void snd_pcm_hw_strategy_simple_free(snd_pcm_hw_strategy_t *strategy) | |
| { | |
| snd_pcm_hw_strategy_simple_t *pars = strategy->private_data; | |
| int k; | |
| for (k = 0; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; ++k) { | |
| if (pars[k].valid && pars[k].free) | |
| pars[k].free(&pars[k]); | |
| } | |
| free(pars); | |
| } | |
| int snd_pcm_hw_strategy_simple_choose_param(const snd_pcm_hw_params_t *params, | |
| snd_pcm_t *pcm ATTRIBUTE_UNUSED, | |
| const snd_pcm_hw_strategy_t *strategy) | |
| { | |
| snd_pcm_hw_param_t var; | |
| int best_var = -1; | |
| const snd_pcm_hw_strategy_simple_t *pars = strategy->private_data; | |
| unsigned int min_choices = UINT_MAX; | |
| unsigned int min_order = UINT_MAX; | |
| for (var = 0; var <= SND_PCM_HW_PARAM_LAST_INTERVAL; ++var) { | |
| const snd_pcm_hw_strategy_simple_t *p = &pars[var]; | |
| unsigned int choices; | |
| if (!p->valid) | |
| continue; | |
| choices = snd_pcm_hw_param_count(params, var); | |
| if (choices == 1) | |
| continue; | |
| assert(choices != 0); | |
| if (p->order < min_order || | |
| (p->order == min_order && | |
| choices < min_choices)) { | |
| min_order = p->order; | |
| min_choices = choices; | |
| best_var = var; | |
| } | |
| } | |
| return best_var; | |
| } | |
| int snd_pcm_hw_strategy_simple_next_value(snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_param_t var, | |
| int value, int *dir, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_t *strategy) | |
| { | |
| const snd_pcm_hw_strategy_simple_t *pars = strategy->private_data; | |
| assert(pars[var].valid); | |
| return pars[var].next_value(params, var, value, dir, pcm, &pars[var]); | |
| } | |
| int snd_pcm_hw_strategy_simple_min_badness(const snd_pcm_hw_params_t *params, | |
| unsigned int max_badness, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_t *strategy) | |
| { | |
| snd_pcm_hw_param_t var; | |
| unsigned int badness = 0; | |
| const snd_pcm_hw_strategy_simple_t *pars = strategy->private_data; | |
| for (var = 0; var <= SND_PCM_HW_PARAM_LAST_INTERVAL; ++var) { | |
| unsigned int b; | |
| if (!pars[var].valid) | |
| continue; | |
| b = pars[var].min_badness(params, var, pcm, &pars[var]); | |
| if (b > max_badness || max_badness - b < badness) | |
| return -E2BIG; | |
| badness += b; | |
| } | |
| return badness; | |
| } | |
| void snd_pcm_hw_strategy_simple_near_free(snd_pcm_hw_strategy_simple_t *par) | |
| { | |
| snd_pcm_hw_strategy_simple_near_t *p = par->private_data; | |
| free(p); | |
| } | |
| unsigned int snd_pcm_hw_strategy_simple_near_min_badness(const snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_param_t var, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_simple_t *par) | |
| { | |
| const snd_pcm_hw_strategy_simple_near_t *p = par->private_data; | |
| snd_pcm_hw_params_t params1 = *params; | |
| int value = snd_pcm_hw_param_set_near(pcm, ¶ms1, var, p->best, 0); | |
| int diff; | |
| assert(value >= 0); | |
| diff = p->best - value; | |
| if (diff < 0) | |
| diff = -diff; | |
| return diff * p->mul; | |
| } | |
| int snd_pcm_hw_strategy_simple_near_next_value(snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_param_t var, | |
| int value, int *dir, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_simple_t *par) | |
| { | |
| const snd_pcm_hw_strategy_simple_near_t *p = par->private_data; | |
| if (value < 0) { | |
| *dir = 0; | |
| return snd_pcm_hw_param_set_near(pcm, params, var, p->best, dir); | |
| } else | |
| return snd_pcm_hw_param_set_next(pcm, params, var, p->best, 0, value, dir); | |
| } | |
| void snd_pcm_hw_strategy_simple_choices_free(snd_pcm_hw_strategy_simple_t *par) | |
| { | |
| snd_pcm_hw_strategy_simple_choices_t *p = par->private_data; | |
| // free(p->choices); | |
| free(p); | |
| } | |
| unsigned int snd_pcm_hw_strategy_simple_choices_min_badness(const snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_param_t var, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_simple_t *par) | |
| { | |
| const snd_pcm_hw_strategy_simple_choices_t *p = par->private_data; | |
| unsigned int k; | |
| for (k = 0; k < p->count; ++k) { | |
| if (snd_pcm_hw_param_set(pcm, (snd_pcm_hw_params_t *) params, SND_TEST, var, p->choices[k].value, 0)) | |
| return p->choices[k].badness; | |
| } | |
| assert(0); | |
| return UINT_MAX; | |
| } | |
| int snd_pcm_hw_strategy_simple_choices_next_value(snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_param_t var, | |
| int value, int *dir, | |
| snd_pcm_t *pcm, | |
| const snd_pcm_hw_strategy_simple_t *par) | |
| { | |
| const snd_pcm_hw_strategy_simple_choices_t *p = par->private_data; | |
| unsigned int k = 0; | |
| if (value >= 0) { | |
| for (; k < p->count; ++k) { | |
| if (p->choices[k].value == (unsigned int) value) { | |
| k++; | |
| break; | |
| } | |
| } | |
| } | |
| for (; k < p->count; ++k) { | |
| unsigned int v = p->choices[k].value; | |
| int err = snd_pcm_hw_param_set(pcm, params, SND_TRY, var, v, 0); | |
| if (err < 0) | |
| continue; | |
| *dir = 0; | |
| return v; | |
| } | |
| return -1; | |
| } | |
| void snd_pcm_hw_strategy_free(snd_pcm_hw_strategy_t *strategy) | |
| { | |
| if (strategy->free) | |
| strategy->free(strategy); | |
| free(strategy); | |
| } | |
| int snd_pcm_hw_strategy_simple(snd_pcm_hw_strategy_t **strategyp, | |
| unsigned int badness_min, | |
| unsigned int badness_max) | |
| { | |
| snd_pcm_hw_strategy_simple_t *data; | |
| snd_pcm_hw_strategy_t *s; | |
| assert(strategyp); | |
| data = calloc(SND_PCM_HW_PARAM_LAST_INTERVAL + 1, sizeof(*data)); | |
| if (!data) | |
| return -ENOMEM; | |
| s = calloc(1, sizeof(*s)); | |
| if (!s) { | |
| free(data); | |
| return -ENOMEM; | |
| } | |
| s->choose_param = snd_pcm_hw_strategy_simple_choose_param; | |
| s->next_value = snd_pcm_hw_strategy_simple_next_value; | |
| s->min_badness = snd_pcm_hw_strategy_simple_min_badness; | |
| s->badness_min = badness_min; | |
| s->badness_max = badness_max; | |
| s->private_data = data; | |
| s->free = snd_pcm_hw_strategy_simple_free; | |
| *strategyp = s; | |
| return 0; | |
| } | |
| int snd_pcm_hw_strategy_simple_near(snd_pcm_hw_strategy_t *strategy, | |
| int order, | |
| snd_pcm_hw_param_t var, | |
| unsigned int best, | |
| unsigned int mul) | |
| { | |
| snd_pcm_hw_strategy_simple_t *s = strategy->private_data; | |
| snd_pcm_hw_strategy_simple_near_t *data; | |
| assert(strategy); | |
| assert(var <= SND_PCM_HW_PARAM_LAST_INTERVAL); | |
| assert(!s->valid); | |
| data = calloc(1, sizeof(*data)); | |
| if (!data) | |
| return -ENOMEM; | |
| data->best = best; | |
| data->mul = mul; | |
| s += var; | |
| s->order = order; | |
| s->valid = 1; | |
| s->next_value = snd_pcm_hw_strategy_simple_near_next_value; | |
| s->min_badness = snd_pcm_hw_strategy_simple_near_min_badness; | |
| s->private_data = data; | |
| s->free = snd_pcm_hw_strategy_simple_near_free; | |
| return 0; | |
| } | |
| int snd_pcm_hw_strategy_simple_choices(snd_pcm_hw_strategy_t *strategy, | |
| int order, | |
| snd_pcm_hw_param_t var, | |
| unsigned int count, | |
| snd_pcm_hw_strategy_simple_choices_list_t *choices) | |
| { | |
| snd_pcm_hw_strategy_simple_t *s = strategy->private_data; | |
| snd_pcm_hw_strategy_simple_choices_t *data; | |
| assert(strategy); | |
| assert(var <= SND_PCM_HW_PARAM_LAST_INTERVAL); | |
| assert(!s->valid); | |
| data = calloc(1, sizeof(*data)); | |
| if (!data) | |
| return -ENOMEM; | |
| data->count = count; | |
| data->choices = choices; | |
| s += var; | |
| s->valid = 1; | |
| s->order = order; | |
| s->next_value = snd_pcm_hw_strategy_simple_choices_next_value; | |
| s->min_badness = snd_pcm_hw_strategy_simple_choices_min_badness; | |
| s->private_data = data; | |
| s->free = snd_pcm_hw_strategy_simple_choices_free; | |
| return 0; | |
| } | |
| int snd_pcm_hw_params_try_explain_failure1(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *fail, | |
| snd_pcm_hw_params_t *success, | |
| unsigned int depth, | |
| snd_output_t *out) | |
| { | |
| snd_pcm_hw_param_t var; | |
| snd_pcm_hw_params_t i; | |
| if (depth < 1) | |
| return -ENOENT; | |
| for (var = 0; var <= SND_PCM_HW_PARAM_LAST_INTERVAL; var++) { | |
| int err; | |
| i = *success; | |
| _snd_pcm_hw_param_copy(&i, var, fail); | |
| err = snd_pcm_hw_refine(pcm, &i); | |
| if (err == 0 && | |
| snd_pcm_hw_params_try_explain_failure1(pcm, fail, &i, depth - 1, out) < 0) | |
| continue; | |
| snd_output_printf(out, "%s: ", snd_pcm_hw_param_name(var)); | |
| snd_pcm_hw_param_dump(fail, var, out); | |
| snd_output_putc(out, '\n'); | |
| return 0; | |
| } | |
| return -ENOENT; | |
| } | |
| int snd_pcm_hw_params_try_explain_failure(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *fail, | |
| snd_pcm_hw_params_t *success, | |
| unsigned int depth, | |
| snd_output_t *out) | |
| { | |
| snd_pcm_hw_params_t i, any; | |
| int err; | |
| snd_pcm_hw_param_t var; | |
| int done = 0; | |
| assert(pcm && fail); | |
| for (var = 0; var <= SND_PCM_HW_PARAM_LAST_INTERVAL; var++) { | |
| if (!snd_pcm_hw_param_empty(fail, var)) | |
| continue; | |
| snd_output_printf(out, "%s is empty\n", snd_pcm_hw_param_name(var)); | |
| done = 1; | |
| } | |
| if (done) | |
| return 0; | |
| i = *fail; | |
| err = snd_pcm_hw_refine(pcm, &i); | |
| if (err == 0) { | |
| snd_output_printf(out, "Configuration is virtually correct\n"); | |
| return 0; | |
| } | |
| if (!success) { | |
| snd_pcm_hw_params_any(pcm, &any); | |
| success = &any; | |
| } | |
| return snd_pcm_hw_params_try_explain_failure1(pcm, fail, success, depth, out); | |
| } | |
| #endif | |
| typedef struct _snd_pcm_hw_rule snd_pcm_hw_rule_t; | |
| typedef int (*snd_pcm_hw_rule_func_t)(snd_pcm_hw_params_t *params, | |
| const snd_pcm_hw_rule_t *rule); | |
| struct _snd_pcm_hw_rule { | |
| int var; | |
| snd_pcm_hw_rule_func_t func; | |
| int deps[4]; | |
| void *private_data; | |
| }; | |
| static int snd_pcm_hw_rule_mul(snd_pcm_hw_params_t *params, | |
| const snd_pcm_hw_rule_t *rule) | |
| { | |
| snd_interval_t t; | |
| snd_interval_mul(hw_param_interval_c(params, rule->deps[0]), | |
| hw_param_interval_c(params, rule->deps[1]), &t); | |
| return snd_interval_refine(hw_param_interval(params, rule->var), &t); | |
| } | |
| static int snd_pcm_hw_rule_div(snd_pcm_hw_params_t *params, | |
| const snd_pcm_hw_rule_t *rule) | |
| { | |
| snd_interval_t t; | |
| snd_interval_div(hw_param_interval_c(params, rule->deps[0]), | |
| hw_param_interval_c(params, rule->deps[1]), &t); | |
| return snd_interval_refine(hw_param_interval(params, rule->var), &t); | |
| } | |
| static int snd_pcm_hw_rule_muldivk(snd_pcm_hw_params_t *params, | |
| const snd_pcm_hw_rule_t *rule) | |
| { | |
| snd_interval_t t; | |
| snd_interval_muldivk(hw_param_interval_c(params, rule->deps[0]), | |
| hw_param_interval_c(params, rule->deps[1]), | |
| (unsigned long) rule->private_data, &t); | |
| return snd_interval_refine(hw_param_interval(params, rule->var), &t); | |
| } | |
| static int snd_pcm_hw_rule_mulkdiv(snd_pcm_hw_params_t *params, | |
| const snd_pcm_hw_rule_t *rule) | |
| { | |
| snd_interval_t t; | |
| snd_interval_mulkdiv(hw_param_interval_c(params, rule->deps[0]), | |
| (unsigned long) rule->private_data, | |
| hw_param_interval_c(params, rule->deps[1]), &t); | |
| return snd_interval_refine(hw_param_interval(params, rule->var), &t); | |
| } | |
| static int snd_pcm_hw_rule_format(snd_pcm_hw_params_t *params, | |
| const snd_pcm_hw_rule_t *rule) | |
| { | |
| int changed = 0; | |
| snd_pcm_format_t k; | |
| snd_mask_t *mask = hw_param_mask(params, rule->var); | |
| snd_interval_t *i = hw_param_interval(params, rule->deps[0]); | |
| for (k = 0; k <= SND_PCM_FORMAT_LAST; k++) { | |
| int bits; | |
| if (!snd_pcm_format_mask_test(mask, k)) | |
| continue; | |
| bits = snd_pcm_format_physical_width(k); | |
| if (bits < 0) | |
| continue; | |
| if (!snd_interval_test(i, (unsigned int) bits)) { | |
| snd_pcm_format_mask_reset(mask, k); | |
| if (snd_mask_empty(mask)) | |
| return -EINVAL; | |
| changed = 1; | |
| } | |
| } | |
| return changed; | |
| } | |
| static int snd_pcm_hw_rule_sample_bits(snd_pcm_hw_params_t *params, | |
| const snd_pcm_hw_rule_t *rule) | |
| { | |
| unsigned int min, max; | |
| snd_pcm_format_t k; | |
| snd_interval_t *i = hw_param_interval(params, rule->var); | |
| snd_mask_t *mask = hw_param_mask(params, rule->deps[0]); | |
| int c, changed = 0; | |
| min = UINT_MAX; | |
| max = 0; | |
| for (k = 0; k <= SND_PCM_FORMAT_LAST; k++) { | |
| int bits; | |
| if (!snd_pcm_format_mask_test(mask, k)) | |
| continue; | |
| bits = snd_pcm_format_physical_width(k); | |
| if (bits < 0) | |
| continue; | |
| if (min > (unsigned)bits) | |
| min = bits; | |
| if (max < (unsigned)bits) | |
| max = bits; | |
| } | |
| c = snd_interval_refine_min(i, min, 0); | |
| if (c < 0) | |
| return c; | |
| if (c) | |
| changed = 1; | |
| c = snd_interval_refine_max(i, max, 0); | |
| if (c < 0) | |
| return c; | |
| if (c) | |
| changed = 1; | |
| return changed; | |
| } | |
| static const snd_pcm_hw_rule_t refine_rules[] = { | |
| { | |
| .var = SND_PCM_HW_PARAM_FORMAT, | |
| .func = snd_pcm_hw_rule_format, | |
| .deps = { SND_PCM_HW_PARAM_SAMPLE_BITS, -1 }, | |
| .private_data = 0, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_SAMPLE_BITS, | |
| .func = snd_pcm_hw_rule_sample_bits, | |
| .deps = { SND_PCM_HW_PARAM_FORMAT, | |
| SND_PCM_HW_PARAM_SAMPLE_BITS, -1 }, | |
| .private_data = 0, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_SAMPLE_BITS, | |
| .func = snd_pcm_hw_rule_div, | |
| .deps = { SND_PCM_HW_PARAM_FRAME_BITS, | |
| SND_PCM_HW_PARAM_CHANNELS, -1 }, | |
| .private_data = 0, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_FRAME_BITS, | |
| .func = snd_pcm_hw_rule_mul, | |
| .deps = { SND_PCM_HW_PARAM_SAMPLE_BITS, | |
| SND_PCM_HW_PARAM_CHANNELS, -1 }, | |
| .private_data = 0, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_FRAME_BITS, | |
| .func = snd_pcm_hw_rule_mulkdiv, | |
| .deps = { SND_PCM_HW_PARAM_PERIOD_BYTES, | |
| SND_PCM_HW_PARAM_PERIOD_SIZE, -1 }, | |
| .private_data = (void*) 8, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_FRAME_BITS, | |
| .func = snd_pcm_hw_rule_mulkdiv, | |
| .deps = { SND_PCM_HW_PARAM_BUFFER_BYTES, | |
| SND_PCM_HW_PARAM_BUFFER_SIZE, -1 }, | |
| .private_data = (void*) 8, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_CHANNELS, | |
| .func = snd_pcm_hw_rule_div, | |
| .deps = { SND_PCM_HW_PARAM_FRAME_BITS, | |
| SND_PCM_HW_PARAM_SAMPLE_BITS, -1 }, | |
| .private_data = 0, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_RATE, | |
| .func = snd_pcm_hw_rule_mulkdiv, | |
| .deps = { SND_PCM_HW_PARAM_PERIOD_SIZE, | |
| SND_PCM_HW_PARAM_PERIOD_TIME, -1 }, | |
| .private_data = (void*) 1000000, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_RATE, | |
| .func = snd_pcm_hw_rule_mulkdiv, | |
| .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE, | |
| SND_PCM_HW_PARAM_BUFFER_TIME, -1 }, | |
| .private_data = (void*) 1000000, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_PERIODS, | |
| .func = snd_pcm_hw_rule_div, | |
| .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE, | |
| SND_PCM_HW_PARAM_PERIOD_SIZE, -1 }, | |
| .private_data = 0, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_PERIOD_SIZE, | |
| .func = snd_pcm_hw_rule_div, | |
| .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE, | |
| SND_PCM_HW_PARAM_PERIODS, -1 }, | |
| .private_data = 0, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_PERIOD_SIZE, | |
| .func = snd_pcm_hw_rule_mulkdiv, | |
| .deps = { SND_PCM_HW_PARAM_PERIOD_BYTES, | |
| SND_PCM_HW_PARAM_FRAME_BITS, -1 }, | |
| .private_data = (void*) 8, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_PERIOD_SIZE, | |
| .func = snd_pcm_hw_rule_muldivk, | |
| .deps = { SND_PCM_HW_PARAM_PERIOD_TIME, | |
| SND_PCM_HW_PARAM_RATE, -1 }, | |
| .private_data = (void*) 1000000, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_BUFFER_SIZE, | |
| .func = snd_pcm_hw_rule_mul, | |
| .deps = { SND_PCM_HW_PARAM_PERIOD_SIZE, | |
| SND_PCM_HW_PARAM_PERIODS, -1 }, | |
| .private_data = 0, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_BUFFER_SIZE, | |
| .func = snd_pcm_hw_rule_mulkdiv, | |
| .deps = { SND_PCM_HW_PARAM_BUFFER_BYTES, | |
| SND_PCM_HW_PARAM_FRAME_BITS, -1 }, | |
| .private_data = (void*) 8, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_BUFFER_SIZE, | |
| .func = snd_pcm_hw_rule_muldivk, | |
| .deps = { SND_PCM_HW_PARAM_BUFFER_TIME, | |
| SND_PCM_HW_PARAM_RATE, -1 }, | |
| .private_data = (void*) 1000000, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_PERIOD_BYTES, | |
| .func = snd_pcm_hw_rule_muldivk, | |
| .deps = { SND_PCM_HW_PARAM_PERIOD_SIZE, | |
| SND_PCM_HW_PARAM_FRAME_BITS, -1 }, | |
| .private_data = (void*) 8, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_BUFFER_BYTES, | |
| .func = snd_pcm_hw_rule_muldivk, | |
| .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE, | |
| SND_PCM_HW_PARAM_FRAME_BITS, -1 }, | |
| .private_data = (void*) 8, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_PERIOD_TIME, | |
| .func = snd_pcm_hw_rule_mulkdiv, | |
| .deps = { SND_PCM_HW_PARAM_PERIOD_SIZE, | |
| SND_PCM_HW_PARAM_RATE, -1 }, | |
| .private_data = (void*) 1000000, | |
| }, | |
| { | |
| .var = SND_PCM_HW_PARAM_BUFFER_TIME, | |
| .func = snd_pcm_hw_rule_mulkdiv, | |
| .deps = { SND_PCM_HW_PARAM_BUFFER_SIZE, | |
| SND_PCM_HW_PARAM_RATE, -1 }, | |
| .private_data = (void*) 1000000, | |
| }, | |
| }; | |
| #define RULES (sizeof(refine_rules) / sizeof(refine_rules[0])) | |
| #define PCM_BIT(x) \ | |
| (1U << ((x) < 32 ? (x) : ((x) - 32))) | |
| static const snd_mask_t refine_masks[SND_PCM_HW_PARAM_LAST_MASK - SND_PCM_HW_PARAM_FIRST_MASK + 1] = { | |
| [SND_PCM_HW_PARAM_ACCESS - SND_PCM_HW_PARAM_FIRST_MASK] = { | |
| .bits = { | |
| PCM_BIT(SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) | | |
| PCM_BIT(SNDRV_PCM_ACCESS_MMAP_NONINTERLEAVED) | | |
| PCM_BIT(SNDRV_PCM_ACCESS_MMAP_COMPLEX) | | |
| PCM_BIT(SNDRV_PCM_ACCESS_RW_INTERLEAVED) | | |
| PCM_BIT(SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) | |
| }, | |
| }, | |
| [SND_PCM_HW_PARAM_FORMAT - SND_PCM_HW_PARAM_FIRST_MASK] = { | |
| .bits = { | |
| /* first 32bits */ | |
| PCM_BIT(SNDRV_PCM_FORMAT_S8) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U8) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S16_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S16_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U16_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U16_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S24_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S24_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U24_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U24_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S32_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S32_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U32_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U32_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_FLOAT_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_FLOAT_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_FLOAT64_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_FLOAT64_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_IEC958_SUBFRAME) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_IEC958_SUBFRAME) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_MU_LAW) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_A_LAW) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_IMA_ADPCM) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_MPEG) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_GSM) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S20_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S20_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U20_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U20_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_SPECIAL), | |
| /* second 32bits */ | |
| PCM_BIT(SNDRV_PCM_FORMAT_S24_3LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S24_3BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U24_3LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U24_3BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S20_3LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S20_3BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U20_3LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U20_3BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S18_3LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_S18_3BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U18_3LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_U18_3BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_G723_24) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_G723_24) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_G723_40) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_G723_40) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_DSD_U8) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_DSD_U16_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_DSD_U32_LE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_DSD_U16_BE) | | |
| PCM_BIT(SNDRV_PCM_FORMAT_DSD_U32_BE) | |
| }, | |
| }, | |
| [SND_PCM_HW_PARAM_SUBFORMAT - SND_PCM_HW_PARAM_FIRST_MASK] = { | |
| .bits = { | |
| PCM_BIT(SNDRV_PCM_SUBFORMAT_STD) | | |
| PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_MAX) | | |
| PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_20) | | |
| PCM_BIT(SNDRV_PCM_SUBFORMAT_MSBITS_24), | |
| }, | |
| }, | |
| }; | |
| static const snd_interval_t refine_intervals[SND_PCM_HW_PARAM_LAST_INTERVAL - SND_PCM_HW_PARAM_FIRST_INTERVAL + 1] = { | |
| [SND_PCM_HW_PARAM_SAMPLE_BITS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 1, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 1, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_FRAME_BITS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 1, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 1, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_CHANNELS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 1, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 1, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_RATE - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 1, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 0, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_PERIOD_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 0, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 0, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_PERIOD_SIZE - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 0, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 0, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_PERIOD_BYTES - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 0, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 0, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_PERIODS - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 0, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 0, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_BUFFER_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 1, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 0, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_BUFFER_SIZE - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 1, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 1, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_BUFFER_BYTES - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 1, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 1, .empty = 0, | |
| }, | |
| [SND_PCM_HW_PARAM_TICK_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL] = { | |
| .min = 0, .max = UINT_MAX, | |
| .openmin = 0, .openmax = 0, .integer = 0, .empty = 0, | |
| }, | |
| }; | |
| #if 0 | |
| #define RULES_DEBUG | |
| #endif | |
| int snd_pcm_hw_refine_soft(snd_pcm_t *pcm ATTRIBUTE_UNUSED, snd_pcm_hw_params_t *params) | |
| { | |
| unsigned int k; | |
| snd_interval_t *i; | |
| snd_mask_t *m; | |
| unsigned int rstamps[RULES]; | |
| unsigned int vstamps[SND_PCM_HW_PARAM_LAST_INTERVAL + 1]; | |
| unsigned int stamp = 2; | |
| int changed, again; | |
| #ifdef RULES_DEBUG | |
| snd_output_t *log; | |
| snd_output_stdio_attach(&log, stderr, 0); | |
| snd_output_printf(log, "refine_soft '%s' (begin)\n", pcm->name); | |
| snd_pcm_hw_params_dump(params, log); | |
| #endif | |
| for (k = SND_PCM_HW_PARAM_FIRST_MASK; k <= SND_PCM_HW_PARAM_LAST_MASK; k++) { | |
| if (!(params->rmask & (1 << k))) | |
| continue; | |
| changed = snd_mask_refine(hw_param_mask(params, k), | |
| &refine_masks[k - SND_PCM_HW_PARAM_FIRST_MASK]); | |
| if (changed) | |
| params->cmask |= 1 << k; | |
| if (changed < 0) | |
| goto _err; | |
| } | |
| for (k = SND_PCM_HW_PARAM_FIRST_INTERVAL; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; k++) { | |
| if (!(params->rmask & (1 << k))) | |
| continue; | |
| changed = snd_interval_refine(hw_param_interval(params, k), | |
| &refine_intervals[k - SND_PCM_HW_PARAM_FIRST_INTERVAL]); | |
| if (changed) | |
| params->cmask |= 1 << k; | |
| if (changed < 0) | |
| goto _err; | |
| } | |
| for (k = 0; k < RULES; k++) | |
| rstamps[k] = 0; | |
| for (k = 0; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; k++) | |
| vstamps[k] = (params->rmask & (1 << k)) ? 1 : 0; | |
| do { | |
| again = 0; | |
| for (k = 0; k < RULES; k++) { | |
| const snd_pcm_hw_rule_t *r = &refine_rules[k]; | |
| unsigned int d; | |
| int doit = 0; | |
| for (d = 0; r->deps[d] >= 0; d++) { | |
| if (vstamps[r->deps[d]] > rstamps[k]) { | |
| doit = 1; | |
| break; | |
| } | |
| } | |
| if (!doit) | |
| continue; | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "Rule %d (%p): ", k, r->func); | |
| if (r->var >= 0) { | |
| snd_output_printf(log, "%s=", snd_pcm_hw_param_name(r->var)); | |
| snd_pcm_hw_param_dump(params, r->var, log); | |
| snd_output_puts(log, " -> "); | |
| } | |
| #endif | |
| changed = r->func(params, r); | |
| #ifdef RULES_DEBUG | |
| if (r->var >= 0) | |
| snd_pcm_hw_param_dump(params, r->var, log); | |
| for (d = 0; r->deps[d] >= 0; d++) { | |
| snd_output_printf(log, " %s=", snd_pcm_hw_param_name(r->deps[d])); | |
| snd_pcm_hw_param_dump(params, r->deps[d], log); | |
| } | |
| snd_output_putc(log, '\n'); | |
| #endif | |
| rstamps[k] = stamp; | |
| if (changed && r->var >= 0) { | |
| params->cmask |= 1 << r->var; | |
| vstamps[r->var] = stamp; | |
| again = 1; | |
| } | |
| if (changed < 0) | |
| goto _err; | |
| stamp++; | |
| } | |
| } while (again); | |
| if (!params->msbits) { | |
| i = hw_param_interval(params, SND_PCM_HW_PARAM_SAMPLE_BITS); | |
| if (snd_interval_single(i)) | |
| params->msbits = snd_interval_value(i); | |
| m = hw_param_mask_c(params, SNDRV_PCM_HW_PARAM_FORMAT); | |
| if (snd_mask_single(m)) { | |
| snd_pcm_format_t format = snd_mask_min(m); | |
| params->msbits = snd_pcm_format_width(format); | |
| } | |
| } | |
| if (!params->rate_den) { | |
| i = hw_param_interval(params, SND_PCM_HW_PARAM_RATE); | |
| if (snd_interval_single(i)) { | |
| params->rate_num = snd_interval_value(i); | |
| params->rate_den = 1; | |
| } | |
| } | |
| params->rmask = 0; | |
| return 0; | |
| _err: | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "refine_soft '%s' (end-%i)\n", pcm->name, changed); | |
| snd_pcm_hw_params_dump(params, log); | |
| snd_output_close(log); | |
| #endif | |
| return changed; | |
| } | |
| int _snd_pcm_hw_params_refine(snd_pcm_hw_params_t *params, | |
| unsigned int vars, | |
| const snd_pcm_hw_params_t *src) | |
| { | |
| int changed, err = 0; | |
| unsigned int k; | |
| for (k = 0; k <= SND_PCM_HW_PARAM_LAST_INTERVAL; ++k) { | |
| if (!(vars & (1 << k))) | |
| continue; | |
| changed = _snd_pcm_hw_param_refine(params, k, src); | |
| if (changed < 0) | |
| err = changed; | |
| } | |
| params->info &= src->info; | |
| params->flags = src->flags; /* propagate all flags to slave */ | |
| return err; | |
| } | |
| int snd_pcm_hw_refine_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | |
| int (*cprepare)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *params), | |
| int (*cchange)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_params_t *sparams), | |
| int (*sprepare)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *params), | |
| int (*schange)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_params_t *sparams), | |
| int (*srefine)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *sparams)) | |
| { | |
| #ifdef RULES_DEBUG | |
| snd_output_t *log; | |
| #endif | |
| snd_pcm_hw_params_t sparams; | |
| int err; | |
| unsigned int cmask, changed; | |
| #ifdef RULES_DEBUG | |
| snd_output_stdio_attach(&log, stderr, 0); | |
| #endif | |
| err = cprepare(pcm, params); | |
| if (err < 0) | |
| return err; | |
| err = sprepare(pcm, &sparams); | |
| if (err < 0) { | |
| SNDERR("Slave PCM not usable"); | |
| return err; | |
| } | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "hw_refine_slave - enter '%s'\n", pcm->name); | |
| #endif | |
| do { | |
| cmask = params->cmask; | |
| params->cmask = 0; | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "schange '%s' (client)\n", pcm->name); | |
| snd_pcm_hw_params_dump(params, log); | |
| snd_output_printf(log, "schange '%s' (slave)\n", pcm->name); | |
| snd_pcm_hw_params_dump(&sparams, log); | |
| #endif | |
| err = schange(pcm, params, &sparams); | |
| if (err >= 0) { | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "srefine '%s' (client)\n", pcm->name); | |
| snd_pcm_hw_params_dump(params, log); | |
| snd_output_printf(log, "srefine '%s' (slave)\n", pcm->name); | |
| snd_pcm_hw_params_dump(&sparams, log); | |
| #endif | |
| err = srefine(pcm, &sparams); | |
| if (err < 0) { | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "srefine '%s', err < 0 (%i) (client)\n", pcm->name, err); | |
| snd_pcm_hw_params_dump(params, log); | |
| snd_output_printf(log, "srefine '%s', err < 0 (%i) (slave)\n", pcm->name, err); | |
| snd_pcm_hw_params_dump(&sparams, log); | |
| #endif | |
| cchange(pcm, params, &sparams); | |
| return err; | |
| } | |
| } else { | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "schange '%s', err < 0 (%i) (client)\n", pcm->name, err); | |
| snd_pcm_hw_params_dump(params, log); | |
| snd_output_printf(log, "schange '%s', err < 0 (%i) (slave)\n", pcm->name, err); | |
| snd_pcm_hw_params_dump(&sparams, log); | |
| #endif | |
| cchange(pcm, params, &sparams); | |
| return err; | |
| } | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "cchange '%s'\n", pcm->name); | |
| #endif | |
| err = cchange(pcm, params, &sparams); | |
| if (err < 0) | |
| return err; | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "refine_soft '%s'\n", pcm->name); | |
| #endif | |
| err = snd_pcm_hw_refine_soft(pcm, params); | |
| changed = params->cmask; | |
| params->cmask |= cmask; | |
| if (err < 0) | |
| return err; | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "refine_soft ok '%s'\n", pcm->name); | |
| #endif | |
| } while (changed); | |
| #ifdef RULES_DEBUG | |
| snd_output_printf(log, "refine_slave - leave '%s'\n", pcm->name); | |
| snd_output_close(log); | |
| #endif | |
| return 0; | |
| } | |
| int snd_pcm_hw_params_slave(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, | |
| int (*cchange)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_params_t *sparams), | |
| int (*sprepare)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *params), | |
| int (*schange)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *params, | |
| snd_pcm_hw_params_t *sparams), | |
| int (*sparams)(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *sparams)) | |
| { | |
| snd_pcm_hw_params_t slave_params; | |
| int err; | |
| err = sprepare(pcm, &slave_params); | |
| if (err < 0) | |
| return err; | |
| err = schange(pcm, params, &slave_params); | |
| if (err < 0) | |
| return err; | |
| err = sparams(pcm, &slave_params); | |
| if (err < 0) | |
| cchange(pcm, params, &slave_params); | |
| return err; | |
| } | |
| static int snd_pcm_sw_params_default(snd_pcm_t *pcm, snd_pcm_sw_params_t *params) | |
| { | |
| assert(pcm && params); | |
| assert(pcm->setup); | |
| params->proto = SNDRV_PCM_VERSION; | |
| params->tstamp_mode = SND_PCM_TSTAMP_NONE; | |
| params->tstamp_type = pcm->tstamp_type; | |
| params->period_step = 1; | |
| params->sleep_min = 0; | |
| params->avail_min = pcm->period_size; | |
| params->xfer_align = 1; | |
| params->start_threshold = 1; | |
| params->stop_threshold = pcm->buffer_size; | |
| params->silence_threshold = 0; | |
| params->silence_size = 0; | |
| params->boundary = pcm->buffer_size; | |
| /* this should not happen (bad child?) */ | |
| if (params->boundary == 0) | |
| return -EINVAL; | |
| while (params->boundary * 2 <= LONG_MAX - pcm->buffer_size) | |
| params->boundary *= 2; | |
| return 0; | |
| } | |
| #if 0 | |
| #define REFINE_DEBUG | |
| #endif | |
| int snd_pcm_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) | |
| { | |
| int res; | |
| #ifdef REFINE_DEBUG | |
| snd_output_t *log; | |
| snd_output_stdio_attach(&log, stderr, 0); | |
| #endif | |
| assert(pcm && params); | |
| #ifdef REFINE_DEBUG | |
| snd_output_printf(log, "REFINE called:\n"); | |
| snd_pcm_hw_params_dump(params, log); | |
| #endif | |
| if (pcm->ops->hw_refine) | |
| res = pcm->ops->hw_refine(pcm->op_arg, params); | |
| else | |
| res = -ENOSYS; | |
| #ifdef REFINE_DEBUG | |
| snd_output_printf(log, "refine done - result = %i\n", res); | |
| snd_pcm_hw_params_dump(params, log); | |
| snd_output_close(log); | |
| #endif | |
| return res; | |
| } | |
| /* Install one of the configurations present in 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_size | |
| max periods | |
| Return 0 on success otherwise a negative error code | |
| */ | |
| int _snd_pcm_hw_params_internal(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) | |
| { | |
| int err; | |
| snd_pcm_sw_params_t sw; | |
| int fb, min_align; | |
| err = snd_pcm_hw_refine(pcm, params); | |
| if (err < 0) | |
| return err; | |
| snd_pcm_hw_params_choose(pcm, params); | |
| if (pcm->setup) { | |
| err = snd_pcm_hw_free(pcm); | |
| if (err < 0) | |
| return err; | |
| } | |
| if (pcm->ops->hw_params) | |
| err = pcm->ops->hw_params(pcm->op_arg, params); | |
| else | |
| err = -ENOSYS; | |
| if (err < 0) | |
| return err; | |
| pcm->setup = 1; | |
| INTERNAL(snd_pcm_hw_params_get_access)(params, &pcm->access); | |
| INTERNAL(snd_pcm_hw_params_get_format)(params, &pcm->format); | |
| INTERNAL(snd_pcm_hw_params_get_subformat)(params, &pcm->subformat); | |
| INTERNAL(snd_pcm_hw_params_get_channels)(params, &pcm->channels); | |
| INTERNAL(snd_pcm_hw_params_get_rate)(params, &pcm->rate, 0); | |
| snd_interval_copy(&pcm->periods, ¶ms->intervals[SND_PCM_HW_PARAM_PERIODS - SND_PCM_HW_PARAM_FIRST_INTERVAL]); | |
| snd_interval_copy(&pcm->buffer_time, ¶ms->intervals[SND_PCM_HW_PARAM_BUFFER_TIME - SND_PCM_HW_PARAM_FIRST_INTERVAL]); | |
| INTERNAL(snd_pcm_hw_params_get_period_time)(params, &pcm->period_time, 0); | |
| INTERNAL(snd_pcm_hw_params_get_period_size)(params, &pcm->period_size, 0); | |
| INTERNAL(snd_pcm_hw_params_get_buffer_size)(params, &pcm->buffer_size); | |
| pcm->sample_bits = snd_pcm_format_physical_width(pcm->format); | |
| pcm->frame_bits = pcm->sample_bits * pcm->channels; | |
| fb = pcm->frame_bits; | |
| min_align = 1; | |
| while (fb % 8) { | |
| fb *= 2; | |
| min_align *= 2; | |
| } | |
| pcm->min_align = min_align; | |
| pcm->hw_flags = params->flags; | |
| pcm->info = params->info; | |
| pcm->msbits = params->msbits; | |
| pcm->rate_num = params->rate_num; | |
| pcm->rate_den = params->rate_den; | |
| pcm->fifo_size = params->fifo_size; | |
| /* Default sw params */ | |
| memset(&sw, 0, sizeof(sw)); | |
| err = snd_pcm_sw_params_default(pcm, &sw); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_sw_params(pcm, &sw); | |
| if (err < 0) | |
| return err; | |
| if (pcm->mmap_rw || | |
| pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED || | |
| pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED || | |
| pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX) { | |
| err = snd_pcm_mmap(pcm); | |
| } | |
| if (err < 0) | |
| return err; | |
| return 0; | |
| } | |