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