* new tool pacmd
[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 != 7
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            "Resample method: %s\n",
329            i->index,
330            i->name,
331            i->owner_module != PA_INVALID_INDEX ? t : "n/a",
332            i->client != PA_INVALID_INDEX ? k : "n/a",
333            i->sink,
334            s,
335            i->volume, pa_volume_to_dB(i->volume),
336            (double) i->buffer_usec,
337            (double) i->sink_usec,
338            i->resample_method ? i->resample_method : "n/a");
339 }
340
341 static void get_source_output_info_callback(struct pa_context *c, const struct pa_source_output_info *i, int is_last, void *userdata) {
342     char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX];
343
344     if (is_last < 0) {
345         fprintf(stderr, "Failed to get source output information: %s\n", pa_strerror(pa_context_errno(c)));
346         quit(1);
347         return;
348     }
349
350     if (is_last) {
351         complete_action();
352         return;
353     }
354     
355     assert(i);
356
357     if (nl)
358         printf("\n");
359     nl = 1;
360
361     pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
362     snprintf(t, sizeof(t), "%u", i->owner_module);
363     snprintf(k, sizeof(k), "%u", i->client);
364     
365     printf("*** Source Output #%u ***\n"
366            "Name: %s\n"
367            "Owner Module: %s\n"
368            "Client: %s\n"
369            "Source: %u\n"
370            "Sample Specification: %s\n"
371            "Buffer Latency: %0.0f usec\n"
372            "Source Latency: %0.0f usec\n"
373            "Resample method: %s\n",
374            i->index,
375            i->name,
376            i->owner_module != PA_INVALID_INDEX ? t : "n/a",
377            i->client != PA_INVALID_INDEX ? k : "n/a",
378            i->source,
379            s,
380            (double) i->buffer_usec,
381            (double) i->source_usec,
382            i->resample_method ? i->resample_method : "n/a");
383 }
384
385 static void get_sample_info_callback(struct pa_context *c, const struct pa_sample_info *i, int is_last, void *userdata) {
386     char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX];
387
388     if (is_last < 0) {
389         fprintf(stderr, "Failed to get sample information: %s\n", pa_strerror(pa_context_errno(c)));
390         quit(1);
391         return;
392     }
393
394     if (is_last) {
395         complete_action();
396         return;
397     }
398     
399     assert(i);
400
401     if (nl)
402         printf("\n");
403     nl = 1;
404
405     pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
406     pa_bytes_snprint(t, sizeof(t), i->bytes);
407     
408     printf("*** Sample #%u ***\n"
409            "Name: %s\n"
410            "Volume: 0x%03x (%0.2f dB)\n"
411            "Sample Specification: %s\n"
412            "Duration: %0.1fs\n"
413            "Size: %s\n"
414            "Lazy: %s\n"
415            "Filename: %s\n",
416            i->index,
417            i->name,
418            i->volume, pa_volume_to_dB(i->volume),
419            pa_sample_spec_valid(&i->sample_spec) ? s : "n/a",
420            (double) i->duration/1000000,
421            t,
422            i->lazy ? "yes" : "no",
423            i->filename ? i->filename : "n/a");
424 }
425
426 static void get_autoload_info_callback(struct pa_context *c, const struct pa_autoload_info *i, int is_last, void *userdata) {
427     if (is_last < 0) {
428         fprintf(stderr, "Failed to get autoload information: %s\n", pa_strerror(pa_context_errno(c)));
429         quit(1);
430         return;
431     }
432
433     if (is_last) {
434         complete_action();
435         return;
436     }
437     
438     assert(i);
439
440     if (nl)
441         printf("\n");
442     nl = 1;
443
444     printf("*** Autoload Entry #%u ***\n"
445            "Name: %s\n"
446            "Type: %s\n"
447            "Module: %s\n"
448            "Argument: %s\n",
449            i->index,
450            i->name,
451            i->type == PA_AUTOLOAD_SINK ? "sink" : "source",
452            i->module,
453            i->argument);
454 }
455
456 static void simple_callback(struct pa_context *c, int success, void *userdata) {
457     if (!success) {
458         fprintf(stderr, "Failure: %s\n", pa_strerror(pa_context_errno(c)));
459         quit(1);
460         return;
461     }
462
463     complete_action();
464 }
465
466 static void stream_state_callback(struct pa_stream *s, void *userdata) {
467     assert(s);
468
469     switch (pa_stream_get_state(s)) {
470         case PA_STREAM_CREATING:
471         case PA_STREAM_READY:
472             break;
473             
474         case PA_STREAM_TERMINATED:
475             drain();
476             break;
477             
478         case PA_STREAM_FAILED:
479         default:
480             fprintf(stderr, "Failed to upload sample: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
481             quit(1);
482     }
483 }
484
485 static void stream_write_callback(struct pa_stream *s, size_t length, void *userdata) {
486     sf_count_t l;
487     float *d;
488     assert(s && length && sndfile);
489
490     d = malloc(length);
491     assert(d);
492
493     assert(sample_length >= length);
494     l = length/pa_frame_size(&sample_spec);
495
496     if ((sf_readf_float(sndfile, d, l)) != l) {
497         free(d);
498         fprintf(stderr, "Premature end of file\n");
499         quit(1);
500     }
501     
502     pa_stream_write(s, d, length, free, 0);
503
504     sample_length -= length;
505
506     if (sample_length  <= 0) {
507         pa_stream_set_write_callback(sample_stream, NULL, NULL);
508         pa_stream_finish_upload(sample_stream);
509     }
510 }
511
512 static void context_state_callback(struct pa_context *c, void *userdata) {
513     assert(c);
514     switch (pa_context_get_state(c)) {
515         case PA_CONTEXT_CONNECTING:
516         case PA_CONTEXT_AUTHORIZING:
517         case PA_CONTEXT_SETTING_NAME:
518             break;
519
520         case PA_CONTEXT_READY:
521             switch (action) {
522                 case STAT:
523                     actions = 2;
524                     pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
525                     pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
526                     break;
527
528                 case PLAY_SAMPLE: 
529                     pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
530                     break;
531
532                 case REMOVE_SAMPLE:
533                     pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
534                     break;
535
536                 case UPLOAD_SAMPLE:
537                     sample_stream = pa_stream_new(c, sample_name, &sample_spec);
538                     assert(sample_stream);
539                     
540                     pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
541                     pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
542                     pa_stream_connect_upload(sample_stream, sample_length);
543                     break;
544                     
545                 case EXIT:
546                     pa_context_exit_daemon(c);
547                     drain();
548
549                 case LIST:
550                     actions = 8;
551                     pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
552                     pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
553                     pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
554                     pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
555                     pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL)); 
556                     pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
557                     pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
558                     pa_operation_unref(pa_context_get_autoload_info_list(c, get_autoload_info_callback, NULL));
559                     break;
560
561                 default:
562                     assert(0);
563             }
564             break;
565
566         case PA_CONTEXT_TERMINATED:
567             quit(0);
568             break;
569
570         case PA_CONTEXT_FAILED:
571         default:
572             fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
573             quit(1);
574     }
575 }
576
577 static void exit_signal_callback(struct pa_mainloop_api *m, struct pa_signal_event *e, int sig, void *userdata) {
578     fprintf(stderr, "Got SIGINT, exiting.\n");
579     quit(0);
580 }
581
582 static void help(const char *argv0) {
583
584     printf("%s [options] stat\n"
585            "%s [options] list\n"
586            "%s [options] exit\n"
587            "%s [options] upload-sample FILENAME [NAME]\n"
588            "%s [options] play-sample NAME [SINK]\n"
589            "%s [options] remove-sample NAME\n\n"
590            "  -h, --help                            Show this help\n"
591            "      --version                         Show version\n\n"
592            "  -s, --server=SERVER                   The name of the server to connect to\n"
593            "  -n, --client-name=NAME                How to call this client on the server\n",
594            argv0, argv0, argv0, argv0, argv0, argv0);
595 }
596
597 enum { ARG_VERSION = 256 };
598
599 int main(int argc, char *argv[]) {
600     struct pa_mainloop* m = NULL;
601     char tmp[PATH_MAX];
602     int ret = 1, r, c;
603     char *server = NULL, *client_name = NULL, *bn;
604
605     static const struct option long_options[] = {
606         {"server",      1, NULL, 's'},
607         {"client-name", 1, NULL, 'n'},
608         {"version",     0, NULL, ARG_VERSION},
609         {"help",        0, NULL, 'h'},
610         {NULL,          0, NULL, 0}
611     };
612
613     if (!(bn = strrchr(argv[0], '/')))
614         bn = argv[0];
615     else
616         bn++;
617     
618     while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
619         switch (c) {
620             case 'h' :
621                 help(bn);
622                 ret = 0;
623                 goto quit;
624                 
625             case ARG_VERSION:
626                 printf("pactl "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
627                 ret = 0;
628                 goto quit;
629
630             case 's':
631                 free(server);
632                 server = strdup(optarg);
633                 break;
634
635             case 'n':
636                 free(client_name);
637                 client_name = strdup(optarg);
638                 break;
639
640             default:
641                 goto quit;
642         }
643     }
644
645     if (!client_name)
646         client_name = strdup(bn);
647     
648     if (optind < argc) {
649         if (!strcmp(argv[optind], "stat"))
650             action = STAT;
651         else if (!strcmp(argv[optind], "exit"))
652             action = EXIT;
653         else if (!strcmp(argv[optind], "list"))
654             action = LIST;
655         else if (!strcmp(argv[optind], "upload-sample")) {
656             struct SF_INFO sfinfo;
657             action = UPLOAD_SAMPLE;
658
659             if (optind+1 >= argc) {
660                 fprintf(stderr, "Please specify a sample file to load\n");
661                 goto quit;
662             }
663
664             if (optind+2 < argc)
665                 sample_name = strdup(argv[optind+2]);
666             else {
667                 char *f = strrchr(argv[optind+1], '/');
668                 size_t n;
669                 if (f)
670                     f++;
671                 else
672                     f = argv[optind];
673
674                 n = strcspn(f, ".");
675                 strncpy(tmp, f, n);
676                 tmp[n] = 0;
677                 sample_name = strdup(tmp);
678             }
679             
680             memset(&sfinfo, 0, sizeof(sfinfo));
681             if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfinfo))) {
682                 fprintf(stderr, "Failed to open sound file.\n");
683                 goto quit;
684             }
685             
686             sample_spec.format =  PA_SAMPLE_FLOAT32;
687             sample_spec.rate = sfinfo.samplerate;
688             sample_spec.channels = sfinfo.channels;
689
690             sample_length = sfinfo.frames*pa_frame_size(&sample_spec);
691         } else if (!strcmp(argv[optind], "play-sample")) {
692             action = PLAY_SAMPLE;
693             if (optind+1 >= argc) {
694                 fprintf(stderr, "You have to specify a sample name to play\n");
695                 goto quit;
696             }
697
698             sample_name = strdup(argv[optind+1]);
699
700             if (optind+2 < argc)
701                 device = strdup(argv[optind+2]);
702             
703         } else if (!strcmp(argv[optind], "remove-sample")) {
704             action = REMOVE_SAMPLE;
705             if (optind+1 >= argc) {
706                 fprintf(stderr, "You have to specify a sample name to remove\n");
707                 goto quit;
708             }
709
710             sample_name = strdup(argv[optind+1]);
711         }
712     }
713
714     if (action == NONE) {
715         fprintf(stderr, "No valid command specified.\n");
716         goto quit;
717     }
718
719     if (!(m = pa_mainloop_new())) {
720         fprintf(stderr, "pa_mainloop_new() failed.\n");
721         goto quit;
722     }
723
724     mainloop_api = pa_mainloop_get_api(m);
725
726     r = pa_signal_init(mainloop_api);
727     assert(r == 0);
728     pa_signal_new(SIGINT, exit_signal_callback, NULL);
729     signal(SIGPIPE, SIG_IGN);
730     
731     if (!(context = pa_context_new(mainloop_api, client_name))) {
732         fprintf(stderr, "pa_context_new() failed.\n");
733         goto quit;
734     }
735
736     pa_context_set_state_callback(context, context_state_callback, NULL);
737     pa_context_connect(context, server, 1, NULL);
738
739     if (pa_mainloop_run(m, &ret) < 0) {
740         fprintf(stderr, "pa_mainloop_run() failed.\n");
741         goto quit;
742     }
743
744 quit:
745     if (sample_stream)
746         pa_stream_unref(sample_stream);
747
748     if (context)
749         pa_context_unref(context);
750
751     if (m) {
752         pa_signal_done();
753         pa_mainloop_free(m);
754     }
755     
756     if (sndfile)
757         sf_close(sndfile);
758
759     free(server);
760     free(device);
761     free(sample_name);
762
763     return ret;
764 }