fix pacat
[profile/ivi/pulseaudio.git] / src / utils / pacat.c
1 /* $Id$ */
2
3 /***
4   This file is part of polypaudio.
5  
6   polypaudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as published
8   by the Free Software Foundation; either version 2 of the License,
9   or (at your option) any later version.
10  
11   polypaudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public License
17   along with polypaudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <signal.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <assert.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <getopt.h>
34
35 #include <polyp/polypaudio.h>
36 #include <polyp/mainloop.h>
37 #include <polyp/mainloop-signal.h>
38
39 #if PA_API_VERSION != 8
40 #error Invalid Polypaudio API version
41 #endif
42
43 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
44
45 static pa_context *context = NULL;
46 static pa_stream *stream = NULL;
47 static pa_mainloop_api *mainloop_api = NULL;
48
49 static void *buffer = NULL;
50 static size_t buffer_length = 0, buffer_index = 0;
51
52 static pa_io_event* stdio_event = NULL;
53
54 static char *stream_name = NULL, *client_name = NULL, *device = NULL;
55
56 static int verbose = 0;
57 static pa_volume_t volume = PA_VOLUME_NORM;
58
59 static pa_sample_spec sample_spec = {
60     .format = PA_SAMPLE_S16LE,
61     .rate = 44100,
62     .channels = 2
63 };
64
65 /* A shortcut for terminating the application */
66 static void quit(int ret) {
67     assert(mainloop_api);
68     mainloop_api->quit(mainloop_api, ret);
69 }
70
71 /* Write some data to the stream */
72 static void do_stream_write(size_t length) {
73     size_t l;
74     assert(length);
75
76     if (!buffer || !buffer_length)
77         return;
78     
79     l = length;
80     if (l > buffer_length)
81         l = buffer_length;
82     
83     if (pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE) < 0) {
84         fprintf(stderr, "pa_stream_write() failed: %s\n", pa_strerror(pa_context_errno(context)));
85         quit(1);
86         return;
87     }
88     
89     buffer_length -= l;
90     buffer_index += l;
91     
92     if (!buffer_length) {
93         free(buffer);
94         buffer = NULL;
95         buffer_index = buffer_length = 0;
96     }
97 }
98
99 /* This is called whenever new data may be written to the stream */
100 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
101     assert(s && length);
102
103     if (stdio_event)
104         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
105
106     if (!buffer)
107         return;
108
109     do_stream_write(length);
110 }
111
112 /* This is called whenever new data may is available */
113 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
114     const void *data;
115     assert(s && length);
116
117     if (stdio_event)
118         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
119
120     if (pa_stream_peek(s, &data, &length) < 0) {
121         fprintf(stderr, "pa_stream_peek() failed: %s\n", pa_strerror(pa_context_errno(context)));
122         quit(1);
123         return;
124     }
125     
126     assert(data && length);
127
128     if (buffer) {
129         fprintf(stderr, "Buffer overrun, dropping incoming data\n");
130         if (pa_stream_drop(s) < 0) {
131             fprintf(stderr, "pa_stream_drop() failed: %s\n", pa_strerror(pa_context_errno(context)));
132             quit(1);
133         }
134         return;
135     }
136
137     buffer = malloc(buffer_length = length);
138     assert(buffer);
139     memcpy(buffer, data, length);
140     buffer_index = 0;
141     pa_stream_drop(s);
142 }
143
144 /* This routine is called whenever the stream state changes */
145 static void stream_state_callback(pa_stream *s, void *userdata) {
146     assert(s);
147
148     switch (pa_stream_get_state(s)) {
149         case PA_STREAM_CREATING:
150         case PA_STREAM_TERMINATED:
151             break;
152
153         case PA_STREAM_READY:
154             if (verbose)
155                 fprintf(stderr, "Stream successfully created\n");
156             break;
157             
158         case PA_STREAM_FAILED:
159         default:
160             fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
161             quit(1);
162     }
163 }
164
165 /* This is called whenever the context status changes */
166 static void context_state_callback(pa_context *c, void *userdata) {
167     assert(c);
168
169     switch (pa_context_get_state(c)) {
170         case PA_CONTEXT_CONNECTING:
171         case PA_CONTEXT_AUTHORIZING:
172         case PA_CONTEXT_SETTING_NAME:
173             break;
174         
175         case PA_CONTEXT_READY: {
176             int r;
177             
178             assert(c && !stream);
179
180             if (verbose)
181                 fprintf(stderr, "Connection established.\n");
182
183             if (!(stream = pa_stream_new(c, stream_name, &sample_spec, NULL))) {
184                 fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(c)));
185                 goto fail;
186             }
187
188             pa_stream_set_state_callback(stream, stream_state_callback, NULL);
189             pa_stream_set_write_callback(stream, stream_write_callback, NULL);
190             pa_stream_set_read_callback(stream, stream_read_callback, NULL);
191
192             if (mode == PLAYBACK) {
193                 pa_cvolume cv;
194                 if ((r = pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, sample_spec.channels, volume), NULL)) < 0) {
195                     fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(c)));
196                     goto fail;
197                 }
198                     
199             } else {
200                 if ((r = pa_stream_connect_record(stream, device, NULL, 0)) < 0) {
201                     fprintf(stderr, "pa_stream_connect_record() failed: %s\n", pa_strerror(pa_context_errno(c)));
202                     goto fail;
203                 }
204             }
205                 
206             break;
207         }
208             
209         case PA_CONTEXT_TERMINATED:
210             quit(0);
211             break;
212
213         case PA_CONTEXT_FAILED:
214         default:
215             fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
216             goto fail;
217     }
218
219     return;
220     
221 fail:
222     quit(1);
223     
224 }
225
226 /* Connection draining complete */
227 static void context_drain_complete(pa_context*c, void *userdata) {
228     pa_context_disconnect(c);
229 }
230
231 /* Stream draining complete */
232 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
233     pa_operation *o;
234
235     if (!success) {
236         fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
237         quit(1);
238     }
239     
240     if (verbose)    
241         fprintf(stderr, "Playback stream drained.\n");
242
243     pa_stream_disconnect(stream);
244     pa_stream_unref(stream);
245     stream = NULL;
246     
247     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
248         pa_context_disconnect(context);
249     else {
250         if (verbose)
251             fprintf(stderr, "Draining connection to server.\n");
252     }
253 }
254
255 /* New data on STDIN **/
256 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
257     size_t l, w = 0;
258     ssize_t r;
259     assert(a == mainloop_api && e && stdio_event == e);
260
261     if (buffer) {
262         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
263         return;
264     }
265
266     if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
267         l = 4096;
268     
269     buffer = malloc(l);
270     assert(buffer);
271     if ((r = read(fd, buffer, l)) <= 0) {
272         if (r == 0) {
273             pa_operation *o;
274             
275             if (verbose)
276                 fprintf(stderr, "Got EOF.\n");
277             
278             if (!(o = pa_stream_drain(stream, stream_drain_complete, NULL))) {
279                 fprintf(stderr, "pa_stream_drain(): %s\n", pa_strerror(pa_context_errno(context)));
280                 quit(1);
281                 return;
282             }
283
284             pa_operation_unref(o);
285         } else {
286             fprintf(stderr, "read() failed: %s\n", strerror(errno));
287             quit(1);
288         }
289
290         mainloop_api->io_free(stdio_event);
291         stdio_event = NULL;
292         return;
293     }
294
295     buffer_length = r;
296     buffer_index = 0;
297
298     if (w)
299         do_stream_write(w);
300 }
301
302 /* Some data may be written to STDOUT */
303 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
304     ssize_t r;
305     assert(a == mainloop_api && e && stdio_event == e);
306
307     if (!buffer) {
308         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
309         return;
310     }
311
312     assert(buffer_length);
313     
314     if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
315         fprintf(stderr, "write() failed: %s\n", strerror(errno));
316         quit(1);
317
318         mainloop_api->io_free(stdio_event);
319         stdio_event = NULL;
320         return;
321     }
322
323     buffer_length -= r;
324     buffer_index += r;
325
326     if (!buffer_length) {
327         free(buffer);
328         buffer = NULL;
329         buffer_length = buffer_index = 0;
330     }
331 }
332
333 /* UNIX signal to quit recieved */
334 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
335     if (verbose)
336         fprintf(stderr, "Got signal, exiting.\n");
337     quit(0);
338     
339 }
340
341 /* Show the current latency */
342 static void stream_get_latency_callback(pa_stream *s, const pa_latency_info *i, void *userdata) {
343     pa_usec_t total;
344     int negative = 0;
345     assert(s);
346
347     if (!i) {
348         fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
349         quit(1);
350         return;
351     }
352
353     total = pa_stream_get_latency(s, i, &negative);
354
355     fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; source: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n",
356             (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, (float) total * (negative?-1:1),
357             i->synchronized_clocks ? "yes" : "no");
358 }
359
360 /* Someone requested that the latency is shown */
361 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
362     fprintf(stderr, "Got SIGUSR1, requesting latency.\n");
363     pa_operation_unref(pa_stream_get_latency_info(stream, stream_get_latency_callback, NULL));
364 }
365
366
367 static void help(const char *argv0) {
368
369     printf("%s [options]\n\n"
370            "  -h, --help                            Show this help\n"
371            "      --version                         Show version\n\n"
372            "  -r, --record                          Create a connection for recording\n"
373            "  -p, --playback                        Create a connection for playback\n\n"
374            "  -v, --verbose                         Enable verbose operations\n\n"
375            "  -s, --server=SERVER                   The name of the server to connect to\n"
376            "  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
377            "  -n, --client-name=NAME                How to call this client on the server\n"
378            "      --stream-name=NAME                How to call this stream on the server\n"
379            "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...256\n"
380            "      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
381            "      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
382            "                                        float32be, ulaw, alaw (defaults to s16ne)\n"
383            "      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
384            "                                        (defaults to 2)\n",
385            argv0);
386 }
387
388 enum {
389     ARG_VERSION = 256,
390     ARG_STREAM_NAME,
391     ARG_VOLUME,
392     ARG_SAMPLERATE,
393     ARG_SAMPLEFORMAT,
394     ARG_CHANNELS
395 };
396
397 int main(int argc, char *argv[]) {
398     pa_mainloop* m = NULL;
399     int ret = 1, r, c;
400     char *bn, *server = NULL;
401
402     static const struct option long_options[] = {
403         {"record",      0, NULL, 'r'},
404         {"playback",    0, NULL, 'p'},
405         {"device",      1, NULL, 'd'},
406         {"server",      1, NULL, 's'},
407         {"client-name", 1, NULL, 'n'},
408         {"stream-name", 1, NULL, ARG_STREAM_NAME},
409         {"version",     0, NULL, ARG_VERSION},
410         {"help",        0, NULL, 'h'},
411         {"verbose",     0, NULL, 'v'},
412         {"volume",      1, NULL, ARG_VOLUME},
413         {"rate",        1, NULL, ARG_SAMPLERATE},
414         {"format",      1, NULL, ARG_SAMPLEFORMAT},
415         {"channels",    1, NULL, ARG_CHANNELS},
416         {NULL,          0, NULL, 0}
417     };
418
419     if (!(bn = strrchr(argv[0], '/')))
420         bn = argv[0];
421     else
422         bn++;
423
424     if (strstr(bn, "rec") || strstr(bn, "mon"))
425         mode = RECORD;
426     else if (strstr(bn, "cat") || strstr(bn, "play"))
427         mode = PLAYBACK;
428
429     while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
430
431         switch (c) {
432             case 'h' :
433                 help(bn);
434                 ret = 0;
435                 goto quit;
436                 
437             case ARG_VERSION:
438                 printf("pacat "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
439                 ret = 0;
440                 goto quit;
441
442             case 'r':
443                 mode = RECORD;
444                 break;
445
446             case 'p':
447                 mode = PLAYBACK;
448                 break;
449
450             case 'd':
451                 free(device);
452                 device = strdup(optarg);
453                 break;
454
455             case 's':
456                 free(server);
457                 server = strdup(optarg);
458                 break;
459
460             case 'n':
461                 free(client_name);
462                 client_name = strdup(optarg);
463                 break;
464
465             case ARG_STREAM_NAME:
466                 free(stream_name);
467                 stream_name = strdup(optarg);
468                 break;
469
470             case 'v':
471                 verbose = 1;
472                 break;
473
474             case ARG_VOLUME: {
475                 int v = atoi(optarg);
476                 volume = v < 0 ? 0 : v;
477                 break;
478             }
479
480             case ARG_CHANNELS: 
481                 sample_spec.channels = atoi(optarg);
482                 break;
483
484             case ARG_SAMPLEFORMAT:
485                 sample_spec.format = pa_parse_sample_format(optarg);
486                 break;
487
488             case ARG_SAMPLERATE:
489                 sample_spec.rate = atoi(optarg);
490                 break;
491
492             default:
493                 goto quit;
494         }
495     }
496
497     if (!client_name)
498         client_name = strdup(bn);
499
500     if (!stream_name)
501         stream_name = strdup(client_name);
502
503     if (!pa_sample_spec_valid(&sample_spec)) {
504         fprintf(stderr, "Invalid sample specification\n");
505         goto quit;
506     }
507     
508     if (verbose) {
509         char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
510         pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
511         fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t);
512     }
513
514     /* Set up a new main loop */
515     if (!(m = pa_mainloop_new())) {
516         fprintf(stderr, "pa_mainloop_new() failed.\n");
517         goto quit;
518     }
519
520     mainloop_api = pa_mainloop_get_api(m);
521
522     r = pa_signal_init(mainloop_api);
523     assert(r == 0);
524     pa_signal_new(SIGINT, exit_signal_callback, NULL);
525     pa_signal_new(SIGTERM, exit_signal_callback, NULL);
526 #ifdef SIGUSR1
527     pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
528 #endif
529 #ifdef SIGPIPE
530     signal(SIGPIPE, SIG_IGN);
531 #endif
532     
533     if (!(stdio_event = mainloop_api->io_new(mainloop_api,
534                                              mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
535                                              mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
536                                              mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
537         fprintf(stderr, "source_io() failed.\n");
538         goto quit;
539     }
540
541     /* Create a new connection context */
542     if (!(context = pa_context_new(mainloop_api, client_name))) {
543         fprintf(stderr, "pa_context_new() failed.\n");
544         goto quit;
545     }
546
547     pa_context_set_state_callback(context, context_state_callback, NULL);
548
549     /* Connect the context */
550     pa_context_connect(context, server, 0, NULL);
551
552     /* Run the main loop */
553     if (pa_mainloop_run(m, &ret) < 0) {
554         fprintf(stderr, "pa_mainloop_run() failed.\n");
555         goto quit;
556     }
557     
558 quit:
559     if (stream)
560         pa_stream_unref(stream);
561
562     if (context)
563         pa_context_unref(context);
564
565     if (stdio_event) {
566         assert(mainloop_api);
567         mainloop_api->io_free(stdio_event);
568     }
569     
570     if (m) {
571         pa_signal_done();
572         pa_mainloop_free(m);
573     }
574
575     free(buffer);
576
577     free(server);
578     free(device);
579     free(client_name);
580     free(stream_name);
581     
582     return ret;
583 }