]> git.alsa-project.org Git - alsa-lib.git/commitdiff
Implemented correctly pcm_plug layer
authorAbramo Bagnara <abramo@alsa-project.org>
Fri, 8 Dec 2000 15:41:14 +0000 (15:41 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Fri, 8 Dec 2000 15:41:14 +0000 (15:41 +0000)
src/pcm/pcm_local.h
src/pcm/pcm_plug.c

index 82b8f7de2a0252cd6c6077d5c2566be85b7d42bf..c4c047a8e64547663ebcff9f71320b3ea5ee1900 100644 (file)
@@ -173,6 +173,10 @@ void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *i
 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);
+int snd_pcm_hw_info_par_nearest_next(const snd_pcm_hw_info_t *info,
+                                    unsigned int param,
+                                    unsigned int best, int value,
+                                    snd_pcm_t *pcm);
 
 static inline size_t snd_pcm_mmap_playback_avail(snd_pcm_t *pcm)
 {
index 28b2af9f25cfafbc240f491794c7a6d83d9fcb87..39930a1c8f62bf1997f0bf5806052c720ee02dd6 100644 (file)
@@ -31,80 +31,6 @@ typedef struct {
        unsigned int tt_ssize, tt_cused, tt_sused;
 } snd_pcm_plug_t;
 
-
-static int format_badness(unsigned int src, unsigned int dst)
-{
-       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 {
-               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)
 {
        snd_pcm_plug_t *plug = pcm->private;
@@ -148,11 +74,117 @@ static int snd_pcm_plug_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
        return 0;
 }
 
+static unsigned int linear_preferred_formats[] = {
+#ifdef SND_LITTLE_ENDIAN
+       SND_PCM_FORMAT_S16_LE,
+       SND_PCM_FORMAT_U16_LE,
+       SND_PCM_FORMAT_S16_BE,
+       SND_PCM_FORMAT_U16_BE,
+#else
+       SND_PCM_FORMAT_S16_BE,
+       SND_PCM_FORMAT_U16_BE,
+       SND_PCM_FORMAT_S16_LE,
+       SND_PCM_FORMAT_U16_LE,
+#endif
+#ifdef SND_LITTLE_ENDIAN
+       SND_PCM_FORMAT_S24_LE,
+       SND_PCM_FORMAT_U24_LE,
+       SND_PCM_FORMAT_S24_BE,
+       SND_PCM_FORMAT_U24_BE,
+#else
+       SND_PCM_FORMAT_S24_BE,
+       SND_PCM_FORMAT_U24_BE,
+       SND_PCM_FORMAT_S24_LE,
+       SND_PCM_FORMAT_U24_LE,
+#endif
+#ifdef SND_LITTLE_ENDIAN
+       SND_PCM_FORMAT_S32_LE,
+       SND_PCM_FORMAT_U32_LE,
+       SND_PCM_FORMAT_S32_BE,
+       SND_PCM_FORMAT_U32_BE,
+#else
+       SND_PCM_FORMAT_S32_BE,
+       SND_PCM_FORMAT_U32_BE,
+       SND_PCM_FORMAT_S32_LE,
+       SND_PCM_FORMAT_U32_LE,
+#endif
+       SND_PCM_FORMAT_S8,
+       SND_PCM_FORMAT_U8
+};
+
+static unsigned int nonlinear_preferred_formats[] = {
+       SND_PCM_FORMAT_MU_LAW,
+       SND_PCM_FORMAT_A_LAW,
+       SND_PCM_FORMAT_IMA_ADPCM,
+};
+
+static int snd_pcm_plug_slave_format(int format, unsigned int format_mask)
+{
+       int w, u, e, wid, w1, dw;
+       if (format_mask & (1 << format))
+               return format;
+       if (!((1 << format) & SND_PCM_FMTBIT_LINEAR)) {
+               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(linear_preferred_formats) / sizeof(linear_preferred_formats[0]); ++i) {
+                               unsigned int f = linear_preferred_formats[i];
+                               if (format_mask & (1 << f))
+                                       return f;
+                       }
+                       /* Fall through */
+               default:
+                       return -EINVAL;
+               }
+
+       }
+       if (!(format_mask & SND_PCM_FMTBIT_LINEAR)) {
+               unsigned int i;
+               for (i = 0; i < sizeof(nonlinear_preferred_formats) / sizeof(nonlinear_preferred_formats[0]); ++i) {
+                       unsigned int f = nonlinear_preferred_formats[i];
+                       if (format_mask & (1 << f))
+                               return f;
+               }
+               return -EINVAL;
+       }
+       w = snd_pcm_format_width(format);
+       u = snd_pcm_format_unsigned(format);
+       e = snd_pcm_format_big_endian(format);
+       w1 = w;
+       dw = 8;
+       for (wid = 0; wid < 4; ++wid) {
+               int end, e1 = e;
+               for (end = 0; end < 2; ++end) {
+                       int sgn, u1 = u;
+                       for (sgn = 0; sgn < 2; ++sgn) {
+                               int f;
+                               f = snd_pcm_build_linear_format(w1, u1, e1);
+                               if (f >= 0 && format_mask & (1 << f))
+                                       return f;
+                               u1 = !u1;
+                       }
+                       e1 = !e1;
+               }
+               if (w1 < 32)
+                       w1 += dw;
+               else {
+                       w1 = w - 8;
+                       dw = -8;
+               }
+       }
+       return -EINVAL;
+}
+
 static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
 {
        snd_pcm_plug_t *plug = pcm->private;
        snd_pcm_t *slave = plug->req_slave;
        snd_pcm_hw_info_t sinfo;
+       int rate_min, rate_max;
+       int channels_min, channels_max;
+       unsigned int format, format_mask;
        int err;
        
        info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | 
@@ -193,6 +225,62 @@ static int snd_pcm_plug_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
        sinfo.rate_max = RATE_MAX;
 
        err = snd_pcm_hw_info(slave, &sinfo);
+       if (err < 0)
+               goto _err;
+       
+       rate_min = snd_pcm_hw_info_par_nearest_next(&sinfo,
+                                                   SND_PCM_HW_INFO_RATE,
+                                                   info->rate_min, -1,
+                                                   slave);
+       assert(rate_min >= 0);
+       if ((int)info->rate_max - rate_min > 1) {
+               rate_max = snd_pcm_hw_info_par_nearest_next(&sinfo,
+                                                           SND_PCM_HW_INFO_RATE,
+                                                           info->rate_max, -1,
+                                                           slave);
+               assert(rate_max >= 0);
+       } else
+               rate_max = rate_min;
+       sinfo.rate_min = rate_min;
+       sinfo.rate_max = rate_max;
+
+       err = snd_pcm_hw_info(slave, &sinfo);
+       assert(err >= 0);
+
+       channels_min = snd_pcm_hw_info_par_nearest_next(&sinfo,
+                                                   SND_PCM_HW_INFO_CHANNELS,
+                                                   info->channels_min, -1,
+                                                   slave);
+       assert(channels_min >= 0);
+       if ((int)info->channels_max - channels_min > 1) {
+               channels_max = snd_pcm_hw_info_par_nearest_next(&sinfo,
+                                                           SND_PCM_HW_INFO_CHANNELS,
+                                                           info->channels_max, -1,
+                                                           slave);
+               assert(channels_max >= 0);
+       } else
+               channels_max = channels_min;
+       sinfo.channels_min = channels_min;
+       sinfo.channels_max = channels_max;
+
+       err = snd_pcm_hw_info(slave, &sinfo);
+       assert(err >= 0);
+
+       format_mask = 0;
+       for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
+               int f;
+               if (!(info->format_mask & (1 << format)))
+                       continue;
+               f = snd_pcm_plug_slave_format(format, sinfo.format_mask);
+               assert(f >= 0);
+               format_mask |= (1 << f);
+       }
+       sinfo.format_mask = format_mask;
+
+       err = snd_pcm_hw_info(slave, &sinfo);
+       assert(err >= 0);
+
+ _err:
        info->subformat_mask = sinfo.subformat_mask;
        info->fragment_length_min = sinfo.fragment_length_min;
        info->fragment_length_max = sinfo.fragment_length_max;
