]> git.alsa-project.org Git - alsa-lib.git/commitdiff
More complete code
authorJaroslav Kysela <perex@perex.cz>
Tue, 17 Apr 2001 10:01:57 +0000 (10:01 +0000)
committerJaroslav Kysela <perex@perex.cz>
Tue, 17 Apr 2001 10:01:57 +0000 (10:01 +0000)
src/pcm/pcm_surr.c

index 66231bd66876352dffecbf890089e8df31cb6daf..af5be6e6f4f38b6e883ec931dbf9e28068087e8b 100644 (file)
@@ -31,6 +31,7 @@
 #include <byteswap.h>
 #include <limits.h>
 #include <sys/shm.h>
+#include "../control/control_local.h"
 #include "pcm_local.h"
 #include "pcm_plugin.h"
 
@@ -40,16 +41,15 @@ typedef struct {
        unsigned int channels;  /* count of channels (4 or 6) */
        int pcms;               /* count of PCM channels */
        snd_pcm_t *pcm[3];      /* up to three PCM stereo streams */    
+       int linked[3];          /* streams are linked */
 } snd_pcm_surround_t;
 
+static int snd_pcm_surround_free(snd_pcm_surround_t *surr);
+
 static int snd_pcm_surround_close(snd_pcm_t *pcm)
 {
-       int i;
        snd_pcm_surround_t *surr = pcm->private_data;
-       for (i = 0; i < surr->pcms; i++)
-               snd_pcm_close(surr->pcm[i]);
-       free(surr);
-       return 0;
+       return snd_pcm_surround_free(surr);
 }
 
 static int snd_pcm_surround_nonblock(snd_pcm_t *pcm, int nonblock)
@@ -86,12 +86,21 @@ static int snd_pcm_surround_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
 static int snd_pcm_surround_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
 {
        snd_pcm_surround_t *surr = pcm->private_data;
+       int err;
        if (surr->pcms == 1 || info->channel == 0 || info->channel == 1)
                return snd_pcm_channel_info(surr->pcm[0], info);
-       if (surr->pcms > 1 && (info->channel == 2 || info->channel == 3))
-               return snd_pcm_channel_info(surr->pcm[1], info);
-       if (surr->pcms > 2 && (info->channel == 3 || info->channel == 4))
-               return snd_pcm_channel_info(surr->pcm[2], info);
+       if (surr->pcms > 1 && (info->channel == 2 || info->channel == 3)) {
+               info->channel -= 2;
+               err = snd_pcm_channel_info(surr->pcm[1], info);
+               info->channel += 2;
+               return err;
+       }
+       if (surr->pcms > 2 && (info->channel == 3 || info->channel == 4)) {
+               info->channel -= 4;
+               err = snd_pcm_channel_info(surr->pcm[2], info);
+               info->channel += 4;
+               return err;
+       }
        return -EINVAL;
 }
 
@@ -239,53 +248,83 @@ static int snd_pcm_surround_interval_channels(snd_pcm_surround_t *surr,
        if (interval->empty)
                return -EINVAL;
        if (interval->openmin) {
-               if (!refine)
+               if (!refine) {
+                       interval->empty = 1;
                        return -EINVAL;
+               }
                interval->min = surr->channels;
                interval->openmin = 0;
        }
        if (interval->openmax) {
-               if (!refine)
+               if (!refine) {
+                       interval->empty = 1;
                        return -EINVAL;
+               }
                interval->max = surr->channels;
                interval->openmax = 0;
        }
        if (refine && interval->min <= surr->channels && interval->max >= surr->channels)
                interval->min = interval->max = surr->channels;
-       if (interval->min != interval->max || interval->min != surr->channels)
+       if (interval->min != interval->max || interval->min != surr->channels) {
+               interval->empty = 1;
                return -EINVAL;
+       }
        if (surr->pcms != 1)
                interval->min = interval->max = 2;
        return 0;
 }
 
