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