1 
2 /*
3  *  Diverse SLab audio routines.
4  *  Copyright (c) by Nick Copeland <nickycopeland@hotmail.com> 1996,2012
5  *
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 3 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, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 
23 /*
24  * These are largely device specific operations for the audio devices. Some of
25  * the calls are general, but may eventually be used by several of the SLab
26  * applications.
27  *
28  * This file contains the ALSA specific calls.
29  */
30 
31 #ifdef DEBUG
32 #include <stdio.h>
33 #endif
34 #include <unistd.h>
35 #include <fcntl.h>
36 #ifdef SUBFRAGMENT
37 #include <malloc.h>
38 #endif
39 #include <stdlib.h>
40 
41 /*
42  * Audio device structure format definitions.
43  */
44 #include "slabaudiodev.h"
45 
46 #if (BRISTOL_HAS_ALSA  == 1)
47 #include "slabalsadev.h"
48 
49 static struct adev alsaDev[MAX_DEVICES + 1];
50 
51 int
checkAudioALSAcaps(audioDev,devID,fd)52 checkAudioALSAcaps(audioDev, devID, fd)
53 duplexDev *audioDev;
54 int devID, fd;
55 {
56 	return(0);
57 }
58 
59 const char *
getAlsaName(duplexDev * audioDev,int cont)60 getAlsaName(duplexDev *audioDev, int cont)
61 {
62 	snd_mixer_selem_id_t *sid;
63 
64 	sid = (snd_mixer_selem_id_t *)(((char *) alsaDev[audioDev->devID].mixer_sid)
65 		+ snd_mixer_selem_id_sizeof() * cont);
66 
67 if (audioDev->cflags & SLAB_AUDIODBG)
68 printf("getAlsaName(%i): \"%s\"\n", cont, snd_mixer_selem_id_get_name(sid));
69 	return(snd_mixer_selem_id_get_name(sid));
70 }
71 
72 int
getAlsaStereoStatus(duplexDev * audioDev,int cont)73 getAlsaStereoStatus(duplexDev *audioDev, int cont)
74 {
75 	/*
76 	 * Cannot find a stereo status switch, so for now assume that only
77 	 * Master Mono and Mic are mono.
78 	 */
79 	if (strcmp(getAlsaName(audioDev, cont), "Master Mono") == 0)
80 		return(1);
81 	if (strcmp(getAlsaName(audioDev, cont), "Mic") == 0)
82 		return(1);
83 	if (strcmp(getAlsaName(audioDev, cont), "Center") == 0)
84 		return(1);
85 	if (strcmp(getAlsaName(audioDev, cont), "LFE") == 0)
86 		return(1);
87 	if (strcmp(getAlsaName(audioDev, cont), "Wave Center") == 0)
88 		return(1);
89 	if (strcmp(getAlsaName(audioDev, cont), "Wave LFE") == 0)
90 		return(1);
91 	if (strcmp(getAlsaName(audioDev, cont), "Phone") == 0)
92 		return(1);
93 	if (strcmp(getAlsaName(audioDev, cont), "PC Speaker") == 0)
94 		return(1);
95 	if (strcmp(getAlsaName(audioDev, cont), "Headphone LFE") == 0)
96 		return(1);
97 	if (strcmp(getAlsaName(audioDev, cont), "Headphone Center") == 0)
98 		return(1);
99 	if (strcmp(getAlsaName(audioDev, cont), "3D Control - Switch") == 0)
100 		return(1);
101 	if (strcmp(getAlsaName(audioDev, cont), "Mic Boost (+20dB)") == 0)
102 		return(1);
103 	if (strcmp(getAlsaName(audioDev, cont), "External Amplifier Power Down")
104 		== 0)
105 		return(1);
106 	if (strcmp(getAlsaName(audioDev, cont), "3D Control Sigmatel - Depth") == 0)
107 		return(1);
108 
109 	return(2);
110 }
111 
112 int
setAlsaValue(duplexDev * audioDev,int cont,int side,int value)113 setAlsaValue(duplexDev *audioDev, int cont, int side, int value)
114 {
115 	snd_mixer_elem_t *elem;
116 	snd_mixer_selem_id_t *sid;
117 	long vmin, vmax, vol;
118 
119 	if ((--side == 1) && (getAlsaStereoStatus(audioDev, cont) < 2))
120 		return(0);
121 
122 	sid = (snd_mixer_selem_id_t *)(((char *) alsaDev[audioDev->devID].mixer_sid)
123 		+ snd_mixer_selem_id_sizeof() * cont);
124 
125     elem = snd_mixer_find_selem(alsaDev[audioDev->devID].mh, sid);
126 
127 if (audioDev->cflags & SLAB_AUDIODBG)
128 printf("setAlsaValue(%i, %i, %i)\n", cont, side, value);
129 
130 	if (snd_mixer_selem_has_playback_volume(elem))
131 	{
132 if (audioDev->cflags & SLAB_AUDIODBG)
133 printf("HAS PLAYBACK FOUND\n");
134     	snd_mixer_selem_get_playback_volume_range(elem, &vmin, &vmax);
135 	} else {
136 if (audioDev->cflags & SLAB_AUDIODBG)
137 printf("HAS CAPTURE FOUND\n");
138     	snd_mixer_selem_get_capture_volume_range(elem, &vmin, &vmax);
139 	}
140 
141 	vol = value * (vmax - vmin) / 100;
142 
143 	/*
144 	 * If you get violations in this code, with assert failures in the sound
145 	 * library then it is likely this controller is either mono, or does not
146 	 * support continuous control (is a switch). I should work on the correct
147 	 * way to determine the capabilities of a device
148 	 */
149 	if (snd_mixer_selem_has_playback_volume(elem))
150 	{
151 if (audioDev->cflags & SLAB_AUDIODBG)
152 printf("PLAYBACK VOLUME\n");
153 
154     	if (snd_mixer_selem_set_playback_volume(elem, side, vol) < -1)
155 			printf("failed to set value\n");
156 	} else if (snd_mixer_selem_has_capture_volume(elem)) {
157 if (audioDev->cflags & SLAB_AUDIODBG)
158 printf("CAPTURE VOLUME\n");
159 	   	if (snd_mixer_selem_set_capture_volume(elem, side, vol) < -1)
160 			printf("failed to set value\n");
161 	}
162 
163 	return(0);
164 }
165 /*
166  * This is for ALSA 0.9, there were such massive alterations to the audio
167  * interface definitions that a general rewrite was required.
168  * This is always painful stuff, hopefully after this release things will
169  * be a bit more stable.
170  */
171 
172 int
validAlsaDev(duplexDev * audioDev,int cont)173 validAlsaDev(duplexDev *audioDev, int cont)
174 {
175 	if (cont >= alsaDev[audioDev->devID].elem_count)
176 		return(0);
177 	return(1);
178 }
179 
180 int
getAlsaRecordability(duplexDev * audioDev,int cont)181 getAlsaRecordability(duplexDev *audioDev, int cont)
182 {
183 	snd_mixer_elem_t *elem;
184 	snd_mixer_selem_id_t *sid;
185 
186 if (audioDev->cflags & SLAB_AUDIODBG)
187 printf("getRecordability\n");
188 	sid = (snd_mixer_selem_id_t *)(((char *) alsaDev[audioDev->devID].mixer_sid)
189 		+ snd_mixer_selem_id_sizeof() * cont);
190 
191     elem = snd_mixer_find_selem(alsaDev[audioDev->devID].mh, sid);
192 
193 	if (snd_mixer_selem_has_capture_switch(elem))
194 		return(0);
195 
196 	return(-2);
197 }
198 
199 int
getAlsaMutability(duplexDev * audioDev,int cont)200 getAlsaMutability(duplexDev *audioDev, int cont)
201 {
202 	snd_mixer_elem_t *elem;
203 	snd_mixer_selem_id_t *sid;
204 
205 if (audioDev->cflags & SLAB_AUDIODBG)
206 printf("getMutability\n");
207 	sid = (snd_mixer_selem_id_t *)(((char *) alsaDev[audioDev->devID].mixer_sid)
208 		+ snd_mixer_selem_id_sizeof() * cont);
209 
210     elem = snd_mixer_find_selem(alsaDev[audioDev->devID].mh, sid);
211 
212 	/*
213 	 * If we have playback capability then we also have mutability.
214 	 */
215 	if (snd_mixer_selem_has_playback_switch(elem))
216 		return(1);
217 
218 	return(0);
219 }
220 
221 int
getAlsaCapability(duplexDev * audioDev,int cont)222 getAlsaCapability(duplexDev *audioDev, int cont)
223 {
224 if (audioDev->cflags & SLAB_AUDIODBG)
225 printf("getAlsaCapability(%i)\n", cont);
226 
227 	if (cont >= alsaDev[audioDev->devID].elem_count)
228 		return(-1);
229 	return(cont);
230 }
231 
232 int
getAlsaCapByName(duplexDev * audioDev,char * name)233 getAlsaCapByName(duplexDev *audioDev, char *name)
234 {
235 	snd_mixer_selem_id_t *sid;
236 	int cont;
237 
238 	if (name[strlen(name) - 1] == ' ')
239 		name[strlen(name) - 1] = '\0';
240 
241 if (audioDev->cflags & SLAB_AUDIODBG)
242 printf("getAlsaCapByName(%s)\n", name);
243 	for (cont = 0; cont < alsaDev[audioDev->devID].elem_count; cont++)
244 	{
245 		sid = (snd_mixer_selem_id_t *)(((char *)
246 			alsaDev[audioDev->devID].mixer_sid)
247 			+ snd_mixer_selem_id_sizeof() * cont);
248 
249 		if (strcmp(snd_mixer_selem_id_get_name(sid), name) == 0)
250 			return(cont);
251 	}
252 	return(-1);
253 }
254 
255 int
getAlsaValue(duplexDev * audioDev,int cont,int side)256 getAlsaValue(duplexDev *audioDev, int cont, int side)
257 {
258 	snd_mixer_elem_t *elem;
259 	snd_mixer_selem_id_t *sid;
260 	long vmin, vmax, vol;
261 
262 	sid = (snd_mixer_selem_id_t *)(((char *) alsaDev[audioDev->devID].mixer_sid)
263 		+ snd_mixer_selem_id_sizeof() * cont);
264 
265     elem = snd_mixer_find_selem(alsaDev[audioDev->devID].mh, sid);
266 
267 if (audioDev->cflags & SLAB_AUDIODBG)
268 printf("getAlsaValue\n");
269 
270 	if (snd_mixer_selem_has_playback_volume(elem))
271 	{
272     	snd_mixer_selem_get_playback_volume_range(elem, &vmin, &vmax);
273 	    snd_mixer_selem_get_playback_volume(elem, side, &vol);
274 	} else {
275     	snd_mixer_selem_get_capture_volume_range(elem, &vmin, &vmax);
276 	    snd_mixer_selem_get_capture_volume(elem, side, &vol);
277 	}
278 
279 	return(vol * 100 / (vmax - vmin));
280 }
281 
282 int
setAudioALSAparam(duplexDev * audioDev,int devID,char * param,short left,short right)283 setAudioALSAparam(duplexDev *audioDev, int devID, char *param,
284 	short left, short right)
285 {
286 	int cont;
287 
288 if (audioDev->cflags & SLAB_AUDIODBG)
289 printf("setAudioALSAparam(%i)\n", devID);
290 
291 	if ((cont = getAlsaCapability(audioDev, devID)) == -1)
292 	{
293 if (audioDev->cflags & SLAB_AUDIODBG)
294 printf("could not find capability \"%s\"\n", param);
295 		return(0);
296 	}
297 
298 	setAlsaValue(audioDev, cont, 1, left);
299 
300 	if (getAlsaStereoStatus(audioDev, cont) > 1)
301 		setAlsaValue(audioDev, cont, 2, right);
302 
303 	return(0);
304 }
305 
306 int
setAlsaRecordSource(duplexDev * audioDev,int cont,int position)307 setAlsaRecordSource(duplexDev *audioDev, int cont, int position)
308 {
309 	snd_mixer_elem_t *elem;
310 	snd_mixer_selem_id_t *sid;
311 
312 if (audioDev->cflags & SLAB_AUDIODBG)
313 printf("setAlsaRecordSource\n");
314 	sid = (snd_mixer_selem_id_t *)(((char *) alsaDev[audioDev->devID].mixer_sid)
315 		+ snd_mixer_selem_id_sizeof() * cont);
316 
317     elem = snd_mixer_find_selem(alsaDev[audioDev->devID].mh, sid);
318 
319 	if (snd_mixer_selem_has_capture_switch(elem))
320 	{
321 		snd_mixer_selem_set_capture_switch(elem, 0, position);
322 		snd_mixer_selem_set_capture_switch(elem, 1, position);
323 	}
324 
325 	return(0);
326 }
327 
328 const char *
getAlsaDeviceName(duplexDev * audioDev)329 getAlsaDeviceName(duplexDev *audioDev)
330 {
331 if (audioDev->cflags & SLAB_AUDIODBG)
332 printf("setDeviceName(%s)\n", alsaDev[audioDev->devID].name);
333 	return(alsaDev[audioDev->devID].name);
334 }
335 
336 int
closeALSAmixer(audioDev)337 closeALSAmixer(audioDev)
338 duplexDev *audioDev;
339 {
340 	if (audioDev->cflags & SLAB_AUDIODBG)
341 		printf("closeALSAmixer(): %p\n", alsaDev[audioDev->devID].mh);
342 
343 	if (alsaDev[audioDev->devID].mh != (snd_mixer_t *) NULL)
344 	{
345 		int err;
346 
347 		if (audioDev->cflags & SLAB_AUDIODBG)
348 			printf("real closeALSAmixer(): %p\n", alsaDev[audioDev->devID].mh);
349 
350 		if ((err = snd_mixer_close((void *) alsaDev[audioDev->devID].mh)) < 0)
351 		{
352 			if (audioDev->cflags & SLAB_AUDIODBG)
353 				printf("SND Mixer Close error: %s\n", snd_strerror(err));
354 		}
355 		if ((err = snd_ctl_close(alsaDev[audioDev->devID].ch)) < 0) {
356 			if (audioDev->cflags & SLAB_AUDIODBG)
357 				printf("SND CTL Close error: %s\n", snd_strerror(err));
358 		}
359 	}
360 
361 	alsaDev[audioDev->devID].mh = NULL;
362 	alsaDev[audioDev->devID].ch = NULL;
363 
364 	return(0);
365 }
366 
367 int
setAlsaMute(duplexDev * audioDev,int cont,int onoff)368 setAlsaMute(duplexDev *audioDev, int cont, int onoff)
369 {
370 	snd_mixer_elem_t *elem;
371 	snd_mixer_selem_id_t *sid;
372 	int join;
373 
374 if (audioDev->cflags & SLAB_AUDIODBG)
375 printf("setAlsaMute(%i, %i)\n", cont, onoff);
376 	sid = (snd_mixer_selem_id_t *)(((char *) alsaDev[audioDev->devID].mixer_sid)
377 		+ snd_mixer_selem_id_sizeof() * cont);
378 
379     elem = snd_mixer_find_selem(alsaDev[audioDev->devID].mh, sid);
380 
381 join = snd_mixer_selem_has_playback_volume_joined(elem);
382 if (audioDev->cflags & SLAB_AUDIODBG)
383 printf("joined on device %i is %i\n", cont, join);
384 
385 	if (snd_mixer_selem_has_playback_switch(elem))
386 	{
387 		snd_mixer_selem_set_playback_switch(elem, 0, 1 - onoff);
388 		if (getAlsaStereoStatus(audioDev, cont) > 1)
389 			snd_mixer_selem_set_playback_switch(elem, 1, 1 - onoff);
390 	}
391 
392 	return(1);
393 }
394 
395 int
openALSAmixer(duplexDev * audioDev)396 openALSAmixer(duplexDev *audioDev)
397 {
398 	int err, selem_count, elem_index = 0, mixer_n_selems = 0;
399 	snd_mixer_selem_id_t *sid;
400 
401 	snd_ctl_card_info_alloca(&alsaDev[audioDev->devID].hwInfo);
402 
403 	if (alsaDev[audioDev->devID].ch != NULL)
404 		return(0);
405 
406 	if ((err = snd_ctl_open(&alsaDev[audioDev->devID].ch,
407 		&audioDev->mixerName[0], 0)) < 0)
408 	{
409 		printf("Could not open control interface\n");
410 		return(-1);
411 	}
412 	if ((err = snd_ctl_card_info(alsaDev[audioDev->devID].ch,
413 		alsaDev[audioDev->devID].hwInfo)) < 0)
414 	{
415 		printf("Could not get hardware info\n");
416 		return(-1);
417 	}
418 
419 	alsaDev[audioDev->devID].name =
420 		strdup(snd_ctl_card_info_get_name(alsaDev[audioDev->devID].hwInfo));
421 
422 	if (audioDev->cflags & SLAB_AUDIODBG)
423 	{
424 		printf("Found: %s\n", alsaDev[audioDev->devID].name);
425 		printf("Hardware: %s\n",
426 			snd_ctl_card_info_get_mixername(alsaDev[audioDev->devID].hwInfo));
427 	}
428 
429 	if ((err = snd_mixer_open(&alsaDev[audioDev->devID].mh, 0)) < 0)
430 	{
431 		printf("Could not get mixer\n");
432 		return(-1);
433 	}
434 
435 	if ((err = snd_mixer_attach(alsaDev[audioDev->devID].mh,
436 		&audioDev->mixerName[0])) < 0)
437 	{
438 		printf("Could not attach to mixer %s\n", audioDev->mixerName);
439 		return(-1);
440 	}
441 	if ((err = snd_mixer_selem_register(alsaDev[audioDev->devID].mh,
442 		NULL, NULL)) < 0)
443 	{
444 		printf("Could not get mixer\n");
445 		return(-1);
446 	}
447 /*
448 	snd_mixer_set_callback(alsaDev[audioDev->devID].mh, mixer_event);
449 */
450 	if ((err = snd_mixer_load (alsaDev[audioDev->devID].mh)) < 0)
451 	{
452 		printf("Could not get mixer\n");
453 		return(-1);
454 	}
455 	selem_count = snd_mixer_get_count(alsaDev[audioDev->devID].mh);
456 
457 	alsaDev[audioDev->devID].mixer_sid
458 		= malloc(snd_mixer_selem_id_sizeof() * selem_count);
459 
460 	for (alsaDev[audioDev->devID].eInfo[mixer_n_selems]
461 			= snd_mixer_first_elem(alsaDev[audioDev->devID].mh);
462 		alsaDev[audioDev->devID].eInfo[mixer_n_selems];
463 		alsaDev[audioDev->devID].eInfo[mixer_n_selems]
464 			= snd_mixer_elem_next(
465 				alsaDev[audioDev->devID].eInfo[mixer_n_selems - 1]))
466 	{
467 		sid = (snd_mixer_selem_id_t *)(((char *)
468 			alsaDev[audioDev->devID].mixer_sid)
469 			+ snd_mixer_selem_id_sizeof() * mixer_n_selems);
470 		snd_mixer_selem_get_id(alsaDev[audioDev->devID].eInfo[mixer_n_selems],
471 			sid);
472 		if (!snd_mixer_selem_is_active(alsaDev[audioDev->devID].eInfo[
473 			mixer_n_selems]))
474 			break;
475 		mixer_n_selems++;
476 	}
477 
478 	if (audioDev->cflags & SLAB_AUDIODBG)
479 		printf("found %i elements\n", mixer_n_selems);
480 
481 	alsaDev[audioDev->devID].elem_count = mixer_n_selems;
482 
483     for (elem_index = 0; elem_index < mixer_n_selems; elem_index++)
484 	{
485 		sid = (snd_mixer_selem_id_t *)(((char *)
486 			alsaDev[audioDev->devID].mixer_sid)
487 			+ snd_mixer_selem_id_sizeof() * elem_index);
488 		if (audioDev->cflags & SLAB_AUDIODBG)
489 			printf("	%s\n", snd_mixer_selem_id_get_name(sid));
490 	}
491 
492 	return(0);
493 }
494 #endif /* ALSA VERSION */
495 
496