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