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