]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Cleaned all hw_info. Removed snd_pcm_hw_{info,params}_rules* and changed strategy...
authorAbramo Bagnara <abramo@alsa-project.org>
Wed, 29 Nov 2000 08:32:36 +0000 (08:32 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Wed, 29 Nov 2000 08:32:36 +0000 (08:32 +0000)
15 files changed:
include/pcm.h
src/pcm/pcm.c
src/pcm/pcm_adpcm.c
src/pcm/pcm_alaw.c
src/pcm/pcm_linear.c
src/pcm/pcm_local.h
src/pcm/pcm_misc.c
src/pcm/pcm_mulaw.c
src/pcm/pcm_multi.c
src/pcm/pcm_plug.c
src/pcm/pcm_plugin.h
src/pcm/pcm_rate.c
src/pcm/pcm_route.c
src/pcm/pcm_share.c
src/pcm/pcm_shm.c

index 872e12ece9ce61f06b946efb3b0c439017b80a85..89b6d175cc71d03ebc659b67e064f2e5a12658b5 100644 (file)
@@ -102,17 +102,8 @@ int snd_pcm_unlink(snd_pcm_t *pcm);
 int snd_pcm_wait(snd_pcm_t *pcm, int timeout);
 ssize_t snd_pcm_avail_update(snd_pcm_t *pcm);
 int snd_pcm_set_avail_min(snd_pcm_t *pcm, size_t size);
-void snd_pcm_hw_info_all(snd_pcm_hw_info_t *info);
-int snd_pcm_hw_params_rules(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
-                           unsigned int count, int *rules);
-int snd_pcm_hw_params_rulesv(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, ...);
-int snd_pcm_hw_info_rules(snd_pcm_t *pcm, 
-                         snd_pcm_hw_info_t *info,
-                         snd_pcm_hw_params_t *params,
-                         unsigned int count, int *rules);
-int snd_pcm_hw_info_rulesv(snd_pcm_t *pcm, 
-                          snd_pcm_hw_info_t *info,
-                          snd_pcm_hw_params_t *params, ...);
+void snd_pcm_hw_info_any(snd_pcm_hw_info_t *info);
+int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info);
 
 typedef struct _snd_pcm_strategy snd_pcm_strategy_t;
 
@@ -123,19 +114,25 @@ typedef struct _snd_pcm_strategy_simple_choices_list {
 } 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);
+                            const snd_pcm_strategy_t *strategy);
 
 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,
+int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp,
+                           unsigned int badness_min,
+                           unsigned int badness_max);
+int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy, int order,
                                 unsigned int param,
                                 unsigned long best,
                                 unsigned int mul);
-int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy,
+int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy, int order,
                                    unsigned int param,
                                    unsigned int count,
                                    snd_pcm_strategy_simple_choices_list_t *choices);
+int snd_pcm_hw_info_try_explain_failure(snd_pcm_t *pcm,
+                                       snd_pcm_hw_info_t *fail,
+                                       snd_pcm_hw_info_t *success,
+                                       unsigned int depth,
+                                       FILE *fp);
 
 /* mmap */
 snd_pcm_channel_area_t *snd_pcm_mmap_areas(snd_pcm_t *pcm);
@@ -149,8 +146,8 @@ ssize_t snd_pcm_mmap_readi(snd_pcm_t *pcm, void *buffer, size_t size);
 ssize_t snd_pcm_mmap_writen(snd_pcm_t *pcm, void **bufs, size_t size);
 ssize_t snd_pcm_mmap_readn(snd_pcm_t *pcm, void **bufs, size_t size);
 
-const char *snd_pcm_format_name(int format);
-const char *snd_pcm_format_description(int format);
+const char *snd_pcm_format_name(unsigned int format);
+const char *snd_pcm_format_description(unsigned int format);
 int snd_pcm_format_value(const char* name);
 
 int snd_pcm_area_silence(snd_pcm_channel_area_t *dst_channel, size_t dst_offset,
@@ -177,6 +174,7 @@ int snd_pcm_format_unsigned(int format);
 int snd_pcm_format_linear(int format);
 int snd_pcm_format_little_endian(int format);
 int snd_pcm_format_big_endian(int format);
+int snd_pcm_format_cpu_endian(int format);
 int snd_pcm_format_width(int format);                  /* in bits */
 int snd_pcm_format_physical_width(int format);         /* in bits */
 int snd_pcm_build_linear_format(int width, int unsignd, int big_endian);
index cbf447e13e4b82e52443678171b51558be1dfbb4..c0fca4be9d3e9c9f59dd9db0db74b9a6a5679788 100644 (file)
@@ -112,7 +112,7 @@ int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
        return err;
 }
 
-void snd_pcm_hw_info_all(snd_pcm_hw_info_t *info)
+void snd_pcm_hw_info_any(snd_pcm_hw_info_t *info)
 {
        assert(info);
        info->access_mask = ~0;
@@ -143,119 +143,67 @@ void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *i
        info->buffer_size_min = info->buffer_size_max = params->fragment_size * params->fragments;
 }
 
-void snd_pcm_hw_info_to_params(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params)
-{
-       assert(info->access_mask && 
-              !(info->access_mask & (info->access_mask - 1)));
-       params->access = ffs(info->access_mask) - 1;
-       assert(info->format_mask && 
-              !(info->format_mask & (info->format_mask - 1)));
-       params->format = ffs(info->format_mask) - 1;
-       assert(info->subformat_mask && 
-              !(info->subformat_mask & (info->subformat_mask - 1)));
-       params->subformat = ffs(info->subformat_mask) - 1;
-       assert(info->channels_min == info->channels_max);
-       params->channels = info->channels_min;
-       assert(info->rate_min == info->rate_max);
-       params->rate = info->rate_min;
-       assert(info->fragment_size_min == info->fragment_size_max);
-       params->fragment_size = info->fragment_size_min;
-       assert(info->fragments_min == info->fragments_max);
-       params->fragments = info->fragments_min;
-}
-
-void snd_pcm_hw_info_to_params_fail(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params)
-{
-       unsigned int f = 0;
-       if (info->access_mask == 0)
-               f |= SND_PCM_HW_PARBIT_ACCESS;
-       if (info->format_mask == 0)
-               f |= SND_PCM_HW_PARBIT_FORMAT;
-       if (info->subformat_mask == 0)
-               f |= SND_PCM_HW_PARBIT_SUBFORMAT;
-       if (info->channels_min > info->channels_max)
-               f |= SND_PCM_HW_PARBIT_CHANNELS;
-       if (info->rate_min > info->rate_max)
-               f |= SND_PCM_HW_PARBIT_RATE;
-       if (info->fragment_size_min > info->fragment_size_max)
-               f |= SND_PCM_HW_PARBIT_FRAGMENT_SIZE;
-       if (info->fragments_min > info->fragments_max)
-               f |= SND_PCM_HW_PARBIT_FRAGMENTS;
-       assert(f);
-       params->fail_mask = f;
-}
-
-int _snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+int snd_pcm_hw_info_to_params(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params)
 {
+       snd_pcm_hw_info_t i = *info;
        int err;
-       snd_pcm_hw_info_t info;
-       
-       snd_pcm_hw_params_to_info(params, &info);
-       err = snd_pcm_hw_info(pcm, &info);
-       if (err < 0) {
-               snd_pcm_hw_info_to_params_fail(&info, params);
-               return err;
-       }
-       snd_pcm_hw_info_to_params(&info, params);
-       if ((err = pcm->ops->hw_params(pcm->op_arg, params)) < 0)
-               return err;
-       pcm->setup = 1;
-       pcm->access = params->access;
-       pcm->format = params->format;
-       pcm->subformat = params->subformat;
-       pcm->rate = params->rate;
-       pcm->channels = params->channels;
-       pcm->fragment_size = params->fragment_size;
-       pcm->fragments = params->fragments;
-       pcm->bits_per_sample = snd_pcm_format_physical_width(params->format);
-        pcm->bits_per_frame = pcm->bits_per_sample * params->channels;
-       pcm->buffer_size = params->fragment_size * params->fragments;
 
-       pcm->info = info.info;
-       pcm->msbits = info.msbits;
-       pcm->rate_master = info.rate_master;
-       pcm->rate_divisor = info.rate_divisor;
-       pcm->fifo_size = info.fifo_size;
+       err = snd_pcm_hw_info(pcm, &i);
+       if (err < 0)
+               return err;
 
-       /* Default sw params */
-       pcm->start_mode = SND_PCM_START_DATA;
-       pcm->ready_mode = SND_PCM_READY_FRAGMENT;
-       pcm->xrun_mode = SND_PCM_XRUN_FRAGMENT;
-       pcm->avail_min = pcm->fragment_size;
-       pcm->xfer_min = pcm->fragment_size;
-       pcm->xfer_align = pcm->fragment_size;
-       pcm->time = 0;
-       pcm->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size;
+       assert(i.access_mask);
+       if (i.access_mask & (i.access_mask - 1)) {
+               i.access_mask = 1 << (ffs(i.access_mask - 1));
+               err = snd_pcm_hw_info(pcm, info);
+               assert(err >= 0);
+       }
+       assert(i.format_mask);
+       if (i.format_mask & (i.format_mask - 1)) {
+               i.format_mask = 1 << (ffs(i.format_mask - 1));
+               err = snd_pcm_hw_info(pcm, info);
+               assert(err >= 0);
+       }
+       assert(i.subformat_mask);
+       if (i.subformat_mask & (i.subformat_mask - 1)) {
+               i.subformat_mask = 1 << (ffs(i.subformat_mask - 1));
+               err = snd_pcm_hw_info(pcm, info);
+               assert(err >= 0);
+       }
+       assert(i.channels_min <= i.channels_max);
+       if (i.channels_min < i.channels_max) {
+               i.channels_max = i.channels_min;
+               err = snd_pcm_hw_info(pcm, info);
+               assert(err >= 0);
+       }
+       assert(i.rate_min <= i.rate_max);
+       if (i.rate_min < i.rate_max) {
+               i.rate_max = i.rate_min;
+               err = snd_pcm_hw_info(pcm, info);
+               assert(err >= 0);
+       }
+       assert(i.fragment_size_min <= i.fragment_size_max);
+       if (i.fragment_size_min < i.fragment_size_max) {
+               i.fragment_size_max = i.fragment_size_min;
+               err = snd_pcm_hw_info(pcm, info);
+               assert(err >= 0);
+       }
+       assert(i.fragments_min <= i.fragments_max);
+       if (i.fragments_min < i.fragments_max) {
+               i.fragments_max = i.fragments_min;
+               err = snd_pcm_hw_info(pcm, info);
+               assert(err >= 0);
+       }
+       params->access = ffs(i.access_mask) - 1;
+       params->format = ffs(i.format_mask) - 1;
+       params->subformat = ffs(i.subformat_mask) - 1;
+       params->channels = i.channels_min;
+       params->rate = i.rate_min;
+       params->fragment_size = i.fragment_size_min;
+       params->fragments = i.fragments_min;
        return 0;
 }
 
