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