diff options
author | Abramo Bagnara <abramo@alsa-project.org> | 2000-11-26 12:16:18 +0000 |
---|---|---|
committer | Abramo Bagnara <abramo@alsa-project.org> | 2000-11-26 12:16:18 +0000 |
commit | 867ad7b2bcb6b0a117cde8c59c0c2bdb51c0b434 (patch) | |
tree | aa963affe3aafa39d1d0c5bc50a3c47d9c1d0dc5 | |
parent | 115c827b2478cbf9bca89e603f1552433bc9518d (diff) |
Implemeted optimal hw_params choice
-rw-r--r-- | include/pcm.h | 23 | ||||
-rw-r--r-- | src/pcm/pcm.c | 624 |
2 files changed, 644 insertions, 3 deletions
diff --git a/include/pcm.h b/include/pcm.h index d8ddc37c..872e12ec 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -114,6 +114,29 @@ int snd_pcm_hw_info_rulesv(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params, ...); +typedef struct _snd_pcm_strategy snd_pcm_strategy_t; + +/* choices need to be sorted on ascending badness */ +typedef struct _snd_pcm_strategy_simple_choices_list { + unsigned long value; + unsigned int badness; +} snd_pcm_strategy_simple_choices_list_t; + +int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, + const snd_pcm_strategy_t *strategy, + unsigned int min_badness, unsigned int max_badness); + +int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy); +int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp); +int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, + unsigned int param, + unsigned long best, + unsigned int mul); +int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, + unsigned int param, + unsigned int count, + snd_pcm_strategy_simple_choices_list_t *choices); + /* mmap */ snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm); snd_pcm_channel_area_t *snd_pcm_mmap_running_areas(snd_pcm_t *pcm); diff --git a/src/pcm/pcm.c b/src/pcm/pcm.c index 4861d08e..cbf447e1 100644 --- a/src/pcm/pcm.c +++ b/src/pcm/pcm.c @@ -679,7 +679,7 @@ int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp) { unsigned int k; fputs("access:", fp); - if (info->access_mask == ~0) + if (info->access_mask == ~0U) fputs(" ALL", fp); else if (info->access_mask) { for (k = 0; k <= SND_PCM_ACCESS_LAST; ++k) @@ -692,7 +692,7 @@ int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp) putc('\n', fp); fputs("format:", fp); - if (info->format_mask == ~0) + if (info->format_mask == ~0U) fputs(" ALL", fp); else if (info->format_mask) { for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) @@ -705,7 +705,7 @@ int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp) putc('\n', fp); fputs("subformat:", fp); - if (info->subformat_mask == ~0) + if (info->subformat_mask == ~0U) fputs(" ALL", fp); else if (info->subformat_mask) { for (k = 0; k <= SND_PCM_SUBFORMAT_LAST; ++k) @@ -2729,7 +2729,625 @@ int snd_pcm_hw_params_rulesv(snd_pcm_t *pcm, return snd_pcm_hw_params_rules(pcm, params, count, rules); } +struct _snd_pcm_strategy { + int (*choose_param)(const snd_pcm_hw_info_t *info, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy); + long (*next_value)(const snd_pcm_hw_info_t *info, + unsigned int param, + long value, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy); + int (*min_badness)(const snd_pcm_hw_info_t *info, + unsigned int max_badness, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy); + void *private; + void (*free)(snd_pcm_strategy_t *strategy); +}; + +/* Independent badness */ +typedef struct _snd_pcm_strategy_simple snd_pcm_strategy_simple_t; + +struct _snd_pcm_strategy_simple { + int valid; + long (*next_value)(const snd_pcm_hw_info_t *info, + unsigned int param, + long value, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par); + unsigned int (*min_badness)(const snd_pcm_hw_info_t *info, + unsigned int param, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par); + void *private; + void (*free)(snd_pcm_strategy_simple_t *strategy); +}; + +typedef struct _snd_pcm_strategy_simple_near { + long best; + unsigned int mul; +} snd_pcm_strategy_simple_near_t; + +typedef struct _snd_pcm_strategy_simple_choices { + unsigned int count; + /* choices need to be sorted on ascending badness */ + snd_pcm_strategy_simple_choices_list_t *choices; +} snd_pcm_strategy_simple_choices_t; + +static inline unsigned int hweight32(u_int32_t v) +{ + v = (v & 0x55555555) + ((v >> 1) & 0x55555555); + v = (v & 0x33333333) + ((v >> 2) & 0x33333333); + v = (v & 0x0F0F0F0F) + ((v >> 4) & 0x0F0F0F0F); + v = (v & 0x00FF00FF) + ((v >> 8) & 0x00FF00FF); + return (v & 0x0000FFFF) + ((v >> 16) & 0x0000FFFF); +} + +static inline unsigned int ld2(u_int32_t v) +{ + unsigned r = 0; + + if (v >= 0x10000) { + v >>= 16; + r += 16; + } + if (v >= 0x100) { + v >>= 8; + r += 8; + } + if (v >= 0x10) { + v >>= 4; + r += 4; + } + if (v >= 4) { + v >>= 2; + r += 2; + } + if (v >= 2) + r++; + return r; +} + + +static unsigned long par_choices(const snd_pcm_hw_info_t *info, unsigned int param) +{ + switch (param) { + case SND_PCM_HW_PARAM_ACCESS: + return hweight32(info->access_mask); + case SND_PCM_HW_PARAM_FORMAT: + return hweight32(info->format_mask); + case SND_PCM_HW_PARAM_SUBFORMAT: + return hweight32(info->subformat_mask); + case SND_PCM_HW_PARAM_CHANNELS: + return info->channels_max - info->channels_min + 1; + case SND_PCM_HW_PARAM_RATE: + return info->rate_max - info->rate_min + 1; + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + return info->fragment_size_max - info->fragment_size_min + 1; + case SND_PCM_HW_PARAM_FRAGMENTS: + return info->fragments_max - info->fragments_min + 1; + case SND_PCM_HW_PARAM_BUFFER_SIZE: + return info->buffer_size_max - info->buffer_size_min + 1; + default: + assert(0); + return 0; + } +} + +static unsigned long par_refine_min(snd_pcm_hw_info_t *info, + unsigned int param, + unsigned long value) +{ + int i; + switch (param) { + case SND_PCM_HW_PARAM_ACCESS: + if (value >= 32) { + info->access_mask = 0; + return 32; + } else + info->access_mask &= ~((1 << value) - 1); + i = ffs(info->access_mask); + if (i == 0) + return 32; + return i - 1; + case SND_PCM_HW_PARAM_FORMAT: + if (value >= 32) { + info->format_mask = 0; + return 32; + } else + info->format_mask &= ~((1 << value) - 1); + i = ffs(info->format_mask); + if (i == 0) + return 32; + return i - 1; + case SND_PCM_HW_PARAM_SUBFORMAT: + if (value >= 32) { + info->subformat_mask = 0; + return 32; + } else + info->subformat_mask &= ~((1 << value) - 1); + i = ffs(info->subformat_mask); + if (i == 0) + return 32; + return i - 1; + case SND_PCM_HW_PARAM_CHANNELS: + if (value > info->channels_min) + info->channels_min = value; + return info->channels_min; + case SND_PCM_HW_PARAM_RATE: + if (value > info->rate_min) + info->rate_min = value; + return info->rate_min; + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + if (value > info->fragment_size_min) + info->fragment_size_min = value; + return info->fragment_size_min; + case SND_PCM_HW_PARAM_FRAGMENTS: + if (value > info->fragments_min) + info->fragments_min = value; + return info->fragments_min; + case SND_PCM_HW_PARAM_BUFFER_SIZE: + if (value > info->buffer_size_min) + info->buffer_size_min = value; + return info->buffer_size_min; + default: + assert(0); + return 0; + } +} + +static unsigned long par_refine_max(snd_pcm_hw_info_t *info, + unsigned int param, + unsigned long value) +{ + switch (param) { + case SND_PCM_HW_PARAM_ACCESS: + if (value < 31) + info->access_mask &= (1 << (value + 1)) - 1; + return ld2(info->access_mask); + case SND_PCM_HW_PARAM_FORMAT: + if (value < 31) + info->format_mask &= (1 << (value + 1)) - 1; + return ld2(info->format_mask); + case SND_PCM_HW_PARAM_SUBFORMAT: + if (value < 31) + info->subformat_mask &= (1 << (value + 1)) - 1; + return ld2(info->subformat_mask); + case SND_PCM_HW_PARAM_CHANNELS: + if (value < info->channels_max) + info->channels_max = value; + return info->channels_max; + case SND_PCM_HW_PARAM_RATE: + if (value < info->rate_max) + info->rate_max = value; + return info->rate_max; + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + if (value < info->fragment_size_max) + info->fragment_size_max = value; + return info->fragment_size_max; + case SND_PCM_HW_PARAM_FRAGMENTS: + if (value < info->fragments_max) + info->fragments_max = value; + return info->fragments_max; + case SND_PCM_HW_PARAM_BUFFER_SIZE: + if (value < info->buffer_size_max) + info->buffer_size_max = value; + return info->buffer_size_max; + default: + assert(0); + return 0; + } +} + +static void par_set(snd_pcm_hw_info_t *info, unsigned int param, + unsigned long value) +{ + switch (param) { + case SND_PCM_HW_PARAM_ACCESS: + info->access_mask = 1 << value; + break; + case SND_PCM_HW_PARAM_FORMAT: + info->format_mask = 1 << value; + break; + case SND_PCM_HW_PARAM_SUBFORMAT: + info->subformat_mask = 1 << value; + break; + case SND_PCM_HW_PARAM_CHANNELS: + info->channels_min = info->channels_max = value; + break; + case SND_PCM_HW_PARAM_RATE: + info->rate_min = info->rate_max = value; + break; + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + info->fragment_size_min = info->fragment_size_max = value; + break; + case SND_PCM_HW_PARAM_FRAGMENTS: + info->fragments_min = info->fragments_max = value; + break; + case SND_PCM_HW_PARAM_BUFFER_SIZE: + info->buffer_size_min = info->buffer_size_max = value; + break; + default: + assert(0); + break; + } +} + +static int par_check(const snd_pcm_hw_info_t *info, unsigned int param, + unsigned long value) +{ + switch (param) { + case SND_PCM_HW_PARAM_ACCESS: + return info->access_mask & (1 << value); + case SND_PCM_HW_PARAM_FORMAT: + return info->format_mask & (1 << value); + case SND_PCM_HW_PARAM_SUBFORMAT: + return info->subformat_mask & (1 << value); + case SND_PCM_HW_PARAM_CHANNELS: + return value >= info->channels_min && + value <= info->channels_max; + case SND_PCM_HW_PARAM_RATE: + return value >= info->rate_min && + value <= info->rate_max; + case SND_PCM_HW_PARAM_FRAGMENT_SIZE: + return value >= info->fragment_size_min && + value <= info->fragment_size_max; + case SND_PCM_HW_PARAM_FRAGMENTS: + return value >= info->fragments_min && + value <= info->fragments_max; + case SND_PCM_HW_PARAM_BUFFER_SIZE: + return value >= info->buffer_size_min && + value <= info->buffer_size_max; + default: + assert(0); + return 0; + } +} + +static long par_nearest_next(const snd_pcm_hw_info_t *info, unsigned int param, + unsigned long best, long value, snd_pcm_t *pcm) +{ + unsigned long min, max; + unsigned long d1, d2; + unsigned long max1, min2; + snd_pcm_hw_info_t i1, i2; + int err1 = -EINVAL; + int err2 = -EINVAL; + + i1 = *info; + i2 = *info; + max = par_refine_max(&i1, param, ULONG_MAX); + min = par_refine_min(&i2, param, 0); + if (value < 0) { + d1 = 0; + d2 = 0; + } else { + long diff = value - best; + if (diff < 0) { + d1 = -diff + 1; + d2 = -diff; + } else { + d1 = diff + 1; + d2 = diff + 1; + } + } + if (best > d1) + max1 = best - d1; + else + max1 = 0; + min2 = best + d2; + max1 = par_refine_max(&i1, param, max1); + min2 = par_refine_min(&i2, param, min2); + if (min <= max1) { + err1 = snd_pcm_hw_info(pcm, &i1); + if (err1 >= 0) + max1 = par_refine_max(&i1, param, max1); + } + if (min2 <= max && (err1 < 0 || best - max1 > min2 - best)) { + err2 = snd_pcm_hw_info(pcm, &i2); + if (err2 >= 0) + min2 = par_refine_min(&i2, param, min2); + } + if (err1 < 0) { + if (err2 < 0) + return -1; + return min2; + } else if (err2 < 0) + return max1; + if (best - max1 <= min2 - best) + return max1; + return min2; +} + +int snd_pcm_hw_info_strategy1(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, + const snd_pcm_strategy_t *strategy, + unsigned int min_badness, unsigned int max_badness) +{ + snd_pcm_hw_info_t best_info; + int param; + long value; + unsigned int best_badness; + int badness; + badness = strategy->min_badness(info, max_badness, pcm, strategy); +#if 0 + printf("\nBadness: %d\n", badness); + snd_pcm_dump_hw_info(info, stdout); +#endif + if (badness < 0) + return -EINVAL; + if ((unsigned int)badness > min_badness) + min_badness = badness; + param = strategy->choose_param(info, pcm, strategy); + if (param < 0) + return badness; + best_badness = UINT_MAX; + value = -1; + while (1) { + snd_pcm_hw_info_t info1; + int err; + value = strategy->next_value(info, param, value, pcm, strategy); + if (value < 0) + break; + info1 = *info; + par_set(&info1, param, value); + err = snd_pcm_hw_info(pcm, &info1); + if (err < 0) + continue; + badness = snd_pcm_hw_info_strategy1(pcm, &info1, strategy, min_badness, max_badness); + if (badness < 0) + continue; + if ((unsigned int) badness <= min_badness) { + *info = info1; + return badness; + } + best_badness = badness; + best_info = info1; + max_badness = badness - 1; + } + if (best_badness == UINT_MAX) + return -EINVAL; + *info = best_info; + return best_badness; +} + +int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, + const snd_pcm_strategy_t *strategy, + unsigned int min_badness, unsigned int max_badness) +{ + int err; + err = snd_pcm_hw_info(pcm, info); + if (err < 0) + return err; + return snd_pcm_hw_info_strategy1(pcm, info, strategy, min_badness, max_badness); +} + + +void snd_pcm_strategy_simple_free(snd_pcm_strategy_t *strategy) +{ + snd_pcm_strategy_simple_t *pars = strategy->private; + int k; + for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) { + if (pars[k].valid && pars[k].free) + pars[k].free(&pars[k]); + } + free(pars); +} + +int snd_pcm_strategy_simple_choose_param(const snd_pcm_hw_info_t *info, + snd_pcm_t *pcm ATTRIBUTE_UNUSED, + const snd_pcm_strategy_t *strategy) +{ + unsigned int param; + int best_param = -1; + const snd_pcm_strategy_simple_t *pars = strategy->private; + unsigned long min_choices = ULONG_MAX; + for (param = 0; param <= SND_PCM_HW_PARAM_LAST; ++param) { + unsigned int choices; + if (!pars[param].valid) + continue; + choices = par_choices(info, param); + if (choices == 1) + continue; + assert(choices != 0); + if (choices < min_choices) { + min_choices = choices; + best_param = param; + } + } + return best_param; +} + +long snd_pcm_strategy_simple_next_value(const snd_pcm_hw_info_t *info, + unsigned int param, + long value, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy) +{ + const snd_pcm_strategy_simple_t *pars = strategy->private; + assert(pars[param].valid); + return pars[param].next_value(info, param, value, pcm, &pars[param]); +} + + +int snd_pcm_strategy_simple_min_badness(const snd_pcm_hw_info_t *info, + unsigned int max_badness, + snd_pcm_t *pcm, + const snd_pcm_strategy_t *strategy) +{ + unsigned int param; + unsigned int badness = 0; + const snd_pcm_strategy_simple_t *pars = strategy->private; + for (param = 0; param <= SND_PCM_HW_PARAM_LAST; ++param) { + unsigned int b; + if (!pars[param].valid) + continue; + b = pars[param].min_badness(info, param, pcm, &pars[param]); + if (b > max_badness || max_badness - b < badness) + return -EINVAL; + badness += b; + } + return badness; +} + + +void snd_pcm_strategy_simple_near_free(snd_pcm_strategy_simple_t *par) +{ + snd_pcm_strategy_simple_near_t *p = par->private; + free(p); +} + +unsigned int snd_pcm_strategy_simple_near_min_badness(const snd_pcm_hw_info_t *info, + unsigned int param, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par) +{ + const snd_pcm_strategy_simple_near_t *p = par->private; + long value = par_nearest_next(info, param, p->best, -1, pcm); + long diff; + assert(value >= 0); + diff = p->best - value; + if (diff < 0) + diff = -diff; + return diff * p->mul; +} + +long snd_pcm_strategy_simple_near_next_value(const snd_pcm_hw_info_t *info, + unsigned int param, + long value, + snd_pcm_t *pcm, + const snd_pcm_strategy_simple_t *par) +{ + const snd_pcm_strategy_simple_near_t *p = par->private; + return par_nearest_next(info, param, p->best, value, pcm); +} + +void snd_pcm_strategy_simple_choices_free(snd_pcm_strategy_simple_t *par) +{ + snd_pcm_strategy_simple_choices_t *p = par->private; +// free(p->choices); + free(p); +} + +unsigned int snd_pcm_strategy_simple_choices_min_badness(const snd_pcm_hw_info_t *info, + unsigned int param, + snd_pcm_t *pcm ATTRIBUTE_UNUSED, + const snd_pcm_strategy_simple_t *par) +{ + const snd_pcm_strategy_simple_choices_t *p = par->private; + unsigned int k; + for (k = 0; k < p->count; ++k) { + if (par_check(info, param, p->choices[k].value)) + return p->choices[k].badness; + } + assert(0); + return UINT_MAX; +} + +long snd_pcm_strategy_simple_choices_next_value(const snd_pcm_hw_info_t *info, + unsigned int param, + long value, + snd_pcm_t *pcm ATTRIBUTE_UNUSED, + const snd_pcm_strategy_simple_t *par) +{ + const snd_pcm_strategy_simple_choices_t *p = par->private; + unsigned int k = 0; + if (value >= 0) { + for (; k < p->count; ++k) { + if (p->choices[k].value == (unsigned long) value) { + k++; + break; + } + } + } + for (; k < p->count; ++k) { + unsigned long v = p->choices[k].value; + if (par_check(info, param, v)) + return v; + } + return -1; +} + +int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy) +{ + if (strategy->free) + strategy->free(strategy); + free(strategy); + return 0; +} + +int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp) +{ + snd_pcm_strategy_simple_t *data; + snd_pcm_strategy_t *s; + assert(strategyp); + data = calloc(SND_PCM_HW_PARAM_LAST + 1, sizeof(*data)); + if (!data) + return -ENOMEM; + s = calloc(1, sizeof(*s)); + if (!s) { + free(data); + return -ENOMEM; + } + s->choose_param = snd_pcm_strategy_simple_choose_param; + s->next_value = snd_pcm_strategy_simple_next_value; + s->min_badness = snd_pcm_strategy_simple_min_badness; + s->private = data; + s->free = snd_pcm_strategy_simple_free; + *strategyp = s; + return 0; +} + +int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, + unsigned int param, + unsigned long best, + unsigned int mul) +{ + snd_pcm_strategy_simple_t *s = strategy->private; + snd_pcm_strategy_simple_near_t *data; + assert(strategy); + assert(param <= SND_PCM_HW_PARAM_LAST); + assert(!s->valid); + data = calloc(1, sizeof(*data)); + if (!data) + return -ENOMEM; + data->best = best; + data->mul = mul; + s += param; + s->valid = 1; + s->next_value = snd_pcm_strategy_simple_near_next_value; + s->min_badness = snd_pcm_strategy_simple_near_min_badness; + s->private = data; + s->free = snd_pcm_strategy_simple_near_free; + return 0; +} + +int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, + unsigned int param, + unsigned int count, + snd_pcm_strategy_simple_choices_list_t *choices) +{ + snd_pcm_strategy_simple_t *s = strategy->private; + snd_pcm_strategy_simple_choices_t *data; + assert(strategy); + assert(param <= SND_PCM_HW_PARAM_LAST); + assert(!s->valid); + data = calloc(1, sizeof(*data)); + if (!data) + return -ENOMEM; + data->count = count; + data->choices = choices; + s += param; + s->valid = 1; + s->next_value = snd_pcm_strategy_simple_choices_next_value; + s->min_badness = snd_pcm_strategy_simple_choices_min_badness; + s->private = data; + s->free = snd_pcm_strategy_simple_choices_free; + return 0; +} + size_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm) { return *pcm->hw_ptr; } + |