]> code.delx.au - pulseaudio/blob - src/utils/pactl.c
introspect: Get formats for sources
[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], cm[PA_CHANNEL_MAP_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 "\tBuffer Latency: %0.0f usec\n"
684 "\tSource Latency: %0.0f usec\n"
685 "\tResample method: %s\n"
686 "\tProperties:\n\t\t%s\n"),
687 i->index,
688 pa_strnull(i->driver),
689 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
690 i->client != PA_INVALID_INDEX ? k : _("n/a"),
691 i->source,
692 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
693 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
694 (double) i->buffer_usec,
695 (double) i->source_usec,
696 i->resample_method ? i->resample_method : _("n/a"),
697 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
698
699 pa_xfree(pl);
700 }
701
702 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
703 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];
704 char *pl;
705
706 if (is_last < 0) {
707 pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
708 quit(1);
709 return;
710 }
711
712 if (is_last) {
713 complete_action();
714 return;
715 }
716
717 pa_assert(i);
718
719 if (nl && !short_list_format)
720 printf("\n");
721 nl = TRUE;
722
723 pa_bytes_snprint(t, sizeof(t), i->bytes);
724
725 if (short_list_format) {
726 printf("%u\t%s\t%s\t%0.3f\n",
727 i->index,
728 i->name,
729 pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : "-",
730 (double) i->duration/1000000.0);
731 return;
732 }
733
734 printf(_("Sample #%u\n"
735 "\tName: %s\n"
736 "\tSample Specification: %s\n"
737 "\tChannel Map: %s\n"
738 "\tVolume: %s\n"
739 "\t %s\n"
740 "\t balance %0.2f\n"
741 "\tDuration: %0.1fs\n"
742 "\tSize: %s\n"
743 "\tLazy: %s\n"
744 "\tFilename: %s\n"
745 "\tProperties:\n\t\t%s\n"),
746 i->index,
747 i->name,
748 pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"),
749 pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
750 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
751 pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
752 pa_cvolume_get_balance(&i->volume, &i->channel_map),
753 (double) i->duration/1000000.0,
754 t,
755 pa_yes_no(i->lazy),
756 i->filename ? i->filename : _("n/a"),
757 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
758
759 pa_xfree(pl);
760 }
761
762 static void simple_callback(pa_context *c, int success, void *userdata) {
763 if (!success) {
764 pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
765 quit(1);
766 return;
767 }
768
769 complete_action();
770 }
771
772 static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
773 if (idx == PA_INVALID_INDEX) {
774 pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
775 quit(1);
776 return;
777 }
778
779 printf("%u\n", idx);
780
781 complete_action();
782 }
783
784 static void volume_relative_adjust(pa_cvolume *cv) {
785 pa_assert((volume_flags & VOL_RELATIVE) == VOL_RELATIVE);
786
787 /* Relative volume change is additive in case of UINT or PERCENT
788 * and multiplicative for LINEAR or DECIBEL */
789 if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
790 pa_volume_t v = pa_cvolume_avg(cv);
791 v = v + volume < PA_VOLUME_NORM ? PA_VOLUME_MUTED : v + volume - PA_VOLUME_NORM;
792 pa_cvolume_set(cv, 1, v);
793 }
794 if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL) {
795 pa_sw_cvolume_multiply_scalar(cv, cv, volume);
796 }
797 }
798
799 static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
800 pa_cvolume cv;
801
802 if (is_last < 0) {
803 pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
804 quit(1);
805 return;
806 }
807
808 if (is_last)
809 return;
810
811 pa_assert(i);
812
813 cv = i->volume;
814 volume_relative_adjust(&cv);
815 pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
816 }
817
818 static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
819 pa_cvolume cv;
820
821 if (is_last < 0) {
822 pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
823 quit(1);
824 return;
825 }
826
827 if (is_last)
828 return;
829
830 pa_assert(i);
831
832 cv = i->volume;
833 volume_relative_adjust(&cv);
834 pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
835 }
836
837 static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
838 pa_cvolume cv;
839
840 if (is_last < 0) {
841 pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
842 quit(1);
843 return;
844 }
845
846 if (is_last)
847 return;
848
849 pa_assert(i);
850
851 cv = i->volume;
852 volume_relative_adjust(&cv);
853 pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
854 }
855
856 static void stream_state_callback(pa_stream *s, void *userdata) {
857 pa_assert(s);
858
859 switch (pa_stream_get_state(s)) {
860 case PA_STREAM_CREATING:
861 case PA_STREAM_READY:
862 break;
863
864 case PA_STREAM_TERMINATED:
865 drain();
866 break;
867
868 case PA_STREAM_FAILED:
869 default:
870 pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
871 quit(1);
872 }
873 }
874
875 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
876 sf_count_t l;
877 float *d;
878 pa_assert(s && length && sndfile);
879
880 d = pa_xmalloc(length);
881
882 pa_assert(sample_length >= length);
883 l = (sf_count_t) (length/pa_frame_size(&sample_spec));
884
885 if ((sf_readf_float(sndfile, d, l)) != l) {
886 pa_xfree(d);
887 pa_log(_("Premature end of file"));
888 quit(1);
889 return;
890 }
891
892 pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
893
894 sample_length -= length;
895
896 if (sample_length <= 0) {
897 pa_stream_set_write_callback(sample_stream, NULL, NULL);
898 pa_stream_finish_upload(sample_stream);
899 }
900 }
901
902 static const char *subscription_event_type_to_string(pa_subscription_event_type_t t) {
903
904 switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
905
906 case PA_SUBSCRIPTION_EVENT_NEW:
907 return _("new");
908
909 case PA_SUBSCRIPTION_EVENT_CHANGE:
910 return _("change");
911
912 case PA_SUBSCRIPTION_EVENT_REMOVE:
913 return _("remove");
914 }
915
916 return _("unknown");
917 }
918
919 static const char *subscription_event_facility_to_string(pa_subscription_event_type_t t) {
920
921 switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
922
923 case PA_SUBSCRIPTION_EVENT_SINK:
924 return _("sink");
925
926 case PA_SUBSCRIPTION_EVENT_SOURCE:
927 return _("source");
928
929 case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
930 return _("sink-input");
931
932 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
933 return _("source-output");
934
935 case PA_SUBSCRIPTION_EVENT_MODULE:
936 return _("module");
937
938 case PA_SUBSCRIPTION_EVENT_CLIENT:
939 return _("client");
940
941 case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
942 return _("sample-cache");
943
944 case PA_SUBSCRIPTION_EVENT_SERVER:
945 return _("server");
946
947 case PA_SUBSCRIPTION_EVENT_CARD:
948 return _("server");
949 }
950
951 return _("unknown");
952 }
953
954 static void context_subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
955 pa_assert(c);
956
957 printf(_("Event '%s' on %s #%u\n"),
958 subscription_event_type_to_string(t),
959 subscription_event_facility_to_string(t),
960 idx);
961 }
962
963 static void context_state_callback(pa_context *c, void *userdata) {
964 pa_assert(c);
965 switch (pa_context_get_state(c)) {
966 case PA_CONTEXT_CONNECTING:
967 case PA_CONTEXT_AUTHORIZING:
968 case PA_CONTEXT_SETTING_NAME:
969 break;
970
971 case PA_CONTEXT_READY:
972 switch (action) {
973 case STAT:
974 pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
975 break;
976
977 case INFO:
978 pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
979 break;
980
981 case PLAY_SAMPLE:
982 pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL));
983 break;
984
985 case REMOVE_SAMPLE:
986 pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
987 break;
988
989 case UPLOAD_SAMPLE:
990 sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
991 pa_assert(sample_stream);
992
993 pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
994 pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
995 pa_stream_connect_upload(sample_stream, sample_length);
996 break;
997
998 case EXIT:
999 pa_operation_unref(pa_context_exit_daemon(c, simple_callback, NULL));
1000 break;
1001
1002 case LIST:
1003 if (list_type) {
1004 if (pa_streq(list_type, "modules"))
1005 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
1006 else if (pa_streq(list_type, "sinks"))
1007 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
1008 else if (pa_streq(list_type, "sources"))
1009 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
1010 else if (pa_streq(list_type, "sink-inputs"))
1011 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
1012 else if (pa_streq(list_type, "source-outputs"))
1013 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
1014 else if (pa_streq(list_type, "clients"))
1015 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
1016 else if (pa_streq(list_type, "samples"))
1017 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
1018 else if (pa_streq(list_type, "cards"))
1019 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
1020 else
1021 pa_assert_not_reached();
1022 } else {
1023 actions = 8;
1024 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
1025 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
1026 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
1027 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
1028 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
1029 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
1030 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
1031 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
1032 }
1033 break;
1034
1035 case MOVE_SINK_INPUT:
1036 pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL));
1037 break;
1038
1039 case MOVE_SOURCE_OUTPUT:
1040 pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
1041 break;
1042
1043 case LOAD_MODULE:
1044 pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
1045 break;
1046
1047 case UNLOAD_MODULE:
1048 pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
1049 break;
1050
1051 case SUSPEND_SINK:
1052 if (sink_name)
1053 pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
1054 else
1055 pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
1056 break;
1057
1058 case SUSPEND_SOURCE:
1059 if (source_name)
1060 pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
1061 else
1062 pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
1063 break;
1064
1065 case SET_CARD_PROFILE:
1066 pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
1067 break;
1068
1069 case SET_SINK_PORT:
1070 pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
1071 break;
1072
1073 case SET_SOURCE_PORT:
1074 pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
1075 break;
1076
1077 case SET_SINK_MUTE:
1078 pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL));
1079 break;
1080
1081 case SET_SOURCE_MUTE:
1082 pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL));
1083 break;
1084
1085 case SET_SINK_INPUT_MUTE:
1086 pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL));
1087 break;
1088
1089 case SET_SINK_VOLUME:
1090 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1091 pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL));
1092 } else {
1093 pa_cvolume v;
1094 pa_cvolume_set(&v, 1, volume);
1095 pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL));
1096 }
1097 break;
1098
1099 case SET_SOURCE_VOLUME:
1100 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1101 pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL));
1102 } else {
1103 pa_cvolume v;
1104 pa_cvolume_set(&v, 1, volume);
1105 pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL));
1106 }
1107 break;
1108
1109 case SET_SINK_INPUT_VOLUME:
1110 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1111 pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL));
1112 } else {
1113 pa_cvolume v;
1114 pa_cvolume_set(&v, 1, volume);
1115 pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL));
1116 }
1117 break;
1118
1119 case SUBSCRIBE:
1120 pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
1121
1122 pa_operation_unref(pa_context_subscribe(
1123 c,
1124 PA_SUBSCRIPTION_MASK_SINK|
1125 PA_SUBSCRIPTION_MASK_SOURCE|
1126 PA_SUBSCRIPTION_MASK_SINK_INPUT|
1127 PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
1128 PA_SUBSCRIPTION_MASK_MODULE|
1129 PA_SUBSCRIPTION_MASK_CLIENT|
1130 PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
1131 PA_SUBSCRIPTION_MASK_SERVER|
1132 PA_SUBSCRIPTION_MASK_CARD,
1133 NULL,
1134 NULL));
1135 break;
1136
1137 default:
1138 pa_assert_not_reached();
1139 }
1140 break;
1141
1142 case PA_CONTEXT_TERMINATED:
1143 quit(0);
1144 break;
1145
1146 case PA_CONTEXT_FAILED:
1147 default:
1148 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
1149 quit(1);
1150 }
1151 }
1152
1153 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
1154 pa_log(_("Got SIGINT, exiting."));
1155 quit(0);
1156 }
1157
1158 static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
1159 double v;
1160 char *vs;
1161
1162 pa_assert(vol_spec);
1163 pa_assert(vol);
1164 pa_assert(vol_flags);
1165
1166 vs = pa_xstrdup(vol_spec);
1167
1168 *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
1169 if (strchr(vs, '.'))
1170 *vol_flags |= VOL_LINEAR;
1171 if (pa_endswith(vs, "%")) {
1172 *vol_flags |= VOL_PERCENT;
1173 vs[strlen(vs)-1] = 0;
1174 }
1175 if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
1176 *vol_flags |= VOL_DECIBEL;
1177 vs[strlen(vs)-2] = 0;
1178 }
1179
1180 if (pa_atod(vs, &v) < 0) {
1181 pa_log(_("Invalid volume specification"));
1182 pa_xfree(vs);
1183 return -1;
1184 }
1185
1186 pa_xfree(vs);
1187
1188 if ((*vol_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1189 if ((*vol_flags & 0x0F) == VOL_UINT)
1190 v += (double) PA_VOLUME_NORM;
1191 if ((*vol_flags & 0x0F) == VOL_PERCENT)
1192 v += 100.0;
1193 if ((*vol_flags & 0x0F) == VOL_LINEAR)
1194 v += 1.0;
1195 }
1196 if ((*vol_flags & 0x0F) == VOL_PERCENT)
1197 v = v * (double) PA_VOLUME_NORM / 100;
1198 if ((*vol_flags & 0x0F) == VOL_LINEAR)
1199 v = pa_sw_volume_from_linear(v);
1200 if ((*vol_flags & 0x0F) == VOL_DECIBEL)
1201 v = pa_sw_volume_from_dB(v);
1202
1203 if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
1204 pa_log(_("Volume outside permissible range.\n"));
1205 return -1;
1206 }
1207
1208 *vol = (pa_volume_t) v;
1209
1210 return 0;
1211 }
1212
1213 static void help(const char *argv0) {
1214
1215 printf(_("%s [options] stat\n"
1216 "%s [options] info\n"
1217 "%s [options] list [short] [TYPE]\n"
1218 "%s [options] exit\n"
1219 "%s [options] upload-sample FILENAME [NAME]\n"
1220 "%s [options] play-sample NAME [SINK]\n"
1221 "%s [options] remove-sample NAME\n"
1222 "%s [options] move-sink-input SINKINPUT SINK\n"
1223 "%s [options] move-source-output SOURCEOUTPUT SOURCE\n"
1224 "%s [options] load-module NAME [ARGS ...]\n"
1225 "%s [options] unload-module MODULE\n"
1226 "%s [options] suspend-sink SINK 1|0\n"
1227 "%s [options] suspend-source SOURCE 1|0\n"
1228 "%s [options] set-card-profile CARD PROFILE\n"
1229 "%s [options] set-sink-port SINK PORT\n"
1230 "%s [options] set-source-port SOURCE PORT\n"
1231 "%s [options] set-sink-volume SINK VOLUME\n"
1232 "%s [options] set-source-volume SOURCE VOLUME\n"
1233 "%s [options] set-sink-input-volume SINKINPUT VOLUME\n"
1234 "%s [options] set-sink-mute SINK 1|0\n"
1235 "%s [options] set-source-mute SOURCE 1|0\n"
1236 "%s [options] set-sink-input-mute SINKINPUT 1|0\n"
1237 "%s [options] subscribe\n\n"
1238 " -h, --help Show this help\n"
1239 " --version Show version\n\n"
1240 " -s, --server=SERVER The name of the server to connect to\n"
1241 " -n, --client-name=NAME How to call this client on the server\n"),
1242 argv0, argv0, argv0, argv0, argv0,
1243 argv0, argv0, argv0, argv0, argv0,
1244 argv0, argv0, argv0, argv0, argv0,
1245 argv0, argv0, argv0, argv0, argv0,
1246 argv0, argv0, argv0);
1247 }
1248
1249 enum {
1250 ARG_VERSION = 256
1251 };
1252
1253 int main(int argc, char *argv[]) {
1254 pa_mainloop *m = NULL;
1255 int ret = 1, c;
1256 char *server = NULL, *bn;
1257
1258 static const struct option long_options[] = {
1259 {"server", 1, NULL, 's'},
1260 {"client-name", 1, NULL, 'n'},
1261 {"version", 0, NULL, ARG_VERSION},
1262 {"help", 0, NULL, 'h'},
1263 {NULL, 0, NULL, 0}
1264 };
1265
1266 setlocale(LC_ALL, "");
1267 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
1268
1269 bn = pa_path_get_filename(argv[0]);
1270
1271 proplist = pa_proplist_new();
1272
1273 while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
1274 switch (c) {
1275 case 'h' :
1276 help(bn);
1277 ret = 0;
1278 goto quit;
1279
1280 case ARG_VERSION:
1281 printf(_("pactl %s\n"
1282 "Compiled with libpulse %s\n"
1283 "Linked with libpulse %s\n"),
1284 PACKAGE_VERSION,
1285 pa_get_headers_version(),
1286 pa_get_library_version());
1287 ret = 0;
1288 goto quit;
1289
1290 case 's':
1291 pa_xfree(server);
1292 server = pa_xstrdup(optarg);
1293 break;
1294
1295 case 'n': {
1296 char *t;
1297
1298 if (!(t = pa_locale_to_utf8(optarg)) ||
1299 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
1300
1301 pa_log(_("Invalid client name '%s'"), t ? t : optarg);
1302 pa_xfree(t);
1303 goto quit;
1304 }
1305
1306 pa_xfree(t);
1307 break;
1308 }
1309
1310 default:
1311 goto quit;
1312 }
1313 }
1314
1315 if (optind < argc) {
1316 if (pa_streq(argv[optind], "stat"))
1317 action = STAT;
1318
1319 else if (pa_streq(argv[optind], "info"))
1320 action = INFO;
1321
1322 else if (pa_streq(argv[optind], "exit"))
1323 action = EXIT;
1324
1325 else if (pa_streq(argv[optind], "list")) {
1326 action = LIST;
1327
1328 for (int i = optind+1; i < argc; i++){
1329 if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
1330 pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
1331 pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
1332 pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) {
1333 list_type = pa_xstrdup(argv[i]);
1334 } else if (pa_streq(argv[i], "short")) {
1335 short_list_format = TRUE;
1336 } else {
1337 pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
1338 goto quit;
1339 }
1340 }
1341
1342 } else if (pa_streq(argv[optind], "upload-sample")) {
1343 struct SF_INFO sfi;
1344 action = UPLOAD_SAMPLE;
1345
1346 if (optind+1 >= argc) {
1347 pa_log(_("Please specify a sample file to load"));
1348 goto quit;
1349 }
1350
1351 if (optind+2 < argc)
1352 sample_name = pa_xstrdup(argv[optind+2]);
1353 else {
1354 char *f = pa_path_get_filename(argv[optind+1]);
1355 sample_name = pa_xstrndup(f, strcspn(f, "."));
1356 }
1357
1358 pa_zero(sfi);
1359 if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
1360 pa_log(_("Failed to open sound file."));
1361 goto quit;
1362 }
1363
1364 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
1365 pa_log(_("Failed to determine sample specification from file."));
1366 goto quit;
1367 }
1368 sample_spec.format = PA_SAMPLE_FLOAT32;
1369
1370 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1371 if (sample_spec.channels > 2)
1372 pa_log(_("Warning: Failed to determine sample specification from file."));
1373 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1374 }
1375
1376 pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
1377 sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
1378
1379 } else if (pa_streq(argv[optind], "play-sample")) {
1380 action = PLAY_SAMPLE;
1381 if (argc != optind+2 && argc != optind+3) {
1382 pa_log(_("You have to specify a sample name to play"));
1383 goto quit;
1384 }
1385
1386 sample_name = pa_xstrdup(argv[optind+1]);
1387
1388 if (optind+2 < argc)
1389 sink_name = pa_xstrdup(argv[optind+2]);
1390
1391 } else if (pa_streq(argv[optind], "remove-sample")) {
1392 action = REMOVE_SAMPLE;
1393 if (argc != optind+2) {
1394 pa_log(_("You have to specify a sample name to remove"));
1395 goto quit;
1396 }
1397
1398 sample_name = pa_xstrdup(argv[optind+1]);
1399
1400 } else if (pa_streq(argv[optind], "move-sink-input")) {
1401 action = MOVE_SINK_INPUT;
1402 if (argc != optind+3) {
1403 pa_log(_("You have to specify a sink input index and a sink"));
1404 goto quit;
1405 }
1406
1407 sink_input_idx = (uint32_t) atoi(argv[optind+1]);
1408 sink_name = pa_xstrdup(argv[optind+2]);
1409
1410 } else if (pa_streq(argv[optind], "move-source-output")) {
1411 action = MOVE_SOURCE_OUTPUT;
1412 if (argc != optind+3) {
1413 pa_log(_("You have to specify a source output index and a source"));
1414 goto quit;
1415 }
1416
1417 source_output_idx = (uint32_t) atoi(argv[optind+1]);
1418 source_name = pa_xstrdup(argv[optind+2]);
1419
1420 } else if (pa_streq(argv[optind], "load-module")) {
1421 int i;
1422 size_t n = 0;
1423 char *p;
1424
1425 action = LOAD_MODULE;
1426
1427 if (argc <= optind+1) {
1428 pa_log(_("You have to specify a module name and arguments."));
1429 goto quit;
1430 }
1431
1432 module_name = argv[optind+1];
1433
1434 for (i = optind+2; i < argc; i++)
1435 n += strlen(argv[i])+1;
1436
1437 if (n > 0) {
1438 p = module_args = pa_xmalloc(n);
1439
1440 for (i = optind+2; i < argc; i++)
1441 p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
1442 }
1443
1444 } else if (pa_streq(argv[optind], "unload-module")) {
1445 action = UNLOAD_MODULE;
1446
1447 if (argc != optind+2) {
1448 pa_log(_("You have to specify a module index"));
1449 goto quit;
1450 }
1451
1452 module_index = (uint32_t) atoi(argv[optind+1]);
1453
1454 } else if (pa_streq(argv[optind], "suspend-sink")) {
1455 action = SUSPEND_SINK;
1456
1457 if (argc > optind+3 || optind+1 >= argc) {
1458 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
1459 goto quit;
1460 }
1461
1462 suspend = pa_parse_boolean(argv[argc-1]);
1463
1464 if (argc > optind+2)
1465 sink_name = pa_xstrdup(argv[optind+1]);
1466
1467 } else if (pa_streq(argv[optind], "suspend-source")) {
1468 action = SUSPEND_SOURCE;
1469
1470 if (argc > optind+3 || optind+1 >= argc) {
1471 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
1472 goto quit;
1473 }
1474
1475 suspend = pa_parse_boolean(argv[argc-1]);
1476
1477 if (argc > optind+2)
1478 source_name = pa_xstrdup(argv[optind+1]);
1479 } else if (pa_streq(argv[optind], "set-card-profile")) {
1480 action = SET_CARD_PROFILE;
1481
1482 if (argc != optind+3) {
1483 pa_log(_("You have to specify a card name/index and a profile name"));
1484 goto quit;
1485 }
1486
1487 card_name = pa_xstrdup(argv[optind+1]);
1488 profile_name = pa_xstrdup(argv[optind+2]);
1489
1490 } else if (pa_streq(argv[optind], "set-sink-port")) {
1491 action = SET_SINK_PORT;
1492
1493 if (argc != optind+3) {
1494 pa_log(_("You have to specify a sink name/index and a port name"));
1495 goto quit;
1496 }
1497
1498 sink_name = pa_xstrdup(argv[optind+1]);
1499 port_name = pa_xstrdup(argv[optind+2]);
1500
1501 } else if (pa_streq(argv[optind], "set-source-port")) {
1502 action = SET_SOURCE_PORT;
1503
1504 if (argc != optind+3) {
1505 pa_log(_("You have to specify a source name/index and a port name"));
1506 goto quit;
1507 }
1508
1509 source_name = pa_xstrdup(argv[optind+1]);
1510 port_name = pa_xstrdup(argv[optind+2]);
1511
1512 } else if (pa_streq(argv[optind], "set-sink-volume")) {
1513 action = SET_SINK_VOLUME;
1514
1515 if (argc != optind+3) {
1516 pa_log(_("You have to specify a sink name/index and a volume"));
1517 goto quit;
1518 }
1519
1520 sink_name = pa_xstrdup(argv[optind+1]);
1521
1522 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1523 goto quit;
1524
1525 } else if (pa_streq(argv[optind], "set-source-volume")) {
1526 action = SET_SOURCE_VOLUME;
1527
1528 if (argc != optind+3) {
1529 pa_log(_("You have to specify a source name/index and a volume"));
1530 goto quit;
1531 }
1532
1533 source_name = pa_xstrdup(argv[optind+1]);
1534
1535 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1536 goto quit;
1537
1538 } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
1539 action = SET_SINK_INPUT_VOLUME;
1540
1541 if (argc != optind+3) {
1542 pa_log(_("You have to specify a sink input index and a volume"));
1543 goto quit;
1544 }
1545
1546 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1547 pa_log(_("Invalid sink input index"));
1548 goto quit;
1549 }
1550
1551 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1552 goto quit;
1553
1554 } else if (pa_streq(argv[optind], "set-sink-mute")) {
1555 int b;
1556 action = SET_SINK_MUTE;
1557
1558 if (argc != optind+3) {
1559 pa_log(_("You have to specify a sink name/index and a mute boolean"));
1560 goto quit;
1561 }
1562
1563 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1564 pa_log(_("Invalid mute specification"));
1565 goto quit;
1566 }
1567
1568 sink_name = pa_xstrdup(argv[optind+1]);
1569 mute = b;
1570
1571 } else if (pa_streq(argv[optind], "set-source-mute")) {
1572 int b;
1573 action = SET_SOURCE_MUTE;
1574
1575 if (argc != optind+3) {
1576 pa_log(_("You have to specify a source name/index and a mute boolean"));
1577 goto quit;
1578 }
1579
1580 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1581 pa_log(_("Invalid mute specification"));
1582 goto quit;
1583 }
1584
1585 source_name = pa_xstrdup(argv[optind+1]);
1586 mute = b;
1587
1588 } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
1589 int b;
1590 action = SET_SINK_INPUT_MUTE;
1591
1592 if (argc != optind+3) {
1593 pa_log(_("You have to specify a sink input index and a mute boolean"));
1594 goto quit;
1595 }
1596
1597 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1598 pa_log(_("Invalid sink input index specification"));
1599 goto quit;
1600 }
1601
1602 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1603 pa_log(_("Invalid mute specification"));
1604 goto quit;
1605 }
1606
1607 mute = b;
1608
1609 } else if (pa_streq(argv[optind], "subscribe"))
1610
1611 action = SUBSCRIBE;
1612
1613 else if (pa_streq(argv[optind], "help")) {
1614 help(bn);
1615 ret = 0;
1616 goto quit;
1617 }
1618 }
1619
1620 if (action == NONE) {
1621 pa_log(_("No valid command specified."));
1622 goto quit;
1623 }
1624
1625 if (!(m = pa_mainloop_new())) {
1626 pa_log(_("pa_mainloop_new() failed."));
1627 goto quit;
1628 }
1629
1630 mainloop_api = pa_mainloop_get_api(m);
1631
1632 pa_assert_se(pa_signal_init(mainloop_api) == 0);
1633 pa_signal_new(SIGINT, exit_signal_callback, NULL);
1634 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1635 pa_disable_sigpipe();
1636
1637 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1638 pa_log(_("pa_context_new() failed."));
1639 goto quit;
1640 }
1641
1642 pa_context_set_state_callback(context, context_state_callback, NULL);
1643 if (pa_context_connect(context, server, 0, NULL) < 0) {
1644 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1645 goto quit;
1646 }
1647
1648 if (pa_mainloop_run(m, &ret) < 0) {
1649 pa_log(_("pa_mainloop_run() failed."));
1650 goto quit;
1651 }
1652
1653 quit:
1654 if (sample_stream)
1655 pa_stream_unref(sample_stream);
1656
1657 if (context)
1658 pa_context_unref(context);
1659
1660 if (m) {
1661 pa_signal_done();
1662 pa_mainloop_free(m);
1663 }
1664
1665 pa_xfree(server);
1666 pa_xfree(list_type);
1667 pa_xfree(sample_name);
1668 pa_xfree(sink_name);
1669 pa_xfree(source_name);
1670 pa_xfree(module_args);
1671 pa_xfree(card_name);
1672 pa_xfree(profile_name);
1673 pa_xfree(port_name);
1674
1675 if (sndfile)
1676 sf_close(sndfile);
1677
1678 if (proplist)
1679 pa_proplist_free(proplist);
1680
1681 return ret;
1682 }