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