2 This file is part of PulseAudio.
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
40 #include <pulse/pulseaudio.h>
41 #include <pulse/rtclock.h>
43 #include <pulsecore/core-util.h>
44 #include <pulsecore/i18n.h>
45 #include <pulsecore/log.h>
46 #include <pulsecore/macro.h>
47 #include <pulsecore/sndfile-util.h>
49 #define TIME_EVENT_USEC 50000
51 #define CLEAR_LINE "\x1B[K"
53 static enum { RECORD
, PLAYBACK
} mode
= PLAYBACK
;
55 static pa_context
*context
= NULL
;
56 static pa_stream
*stream
= NULL
;
57 static pa_mainloop_api
*mainloop_api
= NULL
;
59 static void *buffer
= NULL
;
60 static size_t buffer_length
= 0, buffer_index
= 0;
62 static pa_io_event
* stdio_event
= NULL
;
64 static pa_proplist
*proplist
= NULL
;
65 static char *device
= NULL
;
67 static SNDFILE
* sndfile
= NULL
;
69 static pa_bool_t verbose
= FALSE
;
70 static pa_volume_t volume
= PA_VOLUME_NORM
;
71 static pa_bool_t volume_is_set
= FALSE
;
73 static pa_sample_spec sample_spec
= {
74 .format
= PA_SAMPLE_S16LE
,
78 static pa_bool_t sample_spec_set
= FALSE
;
80 static pa_channel_map channel_map
;
81 static pa_bool_t channel_map_set
= FALSE
;
83 static sf_count_t (*readf_function
)(SNDFILE
*_sndfile
, void *ptr
, sf_count_t frames
) = NULL
;
84 static sf_count_t (*writef_function
)(SNDFILE
*_sndfile
, const void *ptr
, sf_count_t frames
) = NULL
;
86 static pa_stream_flags_t flags
= 0;
88 static size_t latency
= 0, process_time
= 0;
89 static int32_t latency_msec
= 0, process_time_msec
= 0;
91 static pa_bool_t raw
= TRUE
;
92 static int file_format
= -1;
94 /* A shortcut for terminating the application */
95 static void quit(int ret
) {
96 pa_assert(mainloop_api
);
97 mainloop_api
->quit(mainloop_api
, ret
);
100 /* Connection draining complete */
101 static void context_drain_complete(pa_context
*c
, void *userdata
) {
102 pa_context_disconnect(c
);
105 /* Stream draining complete */
106 static void stream_drain_complete(pa_stream
*s
, int success
, void *userdata
) {
107 pa_operation
*o
= NULL
;
110 pa_log(_("Failed to drain stream: %s"), pa_strerror(pa_context_errno(context
)));
115 pa_log(_("Playback stream drained."));
117 pa_stream_disconnect(stream
);
118 pa_stream_unref(stream
);
121 if (!(o
= pa_context_drain(context
, context_drain_complete
, NULL
)))
122 pa_context_disconnect(context
);
124 pa_operation_unref(o
);
126 pa_log(_("Draining connection to server."));
131 static void start_drain(void) {
136 pa_stream_set_write_callback(stream
, NULL
, NULL
);
138 if (!(o
= pa_stream_drain(stream
, stream_drain_complete
, NULL
))) {
139 pa_log(_("pa_stream_drain(): %s"), pa_strerror(pa_context_errno(context
)));
144 pa_operation_unref(o
);
149 /* Write some data to the stream */
150 static void do_stream_write(size_t length
) {
154 if (!buffer
|| !buffer_length
)
158 if (l
> buffer_length
)
161 if (pa_stream_write(stream
, (uint8_t*) buffer
+ buffer_index
, l
, NULL
, 0, PA_SEEK_RELATIVE
) < 0) {
162 pa_log(_("pa_stream_write() failed: %s"), pa_strerror(pa_context_errno(context
)));
170 if (!buffer_length
) {
173 buffer_index
= buffer_length
= 0;
177 /* This is called whenever new data may be written to the stream */
178 static void stream_write_callback(pa_stream
*s
, size_t length
, void *userdata
) {
180 pa_assert(length
> 0);
186 mainloop_api
->io_enable(stdio_event
, PA_IO_EVENT_INPUT
);
191 do_stream_write(length
);
200 size_t data_length
= length
;
202 if (pa_stream_begin_write(s
, &data
, &data_length
) < 0) {
203 pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context
)));
208 if (readf_function
) {
209 size_t k
= pa_frame_size(&sample_spec
);
211 if ((bytes
= readf_function(sndfile
, data
, (sf_count_t
) (data_length
/k
))) > 0)
212 bytes
*= (sf_count_t
) k
;
215 bytes
= sf_read_raw(sndfile
, data
, (sf_count_t
) data_length
);
218 pa_stream_write(s
, data
, (size_t) bytes
, NULL
, 0, PA_SEEK_RELATIVE
);
220 pa_stream_cancel_write(s
);
223 if (bytes
< (sf_count_t
) data_length
) {
228 /* Request fulfilled */
229 if ((size_t) bytes
>= length
)
237 /* This is called whenever new data may is available */
238 static void stream_read_callback(pa_stream
*s
, size_t length
, void *userdata
) {
241 pa_assert(length
> 0);
247 mainloop_api
->io_enable(stdio_event
, PA_IO_EVENT_OUTPUT
);
249 while (pa_stream_readable_size(s
) > 0) {
252 if (pa_stream_peek(s
, &data
, &length
) < 0) {
253 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context
)));
259 pa_assert(length
> 0);
262 buffer
= pa_xrealloc(buffer
, buffer_length
+ length
);
263 memcpy((uint8_t*) buffer
+ buffer_length
, data
, length
);
264 buffer_length
+= length
;
266 buffer
= pa_xmalloc(length
);
267 memcpy(buffer
, data
, length
);
268 buffer_length
= length
;
278 while (pa_stream_readable_size(s
) > 0) {
282 if (pa_stream_peek(s
, &data
, &length
) < 0) {
283 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context
)));
289 pa_assert(length
> 0);
291 if (writef_function
) {
292 size_t k
= pa_frame_size(&sample_spec
);
294 if ((bytes
= writef_function(sndfile
, data
, (sf_count_t
) (length
/k
))) > 0)
295 bytes
*= (sf_count_t
) k
;
298 bytes
= sf_write_raw(sndfile
, data
, (sf_count_t
) length
);
300 if (bytes
< (sf_count_t
) length
)
308 /* This routine is called whenever the stream state changes */
309 static void stream_state_callback(pa_stream
*s
, void *userdata
) {
312 switch (pa_stream_get_state(s
)) {
313 case PA_STREAM_CREATING
:
314 case PA_STREAM_TERMINATED
:
317 case PA_STREAM_READY
:
320 const pa_buffer_attr
*a
;
321 char cmt
[PA_CHANNEL_MAP_SNPRINT_MAX
], sst
[PA_SAMPLE_SPEC_SNPRINT_MAX
];
323 pa_log(_("Stream successfully created."));
325 if (!(a
= pa_stream_get_buffer_attr(s
)))
326 pa_log(_("pa_stream_get_buffer_attr() failed: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s
))));
329 if (mode
== PLAYBACK
)
330 pa_log(_("Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"), a
->maxlength
, a
->tlength
, a
->prebuf
, a
->minreq
);
332 pa_assert(mode
== RECORD
);
333 pa_log(_("Buffer metrics: maxlength=%u, fragsize=%u"), a
->maxlength
, a
->fragsize
);
337 pa_log(_("Using sample spec '%s', channel map '%s'."),
338 pa_sample_spec_snprint(sst
, sizeof(sst
), pa_stream_get_sample_spec(s
)),
339 pa_channel_map_snprint(cmt
, sizeof(cmt
), pa_stream_get_channel_map(s
)));
341 pa_log(_("Connected to device %s (%u, %ssuspended)."),
342 pa_stream_get_device_name(s
),
343 pa_stream_get_device_index(s
),
344 pa_stream_is_suspended(s
) ? "" : "not ");
349 case PA_STREAM_FAILED
:
351 pa_log(_("Stream error: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s
))));
356 static void stream_suspended_callback(pa_stream
*s
, void *userdata
) {
360 if (pa_stream_is_suspended(s
))
361 pa_log(_("Stream device suspended.%s"), CLEAR_LINE
);
363 pa_log(_("Stream device resumed.%s"), CLEAR_LINE
);
367 static void stream_underflow_callback(pa_stream
*s
, void *userdata
) {
371 pa_log(_("Stream underrun.%s"), CLEAR_LINE
);
374 static void stream_overflow_callback(pa_stream
*s
, void *userdata
) {
378 pa_log(_("Stream overrun.%s"), CLEAR_LINE
);
381 static void stream_started_callback(pa_stream
*s
, void *userdata
) {
385 pa_log(_("Stream started.%s"), CLEAR_LINE
);
388 static void stream_moved_callback(pa_stream
*s
, void *userdata
) {
392 pa_log(_("Stream moved to device %s (%u, %ssuspended).%s"), pa_stream_get_device_name(s
), pa_stream_get_device_index(s
), pa_stream_is_suspended(s
) ? "" : _("not "), CLEAR_LINE
);
395 static void stream_buffer_attr_callback(pa_stream
*s
, void *userdata
) {
399 pa_log(_("Stream buffer attributes changed.%s"), CLEAR_LINE
);
402 static void stream_event_callback(pa_stream
*s
, const char *name
, pa_proplist
*pl
, void *userdata
) {
409 t
= pa_proplist_to_string_sep(pl
, ", ");
410 pa_log("Got event '%s', properties '%s'", name
, t
);
414 /* This is called whenever the context status changes */
415 static void context_state_callback(pa_context
*c
, void *userdata
) {
418 switch (pa_context_get_state(c
)) {
419 case PA_CONTEXT_CONNECTING
:
420 case PA_CONTEXT_AUTHORIZING
:
421 case PA_CONTEXT_SETTING_NAME
:
424 case PA_CONTEXT_READY
: {
425 pa_buffer_attr buffer_attr
;
431 pa_log(_("Connection established.%s"), CLEAR_LINE
);
433 if (!(stream
= pa_stream_new_with_proplist(c
, NULL
, &sample_spec
, &channel_map
, proplist
))) {
434 pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c
)));
438 pa_stream_set_state_callback(stream
, stream_state_callback
, NULL
);
439 pa_stream_set_write_callback(stream
, stream_write_callback
, NULL
);
440 pa_stream_set_read_callback(stream
, stream_read_callback
, NULL
);
441 pa_stream_set_suspended_callback(stream
, stream_suspended_callback
, NULL
);
442 pa_stream_set_moved_callback(stream
, stream_moved_callback
, NULL
);
443 pa_stream_set_underflow_callback(stream
, stream_underflow_callback
, NULL
);
444 pa_stream_set_overflow_callback(stream
, stream_overflow_callback
, NULL
);
445 pa_stream_set_started_callback(stream
, stream_started_callback
, NULL
);
446 pa_stream_set_event_callback(stream
, stream_event_callback
, NULL
);
447 pa_stream_set_buffer_attr_callback(stream
, stream_buffer_attr_callback
, NULL
);
449 pa_zero(buffer_attr
);
450 buffer_attr
.maxlength
= (uint32_t) -1;
451 buffer_attr
.prebuf
= (uint32_t) -1;
453 if (latency_msec
> 0) {
454 buffer_attr
.fragsize
= buffer_attr
.tlength
= pa_usec_to_bytes(latency_msec
* PA_USEC_PER_MSEC
, &sample_spec
);
455 flags
|= PA_STREAM_ADJUST_LATENCY
;
456 } else if (latency
> 0) {
457 buffer_attr
.fragsize
= buffer_attr
.tlength
= (uint32_t) latency
;
458 flags
|= PA_STREAM_ADJUST_LATENCY
;
460 buffer_attr
.fragsize
= buffer_attr
.tlength
= (uint32_t) -1;
462 if (process_time_msec
> 0) {
463 buffer_attr
.minreq
= pa_usec_to_bytes(process_time_msec
* PA_USEC_PER_MSEC
, &sample_spec
);
464 } else if (process_time
> 0)
465 buffer_attr
.minreq
= (uint32_t) process_time
;
467 buffer_attr
.minreq
= (uint32_t) -1;
469 if (mode
== PLAYBACK
) {
471 if (pa_stream_connect_playback(stream
, device
, &buffer_attr
, flags
, volume_is_set
? pa_cvolume_set(&cv
, sample_spec
.channels
, volume
) : NULL
, NULL
) < 0) {
472 pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c
)));
477 if (pa_stream_connect_record(stream
, device
, &buffer_attr
, flags
) < 0) {
478 pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c
)));
486 case PA_CONTEXT_TERMINATED
:
490 case PA_CONTEXT_FAILED
:
492 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c
)));
503 /* New data on STDIN **/
504 static void stdin_callback(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
508 pa_assert(a
== mainloop_api
);
510 pa_assert(stdio_event
== e
);
513 mainloop_api
->io_enable(stdio_event
, PA_IO_EVENT_NULL
);
517 if (!stream
|| pa_stream_get_state(stream
) != PA_STREAM_READY
|| !(l
= w
= pa_stream_writable_size(stream
)))
520 buffer
= pa_xmalloc(l
);
522 if ((r
= read(fd
, buffer
, l
)) <= 0) {
525 pa_log(_("Got EOF."));
530 pa_log(_("read() failed: %s"), strerror(errno
));
534 mainloop_api
->io_free(stdio_event
);
539 buffer_length
= (uint32_t) r
;
546 /* Some data may be written to STDOUT */
547 static void stdout_callback(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
550 pa_assert(a
== mainloop_api
);
552 pa_assert(stdio_event
== e
);
555 mainloop_api
->io_enable(stdio_event
, PA_IO_EVENT_NULL
);
559 pa_assert(buffer_length
);
561 if ((r
= write(fd
, (uint8_t*) buffer
+buffer_index
, buffer_length
)) <= 0) {
562 pa_log(_("write() failed: %s"), strerror(errno
));
565 mainloop_api
->io_free(stdio_event
);
570 buffer_length
-= (uint32_t) r
;
571 buffer_index
+= (uint32_t) r
;
573 if (!buffer_length
) {
576 buffer_length
= buffer_index
= 0;
580 /* UNIX signal to quit received */
581 static void exit_signal_callback(pa_mainloop_api
*m
, pa_signal_event
*e
, int sig
, void *userdata
) {
583 pa_log(_("Got signal, exiting."));
587 /* Show the current latency */
588 static void stream_update_timing_callback(pa_stream
*s
, int success
, void *userdata
) {
595 pa_stream_get_time(s
, &usec
) < 0 ||
596 pa_stream_get_latency(s
, &l
, &negative
) < 0) {
597 pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context
)));
602 fprintf(stderr
, _("Time: %0.3f sec; Latency: %0.0f usec."),
603 (float) usec
/ 1000000,
604 (float) l
* (negative
?-1.0f
:1.0f
));
605 fprintf(stderr
, " \r");
609 /* Someone requested that the latency is shown */
610 static void sigusr1_signal_callback(pa_mainloop_api
*m
, pa_signal_event
*e
, int sig
, void *userdata
) {
615 pa_operation_unref(pa_stream_update_timing_info(stream
, stream_update_timing_callback
, NULL
));
619 static void time_event_callback(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
620 if (stream
&& pa_stream_get_state(stream
) == PA_STREAM_READY
) {
622 if (!(o
= pa_stream_update_timing_info(stream
, stream_update_timing_callback
, NULL
)))
623 pa_log(_("pa_stream_update_timing_info() failed: %s"), pa_strerror(pa_context_errno(context
)));
625 pa_operation_unref(o
);
628 pa_context_rttime_restart(context
, e
, pa_rtclock_now() + TIME_EVENT_USEC
);
631 static void help(const char *argv0
) {
633 printf(_("%s [options]\n\n"
634 " -h, --help Show this help\n"
635 " --version Show version\n\n"
636 " -r, --record Create a connection for recording\n"
637 " -p, --playback Create a connection for playback\n\n"
638 " -v, --verbose Enable verbose operations\n\n"
639 " -s, --server=SERVER The name of the server to connect to\n"
640 " -d, --device=DEVICE The name of the sink/source to connect to\n"
641 " -n, --client-name=NAME How to call this client on the server\n"
642 " --stream-name=NAME How to call this stream on the server\n"
643 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
644 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
645 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
646 " float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
647 " s24-32le, s24-32be (defaults to s16ne)\n"
648 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
650 " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
651 " --fix-format Take the sample format from the sink the stream is\n"
652 " being connected to.\n"
653 " --fix-rate Take the sampling rate from the sink the stream is\n"
654 " being connected to.\n"
655 " --fix-channels Take the number of channels and the channel map\n"
656 " from the sink the stream is being connected to.\n"
657 " --no-remix Don't upmix or downmix channels.\n"
658 " --no-remap Map channels by index instead of name.\n"
659 " --latency=BYTES Request the specified latency in bytes.\n"
660 " --process-time=BYTES Request the specified process time per request in bytes.\n"
661 " --latency-msec=MSEC Request the specified latency in msec.\n"
662 " --process-time-msec=MSEC Request the specified process time per request in msec.\n"
663 " --property=PROPERTY=VALUE Set the specified property to the specified value.\n"
664 " --raw Record/play raw PCM data.\n"
665 " --passthrough passthrough data \n"
666 " --file-format[=FFORMAT] Record/play formatted PCM data.\n"
667 " --list-file-formats List available file formats.\n")
690 ARG_LIST_FILE_FORMATS
,
692 ARG_PROCESS_TIME_MSEC
695 int main(int argc
, char *argv
[]) {
696 pa_mainloop
* m
= NULL
;
698 char *bn
, *server
= NULL
;
699 pa_time_event
*time_event
= NULL
;
700 const char *filename
= NULL
;
702 static const struct option long_options
[] = {
703 {"record", 0, NULL
, 'r'},
704 {"playback", 0, NULL
, 'p'},
705 {"device", 1, NULL
, 'd'},
706 {"server", 1, NULL
, 's'},
707 {"client-name", 1, NULL
, 'n'},
708 {"stream-name", 1, NULL
, ARG_STREAM_NAME
},
709 {"version", 0, NULL
, ARG_VERSION
},
710 {"help", 0, NULL
, 'h'},
711 {"verbose", 0, NULL
, 'v'},
712 {"volume", 1, NULL
, ARG_VOLUME
},
713 {"rate", 1, NULL
, ARG_SAMPLERATE
},
714 {"format", 1, NULL
, ARG_SAMPLEFORMAT
},
715 {"channels", 1, NULL
, ARG_CHANNELS
},
716 {"channel-map", 1, NULL
, ARG_CHANNELMAP
},
717 {"fix-format", 0, NULL
, ARG_FIX_FORMAT
},
718 {"fix-rate", 0, NULL
, ARG_FIX_RATE
},
719 {"fix-channels", 0, NULL
, ARG_FIX_CHANNELS
},
720 {"no-remap", 0, NULL
, ARG_NO_REMAP
},
721 {"no-remix", 0, NULL
, ARG_NO_REMIX
},
722 {"latency", 1, NULL
, ARG_LATENCY
},
723 {"process-time", 1, NULL
, ARG_PROCESS_TIME
},
724 {"property", 1, NULL
, ARG_PROPERTY
},
725 {"raw", 0, NULL
, ARG_RAW
},
726 {"passthrough", 0, NULL
, ARG_PASSTHROUGH
},
727 {"file-format", 2, NULL
, ARG_FILE_FORMAT
},
728 {"list-file-formats", 0, NULL
, ARG_LIST_FILE_FORMATS
},
729 {"latency-msec", 1, NULL
, ARG_LATENCY_MSEC
},
730 {"process-time-msec", 1, NULL
, ARG_PROCESS_TIME_MSEC
},
734 setlocale(LC_ALL
, "");
735 bindtextdomain(GETTEXT_PACKAGE
, PULSE_LOCALEDIR
);
737 bn
= pa_path_get_filename(argv
[0]);
739 if (strstr(bn
, "play")) {
742 } else if (strstr(bn
, "record")) {
745 } else if (strstr(bn
, "cat")) {
748 } if (strstr(bn
, "rec") || strstr(bn
, "mon")) {
753 proplist
= pa_proplist_new();
755 while ((c
= getopt_long(argc
, argv
, "rpd:s:n:hv", long_options
, NULL
)) != -1) {
764 printf(_("pacat %s\n"
765 "Compiled with libpulse %s\n"
766 "Linked with libpulse %s\n"),
768 pa_get_headers_version(),
769 pa_get_library_version());
783 device
= pa_xstrdup(optarg
);
788 server
= pa_xstrdup(optarg
);
794 if (!(t
= pa_locale_to_utf8(optarg
)) ||
795 pa_proplist_sets(proplist
, PA_PROP_APPLICATION_NAME
, t
) < 0) {
797 pa_log(_("Invalid client name '%s'"), t
? t
: optarg
);
806 case ARG_STREAM_NAME
: {
809 if (!(t
= pa_locale_to_utf8(optarg
)) ||
810 pa_proplist_sets(proplist
, PA_PROP_MEDIA_NAME
, t
) < 0) {
812 pa_log(_("Invalid stream name '%s'"), t
? t
: optarg
);
826 int v
= atoi(optarg
);
827 volume
= v
< 0 ? 0U : (pa_volume_t
) v
;
828 volume_is_set
= TRUE
;
833 sample_spec
.channels
= (uint8_t) atoi(optarg
);
834 sample_spec_set
= TRUE
;
837 case ARG_SAMPLEFORMAT
:
838 sample_spec
.format
= pa_parse_sample_format(optarg
);
839 sample_spec_set
= TRUE
;
843 sample_spec
.rate
= (uint32_t) atoi(optarg
);
844 sample_spec_set
= TRUE
;
848 if (!pa_channel_map_parse(&channel_map
, optarg
)) {
849 pa_log(_("Invalid channel map '%s'"), optarg
);
853 channel_map_set
= TRUE
;
856 case ARG_FIX_CHANNELS
:
857 flags
|= PA_STREAM_FIX_CHANNELS
;
861 flags
|= PA_STREAM_FIX_RATE
;
865 flags
|= PA_STREAM_FIX_FORMAT
;
869 flags
|= PA_STREAM_NO_REMIX_CHANNELS
;
873 flags
|= PA_STREAM_NO_REMAP_CHANNELS
;
877 if (((latency
= (size_t) atoi(optarg
))) <= 0) {
878 pa_log(_("Invalid latency specification '%s'"), optarg
);
883 case ARG_PROCESS_TIME
:
884 if (((process_time
= (size_t) atoi(optarg
))) <= 0) {
885 pa_log(_("Invalid process time specification '%s'"), optarg
);
890 case ARG_LATENCY_MSEC
:
891 if (((latency_msec
= (int32_t) atoi(optarg
))) <= 0) {
892 pa_log(_("Invalid latency specification '%s'"), optarg
);
897 case ARG_PROCESS_TIME_MSEC
:
898 if (((process_time_msec
= (int32_t) atoi(optarg
))) <= 0) {
899 pa_log(_("Invalid process time specification '%s'"), optarg
);
907 if (!(t
= pa_locale_to_utf8(optarg
)) ||
908 pa_proplist_setp(proplist
, t
) < 0) {
911 pa_log(_("Invalid property '%s'"), optarg
);
923 case ARG_PASSTHROUGH
:
924 flags
|= PA_STREAM_PASSTHROUGH
;
927 case ARG_FILE_FORMAT
:
929 if ((file_format
= pa_sndfile_format_from_string(optarg
)) < 0) {
930 pa_log(_("Unknown file format %s."), optarg
);
938 case ARG_LIST_FILE_FORMATS
:
939 pa_sndfile_dump_formats();
948 if (!pa_sample_spec_valid(&sample_spec
)) {
949 pa_log(_("Invalid sample specification"));
953 if (optind
+1 == argc
) {
956 filename
= argv
[optind
];
958 if ((fd
= pa_open_cloexec(argv
[optind
], mode
== PLAYBACK
? O_RDONLY
: O_WRONLY
|O_TRUNC
|O_CREAT
, 0666)) < 0) {
959 pa_log(_("open(): %s"), strerror(errno
));
963 if (dup2(fd
, mode
== PLAYBACK
? STDIN_FILENO
: STDOUT_FILENO
) < 0) {
964 pa_log(_("dup2(): %s"), strerror(errno
));
970 } else if (optind
+1 <= argc
) {
971 pa_log(_("Too many arguments."));
979 if (mode
== RECORD
) {
980 /* This might patch up the sample spec */
981 if (pa_sndfile_write_sample_spec(&sfi
, &sample_spec
) < 0) {
982 pa_log(_("Failed to generate sample specification for file."));
986 if (file_format
<= 0) {
988 if (filename
&& (extension
= strrchr(filename
, '.')))
989 file_format
= pa_sndfile_format_from_string(extension
+1);
990 if (file_format
<= 0)
991 file_format
= SF_FORMAT_WAV
;
992 /* Transparently upgrade classic .wav to wavex for multichannel audio */
993 if (file_format
== SF_FORMAT_WAV
&&
994 (sample_spec
.channels
> 2 ||
996 !(sample_spec
.channels
== 1 && channel_map
.map
[0] == PA_CHANNEL_POSITION_MONO
) &&
997 !(sample_spec
.channels
== 2 && channel_map
.map
[0] == PA_CHANNEL_POSITION_LEFT
998 && channel_map
.map
[1] == PA_CHANNEL_POSITION_RIGHT
))))
999 file_format
= SF_FORMAT_WAVEX
;
1002 sfi
.format
|= file_format
;
1005 if (!(sndfile
= sf_open_fd(mode
== RECORD
? STDOUT_FILENO
: STDIN_FILENO
,
1006 mode
== RECORD
? SFM_WRITE
: SFM_READ
,
1008 pa_log(_("Failed to open audio file."));
1012 if (mode
== PLAYBACK
) {
1013 if (sample_spec_set
)
1014 pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));
1016 if (pa_sndfile_read_sample_spec(sndfile
, &sample_spec
) < 0) {
1017 pa_log(_("Failed to determine sample specification from file."));
1020 sample_spec_set
= TRUE
;
1022 if (!channel_map_set
) {
1023 /* Allow the user to overwrite the channel map on the command line */
1024 if (pa_sndfile_read_channel_map(sndfile
, &channel_map
) < 0) {
1025 if (sample_spec
.channels
> 2)
1026 pa_log(_("Warning: Failed to determine channel map from file."));
1028 channel_map_set
= TRUE
;
1033 if (!channel_map_set
)
1034 pa_channel_map_init_extend(&channel_map
, sample_spec
.channels
, PA_CHANNEL_MAP_DEFAULT
);
1036 if (!pa_channel_map_compatible(&channel_map
, &sample_spec
)) {
1037 pa_log(_("Channel map doesn't match sample specification"));
1044 if (mode
== PLAYBACK
)
1045 readf_function
= pa_sndfile_readf_function(&sample_spec
);
1047 if (pa_sndfile_write_channel_map(sndfile
, &channel_map
) < 0)
1048 pa_log(_("Warning: failed to write channel map to file."));
1050 writef_function
= pa_sndfile_writef_function(&sample_spec
);
1053 /* Fill in libsndfile prop list data */
1054 sfp
= pa_proplist_new();
1055 pa_sndfile_init_proplist(sndfile
, sfp
);
1056 pa_proplist_update(proplist
, PA_UPDATE_MERGE
, sfp
);
1057 pa_proplist_free(sfp
);
1061 char tss
[PA_SAMPLE_SPEC_SNPRINT_MAX
], tcm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
1063 pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
1064 mode
== RECORD
? _("recording") : _("playback"),
1065 pa_sample_spec_snprint(tss
, sizeof(tss
), &sample_spec
),
1066 pa_channel_map_snprint(tcm
, sizeof(tcm
), &channel_map
));
1069 /* Fill in client name if none was set */
1070 if (!pa_proplist_contains(proplist
, PA_PROP_APPLICATION_NAME
)) {
1073 if ((t
= pa_locale_to_utf8(bn
))) {
1074 pa_proplist_sets(proplist
, PA_PROP_APPLICATION_NAME
, t
);
1079 /* Fill in media name if none was set */
1080 if (!pa_proplist_contains(proplist
, PA_PROP_MEDIA_NAME
)) {
1083 if ((t
= filename
) ||
1084 (t
= pa_proplist_gets(proplist
, PA_PROP_APPLICATION_NAME
)))
1085 pa_proplist_sets(proplist
, PA_PROP_MEDIA_NAME
, t
);
1088 /* Set up a new main loop */
1089 if (!(m
= pa_mainloop_new())) {
1090 pa_log(_("pa_mainloop_new() failed."));
1094 mainloop_api
= pa_mainloop_get_api(m
);
1096 pa_assert_se(pa_signal_init(mainloop_api
) == 0);
1097 pa_signal_new(SIGINT
, exit_signal_callback
, NULL
);
1098 pa_signal_new(SIGTERM
, exit_signal_callback
, NULL
);
1100 pa_signal_new(SIGUSR1
, sigusr1_signal_callback
, NULL
);
1102 pa_disable_sigpipe();
1105 if (!(stdio_event
= mainloop_api
->io_new(mainloop_api
,
1106 mode
== PLAYBACK
? STDIN_FILENO
: STDOUT_FILENO
,
1107 mode
== PLAYBACK
? PA_IO_EVENT_INPUT
: PA_IO_EVENT_OUTPUT
,
1108 mode
== PLAYBACK
? stdin_callback
: stdout_callback
, NULL
))) {
1109 pa_log(_("io_new() failed."));
1114 /* Create a new connection context */
1115 if (!(context
= pa_context_new_with_proplist(mainloop_api
, NULL
, proplist
))) {
1116 pa_log(_("pa_context_new() failed."));
1120 pa_context_set_state_callback(context
, context_state_callback
, NULL
);
1122 /* Connect the context */
1123 if (pa_context_connect(context
, server
, 0, NULL
) < 0) {
1124 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context
)));
1129 if (!(time_event
= pa_context_rttime_new(context
, pa_rtclock_now() + TIME_EVENT_USEC
, time_event_callback
, NULL
))) {
1130 pa_log(_("pa_context_rttime_new() failed."));
1135 /* Run the main loop */
1136 if (pa_mainloop_run(m
, &ret
) < 0) {
1137 pa_log(_("pa_mainloop_run() failed."));
1143 pa_stream_unref(stream
);
1146 pa_context_unref(context
);
1149 pa_assert(mainloop_api
);
1150 mainloop_api
->io_free(stdio_event
);
1154 pa_assert(mainloop_api
);
1155 mainloop_api
->time_free(time_event
);
1160 pa_mainloop_free(m
);
1172 pa_proplist_free(proplist
);