diff options
| author | Nemu Manaka <beatrice@dangofactory.local> | 2026-08-20 03:56:21 +0900 |
|---|---|---|
| committer | Nemu Manaka <beatrice@dangofactory.local> | 2026-08-20 03:56:21 +0900 |
| commit | 5f40b453cdf38e10e851dba25a8d6cd2e327e6e6 (patch) | |
| tree | 4e576a5d1ec688f224ca027c0339b1ed1b00ba19 /wmamixer.c | |
| parent | 1a21b9d42f221e3d05b950eb06b9ac3a06af474f (diff) | |
| download | wmamixer-5f40b453cdf38e10e851dba25a8d6cd2e327e6e6.tar.gz wmamixer-5f40b453cdf38e10e851dba25a8d6cd2e327e6e6.tar.bz2 wmamixer-5f40b453cdf38e10e851dba25a8d6cd2e327e6e6.zip | |
OSS
Diffstat (limited to 'wmamixer.c')
| -rw-r--r-- | wmamixer.c | 288 |
1 files changed, 238 insertions, 50 deletions
@@ -1,4 +1,4 @@ -// wmamixer - An ALSA mixer designed for WindowMaker with scrollwheel support +// wmamixer - An ALSA/OSS mixer designed for WindowMaker with scrollwheel support // Copyright (C) 2015 Roman Dobosz <gryf@vimja.com> // Copyright (C) 2003 Damian Kramer <psiren@hibernaculum.net> // Copyright (C) 1998 Sam Hawker <shawkie@geocities.com> @@ -127,7 +127,8 @@ int main(int argc, char **argv) { } XFlush(d_display); - snd_mixer_handle_events(mix->handle); + if (mix->backend == BACKEND_ALSA) + snd_mixer_handle_events(mix->handle); usleep(50000); } } @@ -153,66 +154,107 @@ struct Mixer *Mixer_create() { const char *name; slideCaptureMono capabilities; - snd_mixer_selem_id_t *sid; - snd_mixer_elem_t *elem; - snd_mixer_selem_id_alloca(&sid); - assert(mixer != NULL); mixer->devices_no = 0; + mixer->backend = BACKEND_ALSA; + mixer->oss_fd_count = 0; + for(int i=0;i<MAX_OSS_DEVS;i++) { mixer->oss_fds[i] = -1; mixer->oss_paths[i] = NULL; } + + // --- Try ALSA first unless forced OSS --- + if (!force_oss) { + snd_mixer_selem_id_t *sid; + snd_mixer_elem_t *elem; + snd_mixer_selem_id_alloca(&sid); + + if ((err = snd_mixer_open(&mixer->handle, 0)) < 0) { + fprintf(stderr, "Mixer %s open error: %s\n", card, snd_strerror(err)); + } else if ((err = snd_mixer_attach(mixer->handle, card)) < 0) { + fprintf(stderr, "Mixer attach %s error: %s\n", card, snd_strerror(err)); + snd_mixer_close(mixer->handle); + mixer->handle = NULL; + } else if ((err = snd_mixer_selem_register(mixer->handle, + smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) { + fprintf(stderr, "Mixer register error: %s\n", snd_strerror(err)); + snd_mixer_close(mixer->handle); + mixer->handle = NULL; + } else { + err = snd_mixer_load(mixer->handle); - if ((err = snd_mixer_open(&mixer->handle, 0)) < 0) { - fprintf(stderr, "Mixer %s open error: %s", card, snd_strerror(err)); - exit(err); - } + if (err < 0) { + fprintf(stderr, "Mixer %s load error: %s\n", card, snd_strerror(err)); + snd_mixer_close(mixer->handle); + mixer->handle = NULL; + } else { + for (elem = snd_mixer_first_elem(mixer->handle); elem; + elem = snd_mixer_elem_next(elem)) { + snd_mixer_selem_get_id(elem, sid); - if (smixer_level == 0 && - (err = snd_mixer_attach(mixer->handle, card)) < 0) { - fprintf(stderr, "Mixer attach %s error: %s", card, snd_strerror(err)); - snd_mixer_close(mixer->handle); - exit(err); - } + if (!snd_mixer_selem_is_active(elem)) + continue; - if ((err = snd_mixer_selem_register(mixer->handle, - smixer_level > 0 ? &smixer_options : NULL, NULL)) < 0) { - fprintf(stderr, "Mixer register error: %s", snd_strerror(err)); - snd_mixer_close(mixer->handle); - exit(err); - } + capabilities = Mixer_get_capabilities(elem); - err = snd_mixer_load(mixer->handle); + if (!capabilities.isvolume) + continue; + name = snd_mixer_selem_id_get_name(sid); - if (err < 0) { - fprintf(stderr, "Mixer %s load error: %s", card, snd_strerror(err)); - snd_mixer_close(mixer->handle); - exit(err); - } - for (elem = snd_mixer_first_elem(mixer->handle); elem; - elem = snd_mixer_elem_next(elem)) { - snd_mixer_selem_get_id(elem, sid); + selems[mixer->devices_no] = malloc(sizeof(struct Selem)); + memset(selems[mixer->devices_no], 0, sizeof(struct Selem)); + selems[mixer->devices_no]->elem = elem; + selems[mixer->devices_no]->capture = capabilities.capture; + selems[mixer->devices_no]->oss_chan = -1; + selems[mixer->devices_no]->oss_dev_idx = -1; - if (!snd_mixer_selem_is_active(elem)) - continue; + Mixer_set_limits(elem, selems[mixer->devices_no]); + Mixer_set_selem_props(selems[mixer->devices_no], name); + Mixer_set_channels(selems[mixer->devices_no]); - capabilities = Mixer_get_capabilities(elem); + mixer->devices_no++; + if (mixer->devices_no == 32) { + break; + } + } + if (mixer->devices_no > 0) return mixer; + } + } + } - if (!capabilities.isvolume) - continue; - name = snd_mixer_selem_id_get_name(sid); + // --- Fallback to OSS (or forced) --- + fprintf(stderr, "%s: Using OSS backend\n", NAME); + mixer->backend = BACKEND_OSS; - selems[mixer->devices_no] = malloc(sizeof(struct Selem)); - selems[mixer->devices_no]->elem = elem; - selems[mixer->devices_no]->capture = capabilities.capture; + // Determine device list: command line args or default + int dev_count = g_oss_dev_count; + char **dev_list = g_oss_dev_list; + char *default_dev = "/dev/mixer"; - Mixer_set_limits(elem, selems[mixer->devices_no]); - Mixer_set_selem_props(selems[mixer->devices_no], name); - Mixer_set_channels(selems[mixer->devices_no]); + if (dev_count == 0) { + dev_count = 1; + dev_list = &default_dev; + } - mixer->devices_no++; - if (mixer->devices_no == 32) { - // stop here. This is ridiculous anyway - break; + for (int d = 0; d < dev_count && d < MAX_OSS_DEVS; d++) { + int fd = open(dev_list[d], O_RDWR | O_CLOEXEC); + if (fd < 0) { + fprintf(stderr, "%s: Warning: Cannot open OSS mixer %s: %s\n", NAME, dev_list[d], strerror(errno)); + continue; } + mixer->oss_fds[mixer->oss_fd_count] = fd; + mixer->oss_paths[mixer->oss_fd_count] = strdup(dev_list[d]); + mixer->oss_fd_count++; + } + + if (mixer->oss_fd_count == 0) { + fprintf(stderr, "%s: No OSS mixer devices could be opened.\n", NAME); + free(mixer); + exit(1); } + + // Enumerate channels from all opened devices + if (!Mixer_oss_enumerate(mixer)) { + fprintf(stderr, "%s: No supported OSS channels found on any device.\n", NAME); + } + return mixer; } @@ -224,6 +266,9 @@ void Mixer_set_selem_props(struct Selem *selem, const char *name) { *line = "Line", *aux = "Aux"; + // OSS elements have names set in Mixer_oss_enumerate + if (mix->backend == BACKEND_OSS) return; + if (strcmp("Master", name) == 0) { selem->name = "mstr"; selem->iconIndex = 0; @@ -420,6 +465,11 @@ static int convert_prange(long val, long min, long max) { int Mixer_get_volume(int current, int channelIndex) { struct Selem *selem = selems[current]; + if (mix->backend == BACKEND_OSS) { + int fd = mix->oss_fds[selem->oss_dev_idx]; + return Mixer_oss_get_volume(fd, selem->oss_chan); + } + long raw; int volume; @@ -437,6 +487,12 @@ int Mixer_get_volume(int current, int channelIndex) { void Mixer_set_volume(int current, int channelIndex, int value) { struct Selem *selem = selems[current]; + if (mix->backend == BACKEND_OSS) { + int fd = mix->oss_fds[selem->oss_dev_idx]; + Mixer_oss_set_volume(fd, selem->oss_chan, value); + return; + } + long raw; raw = (long)convert_prange1(value, selem->min, selem->max); @@ -468,7 +524,14 @@ int Mixer_read_right(int current) { void Mixer_destroy(struct Mixer *mixer) { assert(mixer != NULL); - snd_mixer_close(mixer->handle); + if (mixer->backend == BACKEND_ALSA && mixer->handle) + snd_mixer_close(mixer->handle); + if (mixer->backend == BACKEND_OSS) { + for (int d = 0; d < mixer->oss_fd_count; d++) { + if (mixer->oss_fds[d] >= 0) close(mixer->oss_fds[d]); + free(mixer->oss_paths[d]); + } + } free(mixer); } @@ -476,10 +539,117 @@ void Selem_destroy() { int i; for (i = 0; i < mix->devices_no; i++) { + free(selems[i]->name); free(selems[i]); } } +// ------------------------------------------------------------ +// OSS Implementation +// ------------------------------------------------------------ + +static const struct { + const char *name; + int idx; + int icon; + const char *short_name; +} oss_map[] = { + {"vol", SOUND_MIXER_VOLUME, 0, "vol"}, + {"pcm", SOUND_MIXER_PCM, 1, "pcm"}, + {"speaker", SOUND_MIXER_SPEAKER, 6, "spk"}, + {"line", SOUND_MIXER_LINE, 5, "lin"}, + {"mic", SOUND_MIXER_MIC, 4, "mic"}, + {"cd", SOUND_MIXER_CD, 12, "cd "}, + {"reclev", SOUND_MIXER_RECLEV, 3, "rec"}, + {"igain", SOUND_MIXER_IGAIN, 3, "iga"}, + {"ogain", SOUND_MIXER_OGAIN, 6, "oga"}, + {"line1", SOUND_MIXER_LINE1, 5, "ln1"}, + {"line2", SOUND_MIXER_LINE2, 5, "ln2"}, + {"line3", SOUND_MIXER_LINE3, 5, "ln3"}, + {"dig1", SOUND_MIXER_DIGITAL1, 15, "dg1"}, + {"dig2", SOUND_MIXER_DIGITAL2, 15, "dg2"}, + {"dig3", SOUND_MIXER_DIGITAL3, 15, "dg3"}, + {"phin", SOUND_MIXER_PHONEIN, 13, "phn"}, + {"phout", SOUND_MIXER_PHONEOUT, 13, "pho"}, + {"video", SOUND_MIXER_VIDEO, 14, "vid"}, + {"radio", SOUND_MIXER_RADIO, 14, "rad"}, + {"monitor", SOUND_MIXER_MONITOR, 6, "mon"}, +}; + + +bool Mixer_oss_enumerate(struct Mixer *mixer) { + bool found_any = false; + + for (int d = 0; d < mixer->oss_fd_count; d++) { + int fd = mixer->oss_fds[d]; + int devmask = 0, recmask = 0, recsrc = 0; + + if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask) < 0) continue; + ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask); + ioctl(fd, SOUND_MIXER_READ_RECSRC, &recsrc); + + const char *path = mixer->oss_paths[d]; + const char *num_str = ""; + if (path) { + const char *p = path + strlen(path) - 1; + while (p >= path && isdigit(*p)) p--; + if (p < path + strlen(path) - 1) { + num_str = p + 1; + } + } + + for (size_t i = 0; i < sizeof(oss_map)/sizeof(oss_map[0]); i++) { + int idx = oss_map[i].idx; + if (!(devmask & (1 << idx))) continue; + struct Selem *s = malloc(sizeof(struct Selem)); + memset(s, 0, sizeof(*s)); + char display_name[8]; + char base[5]; + strncpy(base, oss_map[i].short_name, 4); + base[4] = '\0'; + int blen = strlen(base); + while (blen > 0 && base[blen-1] == ' ') base[--blen] = '\0'; + snprintf(display_name, sizeof(display_name), "%s%s", base, num_str); + + s->name = strdup(display_name); + + s->iconIndex = oss_map[i].icon; + s->oss_chan = idx; + s->oss_dev_idx = d; + s->oss_devmask = devmask; + s->stereo = true; + s->capture = (recmask & (1 << idx)) != 0; + s->elem = NULL; + + selems[mixer->devices_no++] = s; + found_any = true; + if (mixer->devices_no >= 32) return found_any; + } + } + return found_any; +} + +int Mixer_oss_get_volume(int fd, int chan) { + int vol = 0; + // Try stereo ioctl (MIXER_READ with | (1<<8) for stereo) + if (ioctl(fd, MIXER_READ(chan | (1 << 8)), &vol) == 0) { + return (vol & 0x7f) * 100 / 127; + } + // Fallback to legacy mono ioctl + if (ioctl(fd, SOUND_MIXER_READ_VOLUME, &vol) == 0) { + return (vol & 0x7f) * 100 / 127; + } + return 0; +} + +int Mixer_oss_set_volume(int fd, int chan, int value) { + int vol = (value * 127) / 100; + if (vol < 0) vol = 0; + if (vol > 127) vol = 127; + int stereo_vol = vol | (vol << 8); + if (ioctl(fd, MIXER_WRITE(chan | (1 << 8)), &stereo_vol) == 0) return 0; + return ioctl(fd, SOUND_MIXER_WRITE_VOLUME, &vol); +} void initXWin(int argc, char **argv) { bool pos; @@ -595,6 +765,22 @@ void scanArgs(int argc, char **argv) { if (strcmp(argv[i], "-novol") == 0) no_volume_display = 1; + // OSS Device selection (multiple allowed) + if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--oss") == 0) { + force_oss = true; + if (i + 1 < argc && argv[i+1][0] != '-') { + if (g_oss_dev_count < MAX_OSS_DEVS) { + g_oss_dev_list[g_oss_dev_count++] = argv[++i]; + } + } else { + // -o without argument implies default /dev/mixer + if (g_oss_dev_count < MAX_OSS_DEVS) { + g_oss_dev_list[g_oss_dev_count++] = "/dev/mixer"; + } + } + continue; + } + if (strcmp(argv[i], "-d") == 0) { if (i < argc - 1) { i++; @@ -877,7 +1063,7 @@ void drawBtn(int x, int y, int w, int h, bool down) { } void usage() { - printf("%s - An ALSA mixer designed for WindowMaker with ", NAME); + printf("%s - An ALSA/OSS mixer designed for WindowMaker with ", NAME); fputs("\ scrollwheel support\n\ Copyright (C) 2015 Roman Dobosz <gryf@vimja.com>\n\ @@ -894,9 +1080,11 @@ See the README file for a more complete notice.\n\n", stdout); -w use WithdrawnState (for WindowMaker)\n\ -s shaped window\n\ -a use smaller window (for AfterStep Wharf)\n\ + -novol do not display volume level\n\ + -o | --oss [dev] use OSS mixer (default /dev/mixer), can be specified multiple times\n\ -l led_color use the specified color for led display\n\ -b back_color use the specified color for backgrounds\n\ - -d alsa_device use specified device (rather than 'default')\n\ + -d alsa_device use specified ALSA device (rather than 'default')\n\ -position position set window position (see X manual pages)\n\ -display display select target display (see X manual pages)\n\n", stdout); |
