wmpager: add missing Makefile.am
[dockapps.git] / wmix / mixer-oss.c
blobe5616080e2b7dc65e1cb95f0eacf56f7dd906d89
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.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 void mixer_init(const char *mixer_device, bool verbose, const char * exclude[])
226 int devmask, srcmask, recmask, stmask;
227 struct mixer_info m_info;
228 int count;
229 int mask;
231 mixer_fd = open(mixer_device, O_RDWR);
233 if (mixer_fd == -1) {
234 fprintf(stderr, "error: cannot open mixer device %s\n",
235 mixer_device);
236 exit(EXIT_FAILURE);
239 if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
240 fputs("error: device mask ioctl failed\n", stderr);
241 exit(EXIT_FAILURE);
244 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
245 fputs("error: recording source mask ioctl failed\n", stderr);
246 exit(EXIT_FAILURE);
249 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) {
250 fputs("error: recording mask ioctl failed\n", stderr);
251 exit(EXIT_FAILURE);
254 if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stmask) == -1) {
255 fputs("error: stereo mask ioctl failed\n", stderr);
256 exit(EXIT_FAILURE);
259 if (ioctl(mixer_fd, SOUND_MIXER_INFO, &m_info) == -1) {
260 fputs("error: could not read mixer info\n", stderr);
261 exit(EXIT_FAILURE);
264 if (verbose) {
265 printf("%s (%s)\n", m_info.name, m_info.id);
266 puts("Supported channels:");
268 for (count = 0; count < SOUND_MIXER_NRDEVICES; count++) {
269 mask = 1 << count;
270 if ((mask & devmask) && (!is_exclude((short_names[count]),exclude))) {
271 mixer[n_channels].name = channel_names[count];
272 mixer[n_channels].sname = short_names[count];
273 mixer[n_channels].dev = count;
274 mixer[n_channels].prev_dev_lr_volume = -1;
275 mixer[n_channels].can_record = (mask & recmask) != 0;
276 mixer[n_channels].is_recording = (mask & srcmask) != 0;
277 mixer[n_channels].is_stereo = (mask & stmask) != 0;
278 mixer[n_channels].is_muted = false;
279 ++n_channels;
280 if (verbose)
281 printf(" %d: %s \t(%s)\n", n_channels,
282 channel_names[count],
283 short_names[count]);
284 } else if ((mask & devmask) && verbose)
285 printf(" x: %s \t(%s) - disabled\n", channel_names[count],
286 short_names[count]);
288 get_mixer_state();
291 bool mixer_is_changed(void)
293 return get_mixer_state();
296 int mixer_get_channel_count(void)
298 return n_channels;
301 int mixer_get_channel(void)
303 return cur_channel;
306 const char *mixer_get_channel_name(void)
308 return mixer[cur_channel].name;
311 const char *mixer_get_short_name(void)
313 return mixer[cur_channel].sname;
316 void mixer_set_channel(int channel)
318 assert((channel >= 0) && (channel < n_channels));
320 cur_channel = channel;
321 get_record_state();
324 void mixer_set_channel_rel(int delta_channel)
326 cur_channel = (cur_channel + delta_channel) % n_channels;
327 if (cur_channel < 0)
328 cur_channel += n_channels;
329 get_record_state();
332 float mixer_get_volume(void)
334 get_mixer_state();
335 return mixer[cur_channel].volume;
338 void mixer_set_volume(float volume)
340 assert((volume >= 0.0) && (volume <= 1.0));
342 mixer[cur_channel].volume = volume;
343 set_mixer_state();
346 void mixer_set_volume_rel(float delta_volume)
348 mixer[cur_channel].volume += delta_volume;
349 mixer[cur_channel].volume = CLAMP(mixer[cur_channel].volume, 0.0, 1.0);
350 set_mixer_state();
353 float mixer_get_balance(void)
355 get_mixer_state();
356 return mixer[cur_channel].balance;
359 void mixer_set_balance(float balance)
361 assert((balance >= -1.0) && (balance <= 1.0));
363 if (mixer[cur_channel].is_stereo) {
364 mixer[cur_channel].balance = balance;
365 set_mixer_state();
369 void mixer_set_balance_rel(float delta_balance)
371 if (mixer[cur_channel].is_stereo) {
372 mixer[cur_channel].balance += delta_balance;
373 mixer[cur_channel].balance =
374 CLAMP(mixer[cur_channel].balance, -1.0, 1.0);
375 set_mixer_state();
379 void mixer_toggle_mute(void)
381 mixer[cur_channel].is_muted = !mixer[cur_channel].is_muted;
383 set_mixer_state();
386 void mixer_toggle_rec(void)
388 if (mixer[cur_channel].can_record) {
389 mixer[cur_channel].is_recording = !mixer[cur_channel].is_recording;
390 set_record_state();
391 get_record_state();
395 bool mixer_is_muted(void)
397 return mixer[cur_channel].is_muted;
400 bool mixer_is_stereo(void)
402 return mixer[cur_channel].is_stereo;
405 bool mixer_is_rec(void)
407 return mixer[cur_channel].is_recording;
410 bool mixer_can_rec(void)
412 return mixer[cur_channel].can_record;
415 bool is_exclude(const char *short_name, const char *exclude[])
417 int count = 0;
418 while (count < SOUND_MIXER_NRDEVICES && exclude[count] != NULL){
419 if ( strcmp(short_name, exclude[count]) == 0 )
420 return true;
421 count++;
423 return false;