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