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