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