l10n: Updates to Russian (ru) translation
[pulseaudio-mirror.git] / src / utils / pacat.c
blob6cbff2e6fa7927f111417d89ceec7b16980fe933
1 /***
2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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.
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.
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 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
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>
38 #include <sndfile.h>
40 #include <pulse/i18n.h>
41 #include <pulse/pulseaudio.h>
42 #include <pulse/rtclock.h>
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>
50 #define TIME_EVENT_USEC 50000
52 #define CLEAR_LINE "\x1B[K"
54 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
56 static pa_context *context = NULL;
57 static pa_stream *stream = NULL;
58 static pa_mainloop_api *mainloop_api = NULL;
60 static void *buffer = NULL;
61 static size_t buffer_length = 0, buffer_index = 0;
63 static pa_io_event* stdio_event = NULL;
65 static pa_proplist *proplist = NULL;
66 static char *device = NULL;
68 static SNDFILE* sndfile = NULL;
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;
74 static pa_sample_spec sample_spec = {
75 .format = PA_SAMPLE_S16LE,
76 .rate = 44100,
77 .channels = 2
79 static pa_bool_t sample_spec_set = FALSE;
81 static pa_channel_map channel_map;
82 static pa_bool_t channel_map_set = FALSE;
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;
87 static pa_stream_flags_t flags = 0;
89 static size_t latency = 0, process_time = 0;
90 static int32_t latency_msec = 0, process_time_msec = 0;
92 static pa_bool_t raw = TRUE;
93 static int file_format = -1;
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);
101 /* Connection draining complete */
102 static void context_drain_complete(pa_context*c, void *userdata) {
103 pa_context_disconnect(c);
106 /* Stream draining complete */
107 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
109 if (!success) {
110 pa_log(_("Failed to drain stream: %s"), pa_strerror(pa_context_errno(context)));
111 quit(1);
114 if (verbose)
115 pa_log(_("Playback stream drained."));
117 pa_stream_disconnect(stream);
118 pa_stream_unref(stream);
119 stream = NULL;
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."));
129 /* Start draining */
130 static void start_drain(void) {
132 if (stream) {
133 pa_operation *o;
135 pa_stream_set_write_callback(stream, NULL, NULL);
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;
143 pa_operation_unref(o);
144 } else
145 quit(0);
148 /* Write some data to the stream */
149 static void do_stream_write(size_t length) {
150 size_t l;
151 pa_assert(length);
153 if (!buffer || !buffer_length)
154 return;
156 l = length;
157 if (l > buffer_length)
158 l = buffer_length;
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;
166 buffer_length -= l;
167 buffer_index += l;
169 if (!buffer_length) {
170 pa_xfree(buffer);
171 buffer = NULL;
172 buffer_index = buffer_length = 0;
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);
181 if (raw) {
182 pa_assert(!sndfile);
184 if (stdio_event)
185 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
187 if (!buffer)
188 return;
190 do_stream_write(length);
192 } else {
193 sf_count_t bytes;
194 void *data;
196 pa_assert(sndfile);
198 for (;;) {
199 size_t data_length = length;
201 if (pa_stream_begin_write(s, &data, &data_length) < 0) {
202 pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context)));
203 quit(1);
204 return;
207 if (readf_function) {
208 size_t k = pa_frame_size(&sample_spec);
210 if ((bytes = readf_function(sndfile, data, (sf_count_t) (data_length/k))) > 0)
211 bytes *= (sf_count_t) k;
213 } else
214 bytes = sf_read_raw(sndfile, data, (sf_count_t) data_length);
216 if (bytes > 0)
217 pa_stream_write(s, data, (size_t) bytes, NULL, 0, PA_SEEK_RELATIVE);
218 else
219 pa_stream_cancel_write(s);
221 /* EOF? */
222 if (bytes < (sf_count_t) data_length) {
223 start_drain();
224 break;
227 /* Request fulfilled */
228 if ((size_t) bytes >= length)
229 break;
231 length -= bytes;
236 /* This is called whenever new data may is available */
237 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
239 pa_assert(s);
240 pa_assert(length > 0);
242 if (raw) {
243 pa_assert(!sndfile);
245 if (stdio_event)
246 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
248 while (pa_stream_readable_size(s) > 0) {
249 const void *data;
251 if (pa_stream_peek(s, &data, &length) < 0) {
252 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
253 quit(1);
254 return;
257 pa_assert(data);
258 pa_assert(length > 0);
260 if (buffer) {
261 buffer = pa_xrealloc(buffer, buffer_length + length);
262 memcpy((uint8_t*) buffer + buffer_length, data, length);
263 buffer_length += length;
264 } else {
265 buffer = pa_xmalloc(length);
266 memcpy(buffer, data, length);
267 buffer_length = length;
268 buffer_index = 0;
271 pa_stream_drop(s);
274 } else {
275 pa_assert(sndfile);
277 while (pa_stream_readable_size(s) > 0) {
278 sf_count_t bytes;
279 const void *data;
281 if (pa_stream_peek(s, &data, &length) < 0) {
282 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context)));
283 quit(1);
284 return;
287 pa_assert(data);
288 pa_assert(length > 0);
290 if (writef_function) {
291 size_t k = pa_frame_size(&sample_spec);
293 if ((bytes = writef_function(sndfile, data, (sf_count_t) (length/k))) > 0)
294 bytes *= (sf_count_t) k;
296 } else
297 bytes = sf_write_raw(sndfile, data, (sf_count_t) length);
299 if (bytes < (sf_count_t) length)
300 quit(1);
302 pa_stream_drop(s);
307 /* This routine is called whenever the stream state changes */
308 static void stream_state_callback(pa_stream *s, void *userdata) {
309 pa_assert(s);
311 switch (pa_stream_get_state(s)) {
312 case PA_STREAM_CREATING:
313 case PA_STREAM_TERMINATED:
314 break;
316 case PA_STREAM_READY:
318 if (verbose) {
319 const pa_buffer_attr *a;
320 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
322 pa_log(_("Stream successfully created."));
324 if (!(a = pa_stream_get_buffer_attr(s)))
325 pa_log(_("pa_stream_get_buffer_attr() failed: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
326 else {
328 if (mode == PLAYBACK)
329 pa_log(_("Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"), a->maxlength, a->tlength, a->prebuf, a->minreq);
330 else {
331 pa_assert(mode == RECORD);
332 pa_log(_("Buffer metrics: maxlength=%u, fragsize=%u"), a->maxlength, a->fragsize);
336 pa_log(_("Using sample spec '%s', channel map '%s'."),
337 pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
338 pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
340 pa_log(_("Connected to device %s (%u, %ssuspended)."),
341 pa_stream_get_device_name(s),
342 pa_stream_get_device_index(s),
343 pa_stream_is_suspended(s) ? "" : "not ");
346 break;
348 case PA_STREAM_FAILED:
349 default:
350 pa_log(_("Stream error: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
351 quit(1);
355 static void stream_suspended_callback(pa_stream *s, void *userdata) {
356 pa_assert(s);
358 if (verbose) {
359 if (pa_stream_is_suspended(s))
360 pa_log(_("Stream device suspended.%s"), CLEAR_LINE);
361 else
362 pa_log(_("Stream device resumed.%s"), CLEAR_LINE);
366 static void stream_underflow_callback(pa_stream *s, void *userdata) {
367 pa_assert(s);
369 if (verbose)
370 pa_log(_("Stream underrun.%s"), CLEAR_LINE);
373 static void stream_overflow_callback(pa_stream *s, void *userdata) {
374 pa_assert(s);
376 if (verbose)
377 pa_log(_("Stream overrun.%s"), CLEAR_LINE);
380 static void stream_started_callback(pa_stream *s, void *userdata) {
381 pa_assert(s);
383 if (verbose)
384 pa_log(_("Stream started.%s"), CLEAR_LINE);
387 static void stream_moved_callback(pa_stream *s, void *userdata) {
388 pa_assert(s);
390 if (verbose)
391 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);
394 static void stream_buffer_attr_callback(pa_stream *s, void *userdata) {
395 pa_assert(s);
397 if (verbose)
398 pa_log(_("Stream buffer attributes changed.%s"), CLEAR_LINE);
401 static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
402 char *t;
404 pa_assert(s);
405 pa_assert(name);
406 pa_assert(pl);
408 t = pa_proplist_to_string_sep(pl, ", ");
409 pa_log("Got event '%s', properties '%s'", name, t);
410 pa_xfree(t);
413 /* This is called whenever the context status changes */
414 static void context_state_callback(pa_context *c, void *userdata) {
415 pa_assert(c);
417 switch (pa_context_get_state(c)) {
418 case PA_CONTEXT_CONNECTING:
419 case PA_CONTEXT_AUTHORIZING:
420 case PA_CONTEXT_SETTING_NAME:
421 break;
423 case PA_CONTEXT_READY: {
424 pa_buffer_attr buffer_attr;
426 pa_assert(c);
427 pa_assert(!stream);
429 if (verbose)
430 pa_log(_("Connection established.%s"), CLEAR_LINE);
432 if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) {
433 pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c)));
434 goto fail;
437 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
438 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
439 pa_stream_set_read_callback(stream, stream_read_callback, NULL);
440 pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
441 pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
442 pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
443 pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
444 pa_stream_set_started_callback(stream, stream_started_callback, NULL);
445 pa_stream_set_event_callback(stream, stream_event_callback, NULL);
446 pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL);
448 pa_zero(buffer_attr);
449 buffer_attr.maxlength = (uint32_t) -1;
450 buffer_attr.prebuf = (uint32_t) -1;
452 if (latency_msec > 0) {
453 buffer_attr.fragsize = buffer_attr.tlength = pa_usec_to_bytes(latency_msec * PA_USEC_PER_MSEC, &sample_spec);
454 flags |= PA_STREAM_ADJUST_LATENCY;
455 } else if (latency > 0) {
456 buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) latency;
457 flags |= PA_STREAM_ADJUST_LATENCY;
458 } else
459 buffer_attr.fragsize = buffer_attr.tlength = (uint32_t) -1;
461 if (process_time_msec > 0) {
462 buffer_attr.minreq = pa_usec_to_bytes(process_time_msec * PA_USEC_PER_MSEC, &sample_spec);
463 } else if (process_time > 0)
464 buffer_attr.minreq = (uint32_t) process_time;
465 else
466 buffer_attr.minreq = (uint32_t) -1;
468 if (mode == PLAYBACK) {
469 pa_cvolume cv;
470 if (pa_stream_connect_playback(stream, device, &buffer_attr, flags, volume_is_set ? pa_cvolume_set(&cv, sample_spec.channels, volume) : NULL, NULL) < 0) {
471 pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c)));
472 goto fail;
475 } else {
476 if (pa_stream_connect_record(stream, device, &buffer_attr, flags) < 0) {
477 pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c)));
478 goto fail;
482 break;
485 case PA_CONTEXT_TERMINATED:
486 quit(0);
487 break;
489 case PA_CONTEXT_FAILED:
490 default:
491 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
492 goto fail;
495 return;
497 fail:
498 quit(1);
502 /* New data on STDIN **/
503 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
504 size_t l, w = 0;
505 ssize_t r;
507 pa_assert(a == mainloop_api);
508 pa_assert(e);
509 pa_assert(stdio_event == e);
511 if (buffer) {
512 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
513 return;
516 if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
517 l = 4096;
519 buffer = pa_xmalloc(l);
521 if ((r = read(fd, buffer, l)) <= 0) {
522 if (r == 0) {
523 if (verbose)
524 pa_log(_("Got EOF."));
526 start_drain();
528 } else {
529 pa_log(_("read() failed: %s"), strerror(errno));
530 quit(1);
533 mainloop_api->io_free(stdio_event);
534 stdio_event = NULL;
535 return;
538 buffer_length = (uint32_t) r;
539 buffer_index = 0;
541 if (w)
542 do_stream_write(w);
545 /* Some data may be written to STDOUT */
546 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
547 ssize_t r;
549 pa_assert(a == mainloop_api);
550 pa_assert(e);
551 pa_assert(stdio_event == e);
553 if (!buffer) {
554 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
555 return;
558 pa_assert(buffer_length);
560 if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
561 pa_log(_("write() failed: %s"), strerror(errno));
562 quit(1);
564 mainloop_api->io_free(stdio_event);
565 stdio_event = NULL;
566 return;
569 buffer_length -= (uint32_t) r;
570 buffer_index += (uint32_t) r;
572 if (!buffer_length) {
573 pa_xfree(buffer);
574 buffer = NULL;
575 buffer_length = buffer_index = 0;
579 /* UNIX signal to quit recieved */
580 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
581 if (verbose)
582 pa_log(_("Got signal, exiting."));
583 quit(0);
586 /* Show the current latency */
587 static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
588 pa_usec_t l, usec;
589 int negative = 0;
591 pa_assert(s);
593 if (!success ||
594 pa_stream_get_time(s, &usec) < 0 ||
595 pa_stream_get_latency(s, &l, &negative) < 0) {
596 pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context)));
597 quit(1);
598 return;
601 fprintf(stderr, _("Time: %0.3f sec; Latency: %0.0f usec."),
602 (float) usec / 1000000,
603 (float) l * (negative?-1.0f:1.0f));
604 fprintf(stderr, " \r");
607 /* Someone requested that the latency is shown */
608 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
610 if (!stream)
611 return;
613 pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
616 static void time_event_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
617 if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
618 pa_operation *o;
619 if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
620 pa_log(_("pa_stream_update_timing_info() failed: %s"), pa_strerror(pa_context_errno(context)));
621 else
622 pa_operation_unref(o);
625 pa_context_rttime_restart(context, e, pa_rtclock_now() + TIME_EVENT_USEC);
628 static void help(const char *argv0) {
630 printf(_("%s [options]\n\n"
631 " -h, --help Show this help\n"
632 " --version Show version\n\n"
633 " -r, --record Create a connection for recording\n"
634 " -p, --playback Create a connection for playback\n\n"
635 " -v, --verbose Enable verbose operations\n\n"
636 " -s, --server=SERVER The name of the server to connect to\n"
637 " -d, --device=DEVICE The name of the sink/source to connect to\n"
638 " -n, --client-name=NAME How to call this client on the server\n"
639 " --stream-name=NAME How to call this stream on the server\n"
640 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
641 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
642 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
643 " float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
644 " s24-32le, s24-32be (defaults to s16ne)\n"
645 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
646 " (defaults to 2)\n"
647 " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
648 " --fix-format Take the sample format from the sink the stream is\n"
649 " being connected to.\n"
650 " --fix-rate Take the sampling rate from the sink the stream is\n"
651 " being connected to.\n"
652 " --fix-channels Take the number of channels and the channel map\n"
653 " from the sink the stream is being connected to.\n"
654 " --no-remix Don't upmix or downmix channels.\n"
655 " --no-remap Map channels by index instead of name.\n"
656 " --latency=BYTES Request the specified latency in bytes.\n"
657 " --process-time=BYTES Request the specified process time per request in bytes.\n"
658 " --latency-msec=MSEC Request the specified latency in msec.\n"
659 " --process-time-msec=MSEC Request the specified process time per request in msec.\n"
660 " --property=PROPERTY=VALUE Set the specified property to the specified value.\n"
661 " --raw Record/play raw PCM data.\n"
662 " --file-format[=FFORMAT] Record/play formatted PCM data.\n"
663 " --list-file-formats List available file formats.\n")
664 , argv0);
667 enum {
668 ARG_VERSION = 256,
669 ARG_STREAM_NAME,
670 ARG_VOLUME,
671 ARG_SAMPLERATE,
672 ARG_SAMPLEFORMAT,
673 ARG_CHANNELS,
674 ARG_CHANNELMAP,
675 ARG_FIX_FORMAT,
676 ARG_FIX_RATE,
677 ARG_FIX_CHANNELS,
678 ARG_NO_REMAP,
679 ARG_NO_REMIX,
680 ARG_LATENCY,
681 ARG_PROCESS_TIME,
682 ARG_RAW,
683 ARG_PROPERTY,
684 ARG_FILE_FORMAT,
685 ARG_LIST_FILE_FORMATS,
686 ARG_LATENCY_MSEC,
687 ARG_PROCESS_TIME_MSEC
690 int main(int argc, char *argv[]) {
691 pa_mainloop* m = NULL;
692 int ret = 1, c;
693 char *bn, *server = NULL;
694 pa_time_event *time_event = NULL;
695 const char *filename = NULL;
697 static const struct option long_options[] = {
698 {"record", 0, NULL, 'r'},
699 {"playback", 0, NULL, 'p'},
700 {"device", 1, NULL, 'd'},
701 {"server", 1, NULL, 's'},
702 {"client-name", 1, NULL, 'n'},
703 {"stream-name", 1, NULL, ARG_STREAM_NAME},
704 {"version", 0, NULL, ARG_VERSION},
705 {"help", 0, NULL, 'h'},
706 {"verbose", 0, NULL, 'v'},
707 {"volume", 1, NULL, ARG_VOLUME},
708 {"rate", 1, NULL, ARG_SAMPLERATE},
709 {"format", 1, NULL, ARG_SAMPLEFORMAT},
710 {"channels", 1, NULL, ARG_CHANNELS},
711 {"channel-map", 1, NULL, ARG_CHANNELMAP},
712 {"fix-format", 0, NULL, ARG_FIX_FORMAT},
713 {"fix-rate", 0, NULL, ARG_FIX_RATE},
714 {"fix-channels", 0, NULL, ARG_FIX_CHANNELS},
715 {"no-remap", 0, NULL, ARG_NO_REMAP},
716 {"no-remix", 0, NULL, ARG_NO_REMIX},
717 {"latency", 1, NULL, ARG_LATENCY},
718 {"process-time", 1, NULL, ARG_PROCESS_TIME},
719 {"property", 1, NULL, ARG_PROPERTY},
720 {"raw", 0, NULL, ARG_RAW},
721 {"file-format", 2, NULL, ARG_FILE_FORMAT},
722 {"list-file-formats", 0, NULL, ARG_LIST_FILE_FORMATS},
723 {"latency-msec", 1, NULL, ARG_LATENCY_MSEC},
724 {"process-time-msec", 1, NULL, ARG_PROCESS_TIME_MSEC},
725 {NULL, 0, NULL, 0}
728 setlocale(LC_ALL, "");
729 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
731 bn = pa_path_get_filename(argv[0]);
733 if (strstr(bn, "play")) {
734 mode = PLAYBACK;
735 raw = FALSE;
736 } else if (strstr(bn, "record")) {
737 mode = RECORD;
738 raw = FALSE;
739 } else if (strstr(bn, "cat")) {
740 mode = PLAYBACK;
741 raw = TRUE;
742 } if (strstr(bn, "rec") || strstr(bn, "mon")) {
743 mode = RECORD;
744 raw = TRUE;
747 proplist = pa_proplist_new();
749 while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
751 switch (c) {
752 case 'h' :
753 help(bn);
754 ret = 0;
755 goto quit;
757 case ARG_VERSION:
758 printf(_("pacat %s\n"
759 "Compiled with libpulse %s\n"
760 "Linked with libpulse %s\n"),
761 PACKAGE_VERSION,
762 pa_get_headers_version(),
763 pa_get_library_version());
764 ret = 0;
765 goto quit;
767 case 'r':
768 mode = RECORD;
769 break;
771 case 'p':
772 mode = PLAYBACK;
773 break;
775 case 'd':
776 pa_xfree(device);
777 device = pa_xstrdup(optarg);
778 break;
780 case 's':
781 pa_xfree(server);
782 server = pa_xstrdup(optarg);
783 break;
785 case 'n': {
786 char *t;
788 if (!(t = pa_locale_to_utf8(optarg)) ||
789 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
791 pa_log(_("Invalid client name '%s'"), t ? t : optarg);
792 pa_xfree(t);
793 goto quit;
796 pa_xfree(t);
797 break;
800 case ARG_STREAM_NAME: {
801 char *t;
803 if (!(t = pa_locale_to_utf8(optarg)) ||
804 pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t) < 0) {
806 pa_log(_("Invalid stream name '%s'"), t ? t : optarg);
807 pa_xfree(t);
808 goto quit;
811 pa_xfree(t);
812 break;
815 case 'v':
816 verbose = 1;
817 break;
819 case ARG_VOLUME: {
820 int v = atoi(optarg);
821 volume = v < 0 ? 0U : (pa_volume_t) v;
822 volume_is_set = TRUE;
823 break;
826 case ARG_CHANNELS:
827 sample_spec.channels = (uint8_t) atoi(optarg);
828 sample_spec_set = TRUE;
829 break;
831 case ARG_SAMPLEFORMAT:
832 sample_spec.format = pa_parse_sample_format(optarg);
833 sample_spec_set = TRUE;
834 break;
836 case ARG_SAMPLERATE:
837 sample_spec.rate = (uint32_t) atoi(optarg);
838 sample_spec_set = TRUE;
839 break;
841 case ARG_CHANNELMAP:
842 if (!pa_channel_map_parse(&channel_map, optarg)) {
843 pa_log(_("Invalid channel map '%s'"), optarg);
844 goto quit;
847 channel_map_set = TRUE;
848 break;
850 case ARG_FIX_CHANNELS:
851 flags |= PA_STREAM_FIX_CHANNELS;
852 break;
854 case ARG_FIX_RATE:
855 flags |= PA_STREAM_FIX_RATE;
856 break;
858 case ARG_FIX_FORMAT:
859 flags |= PA_STREAM_FIX_FORMAT;
860 break;
862 case ARG_NO_REMIX:
863 flags |= PA_STREAM_NO_REMIX_CHANNELS;
864 break;
866 case ARG_NO_REMAP:
867 flags |= PA_STREAM_NO_REMAP_CHANNELS;
868 break;
870 case ARG_LATENCY:
871 if (((latency = (size_t) atoi(optarg))) <= 0) {
872 pa_log(_("Invalid latency specification '%s'"), optarg);
873 goto quit;
875 break;
877 case ARG_PROCESS_TIME:
878 if (((process_time = (size_t) atoi(optarg))) <= 0) {
879 pa_log(_("Invalid process time specification '%s'"), optarg);
880 goto quit;
882 break;
884 case ARG_LATENCY_MSEC:
885 if (((latency_msec = (int32_t) atoi(optarg))) <= 0) {
886 pa_log(_("Invalid latency specification '%s'"), optarg);
887 goto quit;
889 break;
891 case ARG_PROCESS_TIME_MSEC:
892 if (((process_time_msec = (int32_t) atoi(optarg))) <= 0) {
893 pa_log(_("Invalid process time specification '%s'"), optarg);
894 goto quit;
896 break;
898 case ARG_PROPERTY: {
899 char *t;
901 if (!(t = pa_locale_to_utf8(optarg)) ||
902 pa_proplist_setp(proplist, t) < 0) {
904 pa_xfree(t);
905 pa_log(_("Invalid property '%s'"), optarg);
906 goto quit;
909 pa_xfree(t);
910 break;
913 case ARG_RAW:
914 raw = TRUE;
915 break;
917 case ARG_FILE_FORMAT:
918 raw = FALSE;
920 if (optarg) {
921 if ((file_format = pa_sndfile_format_from_string(optarg)) < 0) {
922 pa_log(_("Unknown file format %s."), optarg);
923 goto quit;
927 raw = FALSE;
928 break;
930 case ARG_LIST_FILE_FORMATS:
931 pa_sndfile_dump_formats();
932 ret = 0;
933 goto quit;
935 default:
936 goto quit;
940 if (!pa_sample_spec_valid(&sample_spec)) {
941 pa_log(_("Invalid sample specification"));
942 goto quit;
945 if (optind+1 == argc) {
946 int fd;
948 filename = argv[optind];
950 if ((fd = pa_open_cloexec(argv[optind], mode == PLAYBACK ? O_RDONLY : O_WRONLY|O_TRUNC|O_CREAT, 0666)) < 0) {
951 pa_log(_("open(): %s"), strerror(errno));
952 goto quit;
955 if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
956 pa_log(_("dup2(): %s"), strerror(errno));
957 goto quit;
960 pa_close(fd);
962 } else if (optind+1 <= argc) {
963 pa_log(_("Too many arguments."));
964 goto quit;
967 if (!raw) {
968 SF_INFO sfi;
969 pa_zero(sfi);
971 if (mode == RECORD) {
972 /* This might patch up the sample spec */
973 if (pa_sndfile_write_sample_spec(&sfi, &sample_spec) < 0) {
974 pa_log(_("Failed to generate sample specification for file."));
975 goto quit;
978 /* Transparently upgrade classic .wav to wavex for multichannel audio */
979 if (file_format <= 0) {
980 if ((sample_spec.channels == 2 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_LEFT &&
981 channel_map.map[1] == PA_CHANNEL_POSITION_RIGHT))) ||
982 (sample_spec.channels == 1 && (!channel_map_set || (channel_map.map[0] == PA_CHANNEL_POSITION_MONO))))
983 file_format = SF_FORMAT_WAV;
984 else
985 file_format = SF_FORMAT_WAVEX;
988 sfi.format |= file_format;
991 if (!(sndfile = sf_open_fd(mode == RECORD ? STDOUT_FILENO : STDIN_FILENO,
992 mode == RECORD ? SFM_WRITE : SFM_READ,
993 &sfi, 0))) {
994 pa_log(_("Failed to open audio file."));
995 goto quit;
998 if (mode == PLAYBACK) {
999 if (sample_spec_set)
1000 pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));
1002 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
1003 pa_log(_("Failed to determine sample specification from file."));
1004 goto quit;
1006 sample_spec_set = TRUE;
1008 if (!channel_map_set) {
1009 /* Allow the user to overwrite the channel map on the command line */
1010 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1011 if (sample_spec.channels > 2)
1012 pa_log(_("Warning: Failed to determine channel map from file."));
1013 } else
1014 channel_map_set = TRUE;
1019 if (!channel_map_set)
1020 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1022 if (!pa_channel_map_compatible(&channel_map, &sample_spec)) {
1023 pa_log(_("Channel map doesn't match sample specification"));
1024 goto quit;
1027 if (!raw) {
1028 pa_proplist *sfp;
1030 if (mode == PLAYBACK)
1031 readf_function = pa_sndfile_readf_function(&sample_spec);
1032 else {
1033 if (pa_sndfile_write_channel_map(sndfile, &channel_map) < 0)
1034 pa_log(_("Warning: failed to write channel map to file."));
1036 writef_function = pa_sndfile_writef_function(&sample_spec);
1039 /* Fill in libsndfile prop list data */
1040 sfp = pa_proplist_new();
1041 pa_sndfile_init_proplist(sndfile, sfp);
1042 pa_proplist_update(proplist, PA_UPDATE_MERGE, sfp);
1043 pa_proplist_free(sfp);
1046 if (verbose) {
1047 char tss[PA_SAMPLE_SPEC_SNPRINT_MAX], tcm[PA_CHANNEL_MAP_SNPRINT_MAX];
1049 pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
1050 mode == RECORD ? _("recording") : _("playback"),
1051 pa_sample_spec_snprint(tss, sizeof(tss), &sample_spec),
1052 pa_channel_map_snprint(tcm, sizeof(tcm), &channel_map));
1055 /* Fill in client name if none was set */
1056 if (!pa_proplist_contains(proplist, PA_PROP_APPLICATION_NAME)) {
1057 char *t;
1059 if ((t = pa_locale_to_utf8(bn))) {
1060 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t);
1061 pa_xfree(t);
1065 /* Fill in media name if none was set */
1066 if (!pa_proplist_contains(proplist, PA_PROP_MEDIA_NAME)) {
1067 const char *t;
1069 if ((t = filename) ||
1070 (t = pa_proplist_gets(proplist, PA_PROP_APPLICATION_NAME)))
1071 pa_proplist_sets(proplist, PA_PROP_MEDIA_NAME, t);
1074 /* Set up a new main loop */
1075 if (!(m = pa_mainloop_new())) {
1076 pa_log(_("pa_mainloop_new() failed."));
1077 goto quit;
1080 mainloop_api = pa_mainloop_get_api(m);
1082 pa_assert_se(pa_signal_init(mainloop_api) == 0);
1083 pa_signal_new(SIGINT, exit_signal_callback, NULL);
1084 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1085 #ifdef SIGUSR1
1086 pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
1087 #endif
1088 pa_disable_sigpipe();
1090 if (raw) {
1091 if (!(stdio_event = mainloop_api->io_new(mainloop_api,
1092 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
1093 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
1094 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
1095 pa_log(_("io_new() failed."));
1096 goto quit;
1100 /* Create a new connection context */
1101 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1102 pa_log(_("pa_context_new() failed."));
1103 goto quit;
1106 pa_context_set_state_callback(context, context_state_callback, NULL);
1108 /* Connect the context */
1109 if (pa_context_connect(context, server, 0, NULL) < 0) {
1110 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1111 goto quit;
1114 if (verbose) {
1115 if (!(time_event = pa_context_rttime_new(context, pa_rtclock_now() + TIME_EVENT_USEC, time_event_callback, NULL))) {
1116 pa_log(_("pa_context_rttime_new() failed."));
1117 goto quit;
1121 /* Run the main loop */
1122 if (pa_mainloop_run(m, &ret) < 0) {
1123 pa_log(_("pa_mainloop_run() failed."));
1124 goto quit;
1127 quit:
1128 if (stream)
1129 pa_stream_unref(stream);
1131 if (context)
1132 pa_context_unref(context);
1134 if (stdio_event) {
1135 pa_assert(mainloop_api);
1136 mainloop_api->io_free(stdio_event);
1139 if (time_event) {
1140 pa_assert(mainloop_api);
1141 mainloop_api->time_free(time_event);
1144 if (m) {
1145 pa_signal_done();
1146 pa_mainloop_free(m);
1149 pa_xfree(buffer);
1151 pa_xfree(server);
1152 pa_xfree(device);
1154 if (sndfile)
1155 sf_close(sndfile);
1157 if (proplist)
1158 pa_proplist_free(proplist);
1160 return ret;