]> code.delx.au - pulseaudio/blob - src/utils/pactl.c
pactl: implement pactl set-{sink|source}-port
[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 #define BUFSIZE (16*1024)
48
49 static pa_context *context = NULL;
50 static pa_mainloop_api *mainloop_api = NULL;
51
52 static char
53 *device = NULL,
54 *sample_name = NULL,
55 *sink_name = NULL,
56 *source_name = NULL,
57 *module_name = NULL,
58 *module_args = NULL,
59 *card_name = NULL,
60 *profile_name = NULL,
61 *port_name = NULL;
62
63 static uint32_t
64 sink_input_idx = PA_INVALID_INDEX,
65 source_output_idx = PA_INVALID_INDEX;
66
67 static uint32_t module_index;
68 static pa_bool_t suspend;
69
70 static pa_proplist *proplist = NULL;
71
72 static SNDFILE *sndfile = NULL;
73 static pa_stream *sample_stream = NULL;
74 static pa_sample_spec sample_spec;
75 static pa_channel_map channel_map;
76 static size_t sample_length = 0;
77
78 static int actions = 1;
79
80 static pa_bool_t nl = FALSE;
81
82 static enum {
83 NONE,
84 EXIT,
85 STAT,
86 UPLOAD_SAMPLE,
87 PLAY_SAMPLE,
88 REMOVE_SAMPLE,
89 LIST,
90 MOVE_SINK_INPUT,
91 MOVE_SOURCE_OUTPUT,
92 LOAD_MODULE,
93 UNLOAD_MODULE,
94 SUSPEND_SINK,
95 SUSPEND_SOURCE,
96 SET_CARD_PROFILE,
97 SET_SINK_PORT,
98 SET_SOURCE_PORT
99 } action = NONE;
100
101 static void quit(int ret) {
102 pa_assert(mainloop_api);
103 mainloop_api->quit(mainloop_api, ret);
104 }
105
106 static void context_drain_complete(pa_context *c, void *userdata) {
107 pa_context_disconnect(c);
108 }
109
110 static void drain(void) {
111 pa_operation *o;
112 if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
113 pa_context_disconnect(context);
114 else
115 pa_operation_unref(o);
116 }
117
118 static void complete_action(void) {
119 pa_assert(actions > 0);
120
121 if (!(--actions))
122 drain();
123 }
124
125 static void stat_callback(pa_context *c, const pa_stat_info *i, void *userdata) {
126 char s[128];
127 if (!i) {
128 pa_log(_("Failed to get statistics: %s\n"), pa_strerror(pa_context_errno(c)));
129 quit(1);
130 return;
131 }
132
133 pa_bytes_snprint(s, sizeof(s), i->memblock_total_size);
134 printf(_("Currently in use: %u blocks containing %s bytes total.\n"), i->memblock_total, s);
135
136 pa_bytes_snprint(s, sizeof(s), i->memblock_allocated_size);
137 printf(_("Allocated during whole lifetime: %u blocks containing %s bytes total.\n"), i->memblock_allocated, s);
138
139 pa_bytes_snprint(s, sizeof(s), i->scache_size);
140 printf(_("Sample cache size: %s\n"), s);
141
142 complete_action();
143 }
144
145 static void get_server_info_callback(pa_context *c, const pa_server_info *i, void *useerdata) {
146 char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
147
148 if (!i) {
149 pa_log(_("Failed to get server information: %s\n"), pa_strerror(pa_context_errno(c)));
150 quit(1);
151 return;
152 }
153
154 pa_sample_spec_snprint(ss, sizeof(ss), &i->sample_spec);
155 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map);
156
157 printf(_("User name: %s\n"
158 "Host Name: %s\n"
159 "Server Name: %s\n"
160 "Server Version: %s\n"
161 "Default Sample Specification: %s\n"
162 "Default Channel Map: %s\n"
163 "Default Sink: %s\n"
164 "Default Source: %s\n"
165 "Cookie: %08x\n"),
166 i->user_name,
167 i->host_name,
168 i->server_name,
169 i->server_version,
170 ss,
171 cm,
172 i->default_sink_name,
173 i->default_source_name,
174 i->cookie);
175
176 complete_action();
177 }
178
179 static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
180
181 static const char *state_table[] = {
182 [1+PA_SINK_INVALID_STATE] = "n/a",
183 [1+PA_SINK_RUNNING] = "RUNNING",
184 [1+PA_SINK_IDLE] = "IDLE",
185 [1+PA_SINK_SUSPENDED] = "SUSPENDED"
186 };
187
188 char
189 s[PA_SAMPLE_SPEC_SNPRINT_MAX],
190 cv[PA_CVOLUME_SNPRINT_MAX],
191 cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
192 v[PA_VOLUME_SNPRINT_MAX],
193 vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
194 cm[PA_CHANNEL_MAP_SNPRINT_MAX];
195 char *pl;
196
197 if (is_last < 0) {
198 pa_log(_("Failed to get sink information: %s\n"), pa_strerror(pa_context_errno(c)));
199 quit(1);
200 return;
201 }
202
203 if (is_last) {
204 complete_action();
205 return;
206 }
207
208 pa_assert(i);
209
210 if (nl)
211 printf("\n");
212 nl = TRUE;
213
214 printf(_("Sink #%u\n"
215 "\tState: %s\n"
216 "\tName: %s\n"
217 "\tDescription: %s\n"
218 "\tDriver: %s\n"
219 "\tSample Specification: %s\n"
220 "\tChannel Map: %s\n"
221 "\tOwner Module: %u\n"
222 "\tMute: %s\n"
223 "\tVolume: %s%s%s\n"
224 "\t balance %0.2f\n"
225 "\tBase Volume: %s%s%s\n"
226 "\tMonitor Source: %s\n"
227 "\tLatency: %0.0f usec, configured %0.0f usec\n"
228 "\tFlags: %s%s%s%s%s%s\n"
229 "\tProperties:\n\t\t%s\n"),
230 i->index,
231 state_table[1+i->state],
232 i->name,
233 pa_strnull(i->description),
234 pa_strnull(i->driver),
235 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
236 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
237 i->owner_module,
238 pa_yes_no(i->mute),
239 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
240 i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
241 i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
242 pa_cvolume_get_balance(&i->volume, &i->channel_map),
243 pa_volume_snprint(v, sizeof(v), i->base_volume),
244 i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "",
245 i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
246 pa_strnull(i->monitor_source_name),
247 (double) i->latency, (double) i->configured_latency,
248 i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "",
249 i->flags & PA_SINK_NETWORK ? "NETWORK " : "",
250 i->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
251 i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
252 i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
253 i->flags & PA_SINK_LATENCY ? "LATENCY " : "",
254 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
255
256 pa_xfree(pl);
257 }
258
259 static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
260
261 static const char *state_table[] = {
262 [1+PA_SOURCE_INVALID_STATE] = "n/a",
263 [1+PA_SOURCE_RUNNING] = "RUNNING",
264 [1+PA_SOURCE_IDLE] = "IDLE",
265 [1+PA_SOURCE_SUSPENDED] = "SUSPENDED"
266 };
267
268 char
269 s[PA_SAMPLE_SPEC_SNPRINT_MAX],
270 cv[PA_CVOLUME_SNPRINT_MAX],
271 cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX],
272 v[PA_VOLUME_SNPRINT_MAX],
273 vdb[PA_SW_VOLUME_SNPRINT_DB_MAX],
274 cm[PA_CHANNEL_MAP_SNPRINT_MAX];
275 char *pl;
276
277 if (is_last < 0) {
278 pa_log(_("Failed to get source information: %s\n"), pa_strerror(pa_context_errno(c)));
279 quit(1);
280 return;
281 }
282
283 if (is_last) {
284 complete_action();
285 return;
286 }
287
288 pa_assert(i);
289
290 if (nl)
291 printf("\n");
292 nl = TRUE;
293
294 printf(_("Source #%u\n"
295 "\tState: %s\n"
296 "\tName: %s\n"
297 "\tDescription: %s\n"
298 "\tDriver: %s\n"
299 "\tSample Specification: %s\n"
300 "\tChannel Map: %s\n"
301 "\tOwner Module: %u\n"
302 "\tMute: %s\n"
303 "\tVolume: %s%s%s\n"
304 "\t balance %0.2f\n"
305 "\tBase Volume: %s%s%s\n"
306 "\tMonitor of Sink: %s\n"
307 "\tLatency: %0.0f usec, configured %0.0f usec\n"
308 "\tFlags: %s%s%s%s%s%s\n"
309 "\tProperties:\n\t\t%s\n"),
310 i->index,
311 state_table[1+i->state],
312 i->name,
313 pa_strnull(i->description),
314 pa_strnull(i->driver),
315 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
316 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
317 i->owner_module,
318 pa_yes_no(i->mute),
319 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
320 i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
321 i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "",
322 pa_cvolume_get_balance(&i->volume, &i->channel_map),
323 pa_volume_snprint(v, sizeof(v), i->base_volume),
324 i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "",
325 i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "",
326 i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"),
327 (double) i->latency, (double) i->configured_latency,
328 i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "",
329 i->flags & PA_SOURCE_NETWORK ? "NETWORK " : "",
330 i->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "",
331 i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "",
332 i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "",
333 i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "",
334 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
335
336 pa_xfree(pl);
337 }
338
339 static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) {
340 char t[32];
341 char *pl;
342
343 if (is_last < 0) {
344 pa_log(_("Failed to get module information: %s\n"), pa_strerror(pa_context_errno(c)));
345 quit(1);
346 return;
347 }
348
349 if (is_last) {
350 complete_action();
351 return;
352 }
353
354 pa_assert(i);
355
356 if (nl)
357 printf("\n");
358 nl = TRUE;
359
360 pa_snprintf(t, sizeof(t), "%u", i->n_used);
361
362 printf(_("Module #%u\n"
363 "\tName: %s\n"
364 "\tArgument: %s\n"
365 "\tUsage counter: %s\n"
366 "\tProperties:\n\t\t%s\n"),
367 i->index,
368 i->name,
369 i->argument ? i->argument : "",
370 i->n_used != PA_INVALID_INDEX ? t : _("n/a"),
371 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
372
373 pa_xfree(pl);
374 }
375
376 static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) {
377 char t[32];
378 char *pl;
379
380 if (is_last < 0) {
381 pa_log(_("Failed to get client information: %s\n"), pa_strerror(pa_context_errno(c)));
382 quit(1);
383 return;
384 }
385
386 if (is_last) {
387 complete_action();
388 return;
389 }
390
391 pa_assert(i);
392
393 if (nl)
394 printf("\n");
395 nl = TRUE;
396
397 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
398
399 printf(_("Client #%u\n"
400 "\tDriver: %s\n"
401 "\tOwner Module: %s\n"
402 "\tProperties:\n\t\t%s\n"),
403 i->index,
404 pa_strnull(i->driver),
405 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
406 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
407
408 pa_xfree(pl);
409 }
410
411 static void get_card_info_callback(pa_context *c, const pa_card_info *i, int is_last, void *userdata) {
412 char t[32];
413 char *pl;
414
415 if (is_last < 0) {
416 pa_log(_("Failed to get card information: %s\n"), pa_strerror(pa_context_errno(c)));
417 complete_action();
418 return;
419 }
420
421 if (is_last) {
422 complete_action();
423 return;
424 }
425
426 pa_assert(i);
427
428 if (nl)
429 printf("\n");
430 nl = TRUE;
431
432 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
433
434 printf(_("Card #%u\n"
435 "\tName: %s\n"
436 "\tDriver: %s\n"
437 "\tOwner Module: %s\n"
438 "\tProperties:\n\t\t%s\n"),
439 i->index,
440 i->name,
441 pa_strnull(i->driver),
442 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
443 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
444
445 if (i->profiles) {
446 pa_card_profile_info *p;
447
448 printf(_("\tProfiles:\n"));
449 for (p = i->profiles; p->name; p++)
450 printf("\t\t%s: %s (sinks: %u, sources: %u, priority. %u)\n", p->name, p->description, p->n_sinks, p->n_sources, p->priority);
451 }
452
453 if (i->active_profile)
454 printf(_("\tActive Profile: %s\n"),
455 i->active_profile->name);
456
457 pa_xfree(pl);
458 }
459
460 static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
461 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];
462 char *pl;
463
464 if (is_last < 0) {
465 pa_log(_("Failed to get sink input information: %s\n"), pa_strerror(pa_context_errno(c)));
466 quit(1);
467 return;
468 }
469
470 if (is_last) {
471 complete_action();
472 return;
473 }
474
475 pa_assert(i);
476
477 if (nl)
478 printf("\n");
479 nl = TRUE;
480
481 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
482 pa_snprintf(k, sizeof(k), "%u", i->client);
483
484 printf(_("Sink Input #%u\n"
485 "\tDriver: %s\n"
486 "\tOwner Module: %s\n"
487 "\tClient: %s\n"
488 "\tSink: %u\n"
489 "\tSample Specification: %s\n"
490 "\tChannel Map: %s\n"
491 "\tMute: %s\n"
492 "\tVolume: %s\n"
493 "\t %s\n"
494 "\t balance %0.2f\n"
495 "\tBuffer Latency: %0.0f usec\n"
496 "\tSink Latency: %0.0f usec\n"
497 "\tResample method: %s\n"
498 "\tProperties:\n\t\t%s\n"),
499 i->index,
500 pa_strnull(i->driver),
501 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
502 i->client != PA_INVALID_INDEX ? k : _("n/a"),
503 i->sink,
504 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
505 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
506 pa_yes_no(i->mute),
507 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
508 pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
509 pa_cvolume_get_balance(&i->volume, &i->channel_map),
510 (double) i->buffer_usec,
511 (double) i->sink_usec,
512 i->resample_method ? i->resample_method : _("n/a"),
513 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
514
515 pa_xfree(pl);
516 }
517
518 static void get_source_output_info_callback(pa_context *c, const pa_source_output_info *i, int is_last, void *userdata) {
519 char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
520 char *pl;
521
522 if (is_last < 0) {
523 pa_log(_("Failed to get source output information: %s\n"), pa_strerror(pa_context_errno(c)));
524 quit(1);
525 return;
526 }
527
528 if (is_last) {
529 complete_action();
530 return;
531 }
532
533 pa_assert(i);
534
535 if (nl)
536 printf("\n");
537 nl = TRUE;
538
539
540 pa_snprintf(t, sizeof(t), "%u", i->owner_module);
541 pa_snprintf(k, sizeof(k), "%u", i->client);
542
543 printf(_("Source Output #%u\n"
544 "\tDriver: %s\n"
545 "\tOwner Module: %s\n"
546 "\tClient: %s\n"
547 "\tSource: %u\n"
548 "\tSample Specification: %s\n"
549 "\tChannel Map: %s\n"
550 "\tBuffer Latency: %0.0f usec\n"
551 "\tSource Latency: %0.0f usec\n"
552 "\tResample method: %s\n"
553 "\tProperties:\n\t\t%s\n"),
554 i->index,
555 pa_strnull(i->driver),
556 i->owner_module != PA_INVALID_INDEX ? t : _("n/a"),
557 i->client != PA_INVALID_INDEX ? k : _("n/a"),
558 i->source,
559 pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec),
560 pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map),
561 (double) i->buffer_usec,
562 (double) i->source_usec,
563 i->resample_method ? i->resample_method : _("n/a"),
564 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
565
566 pa_xfree(pl);
567 }
568
569 static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) {
570 char t[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];
571 char *pl;
572
573 if (is_last < 0) {
574 pa_log(_("Failed to get sample information: %s\n"), pa_strerror(pa_context_errno(c)));
575 quit(1);
576 return;
577 }
578
579 if (is_last) {
580 complete_action();
581 return;
582 }
583
584 pa_assert(i);
585
586 if (nl)
587 printf("\n");
588 nl = TRUE;
589
590 pa_bytes_snprint(t, sizeof(t), i->bytes);
591
592 printf(_("Sample #%u\n"
593 "\tName: %s\n"
594 "\tSample Specification: %s\n"
595 "\tChannel Map: %s\n"
596 "\tVolume: %s\n"
597 "\t %s\n"
598 "\t balance %0.2f\n"
599 "\tDuration: %0.1fs\n"
600 "\tSize: %s\n"
601 "\tLazy: %s\n"
602 "\tFilename: %s\n"
603 "\tProperties:\n\t\t%s\n"),
604 i->index,
605 i->name,
606 pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"),
607 pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"),
608 pa_cvolume_snprint(cv, sizeof(cv), &i->volume),
609 pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume),
610 pa_cvolume_get_balance(&i->volume, &i->channel_map),
611 (double) i->duration/1000000.0,
612 t,
613 pa_yes_no(i->lazy),
614 i->filename ? i->filename : _("n/a"),
615 pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t"));
616
617 pa_xfree(pl);
618 }
619
620 static void simple_callback(pa_context *c, int success, void *userdata) {
621 if (!success) {
622 pa_log(_("Failure: %s\n"), pa_strerror(pa_context_errno(c)));
623 quit(1);
624 return;
625 }
626
627 complete_action();
628 }
629
630 static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
631 if (idx == PA_INVALID_INDEX) {
632 pa_log(_("Failure: %s\n"), pa_strerror(pa_context_errno(c)));
633 quit(1);
634 return;
635 }
636
637 printf("%u\n", idx);
638
639 complete_action();
640 }
641
642 static void stream_state_callback(pa_stream *s, void *userdata) {
643 pa_assert(s);
644
645 switch (pa_stream_get_state(s)) {
646 case PA_STREAM_CREATING:
647 case PA_STREAM_READY:
648 break;
649
650 case PA_STREAM_TERMINATED:
651 drain();
652 break;
653
654 case PA_STREAM_FAILED:
655 default:
656 pa_log(_("Failed to upload sample: %s\n"), pa_strerror(pa_context_errno(pa_stream_get_context(s))));
657 quit(1);
658 }
659 }
660
661 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
662 sf_count_t l;
663 float *d;
664 pa_assert(s && length && sndfile);
665
666 d = pa_xmalloc(length);
667
668 pa_assert(sample_length >= length);
669 l = (sf_count_t) (length/pa_frame_size(&sample_spec));
670
671 if ((sf_readf_float(sndfile, d, l)) != l) {
672 pa_xfree(d);
673 pa_log(_("Premature end of file\n"));
674 quit(1);
675 return;
676 }
677
678 pa_stream_write(s, d, length, pa_xfree, 0, PA_SEEK_RELATIVE);
679
680 sample_length -= length;
681
682 if (sample_length <= 0) {
683 pa_stream_set_write_callback(sample_stream, NULL, NULL);
684 pa_stream_finish_upload(sample_stream);
685 }
686 }
687
688 static void context_state_callback(pa_context *c, void *userdata) {
689 pa_assert(c);
690 switch (pa_context_get_state(c)) {
691 case PA_CONTEXT_CONNECTING:
692 case PA_CONTEXT_AUTHORIZING:
693 case PA_CONTEXT_SETTING_NAME:
694 break;
695
696 case PA_CONTEXT_READY:
697 switch (action) {
698 case STAT:
699 actions = 2;
700 pa_operation_unref(pa_context_stat(c, stat_callback, NULL));
701 pa_operation_unref(pa_context_get_server_info(c, get_server_info_callback, NULL));
702 break;
703
704 case PLAY_SAMPLE:
705 pa_operation_unref(pa_context_play_sample(c, sample_name, device, PA_VOLUME_NORM, simple_callback, NULL));
706 break;
707
708 case REMOVE_SAMPLE:
709 pa_operation_unref(pa_context_remove_sample(c, sample_name, simple_callback, NULL));
710 break;
711
712 case UPLOAD_SAMPLE:
713 sample_stream = pa_stream_new(c, sample_name, &sample_spec, NULL);
714 pa_assert(sample_stream);
715
716 pa_stream_set_state_callback(sample_stream, stream_state_callback, NULL);
717 pa_stream_set_write_callback(sample_stream, stream_write_callback, NULL);
718 pa_stream_connect_upload(sample_stream, sample_length);
719 break;
720
721 case EXIT:
722 pa_operation_unref(pa_context_exit_daemon(c, simple_callback, NULL));
723 break;
724
725 case LIST:
726 actions = 8;
727 pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL));
728 pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL));
729 pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL));
730 pa_operation_unref(pa_context_get_sink_input_info_list(c, get_sink_input_info_callback, NULL));
731 pa_operation_unref(pa_context_get_source_output_info_list(c, get_source_output_info_callback, NULL));
732 pa_operation_unref(pa_context_get_client_info_list(c, get_client_info_callback, NULL));
733 pa_operation_unref(pa_context_get_sample_info_list(c, get_sample_info_callback, NULL));
734 pa_operation_unref(pa_context_get_card_info_list(c, get_card_info_callback, NULL));
735 break;
736
737 case MOVE_SINK_INPUT:
738 pa_operation_unref(pa_context_move_sink_input_by_name(c, sink_input_idx, sink_name, simple_callback, NULL));
739 break;
740
741 case MOVE_SOURCE_OUTPUT:
742 pa_operation_unref(pa_context_move_source_output_by_name(c, source_output_idx, source_name, simple_callback, NULL));
743 break;
744
745 case LOAD_MODULE:
746 pa_operation_unref(pa_context_load_module(c, module_name, module_args, index_callback, NULL));
747 break;
748
749 case UNLOAD_MODULE:
750 pa_operation_unref(pa_context_unload_module(c, module_index, simple_callback, NULL));
751 break;
752
753 case SUSPEND_SINK:
754 if (sink_name)
755 pa_operation_unref(pa_context_suspend_sink_by_name(c, sink_name, suspend, simple_callback, NULL));
756 else
757 pa_operation_unref(pa_context_suspend_sink_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
758 break;
759
760 case SUSPEND_SOURCE:
761 if (source_name)
762 pa_operation_unref(pa_context_suspend_source_by_name(c, source_name, suspend, simple_callback, NULL));
763 else
764 pa_operation_unref(pa_context_suspend_source_by_index(c, PA_INVALID_INDEX, suspend, simple_callback, NULL));
765 break;
766
767 case SET_CARD_PROFILE:
768 pa_operation_unref(pa_context_set_card_profile_by_name(c, card_name, profile_name, simple_callback, NULL));
769 break;
770
771 case SET_SINK_PORT:
772 pa_operation_unref(pa_context_set_sink_port_by_name(c, sink_name, port_name, simple_callback, NULL));
773 break;
774
775 case SET_SOURCE_PORT:
776 pa_operation_unref(pa_context_set_source_port_by_name(c, source_name, port_name, simple_callback, NULL));
777 break;
778
779 default:
780 pa_assert_not_reached();
781 }
782 break;
783
784 case PA_CONTEXT_TERMINATED:
785 quit(0);
786 break;
787
788 case PA_CONTEXT_FAILED:
789 default:
790 pa_log(_("Connection failure: %s\n"), pa_strerror(pa_context_errno(c)));
791 quit(1);
792 }
793 }
794
795 static void exit_signal_callback(pa_mainloop_api *m, pa_signal_event *e, int sig, void *userdata) {
796 pa_log(_("Got SIGINT, exiting.\n"));
797 quit(0);
798 }
799
800 static void help(const char *argv0) {
801
802 printf(_("%s [options] stat\n"
803 "%s [options] list\n"
804 "%s [options] exit\n"
805 "%s [options] upload-sample FILENAME [NAME]\n"
806 "%s [options] play-sample NAME [SINK]\n"
807 "%s [options] remove-sample NAME\n"
808 "%s [options] move-sink-input ID SINK\n"
809 "%s [options] move-source-output ID SOURCE\n"
810 "%s [options] load-module NAME [ARGS ...]\n"
811 "%s [options] unload-module ID\n"
812 "%s [options] suspend-sink [SINK] 1|0\n"
813 "%s [options] suspend-source [SOURCE] 1|0\n"
814 "%s [options] set-card-profile [CARD] [PROFILE] \n"
815 "%s [options] set-sink-port [SINK] [PORT] \n"
816 "%s [options] set-source-port [SOURCE] [PORT] \n\n"
817 " -h, --help Show this help\n"
818 " --version Show version\n\n"
819 " -s, --server=SERVER The name of the server to connect to\n"
820 " -n, --client-name=NAME How to call this client on the server\n"),
821 argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0, argv0);
822 }
823
824 enum {
825 ARG_VERSION = 256
826 };
827
828 int main(int argc, char *argv[]) {
829 pa_mainloop* m = NULL;
830 int ret = 1, c;
831 char *server = NULL, *bn;
832
833 static const struct option long_options[] = {
834 {"server", 1, NULL, 's'},
835 {"client-name", 1, NULL, 'n'},
836 {"version", 0, NULL, ARG_VERSION},
837 {"help", 0, NULL, 'h'},
838 {NULL, 0, NULL, 0}
839 };
840
841 setlocale(LC_ALL, "");
842 bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
843
844 bn = pa_path_get_filename(argv[0]);
845
846 proplist = pa_proplist_new();
847
848 while ((c = getopt_long(argc, argv, "s:n:h", long_options, NULL)) != -1) {
849 switch (c) {
850 case 'h' :
851 help(bn);
852 ret = 0;
853 goto quit;
854
855 case ARG_VERSION:
856 printf(_("pactl %s\n"
857 "Compiled with libpulse %s\n"
858 "Linked with libpulse %s\n"),
859 PACKAGE_VERSION,
860 pa_get_headers_version(),
861 pa_get_library_version());
862 ret = 0;
863 goto quit;
864
865 case 's':
866 pa_xfree(server);
867 server = pa_xstrdup(optarg);
868 break;
869
870 case 'n': {
871 char *t;
872
873 if (!(t = pa_locale_to_utf8(optarg)) ||
874 pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, t) < 0) {
875
876 pa_log(_("Invalid client name '%s'\n"), t ? t : optarg);
877 pa_xfree(t);
878 goto quit;
879 }
880
881 pa_xfree(t);
882 break;
883 }
884
885 default:
886 goto quit;
887 }
888 }
889
890 if (optind < argc) {
891 if (pa_streq(argv[optind], "stat"))
892 action = STAT;
893 else if (pa_streq(argv[optind], "exit"))
894 action = EXIT;
895 else if (pa_streq(argv[optind], "list"))
896 action = LIST;
897 else if (pa_streq(argv[optind], "upload-sample")) {
898 struct SF_INFO sfi;
899 action = UPLOAD_SAMPLE;
900
901 if (optind+1 >= argc) {
902 pa_log(_("Please specify a sample file to load\n"));
903 goto quit;
904 }
905
906 if (optind+2 < argc)
907 sample_name = pa_xstrdup(argv[optind+2]);
908 else {
909 char *f = pa_path_get_filename(argv[optind+1]);
910 sample_name = pa_xstrndup(f, strcspn(f, "."));
911 }
912
913 pa_zero(sfi);
914 if (!(sndfile = sf_open(argv[optind+1], SFM_READ, &sfi))) {
915 pa_log(_("Failed to open sound file.\n"));
916 goto quit;
917 }
918
919 if (pa_sndfile_read_sample_spec(sndfile, &sample_spec) < 0) {
920 pa_log(_("Failed to determine sample specification from file.\n"));
921 goto quit;
922 }
923 sample_spec.format = PA_SAMPLE_FLOAT32;
924
925 if (pa_sndfile_read_channel_map(sndfile, &channel_map) < 0) {
926 if (sample_spec.channels > 2)
927 pa_log(_("Warning: Failed to determine sample specification from file.\n"));
928 pa_channel_map_init_extend(&channel_map, sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
929 }
930
931 pa_assert(pa_channel_map_compatible(&channel_map, &sample_spec));
932 sample_length = (size_t) sfi.frames*pa_frame_size(&sample_spec);
933
934 } else if (pa_streq(argv[optind], "play-sample")) {
935 action = PLAY_SAMPLE;
936 if (argc != optind+2 && argc != optind+3) {
937 pa_log(_("You have to specify a sample name to play\n"));
938 goto quit;
939 }
940
941 sample_name = pa_xstrdup(argv[optind+1]);
942
943 if (optind+2 < argc)
944 device = pa_xstrdup(argv[optind+2]);
945
946 } else if (pa_streq(argv[optind], "remove-sample")) {
947 action = REMOVE_SAMPLE;
948 if (argc != optind+2) {
949 pa_log(_("You have to specify a sample name to remove\n"));
950 goto quit;
951 }
952
953 sample_name = pa_xstrdup(argv[optind+1]);
954
955 } else if (pa_streq(argv[optind], "move-sink-input")) {
956 action = MOVE_SINK_INPUT;
957 if (argc != optind+3) {
958 pa_log(_("You have to specify a sink input index and a sink\n"));
959 goto quit;
960 }
961
962 sink_input_idx = (uint32_t) atoi(argv[optind+1]);
963 sink_name = pa_xstrdup(argv[optind+2]);
964
965 } else if (pa_streq(argv[optind], "move-source-output")) {
966 action = MOVE_SOURCE_OUTPUT;
967 if (argc != optind+3) {
968 pa_log(_("You have to specify a source output index and a source\n"));
969 goto quit;
970 }
971
972 source_output_idx = (uint32_t) atoi(argv[optind+1]);
973 source_name = pa_xstrdup(argv[optind+2]);
974
975 } else if (pa_streq(argv[optind], "load-module")) {
976 int i;
977 size_t n = 0;
978 char *p;
979
980 action = LOAD_MODULE;
981
982 if (argc <= optind+1) {
983 pa_log(_("You have to specify a module name and arguments.\n"));
984 goto quit;
985 }
986
987 module_name = argv[optind+1];
988
989 for (i = optind+2; i < argc; i++)
990 n += strlen(argv[i])+1;
991
992 if (n > 0) {
993 p = module_args = pa_xmalloc(n);
994
995 for (i = optind+2; i < argc; i++)
996 p += sprintf(p, "%s%s", p == module_args ? "" : " ", argv[i]);
997 }
998
999 } else if (pa_streq(argv[optind], "unload-module")) {
1000 action = UNLOAD_MODULE;
1001
1002 if (argc != optind+2) {
1003 pa_log(_("You have to specify a module index\n"));
1004 goto quit;
1005 }
1006
1007 module_index = (uint32_t) atoi(argv[optind+1]);
1008
1009 } else if (pa_streq(argv[optind], "suspend-sink")) {
1010 action = SUSPEND_SINK;
1011
1012 if (argc > optind+3 || optind+1 >= argc) {
1013 pa_log(_("You may not specify more than one sink. You have to specify a boolean value.\n"));
1014 goto quit;
1015 }
1016
1017 suspend = pa_parse_boolean(argv[argc-1]);
1018
1019 if (argc > optind+2)
1020 sink_name = pa_xstrdup(argv[optind+1]);
1021
1022 } else if (pa_streq(argv[optind], "suspend-source")) {
1023 action = SUSPEND_SOURCE;
1024
1025 if (argc > optind+3 || optind+1 >= argc) {
1026 pa_log(_("You may not specify more than one source. You have to specify a boolean value.\n"));
1027 goto quit;
1028 }
1029
1030 suspend = pa_parse_boolean(argv[argc-1]);
1031
1032 if (argc > optind+2)
1033 source_name = pa_xstrdup(argv[optind+1]);
1034 } else if (pa_streq(argv[optind], "set-card-profile")) {
1035 action = SET_CARD_PROFILE;
1036
1037 if (argc != optind+3) {
1038 pa_log(_("You have to specify a card name/index and a profile name\n"));
1039 goto quit;
1040 }
1041
1042 card_name = pa_xstrdup(argv[optind+1]);
1043 profile_name = pa_xstrdup(argv[optind+2]);
1044
1045 } else if (pa_streq(argv[optind], "set-sink-port")) {
1046 action = SET_SINK_PORT;
1047
1048 if (argc != optind+3) {
1049 pa_log(_("You have to specify a sink name/index and a port name\n"));
1050 goto quit;
1051 }
1052
1053 sink_name = pa_xstrdup(argv[optind+1]);
1054 port_name = pa_xstrdup(argv[optind+2]);
1055
1056 } else if (pa_streq(argv[optind], "set-source-port")) {
1057 action = SET_SOURCE_PORT;
1058
1059 if (argc != optind+3) {
1060 pa_log(_("You have to specify a source name/index and a port name\n"));
1061 goto quit;
1062 }
1063
1064 source_name = pa_xstrdup(argv[optind+1]);
1065 port_name = pa_xstrdup(argv[optind+2]);
1066
1067 } else if (pa_streq(argv[optind], "help")) {
1068 help(bn);
1069 ret = 0;
1070 goto quit;
1071 }
1072 }
1073
1074 if (action == NONE) {
1075 pa_log(_("No valid command specified.\n"));
1076 goto quit;
1077 }
1078
1079 if (!(m = pa_mainloop_new())) {
1080 pa_log(_("pa_mainloop_new() failed.\n"));
1081 goto quit;
1082 }
1083
1084 mainloop_api = pa_mainloop_get_api(m);
1085
1086 pa_assert_se(pa_signal_init(mainloop_api) == 0);
1087 pa_signal_new(SIGINT, exit_signal_callback, NULL);
1088 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
1089 pa_disable_sigpipe();
1090
1091 if (!(context = pa_context_new_with_proplist(mainloop_api, NULL, proplist))) {
1092 pa_log(_("pa_context_new() failed.\n"));
1093 goto quit;
1094 }
1095
1096 pa_context_set_state_callback(context, context_state_callback, NULL);
1097 if (pa_context_connect(context, server, 0, NULL) < 0) {
1098 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context)));
1099 goto quit;
1100 }
1101
1102 if (pa_mainloop_run(m, &ret) < 0) {
1103 pa_log(_("pa_mainloop_run() failed.\n"));
1104 goto quit;
1105 }
1106
1107 quit:
1108 if (sample_stream)
1109 pa_stream_unref(sample_stream);
1110
1111 if (context)
1112 pa_context_unref(context);
1113
1114 if (m) {
1115 pa_signal_done();
1116 pa_mainloop_free(m);
1117 }
1118
1119 pa_xfree(server);
1120 pa_xfree(device);
1121 pa_xfree(sample_name);
1122 pa_xfree(sink_name);
1123 pa_xfree(source_name);
1124 pa_xfree(module_args);
1125 pa_xfree(card_name);
1126 pa_xfree(profile_name);
1127
1128 if (sndfile)
1129 sf_close(sndfile);
1130
1131 if (proplist)
1132 pa_proplist_free(proplist);
1133
1134 return ret;
1135 }