wmix: call `XSetCommand()`.
[dockapps.git] / wmix / mixer-oss.c
blob0c0e03fe2fac4c8e4d564b7c153c413becc4e7a8
1 /* WMix 3.0 -- a mixer using the OSS mixer API.
2 * Copyright (C) 2000, 2001
3 * Daniel Richard G. <skunk@mit.edu>,
4 * timecop <timecop@japan.co.jp>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <sys/ioctl.h>
31 #include <sys/soundcard.h>
33 #include "include/common.h"
34 #include "include/misc.h"
35 #include "include/mixer-oss.h"
37 #define WMVOLUME_CHANNEL_NAMES \
38 "Master volume", \
39 "Bass", \
40 "Treble", \
41 "FM Synth volume", \
42 "PCM Wave volume", \
43 "PC Speaker", \
44 "Line In level", \
45 "Microphone level", \
46 "CD volume", \
47 "Recording monitor", \
48 "PCM Wave 2 volume", \
49 "Recording volume", \
50 "Input gain", \
51 "Output gain", \
52 "Line In 1", \
53 "Line In 2", \
54 "Line In 3", \
55 "Digital In 1", \
56 "Digital In 2", \
57 "Digital In 3", \
58 "Phone input", \
59 "Phone output", \
60 "Video volume", \
61 "Radio volume", \
62 "Monitor volume"
64 #ifdef OSS_CHANNEL_NAMES
65 #define CHANNEL_NAMES SOUND_DEVICE_LABELS
66 #else
67 #define CHANNEL_NAMES WMVOLUME_CHANNEL_NAMES
68 #endif
70 typedef struct {
71 const char *name; /* name of channel */
72 const char *sname; /* short name of the channel */
73 int dev; /* channel device number */
74 int prev_dev_lr_volume; /* last known left/right volume
75 * (in device format) */
76 float volume; /* volume, in [0, 1] */
77 float balance; /* balance, in [-1, 1] */
78 bool can_record; /* capable of recording? */
79 bool is_recording; /* is it recording? */
80 bool is_stereo; /* capable of stereo? */
81 bool is_muted; /* is it muted? */
82 } MixerChannel;
84 static const char *channel_names[] = { CHANNEL_NAMES };
85 static const char *short_names[] = SOUND_DEVICE_LABELS;
87 static int mixer_fd;
89 static MixerChannel mixer[SOUND_MIXER_NRDEVICES];
90 static int n_channels = 0;
91 static int cur_channel = 0;
93 static int prev_modify_counter = -1;
95 static bool get_mixer_state(void)
97 struct mixer_info m_info;
98 int dev_lr_volume, dev_left_volume, dev_right_volume;
99 float left, right;
100 int srcmask;
101 int ch;
103 /* to really keep track of updates */
104 static MixerChannel oldmixer[SOUND_MIXER_NRDEVICES];
106 ioctl(mixer_fd, SOUND_MIXER_INFO, &m_info);
108 if (m_info.modify_counter == prev_modify_counter)
110 * Mixer state has not changed
112 return false;
114 /* Mixer state was changed by another program, so we need
115 * to update. As OSS cannot tell us specifically which
116 * channels changed, we read all of them in.
118 * prev_modify_counter was initialized to -1, so this part
119 * is guaranteed to run the first time this routine is
120 * called.
123 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
124 fprintf(stderr, "mixer read failed\n");
125 perror(NULL);
126 exit(EXIT_FAILURE);
129 for (ch = 0; ch < n_channels; ch++) {
130 if (ioctl(mixer_fd, MIXER_READ(mixer[ch].dev), &dev_lr_volume) ==
131 -1) {
132 fprintf(stderr, "mixer read failed\n");
133 exit(EXIT_FAILURE);
136 if (dev_lr_volume != mixer[ch].prev_dev_lr_volume) {
137 dev_left_volume = dev_lr_volume & 0xFF;
138 dev_right_volume = dev_lr_volume >> 8;
140 if ((dev_left_volume > 0) || (dev_right_volume > 0))
141 mixer[ch].is_muted = false;
143 left = (float) dev_left_volume / 100.0;
144 right = (float) dev_right_volume / 100.0;
146 if (!mixer[ch].is_muted) {
147 if (mixer[ch].is_stereo)
148 lr_to_vb(left,
149 right, &mixer[ch].volume, &mixer[ch].balance);
150 else {
151 mixer[ch].volume = left;
152 mixer[ch].balance = 0.0;
155 mixer[ch].prev_dev_lr_volume = dev_lr_volume;
158 mixer[ch].is_recording = ((1 << mixer[ch].dev) & srcmask) != 0;
160 prev_modify_counter = m_info.modify_counter;
161 /* check if this was due to OSS stupidity or if we really changed */
162 if (!memcmp(&mixer, &oldmixer, sizeof(mixer))) {
163 memcpy(&oldmixer, &mixer, sizeof(mixer));
164 return false;
166 memcpy(&oldmixer, &mixer, sizeof(mixer));
167 return true;
170 static void set_mixer_state(void)
172 float left, right;
173 int dev_left_volume, dev_right_volume, dev_lr_volume;
175 if (mixer[cur_channel].is_muted) {
176 left = 0.0;
177 right = 0.0;
178 } else
179 vb_to_lr(mixer[cur_channel].volume,
180 mixer[cur_channel].balance, &left, &right);
182 dev_left_volume = (int) (100.0 * left);
183 dev_right_volume = (int) (100.0 * right);
184 dev_lr_volume = (dev_right_volume << 8) | dev_left_volume;
185 ioctl(mixer_fd, MIXER_WRITE(mixer[cur_channel].dev), &dev_lr_volume);
188 static void get_record_state(void)
190 int srcmask;
191 int ch;
193 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
194 fprintf(stderr, "mixer read failed\n");
195 perror(NULL);
196 exit(EXIT_FAILURE);
199 for (ch = 0; ch < n_channels; ch++) {
200 mixer[ch].is_recording = ((1 << mixer[ch].dev) & srcmask) != 0;
204 static void set_record_state(void)
206 int srcmask;
208 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
209 fputs("error: recording source mask ioctl failed\n", stderr);
210 exit(EXIT_FAILURE);
213 if (((1 << mixer[cur_channel].dev) & srcmask) == 0)
214 srcmask |= (1 << mixer[cur_channel].dev);
215 else
216 srcmask &= ~(1 << mixer[cur_channel].dev);
218 if (ioctl(mixer_fd, SOUND_MIXER_WRITE_RECSRC, &srcmask) == -1) {
219 fputs("error: recording source mask ioctl failed\n", stderr);
220 exit(EXIT_FAILURE);
224 static bool is_exclude(const char *short_name, const char *exclude[])
226 int count;
227 int len;
229 for (count = 0; exclude[count] != NULL; count++) {
232 * Short names may be padded with spaces, because apparently there is a minimum
233 * length requirement of 6 characters for the name, and we do not want to
234 * include this padding in the match
236 len = strlen(short_name);
237 while (len > 0) {
238 if (short_name[len - 1] == ' ')
239 len--;
240 else
241 break;
244 if (strncmp(short_name, exclude[count], len) != 0)
245 continue;
247 if (exclude[count][len] != '\0')
248 continue;
250 /* Check the remaining in short name is only space */
251 while (short_name[len] == ' ')
252 len++;
254 if (short_name[len] == '\0')
255 return true;
257 return false;
260 void mixer_oss_init(const char *mixer_device, bool verbose, const char * exclude[])
262 int devmask, srcmask, recmask, stmask;
263 struct mixer_info m_info;
264 int count;
265 int mask;
267 mixer_fd = open(mixer_device, O_RDWR);
269 if (mixer_fd == -1) {
270 fprintf(stderr, "error: cannot open mixer device %s\n",
271 mixer_device);
272 exit(EXIT_FAILURE);
275 if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
276 fputs("error: device mask ioctl failed\n", stderr);
277 exit(EXIT_FAILURE);
280 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
281 fputs("error: recording source mask ioctl failed\n", stderr);
282 exit(EXIT_FAILURE);
285 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) {
286 fputs("error: recording mask ioctl failed\n", stderr);
287 exit(EXIT_FAILURE);
290 if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stmask) == -1) {
291 fputs("error: stereo mask ioctl failed\n", stderr);
292 exit(EXIT_FAILURE);
295 if (ioctl(mixer_fd, SOUND_MIXER_INFO, &m_info) == -1) {
296 fputs("error: could not read mixer info\n", stderr);
297 exit(EXIT_FAILURE);
300 if (verbose) {
301 printf("Sound card: %s (%s)\n", m_info.name, m_info.id);
302 puts("Supported channels:");
304 for (count = 0; count < SOUND_MIXER_NRDEVICES; count++) {
305 mask = 1 << count;
306 if (!(mask & devmask))
307 continue;
309 if (!is_exclude(short_names[count], exclude)) {
310 mixer[n_channels].name = channel_names[count];
311 mixer[n_channels].sname = short_names[count];
312 mixer[n_channels].dev = count;
313 mixer[n_channels].prev_dev_lr_volume = -1;
314 mixer[n_channels].can_record = (mask & recmask) != 0;
315 mixer[n_channels].is_recording = (mask & srcmask) != 0;
316 mixer[n_channels].is_stereo = (mask & stmask) != 0;
317 mixer[n_channels].is_muted = false;
318 ++n_channels;
319 if (verbose)
320 printf(" %d: %s \t(%s)\n", n_channels, channel_names[count], short_names[count]);
321 } else if (verbose)
322 printf(" x: %s \t(%s) - disabled\n", channel_names[count], short_names[count]);
324 get_mixer_state();
327 bool mixer_oss_is_changed(void)
329 return get_mixer_state();
332 int mixer_oss_get_channel_count(void)
334 return n_channels;
337 int mixer_oss_get_channel(void)
339 return cur_channel;
342 const char *mixer_oss_get_channel_name(void)
344 return mixer[cur_channel].name;
347 const char *mixer_oss_get_short_name(void)
349 return mixer[cur_channel].sname;
352 void mixer_oss_set_channel(int channel)
354 assert((channel >= 0) && (channel < n_channels));
356 cur_channel = channel;
357 get_record_state();
360 void mixer_oss_set_channel_rel(int delta_channel)
362 cur_channel = (cur_channel + delta_channel) % n_channels;
363 if (cur_channel < 0)
364 cur_channel += n_channels;
365 get_record_state();
368 float mixer_oss_get_volume(void)
370 get_mixer_state();
371 return mixer[cur_channel].volume;
374 void mixer_oss_set_volume(float volume)
376 assert((volume >= 0.0) && (volume <= 1.0));
378 mixer[cur_channel].volume = volume;
379 set_mixer_state();
382 void mixer_oss_set_volume_rel(float delta_volume)
384 mixer[cur_channel].volume += delta_volume;
385 mixer[cur_channel].volume = CLAMP(mixer[cur_channel].volume, 0.0, 1.0);
386 set_mixer_state();
389 float mixer_oss_get_balance(void)
391 get_mixer_state();
392 return mixer[cur_channel].balance;
395 void mixer_oss_set_balance(float balance)
397 assert((balance >= -1.0) && (balance <= 1.0));
399 if (mixer[cur_channel].is_stereo) {
400 mixer[cur_channel].balance = balance;
401 set_mixer_state();
405 void mixer_oss_set_balance_rel(float delta_balance)
407 if (mixer[cur_channel].is_stereo) {
408 mixer[cur_channel].balance += delta_balance;
409 mixer[cur_channel].balance =
410 CLAMP(mixer[cur_channel].balance, -1.0, 1.0);
411 set_mixer_state();
415 void mixer_oss_toggle_mute(void)
417 mixer[cur_channel].is_muted = !mixer[cur_channel].is_muted;
419 set_mixer_state();
422 void mixer_oss_toggle_rec(void)
424 if (mixer[cur_channel].can_record) {
425 mixer[cur_channel].is_recording = !mixer[cur_channel].is_recording;
426 set_record_state();
427 get_record_state();
431 bool mixer_oss_is_muted(void)
433 return mixer[cur_channel].is_muted;
436 bool mixer_oss_is_stereo(void)
438 return mixer[cur_channel].is_stereo;
441 bool mixer_oss_is_rec(void)
443 return mixer[cur_channel].is_recording;
446 bool mixer_oss_can_rec(void)
448 return mixer[cur_channel].can_record;