+static void snd_pcm_surround_interval_channels_fixup(snd_pcm_surround_t *surr,
+                                                    snd_pcm_hw_params_t *params)
+{
+       snd_interval_t *interval;
+       interval = &params->intervals[SND_PCM_HW_PARAM_CHANNELS-SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
+       interval->min = interval->max = surr->channels;
+}
+
 static int snd_pcm_surround_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
 {
        snd_pcm_surround_t *surr = pcm->private_data;
-       int i, err = snd_pcm_surround_interval_channels(surr, params, 1);
+       int i, err;
+
+       err = snd_pcm_surround_interval_channels(surr, params, 1);
        if (err < 0)
                return err;
        if (surr->pcms == 1)
                return snd_pcm_hw_refine(surr->pcm[0], params);
        for (i = 0; i < surr->pcms; i++) {
                err = snd_pcm_hw_refine(surr->pcm[i], params);
-               if (err < 0)
+               if (err < 0) {
+                       snd_pcm_surround_interval_channels_fixup(surr, params);
                        return err;
+               }
        }
+       snd_pcm_surround_interval_channels_fixup(surr, params);
        return 0;
 }
 
 static int snd_pcm_surround_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
 {
        snd_pcm_surround_t *surr = pcm->private_data;
-       int i, err = snd_pcm_surround_interval_channels(surr, params, 0);
+       int i, err;
+       
+       err = snd_pcm_surround_interval_channels(surr, params, 0);
        if (err < 0)
                return err;
        if (surr->pcms == 1)
                return snd_pcm_hw_params(surr->pcm[0], params);
        for (i = 0; i < surr->pcms; i++) {
                err = snd_pcm_hw_params(surr->pcm[i], params);
-               if (err < 0)
+               if (err < 0) {
+                       snd_pcm_surround_interval_channels_fixup(surr, params);
+                       return err;
+               }
+       }
+       snd_pcm_surround_interval_channels_fixup(surr, params);
+       surr->linked[0] = 0;
+       for (i = 1; i < surr->pcms; i++) {
+               err = snd_pcm_link(surr->pcm[0], surr->pcm[1]);
+               if ((surr->linked[1] = (err >= 0)) == 0)
                        return err;
        }
        return 0;
@@ -293,14 +332,20 @@ static int snd_pcm_surround_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * para
 
 static int snd_pcm_surround_hw_free(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
 {
-       int i, err;
+       int i, err, res = 0;
        snd_pcm_surround_t *surr = pcm->private_data;
        for (i = 0; i < surr->pcms; i++) {
                err = snd_pcm_hw_free(surr->pcm[i]);
                if (err < 0)
-                       return err;
+                       res = err;
+               if (!surr->linked[i])
+                       continue;
+               surr->linked[i] = 0;
+               err = snd_pcm_unlink(surr->pcm[i]);
+               if (err < 0)
+                       res = err;
        }
-       return 0;
+       return res;
 }
 
 static int snd_pcm_surround_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
@@ -315,15 +360,15 @@ static int snd_pcm_surround_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * para
        return 0;
 }
 
-static int snd_pcm_surround_mmap(snd_pcm_t *pcm)
+static int snd_pcm_surround_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
 {
-       snd_pcm_surround_t *surr = pcm->private_data;
+       // snd_pcm_surround_t *surr = pcm->private_data;
        return 0;
 }
 
-static int snd_pcm_surround_munmap(snd_pcm_t *pcm)
+static int snd_pcm_surround_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
 {
-       snd_pcm_surround_t *surr = pcm->private_data;
+       // snd_pcm_surround_t *surr = pcm->private_data;
        return 0;
 }
 
@@ -370,23 +415,104 @@ snd_pcm_fast_ops_t snd_pcm_surround_fast_ops = {
        mmap_commit: snd_pcm_surround_mmap_commit,
 };
 
+static int snd_pcm_surround_free(snd_pcm_surround_t *surr)
+{
+       int i;
+
+       assert(surr);
+       for (i = 2; i >= 0; i--) {
+               if (surr->pcm[i] == NULL)
+                       continue;
+               snd_pcm_close(surr->pcm[i]);
+               surr->pcm[i] = NULL;
+       }
+       free(surr);
+       return 0;
+}
+
+static int snd_pcm_surround_three_streams(snd_pcm_surround_t *surr,
+                                         snd_pcm_surround_type_t type,
+                                         int card,
+                                         int dev0, int subdev0,
+                                         int dev1, int subdev1,
+                                         int dev2, int subdev2,
+                                         int mode)
+{
+       int err;
+
+       if ((err = snd_pcm_hw_open(&surr->pcm[0], "Surround L/R", card, dev0,
+                                  subdev0, SND_PCM_STREAM_PLAYBACK, mode)) < 0)
+               return err;
+       surr->pcms++;
+       if ((err = snd_pcm_hw_open(&surr->pcm[1], "Surround Rear L/R", card, dev1,
+                                  subdev1, SND_PCM_STREAM_PLAYBACK, mode)) < 0)
+               return err;
+       surr->pcms++;
+       if (type == SND_PCM_SURROUND_51) {
+               if ((err = snd_pcm_hw_open(&surr->pcm[2], "Surround Center/LFE", card, dev2,
+                                          subdev2, SND_PCM_STREAM_PLAYBACK, mode)) < 0)
+                       return err;
+               surr->pcms++;
+       }
+       return 0;
+}
+
 int snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, int card, int dev,
                          snd_pcm_surround_type_t type,
                          snd_pcm_stream_t stream, int mode)
 {
        snd_pcm_t *pcm;
        snd_pcm_surround_t *surr;
+       snd_ctl_t *ctl;
+       snd_ctl_card_info_t *info;
+       int err;
+
        assert(pcmp);
        if (stream == SND_PCM_STREAM_CAPTURE)
                return -EINVAL;                 /* not supported at the time */
+       if (dev != 0)
+               return -EINVAL;                 /* not supported at the time */
        surr = calloc(1, sizeof(snd_pcm_surround_t));
-       if (!surr) {
+       if (!surr)
                return -ENOMEM;
+       switch (type) {
+       case SND_PCM_SURROUND_40:
+               surr->channels = 4;
+               break;
+       case SND_PCM_SURROUND_51:
+               surr->channels = 6;
+               break;
+       default:
+               snd_pcm_surround_free(surr);
+               return -EINVAL;
        }
-       
+       if ((err = snd_ctl_hw_open(&ctl, "Surround", card, 0)) < 0) {
+               snd_pcm_surround_free(surr);
+               return err;
+       }
+       snd_ctl_card_info_alloca(&info);
+       if ((err = snd_ctl_card_info(ctl, info)) < 0) {
+               snd_ctl_close(ctl);
+               snd_pcm_surround_free(surr);
+               return err;
+       }
+       switch (snd_ctl_card_info_get_type(info)) {
+       case SND_CARD_TYPE_SI_7018:
+               if ((err = snd_pcm_surround_three_streams(surr, type, card,
+                                                         0, -1, 0, -1, 0, -1, mode)) < 0) {
+                       snd_pcm_surround_free(surr);
+                       return err;
+               }
+               break;
+       default:
+               snd_ctl_close(ctl);
+               snd_pcm_surround_free(surr);
+               return -ENODEV;
+       }
+       snd_ctl_close(ctl);
        pcm = calloc(1, sizeof(snd_pcm_t));
        if (!pcm) {
-               free(surr);
+               snd_pcm_surround_free(surr);
                return -ENOMEM;
        }
        if (name)
@@ -399,9 +525,9 @@ int snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, int card, int dev,
        pcm->fast_ops = &snd_pcm_surround_fast_ops;
        pcm->fast_op_arg = pcm;
        pcm->private_data = surr;
-       pcm->poll_fd = -1;      /* FIXME */
-       pcm->hw_ptr = NULL;     /* FIXME */
-       pcm->appl_ptr = NULL;   /* FIXME */
+       pcm->poll_fd = surr->pcm[0]->poll_fd;
+       pcm->hw_ptr = surr->pcm[0]->hw_ptr;
+       pcm->appl_ptr = surr->pcm[0]->appl_ptr;
        *pcmp = pcm;
 
        return 0;
@@ -456,12 +582,13 @@ int _snd_pcm_surround_open(snd_pcm_t **pcmp, const char *name, snd_config_t *con
                        continue;
                }
 #endif
-               if (strcmp(id, "type") == 0) {
+               if (strcmp(id, "stype") == 0) {
                        err = snd_config_get_string(n, &str);
                        if (strcmp(str, "40") == 0 || strcmp(str, "4.0") == 0)
                                type = SND_PCM_SURROUND_40;
                        else if (strcmp(str, "51") == 0 || strcmp(str, "5.1") == 0)
                                type = SND_PCM_SURROUND_51;
+                       continue;
                }
                SNDERR("Unknown field %s", id);
                return -EINVAL;