]> code.delx.au - pulseaudio/blob - src/utils/pactl.c
pactl: Add short output format for list action
[pulseaudio] / src / utils / pactl.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5
6 PulseAudio 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.1 of the License,
9 or (at your option) any later version.
10
11 PulseAudio 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 PulseAudio; 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 #include <locale.h>
36
37 #include <sndfile.h>
38
39 #include <pulse/i18n.h>
40 #include <pulse/pulseaudio.h>
41
42 #include <pulsecore/macro.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/sndfile-util.h>
46
47 static pa_context *context = NULL;
48 static pa_mainloop_api *mainloop_api = NULL;
49
50 static char
51 *list_type = NULL,
52 *sample_name = NULL,
53 *sink_name = NULL,
54 *source_name = NULL,
55 *module_name = NULL,
56 *module_args = NULL,
57 *card_name = NULL,
58 *profile_name = NULL,
59 *port_name = NULL;
60
61 static uint32_t
62 sink_input_idx = PA_INVALID_INDEX,
63 source_output_idx = PA_INVALID_INDEX;
64
65 static pa_bool_t short_list_format = FALSE;
66 static uint32_t module_index;
67 static pa_bool_t suspend;
68 static pa_bool_t mute;
69 static pa_volume_t volume;
70 static enum volume_flags {
71 VOL_UINT = 0,
72 VOL_PERCENT = 1,
73 VOL_LINEAR = 2,
74 VOL_DECIBEL = 3,
75 VOL_ABSOLUTE = 0 << 4,
76 VOL_RELATIVE = 1 << 4,
77 } volume_flags;
78
79 static pa_proplist *proplist = NULL;
80
81 static SNDFILE *sndfile = NULL;
82 static pa_stream *sample_stream = NULL;
83 static pa_sample_spec sample_spec;
84 static pa_channel_map channel_map;
85 static size_t sample_length = 0;
86 static int actions = 1;
87
88 static pa_bool_t nl = FALSE;
89
90 static enum {
91 NONE,
92 EXIT,
93 STAT,
94 INFO,
95 UPLOAD_SAMPLE,
96 PLAY_SAMPLE,
97 REMOVE_SAMPLE,
98 LIST,
99 MOVE_SINK_INPUT,
100 MOVE_SOURCE_OUTPUT,
101 LOAD_MODULE,
102 UNLOAD_MODULE,
103 SUSPEND_SINK,
104 SUSPEND_SOURCE,
105 SET_CARD_PROFILE,
106 SET_SINK_PORT,
107 SET_SOURCE_PORT,
108 SET_SINK_VOLUME,
109 SET_SOURCE_VOLUME,
110 SET_SINK_INPUT_VOLUME,
111 SET_SINK_MUTE,
112 SET_SOURCE_MUTE,
113 SET_SINK_INPUT_MUTE,
114 SUBSCRIBE
115 } action = NONE;
116
117 static void quit(int ret) {
118 pa_assert(mainloop_api);
119 mainloop_api->quit(mainloop_api, ret);
120 }
121
122 static void context_drain_complete(pa_context *c, void *userdata) {
123 pa_context_disconnect(c);
124 }
125
126 static void drain(void) {
127 pa_operation *o;
128
129 if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
130 pa_context_disconnect(context);
131 else
132 pa_operation_unref(o);
133 }
134
135 static void complete_action(void) {
136 pa_assert(actions > 0);
137
138 if (!(--actions))
139 drain();
140 }
141
142 static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
143 char s[PA_BYTES_SNPRINT_MAX];
144 if (!i) {
145 pa_log(_("Failed to get statistics: %s"), pa_strerror(pa_context_errno(c)));
146 quit(1);
147 return;
148 }
149
150 pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
151 printf(_("Currently in use: %u blocks containing %s bytes total.\n"), i->memblock_total, s);
152
153 pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
154 printf(_("Allocated during whole lifetime: %u blocks containing %s bytes total.\n"), i->memblock_allocated, s);
155
156 pa_bytes_snprint(s, sizeof(s), i->scache_size);
157 printf(_("Sample cache size: %s\n"), s);
158
159 complete_action();
160 }
161
162 static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
163 char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
164
165 if (!i) {
166 pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
167 quit(1);
168 return;
169 }
170
171 printf(_("Server String: %s\n"
172 "Library Protocol Version: %u\n"
173 "Server Protocol Version: %u\n"
174 "Is Local: %s\n"
175 "Client Index: %u\n"
176 "Tile Size: %zu\n"),
177 pa_context_get_server(c),
178 pa_context_get_protocol_version(c),
179 pa_context_get_server_protocol_version(c),
180 pa_yes_no(pa_context_is_local(c)),
181 pa_context_get_index(c),
182 pa_context_get_tile_size(c, NULL));
183
184 pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
185 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
186
187 printf(_("User Name: %s\n"
188 "Host Name: %s\n"
189 "Server Name: %s\n"
190 "Server Version: %s\n"
191 "Default Sample Specification: %s\n"
192 "Default Channel Map: %s\n"
193 "Default Sink: %s\n"
194 "Default Source: %s\n"
195 "Cookie: %04x:%04x\n"),
196 i->user_name,
197 i->host_name,
198 i->server_name,
199 i->server_version,
200 ss,
201 cm,
202 i->default_sink_name,
203 i->default_source_name,
204 i->cookie >> 16,
205 i->cookie & 0xFFFFU);
206
207 complete_action();
208 }
209
210 static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
211
212 static const char *state_table[] = {
213 [1+PA_SINK_INVALID_STATE] = "n/a",
214 [1+PA_SINK_RUNNING] = "RUNNING",
215 [1+PA_SINK_IDLE] = "IDLE",
216 [1+PA_SINK_SUSPENDED] = "SUSPENDED"
217 };
218
219 char
220 s[PA_SAMPLE_SPEC_SNPRINT_MAX],
221 cv[PA_CVOLUME_SNPRINT_MAX],
222 cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
223 v[PA_VOLUME_SNPRINT_MAX],
224 vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
225 cm[PA_CHANNEL_MAP_SNPRINT_MAX];
226 char *pl;
227
228 if (is_last < 0) {
229 pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
230 quit(1);
231 return;
232 }
233
234 if (is_last) {
235 complete_action();
236 return;
237 }
238
239 pa_assert(i);
240
241 if (nl && !short_list_format)
242 printf("\n");
243 nl = TRUE;
244
245 if (short_list_format) {
246 printf("%u\t%s\t%s\t%s\t%s\n",
247 i->index,
248 i->name,
249 pa_strnull(i->driver),
250 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
251 state_table[1+i->state]);
252 return;
253 }
254
255 printf(_("Sink #%u\n"
256 "\tState: %s\n"
257 "\tName: %s\n"
258 "\tDescription: %s\n"
259 "\tDriver: %s\n"
260 "\tSample Specification: %s\n"
261 "\tChannel Map: %s\n"
262 "\tOwner Module: %u\n"
263 "\tMute: %s\n"
264 "\tVolume: %s%s%s\n"
265 "\t balance %0.2f\n"
266 "\tBase Volume: %s%s%s\n"
267 "\tMonitor Source: %s\n"
268 "\tLatency: %0.0f usec, configured %0.0f usec\n"
269 "\tFlags: %s%s%s%s%s%s\n"
270 "\tProperties:\n\t\t%s\n"),
271 i->index,
272 state_table[1+i->state],
273 i->name,
274 pa_strnull(i->description),
275 pa_strnull(i->driver),
276 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
277 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
278 i->owner_module,
279 pa_yes_no(i->mute),
280 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
281 i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
282 i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
283 pa_cvolume_get_balance(&i->volume, &i->channel_map),
284 pa_volume_snprint(v, sizeof(v), i->base_volume),
285 i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
286 i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
287 pa_strnull(i->monitor_source_name),
288 (double) i->latency, (double) i->configured_latency,
289 i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
290 i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
291 i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
292 i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
293 i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
294 i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
295 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
296
297 pa_xfree(pl);
298
299 if (i->ports) {
300 pa_sink_port_info **p;
301
302 printf(_("\tPorts:\n"));
303 for (p = i->ports; *p; p++)
304 printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority);
305 }
306
307 if (i->active_port)
308 printf(_("\tActive Port: %s\n"),
309 i->active_port->name);
310 }
311
312 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
313
314 static const char *state_table[] = {
315 [1+PA_SOURCE_INVALID_STATE] = "n/a",
316 [1+PA_SOURCE_RUNNING] = "RUNNING",
317 [1+PA_SOURCE_IDLE] = "IDLE",
318 [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
319 };
320
321 char
322 s[PA_SAMPLE_SPEC_SNPRINT_MAX],
323 cv[PA_CVOLUME_SNPRINT_MAX],
324 cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
325 v[PA_VOLUME_SNPRINT_MAX],
326 vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
327 cm[PA_CHANNEL_MAP_SNPRINT_MAX];
328 char *pl;
329
330 if (is_last < 0) {
331 pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
332 quit(1);
333 return;
334 }
335
336 if (is_last) {
337 complete_action();
338 return;
339 }
340
341 pa_assert(i);
342
343 if (nl && !short_list_format)
344 printf("\n");
345 nl = TRUE;
346
347 if (short_list_format) {
348 printf("%u\t%s\t%s\t%s\t%s\n",
349 i->index,
350 i->name,
351 pa_strnull(i->driver),
352 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
353 state_table[1+i->state]);
354 return;
355 }
356
357 printf(_("Source #%u\n"
358 "\tState: %s\n"
359 "\tName: %s\n"
360 "\tDescription: %s\n"
361 "\tDriver: %s\n"
362 "\tSample Specification: %s\n"
363 "\tChannel Map: %s\n"
364 "\tOwner Module: %u\n"
365 "\tMute: %s\n"
366 "\tVolume: %s%s%s\n"
367 "\t balance %0.2f\n"
368 "\tBase Volume: %s%s%s\n"
369 "\tMonitor of Sink: %s\n"
370 "\tLatency: %0.0f usec, configured %0.0f usec\n"
371 "\tFlags: %s%s%s%s%s%s\n"
372 "\tProperties:\n\t\t%s\n"),
373 i->index,
374 state_table[1+i->state],
375 i->name,
376 pa_strnull(i->description),
377 pa_strnull(i->driver),
378 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
379 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
380 i->owner_module,
381 pa_yes_no(i->mute),
382 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
383 i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
384 i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
385 pa_cvolume_get_balance(&i->volume, &i->channel_map),
386 pa_volume_snprint(v, sizeof(v), i->base_volume),
387 i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
388 i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
389 i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
390 (double) i->latency, (double) i->configured_latency,
391 i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
392 i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
393 i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
394 i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
395 i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
396 i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
397 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
398
399 pa_xfree(pl);
400
401 if (i->ports) {
402 pa_source_port_info **p;
403
404 printf(_("\tPorts:\n"));
405 for (p = i->ports; *p; p++)
406 printf("\t\t%s: %s (priority. %u)\n", (*p)->name, (*p)->description, (*p)->priority);
407 }
408
409 if (i->active_port)
410 printf(_("\tActive Port: %s\n"),
411 i->active_port->name);
412 }
413
414 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
415 char t[32];
416 char *pl;
417
418 if (is_last < 0) {
419 pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
420 quit(1);
421 return;
422 }
423
424 if (is_last) {
425 complete_action();
426 return;
427 }
428
429 pa_assert(i);
430
431 if (nl && !short_list_format)
432 printf("\n");
433 nl = TRUE;
434
435 pa_snprintf(t, sizeof(t), "%u", i->n_used);
436
437 if (short_list_format) {
438 printf("%u\t%s\t%s\t\n", i->index, i->name, i->argument ? i->argument : "");
439 return;
440 }
441
442 printf(_("Module #%u\n"
443 "\tName: %s\n"
444 "\tArgument: %s\n"
445 "\tUsage counter: %s\n"
446 "\tProperties:\n\t\t%s\n"),
447 i->index,
448 i->name,
449 i->argument ? i->argument : "",
450 i->n_used != PA_INVALID_INDEX ? t : _("n/a"),
451 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
452
453 pa_xfree(pl);
454 }
455
456 static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
457 char t[32];
458 char *pl;
459
460 if (is_last < 0) {
461 pa_log(_("Failed to get client information: %s"), pa_strerror(pa_context_errno(c)));
462 quit(1);
463 return;
464 }
465
466 if (is_last) {
467 complete_action();
468 return;
469 }
470
471 pa_assert(i);
472
473 if (nl && !short_list_format)
474 printf("\n");
475 nl = TRUE;
476
477 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
478
479 if (short_list_format) {
480 printf("%u\t%s\t%s\n",
481 i->index,
482 pa_strnull(i->driver),
483 pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)));
484 return;
485 }
486
487 printf(_("Client #%u\n"
488 "\tDriver: %s\n"
489 "\tOwner Module: %s\n"
490 "\tProperties:\n\t\t%s\n"),
491 i->index,
492 pa_strnull(i->driver),
493 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
494 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
495
496 pa_xfree(pl);
497 }
498
499 static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
500 char t[32];
501 char *pl;
502
503 if (is_last < 0) {
504 pa_log(_("Failed to get card information: %s"), pa_strerror(pa_context_errno(c)));
505 complete_action();
506 return;
507 }
508
509 if (is_last) {
510 complete_action();
511 return;
512 }
513
514 pa_assert(i);
515
516 if (nl && !short_list_format)
517 printf("\n");
518 nl = TRUE;
519
520 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
521
522 if (short_list_format) {
523 printf("%u\t%s\t%s\n", i->index, i->name, pa_strnull(i->driver));
524 return;
525 }
526
527 printf(_("Card #%u\n"
528 "\tName: %s\n"
529 "\tDriver: %s\n"
530 "\tOwner Module: %s\n"
531 "\tProperties:\n\t\t%s\n"),
532 i->index,
533 i->name,
534 pa_strnull(i->driver),
535 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
536 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
537
538 if (i->profiles) {
539 pa_card_profile_info *p;
540
541 printf(_("\tProfiles:\n"));
542 for (p = i->profiles; p->name; p++)
543 printf("\t\t%s: %s (sinks: %u, sources: %u, priority. %u)\n", p->name, p->description, p->n_sinks, p->n_sources, p->priority);
544 }
545
546 if (i->active_profile)
547 printf(_("\tActive Profile: %s\n"),
548 i->active_profile->name);
549
550 pa_xfree(pl);
551 }
552
553 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
554 char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
555 char *pl;
556
557 if (is_last < 0) {
558 pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
559 quit(1);
560 return;
561 }
562
563 if (is_last) {
564 complete_action();
565 return;
566 }
567
568 pa_assert(i);
569
570 if (nl && !short_list_format)
571 printf("\n");
572 nl = TRUE;
573
574 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
575 pa_snprintf(k, sizeof(k), "%u", i->client);
576
577 if (short_list_format) {
578 printf("%u\t%u\t%s\t%s\t%s\n",
579 i->index,
580 i->sink,
581 i->client != PA_INVALID_INDEX ? k : "-",
582 pa_strnull(i->driver),
583 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec));
584 return;
585 }
586
587 printf(_("Sink Input #%u\n"
588 "\tDriver: %s\n"
589 "\tOwner Module: %s\n"
590 "\tClient: %s\n"
591 "\tSink: %u\n"
592 "\tSample Specification: %s\n"
593 "\tChannel Map: %s\n"
594 "\tMute: %s\n"
595 "\tVolume: %s\n"
596 "\t %s\n"
597 "\t balance %0.2f\n"
598 "\tBuffer Latency: %0.0f usec\n"
599 "\tSink Latency: %0.0f usec\n"
600 "\tResample method: %s\n"
601 "\tProperties:\n\t\t%s\n"),
602 i->index,
603 pa_strnull(i->driver),
604 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
605 i->client != PA_INVALID_INDEX ? k : _("n/a"),
606 i->sink,
607 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
608 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
609 pa_yes_no(i->mute),
610 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
611 pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
612 pa_cvolume_get_balance(&i->volume, &i->channel_map),
613 (double) i->buffer_usec,
614 (double) i->sink_usec,
615 i->resample_method ? i->resample_method : _("n/a"),
616 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
617
618 pa_xfree(pl);
619 }
620
621 static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
622 char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
623 char *pl;
624
625 if (is_last < 0) {
626 pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
627 quit(1);
628 return;
629 }
630
631 if (is_last) {
632 complete_action();
633 return;
634 }
635
636 pa_assert(i);
637
638 if (nl && !short_list_format)
639 printf("\n");
640 nl = TRUE;
641
642
643 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
644 pa_snprintf(k, sizeof(k), "%u", i->client);
645
646 if (short_list_format) {
647 printf("%u\t%u\t%s\t%s\t%s\n",
648 i->index,
649 i->source,
650 i->client != PA_INVALID_INDEX ? k : "-",
651 pa_strnull(i->driver),
652 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec));
653 return;
654 }
655
656 printf(_("Source Output #%u\n"
657 "\tDriver: %s\n"
658 "\tOwner Module: %s\n"
659 "\tClient: %s\n"
660 "\tSource: %u\n"
661 "\tSample Specification: %s\n"
662 "\tChannel Map: %s\n"
663 "\tBuffer Latency: %0.0f usec\n"
664 "\tSource Latency: %0.0f usec\n"
665 "\tResample method: %s\n"
666 "\tProperties:\n\t\t%s\n"),
667 i->index,
668 pa_strnull(i->driver),
669 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
670 i->client != PA_INVALID_INDEX ? k : _("n/a"),
671 i->source,
672 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
673 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
674 (double) i->buffer_usec,
675 (double) i->source_usec,
676 i->resample_method ? i->resample_method : _("n/a"),
677 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
678
679 pa_xfree(pl);
680 }
681
682 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
683 char t[PA_BYTES_SNPRINT_MAX], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
684 char *pl;
685
686 if (is_last < 0) {
687 pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
688 quit(1);
689 return;
690 }
691
692 if (is_last) {
693 complete_action();
694 return;
695 }
696
697 pa_assert(i);
698
699 if (nl && !short_list_format)
700 printf("\n");
701 nl = TRUE;
702
703 pa_bytes_snprint(t, sizeof(t), i->bytes);
704
705 if (short_list_format) {
706 printf("%u\t%s\t%s\t%0.3f\n",
707 i->index,
708 i->name,
709 pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : "-",
710 (double) i->duration/1000000.0);
711 return;
712 }
713
714 printf(_("Sample #%u\n"
715 "\tName: %s\n"
716 "\tSample Specification: %s\n"
717 "\tChannel Map: %s\n"
718 "\tVolume: %s\n"
719 "\t %s\n"
720 "\t balance %0.2f\n"
721 "\tDuration: %0.1fs\n"
722 "\tSize: %s\n"
723 "\tLazy: %s\n"
724 "\tFilename: %s\n"
725 "\tProperties:\n\t\t%s\n"),
726 i->index,
727 i->name,
728 pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"),
729 pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
730 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
731 pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
732 pa_cvolume_get_balance(&i->volume, &i->channel_map),
733 (double) i->duration/1000000.0,
734 t,
735 pa_yes_no(i->lazy),
736 i->filename ? i->filename : _("n/a"),
737 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
738
739 pa_xfree(pl);
740 }
741
742 static void simple_callback(pa_context *c, int success, void *userdata) {
743 if (!success) {
744 pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
745 quit(1);
746 return;
747 }
748
749 complete_action();
750 }
751
752 static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
753 if (idx == PA_INVALID_INDEX) {
754 pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
755 quit(1);
756 return;
757 }
758
759 printf("%u\n", idx);
760
761 complete_action();
762 }
763
764 static void volume_relative_adjust(pa_cvolume *cv) {
765 pa_assert((volume_flags & VOL_RELATIVE) == VOL_RELATIVE);
766
767 /* Relative volume change is additive in case of UINT or PERCENT
768 * and multiplicative for LINEAR or DECIBEL */
769 if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
770 pa_volume_t v = pa_cvolume_avg(cv);
771 v = v + volume < PA_VOLUME_NORM ? PA_VOLUME_MUTED : v + volume - PA_VOLUME_NORM;
772 pa_cvolume_set(cv, 1, v);
773 }
774 if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL) {
775 pa_sw_cvolume_multiply_scalar(cv, cv, volume);
776 }
777 }
778
779 static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
780 pa_cvolume cv;
781
782 if (is_last < 0) {
783 pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
784 quit(1);
785 return;
786 }
787
788 if (is_last)
789 return;
790
791 pa_assert(i);
792
793 cv = i->volume;
794 volume_relative_adjust(&cv);
795 pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
796 }
797
798 static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
799 pa_cvolume cv;
800
801 if (is_last < 0) {
802 pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
803 quit(1);
804 return;
805 }
806
807 if (is_last)
808 return;
809
810 pa_assert(i);
811
812 cv = i->volume;
813 volume_relative_adjust(&cv);
814 pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
815 }
816
817 static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
818 pa_cvolume cv;
819
820 if (is_last < 0) {
821 pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
822 quit(1);
823 return;
824 }
825
826 if (is_last)
827 return;
828
829 pa_assert(i);
830
831 cv = i->volume;
832 volume_relative_adjust(&cv);
833 pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
834 }
835
836 static void stream_state_callback(pa_stream *s, void *userdata) {
837 pa_assert(s);
838
839 switch (pa_stream_get_state(s)) {
840 case PA_STREAM_CREATING:
841 case PA_STREAM_READY:
842 break;
843
844 case PA_STREAM_TERMINATED:
845 drain();
846 break;
847
848 case PA_STREAM_FAILED:
849 default:
850 pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
851 quit(1);
852 }
853 }
854
855 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
856 sf_count_t l;
857 float *d;
858 pa_assert(s && length && sndfile);
859
860 d = pa_xmalloc(length);
861
862 pa_assert(sample_length >= length);
863 l = (sf_count_t) (length/pa_frame_size(&sample_spec));
864
865 if ((sf_readf_float(sndfile, d, l)) != l) {
866 pa_xfree(d);
867 pa_log(_("Premature end of file"));
868 quit(1);
869 return;
870 }
871
872 pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
873
874 sample_length -= length;
875
876 if (sample_length <= 0) {
877 pa_stream_set_write_callback(sample_stream, NULL, NULL);
878 pa_stream_finish_upload(sample_stream);
879 }
880 }
881
882 static const char *subscription_event_type_to_string(pa_subscription_event_type_t t) {
883
884 switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
885
886 case PA_SUBSCRIPTION_EVENT_NEW:
887 return _("new");
888
889 case PA_SUBSCRIPTION_EVENT_CHANGE:
890 return _("change");
891
892 case PA_SUBSCRIPTION_EVENT_REMOVE:
893 return _("remove");
894 }
895
896 return _("unknown");
897 }
898
899 static const char *subscription_event_facility_to_string(pa_subscription_event_type_t t) {
900
901 switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
902
903 case PA_SUBSCRIPTION_EVENT_SINK:
904 return _("sink");
905
906 case PA_SUBSCRIPTION_EVENT_SOURCE:
907 return _("source");
908
909 case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
910 return _("sink-input");
911
912 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
913 return _("source-output");
914
915 case PA_SUBSCRIPTION_EVENT_MODULE:
916 return _("module");
917
918 case PA_SUBSCRIPTION_EVENT_CLIENT:
919 return _("client");
920
921 case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
922 return _("sample-cache");
923
924 case PA_SUBSCRIPTION_EVENT_SERVER:
925 return _("server");
926
927 case PA_SUBSCRIPTION_EVENT_CARD:
928 return _("server");
929 }
930
931 return _("unknown");
932 }
933
934 static void context_subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
935 pa_assert(c);
936
937 printf(_("Event '%s' on %s #%u\n"),
938 subscription_event_type_to_string(t),
939 subscription_event_facility_to_string(t),
940 idx);
941 }
942
943 static void context_state_callback(pa_context *c, void *userdata) {
944 pa_assert(c);
945 switch (pa_context_get_state(c)) {
946 case PA_CONTEXT_CONNECTING:
947 case PA_CONTEXT_AUTHORIZING:
948 case PA_CONTEXT_SETTING_NAME:
949 break;
950
951 case PA_CONTEXT_READY:
952 switch (action) {
953 case STAT:
954 pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
955 break;
956
957 case INFO:
958 pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
959 break;
960
961 case PLAY_SAMPLE:
962 pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL));
963 break;
964
965 case REMOVE_SAMPLE:
966 pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
967 break;
968
969 case UPLOAD_SAMPLE:
970 sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
971 pa_assert(sample_stream);
972
973 pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
974 pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
975 pa_stream_connect_upload(sample_stream, sample_length);
976 break;
977
978 case EXIT:
979 pa_operation_unref(pa_context_exit_daemon(c, simple_callback, NULL));
980 break;
981
982 case LIST:
983 if (list_type) {
984 if (pa_streq(list_type, "modules"))
985 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
986 else if (pa_streq(list_type, "sinks"))
987 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
988 else if (pa_streq(list_type, "sources"))
989 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
990 else if (pa_streq(list_type, "sink-inputs"))
991 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
992 else if (pa_streq(list_type, "source-outputs"))
993 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
994 else if (pa_streq(list_type, "clients"))
995 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
996 else if (pa_streq(list_type, "samples"))
997 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
998 else if (pa_streq(list_type, "cards"))
999 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
1000 else
1001 pa_assert_not_reached();
1002 } else {
1003 actions = 8;
1004 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
1005 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
1006 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
1007 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
1008 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
1009 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
1010 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
1011 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
1012 }
1013 break;
1014
1015 case MOVE_SINK_INPUT:
1016 pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL));
1017 break;
1018
1019 case MOVE_SOURCE_OUTPUT:
1020 pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
1021 break;
1022
1023 case LOAD_MODULE:
1024 pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
1025 break;
1026
1027 case UNLOAD_MODULE:
1028 pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
1029 break;
1030
1031 case SUSPEND_SINK:
1032 if (sink_name)
1033 pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
1034 else
1035 pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
1036 break;
1037
1038 case SUSPEND_SOURCE:
1039 if (source_name)
1040 pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
1041 else
1042 pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
1043 break;
1044
1045 case SET_CARD_PROFILE:
1046 pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
1047 break;
1048
1049 case SET_SINK_PORT:
1050 pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
1051 break;
1052
1053 case SET_SOURCE_PORT:
1054 pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
1055 break;
1056
1057 case SET_SINK_MUTE:
1058 pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL));
1059 break;
1060
1061 case SET_SOURCE_MUTE:
1062 pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL));
1063 break;
1064
1065 case SET_SINK_INPUT_MUTE:
1066 pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL));
1067 break;
1068
1069 case SET_SINK_VOLUME:
1070 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1071 pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL));
1072 } else {
1073 pa_cvolume v;
1074 pa_cvolume_set(&v, 1, volume);
1075 pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL));
1076 }
1077 break;
1078
1079 case SET_SOURCE_VOLUME:
1080 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1081 pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL));
1082 } else {
1083 pa_cvolume v;
1084 pa_cvolume_set(&v, 1, volume);
1085 pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL));
1086 }
1087 break;
1088
1089 case SET_SINK_INPUT_VOLUME:
1090 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1091 pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL));
1092 } else {
1093 pa_cvolume v;
1094 pa_cvolume_set(&v, 1, volume);
1095 pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL));
1096 }
1097 break;
1098
1099 case SUBSCRIBE:
1100 pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
1101
1102 pa_operation_unref(pa_context_subscribe(
1103 c,
1104 PA_SUBSCRIPTION_MASK_SINK|
1105 PA_SUBSCRIPTION_MASK_SOURCE|
1106 PA_SUBSCRIPTION_MASK_SINK_INPUT|
1107 PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
1108 PA_SUBSCRIPTION_MASK_MODULE|
1109 PA_SUBSCRIPTION_MASK_CLIENT|
1110 PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
1111 PA_SUBSCRIPTION_MASK_SERVER|
1112 PA_SUBSCRIPTION_MASK_CARD,
1113 NULL,
1114 NULL));
1115 break;
1116
1117 default:
1118 pa_assert_not_reached();
1119 }
1120 break;
1121
1122 case PA_CONTEXT_TERMINATED:
1123 quit(0);
1124 break;
1125
1126 case PA_CONTEXT_FAILED:
1127 default:
1128 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
1129 quit(1);
1130 }
1131 }
1132
1133 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
1134 pa_log(_("Got SIGINT, exiting."));
1135 quit(0);
1136 }
1137
1138 static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
1139 double v;
1140 char *vs;
1141
1142 pa_assert(vol_spec);
1143 pa_assert(vol);
1144 pa_assert(vol_flags);
1145
1146 vs = pa_xstrdup(vol_spec);
1147
1148 *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
1149 if (strchr(vs, '.'))
1150 *vol_flags |= VOL_LINEAR;
1151 if (pa_endswith(vs, "%")) {
1152 *vol_flags |= VOL_PERCENT;
1153 vs[strlen(vs)-1] = 0;
1154 }
1155 if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
1156 *vol_flags |= VOL_DECIBEL;
1157 vs[strlen(vs)-2] = 0;
1158 }
1159
1160 if (pa_atod(vs, &v) < 0) {
1161 pa_log(_("Invalid volume specification"));
1162 pa_xfree(vs);
1163 return -1;
1164 }
1165
1166 pa_xfree(vs);
1167
1168 if ((*vol_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1169 if ((*vol_flags & 0x0F) == VOL_UINT)
1170 v += (double) PA_VOLUME_NORM;
1171 if ((*vol_flags & 0x0F) == VOL_PERCENT)
1172 v += 100.0;
1173 if ((*vol_flags & 0x0F) == VOL_LINEAR)
1174 v += 1.0;
1175 }
1176 if ((*vol_flags & 0x0F) == VOL_PERCENT)
1177 v = v * (double) PA_VOLUME_NORM / 100;
1178 if ((*vol_flags & 0x0F) == VOL_LINEAR)
1179 v = pa_sw_volume_from_linear(v);
1180 if ((*vol_flags & 0x0F) == VOL_DECIBEL)
1181 v = pa_sw_volume_from_dB(v);
1182
1183 if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
1184 pa_log(_("Volume outside permissible range.\n"));
1185 return -1;
1186 }
1187
1188 *vol = (pa_volume_t) v;
1189
1190 return 0;
1191 }
1192
1193 static void help(const char *argv0) {
1194
1195 printf(_("%s [options] stat\n"
1196 "%s [options] info\n"
1197 "%s [options] list [short] [TYPE]\n"
1198 "%s [options] exit\n"
1199 "%s [options] upload-sample FILENAME [NAME]\n"
1200 "%s [options] play-sample NAME [SINK]\n"
1201 "%s [options] remove-sample NAME\n"
1202 "%s [options] move-sink-input SINKINPUT SINK\n"
1203 "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
1204 "%s [options] load-module NAME [ARGS ...]\n"
1205 "%s [options] unload-module MODULE\n"
1206 "%s [options] suspend-sink SINK 1|0\n"
1207 "%s [options] suspend-source SOURCE 1|0\n"
1208 "%s [options] set-card-profile CARD PROFILE\n"
1209 "%s [options] set-sink-port SINK PORT\n"
1210 "%s [options] set-source-port SOURCE PORT\n"
1211 "%s [options] set-sink-volume SINK VOLUME\n"
1212 "%s [options] set-source-volume SOURCE VOLUME\n"
1213 "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
1214 "%s [options] set-sink-mute SINK 1|0\n"
1215 "%s [options] set-source-mute SOURCE 1|0\n"
1216 "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
1217 "%s [options] subscribe\n\n"
1218 " -h, --help Show this help\n"
1219 " --version Show version\n\n"
1220 " -s, --server=SERVER The name of the server to connect to\n"
1221 " -n, --client-name=NAME How to call this client on the server\n"),
1222 argv0, argv0, argv0, argv0, argv0,
1223 argv0, argv0, argv0, argv0, argv0,
1224 argv0, argv0, argv0, argv0, argv0,
1225 argv0, argv0, argv0, argv0, argv0,
1226 argv0, argv0, argv0);
1227 }
1228
1229 enum {
1230 ARG_VERSION = 256
1231 };
1232
1233 int main(int argc, char *argv[]) {
1234 pa_mainloop *m = NULL;
1235 int ret = 1, c;
1236 char *server = NULL, *bn;
1237
1238 static const struct option long_options[] = {
1239 {"server", 1, NULL, 's'},
1240 {"client-name", 1, NULL, 'n'},
1241 {"version", 0, NULL, ARG_VERSION},
1242 {"help", 0, NULL, 'h'},
1243 {NULL, 0, NULL, 0}
1244 };
1245
1246 setlocale(LC_ALL, "");
1247 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
1248
1249 bn = pa_path_get_filename(argv[0]);
1250
1251 proplist = pa_proplist_new();
1252
1253 while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
1254 switch (c) {
1255 case 'h' :
1256 help(bn);
1257 ret = 0;
1258 goto quit;
1259
1260 case ARG_VERSION:
1261 printf(_("pactl %s\n"
1262 "Compiled with libpulse %s\n"
1263 "Linked with libpulse %s\n"),
1264 PACKAGE_VERSION,
1265 pa_get_headers_version(),
1266 pa_get_library_version());
1267 ret = 0;
1268 goto quit;
1269
1270 case 's':
1271 pa_xfree(server);
1272 server = pa_xstrdup(optarg);
1273 break;
1274
1275 case 'n': {
1276 char *t;
1277
1278 if (!(t = pa_locale_to_utf8(optarg)) ||
1279 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
1280
1281 pa_log(_("Invalid client name '%s'"), t ? t : optarg);
1282 pa_xfree(t);
1283 goto quit;
1284 }
1285
1286 pa_xfree(t);
1287 break;
1288 }
1289
1290 default:
1291 goto quit;
1292 }
1293 }
1294
1295 if (optind < argc) {
1296 if (pa_streq(argv[optind], "stat"))
1297 action = STAT;
1298
1299 else if (pa_streq(argv[optind], "info"))
1300 action = INFO;
1301
1302 else if (pa_streq(argv[optind], "exit"))
1303 action = EXIT;
1304
1305 else if (pa_streq(argv[optind], "list")) {
1306 action = LIST;
1307
1308 for (int i = optind+1; i < argc; i++){
1309 if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
1310 pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
1311 pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
1312 pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) {
1313 list_type = pa_xstrdup(argv[i]);
1314 } else if (pa_streq(argv[i], "short")) {
1315 short_list_format = TRUE;
1316 } else {
1317 pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
1318 goto quit;
1319 }
1320 }
1321
1322 } else if (pa_streq(argv[optind], "upload-sample")) {
1323 struct SF_INFO sfi;
1324 action = UPLOAD_SAMPLE;
1325
1326 if (optind+1 >= argc) {
1327 pa_log(_("Please specify a sample file to load"));
1328 goto quit;
1329 }
1330
1331 if (optind+2 < argc)
1332 sample_name = pa_xstrdup(argv[optind+2]);
1333 else {
1334 char *f = pa_path_get_filename(argv[optind+1]);
1335 sample_name = pa_xstrndup(f, strcspn(f, "."));
1336 }
1337
1338 pa_zero(sfi);
1339 if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
1340 pa_log(_("Failed to open sound file."));
1341 goto quit;
1342 }
1343
1344 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
1345 pa_log(_("Failed to determine sample specification from file."));
1346 goto quit;
1347 }
1348 sample_spec.format = PA_SAMPLE_FLOAT32;
1349
1350 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1351 if (sample_spec.channels > 2)
1352 pa_log(_("Warning: Failed to determine sample specification from file."));
1353 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1354 }
1355
1356 pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
1357 sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
1358
1359 } else if (pa_streq(argv[optind], "play-sample")) {
1360 action = PLAY_SAMPLE;
1361 if (argc != optind+2 && argc != optind+3) {
1362 pa_log(_("You have to specify a sample name to play"));
1363 goto quit;
1364 }
1365
1366 sample_name = pa_xstrdup(argv[optind+1]);
1367
1368 if (optind+2 < argc)
1369 sink_name = pa_xstrdup(argv[optind+2]);
1370
1371 } else if (pa_streq(argv[optind], "remove-sample")) {
1372 action = REMOVE_SAMPLE;
1373 if (argc != optind+2) {
1374 pa_log(_("You have to specify a sample name to remove"));
1375 goto quit;
1376 }
1377
1378 sample_name = pa_xstrdup(argv[optind+1]);
1379
1380 } else if (pa_streq(argv[optind], "move-sink-input")) {
1381 action = MOVE_SINK_INPUT;
1382 if (argc != optind+3) {
1383 pa_log(_("You have to specify a sink input index and a sink"));
1384 goto quit;
1385 }
1386
1387 sink_input_idx = (uint32_t) atoi(argv[optind+1]);
1388 sink_name = pa_xstrdup(argv[optind+2]);
1389
1390 } else if (pa_streq(argv[optind], "move-source-output")) {
1391 action = MOVE_SOURCE_OUTPUT;
1392 if (argc != optind+3) {
1393 pa_log(_("You have to specify a source output index and a source"));
1394 goto quit;
1395 }
1396
1397 source_output_idx = (uint32_t) atoi(argv[optind+1]);
1398 source_name = pa_xstrdup(argv[optind+2]);
1399
1400 } else if (pa_streq(argv[optind], "load-module")) {
1401 int i;
1402 size_t n = 0;
1403 char *p;
1404
1405 action = LOAD_MODULE;
1406
1407 if (argc <= optind+1) {
1408 pa_log(_("You have to specify a module name and arguments."));
1409 goto quit;
1410 }
1411
1412 module_name = argv[optind+1];
1413
1414 for (i = optind+2; i < argc; i++)
1415 n += strlen(argv[i])+1;
1416
1417 if (n > 0) {
1418 p = module_args = pa_xmalloc(n);
1419
1420 for (i = optind+2; i < argc; i++)
1421 p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
1422 }
1423
1424 } else if (pa_streq(argv[optind], "unload-module")) {
1425 action = UNLOAD_MODULE;
1426
1427 if (argc != optind+2) {
1428 pa_log(_("You have to specify a module index"));
1429 goto quit;
1430 }
1431
1432 module_index = (uint32_t) atoi(argv[optind+1]);
1433
1434 } else if (pa_streq(argv[optind], "suspend-sink")) {
1435 action = SUSPEND_SINK;
1436
1437 if (argc > optind+3 || optind+1 >= argc) {
1438 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
1439 goto quit;
1440 }
1441
1442 suspend = pa_parse_boolean(argv[argc-1]);
1443
1444 if (argc > optind+2)
1445 sink_name = pa_xstrdup(argv[optind+1]);
1446
1447 } else if (pa_streq(argv[optind], "suspend-source")) {
1448 action = SUSPEND_SOURCE;
1449
1450 if (argc > optind+3 || optind+1 >= argc) {
1451 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
1452 goto quit;
1453 }
1454
1455 suspend = pa_parse_boolean(argv[argc-1]);
1456
1457 if (argc > optind+2)
1458 source_name = pa_xstrdup(argv[optind+1]);
1459 } else if (pa_streq(argv[optind], "set-card-profile")) {
1460 action = SET_CARD_PROFILE;
1461
1462 if (argc != optind+3) {
1463 pa_log(_("You have to specify a card name/index and a profile name"));
1464 goto quit;
1465 }
1466
1467 card_name = pa_xstrdup(argv[optind+1]);
1468 profile_name = pa_xstrdup(argv[optind+2]);
1469
1470 } else if (pa_streq(argv[optind], "set-sink-port")) {
1471 action = SET_SINK_PORT;
1472
1473 if (argc != optind+3) {
1474 pa_log(_("You have to specify a sink name/index and a port name"));
1475 goto quit;
1476 }
1477
1478 sink_name = pa_xstrdup(argv[optind+1]);
1479 port_name = pa_xstrdup(argv[optind+2]);
1480
1481 } else if (pa_streq(argv[optind], "set-source-port")) {
1482 action = SET_SOURCE_PORT;
1483
1484 if (argc != optind+3) {
1485 pa_log(_("You have to specify a source name/index and a port name"));
1486 goto quit;
1487 }
1488
1489 source_name = pa_xstrdup(argv[optind+1]);
1490 port_name = pa_xstrdup(argv[optind+2]);
1491
1492 } else if (pa_streq(argv[optind], "set-sink-volume")) {
1493 action = SET_SINK_VOLUME;
1494
1495 if (argc != optind+3) {
1496 pa_log(_("You have to specify a sink name/index and a volume"));
1497 goto quit;
1498 }
1499
1500 sink_name = pa_xstrdup(argv[optind+1]);
1501
1502 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1503 goto quit;
1504
1505 } else if (pa_streq(argv[optind], "set-source-volume")) {
1506 action = SET_SOURCE_VOLUME;
1507
1508 if (argc != optind+3) {
1509 pa_log(_("You have to specify a source name/index and a volume"));
1510 goto quit;
1511 }
1512
1513 source_name = pa_xstrdup(argv[optind+1]);
1514
1515 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1516 goto quit;
1517
1518 } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
1519 action = SET_SINK_INPUT_VOLUME;
1520
1521 if (argc != optind+3) {
1522 pa_log(_("You have to specify a sink input index and a volume"));
1523 goto quit;
1524 }
1525
1526 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1527 pa_log(_("Invalid sink input index"));
1528 goto quit;
1529 }
1530
1531 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1532 goto quit;
1533
1534 } else if (pa_streq(argv[optind], "set-sink-mute")) {
1535 int b;
1536 action = SET_SINK_MUTE;
1537
1538 if (argc != optind+3) {
1539 pa_log(_("You have to specify a sink name/index and a mute boolean"));
1540 goto quit;
1541 }
1542
1543 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1544 pa_log(_("Invalid mute specification"));
1545 goto quit;
1546 }
1547
1548 sink_name = pa_xstrdup(argv[optind+1]);
1549 mute = b;
1550
1551 } else if (pa_streq(argv[optind], "set-source-mute")) {
1552 int b;
1553 action = SET_SOURCE_MUTE;
1554
1555 if (argc != optind+3) {
1556 pa_log(_("You have to specify a source name/index and a mute boolean"));
1557 goto quit;
1558 }
1559
1560 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1561 pa_log(_("Invalid mute specification"));
1562 goto quit;
1563 }
1564
1565 source_name = pa_xstrdup(argv[optind+1]);
1566 mute = b;
1567
1568 } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
1569 int b;
1570 action = SET_SINK_INPUT_MUTE;
1571
1572 if (argc != optind+3) {
1573 pa_log(_("You have to specify a sink input index and a mute boolean"));
1574 goto quit;
1575 }
1576
1577 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1578 pa_log(_("Invalid sink input index specification"));
1579 goto quit;
1580 }
1581
1582 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1583 pa_log(_("Invalid mute specification"));
1584 goto quit;
1585 }
1586
1587 mute = b;
1588
1589 } else if (pa_streq(argv[optind], "subscribe"))
1590
1591 action = SUBSCRIBE;
1592
1593 else if (pa_streq(argv[optind], "help")) {
1594 help(bn);
1595 ret = 0;
1596 goto quit;
1597 }
1598 }
1599
1600 if (action == NONE) {
1601 pa_log(_("No valid command specified."));
1602 goto quit;
1603 }
1604
1605 if (!(m = pa_mainloop_new())) {
1606 pa_log(_("pa_mainloop_new() failed."));
1607 goto quit;
1608 }
1609
1610 mainloop_api = pa_mainloop_get_api(m);
1611
1612 pa_assert_se(pa_signal_init(mainloop_api) == 0);
1613 pa_signal_new(SIGINT, exit_signal_callback, NULL);
1614 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1615 pa_disable_sigpipe();
1616
1617 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1618 pa_log(_("pa_context_new() failed."));
1619 goto quit;
1620 }
1621
1622 pa_context_set_state_callback(context, context_state_callback, NULL);
1623 if (pa_context_connect(context, server, 0, NULL) < 0) {
1624 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1625 goto quit;
1626 }
1627
1628 if (pa_mainloop_run(m, &ret) < 0) {
1629 pa_log(_("pa_mainloop_run() failed."));
1630 goto quit;
1631 }
1632
1633 quit:
1634 if (sample_stream)
1635 pa_stream_unref(sample_stream);
1636
1637 if (context)
1638 pa_context_unref(context);
1639
1640 if (m) {
1641 pa_signal_done();
1642 pa_mainloop_free(m);
1643 }
1644
1645 pa_xfree(server);
1646 pa_xfree(list_type);
1647 pa_xfree(sample_name);
1648 pa_xfree(sink_name);
1649 pa_xfree(source_name);
1650 pa_xfree(module_args);
1651 pa_xfree(card_name);
1652 pa_xfree(profile_name);
1653 pa_xfree(port_name);
1654
1655 if (sndfile)
1656 sf_close(sndfile);
1657
1658 if (proplist)
1659 pa_proplist_free(proplist);
1660
1661 return ret;
1662 }