summaryrefslogtreecommitdiff
path: root/wmamixer.c
diff options
context:
space:
mode:
authorNemu Manaka <beatrice@dangofactory.local>2026-08-20 03:56:21 +0900
committerNemu Manaka <beatrice@dangofactory.local>2026-08-20 03:56:21 +0900
commit5f40b453cdf38e10e851dba25a8d6cd2e327e6e6 (patch)
tree4e576a5d1ec688f224ca027c0339b1ed1b00ba19 /wmamixer.c
parent1a21b9d42f221e3d05b950eb06b9ac3a06af474f (diff)
downloadwmamixer-5f40b453cdf38e10e851dba25a8d6cd2e327e6e6.tar.gz
wmamixer-5f40b453cdf38e10e851dba25a8d6cd2e327e6e6.tar.bz2
wmamixer-5f40b453cdf38e10e851dba25a8d6cd2e327e6e6.zip
OSS
Diffstat (limited to 'wmamixer.c')
-rw-r--r--wmamixer.c288
1 files changed, 238 insertions, 50 deletions
diff --git a/wmamixer.c b/wmamixer.c
index d04d0c5..58ab9f0 100644
--- a/wmamixer.c
+++ b/wmamixer.c
@@ -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);