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