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