From 72304667293bc1770379d1f2064fcfe5888efc0b Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Sun, 18 Feb 2001 17:56:38 +0000 Subject: [PATCH] Added range specification to simple mixer. Fixes to alsamixer. Added mixer support to alsa-oss --- alsa-oss.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 510 insertions(+), 7 deletions(-) diff --git a/alsa-oss.c b/alsa-oss.c index a00e34a..305df18 100644 --- a/alsa-oss.c +++ b/alsa-oss.c @@ -36,6 +36,9 @@ #include #include +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; -- 2.47.1