]> code.delx.au - pulseaudio/blob - src/utils/pacat.c
Merge commit 'flameeyes/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
43 #include <pulsecore/macro.h>
44 #include <pulsecore/core-util.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/sndfile-util.h>
47
48 #define TIME_EVENT_USEC 50000
49
50 #define CLEAR_LINE "\x1B[K"
51
52 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
53
54 static pa_context *context = NULL;
55 static pa_stream *stream = NULL;
56 static pa_mainloop_api *mainloop_api = NULL;
57
58 static void *buffer = NULL;
59 static size_t buffer_length = 0, buffer_index = 0;
60
61 static pa_io_event* stdio_event = NULL;
62
63 static pa_proplist *proplist = NULL;
64 static char *device = NULL;
65
66 static SNDFILE* sndfile = NULL;
67
68 static pa_bool_t verbose = FALSE;
69 static pa_volume_t volume = PA_VOLUME_NORM;
70 static pa_bool_t volume_is_set = FALSE;
71
72 static pa_sample_spec sample_spec = {
73 .format = PA_SAMPLE_S16LE,
74 .rate = 44100,
75 .channels = 2
76 };
77 static pa_bool_t sample_spec_set = FALSE;
78
79 static pa_channel_map channel_map;
80 static pa_bool_t channel_map_set = FALSE;
81
82 static sf_count_t (*readf_function)(SNDFILE *_sndfile, void *ptr, sf_count_t frames) = NULL;
83 static sf_count_t (*writef_function)(SNDFILE *_sndfile, const void *ptr, sf_count_t frames) = NULL;
84
85 static pa_stream_flags_t flags = 0;
86
87 static size_t latency = 0, process_time = 0;
88
89 static pa_bool_t raw = TRUE;
90 static int file_format = -1;
91
92 /* A shortcut for terminating the application */
93 static void quit(int ret) {
94 pa_assert(mainloop_api);
95 mainloop_api->quit(mainloop_api, ret);
96 }
97
98 /* Connection draining complete */
99 static void context_drain_complete(pa_context*c, void *userdata) {
100 pa_context_disconnect(c);
101 }
102
103 /* Stream draining complete */
104 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
105
106 if (!success) {
107 pa_log(_("Failed to drain stream: %s\n"), pa_strerror(pa_context_errno(context)));
108 quit(1);
109 }
110
111 if (verbose)
112 pa_log(_("Playback stream drained.\n"));
113
114 pa_stream_disconnect(stream);
115 pa_stream_unref(stream);
116 stream = NULL;
117
118 if (!pa_context_drain(context, context_drain_complete, NULL))
119 pa_context_disconnect(context);
120 else {
121 if (verbose)
122 pa_log(_("Draining connection to server.\n"));
123 }
124 }
125
126 /* Start draining */
127 static void start_drain(void) {
128
129 if (stream) {
130 pa_operation *o;
131
132 pa_stream_set_write_callback(stream, NULL, NULL);
133
134 if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
135 pa_log(_("pa_stream_drain(): %s\n"), pa_strerror(pa_context_errno(context)));
136 quit(1);
137 return;
138 }
139
140 pa_operation_unref(o);
141 } else
142 quit(0);
143 }
144
145 /* Write some data to the stream */
146 static void do_stream_write(size_t length) {
147 size_t l;
148 pa_assert(length);
149
150 if (!buffer || !buffer_length)
151 return;
152
153 l = length;
154 if (l > buffer_length)
155 l = buffer_length;
156
157 if (pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
158 pa_log(_("pa_stream_write() failed: %s\n"), pa_strerror(pa_context_errno(context)));
159 quit(1);
160 return;
161 }
162
163 buffer_length -= l;
164 buffer_index += l;
165
166 if (!buffer_length) {
167 pa_xfree(buffer);
168 buffer = NULL;
169 buffer_index = buffer_length = 0;
170 }
171 }
172
173 /* This is called whenever new data may be written to the stream */
174 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
175 pa_assert(s);
176 pa_assert(length > 0);
177
178 if (raw) {
179 pa_assert(!sndfile);
180
181 if (stdio_event)
182 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
183
184 if (!buffer)
185 return;
186
187 do_stream_write(length);
188
189 } else {
190 sf_count_t bytes;
191 void *data;
192
193 pa_assert(sndfile);
194
195 data = pa_xmalloc(length);
196
197 if (readf_function) {
198 size_t k = pa_frame_size(&sample_spec);
199
200 if ((bytes = readf_function(sndfile, data, (sf_count_t) (length/k))) > 0)
201 bytes *= (sf_count_t) k;
202
203 } else
204 bytes = sf_read_raw(sndfile, data, (sf_count_t) length);
205
206 if (bytes > 0)
207 pa_stream_write(s, data, (size_t) bytes, pa_xfree, 0, PA_SEEK_RELATIVE);
208 else
209 pa_xfree(data);
210
211 if (bytes < (sf_count_t) length)
212 start_drain();
213 }
214 }
215
216 /* This is called whenever new data may is available */
217 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
218
219 pa_assert(s);
220 pa_assert(length > 0);
221
222 if (raw) {
223 pa_assert(!sndfile);
224
225 if (stdio_event)
226 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
227
228
229 while (pa_stream_readable_size(s) > 0) {
230 const void *data;
231
232 if (pa_stream_peek(s, &data, &length) < 0) {
233 pa_log(_("pa_stream_peek() failed: %s\n"), pa_strerror(pa_context_errno(context)));
234 quit(1);
235 return;
236 }
237
238 pa_assert(data);
239 pa_assert(length > 0);
240
241 if (buffer) {
242 buffer = pa_xrealloc(buffer, buffer_length + length);
243 memcpy((uint8_t*) buffer + buffer_length, data, length);
244 buffer_length += length;
245 } else {
246 buffer = pa_xmalloc(length);
247 memcpy(buffer, data, length);
248 buffer_length = length;
249 buffer_index = 0;
250 }
251 pa_stream_drop(s);
252 }
253
254 } else {
255 pa_assert(sndfile);
256
257 while (pa_stream_readable_size(s) > 0) {
258 sf_count_t bytes;
259 const void *data;
260
261 if (pa_stream_peek(s, &data, &length) < 0) {
262 pa_log(_("pa_stream_peek() failed: %s\n"), pa_strerror(pa_context_errno(context)));
263 quit(1);
264 return;
265 }
266
267 pa_assert(data);
268 pa_assert(length > 0);
269
270 if (writef_function) {
271 size_t k = pa_frame_size(&sample_spec);
272
273 if ((bytes = writef_function(sndfile, data, (sf_count_t) (length/k))) > 0)
274 bytes *= (sf_count_t) k;
275
276 } else
277 bytes = sf_write_raw(sndfile, data, (sf_count_t) length);
278
279 if (bytes < (sf_count_t) length)
280 quit(1);
281
282 pa_stream_drop(s);
283 }
284 }
285 }
286
287 /* This routine is called whenever the stream state changes */
288 static void stream_state_callback(pa_stream *s, void *userdata) {
289 pa_assert(s);
290
291 switch (pa_stream_get_state(s)) {
292 case PA_STREAM_CREATING:
293 case PA_STREAM_TERMINATED:
294 break;
295
296 case PA_STREAM_READY:
297
298 if (verbose) {
299 const pa_buffer_attr *a;
300 char cmt[PA_CHANNEL_MAP_SNPRINT_MAX], sst[PA_SAMPLE_SPEC_SNPRINT_MAX];
301
302 pa_log(_("Stream successfully created.\n"));
303
304 if (!(a = pa_stream_get_buffer_attr(s)))
305 pa_log(_("pa_stream_get_buffer_attr() failed: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
306 else {
307
308 if (mode == PLAYBACK)
309 pa_log(_("Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u\n"), a->maxlength, a->tlength, a->prebuf, a->minreq);
310 else {
311 pa_assert(mode == RECORD);
312 pa_log(_("Buffer metrics: maxlength=%u, fragsize=%u\n"), a->maxlength, a->fragsize);
313 }
314 }
315
316 pa_log(_("Using sample spec '%s', channel map '%s'.\n"),
317 pa_sample_spec_snprint(sst, sizeof(sst), pa_stream_get_sample_spec(s)),
318 pa_channel_map_snprint(cmt, sizeof(cmt), pa_stream_get_channel_map(s)));
319
320 pa_log(_("Connected to device %s (%u, %ssuspended).\n"),
321 pa_stream_get_device_name(s),
322 pa_stream_get_device_index(s),
323 pa_stream_is_suspended(s) ? "" : "not ");
324 }
325
326 break;
327
328 case PA_STREAM_FAILED:
329 default:
330 pa_log(_("Stream error: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
331 quit(1);
332 }
333 }
334
335 static void stream_suspended_callback(pa_stream *s, void *userdata) {
336 pa_assert(s);
337
338 if (verbose) {
339 if (pa_stream_is_suspended(s))
340 pa_log(_("Stream device suspended.%s \n"), CLEAR_LINE);
341 else
342 pa_log(_("Stream device resumed.%s \n"), CLEAR_LINE);
343 }
344 }
345
346 static void stream_underflow_callback(pa_stream *s, void *userdata) {
347 pa_assert(s);
348
349 if (verbose)
350 pa_log(_("Stream underrun.%s \n"), CLEAR_LINE);
351 }
352
353 static void stream_overflow_callback(pa_stream *s, void *userdata) {
354 pa_assert(s);
355
356 if (verbose)
357 pa_log(_("Stream overrun.%s \n"), CLEAR_LINE);
358 }
359
360 static void stream_started_callback(pa_stream *s, void *userdata) {
361 pa_assert(s);
362
363 if (verbose)
364 pa_log(_("Stream started.%s \n"), CLEAR_LINE);
365 }
366
367 static void stream_moved_callback(pa_stream *s, void *userdata) {
368 pa_assert(s);
369
370 if (verbose)
371 pa_log(_("Stream moved to device %s (%u, %ssuspended).%s \n"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "), CLEAR_LINE);
372 }
373
374 static void stream_buffer_attr_callback(pa_stream *s, void *userdata) {
375 pa_assert(s);
376
377 if (verbose)
378 pa_log(_("Stream buffer attributes changed.%s \n"), CLEAR_LINE);
379 }
380
381 static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
382 char *t;
383
384 pa_assert(s);
385 pa_assert(name);
386 pa_assert(pl);
387
388 t = pa_proplist_to_string_sep(pl, ", ");
389 pa_log("Got event '%s', properties '%s'\n", name, t);
390 pa_xfree(t);
391 }
392
393 /* This is called whenever the context status changes */
394 static void context_state_callback(pa_context *c, void *userdata) {
395 pa_assert(c);
396
397 switch (pa_context_get_state(c)) {
398 case PA_CONTEXT_CONNECTING:
399 case PA_CONTEXT_AUTHORIZING:
400 case PA_CONTEXT_SETTING_NAME:
401 break;
402
403 case PA_CONTEXT_READY: {
404 int r;
405 pa_buffer_attr buffer_attr;
406
407 pa_assert(c);
408 pa_assert(!stream);
409
410 if (verbose)
411 pa_log(_("Connection established.%s \n"), CLEAR_LINE);
412
413 if (!(stream = pa_stream_new_with_proplist(c, NULL, &sample_spec, &channel_map, proplist))) {
414 pa_log(_("pa_stream_new() failed: %s\n"), pa_strerror(pa_context_errno(c)));
415 goto fail;
416 }
417
418 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
419 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
420 pa_stream_set_read_callback(stream, stream_read_callback, NULL);
421 pa_stream_set_suspended_callback(stream, stream_suspended_callback, NULL);
422 pa_stream_set_moved_callback(stream, stream_moved_callback, NULL);
423 pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
424 pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
425 pa_stream_set_started_callback(stream, stream_started_callback, NULL);
426 pa_stream_set_event_callback(stream, stream_event_callback, NULL);
427 pa_stream_set_buffer_attr_callback(stream, stream_buffer_attr_callback, NULL);
428
429 if (latency > 0) {
430 memset(&buffer_attr, 0, sizeof(buffer_attr));
431 buffer_attr.tlength = (uint32_t) latency;
432 buffer_attr.minreq = (uint32_t) process_time;
433 buffer_attr.maxlength = (uint32_t) -1;
434 buffer_attr.prebuf = (uint32_t) -1;
435 buffer_attr.fragsize = (uint32_t) latency;
436 flags |= PA_STREAM_ADJUST_LATENCY;
437 }
438
439 if (mode == PLAYBACK) {
440 pa_cvolume cv;
441 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) {
442 pa_log(_("pa_stream_connect_playback() failed: %s\n"), pa_strerror(pa_context_errno(c)));
443 goto fail;
444 }
445
446 } else {
447 if ((r = pa_stream_connect_record(stream, device, latency > 0 ? &buffer_attr : NULL, flags)) < 0) {
448 pa_log(_("pa_stream_connect_record() failed: %s\n"), pa_strerror(pa_context_errno(c)));
449 goto fail;
450 }
451 }
452
453 break;
454 }
455
456 case PA_CONTEXT_TERMINATED:
457 quit(0);
458 break;
459
460 case PA_CONTEXT_FAILED:
461 default:
462 pa_log(_("Connection failure: %s\n"), pa_strerror(pa_context_errno(c)));
463 goto fail;
464 }
465
466 return;
467
468 fail:
469 quit(1);
470
471 }
472
473 /* New data on STDIN **/
474 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
475 size_t l, w = 0;
476 ssize_t r;
477
478 pa_assert(a == mainloop_api);
479 pa_assert(e);
480 pa_assert(stdio_event == e);
481
482 if (buffer) {
483 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
484 return;
485 }
486
487 if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
488 l = 4096;
489
490 buffer = pa_xmalloc(l);
491
492 if ((r = read(fd, buffer, l)) <= 0) {
493 if (r == 0) {
494 if (verbose)
495 pa_log(_("Got EOF.\n"));
496
497 start_drain();
498
499 } else {
500 pa_log(_("read() failed: %s\n"), strerror(errno));
501 quit(1);
502 }
503
504 mainloop_api->io_free(stdio_event);
505 stdio_event = NULL;
506 return;
507 }
508
509 buffer_length = (uint32_t) r;
510 buffer_index = 0;
511
512 if (w)
513 do_stream_write(w);
514 }
515
516 /* Some data may be written to STDOUT */
517 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
518 ssize_t r;
519
520 pa_assert(a == mainloop_api);
521 pa_assert(e);
522 pa_assert(stdio_event == e);
523
524 if (!buffer) {
525 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
526 return;
527 }
528
529 pa_assert(buffer_length);
530
531 if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
532 pa_log(_("write() failed: %s\n"), strerror(errno));
533 quit(1);
534
535 mainloop_api->io_free(stdio_event);
536 stdio_event = NULL;
537 return;
538 }
539
540 buffer_length -= (uint32_t) r;
541 buffer_index += (uint32_t) r;
542
543 if (!buffer_length) {
544 pa_xfree(buffer);
545 buffer = NULL;
546 buffer_length = buffer_index = 0;
547 }
548 }
549
550 /* UNIX signal to quit recieved */
551 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
552 if (verbose)
553 pa_log(_("Got signal, exiting.\n"));
554 quit(0);
555 }
556
557 /* Show the current latency */
558 static void stream_update_timing_callback(pa_stream *s, int success, void *userdata) {
559 pa_usec_t l, usec;
560 int negative = 0;
561
562 pa_assert(s);
563
564 if (!success ||
565 pa_stream_get_time(s, &usec) < 0 ||
566 pa_stream_get_latency(s, &l, &negative) < 0) {
567 pa_log(_("Failed to get latency: %s\n"), pa_strerror(pa_context_errno(context)));
568 quit(1);
569 return;
570 }
571
572 pa_log(_("Time: %0.3f sec; Latency: %0.0f usec. \r"),
573 (float) usec / 1000000,
574 (float) l * (negative?-1.0f:1.0f));
575 }
576
577 /* Someone requested that the latency is shown */
578 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
579
580 if (!stream)
581 return;
582
583 pa_operation_unref(pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL));
584 }
585
586 static void time_event_callback(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
587 struct timeval next;
588
589 if (stream && pa_stream_get_state(stream) == PA_STREAM_READY) {
590 pa_operation *o;
591 if (!(o = pa_stream_update_timing_info(stream, stream_update_timing_callback, NULL)))
592 pa_log(_("pa_stream_update_timing_info() failed: %s\n"), pa_strerror(pa_context_errno(context)));
593 else
594 pa_operation_unref(o);
595 }
596
597 pa_gettimeofday(&next);
598 pa_timeval_add(&next, TIME_EVENT_USEC);
599
600 m->time_restart(e, &next);
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'\n"), 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'\n"), 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'\n"), 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'\n"), 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'\n"), 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'\n"), 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\n"));
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\n"), strerror(errno));
908 goto quit;
909 }
910
911 if (dup2(fd, mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO) < 0) {
912 pa_log(_("dup2(): %s\n"), strerror(errno));
913 goto quit;
914 }
915
916 pa_close(fd);
917
918 } else if (optind+1 <= argc) {
919 pa_log(_("Too many arguments.\n"));
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.\n"));
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.\n"));
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.\n"));
957
958 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
959 pa_log(_("Failed to determine sample specification from file.\n"));
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.\n"));
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\n"));
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.\n"));
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'.\n"),
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.\n"));
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.\n"));
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.\n"));
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\n"), pa_strerror(pa_context_errno(context)));
1067 goto quit;
1068 }
1069
1070 if (verbose) {
1071 struct timeval tv;
1072
1073 pa_gettimeofday(&tv);
1074 pa_timeval_add(&tv, TIME_EVENT_USEC);
1075
1076 if (!(time_event = mainloop_api->time_new(mainloop_api, &tv, time_event_callback, NULL))) {
1077 pa_log(_("time_new() failed.\n"));
1078 goto quit;
1079 }
1080 }
1081
1082 /* Run the main loop */
1083 if (pa_mainloop_run(m, &ret) < 0) {
1084 pa_log(_("pa_mainloop_run() failed.\n"));
1085 goto quit;
1086 }
1087
1088 quit:
1089 if (stream)
1090 pa_stream_unref(stream);
1091
1092 if (context)
1093 pa_context_unref(context);
1094
1095 if (stdio_event) {
1096 pa_assert(mainloop_api);
1097 mainloop_api->io_free(stdio_event);
1098 }
1099
1100 if (time_event) {
1101 pa_assert(mainloop_api);
1102 mainloop_api->time_free(time_event);
1103 }
1104
1105 if (m) {
1106 pa_signal_done();
1107 pa_mainloop_free(m);
1108 }
1109
1110 pa_xfree(buffer);
1111
1112 pa_xfree(server);
1113 pa_xfree(device);
1114
1115 if (sndfile)
1116 sf_close(sndfile);
1117
1118 if (proplist)
1119 pa_proplist_free(proplist);
1120
1121 return ret;
1122 }