Make the whole stuff LGPL only
[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 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 <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 != 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 #%u ***\n"
441            "Name: %s\n"
442            "Type: %s\n"
443            "Module: %s\n"
444            "Argument: %s\n",
445            i->index,
446            i->name,
447            i->type == PA_AUTOLOAD_SINK ? "sink" : "source",
448            i->module,
449            i->argument);
450 }
451
452 static void simple_callback(struct pa_context *c, int success, void *userdata) {
453     if (!success) {
454         fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c)));
455         quit(1);
456         return;
457     }
458
459     complete_action();
460 }
461
462 static void stream_state_callback(struct pa_stream *s, void *userdata) {
463     assert(s);
464
465     switch (pa_stream_get_state(s)) {
466         case PA_STREAM_CREATING:
467         case PA_STREAM_READY:
468             break;
469             
470         case PA_STREAM_TERMINATED:
471             drain();
472             break;
473             
474         case PA_STREAM_FAILED:
475         default:
476             fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
477             quit(1);
478     }
479 }
480
481 static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
482     sf_count_t l;
483     float *d;
484     assert(s && length && sndfile);
485
486     d = malloc(length);
487     assert(d);
488
489     assert(sample_length >= length);
490     l = length/pa_frame_size(&sample_spec);
491
492     if ((sf_readf_float(sndfile, d, l)) != l) {
493         free(d);
494         fprintf(stderr, "Premature end of file\n");
495         quit(1);
496     }
497     
498     pa_stream_write(s, d, length, free, 0);
499
500     sample_length -= length;
501
502     if (sample_length  <= 0) {
503         pa_stream_set_write_callback(sample_stream, NULL, NULL);
504         pa_stream_finish_upload(sample_stream);
505     }
506 }
507
508 static void context_state_callback(struct pa_context *c, void *userdata) {
509     assert(c);
510     switch (pa_context_get_state(c)) {
511         case PA_CONTEXT_CONNECTING:
512         case PA_CONTEXT_AUTHORIZING:
513         case PA_CONTEXT_SETTING_NAME:
514             break;
515
516         case PA_CONTEXT_READY:
517             switch (action) {
518                 case STAT:
519                     actions = 2;
520                     pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
521                     pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
522                     break;
523
524                 case PLAY_SAMPLE: 
525                     pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
526                     break;
527
528                 case REMOVE_SAMPLE:
529                     pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
530                     break;
531
532                 case UPLOAD_SAMPLE:
533                     sample_stream = pa_stream_new(c, sample_name, &sample_spec);
534                     assert(sample_stream);
535                     
536                     pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
537                     pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
538                     pa_stream_connect_upload(sample_stream, sample_length);
539                     break;
540                     
541                 case EXIT:
542                     pa_context_exit_daemon(c);
543                     drain();
544
545                 case LIST:
546                     actions = 8;
547                     pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
548                     pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
549                     pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
550                     pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
551                     pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL)); 
552                     pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
553                     pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
554                     pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL));
555                     break;
556
557                 default:
558                     assert(0);
559             }
560             break;
561
562         case PA_CONTEXT_TERMINATED:
563             quit(0);
564             break;
565
566         case PA_CONTEXT_FAILED:
567         default:
568             fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
569             quit(1);
570     }
571 }
572
573 static void exit_signal_callback(struct pa_mainloop_api *m, struct pa_signal_event *e, int sig, void *userdata) {
574     fprintf(stderr, "Got SIGINT, exiting.\n");
575     quit(0);
576 }
577
578 static void help(const char *argv0) {
579
580     printf("%s [options] stat\n"
581            "%s [options] list\n"
582            "%s [options] exit\n"
583            "%s [options] upload-sample FILENAME [NAME]\n"
584            "%s [options] play-sample NAME [SINK]\n"
585            "%s [options] remove-sample NAME\n\n"
586            "  -h, --help                            Show this help\n"
587            "      --version                         Show version\n\n"
588            "  -s, --server=SERVER                   The name of the server to connect to\n"
589            "  -n, --client-name=NAME                How to call this client on the server\n",
590            argv0, argv0, argv0, argv0, argv0, argv0);
591 }
592
593 enum { ARG_VERSION = 256 };
594
595 int main(int argc, char *argv[]) {
596     struct pa_mainloop* m = NULL;
597     char tmp[PATH_MAX];
598     int ret = 1, r, c;
599     char *server = NULL, *client_name = NULL, *bn;
600
601     static const struct option long_options[] = {
602         {"server",      1, NULL, 's'},
603         {"client-name", 1, NULL, 'n'},
604         {"version",     0, NULL, ARG_VERSION},
605         {"help",        0, NULL, 'h'},
606         {NULL,          0, NULL, 0}
607     };
608
609     if (!(bn = strrchr(argv[0], '/')))
610         bn = argv[0];
611     else
612         bn++;
613     
614     while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
615         switch (c) {
616             case 'h' :
617                 help(bn);
618                 ret = 0;
619                 goto quit;
620                 
621             case ARG_VERSION:
622                 printf("pactl "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
623                 ret = 0;
624                 goto quit;
625
626             case 's':
627                 free(server);
628                 server = strdup(optarg);
629                 break;
630
631             case 'n':
632                 free(client_name);
633                 client_name = strdup(optarg);
634                 break;
635
636             default:
637                 goto quit;
638         }
639     }
640
641     if (!client_name)
642         client_name = strdup(bn);
643     
644     if (optind < argc) {
645         if (!strcmp(argv[optind], "stat"))
646             action = STAT;
647         else if (!strcmp(argv[optind], "exit"))
648             action = EXIT;
649         else if (!strcmp(argv[optind], "list"))
650             action = LIST;
651         else if (!strcmp(argv[optind], "upload-sample")) {
652             struct SF_INFO sfinfo;
653             action = UPLOAD_SAMPLE;
654
655             if (optind+1 >= argc) {
656                 fprintf(stderr, "Please specify a sample file to load\n");
657                 goto quit;
658             }
659
660             if (optind+2 < argc)
661                 sample_name = strdup(argv[optind+2]);
662             else {
663                 char *f = strrchr(argv[optind+1], '/');
664                 size_t n;
665                 if (f)
666                     f++;
667                 else
668                     f = argv[optind];
669
670                 n = strcspn(f, ".");
671                 strncpy(tmp, f, n);
672                 tmp[n] = 0;
673                 sample_name = strdup(tmp);
674             }
675             
676             memset(&sfinfo, 0, sizeof(sfinfo));
677             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfinfo))) {
678                 fprintf(stderr, "Failed to open sound file.\n");
679                 goto quit;
680             }
681             
682             sample_spec.format =  PA_SAMPLE_FLOAT32;
683             sample_spec.rate = sfinfo.samplerate;
684             sample_spec.channels = sfinfo.channels;
685
686             sample_length = sfinfo.frames*pa_frame_size(&sample_spec);
687         } else if (!strcmp(argv[optind], "play-sample")) {
688             action = PLAY_SAMPLE;
689             if (optind+1 >= argc) {
690                 fprintf(stderr, "You have to specify a sample name to play\n");
691                 goto quit;
692             }
693
694             sample_name = strdup(argv[optind+1]);
695
696             if (optind+2 < argc)
697                 device = strdup(argv[optind+2]);
698             
699         } else if (!strcmp(argv[optind], "remove-sample")) {
700             action = REMOVE_SAMPLE;
701             if (optind+1 >= argc) {
702                 fprintf(stderr, "You have to specify a sample name to remove\n");
703                 goto quit;
704             }
705
706             sample_name = strdup(argv[optind+1]);
707         }
708     }
709
710     if (action == NONE) {
711         fprintf(stderr, "No valid command specified.\n");
712         goto quit;
713     }
714
715     if (!(m = pa_mainloop_new())) {
716         fprintf(stderr, "pa_mainloop_new() failed.\n");
717         goto quit;
718     }
719
720     mainloop_api = pa_mainloop_get_api(m);
721
722     r = pa_signal_init(mainloop_api);
723     assert(r == 0);
724     pa_signal_new(SIGINT, exit_signal_callback, NULL);
725     signal(SIGPIPE, SIG_IGN);
726     
727     if (!(context = pa_context_new(mainloop_api, client_name))) {
728         fprintf(stderr, "pa_context_new() failed.\n");
729         goto quit;
730     }
731
732     pa_context_set_state_callback(context, context_state_callback, NULL);
733     pa_context_connect(context, server, 1, NULL);
734
735     if (pa_mainloop_run(m, &ret) < 0) {
736         fprintf(stderr, "pa_mainloop_run() failed.\n");
737         goto quit;
738     }
739
740 quit:
741     if (sample_stream)
742         pa_stream_unref(sample_stream);
743
744     if (context)
745         pa_context_unref(context);
746
747     if (m) {
748         pa_signal_done();
749         pa_mainloop_free(m);
750     }
751     
752     if (sndfile)
753         sf_close(sndfile);
754
755     free(server);
756     free(device);
757     free(sample_name);
758
759     return ret;
760 }