change calls of pa_context_connect() to pass flags arugment correctly
[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     pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0, PA_SEEK_RELATIVE);
84     buffer_length -= l;
85     buffer_index += l;
86     
87     if (!buffer_length) {
88         free(buffer);
89         buffer = NULL;
90         buffer_index = buffer_length = 0;
91     }
92 }
93
94 /* This is called whenever new data may be written to the stream */
95 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
96     assert(s && length);
97
98     if (stdio_event)
99         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
100
101     if (!buffer)
102         return;
103
104     do_stream_write(length);
105 }
106
107 /* This is called whenever new data may is available */
108 static void stream_read_callback(pa_stream *s, size_t length, void *userdata) {
109     const void *data;
110     assert(s && length);
111
112     if (stdio_event)
113         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
114
115     pa_stream_peek(s, &data, &length);
116     assert(data && length);
117
118     if (buffer) {
119         fprintf(stderr, "Buffer overrun, dropping incoming data\n");
120         pa_stream_drop(s);
121         return;
122     }
123
124     buffer = malloc(buffer_length = length);
125     assert(buffer);
126     memcpy(buffer, data, length);
127     buffer_index = 0;
128     pa_stream_drop(s);
129 }
130
131 /* This routine is called whenever the stream state changes */
132 static void stream_state_callback(pa_stream *s, void *userdata) {
133     assert(s);
134
135     switch (pa_stream_get_state(s)) {
136         case PA_STREAM_CREATING:
137         case PA_STREAM_TERMINATED:
138             break;
139
140         case PA_STREAM_READY:
141             if (verbose)
142                 fprintf(stderr, "Stream successfully created\n");
143             break;
144             
145         case PA_STREAM_FAILED:
146         default:
147             fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
148             quit(1);
149     }
150 }
151
152 /* This is called whenever the context status changes */
153 static void context_state_callback(pa_context *c, void *userdata) {
154     assert(c);
155
156     switch (pa_context_get_state(c)) {
157         case PA_CONTEXT_CONNECTING:
158         case PA_CONTEXT_AUTHORIZING:
159         case PA_CONTEXT_SETTING_NAME:
160             break;
161         
162         case PA_CONTEXT_READY:
163             
164             assert(c && !stream);
165
166             if (verbose)
167                 fprintf(stderr, "Connection established.\n");
168
169             stream = pa_stream_new(c, stream_name, &sample_spec, NULL);
170             assert(stream);
171
172             pa_stream_set_state_callback(stream, stream_state_callback, NULL);
173             pa_stream_set_write_callback(stream, stream_write_callback, NULL);
174             pa_stream_set_read_callback(stream, stream_read_callback, NULL);
175
176             if (mode == PLAYBACK) {
177                 pa_cvolume cv;
178                 pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume), NULL);
179             } else
180                 pa_stream_connect_record(stream, device, NULL, 0);
181                 
182             break;
183             
184         case PA_CONTEXT_TERMINATED:
185             quit(0);
186             break;
187
188         case PA_CONTEXT_FAILED:
189         default:
190             fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
191             quit(1);
192     }
193 }
194
195 /* Connection draining complete */
196 static void context_drain_complete(pa_context*c, void *userdata) {
197     pa_context_disconnect(c);
198 }
199
200 /* Stream draining complete */
201 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
202     pa_operation *o;
203
204     if (!success) {
205         fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
206         quit(1);
207     }
208     
209     if (verbose)    
210         fprintf(stderr, "Playback stream drained.\n");
211
212     pa_stream_disconnect(stream);
213     pa_stream_unref(stream);
214     stream = NULL;
215     
216     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
217         pa_context_disconnect(context);
218     else {
219         pa_operation_unref(o);
220
221         if (verbose)
222             fprintf(stderr, "Draining connection to server.\n");
223     }
224 }
225
226 /* New data on STDIN **/
227 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
228     size_t l, w = 0;
229     ssize_t r;
230     assert(a == mainloop_api && e && stdio_event == e);
231
232     if (buffer) {
233         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
234         return;
235     }
236
237     if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
238         l = 4096;
239     
240     buffer = malloc(l);
241     assert(buffer);
242     if ((r = read(fd, buffer, l)) <= 0) {
243         if (r == 0) {
244             if (verbose)
245                 fprintf(stderr, "Got EOF.\n");
246             pa_operation_unref(pa_stream_drain(stream, stream_drain_complete, NULL));
247         } else {
248             fprintf(stderr, "read() failed: %s\n", strerror(errno));
249             quit(1);
250         }
251
252         mainloop_api->io_free(stdio_event);
253         stdio_event = NULL;
254         return;
255     }
256
257     buffer_length = r;
258     buffer_index = 0;
259
260     if (w)
261         do_stream_write(w);
262 }
263
264 /* Some data may be written to STDOUT */
265 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
266     ssize_t r;
267     assert(a == mainloop_api && e && stdio_event == e);
268
269     if (!buffer) {
270         mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
271         return;
272     }
273
274     assert(buffer_length);
275     
276     if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
277         fprintf(stderr, "write() failed: %s\n", strerror(errno));
278         quit(1);
279
280         mainloop_api->io_free(stdio_event);
281         stdio_event = NULL;
282         return;
283     }
284
285     buffer_length -= r;
286     buffer_index += r;
287
288     if (!buffer_length) {
289         free(buffer);
290         buffer = NULL;
291         buffer_length = buffer_index = 0;
292     }
293 }
294
295 /* UNIX signal to quit recieved */
296 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
297     if (verbose)
298         fprintf(stderr, "Got signal, exiting.\n");
299     quit(0);
300     
301 }
302
303 /* Show the current latency */
304 static void stream_get_latency_callback(pa_stream *s, const pa_latency_info *i, void *userdata) {
305     pa_usec_t total;
306     int negative = 0;
307     assert(s);
308
309     if (!i) {
310         fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
311         quit(1);
312         return;
313     }
314
315     total = pa_stream_get_latency(s, i, &negative);
316
317     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",
318             (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, (float) total * (negative?-1:1),
319             i->synchronized_clocks ? "yes" : "no");
320 }
321
322 /* Someone requested that the latency is shown */
323 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
324     fprintf(stderr, "Got SIGUSR1, requesting latency.\n");
325     pa_operation_unref(pa_stream_get_latency_info(stream, stream_get_latency_callback, NULL));
326 }
327
328
329 static void help(const char *argv0) {
330
331     printf("%s [options]\n\n"
332            "  -h, --help                            Show this help\n"
333            "      --version                         Show version\n\n"
334            "  -r, --record                          Create a connection for recording\n"
335            "  -p, --playback                        Create a connection for playback\n\n"
336            "  -v, --verbose                         Enable verbose operations\n\n"
337            "  -s, --server=SERVER                   The name of the server to connect to\n"
338            "  -d, --device=DEVICE                   The name of the sink/source to connect to\n"
339            "  -n, --client-name=NAME                How to call this client on the server\n"
340            "      --stream-name=NAME                How to call this stream on the server\n"
341            "      --volume=VOLUME                   Specify the initial (linear) volume in range 0...256\n"
342            "      --rate=SAMPLERATE                 The sample rate in Hz (defaults to 44100)\n"
343            "      --format=SAMPLEFORMAT             The sample type, one of s16le, s16be, u8, float32le,\n"
344            "                                        float32be, ulaw, alaw (defaults to s16ne)\n"
345            "      --channels=CHANNELS               The number of channels, 1 for mono, 2 for stereo\n"
346            "                                        (defaults to 2)\n",
347            argv0);
348 }
349
350 enum {
351     ARG_VERSION = 256,
352     ARG_STREAM_NAME,
353     ARG_VOLUME,
354     ARG_SAMPLERATE,
355     ARG_SAMPLEFORMAT,
356     ARG_CHANNELS
357 };
358
359 int main(int argc, char *argv[]) {
360     pa_mainloop* m = NULL;
361     int ret = 1, r, c;
362     char *bn, *server = NULL;
363
364     static const struct option long_options[] = {
365         {"record",      0, NULL, 'r'},
366         {"playback",    0, NULL, 'p'},
367         {"device",      1, NULL, 'd'},
368         {"server",      1, NULL, 's'},
369         {"client-name", 1, NULL, 'n'},
370         {"stream-name", 1, NULL, ARG_STREAM_NAME},
371         {"version",     0, NULL, ARG_VERSION},
372         {"help",        0, NULL, 'h'},
373         {"verbose",     0, NULL, 'v'},
374         {"volume",      1, NULL, ARG_VOLUME},
375         {"rate",        1, NULL, ARG_SAMPLERATE},
376         {"format",      1, NULL, ARG_SAMPLEFORMAT},
377         {"channels",    1, NULL, ARG_CHANNELS},
378         {NULL,          0, NULL, 0}
379     };
380
381     if (!(bn = strrchr(argv[0], '/')))
382         bn = argv[0];
383     else
384         bn++;
385
386     if (strstr(bn, "rec") || strstr(bn, "mon"))
387         mode = RECORD;
388     else if (strstr(bn, "cat") || strstr(bn, "play"))
389         mode = PLAYBACK;
390
391     while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
392
393         switch (c) {
394             case 'h' :
395                 help(bn);
396                 ret = 0;
397                 goto quit;
398                 
399             case ARG_VERSION:
400                 printf("pacat "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
401                 ret = 0;
402                 goto quit;
403
404             case 'r':
405                 mode = RECORD;
406                 break;
407
408             case 'p':
409                 mode = PLAYBACK;
410                 break;
411
412             case 'd':
413                 free(device);
414                 device = strdup(optarg);
415                 break;
416
417             case 's':
418                 free(server);
419                 server = strdup(optarg);
420                 break;
421
422             case 'n':
423                 free(client_name);
424                 client_name = strdup(optarg);
425                 break;
426
427             case ARG_STREAM_NAME:
428                 free(stream_name);
429                 stream_name = strdup(optarg);
430                 break;
431
432             case 'v':
433                 verbose = 1;
434                 break;
435
436             case ARG_VOLUME: {
437                 int v = atoi(optarg);
438                 volume = v < 0 ? 0 : v;
439                 break;
440             }
441
442             case ARG_CHANNELS: 
443                 sample_spec.channels = atoi(optarg);
444                 break;
445
446             case ARG_SAMPLEFORMAT:
447                 sample_spec.format = pa_parse_sample_format(optarg);
448                 break;
449
450             case ARG_SAMPLERATE:
451                 sample_spec.rate = atoi(optarg);
452                 break;
453
454             default:
455                 goto quit;
456         }
457     }
458
459     if (!client_name)
460         client_name = strdup(bn);
461
462     if (!stream_name)
463         stream_name = strdup(client_name);
464
465     if (!pa_sample_spec_valid(&sample_spec)) {
466         fprintf(stderr, "Invalid sample specification\n");
467         goto quit;
468     }
469     
470     if (verbose) {
471         char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
472         pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
473         fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t);
474     }
475
476     /* Set up a new main loop */
477     if (!(m = pa_mainloop_new())) {
478         fprintf(stderr, "pa_mainloop_new() failed.\n");
479         goto quit;
480     }
481
482     mainloop_api = pa_mainloop_get_api(m);
483
484     r = pa_signal_init(mainloop_api);
485     assert(r == 0);
486     pa_signal_new(SIGINT, exit_signal_callback, NULL);
487     pa_signal_new(SIGTERM, exit_signal_callback, NULL);
488 #ifdef SIGUSR1
489     pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
490 #endif
491 #ifdef SIGPIPE
492     signal(SIGPIPE, SIG_IGN);
493 #endif
494     
495     if (!(stdio_event = mainloop_api->io_new(mainloop_api,
496                                              mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
497                                              mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
498                                              mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
499         fprintf(stderr, "source_io() failed.\n");
500         goto quit;
501     }
502
503     /* Create a new connection context */
504     if (!(context = pa_context_new(mainloop_api, client_name))) {
505         fprintf(stderr, "pa_context_new() failed.\n");
506         goto quit;
507     }
508
509     pa_context_set_state_callback(context, context_state_callback, NULL);
510
511     /* Connect the context */
512     pa_context_connect(context, server, 0, NULL);
513
514     /* Run the main loop */
515     if (pa_mainloop_run(m, &ret) < 0) {
516         fprintf(stderr, "pa_mainloop_run() failed.\n");
517         goto quit;
518     }
519     
520 quit:
521     if (stream)
522         pa_stream_unref(stream);
523
524     if (context)
525         pa_context_unref(context);
526
527     if (stdio_event) {
528         assert(mainloop_api);
529         mainloop_api->io_free(stdio_event);
530     }
531     
532     if (m) {
533         pa_signal_done();
534         pa_mainloop_free(m);
535     }
536
537     free(buffer);
538
539     free(server);
540     free(device);
541     free(client_name);
542     free(stream_name);
543     
544     return ret;
545 }