cf2f51c392d052f0420d9b8ea345c07accb13ab5
[profile/ivi/pulseaudio-panda.git] / polyp / pactl.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 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 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 <limits.h>
34 #include <getopt.h>
35
36 #include <sndfile.h>
37
38 #include <polyp/polyplib.h>
39 #include <polyp/polyplib-error.h>
40 #include <polyp/mainloop.h>
41 #include <polyp/mainloop-signal.h>
42 #include <polyp/sample.h>
43
44 #if PA_API_VERSION != PA_API_VERSION_0_6
45 #error Invalid Polypaudio API version
46 #endif
47
48 #define BUFSIZE 1024
49
50 static struct pa_context *context = NULL;
51 static struct pa_mainloop_api *mainloop_api = NULL;
52
53 static char *device = NULL, *sample_name = NULL;
54
55 static SNDFILE *sndfile = NULL;
56 static struct pa_stream *sample_stream = NULL;
57 static struct pa_sample_spec sample_spec;
58 static size_t sample_length = 0;
59
60 static int actions = 1;
61
62 static int nl = 0;
63
64 static enum {
65     NONE,
66     EXIT,
67     STAT,
68     UPLOAD_SAMPLE,
69     PLAY_SAMPLE,
70     REMOVE_SAMPLE,
71     LIST
72 } action = NONE;
73
74 static void quit(int ret) {
75     assert(mainloop_api);
76     mainloop_api->quit(mainloop_api, ret);
77 }
78
79
80 static void context_drain_complete(struct pa_context *c, void *userdata) {
81     pa_context_disconnect(c);
82 }
83
84 static void drain(void) {
85     struct pa_operation *o;
86     if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
87         pa_context_disconnect(context);
88     else
89         pa_operation_unref(o);
90 }
91
92
93 static void complete_action(void) {
94     assert(actions > 0);
95
96     if (!(--actions))
97         drain();
98 }
99
100 static void stat_callback(struct pa_context *c, const struct pa_stat_info *i, void *userdata) {
101     char s[128];
102     if (!i) {
103         fprintf(stderr, "Failed to get statistics: %s\n", pa_strerror(pa_context_errno(c)));
104         quit(1);
105         return;
106     }
107
108     pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
109     printf("Currently in use: %u blocks containing %s bytes total.\n", i->memblock_total, s);
110
111     pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
112     printf("Allocated during whole lifetime: %u blocks containing %s bytes total.\n", i->memblock_allocated, s);
113
114     pa_bytes_snprint(s, sizeof(s), i->scache_size);
115     printf("Sample cache size: %s\n", s);
116     
117     complete_action();
118 }
119
120 static void get_server_info_callback(struct pa_context *c, const struct pa_server_info *i, void *useerdata) {
121     char s[PA_SAMPLE_SPEC_SNPRINT_MAX];
122     
123     if (!i) {
124         fprintf(stderr, "Failed to get server information: %s\n", pa_strerror(pa_context_errno(c)));
125         quit(1);
126         return;
127     }
128
129     pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
130
131     printf("User name: %s\n"
132            "Host Name: %s\n"
133            "Server Name: %s\n"
134            "Server Version: %s\n"
135            "Default Sample Specification: %s\n"
136            "Default Sink: %s\n"
137            "Default Source: %s\n",
138            i->user_name,
139            i->host_name,
140            i->server_name,
141            i->server_version,
142            s,
143            i->default_sink_name,
144            i->default_source_name);
145
146     complete_action();
147 }
148
149 static void get_sink_info_callback(struct pa_context *c, const struct pa_sink_info *i, int is_last, void *userdata) {
150     char s[PA_SAMPLE_SPEC_SNPRINT_MAX];
151
152     if (is_last < 0) {
153         fprintf(stderr, "Failed to get sink information: %s\n", pa_strerror(pa_context_errno(c)));
154         quit(1);
155         return;
156     }
157
158     if (is_last) {
159         complete_action();
160         return;
161     }
162     
163     assert(i);
164
165     if (nl)
166         printf("\n");
167     nl = 1;
168
169     pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
170     
171     printf("*** Sink #%u ***\n"
172            "Name: %s\n"
173            "Description: %s\n"
174            "Sample Specification: %s\n"
175            "Owner Module: %u\n"
176            "Volume: 0x%03x (%0.2f dB)\n"
177            "Monitor Source: %u\n"
178            "Latency: %0.0f usec\n",
179            i->index,
180            i->name,
181            i->description,
182            s,
183            i->owner_module,
184            i->volume, pa_volume_to_dB(i->volume),
185            i->monitor_source,
186            (double) i->latency);
187 }
188
189 static void get_source_info_callback(struct pa_context *c, const struct pa_source_info *i, int is_last, void *userdata) {
190     char s[PA_SAMPLE_SPEC_SNPRINT_MAX], t[32];
191
192     if (is_last < 0) {
193         fprintf(stderr, "Failed to get source information: %s\n", pa_strerror(pa_context_errno(c)));
194         quit(1);
195         return;
196     }
197
198     if (is_last) {
199         complete_action();
200         return;
201     }
202     
203     assert(i);
204
205     if (nl)
206         printf("\n");
207     nl = 1;
208
209     snprintf(t, sizeof(t), "%u", i->monitor_of_sink);
210     
211     pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
212     
213     printf("*** Source #%u ***\n"
214            "Name: %s\n"
215            "Description: %s\n"
216            "Sample Specification: %s\n"
217            "Owner Module: %u\n"
218            "Monitor of Sink: %s\n"
219            "Latency: %0.0f usec\n",
220            i->index,
221            i->name,
222            i->description,
223            s,
224            i->owner_module,
225            i->monitor_of_sink != PA_INVALID_INDEX ? t : "no",
226            (double) i->latency);
227 }
228
229 static void get_module_info_callback(struct pa_context *c, const struct pa_module_info *i, int is_last, void *userdata) {
230     char t[32];
231
232     if (is_last < 0) {
233         fprintf(stderr, "Failed to get module information: %s\n", pa_strerror(pa_context_errno(c)));
234         quit(1);
235         return;
236     }
237
238     if (is_last) {
239         complete_action();
240         return;
241     }
242     
243     assert(i);
244
245     if (nl)
246         printf("\n");
247     nl = 1;
248
249     snprintf(t, sizeof(t), "%u", i->n_used);
250     
251     printf("*** Module #%u ***\n"
252            "Name: %s\n"
253            "Argument: %s\n"
254            "Usage counter: %s\n"
255            "Auto unload: %s\n",
256            i->index,
257            i->name,
258            i->argument,
259            i->n_used != PA_INVALID_INDEX ? t : "n/a",
260            i->auto_unload ? "yes" : "no");
261 }
262
263 static void get_client_info_callback(struct pa_context *c, const struct pa_client_info *i, int is_last, void *userdata) {
264     char t[32];
265
266     if (is_last < 0) {
267         fprintf(stderr, "Failed to get client information: %s\n", pa_strerror(pa_context_errno(c)));
268         quit(1);
269         return;
270     }
271
272     if (is_last) {
273         complete_action();
274         return;
275     }
276     
277     assert(i);
278
279     if (nl)
280         printf("\n");
281     nl = 1;
282
283     snprintf(t, sizeof(t), "%u", i->owner_module);
284     
285     printf("*** Client #%u ***\n"
286            "Name: %s\n"
287            "Owner Module: %s\n"
288            "Protocol Name: %s\n",
289            i->index,
290            i->name,
291            i->owner_module != PA_INVALID_INDEX ? t : "n/a",
292            i->protocol_name);
293 }
294
295 static void get_sink_input_info_callback(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) {
296     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX];
297
298     if (is_last < 0) {
299         fprintf(stderr, "Failed to get sink input information: %s\n", pa_strerror(pa_context_errno(c)));
300         quit(1);
301         return;
302     }
303
304     if (is_last) {
305         complete_action();
306         return;
307     }
308     
309     assert(i);
310
311     if (nl)
312         printf("\n");
313     nl = 1;
314
315     pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
316     snprintf(t, sizeof(t), "%u", i->owner_module);
317     snprintf(k, sizeof(k), "%u", i->client);
318     
319     printf("*** Sink Input #%u ***\n"
320            "Name: %s\n"
321            "Owner Module: %s\n"
322            "Client: %s\n"
323            "Sink: %u\n"
324            "Sample Specification: %s\n"
325            "Volume: 0x%03x (%0.2f dB)\n"
326            "Buffer Latency: %0.0f usec\n"
327            "Sink Latency: %0.0f usec\n",
328            i->index,
329            i->name,
330            i->owner_module != PA_INVALID_INDEX ? t : "n/a",
331            i->client != PA_INVALID_INDEX ? k : "n/a",
332            i->sink,
333            s,
334            i->volume, pa_volume_to_dB(i->volume),
335            (double) i->buffer_usec,
336            (double) i->sink_usec);
337 }
338
339 static void get_source_output_info_callback(struct pa_context *c, const struct pa_source_output_info *i, int is_last, void *userdata) {
340     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX];
341
342     if (is_last < 0) {
343         fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c)));
344         quit(1);
345         return;
346     }
347
348     if (is_last) {
349         complete_action();
350         return;
351     }
352     
353     assert(i);
354
355     if (nl)
356         printf("\n");
357     nl = 1;
358
359     pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
360     snprintf(t, sizeof(t), "%u", i->owner_module);
361     snprintf(k, sizeof(k), "%u", i->client);
362     
363     printf("*** Source Output #%u ***\n"
364            "Name: %s\n"
365            "Owner Module: %s\n"
366            "Client: %s\n"
367            "Source: %u\n"
368            "Sample Specification: %s\n"
369            "Buffer Latency: %0.0f usec\n"
370            "Source Latency: %0.0f usec\n",
371            i->index,
372            i->name,
373            i->owner_module != PA_INVALID_INDEX ? t : "n/a",
374            i->client != PA_INVALID_INDEX ? k : "n/a",
375            i->source,
376            s,
377            (double) i->buffer_usec,
378            (double) i->source_usec);
379 }
380
381 static void get_sample_info_callback(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata) {
382     char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX];
383
384     if (is_last < 0) {
385         fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c)));
386         quit(1);
387         return;
388     }
389
390     if (is_last) {
391         complete_action();
392         return;
393     }
394     
395     assert(i);
396
397     if (nl)
398         printf("\n");
399     nl = 1;
400
401     pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
402     pa_bytes_snprint(t, sizeof(t), i->bytes);
403     
404     printf("*** Sample #%u ***\n"
405            "Name: %s\n"
406            "Volume: 0x%03x (%0.2f dB)\n"
407            "Sample Specification: %s\n"
408            "Duration: %0.1fs\n"
409            "Size: %s\n"
410            "Lazy: %s\n"
411            "Filename: %s\n",
412            i->index,
413            i->name,
414            i->volume, pa_volume_to_dB(i->volume),
415            pa_sample_spec_valid(&i->sample_spec) ? s : "n/a",
416            (double) i->duration/1000000,
417            t,
418            i->lazy ? "yes" : "no",
419            i->filename ? i->filename : "n/a");
420 }
421
422 static void get_autoload_info_callback(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata) {
423     if (is_last < 0) {
424         fprintf(stderr, "Failed to get autoload information: %s\n", pa_strerror(pa_context_errno(c)));
425         quit(1);
426         return;
427     }
428
429     if (is_last) {
430         complete_action();
431         return;
432     }
433     
434     assert(i);
435
436     if (nl)
437         printf("\n");
438     nl = 1;
439
440     printf("*** Autoload Entry ***\n"
441            "Name: %s\n"
442            "Type: %s\n"
443            "Module: %s\n"
444            "Argument: %s\n",
445            i->name,
446            i->type == PA_AUTOLOAD_SINK ? "sink" : "source",
447            i->module,
448            i->argument);
449 }
450
451 static void simple_callback(struct pa_context *c, int success, void *userdata) {
452     if (!success) {
453         fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c)));
454         quit(1);
455         return;
456     }
457
458     complete_action();
459 }
460
461 static void stream_state_callback(struct pa_stream *s, void *userdata) {
462     assert(s);
463
464     switch (pa_stream_get_state(s)) {
465         case PA_STREAM_CREATING:
466         case PA_STREAM_READY:
467             break;
468             
469         case PA_STREAM_TERMINATED:
470             drain();
471             break;
472             
473         case PA_STREAM_FAILED:
474         default:
475             fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
476             quit(1);
477     }
478 }
479
480 static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
481     sf_count_t l;
482     float *d;
483     assert(s && length && sndfile);
484
485     d = malloc(length);
486     assert(d);
487
488     assert(sample_length >= length);
489     l = length/pa_frame_size(&sample_spec);
490
491     if ((sf_readf_float(sndfile, d, l)) != l) {
492         free(d);
493         fprintf(stderr, "Premature end of file\n");
494         quit(1);
495     }
496     
497     pa_stream_write(s, d, length, free, 0);
498
499     sample_length -= length;
500
501     if (sample_length  <= 0) {
502         pa_stream_set_write_callback(sample_stream, NULL, NULL);
503         pa_stream_finish_upload(sample_stream);
504     }
505 }
506
507 static void context_state_callback(struct pa_context *c, void *userdata) {
508     assert(c);
509     switch (pa_context_get_state(c)) {
510         case PA_CONTEXT_CONNECTING:
511         case PA_CONTEXT_AUTHORIZING:
512         case PA_CONTEXT_SETTING_NAME:
513             break;
514
515         case PA_CONTEXT_READY:
516             switch (action) {
517                 case STAT:
518                     actions = 2;
519                     pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
520                     pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
521                     break;
522
523                 case PLAY_SAMPLE: 
524                     pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
525                     break;
526
527                 case REMOVE_SAMPLE:
528                     pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
529                     break;
530
531                 case UPLOAD_SAMPLE:
532                     sample_stream = pa_stream_new(c, sample_name, &sample_spec);
533                     assert(sample_stream);
534                     
535                     pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
536                     pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
537                     pa_stream_connect_upload(sample_stream, sample_length);
538                     break;
539                     
540                 case EXIT:
541                     pa_context_exit_daemon(c);
542                     drain();
543
544                 case LIST:
545                     actions = 8;
546                     pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
547                     pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
548                     pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
549                     pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
550                     pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL)); 
551                     pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
552                     pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
553                     pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL));
554                     break;
555
556                 default:
557                     assert(0);
558             }
559             break;
560
561         case PA_CONTEXT_TERMINATED:
562             quit(0);
563             break;
564
565         case PA_CONTEXT_FAILED:
566         default:
567             fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
568             quit(1);
569     }
570 }
571
572 static void exit_signal_callback(struct pa_mainloop_api *m, struct pa_signal_event *e, int sig, void *userdata) {
573     fprintf(stderr, "Got SIGINT, exiting.\n");
574     quit(0);
575 }
576
577 static void help(const char *argv0) {
578
579     printf("%s [options] stat\n"
580            "%s [options] list\n"
581            "%s [options] exit\n"
582            "%s [options] upload-sample FILENAME [NAME]\n"
583            "%s [options] play-sample NAME [SINK]\n"
584            "%s [options] remove-sample NAME\n\n"
585            "  -h, --help                            Show this help\n"
586            "      --version                         Show version\n\n"
587            "  -s, --server=SERVER                   The name of the server to connect to\n"
588            "  -n, --client-name=NAME                How to call this client on the server\n",
589            argv0, argv0, argv0, argv0, argv0, argv0);
590 }
591
592 enum { ARG_VERSION = 256 };
593
594 int main(int argc, char *argv[]) {
595     struct pa_mainloop* m = NULL;
596     char tmp[PATH_MAX];
597     int ret = 1, r, c;
598     char *server = NULL, *client_name = NULL, *bn;
599
600     static const struct option long_options[] = {
601         {"server",      1, NULL, 's'},
602         {"client-name", 1, NULL, 'n'},
603         {"version",     0, NULL, ARG_VERSION},
604         {"help",        0, NULL, 'h'},
605         {NULL,          0, NULL, 0}
606     };
607
608     if (!(bn = strrchr(argv[0], '/')))
609         bn = argv[0];
610     else
611         bn++;
612     
613     while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
614         switch (c) {
615             case 'h' :
616                 help(bn);
617                 ret = 0;
618                 goto quit;
619                 
620             case ARG_VERSION:
621                 printf("pactl "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
622                 ret = 0;
623                 goto quit;
624
625             case 's':
626                 free(server);
627                 server = strdup(optarg);
628                 break;
629
630             case 'n':
631                 free(client_name);
632                 client_name = strdup(optarg);
633                 break;
634
635             default:
636                 goto quit;
637         }
638     }
639
640     if (!client_name)
641         client_name = strdup(bn);
642     
643     if (optind < argc) {
644         if (!strcmp(argv[optind], "stat"))
645             action = STAT;
646         else if (!strcmp(argv[optind], "exit"))
647             action = EXIT;
648         else if (!strcmp(argv[optind], "list"))
649             action = LIST;
650         else if (!strcmp(argv[optind], "upload-sample")) {
651             struct SF_INFO sfinfo;
652             action = UPLOAD_SAMPLE;
653
654             if (optind+1 >= argc) {
655                 fprintf(stderr, "Please specify a sample file to load\n");
656                 goto quit;
657             }
658
659             if (optind+2 < argc)
660                 sample_name = strdup(argv[optind+2]);
661             else {
662                 char *f = strrchr(argv[optind+1], '/');
663                 size_t n;
664                 if (f)
665                     f++;
666                 else
667                     f = argv[optind];
668
669                 n = strcspn(f, ".");
670                 strncpy(tmp, f, n);
671                 tmp[n] = 0;
672                 sample_name = strdup(tmp);
673             }
674             
675             memset(&sfinfo, 0, sizeof(sfinfo));
676             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfinfo))) {
677                 fprintf(stderr, "Failed to open sound file.\n");
678                 goto quit;
679             }
680             
681             sample_spec.format =  PA_SAMPLE_FLOAT32;
682             sample_spec.rate = sfinfo.samplerate;
683             sample_spec.channels = sfinfo.channels;
684
685             sample_length = sfinfo.frames*pa_frame_size(&sample_spec);
686         } else if (!strcmp(argv[optind], "play-sample")) {
687             action = PLAY_SAMPLE;
688             if (optind+1 >= argc) {
689                 fprintf(stderr, "You have to specify a sample name to play\n");
690                 goto quit;
691             }
692
693             sample_name = strdup(argv[optind+1]);
694
695             if (optind+2 < argc)
696                 device = strdup(argv[optind+2]);
697             
698         } else if (!strcmp(argv[optind], "remove-sample")) {
699             action = REMOVE_SAMPLE;
700             if (optind+1 >= argc) {
701                 fprintf(stderr, "You have to specify a sample name to remove\n");
702                 goto quit;
703             }
704
705             sample_name = strdup(argv[optind+1]);
706         }
707     }
708
709     if (action == NONE) {
710         fprintf(stderr, "No valid command specified.\n");
711         goto quit;
712     }
713
714     if (!(m = pa_mainloop_new())) {
715         fprintf(stderr, "pa_mainloop_new() failed.\n");
716         goto quit;
717     }
718
719     mainloop_api = pa_mainloop_get_api(m);
720
721     r = pa_signal_init(mainloop_api);
722     assert(r == 0);
723     pa_signal_new(SIGINT, exit_signal_callback, NULL);
724     signal(SIGPIPE, SIG_IGN);
725     
726     if (!(context = pa_context_new(mainloop_api, client_name))) {
727         fprintf(stderr, "pa_context_new() failed.\n");
728         goto quit;
729     }
730
731     pa_context_set_state_callback(context, context_state_callback, NULL);
732     pa_context_connect(context, server, 1, NULL);
733
734     if (pa_mainloop_run(m, &ret) < 0) {
735         fprintf(stderr, "pa_mainloop_run() failed.\n");
736         goto quit;
737     }
738
739 quit:
740     if (sample_stream)
741         pa_stream_unref(sample_stream);
742
743     if (context)
744         pa_context_unref(context);
745
746     if (m) {
747         pa_signal_done();
748         pa_mainloop_free(m);
749     }
750     
751     if (sndfile)
752         sf_close(sndfile);
753
754     free(server);
755     free(device);
756     free(sample_name);
757
758     return ret;
759 }