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