@@ -407,9 +495,7 @@ 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 format;
        int err;
 
        sparams = *params;
@@ -422,44 +508,41 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
        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;
-       err = snd_pcm_strategy_simple(&strategy, 1000000, 2000000);
+
+       err = snd_pcm_hw_info(slave, &sinfo);
        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_strategy_free(strategy);
-               return err;
-       }
-       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;
-       }
-       nformats = snd_pcm_plug_format_choices(pcm->stream, formats,
-                                              params->format, 
-                                              sinfo.format_mask);
-       err = snd_pcm_strategy_simple_choices(strategy, 2, SND_PCM_HW_PARAM_FORMAT,
-                                             nformats, formats);
-       if (err < 0) {
-               snd_pcm_strategy_free(strategy);
+
+       err = snd_pcm_hw_info_par_nearest_next(&sinfo,
+                                              SND_PCM_HW_INFO_RATE,
+                                              params->rate, -1,
+                                              slave);
+       if (err < 0)
                return err;
-       }
-       err = snd_pcm_hw_info_strategy(slave, &sinfo, strategy);
-       snd_pcm_strategy_free(strategy);
+       sinfo.rate_min = sinfo.rate_max = err;
+
+       err = snd_pcm_hw_info(slave, &sinfo);
+       assert(err >= 0);
+
+       err = snd_pcm_hw_info_par_nearest_next(&sinfo,
+                                              SND_PCM_HW_INFO_CHANNELS,
+                                              params->channels, -1,
+                                              slave);
        if (err < 0)
                return err;
+       sinfo.channels_min = sinfo.channels_max = 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;
+       err = snd_pcm_hw_info(slave, &sinfo);
+       assert(err >= 0);
+
+       format = snd_pcm_plug_slave_format(params->format, sinfo.format_mask);
+       assert(format >= 0);
+
+       sparams.format = format;
        assert(sinfo.rate_min == sinfo.rate_max);
        sparams.rate = sinfo.rate_min;
+       assert(sinfo.channels_min == sinfo.channels_max);
+       sparams.channels = sinfo.channels_min;
        
        err = snd_pcm_plug_insert_plugins(pcm, params, &sparams);
        if (err < 0)