-int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
-{
-       int err;
-       assert(pcm && params);
-       if (pcm->setup && pcm->mmap_channels && 
-           (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_munmap(pcm);
-               if (err < 0)
-                       return err;
-       }
-       err = _snd_pcm_hw_params(pcm, params);
-       if (pcm->setup &&
-           (pcm->mmap_rw || 
-            (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
-             pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED ||
-             pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) {
-               int err;
-               err = snd_pcm_mmap(pcm);
-               if (err < 0)
-                       return err;
-       }
-       return err;
-}
-
 int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
 {
        int err;
@@ -475,73 +423,35 @@ int snd_pcm_poll_descriptor(snd_pcm_t *pcm)
        return pcm->poll_fd;
 }
 
-typedef struct {
-       int value;
-       const char* name;
-       const char* desc;
-} assoc_t;
-
-static assoc_t *assoc_value(int value, assoc_t *alist)
-{
-       while (alist->name) {
-               if (value == alist->value)
-                       return alist;
-               alist++;
-       }
-       return 0;
-}
-
-static assoc_t *assoc_name(const char *name, assoc_t *alist)
-{
-       while (alist->name) {
-               if (strcasecmp(name, alist->name) == 0)
-                       return alist;
-               alist++;
-       }
-       return 0;
-}
-
-static const char *assoc(int value, assoc_t *alist)
-{
-       assoc_t *a;
-       a = assoc_value(value, alist);
-       if (a)
-               return a->name;
-       return "UNKNOWN";
-}
+#define STATE(v) [SND_PCM_STATE_##v] = #v
+#define STREAM(v) [SND_PCM_STREAM_##v] = #v
+#define READY(v) [SND_PCM_READY_##v] = #v
+#define XRUN(v) [SND_PCM_XRUN_##v] = #v
+#define ACCESS(v) [SND_PCM_ACCESS_##v] = #v
+#define START(v) [SND_PCM_START_##v] = #v
+#define HW_PARAM(v) [SND_PCM_HW_PARAM_##v] = #v
+#define SW_PARAM(v) [SND_PCM_SW_PARAM_##v] = #v
+#define FORMAT(v) [SND_PCM_FORMAT_##v] = #v
+#define SUBFORMAT(v) [SND_PCM_SUBFORMAT_##v] = #v 
 
-#define STATE(v) { SND_PCM_STATE_##v, #v, #v }
-#define STREAM(v) { SND_PCM_STREAM_##v, #v, #v }
-#define READY(v) { SND_PCM_READY_##v, #v, #v }
-#define XRUN(v) { SND_PCM_XRUN_##v, #v, #v }
-#define ACCESS(v) { SND_PCM_ACCESS_##v, #v, #v }
-#define FORMAT(v, d) { SND_PCM_FORMAT_##v, #v, d }
-#define SUBFORMAT(v, d) { SND_PCM_SUBFORMAT_##v, #v, d }
-#define XRUN_ACT(v) { SND_PCM_XRUN_ACT_##v, #v, #v }
-#define START(v) { SND_PCM_START_##v, #v, #v }
-#define FILL(v) { SND_PCM_FILL_##v, #v, #v }
-#define HW_PARAM(v) { SND_PCM_HW_PARAM_##v, #v, #v }
-#define SW_PARAM(v) { SND_PCM_SW_PARAM_##v, #v, #v }
-#define END { 0, NULL, NULL }
+#define FORMATD(v, d) [SND_PCM_FORMAT_##v] = d
+#define SUBFORMATD(v, d) [SND_PCM_SUBFORMAT_##v] = d 
 
-static assoc_t streams[] = {
+char *snd_pcm_stream_names[] = {
        STREAM(PLAYBACK),
        STREAM(CAPTURE),
-       END
 };
 
-static assoc_t states[] = {
+char *snd_pcm_state_names[] = {
        STATE(OPEN),
        STATE(SETUP),
        STATE(PREPARED),
        STATE(RUNNING),
        STATE(XRUN),
        STATE(PAUSED),
-       END
 };
 
-#if 0
-static assoc_t hw_params[] = {
+char *snd_pcm_hw_param_names[] = {
        HW_PARAM(ACCESS),
        HW_PARAM(FORMAT),
        HW_PARAM(SUBFORMAT),
@@ -550,10 +460,9 @@ static assoc_t hw_params[] = {
        HW_PARAM(FRAGMENT_SIZE),
        HW_PARAM(FRAGMENTS),
        HW_PARAM(BUFFER_SIZE),
-       END
 };
 
-static assoc_t sw_params[] = {
+char *snd_pcm_sw_param_names[] = {
        SW_PARAM(START_MODE),
        SW_PARAM(READY_MODE),
        SW_PARAM(AVAIL_MIN),
@@ -561,91 +470,122 @@ static assoc_t sw_params[] = {
        SW_PARAM(XFER_ALIGN),
        SW_PARAM(XRUN_MODE),
        SW_PARAM(TIME),
-       END
 };
-#endif
 
-static assoc_t accesses[] = {
+char *snd_pcm_access_names[] = {
        ACCESS(MMAP_INTERLEAVED), 
        ACCESS(MMAP_NONINTERLEAVED),
        ACCESS(MMAP_COMPLEX),
        ACCESS(RW_INTERLEAVED),
        ACCESS(RW_NONINTERLEAVED),
-       END
 };
 
-static assoc_t formats[] = {
-       FORMAT(S8, "Signed 8-bit"), 
-       FORMAT(U8, "Unsigned 8-bit"),
-       FORMAT(S16_LE, "Signed 16-bit Little Endian"),
-       FORMAT(S16_BE, "Signed 16-bit Big Endian"),
-       FORMAT(U16_LE, "Unsigned 16-bit Little Endian"),
-       FORMAT(U16_BE, "Unsigned 16-bit Big Endian"),
-       FORMAT(S24_LE, "Signed 24-bit Little Endian"),
-       FORMAT(S24_BE, "Signed 24-bit Big Endian"),
-       FORMAT(U24_LE, "Unsigned 24-bit Little Endian"),
-       FORMAT(U24_BE, "Unsigned 24-bit Big Endian"),
-       FORMAT(S32_LE, "Signed 32-bit Little Endian"),
-       FORMAT(S32_BE, "Signed 32-bit Big Endian"),
-       FORMAT(U32_LE, "Unsigned 32-bit Little Endian"),
-       FORMAT(U32_BE, "Unsigned 32-bit Big Endian"),
-       FORMAT(FLOAT_LE, "Float Little Endian"),
-       FORMAT(FLOAT_BE, "Float Big Endian"),
-       FORMAT(FLOAT64_LE, "Float64 Little Endian"),
-       FORMAT(FLOAT64_BE, "Float64 Big Endian"),
-       FORMAT(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"),
-       FORMAT(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"),
-       FORMAT(MU_LAW, "Mu-Law"),
-       FORMAT(A_LAW, "A-Law"),
-       FORMAT(IMA_ADPCM, "Ima-ADPCM"),
-       FORMAT(MPEG, "MPEG"),
-       FORMAT(GSM, "GSM"),
-       FORMAT(SPECIAL, "Special"),
-       END 
+char *snd_pcm_format_names[] = {
+       FORMAT(S8),
+       FORMAT(U8),
+       FORMAT(S16_LE),
+       FORMAT(S16_BE),
+       FORMAT(U16_LE),
+       FORMAT(U16_BE),
+       FORMAT(S24_LE),
+       FORMAT(S24_BE),
+       FORMAT(U24_LE),
+       FORMAT(U24_BE),
+       FORMAT(S32_LE),
+       FORMAT(S32_BE),
+       FORMAT(U32_LE),
+       FORMAT(U32_BE),
+       FORMAT(FLOAT_LE),
+       FORMAT(FLOAT_BE),
+       FORMAT(FLOAT64_LE),
+       FORMAT(FLOAT64_BE),
+       FORMAT(IEC958_SUBFRAME_LE),
+       FORMAT(IEC958_SUBFRAME_BE),
+       FORMAT(MU_LAW),
+       FORMAT(A_LAW),
+       FORMAT(IMA_ADPCM),
+       FORMAT(MPEG),
+       FORMAT(GSM),
+       FORMAT(SPECIAL),
+};
+
+char *snd_pcm_format_descriptions[] = {
+       FORMATD(S8, "Signed 8-bit"), 
+       FORMATD(U8, "Unsigned 8-bit"),
+       FORMATD(S16_LE, "Signed 16-bit Little Endian"),
+       FORMATD(S16_BE, "Signed 16-bit Big Endian"),
+       FORMATD(U16_LE, "Unsigned 16-bit Little Endian"),
+       FORMATD(U16_BE, "Unsigned 16-bit Big Endian"),
+       FORMATD(S24_LE, "Signed 24-bit Little Endian"),
+       FORMATD(S24_BE, "Signed 24-bit Big Endian"),
+       FORMATD(U24_LE, "Unsigned 24-bit Little Endian"),
+       FORMATD(U24_BE, "Unsigned 24-bit Big Endian"),
+       FORMATD(S32_LE, "Signed 32-bit Little Endian"),
+       FORMATD(S32_BE, "Signed 32-bit Big Endian"),
+       FORMATD(U32_LE, "Unsigned 32-bit Little Endian"),
+       FORMATD(U32_BE, "Unsigned 32-bit Big Endian"),
+       FORMATD(FLOAT_LE, "Float Little Endian"),
+       FORMATD(FLOAT_BE, "Float Big Endian"),
+       FORMATD(FLOAT64_LE, "Float64 Little Endian"),
+       FORMATD(FLOAT64_BE, "Float64 Big Endian"),
+       FORMATD(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"),
+       FORMATD(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"),
+       FORMATD(MU_LAW, "Mu-Law"),
+       FORMATD(A_LAW, "A-Law"),
+       FORMATD(IMA_ADPCM, "Ima-ADPCM"),
+       FORMATD(MPEG, "MPEG"),
+       FORMATD(GSM, "GSM"),
+       FORMATD(SPECIAL, "Special"),
+};
+
+char *snd_pcm_subformat_names[] = {
+       SUBFORMAT(STD), 
 };
 
-static assoc_t subformats[] = {
-       SUBFORMAT(STD, "Standard"), 
-       END
+char *snd_pcm_subformat_descriptions[] = {
+       SUBFORMATD(STD, "Standard"), 
 };
 
-static assoc_t starts[] = {
+char *snd_pcm_start_mode_names[] = {
        START(EXPLICIT),
        START(DATA),
-       END
 };
-static assoc_t readys[] = {
+
+char *snd_pcm_ready_mode_names[] = {
        READY(FRAGMENT),
        READY(ASAP),
-       END
 };
 
-static assoc_t xruns[] = {
+char *snd_pcm_xrun_mode_names[] = {
        XRUN(ASAP),
        XRUN(FRAGMENT),
        XRUN(NONE),
-       END
 };
 
-static assoc_t onoff[] = {
-       {0, "OFF", NULL},
-       {1, "ON", NULL},
-       {-1, "ON", NULL},
-       END
+static char *onoff[] = {
+       [0] = "OFF",
+       [1] = "ON",
 };
 
+#define assoc(value, names) ({ \
+       unsigned int __v = value; \
+       assert(__v < sizeof(names) / sizeof(names[0])); \
+       names[__v]; \
+})
+
+
 int snd_pcm_dump_hw_setup(snd_pcm_t *pcm, FILE *fp)
 {
        assert(pcm);
        assert(fp);
        assert(pcm->setup);
-        fprintf(fp, "stream       : %s\n", assoc(pcm->stream, streams));
-       fprintf(fp, "access       : %s\n", assoc(pcm->access, accesses));
-       fprintf(fp, "format       : %s\n", assoc(pcm->format, formats));
-       fprintf(fp, "subformat    : %s\n", assoc(pcm->subformat, subformats));
+        fprintf(fp, "stream       : %s\n", assoc(pcm->stream, snd_pcm_stream_names));
+       fprintf(fp, "access       : %s\n", assoc(pcm->access, snd_pcm_access_names));
+       fprintf(fp, "format       : %s\n", assoc(pcm->format, snd_pcm_format_names));
+       fprintf(fp, "subformat    : %s\n", assoc(pcm->subformat, snd_pcm_subformat_names));
        fprintf(fp, "channels     : %d\n", pcm->channels);
        fprintf(fp, "rate         : %d\n", pcm->rate);
-       fprintf(fp, "rate         : %g (%d/%d)\n", (double) pcm->rate_master / pcm->rate_divisor, pcm->rate_master, pcm->rate_divisor);
+       fprintf(fp, "exact rate   : %g (%d/%d)\n", (double) pcm->rate_master / pcm->rate_divisor, pcm->rate_master, pcm->rate_divisor);
        fprintf(fp, "msbits       : %d\n", pcm->msbits);
        fprintf(fp, "fragment_size: %ld\n", (long)pcm->fragment_size);
        fprintf(fp, "fragments    : %d\n", pcm->fragments);
@@ -657,9 +597,9 @@ int snd_pcm_dump_sw_setup(snd_pcm_t *pcm, FILE *fp)
        assert(pcm);
        assert(fp);
        assert(pcm->setup);
-       fprintf(fp, "start_mode   : %s\n", assoc(pcm->start_mode, starts));
-       fprintf(fp, "ready_mode   : %s\n", assoc(pcm->ready_mode, readys));
-       fprintf(fp, "xrun_mode    : %s\n", assoc(pcm->xrun_mode, xruns));
+       fprintf(fp, "start_mode   : %s\n", assoc(pcm->start_mode, snd_pcm_start_mode_names));
+       fprintf(fp, "ready_mode   : %s\n", assoc(pcm->ready_mode, snd_pcm_ready_mode_names));
+       fprintf(fp, "xrun_mode    : %s\n", assoc(pcm->xrun_mode, snd_pcm_xrun_mode_names));
        fprintf(fp, "avail_min    : %ld\n", (long)pcm->avail_min);
        fprintf(fp, "xfer_min     : %ld\n", (long)pcm->xfer_min);
        fprintf(fp, "xfer_align   : %ld\n", (long)pcm->xfer_align);
@@ -675,131 +615,26 @@ int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *fp)
        return 0;
 }
 
-int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp)
-{
-       unsigned int k;
-       fputs("access:", fp);
-       if (info->access_mask == ~0U)
-               fputs(" ALL", fp);
-       else if (info->access_mask) {
-               for (k = 0; k <= SND_PCM_ACCESS_LAST; ++k)
-                       if (info->access_mask & (1U << k)) {
-                               putc(' ', fp);
-                               fputs(assoc(k, accesses), fp);
-                       }
-       } else
-               fputs(" NONE", fp);
-       putc('\n', fp);
-
-       fputs("format:", fp);
-       if (info->format_mask == ~0U)
-               fputs(" ALL", fp);
-       else if (info->format_mask) {
-               for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k)
-                       if (info->format_mask & (1U << k)) {
-                               putc(' ', fp);
-                               fputs(assoc(k, formats), fp);
-                       }
-       } else
-               fputs(" NONE", fp);
-       putc('\n', fp);
-       
-       fputs("subformat:", fp);
-       if (info->subformat_mask == ~0U)
-               fputs(" ALL", fp);
-       else if (info->subformat_mask) {
-               for (k = 0; k <= SND_PCM_SUBFORMAT_LAST; ++k)
-                       if (info->subformat_mask & (1U << k)) {
-                               putc(' ', fp);
-                               fputs(assoc(k, subformats), fp);
-                       }
-       } else
-               fputs(" NONE", fp);
-       putc('\n', fp);
-
-       fputs("channels: ", fp);
-       if (info->channels_min <= 1 && info->channels_max == UINT_MAX)
-               fputs("ALL", fp);
-       else if (info->channels_min > info->channels_max)
-               fputs("NONE", fp);
-       else {
-               fprintf(fp, "%u", info->channels_min);
-               if (info->channels_min < info->channels_max)
-                       fprintf(fp, " - %u", info->channels_max);
-       }
-       putc('\n', fp);
-
-       fputs("rate: ", fp);
-       if (info->rate_min <= 1 && info->rate_max == UINT_MAX)
-               fputs("ALL", fp);
-       else if (info->rate_min > info->rate_max)
-               fputs("NONE", fp);
-       else {
-               fprintf(fp, "%u", info->rate_min);
-               if (info->rate_min < info->rate_max)
-                       fprintf(fp, " - %u", info->rate_max);
-       }
-       putc('\n', fp);
-
-       fputs("fragment_size: ", fp);
-       if (info->fragment_size_min <= 1 && 
-           info->fragment_size_max == ULONG_MAX)
-               fputs("ALL", fp);
-       else if (info->fragment_size_min > info->fragment_size_max)
-               fputs("NONE", fp);
-       else {
-               fprintf(fp, "%lu", (unsigned long)info->fragment_size_min);
-               if (info->fragment_size_min < info->fragment_size_max)
-                       fprintf(fp, " - %lu", (unsigned long)info->fragment_size_max);
-       }
-       putc('\n', fp);
-
-       fputs("fragments: ", fp);
-       if (info->fragments_min <= 1 && info->fragments_max == UINT_MAX)
-               fputs("ALL", fp);
-       else if (info->fragments_min > info->fragments_max)
-               fputs("NONE", fp);
-       else {
-               fprintf(fp, "%u", info->fragments_min);
-               if (info->fragments_min < info->fragments_max)
-                       fprintf(fp, " - %u", info->fragments_max);
-       }
-       putc('\n', fp);
-
-       fputs("buffer_size: ", fp);
-       if (info->buffer_size_min <= 1 && 
-           info->buffer_size_max == ULONG_MAX)
-               fputs("ALL", fp);
-       else if (info->buffer_size_min > info->buffer_size_max)
-               fputs("NONE", fp);
-       else {
-               fprintf(fp, "%lu", (unsigned long)info->buffer_size_min);
-               if (info->buffer_size_min < info->buffer_size_max)
-                       fprintf(fp, " - %lu", (unsigned long)info->buffer_size_max);
-       }
-       putc('\n', fp);
-
-       return 0;
-}
-
 int snd_pcm_dump_hw_params_fail(snd_pcm_hw_params_t *params, FILE *fp)
 {
        int k;
-       if (params->fail_mask == 0)
+       if (params->fail_mask == 0) {
+               fprintf(fp, "unknown hw_params failure reason\n");
                return 0;
+       }
        fprintf(fp, "hw_params failed on the following field value(s):\n");
        for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) {
                if (!(params->fail_mask & (1U << k)))
                        continue;
                switch (k) {
                case SND_PCM_HW_PARAM_ACCESS:
-                       fprintf(fp, "access: %s\n", assoc(params->access, accesses));
+                       fprintf(fp, "access: %s\n", assoc(params->access, snd_pcm_access_names));
                        break;
                case SND_PCM_HW_PARAM_FORMAT:
-                       fprintf(fp, "format: %s\n", assoc(params->format, formats));
+                       fprintf(fp, "format: %s\n", assoc(params->format, snd_pcm_format_names));
                        break;
                case SND_PCM_HW_PARAM_SUBFORMAT:
-                       fprintf(fp, "subformat: %s\n", assoc(params->subformat, subformats));
+                       fprintf(fp, "subformat: %s\n", assoc(params->subformat, snd_pcm_subformat_names));
                        break;
                case SND_PCM_HW_PARAM_CHANNELS:
                        fprintf(fp, "channels: %d\n", params->channels);
@@ -824,21 +659,23 @@ int snd_pcm_dump_hw_params_fail(snd_pcm_hw_params_t *params, FILE *fp)
 int snd_pcm_dump_sw_params_fail(snd_pcm_sw_params_t *params, FILE *fp)
 {
        int k;
-       if (params->fail_mask == 0)
+       if (params->fail_mask == 0) {
+               fprintf(fp, "unknown sw_params failure reason\n");
                return 0;
+       }
        fprintf(fp, "sw_params failed on the following field value(s):\n");
        for (k = 0; k <= SND_PCM_SW_PARAM_LAST; ++k) {
                if (!(params->fail_mask & (1U << k)))
                        continue;
                switch (k) {
                case SND_PCM_SW_PARAM_START_MODE:
-                       fprintf(fp, "start_mode: %s\n", assoc(params->start_mode, starts));
+                       fprintf(fp, "start_mode: %s\n", assoc(params->start_mode, snd_pcm_start_mode_names));
                        break;
                case SND_PCM_SW_PARAM_READY_MODE:
-                       fprintf(fp, "ready_mode: %s\n", assoc(params->ready_mode, readys));
+                       fprintf(fp, "ready_mode: %s\n", assoc(params->ready_mode, snd_pcm_ready_mode_names));
                        break;
                case SND_PCM_SW_PARAM_XRUN_MODE:
-                       fprintf(fp, "xrun_mode: %s\n", assoc(params->xrun_mode, xruns));
+                       fprintf(fp, "xrun_mode: %s\n", assoc(params->xrun_mode, snd_pcm_xrun_mode_names));
                        break;
                case SND_PCM_SW_PARAM_AVAIL_MIN:
                        fprintf(fp, "avail_min: %ld\n", (long)params->avail_min);
@@ -863,7 +700,7 @@ int snd_pcm_dump_sw_params_fail(snd_pcm_sw_params_t *params, FILE *fp)
 int snd_pcm_dump_status(snd_pcm_status_t *status, FILE *fp)
 {
        assert(status);
-       fprintf(fp, "state       : %s\n", assoc(status->state, states));
+       fprintf(fp, "state       : %s\n", assoc(status->state, snd_pcm_state_names));
        fprintf(fp, "trigger_time: %ld.%06ld\n",
                status->trigger_time.tv_sec, status->trigger_time.tv_usec);
        fprintf(fp, "tstamp      : %ld.%06ld\n",
@@ -882,27 +719,25 @@ int snd_pcm_dump(snd_pcm_t *pcm, FILE *fp)
        return 0;
 }
 
-const char *snd_pcm_format_name(int format)
+const char *snd_pcm_format_name(unsigned int format)
 {
-       assoc_t *a = assoc_value(format, formats);
-       if (a)
-               return a->name;
-       return 0;
+       assert(format <= SND_PCM_FORMAT_LAST);
+       return snd_pcm_format_names[format];
 }
 
-const char *snd_pcm_format_description(int format)
+const char *snd_pcm_format_description(unsigned int format)
 {
-       assoc_t *a = assoc_value(format, formats);
-       if (a)
-               return a->desc;
-       return "Unknown";
+       assert(format <= SND_PCM_FORMAT_LAST);
+       return snd_pcm_format_descriptions[format];
 }
 
 int snd_pcm_format_value(const char* name)
 {
-       assoc_t *a = assoc_name(name, formats);
-       if (a)
-               return a->value;
+       unsigned int format;
+       for (format = 0; format <= SND_PCM_FORMAT_LAST; format++)
+               if (snd_pcm_format_names[format] &&
+                   strcasecmp(name, snd_pcm_format_names[format]) == 0)
+                       return format;
        return -1;
 }
 
@@ -1621,1352 +1456,304 @@ struct {
 
 #define SND_PCM_RATES (sizeof(snd_pcm_rates) / sizeof(snd_pcm_rates[0]))
 
-int snd_pcm_hw_info_rules_access(snd_pcm_t *pcm, 
-                                snd_pcm_hw_info_t *info,
-                                snd_pcm_hw_params_t *params,
-                                unsigned int count, int *rules)
+int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
+                          snd_pcm_hw_info_t *info)
 {
-       int k;
-       unsigned int rel, mask;
-       snd_pcm_hw_info_t i;
-       rel = *rules & SND_PCM_RULE_REL_MASK;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LT:
-               mask = (1U << params->access) - 1;
+       int err = snd_pcm_hw_info_to_params(pcm, info, params);
+       assert(err >= 0);
+       return snd_pcm_hw_params(pcm, params);
+}
+
+struct _snd_pcm_strategy {
+       unsigned int badness_min, badness_max;
+       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;
+       unsigned int order;
+       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;
+}
+
+typedef struct {
+       enum { MASK, MINMAX } type;
+       char **names;
+       unsigned int last;
+} par_desc_t;
+
+par_desc_t hw_pars[SND_PCM_HW_PARAM_LAST + 1] = {
+       [SND_PCM_HW_PARAM_ACCESS] = {
+               type: MASK,
+               names: snd_pcm_access_names,
+               last: SND_PCM_ACCESS_LAST,
+       },
+       [SND_PCM_HW_PARAM_FORMAT] = {
+               type: MASK,
+               names: snd_pcm_format_names,
+               last: SND_PCM_FORMAT_LAST,
+       },
+       [SND_PCM_HW_PARAM_SUBFORMAT] = {
+               type: MASK,
+               names: snd_pcm_subformat_names,
+               last: SND_PCM_SUBFORMAT_LAST,
+       },
+       [SND_PCM_HW_PARAM_CHANNELS] = {
+               type: MINMAX,
+               names: 0,
+               last: 0,
+       },
+       [SND_PCM_HW_PARAM_RATE] = {
+               type: MINMAX,
+               names: 0,
+               last: 0,
+       },
+       [SND_PCM_HW_PARAM_FRAGMENT_SIZE] = {
+               type: MINMAX,
+               names: 0,
+               last: 0,
+       },
+       [SND_PCM_HW_PARAM_FRAGMENTS] = {
+               type: MINMAX,
+               names: 0,
+               last: 0,
+       },
+       [SND_PCM_HW_PARAM_BUFFER_SIZE] = {
+               type: MINMAX,
+               names: 0,
+               last: 0,
+       },
+};
+
+unsigned int snd_pcm_hw_info_par_get_mask(const snd_pcm_hw_info_t *info,
+                                         unsigned int param)
+{
+       switch (param) {
+       case SND_PCM_HW_PARAM_ACCESS:
+               return info->access_mask;
+       case SND_PCM_HW_PARAM_FORMAT:
+               return info->format_mask;
+       case SND_PCM_HW_PARAM_SUBFORMAT:
+               return info->subformat_mask;
+       default:
+               assert(0);
+               return 0;
+       }
+}
+       
+void snd_pcm_hw_info_par_get_minmax(const snd_pcm_hw_info_t *info,
+                                   unsigned int param,
+                                   unsigned long *min, unsigned long *max)
+{
+       switch (param) {
+       case SND_PCM_HW_PARAM_ACCESS:
+       case SND_PCM_HW_PARAM_FORMAT:
+       case SND_PCM_HW_PARAM_SUBFORMAT:
+       {
+               unsigned int mask = snd_pcm_hw_info_par_get_mask(info, param);
+               if (!mask) {
+                       *min = 32;
+                       *max = 0;
+               } else {
+                       *min = ffs(mask) - 1;
+                       *max = ld2(mask);
+               }
                break;
-       case SND_PCM_RULE_REL_LE:
-               mask = (1U << (params->access + 1)) - 1;
+       }
+       case SND_PCM_HW_PARAM_CHANNELS:
+               *min = info->channels_min;
+               *max = info->channels_max;
                break;
-       case SND_PCM_RULE_REL_GT:
-               mask = ~((1U << (params->access + 1)) - 1);
+       case SND_PCM_HW_PARAM_RATE:
+               *min = info->rate_min;
+               *max = info->rate_max;
                break;
-       case SND_PCM_RULE_REL_GE:
-               mask = ~((1U << params->access) - 1);
+       case SND_PCM_HW_PARAM_FRAGMENT_SIZE:
+               *min = info->fragment_size_min;
+               *max = info->fragment_size_max;
                break;
-       case SND_PCM_RULE_REL_EQ:
-               mask = 1U << params->access;
+       case SND_PCM_HW_PARAM_FRAGMENTS:
+               *min = info->fragments_min;
+               *max = info->fragments_max;
                break;
-       case SND_PCM_RULE_REL_NEAR:
-       {
-               unsigned int diff = 0;
-               int n;
-               for (diff = 0; diff < 32; ++diff) {
-                       n = (int)params->access - (int)diff;
-                       if (n >= 0) {
-                               unsigned int bit = 1U << n;
-                               if (info->access_mask & bit) {
-                                       i = *info;
-                                       i.access_mask = bit;
-                                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                               *info = i;
-                                               return 0;
-                                       }
-                               }
-                       } else if (params->access + diff > SND_PCM_ACCESS_LAST)
-                               break;
-                       if (diff == 0)
-                               continue;
-                       n = params->access + diff;
-                       if (n <= SND_PCM_ACCESS_LAST) {
-                               unsigned int bit = 1U << n;
-                               if (info->access_mask & bit) {
-                                       i = *info;
-                                       i.access_mask = bit;
-                                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                               *info = i;
-                                               return 0;
-                                       }
-                               }
-                       }
-               }
-               info->access_mask = 0;
-               return -EINVAL;
-       }
-       case SND_PCM_RULE_REL_BITS:
-               mask = params->access;
+       case SND_PCM_HW_PARAM_BUFFER_SIZE:
+               *min = info->buffer_size_min;
+               *max = info->buffer_size_max;
                break;
        default:
                assert(0);
-               return -EINVAL;
        }
-       info->access_mask &= mask;
-       if (info->access_mask == 0)
-               return -EINVAL;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LE:
-       case SND_PCM_RULE_REL_LT:
-               for (k = SND_PCM_ACCESS_LAST; k >= 0; --k) {
-                       if (!(info->access_mask & (1U << k)))
-                               continue;
-                       i = *info;
-                       i.access_mask = 1U << k;
-                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               info->access_mask = 0;
-               return -EINVAL;
-       default:
-               for (k = 0; k <= SND_PCM_ACCESS_LAST; ++k) {
-                       if (!(info->access_mask & (1U << k)))
-                               continue;
-                       i = *info;
-                       i.access_mask = 1U << k;
-                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               info->access_mask = 0;
-               return -EINVAL;
-       }               
-       return 0;
 }
 
-int snd_pcm_hw_info_rules_format(snd_pcm_t *pcm, 
-                                snd_pcm_hw_info_t *info,
-                                snd_pcm_hw_params_t *params,
-                                unsigned int count, int *rules)
+void snd_pcm_hw_info_par_set_mask(snd_pcm_hw_info_t *info, unsigned int param,
+                                 unsigned int v)
 {
-       int k;
-       unsigned int rel, mask;
-       snd_pcm_hw_info_t i;
-       rel = *rules & SND_PCM_RULE_REL_MASK;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LT:
-               mask = (1U << params->format) - 1;
-               break;
-       case SND_PCM_RULE_REL_LE:
-               mask = (1U << (params->format + 1)) - 1;
-               break;
-       case SND_PCM_RULE_REL_GT:
-               mask = ~((1U << (params->format + 1)) - 1);
+       switch (param) {
+       case SND_PCM_HW_PARAM_ACCESS:
+               info->access_mask = v;
                break;
-       case SND_PCM_RULE_REL_GE:
-               mask = ~((1U << params->format) - 1);
+       case SND_PCM_HW_PARAM_FORMAT:
+               info->format_mask = v;
                break;
-       case SND_PCM_RULE_REL_EQ:
-               mask = 1U << params->format;
+       case SND_PCM_HW_PARAM_SUBFORMAT:
+               info->subformat_mask = v;
                break;
-       case SND_PCM_RULE_REL_NEAR:
-       {
-               unsigned int diff = 0;
-               int n;
-               for (diff = 0; diff < 32; ++diff) {
-                       n = (int)params->format - (int)diff;
-                       if (n >= 0) {
-                               unsigned int bit = 1U << n;
-                               if (info->format_mask & bit) {
-                                       i = *info;
-                                       i.format_mask = bit;
-                                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                               *info = i;
-                                               return 0;
-                                       }
-                               }
-                       } else if (params->format + diff > SND_PCM_FORMAT_LAST)
-                               break;
-                       if (diff == 0)
-                               continue;
-                       n = params->format + diff;
-                       if (n <= SND_PCM_FORMAT_LAST) {
-                               unsigned int bit = 1U << n;
-                               if (info->format_mask & bit) {
-                                       i = *info;
-                                       i.format_mask = bit;
-                                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                               *info = i;
-                                               return 0;
-                                       }
-                               }
-                       }
-               }
-               info->format_mask = 0;
-               return -EINVAL;
-       }
-       case SND_PCM_RULE_REL_BITS:
-               mask = params->format;
-               break;
-       default:
-               assert(0);
-               return -EINVAL;
-       }
-       info->format_mask &= mask;
-       if (info->format_mask == 0)
-               return -EINVAL;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LE:
-       case SND_PCM_RULE_REL_LT:
-               for (k = SND_PCM_FORMAT_LAST; k >= 0; --k) {
-                       if (!(info->format_mask & (1U << k)))
-                               continue;
-                       i = *info;
-                       i.format_mask = 1U << k;
-                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               info->format_mask = 0;
-               return -EINVAL;
-       default:
-               for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) {
-                       if (!(info->format_mask & (1U << k)))
-                               continue;
-                       i = *info;
-                       i.format_mask = 1U << k;
-                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               info->format_mask = 0;
-               return -EINVAL;
-       }               
-       return 0;
-}
-
-int snd_pcm_hw_info_rules_subformat(snd_pcm_t *pcm, 
-                                   snd_pcm_hw_info_t *info,
-                                   snd_pcm_hw_params_t *params,
-                                   unsigned int count, int *rules)
-{
-       int k;
-       unsigned int rel, mask;
-       snd_pcm_hw_info_t i;
-       rel = *rules & SND_PCM_RULE_REL_MASK;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LT:
-               mask = (1U << params->subformat) - 1;
-               break;
-       case SND_PCM_RULE_REL_LE:
-               mask = (1U << (params->subformat + 1)) - 1;
-               break;
-       case SND_PCM_RULE_REL_GT:
-               mask = ~((1U << (params->subformat + 1)) - 1);
-               break;
-       case SND_PCM_RULE_REL_GE:
-               mask = ~((1U << params->subformat) - 1);
-               break;
-       case SND_PCM_RULE_REL_EQ:
-               mask = 1U << params->subformat;
-               break;
-       case SND_PCM_RULE_REL_NEAR:
-       {
-               unsigned int diff = 0;
-               int n;
-               for (diff = 0; diff < 32; ++diff) {
-                       n = (int)params->subformat - (int)diff;
-                       if (n >= 0) {
-                               unsigned int bit = 1U << n;
-                               if (info->subformat_mask & bit) {
-                                       i = *info;
-                                       i.subformat_mask = bit;
-                                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                               *info = i;
-                                               return 0;
-                                       }
-                               }
-                       } else if (params->subformat + diff > SND_PCM_SUBFORMAT_LAST)
-                               break;
-                       if (diff == 0)
-                               continue;
-                       n = params->subformat + diff;
-                       if (n <= SND_PCM_SUBFORMAT_LAST) {
-                               unsigned int bit = 1U << n;
-                               if (info->subformat_mask & bit) {
-                                       i = *info;
-                                       i.subformat_mask = bit;
-                                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                               *info = i;
-                                               return 0;
-                                       }
-                               }
-                       }
-               }
-               info->subformat_mask = 0;
-               return -EINVAL;
-       }
-       case SND_PCM_RULE_REL_BITS:
-               mask = params->subformat;
-               break;
-       default:
-               assert(0);
-               return -EINVAL;
-       }
-       info->subformat_mask &= mask;
-       if (info->subformat_mask == 0)
-               return -EINVAL;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LE:
-       case SND_PCM_RULE_REL_LT:
-               for (k = SND_PCM_SUBFORMAT_LAST; k >= 0; --k) {
-                       if (!(info->subformat_mask & (1U << k)))
-                               continue;
-                       i = *info;
-                       i.subformat_mask = 1U << k;
-                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               info->subformat_mask = 0;
-               return -EINVAL;
-       default:
-               for (k = 0; k <= SND_PCM_SUBFORMAT_LAST; ++k) {
-                       if (!(info->subformat_mask & (1U << k)))
-                               continue;
-                       i = *info;
-                       i.subformat_mask = 1U << k;
-                       if (snd_pcm_hw_info(pcm, &i) >= 0 &&
-                           snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               info->subformat_mask = 0;
-               return -EINVAL;
-       }               
-       return 0;
-}
-
-int snd_pcm_hw_info_rules_channels(snd_pcm_t *pcm, 
-                                  snd_pcm_hw_info_t *info,
-                                  snd_pcm_hw_params_t *params,
-                                  unsigned int count, int *rules)
-{
-       int err;
-       unsigned int rel;
-       snd_pcm_hw_info_t i;
-       rel = *rules & SND_PCM_RULE_REL_MASK;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LT:
-               if (info->channels_max > params->channels - 1)
-                       info->channels_max = params->channels - 1;
-               goto _le;
-       case SND_PCM_RULE_REL_LE:
-               if (info->channels_max > params->channels)
-                       info->channels_max = params->channels;
-       _le:
-               while (1) {
-                       if (info->channels_min > info->channels_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->channels_min = i.channels_min;
-                               info->channels_max = i.channels_max;
-                               return err;
-                       }
-                       i.channels_min = i.channels_max;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       info->channels_max--;
-               }
-               break;
-       case SND_PCM_RULE_REL_GT:
-               if (info->channels_min < params->channels + 1)
-                       info->channels_min = params->channels + 1;
-               goto _ge;
-       case SND_PCM_RULE_REL_GE:
-               if (info->channels_min < params->channels)
-                       info->channels_min = params->channels;
-       _ge:
-               while (1) {
-                       if (info->channels_min > info->channels_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->channels_min = i.channels_min;
-                               info->channels_max = i.channels_max;
-                               return err;
-                       }
-                       i.channels_max = i.channels_min;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       info->channels_min++;
-               }
-               break;
-       case SND_PCM_RULE_REL_EQ:
-               info->channels_min = params->channels;
-               info->channels_max = params->channels;
-               return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1);
-               break;
-       case SND_PCM_RULE_REL_NEAR:
-       {
-               unsigned int max1, min2;
-               int err1 = -EINVAL, err2 = -EINVAL;
-               max1 = params->channels;
-               min2 = params->channels+1;
-               if (info->channels_min <= max1) {
-                       i = *info;
-                       i.channels_max = max1;
-                       err1 = snd_pcm_hw_info(pcm, &i);
-                       /* shortcut for common case */
-                       if (err1 >= 0 && max1 == i.channels_max) {
-                               i.channels_min = max1;
-                               if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                       *info = i;
-                                       return 0;
-                               }
-                               i.channels_max = max1 - 1;
-                               err1 = snd_pcm_hw_info(pcm, &i);
-                       }
-                       max1 = i.channels_max;
-               }
-               if (min2 <= info->channels_max) {
-                       i = *info;
-                       i.channels_min = min2;
-                       err2 = snd_pcm_hw_info(pcm, &i);
-                       min2 = i.channels_min;
-               }
-               while (1) {
-                       unsigned int channels;
-                       if (err1 >= 0) {
-                               if (err2 >= 0) {
-                                       if (params->channels - max1 < 
-                                           min2 - params->channels)
-                                               channels = max1;
-                                       else
-                                               channels = min2;
-                               } else
-                                       channels = max1;
-                       } else if (err2 >= 0)
-                               channels = min2;
-                       else {
-                               info->channels_min = UINT_MAX;
-                               info->channels_max = 0;
-                               return -EINVAL;
-                       }
-                       i = *info;
-                       i.channels_min = i.channels_max = channels;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       if (channels == max1) {
-                               max1--;
-                               i = *info;
-                               i.channels_max = max1;
-                               err1 = snd_pcm_hw_info(pcm, &i);
-                               max1 = i.channels_max;
-                       } else {
-                               min2++;
-                               i = *info;
-                               i.channels_min = min2;
-                               err2 = snd_pcm_hw_info(pcm, &i);
-                               min2 = i.channels_min;
-                       }
-                               
-               }
-               break;
-       }
-       case SND_PCM_RULE_REL_BITS:
-       {
-               unsigned int k;
-               for (k = info->channels_min; k < 32; ++k) {
-                       if (!(params->channels & (1U << k)))
-                               continue;
-                       info->channels_min = k;
-                       if (info->channels_min > info->channels_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->channels_min = i.channels_min;
-                               info->channels_max = i.channels_max;
-                               return err;
-                       }
-                       i.channels_max = i.channels_min;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               break;
-       }
-       default:
-               assert(0);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-int snd_pcm_hw_info_rules_rate(snd_pcm_t *pcm, 
-                              snd_pcm_hw_info_t *info,
-                              snd_pcm_hw_params_t *params,
-                              unsigned int count, int *rules)
-{
-       int err;
-       unsigned int rel;
-       snd_pcm_hw_info_t i;
-       rel = *rules & SND_PCM_RULE_REL_MASK;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LT:
-               if (info->rate_max > params->rate - 1)
-                       info->rate_max = params->rate - 1;
-               goto _le;
-       case SND_PCM_RULE_REL_LE:
-               if (info->rate_max > params->rate)
-                       info->rate_max = params->rate;
-       _le:
-               while (1) {
-                       if (info->rate_min > info->rate_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->rate_min = i.rate_min;
-                               info->rate_max = i.rate_max;
-                               return err;
-                       }
-                       i.rate_min = i.rate_max;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       info->rate_max--;
-               }
-               break;
-       case SND_PCM_RULE_REL_GT:
-               if (info->rate_min < params->rate + 1)
-                       info->rate_min = params->rate + 1;
-               goto _ge;
-       case SND_PCM_RULE_REL_GE:
-               if (info->rate_min < params->rate)
-                       info->rate_min = params->rate;
-       _ge:
-               while (1) {
-                       if (info->rate_min > info->rate_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->rate_min = i.rate_min;
-                               info->rate_max = i.rate_max;
-                               return err;
-                       }
-                       i.rate_max = i.rate_min;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       info->rate_min++;
-               }
-               break;
-       case SND_PCM_RULE_REL_EQ:
-               info->rate_min = params->rate;
-               info->rate_max = params->rate;
-               return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1);
-               break;
-       case SND_PCM_RULE_REL_NEAR:
-       {
-               unsigned int max1, min2;
-               int err1 = -EINVAL, err2 = -EINVAL;
-               max1 = params->rate;
-               min2 = params->rate+1;
-               if (info->rate_min <= max1) {
-                       i = *info;
-                       i.rate_max = max1;
-                       err1 = snd_pcm_hw_info(pcm, &i);
-                       /* shortcut for common case */
-                       if (err1 >= 0 && max1 == i.rate_max) {
-                               i.rate_min = max1;
-                               if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                       *info = i;
-                                       return 0;
-                               }
-                               i.rate_max = max1 - 1;
-                               err1 = snd_pcm_hw_info(pcm, &i);
-                       }
-                       max1 = i.rate_max;
-               }
-               if (min2 <= info->rate_max) {
-                       i = *info;
-                       i.rate_min = min2;
-                       err2 = snd_pcm_hw_info(pcm, &i);
-                       min2 = i.rate_min;
-               }
-               while (1) {
-                       unsigned int rate;
-                       if (err1 >= 0) {
-                               if (err2 >= 0) {
-                                       if (params->rate - max1 < 
-                                           min2 - params->rate)
-                                               rate = max1;
-                                       else
-                                               rate = min2;
-                               } else
-                                       rate = max1;
-                       } else if (err2 >= 0)
-                               rate = min2;
-                       else {
-                               info->rate_min = UINT_MAX;
-                               info->rate_max = 0;
-                               return -EINVAL;
-                       }
-                       i = *info;
-                       i.rate_min = i.rate_max = rate;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       if (rate == max1) {
-                               max1--;
-                               i = *info;
-                               i.rate_max = max1;
-                               err1 = snd_pcm_hw_info(pcm, &i);
-                               max1 = i.rate_max;
-                       } else {
-                               min2++;
-                               i = *info;
-                               i.rate_min = min2;
-                               err2 = snd_pcm_hw_info(pcm, &i);
-                               min2 = i.rate_min;
-                       }
-                               
-               }
-               break;
-       }
-       case SND_PCM_RULE_REL_BITS:
-       {
-               unsigned int k;
-               for (k = 0; k < SND_PCM_RATES; ++k) {
-                       if (snd_pcm_rates[k].rate < info->rate_min)
-                               continue;
-                       if (!(params->rate & snd_pcm_rates[k].flag))
-                               continue;
-                       info->rate_min = snd_pcm_rates[k].rate;
-                       if (info->rate_min > info->rate_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->rate_min = i.rate_min;
-                               info->rate_max = i.rate_max;
-                               return err;
-                       }
-                       i.rate_max = i.rate_min;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               break;
-       }
-       default:
-               assert(0);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-int snd_pcm_hw_info_rules_fragment_size(snd_pcm_t *pcm, 
-                                       snd_pcm_hw_info_t *info,
-                                       snd_pcm_hw_params_t *params,
-                                       unsigned int count, int *rules)
-{
-       int err;
-       unsigned int rel;
-       snd_pcm_hw_info_t i;
-       rel = *rules & SND_PCM_RULE_REL_MASK;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LT:
-               if (info->fragment_size_max > params->fragment_size - 1)
-                       info->fragment_size_max = params->fragment_size - 1;
-               goto _le;
-       case SND_PCM_RULE_REL_LE:
-               if (info->fragment_size_max > params->fragment_size)
-                       info->fragment_size_max = params->fragment_size;
-       _le:
-               while (1) {
-                       if (info->fragment_size_min > info->fragment_size_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->fragment_size_min = i.fragment_size_min;
-                               info->fragment_size_max = i.fragment_size_max;
-                               return err;
-                       }
-                       i.fragment_size_min = i.fragment_size_max;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       info->fragment_size_max--;
-               }
-               break;
-       case SND_PCM_RULE_REL_GT:
-               if (info->fragment_size_min < params->fragment_size + 1)
-                       info->fragment_size_min = params->fragment_size + 1;
-               goto _ge;
-       case SND_PCM_RULE_REL_GE:
-               if (info->fragment_size_min < params->fragment_size)
-                       info->fragment_size_min = params->fragment_size;
-       _ge:
-               while (1) {
-                       if (info->fragment_size_min > info->fragment_size_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->fragment_size_min = i.fragment_size_min;
-                               info->fragment_size_max = i.fragment_size_max;
-                               return err;
-                       }
-                       i.fragment_size_max = i.fragment_size_min;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       info->fragment_size_min++;
-               }
-               break;
-       case SND_PCM_RULE_REL_EQ:
-               info->fragment_size_min = params->fragment_size;
-               info->fragment_size_max = params->fragment_size;
-               return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1);
-               break;
-       case SND_PCM_RULE_REL_NEAR:
-       {
-               size_t max1, min2;
-               int err1 = -EINVAL, err2 = -EINVAL;
-               max1 = params->fragment_size;
-               min2 = params->fragment_size+1;
-               if (info->fragment_size_min <= max1) {
-                       i = *info;
-                       i.fragment_size_max = max1;
-                       err1 = snd_pcm_hw_info(pcm, &i);
-                       /* shortcut for common case */
-                       if (err1 >= 0 && max1 == i.fragment_size_max) {
-                               i.fragment_size_min = max1;
-                               if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                       *info = i;
-                                       return 0;
-                               }
-                               i.fragment_size_max = max1 - 1;
-                               err1 = snd_pcm_hw_info(pcm, &i);
-                       }
-                       max1 = i.fragment_size_max;
-               }
-               if (min2 <= info->fragment_size_max) {
-                       i = *info;
-                       i.fragment_size_min = min2;
-                       err2 = snd_pcm_hw_info(pcm, &i);
-                       min2 = i.fragment_size_min;
-               }
-               while (1) {
-                       size_t fragment_size;
-                       if (err1 >= 0) {
-                               if (err2 >= 0) {
-                                       if (params->fragment_size - max1 < 
-                                           min2 - params->fragment_size)
-                                               fragment_size = max1;
-                                       else
-                                               fragment_size = min2;
-                               } else
-                                       fragment_size = max1;
-                       } else if (err2 >= 0)
-                               fragment_size = min2;
-                       else {
-                               info->fragment_size_min = ULONG_MAX;
-                               info->fragment_size_max = 0;
-                               return -EINVAL;
-                       }
-                       i = *info;
-                       i.fragment_size_min = i.fragment_size_max = fragment_size;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       if (fragment_size == max1) {
-                               max1--;
-                               i = *info;
-                               i.fragment_size_max = max1;
-                               err1 = snd_pcm_hw_info(pcm, &i);
-                               max1 = i.fragment_size_max;
-                       } else {
-                               min2++;
-                               i = *info;
-                               i.fragment_size_min = min2;
-                               err2 = snd_pcm_hw_info(pcm, &i);
-                               min2 = i.fragment_size_min;
-                       }
-                               
-               }
-               break;
-       }
-       case SND_PCM_RULE_REL_BITS:
-       {
-               unsigned int k;
-               for (k = info->fragment_size_min; k < 32; ++k) {
-                       if (!(params->fragment_size & (1U << k)))
-                               continue;
-                       info->fragment_size_min = 1U << k;
-                       if (info->fragment_size_min > info->fragment_size_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->fragment_size_min = i.fragment_size_min;
-                               info->fragment_size_max = i.fragment_size_max;
-                               return err;
-                       }
-                       i.fragment_size_max = i.fragment_size_min;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               break;
-       }
-       default:
-               assert(0);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-int snd_pcm_hw_info_rules_fragments(snd_pcm_t *pcm, 
-                                   snd_pcm_hw_info_t *info,
-                                   snd_pcm_hw_params_t *params,
-                                   unsigned int count, int *rules)
-{
-       int err;
-       unsigned int rel;
-       snd_pcm_hw_info_t i;
-       rel = *rules & SND_PCM_RULE_REL_MASK;
-       switch (rel) {
-       case SND_PCM_RULE_REL_LT:
-               if (info->fragments_max > params->fragments - 1)
-                       info->fragments_max = params->fragments - 1;
-               goto _le;
-       case SND_PCM_RULE_REL_LE:
-               if (info->fragments_max > params->fragments)
-                       info->fragments_max = params->fragments;
-       _le:
-               while (1) {
-                       if (info->fragments_min > info->fragments_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->fragments_min = i.fragments_min;
-                               info->fragments_max = i.fragments_max;
-                               return err;
-                       }
-                       i.fragments_min = i.fragments_max;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       info->fragments_max--;
-               }
-               break;
-       case SND_PCM_RULE_REL_GT:
-               if (info->fragments_min < params->fragments + 1)
-                       info->fragments_min = params->fragments + 1;
-               goto _ge;
-       case SND_PCM_RULE_REL_GE:
-               if (info->fragments_min < params->fragments)
-                       info->fragments_min = params->fragments;
-       _ge:
-               while (1) {
-                       if (info->fragments_min > info->fragments_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->fragments_min = i.fragments_min;
-                               info->fragments_max = i.fragments_max;
-                               return err;
-                       }
-                       i.fragments_max = i.fragments_min;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       info->fragments_min++;
-               }
-               break;
-       case SND_PCM_RULE_REL_EQ:
-               info->fragments_min = params->fragments;
-               info->fragments_max = params->fragments;
-               return snd_pcm_hw_info_rules(pcm, info, params, count - 1, rules + 1);
-               break;
-       case SND_PCM_RULE_REL_NEAR:
-       {
-               unsigned int max1, min2;
-               int err1 = -EINVAL, err2 = -EINVAL;
-               max1 = params->fragments;
-               min2 = params->fragments+1;
-               if (info->fragments_min <= max1) {
-                       i = *info;
-                       i.fragments_max = max1;
-                       err1 = snd_pcm_hw_info(pcm, &i);
-                       /* shortcut for common case */
-                       if (err1 >= 0 && max1 == i.fragments_max) {
-                               i.fragments_min = max1;
-                               if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                                       *info = i;
-                                       return 0;
-                               }
-                               i.fragments_max = max1 - 1;
-                               err1 = snd_pcm_hw_info(pcm, &i);
-                       }
-                       max1 = i.fragments_max;
-               }
-               if (min2 <= info->fragments_max) {
-                       i = *info;
-                       i.fragments_min = min2;
-                       err2 = snd_pcm_hw_info(pcm, &i);
-                       min2 = i.fragments_min;
-               }
-               while (1) {
-                       unsigned int fragments;
-                       if (err1 >= 0) {
-                               if (err2 >= 0) {
-                                       if (params->fragments - max1 < 
-                                           min2 - params->fragments)
-                                               fragments = max1;
-                                       else
-                                               fragments = min2;
-                               } else
-                                       fragments = max1;
-                       } else if (err2 >= 0)
-                               fragments = min2;
-                       else {
-                               info->fragments_min = UINT_MAX;
-                               info->fragments_max = 0;
-                               return -EINVAL;
-                       }
-                       i = *info;
-                       i.fragments_min = i.fragments_max = fragments;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-                       if (fragments == max1) {
-                               max1--;
-                               i = *info;
-                               i.fragments_max = max1;
-                               err1 = snd_pcm_hw_info(pcm, &i);
-                               max1 = i.fragments_max;
-                       } else {
-                               min2++;
-                               i = *info;
-                               i.fragments_min = min2;
-                               err2 = snd_pcm_hw_info(pcm, &i);
-                               min2 = i.fragments_min;
-                       }
-                               
-               }
-               break;
-       }
-       case SND_PCM_RULE_REL_BITS:
-       {
-               unsigned int k;
-               for (k = info->fragments_min; k < 32; ++k) {
-                       if (!(params->fragments & (1U << k)))
-                               continue;
-                       info->fragments_min = k;
-                       if (info->fragments_min > info->fragments_max)
-                               return -EINVAL;
-                       i = *info;
-                       err = snd_pcm_hw_info(pcm, &i);
-                       if (err < 0) {
-                               info->fragments_min = i.fragments_min;
-                               info->fragments_max = i.fragments_max;
-                               return err;
-                       }
-                       i.fragments_max = i.fragments_min;
-                       if (snd_pcm_hw_info_rules(pcm, &i, params, count - 1, rules + 1) >= 0) {
-                               *info = i;
-                               return 0;
-                       }
-               }
-               break;
-       }
-       default:
-               assert(0);
-               return -EINVAL;
-       }
-       return 0;
-}
-
-int snd_pcm_hw_info_rules(snd_pcm_t *pcm, 
-                         snd_pcm_hw_info_t *info,
-                         snd_pcm_hw_params_t *params,
-                         unsigned int count, int *rules)
-{
-       unsigned int par;
-       if (count == 0)
-               return snd_pcm_hw_info(pcm, info);
-       par = rules[0] & SND_PCM_RULE_PAR_MASK;
-       switch (par) {
-       case SND_PCM_HW_PARAM_ACCESS:
-               return snd_pcm_hw_info_rules_access(pcm, info, params, count, rules);
-       case SND_PCM_HW_PARAM_FORMAT:
-               return snd_pcm_hw_info_rules_format(pcm, info, params, count, rules);
-       case SND_PCM_HW_PARAM_SUBFORMAT:
-               return snd_pcm_hw_info_rules_subformat(pcm, info, params, count, rules);
-       case SND_PCM_HW_PARAM_CHANNELS:
-               return snd_pcm_hw_info_rules_channels(pcm, info, params, count, rules);
-       case SND_PCM_HW_PARAM_RATE:
-               return snd_pcm_hw_info_rules_rate(pcm, info, params, count, rules);
-       case SND_PCM_HW_PARAM_FRAGMENT_SIZE:
-               return snd_pcm_hw_info_rules_fragment_size(pcm, info, params, count, rules);
-       case SND_PCM_HW_PARAM_FRAGMENTS:
-               return snd_pcm_hw_info_rules_fragments(pcm, info, params, count, rules);
-       default:
-               assert(0);
-               return -EINVAL;
-       }
-}
-
-int snd_pcm_hw_info_rulesv(snd_pcm_t *pcm, 
-                          snd_pcm_hw_info_t *info,
-                          snd_pcm_hw_params_t *params, ...)
-{
-       va_list arg;
-       unsigned int count = 0;
-       int rules[32];
-       va_start(arg, params);
-       while (1) {
-               int i = va_arg(arg, int);
-               if (i == -1)
-                       break;
-               rules[count++] = i;
-       }
-       va_end(arg);
-       return snd_pcm_hw_info_rules(pcm, info, params, count, rules);
-}
-
-int snd_pcm_hw_params_rules(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
-                           unsigned int count, int *rules)
-{
-       int err;
-       snd_pcm_hw_info_t info;
-       unsigned int k;
-       snd_pcm_hw_params_to_info(params, &info);
-       for (k = 0; k < count; ++k) {
-               switch (rules[k] & SND_PCM_RULE_PAR_MASK) {
-               case SND_PCM_HW_PARAM_ACCESS:
-                       info.access_mask = ~0;
-                       break;
-               case SND_PCM_HW_PARAM_FORMAT:
-                       info.format_mask = ~0;
-                       break;
-               case SND_PCM_HW_PARAM_SUBFORMAT:
-                       info.subformat_mask = ~0;
-                       break;
-               case SND_PCM_HW_PARAM_CHANNELS:
-                       info.channels_min = 1;
-                       info.channels_max = UINT_MAX;
-                       break;
-               case SND_PCM_HW_PARAM_RATE:
-                       info.rate_min = 0;
-                       info.rate_max = UINT_MAX;
-                       break;
-               case SND_PCM_HW_PARAM_FRAGMENT_SIZE:
-                       info.fragment_size_min = 1;
-                       info.fragment_size_max = ULONG_MAX;
-                       info.buffer_size_min = 1;
-                       info.buffer_size_max = ULONG_MAX;
-                       break;
-               case SND_PCM_HW_PARAM_FRAGMENTS:
-                       info.fragments_min = 1;
-                       info.fragments_max = UINT_MAX;
-                       info.buffer_size_min = 1;
-                       info.buffer_size_max = ULONG_MAX;
-                       break;
-               default:
-                       assert(0);
-                       break;
-               }
-       }
-       err = snd_pcm_hw_info_rules(pcm, &info, params, count, rules);
-       if (err < 0) {
-               snd_pcm_hw_info_to_params_fail(&info, params);
-               return err;
-       }
-       snd_pcm_hw_info_to_params(&info, params);
-       return snd_pcm_hw_params(pcm, params);
-}
-
-int snd_pcm_hw_params_rulesv(snd_pcm_t *pcm, 
-                            snd_pcm_hw_params_t *params, ...)
-{
-       va_list arg;
-       unsigned int count = 0;
-       int rules[32];
-       va_start(arg, params);
-       while (1) {
-               int i = va_arg(arg, int);
-               if (i == -1)
-                       break;
-               rules[count++] = i;
-       }
-       va_end(arg);
-       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,
+void snd_pcm_hw_info_par_set_minmax(snd_pcm_hw_info_t *info,
                                    unsigned int param,
-                                   unsigned long value)
+                                   unsigned long min, unsigned long max)
 {
-       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;
+       {
+               unsigned int mask;
+               if (min >= 32 || max <= 0 || min > max) {
+                       snd_pcm_hw_info_par_set_mask(info, param, 0);
+                       break;
+               }
+               if (max >= 31) {
+                       max = 31;
+                       if (min <= 0)
+                               break;
+               }
+               mask = snd_pcm_hw_info_par_get_mask(info, param);
+               mask &= ((1U << (max - min + 1)) - 1) << min;
+               snd_pcm_hw_info_par_set_mask(info, param, mask);
+               break;
        }
-}
-
-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;
+               info->channels_min = min;
+               info->channels_max = max;
+               break;
        case SND_PCM_HW_PARAM_RATE:
-               if (value < info->rate_max)
-                       info->rate_max = value;
-               return info->rate_max;
+               info->rate_min = min;
+               info->rate_max = max;
+               break;
        case SND_PCM_HW_PARAM_FRAGMENT_SIZE:
-               if (value < info->fragment_size_max)
-                       info->fragment_size_max = value;
-               return info->fragment_size_max;
+               info->fragment_size_min = min;
+               info->fragment_size_max = max;
+               break;
        case SND_PCM_HW_PARAM_FRAGMENTS:
-               if (value < info->fragments_max)
-                       info->fragments_max = value;
-               return info->fragments_max;
+               info->fragments_min = min;
+               info->fragments_max = max;
+               break;
        case SND_PCM_HW_PARAM_BUFFER_SIZE:
-               if (value < info->buffer_size_max)
-                       info->buffer_size_max = value;
-               return info->buffer_size_max;
+               info->buffer_size_min = min;
+               info->buffer_size_max = max;
+               break;
        default:
                assert(0);
-               return 0;
        }
 }
 
-static void par_set(snd_pcm_hw_info_t *info, unsigned int param,
-                   unsigned long value)
+void snd_pcm_hw_info_par_copy(snd_pcm_hw_info_t *info, unsigned int param,
+                             snd_pcm_hw_info_t *src)
 {
        switch (param) {
        case SND_PCM_HW_PARAM_ACCESS:
-               info->access_mask = 1 << value;
+               info->access_mask = src->access_mask;
                break;
        case SND_PCM_HW_PARAM_FORMAT:
-               info->format_mask = 1 << value;
+               info->format_mask = src->format_mask;
                break;
        case SND_PCM_HW_PARAM_SUBFORMAT:
-               info->subformat_mask = 1 << value;
+               info->subformat_mask = src->subformat_mask;
                break;
        case SND_PCM_HW_PARAM_CHANNELS:
-               info->channels_min = info->channels_max = value;
+               info->channels_min = src->channels_min;
+               info->channels_max = src->channels_max;
                break;
        case SND_PCM_HW_PARAM_RATE:
-               info->rate_min = info->rate_max = value;
+               info->rate_min = src->rate_min;
+               info->rate_max = src->rate_max;
                break;
        case SND_PCM_HW_PARAM_FRAGMENT_SIZE:
-               info->fragment_size_min = info->fragment_size_max = value;
+               info->fragment_size_min = src->fragment_size_min;
+               info->fragment_size_max = src->fragment_size_max;
                break;
        case SND_PCM_HW_PARAM_FRAGMENTS:
-               info->fragments_min = info->fragments_max = value;
+               info->fragments_min = src->fragments_min;
+               info->fragments_max = src->fragments_max;
                break;
        case SND_PCM_HW_PARAM_BUFFER_SIZE:
-               info->buffer_size_min = info->buffer_size_max = value;
+               info->buffer_size_min = src->buffer_size_min;
+               info->buffer_size_max = src->buffer_size_max;
                break;
        default:
                assert(0);
@@ -2974,39 +1761,79 @@ static void par_set(snd_pcm_hw_info_t *info, unsigned int param,
        }
 }
 
-static int par_check(const snd_pcm_hw_info_t *info, unsigned int param,
-                    unsigned long value)
+unsigned long snd_pcm_hw_info_par_choices(const snd_pcm_hw_info_t *info,
+                                         unsigned int param)
 {
-       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;
+       par_desc_t *p;
+       assert(param <= SND_PCM_HW_PARAM_LAST);
+       p = &hw_pars[param];
+       switch (p->type) {
+       case MASK:
+               return hweight32(snd_pcm_hw_info_par_get_mask(info, param));
+       case MINMAX:
+       {
+               unsigned long min, max;
+               snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
+               return max - min + 1;
+       }
+       default:
+               assert(0);
+               return 0;
+       }
+}
+
+unsigned long snd_pcm_hw_info_par_refine_min(snd_pcm_hw_info_t *info,
+                                            unsigned int param,
+                                            unsigned long value)
+{
+       unsigned long min, max;
+       snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
+       if (min < value) {
+               min = value;
+               snd_pcm_hw_info_par_set_minmax(info, param, min, max);
+       }
+       return min;
+}
+
+unsigned long snd_pcm_hw_info_par_refine_max(snd_pcm_hw_info_t *info,
+                                            unsigned int param,
+                                            unsigned long value)
+{
+       unsigned long min, max;
+       snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
+       if (max > value) {
+               max = value;
+               snd_pcm_hw_info_par_set_minmax(info, param, min, max);
+       }
+       return max;
+}
+
+int snd_pcm_hw_info_par_check(const snd_pcm_hw_info_t *info, 
+                             unsigned int param,
+                             unsigned long value)
+{
+       par_desc_t *p;
+       assert(param <= SND_PCM_HW_PARAM_LAST);
+       p = &hw_pars[param];
+       switch (p->type) {
+       case MASK:
+               return snd_pcm_hw_info_par_get_mask(info, param) & (1 << value);
+       case MINMAX:
+       {
+               unsigned long min, max;
+               snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
+               return value >= min && value <= 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)
+long snd_pcm_hw_info_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;
@@ -3015,10 +1842,9 @@ static long par_nearest_next(const snd_pcm_hw_info_t *info, unsigned int param,
        int err1 = -EINVAL;
        int err2 = -EINVAL;
        
+       snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
        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;
@@ -3037,17 +1863,17 @@ static long par_nearest_next(const snd_pcm_hw_info_t *info, unsigned int param,
        else
                max1 = 0;
        min2 = best + d2;
-       max1 = par_refine_max(&i1, param, max1);
-       min2 = par_refine_min(&i2, param, min2);
+       max1 = snd_pcm_hw_info_par_refine_max(&i1, param, max1);
+       min2 = snd_pcm_hw_info_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);
+                       max1 = snd_pcm_hw_info_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);
+                       min2 = snd_pcm_hw_info_par_refine_min(&i2, param, min2);
        }
        if (err1 < 0) {
                if (err2 < 0)
@@ -3060,66 +1886,213 @@ static long par_nearest_next(const snd_pcm_hw_info_t *info, unsigned int param,
        return min2;
 }
 
+void snd_pcm_hw_info_par_dump(snd_pcm_hw_info_t *info, unsigned int param, FILE *fp)
+{
+       par_desc_t *p;
+       assert(param <= SND_PCM_HW_PARAM_LAST);
+       p = &hw_pars[param];
+       switch (p->type) {
+       case MASK:
+       {
+               unsigned int mask = snd_pcm_hw_info_par_get_mask(info, param);
+               if (mask == ~0U)
+                       fputs(" ALL", fp);
+               else if (mask) {
+                       unsigned int k;
+                       for (k = 0; k <= p->last; ++k)
+                               if (mask & (1U << k)) {
+                                       putc(' ', fp);
+                                       fputs(p->names[k], fp);
+                               }
+               } else
+                       fputs(" NONE", fp);
+               break;
+       }
+       case MINMAX:
+       {
+               unsigned long min, max;
+               snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
+               printf("%ld - %ld", min, max);
+       }
+       default:
+               assert(0);
+               break;
+       }
+}
+
+int snd_pcm_hw_info_par_empty(snd_pcm_hw_info_t *info, unsigned int param)
+{
+       par_desc_t *p;
+       assert(param <= SND_PCM_HW_PARAM_LAST);
+       p = &hw_pars[param];
+       switch (p->type) {
+       case MASK:
+               return !snd_pcm_hw_info_par_get_mask(info, param);
+       case MINMAX:
+       {
+               unsigned long min, max;
+               snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
+               return min > max;
+       }
+       default:
+               assert(0);
+               return 0;;
+       }
+}
+
+unsigned int snd_pcm_hw_info_fail_mask(snd_pcm_hw_info_t *info)
+{
+       unsigned int k, mask = 0;
+       for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) {
+               if (snd_pcm_hw_info_par_empty(info, k))
+                       mask |= 1 << k;
+       }
+       return mask;
+}
+
+int _snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+       int err;
+       snd_pcm_hw_info_t info;
+
+       snd_pcm_hw_params_to_info(params, &info);
+       err = snd_pcm_hw_info(pcm, &info);
+       if (err < 0) {
+               params->fail_mask = snd_pcm_hw_info_fail_mask(&info);
+               return err;
+       }
+       
+       if ((err = pcm->ops->hw_params(pcm->op_arg, params)) < 0)
+               return err;
+       pcm->setup = 1;
+       pcm->access = params->access;
+       pcm->format = params->format;
+       pcm->subformat = params->subformat;
+       pcm->rate = params->rate;
+       pcm->channels = params->channels;
+       pcm->fragment_size = params->fragment_size;
+       pcm->fragments = params->fragments;
+       pcm->bits_per_sample = snd_pcm_format_physical_width(params->format);
+        pcm->bits_per_frame = pcm->bits_per_sample * params->channels;
+       pcm->buffer_size = params->fragment_size * params->fragments;
+
+       pcm->info = info.info;
+       pcm->msbits = info.msbits;
+       pcm->rate_master = info.rate_master;
+       pcm->rate_divisor = info.rate_divisor;
+       pcm->fifo_size = info.fifo_size;
+
+       /* Default sw params */
+       pcm->start_mode = SND_PCM_START_DATA;
+       pcm->ready_mode = SND_PCM_READY_FRAGMENT;
+       pcm->xrun_mode = SND_PCM_XRUN_FRAGMENT;
+       pcm->avail_min = pcm->fragment_size;
+       pcm->xfer_min = pcm->fragment_size;
+       pcm->xfer_align = pcm->fragment_size;
+       pcm->time = 0;
+       pcm->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size;
+       return 0;
+}
+
+int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
+{
+       int err;
+       assert(pcm && params);
+       if (pcm->setup && pcm->mmap_channels && 
+           (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_munmap(pcm);
+               if (err < 0)
+                       return err;
+       }
+       err = _snd_pcm_hw_params(pcm, params);
+       if (pcm->setup &&
+           (pcm->mmap_rw || 
+            (pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
+             pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED ||
+             pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) {
+               int err;
+               err = snd_pcm_mmap(pcm);
+               if (err < 0)
+                       return err;
+       }
+       return err;
+}
+
 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)
+                             unsigned int badness_min, unsigned int badness_max)
 {
        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);
+       unsigned int mask = ~0;
+       int badness = strategy->min_badness(info, badness_max, pcm, strategy);
+       snd_pcm_hw_info_t info1;
 #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;
+               return badness;
+       if ((unsigned int)badness > badness_min)
+               badness_min = badness_min;
        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);
+               snd_pcm_hw_info_par_set_minmax(&info1, param, value, 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;
+               if (err >= 0) {
+                       badness = snd_pcm_hw_info_strategy1(pcm, &info1, strategy, badness_min, badness_max);
+                       if (badness >= 0) {
+                               
+                               if ((unsigned int) badness <= badness_min) {
+                                       *info = info1;
+                                       return badness;
+                               }
+                               best_badness = badness;
+                               best_info = info1;
+                               badness_max = badness - 1;
+                               continue;
+                       }
+                       if (badness != -EINVAL)
+                               continue;
                }
-               best_badness = badness;
-               best_info = info1;
-               max_badness = badness - 1;
+               mask &= snd_pcm_hw_info_fail_mask(&info1);
        }
-       if (best_badness == UINT_MAX)
+       if (best_badness == UINT_MAX) {
+               for (param = 0; param <= SND_PCM_HW_PARAM_LAST; param++) {
+                       if (!(mask & (1 << param)))
+                               continue;
+                       snd_pcm_hw_info_par_copy(info, param, &info1);
+               }
                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)
+                            const snd_pcm_strategy_t *strategy)
 {
        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);
+       return snd_pcm_hw_info_strategy1(pcm, info, strategy,
+                                        strategy->badness_min,
+                                        strategy->badness_max);
 }
 
 
@@ -3142,15 +2115,20 @@ int snd_pcm_strategy_simple_choose_param(const snd_pcm_hw_info_t *info,
        int best_param = -1;
        const snd_pcm_strategy_simple_t *pars = strategy->private;
        unsigned long min_choices = ULONG_MAX;
+       unsigned int min_order = UINT_MAX;
        for (param = 0; param <= SND_PCM_HW_PARAM_LAST; ++param) {
+               const snd_pcm_strategy_simple_t *p = &pars[param];
                unsigned int choices;
-               if (!pars[param].valid)
+               if (!p->valid)
                        continue;
-               choices = par_choices(info, param);
+               choices = snd_pcm_hw_info_par_choices(info, param);
                if (choices == 1)
                        continue;
                assert(choices != 0);
-               if (choices < min_choices) {
+               if (p->order < min_order ||
+                   (p->order == min_order &&
+                    choices < min_choices)) {
+                       min_order = p->order;
                        min_choices = choices;
                        best_param = param;
                }
@@ -3184,7 +2162,7 @@ int snd_pcm_strategy_simple_min_badness(const snd_pcm_hw_info_t *info,
                        continue;
                b = pars[param].min_badness(info, param, pcm, &pars[param]);
                if (b > max_badness || max_badness - b < badness)
-                       return -EINVAL;
+                       return -E2BIG;
                badness += b;
        }
        return badness;
@@ -3203,7 +2181,7 @@ unsigned int snd_pcm_strategy_simple_near_min_badness(const snd_pcm_hw_info_t *i
                                                      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 value = snd_pcm_hw_info_par_nearest_next(info, param, p->best, -1, pcm);
        long diff;
        assert(value >= 0);
        diff = p->best - value;
@@ -3219,7 +2197,7 @@ long snd_pcm_strategy_simple_near_next_value(const snd_pcm_hw_info_t *info,
                                             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);
+       return snd_pcm_hw_info_par_nearest_next(info, param, p->best, value, pcm);
 }
 
 void snd_pcm_strategy_simple_choices_free(snd_pcm_strategy_simple_t *par)
@@ -3237,7 +2215,7 @@ unsigned int snd_pcm_strategy_simple_choices_min_badness(const snd_pcm_hw_info_t
        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))
+               if (snd_pcm_hw_info_par_check(info, param, p->choices[k].value))
                        return p->choices[k].badness;
        }
        assert(0);
@@ -3262,7 +2240,7 @@ long snd_pcm_strategy_simple_choices_next_value(const snd_pcm_hw_info_t *info,
        }
        for (; k < p->count; ++k) {
                unsigned long v = p->choices[k].value;
-               if (par_check(info, param, v))
+               if (snd_pcm_hw_info_par_check(info, param, v))
                        return v;
        }
        return -1;
@@ -3276,7 +2254,9 @@ int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy)
        return 0;
 }
 
-int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp)
+int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp,
+                           unsigned int badness_min,
+                           unsigned int badness_max)
 {
        snd_pcm_strategy_simple_t *data;
        snd_pcm_strategy_t *s;
@@ -3292,6 +2272,8 @@ int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp)
        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->badness_min = badness_min;
+       s->badness_max = badness_max;
        s->private = data;
        s->free = snd_pcm_strategy_simple_free;
        *strategyp = s;
@@ -3299,6 +2281,7 @@ int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp)
 }
 
 int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy,
+                                int order,
                                 unsigned int param,
                                 unsigned long best,
                                 unsigned int mul)
@@ -3314,6 +2297,7 @@ int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy,
        data->best = best;
        data->mul = mul;
        s += param;
+       s->order = order;
        s->valid = 1;
        s->next_value = snd_pcm_strategy_simple_near_next_value;
        s->min_badness = snd_pcm_strategy_simple_near_min_badness;
@@ -3323,6 +2307,7 @@ int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy,
 }
 
 int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy,
+                                   int order,
                                    unsigned int param,
                                    unsigned int count,
                                    snd_pcm_strategy_simple_choices_list_t *choices)
@@ -3339,6 +2324,7 @@ int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy,
        data->choices = choices;
        s += param;
        s->valid = 1;
+       s->order = order;
        s->next_value = snd_pcm_strategy_simple_choices_next_value;
        s->min_badness = snd_pcm_strategy_simple_choices_min_badness;
        s->private = data;
@@ -3346,6 +2332,78 @@ int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy,
        return 0;
 }
 
+int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp)
+{
+       unsigned int param;
+       for (param = 0; param <= SND_PCM_HW_PARAM_LAST; param++) {
+               fprintf(fp, "%s: ", snd_pcm_hw_param_names[param]);
+               snd_pcm_hw_info_par_dump(info, param, fp);
+               putc('\n', fp);
+       }
+       return 0;
+}
+
+int snd_pcm_hw_info_try_explain_failure1(snd_pcm_t *pcm,
+                                        snd_pcm_hw_info_t *fail,
+                                        snd_pcm_hw_info_t *success,
+                                        unsigned int depth,
+                                        FILE *fp)
+{
+       unsigned int param;
+       snd_pcm_hw_info_t i;
+       if (depth < 1)
+               return -ENOENT;
+       for (param = 0; param <= SND_PCM_HW_PARAM_LAST; param++) {
+               int err;
+               i = *success;
+               snd_pcm_hw_info_par_copy(&i, param, fail);
+               err = snd_pcm_hw_info(pcm, &i);
+               if (err == 0 && 
+                   snd_pcm_hw_info_try_explain_failure1(pcm, fail, &i, depth - 1, fp) < 0)
+                       continue;
+               fprintf(fp, "%s: ", snd_pcm_hw_param_names[param]);
+               snd_pcm_hw_info_par_dump(fail, param, fp);
+               putc('\n', fp);
+               return 0;
+       }
+       return -ENOENT;
+}
+
+int snd_pcm_hw_info_try_explain_failure(snd_pcm_t *pcm,
+                                       snd_pcm_hw_info_t *fail,
+                                       snd_pcm_hw_info_t *success,
+                                       unsigned int depth,
+                                       FILE *fp)
+{
+       snd_pcm_hw_info_t i, any;
+       int err;
+       unsigned int fail_mask;
+       assert(pcm && fail);
+        fail_mask = snd_pcm_hw_info_fail_mask(fail);
+       if (fail_mask) {
+               unsigned int param;
+               for (param = 0; param <= SND_PCM_HW_PARAM_LAST; param++) {
+                       if (!(fail_mask & (1 << param)))
+                               continue;
+                       fprintf(fp, "%s: ", snd_pcm_hw_param_names[param]);
+                       snd_pcm_hw_info_par_dump(fail, param, fp);
+                       putc('\n', fp);
+               }
+               return 0;
+       }
+       i = *fail;
+       err = snd_pcm_hw_info(pcm, &i);
+       if (err == 0) {
+               fprintf(fp, "Too low max badness or configuration temporarily unavailable\n");
+               return 0;
+       }
+       if (!success) {
+               snd_pcm_hw_info_any(&any);
+               success = &any;
+       }
+       return snd_pcm_hw_info_try_explain_failure1(pcm, fail, success, depth, fp);
+}
+
 size_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm)
 {
        return *pcm->hw_ptr;
index d6be0e576627ad9dc4d7685addf8cc7550814773..70882a036c0a30112f2bf07c050cd0080d1f08b1 100644 (file)
@@ -352,12 +352,8 @@ static int snd_pcm_adpcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
        info->format_mask = 1U << adpcm->sformat;
        info->access_mask = SND_PCM_ACCBIT_MMAP;
        err = snd_pcm_hw_info(adpcm->plug.slave, info);
-       if (info->format_mask)
-               info->format_mask = format_mask;
-       if (info->access_mask) {
-               adpcm->plug.saccess_mask = info->access_mask;
-               info->access_mask = access_mask;
-       }
+       info->format_mask = format_mask;
+       info->access_mask = access_mask;
        if (err < 0)
                return err;
        info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@@ -369,25 +365,19 @@ static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
 {
        snd_pcm_adpcm_t *adpcm = pcm->private;
        snd_pcm_t *slave = adpcm->plug.slave;
-       unsigned int format, access;
+       snd_pcm_hw_info_t sinfo;
+       snd_pcm_hw_params_t sparams;
        int err;
-       format = params->format;
-       access = params->access;
-       params->format = adpcm->sformat;
-       if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
-       else if (adpcm->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
-       else
-               assert(0);
-       err = snd_pcm_hw_params(slave, params);
-       params->format = format;
-       params->access = access;
+       snd_pcm_hw_params_to_info(params, &sinfo);
+       sinfo.format_mask = 1 << adpcm->sformat;
+       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
+       err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
+       params->fail_mask = sparams.fail_mask;
        if (err < 0)
                return err;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
                if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
-                       adpcm->getput_idx = get_index(format, SND_PCM_FORMAT_S16);
+                       adpcm->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16);
                        adpcm->func = adpcm_encode;
                } else {
                        adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, adpcm->sformat);
@@ -395,7 +385,7 @@ static int snd_pcm_adpcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
                }
        } else {
                if (adpcm->sformat == SND_PCM_FORMAT_IMA_ADPCM) {
-                       adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, format);
+                       adpcm->getput_idx = put_index(SND_PCM_FORMAT_S16, params->format);
                        adpcm->func = adpcm_decode;
                } else {
                        adpcm->getput_idx = get_index(adpcm->sformat, SND_PCM_FORMAT_S16);
index fd5f619ccc660ad0f6150861113df37f4be01a35..16dd739914d25d401548c5d390f51af108877a55 100644 (file)
@@ -234,12 +234,8 @@ static int snd_pcm_alaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
        info->format_mask = 1U << alaw->sformat;
        info->access_mask = SND_PCM_ACCBIT_MMAP;
        err = snd_pcm_hw_info(alaw->plug.slave, info);
-       if (info->format_mask)
-               info->format_mask = format_mask;
-       if (info->access_mask) {
-               alaw->plug.saccess_mask = info->access_mask;
-               info->access_mask = access_mask;
-       }
+       info->format_mask = format_mask;
+       info->access_mask = access_mask;
        if (err < 0)
                return err;
        info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@@ -251,25 +247,19 @@ static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
 {
        snd_pcm_alaw_t *alaw = pcm->private;
        snd_pcm_t *slave = alaw->plug.slave;
-       unsigned int format, access;
+       snd_pcm_hw_info_t sinfo;
+       snd_pcm_hw_params_t sparams;
        int err;
-       format = params->format;
-       access = params->access;
-       params->format = alaw->sformat;
-       if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
-       else if (alaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
-       else
-               assert(0);
-       err = snd_pcm_hw_params(slave, params);
-       params->format = format;
-       params->access = access;
+       snd_pcm_hw_params_to_info(params, &sinfo);
+       sinfo.format_mask = 1 << alaw->sformat;
+       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
+       err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
+       params->fail_mask = sparams.fail_mask;
        if (err < 0)
                return err;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
                if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) {
-                       alaw->getput_idx = get_index(format, SND_PCM_FORMAT_S16);
+                       alaw->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16);
                        alaw->func = alaw_encode;
                } else {
                        alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, alaw->sformat);
@@ -277,7 +267,7 @@ static int snd_pcm_alaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
                }
        } else {
                if (alaw->sformat == SND_PCM_FORMAT_MU_LAW) {
-                       alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, format);
+                       alaw->getput_idx = put_index(SND_PCM_FORMAT_S16, params->format);
                        alaw->func = alaw_decode;
                } else {
                        alaw->getput_idx = get_index(alaw->sformat, SND_PCM_FORMAT_S16);
index 74ada49cfae9da9c1d0f964671d5ab2a64336220..ba577ff9cc8ad7e86aa9a00aab080afb59f607e4 100644 (file)
@@ -92,12 +92,8 @@ static int snd_pcm_linear_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
        info->format_mask = 1U << linear->sformat;
        info->access_mask = SND_PCM_ACCBIT_MMAP;
        err = snd_pcm_hw_info(linear->plug.slave, info);
-       if (info->format_mask)
-               info->format_mask = format_mask;
-       if (info->access_mask) {
-               linear->plug.saccess_mask = info->access_mask;
-               info->access_mask = access_mask;
-       }
+       info->format_mask = format_mask;
+       info->access_mask = access_mask;
        if (err < 0)
                return err;
        info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@@ -109,28 +105,22 @@ static int snd_pcm_linear_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 {
        snd_pcm_linear_t *linear = pcm->private;
        snd_pcm_t *slave = linear->plug.slave;
-       unsigned int format, access;
+       snd_pcm_hw_info_t sinfo;
+       snd_pcm_hw_params_t sparams;
        int err;
-       format = params->format;
-       access = params->access;
-       params->format = linear->sformat;
-       if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
-       else if (linear->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
-       else
-               assert(0);
-       err = snd_pcm_hw_params(slave, params);
-       params->format = format;
-       params->access = access;
+       snd_pcm_hw_params_to_info(params, &sinfo);
+       sinfo.format_mask = 1 << linear->sformat;
+       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
+       err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
+       params->fail_mask = sparams.fail_mask;
        if (err < 0)
                return err;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
-               linear->conv_idx = conv_index(format,
+               linear->conv_idx = conv_index(params->format,
                                              linear->sformat);
        else
                linear->conv_idx = conv_index(linear->sformat,
-                                             format);
+                                             params->format);
        return 0;
 }
 
index 9065a1c09b86106676d236891b67e0db29d34bf8..df8af730b5e9297cb8001c6833fcbdab80659a63 100644 (file)
@@ -170,8 +170,7 @@ ssize_t snd_pcm_read_mmap(snd_pcm_t *pcm, size_t size);
 ssize_t snd_pcm_write_mmap(snd_pcm_t *pcm, size_t size);
 int snd_pcm_hw_info_complete(snd_pcm_hw_info_t *info);
 void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info);
-void snd_pcm_hw_info_to_params(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params);
-void snd_pcm_hw_info_to_params_fail(snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params);
+int snd_pcm_hw_info_to_params(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params);
 int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info);
 int snd_pcm_channel_info_shm(snd_pcm_t *pcm, snd_pcm_channel_info_t *info, int shmid);
 
