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