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