]> code.delx.au - pulseaudio/blob - src/utils/pacat.c
pacat: allow configuration of latency in msec
[pulseaudio] / src / utils / pacat.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <signal.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <getopt.h>
35 #include <fcntl.h>
36 #include <locale.h>
37
38 #include <sndfile.h>
39
40 #include <pulse/i18n.h>
41 #include <pulse/pulseaudio.h>
42 #include <pulse/rtclock.h>
43
44 #include <pulsecore/macro.h>
45 #include <pulsecore/core-util.h>
46 #include <pulsecore/log.h>
47 #include <pulsecore/sndfile-util.h>
48 #include <pulsecore/core-util.h>
49
50 #define TIME_EVENT_USEC 50000
51
52 #define CLEAR_LINE "\x1B[K"
53
54 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
55
56 static pa_context *context = NULL;
57 static pa_stream *stream = NULL;
58 static pa_mainloop_api *mainloop_api = NULL;
59
60 static void *buffer = NULL;
61 static size_t buffer_length = 0, buffer_index = 0;
62
63 static pa_io_event* stdio_event = NULL;
64
65 static pa_proplist *proplist = NULL;
66 static char *device = NULL;
67
68 static SNDFILE* sndfile = NULL;
69
70 static pa_bool_t verbose = FALSE;
71 static pa_volume_t volume = PA_VOLUME_NORM;
72 static pa_bool_t volume_is_set = FALSE;
73
74 static pa_sample_spec sample_spec = {
75 .format = PA_SAMPLE_S16LE,
76 .rate = 44100,
77 .channels = 2
78 };
79 static pa_bool_t sample_spec_set = FALSE;
80
81 static pa_channel_map channel_map;
82 static pa_bool_t channel_map_set = FALSE;
83
84 static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL;
85 static sf_count_t (*writef_function)(SNDFILE *_sndfile, const void *ptr, sf_count_t frames) = NULL;
86
87 static pa_stream_flags_t flags = 0;
88
89 static size_t latency = 0, process_time = 0;
90 static int32_t latency_msec = 0, process_time_msec = 0;
91
92 static pa_bool_t raw = TRUE;
93 static int file_format = -1;
94
95 /* A shortcut for terminating the application */
96 static void quit(int ret) {
97 pa_assert(mainloop_api);
98 mainloop_api->quit(mainloop_api, ret);
99 }
100
101 /* Connection draining complete */
102 static void context_drain_complete(pa_context*c, void *userdata) {
103 pa_context_disconnect(c);
104 }
105
106 /* Stream draining complete */
107 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
108
109 if (!success) {
110 pa_log(_("Failed to drain stream: %s"), pa_strerror(pa_context_errno(context)));
111 quit(1);
112 }
113
114 if (verbose)
115 pa_log(_("Playback stream drained."));
116
117 pa_stream_disconnect(stream);
118 pa_stream_unref(stream);
119 stream = NULL;
120
121 if (!pa_context_drain(context, context_drain_complete, NULL))
122 pa_context_disconnect(context);
123 else {
124 if (verbose)
125 pa_log(_("Draining connection to server."));
126 }
127 }
128
129 /* Start draining */
130 static void start_drain(void) {
131
132 if (stream) {
133 pa_operation *o;
134
135 pa_stream_set_write_callback(stream, NULL, NULL);
136
137 if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
138 pa_log(_("pa_stream_drain(): %s"), pa_strerror(pa_context_errno(context)));
139 quit(1);
140 return;
141 }
142
143 pa_operation_unref(o);
144 } else
145 quit(0);
146 }
147
148 /* Write some data to the stream */
149 static void do_stream_write(size_t length) {
150 size_t l;
151 pa_assert(length);
152
153 if (!buffer || !buffer_length)
154 return;
155
156 l = length;
157 if (l > buffer_length)
158 l = buffer_length;
159
160 if (pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
161 pa_log(_("pa_stream_write() failed: %s"), pa_strerror(pa_context_errno(context)));
162 quit(1);
163 return;
164 }
165
166 buffer_length -= l;
167 buffer_index += l;
168
169 if (!buffer_length) {
170 pa_xfree(buffer);
171 buffer = NULL;
172 buffer_index = buffer_length = 0;
173 }
174 }
175
176 /* This is called whenever new data may be written to the stream */
177 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
178 pa_assert(s);
179 pa_assert(length > 0);
180
181 if (raw) {
182 pa_assert(!sndfile);
183
184 if (stdio_event)
185 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
186
187 if (!buffer)
188 return;
189
190 do_stream_write(length);
191
192 } else {
193 sf_count_t bytes;
194 void *data;
195
196 pa_assert(sndfile);
197
198 if (pa_stream_begin_write(s, &data, &length) < 0) {
199 pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context)));
200 quit(1);
201 return;
202 }
203
204 if (readf_function) {
205 size_t k = pa_frame_size(&sample_spec);
206
207 if ((bytes = readf_function(sndfile, data, (sf_count_t) (length/k))) > 0)
208 bytes *= (sf_count_t) k;
209
210 } else
211 bytes = sf_read_raw(sndfile, data, (sf_count_t) length);
212
213 if (bytes > 0)
214 pa_stream_write(s, data, (size_t) bytes, NULL, 0, PA_SEEK_RELATIVE);
215 else
216 pa_stream_cancel_write(s);
217
218 if (bytes < (sf_count_t) length)
219 start_drain();
220 }
221 }
222
223 /* This is called whenever new data may is available */
224 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
225
226 pa_assert(s);
227 pa_assert(length > 0);
228
229 if (raw) {
230 pa_assert(!sndfile);
231
232 if (stdio_event)
233 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
234
235 while (pa_stream_readable_size(s) > 0) {
236 const void *data;
237
238 if (pa_stream_peek(s, &data, &length) < 0) {
239 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
240 quit(1);
241 return;
242 }
243
244 pa_assert(data);
245 pa_assert(length > 0);
246
247 if (buffer) {
248 buffer = pa_xrealloc(buffer, buffer_length + length);
249 memcpy((uint8_t*) buffer + buffer_length, data, length);
250 buffer_length += length;
251 } else {
252 buffer = pa_xmalloc(length);
253 memcpy(buffer, data, length);
254 buffer_length = length;
255 buffer_index = 0;
256 }
257
258 pa_stream_drop(s);
259 }
260
261 } else {
262 pa_assert(sndfile);
263
264 while (pa_stream_readable_size(s) > 0) {
265 sf_count_t bytes;
266 const void *data;
267
268 if (pa_stream_peek(s, &data, &length) < 0) {
269 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
270 quit(1);
271 return;
272 }
273
274 pa_assert(data);
275 pa_assert(length > 0);
276
277 if (writef_function) {
278 size_t k = pa_frame_size(&sample_spec);
279
280 if ((bytes = writef_function(sndfile, data, (sf_count_t) (length/k))) > 0)
281 bytes *= (sf_count_t) k;
282
283 } else
284 bytes = sf_write_raw(sndfile, data, (sf_count_t) length);
285
286 if (bytes < (sf_count_t) length)
287 quit(1);
288
289 pa_stream_drop(s);
290 }
291 }
292 }
293
294 /* This routine is called whenever the stream state changes */
295 static void stream_state_callback(pa_stream *s, void *userdata) {
296 pa_assert(s);
297
298 switch (pa_stream_get_state(s)) {
299 case PA_STREAM_CREATING:
300 case PA_STREAM_TERMINATED:
301 break;
302
303 case PA_STREAM_READY:
304
305 if (verbose) {
306 const pa_buffer_attr *a;
307 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
308
309 pa_log(_("Stream successfully created."));
310
311 if (!(a = pa_stream_get_buffer_attr(s)))
312 pa_log(_("pa_stream_get_buffer_attr() failed: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
313 else {
314
315 if (mode == PLAYBACK)
316 pa_log(_("Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"), a->maxlength, a->tlength, a->prebuf, a->minreq);
317 else {
318 pa_assert(mode == RECORD);
319 pa_log(_("Buffer metrics: maxlength=%u, fragsize=%u"), a->maxlength, a->fragsize);
320 }
321 }
322
323 pa_log(_("Using sample spec '%s', channel map '%s'."),
324 pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
325 pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
326
327 pa_log(_("Connected to device %s (%u, %ssuspended)."),
328 pa_stream_get_device_name(s),
329 pa_stream_get_device_index(s),
330 pa_stream_is_suspended(s) ? "" : "not ");
331 }
332
333 break;
334
335 case PA_STREAM_FAILED:
336 default:
337 pa_log(_("Stream error: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
338 quit(1);
339 }
340 }
341
342 static void stream_suspended_callback(pa_stream *s, void *userdata) {
343 pa_assert(s);
344
345 if (verbose) {
346 if (pa_stream_is_suspended(s))
347 pa_log(_("Stream device suspended.%s"), CLEAR_LINE);
348 else
349 pa_log(_("Stream device resumed.%s"), CLEAR_LINE);
350 }
351 }
352
353 static void stream_underflow_callback(pa_stream *s, void *userdata) {
354 pa_assert(s);
355
356 if (verbose)
357 pa_log(_("Stream underrun.%s"), CLEAR_LINE);
358 }
359
360 static void stream_overflow_callback(pa_stream *s, void *userdata) {
361 pa_assert(s);
362
363 if (verbose)
364 pa_log(_("Stream overrun.%s"), CLEAR_LINE);
365 }
366
367 static void stream_started_callback(pa_stream *s, void *userdata) {
368 pa_assert(s);
369
370 if (verbose)
371 pa_log(_("Stream started.%s"), CLEAR_LINE);
372 }
373
374 static void stream_moved_callback(pa_stream *s, void *userdata) {
375 pa_assert(s);
376
377 if (verbose)
378 pa_log(_("Stream moved to device %s (%u, %ssuspended).%s"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "), CLEAR_LINE);
379 }
380
381 static void stream_buffer_attr_callback(pa_stream *s, void *userdata) {
382 pa_assert(s);
383
384 if (verbose)
385 pa_log(_("Stream buffer attributes changed.%s"), CLEAR_LINE);
386 }
387
388 static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
389 char *t;
390
391 pa_assert(s);
392 pa_assert(name);
393 pa_assert(pl);
394
395 t = pa_proplist_to_string_sep(pl, ", ");
396 pa_log("Got event '%s', properties '%s'", name, t);
397 pa_xfree(t);
398 }
399
400 /* This is called whenever the context status changes */
401 static void context_state_callback(pa_context *c, void *userdata) {
402 pa_assert(c);
403
404 switch (pa_context_get_state(c)) {
405 case PA_CONTEXT_CONNECTING:
406 case PA_CONTEXT_AUTHORIZING:
407 case PA_CONTEXT_SETTING_NAME:
408 break;
409
410 case PA_CONTEXT_READY: {
411 pa_buffer_attr buffer_attr;
412
413 pa_assert(c);
414 pa_assert(!stream);
415
416 if (verbose)
417 pa_log(_("Connection established.%s"), CLEAR_LINE);
418
419 if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) {
420 pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c)));
421 goto fail;
422 }
423
424 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
425 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
426 pa_stream_set_read_callback(stream, stream_read_callback, NULL);
427 pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
428 pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
429 pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
430 pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
431 pa_stream_set_started_callback(stream, stream_started_callback, NULL);
432 pa_stream_set_event_callback(stream, stream_event_callback, NULL);
433 pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL);
434
435 pa_zero(buffer_attr);
436 buffer_attr.maxlength = (uint32_t) -1;
437 buffer_attr.prebuf = (uint32_t) -1;
438
439 if (latency_msec > 0) {
440 buffer_attr.fragsize = buffer_attr.tlength = pa_usec_to_bytes(latency_msec * PA_USEC_PER_MSEC, &sample_spec);
441 flags |= PA_STREAM_ADJUST_LATENCY;
442 } else if (latency > 0) {
443 buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) latency;
444 flags |= PA_STREAM_ADJUST_LATENCY;
445 } else
446 buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) -1;
447
448 if (process_time_msec > 0) {
449 buffer_attr.minreq = pa_usec_to_bytes(process_time_msec * PA_USEC_PER_MSEC, &sample_spec);
450 } else if (process_time > 0)
451 buffer_attr.minreq = (uint32_t) process_time;
452 else
453 buffer_attr.minreq = (uint32_t) -1;
454
455 if (mode == PLAYBACK) {
456 pa_cvolume cv;
457 if (pa_stream_connect_playback(stream, device, &buffer_attr, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL) < 0) {
458 pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c)));
459 goto fail;
460 }
461
462 } else {
463 if (pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags) < 0) {
464 pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c)));
465 goto fail;
466 }
467 }
468
469 break;
470 }
471
472 case PA_CONTEXT_TERMINATED:
473 quit(0);
474 break;
475
476 case PA_CONTEXT_FAILED:
477 default:
478 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
479 goto fail;
480 }
481
482 return;
483
484 fail:
485 quit(1);
486
487 }
488
489 /* New data on STDIN **/
490 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
491 size_t l, w = 0;
492 ssize_t r;
493
494 pa_assert(a == mainloop_api);
495 pa_assert(e);
496 pa_assert(stdio_event == e);
497
498 if (buffer) {
499 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
500 return;
501 }
502
503 if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
504 l = 4096;
505
506 buffer = pa_xmalloc(l);
507
508 if ((r = read(fd, buffer, l)) <= 0) {
509 if (r == 0) {
510 if (verbose)
511 pa_log(_("Got EOF."));
512
513 start_drain();
514
515 } else {
516 pa_log(_("read() failed: %s"), strerror(errno));
517 quit(1);
518 }
519
520 mainloop_api->io_free(stdio_event);
521 stdio_event = NULL;
522 return;
523 }
524
525 buffer_length = (uint32_t) r;
526 buffer_index = 0;
527
528 if (w)
529 do_stream_write(w);
530 }
531
532 /* Some data may be written to STDOUT */
533 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
534 ssize_t r;
535
536 pa_assert(a == mainloop_api);
537 pa_assert(e);
538 pa_assert(stdio_event == e);
539
540 if (!buffer) {
541 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
542 return;
543 }
544
545 pa_assert(buffer_length);
546
547 if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
548 pa_log(_("write() failed: %s"), strerror(errno));
549 quit(1);
550
551 mainloop_api->io_free(stdio_event);
552 stdio_event = NULL;
553 return;
554 }
555
556 buffer_length -= (uint32_t) r;
557 buffer_index += (uint32_t) r;
558
559 if (!buffer_length) {
560 pa_xfree(buffer);
561 buffer = NULL;
562 buffer_length = buffer_index = 0;
563 }
564 }
565
566 /* UNIX signal to quit recieved */
567 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
568 if (verbose)
569 pa_log(_("Got signal, exiting."));
570 quit(0);
571 }
572
573 /* Show the current latency */
574 static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
575 pa_usec_t l, usec;
576 int negative = 0;
577
578 pa_assert(s);
579
580 if (!success ||
581 pa_stream_get_time(s, &usec) < 0 ||
582 pa_stream_get_latency(s, &l, &negative) < 0) {
583 pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context)));
584 quit(1);
585 return;
586 }
587
588 fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec."),
589 (float) usec / 1000000,
590 (float) l * (negative?-1.0f:1.0f));
591 fprintf(stderr, " \r");
592 }
593
594 /* Someone requested that the latency is shown */
595 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
596
597 if (!stream)
598 return;
599
600 pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
601 }
602
603 static void time_event_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
604 if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
605 pa_operation *o;
606 if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
607 pa_log(_("pa_stream_update_timing_info() failed: %s"), pa_strerror(pa_context_errno(context)));
608 else
609 pa_operation_unref(o);
610 }
611
612 pa_context_rttime_restart(context, e, pa_rtclock_now() + TIME_EVENT_USEC);
613 }
614
615 static void help(const char *argv0) {
616
617 printf(_("%s [options]\n\n"
618 " -h, --help Show this help\n"
619 " --version Show version\n\n"
620 " -r, --record Create a connection for recording\n"
621 " -p, --playback Create a connection for playback\n\n"
622 " -v, --verbose Enable verbose operations\n\n"
623 " -s, --server=SERVER The name of the server to connect to\n"
624 " -d, --device=DEVICE The name of the sink/source to connect to\n"
625 " -n, --client-name=NAME How to call this client on the server\n"
626 " --stream-name=NAME How to call this stream on the server\n"
627 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
628 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
629 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
630 " float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
631 " s24-32le, s24-32be (defaults to s16ne)\n"
632 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
633 " (defaults to 2)\n"
634 " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
635 " --fix-format Take the sample format from the sink the stream is\n"
636 " being connected to.\n"
637 " --fix-rate Take the sampling rate from the sink the stream is\n"
638 " being connected to.\n"
639 " --fix-channels Take the number of channels and the channel map\n"
640 " from the sink the stream is being connected to.\n"
641 " --no-remix Don't upmix or downmix channels.\n"
642 " --no-remap Map channels by index instead of name.\n"
643 " --latency=BYTES Request the specified latency in bytes.\n"
644 " --process-time=BYTES Request the specified process time per request in bytes.\n"
645 " --latency-msec=MSEC Request the specified latency in msec.\n"
646 " --process-time-msec=MSEC Request the specified process time per request in msec.\n"
647 " --property=PROPERTY=VALUE Set the specified property to the specified value.\n"
648 " --raw Record/play raw PCM data.\n"
649 " --file-format[=FFORMAT] Record/play formatted PCM data.\n"
650 " --list-file-formats List available file formats.\n")
651 , argv0);
652 }
653
654 enum {
655 ARG_VERSION = 256,
656 ARG_STREAM_NAME,
657 ARG_VOLUME,
658 ARG_SAMPLERATE,
659 ARG_SAMPLEFORMAT,
660 ARG_CHANNELS,
661 ARG_CHANNELMAP,
662 ARG_FIX_FORMAT,
663 ARG_FIX_RATE,
664 ARG_FIX_CHANNELS,
665 ARG_NO_REMAP,
666 ARG_NO_REMIX,
667 ARG_LATENCY,
668 ARG_PROCESS_TIME,
669 ARG_RAW,
670 ARG_PROPERTY,
671 ARG_FILE_FORMAT,
672 ARG_LIST_FILE_FORMATS,
673 ARG_LATENCY_MSEC,
674 ARG_PROCESS_TIME_MSEC
675 };
676
677 int main(int argc, char *argv[]) {
678 pa_mainloop* m = NULL;
679 int ret = 1, c;
680 char *bn, *server = NULL;
681 pa_time_event *time_event = NULL;
682 const char *filename = NULL;
683
684 static const struct option long_options[] = {
685 {"record", 0, NULL, 'r'},
686 {"playback", 0, NULL, 'p'},
687 {"device", 1, NULL, 'd'},
688 {"server", 1, NULL, 's'},
689 {"client-name", 1, NULL, 'n'},
690 {"stream-name", 1, NULL, ARG_STREAM_NAME},
691 {"version", 0, NULL, ARG_VERSION},
692 {"help", 0, NULL, 'h'},
693 {"verbose", 0, NULL, 'v'},
694 {"volume", 1, NULL, ARG_VOLUME},
695 {"rate", 1, NULL, ARG_SAMPLERATE},
696 {"format", 1, NULL, ARG_SAMPLEFORMAT},
697 {"channels", 1, NULL, ARG_CHANNELS},
698 {"channel-map", 1, NULL, ARG_CHANNELMAP},
699 {"fix-format", 0, NULL, ARG_FIX_FORMAT},
700 {"fix-rate", 0, NULL, ARG_FIX_RATE},
701 {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
702 {"no-remap", 0, NULL, ARG_NO_REMAP},
703 {"no-remix", 0, NULL, ARG_NO_REMIX},
704 {"latency", 1, NULL, ARG_LATENCY},
705 {"process-time", 1, NULL, ARG_PROCESS_TIME},
706 {"property", 1, NULL, ARG_PROPERTY},
707 {"raw", 0, NULL, ARG_RAW},
708 {"file-format", 2, NULL, ARG_FILE_FORMAT},
709 {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
710 {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
711 {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC},
712 {NULL, 0, NULL, 0}
713 };
714
715 setlocale(LC_ALL, "");
716 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
717
718 bn = pa_path_get_filename(argv[0]);
719
720 if (strstr(bn, "play")) {
721 mode = PLAYBACK;
722 raw = FALSE;
723 } else if (strstr(bn, "record")) {
724 mode = RECORD;
725 raw = FALSE;
726 } else if (strstr(bn, "cat")) {
727 mode = PLAYBACK;
728 raw = TRUE;
729 } if (strstr(bn, "rec") || strstr(bn, "mon")) {
730 mode = RECORD;
731 raw = TRUE;
732 }
733
734 proplist = pa_proplist_new();
735
736 while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
737
738 switch (c) {
739 case 'h' :
740 help(bn);
741 ret = 0;
742 goto quit;
743
744 case ARG_VERSION:
745 printf(_("pacat %s\n"
746 "Compiled with libpulse %s\n"
747 "Linked with libpulse %s\n"),
748 PACKAGE_VERSION,
749 pa_get_headers_version(),
750 pa_get_library_version());
751 ret = 0;
752 goto quit;
753
754 case 'r':
755 mode = RECORD;
756 break;
757
758 case 'p':
759 mode = PLAYBACK;
760 break;
761
762 case 'd':
763 pa_xfree(device);
764 device = pa_xstrdup(optarg);
765 break;
766
767 case 's':
768 pa_xfree(server);
769 server = pa_xstrdup(optarg);
770 break;
771
772 case 'n': {
773 char *t;
774
775 if (!(t = pa_locale_to_utf8(optarg)) ||
776 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
777
778 pa_log(_("Invalid client name '%s'"), t ? t : optarg);
779 pa_xfree(t);
780 goto quit;
781 }
782
783 pa_xfree(t);
784 break;
785 }
786
787 case ARG_STREAM_NAME: {
788 char *t;
789
790 if (!(t = pa_locale_to_utf8(optarg)) ||
791 pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {
792
793 pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
794 pa_xfree(t);
795 goto quit;
796 }
797
798 pa_xfree(t);
799 break;
800 }
801
802 case 'v':
803 verbose = 1;
804 break;
805
806 case ARG_VOLUME: {
807 int v = atoi(optarg);
808 volume = v < 0 ? 0U : (pa_volume_t) v;
809 volume_is_set = TRUE;
810 break;
811 }
812
813 case ARG_CHANNELS:
814 sample_spec.channels = (uint8_t) atoi(optarg);
815 sample_spec_set = TRUE;
816 break;
817
818 case ARG_SAMPLEFORMAT:
819 sample_spec.format = pa_parse_sample_format(optarg);
820 sample_spec_set = TRUE;
821 break;
822
823 case ARG_SAMPLERATE:
824 sample_spec.rate = (uint32_t) atoi(optarg);
825 sample_spec_set = TRUE;
826 break;
827
828 case ARG_CHANNELMAP:
829 if (!pa_channel_map_parse(&channel_map, optarg)) {
830 pa_log(_("Invalid channel map '%s'"), optarg);
831 goto quit;
832 }
833
834 channel_map_set = TRUE;
835 break;
836
837 case ARG_FIX_CHANNELS:
838 flags |= PA_STREAM_FIX_CHANNELS;
839 break;
840
841 case ARG_FIX_RATE:
842 flags |= PA_STREAM_FIX_RATE;
843 break;
844
845 case ARG_FIX_FORMAT:
846 flags |= PA_STREAM_FIX_FORMAT;
847 break;
848
849 case ARG_NO_REMIX:
850 flags |= PA_STREAM_NO_REMIX_CHANNELS;
851 break;
852
853 case ARG_NO_REMAP:
854 flags |= PA_STREAM_NO_REMAP_CHANNELS;
855 break;
856
857 case ARG_LATENCY:
858 if (((latency = (size_t) atoi(optarg))) <= 0) {
859 pa_log(_("Invalid latency specification '%s'"), optarg);
860 goto quit;
861 }
862 break;
863
864 case ARG_PROCESS_TIME:
865 if (((process_time = (size_t) atoi(optarg))) <= 0) {
866 pa_log(_("Invalid process time specification '%s'"), optarg);
867 goto quit;
868 }
869 break;
870
871 case ARG_LATENCY_MSEC:
872 if (((latency_msec = (int32_t) atoi(optarg))) <= 0) {
873 pa_log(_("Invalid latency specification '%s'"), optarg);
874 goto quit;
875 }
876 break;
877
878 case ARG_PROCESS_TIME_MSEC:
879 if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) {
880 pa_log(_("Invalid process time specification '%s'"), optarg);
881 goto quit;
882 }
883 break;
884
885 case ARG_PROPERTY: {
886 char *t;
887
888 if (!(t = pa_locale_to_utf8(optarg)) ||
889 pa_proplist_setp(proplist, t) < 0) {
890
891 pa_xfree(t);
892 pa_log(_("Invalid property '%s'"), optarg);
893 goto quit;
894 }
895
896 pa_xfree(t);
897 break;
898 }
899
900 case ARG_RAW:
901 raw = TRUE;
902 break;
903
904 case ARG_FILE_FORMAT:
905 raw = FALSE;
906
907 if (optarg) {
908 if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) {
909 pa_log(_("Unknown file format %s."), optarg);
910 goto quit;
911 }
912 }
913
914 raw = FALSE;
915 break;
916
917 case ARG_LIST_FILE_FORMATS:
918 pa_sndfile_dump_formats();
919 ret = 0;
920 goto quit;
921
922 default:
923 goto quit;
924 }
925 }
926
927 if (!pa_sample_spec_valid(&sample_spec)) {
928 pa_log(_("Invalid sample specification"));
929 goto quit;
930 }
931
932 if (optind+1 == argc) {
933 int fd;
934
935 filename = argv[optind];
936
937 if ((fd = pa_open_cloexec(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
938 pa_log(_("open(): %s"), strerror(errno));
939 goto quit;
940 }
941
942 if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
943 pa_log(_("dup2(): %s"), strerror(errno));
944 goto quit;
945 }
946
947 pa_close(fd);
948
949 } else if (optind+1 <= argc) {
950 pa_log(_("Too many arguments."));
951 goto quit;
952 }
953
954 if (!raw) {
955 SF_INFO sfi;
956 pa_zero(sfi);
957
958 if (mode == RECORD) {
959 /* This might patch up the sample spec */
960 if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) {
961 pa_log(_("Failed to generate sample specification for file."));
962 goto quit;
963 }
964
965 /* Transparently upgrade classic .wav to wavex for multichannel audio */
966 if (file_format <= 0) {
967 if ((sample_spec.channels == 2 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_LEFT &&
968 channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))) ||
969 (sample_spec.channels == 1 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_MONO))))
970 file_format = SF_FORMAT_WAV;
971 else
972 file_format = SF_FORMAT_WAVEX;
973 }
974
975 sfi.format |= file_format;
976 }
977
978 if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO,
979 mode == RECORD ? SFM_WRITE : SFM_READ,
980 &sfi, 0))) {
981 pa_log(_("Failed to open audio file."));
982 goto quit;
983 }
984
985 if (mode == PLAYBACK) {
986 if (sample_spec_set)
987 pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));
988
989 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
990 pa_log(_("Failed to determine sample specification from file."));
991 goto quit;
992 }
993 sample_spec_set = TRUE;
994
995 if (!channel_map_set) {
996 /* Allow the user to overwrite the channel map on the command line */
997 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
998 if (sample_spec.channels > 2)
999 pa_log(_("Warning: Failed to determine channel map from file."));
1000 } else
1001 channel_map_set = TRUE;
1002 }
1003 }
1004 }
1005
1006 if (!channel_map_set)
1007 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1008
1009 if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
1010 pa_log(_("Channel map doesn't match sample specification"));
1011 goto quit;
1012 }
1013
1014 if (!raw) {
1015 pa_proplist *sfp;
1016
1017 if (mode == PLAYBACK)
1018 readf_function = pa_sndfile_readf_function(&sample_spec);
1019 else {
1020 if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0)
1021 pa_log(_("Warning: failed to write channel map to file."));
1022
1023 writef_function = pa_sndfile_writef_function(&sample_spec);
1024 }
1025
1026 /* Fill in libsndfile prop list data */
1027 sfp = pa_proplist_new();
1028 pa_sndfile_init_proplist(sndfile, sfp);
1029 pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp);
1030 pa_proplist_free(sfp);
1031 }
1032
1033 if (verbose) {
1034 char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];
1035
1036 pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
1037 mode == RECORD ? _("recording") : _("playback"),
1038 pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
1039 pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
1040 }
1041
1042 /* Fill in client name if none was set */
1043 if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) {
1044 char *t;
1045
1046 if ((t = pa_locale_to_utf8(bn))) {
1047 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t);
1048 pa_xfree(t);
1049 }
1050 }
1051
1052 /* Fill in media name if none was set */
1053 if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
1054 const char *t;
1055
1056 if ((t = filename) ||
1057 (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME)))
1058 pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t);
1059 }
1060
1061 /* Set up a new main loop */
1062 if (!(m = pa_mainloop_new())) {
1063 pa_log(_("pa_mainloop_new() failed."));
1064 goto quit;
1065 }
1066
1067 mainloop_api = pa_mainloop_get_api(m);
1068
1069 pa_assert_se(pa_signal_init(mainloop_api) == 0);
1070 pa_signal_new(SIGINT, exit_signal_callback, NULL);
1071 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1072 #ifdef SIGUSR1
1073 pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
1074 #endif
1075 pa_disable_sigpipe();
1076
1077 if (raw) {
1078 if (!(stdio_event = mainloop_api->io_new(mainloop_api,
1079 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
1080 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
1081 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
1082 pa_log(_("io_new() failed."));
1083 goto quit;
1084 }
1085 }
1086
1087 /* Create a new connection context */
1088 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1089 pa_log(_("pa_context_new() failed."));
1090 goto quit;
1091 }
1092
1093 pa_context_set_state_callback(context, context_state_callback, NULL);
1094
1095 /* Connect the context */
1096 if (pa_context_connect(context, server, 0, NULL) < 0) {
1097 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1098 goto quit;
1099 }
1100
1101 if (verbose) {
1102 if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) {
1103 pa_log(_("pa_context_rttime_new() failed."));
1104 goto quit;
1105 }
1106 }
1107
1108 /* Run the main loop */
1109 if (pa_mainloop_run(m, &ret) < 0) {
1110 pa_log(_("pa_mainloop_run() failed."));
1111 goto quit;
1112 }
1113
1114 quit:
1115 if (stream)
1116 pa_stream_unref(stream);
1117
1118 if (context)
1119 pa_context_unref(context);
1120
1121 if (stdio_event) {
1122 pa_assert(mainloop_api);
1123 mainloop_api->io_free(stdio_event);
1124 }
1125
1126 if (time_event) {
1127 pa_assert(mainloop_api);
1128 mainloop_api->time_free(time_event);
1129 }
1130
1131 if (m) {
1132 pa_signal_done();
1133 pa_mainloop_free(m);
1134 }
1135
1136 pa_xfree(buffer);
1137
1138 pa_xfree(server);
1139 pa_xfree(device);
1140
1141 if (sndfile)
1142 sf_close(sndfile);
1143
1144 if (proplist)
1145 pa_proplist_free(proplist);
1146
1147 return ret;
1148 }