]> git.alsa-project.org Git - alsa-oss.git/commitdiff
Added range specification to simple mixer. Fixes to alsamixer. Added mixer support...
authorAbramo Bagnara <abramo@alsa-project.org>
Sun, 18 Feb 2001 17:56:38 +0000 (17:56 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Sun, 18 Feb 2001 17:56:38 +0000 (17:56 +0000)
alsa-oss.c

index a00e34a7099c651e69f45f45ef193d2af937350c..305df18de13324062bd17ad1048f749c4177d708 100644 (file)
@@ -36,6 +36,9 @@
 #include <linux/soundcard.h>
 #include <sys/asoundlib.h>
 
+snd_pcm_uframes_t _snd_pcm_boundary(snd_pcm_t *pcm);
+snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm);
+
 #define _GNU_SOURCE
 
 static int debug = 0;
@@ -88,9 +91,6 @@ typedef struct ops {
 } ops_t;
 
 
-#define FD_CLASSES 1
-static ops_t ops[FD_CLASSES];
-
 typedef struct {
        snd_pcm_t *pcm;
        size_t frame_bytes;
@@ -115,7 +115,20 @@ typedef struct {
        oss_dsp_stream_t streams[2];
 } oss_dsp_t;
 
-typedef enum { FD_OSS_DSP = 0 } fd_class_t;
+
+typedef struct {
+       snd_mixer_t *mix;
+       unsigned int modify_counter;
+       snd_mixer_elem_t *elems[SOUND_MIXER_NRDEVICES];
+} oss_mixer_t;
+
+typedef enum {
+  FD_OSS_DSP,
+  FD_OSS_MIXER,
+  FD_CLASSES,
+} fd_class_t;
+
+static ops_t ops[FD_CLASSES];
 
 typedef struct {
        int count;
@@ -315,7 +328,7 @@ static int oss_dsp_close(int fd)
        free(dsp);
        if (result < 0) {
                errno = -result;
-               return -1;
+               result = -1;
        }
        DEBUG("close(%d) -> %d", fd, result);
        if (result < 0)
@@ -401,6 +414,160 @@ static int oss_dsp_open(int card, int device, int oflag, mode_t mode)
        return -1;
 }
 
+int oss_mixer_dev(const char *name, unsigned int index)
+{
+       static struct {
+               char *name;
+               unsigned int index;
+       } id[SOUND_MIXER_NRDEVICES] = {
+               [SOUND_MIXER_VOLUME] = { "Master", 0 },
+               [SOUND_MIXER_BASS] = { "Tone Control - Bass", 0 },
+               [SOUND_MIXER_TREBLE] = { "Tone Control - Treble", 0 },
+               [SOUND_MIXER_SYNTH] = { "Synth", 0 },
+               [SOUND_MIXER_PCM] = { "PCM", 0 },
+               [SOUND_MIXER_SPEAKER] = { "PC Speaker", 0 },
+               [SOUND_MIXER_LINE] = { "Line", 0 },
+               [SOUND_MIXER_MIC] = { "Mic", 0 },
+               [SOUND_MIXER_CD] = { "CD", 0 },
+               [SOUND_MIXER_IMIX] = { "Monitor Mix", 0 },
+               [SOUND_MIXER_ALTPCM] = { "PCM", 1 },
+               [SOUND_MIXER_RECLEV] = { "-- nothing --", 0 },
+               [SOUND_MIXER_IGAIN] = { "Capture", 0 },
+               [SOUND_MIXER_OGAIN] = { "Playback", 0 },
+               [SOUND_MIXER_LINE1] = { "Aux", 0 },
+               [SOUND_MIXER_LINE2] = { "Aux", 1 },
+               [SOUND_MIXER_LINE3] = { "Aux", 2 },
+               [SOUND_MIXER_DIGITAL1] = { "Digital", 0 },
+               [SOUND_MIXER_DIGITAL2] = { "Digital", 1 },
+               [SOUND_MIXER_DIGITAL3] = { "Digital", 2 },
+               [SOUND_MIXER_PHONEIN] = { "Phone", 0 },
+               [SOUND_MIXER_PHONEOUT] = { "Phone", 1 },
+               [SOUND_MIXER_VIDEO] = { "Video", 0 },
+               [SOUND_MIXER_RADIO] = { "Radio", 0 },
+               [SOUND_MIXER_MONITOR] = { "Monitor", 0 },
+       };
+       unsigned int k;
+       for (k = 0; k < SOUND_MIXER_NRDEVICES; ++k) {
+               if (index == id[k].index &&
+                   strcmp(name, id[k].name) == 0)
+                       return k;
+       }
+       return -1;
+}
+
+static int oss_mixer_close(int fd)
+{
+       int err, result = 0;
+       oss_mixer_t *mixer = fds[fd]->private;
+       err = snd_mixer_close(mixer->mix);
+       if (err < 0)
+               result = err;
+       free(mixer);
+       if (result < 0) {
+               errno = -result;
+               result = -1;
+       }
+       DEBUG("close(%d) -> %d", fd, result);
+       if (result < 0)
+               DEBUG("(errno=%d)\n", errno);
+       else
+               DEBUG("\n");
+       return 0;
+}
+
+int mixer_elem_callback(snd_mixer_elem_t *elem, unsigned int mask)
+{
+       oss_mixer_t *mixer = snd_mixer_elem_get_callback_private(elem);
+       if (mask == SND_CTL_EVENT_MASK_REMOVE) {
+               int idx = oss_mixer_dev(snd_mixer_selem_get_name(elem),
+                                       snd_mixer_selem_get_index(elem));
+               if (idx >= 0)
+                       mixer->elems[idx] = 0;
+               return 0;
+       }
+       if (mask & SND_CTL_EVENT_MASK_VALUE) {
+               mixer->modify_counter++;
+       }
+       return 0;
+}
+
+int mixer_callback(snd_mixer_t *mixer, unsigned int mask, 
+                  snd_mixer_elem_t *elem)
+{
+       if (mask & SND_CTL_EVENT_MASK_ADD) {
+               oss_mixer_t *mix = snd_mixer_get_callback_private(mixer);
+               int idx = oss_mixer_dev(snd_mixer_selem_get_name(elem),
+                                       snd_mixer_selem_get_index(elem));
+               if (idx >= 0) {
+                       mix->elems[idx] = elem;
+                       snd_mixer_selem_set_playback_volume_range(elem, 0, 100);
+                       snd_mixer_selem_set_capture_volume_range(elem, 0, 100);
+                       snd_mixer_elem_set_callback(elem, mixer_elem_callback);
+                       snd_mixer_elem_set_callback_private(elem, snd_mixer_get_callback_private(mixer));
+               }
+       }
+       return 0;
+}
+
+static int oss_mixer_open(int card, int device, int oflag, mode_t mode ATTRIBUTE_UNUSED)
+{
+       oss_mixer_t *mixer;
+       int fd = -1;
+       int result;
+       char name[64];
+
+       switch (device) {
+       case OSS_DEVICE_MIXER:
+               sprintf(name, "mixer%d", card);
+               break;
+       case OSS_DEVICE_AMIXER:
+               sprintf(name, "amixer%d", card);
+               break;
+       default:
+               return RETRY;
+       }
+       switch (oflag & O_ACCMODE) {
+       case O_RDONLY:
+       case O_WRONLY:
+       case O_RDWR:
+               break;
+       default:
+               errno = EINVAL;
+               return -1;
+       }
+       fd = _open("/dev/null", oflag & O_ACCMODE);
+       assert(fd >= 0);
+       fds[fd] = calloc(1, sizeof(fd_t));
+       fds[fd]->class = FD_OSS_MIXER;
+       mixer = calloc(1, sizeof(oss_mixer_t));
+       if (!mixer) {
+               errno = -ENOMEM;
+               return -1;
+       }
+       fds[fd]->private = mixer;
+       result = snd_mixer_open(&mixer->mix);
+       if (result < 0)
+               goto _error;
+       result = snd_mixer_attach(mixer->mix, name);
+       if (result < 0)
+               goto _error1;
+       result = snd_mixer_selem_register(mixer->mix, NULL, NULL);
+       if (result < 0)
+               goto _error1;
+       snd_mixer_set_callback(mixer->mix, mixer_callback);
+       snd_mixer_set_callback_private(mixer->mix, mixer);
+       result = snd_mixer_load(mixer->mix);
+       if (result < 0)
+               goto _error1;
+       return fd;
+ _error1:
+       snd_mixer_close(mixer->mix);
+ _error:
+       close(fd);
+       errno = -result;
+       return -1;
+}
+
 static int oss_open(const char *file, int oflag, ...)
 {
        int result;
@@ -427,6 +594,11 @@ static int oss_open(const char *file, int oflag, ...)
                result = oss_dsp_open(card, device, oflag, mode);
                DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
                return result;
+       case OSS_DEVICE_MIXER:
+       case OSS_DEVICE_AMIXER:
+               result = oss_mixer_open(card, device, oflag, mode);
+               DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
+               return result;
        default:
                return RETRY;
        }
@@ -1041,8 +1213,320 @@ static int oss_dsp_munmap(int fd, void *addr ATTRIBUTE_UNUSED, size_t len ATTRIB
        return 0;
 }
 
-static ops_t ops[FD_CLASSES] = {
+static ssize_t oss_mixer_write(int fd ATTRIBUTE_UNUSED, const void *buf ATTRIBUTE_UNUSED, size_t n ATTRIBUTE_UNUSED)
+{
+       errno = -EBADFD;
+       return -1;
+}
+
+static ssize_t oss_mixer_read(int fd ATTRIBUTE_UNUSED, void *buf ATTRIBUTE_UNUSED, size_t n ATTRIBUTE_UNUSED)
+{
+       errno = -EBADFD;
+       return -1;
+}
+
+
+static int oss_mixer_read_recsrc(oss_mixer_t *mixer, unsigned int *ret)
+{
+       unsigned int mask = 0;
+       unsigned int k;
+       int err = 0;
+       for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+               snd_mixer_elem_t *elem = mixer->elems[k];
+               if (elem && 
+                   snd_mixer_selem_has_capture_switch(elem)) {
+                       int sw;
+                       err = snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &sw);
+                       if (err < 0)
+                               break;
+                       if (sw)
+                               mask |= 1 << k;
+               }
+       }
+       *ret = mask;
+       return err;
+}
+
+
+static int oss_mixer_ioctl(int fd, unsigned long cmd, ...)
+{
+       int err = 0;
+       va_list args;
+       void *arg;
+       oss_mixer_t *mixer = fds[fd]->private;
+       snd_mixer_t *mix = mixer->mix;
+       unsigned int dev;
+
+       va_start(args, cmd);
+       arg = va_arg(args, void *);
+       va_end(args);
+       DEBUG("ioctl(%d, ", fd);
+       switch (cmd) {
+       case OSS_GETVERSION:
+               *(int*)arg = SOUND_VERSION;
+               DEBUG("OSS_GETVERSION, %p) -> [%d]\n", arg, *(int*)arg);
+               break;
+       case SOUND_MIXER_INFO:
+       {
+               mixer_info *info = arg;
+               snd_mixer_handle_events(mix);
+               strcpy(info->id, "alsa-oss");
+               strcpy(info->name, "alsa-oss");
+               info->modify_counter = mixer->modify_counter;
+               DEBUG("SOUND_MIXER_INFO, %p) -> {%s, %s, %d}\n", info, info->id, info->name, info->modify_counter);
+               break;
+       }
+       case SOUND_OLD_MIXER_INFO:
+       {
+               _old_mixer_info *info = arg;
+               strcpy(info->id, "alsa-oss");
+               strcpy(info->name, "alsa-oss");
+               DEBUG("SOUND_OLD_MIXER_INFO, %p) -> {%s, %s}\n", info, info->id, info->name);
+               break;
+       }
+       case SOUND_MIXER_WRITE_RECSRC:
+       {
+               unsigned int k, mask = *(unsigned int *) arg;
+               unsigned int old;
+               int excl = 0;
+               DEBUG("SOUND_MIXER_WRITE_RECSRC, %p) -> [%x]", arg, mask);
+               err = oss_mixer_read_recsrc(mixer, &old);
+               if (err < 0)
+                       break;
+               for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+                       snd_mixer_elem_t *elem = mixer->elems[k];
+                       if (elem && 
+                           snd_mixer_selem_has_capture_switch(elem)) {
+                               if (!excl &&
+                                   snd_mixer_selem_has_capture_switch_exclusive(elem) &&
+                                   mask & ~old) {
+                                       mask &= ~old;
+                                       excl = 1;
+                               }
+                               err = snd_mixer_selem_set_capture_switch_all(elem, !!(mask & 1 << k));
+                               if (err < 0)
+                                       break;
+                       }
+               }
+               if (err < 0)
+                       break;
+               goto __read_recsrc;
+       }
+       case SOUND_MIXER_READ_RECSRC:
+       {
+               unsigned int mask;
+               DEBUG("SOUND_MIXER_READ_RECSRC, %p) ->", arg);
+       __read_recsrc:
+               err = oss_mixer_read_recsrc(mixer, &mask);
+               *(int *)arg = mask;
+               DEBUG(" [%x]\n", mask);
+               break;
+       }
+       case SOUND_MIXER_READ_DEVMASK:
+       {
+               int k, mask = 0;
+               for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+                       snd_mixer_elem_t *elem = mixer->elems[k];
+                       if (elem && 
+                           snd_mixer_selem_has_playback_volume(elem))
+                               mask |= 1 << k;
+               }
+               *(int *)arg = mask;
+               DEBUG("SOUND_MIXER_READ_DEVMASK, %p) -> [%x]\n", arg, mask);
+               break;
+       }
+       case SOUND_MIXER_READ_RECMASK:
+       {
+               int k, mask = 0;
+               for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+                       snd_mixer_elem_t *elem = mixer->elems[k];
+                       if (elem &&
+                           snd_mixer_selem_has_capture_switch(elem))
+                               mask |= 1 << k;
+               }
+               *(int *)arg = mask;
+               DEBUG("SOUND_MIXER_READ_RECMASK, %p) -> [%x]\n", arg, mask);
+               break;
+       }
+       case SOUND_MIXER_READ_STEREODEVS:
        {
+               int k, mask = 0;
+               for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+                       snd_mixer_elem_t *elem = mixer->elems[k];
+                       if (elem && 
+                           snd_mixer_selem_has_playback_volume(elem) &&
+                           !snd_mixer_selem_is_playback_mono(elem))
+                               mask |= 1 << k;
+               }
+               *(int *)arg = mask;
+               DEBUG("SOUND_MIXER_READ_STEREODEVS, %p) -> [%x]\n", arg, mask);
+               break;
+       }
+       case SOUND_MIXER_READ_CAPS:
+       {
+               int k;
+               *(int *)arg = 0;
+               for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+                       snd_mixer_elem_t *elem = mixer->elems[k];
+                       if (elem && 
+                           snd_mixer_selem_has_capture_switch_exclusive(elem)) {
+                               * (int*) arg = SOUND_CAP_EXCL_INPUT;
+                               break;
+                       }
+               }
+               DEBUG("SOUND_MIXER_READ_CAPS, %p) -> [%x]\n", arg, *(int*) arg);
+               break;
+       }
+       default:
+               if (cmd >= MIXER_WRITE(0) && cmd < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) {
+                       snd_mixer_elem_t *elem;
+                       long lvol, rvol;
+                       dev = cmd & 0xff;
+                       lvol = *(int *)arg & 0xff;
+                       if (lvol > 100)
+                               lvol = 100;
+                       rvol = (*(int *)arg >> 8) & 0xff;
+                       if (rvol > 100)
+                               rvol = 100;
+                       DEBUG("SOUND_MIXER_WRITE[%d], %p) -> {%ld, %ld}", dev, arg, lvol, rvol);
+                       elem = mixer->elems[dev];
+                       if (!elem) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       if (!snd_mixer_selem_has_playback_volume(elem)) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol);
+                       if (err < 0) 
+                               break;
+                       if (snd_mixer_selem_is_playback_mono(elem)) {
+                               if (snd_mixer_selem_has_playback_switch(elem))
+                                       err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0);
+                               if (err < 0) 
+                                       break;
+                       } else {
+                               err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol);
+                               if (err < 0) 
+                                       break;
+                               if (snd_mixer_selem_has_playback_switch(elem)) {
+                                       if (snd_mixer_selem_has_playback_switch_joined(elem))
+                                               err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0 || rvol != 0);
+                                       else {
+                                               err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0);
+                                               if (err < 0) 
+                                                       break;
+                                               err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol != 0);
+                                               if (err < 0) 
+                                                       break;
+                                       }
+                               }
+                       }
+                       if (!snd_mixer_selem_has_capture_volume(elem))
+                               break;
+                       err = snd_mixer_selem_set_capture_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol);
+                       if (err < 0) 
+                               break;
+                       if (!snd_mixer_selem_is_capture_mono(elem)) {
+                               err = snd_mixer_selem_set_capture_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol);
+                               if (err < 0) 
+                                       break;
+                       }
+                       goto __read;
+               }
+               if (cmd >= MIXER_READ(0) && cmd < MIXER_READ(SOUND_MIXER_NRDEVICES)) {
+                       snd_mixer_elem_t *elem;
+                       long lvol, rvol;
+                       int sw;
+                       dev = cmd & 0xff;
+                       DEBUG("SOUND_MIXER_READ[%d], %p) ->", dev, arg);
+               __read:
+                       elem = mixer->elems[dev];
+                       if (!elem) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       if (!snd_mixer_selem_has_playback_volume(elem)) {
+                               err = -EINVAL;
+                               break;
+                       }
+                       err = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &sw);
+                       if (err < 0) 
+                               break;
+                       if (sw) {
+                               err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &lvol);
+                               if (err < 0) 
+                                       break;
+                       } else
+                               lvol = 0;
+                       if (snd_mixer_selem_is_playback_mono(elem)) {
+                               rvol = lvol;
+                       } else {
+                               err = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &sw);
+                               if (err < 0) 
+                                       break;
+                               if (sw) {
+                                       err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &rvol);
+                                       if (err < 0) 
+                                               break;
+                               } else
+                                       rvol = 0;
+                       }
+                       * (int*) arg = lvol | (rvol << 8);
+                       DEBUG("{%ld, %ld}\n", lvol, rvol);
+                       break;
+               }
+               DEBUG("%lx, %p)\n", cmd, arg);
+               err = -ENXIO;
+               break;
+       }
+       if (err >= 0)
+               return 0;
+       errno = -err;
+       return -1;
+}
+
+static int oss_mixer_fcntl(int fd, int cmd, ...)
+{
+       int result;
+       va_list args;
+       long arg;
+
+       va_start(args, cmd);
+       arg = va_arg(args, long);
+       va_end(args);
+       
+       DEBUG("fcntl(%d, ", fd);
+       result = _fcntl(fd, cmd, arg);
+       if (result < 0)
+               return result;
+       switch (cmd) {
+       case F_DUPFD:
+               DEBUG("F_DUPFD, %ld)\n", arg);
+               fds[arg] = fds[fd];
+               return result;
+       default:
+               DEBUG("%x, %ld)\n", cmd, arg);
+               return result;
+       }
+       return -1;
+}
+
+static void *oss_mixer_mmap(void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED, int prot ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, int fd ATTRIBUTE_UNUSED, off_t offset ATTRIBUTE_UNUSED)
+{
+       errno = -EBADFD;
+       return MAP_FAILED;
+}
+
+static int oss_mixer_munmap(int fd ATTRIBUTE_UNUSED, void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED)
+{
+       errno = -EBADFD;
+       return -1;
+}
+
+static ops_t ops[FD_CLASSES] = {
+       [FD_OSS_DSP] = {
                open: oss_open,
                close: oss_dsp_close,
                write: oss_dsp_write,
@@ -1051,7 +1535,17 @@ static ops_t ops[FD_CLASSES] = {
                fcntl: oss_dsp_fcntl,
                mmap: oss_dsp_mmap,
                munmap: oss_dsp_munmap,
-       }
+       },
+       [FD_OSS_MIXER] = {
+               open: oss_open,
+               close: oss_mixer_close,
+               write: oss_mixer_write,
+               read: oss_mixer_read,
+               ioctl: oss_mixer_ioctl,
+               fcntl: oss_mixer_fcntl,
+               mmap: oss_mixer_mmap,
+               munmap: oss_mixer_munmap,
+       },
 };
 
 int open(const char *file, int oflag, ...)
@@ -1225,6 +1719,15 @@ void dump_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds,
 }
 #endif
 
+int snd_pcm_poll_descriptor(snd_pcm_t *pcm)
+{
+       int err;
+       struct pollfd pfds[2];
+       err = snd_pcm_poll_descriptors(pcm, pfds, 2);
+       assert(err == 1);
+       return pfds[0].fd;
+}
+
 int poll(struct pollfd *pfds, unsigned long nfds, int timeout)
 {
        unsigned int k;