]> code.delx.au - pulseaudio/blob - src/utils/pactl.c
2c5be93498901df80219a124c23bdcd92c7ece90
[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/pulseaudio.h>
39
40 #include <pulsecore/i18n.h>
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 if (short_list_format)
1005 break;
1006 actions++;
1007
1008 case INFO:
1009 pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
1010 break;
1011
1012 case PLAY_SAMPLE:
1013 pa_operation_unref(pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL));
1014 break;
1015
1016 case REMOVE_SAMPLE:
1017 pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
1018 break;
1019
1020 case UPLOAD_SAMPLE:
1021 sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
1022 pa_assert(sample_stream);
1023
1024 pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
1025 pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
1026 pa_stream_connect_upload(sample_stream, sample_length);
1027 break;
1028
1029 case EXIT:
1030 pa_operation_unref(pa_context_exit_daemon(c, simple_callback, NULL));
1031 break;
1032
1033 case LIST:
1034 if (list_type) {
1035 if (pa_streq(list_type, "modules"))
1036 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
1037 else if (pa_streq(list_type, "sinks"))
1038 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
1039 else if (pa_streq(list_type, "sources"))
1040 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
1041 else if (pa_streq(list_type, "sink-inputs"))
1042 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
1043 else if (pa_streq(list_type, "source-outputs"))
1044 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
1045 else if (pa_streq(list_type, "clients"))
1046 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
1047 else if (pa_streq(list_type, "samples"))
1048 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
1049 else if (pa_streq(list_type, "cards"))
1050 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
1051 else
1052 pa_assert_not_reached();
1053 } else {
1054 actions = 8;
1055 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
1056 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
1057 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
1058 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
1059 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
1060 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
1061 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
1062 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
1063 }
1064 break;
1065
1066 case MOVE_SINK_INPUT:
1067 pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL));
1068 break;
1069
1070 case MOVE_SOURCE_OUTPUT:
1071 pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
1072 break;
1073
1074 case LOAD_MODULE:
1075 pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
1076 break;
1077
1078 case UNLOAD_MODULE:
1079 pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
1080 break;
1081
1082 case SUSPEND_SINK:
1083 if (sink_name)
1084 pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
1085 else
1086 pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
1087 break;
1088
1089 case SUSPEND_SOURCE:
1090 if (source_name)
1091 pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
1092 else
1093 pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
1094 break;
1095
1096 case SET_CARD_PROFILE:
1097 pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
1098 break;
1099
1100 case SET_SINK_PORT:
1101 pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
1102 break;
1103
1104 case SET_SOURCE_PORT:
1105 pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
1106 break;
1107
1108 case SET_SINK_MUTE:
1109 pa_operation_unref(pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL));
1110 break;
1111
1112 case SET_SOURCE_MUTE:
1113 pa_operation_unref(pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL));
1114 break;
1115
1116 case SET_SINK_INPUT_MUTE:
1117 pa_operation_unref(pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL));
1118 break;
1119
1120 case SET_SINK_VOLUME:
1121 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1122 pa_operation_unref(pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL));
1123 } else {
1124 pa_cvolume v;
1125 pa_cvolume_set(&v, 1, volume);
1126 pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &v, simple_callback, NULL));
1127 }
1128 break;
1129
1130 case SET_SOURCE_VOLUME:
1131 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1132 pa_operation_unref(pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL));
1133 } else {
1134 pa_cvolume v;
1135 pa_cvolume_set(&v, 1, volume);
1136 pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &v, simple_callback, NULL));
1137 }
1138 break;
1139
1140 case SET_SINK_INPUT_VOLUME:
1141 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1142 pa_operation_unref(pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL));
1143 } else {
1144 pa_cvolume v;
1145 pa_cvolume_set(&v, 1, volume);
1146 pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &v, simple_callback, NULL));
1147 }
1148 break;
1149
1150 case SET_SOURCE_OUTPUT_VOLUME:
1151 if ((volume_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1152 pa_operation_unref(pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL));
1153 } else {
1154 pa_cvolume v;
1155 pa_cvolume_set(&v, 1, volume);
1156 pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &v, simple_callback, NULL));
1157 }
1158 break;
1159
1160 case SUBSCRIBE:
1161 pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
1162
1163 pa_operation_unref(pa_context_subscribe(
1164 c,
1165 PA_SUBSCRIPTION_MASK_SINK|
1166 PA_SUBSCRIPTION_MASK_SOURCE|
1167 PA_SUBSCRIPTION_MASK_SINK_INPUT|
1168 PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
1169 PA_SUBSCRIPTION_MASK_MODULE|
1170 PA_SUBSCRIPTION_MASK_CLIENT|
1171 PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
1172 PA_SUBSCRIPTION_MASK_SERVER|
1173 PA_SUBSCRIPTION_MASK_CARD,
1174 NULL,
1175 NULL));
1176 break;
1177
1178 default:
1179 pa_assert_not_reached();
1180 }
1181 break;
1182
1183 case PA_CONTEXT_TERMINATED:
1184 quit(0);
1185 break;
1186
1187 case PA_CONTEXT_FAILED:
1188 default:
1189 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
1190 quit(1);
1191 }
1192 }
1193
1194 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
1195 pa_log(_("Got SIGINT, exiting."));
1196 quit(0);
1197 }
1198
1199 static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
1200 double v;
1201 char *vs;
1202
1203 pa_assert(vol_spec);
1204 pa_assert(vol);
1205 pa_assert(vol_flags);
1206
1207 vs = pa_xstrdup(vol_spec);
1208
1209 *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
1210 if (strchr(vs, '.'))
1211 *vol_flags |= VOL_LINEAR;
1212 if (pa_endswith(vs, "%")) {
1213 *vol_flags |= VOL_PERCENT;
1214 vs[strlen(vs)-1] = 0;
1215 }
1216 if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
1217 *vol_flags |= VOL_DECIBEL;
1218 vs[strlen(vs)-2] = 0;
1219 }
1220
1221 if (pa_atod(vs, &v) < 0) {
1222 pa_log(_("Invalid volume specification"));
1223 pa_xfree(vs);
1224 return -1;
1225 }
1226
1227 pa_xfree(vs);
1228
1229 if ((*vol_flags & VOL_RELATIVE) == VOL_RELATIVE) {
1230 if ((*vol_flags & 0x0F) == VOL_UINT)
1231 v += (double) PA_VOLUME_NORM;
1232 if ((*vol_flags & 0x0F) == VOL_PERCENT)
1233 v += 100.0;
1234 if ((*vol_flags & 0x0F) == VOL_LINEAR)
1235 v += 1.0;
1236 }
1237 if ((*vol_flags & 0x0F) == VOL_PERCENT)
1238 v = v * (double) PA_VOLUME_NORM / 100;
1239 if ((*vol_flags & 0x0F) == VOL_LINEAR)
1240 v = pa_sw_volume_from_linear(v);
1241 if ((*vol_flags & 0x0F) == VOL_DECIBEL)
1242 v = pa_sw_volume_from_dB(v);
1243
1244 if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
1245 pa_log(_("Volume outside permissible range.\n"));
1246 return -1;
1247 }
1248
1249 *vol = (pa_volume_t) v;
1250
1251 return 0;
1252 }
1253
1254 static void help(const char *argv0) {
1255
1256 printf("%s %s %s\n", argv0, _("[options]"), "stat [short]");
1257 printf("%s %s %s\n", argv0, _("[options]"), "info");
1258 printf("%s %s %s %s\n", argv0, _("[options]"), "list [short]", _("[TYPE]"));
1259 printf("%s %s %s\n", argv0, _("[options]"), "exit");
1260 printf("%s %s %s %s\n", argv0, _("[options]"), "upload-sample", _("FILENAME [NAME]"));
1261 printf("%s %s %s %s\n", argv0, _("[options]"), "play-sample ", _("NAME [SINK]"));
1262 printf("%s %s %s %s\n", argv0, _("[options]"), "remove-sample ", _("NAME"));
1263 printf("%s %s %s %s\n", argv0, _("[options]"), "load-module ", _("NAME [ARGS ...]"));
1264 printf("%s %s %s %s\n", argv0, _("[options]"), "unload-module ", _("#N"));
1265 printf("%s %s %s %s\n", argv0, _("[options]"), "move-(sink-input|source-output)", _("#N SINK|SOURCE"));
1266 printf("%s %s %s %s\n", argv0, _("[options]"), "suspend-(sink|source)", _("NAME|#N 1|0"));
1267 printf("%s %s %s %s\n", argv0, _("[options]"), "set-card-profile ", _("CARD PROFILE"));
1268 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-port", _("NAME|#N PORT"));
1269 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-volume", _("NAME|#N VOLUME"));
1270 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-volume", _("#N VOLUME"));
1271 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-mute", _("NAME|#N 1|0"));
1272 printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-input-mute", _("#N 1|0"));
1273 printf("%s %s %s\n", argv0, _("[options]"), "subscribe");
1274
1275 printf(_("\n"
1276 " -h, --help Show this help\n"
1277 " --version Show version\n\n"
1278 " -s, --server=SERVER The name of the server to connect to\n"
1279 " -n, --client-name=NAME How to call this client on the server\n"));
1280 }
1281
1282 enum {
1283 ARG_VERSION = 256
1284 };
1285
1286 int main(int argc, char *argv[]) {
1287 pa_mainloop *m = NULL;
1288 int ret = 1, c;
1289 char *server = NULL, *bn;
1290
1291 static const struct option long_options[] = {
1292 {"server", 1, NULL, 's'},
1293 {"client-name", 1, NULL, 'n'},
1294 {"version", 0, NULL, ARG_VERSION},
1295 {"help", 0, NULL, 'h'},
1296 {NULL, 0, NULL, 0}
1297 };
1298
1299 setlocale(LC_ALL, "");
1300 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
1301
1302 bn = pa_path_get_filename(argv[0]);
1303
1304 proplist = pa_proplist_new();
1305
1306 while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
1307 switch (c) {
1308 case 'h' :
1309 help(bn);
1310 ret = 0;
1311 goto quit;
1312
1313 case ARG_VERSION:
1314 printf(_("pactl %s\n"
1315 "Compiled with libpulse %s\n"
1316 "Linked with libpulse %s\n"),
1317 PACKAGE_VERSION,
1318 pa_get_headers_version(),
1319 pa_get_library_version());
1320 ret = 0;
1321 goto quit;
1322
1323 case 's':
1324 pa_xfree(server);
1325 server = pa_xstrdup(optarg);
1326 break;
1327
1328 case 'n': {
1329 char *t;
1330
1331 if (!(t = pa_locale_to_utf8(optarg)) ||
1332 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
1333
1334 pa_log(_("Invalid client name '%s'"), t ? t : optarg);
1335 pa_xfree(t);
1336 goto quit;
1337 }
1338
1339 pa_xfree(t);
1340 break;
1341 }
1342
1343 default:
1344 goto quit;
1345 }
1346 }
1347
1348 if (optind < argc) {
1349 if (pa_streq(argv[optind], "stat")) {
1350 action = STAT;
1351 short_list_format = FALSE;
1352 if (optind+1 < argc && pa_streq(argv[optind+1], "short"))
1353 short_list_format = TRUE;
1354
1355 } else if (pa_streq(argv[optind], "info"))
1356 action = INFO;
1357
1358 else if (pa_streq(argv[optind], "exit"))
1359 action = EXIT;
1360
1361 else if (pa_streq(argv[optind], "list")) {
1362 action = LIST;
1363
1364 for (int i = optind+1; i < argc; i++){
1365 if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
1366 pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
1367 pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
1368 pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards")) {
1369 list_type = pa_xstrdup(argv[i]);
1370 } else if (pa_streq(argv[i], "short")) {
1371 short_list_format = TRUE;
1372 } else {
1373 pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards");
1374 goto quit;
1375 }
1376 }
1377
1378 } else if (pa_streq(argv[optind], "upload-sample")) {
1379 struct SF_INFO sfi;
1380 action = UPLOAD_SAMPLE;
1381
1382 if (optind+1 >= argc) {
1383 pa_log(_("Please specify a sample file to load"));
1384 goto quit;
1385 }
1386
1387 if (optind+2 < argc)
1388 sample_name = pa_xstrdup(argv[optind+2]);
1389 else {
1390 char *f = pa_path_get_filename(argv[optind+1]);
1391 sample_name = pa_xstrndup(f, strcspn(f, "."));
1392 }
1393
1394 pa_zero(sfi);
1395 if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
1396 pa_log(_("Failed to open sound file."));
1397 goto quit;
1398 }
1399
1400 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
1401 pa_log(_("Failed to determine sample specification from file."));
1402 goto quit;
1403 }
1404 sample_spec.format = PA_SAMPLE_FLOAT32;
1405
1406 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
1407 if (sample_spec.channels > 2)
1408 pa_log(_("Warning: Failed to determine sample specification from file."));
1409 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
1410 }
1411
1412 pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
1413 sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
1414
1415 } else if (pa_streq(argv[optind], "play-sample")) {
1416 action = PLAY_SAMPLE;
1417 if (argc != optind+2 && argc != optind+3) {
1418 pa_log(_("You have to specify a sample name to play"));
1419 goto quit;
1420 }
1421
1422 sample_name = pa_xstrdup(argv[optind+1]);
1423
1424 if (optind+2 < argc)
1425 sink_name = pa_xstrdup(argv[optind+2]);
1426
1427 } else if (pa_streq(argv[optind], "remove-sample")) {
1428 action = REMOVE_SAMPLE;
1429 if (argc != optind+2) {
1430 pa_log(_("You have to specify a sample name to remove"));
1431 goto quit;
1432 }
1433
1434 sample_name = pa_xstrdup(argv[optind+1]);
1435
1436 } else if (pa_streq(argv[optind], "move-sink-input")) {
1437 action = MOVE_SINK_INPUT;
1438 if (argc != optind+3) {
1439 pa_log(_("You have to specify a sink input index and a sink"));
1440 goto quit;
1441 }
1442
1443 sink_input_idx = (uint32_t) atoi(argv[optind+1]);
1444 sink_name = pa_xstrdup(argv[optind+2]);
1445
1446 } else if (pa_streq(argv[optind], "move-source-output")) {
1447 action = MOVE_SOURCE_OUTPUT;
1448 if (argc != optind+3) {
1449 pa_log(_("You have to specify a source output index and a source"));
1450 goto quit;
1451 }
1452
1453 source_output_idx = (uint32_t) atoi(argv[optind+1]);
1454 source_name = pa_xstrdup(argv[optind+2]);
1455
1456 } else if (pa_streq(argv[optind], "load-module")) {
1457 int i;
1458 size_t n = 0;
1459 char *p;
1460
1461 action = LOAD_MODULE;
1462
1463 if (argc <= optind+1) {
1464 pa_log(_("You have to specify a module name and arguments."));
1465 goto quit;
1466 }
1467
1468 module_name = argv[optind+1];
1469
1470 for (i = optind+2; i < argc; i++)
1471 n += strlen(argv[i])+1;
1472
1473 if (n > 0) {
1474 p = module_args = pa_xmalloc(n);
1475
1476 for (i = optind+2; i < argc; i++)
1477 p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
1478 }
1479
1480 } else if (pa_streq(argv[optind], "unload-module")) {
1481 action = UNLOAD_MODULE;
1482
1483 if (argc != optind+2) {
1484 pa_log(_("You have to specify a module index"));
1485 goto quit;
1486 }
1487
1488 module_index = (uint32_t) atoi(argv[optind+1]);
1489
1490 } else if (pa_streq(argv[optind], "suspend-sink")) {
1491 action = SUSPEND_SINK;
1492
1493 if (argc > optind+3 || optind+1 >= argc) {
1494 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
1495 goto quit;
1496 }
1497
1498 suspend = pa_parse_boolean(argv[argc-1]);
1499
1500 if (argc > optind+2)
1501 sink_name = pa_xstrdup(argv[optind+1]);
1502
1503 } else if (pa_streq(argv[optind], "suspend-source")) {
1504 action = SUSPEND_SOURCE;
1505
1506 if (argc > optind+3 || optind+1 >= argc) {
1507 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
1508 goto quit;
1509 }
1510
1511 suspend = pa_parse_boolean(argv[argc-1]);
1512
1513 if (argc > optind+2)
1514 source_name = pa_xstrdup(argv[optind+1]);
1515 } else if (pa_streq(argv[optind], "set-card-profile")) {
1516 action = SET_CARD_PROFILE;
1517
1518 if (argc != optind+3) {
1519 pa_log(_("You have to specify a card name/index and a profile name"));
1520 goto quit;
1521 }
1522
1523 card_name = pa_xstrdup(argv[optind+1]);
1524 profile_name = pa_xstrdup(argv[optind+2]);
1525
1526 } else if (pa_streq(argv[optind], "set-sink-port")) {
1527 action = SET_SINK_PORT;
1528
1529 if (argc != optind+3) {
1530 pa_log(_("You have to specify a sink name/index and a port name"));
1531 goto quit;
1532 }
1533
1534 sink_name = pa_xstrdup(argv[optind+1]);
1535 port_name = pa_xstrdup(argv[optind+2]);
1536
1537 } else if (pa_streq(argv[optind], "set-source-port")) {
1538 action = SET_SOURCE_PORT;
1539
1540 if (argc != optind+3) {
1541 pa_log(_("You have to specify a source name/index and a port name"));
1542 goto quit;
1543 }
1544
1545 source_name = pa_xstrdup(argv[optind+1]);
1546 port_name = pa_xstrdup(argv[optind+2]);
1547
1548 } else if (pa_streq(argv[optind], "set-sink-volume")) {
1549 action = SET_SINK_VOLUME;
1550
1551 if (argc != optind+3) {
1552 pa_log(_("You have to specify a sink name/index and a volume"));
1553 goto quit;
1554 }
1555
1556 sink_name = pa_xstrdup(argv[optind+1]);
1557
1558 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1559 goto quit;
1560
1561 } else if (pa_streq(argv[optind], "set-source-volume")) {
1562 action = SET_SOURCE_VOLUME;
1563
1564 if (argc != optind+3) {
1565 pa_log(_("You have to specify a source name/index and a volume"));
1566 goto quit;
1567 }
1568
1569 source_name = pa_xstrdup(argv[optind+1]);
1570
1571 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1572 goto quit;
1573
1574 } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
1575 action = SET_SINK_INPUT_VOLUME;
1576
1577 if (argc != optind+3) {
1578 pa_log(_("You have to specify a sink input index and a volume"));
1579 goto quit;
1580 }
1581
1582 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1583 pa_log(_("Invalid sink input index"));
1584 goto quit;
1585 }
1586
1587 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1588 goto quit;
1589
1590 } else if (pa_streq(argv[optind], "set-source-output-volume")) {
1591 action = SET_SOURCE_OUTPUT_VOLUME;
1592
1593 if (argc != optind+3) {
1594 pa_log(_("You have to specify a source output index and a volume"));
1595 goto quit;
1596 }
1597
1598 if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
1599 pa_log(_("Invalid source output index"));
1600 goto quit;
1601 }
1602
1603 if (parse_volume(argv[optind+2], &volume, &volume_flags) < 0)
1604 goto quit;
1605
1606 } else if (pa_streq(argv[optind], "set-sink-mute")) {
1607 int b;
1608 action = SET_SINK_MUTE;
1609
1610 if (argc != optind+3) {
1611 pa_log(_("You have to specify a sink name/index and a mute boolean"));
1612 goto quit;
1613 }
1614
1615 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1616 pa_log(_("Invalid mute specification"));
1617 goto quit;
1618 }
1619
1620 sink_name = pa_xstrdup(argv[optind+1]);
1621 mute = b;
1622
1623 } else if (pa_streq(argv[optind], "set-source-mute")) {
1624 int b;
1625 action = SET_SOURCE_MUTE;
1626
1627 if (argc != optind+3) {
1628 pa_log(_("You have to specify a source name/index and a mute boolean"));
1629 goto quit;
1630 }
1631
1632 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1633 pa_log(_("Invalid mute specification"));
1634 goto quit;
1635 }
1636
1637 source_name = pa_xstrdup(argv[optind+1]);
1638 mute = b;
1639
1640 } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
1641 int b;
1642 action = SET_SINK_INPUT_MUTE;
1643
1644 if (argc != optind+3) {
1645 pa_log(_("You have to specify a sink input index and a mute boolean"));
1646 goto quit;
1647 }
1648
1649 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
1650 pa_log(_("Invalid sink input index specification"));
1651 goto quit;
1652 }
1653
1654 if ((b = pa_parse_boolean(argv[optind+2])) < 0) {
1655 pa_log(_("Invalid mute specification"));
1656 goto quit;
1657 }
1658
1659 mute = b;
1660
1661 } else if (pa_streq(argv[optind], "subscribe"))
1662
1663 action = SUBSCRIBE;
1664
1665 else if (pa_streq(argv[optind], "help")) {
1666 help(bn);
1667 ret = 0;
1668 goto quit;
1669 }
1670 }
1671
1672 if (action == NONE) {
1673 pa_log(_("No valid command specified."));
1674 goto quit;
1675 }
1676
1677 if (!(m = pa_mainloop_new())) {
1678 pa_log(_("pa_mainloop_new() failed."));
1679 goto quit;
1680 }
1681
1682 mainloop_api = pa_mainloop_get_api(m);
1683
1684 pa_assert_se(pa_signal_init(mainloop_api) == 0);
1685 pa_signal_new(SIGINT, exit_signal_callback, NULL);
1686 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1687 pa_disable_sigpipe();
1688
1689 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1690 pa_log(_("pa_context_new() failed."));
1691 goto quit;
1692 }
1693
1694 pa_context_set_state_callback(context, context_state_callback, NULL);
1695 if (pa_context_connect(context, server, 0, NULL) < 0) {
1696 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1697 goto quit;
1698 }
1699
1700 if (pa_mainloop_run(m, &ret) < 0) {
1701 pa_log(_("pa_mainloop_run() failed."));
1702 goto quit;
1703 }
1704
1705 quit:
1706 if (sample_stream)
1707 pa_stream_unref(sample_stream);
1708
1709 if (context)
1710 pa_context_unref(context);
1711
1712 if (m) {
1713 pa_signal_done();
1714 pa_mainloop_free(m);
1715 }
1716
1717 pa_xfree(server);
1718 pa_xfree(list_type);
1719 pa_xfree(sample_name);
1720 pa_xfree(sink_name);
1721 pa_xfree(source_name);
1722 pa_xfree(module_args);
1723 pa_xfree(card_name);
1724 pa_xfree(profile_name);
1725 pa_xfree(port_name);
1726
1727 if (sndfile)
1728 sf_close(sndfile);
1729
1730 if (proplist)
1731 pa_proplist_free(proplist);
1732
1733 return ret;
1734 }