index a67902154c8bf1c1bd6c79570d75d6d7f0a41702..5d0f0c2065236f1b46f3bf93e3950f6982be9f18 100644 (file)
@@ -65,9 +65,9 @@ int snd_pcm_format_unsigned(int format)
        int val;
 
        val = snd_pcm_format_signed(format);
-       if (val >= 0)
-               val ^= 1;
-       return val;
+       if (val < 0)
+               return val;
+       return !val;
 }
 
 int snd_pcm_format_linear(int format)
@@ -113,6 +113,15 @@ int snd_pcm_format_big_endian(int format)
        return !val;
 }
 
+int snd_pcm_format_cpu_endian(int format)
+{
+#ifdef SND_LITTLE_ENDIAN
+       return snd_pcm_format_little_endian(format);
+#else
+       return snd_pcm_format_big_endian(format);
+#endif
+}
+
 int snd_pcm_format_width(int format)
 {
        switch (format) {
index 01cd202cc9ac235f7bdfadc884884a2e19107b48..90aa36a0a0c76f83ebb6ec2866321add1ce15e85 100644 (file)
@@ -251,12 +251,8 @@ static int snd_pcm_mulaw_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
        info->format_mask = 1U << mulaw->sformat;
        info->access_mask = SND_PCM_ACCBIT_MMAP;
        err = snd_pcm_hw_info(mulaw->plug.slave, info);
-       if (info->format_mask)
-               info->format_mask = format_mask;
-       if (info->access_mask) {
-               mulaw->plug.saccess_mask = info->access_mask;
-               info->access_mask = access_mask;
-       }
+       info->format_mask = format_mask;
+       info->access_mask = access_mask;
        if (err < 0)
                return err;
        info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@@ -268,25 +264,19 @@ static int snd_pcm_mulaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
 {
        snd_pcm_mulaw_t *mulaw = pcm->private;
        snd_pcm_t *slave = mulaw->plug.slave;
-       unsigned int format, access;
+       snd_pcm_hw_info_t sinfo;
+       snd_pcm_hw_params_t sparams;
        int err;
-       format = params->format;
-       access = params->access;
-       params->format = mulaw->sformat;
-       if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
-       else if (mulaw->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
-       else
-               assert(0);
-       err = snd_pcm_hw_params(slave, params);
-       params->format = format;
-       params->access = access;
+       snd_pcm_hw_params_to_info(params, &sinfo);
+       sinfo.format_mask = 1 << mulaw->sformat;
+       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
+       err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
+       params->fail_mask = sparams.fail_mask;
        if (err < 0)
                return err;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
                if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) {
-                       mulaw->getput_idx = get_index(format, SND_PCM_FORMAT_S16);
+                       mulaw->getput_idx = get_index(params->format, SND_PCM_FORMAT_S16);
                        mulaw->func = mulaw_encode;
                } else {
                        mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, mulaw->sformat);
@@ -294,7 +284,7 @@ static int snd_pcm_mulaw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
                }
        } else {
                if (mulaw->sformat == SND_PCM_FORMAT_MU_LAW) {
-                       mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, format);
+                       mulaw->getput_idx = put_index(SND_PCM_FORMAT_S16, params->format);
                        mulaw->func = mulaw_decode;
                } else {
                        mulaw->getput_idx = get_index(mulaw->sformat, SND_PCM_FORMAT_S16);
index a20161029aed09ab34fc952632b0cba978c2750d..9567800455aa4935dcce084879cfa6dc13f123db 100644 (file)
@@ -31,7 +31,6 @@ typedef struct {
        snd_pcm_t *pcm;
        unsigned int channels_count;
        int close_slave;
-       unsigned int access_mask;
 } snd_pcm_multi_slave_t;
 
 typedef struct {
@@ -96,61 +95,76 @@ static int snd_pcm_multi_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
        snd_pcm_multi_t *multi = pcm->private;
        unsigned int k;
        snd_pcm_hw_info_t i;
-       unsigned int access_mask = ~0;
+       unsigned int access_mask, saccess_mask;
+       int changed = 0;
        int err = 0;
        if (info->channels_min < multi->channels_count)
                info->channels_min = multi->channels_count;
        if (info->channels_max > multi->channels_count)
                info->channels_max = multi->channels_count;
-       if (info->channels_max > info->channels_max)
+       if (info->channels_min > info->channels_max)
                return -EINVAL;
        i = *info;
-       for (k = 0; k < multi->slaves_count; ++k) {
-               snd_pcm_t *slave = multi->slaves[k].pcm;
-               i.access_mask = SND_PCM_ACCBIT_MMAP;
-               i.channels_min = i.channels_max = multi->slaves[k].channels_count;
-               err = snd_pcm_hw_info(slave, &i);
-               access_mask &= i.access_mask;
-               if (err < 0)
-                       break;
-               multi->slaves[k].access_mask = i.access_mask;
-       }
+       saccess_mask = ~0;
+       changed = 0;
+       do {
+               for (k = 0; k < multi->slaves_count; ++k) {
+                       snd_pcm_t *slave = multi->slaves[k].pcm;
+                       snd_pcm_hw_info_t sinfo = i;
+                       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
+                       sinfo.channels_min = sinfo.channels_max = multi->slaves[k].channels_count;
+                       err = snd_pcm_hw_info(slave, &sinfo);
+                       if (err < 0) {
+                               sinfo.access_mask = info->access_mask;
+                               sinfo.channels_min = multi->channels_count;
+                               sinfo.channels_max = multi->channels_count;
+                               *info = sinfo;
+                               return err;
+                       }
+                       if (i.format_mask != sinfo.format_mask ||
+                           i.subformat_mask != sinfo.subformat_mask ||
+                           i.rate_min != sinfo.rate_min ||
+                           i.rate_max != sinfo.rate_max ||
+                           i.fragment_size_min != sinfo.fragment_size_min ||
+                           i.fragment_size_max != sinfo.fragment_size_max ||
+                           i.fragments_min != sinfo.fragments_min ||
+                           i.fragments_max != sinfo.fragments_max ||
+                           i.buffer_size_min != sinfo.buffer_size_min ||
+                           i.buffer_size_max != sinfo.buffer_size_max)
+                               changed++;
+                       saccess_mask &= sinfo.access_mask;
+                       i = sinfo;
+               }
+       } while (changed && multi->slaves_count > 1);
+       access_mask = info->access_mask;
        *info = i;
-       if (i.channels_min <= i.channels_max) 
-               info->channels_min = info->channels_max = multi->channels_count;
-       if (i.access_mask) {
-               if (!(access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) ||
-                   multi->slaves_count > 1)
-                       info->access_mask &= ~SND_PCM_ACCBIT_MMAP_INTERLEAVED;
-               if (!(access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED))
-                       info->access_mask &= ~SND_PCM_ACCBIT_MMAP_NONINTERLEAVED;
-       }
-       return err;
+       info->access_mask = access_mask;
+       if (!(saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED) ||
+           multi->slaves_count > 1)
+               info->access_mask &= ~SND_PCM_ACCBIT_MMAP_INTERLEAVED;
+       if (!(saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED))
+               info->access_mask &= ~SND_PCM_ACCBIT_MMAP_NONINTERLEAVED;
+       if (!info->access_mask)
+               return -EINVAL;
+       info->channels_min = info->channels_max = multi->channels_count;
+       return 0;
 }
 
 static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 {
        snd_pcm_multi_t *multi = pcm->private;
        unsigned int i;
-       snd_pcm_hw_params_t p;
        int err;
-       if (params->channels != multi->channels_count) {
-               params->fail_mask = SND_PCM_HW_PARBIT_CHANNELS;
-               return -EINVAL;
-       }
-       p = *params;
        for (i = 0; i < multi->slaves_count; ++i) {
                snd_pcm_t *slave = multi->slaves[i].pcm;
-               if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
-                       p.access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
-               else if (multi->slaves[i].access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
-                       p.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
-               else
-                       assert(0);
-               p.channels = multi->slaves[i].channels_count;
-               err = snd_pcm_hw_params(slave, &p);
+               snd_pcm_hw_info_t sinfo;
+               snd_pcm_hw_params_t sparams;
+               snd_pcm_hw_params_to_info(params, &sinfo);
+               sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
+               sinfo.channels_min = sinfo.channels_max = multi->slaves[i].channels_count;
+               err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
                if (err < 0) {
-                       params->fail_mask = p.fail_mask;
+                       params->fail_mask = sparams.fail_mask;
                        return err;
                }
                err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
index cafe1970568bf75ca7e52c21901cdbb099abe0d2..123a6d51efb12e38eee85526afba90fd5d5872b8 100644 (file)
@@ -32,67 +32,77 @@ typedef struct {
 } snd_pcm_plug_t;
 
 
-static int preferred_formats[] = {
-       SND_PCM_FORMAT_S16_LE,
-       SND_PCM_FORMAT_S16_BE,
-       SND_PCM_FORMAT_U16_LE,
-       SND_PCM_FORMAT_U16_BE,
-       SND_PCM_FORMAT_S24_LE,
-       SND_PCM_FORMAT_S24_BE,
-       SND_PCM_FORMAT_U24_LE,
-       SND_PCM_FORMAT_U24_BE,
-       SND_PCM_FORMAT_S32_LE,
-       SND_PCM_FORMAT_S32_BE,
-       SND_PCM_FORMAT_U32_LE,
-       SND_PCM_FORMAT_U32_BE,
-       SND_PCM_FORMAT_S8,
-       SND_PCM_FORMAT_U8
-};
-
-static int snd_pcm_plug_slave_fmt(int format, unsigned int format_mask)
+static int format_badness(unsigned int src, unsigned int dst)
 {
-       if (snd_pcm_format_linear(format)) {
-               int width = snd_pcm_format_width(format);
-               int unsignd = snd_pcm_format_unsigned(format);
-               int big = snd_pcm_format_big_endian(format);
-               int format1;
-               int wid, width1=width;
-               int dwidth1 = 8;
-               for (wid = 0; wid < 4; ++wid) {
-                       int end, big1 = big;
-                       for (end = 0; end < 2; ++end) {
-                               int sgn, unsignd1 = unsignd;
-                               for (sgn = 0; sgn < 2; ++sgn) {
-                                       format1 = snd_pcm_build_linear_format(width1, unsignd1, big1);
-                                       if (format1 >= 0 &&
-                                           format_mask & (1U << format1))
-                                               return format1;
-                                       unsignd1 = !unsignd1;
-                               }
-                               big1 = !big1;
-                       }
-                       if (width1 == 32) {
-                               dwidth1 = -dwidth1;
-                               width1 = width;
-                       }
-                       width1 += dwidth1;
-               }
-               return ffs(format_mask) - 1;
+       unsigned int badness = 0;
+       if (!snd_pcm_format_linear(src)) {
+               int w;
+               if (!snd_pcm_format_linear(dst))
+                       return format_badness(src, SND_PCM_FORMAT_S16) + 
+                               format_badness(SND_PCM_FORMAT_S16, dst);
+               w = snd_pcm_format_width(dst);
+               if (w < 16) {
+                       /* Resolution loss */
+                       badness += ((16 - w) / 8) * 32;
+                       badness += 16;
+               } else
+                       badness += ((w - 16) / 8) * 32;
+               if (!snd_pcm_format_cpu_endian(dst))
+                       badness += 2;
+               if (!snd_pcm_format_signed(dst))
+                       badness += 1;
+       } else if (!snd_pcm_format_linear(dst)) {
+               int w;
+               w = snd_pcm_format_width(src);
+               if (w < 16)
+                       badness += ((16 - w) / 8) * 32;
+               else
+                       badness += ((w - 16) / 8) * 32;
+               if (!snd_pcm_format_cpu_endian(src))
+                       badness += 2;
+               if (!snd_pcm_format_signed(src))
+                       badness += 1;
        } else {
-               unsigned int i;
-               switch (format) {
-               case SND_PCM_FORMAT_MU_LAW:
-               case SND_PCM_FORMAT_A_LAW:
-               case SND_PCM_FORMAT_IMA_ADPCM:
-                       for (i = 0; i < sizeof(preferred_formats) / sizeof(preferred_formats[0]); ++i) {
-                               int format1 = preferred_formats[i];
-                               if (format_mask & (1U << format1))
-                                       return format1;
-                       }
-               default:
-                       return ffs(format_mask) - 1;
+               int sw = snd_pcm_format_width(src);
+               int dw = snd_pcm_format_width(dst);
+               if (sw < dw) {
+                       badness += ((dw - sw) / 8) * 4;
+               } else if (sw > dw) {
+                       /* Resolution loss */
+                       badness += ((sw - dw) / 8 * 4);
+                       badness += 16;
                }
+               if (snd_pcm_format_little_endian(src) != snd_pcm_format_little_endian(dst))
+                       badness += 2;
+               if (snd_pcm_format_signed(src) != snd_pcm_format_signed(dst))
+                       badness += 1;
        }
+       return badness;
+}
+
+static int compare(const void * _a, const void * _b) {
+       const snd_pcm_strategy_simple_choices_list_t *a = _a, *b = _b;
+       return a->badness - b->badness;
+}
+
+static int snd_pcm_plug_format_choices(unsigned int stream,
+                                      snd_pcm_strategy_simple_choices_list_t *table,
+                                      unsigned int cformat, unsigned int sformat_mask)
+{
+       unsigned int k;
+       unsigned int c = 0;
+       for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) {
+               if (!(sformat_mask & (1 << k)))
+                       continue;
+               table[c].value = k;
+               if (stream == SND_PCM_STREAM_PLAYBACK)
+                       table[c].badness = format_badness(cformat, k);
+               else
+                       table[c].badness = format_badness(k, cformat);
+               c++;
+       }
+       qsort(table, c, sizeof(*table), compare);
+       return c;
 }
 
 static int snd_pcm_plug_close(snd_pcm_t *pcm)
@@ -140,15 +150,12 @@ static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
 
 static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
 {
-       int err;
        snd_pcm_plug_t *plug = pcm->private;
        snd_pcm_t *slave = plug->req_slave;
-       snd_pcm_hw_info_t sinfo, i;
-       snd_pcm_hw_params_t sparams;
-       unsigned int rate_min, rate_max;
-       unsigned int channels_min, channels_max;
-       unsigned int format, format_mask;
-       size_t fragment_size_min, fragment_size_max;
+       snd_pcm_hw_info_t sinfo;
+       int err;
+       size_t size;
+       unsigned int n;
        
        info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | 
                              SND_PCM_ACCBIT_RW_INTERLEAVED |
@@ -162,130 +169,63 @@ static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
        if (info->format_mask == 0)
                return -EINVAL;
 
-       if (info->rate_min < 4000)
-               info->rate_min = 4000;
-       if (info->rate_max > 192000)
-               info->rate_max = 192000;
+       info->subformat_mask &= SND_PCM_SUBFMTBIT_STD;
+       if (info->subformat_mask == 0)
+               return -EINVAL;
+
+       if (info->rate_min < RATE_MIN)
+               info->rate_min = RATE_MIN;
+       if (info->rate_max > RATE_MAX)
+               info->rate_max = RATE_MAX;
        if (info->rate_max < info->rate_min)
                return -EINVAL;
 
        if (info->channels_min < 1)
                info->channels_min = 1;
-       if (info->channels_max > 1024)
-               info->channels_max = 1024;
        if (info->channels_max < info->channels_min)
                return -EINVAL;
 
-       if (info->fragment_size_max > 1024 * 1024)
-               info->fragment_size_max = 1024 * 1024;
-       if (info->fragment_size_max < info->fragment_size_min)
-               return -EINVAL;
-
+       sinfo = *info;
        sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
        sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW |
                             SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM);
-       sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD;
        sinfo.channels_min = 1;
-       sinfo.channels_max = 1024;
-       sinfo.rate_min = 4000;
-       sinfo.rate_max = 192000;
-       sinfo.fragments_min = 1;
-       sinfo.fragments_max = UINT_MAX;
-       sinfo.fragment_size_min = 1;
-       sinfo.fragment_size_max = ULONG_MAX;
-
-       /* Formats */
-       err = snd_pcm_hw_info(slave, &sinfo);
-       if (err < 0) {
-               *info = i;
-               return err;
-       }
-       format_mask = 0;
-       for (format = 0; format < SND_PCM_FORMAT_LAST; ++format) {
-               if (!(info->format_mask & (1 << format)))
-                       continue;
-               err = snd_pcm_plug_slave_fmt(format, sinfo.format_mask);
-               if (err < 0)
-                       info->format_mask &= ~(1 << format);
-               else
-                       format_mask |= (1 << err);
-       }
-       sinfo.format_mask = format_mask;
-
-       /* Rate (and fragment_size) */
-       i = sinfo;
-       sparams.rate = info->rate_min;
-       err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
-                                   SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE,
-                                   -1);
-       if (err < 0) {
-               *info = i;
-               return err;
-       }
-       rate_min = i.rate_min;
-       if (info->rate_max != info->rate_min) {
-               i = sinfo;
-               sparams.rate = info->rate_max;
-               err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
-                                            SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE,
-                                            -1);
-               if (err < 0) {
-                       *info = i;
-                       return err;
-               }
-               rate_max = i.rate_min;
-       } else
-               rate_max = rate_min;
-       sinfo.rate_min = rate_min;
-       sinfo.rate_max = rate_max;
-
-       /* Channels */
-       i = sinfo;
-       sparams.channels = info->channels_min;
-       err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
-                                    SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS,
-                                    -1);
-       if (err < 0) {
-               *info = i;
-               return err;
-       }
-       channels_min = i.channels_min;
-       if (info->channels_max != info->channels_min) {
-               i = sinfo;
-               sparams.channels = info->channels_max;
-               err = snd_pcm_hw_info_rulesv(slave, &i, &sparams,
-                                            SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS,
-                                            -1);
-               if (err < 0) {
-                       *info = i;
-                       return err;
-               }
-               channels_max = i.channels_min;
-       } else
-               channels_max = channels_min;
-       sinfo.channels_min = channels_min;
-       sinfo.channels_max = channels_max;
-
-       sinfo.fragments_min = info->fragments_min;
-       sinfo.fragments_max = info->fragments_max;
+       sinfo.channels_max = UINT_MAX;
+       sinfo.rate_min = RATE_MIN;
+       sinfo.rate_max = RATE_MAX;
        sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max);
        sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min);
-       err = snd_pcm_hw_info(slave, &sinfo);
-       if (err < 0) {
-               *info = sinfo;
-               return err;
-       }
+       sinfo.buffer_size_min = muldiv_down(info->buffer_size_min, sinfo.rate_min, info->rate_max);
+       sinfo.buffer_size_max = muldiv_up(info->buffer_size_max, sinfo.rate_max, info->rate_min);
 
+       err = snd_pcm_hw_info(slave, &sinfo);
        info->subformat_mask = sinfo.subformat_mask;
        info->fragments_min = sinfo.fragments_min;
        info->fragments_max = sinfo.fragments_max;
+       
+       size = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max);
+       if (info->fragment_size_min < size)
+               info->fragment_size_min = size;
+       size = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
+       if (info->fragment_size_max > size)
+               info->fragment_size_max = size;
+
+       size = muldiv_down(sinfo.buffer_size_min, info->rate_min, sinfo.rate_max);
+       if (info->buffer_size_min < size)
+               info->buffer_size_min = size;
+       size = muldiv_up(sinfo.buffer_size_max, info->rate_max, sinfo.rate_min);
+       if (info->buffer_size_max > size)
+               info->buffer_size_max = size;
+
+       n = info->buffer_size_min / info->fragment_size_max;
+       if (info->fragments_min < n)
+               info->fragments_min = n;
+       n = info->buffer_size_max / info->fragment_size_min;
+       if (info->fragments_max > n)
+               info->fragments_max = n;
 
