]> code.delx.au - pulseaudio/blob - polyp/pacat.c
add simple hardware auto detection module
[pulseaudio] / polyp / pacat.c
1 /* $Id$ */
2
3 /***
4 This file is part of polypaudio.
5
6 polypaudio 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 polypaudio 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 polypaudio; 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 <getopt.h>
34
35 #include <polyp/polyplib.h>
36 #include <polyp/polyplib-error.h>
37 #include <polyp/mainloop.h>
38 #include <polyp/mainloop-signal.h>
39 #include <polyp/polyplib-version.h>
40
41 #if PA_API_VERSION != 8
42 #error Invalid Polypaudio API version
43 #endif
44
45 static enum { RECORD, PLAYBACK } mode = PLAYBACK;
46
47 static pa_context *context = NULL;
48 static pa_stream *stream = NULL;
49 static pa_mainloop_api *mainloop_api = NULL;
50
51 static void *buffer = NULL;
52 static size_t buffer_length = 0, buffer_index = 0;
53
54 static pa_io_event* stdio_event = NULL;
55
56 static char *stream_name = NULL, *client_name = NULL, *device = NULL;
57
58 static int verbose = 0;
59 static pa_volume_t volume = PA_VOLUME_NORM;
60
61 static pa_sample_spec sample_spec = {
62 .format = PA_SAMPLE_S16LE,
63 .rate = 44100,
64 .channels = 2
65 };
66
67 /* A shortcut for terminating the application */
68 static void quit(int ret) {
69 assert(mainloop_api);
70 mainloop_api->quit(mainloop_api, ret);
71 }
72
73 /* Write some data to the stream */
74 static void do_stream_write(size_t length) {
75 size_t l;
76 assert(length);
77
78 if (!buffer || !buffer_length)
79 return;
80
81 l = length;
82 if (l > buffer_length)
83 l = buffer_length;
84
85 pa_stream_write(stream, (uint8_t*) buffer + buffer_index, l, NULL, 0);
86 buffer_length -= l;
87 buffer_index += l;
88
89 if (!buffer_length) {
90 free(buffer);
91 buffer = NULL;
92 buffer_index = buffer_length = 0;
93 }
94 }
95
96 /* This is called whenever new data may be written to the stream */
97 static void stream_write_callback(pa_stream *s, size_t length, void *userdata) {
98 assert(s && length);
99
100 if (stdio_event)
101 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_INPUT);
102
103 if (!buffer)
104 return;
105
106 do_stream_write(length);
107 }
108
109 /* This is called whenever new data may is available */
110 static void stream_read_callback(pa_stream *s, const void*data, size_t length, void *userdata) {
111 assert(s && data && length);
112
113 if (stdio_event)
114 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_OUTPUT);
115
116 if (buffer) {
117 fprintf(stderr, "Buffer overrun, dropping incoming data\n");
118 return;
119 }
120
121 buffer = malloc(buffer_length = length);
122 assert(buffer);
123 memcpy(buffer, data, length);
124 buffer_index = 0;
125 }
126
127 /* This routine is called whenever the stream state changes */
128 static void stream_state_callback(pa_stream *s, void *userdata) {
129 assert(s);
130
131 switch (pa_stream_get_state(s)) {
132 case PA_STREAM_CREATING:
133 case PA_STREAM_TERMINATED:
134 break;
135
136 case PA_STREAM_READY:
137 if (verbose)
138 fprintf(stderr, "Stream successfully created\n");
139 break;
140
141 case PA_STREAM_FAILED:
142 default:
143 fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s))));
144 quit(1);
145 }
146 }
147
148 /* This is called whenever the context status changes */
149 static void context_state_callback(pa_context *c, void *userdata) {
150 assert(c);
151
152 switch (pa_context_get_state(c)) {
153 case PA_CONTEXT_CONNECTING:
154 case PA_CONTEXT_AUTHORIZING:
155 case PA_CONTEXT_SETTING_NAME:
156 break;
157
158 case PA_CONTEXT_READY:
159
160 assert(c && !stream);
161
162 if (verbose)
163 fprintf(stderr, "Connection established.\n");
164
165 stream = pa_stream_new(c, stream_name, &sample_spec, NULL);
166 assert(stream);
167
168 pa_stream_set_state_callback(stream, stream_state_callback, NULL);
169 pa_stream_set_write_callback(stream, stream_write_callback, NULL);
170 pa_stream_set_read_callback(stream, stream_read_callback, NULL);
171
172 if (mode == PLAYBACK) {
173 pa_cvolume cv;
174 pa_stream_connect_playback(stream, device, NULL, 0, pa_cvolume_set(&cv, PA_CHANNELS_MAX, volume));
175 } else
176 pa_stream_connect_record(stream, device, NULL, 0);
177
178 break;
179
180 case PA_CONTEXT_TERMINATED:
181 quit(0);
182 break;
183
184 case PA_CONTEXT_FAILED:
185 default:
186 fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(c)));
187 quit(1);
188 }
189 }
190
191 /* Connection draining complete */
192 static void context_drain_complete(pa_context*c, void *userdata) {
193 pa_context_disconnect(c);
194 }
195
196 /* Stream draining complete */
197 static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
198 pa_operation *o;
199
200 if (!success) {
201 fprintf(stderr, "Failed to drain stream: %s\n", pa_strerror(pa_context_errno(context)));
202 quit(1);
203 }
204
205 if (verbose)
206 fprintf(stderr, "Playback stream drained.\n");
207
208 pa_stream_disconnect(stream);
209 pa_stream_unref(stream);
210 stream = NULL;
211
212 if (!(o = pa_context_drain(context, context_drain_complete, NULL)))
213 pa_context_disconnect(context);
214 else {
215 pa_operation_unref(o);
216
217 if (verbose)
218 fprintf(stderr, "Draining connection to server.\n");
219 }
220 }
221
222 /* New data on STDIN **/
223 static void stdin_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
224 size_t l, w = 0;
225 ssize_t r;
226 assert(a == mainloop_api && e && stdio_event == e);
227
228 if (buffer) {
229 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
230 return;
231 }
232
233 if (!stream || pa_stream_get_state(stream) != PA_STREAM_READY || !(l = w = pa_stream_writable_size(stream)))
234 l = 4096;
235
236 buffer = malloc(l);
237 assert(buffer);
238 if ((r = read(fd, buffer, l)) <= 0) {
239 if (r == 0) {
240 if (verbose)
241 fprintf(stderr, "Got EOF.\n");
242 pa_operation_unref(pa_stream_drain(stream, stream_drain_complete, NULL));
243 } else {
244 fprintf(stderr, "read() failed: %s\n", strerror(errno));
245 quit(1);
246 }
247
248 mainloop_api->io_free(stdio_event);
249 stdio_event = NULL;
250 return;
251 }
252
253 buffer_length = r;
254 buffer_index = 0;
255
256 if (w)
257 do_stream_write(w);
258 }
259
260 /* Some data may be written to STDOUT */
261 static void stdout_callback(pa_mainloop_api*a, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) {
262 ssize_t r;
263 assert(a == mainloop_api && e && stdio_event == e);
264
265 if (!buffer) {
266 mainloop_api->io_enable(stdio_event, PA_IO_EVENT_NULL);
267 return;
268 }
269
270 assert(buffer_length);
271
272 if ((r = write(fd, (uint8_t*) buffer+buffer_index, buffer_length)) <= 0) {
273 fprintf(stderr, "write() failed: %s\n", strerror(errno));
274 quit(1);
275
276 mainloop_api->io_free(stdio_event);
277 stdio_event = NULL;
278 return;
279 }
280
281 buffer_length -= r;
282 buffer_index += r;
283
284 if (!buffer_length) {
285 free(buffer);
286 buffer = NULL;
287 buffer_length = buffer_index = 0;
288 }
289 }
290
291 /* UNIX signal to quit recieved */
292 static void exit_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
293 if (verbose)
294 fprintf(stderr, "Got signal, exiting.\n");
295 quit(0);
296
297 }
298
299 /* Show the current latency */
300 static void stream_get_latency_callback(pa_stream *s, const pa_latency_info *i, void *userdata) {
301 pa_usec_t total;
302 int negative = 0;
303 assert(s);
304
305 if (!i) {
306 fprintf(stderr, "Failed to get latency: %s\n", pa_strerror(pa_context_errno(context)));
307 quit(1);
308 return;
309 }
310
311 total = pa_stream_get_latency(s, i, &negative);
312
313 fprintf(stderr, "Latency: buffer: %0.0f usec; sink: %0.0f usec; source: %0.0f usec; transport: %0.0f usec; total: %0.0f usec; synchronized clocks: %s.\n",
314 (float) i->buffer_usec, (float) i->sink_usec, (float) i->source_usec, (float) i->transport_usec, (float) total * (negative?-1:1),
315 i->synchronized_clocks ? "yes" : "no");
316 }
317
318 /* Someone requested that the latency is shown */
319 static void sigusr1_signal_callback(pa_mainloop_api*m, pa_signal_event *e, int sig, void *userdata) {
320 fprintf(stderr, "Got SIGUSR1, requesting latency.\n");
321 pa_operation_unref(pa_stream_get_latency_info(stream, stream_get_latency_callback, NULL));
322 }
323
324
325 static void help(const char *argv0) {
326
327 printf("%s [options]\n\n"
328 " -h, --help Show this help\n"
329 " --version Show version\n\n"
330 " -r, --record Create a connection for recording\n"
331 " -p, --playback Create a connection for playback\n\n"
332 " -v, --verbose Enable verbose operations\n\n"
333 " -s, --server=SERVER The name of the server to connect to\n"
334 " -d, --device=DEVICE The name of the sink/source to connect to\n"
335 " -n, --client-name=NAME How to call this client on the server\n"
336 " --stream-name=NAME How to call this stream on the server\n"
337 " --volume=VOLUME Specify the initial (linear) volume in range 0...256\n"
338 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
339 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
340 " float32be, ulaw, alaw (defaults to s16ne)\n"
341 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
342 " (defaults to 2)\n",
343 argv0);
344 }
345
346 enum {
347 ARG_VERSION = 256,
348 ARG_STREAM_NAME,
349 ARG_VOLUME,
350 ARG_SAMPLERATE,
351 ARG_SAMPLEFORMAT,
352 ARG_CHANNELS
353 };
354
355 int main(int argc, char *argv[]) {
356 pa_mainloop* m = NULL;
357 int ret = 1, r, c;
358 char *bn, *server = NULL;
359
360 static const struct option long_options[] = {
361 {"record", 0, NULL, 'r'},
362 {"playback", 0, NULL, 'p'},
363 {"device", 1, NULL, 'd'},
364 {"server", 1, NULL, 's'},
365 {"client-name", 1, NULL, 'n'},
366 {"stream-name", 1, NULL, ARG_STREAM_NAME},
367 {"version", 0, NULL, ARG_VERSION},
368 {"help", 0, NULL, 'h'},
369 {"verbose", 0, NULL, 'v'},
370 {"volume", 1, NULL, ARG_VOLUME},
371 {"rate", 1, NULL, ARG_SAMPLERATE},
372 {"format", 1, NULL, ARG_SAMPLEFORMAT},
373 {"channels", 1, NULL, ARG_CHANNELS},
374 {NULL, 0, NULL, 0}
375 };
376
377 if (!(bn = strrchr(argv[0], '/')))
378 bn = argv[0];
379 else
380 bn++;
381
382 if (strstr(bn, "rec") || strstr(bn, "mon"))
383 mode = RECORD;
384 else if (strstr(bn, "cat") || strstr(bn, "play"))
385 mode = PLAYBACK;
386
387 while ((c = getopt_long(argc, argv, "rpd:s:n:hv", long_options, NULL)) != -1) {
388
389 switch (c) {
390 case 'h' :
391 help(bn);
392 ret = 0;
393 goto quit;
394
395 case ARG_VERSION:
396 printf("pacat "PACKAGE_VERSION"\nCompiled with libpolyp %s\nLinked with libpolyp %s\n", pa_get_headers_version(), pa_get_library_version());
397 ret = 0;
398 goto quit;
399
400 case 'r':
401 mode = RECORD;
402 break;
403
404 case 'p':
405 mode = PLAYBACK;
406 break;
407
408 case 'd':
409 free(device);
410 device = strdup(optarg);
411 break;
412
413 case 's':
414 free(server);
415 server = strdup(optarg);
416 break;
417
418 case 'n':
419 free(client_name);
420 client_name = strdup(optarg);
421 break;
422
423 case ARG_STREAM_NAME:
424 free(stream_name);
425 stream_name = strdup(optarg);
426 break;
427
428 case 'v':
429 verbose = 1;
430 break;
431
432 case ARG_VOLUME: {
433 int v = atoi(optarg);
434 volume = v < 0 ? 0 : v;
435 break;
436 }
437
438 case ARG_CHANNELS:
439 sample_spec.channels = atoi(optarg);
440 break;
441
442 case ARG_SAMPLEFORMAT:
443 sample_spec.format = pa_parse_sample_format(optarg);
444 break;
445
446 case ARG_SAMPLERATE:
447 sample_spec.rate = atoi(optarg);
448 break;
449
450 default:
451 goto quit;
452 }
453 }
454
455 if (!client_name)
456 client_name = strdup(bn);
457
458 if (!stream_name)
459 stream_name = strdup(client_name);
460
461 if (!pa_sample_spec_valid(&sample_spec)) {
462 fprintf(stderr, "Invalid sample specification\n");
463 goto quit;
464 }
465
466 if (verbose) {
467 char t[PA_SAMPLE_SPEC_SNPRINT_MAX];
468 pa_sample_spec_snprint(t, sizeof(t), &sample_spec);
469 fprintf(stderr, "Opening a %s stream with sample specification '%s'.\n", mode == RECORD ? "recording" : "playback", t);
470 }
471
472 /* Set up a new main loop */
473 if (!(m = pa_mainloop_new())) {
474 fprintf(stderr, "pa_mainloop_new() failed.\n");
475 goto quit;
476 }
477
478 mainloop_api = pa_mainloop_get_api(m);
479
480 r = pa_signal_init(mainloop_api);
481 assert(r == 0);
482 pa_signal_new(SIGINT, exit_signal_callback, NULL);
483 pa_signal_new(SIGTERM, exit_signal_callback, NULL);
484 #ifdef SIGUSR1
485 pa_signal_new(SIGUSR1, sigusr1_signal_callback, NULL);
486 #endif
487 #ifdef SIGPIPE
488 signal(SIGPIPE, SIG_IGN);
489 #endif
490
491 if (!(stdio_event = mainloop_api->io_new(mainloop_api,
492 mode == PLAYBACK ? STDIN_FILENO : STDOUT_FILENO,
493 mode == PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT,
494 mode == PLAYBACK ? stdin_callback : stdout_callback, NULL))) {
495 fprintf(stderr, "source_io() failed.\n");
496 goto quit;
497 }
498
499 /* Create a new connection context */
500 if (!(context = pa_context_new(mainloop_api, client_name))) {
501 fprintf(stderr, "pa_context_new() failed.\n");
502 goto quit;
503 }
504
505 pa_context_set_state_callback(context, context_state_callback, NULL);
506
507 /* Connect the context */
508 pa_context_connect(context, server, 1, NULL);
509
510 /* Run the main loop */
511 if (pa_mainloop_run(m, &ret) < 0) {
512 fprintf(stderr, "pa_mainloop_run() failed.\n");
513 goto quit;
514 }
515
516 quit:
517 if (stream)
518 pa_stream_unref(stream);
519
520 if (context)
521 pa_context_unref(context);
522
523 if (stdio_event) {
524 assert(mainloop_api);
525 mainloop_api->io_free(stdio_event);
526 }
527
528 if (m) {
529 pa_signal_done();
530 pa_mainloop_free(m);
531 }
532
533 free(buffer);
534
535 free(server);
536 free(device);
537 free(client_name);
538 free(stream_name);
539
540 return ret;
541 }