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