]> code.delx.au - pulseaudio/blob - src/utils/pacat.c
pacat: Don't use any buffer attr if we don't set any latency/process time params
[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 const pa_buffer_attr *active_buffer_attr = NULL;
413
414 pa_assert(c);
415 pa_assert(!stream);
416
417 if (verbose)
418 pa_log(_("Connection established.%s"), CLEAR_LINE);
419
420 if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) {
421 pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c)));
422 goto fail;
423 }
424
425 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
426 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
427 pa_stream_set_read_callback(stream, stream_read_callback, NULL);
428 pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
429 pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
430 pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
431 pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
432 pa_stream_set_started_callback(stream, stream_started_callback, NULL);
433 pa_stream_set_event_callback(stream, stream_event_callback, NULL);
434 pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL);
435
436 pa_zero(buffer_attr);
437 buffer_attr.maxlength = (uint32_t) -1;
438 buffer_attr.prebuf = (uint32_t) -1;
439
440 if (latency_msec > 0) {
441 buffer_attr.fragsize = buffer_attr.tlength = pa_usec_to_bytes(latency_msec * PA_USEC_PER_MSEC, &sample_spec);
442 active_buffer_attr = &buffer_attr;
443 flags |= PA_STREAM_ADJUST_LATENCY;
444 } else if (latency > 0) {
445 buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) latency;
446 active_buffer_attr = &buffer_attr;
447 flags |= PA_STREAM_ADJUST_LATENCY;
448 } else
449 buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) -1;
450
451 if (process_time_msec > 0) {
452 buffer_attr.minreq = pa_usec_to_bytes(process_time_msec * PA_USEC_PER_MSEC, &sample_spec);
453 active_buffer_attr = &buffer_attr;
454 } else if (process_time > 0) {
455 buffer_attr.minreq = (uint32_t) process_time;
456 active_buffer_attr = &buffer_attr;
457 } else
458 buffer_attr.minreq = (uint32_t) -1;
459
460 if (mode == PLAYBACK) {
461 pa_cvolume cv;
462 if (pa_stream_connect_playback(stream, device, active_buffer_attr, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL) < 0) {
463 pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c)));
464 goto fail;
465 }
466
467 } else {
468 if (pa_stream_connect_record(stream, device, active_buffer_attr, flags) < 0) {
469 pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c)));
470 goto fail;
471 }
472 }
473
474 break;
475 }
476
477 case PA_CONTEXT_TERMINATED:
478 quit(0);
479 break;
480
481 case PA_CONTEXT_FAILED:
482 default:
483 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
484 goto fail;
485 }
486
487 return;
488
489 fail:
490 quit(1);
491
492 }
493
494 /* New data on STDIN **/
495 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
496 size_t l, w = 0;
497 ssize_t r;
498
499 pa_assert(a == mainloop_api);
500 pa_assert(e);
501 pa_assert(stdio_event == e);
502
503 if (buffer) {
504 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
505 return;
506 }
507
508 if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
509 l = 4096;
510
511 buffer = pa_xmalloc(l);
512
513 if ((r = read(fd, buffer, l)) <= 0) {
514 if (r == 0) {
515 if (verbose)
516 pa_log(_("Got EOF."));
517
518 start_drain();
519
520 } else {
521 pa_log(_("read() failed: %s"), strerror(errno));
522 quit(1);
523 }
524
525 mainloop_api->io_free(stdio_event);
526 stdio_event = NULL;
527 return;
528 }
529
530 buffer_length = (uint32_t) r;
531 buffer_index = 0;
532
533 if (w)
534 do_stream_write(w);
535 }
536
537 /* Some data may be written to STDOUT */
538 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
539 ssize_t r;
540
541 pa_assert(a == mainloop_api);
542 pa_assert(e);
543 pa_assert(stdio_event == e);
544
545 if (!buffer) {
546 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
547 return;
548 }
549
550 pa_assert(buffer_length);
551
552 if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
553 pa_log(_("write() failed: %s"), strerror(errno));
554 quit(1);
555
556 mainloop_api->io_free(stdio_event);
557 stdio_event = NULL;
558 return;
559 }
560
561 buffer_length -= (uint32_t) r;
562 buffer_index += (uint32_t) r;
563
564 if (!buffer_length) {
565 pa_xfree(buffer);
566 buffer = NULL;
567 buffer_length = buffer_index = 0;
568 }
569 }
570
571 /* UNIX signal to quit recieved */
572 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
573 if (verbose)
574 pa_log(_("Got signal, exiting."));
575 quit(0);
576 }
577
578 /* Show the current latency */
579 static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
580 pa_usec_t l, usec;
581 int negative = 0;
582
583 pa_assert(s);
584
585 if (!success ||
586 pa_stream_get_time(s, &usec) < 0 ||
587 pa_stream_get_latency(s, &l, &negative) < 0) {
588 pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context)));
589 quit(1);
590 return;
591 }
592
593 fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec."),
594 (float) usec / 1000000,
595 (float) l * (negative?-1.0f:1.0f));
596 fprintf(stderr, " \r");
597 }
598
599 /* Someone requested that the latency is shown */
600 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
601
602 if (!stream)
603 return;
604
605 pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
606 }
607
608 static void time_event_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
609 if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
610 pa_operation *o;
611 if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
612 pa_log(_("pa_stream_update_timing_info() failed: %s"), pa_strerror(pa_context_errno(context)));
613 else
614 pa_operation_unref(o);
615 }
616
617 pa_context_rttime_restart(context, e, pa_rtclock_now() + TIME_EVENT_USEC);
618 }
619
620 static void help(const char *argv0) {
621
622 printf(_("%s [options]\n\n"
623 " -h, --help Show this help\n"
624 " --version Show version\n\n"
625 " -r, --record Create a connection for recording\n"
626 " -p, --playback Create a connection for playback\n\n"
627 " -v, --verbose Enable verbose operations\n\n"
628 " -s, --server=SERVER The name of the server to connect to\n"
629 " -d, --device=DEVICE The name of the sink/source to connect to\n"
630 " -n, --client-name=NAME How to call this client on the server\n"
631 " --stream-name=NAME How to call this stream on the server\n"
632 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
633 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
634 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
635 " float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
636 " s24-32le, s24-32be (defaults to s16ne)\n"
637 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
638 " (defaults to 2)\n"
639 " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
640 " --fix-format Take the sample format from the sink the stream is\n"
641 " being connected to.\n"
642 " --fix-rate Take the sampling rate from the sink the stream is\n"
643 " being connected to.\n"
644 " --fix-channels Take the number of channels and the channel map\n"
645 " from the sink the stream is being connected to.\n"
646 " --no-remix Don't upmix or downmix channels.\n"
647 " --no-remap Map channels by index instead of name.\n"
648 " --latency=BYTES Request the specified latency in bytes.\n"
649 " --process-time=BYTES Request the specified process time per request in bytes.\n"
650 " --latency-msec=MSEC Request the specified latency in msec.\n"
651 " --process-time-msec=MSEC Request the specified process time per request in msec.\n"
652 " --property=PROPERTY=VALUE Set the specified property to the specified value.\n"
653 " --raw Record/play raw PCM data.\n"
654 " --file-format[=FFORMAT] Record/play formatted PCM data.\n"
655 " --list-file-formats List available file formats.\n")
656 , argv0);
657 }
658
659 enum {
660 ARG_VERSION = 256,
661 ARG_STREAM_NAME,
662 ARG_VOLUME,
663 ARG_SAMPLERATE,
664 ARG_SAMPLEFORMAT,
665 ARG_CHANNELS,
666 ARG_CHANNELMAP,
667 ARG_FIX_FORMAT,
668 ARG_FIX_RATE,
669 ARG_FIX_CHANNELS,
670 ARG_NO_REMAP,
671 ARG_NO_REMIX,
672 ARG_LATENCY,
673 ARG_PROCESS_TIME,
674 ARG_RAW,
675 ARG_PROPERTY,
676 ARG_FILE_FORMAT,
677 ARG_LIST_FILE_FORMATS,
678 ARG_LATENCY_MSEC,
679 ARG_PROCESS_TIME_MSEC
680 };
681
682 int main(int argc, char *argv[]) {
683 pa_mainloop* m = NULL;
684 int ret = 1, c;
685 char *bn, *server = NULL;
686 pa_time_event *time_event = NULL;
687 const char *filename = NULL;
688
689 static const struct option long_options[] = {
690 {"record", 0, NULL, 'r'},
691 {"playback", 0, NULL, 'p'},
692 {"device", 1, NULL, 'd'},
693 {"server", 1, NULL, 's'},
694 {"client-name", 1, NULL, 'n'},
695 {"stream-name", 1, NULL, ARG_STREAM_NAME},
696 {"version", 0, NULL, ARG_VERSION},
697 {"help", 0, NULL, 'h'},
698 {"verbose", 0, NULL, 'v'},
699 {"volume", 1, NULL, ARG_VOLUME},
700 {"rate", 1, NULL, ARG_SAMPLERATE},
701 {"format", 1, NULL, ARG_SAMPLEFORMAT},
702 {"channels", 1, NULL, ARG_CHANNELS},
703 {"channel-map", 1, NULL, ARG_CHANNELMAP},
704 {"fix-format", 0, NULL, ARG_FIX_FORMAT},
705 {"fix-rate", 0, NULL, ARG_FIX_RATE},
706 {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
707 {"no-remap", 0, NULL, ARG_NO_REMAP},
708 {"no-remix", 0, NULL, ARG_NO_REMIX},
709 {"latency", 1, NULL, ARG_LATENCY},
710 {"process-time", 1, NULL, ARG_PROCESS_TIME},
711 {"property", 1, NULL, ARG_PROPERTY},
712 {"raw", 0, NULL, ARG_RAW},
713 {"file-format", 2, NULL, ARG_FILE_FORMAT},
714 {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
715 {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
716 {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC},
717 {NULL, 0, NULL, 0}
718 };
719
720 setlocale(LC_ALL, "");
721 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
722
723 bn = pa_path_get_filename(argv[0]);
724
725 if (strstr(bn, "play")) {
726 mode = PLAYBACK;
727 raw = FALSE;
728 } else if (strstr(bn, "record")) {
729 mode = RECORD;
730 raw = FALSE;
731 } else if (strstr(bn, "cat")) {
732 mode = PLAYBACK;
733 raw = TRUE;
734 } if (strstr(bn, "rec") || strstr(bn, "mon")) {
735 mode = RECORD;
736 raw = TRUE;
737 }
738
739 proplist = pa_proplist_new();
740
741 while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
742
743 switch (c) {
744 case 'h' :
745 help(bn);
746 ret = 0;
747 goto quit;
748
749 case ARG_VERSION:
750 printf(_("pacat %s\n"
751 "Compiled with libpulse %s\n"
752 "Linked with libpulse %s\n"),
753 PACKAGE_VERSION,
754 pa_get_headers_version(),
755 pa_get_library_version());
756 ret = 0;
757 goto quit;
758
759 case 'r':
760 mode = RECORD;
761 break;
762
763 case 'p':
764 mode = PLAYBACK;
765 break;
766
767 case 'd':
768 pa_xfree(device);
769 device = pa_xstrdup(optarg);
770 break;
771
772 case 's':
773 pa_xfree(server);
774 server = pa_xstrdup(optarg);
775 break;
776
777 case 'n': {
778 char *t;
779
780 if (!(t = pa_locale_to_utf8(optarg)) ||
781 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
782
783 pa_log(_("Invalid client name '%s'"), t ? t : optarg);
784 pa_xfree(t);
785 goto quit;
786 }
787
788 pa_xfree(t);
789 break;
790 }
791
792 case ARG_STREAM_NAME: {
793 char *t;
794
795 if (!(t = pa_locale_to_utf8(optarg)) ||
796 pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {
797
798 pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
799 pa_xfree(t);
800 goto quit;
801 }
802
803 pa_xfree(t);
804 break;
805 }
806
807 case 'v':
808 verbose = 1;
809 break;
810
811 case ARG_VOLUME: {
812 int v = atoi(optarg);
813 volume = v < 0 ? 0U : (pa_volume_t) v;
814 volume_is_set = TRUE;
815 break;
816 }
817
818 case ARG_CHANNELS:
819 sample_spec.channels = (uint8_t) atoi(optarg);
820 sample_spec_set = TRUE;
821 break;
822
823 case ARG_SAMPLEFORMAT:
824 sample_spec.format = pa_parse_sample_format(optarg);
825 sample_spec_set = TRUE;
826 break;
827
828 case ARG_SAMPLERATE:
829 sample_spec.rate = (uint32_t) atoi(optarg);
830 sample_spec_set = TRUE;
831 break;
832
833 case ARG_CHANNELMAP:
834 if (!pa_channel_map_parse(&channel_map, optarg)) {
835 pa_log(_("Invalid channel map '%s'"), optarg);
836 goto quit;
837 }
838
839 channel_map_set = TRUE;
840 break;
841
842 case ARG_FIX_CHANNELS:
843 flags |= PA_STREAM_FIX_CHANNELS;
844 break;
845
846 case ARG_FIX_RATE:
847 flags |= PA_STREAM_FIX_RATE;
848 break;
849
850 case ARG_FIX_FORMAT:
851 flags |= PA_STREAM_FIX_FORMAT;
852 break;
853
854 case ARG_NO_REMIX:
855 flags |= PA_STREAM_NO_REMIX_CHANNELS;
856 break;
857
858 case ARG_NO_REMAP:
859 flags |= PA_STREAM_NO_REMAP_CHANNELS;
860 break;
861
862 case ARG_LATENCY:
863 if (((latency = (size_t) atoi(optarg))) <= 0) {
864 pa_log(_("Invalid latency specification '%s'"), optarg);
865 goto quit;
866 }
867 break;
868
869 case ARG_PROCESS_TIME:
870 if (((process_time = (size_t) atoi(optarg))) <= 0) {
871 pa_log(_("Invalid process time specification '%s'"), optarg);
872 goto quit;
873 }
874 break;
875
876 case ARG_LATENCY_MSEC:
877 if (((latency_msec = (int32_t) atoi(optarg))) <= 0) {
878 pa_log(_("Invalid latency specification '%s'"), optarg);
879 goto quit;
880 }
881 break;
882
883 case ARG_PROCESS_TIME_MSEC:
884 if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) {
885 pa_log(_("Invalid process time specification '%s'"), optarg);
886 goto quit;
887 }
888 break;
889
890 case ARG_PROPERTY: {
891 char *t;
892
893 if (!(t = pa_locale_to_utf8(optarg)) ||
894 pa_proplist_setp(proplist, t) < 0) {
895
896 pa_xfree(t);
897 pa_log(_("Invalid property '%s'"), optarg);
898 goto quit;
899 }
900
901 pa_xfree(t);
902 break;
903 }
904
905 case ARG_RAW:
906 raw = TRUE;
907 break;
908
909 case ARG_FILE_FORMAT:
910 raw = FALSE;
911
912 if (optarg) {
913 if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) {
914 pa_log(_("Unknown file format %s."), optarg);
915 goto quit;
916 }
917 }
918
919 raw = FALSE;
920 break;
921
922 case ARG_LIST_FILE_FORMATS:
923 pa_sndfile_dump_formats();
924 ret = 0;
925 goto quit;
926
927 default:
928 goto quit;
929 }
930 }
931
932 if (!pa_sample_spec_valid(&sample_spec)) {
933 pa_log(_("Invalid sample specification"));
934 goto quit;
935 }
936
937 if (optind+1 == argc) {
938 int fd;
939
940 filename = argv[optind];
941
942 if ((fd = pa_open_cloexec(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
943 pa_log(_("open(): %s"), strerror(errno));
944 goto quit;
945 }
946
947 if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
948 pa_log(_("dup2(): %s"), strerror(errno));
949 goto quit;
950 }
951
952 pa_close(fd);
953
954 } else if (optind+1 <= argc) {
955 pa_log(_("Too many arguments."));
956 goto quit;
957 }
958
959 if (!raw) {
960 SF_INFO sfi;
961 pa_zero(sfi);
962
963 if (mode == RECORD) {
964 /* This might patch up the sample spec */
965 if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) {
966 pa_log(_("Failed to generate sample specification for file."));
967 goto quit;
968 }
969
970 /* Transparently upgrade classic .wav to wavex for multichannel audio */
971 if (file_format <= 0) {
972 if ((sample_spec.channels == 2 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_LEFT &&
973 channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))) ||
974 (sample_spec.channels == 1 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_MONO))))
975 file_format = SF_FORMAT_WAV;
976 else
977 file_format = SF_FORMAT_WAVEX;
978 }
979
980 sfi.format |= file_format;
981 }
982
983 if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO,
984 mode == RECORD ? SFM_WRITE : SFM_READ,
985 &sfi, 0))) {
986 pa_log(_("Failed to open audio file."));
987 goto quit;
988 }
989
990 if (mode == PLAYBACK) {
991 if (sample_spec_set)
992 pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));
993
994 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
995 pa_log(_("Failed to determine sample specification from file."));
996 goto quit;
997 }
998 sample_spec_set = TRUE;
999
1000 if (!channel_map_set) {
1001 /* Allow the user to overwrite the channel map on the command line */
1002 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1003 if (sample_spec.channels > 2)
1004 pa_log(_("Warning: Failed to determine channel map from file."));
1005 } else
1006 channel_map_set = TRUE;
1007 }
1008 }
1009 }
1010
1011 if (!channel_map_set)
1012 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1013
1014 if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
1015 pa_log(_("Channel map doesn't match sample specification"));
1016 goto quit;
1017 }
1018
1019 if (!raw) {
1020 pa_proplist *sfp;
1021
1022 if (mode == PLAYBACK)
1023 readf_function = pa_sndfile_readf_function(&sample_spec);
1024 else {
1025 if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0)
1026 pa_log(_("Warning: failed to write channel map to file."));
1027
1028 writef_function = pa_sndfile_writef_function(&sample_spec);
1029 }
1030
1031 /* Fill in libsndfile prop list data */
1032 sfp = pa_proplist_new();
1033 pa_sndfile_init_proplist(sndfile, sfp);
1034 pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp);
1035 pa_proplist_free(sfp);
1036 }
1037
1038 if (verbose) {
1039 char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];
1040
1041 pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
1042 mode == RECORD ? _("recording") : _("playback"),
1043 pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
1044 pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
1045 }
1046
1047 /* Fill in client name if none was set */
1048 if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) {
1049 char *t;
1050
1051 if ((t = pa_locale_to_utf8(bn))) {
1052 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t);
1053 pa_xfree(t);
1054 }
1055 }
1056
1057 /* Fill in media name if none was set */
1058 if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
1059 const char *t;
1060
1061 if ((t = filename) ||
1062 (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME)))
1063 pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t);
1064 }
1065
1066 /* Set up a new main loop */
1067 if (!(m = pa_mainloop_new())) {
1068 pa_log(_("pa_mainloop_new() failed."));
1069 goto quit;
1070 }
1071
1072 mainloop_api = pa_mainloop_get_api(m);
1073
1074 pa_assert_se(pa_signal_init(mainloop_api) == 0);
1075 pa_signal_new(SIGINT, exit_signal_callback, NULL);
1076 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1077 #ifdef SIGUSR1
1078 pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
1079 #endif
1080 pa_disable_sigpipe();
1081
1082 if (raw) {
1083 if (!(stdio_event = mainloop_api->io_new(mainloop_api,
1084 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
1085 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
1086 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
1087 pa_log(_("io_new() failed."));
1088 goto quit;
1089 }
1090 }
1091
1092 /* Create a new connection context */
1093 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1094 pa_log(_("pa_context_new() failed."));
1095 goto quit;
1096 }
1097
1098 pa_context_set_state_callback(context, context_state_callback, NULL);
1099
1100 /* Connect the context */
1101 if (pa_context_connect(context, server, 0, NULL) < 0) {
1102 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1103 goto quit;
1104 }
1105
1106 if (verbose) {
1107 if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) {
1108 pa_log(_("pa_context_rttime_new() failed."));
1109 goto quit;
1110 }
1111 }
1112
1113 /* Run the main loop */
1114 if (pa_mainloop_run(m, &ret) < 0) {
1115 pa_log(_("pa_mainloop_run() failed."));
1116 goto quit;
1117 }
1118
1119 quit:
1120 if (stream)
1121 pa_stream_unref(stream);
1122
1123 if (context)
1124 pa_context_unref(context);
1125
1126 if (stdio_event) {
1127 pa_assert(mainloop_api);
1128 mainloop_api->io_free(stdio_event);
1129 }
1130
1131 if (time_event) {
1132 pa_assert(mainloop_api);
1133 mainloop_api->time_free(time_event);
1134 }
1135
1136 if (m) {
1137 pa_signal_done();
1138 pa_mainloop_free(m);
1139 }
1140
1141 pa_xfree(buffer);
1142
1143 pa_xfree(server);
1144 pa_xfree(device);
1145
1146 if (sndfile)
1147 sf_close(sndfile);
1148
1149 if (proplist)
1150 pa_proplist_free(proplist);
1151
1152 return ret;
1153 }