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