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