-       fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max);
-       fragment_size_max = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
-       if (fragment_size_min > info->fragment_size_min)
-               info->fragment_size_min = fragment_size_min;
-       if (fragment_size_max < info->fragment_size_max)
-               info->fragment_size_max = fragment_size_max;
+       if (err < 0)
+               return err;
        info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
        snd_pcm_hw_info_complete(info);
        return 0;
@@ -490,7 +430,13 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
        snd_pcm_t *slave = plug->req_slave;
        snd_pcm_hw_info_t sinfo;
        snd_pcm_hw_params_t sparams;
+       snd_pcm_strategy_t *strategy;
+       snd_pcm_strategy_simple_choices_list_t formats[SND_PCM_FORMAT_LAST + 1];
+       unsigned int nformats;
        int err;
+
+       params->fail_mask = 0;
+
        sparams = *params;
        snd_pcm_hw_params_to_info(&sparams, &sinfo);
        sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
@@ -498,39 +444,52 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
                             SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM);
        sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD;
        sinfo.channels_min = 1;
-       sinfo.channels_max = 1024;
-       sinfo.rate_min = 4000;
-       sinfo.rate_max = 192000;
+       sinfo.channels_max = UINT_MAX;
+       sinfo.rate_min = RATE_MIN;
+       sinfo.rate_max = RATE_MAX;
+       sinfo.fragments_min = params->fragments;
+       sinfo.fragments_max = params->fragments;
        sinfo.fragment_size_min = 1;
        sinfo.fragment_size_max = ULONG_MAX;
