1 /*
2 	out123: simple program to stream data to an audio output device
3 
4 	copyright 1995-2016 by the mpg123 project - free software under the terms of the LGPL 2.1
5 	see COPYING and AUTHORS files in distribution or http://mpg123.org
6 	initially written by Thomas Orgis (extracted from mpg123.c)
7 
8 	This is a stripped down mpg123 that only uses libout123 to write standard input
9 	to an audio device.
10 
11 	TODO: Add basic parsing of WAV headers to be able to pipe in WAV files, especially
12 	from something like mpg123 -w -.
13 */
14 
15 #define ME "out123"
16 #include "config.h"
17 #include "compat.h"
18 #if WIN32
19 #include "win32_support.h"
20 #endif
21 #include "out123.h"
22 
23 #ifdef HAVE_SYS_WAIT_H
24 #include <sys/wait.h>
25 #endif
26 #ifdef HAVE_SYS_RESOURCE_H
27 #include <sys/resource.h>
28 #endif
29 
30 #include <errno.h>
31 #include <string.h>
32 #include <time.h>
33 
34 #ifdef HAVE_SCHED_H
35 #include <sched.h>
36 #endif
37 
38 #include "sysutil.h"
39 #include "getlopt.h"
40 
41 #include "waves.h"
42 
43 #include "debug.h"
44 
45 /* be paranoid about setpriority support */
46 #ifndef PRIO_PROCESS
47 #undef HAVE_SETPRIORITY
48 #endif
49 
50 static int intflag = FALSE;
51 
52 static void usage(int err);
53 static void want_usage(char* arg);
54 static void long_usage(int err);
55 static void want_long_usage(char* arg);
56 static void print_title(FILE* o);
57 static void give_version(char* arg);
58 
59 static int verbose = 0;
60 static int quiet = FALSE;
61 
62 static FILE* input = NULL;
63 static char *encoding_name = NULL;
64 static int  encoding = MPG123_ENC_SIGNED_16;
65 static int  channels = 2;
66 static long rate     = 44100;
67 static char *driver = NULL;
68 static char *device = NULL;
69 size_t buffer_kb = 0;
70 static int realtime = FALSE;
71 #ifdef HAVE_WINDOWS_H
72 static int w32_priority = 0;
73 #endif
74 static int aggressive = FALSE;
75 static double preload = 0.2;
76 static long outflags = 0;
77 static long gain = -1;
78 static const char *name = NULL; /* Let the out123 library choose "out123". */
79 static double device_buffer; /* output device buffer */
80 long timelimit = -1;
81 off_t offset = 0;
82 
83 char *wave_patterns = NULL;
84 char *wave_freqs    = NULL;
85 char *wave_phases   = NULL;
86 /* Default to around 2 MiB memory for the table. */
87 long wave_limit     = 300000;
88 
89 size_t pcmblock = 1152; /* samples (pcm frames) we treat en bloc */
90 /* To be set after settling format. */
91 size_t pcmframe = 0;
92 unsigned char *audio = NULL;
93 
94 /* Option to play some oscillatory test signals. */
95 struct wave_table *waver = NULL;
96 
97 out123_handle *ao = NULL;
98 char *cmd_name = NULL;
99 /* ThOr: pointers are not TRUE or FALSE */
100 char *equalfile = NULL;
101 int fresh = TRUE;
102 
103 size_t bufferblock = 4096;
104 
105 int OutputDescriptor;
106 
107 char *fullprogname = NULL; /* Copy of argv[0]. */
108 char *binpath; /* Path to myself. */
109 
110 /* File-global storage of command line arguments.
111    They may be needed for cleanup after charset conversion. */
112 static char **argv = NULL;
113 static int    argc = 0;
114 
115 /* Drain output device/buffer, but still give the option to interrupt things. */
controlled_drain(void)116 static void controlled_drain(void)
117 {
118 	int framesize;
119 	long rate;
120 	size_t drain_block;
121 
122 	if(intflag || !out123_buffered(ao))
123 		return;
124 	if(out123_getformat(ao, &rate, NULL, NULL, &framesize))
125 		return;
126 	drain_block = 1024*framesize;
127 	if(!quiet)
128 		fprintf( stderr
129 		,	"\n"ME": draining buffer of %.1f s (you may interrupt)\n"
130 		,	(double)out123_buffered(ao)/framesize/rate );
131 	do {
132 		out123_ndrain(ao, drain_block);
133 	} while(!intflag && out123_buffered(ao));
134 }
135 
safe_exit(int code)136 static void safe_exit(int code)
137 {
138 	char *dummy, *dammy;
139 
140 	if(!code)
141 		controlled_drain();
142 	if(intflag || code)
143 		out123_drop(ao);
144 	out123_del(ao);
145 #ifdef WANT_WIN32_UNICODE
146 	win32_cmdline_free(argc, argv); /* This handles the premature argv == NULL, too. */
147 #endif
148 	/* It's ugly... but let's just fix this still-reachable memory chunk of static char*. */
149 	split_dir_file("", &dummy, &dammy);
150 	if(fullprogname) free(fullprogname);
151 	if(audio) free(audio);
152 	wave_table_del(waver);
153 	exit(code);
154 }
155 
check_fatal_output(int code)156 static void check_fatal_output(int code)
157 {
158 	if(code)
159 	{
160 		if(!quiet)
161 			error2( "out123 error %i: %s"
162 			,	out123_errcode(ao), out123_strerror(ao) );
163 		safe_exit(code);
164 	}
165 }
166 
set_output_module(char * arg)167 static void set_output_module( char *arg )
168 {
169 	unsigned int i;
170 
171 	/* Search for a colon and set the device if found */
172 	for(i=0; i< strlen( arg ); i++) {
173 		if (arg[i] == ':') {
174 			arg[i] = 0;
175 			device = &arg[i+1];
176 			debug1("Setting output device: %s", device);
177 			break;
178 		}
179 	}
180 	/* Set the output module */
181 	driver = arg;
182 	debug1("Setting output module: %s", driver );
183 }
184 
set_output_flag(int flag)185 static void set_output_flag(int flag)
186 {
187   if(outflags <= 0) outflags = flag;
188   else outflags |= flag;
189 }
190 
set_output_h(char * a)191 static void set_output_h(char *a)
192 {
193 	set_output_flag(OUT123_HEADPHONES);
194 }
195 
set_output_s(char * a)196 static void set_output_s(char *a)
197 {
198 	set_output_flag(OUT123_INTERNAL_SPEAKER);
199 }
200 
set_output_l(char * a)201 static void set_output_l(char *a)
202 {
203 	set_output_flag(OUT123_LINE_OUT);
204 }
205 
set_output(char * arg)206 static void set_output(char *arg)
207 {
208 	/* If single letter, it's the legacy output switch for AIX/HP/Sun.
209 	   If longer, it's module[:device] . If zero length, it's rubbish. */
210 	if(strlen(arg) <= 1) switch(arg[0])
211 	{
212 		case 'h': set_output_h(arg); break;
213 		case 's': set_output_s(arg); break;
214 		case 'l': set_output_l(arg); break;
215 		default:
216 			error1("\"%s\" is no valid output", arg);
217 			safe_exit(1);
218 	}
219 	else set_output_module(arg);
220 }
221 
set_verbose(char * arg)222 static void set_verbose (char *arg)
223 {
224     verbose++;
225 }
226 
set_quiet(char * arg)227 static void set_quiet (char *arg)
228 {
229 	verbose=0;
230 	quiet=TRUE;
231 }
232 
set_out_wav(char * arg)233 static void set_out_wav(char *arg)
234 {
235 	driver = "wav";
236 	device = arg;
237 }
238 
set_out_cdr(char * arg)239 void set_out_cdr(char *arg)
240 {
241 	driver = "cdr";
242 	device = arg;
243 }
244 
set_out_au(char * arg)245 void set_out_au(char *arg)
246 {
247 	driver = "au";
248 	device = arg;
249 }
250 
set_out_test(char * arg)251 void set_out_test(char *arg)
252 {
253 	driver = "test";
254 	device = NULL;
255 }
256 
set_out_file(char * arg)257 static void set_out_file(char *arg)
258 {
259 	driver = "raw";
260 	device = arg;
261 }
262 
set_out_stdout(char * arg)263 static void set_out_stdout(char *arg)
264 {
265 	driver = "raw";
266 	device = NULL;
267 }
268 
set_out_stdout1(char * arg)269 static void set_out_stdout1(char *arg)
270 {
271 	driver = "raw";
272 	device = NULL;
273 }
274 
275 #if !defined (HAVE_SCHED_SETSCHEDULER) && !defined (HAVE_WINDOWS_H)
realtime_not_compiled(char * arg)276 static void realtime_not_compiled(char *arg)
277 {
278 	fprintf(stderr, ME": Option '-T / --realtime' not compiled into this binary.\n");
279 }
280 #endif
281 
list_output_modules(char * arg)282 static void list_output_modules(char *arg)
283 {
284 	char **names = NULL;
285 	char **descr = NULL;
286 	int count = -1;
287 	out123_handle *lao;
288 
289 	if((lao=out123_new()))
290 	{
291 		out123_param_string(lao, OUT123_BINDIR, binpath);
292 		out123_param_int(lao, OUT123_VERBOSE, verbose);
293 		if(quiet)
294 			out123_param_int(lao, OUT123_FLAGS, OUT123_QUIET);
295 		if((count=out123_drivers(lao, &names, &descr)) >= 0)
296 		{
297 			int i;
298 			for(i=0; i<count; ++i)
299 			{
300 				printf( "%-15s\t%s\n", names[i], descr[i] );
301 				free(names[i]);
302 				free(descr[i]);
303 			}
304 			free(names);
305 			free(descr);
306 		}
307 		out123_del(lao);
308 	}
309 	else if(!quiet)
310 		error("Failed to create an out123 handle.");
311 	exit(count >= 0 ? 0 : 1);
312 }
313 
list_encodings(char * arg)314 static void list_encodings(char *arg)
315 {
316 	int i;
317 	int enc_count = 0;
318 	int *enc_codes = NULL;
319 
320 	enc_count = out123_enc_list(&enc_codes);
321 	/* All of the returned encodings have to have proper names!
322 	   It is a libout123 bug if not, and it should be quickly caught. */
323 	for(i=0;i<enc_count;++i)
324 		printf( "%s:\t%s\n"
325 		,	out123_enc_name(enc_codes[i]), out123_enc_longname(enc_codes[i]) );
326 	free(enc_codes);
327 	exit(0);
328 }
329 
getencs(void)330 static int getencs(void)
331 {
332 	int encs = 0;
333 	out123_handle *lao;
334 	if(verbose)
335 		fprintf( stderr
336 		,	ME": getting supported encodings for %li Hz, %i channels\n"
337 		,	rate, channels );
338 	if((lao=out123_new()))
339 	{
340 		out123_param_int(lao, OUT123_VERBOSE, verbose);
341 		if(quiet)
342 			out123_param_int(lao, OUT123_FLAGS, OUT123_QUIET);
343 		if(!out123_open(lao, driver, device))
344 			encs = out123_encodings(lao, rate, channels);
345 		else if(!quiet)
346 			error1("cannot open driver: %s", out123_strerror(lao));
347 		out123_del(lao);
348 	}
349 	else if(!quiet)
350 		error("Failed to create an out123 handle.");
351 	return encs;
352 }
353 
test_format(char * arg)354 static void test_format(char *arg)
355 {
356 	int encs;
357 	encs = getencs();
358 	exit((encs & encoding) ? 0 : -1);
359 }
360 
test_encodings(char * arg)361 static void test_encodings(char *arg)
362 {
363 	int encs;
364 	encs = getencs();
365 	printf("%i\n", encs);
366 	exit(!encs);
367 }
368 
query_format(char * arg)369 static void query_format(char *arg)
370 {
371 	out123_handle *lao;
372 
373 	if(verbose)
374 		fprintf(stderr, ME": querying default format\n");
375 	if((lao=out123_new()))
376 	{
377 		out123_param_int(lao, OUT123_VERBOSE, verbose);
378 		if(quiet)
379 			out123_param_int(lao, OUT123_FLAGS, OUT123_QUIET);
380 		if(!out123_open(lao, driver, device))
381 		{
382 			struct mpg123_fmt *fmts = NULL;
383 			int count;
384 			count = out123_formats(lao, NULL, 0, 0, 0, &fmts);
385 			if(count > 0 && fmts[0].encoding > 0)
386 			{
387 				const char *encname = out123_enc_name(fmts[0].encoding);
388 				printf( "--rate %li --channels %i --encoding %s\n"
389 				,	fmts[0].rate, fmts[0].channels
390 				,	encname ? encname : "???" );
391 			}
392 			else
393 			{
394 				if(verbose)
395 					fprintf(stderr, ME": no default format found\n");
396 			}
397 			free(fmts);
398 		}
399 		else if(!quiet)
400 			error1("cannot open driver: %s", out123_strerror(lao));
401 		out123_del(lao);
402 	}
403 	else if(!quiet)
404 		error("Failed to create an out123 handle.");
405 	exit(0);
406 }
407 
408 /* Please note: GLO_NUM expects point to LONG! */
409 /* ThOr:
410  *  Yeah, and despite that numerous addresses to int variables were
411 passed.
412  *  That's not good on my Alpha machine with int=32bit and long=64bit!
413  *  Introduced GLO_INT and GLO_LONG as different bits to make that clear.
414  *  GLO_NUM no longer exists.
415  */
416 topt opts[] = {
417 	{'t', "test",        GLO_INT,  set_out_test, NULL, 0},
418 	{'s', "stdout",      GLO_INT,  set_out_stdout,  NULL, 0},
419 	{'S', "STDOUT",      GLO_INT,  set_out_stdout1, NULL, 0},
420 	{'O', "outfile",     GLO_ARG | GLO_CHAR, set_out_file, NULL, 0},
421 	{'v', "verbose",     0,        set_verbose, 0,           0},
422 	{'q', "quiet",       0,        set_quiet,   0,           0},
423 	{'m',  "mono",       GLO_INT,  0, &channels, 1},
424 	{0,   "stereo",      GLO_INT,  0, &channels, 2},
425 	{'c', "channels",    GLO_ARG | GLO_INT,  0, &channels, 0},
426 	{'r', "rate",        GLO_ARG | GLO_LONG, 0, &rate,  0},
427 	{0,   "headphones",  0,                  set_output_h, 0,0},
428 	{0,   "speaker",     0,                  set_output_s, 0,0},
429 	{0,   "lineout",     0,                  set_output_l, 0,0},
430 	{'o', "output",      GLO_ARG | GLO_CHAR, set_output, 0,  0},
431 	{0,   "list-modules",0,       list_output_modules, NULL,  0},
432 	{'a', "audiodevice", GLO_ARG | GLO_CHAR, 0, &device,  0},
433 #ifndef NOXFERMEM
434 	{'b', "buffer",      GLO_ARG | GLO_LONG, 0, &buffer_kb,  0},
435 	{0, "preload", GLO_ARG|GLO_DOUBLE, 0, &preload, 0},
436 #endif
437 #ifdef HAVE_SETPRIORITY
438 	{0,   "aggressive",	 GLO_INT,  0, &aggressive, 2},
439 #endif
440 #if defined (HAVE_SCHED_SETSCHEDULER) || defined (HAVE_WINDOWS_H)
441 	/* check why this should be a long variable instead of int! */
442 	{'T', "realtime",    GLO_INT,  0, &realtime, TRUE },
443 #else
444 	{'T', "realtime",    0,  realtime_not_compiled, 0,           0 },
445 #endif
446 #ifdef HAVE_WINDOWS_H
447 	{0, "priority", GLO_ARG | GLO_INT, 0, &w32_priority, 0},
448 #endif
449 	{'w', "wav",         GLO_ARG | GLO_CHAR, set_out_wav, 0, 0 },
450 	{0, "cdr",           GLO_ARG | GLO_CHAR, set_out_cdr, 0, 0 },
451 	{0, "au",            GLO_ARG | GLO_CHAR, set_out_au, 0, 0 },
452 	{'?', "help",            0,  want_usage, 0,           0 },
453 	{0 , "longhelp" ,        0,  want_long_usage, 0,      0 },
454 	{0 , "version" ,         0,  give_version, 0,         0 },
455 	{'e', "encoding", GLO_ARG|GLO_CHAR, 0, &encoding_name, 0},
456 	{0, "list-encodings", 0, list_encodings, 0, 0 },
457 	{0, "test-format", 0, test_format, 0, 0 },
458 	{0, "test-encodings", 0, test_encodings, 0, 0},
459 	{0, "query-format", 0, query_format, 0, 0},
460 	{0, "name", GLO_ARG|GLO_CHAR, 0, &name, 0},
461 	{0, "devbuffer", GLO_ARG|GLO_DOUBLE, 0, &device_buffer, 0},
462 	{0, "timelimit", GLO_ARG|GLO_LONG, 0, &timelimit, 0},
463 	{0, "wave-pat", GLO_ARG|GLO_CHAR, 0, &wave_patterns, 0},
464 	{0, "wave-freq", GLO_ARG|GLO_CHAR, 0, &wave_freqs, 0},
465 	{0, "wave-phase", GLO_ARG|GLO_CHAR, 0, &wave_phases, 0},
466 	{0, "wave-limit", GLO_ARG|GLO_LONG, 0, &wave_limit, 0},
467 	{0, 0, 0, 0, 0, 0}
468 };
469 
470 /* An strtok() that also returns empty tokens on multiple separators. */
471 
mytok_count(const char * choppy)472 static size_t mytok_count(const char *choppy)
473 {
474 	size_t count = 0;
475 	if(choppy)
476 	{
477 		count = 1;
478 		do {
479 			if(*choppy == ',')
480 				++count;
481 		} while(*(++choppy));
482 	}
483 	return count;
484 }
485 
mytok(char ** choppy)486 static char *mytok(char **choppy)
487 {
488 	char *tok;
489 	if(!*choppy)
490 		return NULL;
491 	tok  = *choppy;
492 	while(**choppy && **choppy != ',')
493 		++(*choppy);
494 	/* Another token follows if we found a separator. */
495 	if(**choppy == ',')
496 		*(*choppy)++ = 0;
497 	else
498 		*choppy = NULL; /* Nothing left. */
499 	return tok;
500 }
501 
setup_wavegen(void)502 static void setup_wavegen(void)
503 {
504 	size_t count = 0;
505 	size_t i;
506 	double *freq = NULL;
507 	double *phase = NULL;
508 	const char **pat = NULL;
509 
510 	if(wave_freqs)
511 	{
512 		char *tok;
513 		char *next;
514 		count = mytok_count(wave_freqs);
515 		freq = malloc(sizeof(double)*count);
516 		if(!freq){ error("OOM!"); safe_exit(1); }
517 		next = wave_freqs;
518 		for(i=0; i<count; ++i)
519 		{
520 			tok = mytok(&next);
521 			if(tok && *tok)
522 				freq[i] = atof(tok);
523 			else if(i)
524 				freq[i] = freq[i-1];
525 			else
526 				freq[i] = 0;
527 		}
528 	}
529 	else return;
530 
531 	if(count && wave_patterns)
532 	{
533 		char *tok;
534 		char *next = wave_patterns;
535 		pat = malloc(sizeof(char*)*count);
536 		if(!pat){ error("OOM!"); safe_exit(1); }
537 		for(i=0; i<count; ++i)
538 		{
539 			tok = mytok(&next);
540 			if((tok && *tok) || i==0)
541 				pat[i] = tok;
542 			else
543 				pat[i] = pat[i-1];
544 		}
545 	}
546 
547 	if(count && wave_phases)
548 	{
549 		char *tok;
550 		char *next = wave_phases;
551 		phase = malloc(sizeof(double)*count);
552 		if(!phase){ error("OOM!"); safe_exit(1); }
553 		for(i=0; i<count; ++i)
554 		{
555 			tok = mytok(&next);
556 			if(tok && *tok)
557 				phase[i] = atof(tok);
558 			else if(i)
559 				phase[i] = phase[i-1];
560 			else
561 				phase[i] = 0;
562 		}
563 	}
564 
565 	waver = wave_table_new( rate, channels, encoding, count, freq, pat, phase
566 	,	(size_t)wave_limit );
567 	if(!waver)
568 	{
569 		error("Cannot set up wave generator.");
570 		safe_exit(132);
571 	}
572 
573 	if(verbose)
574 	{
575 		fprintf(stderr, "wave table of %" SIZE_P " samples\n", waver->samples);
576 		/* TODO: There is a crash here! pat being optimised away ... */
577 		for(i=0; i<count; ++i)
578 			fprintf( stderr, "wave %" SIZE_P ": %s @ %g Hz (%g Hz) p %g\n"
579 			,	i
580 			,	(pat && pat[i]) ? pat[i] : wave_pattern_default
581 			,	freq[i], waver->freq[i]
582 			,	phase ? phase[i] : 0 );
583 	}
584 
585 	if(phase)
586 		free(phase);
587 	if(pat)
588 		free(pat);
589 	if(freq)
590 		free(freq);
591 }
592 
593 /* return 1 on success, 0 on failure */
play_frame(void)594 int play_frame(void)
595 {
596 	size_t got_samples;
597 	size_t get_samples = pcmblock;
598 	if(timelimit >= 0)
599 	{
600 		if(offset >= timelimit)
601 			return 0;
602 		else if(timelimit < offset+get_samples)
603 			get_samples = (off_t)timelimit-offset;
604 	}
605 	if(waver)
606 		got_samples = wave_table_extract(waver, audio, get_samples);
607 	else
608 		got_samples = fread(audio, pcmframe, get_samples, input);
609 	/* Play what is there to play (starting with second decode_frame call!) */
610 	if(got_samples)
611 	{
612 		size_t got_bytes = pcmframe * got_samples;
613 		if(out123_play(ao, audio, got_bytes) < (int)got_bytes)
614 		{
615 			if(!quiet)
616 			{
617 				error2( "out123 error %i: %s"
618 				,	out123_errcode(ao), out123_strerror(ao) );
619 			}
620 			safe_exit(133);
621 		}
622 		offset += got_samples;
623 		return 1;
624 	}
625 	else return 0;
626 }
627 
628 #if !defined(WIN32) && !defined(GENERIC)
catch_interrupt(void)629 static void catch_interrupt(void)
630 {
631         intflag = TRUE;
632 }
633 #endif
634 
main(int sys_argc,char ** sys_argv)635 int main(int sys_argc, char ** sys_argv)
636 {
637 	int result;
638 #if defined(WIN32)
639 	_setmode(STDIN_FILENO,  _O_BINARY);
640 #endif
641 
642 #if defined (WANT_WIN32_UNICODE)
643 	if(win32_cmdline_utf8(&argc, &argv) != 0)
644 	{
645 		error("Cannot convert command line to UTF8!");
646 		safe_exit(76);
647 	}
648 #else
649 	argv = sys_argv;
650 	argc = sys_argc;
651 #endif
652 
653 	if(!(fullprogname = compat_strdup(argv[0])))
654 	{
655 		error("OOM"); /* Out Of Memory. Don't waste bytes on that error. */
656 		safe_exit(1);
657 	}
658 	/* Extract binary and path, take stuff before/after last / or \ . */
659 	if(  (cmd_name = strrchr(fullprogname, '/'))
660 	  || (cmd_name = strrchr(fullprogname, '\\')))
661 	{
662 		/* There is some explicit path. */
663 		cmd_name[0] = 0; /* End byte for path. */
664 		cmd_name++;
665 		binpath = fullprogname;
666 	}
667 	else
668 	{
669 		cmd_name = fullprogname; /* No path separators there. */
670 		binpath = NULL; /* No path at all. */
671 	}
672 
673 	/* Get default flags. */
674 	{
675 		out123_handle *paro = out123_new();
676 		out123_getparam_int(paro, OUT123_FLAGS, &outflags);
677 		out123_del(paro);
678 	}
679 
680 #ifdef OS2
681         _wildcard(&argc,&argv);
682 #endif
683 
684 	while ((result = getlopt(argc, argv, opts)))
685 	switch (result) {
686 		case GLO_UNKNOWN:
687 			fprintf (stderr, ME": invalid argument: %s\n", loptarg);
688 			usage(1);
689 		case GLO_NOARG:
690 			fprintf (stderr, ME": missing argument for parameter: %s\n", loptarg);
691 			usage(1);
692 	}
693 
694 	if(quiet)
695 		verbose = 0;
696 
697 	/* Ensure cleanup before we cause too much mess. */
698 #if !defined(WIN32) && !defined(GENERIC)
699 	catchsignal(SIGINT, catch_interrupt);
700 	catchsignal(SIGTERM, catch_interrupt);
701 #endif
702 	ao = out123_new();
703 	if(!ao){ error("Failed to allocate output."); exit(1); }
704 
705 	if
706 	( 0
707 	||	out123_param_int(ao, OUT123_FLAGS, outflags)
708 	|| out123_param_float(ao, OUT123_PRELOAD, preload)
709 	|| out123_param_int(ao, OUT123_GAIN, gain)
710 	|| out123_param_int(ao, OUT123_VERBOSE, verbose)
711 	|| out123_param_string(ao, OUT123_NAME, name)
712 	|| out123_param_string(ao, OUT123_BINDIR, binpath)
713 	|| out123_param_float(ao, OUT123_DEVICEBUFFER, device_buffer)
714 	)
715 	{
716 		error("Error setting output parameters. Do you need a usage reminder?");
717 		usage(1);
718 	}
719 
720 #ifdef HAVE_SETPRIORITY
721 	if(aggressive) { /* tst */
722 		int mypid = getpid();
723 		if(!quiet) fprintf(stderr, ME": Aggressively trying to increase priority.\n");
724 		if(setpriority(PRIO_PROCESS,mypid,-20))
725 			error("Failed to aggressively increase priority.\n");
726 	}
727 #endif
728 
729 #if defined (HAVE_SCHED_SETSCHEDULER) && !defined (__CYGWIN__) && !defined (HAVE_WINDOWS_H)
730 /* Cygwin --realtime seems to fail when accessing network, using win32 set priority instead */
731 /* MinGW may have pthread installed, we prefer win32API */
732 	if(realtime)
733 	{  /* Get real-time priority */
734 		struct sched_param sp;
735 		if(!quiet) fprintf(stderr, ME": Getting real-time priority\n");
736 		memset(&sp, 0, sizeof(struct sched_param));
737 		sp.sched_priority = sched_get_priority_min(SCHED_FIFO);
738 		if (sched_setscheduler(0, SCHED_RR, &sp) == -1)
739 			error("Can't get realtime priority\n");
740 	}
741 #endif
742 
743 /* make sure not Cygwin, it doesn't need it */
744 #if defined(WIN32) && defined(HAVE_WINDOWS_H)
745 	/* argument "3" is equivalent to realtime priority class */
746 	win32_set_priority(realtime ? 3 : w32_priority);
747 #endif
748 
749 	if(encoding_name)
750 	{
751 		encoding = out123_enc_byname(encoding_name);
752 		if(!encoding)
753 		{
754 			error1("Unknown encoding '%s' given!\n", encoding_name);
755 			safe_exit(1);
756 		}
757 	}
758 	pcmframe = out123_encsize(encoding)*channels;
759 	bufferblock = pcmblock*pcmframe;
760 	audio = (unsigned char*) malloc(bufferblock);
761 
762 	check_fatal_output(out123_set_buffer(ao, buffer_kb*1024));
763 	/* This needs bufferblock set! */
764 	check_fatal_output(out123_open(ao, driver, device));
765 
766 	if(verbose)
767 	{
768 		long props = 0;
769 		const char *encname;
770 		char *realname = NULL;
771 		encname = out123_enc_name(encoding);
772 		fprintf(stderr, ME": format: %li Hz, %i channels, %s\n"
773 		,	rate, channels, encname ? encname : "???" );
774 		out123_getparam_string(ao, OUT123_NAME, &realname);
775 		if(realname)
776 			fprintf(stderr, ME": output real name: %s\n", realname);
777 		out123_getparam_int(ao, OUT123_PROPFLAGS, &props);
778 		if(props & OUT123_PROP_LIVE)
779 			fprintf(stderr, ME": This is a live sink.\n");
780 	}
781 	check_fatal_output(out123_start(ao, rate, channels, encoding));
782 
783 	input = stdin;
784 	if(wave_freqs)
785 		setup_wavegen();
786 
787 	while(play_frame() && !intflag)
788 	{
789 		/* be happy */
790 	}
791 	if(intflag) /* Make it quick! */
792 	{
793 		if(!quiet)
794 			fprintf(stderr, ME": Interrupted. Dropping the ball.\n");
795 		out123_drop(ao);
796 	}
797 
798 	safe_exit(0); /* That closes output and restores terminal, too. */
799 	return 0;
800 }
801 
output_enclist(void)802 static char* output_enclist(void)
803 {
804 	int i;
805 	char *list = NULL;
806 	char *pos;
807 	size_t len = 0;
808 	int enc_count = 0;
809 	int *enc_codes = NULL;
810 
811 	enc_count = out123_enc_list(&enc_codes);
812 	for(i=0;i<enc_count;++i)
813 		len += strlen(out123_enc_name(enc_codes[i]));
814 	len += enc_count;
815 
816 	if((pos = list = malloc(len))) for(i=0;i<enc_count;++i)
817 	{
818 		const char *name = out123_enc_name(enc_codes[i]);
819 		if(i>0)
820 			*(pos++) = ' ';
821 		strcpy(pos, name);
822 		pos+=strlen(name);
823 	}
824 	free(enc_codes);
825 	return list;
826 }
827 
print_title(FILE * o)828 static void print_title(FILE *o)
829 {
830 	fprintf(o, "Simple audio output with raw PCM input\n");
831 	fprintf(o, "\tversion %s; derived from mpg123 by Michael Hipp and others\n", PACKAGE_VERSION);
832 	fprintf(o, "\tfree software (LGPL) without any warranty but with best wishes\n");
833 }
834 
usage(int err)835 static void usage(int err)  /* print syntax & exit */
836 {
837 	FILE* o = stdout;
838 	if(err)
839 	{
840 		o = stderr;
841 		fprintf(o, ME": You made some mistake in program usage... let me briefly remind you:\n\n");
842 	}
843 	print_title(o);
844 	fprintf(o,"\nusage: %s [option(s)] [file(s) | URL(s) | -]\n", cmd_name);
845 	fprintf(o,"supported options [defaults in brackets]:\n");
846 	fprintf(o,"   -v    increase verbosity level       -q    quiet (only print errors)\n");
847 	fprintf(o,"   -t    testmode (no output)           -s    write to stdout\n");
848 	fprintf(o,"   -w f  write output as WAV file\n");
849 	fprintf(o,"   -b n  output buffer: n Kbytes [0]                                  \n");
850 	fprintf(o,"   -r n  set samplerate [44100]\n");
851 	fprintf(o,"   -o m  select output module           -a d  set audio device\n");
852 	fprintf(o,"   -m    single-channel (mono) instead of stereo\n");
853 	#ifdef HAVE_SCHED_SETSCHEDULER
854 	fprintf(o,"   -T get realtime priority\n");
855 	#endif
856 	fprintf(o,"   -?    this help                      --version  print name + version\n");
857 	fprintf(o,"See the manpage out123(1) or call %s with --longhelp for more parameters and information.\n", cmd_name);
858 	safe_exit(err);
859 }
860 
want_usage(char * arg)861 static void want_usage(char* arg)
862 {
863 	usage(0);
864 }
865 
long_usage(int err)866 static void long_usage(int err)
867 {
868 	char *enclist;
869 	FILE* o = stdout;
870 	if(err)
871 	{
872   	o = stderr;
873   	fprintf(o, "You made some mistake in program usage... let me remind you:\n\n");
874 	}
875 	enclist = output_enclist();
876 	print_title(o);
877 	fprintf(o,"\nusage: %s [option(s)] [file(s) | URL(s) | -]\n", cmd_name);
878 
879 	fprintf(o,"        --name <n>         set instance name (p.ex. JACK client)\n");
880 	fprintf(o," -o <o> --output <o>       select audio output module\n");
881 	fprintf(o,"        --list-modules     list the available modules\n");
882 	fprintf(o," -a <d> --audiodevice <d>  select audio device (for files, empty or - is stdout)\n");
883 	fprintf(o," -s     --stdout           write raw audio to stdout (-o raw -a -)\n");
884 	fprintf(o," -S     --STDOUT           play AND output stream (not implemented yet)\n");
885 	fprintf(o," -O <f> --output <f>       raw output to given file (-o raw -a <f>)\n");
886 	fprintf(o," -w <f> --wav <f>          write samples as WAV file in <f> (-o wav -a <f>)\n");
887 	fprintf(o,"        --au <f>           write samples as Sun AU file in <f> (-o au -a <f>)\n");
888 	fprintf(o,"        --cdr <f>          write samples as raw CD audio file in <f> (-o cdr -a <f>)\n");
889 	fprintf(o," -r <r> --rate <r>         set the audio output rate in Hz (default 44100)\n");
890 	fprintf(o," -c <n> --channels <n>     set channel count to <n>\n");
891 	fprintf(o," -e <c> --encoding <c>     set output encoding (%s)\n"
892 	,	enclist != NULL ? enclist : "OOM!");
893 	fprintf(o," -m     --mono             set channel count to 1\n");
894 	fprintf(o,"        --stereo           set channel count to 2 (default)\n");
895 	fprintf(o,"        --list-encodings   list of encoding short and long names\n");
896 	fprintf(o,"        --test-format      return 0 if configued audio format is supported\n");
897 	fprintf(o,"        --test-encodings   print out possible encodings with given channels/rate\n");
898 	fprintf(o,"        --query-format     print out default format for given device, if any\n");
899 	fprintf(o," -o h   --headphones       (aix/hp/sun) output on headphones\n");
900 	fprintf(o," -o s   --speaker          (aix/hp/sun) output on speaker\n");
901 	fprintf(o," -o l   --lineout          (aix/hp/sun) output to lineout\n");
902 #ifndef NOXFERMEM
903 	fprintf(o," -b <n> --buffer <n>       set play buffer (\"output cache\")\n");
904 	fprintf(o,"        --preload <value>  fraction of buffer to fill before playback\n");
905 #endif
906 	fprintf(o,"        --devbuffer <s>    set device buffer in seconds; <= 0 means default\n");
907 	fprintf(o,"        --timelimit <s>    set time limit in PCM samples if >= 0\n");
908 	fprintf(o,"        --wave-freq <f>    set wave generator frequency or list of those\n");
909 	fprintf(o,"                           with comma separation for enabling a generated\n");
910 	fprintf(o,"                           test signal instead of standard input,\n");
911 	fprintf(o,"                           empty value repeating the previous\n");
912 	fprintf(o,"        --wave-pat <p>     set wave pattern(s) (out of those:\n");
913 	fprintf(o,"                           %s),\n", wave_pattern_list);
914 	fprintf(o,"                           empty value repeating the previous\n");
915 	fprintf(o,"        --wave-phase <p>   set wave phase shift(s), negative values\n");
916 	fprintf(o,"                           inverting the pattern in time and\n");
917 	fprintf(o,"                           empty value repeating the previous\n");
918 	fprintf(o,"        --wave-limit <l>   soft limit on wave table size\n");
919 	fprintf(o," -t     --test             no output, just read and discard data (-o test)\n");
920 	fprintf(o," -v[*]  --verbose          increase verboselevel\n");
921 	#ifdef HAVE_SETPRIORITY
922 	fprintf(o,"        --aggressive       tries to get higher priority (nice)\n");
923 	#endif
924 	#if defined (HAVE_SCHED_SETSCHEDULER) || defined (HAVE_WINDOWS_H)
925 	fprintf(o," -T     --realtime         tries to get realtime priority\n");
926 	#endif
927 	#ifdef HAVE_WINDOWS_H
928 	fprintf(o,"        --priority <n>     use specified process priority\n");
929 	fprintf(o,"                           accepts -2 to 3 as integer arguments\n");
930 	fprintf(o,"                           -2 as idle, 0 as normal and 3 as realtime.\n");
931 	#endif
932 	fprintf(o," -?     --help             give compact help\n");
933 	fprintf(o,"        --longhelp         give this long help listing\n");
934 	fprintf(o,"        --version          give name / version string\n");
935 
936 	fprintf(o,"\nSee the manpage out123(1) for more information.\n");
937 	free(enclist);
938 	safe_exit(err);
939 }
940 
want_long_usage(char * arg)941 static void want_long_usage(char* arg)
942 {
943 	long_usage(0);
944 }
945 
give_version(char * arg)946 static void give_version(char* arg)
947 {
948 	fprintf(stdout, "out123 "PACKAGE_VERSION"\n");
949 	safe_exit(0);
950 }
951