]> code.delx.au - pulseaudio/blob - src/utils/pactl.c
83547a55e6b37bd084144b7b895ff31c69b051da
[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 <getopt.h>
34 #include <locale.h>
35
36 #include <sndfile.h>
37
38 #include <pulse/i18n.h>
39 #include <pulse/pulseaudio.h>
40
41 #include <pulsecore/macro.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/sndfile-util.h>
45
46 static pa_context *context = NULL;
47 static pa_mainloop_api *mainloop_api = NULL;
48
49 static char
50 *list_type = NULL,
51 *sample_name = NULL,
52 *sink_name = NULL,
53 *source_name = NULL,
54 *module_name = NULL,
55 *module_args = NULL,
56 *card_name = NULL,
57 *profile_name = NULL,
58 *port_name = NULL;
59
60 static uint32_t
61 sink_input_idx = PA_INVALID_INDEX,
62 source_output_idx = PA_INVALID_INDEX;
63
64 static pa_bool_t short_list_format = FALSE;
65 static uint32_t module_index;
66 static pa_bool_t suspend;
67 static pa_bool_t mute;
68 static pa_volume_t volume;
69 static enum volume_flags {
70 VOL_UINT = 0,
71 VOL_PERCENT = 1,
72 VOL_LINEAR = 2,
73 VOL_DECIBEL = 3,
74 VOL_ABSOLUTE = 0 << 4,
75 VOL_RELATIVE = 1 << 4,
76 } volume_flags;
77
78 static pa_proplist *proplist = NULL;
79
80 static SNDFILE *sndfile = NULL;
81 static pa_stream *sample_stream = NULL;
82 static pa_sample_spec sample_spec;
83 static pa_channel_map channel_map;
84 static size_t sample_length = 0;
85 static int actions = 1;
86
87 static pa_bool_t nl = FALSE;
88
89 static enum {
90 NONE,
91 EXIT,
92 STAT,
93 INFO,
94 UPLOAD_SAMPLE,
95 PLAY_SAMPLE,
96 REMOVE_SAMPLE,
97 LIST,
98 MOVE_SINK_INPUT,
99 MOVE_SOURCE_OUTPUT,
100 LOAD_MODULE,
101 UNLOAD_MODULE,
102 SUSPEND_SINK,
103 SUSPEND_SOURCE,
104 SET_CARD_PROFILE,
105 SET_SINK_PORT,
106 SET_SOURCE_PORT,
107 SET_SINK_VOLUME,
108 SET_SOURCE_VOLUME,
109 SET_SINK_INPUT_VOLUME,
110 SET_SOURCE_OUTPUT_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 get_source_output_volume_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
867 pa_cvolume cv;
868
869 if (is_last < 0) {
870 pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
871 quit(1);
872 return;
873 }
874
875 if (is_last)
876 return;
877
878 pa_assert(o);
879
880 cv = o->volume;
881 volume_relative_adjust(&cv);
882 pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
883 }
884
885 static void stream_state_callback(pa_stream *s, void *userdata) {
886 pa_assert(s);
887
888 switch (pa_stream_get_state(s)) {
889 case PA_STREAM_CREATING:
890 case PA_STREAM_READY:
891 break;
892
893 case PA_STREAM_TERMINATED:
894 drain();
895 break;
896
897 case PA_STREAM_FAILED:
898 default:
899 pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
900 quit(1);
901 }
902 }
903
904 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
905 sf_count_t l;
906 float *d;
907 pa_assert(s && length && sndfile);
908
909 d = pa_xmalloc(length);
910
911 pa_assert(sample_length >= length);
912 l = (sf_count_t) (length/pa_frame_size(&sample_spec));
913
914 if ((sf_readf_float(sndfile, d, l)) != l) {
915 pa_xfree(d);
916 pa_log(_("Premature end of file"));
917 quit(1);
918 return;
919 }
920
921 pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
922
923 sample_length -= length;
924
925 if (sample_length <= 0) {
926 pa_stream_set_write_callback(sample_stream, NULL, NULL);
927 pa_stream_finish_upload(sample_stream);
928 }
929 }
930
931 static const char *subscription_event_type_to_string(pa_subscription_event_type_t t) {
932
933 switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
934
935 case PA_SUBSCRIPTION_EVENT_NEW:
936 return _("new");
937
938 case PA_SUBSCRIPTION_EVENT_CHANGE:
939 return _("change");
940
941 case PA_SUBSCRIPTION_EVENT_REMOVE:
942 return _("remove");
943 }
944
945 return _("unknown");
946 }
947
948 static const char *subscription_event_facility_to_string(pa_subscription_event_type_t t) {
949
950 switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
951
952 case PA_SUBSCRIPTION_EVENT_SINK:
953 return _("sink");
954
955 case PA_SUBSCRIPTION_EVENT_SOURCE:
956 return _("source");
957
958 case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
959 return _("sink-input");
960
961 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
962 return _("source-output");
963
964 case PA_SUBSCRIPTION_EVENT_MODULE:
965 return _("module");
966
967 case PA_SUBSCRIPTION_EVENT_CLIENT:
968 return _("client");
969
970 case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
971 return _("sample-cache");
972
973 case PA_SUBSCRIPTION_EVENT_SERVER:
974 return _("server");
975
976 case PA_SUBSCRIPTION_EVENT_CARD:
977 return _("server");
978 }
979
980 return _("unknown");
981 }
982
983 static void context_subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
984 pa_assert(c);
985
986 printf(_("Event '%s' on %s #%u\n"),
987 subscription_event_type_to_string(t),
988 subscription_event_facility_to_string(t),
989 idx);
990 }
991
992 static void context_state_callback(pa_context *c, void *userdata) {
993 pa_assert(c);
994 switch (pa_context_get_state(c)) {
995 case PA_CONTEXT_CONNECTING:
996 case PA_CONTEXT_AUTHORIZING:
997 case PA_CONTEXT_SETTING_NAME:
998 break;
999
1000 case PA_CONTEXT_READY:
1001 switch (action) {
1002 case STAT:
1003 pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
1004 break;
1005
1006 case INFO:
1007 pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
1008 break;
1009
1010 case PLAY_SAMPLE:
1011 pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL));
1012 break;
1013
1014 case REMOVE_SAMPLE:
1015 pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
1016 break;
1017
1018 case UPLOAD_SAMPLE:
1019 sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
1020 pa_assert(sample_stream);
1021
1022 pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
1023 pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
1024 pa_stream_connect_upload(sample_stream, sample_length);
1025 break;
1026
1027 case EXIT:
1028 pa_operation_unref(pa_context_exit_daemon(c, simple_callback, NULL));
1029 break;
1030
1031 case LIST:
1032 if (list_type) {
1033 if (pa_streq(list_type, "modules"))
1034 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
1035 else if (pa_streq(list_type, "sinks"))
1036 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
1037 else if (pa_streq(list_type, "sources"))
1038 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
1039 else if (pa_streq(list_type, "sink-inputs"))
1040 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
1041 else if (pa_streq(list_type, "source-outputs"))
1042 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
1043 else if (pa_streq(list_type, "clients"))
1044 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
1045 else if (pa_streq(list_type, "samples"))
1046 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
1047 else if (pa_streq(list_type, "cards"))
1048 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
1049 else
1050 pa_assert_not_reached();
1051 } else {
1052 actions = 8;
1053 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
1054 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
1055 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
1056 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
1057 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
1058 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
1059 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
1060 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
1061 }
1062 break;
1063
1064 case MOVE_SINK_INPUT:
1065 pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL));
1066 break;
1067
1068 case MOVE_SOURCE_OUTPUT:
1069 pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
1070 break;
1071
1072 case LOAD_MODULE:
1073 pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
1074 break;
1075
1076 case UNLOAD_MODULE:
1077 pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
1078 break;
1079
1080 case SUSPEND_SINK:
1081 if (sink_name)
1082 pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
1083 else
1084 pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
1085 break;
1086
1087 case SUSPEND_SOURCE:
1088 if (source_name)
1089 pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
1090 else
1091 pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
1092 break;
1093
1094 case SET_CARD_PROFILE:
1095 pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
1096 break;
1097
1098 case SET_SINK_PORT:
1099 pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
1100 break;
1101
1102 case SET_SOURCE_PORT:
1103 pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
1104 break;
1105
1106 case SET_SINK_MUTE:
1107 pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL));
1108 break;
1109
1110 case SET_SOURCE_MUTE:
1111 pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL));
1112 break;
1113
1114 case SET_SINK_INPUT_MUTE:
1115 pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL));
1116 break;
1117
1118 case SET_SINK_VOLUME:
1119 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1120 pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL));
1121 } else {
1122 pa_cvolume v;
1123 pa_cvolume_set(&v, 1, volume);
1124 pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL));
1125 }
1126 break;
1127
1128 case SET_SOURCE_VOLUME:
1129 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1130 pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL));
1131 } else {
1132 pa_cvolume v;
1133 pa_cvolume_set(&v, 1, volume);
1134 pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL));
1135 }
1136 break;
1137
1138 case SET_SINK_INPUT_VOLUME:
1139 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1140 pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL));
1141 } else {
1142 pa_cvolume v;
1143 pa_cvolume_set(&v, 1, volume);
1144 pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL));
1145 }
1146 break;
1147
1148 case SET_SOURCE_OUTPUT_VOLUME:
1149 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1150 pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL));
1151 } else {
1152 pa_cvolume v;
1153 pa_cvolume_set(&v, 1, volume);
1154 pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &v, simple_callback, NULL));
1155 }
1156 break;
1157
1158 case SUBSCRIBE:
1159 pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
1160
1161 pa_operation_unref(pa_context_subscribe(
1162 c,
1163 PA_SUBSCRIPTION_MASK_SINK|
1164 PA_SUBSCRIPTION_MASK_SOURCE|
1165 PA_SUBSCRIPTION_MASK_SINK_INPUT|
1166 PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
1167 PA_SUBSCRIPTION_MASK_MODULE|
1168 PA_SUBSCRIPTION_MASK_CLIENT|
1169 PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
1170 PA_SUBSCRIPTION_MASK_SERVER|
1171 PA_SUBSCRIPTION_MASK_CARD,
1172 NULL,
1173 NULL));
1174 break;
1175
1176 default:
1177 pa_assert_not_reached();
1178 }
1179 break;
1180
1181 case PA_CONTEXT_TERMINATED:
1182 quit(0);
1183 break;
1184
1185 case PA_CONTEXT_FAILED:
1186 default:
1187 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
1188 quit(1);
1189 }
1190 }
1191
1192 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
1193 pa_log(_("Got SIGINT, exiting."));
1194 quit(0);
1195 }
1196
1197 static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
1198 double v;
1199 char *vs;
1200
1201 pa_assert(vol_spec);
1202 pa_assert(vol);
1203 pa_assert(vol_flags);
1204
1205 vs = pa_xstrdup(vol_spec);
1206
1207 *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
1208 if (strchr(vs, '.'))
1209 *vol_flags |= VOL_LINEAR;
1210 if (pa_endswith(vs, "%")) {
1211 *vol_flags |= VOL_PERCENT;
1212 vs[strlen(vs)-1] = 0;
1213 }
1214 if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
1215 *vol_flags |= VOL_DECIBEL;
1216 vs[strlen(vs)-2] = 0;
1217 }
1218
1219 if (pa_atod(vs, &v) < 0) {
1220 pa_log(_("Invalid volume specification"));
1221 pa_xfree(vs);
1222 return -1;
1223 }
1224
1225 pa_xfree(vs);
1226
1227 if ((*vol_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1228 if ((*vol_flags & 0x0F) == VOL_UINT)
1229 v += (double) PA_VOLUME_NORM;
1230 if ((*vol_flags & 0x0F) == VOL_PERCENT)
1231 v += 100.0;
1232 if ((*vol_flags & 0x0F) == VOL_LINEAR)
1233 v += 1.0;
1234 }
1235 if ((*vol_flags & 0x0F) == VOL_PERCENT)
1236 v = v * (double) PA_VOLUME_NORM / 100;
1237 if ((*vol_flags & 0x0F) == VOL_LINEAR)
1238 v = pa_sw_volume_from_linear(v);
1239 if ((*vol_flags & 0x0F) == VOL_DECIBEL)
1240 v = pa_sw_volume_from_dB(v);
1241
1242 if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
1243 pa_log(_("Volume outside permissible range.\n"));
1244 return -1;
1245 }
1246
1247 *vol = (pa_volume_t) v;
1248
1249 return 0;
1250 }
1251
1252 static void help(const char *argv0) {
1253
1254 printf("%s %s %s\n", argv0, _("[options]"), "stat");
1255 printf("%s %s %s\n", argv0, _("[options]"), "info");
1256 printf("%s %s %s %s\n", argv0, _("[options]"), "list", _("[short] [TYPE]"));
1257 printf("%s %s %s\n", argv0, _("[options]"), "exit");
1258 printf("%s %s %s %s\n", argv0, _("[options]"), "upload-sample", _("FILENAME [NAME]"));
1259 printf("%s %s %s %s\n", argv0, _("[options]"), "play-sample ", _("NAME [SINK]"));
1260 printf("%s %s %s %s\n", argv0, _("[options]"), "remove-sample ", _("NAME"));
1261 printf("%s %s %s %s\n", argv0, _("[options]"), "load-module ", _("NAME [ARGS ...]"));
1262 printf("%s %s %s %s\n", argv0, _("[options]"), "unload-module ", _("#N"));
1263 printf("%s %s %s %s\n", argv0, _("[options]"), "move-(sink-input|source-output)", _("#N SINK|SOURCE"));
1264 printf("%s %s %s %s\n", argv0, _("[options]"), "suspend-(sink|source)", _("NAME|#N 1|0"));
1265 printf("%s %s %s %s\n", argv0, _("[options]"), "set-card-profile ", _("CARD PROFILE"));
1266 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-port", _("NAME|#N PORT"));
1267 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-volume", _("NAME|#N VOLUME"));
1268 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-volume", _("#N VOLUME"));
1269 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-mute", _("NAME|#N 1|0"));
1270 printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-input-mute", _("#N 1|0"));
1271 printf("%s %s %s\n", argv0, _("[options]"), "subscribe");
1272
1273 printf(_("\n"
1274 " -h, --help Show this help\n"
1275 " --version Show version\n\n"
1276 " -s, --server=SERVER The name of the server to connect to\n"
1277 " -n, --client-name=NAME How to call this client on the server\n"));
1278 }
1279
1280 enum {
1281 ARG_VERSION = 256
1282 };
1283
1284 int main(int argc, char *argv[]) {
1285 pa_mainloop *m = NULL;
1286 int ret = 1, c;
1287 char *server = NULL, *bn;
1288
1289 static const struct option long_options[] = {
1290 {"server", 1, NULL, 's'},
1291 {"client-name", 1, NULL, 'n'},
1292 {"version", 0, NULL, ARG_VERSION},
1293 {"help", 0, NULL, 'h'},
1294 {NULL, 0, NULL, 0}
1295 };
1296
1297 setlocale(LC_ALL, "");
1298 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
1299
1300 bn = pa_path_get_filename(argv[0]);
1301
1302 proplist = pa_proplist_new();
1303
1304 while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
1305 switch (c) {
1306 case 'h' :
1307 help(bn);
1308 ret = 0;
1309 goto quit;
1310
1311 case ARG_VERSION:
1312 printf(_("pactl %s\n"
1313 "Compiled with libpulse %s\n"
1314 "Linked with libpulse %s\n"),
1315 PACKAGE_VERSION,
1316 pa_get_headers_version(),
1317 pa_get_library_version());
1318 ret = 0;
1319 goto quit;
1320
1321 case 's':
1322 pa_xfree(server);
1323 server = pa_xstrdup(optarg);
1324 break;
1325
1326 case 'n': {
1327 char *t;
1328
1329 if (!(t = pa_locale_to_utf8(optarg)) ||
1330 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
1331
1332 pa_log(_("Invalid client name '%s'"), t ? t : optarg);
1333 pa_xfree(t);
1334 goto quit;
1335 }
1336
1337 pa_xfree(t);
1338 break;
1339 }
1340
1341 default:
1342 goto quit;
1343 }
1344 }
1345
1346 if (optind < argc) {
1347 if (pa_streq(argv[optind], "stat"))
1348 action = STAT;
1349
1350 else if (pa_streq(argv[optind], "info"))
1351 action = INFO;
1352
1353 else if (pa_streq(argv[optind], "exit"))
1354 action = EXIT;
1355
1356 else if (pa_streq(argv[optind], "list")) {
1357 action = LIST;
1358
1359 for (int i = optind+1; i < argc; i++){
1360 if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
1361 pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
1362 pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
1363 pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) {
1364 list_type = pa_xstrdup(argv[i]);
1365 } else if (pa_streq(argv[i], "short")) {
1366 short_list_format = TRUE;
1367 } else {
1368 pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
1369 goto quit;
1370 }
1371 }
1372
1373 } else if (pa_streq(argv[optind], "upload-sample")) {
1374 struct SF_INFO sfi;
1375 action = UPLOAD_SAMPLE;
1376
1377 if (optind+1 >= argc) {
1378 pa_log(_("Please specify a sample file to load"));
1379 goto quit;
1380 }
1381
1382 if (optind+2 < argc)
1383 sample_name = pa_xstrdup(argv[optind+2]);
1384 else {
1385 char *f = pa_path_get_filename(argv[optind+1]);
1386 sample_name = pa_xstrndup(f, strcspn(f, "."));
1387 }
1388
1389 pa_zero(sfi);
1390 if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
1391 pa_log(_("Failed to open sound file."));
1392 goto quit;
1393 }
1394
1395 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
1396 pa_log(_("Failed to determine sample specification from file."));
1397 goto quit;
1398 }
1399 sample_spec.format = PA_SAMPLE_FLOAT32;
1400
1401 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1402 if (sample_spec.channels > 2)
1403 pa_log(_("Warning: Failed to determine sample specification from file."));
1404 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1405 }
1406
1407 pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
1408 sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
1409
1410 } else if (pa_streq(argv[optind], "play-sample")) {
1411 action = PLAY_SAMPLE;
1412 if (argc != optind+2 && argc != optind+3) {
1413 pa_log(_("You have to specify a sample name to play"));
1414 goto quit;
1415 }
1416
1417 sample_name = pa_xstrdup(argv[optind+1]);
1418
1419 if (optind+2 < argc)
1420 sink_name = pa_xstrdup(argv[optind+2]);
1421
1422 } else if (pa_streq(argv[optind], "remove-sample")) {
1423 action = REMOVE_SAMPLE;
1424 if (argc != optind+2) {
1425 pa_log(_("You have to specify a sample name to remove"));
1426 goto quit;
1427 }
1428
1429 sample_name = pa_xstrdup(argv[optind+1]);
1430
1431 } else if (pa_streq(argv[optind], "move-sink-input")) {
1432 action = MOVE_SINK_INPUT;
1433 if (argc != optind+3) {
1434 pa_log(_("You have to specify a sink input index and a sink"));
1435 goto quit;
1436 }
1437
1438 sink_input_idx = (uint32_t) atoi(argv[optind+1]);
1439 sink_name = pa_xstrdup(argv[optind+2]);
1440
1441 } else if (pa_streq(argv[optind], "move-source-output")) {
1442 action = MOVE_SOURCE_OUTPUT;
1443 if (argc != optind+3) {
1444 pa_log(_("You have to specify a source output index and a source"));
1445 goto quit;
1446 }
1447
1448 source_output_idx = (uint32_t) atoi(argv[optind+1]);
1449 source_name = pa_xstrdup(argv[optind+2]);
1450
1451 } else if (pa_streq(argv[optind], "load-module")) {
1452 int i;
1453 size_t n = 0;
1454 char *p;
1455
1456 action = LOAD_MODULE;
1457
1458 if (argc <= optind+1) {
1459 pa_log(_("You have to specify a module name and arguments."));
1460 goto quit;
1461 }
1462
1463 module_name = argv[optind+1];
1464
1465 for (i = optind+2; i < argc; i++)
1466 n += strlen(argv[i])+1;
1467
1468 if (n > 0) {
1469 p = module_args = pa_xmalloc(n);
1470
1471 for (i = optind+2; i < argc; i++)
1472 p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
1473 }
1474
1475 } else if (pa_streq(argv[optind], "unload-module")) {
1476 action = UNLOAD_MODULE;
1477
1478 if (argc != optind+2) {
1479 pa_log(_("You have to specify a module index"));
1480 goto quit;
1481 }
1482
1483 module_index = (uint32_t) atoi(argv[optind+1]);
1484
1485 } else if (pa_streq(argv[optind], "suspend-sink")) {
1486 action = SUSPEND_SINK;
1487
1488 if (argc > optind+3 || optind+1 >= argc) {
1489 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
1490 goto quit;
1491 }
1492
1493 suspend = pa_parse_boolean(argv[argc-1]);
1494
1495 if (argc > optind+2)
1496 sink_name = pa_xstrdup(argv[optind+1]);
1497
1498 } else if (pa_streq(argv[optind], "suspend-source")) {
1499 action = SUSPEND_SOURCE;
1500
1501 if (argc > optind+3 || optind+1 >= argc) {
1502 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
1503 goto quit;
1504 }
1505
1506 suspend = pa_parse_boolean(argv[argc-1]);
1507
1508 if (argc > optind+2)
1509 source_name = pa_xstrdup(argv[optind+1]);
1510 } else if (pa_streq(argv[optind], "set-card-profile")) {
1511 action = SET_CARD_PROFILE;
1512
1513 if (argc != optind+3) {
1514 pa_log(_("You have to specify a card name/index and a profile name"));
1515 goto quit;
1516 }
1517
1518 card_name = pa_xstrdup(argv[optind+1]);
1519 profile_name = pa_xstrdup(argv[optind+2]);
1520
1521 } else if (pa_streq(argv[optind], "set-sink-port")) {
1522 action = SET_SINK_PORT;
1523
1524 if (argc != optind+3) {
1525 pa_log(_("You have to specify a sink name/index and a port name"));
1526 goto quit;
1527 }
1528
1529 sink_name = pa_xstrdup(argv[optind+1]);
1530 port_name = pa_xstrdup(argv[optind+2]);
1531
1532 } else if (pa_streq(argv[optind], "set-source-port")) {
1533 action = SET_SOURCE_PORT;
1534
1535 if (argc != optind+3) {
1536 pa_log(_("You have to specify a source name/index and a port name"));
1537 goto quit;
1538 }
1539
1540 source_name = pa_xstrdup(argv[optind+1]);
1541 port_name = pa_xstrdup(argv[optind+2]);
1542
1543 } else if (pa_streq(argv[optind], "set-sink-volume")) {
1544 action = SET_SINK_VOLUME;
1545
1546 if (argc != optind+3) {
1547 pa_log(_("You have to specify a sink name/index and a volume"));
1548 goto quit;
1549 }
1550
1551 sink_name = pa_xstrdup(argv[optind+1]);
1552
1553 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1554 goto quit;
1555
1556 } else if (pa_streq(argv[optind], "set-source-volume")) {
1557 action = SET_SOURCE_VOLUME;
1558
1559 if (argc != optind+3) {
1560 pa_log(_("You have to specify a source name/index and a volume"));
1561 goto quit;
1562 }
1563
1564 source_name = pa_xstrdup(argv[optind+1]);
1565
1566 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1567 goto quit;
1568
1569 } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
1570 action = SET_SINK_INPUT_VOLUME;
1571
1572 if (argc != optind+3) {
1573 pa_log(_("You have to specify a sink input index and a volume"));
1574 goto quit;
1575 }
1576
1577 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1578 pa_log(_("Invalid sink input index"));
1579 goto quit;
1580 }
1581
1582 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1583 goto quit;
1584
1585 } else if (pa_streq(argv[optind], "set-source-output-volume")) {
1586 action = SET_SOURCE_OUTPUT_VOLUME;
1587
1588 if (argc != optind+3) {
1589 pa_log(_("You have to specify a source output index and a volume"));
1590 goto quit;
1591 }
1592
1593 if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
1594 pa_log(_("Invalid source output index"));
1595 goto quit;
1596 }
1597
1598 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1599 goto quit;
1600
1601 } else if (pa_streq(argv[optind], "set-sink-mute")) {
1602 int b;
1603 action = SET_SINK_MUTE;
1604
1605 if (argc != optind+3) {
1606 pa_log(_("You have to specify a sink name/index and a mute boolean"));
1607 goto quit;
1608 }
1609
1610 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1611 pa_log(_("Invalid mute specification"));
1612 goto quit;
1613 }
1614
1615 sink_name = pa_xstrdup(argv[optind+1]);
1616 mute = b;
1617
1618 } else if (pa_streq(argv[optind], "set-source-mute")) {
1619 int b;
1620 action = SET_SOURCE_MUTE;
1621
1622 if (argc != optind+3) {
1623 pa_log(_("You have to specify a source name/index and a mute boolean"));
1624 goto quit;
1625 }
1626
1627 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1628 pa_log(_("Invalid mute specification"));
1629 goto quit;
1630 }
1631
1632 source_name = pa_xstrdup(argv[optind+1]);
1633 mute = b;
1634
1635 } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
1636 int b;
1637 action = SET_SINK_INPUT_MUTE;
1638
1639 if (argc != optind+3) {
1640 pa_log(_("You have to specify a sink input index and a mute boolean"));
1641 goto quit;
1642 }
1643
1644 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1645 pa_log(_("Invalid sink input index specification"));
1646 goto quit;
1647 }
1648
1649 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1650 pa_log(_("Invalid mute specification"));
1651 goto quit;
1652 }
1653
1654 mute = b;
1655
1656 } else if (pa_streq(argv[optind], "subscribe"))
1657
1658 action = SUBSCRIBE;
1659
1660 else if (pa_streq(argv[optind], "help")) {
1661 help(bn);
1662 ret = 0;
1663 goto quit;
1664 }
1665 }
1666
1667 if (action == NONE) {
1668 pa_log(_("No valid command specified."));
1669 goto quit;
1670 }
1671
1672 if (!(m = pa_mainloop_new())) {
1673 pa_log(_("pa_mainloop_new() failed."));
1674 goto quit;
1675 }
1676
1677 mainloop_api = pa_mainloop_get_api(m);
1678
1679 pa_assert_se(pa_signal_init(mainloop_api) == 0);
1680 pa_signal_new(SIGINT, exit_signal_callback, NULL);
1681 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1682 pa_disable_sigpipe();
1683
1684 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1685 pa_log(_("pa_context_new() failed."));
1686 goto quit;
1687 }
1688
1689 pa_context_set_state_callback(context, context_state_callback, NULL);
1690 if (pa_context_connect(context, server, 0, NULL) < 0) {
1691 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1692 goto quit;
1693 }
1694
1695 if (pa_mainloop_run(m, &ret) < 0) {
1696 pa_log(_("pa_mainloop_run() failed."));
1697 goto quit;
1698 }
1699
1700 quit:
1701 if (sample_stream)
1702 pa_stream_unref(sample_stream);
1703
1704 if (context)
1705 pa_context_unref(context);
1706
1707 if (m) {
1708 pa_signal_done();
1709 pa_mainloop_free(m);
1710 }
1711
1712 pa_xfree(server);
1713 pa_xfree(list_type);
1714 pa_xfree(sample_name);
1715 pa_xfree(sink_name);
1716 pa_xfree(source_name);
1717 pa_xfree(module_args);
1718 pa_xfree(card_name);
1719 pa_xfree(profile_name);
1720 pa_xfree(port_name);
1721
1722 if (sndfile)
1723 sf_close(sndfile);
1724
1725 if (proplist)
1726 pa_proplist_free(proplist);
1727
1728 return ret;
1729 }