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