-       err = snd_pcm_hw_info_rulesv(slave, &sinfo, params,
-                                   SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_RATE,
-                                   SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_CHANNELS,
-                                    -1);
+       sinfo.buffer_size_min = 1;
+       sinfo.buffer_size_max = ULONG_MAX;
+       err = snd_pcm_strategy_simple(&strategy, 1000000, 2000000);
+       if (err < 0)
+               return err;
+       err = snd_pcm_strategy_simple_near(strategy, 0, SND_PCM_HW_PARAM_RATE,
+                                          params->rate, 1);
        if (err < 0) {
-               snd_pcm_hw_info_to_params_fail(&sinfo, params);
+               snd_pcm_strategy_free(strategy);
                return err;
        }
-       err = snd_pcm_plug_slave_fmt(sparams.format, sinfo.format_mask);
-       if (err < 0)
+       err = snd_pcm_strategy_simple_near(strategy, 1, SND_PCM_HW_PARAM_CHANNELS,
+                                          params->channels, 1);
+       if (err < 0) {
+               snd_pcm_strategy_free(strategy);
                return err;
-       sparams.format = err;
-       sinfo.format_mask = 1U << err;
-       sparams.fragment_size = muldiv_near(params->fragment_size, sparams.rate, params->rate);
-       err = snd_pcm_hw_info_rulesv(slave, &sinfo, &sparams,
-                                    SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE,
-                                    -1);
+       }
+       nformats = snd_pcm_plug_format_choices(pcm->stream, formats,
+                                              params->format, 
+                                              sinfo.access_mask);
+       err = snd_pcm_strategy_simple_choices(strategy, 2, SND_PCM_HW_PARAM_FORMAT,
+                                             nformats, formats);
        if (err < 0) {
-               snd_pcm_hw_info_to_params_fail(&sinfo, params);
+               snd_pcm_strategy_free(strategy);
                return err;
        }
