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>
5 *
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.
10 *
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.
15 *
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
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>
32
33 #include "include/common.h"
34 #include "include/misc.h"
35 #include "include/mixer.h"
36
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"
63
64 #ifdef OSS_CHANNEL_NAMES
65 #define CHANNEL_NAMES SOUND_DEVICE_LABELS
66 #else
67 #define CHANNEL_NAMES WMVOLUME_CHANNEL_NAMES
68 #endif
69
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;
83
84 static const char *channel_names[] = { CHANNEL_NAMES };
85 static const char *short_names[] = SOUND_DEVICE_LABELS;
86
87 static int mixer_fd;
88
89 static MixerChannel mixer[SOUND_MIXER_NRDEVICES];
90 static int n_channels = 0;
91 static int cur_channel = 0;
92
93 static int prev_modify_counter = -1;
94
get_mixer_state(void)95 static bool get_mixer_state(void)
96 {
97 #if 0
98 struct mixer_info m_info;
99 #endif
100 int dev_lr_volume, dev_left_volume, dev_right_volume;
101 float left, right;
102 int srcmask;
103 int ch;
104
105 /* to really keep track of updates */
106 static MixerChannel oldmixer[SOUND_MIXER_NRDEVICES];
107
108 #if 0
109 ioctl(mixer_fd, SOUND_MIXER_INFO, &m_info);
110
111
112 if (m_info.modify_counter == prev_modify_counter)
113 /*
114 * Mixer state has not changed
115 */
116 return false;
117 #endif
118 /* Mixer state was changed by another program, so we need
119 * to update. As OSS cannot tell us specifically which
120 * channels changed, we read all of them in.
121 *
122 * prev_modify_counter was initialized to -1, so this part
123 * is guaranteed to run the first time this routine is
124 * called.
125 */
126
127 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
128 fprintf(stderr, "mixer read failed\n");
129 perror(NULL);
130 exit(EXIT_FAILURE);
131 }
132
133 for (ch = 0; ch < n_channels; ch++) {
134 if (ioctl(mixer_fd, MIXER_READ(mixer[ch].dev), &dev_lr_volume) ==
135 -1) {
136 fprintf(stderr, "mixer read failed\n");
137 exit(EXIT_FAILURE);
138 }
139
140 if (dev_lr_volume != mixer[ch].prev_dev_lr_volume) {
141 dev_left_volume = dev_lr_volume & 0xFF;
142 dev_right_volume = dev_lr_volume >> 8;
143
144 if ((dev_left_volume > 0) || (dev_right_volume > 0))
145 mixer[ch].is_muted = false;
146
147 left = (float) dev_left_volume / 100.0;
148 right = (float) dev_right_volume / 100.0;
149
150 if (!mixer[ch].is_muted) {
151 if (mixer[ch].is_stereo)
152 lr_to_vb(left,
153 right, &mixer[ch].volume, &mixer[ch].balance);
154 else {
155 mixer[ch].volume = left;
156 mixer[ch].balance = 0.0;
157 }
158
159 mixer[ch].prev_dev_lr_volume = dev_lr_volume;
160 }
161 }
162 mixer[ch].is_recording = ((1 << mixer[ch].dev) & srcmask) != 0;
163 }
164 /* prev_modify_counter = m_info.modify_counter; */
165 /* check if this was due to OSS stupidity or if we really changed */
166 if (!memcmp(&mixer, &oldmixer, sizeof(mixer))) {
167 memcpy(&oldmixer, &mixer, sizeof(mixer));
168 return false;
169 }
170 memcpy(&oldmixer, &mixer, sizeof(mixer));
171 return true;
172 }
173
set_mixer_state(void)174 static void set_mixer_state(void)
175 {
176 float left, right;
177 int dev_left_volume, dev_right_volume, dev_lr_volume;
178
179 if (mixer[cur_channel].is_muted) {
180 left = 0.0;
181 right = 0.0;
182 } else
183 vb_to_lr(mixer[cur_channel].volume,
184 mixer[cur_channel].balance, &left, &right);
185
186 dev_left_volume = (int) (100.0 * left);
187 dev_right_volume = (int) (100.0 * right);
188 dev_lr_volume = (dev_right_volume << 8) | dev_left_volume;
189 ioctl(mixer_fd, MIXER_WRITE(mixer[cur_channel].dev), &dev_lr_volume);
190 }
191
get_record_state(void)192 static void get_record_state(void)
193 {
194 int srcmask;
195 int ch;
196
197 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
198 fprintf(stderr, "mixer read failed\n");
199 perror(NULL);
200 exit(EXIT_FAILURE);
201 }
202
203 for (ch = 0; ch < n_channels; ch++) {
204 mixer[ch].is_recording = ((1 << mixer[ch].dev) & srcmask) != 0;
205 }
206 }
207
set_record_state(void)208 static void set_record_state(void)
209 {
210 int srcmask;
211
212 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
213 fputs("error: recording source mask ioctl failed\n", stderr);
214 exit(EXIT_FAILURE);
215 }
216
217 if (((1 << mixer[cur_channel].dev) & srcmask) == 0)
218 srcmask |= (1 << mixer[cur_channel].dev);
219 else
220 srcmask &= ~(1 << mixer[cur_channel].dev);
221
222 if (ioctl(mixer_fd, SOUND_MIXER_WRITE_RECSRC, &srcmask) == -1) {
223 fputs("error: recording source mask ioctl failed\n", stderr);
224 exit(EXIT_FAILURE);
225 }
226 }
227
mixer_init(const char * mixer_device,bool verbose,const char * exclude[])228 void mixer_init(const char *mixer_device, bool verbose, const char * exclude[])
229 {
230 int devmask, srcmask, recmask, stmask;
231 #if 0
232 struct mixer_info m_info;
233 #endif
234 int count;
235 int mask;
236
237 mixer_fd = open(mixer_device, O_RDWR);
238
239 if (mixer_fd == -1) {
240 fprintf(stderr, "error: cannot open mixer device %s\n",
241 mixer_device);
242 exit(EXIT_FAILURE);
243 }
244
245 if (ioctl(mixer_fd, SOUND_MIXER_READ_DEVMASK, &devmask) == -1) {
246 fputs("error: device mask ioctl failed\n", stderr);
247 exit(EXIT_FAILURE);
248 }
249
250 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECSRC, &srcmask) == -1) {
251 fputs("error: recording source mask ioctl failed\n", stderr);
252 exit(EXIT_FAILURE);
253 }
254
255 if (ioctl(mixer_fd, SOUND_MIXER_READ_RECMASK, &recmask) == -1) {
256 fputs("error: recording mask ioctl failed\n", stderr);
257 exit(EXIT_FAILURE);
258 }
259
260 if (ioctl(mixer_fd, SOUND_MIXER_READ_STEREODEVS, &stmask) == -1) {
261 fputs("error: stereo mask ioctl failed\n", stderr);
262 exit(EXIT_FAILURE);
263 }
264
265 #if 0
266 if (ioctl(mixer_fd, SOUND_MIXER_INFO, &m_info) == -1) {
267 fputs("error: could not read mixer info\n", stderr);
268 perror("ioctl");
269 exit(EXIT_FAILURE);
270 }
271
272
273 if (verbose) {
274 printf("%s (%s)\n", m_info.name, m_info.id);
275 puts("Supported channels:");
276 }
277 #endif
278 for (count = 0; count < SOUND_MIXER_NRDEVICES; count++) {
279 mask = 1 << count;
280 if ((mask & devmask) && (!is_exclude((short_names[count]),exclude))) {
281 mixer[n_channels].name = channel_names[count];
282 mixer[n_channels].sname = short_names[count];
283 mixer[n_channels].dev = count;
284 mixer[n_channels].prev_dev_lr_volume = -1;
285 mixer[n_channels].can_record = (mask & recmask) != 0;
286 mixer[n_channels].is_recording = (mask & srcmask) != 0;
287 mixer[n_channels].is_stereo = (mask & stmask) != 0;
288 mixer[n_channels].is_muted = false;
289 ++n_channels;
290 if (verbose)
291 printf(" %d: %s \t(%s)\n", n_channels,
292 channel_names[count],
293 short_names[count]);
294 } else if ((mask & devmask) && verbose)
295 printf(" x: %s \t(%s) - disabled\n", channel_names[count],
296 short_names[count]);
297 }
298 get_mixer_state();
299 }
300
mixer_is_changed(void)301 bool mixer_is_changed(void)
302 {
303 return get_mixer_state();
304 }
305
mixer_get_channel_count(void)306 int mixer_get_channel_count(void)
307 {
308 return n_channels;
309 }
310
mixer_get_channel(void)311 int mixer_get_channel(void)
312 {
313 return cur_channel;
314 }
315
mixer_get_channel_name(void)316 const char *mixer_get_channel_name(void)
317 {
318 return mixer[cur_channel].name;
319 }
320
mixer_get_short_name(void)321 const char *mixer_get_short_name(void)
322 {
323 return mixer[cur_channel].sname;
324 }
325
mixer_set_channel(int channel)326 void mixer_set_channel(int channel)
327 {
328 assert((channel >= 0) && (channel < n_channels));
329
330 cur_channel = channel;
331 get_record_state();
332 }
333
mixer_set_channel_rel(int delta_channel)334 void mixer_set_channel_rel(int delta_channel)
335 {
336 cur_channel = (cur_channel + delta_channel) % n_channels;
337 if (cur_channel < 0)
338 cur_channel += n_channels;
339 get_record_state();
340 }
341
mixer_get_volume(void)342 float mixer_get_volume(void)
343 {
344 get_mixer_state();
345 return mixer[cur_channel].volume;
346 }
347
mixer_set_volume(float volume)348 void mixer_set_volume(float volume)
349 {
350 assert((volume >= 0.0) && (volume <= 1.0));
351
352 mixer[cur_channel].volume = volume;
353 set_mixer_state();
354 }
355
mixer_set_volume_rel(float delta_volume)356 void mixer_set_volume_rel(float delta_volume)
357 {
358 mixer[cur_channel].volume += delta_volume;
359 mixer[cur_channel].volume = CLAMP(mixer[cur_channel].volume, 0.0, 1.0);
360 set_mixer_state();
361 }
362
mixer_get_balance(void)363 float mixer_get_balance(void)
364 {
365 get_mixer_state();
366 return mixer[cur_channel].balance;
367 }
368
mixer_set_balance(float balance)369 void mixer_set_balance(float balance)
370 {
371 assert((balance >= -1.0) && (balance <= 1.0));
372
373 if (mixer[cur_channel].is_stereo) {
374 mixer[cur_channel].balance = balance;
375 set_mixer_state();
376 }
377 }
378
mixer_set_balance_rel(float delta_balance)379 void mixer_set_balance_rel(float delta_balance)
380 {
381 if (mixer[cur_channel].is_stereo) {
382 mixer[cur_channel].balance += delta_balance;
383 mixer[cur_channel].balance =
384 CLAMP(mixer[cur_channel].balance, -1.0, 1.0);
385 set_mixer_state();
386 }
387 }
388
mixer_toggle_mute(void)389 void mixer_toggle_mute(void)
390 {
391 mixer[cur_channel].is_muted = !mixer[cur_channel].is_muted;
392
393 set_mixer_state();
394 }
395
mixer_toggle_rec(void)396 void mixer_toggle_rec(void)
397 {
398 if (mixer[cur_channel].can_record) {
399 mixer[cur_channel].is_recording = !mixer[cur_channel].is_recording;
400 set_record_state();
401 get_record_state();
402 }
403 }
404
mixer_is_muted(void)405 bool mixer_is_muted(void)
406 {
407 return mixer[cur_channel].is_muted;
408 }
409
mixer_is_stereo(void)410 bool mixer_is_stereo(void)
411 {
412 return mixer[cur_channel].is_stereo;
413 }
414
mixer_is_rec(void)415 bool mixer_is_rec(void)
416 {
417 return mixer[cur_channel].is_recording;
418 }
419
mixer_can_rec(void)420 bool mixer_can_rec(void)
421 {
422 return mixer[cur_channel].can_record;
423 }
424
is_exclude(const char * short_name,const char * exclude[])425 bool is_exclude(const char *short_name, const char *exclude[])
426 {
427 int count = 0;
428 while (count < SOUND_MIXER_NRDEVICES && exclude[count] != NULL){
429 if ( strcmp(short_name, exclude[count]) == 0 )
430 return true;
431 count++;
432 }
433 return false;
434 }
435