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