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