6cd01d211b7fde74bba9e7a1ebd27558ff4b475b
[profile/ivi/pulseaudio-panda.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 #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 }