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