1 /*
2  * mixer.c, mixer module
3  *
4  * Copyright (C) 1997 Rasca, Berlin
5  * EMail: thron@gmx.de
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  *
21  *
22  * example which sets the volume of all devices to zero:
23  *
24  * int id, no_of_devs, i;
25  *
26  * id = mixer_init ("/dev/mixer");
27  * if (id > 0) {
28  *     no_of_devs = mixer_num_of_devs (id);
29  *     for (i = 0; i < no_of_devs; i++) {
30  *         mixer_set_vol_left (id, i, 0);
31  *         if (mixer_is_stereo (id, i)) {
32  *             mixer_set_vol_right (id, i, 0);
33  *         }
34  *     }
35  *     mixer_fini (id);
36  * }
37  *
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <sys/ioctl.h>
45 #ifdef ALSA	/* not supported until now */
46 #	include <sys/asound.h>
47 #endif
48 #ifdef OSS
49 #	include <sys/soundcard.h>
50 #endif
51 #include "mixer.h"
52 
53 /* if you have more than two soundcards in your system, adjust the
54  * following accordingly..
55  */
56 #define MAX_MIXER	2
57 #define MIXER_VER	"V0.5"
58 
59 typedef struct {
60     int id;				/* 0 if not used */
61     int no_of_devs;		/* number of devices */
62 	int stereo_devs;
63 	int rec_mask;
64 	int rec_source;
65 	int caps;
66 	int exclusive_input;
67 	int device[SOUND_MIXER_NRDEVICES];	/* valid devices */
68 	int levels[SOUND_MIXER_NRDEVICES];
69     char *file;			/* file name of the device */
70 } MIXER;
71 
72 static MIXER mixer[MAX_MIXER];
73 static int must_init = 1;
74 
75 /*
76  * mixer_init()
77  * returns an id for the following functions. on the first call
78  * it is '1', so you don't remember if you just use one device.
79  * just call all other functions with '1' as the first argument.
80  */
81 int
mixer_init(char * mixer_dev)82 mixer_init (char *mixer_dev) {
83 	int i, id, md;
84 	int dev_mask = 0;
85 
86 	if (!mixer_dev) {
87 		return (0);
88 	}
89 	if (must_init) {
90 		/* just called the first time */
91 		must_init = 0;
92 		for (i = 0; i < MAX_MIXER; i++) {
93 			mixer[i].id =0;
94 		}
95 	}
96 	/* find a free structure */
97 	for (i = 0; i < MAX_MIXER; i++) {
98 		if (mixer[i].id == 0) {
99 			mixer[i].id = i+1;
100 			id = i;
101 			goto CONT;
102 		}
103 	}
104 	return (0);
105 	CONT:
106 
107 	mixer[id].file = (char *) malloc (strlen (mixer_dev)+1);
108 	strcpy (mixer[id].file, mixer_dev);
109 
110 	md = open (mixer[id].file, O_RDWR, 0);
111 	if (md == -1) {
112 		perror (mixer[id].file);
113 		return (0);
114 	}
115 	if (ioctl (md, SOUND_MIXER_READ_DEVMASK, &dev_mask) == -1) {
116 		perror("SOUND_MIXER_READ_DEVMASK");
117 		close(md);
118 		return (0);
119 	}
120 
121 	if (ioctl (md, SOUND_MIXER_READ_RECMASK, &mixer[id].rec_mask) == -1) {
122 		perror("SOUND_MIXER_READ_RECMASK");
123 		close(md);
124 		return (0);
125 	}
126 
127 	if (ioctl (md, SOUND_MIXER_READ_RECSRC, &mixer[id].rec_source) == -1) {
128 		perror("SOUND_MIXER_READ_RECSRC");
129 		close(md);
130 		return (0);
131 	}
132 
133 	if (ioctl (md, SOUND_MIXER_READ_STEREODEVS, &mixer[id].stereo_devs) == -1) {
134 		perror("SOUND_MIXER_READ_STEREODEVS");
135 		close(md);
136 		return (0);
137 	}
138 	if (ioctl (md, SOUND_MIXER_READ_CAPS, &mixer[id].caps) == -1) {
139 		perror("SOUND_MIXER_READ_CAPS");
140 		close(md);
141 		return (0);
142 	}
143 	mixer[id].exclusive_input = mixer[id].caps & SOUND_CAP_EXCL_INPUT;
144 
145 	mixer[id].no_of_devs =0;
146 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)  {
147 		if ((1 << i) & dev_mask) {
148 #		ifdef DEBUG2
149 			fprintf (stderr, "mixer_init(): %d\n", i);
150 #		endif
151 			mixer[id].device[mixer[id].no_of_devs] = i;
152 			mixer[id].no_of_devs++;
153 			ioctl (md, MIXER_READ(i), &mixer[id].levels[i]);
154 		}
155 	}
156 	close (md);
157 	return (id+1);
158 }
159 
160 /*
161  * mixer_fini()
162  * call this if the mixer is not needed any longer
163  */
164 int
mixer_fini(int mixer_id)165 mixer_fini (int mixer_id) {
166 	mixer[mixer_id-1].id = 0;	/* mark it as not used */
167 	free (mixer[mixer_id-1].file);
168 	return (1);
169 }
170 
171 
172 /*
173  */
174 const char
mixer_get_label(int mixer_id,int dev)175 *mixer_get_label (int mixer_id, int dev) {
176 	int id;
177 	char *labels[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_LABELS;
178 
179 	if (dev <= SOUND_MIXER_NRDEVICES) {
180 		id = mixer[mixer_id-1].device[dev];
181 		return (labels[id]);
182 	}
183 	return (NULL);
184 }
185 
186 
187 /*
188  */
189 const char
mixer_get_name(int mixer_id,int dev)190 *mixer_get_name (int mixer_id, int dev) {
191 	int id;
192 	char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
193 
194 	if (dev <= SOUND_MIXER_NRDEVICES) {
195 		id = mixer[mixer_id-1].device[dev];
196 		return (names[id]);
197 	}
198 	return (NULL);
199 }
200 
201 
202 /*
203  */
204 int
mixer_is_stereo(int mixer_id,int dev)205 mixer_is_stereo (int mixer_id, int dev) {
206 	int id;
207 	if (dev <= SOUND_MIXER_NRDEVICES) {
208 		id = mixer[mixer_id-1].device[dev];
209 		return (!!((1 << id) & mixer[mixer_id-1].stereo_devs));
210 	}
211 	return (0);
212 }
213 
214 /*
215  */
216 int
mixer_get_vol_left(int mixer_id,int dev)217 mixer_get_vol_left (int mixer_id, int dev) {
218 	int id, md;
219 	if (dev <= SOUND_MIXER_NRDEVICES) {
220 		id = mixer[mixer_id-1].device[dev];
221 		md = open (mixer[mixer_id-1].file, O_RDWR, 0);
222 		if (md < 0) {
223 			return (-1);
224 		}
225 		ioctl (md, MIXER_READ(id), &mixer[mixer_id-1].levels[id]);
226 		close (md);
227 		return (mixer[mixer_id-1].levels[id] & 0x7F);
228 	}
229 	return (-1);
230 }
231 
232 
233 /*
234  */
235 int
mixer_get_vol_right(int mixer_id,int dev)236 mixer_get_vol_right (int mixer_id, int dev) {
237 	int id;
238 	if (dev <= SOUND_MIXER_NRDEVICES) {
239 		id = mixer[mixer_id-1].device[dev];
240 		return ((mixer[mixer_id-1].levels[id] >> 8) & 0x7F);
241 	}
242 	return (-1);
243 }
244 
245 
246 /*
247  * set the mixer volume of the left channel, returns the
248  * new value or -1 on failure
249  */
250 int
mixer_set_vol_left(int mixer_id,int dev,int val)251 mixer_set_vol_left (int mixer_id, int dev, int val) {
252 	int id, fd, level;
253 	if (dev <= SOUND_MIXER_NRDEVICES) {
254 		id = mixer[mixer_id-1].device[dev];
255 		level = mixer[mixer_id-1].levels[id] =
256 			(mixer[mixer_id-1].levels[id] & 0x7F00) | val;
257 		fd = open (mixer[mixer_id-1].file, O_RDWR, 0);
258 			if (fd < 0)
259 				return (-1);
260 #ifdef DEBUG2
261 		fprintf (stderr, "mixer_set_vol_left() left=%d right=%d\n",
262 					level & 0xff, (level >> 8)& 0xff);
263 #endif
264 		ioctl (fd, MIXER_WRITE(id), &level);
265 		close (fd);
266 		return (level & 0x7F);
267 	}
268 	return (-1);
269 }
270 
271 
272 /*
273  */
mixer_set_vol_right(int mixer_id,int dev,int val)274 int mixer_set_vol_right (int mixer_id, int dev, int val) {
275 	int id, md, level;
276 	if (dev <= SOUND_MIXER_NRDEVICES) {
277 		id = mixer[mixer_id-1].device[dev];
278 		level = mixer[mixer_id-1].levels[id] =
279 			(mixer[mixer_id-1].levels[id] & 0x007F) | (val << 8);
280 		md = open (mixer[mixer_id-1].file, O_RDWR, 0);
281 		if (md == -1)
282 			return (0);
283 		ioctl (md, MIXER_WRITE(id), &level);
284 		close (md);
285 		return ((level >>8) & 0x7F);
286 	}
287 	return (-1);
288 }
289 
290 
291 /*
292  */
293 int
mixer_is_rec_dev(int mixer_id,int dev)294 mixer_is_rec_dev (int mixer_id, int dev) {
295 	int id;
296 	if (dev <= SOUND_MIXER_NRDEVICES) {
297 		id = mixer[mixer_id-1].device[dev];
298 		return ((1 << id) & mixer[mixer_id-1].rec_mask);
299 	}
300 	return (0);
301 }
302 
303 
304 /*
305  * check if recording source is active
306  */
307 int
mixer_is_rec_on(int mixer_id,int dev)308 mixer_is_rec_on (int mixer_id, int dev) {
309 	int id;
310 	if (dev <= SOUND_MIXER_NRDEVICES) {
311 		id = mixer[mixer_id-1].device[dev];
312 		return (((1 << id) & mixer[mixer_id-1].rec_source)>>id);
313 	}
314 	return (0);
315 }
316 
317 
318 /*
319  */
320 int
mixer_set_rec(int mixer_id,int dev,int boolval)321 mixer_set_rec (int mixer_id, int dev, int boolval) {
322 	int id, md, recsrc;
323 	if (dev <= SOUND_MIXER_NRDEVICES) {
324 		id = mixer[mixer_id-1].device[dev];
325 		if (boolval) {
326 			recsrc = mixer[mixer_id-1].rec_source | (boolval << id);
327 		} else {
328 			recsrc = mixer[mixer_id-1].rec_source & ~(1 << id);
329 		}
330 		md = open (mixer[mixer_id-1].file, O_RDWR, 0);
331 		if (md == -1)
332 			return (0);
333 		ioctl (md, SOUND_MIXER_WRITE_RECSRC, &recsrc);
334 		close (md);
335 		mixer[mixer_id-1].rec_source = recsrc;
336 #ifdef	DEBUG2
337 		fprintf (stderr, "mixer_set_rec(%d, %d): %d\n", dev, boolval, recsrc);
338 #endif
339 		return (1);
340 	}
341 	return (0);
342 }
343 
344 
345 /*
346  */
347 int
mixer_num_of_devs(int mixer_id)348 mixer_num_of_devs (int mixer_id) {
349 	if (mixer_id > MAX_MIXER)
350 		return (0);
351 	return (mixer[mixer_id-1].no_of_devs);
352 }
353 
354 /*
355  */
356 int
mixer_get_dev_by_name(int mixer_id,char * dev_name)357 mixer_get_dev_by_name (int mixer_id, char *dev_name) {
358 	int i;
359 	const char *name;
360 	char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
361 
362 	if (mixer_id > MAX_MIXER)
363 		return (-1);
364 	for (i = 0; i < mixer[mixer_id-1].no_of_devs; i++) {
365 		name = names[mixer[mixer_id-1].device[i]];
366 		if (strcmp (name, dev_name) == 0) {
367 #ifdef DEBUG2
368 			fprintf (stderr, "%s %d\n", name, i);
369 #endif
370 			return (i);
371 		}
372 	}
373 	return (-1);
374 }
375 
376 /*
377  * only one channel as recording source?
378  * return: 1  yes
379  *         0  no
380  *        -1  error
381  */
382 int
mixer_exclusive_input(int mixer_id)383 mixer_exclusive_input (int mixer_id)
384 {
385 	if (mixer_id > MAX_MIXER)
386 		return (-1);
387 	return (mixer[mixer_id-1].exclusive_input);
388 }
389 
390