1 /*
2 * A simple PCM loopback utility
3 * Copyright (c) 2010 by Jaroslav Kysela <perex@perex.cz>
4 *
5 * Author: Jaroslav Kysela <perex@perex.cz>
6 *
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 */
23
24 #include "aconfig.h"
25 #include <ctype.h>
26 #include <syslog.h>
27 #include <alsa/asoundlib.h>
28 #include <sys/time.h>
29 #include "alsaloop.h"
30 #include "os_compat.h"
31
id_str(snd_ctl_elem_id_t *id)32 static char *id_str(snd_ctl_elem_id_t *id)
33 {
34 static char str[128];
35
36 sprintf(str, "%i,%s,%i,%i,%s,%i",
37 snd_ctl_elem_id_get_numid(id),
38 snd_ctl_elem_iface_name(snd_ctl_elem_id_get_interface(id)),
39 snd_ctl_elem_id_get_device(id),
40 snd_ctl_elem_id_get_subdevice(id),
41 snd_ctl_elem_id_get_name(id),
42 snd_ctl_elem_id_get_index(id));
43 return str;
44 }
45
control_parse_id(const char *str, snd_ctl_elem_id_t *id)46 int control_parse_id(const char *str, snd_ctl_elem_id_t *id)
47 {
48 int c, size, numid;
49 char *ptr;
50
51 while (*str == ' ' || *str == '\t')
52 str++;
53 if (!(*str))
54 return -EINVAL;
55 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); /* default */
56 while (*str) {
57 if (!strncasecmp(str, "numid=", 6)) {
58 str += 6;
59 numid = atoi(str);
60 if (numid <= 0) {
61 logit(LOG_CRIT, "Invalid numid %d\n", numid);
62 return -EINVAL;
63 }
64 snd_ctl_elem_id_set_numid(id, atoi(str));
65 while (isdigit(*str))
66 str++;
67 } else if (!strncasecmp(str, "iface=", 6)) {
68 str += 6;
69 if (!strncasecmp(str, "card", 4)) {
70 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_CARD);
71 str += 4;
72 } else if (!strncasecmp(str, "mixer", 5)) {
73 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER);
74 str += 5;
75 } else if (!strncasecmp(str, "pcm", 3)) {
76 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
77 str += 3;
78 } else if (!strncasecmp(str, "rawmidi", 7)) {
79 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_RAWMIDI);
80 str += 7;
81 } else if (!strncasecmp(str, "timer", 5)) {
82 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_TIMER);
83 str += 5;
84 } else if (!strncasecmp(str, "sequencer", 9)) {
85 snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_SEQUENCER);
86 str += 9;
87 } else {
88 return -EINVAL;
89 }
90 } else if (!strncasecmp(str, "name=", 5)) {
91 char buf[64];
92 str += 5;
93 ptr = buf;
94 size = 0;
95 if (*str == '\'' || *str == '\"') {
96 c = *str++;
97 while (*str && *str != c) {
98 if (size < (int)sizeof(buf)) {
99 *ptr++ = *str;
100 size++;
101 }
102 str++;
103 }
104 if (*str == c)
105 str++;
106 } else {
107 while (*str && *str != ',') {
108 if (size < (int)sizeof(buf)) {
109 *ptr++ = *str;
110 size++;
111 }
112 str++;
113 }
114 }
115 *ptr = '\0';
116 snd_ctl_elem_id_set_name(id, buf);
117 } else if (!strncasecmp(str, "index=", 6)) {
118 str += 6;
119 snd_ctl_elem_id_set_index(id, atoi(str));
120 while (isdigit(*str))
121 str++;
122 } else if (!strncasecmp(str, "device=", 7)) {
123 str += 7;
124 snd_ctl_elem_id_set_device(id, atoi(str));
125 while (isdigit(*str))
126 str++;
127 } else if (!strncasecmp(str, "subdevice=", 10)) {
128 str += 10;
129 snd_ctl_elem_id_set_subdevice(id, atoi(str));
130 while (isdigit(*str))
131 str++;
132 }
133 if (*str == ',') {
134 str++;
135 } else {
136 if (*str)
137 return -EINVAL;
138 }
139 }
140 return 0;
141 }
142
control_id_match(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2)143 int control_id_match(snd_ctl_elem_id_t *id1, snd_ctl_elem_id_t *id2)
144 {
145 if (snd_ctl_elem_id_get_interface(id1) !=
146 snd_ctl_elem_id_get_interface(id2))
147 return 0;
148 if (snd_ctl_elem_id_get_device(id1) !=
149 snd_ctl_elem_id_get_device(id2))
150 return 0;
151 if (snd_ctl_elem_id_get_subdevice(id1) !=
152 snd_ctl_elem_id_get_subdevice(id2))
153 return 0;
154 if (strcmp(snd_ctl_elem_id_get_name(id1),
155 snd_ctl_elem_id_get_name(id2)) != 0)
156 return 0;
157 if (snd_ctl_elem_id_get_index(id1) !=
158 snd_ctl_elem_id_get_index(id2))
159 return 0;
160 return 1;
161 }
162
control_init1(struct loopback_handle *lhandle, struct loopback_control *ctl)163 static int control_init1(struct loopback_handle *lhandle,
164 struct loopback_control *ctl)
165 {
166 int err;
167
168 snd_ctl_elem_info_set_id(ctl->info, ctl->id);
169 snd_ctl_elem_value_set_id(ctl->value, ctl->id);
170 if (lhandle->ctl == NULL) {
171 logit(LOG_WARNING, "Unable to read control info for '%s'\n", id_str(ctl->id));
172 return -EIO;
173 }
174 err = snd_ctl_elem_info(lhandle->ctl, ctl->info);
175 if (err < 0) {
176 logit(LOG_WARNING, "Unable to read control info '%s': %s\n", id_str(ctl->id), snd_strerror(err));
177 return err;
178 }
179 err = snd_ctl_elem_read(lhandle->ctl, ctl->value);
180 if (err < 0) {
181 logit(LOG_WARNING, "Unable to read control value (init1) '%s': %s\n", id_str(ctl->id), snd_strerror(err));
182 return err;
183 }
184 return 0;
185 }
186
copy_value(struct loopback_control *dst, struct loopback_control *src)187 static int copy_value(struct loopback_control *dst,
188 struct loopback_control *src)
189 {
190 snd_ctl_elem_type_t type;
191 unsigned int i, count;
192
193 type = snd_ctl_elem_info_get_type(dst->info);
194 count = snd_ctl_elem_info_get_count(dst->info);
195 switch (type) {
196 case SND_CTL_ELEM_TYPE_BOOLEAN:
197 for (i = 0; i < count; i++)
198 snd_ctl_elem_value_set_boolean(dst->value,
199 i, snd_ctl_elem_value_get_boolean(src->value, i));
200 break;
201 case SND_CTL_ELEM_TYPE_INTEGER:
202 for (i = 0; i < count; i++) {
203 snd_ctl_elem_value_set_integer(dst->value,
204 i, snd_ctl_elem_value_get_integer(src->value, i));
205 }
206 break;
207 default:
208 logit(LOG_CRIT, "Unable to copy control value for type %s\n", snd_ctl_elem_type_name(type));
209 return -EINVAL;
210 }
211 return 0;
212 }
213
oss_set(struct loopback *loop, struct loopback_ossmixer *ossmix, int enable)214 static int oss_set(struct loopback *loop,
215 struct loopback_ossmixer *ossmix,
216 int enable)
217 {
218 char buf[128], file[128];
219 int fd;
220
221 if (loop->capt->card_number < 0)
222 return 0;
223 if (!enable) {
224 sprintf(buf, "%s \"\" 0\n", ossmix->oss_id);
225 } else {
226 sprintf(buf, "%s \"%s\" %i\n", ossmix->oss_id, ossmix->alsa_id, ossmix->alsa_index);
227 }
228 sprintf(file, "/proc/asound/card%i/oss_mixer", loop->capt->card_number);
229 if (verbose)
230 snd_output_printf(loop->output, "%s: Initialize OSS volume %s: %s", loop->id, file, buf);
231 fd = open(file, O_WRONLY);
232 if (fd >= 0 && write(fd, buf, strlen(buf)) == (ssize_t)strlen(buf)) {
233 close(fd);
234 return 0;
235 }
236 if (fd >= 0)
237 close(fd);
238 logit(LOG_INFO, "%s: Unable to initialize OSS Mixer ID '%s'\n", loop->id, ossmix->oss_id);
239 return -1;
240 }
241
control_init2(struct loopback *loop, struct loopback_mixer *mix)242 static int control_init2(struct loopback *loop,
243 struct loopback_mixer *mix)
244 {
245 snd_ctl_elem_type_t type;
246 unsigned int count;
247 int err;
248
249 snd_ctl_elem_info_copy(mix->dst.info, mix->src.info);
250 snd_ctl_elem_info_set_id(mix->dst.info, mix->dst.id);
251 snd_ctl_elem_value_clear(mix->dst.value);
252 snd_ctl_elem_value_set_id(mix->dst.value, mix->dst.id);
253 type = snd_ctl_elem_info_get_type(mix->dst.info);
254 count = snd_ctl_elem_info_get_count(mix->dst.info);
255 snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id);
256 switch (type) {
257 case SND_CTL_ELEM_TYPE_BOOLEAN:
258 err = snd_ctl_elem_add_boolean(loop->capt->ctl,
259 mix->dst.id, count);
260 copy_value(&mix->dst, &mix->src);
261 break;
262 case SND_CTL_ELEM_TYPE_INTEGER:
263 err = snd_ctl_elem_add_integer(loop->capt->ctl,
264 mix->dst.id, count,
265 snd_ctl_elem_info_get_min(mix->dst.info),
266 snd_ctl_elem_info_get_max(mix->dst.info),
267 snd_ctl_elem_info_get_step(mix->dst.info));
268 copy_value(&mix->dst, &mix->src);
269 break;
270 default:
271 logit(LOG_CRIT, "Unable to handle control type %s\n", snd_ctl_elem_type_name(type));
272 err = -EINVAL;
273 break;
274 }
275 if (err < 0) {
276 logit(LOG_CRIT, "Unable to create control '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
277 return err;
278 }
279 err = snd_ctl_elem_unlock(loop->capt->ctl, mix->dst.id);
280 if (err < 0) {
281 logit(LOG_CRIT, "Unable to unlock control info '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
282 return err;
283 }
284 err = snd_ctl_elem_info(loop->capt->ctl, mix->dst.info);
285 if (err < 0) {
286 logit(LOG_CRIT, "Unable to read control info '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
287 return err;
288 }
289 if (snd_ctl_elem_info_is_tlv_writable(mix->dst.info)) {
290 unsigned int tlv[64];
291 err = snd_ctl_elem_tlv_read(loop->play->ctl,
292 mix->src.id,
293 tlv, sizeof(tlv));
294 if (err < 0) {
295 logit(LOG_CRIT, "Unable to read TLV for '%s': %s\n", id_str(mix->src.id), snd_strerror(err));
296 tlv[0] = tlv[1] = 0;
297 }
298 err = snd_ctl_elem_tlv_write(loop->capt->ctl,
299 mix->dst.id,
300 tlv);
301 if (err < 0) {
302 logit(LOG_CRIT, "Unable to write TLV for '%s': %s\n", id_str(mix->src.id), snd_strerror(err));
303 return err;
304 }
305 }
306 err = snd_ctl_elem_write(loop->capt->ctl, mix->dst.value);
307 if (err < 0) {
308 logit(LOG_CRIT, "Unable to write control value '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
309 return err;
310 }
311 return 0;
312 }
313
control_init(struct loopback *loop)314 int control_init(struct loopback *loop)
315 {
316 struct loopback_mixer *mix;
317 struct loopback_ossmixer *ossmix;
318 int err;
319
320 for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next)
321 oss_set(loop, ossmix, 0);
322 for (mix = loop->controls; mix; mix = mix->next) {
323 err = control_init1(loop->play, &mix->src);
324 if (err < 0) {
325 logit(LOG_WARNING, "%s: Disabling playback control '%s'\n", loop->id, id_str(mix->src.id));
326 mix->skip = 1;
327 continue;
328 }
329 err = control_init2(loop, mix);
330 if (err < 0)
331 return err;
332 }
333 for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) {
334 err = oss_set(loop, ossmix, 1);
335 if (err < 0) {
336 ossmix->skip = 1;
337 logit(LOG_WARNING, "%s: Disabling OSS mixer ID '%s'\n", loop->id, ossmix->oss_id);
338 }
339 }
340 return 0;
341 }
342
control_done(struct loopback *loop)343 int control_done(struct loopback *loop)
344 {
345 struct loopback_mixer *mix;
346 struct loopback_ossmixer *ossmix;
347 int err;
348
349 if (loop->capt->ctl == NULL)
350 return 0;
351 for (ossmix = loop->oss_controls; ossmix; ossmix = ossmix->next) {
352 err = oss_set(loop, ossmix, 0);
353 if (err < 0)
354 logit(LOG_WARNING, "%s: Unable to remove OSS control '%s'\n", loop->id, ossmix->oss_id);
355 }
356 for (mix = loop->controls; mix; mix = mix->next) {
357 if (mix->skip)
358 continue;
359 err = snd_ctl_elem_remove(loop->capt->ctl, mix->dst.id);
360 if (err < 0)
361 logit(LOG_WARNING, "%s: Unable to remove control '%s': %s\n", loop->id, id_str(mix->dst.id), snd_strerror(err));
362 }
363 return 0;
364 }
365
control_event1(struct loopback *loop, struct loopback_mixer *mix, snd_ctl_event_t *ev, int capture)366 static int control_event1(struct loopback *loop,
367 struct loopback_mixer *mix,
368 snd_ctl_event_t *ev,
369 int capture)
370 {
371 unsigned int mask = snd_ctl_event_elem_get_mask(ev);
372 int err;
373
374 if (mask == SND_CTL_EVENT_MASK_REMOVE)
375 return 0;
376 if ((mask & SND_CTL_EVENT_MASK_VALUE) == 0)
377 return 0;
378 if (!capture) {
379 snd_ctl_elem_value_set_id(mix->src.value, mix->src.id);
380 err = snd_ctl_elem_read(loop->play->ctl, mix->src.value);
381 if (err < 0) {
382 logit(LOG_CRIT, "Unable to read control value (event1) '%s': %s\n", id_str(mix->src.id), snd_strerror(err));
383 return err;
384 }
385 copy_value(&mix->dst, &mix->src);
386 err = snd_ctl_elem_write(loop->capt->ctl, mix->dst.value);
387 if (err < 0) {
388 logit(LOG_CRIT, "Unable to write control value (event1) '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
389 return err;
390 }
391 } else {
392 err = snd_ctl_elem_read(loop->capt->ctl, mix->dst.value);
393 if (err < 0) {
394 logit(LOG_CRIT, "Unable to read control value (event2) '%s': %s\n", id_str(mix->dst.id), snd_strerror(err));
395 return err;
396 }
397 copy_value(&mix->src, &mix->dst);
398 err = snd_ctl_elem_write(loop->play->ctl, mix->src.value);
399 if (err < 0) {
400 logit(LOG_CRIT, "Unable to write control value (event2) '%s': %s\n", id_str(mix->src.id), snd_strerror(err));
401 return err;
402 }
403 }
404 return 0;
405 }
406
control_event(struct loopback_handle *lhandle, snd_ctl_event_t *ev)407 int control_event(struct loopback_handle *lhandle, snd_ctl_event_t *ev)
408 {
409 snd_ctl_elem_id_t *id2;
410 struct loopback_mixer *mix;
411 int capt = lhandle == lhandle->loopback->capt;
412 int err;
413
414 snd_ctl_elem_id_alloca(&id2);
415 snd_ctl_event_elem_get_id(ev, id2);
416 for (mix = lhandle->loopback->controls; mix; mix = mix->next) {
417 if (mix->skip)
418 continue;
419 if (control_id_match(id2, capt ? mix->dst.id : mix->src.id)) {
420 err = control_event1(lhandle->loopback, mix, ev, capt);
421 if (err < 0)
422 return err;
423 }
424 }
425 return 0;
426 }
427