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, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <signal.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #include <assert.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <getopt.h>
32 #include <locale.h>
33 #include <ctype.h>
34
35 #ifdef SNDFILE_ENABLE
36 #include <sndfile.h>
37 #endif
38
39 #include <pulse/pulseaudio.h>
40 #include <pulse/ext-device-restore.h>
41 #include <pulse/xmalloc.h>
42
43 #include <pulsecore/i18n.h>
44 #include <pulsecore/json.h>
45 #include <pulsecore/macro.h>
46 #include <pulsecore/core-util.h>
47 #include <pulsecore/log.h>
48
49 #ifdef SNDFILE_ENABLE
50 #include <pulsecore/sndfile-util.h>
51 #endif
52
53 static pa_context *context = NULL;
54 static pa_mainloop_api *mainloop_api = NULL;
55
56 static char
57 *list_type = NULL,
58 *sample_name = NULL,
59 *sink_name = NULL,
60 *source_name = NULL,
61 *module_name = NULL,
62 *module_args = NULL,
63 *card_name = NULL,
64 *profile_name = NULL,
65 *port_name = NULL,
66 *formats = NULL,
67 *object_path = NULL,
68 *message = NULL,
69 *message_args = NULL;
70
71 static uint32_t
72 sink_input_idx = PA_INVALID_INDEX,
73 source_output_idx = PA_INVALID_INDEX,
74 sink_idx = PA_INVALID_INDEX;
75
76 static bool short_list_format = false;
77 static uint32_t module_index;
78 static int32_t latency_offset;
79 static bool suspend;
80 static pa_cvolume volume;
81 static enum volume_flags {
82 VOL_UINT = 0,
83 VOL_PERCENT = 1,
84 VOL_LINEAR = 2,
85 VOL_DECIBEL = 3,
86 VOL_ABSOLUTE = 0 << 4,
87 VOL_RELATIVE = 1 << 4,
88 } volume_flags;
89
90 static enum mute_flags {
91 INVALID_MUTE = -1,
92 UNMUTE = 0,
93 MUTE = 1,
94 TOGGLE_MUTE = 2
95 } mute = INVALID_MUTE;
96
97 static pa_proplist *proplist = NULL;
98
99 #ifdef SNDFILE_ENABLE
100 static SNDFILE *sndfile = NULL;
101 static pa_stream *sample_stream = NULL;
102 static pa_sample_spec sample_spec;
103 static pa_channel_map channel_map;
104 static size_t sample_length = 0;
105 #endif
106
107 /* This variable tracks the number of ongoing asynchronous operations. When a
108 * new operation begins, this is incremented simply with actions++, and when
109 * an operation finishes, this is decremented with the complete_action()
110 * function, which shuts down the program if actions reaches zero. */
111 static int actions = 0;
112
113 static bool nl = false;
114 static pa_json_encoder *list_encoder = NULL;
115 static pa_json_encoder *json_encoder = NULL;
116
117 static enum {
118 NONE,
119 EXIT,
120 STAT,
121 INFO,
122 UPLOAD_SAMPLE,
123 PLAY_SAMPLE,
124 REMOVE_SAMPLE,
125 LIST,
126 MOVE_SINK_INPUT,
127 MOVE_SOURCE_OUTPUT,
128 LOAD_MODULE,
129 UNLOAD_MODULE,
130 SUSPEND_SINK,
131 SUSPEND_SOURCE,
132 SET_CARD_PROFILE,
133 SET_SINK_PORT,
134 GET_DEFAULT_SINK,
135 SET_DEFAULT_SINK,
136 SET_SOURCE_PORT,
137 GET_DEFAULT_SOURCE,
138 SET_DEFAULT_SOURCE,
139 GET_SINK_VOLUME,
140 SET_SINK_VOLUME,
141 GET_SOURCE_VOLUME,
142 SET_SOURCE_VOLUME,
143 SET_SINK_INPUT_VOLUME,
144 SET_SOURCE_OUTPUT_VOLUME,
145 GET_SINK_MUTE,
146 SET_SINK_MUTE,
147 GET_SOURCE_MUTE,
148 SET_SOURCE_MUTE,
149 SET_SINK_INPUT_MUTE,
150 SET_SOURCE_OUTPUT_MUTE,
151 SET_SINK_FORMATS,
152 SET_PORT_LATENCY_OFFSET,
153 SEND_MESSAGE,
154 SUBSCRIBE
155 } action = NONE;
156
157 static enum {
158 TEXT,
159 JSON
160 } format = TEXT;
161
quit(int ret)162 static void quit(int ret) {
163 pa_assert(mainloop_api);
164 mainloop_api->quit(mainloop_api, ret);
165 }
166
context_drain_complete(pa_context * c,void * userdata)167 static void context_drain_complete(pa_context *c, void *userdata) {
168 pa_context_disconnect(c);
169 }
170
drain(void)171 static void drain(void) {
172 pa_operation *o;
173
174 if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
175 pa_context_disconnect(context);
176 else
177 pa_operation_unref(o);
178 }
179
complete_action(void)180 static void complete_action(void) {
181 pa_assert(actions > 0);
182
183 if (!(--actions))
184 drain();
185 }
186
stat_callback(pa_context * c,const pa_stat_info * i,void * userdata)187 static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
188 char s[PA_BYTES_SNPRINT_MAX];
189 if (!i) {
190 pa_log(_("Failed to get statistics: %s"), pa_strerror(pa_context_errno(c)));
191 quit(1);
192 return;
193 }
194
195 if (format == JSON) {
196 printf("{\"current\":{\"blocks\":%u,\"size\":%u},"
197 "\"lifetime\":{\"blocks\":%u,\"size\":%u},"
198 "\"sample_cache_size\":%u}",
199 i->memblock_total,
200 i->memblock_total_size,
201 i->memblock_allocated,
202 i->memblock_allocated_size,
203 i->scache_size);
204 } else {
205 pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
206 printf(ngettext("Currently in use: %u block containing %s bytes total.\n",
207 "Currently in use: %u blocks containing %s bytes total.\n",
208 i->memblock_total),
209 i->memblock_total, s);
210
211 pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
212 printf(ngettext("Allocated during whole lifetime: %u block containing %s bytes total.\n",
213 "Allocated during whole lifetime: %u blocks containing %s bytes total.\n",
214 i->memblock_allocated),
215 i->memblock_allocated, s);
216
217 pa_bytes_snprint(s, sizeof(s), i->scache_size);
218 printf(_("Sample cache size: %s\n"), s);
219 }
220
221 complete_action();
222 }
223
get_default_sink(pa_context * c,const pa_server_info * i,void * userdata)224 static void get_default_sink(pa_context *c, const pa_server_info *i, void *userdata) {
225 if (!i) {
226 pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
227 quit(1);
228 return;
229 }
230
231 printf(_("%s\n"), i->default_sink_name);
232
233 complete_action();
234 }
235
get_default_source(pa_context * c,const pa_server_info * i,void * userdata)236 static void get_default_source(pa_context *c, const pa_server_info *i, void *userdata) {
237 if (!i) {
238 pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
239 quit(1);
240 return;
241 }
242
243 printf(_("%s\n"), i->default_source_name);
244
245 complete_action();
246 }
247
get_server_info_callback(pa_context * c,const pa_server_info * i,void * useerdata)248 static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
249 char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
250
251 if (!i) {
252 pa_log(_("Failed to get server information: %s"), pa_strerror(pa_context_errno(c)));
253 quit(1);
254 return;
255 }
256
257 pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
258 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
259
260 if (format == JSON) {
261 char* tile_size = pa_sprintf_malloc("%zu", pa_context_get_tile_size(c, NULL));
262 char* cookie = pa_sprintf_malloc("%04x:%04x", i->cookie >> 16, i->cookie & 0xFFFFU);
263 pa_json_encoder *encoder = pa_json_encoder_new();
264 pa_json_encoder_begin_element_object(encoder);
265 pa_json_encoder_add_member_string(encoder, "server_string", pa_context_get_server(c));
266 pa_json_encoder_add_member_int(encoder, "library_protocol_version", pa_context_get_protocol_version(c));
267 pa_json_encoder_add_member_int(encoder, "server_protocol_version", pa_context_get_server_protocol_version(c));
268 pa_json_encoder_add_member_string(encoder, "is_local", pa_yes_no_localised(pa_context_is_local(c)));
269 pa_json_encoder_add_member_int(encoder, "client_index", pa_context_get_index(c));
270 pa_json_encoder_add_member_string(encoder, "tile_size", tile_size);
271 pa_json_encoder_add_member_string(encoder, "user_name", i->user_name);
272 pa_json_encoder_add_member_string(encoder, "host_name", i->host_name);
273 pa_json_encoder_add_member_string(encoder, "server_name", i->server_name);
274 pa_json_encoder_add_member_string(encoder, "server_version", i->server_version);
275 pa_json_encoder_add_member_string(encoder, "default_sample_specification", ss);
276 pa_json_encoder_add_member_string(encoder, "default_channel_map", cm);
277 pa_json_encoder_add_member_string(encoder, "default_sink_name", i->default_sink_name);
278 pa_json_encoder_add_member_string(encoder, "default_source_name", i->default_source_name);
279 pa_json_encoder_add_member_string(encoder, "cookie", cookie);
280 pa_json_encoder_end_object(encoder);
281
282 char* json_str = pa_json_encoder_to_string_free(encoder);
283 printf("%s", json_str);
284 pa_xfree(json_str);
285 pa_xfree(tile_size);
286 pa_xfree(cookie);
287 } else {
288 printf(_("Server String: %s\n"
289 "Library Protocol Version: %u\n"
290 "Server Protocol Version: %u\n"
291 "Is Local: %s\n"
292 "Client Index: %u\n"
293 "Tile Size: %zu\n"),
294 pa_context_get_server(c),
295 pa_context_get_protocol_version(c),
296 pa_context_get_server_protocol_version(c),
297 pa_yes_no_localised(pa_context_is_local(c)),
298 pa_context_get_index(c),
299 pa_context_get_tile_size(c, NULL));
300
301 printf(_("User Name: %s\n"
302 "Host Name: %s\n"
303 "Server Name: %s\n"
304 "Server Version: %s\n"
305 "Default Sample Specification: %s\n"
306 "Default Channel Map: %s\n"
307 "Default Sink: %s\n"
308 "Default Source: %s\n"
309 "Cookie: %04x:%04x\n"),
310 i->user_name,
311 i->host_name,
312 i->server_name,
313 i->server_version,
314 ss,
315 cm,
316 i->default_sink_name,
317 i->default_source_name,
318 i->cookie >> 16,
319 i->cookie & 0xFFFFU);
320 }
321
322 complete_action();
323 }
324
get_available_str(int available)325 static const char* get_available_str(int available) {
326 switch (available) {
327 case PA_PORT_AVAILABLE_UNKNOWN: return _("availability unknown");
328 case PA_PORT_AVAILABLE_YES: return _("available");
329 case PA_PORT_AVAILABLE_NO: return _("not available");
330 }
331
332 pa_assert_not_reached();
333 }
334
get_device_port_type(unsigned int type)335 static const char* get_device_port_type(unsigned int type) {
336 static char buf[32];
337 switch (type) {
338 case PA_DEVICE_PORT_TYPE_UNKNOWN: return _("Unknown");
339 case PA_DEVICE_PORT_TYPE_AUX: return _("Aux");
340 case PA_DEVICE_PORT_TYPE_SPEAKER: return _("Speaker");
341 case PA_DEVICE_PORT_TYPE_HEADPHONES: return _("Headphones");
342 case PA_DEVICE_PORT_TYPE_LINE: return _("Line");
343 case PA_DEVICE_PORT_TYPE_MIC: return _("Mic");
344 case PA_DEVICE_PORT_TYPE_HEADSET: return _("Headset");
345 case PA_DEVICE_PORT_TYPE_HANDSET: return _("Handset");
346 case PA_DEVICE_PORT_TYPE_EARPIECE: return _("Earpiece");
347 case PA_DEVICE_PORT_TYPE_SPDIF: return _("SPDIF");
348 case PA_DEVICE_PORT_TYPE_HDMI: return _("HDMI");
349 case PA_DEVICE_PORT_TYPE_TV: return _("TV");
350 case PA_DEVICE_PORT_TYPE_RADIO: return _("Radio");
351 case PA_DEVICE_PORT_TYPE_VIDEO: return _("Video");
352 case PA_DEVICE_PORT_TYPE_USB: return _("USB");
353 case PA_DEVICE_PORT_TYPE_BLUETOOTH: return _("Bluetooth");
354 case PA_DEVICE_PORT_TYPE_PORTABLE: return _("Portable");
355 case PA_DEVICE_PORT_TYPE_HANDSFREE: return _("Handsfree");
356 case PA_DEVICE_PORT_TYPE_CAR: return _("Car");
357 case PA_DEVICE_PORT_TYPE_HIFI: return _("HiFi");
358 case PA_DEVICE_PORT_TYPE_PHONE: return _("Phone");
359 case PA_DEVICE_PORT_TYPE_NETWORK: return _("Network");
360 case PA_DEVICE_PORT_TYPE_ANALOG: return _("Analog");
361 }
362 snprintf(buf, sizeof(buf), "%s-%u", _("Unknown"), type);
363 return buf;
364 }
365
pa_proplist_to_json_object(const pa_proplist * p)366 char* pa_proplist_to_json_object(const pa_proplist *p) {
367 const char *key;
368 void *state = NULL;
369 pa_json_encoder *encoder;
370
371 pa_assert(p);
372
373 encoder = pa_json_encoder_new();
374 pa_json_encoder_begin_element_object(encoder);
375 while (true) {
376 key = pa_proplist_iterate(p, &state);
377 if (!key) break;
378
379 const char *v;
380
381 if ((v = pa_proplist_gets(p, key))) {
382 pa_json_encoder_add_member_string(encoder, key, v);
383 } else {
384 const void *value;
385 size_t nbytes;
386 char *c;
387 char* hex_str;
388
389 pa_assert_se(pa_proplist_get(p, key, &value, &nbytes) == 0);
390 c = pa_xmalloc(nbytes*2+1);
391 pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1);
392
393 hex_str = pa_sprintf_malloc("hex:%s", c);
394 pa_json_encoder_add_member_string(encoder, key, hex_str);
395 pa_xfree(c);
396 pa_xfree(hex_str);
397 }
398 }
399 pa_json_encoder_end_object(encoder);
400
401 return pa_json_encoder_to_string_free(encoder);
402 }
403
pa_sink_ports_to_json_array(pa_sink_port_info ** ports)404 static const char* pa_sink_ports_to_json_array(pa_sink_port_info **ports) {
405 pa_json_encoder *encoder = pa_json_encoder_new();
406 if (!ports) {
407 pa_json_encoder_begin_element_array(encoder);
408 pa_json_encoder_end_array(encoder);
409 return pa_json_encoder_to_string_free(encoder);
410 }
411
412 pa_sink_port_info **p;
413
414 pa_json_encoder_begin_element_array(encoder);
415 for (p = ports; *p; p++) {
416 pa_json_encoder *sink_port_encoder = pa_json_encoder_new();
417 pa_json_encoder_begin_element_object(sink_port_encoder);
418 pa_json_encoder_add_member_string(sink_port_encoder, "name", (*p)->name);
419 pa_json_encoder_add_member_string(sink_port_encoder, "description", (*p)->description);
420 pa_json_encoder_add_member_string(sink_port_encoder, "type", get_device_port_type((*p)->type));
421 pa_json_encoder_add_member_int(sink_port_encoder, "priority", (*p)->priority);
422 pa_json_encoder_add_member_string(sink_port_encoder, "availability_group", (*p)->availability_group);
423 pa_json_encoder_add_member_string(sink_port_encoder, "availability", get_available_str((*p)->available));
424 pa_json_encoder_end_object(sink_port_encoder);
425
426 char* sink_port_str = pa_json_encoder_to_string_free(sink_port_encoder);
427 pa_json_encoder_add_element_raw_json(encoder, sink_port_str);
428 pa_xfree(sink_port_str);
429 }
430 pa_json_encoder_end_array(encoder);
431
432 return pa_json_encoder_to_string_free(encoder);
433 }
434
pa_source_ports_to_json_array(pa_source_port_info ** ports)435 static const char* pa_source_ports_to_json_array(pa_source_port_info **ports) {
436 pa_json_encoder *encoder = pa_json_encoder_new();
437 if (!ports) {
438 pa_json_encoder_begin_element_array(encoder);
439 pa_json_encoder_end_array(encoder);
440 return pa_json_encoder_to_string_free(encoder);
441 }
442
443 pa_source_port_info **p;
444
445 pa_json_encoder_begin_element_array(encoder);
446 for (p = ports; *p; p++) {
447 pa_json_encoder *source_port_encoder = pa_json_encoder_new();
448 pa_json_encoder_begin_element_object(source_port_encoder);
449 pa_json_encoder_add_member_string(source_port_encoder, "name", (*p)->name);
450 pa_json_encoder_add_member_string(source_port_encoder, "description", (*p)->description);
451 pa_json_encoder_add_member_string(source_port_encoder, "type", get_device_port_type((*p)->type));
452 pa_json_encoder_add_member_int(source_port_encoder, "priority", (*p)->priority);
453 pa_json_encoder_add_member_string(source_port_encoder, "availability_group", (*p)->availability_group);
454 pa_json_encoder_add_member_string(source_port_encoder, "availability", get_available_str((*p)->available));
455 pa_json_encoder_end_object(source_port_encoder);
456
457 char* source_port_str = pa_json_encoder_to_string_free(source_port_encoder);
458 pa_json_encoder_add_element_raw_json(encoder, source_port_str);
459 pa_xfree(source_port_str);
460 }
461 pa_json_encoder_end_array(encoder);
462
463 return pa_json_encoder_to_string_free(encoder);
464 }
465
pa_format_infos_to_json_array(pa_format_info ** formats,uint8_t n_formats)466 static const char* pa_format_infos_to_json_array(pa_format_info **formats, uint8_t n_formats) {
467 pa_json_encoder *encoder = pa_json_encoder_new();
468 if (!formats) {
469 pa_json_encoder_begin_element_array(encoder);
470 pa_json_encoder_end_array(encoder);
471 return pa_json_encoder_to_string_free(encoder);
472 }
473
474 char f[PA_FORMAT_INFO_SNPRINT_MAX];
475 uint8_t i;
476
477 pa_json_encoder_begin_element_array(encoder);
478 for (i = 0; i < n_formats; i++) {
479 pa_json_encoder_add_element_string(encoder, pa_format_info_snprint(f, sizeof(f), formats[i]));
480 }
481 pa_json_encoder_end_array(encoder);
482
483 return pa_json_encoder_to_string_free(encoder);
484 }
485
pa_volume_to_json_object(pa_volume_t v,int print_dB)486 const char* pa_volume_to_json_object(pa_volume_t v, int print_dB) {
487 pa_json_encoder *encoder = pa_json_encoder_new();
488 if (!PA_VOLUME_IS_VALID(v)) {
489 pa_json_encoder_begin_element_object(encoder);
490 pa_json_encoder_add_member_string(encoder, "error", _("(invalid)"));
491 pa_json_encoder_end_object(encoder);
492 return pa_json_encoder_to_string_free(encoder);
493 }
494
495 char dB[PA_SW_VOLUME_SNPRINT_DB_MAX];
496 char* value_percent = pa_sprintf_malloc("%u%%", (unsigned)(((uint64_t)v * 100 + (uint64_t)PA_VOLUME_NORM / 2) / (uint64_t)PA_VOLUME_NORM));
497 pa_json_encoder_begin_element_object(encoder);
498 pa_json_encoder_add_member_int(encoder, "value", v);
499 pa_json_encoder_add_member_string(encoder, "value_percent", value_percent);
500 pa_json_encoder_add_member_string(encoder, "db", print_dB ? pa_sw_volume_snprint_dB(dB, sizeof(dB), v) : NULL);
501 pa_json_encoder_end_object(encoder);
502 pa_xfree(value_percent);
503
504 return pa_json_encoder_to_string_free(encoder);
505 }
506
pa_cvolume_to_json_object(const pa_cvolume * c,const pa_channel_map * map,int print_dB)507 const char* pa_cvolume_to_json_object(const pa_cvolume *c, const pa_channel_map *map, int print_dB) {
508 pa_json_encoder *encoder = pa_json_encoder_new();
509 if (!pa_cvolume_valid(c)) {
510 pa_json_encoder_begin_element_object(encoder);
511 pa_json_encoder_add_member_string(encoder, "error", _("(invalid)"));
512 pa_json_encoder_end_object(encoder);
513 return pa_json_encoder_to_string_free(encoder);
514 }
515
516 pa_assert(!map || (map->channels == c->channels));
517 pa_assert(!map || pa_channel_map_valid(map));
518
519 pa_json_encoder_begin_element_object(encoder);
520 for (unsigned channel = 0; channel < c->channels; channel++) {
521 char channel_position[32];
522 if (map)
523 pa_snprintf(channel_position, sizeof(channel_position), "%s", pa_channel_position_to_string(map->map[channel]));
524 else
525 pa_snprintf(channel_position, sizeof(channel_position), "%u", channel);
526
527 pa_json_encoder_add_member_raw_json(encoder,
528 channel_position,
529 pa_volume_to_json_object(c->values[channel], print_dB));
530 }
531 pa_json_encoder_end_object(encoder);
532
533 return pa_json_encoder_to_string_free(encoder);
534 }
535
pa_json_encoder_end_array_handler(const char * name)536 static void pa_json_encoder_end_array_handler(const char *name) {
537 pa_assert(json_encoder != NULL);
538
539 pa_json_encoder_end_array(json_encoder);
540 char* json_str = pa_json_encoder_to_string_free(json_encoder);
541 if (list_encoder != NULL) {
542 pa_json_encoder_add_member_raw_json(list_encoder, name, json_str);
543 } else {
544 printf("%s", json_str);
545 }
546 pa_xfree(json_str);
547
548 json_encoder = NULL;
549 }
550
get_sink_info_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)551 static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
552
553 static const char *state_table[] = {
554 [1+PA_SINK_INVALID_STATE] = "n/a",
555 [1+PA_SINK_RUNNING] = "RUNNING",
556 [1+PA_SINK_IDLE] = "IDLE",
557 [1+PA_SINK_SUSPENDED] = "SUSPENDED"
558 };
559
560 char
561 s[PA_SAMPLE_SPEC_SNPRINT_MAX],
562 cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
563 v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
564 cm[PA_CHANNEL_MAP_SNPRINT_MAX],
565 f[PA_FORMAT_INFO_SNPRINT_MAX];
566 char *pl;
567
568 if (format == JSON && json_encoder == NULL) {
569 json_encoder = pa_json_encoder_new();
570 pa_json_encoder_begin_element_array(json_encoder);
571 }
572
573 if (is_last < 0) {
574 pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
575 quit(1);
576 return;
577 }
578
579 if (is_last) {
580 if (format == JSON) {
581 pa_json_encoder_end_array_handler("sinks");
582 }
583 complete_action();
584 return;
585 }
586
587 pa_assert(i);
588
589 if (nl && !short_list_format && format == TEXT)
590 printf("\n");
591 nl = true;
592
593 char *sample_spec = pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
594 if (short_list_format) {
595 if (format == JSON) {
596 pa_json_encoder *encoder = pa_json_encoder_new();
597 pa_json_encoder_begin_element_object(encoder);
598 pa_json_encoder_add_member_int(encoder, "index", i->index);
599 pa_json_encoder_add_member_string(encoder, "name", i->name);
600 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
601 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
602 pa_json_encoder_add_member_string(encoder, "state", state_table[1+i->state]);
603 pa_json_encoder_end_object(encoder);
604
605 char* json_str = pa_json_encoder_to_string_free(encoder);
606 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
607 pa_xfree(json_str);
608 } else {
609 printf("%u\t%s\t%s\t%s\t%s\n",
610 i->index,
611 i->name,
612 pa_strnull(i->driver),
613 sample_spec,
614 state_table[1+i->state]);
615 }
616 return;
617 }
618
619 char *channel_map = pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
620 float volume_balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
621
622 if (format == JSON) {
623 pa_json_encoder *latency_encoder = pa_json_encoder_new();
624 pa_json_encoder_begin_element_object(latency_encoder);
625 pa_json_encoder_add_member_double(latency_encoder, "actual", (double) i->latency, 2);
626 pa_json_encoder_add_member_double(latency_encoder, "configured", (double) i->configured_latency, 2);
627 pa_json_encoder_end_object(latency_encoder);
628 char* latency_json_str = pa_json_encoder_to_string_free(latency_encoder);
629
630 pa_json_encoder *flags_encoder = pa_json_encoder_new();
631 pa_json_encoder_begin_element_array(flags_encoder);
632 if (i->flags & PA_SINK_HARDWARE) pa_json_encoder_add_element_string(flags_encoder, "HARDWARE");
633 if (i->flags & PA_SINK_NETWORK) pa_json_encoder_add_element_string(flags_encoder, "NETWORK");
634 if (i->flags & PA_SINK_HW_MUTE_CTRL) pa_json_encoder_add_element_string(flags_encoder, "HW_MUTE_CTRL");
635 if (i->flags & PA_SINK_HW_VOLUME_CTRL) pa_json_encoder_add_element_string(flags_encoder, "HW_VOLUME_CTRL");
636 if (i->flags & PA_SINK_DECIBEL_VOLUME) pa_json_encoder_add_element_string(flags_encoder, "DECIBEL_VOLUME");
637 if (i->flags & PA_SINK_LATENCY) pa_json_encoder_add_element_string(flags_encoder, "LATENCY");
638 if (i->flags & PA_SINK_SET_FORMATS) pa_json_encoder_add_element_string(flags_encoder, "SET_FORMATS");
639 pa_json_encoder_end_array(flags_encoder);
640 char* flags_json_str = pa_json_encoder_to_string_free(flags_encoder);
641
642 pa_json_encoder *encoder = pa_json_encoder_new();
643 pa_json_encoder_begin_element_object(encoder);
644 pa_json_encoder_add_member_int(encoder, "index", i->index);
645 pa_json_encoder_add_member_string(encoder, "state", state_table[1+i->state]);
646 pa_json_encoder_add_member_string(encoder, "name", i->name);
647 pa_json_encoder_add_member_string(encoder, "description", i->description);
648 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
649 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
650 pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
651 pa_json_encoder_add_member_int(encoder, "owner_module", i->owner_module);
652 pa_json_encoder_add_member_bool(encoder, "mute", i->mute);
653 pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, i->flags & PA_SINK_DECIBEL_VOLUME));
654 pa_json_encoder_add_member_double(encoder, "balance", volume_balance, 2);
655 pa_json_encoder_add_member_raw_json(encoder, "base_volume", pa_volume_to_json_object(i->base_volume, i->flags & PA_SINK_DECIBEL_VOLUME));
656 pa_json_encoder_add_member_string(encoder, "monitor_source", i->monitor_source_name);
657 pa_json_encoder_add_member_raw_json(encoder, "latency", latency_json_str);
658 pa_json_encoder_add_member_raw_json(encoder, "flags", flags_json_str);
659 pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
660 pa_json_encoder_add_member_raw_json(encoder, "ports", pa_sink_ports_to_json_array(i->ports));
661 i->active_port ? pa_json_encoder_add_member_string(encoder, "active_port", i->active_port->name): pa_json_encoder_add_member_null(encoder, "active_port");
662 pa_json_encoder_add_member_raw_json(encoder, "formats", pa_format_infos_to_json_array(i->formats, i->n_formats));
663 pa_json_encoder_end_object(encoder);
664
665 char* json_str = pa_json_encoder_to_string_free(encoder);
666 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
667 pa_xfree(json_str);
668 pa_xfree(latency_json_str);
669 pa_xfree(flags_json_str);
670 } else {
671 printf(_("Sink #%u\n"
672 "\tState: %s\n"
673 "\tName: %s\n"
674 "\tDescription: %s\n"
675 "\tDriver: %s\n"
676 "\tSample Specification: %s\n"
677 "\tChannel Map: %s\n"
678 "\tOwner Module: %u\n"
679 "\tMute: %s\n"
680 "\tVolume: %s\n"
681 "\t balance %0.2f\n"
682 "\tBase Volume: %s\n"
683 "\tMonitor Source: %s\n"
684 "\tLatency: %0.0f usec, configured %0.0f usec\n"
685 "\tFlags: %s%s%s%s%s%s%s\n"
686 "\tProperties:\n\t\t%s\n"),
687 i->index,
688 state_table[1+i->state],
689 i->name,
690 pa_strnull(i->description),
691 pa_strnull(i->driver),
692 sample_spec,
693 channel_map,
694 i->owner_module,
695 pa_yes_no_localised(i->mute),
696 pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, i->flags & PA_SINK_DECIBEL_VOLUME),
697 volume_balance,
698 pa_volume_snprint_verbose(v, sizeof(v), i->base_volume, i->flags & PA_SINK_DECIBEL_VOLUME),
699 pa_strnull(i->monitor_source_name),
700 (double) i->latency, (double) i->configured_latency,
701 i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
702 i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
703 i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
704 i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
705 i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
706 i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
707 i->flags & PA_SINK_SET_FORMATS ? "SET_FORMATS " : "",
708 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
709
710 if (i->ports) {
711 pa_sink_port_info **p;
712
713 printf(_("\tPorts:\n"));
714 for (p = i->ports; *p; p++)
715 printf(_("\t\t%s: %s (type: %s, priority: %u%s%s, %s)\n"),
716 (*p)->name, (*p)->description, get_device_port_type((*p)->type),
717 (*p)->priority, (*p)->availability_group ? _(", availability group: ") : "",
718 (*p)->availability_group ?: "", get_available_str((*p)->available));
719 }
720
721 if (i->active_port)
722 printf(_("\tActive Port: %s\n"),
723 i->active_port->name);
724
725 if (i->formats) {
726 uint8_t j;
727
728 printf(_("\tFormats:\n"));
729 for (j = 0; j < i->n_formats; j++)
730 printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
731 }
732 }
733
734 pa_xfree(pl);
735 }
736
get_source_info_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)737 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
738
739 static const char *state_table[] = {
740 [1+PA_SOURCE_INVALID_STATE] = "n/a",
741 [1+PA_SOURCE_RUNNING] = "RUNNING",
742 [1+PA_SOURCE_IDLE] = "IDLE",
743 [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
744 };
745
746 char
747 s[PA_SAMPLE_SPEC_SNPRINT_MAX],
748 cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX],
749 v[PA_VOLUME_SNPRINT_VERBOSE_MAX],
750 cm[PA_CHANNEL_MAP_SNPRINT_MAX],
751 f[PA_FORMAT_INFO_SNPRINT_MAX];
752 char *pl;
753
754 if (format == JSON && json_encoder == NULL) {
755 json_encoder = pa_json_encoder_new();
756 pa_json_encoder_begin_element_array(json_encoder);
757 }
758
759 if (is_last < 0) {
760 pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
761 quit(1);
762 return;
763 }
764
765 if (is_last) {
766 if (format == JSON) {
767 pa_json_encoder_end_array_handler("sources");
768 }
769 complete_action();
770 return;
771 }
772
773 pa_assert(i);
774
775 if (nl && !short_list_format && format == TEXT)
776 printf("\n");
777 nl = true;
778
779 char *sample_spec = pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
780 if (short_list_format) {
781 if (format == JSON) {
782 pa_json_encoder *encoder = pa_json_encoder_new();
783 pa_json_encoder_begin_element_object(encoder);
784 pa_json_encoder_add_member_int(encoder, "index", i->index);
785 pa_json_encoder_add_member_string(encoder, "name", i->name);
786 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
787 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
788 pa_json_encoder_add_member_string(encoder, "state", state_table[1+i->state]);
789 pa_json_encoder_end_object(encoder);
790
791 char* json_str = pa_json_encoder_to_string_free(encoder);
792 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
793 pa_xfree(json_str);
794 } else {
795 printf("%u\t%s\t%s\t%s\t%s\n",
796 i->index,
797 i->name,
798 pa_strnull(i->driver),
799 sample_spec,
800 state_table[1+i->state]);
801 }
802 return;
803 }
804
805 char *channel_map = pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
806 float volume_balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
807
808 if (format == JSON) {
809 pa_json_encoder *latency_encoder = pa_json_encoder_new();
810 pa_json_encoder_begin_element_object(latency_encoder);
811 pa_json_encoder_add_member_double(latency_encoder, "actual", (double) i->latency, 2);
812 pa_json_encoder_add_member_double(latency_encoder, "configured", (double) i->configured_latency, 2);
813 pa_json_encoder_end_object(latency_encoder);
814 char* latency_json_str = pa_json_encoder_to_string_free(latency_encoder);
815
816 pa_json_encoder *flags_encoder = pa_json_encoder_new();
817 pa_json_encoder_begin_element_array(flags_encoder);
818 if (i->flags & PA_SOURCE_HARDWARE) pa_json_encoder_add_element_string(flags_encoder, "HARDWARE");
819 if (i->flags & PA_SOURCE_NETWORK) pa_json_encoder_add_element_string(flags_encoder, "NETWORK");
820 if (i->flags & PA_SOURCE_HW_MUTE_CTRL) pa_json_encoder_add_element_string(flags_encoder, "HW_MUTE_CTRL");
821 if (i->flags & PA_SOURCE_HW_VOLUME_CTRL) pa_json_encoder_add_element_string(flags_encoder, "HW_VOLUME_CTRL");
822 if (i->flags & PA_SOURCE_DECIBEL_VOLUME) pa_json_encoder_add_element_string(flags_encoder, "DECIBEL_VOLUME");
823 if (i->flags & PA_SOURCE_LATENCY) pa_json_encoder_add_element_string(flags_encoder, "LATENCY");
824 pa_json_encoder_end_array(flags_encoder);
825 char* flags_json_str = pa_json_encoder_to_string_free(flags_encoder);
826
827 pa_json_encoder *encoder = pa_json_encoder_new();
828 pa_json_encoder_begin_element_object(encoder);
829 pa_json_encoder_add_member_int(encoder, "index", i->index);
830 pa_json_encoder_add_member_string(encoder, "state", state_table[1+i->state]);
831 pa_json_encoder_add_member_string(encoder, "name", i->name);
832 pa_json_encoder_add_member_string(encoder, "description", i->description);
833 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
834 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
835 pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
836 pa_json_encoder_add_member_int(encoder, "owner_module", i->owner_module);
837 pa_json_encoder_add_member_bool(encoder, "mute", i->mute);
838 pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, i->flags & PA_SINK_DECIBEL_VOLUME));
839 pa_json_encoder_add_member_double(encoder, "balance", volume_balance, 2);
840 pa_json_encoder_add_member_raw_json(encoder, "base_volume", pa_volume_to_json_object(i->base_volume, i->flags & PA_SINK_DECIBEL_VOLUME));
841 pa_json_encoder_add_member_string(encoder, "monitor_source", i->monitor_of_sink_name);
842 pa_json_encoder_add_member_raw_json(encoder, "latency", latency_json_str);
843 pa_json_encoder_add_member_raw_json(encoder, "flags", flags_json_str);
844 pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
845 pa_json_encoder_add_member_raw_json(encoder, "ports", pa_source_ports_to_json_array(i->ports));
846 i->active_port ? pa_json_encoder_add_member_string(encoder, "active_port", i->active_port->name) : pa_json_encoder_add_member_null(encoder, "active_port");
847 pa_json_encoder_add_member_raw_json(encoder, "formats", pa_format_infos_to_json_array(i->formats, i->n_formats));
848 pa_json_encoder_end_object(encoder);
849
850 char* json_str = pa_json_encoder_to_string_free(encoder);
851 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
852 pa_xfree(json_str);
853 pa_xfree(latency_json_str);
854 pa_xfree(flags_json_str);
855 } else {
856 printf(_("Source #%u\n"
857 "\tState: %s\n"
858 "\tName: %s\n"
859 "\tDescription: %s\n"
860 "\tDriver: %s\n"
861 "\tSample Specification: %s\n"
862 "\tChannel Map: %s\n"
863 "\tOwner Module: %u\n"
864 "\tMute: %s\n"
865 "\tVolume: %s\n"
866 "\t balance %0.2f\n"
867 "\tBase Volume: %s\n"
868 "\tMonitor of Sink: %s\n"
869 "\tLatency: %0.0f usec, configured %0.0f usec\n"
870 "\tFlags: %s%s%s%s%s%s\n"
871 "\tProperties:\n\t\t%s\n"),
872 i->index,
873 state_table[1+i->state],
874 i->name,
875 pa_strnull(i->description),
876 pa_strnull(i->driver),
877 sample_spec,
878 channel_map,
879 i->owner_module,
880 pa_yes_no_localised(i->mute),
881 pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, i->flags & PA_SOURCE_DECIBEL_VOLUME),
882 volume_balance,
883 pa_volume_snprint_verbose(v, sizeof(v), i->base_volume, i->flags & PA_SOURCE_DECIBEL_VOLUME),
884 i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
885 (double) i->latency, (double) i->configured_latency,
886 i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
887 i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
888 i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
889 i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
890 i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
891 i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
892 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
893
894 if (i->ports) {
895 pa_source_port_info **p;
896
897 printf(_("\tPorts:\n"));
898 for (p = i->ports; *p; p++)
899 printf(_("\t\t%s: %s (type: %s, priority: %u%s%s, %s)\n"),
900 (*p)->name, (*p)->description, get_device_port_type((*p)->type),
901 (*p)->priority, (*p)->availability_group ? _(", availability group: ") : "",
902 (*p)->availability_group ?: "", get_available_str((*p)->available));
903 }
904
905 if (i->active_port)
906 printf(_("\tActive Port: %s\n"),
907 i->active_port->name);
908
909 if (i->formats) {
910 uint8_t j;
911
912 printf(_("\tFormats:\n"));
913 for (j = 0; j < i->n_formats; j++)
914 printf("\t\t%s\n", pa_format_info_snprint(f, sizeof(f), i->formats[j]));
915 }
916 }
917
918 pa_xfree(pl);
919 }
920
get_module_info_callback(pa_context * c,const pa_module_info * i,int is_last,void * userdata)921 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
922 char t[32];
923 char *pl;
924
925 if (format == JSON && json_encoder == NULL) {
926 json_encoder = pa_json_encoder_new();
927 pa_json_encoder_begin_element_array(json_encoder);
928 }
929
930 if (is_last < 0) {
931 pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
932 quit(1);
933 return;
934 }
935
936 if (is_last) {
937 if (format == JSON) {
938 pa_json_encoder_end_array_handler("modules");
939 }
940 complete_action();
941 return;
942 }
943
944 pa_assert(i);
945
946 if (nl && !short_list_format && format == TEXT)
947 printf("\n");
948 nl = true;
949
950 pa_snprintf(t, sizeof(t), "%u", i->n_used);
951
952 if (short_list_format) {
953 if (format == JSON) {
954 pa_json_encoder *encoder = pa_json_encoder_new();
955 pa_json_encoder_begin_element_object(encoder);
956 pa_json_encoder_add_member_string(encoder, "name", i->name);
957 pa_json_encoder_add_member_string(encoder, "argument", i->argument);
958 pa_json_encoder_end_object(encoder);
959
960 char* json_str = pa_json_encoder_to_string_free(encoder);
961 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
962 pa_xfree(json_str);
963 } else {
964 printf("%u\t%s\t%s\t\n", i->index, i->name, i->argument ? i->argument : "");
965 }
966 return;
967 }
968
969 char *n_used = i->n_used != PA_INVALID_INDEX ? t : _("n/a");
970 if (format == JSON) {
971 pa_json_encoder *encoder = pa_json_encoder_new();
972 pa_json_encoder_begin_element_object(encoder);
973 pa_json_encoder_add_member_string(encoder, "name", i->name);
974 pa_json_encoder_add_member_string(encoder, "argument", i->argument);
975 pa_json_encoder_add_member_string(encoder, "usage_counter", n_used);
976 pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
977 pa_json_encoder_end_object(encoder);
978
979 char* json_str = pa_json_encoder_to_string_free(encoder);
980 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
981 pa_xfree(json_str);
982 } else {
983 printf(_("Module #%u\n"
984 "\tName: %s\n"
985 "\tArgument: %s\n"
986 "\tUsage counter: %s\n"
987 "\tProperties:\n\t\t%s\n"),
988 i->index,
989 i->name,
990 i->argument ? i->argument : "",
991 n_used,
992 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
993
994 }
995
996 pa_xfree(pl);
997 }
998
get_client_info_callback(pa_context * c,const pa_client_info * i,int is_last,void * userdata)999 static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
1000 char t[32];
1001 char *pl;
1002
1003 if (format == JSON && json_encoder == NULL) {
1004 json_encoder = pa_json_encoder_new();
1005 pa_json_encoder_begin_element_array(json_encoder);
1006 }
1007
1008 if (is_last < 0) {
1009 pa_log(_("Failed to get client information: %s"), pa_strerror(pa_context_errno(c)));
1010 quit(1);
1011 return;
1012 }
1013
1014 if (is_last) {
1015 if (format == JSON) {
1016 pa_json_encoder_end_array_handler("clients");
1017 }
1018 complete_action();
1019 return;
1020 }
1021
1022 pa_assert(i);
1023
1024 if (nl && !short_list_format && format == TEXT)
1025 printf("\n");
1026 nl = true;
1027
1028 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
1029
1030 if (short_list_format) {
1031 if (format == JSON) {
1032 pa_json_encoder *encoder = pa_json_encoder_new();
1033 pa_json_encoder_begin_element_object(encoder);
1034 pa_json_encoder_add_member_int(encoder, "index", i->index);
1035 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1036 pa_json_encoder_add_member_string(encoder, PA_PROP_APPLICATION_PROCESS_BINARY, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
1037 pa_json_encoder_end_object(encoder);
1038
1039 char* json_str = pa_json_encoder_to_string_free(encoder);
1040 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1041 pa_xfree(json_str);
1042 } else {
1043 printf("%u\t%s\t%s\n",
1044 i->index,
1045 pa_strnull(i->driver),
1046 pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_PROCESS_BINARY)));
1047 }
1048 return;
1049 } else {
1050 if (format == JSON) {
1051 pa_json_encoder *encoder = pa_json_encoder_new();
1052 pa_json_encoder_begin_element_object(encoder);
1053 pa_json_encoder_add_member_int(encoder, "index", i->index);
1054 i->driver ? pa_json_encoder_add_member_string(encoder, "driver", i->driver) : pa_json_encoder_add_member_null(encoder, "driver");
1055 i->owner_module != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "owner_module", t) : pa_json_encoder_add_member_null(encoder, "owner_module");
1056 pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1057 pa_json_encoder_end_object(encoder);
1058
1059 char* json_str = pa_json_encoder_to_string_free(encoder);
1060 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1061 pa_xfree(json_str);
1062 } else {
1063 printf(_("Client #%u\n"
1064 "\tDriver: %s\n"
1065 "\tOwner Module: %s\n"
1066 "\tProperties:\n\t\t%s\n"),
1067 i->index,
1068 pa_strnull(i->driver),
1069 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
1070 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1071 }
1072 }
1073
1074 pa_xfree(pl);
1075 }
1076
pa_card_profile_info_2_to_json_object(pa_card_profile_info2 ** profiles2)1077 const char* pa_card_profile_info_2_to_json_object(pa_card_profile_info2 **profiles2) {
1078 pa_json_encoder *encoder = pa_json_encoder_new();
1079 if (!profiles2) {
1080 pa_json_encoder_begin_element_object(encoder);
1081 pa_json_encoder_end_object(encoder);
1082 return pa_json_encoder_to_string_free(encoder);
1083 }
1084
1085 pa_card_profile_info2 **p;
1086
1087 pa_json_encoder_begin_element_object(encoder);
1088 for (p = profiles2; *p; p++) {
1089 pa_json_encoder *info_json_2_encoder = pa_json_encoder_new();
1090 pa_json_encoder_begin_element_object(info_json_2_encoder);
1091 pa_json_encoder_add_member_string(info_json_2_encoder, "description", (*p)->description);
1092 pa_json_encoder_add_member_int(info_json_2_encoder, "sinks", (*p)->n_sinks);
1093 pa_json_encoder_add_member_int(info_json_2_encoder, "sources", (*p)->n_sources);
1094 pa_json_encoder_add_member_int(info_json_2_encoder, "priority", (*p)->priority);
1095 pa_json_encoder_add_member_bool(info_json_2_encoder, "available", (*p)->available);
1096 pa_json_encoder_end_object(info_json_2_encoder);
1097
1098 char *info_json_2_str = pa_json_encoder_to_string_free(info_json_2_encoder);
1099 pa_json_encoder_add_member_raw_json(encoder, (*p)->name, info_json_2_str);
1100 pa_xfree(info_json_2_str);
1101 }
1102 pa_json_encoder_end_object(encoder);
1103
1104 return pa_json_encoder_to_string_free(encoder);
1105 }
1106
pa_card_profile_info_to_json_array(pa_card_profile_info ** info)1107 const char* pa_card_profile_info_to_json_array(pa_card_profile_info **info) {
1108 pa_json_encoder *encoder = pa_json_encoder_new();
1109 if (!info) {
1110 pa_json_encoder_begin_element_array(encoder);
1111 pa_json_encoder_end_array(encoder);
1112 return pa_json_encoder_to_string_free(encoder);
1113 }
1114
1115 pa_card_profile_info **p;
1116
1117 pa_json_encoder_begin_element_array(encoder);
1118 for (p = info; *p; p++) {
1119 pa_json_encoder_add_element_string(encoder, (*p)->name);
1120 }
1121 pa_json_encoder_end_array(encoder);
1122
1123 return pa_json_encoder_to_string_free(encoder);
1124 }
1125
pa_card_port_info_to_json_object(pa_card_port_info ** info)1126 const char* pa_card_port_info_to_json_object(pa_card_port_info **info) {
1127 pa_json_encoder *encoder = pa_json_encoder_new();
1128 if (!info) {
1129 pa_json_encoder_begin_element_object(encoder);
1130 pa_json_encoder_end_object(encoder);
1131 return pa_json_encoder_to_string_free(encoder);
1132 }
1133
1134 pa_card_port_info **p;
1135 char *pl;
1136
1137 pa_json_encoder_begin_element_object(encoder);
1138 for (p = info; *p; p++) {
1139 pa_card_profile_info **pr = (*p)->profiles;
1140
1141 char* latency_offset_str = pa_sprintf_malloc("%"PRId64" usec", (*p)->latency_offset);
1142 pa_json_encoder *port_info_encoder = pa_json_encoder_new();
1143 pa_json_encoder_begin_element_object(port_info_encoder);
1144 pa_json_encoder_add_member_string(port_info_encoder, "description", (*p)->description);
1145 pa_json_encoder_add_member_string(port_info_encoder, "type", get_device_port_type((*p)->type));
1146 pa_json_encoder_add_member_int(port_info_encoder, "priority", (*p)->priority);
1147 pa_json_encoder_add_member_string(port_info_encoder, "latency_offset", latency_offset_str);
1148 pa_json_encoder_add_member_string(port_info_encoder, "availability_group", (*p)->availability_group);
1149 pa_json_encoder_add_member_string(port_info_encoder, "availability", get_available_str((*p)->available));
1150 pa_json_encoder_add_member_raw_json(port_info_encoder, "properties", pl = pa_proplist_to_json_object((*p)->proplist));
1151 pa_json_encoder_add_member_raw_json(port_info_encoder, "profiles", pa_card_profile_info_to_json_array(pr));
1152 pa_json_encoder_end_object(port_info_encoder);
1153
1154 char *port_info_str = pa_json_encoder_to_string_free(port_info_encoder);
1155 pa_json_encoder_add_member_raw_json(encoder, (*p)->name, port_info_str);
1156 pa_xfree(port_info_str);
1157 pa_xfree(latency_offset_str);
1158 pa_xfree(pl);
1159 }
1160 pa_json_encoder_end_object(encoder);
1161
1162 return pa_json_encoder_to_string_free(encoder);
1163 }
1164
get_card_info_callback(pa_context * c,const pa_card_info * i,int is_last,void * userdata)1165 static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
1166 char t[32];
1167 char *pl;
1168
1169 if (format == JSON && json_encoder == NULL) {
1170 json_encoder = pa_json_encoder_new();
1171 pa_json_encoder_begin_element_array(json_encoder);
1172 }
1173
1174 if (is_last < 0) {
1175 pa_log(_("Failed to get card information: %s"), pa_strerror(pa_context_errno(c)));
1176 complete_action();
1177 return;
1178 }
1179
1180 if (is_last) {
1181 if (format == JSON) {
1182 pa_json_encoder_end_array_handler("cards");
1183 }
1184 complete_action();
1185 return;
1186 }
1187
1188 pa_assert(i);
1189
1190 if (nl && !short_list_format && format == TEXT)
1191 printf("\n");
1192 nl = true;
1193
1194 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
1195
1196 if (short_list_format) {
1197 if (format == JSON) {
1198 pa_json_encoder *encoder = pa_json_encoder_new();
1199 pa_json_encoder_begin_element_object(encoder);
1200 pa_json_encoder_add_member_int(encoder, "index", i->index);
1201 pa_json_encoder_add_member_string(encoder, "name", i->name);
1202 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1203 pa_json_encoder_end_object(encoder);
1204
1205 char* json_str = pa_json_encoder_to_string_free(encoder);
1206 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1207 pa_xfree(json_str);
1208 } else {
1209 printf("%u\t%s\t%s\n", i->index, i->name, pa_strnull(i->driver));
1210 }
1211 return;
1212 }
1213
1214 if (format == JSON) {
1215 pa_json_encoder *encoder = pa_json_encoder_new();
1216 pa_json_encoder_begin_element_object(encoder);
1217 pa_json_encoder_add_member_int(encoder, "index", i->index);
1218 pa_json_encoder_add_member_string(encoder, "name", i->name);
1219 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1220 i->owner_module != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "owner_module", t) : pa_json_encoder_add_member_null(encoder, "owner_module");
1221 pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1222 pa_json_encoder_add_member_raw_json(encoder, "profiles", i->n_profiles > 0 ? pa_card_profile_info_2_to_json_object(i->profiles2) : "{}");
1223 i->active_profile ? pa_json_encoder_add_member_string(encoder, "active_profile", i->active_profile->name) : pa_json_encoder_add_member_null(encoder, "active_profile");
1224 pa_json_encoder_add_member_raw_json(encoder, "ports", pa_card_port_info_to_json_object(i->ports));
1225 pa_json_encoder_end_object(encoder);
1226
1227 char* json_str = pa_json_encoder_to_string_free(encoder);
1228 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1229 pa_xfree(json_str);
1230 } else {
1231 printf(_("Card #%u\n"
1232 "\tName: %s\n"
1233 "\tDriver: %s\n"
1234 "\tOwner Module: %s\n"
1235 "\tProperties:\n\t\t%s\n"),
1236 i->index,
1237 i->name,
1238 pa_strnull(i->driver),
1239 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
1240 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1241
1242 if (i->n_profiles > 0) {
1243 pa_card_profile_info2 **p;
1244
1245 printf(_("\tProfiles:\n"));
1246 for (p = i->profiles2; *p; p++)
1247 printf(_("\t\t%s: %s (sinks: %u, sources: %u, priority: %u, available: %s)\n"), (*p)->name,
1248 (*p)->description, (*p)->n_sinks, (*p)->n_sources, (*p)->priority, pa_yes_no_localised((*p)->available));
1249 }
1250
1251 if (i->active_profile)
1252 printf(_("\tActive Profile: %s\n"),
1253 i->active_profile->name);
1254
1255 if (i->ports) {
1256 pa_card_port_info **p;
1257
1258 printf(_("\tPorts:\n"));
1259 for (p = i->ports; *p; p++) {
1260 pa_card_profile_info **pr = (*p)->profiles;
1261 printf(_("\t\t%s: %s (type: %s, priority: %u, latency offset: %" PRId64 " usec%s%s, %s)\n"), (*p)->name,
1262 (*p)->description, get_device_port_type((*p)->type), (*p)->priority, (*p)->latency_offset,
1263 (*p)->availability_group ? _(", availability group: ") : "", (*p)->availability_group ?: "",
1264 get_available_str((*p)->available));
1265
1266 if (!pa_proplist_isempty((*p)->proplist)) {
1267 pa_xfree(pl);
1268 printf(_("\t\t\tProperties:\n\t\t\t\t%s\n"), pl = pa_proplist_to_string_sep((*p)->proplist, "\n\t\t\t\t"));
1269 }
1270
1271 if (pr) {
1272 printf(_("\t\t\tPart of profile(s): %s"), pa_strnull((*pr)->name));
1273 pr++;
1274 while (*pr) {
1275 printf(", %s", pa_strnull((*pr)->name));
1276 pr++;
1277 }
1278 printf("\n");
1279 }
1280 }
1281 }
1282 }
1283
1284 pa_xfree(pl);
1285 }
1286
get_sink_input_info_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)1287 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
1288 char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
1289 char *pl;
1290
1291 if (format == JSON && json_encoder == NULL) {
1292 json_encoder = pa_json_encoder_new();
1293 pa_json_encoder_begin_element_array(json_encoder);
1294 }
1295
1296 if (is_last < 0) {
1297 pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
1298 quit(1);
1299 return;
1300 }
1301
1302 if (is_last) {
1303 if (format == JSON) {
1304 pa_json_encoder_end_array_handler("sink_inputs");
1305 }
1306 complete_action();
1307 return;
1308 }
1309
1310 pa_assert(i);
1311
1312 if (nl && !short_list_format && format == TEXT)
1313 printf("\n");
1314 nl = true;
1315
1316 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
1317 pa_snprintf(k, sizeof(k), "%u", i->client);
1318
1319 char *sample_spec = pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
1320 if (short_list_format) {
1321 if (format == JSON) {
1322 pa_json_encoder *encoder = pa_json_encoder_new();
1323 pa_json_encoder_begin_element_object(encoder);
1324 pa_json_encoder_add_member_int(encoder, "index", i->index);
1325 pa_json_encoder_add_member_int(encoder, "sink", i->sink);
1326 i->client != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "client", k) : pa_json_encoder_add_member_null(encoder, "client");
1327 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1328 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1329 pa_json_encoder_end_object(encoder);
1330
1331 char* json_str = pa_json_encoder_to_string_free(encoder);
1332 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1333 pa_xfree(json_str);
1334 } else {
1335 printf("%u\t%u\t%s\t%s\t%s\n",
1336 i->index,
1337 i->sink,
1338 i->client != PA_INVALID_INDEX ? k : "-",
1339 pa_strnull(i->driver),
1340 sample_spec);
1341 }
1342 return;
1343 }
1344
1345 char *channel_map = pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
1346 char *format_info = pa_format_info_snprint(f, sizeof(f), i->format);
1347 float balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
1348 if (format == JSON) {
1349 pa_json_encoder *encoder = pa_json_encoder_new();
1350 pa_json_encoder_begin_element_object(encoder);
1351 pa_json_encoder_add_member_int(encoder, "index", i->index);
1352 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1353 i->owner_module != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "owner_module", t) : pa_json_encoder_add_member_null(encoder, "owner_module");
1354 i->client != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "client", k) : pa_json_encoder_add_member_null(encoder, "client");
1355 pa_json_encoder_add_member_int(encoder, "sink", i->sink);
1356 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1357 pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
1358 pa_json_encoder_add_member_string(encoder, "format", format_info);
1359 pa_json_encoder_add_member_bool(encoder, "corked", i->corked);
1360 pa_json_encoder_add_member_bool(encoder, "mute", i->mute);
1361 pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, true));
1362 pa_json_encoder_add_member_double(encoder, "balance", balance, 2);
1363 pa_json_encoder_add_member_double(encoder, "buffer_latency_usec", (double) i->buffer_usec, 2);
1364 pa_json_encoder_add_member_double(encoder, "sink_latency_usec", (double) i->sink_usec, 2);
1365 pa_json_encoder_add_member_string(encoder, "resample_method", i->resample_method);
1366 pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1367 pa_json_encoder_end_object(encoder);
1368
1369 char* json_str = pa_json_encoder_to_string_free(encoder);
1370 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1371 pa_xfree(json_str);
1372 } else {
1373 printf(_("Sink Input #%u\n"
1374 "\tDriver: %s\n"
1375 "\tOwner Module: %s\n"
1376 "\tClient: %s\n"
1377 "\tSink: %u\n"
1378 "\tSample Specification: %s\n"
1379 "\tChannel Map: %s\n"
1380 "\tFormat: %s\n"
1381 "\tCorked: %s\n"
1382 "\tMute: %s\n"
1383 "\tVolume: %s\n"
1384 "\t balance %0.2f\n"
1385 "\tBuffer Latency: %0.0f usec\n"
1386 "\tSink Latency: %0.0f usec\n"
1387 "\tResample method: %s\n"
1388 "\tProperties:\n\t\t%s\n"),
1389 i->index,
1390 pa_strnull(i->driver),
1391 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
1392 i->client != PA_INVALID_INDEX ? k : _("n/a"),
1393 i->sink,
1394 sample_spec,
1395 channel_map,
1396 format_info,
1397 pa_yes_no_localised(i->corked),
1398 pa_yes_no_localised(i->mute),
1399 pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1400 balance,
1401 (double) i->buffer_usec,
1402 (double) i->sink_usec,
1403 i->resample_method ? i->resample_method : _("n/a"),
1404 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1405 }
1406
1407 pa_xfree(pl);
1408 }
1409
get_source_output_info_callback(pa_context * c,const pa_source_output_info * i,int is_last,void * userdata)1410 static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
1411 char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], f[PA_FORMAT_INFO_SNPRINT_MAX];
1412 char *pl;
1413
1414 if (format == JSON && json_encoder == NULL) {
1415 json_encoder = pa_json_encoder_new();
1416 pa_json_encoder_begin_element_array(json_encoder);
1417 }
1418
1419 if (is_last < 0) {
1420 pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
1421 quit(1);
1422 return;
1423 }
1424
1425 if (is_last) {
1426 if (format == JSON) {
1427 pa_json_encoder_end_array_handler("source_outputs");
1428 }
1429 complete_action();
1430 return;
1431 }
1432
1433 pa_assert(i);
1434
1435 if (nl && !short_list_format && format == TEXT)
1436 printf("\n");
1437 nl = true;
1438
1439 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
1440 pa_snprintf(k, sizeof(k), "%u", i->client);
1441
1442 char *sample_spec = pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec);
1443 if (short_list_format) {
1444 if (format == JSON) {
1445 pa_json_encoder *encoder = pa_json_encoder_new();
1446 pa_json_encoder_begin_element_object(encoder);
1447 pa_json_encoder_add_member_int(encoder, "index", i->index);
1448 pa_json_encoder_add_member_int(encoder, "source", i->source);
1449 i->client != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "client", k) : pa_json_encoder_add_member_null(encoder, "client");
1450 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1451 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1452 pa_json_encoder_end_object(encoder);
1453
1454 char* json_str = pa_json_encoder_to_string_free(encoder);
1455 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1456 pa_xfree(json_str);
1457 } else {
1458 printf("%u\t%u\t%s\t%s\t%s\n",
1459 i->index,
1460 i->source,
1461 i->client != PA_INVALID_INDEX ? k : "-",
1462 pa_strnull(i->driver),
1463 sample_spec);
1464 }
1465 return;
1466 }
1467
1468 char *channel_map = pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
1469 char *format_info = pa_format_info_snprint(f, sizeof(f), i->format);
1470 float balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
1471 if (format == JSON) {
1472 pa_json_encoder *encoder = pa_json_encoder_new();
1473 pa_json_encoder_begin_element_object(encoder);
1474 pa_json_encoder_add_member_int(encoder, "index", i->index);
1475 pa_json_encoder_add_member_string(encoder, "driver", i->driver);
1476 i->owner_module != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "owner_module", t) : pa_json_encoder_add_member_null(encoder, "owner_module");
1477 i->client != PA_INVALID_INDEX ? pa_json_encoder_add_member_string(encoder, "client", k) : pa_json_encoder_add_member_null(encoder, "client");
1478 pa_json_encoder_add_member_int(encoder, "source", i->source);
1479 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1480 pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
1481 pa_json_encoder_add_member_string(encoder, "format", format_info);
1482 pa_json_encoder_add_member_bool(encoder, "corked", i->corked);
1483 pa_json_encoder_add_member_bool(encoder, "mute", i->mute);
1484 pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, true));
1485 pa_json_encoder_add_member_double(encoder, "balance", balance, 2);
1486 pa_json_encoder_add_member_double(encoder, "buffer_latency_usec", (double) i->buffer_usec, 2);
1487 pa_json_encoder_add_member_double(encoder, "source_latency_usec", (double) i->source_usec, 2);
1488 pa_json_encoder_add_member_string(encoder, "resample_method", i->resample_method);
1489 pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1490 pa_json_encoder_end_object(encoder);
1491
1492 char* json_str = pa_json_encoder_to_string_free(encoder);
1493 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1494 pa_xfree(json_str);
1495 } else {
1496 printf(_("Source Output #%u\n"
1497 "\tDriver: %s\n"
1498 "\tOwner Module: %s\n"
1499 "\tClient: %s\n"
1500 "\tSource: %u\n"
1501 "\tSample Specification: %s\n"
1502 "\tChannel Map: %s\n"
1503 "\tFormat: %s\n"
1504 "\tCorked: %s\n"
1505 "\tMute: %s\n"
1506 "\tVolume: %s\n"
1507 "\t balance %0.2f\n"
1508 "\tBuffer Latency: %0.0f usec\n"
1509 "\tSource Latency: %0.0f usec\n"
1510 "\tResample method: %s\n"
1511 "\tProperties:\n\t\t%s\n"),
1512 i->index,
1513 pa_strnull(i->driver),
1514 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
1515 i->client != PA_INVALID_INDEX ? k : _("n/a"),
1516 i->source,
1517 sample_spec,
1518 channel_map,
1519 format_info,
1520 pa_yes_no_localised(i->corked),
1521 pa_yes_no_localised(i->mute),
1522 pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1523 balance,
1524 (double) i->buffer_usec,
1525 (double) i->source_usec,
1526 i->resample_method ? i->resample_method : _("n/a"),
1527 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1528 }
1529
1530 pa_xfree(pl);
1531 }
1532
get_sample_info_callback(pa_context * c,const pa_sample_info * i,int is_last,void * userdata)1533 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
1534 char t[PA_BYTES_SNPRINT_MAX], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
1535 char *pl;
1536
1537 if (format == JSON && json_encoder == NULL) {
1538 json_encoder = pa_json_encoder_new();
1539 pa_json_encoder_begin_element_array(json_encoder);
1540 }
1541
1542 if (is_last < 0) {
1543 pa_log(_("Failed to get sample information: %s"), pa_strerror(pa_context_errno(c)));
1544 quit(1);
1545 return;
1546 }
1547
1548 if (is_last) {
1549 if (format == JSON) {
1550 pa_json_encoder_end_array_handler("samples");
1551 }
1552 complete_action();
1553 return;
1554 }
1555
1556 pa_assert(i);
1557
1558 if (nl && !short_list_format && format == TEXT)
1559 printf("\n");
1560 nl = true;
1561
1562 pa_bytes_snprint(t, sizeof(t), i->bytes);
1563
1564 char *sample_spec = pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : short_list_format ? "-" : _("n/a");
1565 double duration = (double) i->duration/1000000.0;
1566 if (short_list_format) {
1567 if (format == JSON) {
1568 pa_json_encoder *encoder = pa_json_encoder_new();
1569 pa_json_encoder_begin_element_object(encoder);
1570 pa_json_encoder_add_member_int(encoder, "index", i->index);
1571 pa_json_encoder_add_member_string(encoder, "name", i->name);
1572 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1573 pa_json_encoder_add_member_double(encoder, "duration", duration, 3);
1574 pa_json_encoder_end_object(encoder);
1575
1576 char* json_str = pa_json_encoder_to_string_free(encoder);
1577 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1578 pa_xfree(json_str);
1579 } else {
1580 printf("%u\t%s\t%s\t%0.3f\n",
1581 i->index,
1582 i->name,
1583 sample_spec,
1584 duration);
1585 }
1586 return;
1587 }
1588
1589 char *channel_map = pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a");
1590 float balance = pa_cvolume_get_balance(&i->volume, &i->channel_map);
1591 if (format == JSON) {
1592 pa_json_encoder *encoder = pa_json_encoder_new();
1593 pa_json_encoder_begin_element_object(encoder);
1594 pa_json_encoder_add_member_int(encoder, "index", i->index);
1595 pa_json_encoder_add_member_string(encoder, "name", i->name);
1596 pa_json_encoder_add_member_string(encoder, "sample_specification", sample_spec);
1597 pa_json_encoder_add_member_string(encoder, "channel_map", channel_map);
1598 pa_json_encoder_add_member_raw_json(encoder, "volume", pa_cvolume_to_json_object(&i->volume, &i->channel_map, true));
1599 pa_json_encoder_add_member_double(encoder, "balance", balance, 2);
1600 pa_json_encoder_add_member_double(encoder, "duration", duration, 3);
1601 pa_json_encoder_add_member_string(encoder, "size", t);
1602 pa_json_encoder_add_member_bool(encoder, "lazy", i->lazy);
1603 pa_json_encoder_add_member_string(encoder, "filename", i->filename);
1604 pa_json_encoder_add_member_raw_json(encoder, "properties", pl = pa_proplist_to_json_object(i->proplist));
1605 pa_json_encoder_end_object(encoder);
1606
1607 char* json_str = pa_json_encoder_to_string_free(encoder);
1608 pa_json_encoder_add_element_raw_json(json_encoder, json_str);
1609 pa_xfree(json_str);
1610 } else {
1611 printf(_("Sample #%u\n"
1612 "\tName: %s\n"
1613 "\tSample Specification: %s\n"
1614 "\tChannel Map: %s\n"
1615 "\tVolume: %s\n"
1616 "\t balance %0.2f\n"
1617 "\tDuration: %0.1fs\n"
1618 "\tSize: %s\n"
1619 "\tLazy: %s\n"
1620 "\tFilename: %s\n"
1621 "\tProperties:\n\t\t%s\n"),
1622 i->index,
1623 i->name,
1624 sample_spec,
1625 channel_map,
1626 pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1627 balance,
1628 duration,
1629 t,
1630 pa_yes_no_localised(i->lazy),
1631 i->filename ? i->filename : _("n/a"),
1632 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
1633 }
1634
1635 pa_xfree(pl);
1636 }
1637
simple_callback(pa_context * c,int success,void * userdata)1638 static void simple_callback(pa_context *c, int success, void *userdata) {
1639 if (!success) {
1640 pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
1641 quit(1);
1642 return;
1643 }
1644
1645 complete_action();
1646 }
1647
index_callback(pa_context * c,uint32_t idx,void * userdata)1648 static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
1649 if (idx == PA_INVALID_INDEX) {
1650 pa_log(_("Failure: %s"), pa_strerror(pa_context_errno(c)));
1651 quit(1);
1652 return;
1653 }
1654
1655 if (format == JSON) {
1656 pa_json_encoder *encoder = pa_json_encoder_new();
1657 pa_json_encoder_begin_element_object(encoder);
1658 pa_json_encoder_add_member_int(encoder, "index", idx);
1659 pa_json_encoder_end_object(encoder);
1660
1661 char* json_str = pa_json_encoder_to_string_free(encoder);
1662 printf("%s", json_str);
1663 pa_xfree(json_str);
1664 } else {
1665 printf("%u\n", idx);
1666 }
1667
1668 complete_action();
1669 }
1670
send_message_callback(pa_context * c,int success,char * response,void * userdata)1671 static void send_message_callback(pa_context *c, int success, char *response, void *userdata) {
1672
1673 if (!success) {
1674 pa_log(_("Send message failed: %s"), pa_strerror(pa_context_errno(c)));
1675 quit(1);
1676 return;
1677 }
1678
1679 if (format == JSON) {
1680 pa_json_encoder *encoder = pa_json_encoder_new();
1681 pa_json_encoder_begin_element_object(encoder);
1682 pa_json_encoder_add_member_string(encoder, "response", response);
1683 pa_json_encoder_end_object(encoder);
1684
1685 char* json_str = pa_json_encoder_to_string_free(encoder);
1686 printf("%s", json_str);
1687 pa_xfree(json_str);
1688 } else {
1689 printf("%s\n", response);
1690 }
1691
1692 complete_action();
1693 }
1694
list_handlers_callback(pa_context * c,int success,char * response,void * userdata)1695 static void list_handlers_callback(pa_context *c, int success, char *response, void *userdata) {
1696 int err;
1697 pa_json_object *o;
1698 int i;
1699 const pa_json_object *v, *path, *description;
1700
1701 if (!success) {
1702 pa_log(_("list-handlers message failed: %s"), pa_strerror(pa_context_errno(c)));
1703 quit(1);
1704 return;
1705 }
1706
1707 // The response is already JSON encoded
1708 if (format == JSON) {
1709 printf("%s\n", response);
1710 fflush(stdout);
1711 complete_action();
1712 return;
1713 }
1714
1715 o = pa_json_parse(response);
1716
1717 if (!o) {
1718 pa_log(_("list-handlers message response could not be parsed correctly"));
1719 pa_json_object_free(o);
1720 quit(1);
1721 return;
1722 }
1723
1724 if (pa_json_object_get_type(o) != PA_JSON_TYPE_ARRAY) {
1725 pa_log(_("list-handlers message response is not a JSON array"));
1726 pa_json_object_free(o);
1727 quit(1);
1728 return;
1729 }
1730
1731 err = 0;
1732
1733 for (i = 0; i < pa_json_object_get_array_length(o); ++i) {
1734 v = pa_json_object_get_array_member(o, i);
1735 if (pa_json_object_get_type(v) != PA_JSON_TYPE_OBJECT) {
1736 pa_log(_("list-handlers message response array element %d is not a JSON object"), i);
1737 err = -1;
1738 break;
1739 }
1740
1741 path = pa_json_object_get_object_member(v, "name");
1742 if (!path || pa_json_object_get_type(path) != PA_JSON_TYPE_STRING) {
1743 err = -1;
1744 break;
1745 }
1746 description = pa_json_object_get_object_member(v, "description");
1747 if (!description || pa_json_object_get_type(description) != PA_JSON_TYPE_STRING) {
1748 err = -1;
1749 break;
1750 }
1751
1752 if (short_list_format) {
1753 printf("%s\n", pa_json_object_get_string(path));
1754 } else {
1755 if (nl)
1756 printf("\n");
1757 nl = true;
1758
1759 printf("Message Handler %s\n"
1760 "\tDescription: %s\n",
1761 pa_json_object_get_string(path),
1762 pa_json_object_get_string(description));
1763 }
1764 }
1765
1766 if (err < 0) {
1767 pa_log(_("list-handlers message response could not be parsed correctly"));
1768 pa_json_object_free(o);
1769 quit(1);
1770 return;
1771 }
1772
1773 pa_json_object_free(o);
1774
1775 complete_action();
1776 }
1777
volume_relative_adjust(pa_cvolume * cv)1778 static void volume_relative_adjust(pa_cvolume *cv) {
1779 pa_assert(volume_flags & VOL_RELATIVE);
1780
1781 /* Relative volume change is additive in case of UINT or PERCENT
1782 * and multiplicative for LINEAR or DECIBEL */
1783 if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
1784 unsigned i;
1785 for (i = 0; i < cv->channels; i++) {
1786 if (cv->values[i] + volume.values[i] < PA_VOLUME_NORM)
1787 cv->values[i] = PA_VOLUME_MUTED;
1788 else
1789 cv->values[i] = cv->values[i] + volume.values[i] - PA_VOLUME_NORM;
1790 }
1791 }
1792 if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL)
1793 pa_sw_cvolume_multiply(cv, cv, &volume);
1794 }
1795
unload_module_by_name_callback(pa_context * c,const pa_module_info * i,int is_last,void * userdata)1796 static void unload_module_by_name_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
1797 static bool unloaded = false;
1798
1799 if (is_last < 0) {
1800 pa_log(_("Failed to get module information: %s"), pa_strerror(pa_context_errno(c)));
1801 quit(1);
1802 return;
1803 }
1804
1805 if (is_last) {
1806 if (unloaded == false)
1807 pa_log(_("Failed to unload module: Module %s not loaded"), module_name);
1808 complete_action();
1809 return;
1810 }
1811
1812 pa_assert(i);
1813
1814 if (pa_streq(module_name, i->name)) {
1815 unloaded = true;
1816 actions++;
1817 pa_operation_unref(pa_context_unload_module(c, i->index, simple_callback, NULL));
1818 }
1819 }
1820
fill_volume(pa_cvolume * cv,unsigned supported)1821 static void fill_volume(pa_cvolume *cv, unsigned supported) {
1822 if (volume.channels == 1) {
1823 pa_cvolume_set(&volume, supported, volume.values[0]);
1824 } else if (volume.channels != supported) {
1825 pa_log(ngettext("Failed to set volume: You tried to set volumes for %d channel, whereas channel(s) supported = %d\n",
1826 "Failed to set volume: You tried to set volumes for %d channels, whereas channel(s) supported = %d\n",
1827 volume.channels),
1828 volume.channels, supported);
1829 quit(1);
1830 return;
1831 }
1832
1833 if (volume_flags & VOL_RELATIVE)
1834 volume_relative_adjust(cv);
1835 else
1836 *cv = volume;
1837 }
1838
get_sink_mute_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)1839 static void get_sink_mute_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
1840 if (is_last < 0) {
1841 pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
1842 quit(1);
1843 return;
1844 }
1845
1846 if (is_last)
1847 return;
1848
1849 pa_assert(i);
1850
1851 printf(("Mute: %s\n"),
1852 pa_yes_no_localised(i->mute));
1853
1854 complete_action();
1855 }
1856
get_sink_volume_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)1857 static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
1858 if (is_last < 0) {
1859 pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
1860 quit(1);
1861 return;
1862 }
1863
1864 if (is_last)
1865 return;
1866
1867 pa_assert(i);
1868
1869 char cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
1870 printf(("Volume: %s\n"
1871 " balance %0.2f\n"),
1872 pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1873 pa_cvolume_get_balance(&i->volume, &i->channel_map));
1874
1875 complete_action();
1876 }
1877
set_sink_volume_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)1878 static void set_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
1879 pa_cvolume cv;
1880 pa_operation *o;
1881
1882 if (is_last < 0) {
1883 pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
1884 quit(1);
1885 return;
1886 }
1887
1888 if (is_last)
1889 return;
1890
1891 pa_assert(i);
1892
1893 cv = i->volume;
1894 fill_volume(&cv, i->channel_map.channels);
1895
1896 o = pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL);
1897 if (o)
1898 pa_operation_unref(o);
1899 else {
1900 pa_log(_("Failed to set sink volume: %s"), pa_strerror(pa_context_errno(c)));
1901 complete_action();
1902 }
1903 }
1904
get_source_mute_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1905 static void get_source_mute_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1906 if (is_last < 0) {
1907 pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1908 quit(1);
1909 return;
1910 }
1911
1912 if (is_last)
1913 return;
1914
1915 pa_assert(i);
1916
1917 printf(("Mute: %s\n"),
1918 pa_yes_no_localised(i->mute));
1919
1920 complete_action();
1921 }
1922
get_source_volume_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1923 static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1924 if (is_last < 0) {
1925 pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1926 quit(1);
1927 return;
1928 }
1929
1930 if (is_last)
1931 return;
1932
1933 pa_assert(i);
1934
1935 char cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
1936 printf(("Volume: %s\n"
1937 " balance %0.2f\n"),
1938 pa_cvolume_snprint_verbose(cv, sizeof(cv), &i->volume, &i->channel_map, true),
1939 pa_cvolume_get_balance(&i->volume, &i->channel_map));
1940
1941 complete_action();
1942 }
1943
set_source_volume_callback(pa_context * c,const pa_source_info * i,int is_last,void * userdata)1944 static void set_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
1945 pa_cvolume cv;
1946 pa_operation *o;
1947
1948 if (is_last < 0) {
1949 pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
1950 quit(1);
1951 return;
1952 }
1953
1954 if (is_last)
1955 return;
1956
1957 pa_assert(i);
1958
1959 cv = i->volume;
1960 fill_volume(&cv, i->channel_map.channels);
1961
1962 o = pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL);
1963 if (o)
1964 pa_operation_unref(o);
1965 else {
1966 pa_log(_("Failed to set source volume: %s"), pa_strerror(pa_context_errno(c)));
1967 complete_action();
1968 }
1969 }
1970
get_sink_input_volume_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)1971 static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
1972 pa_cvolume cv;
1973
1974 if (is_last < 0) {
1975 pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
1976 quit(1);
1977 return;
1978 }
1979
1980 if (is_last)
1981 return;
1982
1983 pa_assert(i);
1984
1985 cv = i->volume;
1986 fill_volume(&cv, i->channel_map.channels);
1987
1988 pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
1989 }
1990
get_source_output_volume_callback(pa_context * c,const pa_source_output_info * o,int is_last,void * userdata)1991 static void get_source_output_volume_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
1992 pa_cvolume cv;
1993
1994 if (is_last < 0) {
1995 pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
1996 quit(1);
1997 return;
1998 }
1999
2000 if (is_last)
2001 return;
2002
2003 pa_assert(o);
2004
2005 cv = o->volume;
2006 fill_volume(&cv, o->channel_map.channels);
2007
2008 pa_operation_unref(pa_context_set_source_output_volume(c, source_output_idx, &cv, simple_callback, NULL));
2009 }
2010
sink_toggle_mute_callback(pa_context * c,const pa_sink_info * i,int is_last,void * userdata)2011 static void sink_toggle_mute_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
2012 if (is_last < 0) {
2013 pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
2014 quit(1);
2015 return;
2016 }
2017
2018 if (is_last)
2019 return;
2020
2021 pa_assert(i);
2022
2023 pa_operation_unref(pa_context_set_sink_mute_by_name(c, i->name, !i->mute, simple_callback, NULL));
2024 }
2025
source_toggle_mute_callback(pa_context * c,const pa_source_info * o,int is_last,void * userdata)2026 static void source_toggle_mute_callback(pa_context *c, const pa_source_info *o, int is_last, void *userdata) {
2027 if (is_last < 0) {
2028 pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
2029 quit(1);
2030 return;
2031 }
2032
2033 if (is_last)
2034 return;
2035
2036 pa_assert(o);
2037
2038 pa_operation_unref(pa_context_set_source_mute_by_name(c, o->name, !o->mute, simple_callback, NULL));
2039 }
2040
sink_input_toggle_mute_callback(pa_context * c,const pa_sink_input_info * i,int is_last,void * userdata)2041 static void sink_input_toggle_mute_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
2042 if (is_last < 0) {
2043 pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
2044 quit(1);
2045 return;
2046 }
2047
2048 if (is_last)
2049 return;
2050
2051 pa_assert(i);
2052
2053 pa_operation_unref(pa_context_set_sink_input_mute(c, i->index, !i->mute, simple_callback, NULL));
2054 }
2055
source_output_toggle_mute_callback(pa_context * c,const pa_source_output_info * o,int is_last,void * userdata)2056 static void source_output_toggle_mute_callback(pa_context *c, const pa_source_output_info *o, int is_last, void *userdata) {
2057 if (is_last < 0) {
2058 pa_log(_("Failed to get source output information: %s"), pa_strerror(pa_context_errno(c)));
2059 quit(1);
2060 return;
2061 }
2062
2063 if (is_last)
2064 return;
2065
2066 pa_assert(o);
2067
2068 pa_operation_unref(pa_context_set_source_output_mute(c, o->index, !o->mute, simple_callback, NULL));
2069 }
2070
2071 /* PA_MAX_FORMATS is defined in internal.h so we just define a sane value here */
2072 #define MAX_FORMATS 256
2073
set_sink_formats(pa_context * c,uint32_t sink,const char * str)2074 static void set_sink_formats(pa_context *c, uint32_t sink, const char *str) {
2075 pa_format_info *f_arr[MAX_FORMATS] = { 0, };
2076 char *format = NULL;
2077 const char *state = NULL;
2078 int i = 0;
2079 pa_operation *o = NULL;
2080
2081 while ((format = pa_split(str, ";", &state))) {
2082 pa_format_info *f = pa_format_info_from_string(pa_strip(format));
2083
2084 if (!f) {
2085 pa_log(_("Failed to set format: invalid format string %s"), format);
2086 goto error;
2087 }
2088
2089 f_arr[i++] = f;
2090 pa_xfree(format);
2091 }
2092
2093 o = pa_ext_device_restore_save_formats(c, PA_DEVICE_TYPE_SINK, sink, i, f_arr, simple_callback, NULL);
2094 if (o) {
2095 pa_operation_unref(o);
2096 actions++;
2097 }
2098
2099 done:
2100 if (format)
2101 pa_xfree(format);
2102 while (f_arr[i] && i--)
2103 pa_format_info_free(f_arr[i]);
2104
2105 return;
2106
2107 error:
2108 while (f_arr[i] && i--)
2109 pa_format_info_free(f_arr[i]);
2110 quit(1);
2111 goto done;
2112 }
2113
2114 #ifdef SNDFILE_ENABLE
stream_state_callback(pa_stream * s,void * userdata)2115 static void stream_state_callback(pa_stream *s, void *userdata) {
2116 pa_assert(s);
2117
2118 switch (pa_stream_get_state(s)) {
2119 case PA_STREAM_CREATING:
2120 case PA_STREAM_READY:
2121 break;
2122
2123 case PA_STREAM_TERMINATED:
2124 drain();
2125 break;
2126
2127 case PA_STREAM_FAILED:
2128 default:
2129 pa_log(_("Failed to upload sample: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
2130 quit(1);
2131 }
2132 }
2133
stream_write_callback(pa_stream * s,size_t length,void * userdata)2134 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
2135 sf_count_t l;
2136 float *d;
2137 pa_assert(s && length && sndfile);
2138
2139 d = pa_xmalloc(length);
2140
2141 pa_assert(sample_length >= length);
2142 l = (sf_count_t) (length/pa_frame_size(&sample_spec));
2143
2144 if ((sf_readf_float(sndfile, d, l)) != l) {
2145 pa_xfree(d);
2146 pa_log(_("Premature end of file"));
2147 quit(1);
2148 return;
2149 }
2150
2151 pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
2152
2153 sample_length -= length;
2154
2155 if (sample_length <= 0) {
2156 pa_stream_set_write_callback(sample_stream, NULL, NULL);
2157 pa_stream_finish_upload(sample_stream);
2158 }
2159 }
2160 #endif
2161
subscription_event_type_to_string(pa_subscription_event_type_t t)2162 static const char *subscription_event_type_to_string(pa_subscription_event_type_t t) {
2163
2164 switch (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) {
2165
2166 case PA_SUBSCRIPTION_EVENT_NEW:
2167 return _("new");
2168
2169 case PA_SUBSCRIPTION_EVENT_CHANGE:
2170 return _("change");
2171
2172 case PA_SUBSCRIPTION_EVENT_REMOVE:
2173 return _("remove");
2174 }
2175
2176 return _("unknown");
2177 }
2178
subscription_event_facility_to_string(pa_subscription_event_type_t t)2179 static const char *subscription_event_facility_to_string(pa_subscription_event_type_t t) {
2180
2181 switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
2182
2183 case PA_SUBSCRIPTION_EVENT_SINK:
2184 return _("sink");
2185
2186 case PA_SUBSCRIPTION_EVENT_SOURCE:
2187 return _("source");
2188
2189 case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
2190 return _("sink-input");
2191
2192 case PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT:
2193 return _("source-output");
2194
2195 case PA_SUBSCRIPTION_EVENT_MODULE:
2196 return _("module");
2197
2198 case PA_SUBSCRIPTION_EVENT_CLIENT:
2199 return _("client");
2200
2201 case PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE:
2202 return _("sample-cache");
2203
2204 case PA_SUBSCRIPTION_EVENT_SERVER:
2205 return _("server");
2206
2207 case PA_SUBSCRIPTION_EVENT_CARD:
2208 return _("card");
2209 }
2210
2211 return _("unknown");
2212 }
2213
context_subscribe_callback(pa_context * c,pa_subscription_event_type_t t,uint32_t idx,void * userdata)2214 static void context_subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
2215 pa_assert(c);
2216
2217 if (format == JSON) {
2218 pa_json_encoder *encoder = pa_json_encoder_new();
2219 pa_json_encoder_begin_element_object(encoder);
2220 pa_json_encoder_add_member_int(encoder, "index", idx);
2221 pa_json_encoder_add_member_string(encoder, "event", subscription_event_type_to_string(t));
2222 pa_json_encoder_add_member_string(encoder, "on", subscription_event_facility_to_string(t));
2223 pa_json_encoder_end_object(encoder);
2224
2225 char* json_str = pa_json_encoder_to_string_free(encoder);
2226 printf("%s\n", json_str);
2227 pa_xfree(json_str);
2228 } else {
2229 printf(_("Event '%s' on %s #%u\n"),
2230 subscription_event_type_to_string(t),
2231 subscription_event_facility_to_string(t),
2232 idx);
2233 }
2234 fflush(stdout);
2235 }
2236
context_state_callback(pa_context * c,void * userdata)2237 static void context_state_callback(pa_context *c, void *userdata) {
2238 pa_operation *o = NULL;
2239
2240 pa_assert(c);
2241
2242 switch (pa_context_get_state(c)) {
2243 case PA_CONTEXT_CONNECTING:
2244 case PA_CONTEXT_AUTHORIZING:
2245 case PA_CONTEXT_SETTING_NAME:
2246 break;
2247
2248 case PA_CONTEXT_READY:
2249 switch (action) {
2250 case STAT:
2251 o = pa_context_stat(c, stat_callback, NULL);
2252 break;
2253
2254 case INFO:
2255 o = pa_context_get_server_info(c, get_server_info_callback, NULL);
2256 break;
2257
2258 case PLAY_SAMPLE:
2259 o = pa_context_play_sample(c, sample_name, sink_name, PA_VOLUME_NORM, simple_callback, NULL);
2260 break;
2261
2262 case REMOVE_SAMPLE:
2263 o = pa_context_remove_sample(c, sample_name, simple_callback, NULL);
2264 break;
2265 #ifdef SNDFILE_ENABLE
2266 case UPLOAD_SAMPLE:
2267 sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
2268 pa_assert(sample_stream);
2269
2270 pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
2271 pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
2272 pa_stream_connect_upload(sample_stream, sample_length);
2273 actions++;
2274 break;
2275 #endif
2276 case EXIT:
2277 o = pa_context_exit_daemon(c, simple_callback, NULL);
2278 break;
2279
2280 case LIST:
2281 if (list_type) {
2282 if (pa_streq(list_type, "modules"))
2283 o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
2284 else if (pa_streq(list_type, "sinks"))
2285 o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
2286 else if (pa_streq(list_type, "sources"))
2287 o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
2288 else if (pa_streq(list_type, "sink-inputs"))
2289 o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
2290 else if (pa_streq(list_type, "source-outputs"))
2291 o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
2292 else if (pa_streq(list_type, "clients"))
2293 o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
2294 else if (pa_streq(list_type, "samples"))
2295 o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
2296 else if (pa_streq(list_type, "cards"))
2297 o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
2298 else if (pa_streq(list_type, "message-handlers"))
2299 o = pa_context_send_message_to_object(c, "/core", "list-handlers", NULL, list_handlers_callback, NULL);
2300 else
2301 pa_assert_not_reached();
2302 } else {
2303 if (format == JSON) {
2304 list_encoder = pa_json_encoder_new();
2305 pa_json_encoder_begin_element_object(list_encoder);
2306 }
2307
2308 o = pa_context_get_module_info_list(c, get_module_info_callback, NULL);
2309 if (o) {
2310 pa_operation_unref(o);
2311 actions++;
2312 }
2313
2314 o = pa_context_get_sink_info_list(c, get_sink_info_callback, NULL);
2315 if (o) {
2316 pa_operation_unref(o);
2317 actions++;
2318 }
2319
2320 o = pa_context_get_source_info_list(c, get_source_info_callback, NULL);
2321 if (o) {
2322 pa_operation_unref(o);
2323 actions++;
2324 }
2325 o = pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL);
2326 if (o) {
2327 pa_operation_unref(o);
2328 actions++;
2329 }
2330
2331 o = pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL);
2332 if (o) {
2333 pa_operation_unref(o);
2334 actions++;
2335 }
2336
2337 o = pa_context_get_client_info_list(c, get_client_info_callback, NULL);
2338 if (o) {
2339 pa_operation_unref(o);
2340 actions++;
2341 }
2342
2343 o = pa_context_get_sample_info_list(c, get_sample_info_callback, NULL);
2344 if (o) {
2345 pa_operation_unref(o);
2346 actions++;
2347 }
2348
2349 o = pa_context_get_card_info_list(c, get_card_info_callback, NULL);
2350 if (o) {
2351 pa_operation_unref(o);
2352 actions++;
2353 }
2354
2355 o = NULL;
2356 }
2357 break;
2358
2359 case MOVE_SINK_INPUT:
2360 o = pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL);
2361 break;
2362
2363 case MOVE_SOURCE_OUTPUT:
2364 o = pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL);
2365 break;
2366
2367 case LOAD_MODULE:
2368 o = pa_context_load_module(c, module_name, module_args, index_callback, NULL);
2369 break;
2370
2371 case UNLOAD_MODULE:
2372 if (module_name)
2373 o = pa_context_get_module_info_list(c, unload_module_by_name_callback, NULL);
2374 else
2375 o = pa_context_unload_module(c, module_index, simple_callback, NULL);
2376 break;
2377
2378 case SUSPEND_SINK:
2379 if (sink_name)
2380 o = pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL);
2381 else
2382 o = pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
2383 break;
2384
2385 case SUSPEND_SOURCE:
2386 if (source_name)
2387 o = pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL);
2388 else
2389 o = pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL);
2390 break;
2391
2392 case SET_CARD_PROFILE:
2393 o = pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL);
2394 break;
2395
2396 case SET_SINK_PORT:
2397 o = pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL);
2398 break;
2399
2400 case GET_DEFAULT_SINK:
2401 o = pa_context_get_server_info(c, get_default_sink, NULL);
2402 break;
2403
2404 case SET_DEFAULT_SINK:
2405 o = pa_context_set_default_sink(c, sink_name, simple_callback, NULL);
2406 break;
2407
2408 case SET_SOURCE_PORT:
2409 o = pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL);
2410 break;
2411
2412 case GET_DEFAULT_SOURCE:
2413 o = pa_context_get_server_info(c, get_default_source, NULL);
2414 break;
2415
2416 case SET_DEFAULT_SOURCE:
2417 o = pa_context_set_default_source(c, source_name, simple_callback, NULL);
2418 break;
2419
2420 case GET_SINK_MUTE:
2421 o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_mute_callback, NULL);
2422 break;
2423
2424 case SET_SINK_MUTE:
2425 if (mute == TOGGLE_MUTE)
2426 o = pa_context_get_sink_info_by_name(c, sink_name, sink_toggle_mute_callback, NULL);
2427 else
2428 o = pa_context_set_sink_mute_by_name(c, sink_name, mute, simple_callback, NULL);
2429 break;
2430
2431 case GET_SOURCE_MUTE:
2432 o = pa_context_get_source_info_by_name(c, source_name, get_source_mute_callback, NULL);
2433 break;
2434
2435 case SET_SOURCE_MUTE:
2436 if (mute == TOGGLE_MUTE)
2437 o = pa_context_get_source_info_by_name(c, source_name, source_toggle_mute_callback, NULL);
2438 else
2439 o = pa_context_set_source_mute_by_name(c, source_name, mute, simple_callback, NULL);
2440 break;
2441
2442 case SET_SINK_INPUT_MUTE:
2443 if (mute == TOGGLE_MUTE)
2444 o = pa_context_get_sink_input_info(c, sink_input_idx, sink_input_toggle_mute_callback, NULL);
2445 else
2446 o = pa_context_set_sink_input_mute(c, sink_input_idx, mute, simple_callback, NULL);
2447 break;
2448
2449 case SET_SOURCE_OUTPUT_MUTE:
2450 if (mute == TOGGLE_MUTE)
2451 o = pa_context_get_source_output_info(c, source_output_idx, source_output_toggle_mute_callback, NULL);
2452 else
2453 o = pa_context_set_source_output_mute(c, source_output_idx, mute, simple_callback, NULL);
2454 break;
2455
2456 case GET_SINK_VOLUME:
2457 o = pa_context_get_sink_info_by_name(c, sink_name, get_sink_volume_callback, NULL);
2458 break;
2459
2460 case SET_SINK_VOLUME:
2461 o = pa_context_get_sink_info_by_name(c, sink_name, set_sink_volume_callback, NULL);
2462 break;
2463
2464 case GET_SOURCE_VOLUME:
2465 o = pa_context_get_source_info_by_name(c, source_name, get_source_volume_callback, NULL);
2466 break;
2467
2468 case SET_SOURCE_VOLUME:
2469 o = pa_context_get_source_info_by_name(c, source_name, set_source_volume_callback, NULL);
2470 break;
2471
2472 case SET_SINK_INPUT_VOLUME:
2473 o = pa_context_get_sink_input_info(c, sink_input_idx, get_sink_input_volume_callback, NULL);
2474 break;
2475
2476 case SET_SOURCE_OUTPUT_VOLUME:
2477 o = pa_context_get_source_output_info(c, source_output_idx, get_source_output_volume_callback, NULL);
2478 break;
2479
2480 case SET_SINK_FORMATS:
2481 set_sink_formats(c, sink_idx, formats);
2482 break;
2483
2484 case SET_PORT_LATENCY_OFFSET:
2485 o = pa_context_set_port_latency_offset(c, card_name, port_name, latency_offset, simple_callback, NULL);
2486 break;
2487
2488 case SEND_MESSAGE:
2489 o = pa_context_send_message_to_object(c, object_path, message, message_args, send_message_callback, NULL);
2490 break;
2491
2492 case SUBSCRIBE:
2493 pa_context_set_subscribe_callback(c, context_subscribe_callback, NULL);
2494
2495 o = pa_context_subscribe(c,
2496 PA_SUBSCRIPTION_MASK_SINK|
2497 PA_SUBSCRIPTION_MASK_SOURCE|
2498 PA_SUBSCRIPTION_MASK_SINK_INPUT|
2499 PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT|
2500 PA_SUBSCRIPTION_MASK_MODULE|
2501 PA_SUBSCRIPTION_MASK_CLIENT|
2502 PA_SUBSCRIPTION_MASK_SAMPLE_CACHE|
2503 PA_SUBSCRIPTION_MASK_SERVER|
2504 PA_SUBSCRIPTION_MASK_CARD,
2505 NULL,
2506 NULL);
2507 break;
2508
2509 default:
2510 pa_assert_not_reached();
2511 }
2512
2513 if (o) {
2514 pa_operation_unref(o);
2515 actions++;
2516 }
2517
2518 if (actions == 0) {
2519 pa_log("Operation failed: %s", pa_strerror(pa_context_errno(c)));
2520 quit(1);
2521 }
2522
2523 break;
2524
2525 case PA_CONTEXT_TERMINATED:
2526 quit(0);
2527 break;
2528
2529 case PA_CONTEXT_FAILED:
2530 default:
2531 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c)));
2532 quit(1);
2533 }
2534 }
2535
exit_signal_callback(pa_mainloop_api * m,pa_signal_event * e,int sig,void * userdata)2536 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
2537 pa_log(_("Got SIGINT, exiting."));
2538 quit(0);
2539 }
2540
parse_volume(const char * vol_spec,pa_volume_t * vol,enum volume_flags * vol_flags)2541 static int parse_volume(const char *vol_spec, pa_volume_t *vol, enum volume_flags *vol_flags) {
2542 double v;
2543 char *vs;
2544 const char *atod_input;
2545
2546 pa_assert(vol_spec);
2547 pa_assert(vol);
2548 pa_assert(vol_flags);
2549
2550 vs = pa_xstrdup(vol_spec);
2551
2552 *vol_flags = (pa_startswith(vs, "+") || pa_startswith(vs, "-")) ? VOL_RELATIVE : VOL_ABSOLUTE;
2553 if (pa_endswith(vs, "%")) {
2554 *vol_flags |= VOL_PERCENT;
2555 vs[strlen(vs)-1] = 0;
2556 }
2557 else if (pa_endswith(vs, "db") || pa_endswith(vs, "dB")) {
2558 *vol_flags |= VOL_DECIBEL;
2559 vs[strlen(vs)-2] = 0;
2560 }
2561 else if (strchr(vs, '.'))
2562 *vol_flags |= VOL_LINEAR;
2563
2564 atod_input = vs;
2565
2566 if (atod_input[0] == '+')
2567 atod_input++; /* pa_atod() doesn't accept leading '+', so skip it. */
2568
2569 if (pa_atod(atod_input, &v) < 0) {
2570 pa_log(_("Invalid volume specification"));
2571 pa_xfree(vs);
2572 return -1;
2573 }
2574
2575 pa_xfree(vs);
2576
2577 if (*vol_flags & VOL_RELATIVE) {
2578 switch (*vol_flags & 0x0F) {
2579 case VOL_UINT:
2580 v += (double) PA_VOLUME_NORM;
2581 break;
2582 case VOL_PERCENT:
2583 v += 100.0;
2584 break;
2585 case VOL_LINEAR:
2586 v += 1.0;
2587 break;
2588 }
2589 }
2590
2591 switch (*vol_flags & 0x0F) {
2592 case VOL_PERCENT:
2593 v = v * (double) PA_VOLUME_NORM / 100;
2594 break;
2595 case VOL_LINEAR:
2596 v = pa_sw_volume_from_linear(v);
2597 break;
2598 case VOL_DECIBEL:
2599 v = pa_sw_volume_from_dB(v);
2600 break;
2601 }
2602
2603 if (!PA_VOLUME_IS_VALID((pa_volume_t) v)) {
2604 pa_log(_("Volume outside permissible range.\n"));
2605 return -1;
2606 }
2607
2608 *vol = (pa_volume_t) v;
2609
2610 return 0;
2611 }
2612
parse_volumes(char * args[],unsigned n)2613 static int parse_volumes(char *args[], unsigned n) {
2614 unsigned i;
2615
2616 if (n >= PA_CHANNELS_MAX) {
2617 pa_log(_("Invalid number of volume specifications.\n"));
2618 return -1;
2619 }
2620
2621 volume.channels = n;
2622 for (i = 0; i < volume.channels; i++) {
2623 enum volume_flags flags = 0;
2624
2625 if (parse_volume(args[i], &volume.values[i], &flags) < 0)
2626 return -1;
2627
2628 if (i > 0 && flags != volume_flags) {
2629 pa_log(_("Inconsistent volume specification.\n"));
2630 return -1;
2631 } else
2632 volume_flags = flags;
2633 }
2634
2635 return 0;
2636 }
2637
parse_mute(const char * mute_text)2638 static enum mute_flags parse_mute(const char *mute_text) {
2639 int b;
2640
2641 pa_assert(mute_text);
2642
2643 if (pa_streq("toggle", mute_text))
2644 return TOGGLE_MUTE;
2645
2646 b = pa_parse_boolean(mute_text);
2647 switch (b) {
2648 case 0:
2649 return UNMUTE;
2650 case 1:
2651 return MUTE;
2652 default:
2653 return INVALID_MUTE;
2654 }
2655 }
2656
help(const char * argv0)2657 static void help(const char *argv0) {
2658
2659 printf("%s %s %s\n", argv0, _("[options]"), "stat");
2660 printf("%s %s %s\n", argv0, _("[options]"), "info");
2661 printf("%s %s %s %s\n", argv0, _("[options]"), "list [short]", _("[TYPE]"));
2662 printf("%s %s %s\n", argv0, _("[options]"), "exit");
2663 printf("%s %s %s %s\n", argv0, _("[options]"), "upload-sample", _("FILENAME [NAME]"));
2664 printf("%s %s %s %s\n", argv0, _("[options]"), "play-sample ", _("NAME [SINK]"));
2665 printf("%s %s %s %s\n", argv0, _("[options]"), "remove-sample ", _("NAME"));
2666 printf("%s %s %s %s\n", argv0, _("[options]"), "load-module ", _("NAME [ARGS ...]"));
2667 printf("%s %s %s %s\n", argv0, _("[options]"), "unload-module ", _("NAME|#N"));
2668 printf("%s %s %s %s\n", argv0, _("[options]"), "move-(sink-input|source-output)", _("#N SINK|SOURCE"));
2669 printf("%s %s %s %s\n", argv0, _("[options]"), "suspend-(sink|source)", _("NAME|#N 1|0"));
2670 printf("%s %s %s %s\n", argv0, _("[options]"), "set-card-profile ", _("CARD PROFILE"));
2671 printf("%s %s %s\n", argv0, _("[options]"), "get-default-(sink|source)");
2672 printf("%s %s %s %s\n", argv0, _("[options]"), "set-default-(sink|source)", _("NAME"));
2673 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-port", _("NAME|#N PORT"));
2674 printf("%s %s %s %s\n", argv0, _("[options]"), "get-(sink|source)-volume", _("NAME|#N"));
2675 printf("%s %s %s %s\n", argv0, _("[options]"), "get-(sink|source)-mute", _("NAME|#N"));
2676 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-volume", _("NAME|#N VOLUME [VOLUME ...]"));
2677 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-volume", _("#N VOLUME [VOLUME ...]"));
2678 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink|source)-mute", _("NAME|#N 1|0|toggle"));
2679 printf("%s %s %s %s\n", argv0, _("[options]"), "set-(sink-input|source-output)-mute", _("#N 1|0|toggle"));
2680 printf("%s %s %s %s\n", argv0, _("[options]"), "set-sink-formats", _("#N FORMATS"));
2681 printf("%s %s %s %s\n", argv0, _("[options]"), "set-port-latency-offset", _("CARD-NAME|CARD-#N PORT OFFSET"));
2682 printf("%s %s %s %s\n", argv0, _("[options]"), "send-message", _("RECIPIENT MESSAGE [MESSAGE_PARAMETERS]"));
2683 printf("%s %s %s\n", argv0, _("[options]"), "subscribe");
2684 printf(_("\nThe special names @DEFAULT_SINK@, @DEFAULT_SOURCE@ and @DEFAULT_MONITOR@\n"
2685 "can be used to specify the default sink, source and monitor.\n"));
2686
2687 printf(_("\n"
2688 " -h, --help Show this help\n"
2689 " --version Show version\n\n"
2690 " -f, --format=FORMAT The format of the output. Either \"normal\" or \"json\"\n"
2691 " -s, --server=SERVER The name of the server to connect to\n"
2692 " -n, --client-name=NAME How to call this client on the server\n"));
2693 }
2694
2695 enum {
2696 ARG_VERSION = 256
2697 };
2698
main(int argc,char * argv[])2699 int main(int argc, char *argv[]) {
2700 pa_mainloop *m = NULL;
2701 int ret = 1, c;
2702 char *server = NULL, *opt_format = NULL, *bn;
2703
2704 static const struct option long_options[] = {
2705 {"server", 1, NULL, 's'},
2706 {"client-name", 1, NULL, 'n'},
2707 {"format", 1, NULL, 'f'},
2708 {"version", 0, NULL, ARG_VERSION},
2709 {"help", 0, NULL, 'h'},
2710 {NULL, 0, NULL, 0}
2711 };
2712
2713 setlocale(LC_ALL, "");
2714 #ifdef ENABLE_NLS
2715 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
2716 #endif
2717
2718 bn = pa_path_get_filename(argv[0]);
2719
2720 proplist = pa_proplist_new();
2721
2722 while ((c = getopt_long(argc, argv, "+s:n:f:h", long_options, NULL)) != -1) {
2723 switch (c) {
2724 case 'h' :
2725 help(bn);
2726 ret = 0;
2727 goto quit;
2728
2729 case ARG_VERSION:
2730 printf(_("pactl %s\n"
2731 "Compiled with libpulse %s\n"
2732 "Linked with libpulse %s\n"),
2733 PACKAGE_VERSION,
2734 pa_get_headers_version(),
2735 pa_get_library_version());
2736 ret = 0;
2737 goto quit;
2738
2739 case 's':
2740 pa_xfree(server);
2741 server = pa_xstrdup(optarg);
2742 break;
2743
2744 case 'f':
2745 opt_format = pa_xstrdup(optarg);
2746 break;
2747
2748 case 'n': {
2749 char *t;
2750
2751 if (!(t = pa_locale_to_utf8(optarg)) ||
2752 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
2753
2754 pa_log(_("Invalid client name '%s'"), t ? t : optarg);
2755 pa_xfree(t);
2756 goto quit;
2757 }
2758
2759 pa_xfree(t);
2760 break;
2761 }
2762
2763 default:
2764 goto quit;
2765 }
2766 }
2767
2768 if (!opt_format || pa_streq(opt_format, "text")) {
2769 format = TEXT;
2770 } else if (pa_streq(opt_format, "json")) {
2771 format = JSON;
2772 setlocale(LC_NUMERIC, "C");
2773 } else {
2774 pa_log(_("Invalid format value '%s'"), opt_format);
2775 goto quit;
2776 }
2777
2778 if (optind < argc) {
2779 if (pa_streq(argv[optind], "stat")) {
2780 action = STAT;
2781
2782 } else if (pa_streq(argv[optind], "info"))
2783 action = INFO;
2784
2785 else if (pa_streq(argv[optind], "exit"))
2786 action = EXIT;
2787
2788 else if (pa_streq(argv[optind], "list")) {
2789 action = LIST;
2790
2791 for (int i = optind+1; i < argc; i++) {
2792 if (pa_streq(argv[i], "modules") || pa_streq(argv[i], "clients") ||
2793 pa_streq(argv[i], "sinks") || pa_streq(argv[i], "sink-inputs") ||
2794 pa_streq(argv[i], "sources") || pa_streq(argv[i], "source-outputs") ||
2795 pa_streq(argv[i], "samples") || pa_streq(argv[i], "cards") ||
2796 pa_streq(argv[i], "message-handlers")) {
2797 list_type = pa_xstrdup(argv[i]);
2798 } else if (pa_streq(argv[i], "short")) {
2799 short_list_format = true;
2800 } else {
2801 pa_log(_("Specify nothing, or one of: %s"), "modules, sinks, sources, sink-inputs, source-outputs, clients, samples, cards, message-handlers");
2802 goto quit;
2803 }
2804 }
2805
2806 } else if (pa_streq(argv[optind], "upload-sample")) {
2807 #ifdef SNDFILE_ENABLE
2808 struct SF_INFO sfi;
2809 action = UPLOAD_SAMPLE;
2810
2811 if (optind+1 >= argc) {
2812 pa_log(_("Please specify a sample file to load"));
2813 goto quit;
2814 }
2815
2816 if (optind+2 < argc)
2817 sample_name = pa_xstrdup(argv[optind+2]);
2818 else {
2819 char *f = pa_path_get_filename(argv[optind+1]);
2820 sample_name = pa_xstrndup(f, strcspn(f, "."));
2821 }
2822
2823 pa_zero(sfi);
2824 if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
2825 pa_log(_("Failed to open sound file."));
2826 goto quit;
2827 }
2828
2829 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
2830 pa_log(_("Failed to determine sample specification from file."));
2831 goto quit;
2832 }
2833 sample_spec.format = PA_SAMPLE_FLOAT32;
2834
2835 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
2836 if (sample_spec.channels > 2)
2837 pa_log(_("Warning: Failed to determine sample specification from file."));
2838 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
2839 }
2840
2841 pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
2842 sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
2843 #endif
2844 } else if (pa_streq(argv[optind], "play-sample")) {
2845 action = PLAY_SAMPLE;
2846 if (argc != optind+2 && argc != optind+3) {
2847 pa_log(_("You have to specify a sample name to play"));
2848 goto quit;
2849 }
2850
2851 sample_name = pa_xstrdup(argv[optind+1]);
2852
2853 if (optind+2 < argc)
2854 sink_name = pa_xstrdup(argv[optind+2]);
2855
2856 } else if (pa_streq(argv[optind], "remove-sample")) {
2857 action = REMOVE_SAMPLE;
2858 if (argc != optind+2) {
2859 pa_log(_("You have to specify a sample name to remove"));
2860 goto quit;
2861 }
2862
2863 sample_name = pa_xstrdup(argv[optind+1]);
2864
2865 } else if (pa_streq(argv[optind], "move-sink-input")) {
2866 action = MOVE_SINK_INPUT;
2867 if (argc != optind+3) {
2868 pa_log(_("You have to specify a sink input index and a sink"));
2869 goto quit;
2870 }
2871
2872 sink_input_idx = (uint32_t) atoi(argv[optind+1]);
2873 sink_name = pa_xstrdup(argv[optind+2]);
2874
2875 } else if (pa_streq(argv[optind], "move-source-output")) {
2876 action = MOVE_SOURCE_OUTPUT;
2877 if (argc != optind+3) {
2878 pa_log(_("You have to specify a source output index and a source"));
2879 goto quit;
2880 }
2881
2882 source_output_idx = (uint32_t) atoi(argv[optind+1]);
2883 source_name = pa_xstrdup(argv[optind+2]);
2884
2885 } else if (pa_streq(argv[optind], "load-module")) {
2886 int i;
2887 size_t n = 0;
2888 char *p;
2889
2890 action = LOAD_MODULE;
2891
2892 if (argc <= optind+1) {
2893 pa_log(_("You have to specify a module name and arguments."));
2894 goto quit;
2895 }
2896
2897 module_name = argv[optind+1];
2898
2899 for (i = optind+2; i < argc; i++)
2900 n += strlen(argv[i])+1;
2901
2902 if (n > 0) {
2903 p = module_args = pa_xmalloc(n);
2904
2905 for (i = optind+2; i < argc; i++)
2906 p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
2907 }
2908
2909 } else if (pa_streq(argv[optind], "unload-module")) {
2910 action = UNLOAD_MODULE;
2911
2912 if (argc != optind+2) {
2913 pa_log(_("You have to specify a module index or name"));
2914 goto quit;
2915 }
2916
2917 if (pa_atou(argv[optind + 1], &module_index) < 0)
2918 module_name = argv[optind + 1];
2919
2920 } else if (pa_streq(argv[optind], "suspend-sink")) {
2921 int b;
2922
2923 action = SUSPEND_SINK;
2924
2925 if (argc > optind+3 || optind+1 >= argc) {
2926 pa_log(_("You may not specify more than one sink. You have to specify a boolean value."));
2927 goto quit;
2928 }
2929
2930 if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
2931 pa_log(_("Invalid suspend specification."));
2932 goto quit;
2933 }
2934
2935 suspend = !!b;
2936
2937 if (argc > optind+2)
2938 sink_name = pa_xstrdup(argv[optind+1]);
2939
2940 } else if (pa_streq(argv[optind], "suspend-source")) {
2941 int b;
2942
2943 action = SUSPEND_SOURCE;
2944
2945 if (argc > optind+3 || optind+1 >= argc) {
2946 pa_log(_("You may not specify more than one source. You have to specify a boolean value."));
2947 goto quit;
2948 }
2949
2950 if ((b = pa_parse_boolean(argv[argc-1])) < 0) {
2951 pa_log(_("Invalid suspend specification."));
2952 goto quit;
2953 }
2954
2955 suspend = !!b;
2956
2957 if (argc > optind+2)
2958 source_name = pa_xstrdup(argv[optind+1]);
2959 } else if (pa_streq(argv[optind], "set-card-profile")) {
2960 action = SET_CARD_PROFILE;
2961
2962 if (argc != optind+3) {
2963 pa_log(_("You have to specify a card name/index and a profile name"));
2964 goto quit;
2965 }
2966
2967 card_name = pa_xstrdup(argv[optind+1]);
2968 profile_name = pa_xstrdup(argv[optind+2]);
2969
2970 } else if (pa_streq(argv[optind], "set-sink-port")) {
2971 action = SET_SINK_PORT;
2972
2973 if (argc != optind+3) {
2974 pa_log(_("You have to specify a sink name/index and a port name"));
2975 goto quit;
2976 }
2977
2978 sink_name = pa_xstrdup(argv[optind+1]);
2979 port_name = pa_xstrdup(argv[optind+2]);
2980
2981 } else if (pa_streq(argv[optind], "set-default-sink")) {
2982 action = SET_DEFAULT_SINK;
2983
2984 if (argc != optind+2) {
2985 pa_log(_("You have to specify a sink name"));
2986 goto quit;
2987 }
2988
2989 sink_name = pa_xstrdup(argv[optind+1]);
2990
2991 } else if (pa_streq(argv[optind], "get-default-sink")) {
2992 action = GET_DEFAULT_SINK;
2993
2994 } else if (pa_streq(argv[optind], "set-source-port")) {
2995 action = SET_SOURCE_PORT;
2996
2997 if (argc != optind+3) {
2998 pa_log(_("You have to specify a source name/index and a port name"));
2999 goto quit;
3000 }
3001
3002 source_name = pa_xstrdup(argv[optind+1]);
3003 port_name = pa_xstrdup(argv[optind+2]);
3004
3005 } else if (pa_streq(argv[optind], "set-default-source")) {
3006 action = SET_DEFAULT_SOURCE;
3007
3008 if (argc != optind+2) {
3009 pa_log(_("You have to specify a source name"));
3010 goto quit;
3011 }
3012
3013 source_name = pa_xstrdup(argv[optind+1]);
3014
3015 } else if (pa_streq(argv[optind], "get-default-source")) {
3016 action = GET_DEFAULT_SOURCE;
3017
3018 } else if (pa_streq(argv[optind], "get-sink-volume")) {
3019 action = GET_SINK_VOLUME;
3020
3021 if (argc < optind+2) {
3022 pa_log(_("You have to specify a sink name/index"));
3023 goto quit;
3024 }
3025
3026 sink_name = pa_xstrdup(argv[optind+1]);
3027
3028 } else if (pa_streq(argv[optind], "set-sink-volume")) {
3029 action = SET_SINK_VOLUME;
3030
3031 if (argc < optind+3) {
3032 pa_log(_("You have to specify a sink name/index and a volume"));
3033 goto quit;
3034 }
3035
3036 sink_name = pa_xstrdup(argv[optind+1]);
3037
3038 if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3039 goto quit;
3040
3041 } else if (pa_streq(argv[optind], "get-source-volume")) {
3042 action = GET_SOURCE_VOLUME;
3043
3044 if (argc < optind+2) {
3045 pa_log(_("You have to specify a source name/index"));
3046 goto quit;
3047 }
3048
3049 source_name = pa_xstrdup(argv[optind+1]);
3050
3051 } else if (pa_streq(argv[optind], "set-source-volume")) {
3052 action = SET_SOURCE_VOLUME;
3053
3054 if (argc < optind+3) {
3055 pa_log(_("You have to specify a source name/index and a volume"));
3056 goto quit;
3057 }
3058
3059 source_name = pa_xstrdup(argv[optind+1]);
3060
3061 if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3062 goto quit;
3063
3064 } else if (pa_streq(argv[optind], "set-sink-input-volume")) {
3065 action = SET_SINK_INPUT_VOLUME;
3066
3067 if (argc < optind+3) {
3068 pa_log(_("You have to specify a sink input index and a volume"));
3069 goto quit;
3070 }
3071
3072 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
3073 pa_log(_("Invalid sink input index"));
3074 goto quit;
3075 }
3076
3077 if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3078 goto quit;
3079
3080 } else if (pa_streq(argv[optind], "set-source-output-volume")) {
3081 action = SET_SOURCE_OUTPUT_VOLUME;
3082
3083 if (argc < optind+3) {
3084 pa_log(_("You have to specify a source output index and a volume"));
3085 goto quit;
3086 }
3087
3088 if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
3089 pa_log(_("Invalid source output index"));
3090 goto quit;
3091 }
3092
3093 if (parse_volumes(argv+optind+2, argc-(optind+2)) < 0)
3094 goto quit;
3095
3096 } else if (pa_streq(argv[optind], "get-sink-mute")) {
3097 action = GET_SINK_MUTE;
3098
3099 if (argc < optind+2) {
3100 pa_log(_("You have to specify a sink name/index"));
3101 goto quit;
3102 }
3103
3104 sink_name = pa_xstrdup(argv[optind+1]);
3105
3106 } else if (pa_streq(argv[optind], "set-sink-mute")) {
3107 action = SET_SINK_MUTE;
3108
3109 if (argc != optind+3) {
3110 pa_log(_("You have to specify a sink name/index and a mute action (0, 1, or 'toggle')"));
3111 goto quit;
3112 }
3113
3114 if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3115 pa_log(_("Invalid mute specification"));
3116 goto quit;
3117 }
3118
3119 sink_name = pa_xstrdup(argv[optind+1]);
3120
3121 } else if (pa_streq(argv[optind], "get-source-mute")) {
3122 action = GET_SOURCE_MUTE;
3123
3124 if (argc < optind+2) {
3125 pa_log(_("You have to specify a source name/index"));
3126 goto quit;
3127 }
3128
3129 source_name = pa_xstrdup(argv[optind+1]);
3130
3131 } else if (pa_streq(argv[optind], "set-source-mute")) {
3132 action = SET_SOURCE_MUTE;
3133
3134 if (argc != optind+3) {
3135 pa_log(_("You have to specify a source name/index and a mute action (0, 1, or 'toggle')"));
3136 goto quit;
3137 }
3138
3139 if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3140 pa_log(_("Invalid mute specification"));
3141 goto quit;
3142 }
3143
3144 source_name = pa_xstrdup(argv[optind+1]);
3145
3146 } else if (pa_streq(argv[optind], "set-sink-input-mute")) {
3147 action = SET_SINK_INPUT_MUTE;
3148
3149 if (argc != optind+3) {
3150 pa_log(_("You have to specify a sink input index and a mute action (0, 1, or 'toggle')"));
3151 goto quit;
3152 }
3153
3154 if (pa_atou(argv[optind+1], &sink_input_idx) < 0) {
3155 pa_log(_("Invalid sink input index specification"));
3156 goto quit;
3157 }
3158
3159 if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3160 pa_log(_("Invalid mute specification"));
3161 goto quit;
3162 }
3163
3164 } else if (pa_streq(argv[optind], "set-source-output-mute")) {
3165 action = SET_SOURCE_OUTPUT_MUTE;
3166
3167 if (argc != optind+3) {
3168 pa_log(_("You have to specify a source output index and a mute action (0, 1, or 'toggle')"));
3169 goto quit;
3170 }
3171
3172 if (pa_atou(argv[optind+1], &source_output_idx) < 0) {
3173 pa_log(_("Invalid source output index specification"));
3174 goto quit;
3175 }
3176
3177 if ((mute = parse_mute(argv[optind+2])) == INVALID_MUTE) {
3178 pa_log(_("Invalid mute specification"));
3179 goto quit;
3180 }
3181
3182 } else if (pa_streq(argv[optind], "send-message")) {
3183 action = SEND_MESSAGE;
3184
3185 if (argc < optind+3) {
3186 pa_log(_("You have to specify at least an object path and a message name"));
3187 goto quit;
3188 }
3189
3190 object_path = pa_xstrdup(argv[optind + 1]);
3191 message = pa_xstrdup(argv[optind + 2]);
3192 if (argc >= optind+4)
3193 message_args = pa_xstrdup(argv[optind + 3]);
3194
3195 if (argc > optind+4)
3196 pa_log(_("Excess arguments given, they will be ignored. Note that all message parameters must be given as a single string."));
3197
3198 } else if (pa_streq(argv[optind], "subscribe"))
3199
3200 action = SUBSCRIBE;
3201
3202 else if (pa_streq(argv[optind], "set-sink-formats")) {
3203 int32_t tmp;
3204
3205 if (argc != optind+3 || pa_atoi(argv[optind+1], &tmp) < 0) {
3206 pa_log(_("You have to specify a sink index and a semicolon-separated list of supported formats"));
3207 goto quit;
3208 }
3209
3210 sink_idx = tmp;
3211 action = SET_SINK_FORMATS;
3212 formats = pa_xstrdup(argv[optind+2]);
3213
3214 } else if (pa_streq(argv[optind], "set-port-latency-offset")) {
3215 action = SET_PORT_LATENCY_OFFSET;
3216
3217 if (argc != optind+4) {
3218 pa_log(_("You have to specify a card name/index, a port name and a latency offset"));
3219 goto quit;
3220 }
3221
3222 card_name = pa_xstrdup(argv[optind+1]);
3223 port_name = pa_xstrdup(argv[optind+2]);
3224 if (pa_atoi(argv[optind + 3], &latency_offset) < 0) {
3225 pa_log(_("Could not parse latency offset"));
3226 goto quit;
3227 }
3228
3229 } else if (pa_streq(argv[optind], "help")) {
3230 help(bn);
3231 ret = 0;
3232 goto quit;
3233 }
3234 }
3235
3236 if (action == NONE) {
3237 pa_log(_("No valid command specified."));
3238 goto quit;
3239 }
3240
3241 if (!(m = pa_mainloop_new())) {
3242 pa_log(_("pa_mainloop_new() failed."));
3243 goto quit;
3244 }
3245
3246 mainloop_api = pa_mainloop_get_api(m);
3247
3248 pa_assert_se(pa_signal_init(mainloop_api) == 0);
3249 pa_signal_new(SIGINT, exit_signal_callback, NULL);
3250 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
3251 pa_disable_sigpipe();
3252
3253 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
3254 pa_log(_("pa_context_new() failed."));
3255 goto quit;
3256 }
3257
3258 pa_context_set_state_callback(context, context_state_callback, NULL);
3259 if (pa_context_connect(context, server, 0, NULL) < 0) {
3260 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
3261 goto quit;
3262 }
3263
3264 if (pa_mainloop_run(m, &ret) < 0) {
3265 pa_log(_("pa_mainloop_run() failed."));
3266 goto quit;
3267 }
3268
3269 if (format == JSON && list_encoder && !pa_json_encoder_is_empty(list_encoder)) {
3270 pa_json_encoder_end_object(list_encoder);
3271 char* list_json_str = pa_json_encoder_to_string_free(list_encoder);
3272 printf("%s", list_json_str);
3273 pa_xfree(list_json_str);
3274 }
3275
3276 quit:
3277 #ifdef SNDFILE_ENABLE
3278 if (sample_stream)
3279 pa_stream_unref(sample_stream);
3280 #endif
3281 if (context)
3282 pa_context_unref(context);
3283
3284 if (m) {
3285 pa_signal_done();
3286 pa_mainloop_free(m);
3287 }
3288
3289 pa_xfree(server);
3290 pa_xfree(list_type);
3291 pa_xfree(sample_name);
3292 pa_xfree(sink_name);
3293 pa_xfree(source_name);
3294 pa_xfree(module_args);
3295 pa_xfree(card_name);
3296 pa_xfree(profile_name);
3297 pa_xfree(port_name);
3298 pa_xfree(formats);
3299 pa_xfree(object_path);
3300 pa_xfree(message);
3301 pa_xfree(message_args);
3302 #ifdef SNDFILE_ENABLE
3303 if (sndfile)
3304 sf_close(sndfile);
3305 #endif
3306 if (proplist)
3307 pa_proplist_free(proplist);
3308
3309 return ret;
3310 }
3311