-       if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
-               sparams.access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
-       else if (sinfo.access_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
-               sparams.access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
-       else
-               assert(0);
+       err = snd_pcm_hw_info_strategy(slave, &sinfo, strategy);
+       snd_pcm_strategy_free(strategy);
+       if (err < 0)
+               return err;
 
+       assert(sinfo.format_mask && 
+              (sinfo.format_mask & (sinfo.format_mask - 1)) == 0);
+       sparams.format = ffs(sinfo.format_mask) - 1;
+       assert(sinfo.channels_min == sinfo.channels_max);
+       sparams.channels = sinfo.channels_min;
+       assert(sinfo.rate_min == sinfo.rate_max);
+       sparams.rate = sinfo.rate_min;
+       
        err = snd_pcm_plug_insert_plugins(pcm, params, &sparams);
        if (err < 0)
                return err;
index 4047ffb7c12a64df36a7f09095811998a97d3c91..fc449bf286ae23f5e005d480734965939d66df94 100644 (file)
@@ -28,7 +28,6 @@ typedef struct {
        int (*init)(snd_pcm_t *pcm);
        int shmid;
        size_t appl_ptr, hw_ptr;
-       unsigned int saccess_mask;
 } snd_pcm_plugin_t;    
 
 int snd_pcm_plugin_close(snd_pcm_t *pcm);
@@ -65,7 +64,7 @@ int get_index(int src_format, int dst_format);
 int put_index(int src_format, int dst_format);
 int conv_index(int src_format, int dst_format);
 
-#define SND_PCM_FMTBIT_LINEAR (SND_PCM_FMTBIT_S8    |SND_PCM_FMTBIT_U8 | \
+#define SND_PCM_FMTBIT_LINEAR (SND_PCM_FMTBIT_S8     |SND_PCM_FMTBIT_U8 | \
                                SND_PCM_FMTBIT_S16_LE|SND_PCM_FMTBIT_S16_BE | \
                                SND_PCM_FMTBIT_U16_LE|SND_PCM_FMTBIT_U16_BE | \
                                SND_PCM_FMTBIT_S24_LE|SND_PCM_FMTBIT_S24_BE | \
@@ -75,21 +74,34 @@ int conv_index(int src_format, int dst_format);
 
 extern snd_pcm_fast_ops_t snd_pcm_plugin_fast_ops;
 
+static inline ssize_t muldiv(ssize_t a, ssize_t b, ssize_t d, ssize_t corr)
+{
+       double v = ((double) a * b + corr) / d;
+       if (v > LONG_MAX)
+               return LONG_MAX;
+       if (v < LONG_MIN)
+               return LONG_MIN;
+       return v;
+}
+
 static inline ssize_t muldiv_down(ssize_t a, ssize_t b, ssize_t d)
 {
-       return (int64_t) (a * b) / d;
+       return muldiv(a, b, d, 0);
 }
 
 static inline ssize_t muldiv_up(ssize_t a, ssize_t b, ssize_t d)
 {
-       return (int64_t) (a * b + (d - 1)) / d;
+       return muldiv(a, b, d, d - 1);
 }
 
 static inline ssize_t muldiv_near(ssize_t a, ssize_t b, ssize_t d)
 {
-       return (int64_t) (a * b + (d / 2)) / d;
+       return muldiv(a, b, d, d / 2);
 }
 
+#define RATE_MIN 4000
+#define RATE_MAX 192000
+
 #define ROUTE_PLUGIN_FLOAT 1
 #define ROUTE_PLUGIN_RESOLUTION 16
 
index 8684ccec9f830e47962de23962b1d0054af90f1e..94a9a9f81068fac13bb807506cd0aab8b6fedf0b 100644 (file)
@@ -238,7 +238,7 @@ static int snd_pcm_rate_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
        snd_pcm_rate_t *rate = pcm->private;
        snd_pcm_hw_info_t sinfo;
        unsigned int access_mask;
-       size_t fragment_size_min, fragment_size_max;
+       size_t size;
        int err;
        info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | 
                              SND_PCM_ACCBIT_RW_INTERLEAVED |
@@ -250,58 +250,56 @@ static int snd_pcm_rate_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
        info->format_mask &= SND_PCM_FMTBIT_LINEAR;
        if (info->format_mask == 0)
                return -EINVAL;
-       if (info->rate_min < 4000)
-               info->rate_min = 4000;
-       if (info->rate_max > 192000)
-               info->rate_max = 192000;
-       if (info->rate_max < info->rate_min)
+       info->subformat_mask &= SND_PCM_SUBFMTBIT_STD;
+       if (info->subformat_mask == 0)
                return -EINVAL;
-       if (info->fragment_size_max > 1024 * 1024)
-               info->fragment_size_max = 1024 * 1024;
-       if (info->fragment_size_max < info->fragment_size_min)
+       if (info->rate_min < RATE_MIN)
+               info->rate_min = RATE_MIN;
+       if (info->rate_max > RATE_MAX)
+               info->rate_max = RATE_MAX;
+       if (info->rate_max < info->rate_min)
                return -EINVAL;
-       sinfo = *info;
 
-       sinfo.rate_min = rate->srate;
-       sinfo.rate_max = rate->srate;
+       sinfo = *info;
+       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
        if (rate->sformat >= 0)
                sinfo.format_mask = 1U << rate->sformat;
+       sinfo.rate_min = rate->srate;
+       sinfo.rate_max = rate->srate;
        sinfo.fragment_size_min = muldiv_down(info->fragment_size_min, sinfo.rate_min, info->rate_max);
        sinfo.fragment_size_max = muldiv_up(info->fragment_size_max, sinfo.rate_max, info->rate_min);
+       sinfo.buffer_size_min = muldiv_down(info->buffer_size_min, sinfo.rate_min, info->rate_max);
+       sinfo.buffer_size_max = muldiv_up(info->buffer_size_max, sinfo.rate_max, info->rate_min);
                
-       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
        err = snd_pcm_hw_info(rate->plug.slave, &sinfo);
-       info->subformat_mask = sinfo.subformat_mask;
+       if (rate->sformat < 0)
+               info->format_mask = sinfo.format_mask;
        info->channels_min = sinfo.channels_min;
        info->channels_max = sinfo.channels_max;
        info->fragments_min = sinfo.fragments_min;
        info->fragments_max = sinfo.fragments_max;
 
-       if (!sinfo.access_mask) {
-               info->access_mask = 0;
-       }
-       if (!sinfo.format_mask) {
-               info->format_mask = 0;
-       }
-       if (sinfo.rate_min > sinfo.rate_max) {
-               info->rate_min = UINT_MAX;
-               info->rate_max = 0;
-       }
-       if (sinfo.fragment_size_min > sinfo.fragment_size_max) {
-               info->fragment_size_min = ULONG_MAX;
-               info->fragment_size_max = 0;
-       }
+       size = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max);
+       if (info->fragment_size_min < size)
+               info->fragment_size_min = size;
+       size = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
+       if (info->fragment_size_max > size)
+               info->fragment_size_max = size;
+       if (info->fragment_size_min > info->fragment_size_max)
+               return -EINVAL;
+
+       size = muldiv_down(sinfo.buffer_size_min, info->rate_min, sinfo.rate_max);
+       if (info->buffer_size_min < size)
+               info->buffer_size_min = size;
+       size = muldiv_up(sinfo.buffer_size_max, info->rate_max, sinfo.rate_min);
+       if (info->buffer_size_max > size)
+               info->buffer_size_max = size;
+       if (info->buffer_size_min > info->buffer_size_max)
+               return -EINVAL;
        if (err < 0)
                return err;
