1 /*
2 * A simple PCM loopback utility with adaptive sample rate support
3 *
4 * Author: Jaroslav Kysela <perex@perex.cz>
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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 *
21 */
22
23 #include "aconfig.h"
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sched.h>
28 #include <errno.h>
29 #include <getopt.h>
30 #include <alsa/asoundlib.h>
31 #include <sys/time.h>
32 #include <math.h>
33 #include <pthread.h>
34 #include <syslog.h>
35 #include <signal.h>
36 #include "alsaloop.h"
37 #include "os_compat.h"
38
39 struct loopback_thread {
40 int threaded;
41 pthread_t thread;
42 int exitcode;
43 struct loopback **loopbacks;
44 int loopbacks_count;
45 snd_output_t *output;
46 };
47
48 int quit = 0;
49 int verbose = 0;
50 int workarounds = 0;
51 int daemonize = 0;
52 int use_syslog = 0;
53 struct loopback **loopbacks = NULL;
54 int loopbacks_count = 0;
55 char **my_argv = NULL;
56 int my_argc = 0;
57 struct loopback_thread *threads;
58 int threads_count = 0;
59 pthread_t main_job;
60 int arg_default_xrun = 0;
61 int arg_default_wake = 0;
62
my_exit(struct loopback_thread * thread,int exitcode)63 static void my_exit(struct loopback_thread *thread, int exitcode)
64 {
65 int i;
66
67 for (i = 0; i < thread->loopbacks_count; i++)
68 pcmjob_done(thread->loopbacks[i]);
69 if (thread->threaded) {
70 thread->exitcode = exitcode;
71 pthread_exit(0);
72 }
73 exit(exitcode);
74 }
75
create_loopback_handle(struct loopback_handle ** _handle,const char * device,const char * ctldev,const char * id)76 static int create_loopback_handle(struct loopback_handle **_handle,
77 const char *device,
78 const char *ctldev,
79 const char *id)
80 {
81 char idbuf[1024];
82 struct loopback_handle *handle;
83
84 handle = calloc(1, sizeof(*handle));
85 if (handle == NULL)
86 return -ENOMEM;
87 if (device == NULL)
88 device = "hw:0,0";
89 handle->device = strdup(device);
90 if (handle->device == NULL) {
91 free(handle);
92 return -ENOMEM;
93 }
94 if (ctldev) {
95 handle->ctldev = strdup(ctldev);
96 if (handle->ctldev == NULL) {
97 free(handle->device);
98 free(handle);
99 return -ENOMEM;
100 }
101 } else {
102 handle->ctldev = NULL;
103 }
104 snprintf(idbuf, sizeof(idbuf)-1, "%s %s", id, device);
105 idbuf[sizeof(idbuf)-1] = '\0';
106 handle->id = strdup(idbuf);
107 handle->access = SND_PCM_ACCESS_RW_INTERLEAVED;
108 handle->format = SND_PCM_FORMAT_S16_LE;
109 handle->rate = handle->rate_req = 48000;
110 handle->channels = 2;
111 handle->resample = 0;
112 *_handle = handle;
113 return 0;
114 }
115
create_loopback(struct loopback ** _handle,struct loopback_handle * play,struct loopback_handle * capt,snd_output_t * output)116 static int create_loopback(struct loopback **_handle,
117 struct loopback_handle *play,
118 struct loopback_handle *capt,
119 snd_output_t *output)
120 {
121 struct loopback *handle;
122
123 handle = calloc(1, sizeof(*handle));
124 if (handle == NULL)
125 return -ENOMEM;
126 handle->play = play;
127 handle->capt = capt;
128 play->loopback = handle;
129 capt->loopback = handle;
130 handle->latency_req = 0;
131 handle->latency_reqtime = 10000;
132 handle->loop_time = ~0UL;
133 handle->loop_limit = ~0ULL;
134 handle->output = output;
135 handle->state = output;
136 #ifdef USE_SAMPLERATE
137 handle->src_enable = 1;
138 handle->src_converter_type = SRC_SINC_BEST_QUALITY;
139 #endif
140 *_handle = handle;
141 return 0;
142 }
143
set_loop_time(struct loopback * loop,unsigned long loop_time)144 static void set_loop_time(struct loopback *loop, unsigned long loop_time)
145 {
146 loop->loop_time = loop_time;
147 loop->loop_limit = loop->capt->rate * loop_time;
148 }
149
setscheduler(void)150 static void setscheduler(void)
151 {
152 struct sched_param sched_param;
153
154 if (sched_getparam(0, &sched_param) < 0) {
155 logit(LOG_WARNING, "Scheduler getparam failed.\n");
156 return;
157 }
158 sched_param.sched_priority = sched_get_priority_max(SCHED_RR);
159 if (!sched_setscheduler(0, SCHED_RR, &sched_param)) {
160 if (verbose)
161 logit(LOG_WARNING, "Scheduler set to Round Robin with priority %i\n", sched_param.sched_priority);
162 return;
163 }
164 if (verbose)
165 logit(LOG_INFO, "!!!Scheduler set to Round Robin with priority %i FAILED!\n", sched_param.sched_priority);
166 }
167
help(void)168 void help(void)
169 {
170 int k;
171 printf(
172 "Usage: alsaloop [OPTION]...\n\n"
173 "-h,--help help\n"
174 "-g,--config configuration file (one line = one job specified)\n"
175 "-d,--daemonize daemonize the main process and use syslog for errors\n"
176 "-P,--pdevice playback device\n"
177 "-C,--cdevice capture device\n"
178 "-X,--pctl playback ctl device\n"
179 "-Y,--cctl capture ctl device\n"
180 "-x,--prateshift playback 'PCM Rate Shift 100000' ascii ctl name\n"
181 "-l,--latency requested latency in frames\n"
182 "-t,--tlatency requested latency in usec (1/1000000sec)\n"
183 "-f,--format sample format\n"
184 "-c,--channels channels\n"
185 "-r,--rate rate\n"
186 "-n,--resample resample in alsa-lib\n"
187 "-A,--samplerate use converter (0=sincbest,1=sincmedium,2=sincfastest,\n"
188 " 3=zerohold,4=linear)\n"
189 "-B,--buffer buffer size in frames\n"
190 "-E,--period period size in frames\n"
191 "-s,--seconds duration of loop in seconds\n"
192 "-b,--nblock non-block mode (very early process wakeup)\n"
193 "-S,--sync sync mode(0=none,1=simple,2=captshift,3=playshift,4=samplerate,\n"
194 " 5=auto)\n"
195 "-a,--slave stream parameters slave mode (0=auto, 1=on, 2=off)\n"
196 "-T,--thread thread number (-1 = create unique)\n"
197 "-m,--mixer redirect mixer, argument is:\n"
198 " SRC_SLAVE_ID(PLAYBACK)[@DST_SLAVE_ID(CAPTURE)]\n"
199 "-O,--ossmixer rescan and redirect oss mixer, argument is:\n"
200 " ALSA_ID@OSS_ID (for example: \"Master@VOLUME\")\n"
201 "-e,--effect apply an effect (bandpass filter sweep)\n"
202 "-v,--verbose verbose mode (more -v means more verbose)\n"
203 "-w,--workaround use workaround (serialopen)\n"
204 "-U,--xrun xrun profiling\n"
205 "-W,--wake process wake timeout in ms\n"
206 "-z,--syslog use syslog for errors\n"
207 );
208 printf("\nRecognized sample formats are:");
209 for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) {
210 const char *s = snd_pcm_format_name(k);
211 if (s)
212 printf(" %s", s);
213 }
214 printf("\n\n");
215 printf(
216 "Tip #1 (usable 500ms latency, good CPU usage, superb xrun prevention):\n"
217 " alsaloop -t 500000\n"
218 "Tip #2 (superb 1ms latency, but heavy CPU usage):\n"
219 " alsaloop -t 1000\n"
220 );
221 }
222
timediff(struct timeval t1,struct timeval t2)223 static long timediff(struct timeval t1, struct timeval t2)
224 {
225 signed long l;
226
227 t1.tv_sec -= t2.tv_sec;
228 l = (signed long) t1.tv_usec - (signed long) t2.tv_usec;
229 if (l < 0) {
230 t1.tv_sec--;
231 l = 1000000 + l;
232 l %= 1000000;
233 }
234 return (t1.tv_sec * 1000000) + l;
235 }
236
add_loop(struct loopback * loop)237 static void add_loop(struct loopback *loop)
238 {
239 loopbacks = realloc(loopbacks, (loopbacks_count + 1) *
240 sizeof(struct loopback *));
241 if (loopbacks == NULL) {
242 logit(LOG_CRIT, "No enough memory\n");
243 exit(EXIT_FAILURE);
244 }
245 loopbacks[loopbacks_count++] = loop;
246 }
247
init_mixer_control(struct loopback_control * control,char * id)248 static int init_mixer_control(struct loopback_control *control,
249 char *id)
250 {
251 int err;
252
253 err = snd_ctl_elem_id_malloc(&control->id);
254 if (err < 0)
255 return err;
256 err = snd_ctl_elem_info_malloc(&control->info);
257 if (err < 0)
258 return err;
259 err = snd_ctl_elem_value_malloc(&control->value);
260 if (err < 0)
261 return err;
262 err = control_parse_id(id, control->id);
263 if (err < 0)
264 return err;
265 return 0;
266 }
267
add_mixers(struct loopback * loop,char ** mixers,int mixers_count)268 static int add_mixers(struct loopback *loop,
269 char **mixers,
270 int mixers_count)
271 {
272 struct loopback_mixer *mixer, *last = NULL;
273 char *str1;
274 int err;
275
276 while (mixers_count > 0) {
277 mixer = calloc(1, sizeof(*mixer));
278 if (mixer == NULL)
279 return -ENOMEM;
280 if (last)
281 last->next = mixer;
282 else
283 loop->controls = mixer;
284 last = mixer;
285 str1 = strchr(*mixers, '@');
286 if (str1)
287 *str1 = '\0';
288 err = init_mixer_control(&mixer->src, *mixers);
289 if (err < 0) {
290 logit(LOG_CRIT, "Wrong mixer control ID syntax '%s'\n", *mixers);
291 return -EINVAL;
292 }
293 err = init_mixer_control(&mixer->dst, str1 ? str1 + 1 : *mixers);
294 if (err < 0) {
295 logit(LOG_CRIT, "Wrong mixer control ID syntax '%s'\n", str1 ? str1 + 1 : *mixers);
296 return -EINVAL;
297 }
298 if (str1)
299 *str1 = '@';
300 mixers++;
301 mixers_count--;
302 }
303 return 0;
304 }
305
add_oss_mixers(struct loopback * loop,char ** mixers,int mixers_count)306 static int add_oss_mixers(struct loopback *loop,
307 char **mixers,
308 int mixers_count)
309 {
310 struct loopback_ossmixer *mixer, *last = NULL;
311 char *str1, *str2;
312
313 while (mixers_count > 0) {
314 mixer = calloc(1, sizeof(*mixer));
315 if (mixer == NULL)
316 return -ENOMEM;
317 if (last)
318 last->next = mixer;
319 else
320 loop->oss_controls = mixer;
321 last = mixer;
322 str1 = strchr(*mixers, ',');
323 if (str1)
324 *str1 = '\0';
325 str2 = strchr(str1 ? str1 + 1 : *mixers, '@');
326 if (str2)
327 *str2 = '\0';
328 mixer->alsa_id = strdup(*mixers);
329 if (str1)
330 mixer->alsa_index = atoi(str1);
331 mixer->oss_id = strdup(str2 ? str2 + 1 : *mixers);
332 if (mixer->alsa_id == NULL || mixer->oss_id == NULL) {
333 logit(LOG_CRIT, "Not enough memory");
334 return -ENOMEM;
335 }
336 if (str1)
337 *str1 = ',';
338 if (str2)
339 *str2 = ',';
340 mixers++;
341 mixers_count--;
342 }
343 return 0;
344 }
345
enable_syslog(void)346 static void enable_syslog(void)
347 {
348 if (!use_syslog) {
349 use_syslog = 1;
350 openlog("alsaloop", LOG_NDELAY|LOG_PID, LOG_DAEMON);
351 }
352 }
353
354 static int parse_config_file(const char *file, snd_output_t *output);
355
parse_config(int argc,char * argv[],snd_output_t * output,int cmdline)356 static int parse_config(int argc, char *argv[], snd_output_t *output,
357 int cmdline)
358 {
359 struct option long_option[] =
360 {
361 {"help", 0, NULL, 'h'},
362 {"config", 1, NULL, 'g'},
363 {"daemonize", 0, NULL, 'd'},
364 {"pdevice", 1, NULL, 'P'},
365 {"cdevice", 1, NULL, 'C'},
366 {"pctl", 1, NULL, 'X'},
367 {"cctl", 1, NULL, 'Y'},
368 {"prateshift", 1, NULL, 'x'},
369 {"latency", 1, NULL, 'l'},
370 {"tlatency", 1, NULL, 't'},
371 {"format", 1, NULL, 'f'},
372 {"channels", 1, NULL, 'c'},
373 {"rate", 1, NULL, 'r'},
374 {"buffer", 1, NULL, 'B'},
375 {"period", 1, NULL, 'E'},
376 {"seconds", 1, NULL, 's'},
377 {"nblock", 0, NULL, 'b'},
378 {"effect", 0, NULL, 'e'},
379 {"verbose", 0, NULL, 'v'},
380 {"resample", 0, NULL, 'n'},
381 {"samplerate", 1, NULL, 'A'},
382 {"sync", 1, NULL, 'S'},
383 {"slave", 1, NULL, 'a'},
384 {"thread", 1, NULL, 'T'},
385 {"mixer", 1, NULL, 'm'},
386 {"ossmixer", 1, NULL, 'O'},
387 {"workaround", 1, NULL, 'w'},
388 {"xrun", 0, NULL, 'U'},
389 {"syslog", 0, NULL, 'z'},
390 {NULL, 0, NULL, 0},
391 };
392 int err, morehelp;
393 char *arg_config = NULL;
394 char *arg_pdevice = NULL;
395 char *arg_cdevice = NULL;
396 char *arg_pctl = NULL;
397 char *arg_cctl = NULL;
398 char *arg_prateshift = NULL;
399 unsigned int arg_latency_req = 0;
400 unsigned int arg_latency_reqtime = 10000;
401 snd_pcm_format_t arg_format = SND_PCM_FORMAT_S16_LE;
402 unsigned int arg_channels = 2;
403 unsigned int arg_rate = 48000;
404 snd_pcm_uframes_t arg_buffer_size = 0;
405 snd_pcm_uframes_t arg_period_size = 0;
406 unsigned long arg_loop_time = ~0UL;
407 int arg_nblock = 0;
408 // int arg_effect = 0;
409 int arg_resample = 0;
410 #ifdef USE_SAMPLERATE
411 int arg_samplerate = SRC_SINC_FASTEST + 1;
412 #endif
413 int arg_sync = SYNC_TYPE_AUTO;
414 int arg_slave = SLAVE_TYPE_AUTO;
415 int arg_thread = 0;
416 struct loopback *loop = NULL;
417 char *arg_mixers[MAX_MIXERS];
418 int arg_mixers_count = 0;
419 char *arg_ossmixers[MAX_MIXERS];
420 int arg_ossmixers_count = 0;
421 int arg_xrun = arg_default_xrun;
422 int arg_wake = arg_default_wake;
423
424 morehelp = 0;
425 while (1) {
426 int c;
427 if ((c = getopt_long(argc, argv,
428 "hdg:P:C:X:Y:x:l:t:F:f:c:r:s:benvA:S:a:m:T:O:w:UW:z",
429 long_option, NULL)) < 0)
430 break;
431 switch (c) {
432 case 'h':
433 morehelp++;
434 break;
435 case 'g':
436 arg_config = strdup(optarg);
437 break;
438 case 'd':
439 daemonize = 1;
440 enable_syslog();
441 break;
442 case 'P':
443 arg_pdevice = strdup(optarg);
444 break;
445 case 'C':
446 arg_cdevice = strdup(optarg);
447 break;
448 case 'X':
449 arg_pctl = strdup(optarg);
450 break;
451 case 'Y':
452 arg_cctl = strdup(optarg);
453 break;
454 case 'x':
455 arg_prateshift = strdup(optarg);
456 break;
457 case 'l':
458 err = atoi(optarg);
459 arg_latency_req = err >= 4 ? err : 4;
460 break;
461 case 't':
462 err = atoi(optarg);
463 arg_latency_reqtime = err >= 500 ? err : 500;
464 break;
465 case 'f':
466 arg_format = snd_pcm_format_value(optarg);
467 if (arg_format == SND_PCM_FORMAT_UNKNOWN) {
468 logit(LOG_WARNING, "Unknown format, setting to default S16_LE\n");
469 arg_format = SND_PCM_FORMAT_S16_LE;
470 }
471 break;
472 case 'c':
473 err = atoi(optarg);
474 arg_channels = err >= 1 && err < 1024 ? err : 1;
475 break;
476 case 'r':
477 err = atoi(optarg);
478 arg_rate = err >= 4000 && err < 200000 ? err : 44100;
479 break;
480 case 'B':
481 err = atoi(optarg);
482 arg_buffer_size = err >= 32 && err < 200000 ? err : 0;
483 break;
484 case 'E':
485 err = atoi(optarg);
486 arg_period_size = err >= 32 && err < 200000 ? err : 0;
487 break;
488 case 's':
489 err = atoi(optarg);
490 arg_loop_time = err >= 1 && err <= 100000 ? err : 30;
491 break;
492 case 'b':
493 arg_nblock = 1;
494 break;
495 case 'e':
496 // arg_effect = 1;
497 break;
498 case 'n':
499 arg_resample = 1;
500 break;
501 #ifdef USE_SAMPLERATE
502 case 'A':
503 if (strcasecmp(optarg, "sincbest") == 0)
504 arg_samplerate = SRC_SINC_BEST_QUALITY;
505 else if (strcasecmp(optarg, "sincmedium") == 0)
506 arg_samplerate = SRC_SINC_MEDIUM_QUALITY;
507 else if (strcasecmp(optarg, "sincfastest") == 0)
508 arg_samplerate = SRC_SINC_FASTEST;
509 else if (strcasecmp(optarg, "zerohold") == 0)
510 arg_samplerate = SRC_ZERO_ORDER_HOLD;
511 else if (strcasecmp(optarg, "linear") == 0)
512 arg_samplerate = SRC_LINEAR;
513 else
514 arg_samplerate = atoi(optarg);
515 if (arg_samplerate < 0 || arg_samplerate > SRC_LINEAR)
516 arg_sync = SRC_SINC_FASTEST;
517 arg_samplerate += 1;
518 break;
519 #endif
520 case 'S':
521 if (strcasecmp(optarg, "samplerate") == 0)
522 arg_sync = SYNC_TYPE_SAMPLERATE;
523 else if (optarg[0] == 'n')
524 arg_sync = SYNC_TYPE_NONE;
525 else if (optarg[0] == 's')
526 arg_sync = SYNC_TYPE_SIMPLE;
527 else if (optarg[0] == 'c')
528 arg_sync = SYNC_TYPE_CAPTRATESHIFT;
529 else if (optarg[0] == 'p')
530 arg_sync = SYNC_TYPE_PLAYRATESHIFT;
531 else if (optarg[0] == 'r')
532 arg_sync = SYNC_TYPE_SAMPLERATE;
533 else if (optarg[0] == 'a')
534 arg_sync = SYNC_TYPE_AUTO;
535 else
536 arg_sync = atoi(optarg);
537 if (arg_sync < 0 || arg_sync > SYNC_TYPE_LAST)
538 arg_sync = SYNC_TYPE_AUTO;
539 break;
540 case 'a':
541 if (optarg[0] == 'a')
542 arg_slave = SLAVE_TYPE_AUTO;
543 else if (strcasecmp(optarg, "on") == 0)
544 arg_slave = SLAVE_TYPE_ON;
545 else if (strcasecmp(optarg, "off") == 0)
546 arg_slave = SLAVE_TYPE_OFF;
547 else
548 arg_slave = atoi(optarg);
549 if (arg_slave < 0 || arg_slave > SLAVE_TYPE_LAST)
550 arg_slave = SLAVE_TYPE_AUTO;
551 break;
552 case 'T':
553 arg_thread = atoi(optarg);
554 if (arg_thread < 0)
555 arg_thread = 10000000 + loopbacks_count;
556 break;
557 case 'm':
558 if (arg_mixers_count >= MAX_MIXERS) {
559 logit(LOG_CRIT, "Maximum redirected mixer controls reached (max %i)\n", (int)MAX_MIXERS);
560 exit(EXIT_FAILURE);
561 }
562 arg_mixers[arg_mixers_count++] = optarg;
563 break;
564 case 'O':
565 if (arg_ossmixers_count >= MAX_MIXERS) {
566 logit(LOG_CRIT, "Maximum redirected mixer controls reached (max %i)\n", (int)MAX_MIXERS);
567 exit(EXIT_FAILURE);
568 }
569 arg_ossmixers[arg_ossmixers_count++] = optarg;
570 break;
571 case 'v':
572 verbose++;
573 break;
574 case 'w':
575 if (strcasecmp(optarg, "serialopen") == 0)
576 workarounds |= WORKAROUND_SERIALOPEN;
577 break;
578 case 'U':
579 arg_xrun = 1;
580 if (cmdline)
581 arg_default_xrun = 1;
582 break;
583 case 'W':
584 arg_wake = atoi(optarg);
585 if (cmdline)
586 arg_default_wake = arg_wake;
587 break;
588 case 'z':
589 enable_syslog();
590 break;
591 }
592 }
593
594 if (morehelp) {
595 help();
596 exit(EXIT_SUCCESS);
597 }
598 if (arg_config == NULL) {
599 struct loopback_handle *play;
600 struct loopback_handle *capt;
601 err = create_loopback_handle(&play, arg_pdevice, arg_pctl, "playback");
602 if (err < 0) {
603 logit(LOG_CRIT, "Unable to create playback handle.\n");
604 exit(EXIT_FAILURE);
605 }
606 err = create_loopback_handle(&capt, arg_cdevice, arg_cctl, "capture");
607 if (err < 0) {
608 logit(LOG_CRIT, "Unable to create capture handle.\n");
609 exit(EXIT_FAILURE);
610 }
611 err = create_loopback(&loop, play, capt, output);
612 if (err < 0) {
613 logit(LOG_CRIT, "Unable to create loopback handle.\n");
614 exit(EXIT_FAILURE);
615 }
616 play->format = capt->format = arg_format;
617 play->rate = play->rate_req = capt->rate = capt->rate_req = arg_rate;
618 play->channels = capt->channels = arg_channels;
619 play->buffer_size_req = capt->buffer_size_req = arg_buffer_size;
620 play->period_size_req = capt->period_size_req = arg_period_size;
621 play->resample = capt->resample = arg_resample;
622 play->nblock = capt->nblock = arg_nblock ? 1 : 0;
623 loop->latency_req = arg_latency_req;
624 loop->latency_reqtime = arg_latency_reqtime;
625 loop->sync = arg_sync;
626 loop->slave = arg_slave;
627 loop->thread = arg_thread;
628 loop->xrun = arg_xrun;
629 loop->wake = arg_wake;
630 err = add_mixers(loop, arg_mixers, arg_mixers_count);
631 if (err < 0) {
632 logit(LOG_CRIT, "Unable to add mixer controls.\n");
633 exit(EXIT_FAILURE);
634 }
635 err = add_oss_mixers(loop, arg_ossmixers, arg_ossmixers_count);
636 if (err < 0) {
637 logit(LOG_CRIT, "Unable to add ossmixer controls.\n");
638 exit(EXIT_FAILURE);
639 }
640 if (arg_prateshift)
641 play->prateshift_name = arg_prateshift;
642
643 #ifdef USE_SAMPLERATE
644 loop->src_enable = arg_samplerate > 0;
645 if (loop->src_enable)
646 loop->src_converter_type = arg_samplerate - 1;
647 #endif
648 set_loop_time(loop, arg_loop_time);
649 add_loop(loop);
650 return 0;
651 }
652
653 return parse_config_file(arg_config, output);
654 }
655
parse_config_file(const char * file,snd_output_t * output)656 static int parse_config_file(const char *file, snd_output_t *output)
657 {
658 FILE *fp;
659 char line[2048], word[2048];
660 char *str, *ptr;
661 int argc, c, err = 0;
662 char **argv;
663
664 fp = fopen(file, "r");
665 if (fp == NULL) {
666 logit(LOG_CRIT, "Unable to open file '%s': %s\n", file, strerror(errno));
667 return -EIO;
668 }
669 while (!feof(fp)) {
670 if (fgets(line, sizeof(line)-1, fp) == NULL)
671 break;
672 line[sizeof(line)-1] = '\0';
673 my_argv = realloc(my_argv, my_argc + MAX_ARGS * sizeof(char *));
674 if (my_argv == NULL)
675 return -ENOMEM;
676 argv = my_argv + my_argc;
677 argc = 0;
678 argv[argc++] = strdup("<prog>");
679 my_argc++;
680 str = line;
681 while (*str) {
682 ptr = word;
683 while (*str && (*str == ' ' || *str < ' '))
684 str++;
685 if (*str == '#')
686 goto __next;
687 if (*str == '\'' || *str == '\"') {
688 c = *str++;
689 while (*str && *str != c)
690 *ptr++ = *str++;
691 if (*str == c)
692 str++;
693 } else {
694 while (*str && *str != ' ' && *str != '\t')
695 *ptr++ = *str++;
696 }
697 if (ptr != word) {
698 if (*(ptr-1) == '\n')
699 ptr--;
700 *ptr = '\0';
701 if (argc >= MAX_ARGS) {
702 logit(LOG_CRIT, "Too many arguments.");
703 goto __error;
704 }
705 argv[argc++] = strdup(word);
706 my_argc++;
707 }
708 }
709 /* erase runtime variables for getopt */
710 optarg = NULL;
711 optind = opterr = 1;
712 optopt = '?';
713
714 err = parse_config(argc, argv, output, 0);
715 __next:
716 if (err < 0)
717 break;
718 err = 0;
719 }
720 __error:
721 fclose(fp);
722
723 return err;
724 }
725
thread_job1(void * _data)726 static void thread_job1(void *_data)
727 {
728 struct loopback_thread *thread = _data;
729 snd_output_t *output = thread->output;
730 struct pollfd *pfds = NULL;
731 int pfds_count = 0;
732 int i, j, err, wake = 1000000;
733
734 setscheduler();
735
736 for (i = 0; i < thread->loopbacks_count; i++) {
737 err = pcmjob_init(thread->loopbacks[i]);
738 if (err < 0) {
739 logit(LOG_CRIT, "Loopback initialization failure.\n");
740 my_exit(thread, EXIT_FAILURE);
741 }
742 }
743 for (i = 0; i < thread->loopbacks_count; i++) {
744 err = pcmjob_start(thread->loopbacks[i]);
745 if (err < 0) {
746 logit(LOG_CRIT, "Loopback start failure.\n");
747 my_exit(thread, EXIT_FAILURE);
748 }
749 pfds_count += thread->loopbacks[i]->pollfd_count;
750 j = thread->loopbacks[i]->wake;
751 if (j > 0 && j < wake)
752 wake = j;
753 }
754 if (wake >= 1000000)
755 wake = -1;
756 pfds = calloc(pfds_count, sizeof(struct pollfd));
757 if (pfds == NULL || pfds_count <= 0) {
758 logit(LOG_CRIT, "Poll FDs allocation failed.\n");
759 my_exit(thread, EXIT_FAILURE);
760 }
761 while (!quit) {
762 struct timeval tv1, tv2;
763 for (i = j = 0; i < thread->loopbacks_count; i++) {
764 err = pcmjob_pollfds_init(thread->loopbacks[i], &pfds[j]);
765 if (err < 0) {
766 logit(LOG_CRIT, "Poll FD initialization failed.\n");
767 my_exit(thread, EXIT_FAILURE);
768 }
769 j += err;
770 }
771 if (verbose > 10)
772 gettimeofday(&tv1, NULL);
773 err = poll(pfds, j, wake);
774 if (err < 0)
775 err = -errno;
776 if (verbose > 10) {
777 gettimeofday(&tv2, NULL);
778 snd_output_printf(output, "pool took %lius\n", timediff(tv2, tv1));
779 }
780 if (err < 0) {
781 if (err == -EINTR || err == -ERESTART)
782 continue;
783 logit(LOG_CRIT, "Poll failed: %s\n", strerror(-err));
784 my_exit(thread, EXIT_FAILURE);
785 }
786 for (i = j = 0; i < thread->loopbacks_count; i++) {
787 struct loopback *loop = thread->loopbacks[i];
788 if (j < loop->active_pollfd_count) {
789 err = pcmjob_pollfds_handle(loop, &pfds[j]);
790 if (err < 0) {
791 logit(LOG_CRIT, "pcmjob failed.\n");
792 exit(EXIT_FAILURE);
793 }
794 }
795 j += loop->active_pollfd_count;
796 }
797 }
798
799 my_exit(thread, EXIT_SUCCESS);
800 }
801
thread_job(struct loopback_thread * thread)802 static void thread_job(struct loopback_thread *thread)
803 {
804 if (!thread->threaded) {
805 thread_job1(thread);
806 return;
807 }
808 pthread_create(&thread->thread, NULL, (void *) &thread_job1,
809 (void *) thread);
810 }
811
send_to_all(int sig)812 static void send_to_all(int sig)
813 {
814 struct loopback_thread *thread;
815 int i;
816
817 for (i = 0; i < threads_count; i++) {
818 thread = &threads[i];
819 if (thread->threaded)
820 pthread_kill(thread->thread, sig);
821 }
822 }
823
signal_handler(int sig ATTRIBUTE_UNUSED)824 static void signal_handler(int sig ATTRIBUTE_UNUSED)
825 {
826 quit = 1;
827 send_to_all(SIGUSR2);
828 }
829
signal_handler_state(int sig)830 static void signal_handler_state(int sig)
831 {
832 pthread_t self = pthread_self();
833 struct loopback_thread *thread;
834 int i, j;
835
836 if (pthread_equal(main_job, self))
837 send_to_all(SIGUSR1);
838 for (i = 0; i < threads_count; i++) {
839 thread = &threads[i];
840 if (thread->thread == self) {
841 for (j = 0; j < thread->loopbacks_count; j++)
842 pcmjob_state(thread->loopbacks[j]);
843 }
844 }
845 signal(sig, signal_handler_state);
846 }
847
signal_handler_ignore(int sig)848 static void signal_handler_ignore(int sig)
849 {
850 signal(sig, signal_handler_ignore);
851 }
852
main(int argc,char * argv[])853 int main(int argc, char *argv[])
854 {
855 snd_output_t *output;
856 int i, j, k, l, err;
857
858 err = snd_output_stdio_attach(&output, stdout, 0);
859 if (err < 0) {
860 logit(LOG_CRIT, "Output failed: %s\n", snd_strerror(err));
861 exit(EXIT_FAILURE);
862 }
863 err = parse_config(argc, argv, output, 1);
864 if (err < 0) {
865 logit(LOG_CRIT, "Unable to parse arguments or configuration...\n");
866 exit(EXIT_FAILURE);
867 }
868 while (my_argc > 0)
869 free(my_argv[--my_argc]);
870 free(my_argv);
871
872 if (loopbacks_count <= 0) {
873 logit(LOG_CRIT, "No loopback defined...\n");
874 exit(EXIT_FAILURE);
875 }
876
877 if (daemonize) {
878 if (daemon(0, 0) < 0) {
879 logit(LOG_CRIT, "daemon() failed: %s\n", strerror(errno));
880 exit(EXIT_FAILURE);
881 }
882 i = fork();
883 if (i < 0) {
884 logit(LOG_CRIT, "fork() failed: %s\n", strerror(errno));
885 exit(EXIT_FAILURE);
886 }
887 if (i > 0) {
888 /* wait(&i); */
889 exit(EXIT_SUCCESS);
890 }
891 }
892
893 /* we must sort thread IDs */
894 j = -1;
895 do {
896 k = 0x7fffffff;
897 for (i = 0; i < loopbacks_count; i++) {
898 if (loopbacks[i]->thread < k &&
899 loopbacks[i]->thread > j)
900 k = loopbacks[i]->thread;
901 }
902 j++;
903 for (i = 0; i < loopbacks_count; i++) {
904 if (loopbacks[i]->thread == k)
905 loopbacks[i]->thread = j;
906 }
907 } while (k != 0x7fffffff);
908 /* fix maximum thread id */
909 for (i = 0, j = -1; i < loopbacks_count; i++) {
910 if (loopbacks[i]->thread > j)
911 j = loopbacks[i]->thread;
912 }
913 j += 1;
914 threads = calloc(1, sizeof(struct loopback_thread) * j);
915 if (threads == NULL) {
916 logit(LOG_CRIT, "No enough memory\n");
917 exit(EXIT_FAILURE);
918 }
919 /* sort all threads */
920 for (k = 0; k < j; k++) {
921 for (i = l = 0; i < loopbacks_count; i++)
922 if (loopbacks[i]->thread == k)
923 l++;
924 threads[k].loopbacks = malloc(l * sizeof(struct loopback *));
925 threads[k].loopbacks_count = l;
926 threads[k].output = output;
927 threads[k].threaded = j > 1;
928 for (i = l = 0; i < loopbacks_count; i++)
929 if (loopbacks[i]->thread == k)
930 threads[k].loopbacks[l++] = loopbacks[i];
931 }
932 threads_count = j;
933 main_job = pthread_self();
934
935 signal(SIGINT, signal_handler);
936 signal(SIGTERM, signal_handler);
937 signal(SIGABRT, signal_handler);
938 signal(SIGUSR1, signal_handler_state);
939 signal(SIGUSR2, signal_handler_ignore);
940
941 for (k = 0; k < threads_count; k++)
942 thread_job(&threads[k]);
943
944 if (j > 1) {
945 for (k = 0; k < threads_count; k++)
946 pthread_join(threads[k].thread, NULL);
947 }
948
949 if (use_syslog)
950 closelog();
951 exit(EXIT_SUCCESS);
952 }
953