-       
-       fragment_size_min = muldiv_down(sinfo.fragment_size_min, info->rate_min, sinfo.rate_max);
-       fragment_size_max = muldiv_up(sinfo.fragment_size_max, info->rate_max, sinfo.rate_min);
-       if (fragment_size_min > info->fragment_size_min)
-               info->fragment_size_min = fragment_size_min;
-       if (fragment_size_max < info->fragment_size_max)
-               info->fragment_size_max = fragment_size_max;
-       rate->plug.saccess_mask = sinfo.access_mask;
-       info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
+
+       info->info = sinfo.info & ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
        snd_pcm_hw_info_complete(info);
        return 0;
 }
@@ -311,51 +309,35 @@ static int snd_pcm_rate_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
        snd_pcm_rate_t *rate = pcm->private;
        snd_pcm_t *slave = rate->plug.slave;
        snd_pcm_hw_info_t sinfo;
-       unsigned int format, access, crate;
+       snd_pcm_hw_params_t sparams;
        unsigned int src_format, dst_format;
        unsigned int src_rate, dst_rate;
-       size_t fragment_size;
        int mul, div;
        int err;
-       crate = params->rate;
-       format = params->format;
-       fragment_size = params->fragment_size;
-       access = params->access;
-       params->rate = rate->srate;
-       if (rate->sformat >= 0)
-               params->format = rate->sformat;
-       if (rate->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
-       else if (rate->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
-       else
-               assert(0);
-       params->fragment_size = muldiv_near(params->fragment_size, params->rate, crate);
        snd_pcm_hw_params_to_info(params, &sinfo);
-       sinfo.fragment_size_min = 0;
-       sinfo.fragment_size_max = ULONG_MAX;
-       err = snd_pcm_hw_info_rulesv(slave, &sinfo, params,
-                                    SND_PCM_RULE_REL_NEAR | SND_PCM_HW_PARAM_FRAGMENT_SIZE,
-                                    -1);
-       snd_pcm_hw_info_to_params(&sinfo, params);
-       if (err >= 0)
-               err = snd_pcm_hw_params(slave, params);
-       params->format = format;
-       params->rate = crate;
-       params->access = access;
-       params->fragment_size = fragment_size;
+       if (rate->sformat >= 0)
+               sinfo.format_mask = 1 << rate->sformat;
+       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
+       sinfo.rate_min = rate->srate;
+       sinfo.rate_max = rate->srate;
+       sinfo.fragment_size_min = muldiv_down(params->fragment_size, rate->srate, params->rate);
+       sinfo.fragment_size_max = muldiv_up(params->fragment_size, rate->srate, params->rate);
+       sinfo.buffer_size_min = muldiv_down(params->fragment_size * params->fragments, rate->srate, params->rate);
+       sinfo.buffer_size_max = muldiv_up(params->fragment_size * params->fragments, rate->srate, params->rate);
+       err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
+       params->fail_mask = sparams.fail_mask;
        if (err < 0)
                return err;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
-               src_format = format;
+               src_format = params->format;
                dst_format = slave->format;
-               src_rate = crate;
+               src_rate = params->rate;
                dst_rate = slave->rate;
        } else {
                src_format = slave->format;
-               dst_format = format;
+               dst_format = params->format;
                src_rate = slave->rate;
-               dst_rate = crate;
+               dst_rate = params->rate;
        }
        rate->get_idx = get_index(src_format, SND_PCM_FORMAT_S16);
        rate->put_idx = put_index(SND_PCM_FORMAT_S16, dst_format);
index 75a4b106d6ecbd97aae5da0ed84e34f1aaed7694..da35baab70afe15f7582f3275fde9a8e2a21bfd5 100644 (file)
@@ -428,7 +428,8 @@ static int snd_pcm_route_close(snd_pcm_t *pcm)
 static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
 {
        snd_pcm_route_t *route = pcm->private;
-       unsigned int format_mask, access_mask, channels_min, channels_max;
+       unsigned int format_mask, access_mask;
+       unsigned int channels_min, channels_max;
        int err;
        info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | 
                              SND_PCM_ACCBIT_RW_INTERLEAVED |
@@ -437,18 +438,19 @@ static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
        access_mask = info->access_mask;
        if (access_mask == 0)
                return -EINVAL;
+
        info->format_mask &= SND_PCM_FMTBIT_LINEAR;
        format_mask = info->format_mask;
        if (format_mask == 0)
                return -EINVAL;
+
        if (info->channels_min < 1)
                info->channels_min = 1;
-       if (info->channels_max > 1024)
-               info->channels_max = 1024;
-       if (info->channels_max < info->channels_min)
-               return -EINVAL;
        channels_min = info->channels_min;
        channels_max = info->channels_max;
+       if (channels_min > channels_max)
+               return -EINVAL;
+
        if (route->sformat >= 0)
                info->format_mask = 1U << route->sformat;
        if (route->schannels >= 0)
@@ -456,18 +458,13 @@ static int snd_pcm_route_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
                
        info->access_mask = SND_PCM_ACCBIT_MMAP;
        err = snd_pcm_hw_info(route->plug.slave, info);
-       if (info->format_mask) 
+       info->access_mask = access_mask;
+       if (route->sformat >= 0)
                info->format_mask = format_mask;
-       if (info->channels_min <= info->channels_max) {
+       if (route->schannels >= 0) {
                info->channels_min = channels_min;
                info->channels_max = channels_max;
        }
-       if (info->access_mask) {
-               route->plug.saccess_mask = info->access_mask;
-               info->access_mask = access_mask;
-       }
-       if (info->format_mask)
-               info->format_mask = format_mask;
        if (err < 0)
                return err;
        info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID);
@@ -479,34 +476,26 @@ static int snd_pcm_route_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
 {
        snd_pcm_route_t *route = pcm->private;
        snd_pcm_t *slave = route->plug.slave;
-       unsigned int format, access, channels;
        unsigned int src_format, dst_format;
+       snd_pcm_hw_info_t sinfo;
+       snd_pcm_hw_params_t sparams;
        int err;
-       format = params->format;
-       channels = params->channels;
-       access = params->access;
+       snd_pcm_hw_params_to_info(params, &sinfo);
        if (route->sformat >= 0)
-               params->format = route->sformat;
+               sinfo.format_mask = 1 << route->sformat;
        if (route->schannels >= 0)
-               params->channels = route->schannels;
-       if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_INTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
-       else if (route->plug.saccess_mask & SND_PCM_ACCBIT_MMAP_NONINTERLEAVED)
-               params->access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
-       else
-               assert(0);
-       err = snd_pcm_hw_params(slave, params);
-       params->format = format;
-       params->channels = channels;
-       params->access = access;
+               sinfo.channels_min = sinfo.channels_max = route->schannels;
+       sinfo.access_mask = SND_PCM_ACCBIT_MMAP;
+       err = snd_pcm_hw_params_info(slave, &sparams, &sinfo);
+       params->fail_mask = sparams.fail_mask;
        if (err < 0)
                return err;
        if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
-               src_format = format;
+               src_format = params->format;
                dst_format = slave->format;
        } else {
                src_format = slave->format;
-               dst_format = format;
+               dst_format = params->format;
        }
        route->params.get_idx = get_index(src_format, SND_PCM_FORMAT_U16);
        route->params.put_idx = put_index(SND_PCM_FORMAT_U32, dst_format);
index 148353c630bb9727c0ea4de74372305b4f5150e1..d116b2e862eaebc48d4791bc91942abc570288b1 100644 (file)
@@ -455,34 +455,38 @@ static int snd_pcm_share_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
        access_mask = info->access_mask;
        if (access_mask == 0)
                return -EINVAL;
-       if (info->channels_min < share->channels_count)
-               info->channels_min = share->channels_count;
-       if (info->channels_max > share->channels_count)
-               info->channels_max = share->channels_count;
-       if (info->channels_max > info->channels_max)
-               return -EINVAL;
+
        if (slave->format >= 0) {
                info->format_mask &= 1U << slave->format;
                if (!info->format_mask)
                        return -EINVAL;
        }
+
+       if (info->channels_min < share->channels_count)
+               info->channels_min = share->channels_count;
+       if (info->channels_max > share->channels_count)
+               info->channels_max = share->channels_count;
+       if (info->channels_min > info->channels_max)
+               return -EINVAL;
+
        if (slave->rate >= 0) {
                if (info->rate_min < (unsigned)slave->rate)
                        info->rate_min = slave->rate;
                if (info->rate_max > (unsigned)slave->rate)
                        info->rate_max = slave->rate;
-               if (info->rate_max > info->rate_max)
+               if (info->rate_min > info->rate_max)
                        return -EINVAL;
        }
+
        info->access_mask = SND_PCM_ACCBIT_MMAP;
        info->channels_min = info->channels_max = slave->channels_count;
        err = snd_pcm_hw_info(slave->pcm, info);
-       if (info->channels_min <= info->channels_max)
-               info->channels_min = info->channels_max = share->channels_count;
-       if (info->access_mask)
-               info->access_mask = access_mask;
+       info->access_mask = access_mask;
+       info->channels_min = info->channels_max = share->channels_count;
+       if (err < 0)
+               return err;
        info->info |= SND_PCM_INFO_DOUBLE;
-       return err;
+       return 0;
 }
 
 static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
@@ -494,15 +498,21 @@ static int snd_pcm_share_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
        Pthread_mutex_lock(&slave->mutex);
        if (slave->setup_count > 1 || 
            (slave->setup_count == 1 && !pcm->setup)) {
-               if (params->access != spcm->access ||
-                   params->format != spcm->format ||
-                   params->subformat != spcm->subformat ||
-                   params->rate != spcm->rate ||
-                   params->fragments != spcm->fragments ||
-                   params->fragment_size != spcm->fragment_size) {
-                       ERR("slave is already running with different setup");
+               params->fail_mask = 0;
+               if (params->format != spcm->format)
                        params->fail_mask |= SND_PCM_HW_PARBIT_FORMAT;
-                       return -EBUSY;
+               if (params->subformat != spcm->subformat)
+                       params->fail_mask |= SND_PCM_HW_PARBIT_SUBFORMAT;
+               if (params->rate != spcm->rate)
+                       params->fail_mask |= SND_PCM_HW_PARBIT_RATE;
+               if (params->fragments != spcm->fragments)
+                       params->fail_mask |= SND_PCM_HW_PARBIT_FRAGMENTS;
+               if (params->fragment_size != spcm->fragment_size)
+                       params->fail_mask |= SND_PCM_HW_PARBIT_FRAGMENT_SIZE;
+               if (params->fail_mask) {
+                       ERR("slave is already running with different setup");
+                       err = -EBUSY;
+                       goto _end;
                }
        } else {
                snd_pcm_hw_params_t sparams = *params;
index d543f22face9dd159205e1bf141a332b6576ee29..cc6bfa4c31f7dbbddb6049d6aebcb5f8d116361e 100644 (file)
@@ -41,8 +41,8 @@
 
 typedef struct {
        int socket;
-       unsigned int access_mask;
        volatile snd_pcm_shm_ctrl_t *ctrl;
+       unsigned int access_mask;
 } snd_pcm_shm_t;
 
 int receive_fd(int socket, void *data, size_t len, int *fd)
@@ -156,18 +156,19 @@ static int snd_pcm_shm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info)
        unsigned int access_mask = info->access_mask;
        ctrl->cmd = SND_PCM_IOCTL_HW_INFO;
        ctrl->u.hw_info = *info;
-       ctrl->u.hw_info.access_mask |= SND_PCM_ACCBIT_MMAP;
+       ctrl->u.hw_info.access_mask = SND_PCM_ACCBIT_MMAP;
        err = snd_pcm_shm_action(pcm);
-       *info = ctrl->u.hw_info;
-       if (info->access_mask) {
-               shm->access_mask = info->access_mask;
-               info->access_mask |= (SND_PCM_ACCESS_RW_INTERLEAVED |
-                                     SND_PCM_ACCESS_RW_NONINTERLEAVED);
-               info->access_mask &= access_mask;
-       }
        if (err < 0)
                return err;
-       return err;
+       access_mask &= (SND_PCM_ACCESS_RW_INTERLEAVED |
+                       SND_PCM_ACCESS_RW_NONINTERLEAVED |
+                       ctrl->u.hw_info.access_mask);
+       if (!access_mask)
+               return -EINVAL;
+       *info = ctrl->u.hw_info;
+       shm->access_mask = info->access_mask;
+       info->access_mask = access_mask;
+       return 0;
 }
 
 static int snd_pcm_shm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)