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/i18n.h>
41 #include <pulse/pulseaudio.h>
42 #include <pulse/rtclock.h>
44 #include <pulsecore/macro.h>
45 #include <pulsecore/core-util.h>
46 #include <pulsecore/log.h>
47 #include <pulsecore/sndfile-util.h>
48 #include <pulsecore/core-util.h>
50 #define TIME_EVENT_USEC 50000
52 #define CLEAR_LINE "\x1B[K"
54 static enum { RECORD
, PLAYBACK
} mode
= PLAYBACK
;
56 static pa_context
*context
= NULL
;
57 static pa_stream
*stream
= NULL
;
58 static pa_mainloop_api
*mainloop_api
= NULL
;
60 static void *buffer
= NULL
;
61 static size_t buffer_length
= 0, buffer_index
= 0;
63 static pa_io_event
* stdio_event
= NULL
;
65 static pa_proplist
*proplist
= NULL
;
66 static char *device
= NULL
;
68 static SNDFILE
* sndfile
= NULL
;
70 static pa_bool_t verbose
= FALSE
;
71 static pa_volume_t volume
= PA_VOLUME_NORM
;
72 static pa_bool_t volume_is_set
= FALSE
;
74 static pa_sample_spec sample_spec
= {
75 .format
= PA_SAMPLE_S16LE
,
79 static pa_bool_t sample_spec_set
= FALSE
;
81 static pa_channel_map channel_map
;
82 static pa_bool_t channel_map_set
= FALSE
;
84 static sf_count_t (*readf_function
)(SNDFILE
*_sndfile
, void *ptr
, sf_count_t frames
) = NULL
;
85 static sf_count_t (*writef_function
)(SNDFILE
*_sndfile
, const void *ptr
, sf_count_t frames
) = NULL
;
87 static pa_stream_flags_t flags
= 0;
89 static size_t latency
= 0, process_time
= 0;
90 static int32_t latency_msec
= 0, process_time_msec
= 0;
92 static pa_bool_t raw
= TRUE
;
93 static int file_format
= -1;
95 /* A shortcut for terminating the application */
96 static void quit(int ret
) {
97 pa_assert(mainloop_api
);
98 mainloop_api
->quit(mainloop_api
, ret
);
101 /* Connection draining complete */
102 static void context_drain_complete(pa_context
*c
, void *userdata
) {
103 pa_context_disconnect(c
);
106 /* Stream draining complete */
107 static void stream_drain_complete(pa_stream
*s
, int success
, void *userdata
) {
108 pa_operation
*o
= NULL
;
111 pa_log(_("Failed to drain stream: %s"), pa_strerror(pa_context_errno(context
)));
116 pa_log(_("Playback stream drained."));
118 pa_stream_disconnect(stream
);
119 pa_stream_unref(stream
);
122 if (!(o
= pa_context_drain(context
, context_drain_complete
, NULL
)))
123 pa_context_disconnect(context
);
125 pa_operation_unref(o
);
127 pa_log(_("Draining connection to server."));
132 static void start_drain(void) {
137 pa_stream_set_write_callback(stream
, NULL
, NULL
);
139 if (!(o
= pa_stream_drain(stream
, stream_drain_complete
, NULL
))) {
140 pa_log(_("pa_stream_drain(): %s"), pa_strerror(pa_context_errno(context
)));
145 pa_operation_unref(o
);
150 /* Write some data to the stream */
151 static void do_stream_write(size_t length
) {
155 if (!buffer
|| !buffer_length
)
159 if (l
> buffer_length
)
162 if (pa_stream_write(stream
, (uint8_t*) buffer
+ buffer_index
, l
, NULL
, 0, PA_SEEK_RELATIVE
) < 0) {
163 pa_log(_("pa_stream_write() failed: %s"), pa_strerror(pa_context_errno(context
)));
171 if (!buffer_length
) {
174 buffer_index
= buffer_length
= 0;
178 /* This is called whenever new data may be written to the stream */
179 static void stream_write_callback(pa_stream
*s
, size_t length
, void *userdata
) {
181 pa_assert(length
> 0);
187 mainloop_api
->io_enable(stdio_event
, PA_IO_EVENT_INPUT
);
192 do_stream_write(length
);
201 size_t data_length
= length
;
203 if (pa_stream_begin_write(s
, &data
, &data_length
) < 0) {
204 pa_log(_("pa_stream_begin_write() failed: %s"), pa_strerror(pa_context_errno(context
)));
209 if (readf_function
) {
210 size_t k
= pa_frame_size(&sample_spec
);
212 if ((bytes
= readf_function(sndfile
, data
, (sf_count_t
) (data_length
/k
))) > 0)
213 bytes
*= (sf_count_t
) k
;
216 bytes
= sf_read_raw(sndfile
, data
, (sf_count_t
) data_length
);
219 pa_stream_write(s
, data
, (size_t) bytes
, NULL
, 0, PA_SEEK_RELATIVE
);
221 pa_stream_cancel_write(s
);
224 if (bytes
< (sf_count_t
) data_length
) {
229 /* Request fulfilled */
230 if ((size_t) bytes
>= length
)
238 /* This is called whenever new data may is available */
239 static void stream_read_callback(pa_stream
*s
, size_t length
, void *userdata
) {
242 pa_assert(length
> 0);
248 mainloop_api
->io_enable(stdio_event
, PA_IO_EVENT_OUTPUT
);
250 while (pa_stream_readable_size(s
) > 0) {
253 if (pa_stream_peek(s
, &data
, &length
) < 0) {
254 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context
)));
260 pa_assert(length
> 0);
263 buffer
= pa_xrealloc(buffer
, buffer_length
+ length
);
264 memcpy((uint8_t*) buffer
+ buffer_length
, data
, length
);
265 buffer_length
+= length
;
267 buffer
= pa_xmalloc(length
);
268 memcpy(buffer
, data
, length
);
269 buffer_length
= length
;
279 while (pa_stream_readable_size(s
) > 0) {
283 if (pa_stream_peek(s
, &data
, &length
) < 0) {
284 pa_log(_("pa_stream_peek() failed: %s"), pa_strerror(pa_context_errno(context
)));
290 pa_assert(length
> 0);
292 if (writef_function
) {
293 size_t k
= pa_frame_size(&sample_spec
);
295 if ((bytes
= writef_function(sndfile
, data
, (sf_count_t
) (length
/k
))) > 0)
296 bytes
*= (sf_count_t
) k
;
299 bytes
= sf_write_raw(sndfile
, data
, (sf_count_t
) length
);
301 if (bytes
< (sf_count_t
) length
)
309 /* This routine is called whenever the stream state changes */
310 static void stream_state_callback(pa_stream
*s
, void *userdata
) {
313 switch (pa_stream_get_state(s
)) {
314 case PA_STREAM_CREATING
:
315 case PA_STREAM_TERMINATED
:
318 case PA_STREAM_READY
:
321 const pa_buffer_attr
*a
;
322 char cmt
[PA_CHANNEL_MAP_SNPRINT_MAX
], sst
[PA_SAMPLE_SPEC_SNPRINT_MAX
];
324 pa_log(_("Stream successfully created."));
326 if (!(a
= pa_stream_get_buffer_attr(s
)))
327 pa_log(_("pa_stream_get_buffer_attr() failed: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s
))));
330 if (mode
== PLAYBACK
)
331 pa_log(_("Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u"), a
->maxlength
, a
->tlength
, a
->prebuf
, a
->minreq
);
333 pa_assert(mode
== RECORD
);
334 pa_log(_("Buffer metrics: maxlength=%u, fragsize=%u"), a
->maxlength
, a
->fragsize
);
338 pa_log(_("Using sample spec '%s', channel map '%s'."),
339 pa_sample_spec_snprint(sst
, sizeof(sst
), pa_stream_get_sample_spec(s
)),
340 pa_channel_map_snprint(cmt
, sizeof(cmt
), pa_stream_get_channel_map(s
)));
342 pa_log(_("Connected to device %s (%u, %ssuspended)."),
343 pa_stream_get_device_name(s
),
344 pa_stream_get_device_index(s
),
345 pa_stream_is_suspended(s
) ? "" : "not ");
350 case PA_STREAM_FAILED
:
352 pa_log(_("Stream error: %s"), pa_strerror(pa_context_errno(pa_stream_get_context(s
))));
357 static void stream_suspended_callback(pa_stream
*s
, void *userdata
) {
361 if (pa_stream_is_suspended(s
))
362 pa_log(_("Stream device suspended.%s"), CLEAR_LINE
);
364 pa_log(_("Stream device resumed.%s"), CLEAR_LINE
);
368 static void stream_underflow_callback(pa_stream
*s
, void *userdata
) {
372 pa_log(_("Stream underrun.%s"), CLEAR_LINE
);
375 static void stream_overflow_callback(pa_stream
*s
, void *userdata
) {
379 pa_log(_("Stream overrun.%s"), CLEAR_LINE
);
382 static void stream_started_callback(pa_stream
*s
, void *userdata
) {
386 pa_log(_("Stream started.%s"), CLEAR_LINE
);
389 static void stream_moved_callback(pa_stream
*s
, void *userdata
) {
393 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
);
396 static void stream_buffer_attr_callback(pa_stream
*s
, void *userdata
) {
400 pa_log(_("Stream buffer attributes changed.%s"), CLEAR_LINE
);
403 static void stream_event_callback(pa_stream
*s
, const char *name
, pa_proplist
*pl
, void *userdata
) {
410 t
= pa_proplist_to_string_sep(pl
, ", ");
411 pa_log("Got event '%s', properties '%s'", name
, t
);
415 /* This is called whenever the context status changes */
416 static void context_state_callback(pa_context
*c
, void *userdata
) {
419 switch (pa_context_get_state(c
)) {
420 case PA_CONTEXT_CONNECTING
:
421 case PA_CONTEXT_AUTHORIZING
:
422 case PA_CONTEXT_SETTING_NAME
:
425 case PA_CONTEXT_READY
: {
426 pa_buffer_attr buffer_attr
;
432 pa_log(_("Connection established.%s"), CLEAR_LINE
);
434 if (!(stream
= pa_stream_new_with_proplist(c
, NULL
, &sample_spec
, &channel_map
, proplist
))) {
435 pa_log(_("pa_stream_new() failed: %s"), pa_strerror(pa_context_errno(c
)));
439 pa_stream_set_state_callback(stream
, stream_state_callback
, NULL
);
440 pa_stream_set_write_callback(stream
, stream_write_callback
, NULL
);
441 pa_stream_set_read_callback(stream
, stream_read_callback
, NULL
);
442 pa_stream_set_suspended_callback(stream
, stream_suspended_callback
, NULL
);
443 pa_stream_set_moved_callback(stream
, stream_moved_callback
, NULL
);
444 pa_stream_set_underflow_callback(stream
, stream_underflow_callback
, NULL
);
445 pa_stream_set_overflow_callback(stream
, stream_overflow_callback
, NULL
);
446 pa_stream_set_started_callback(stream
, stream_started_callback
, NULL
);
447 pa_stream_set_event_callback(stream
, stream_event_callback
, NULL
);
448 pa_stream_set_buffer_attr_callback(stream
, stream_buffer_attr_callback
, NULL
);
450 pa_zero(buffer_attr
);
451 buffer_attr
.maxlength
= (uint32_t) -1;
452 buffer_attr
.prebuf
= (uint32_t) -1;
454 if (latency_msec
> 0) {
455 buffer_attr
.fragsize
= buffer_attr
.tlength
= pa_usec_to_bytes(latency_msec
* PA_USEC_PER_MSEC
, &sample_spec
);
456 flags
|= PA_STREAM_ADJUST_LATENCY
;
457 } else if (latency
> 0) {
458 buffer_attr
.fragsize
= buffer_attr
.tlength
= (uint32_t) latency
;
459 flags
|= PA_STREAM_ADJUST_LATENCY
;
461 buffer_attr
.fragsize
= buffer_attr
.tlength
= (uint32_t) -1;
463 if (process_time_msec
> 0) {
464 buffer_attr
.minreq
= pa_usec_to_bytes(process_time_msec
* PA_USEC_PER_MSEC
, &sample_spec
);
465 } else if (process_time
> 0)
466 buffer_attr
.minreq
= (uint32_t) process_time
;
468 buffer_attr
.minreq
= (uint32_t) -1;
470 if (mode
== PLAYBACK
) {
472 if (pa_stream_connect_playback(stream
, device
, &buffer_attr
, flags
, volume_is_set
? pa_cvolume_set(&cv
, sample_spec
.channels
, volume
) : NULL
, NULL
) < 0) {
473 pa_log(_("pa_stream_connect_playback() failed: %s"), pa_strerror(pa_context_errno(c
)));
478 if (pa_stream_connect_record(stream
, device
, &buffer_attr
, flags
) < 0) {
479 pa_log(_("pa_stream_connect_record() failed: %s"), pa_strerror(pa_context_errno(c
)));
487 case PA_CONTEXT_TERMINATED
:
491 case PA_CONTEXT_FAILED
:
493 pa_log(_("Connection failure: %s"), pa_strerror(pa_context_errno(c
)));
504 /* New data on STDIN **/
505 static void stdin_callback(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
509 pa_assert(a
== mainloop_api
);
511 pa_assert(stdio_event
== e
);
514 mainloop_api
->io_enable(stdio_event
, PA_IO_EVENT_NULL
);
518 if (!stream
|| pa_stream_get_state(stream
) != PA_STREAM_READY
|| !(l
= w
= pa_stream_writable_size(stream
)))
521 buffer
= pa_xmalloc(l
);
523 if ((r
= read(fd
, buffer
, l
)) <= 0) {
526 pa_log(_("Got EOF."));
531 pa_log(_("read() failed: %s"), strerror(errno
));
535 mainloop_api
->io_free(stdio_event
);
540 buffer_length
= (uint32_t) r
;
547 /* Some data may be written to STDOUT */
548 static void stdout_callback(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t f
, void *userdata
) {
551 pa_assert(a
== mainloop_api
);
553 pa_assert(stdio_event
== e
);
556 mainloop_api
->io_enable(stdio_event
, PA_IO_EVENT_NULL
);
560 pa_assert(buffer_length
);
562 if ((r
= write(fd
, (uint8_t*) buffer
+buffer_index
, buffer_length
)) <= 0) {
563 pa_log(_("write() failed: %s"), strerror(errno
));
566 mainloop_api
->io_free(stdio_event
);
571 buffer_length
-= (uint32_t) r
;
572 buffer_index
+= (uint32_t) r
;
574 if (!buffer_length
) {
577 buffer_length
= buffer_index
= 0;
581 /* UNIX signal to quit recieved */
582 static void exit_signal_callback(pa_mainloop_api
*m
, pa_signal_event
*e
, int sig
, void *userdata
) {
584 pa_log(_("Got signal, exiting."));
588 /* Show the current latency */
589 static void stream_update_timing_callback(pa_stream
*s
, int success
, void *userdata
) {
596 pa_stream_get_time(s
, &usec
) < 0 ||
597 pa_stream_get_latency(s
, &l
, &negative
) < 0) {
598 pa_log(_("Failed to get latency: %s"), pa_strerror(pa_context_errno(context
)));
603 fprintf(stderr
, _("Time: %0.3f sec; Latency: %0.0f usec."),
604 (float) usec
/ 1000000,
605 (float) l
* (negative
?-1.0f
:1.0f
));
606 fprintf(stderr
, " \r");
610 /* Someone requested that the latency is shown */
611 static void sigusr1_signal_callback(pa_mainloop_api
*m
, pa_signal_event
*e
, int sig
, void *userdata
) {
616 pa_operation_unref(pa_stream_update_timing_info(stream
, stream_update_timing_callback
, NULL
));
620 static void time_event_callback(pa_mainloop_api
*m
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
621 if (stream
&& pa_stream_get_state(stream
) == PA_STREAM_READY
) {
623 if (!(o
= pa_stream_update_timing_info(stream
, stream_update_timing_callback
, NULL
)))
624 pa_log(_("pa_stream_update_timing_info() failed: %s"), pa_strerror(pa_context_errno(context
)));
626 pa_operation_unref(o
);
629 pa_context_rttime_restart(context
, e
, pa_rtclock_now() + TIME_EVENT_USEC
);
632 static void help(const char *argv0
) {
634 printf(_("%s [options]\n\n"
635 " -h, --help Show this help\n"
636 " --version Show version\n\n"
637 " -r, --record Create a connection for recording\n"
638 " -p, --playback Create a connection for playback\n\n"
639 " -v, --verbose Enable verbose operations\n\n"
640 " -s, --server=SERVER The name of the server to connect to\n"
641 " -d, --device=DEVICE The name of the sink/source to connect to\n"
642 " -n, --client-name=NAME How to call this client on the server\n"
643 " --stream-name=NAME How to call this stream on the server\n"
644 " --volume=VOLUME Specify the initial (linear) volume in range 0...65536\n"
645 " --rate=SAMPLERATE The sample rate in Hz (defaults to 44100)\n"
646 " --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le,\n"
647 " float32be, ulaw, alaw, s32le, s32be, s24le, s24be,\n"
648 " s24-32le, s24-32be (defaults to s16ne)\n"
649 " --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo\n"
651 " --channel-map=CHANNELMAP Channel map to use instead of the default\n"
652 " --fix-format Take the sample format from the sink the stream is\n"
653 " being connected to.\n"
654 " --fix-rate Take the sampling rate from the sink the stream is\n"
655 " being connected to.\n"
656 " --fix-channels Take the number of channels and the channel map\n"
657 " from the sink the stream is being connected to.\n"
658 " --no-remix Don't upmix or downmix channels.\n"
659 " --no-remap Map channels by index instead of name.\n"
660 " --latency=BYTES Request the specified latency in bytes.\n"
661 " --process-time=BYTES Request the specified process time per request in bytes.\n"
662 " --latency-msec=MSEC Request the specified latency in msec.\n"
663 " --process-time-msec=MSEC Request the specified process time per request in msec.\n"
664 " --property=PROPERTY=VALUE Set the specified property to the specified value.\n"
665 " --raw Record/play raw PCM data.\n"
666 " --passthrough passthrough data \n"
667 " --file-format[=FFORMAT] Record/play formatted PCM data.\n"
668 " --list-file-formats List available file formats.\n")
691 ARG_LIST_FILE_FORMATS
,
693 ARG_PROCESS_TIME_MSEC
696 int main(int argc
, char *argv
[]) {
697 pa_mainloop
* m
= NULL
;
699 char *bn
, *server
= NULL
;
700 pa_time_event
*time_event
= NULL
;
701 const char *filename
= NULL
;
703 static const struct option long_options
[] = {
704 {"record", 0, NULL
, 'r'},
705 {"playback", 0, NULL
, 'p'},
706 {"device", 1, NULL
, 'd'},
707 {"server", 1, NULL
, 's'},
708 {"client-name", 1, NULL
, 'n'},
709 {"stream-name", 1, NULL
, ARG_STREAM_NAME
},
710 {"version", 0, NULL
, ARG_VERSION
},
711 {"help", 0, NULL
, 'h'},
712 {"verbose", 0, NULL
, 'v'},
713 {"volume", 1, NULL
, ARG_VOLUME
},
714 {"rate", 1, NULL
, ARG_SAMPLERATE
},
715 {"format", 1, NULL
, ARG_SAMPLEFORMAT
},
716 {"channels", 1, NULL
, ARG_CHANNELS
},
717 {"channel-map", 1, NULL
, ARG_CHANNELMAP
},
718 {"fix-format", 0, NULL
, ARG_FIX_FORMAT
},
719 {"fix-rate", 0, NULL
, ARG_FIX_RATE
},
720 {"fix-channels", 0, NULL
, ARG_FIX_CHANNELS
},
721 {"no-remap", 0, NULL
, ARG_NO_REMAP
},
722 {"no-remix", 0, NULL
, ARG_NO_REMIX
},
723 {"latency", 1, NULL
, ARG_LATENCY
},
724 {"process-time", 1, NULL
, ARG_PROCESS_TIME
},
725 {"property", 1, NULL
, ARG_PROPERTY
},
726 {"raw", 0, NULL
, ARG_RAW
},
727 {"passthrough", 0, NULL
, ARG_PASSTHROUGH
},
728 {"file-format", 2, NULL
, ARG_FILE_FORMAT
},
729 {"list-file-formats", 0, NULL
, ARG_LIST_FILE_FORMATS
},
730 {"latency-msec", 1, NULL
, ARG_LATENCY_MSEC
},
731 {"process-time-msec", 1, NULL
, ARG_PROCESS_TIME_MSEC
},
735 setlocale(LC_ALL
, "");
736 bindtextdomain(GETTEXT_PACKAGE
, PULSE_LOCALEDIR
);
738 bn
= pa_path_get_filename(argv
[0]);
740 if (strstr(bn
, "play")) {
743 } else if (strstr(bn
, "record")) {
746 } else if (strstr(bn
, "cat")) {
749 } if (strstr(bn
, "rec") || strstr(bn
, "mon")) {
754 proplist
= pa_proplist_new();
756 while ((c
= getopt_long(argc
, argv
, "rpd:s:n:hv", long_options
, NULL
)) != -1) {
765 printf(_("pacat %s\n"
766 "Compiled with libpulse %s\n"
767 "Linked with libpulse %s\n"),
769 pa_get_headers_version(),
770 pa_get_library_version());
784 device
= pa_xstrdup(optarg
);
789 server
= pa_xstrdup(optarg
);
795 if (!(t
= pa_locale_to_utf8(optarg
)) ||
796 pa_proplist_sets(proplist
, PA_PROP_APPLICATION_NAME
, t
) < 0) {
798 pa_log(_("Invalid client name '%s'"), t
? t
: optarg
);
807 case ARG_STREAM_NAME
: {
810 if (!(t
= pa_locale_to_utf8(optarg
)) ||
811 pa_proplist_sets(proplist
, PA_PROP_MEDIA_NAME
, t
) < 0) {
813 pa_log(_("Invalid stream name '%s'"), t
? t
: optarg
);
827 int v
= atoi(optarg
);
828 volume
= v
< 0 ? 0U : (pa_volume_t
) v
;
829 volume_is_set
= TRUE
;
834 sample_spec
.channels
= (uint8_t) atoi(optarg
);
835 sample_spec_set
= TRUE
;
838 case ARG_SAMPLEFORMAT
:
839 sample_spec
.format
= pa_parse_sample_format(optarg
);
840 sample_spec_set
= TRUE
;
844 sample_spec
.rate
= (uint32_t) atoi(optarg
);
845 sample_spec_set
= TRUE
;
849 if (!pa_channel_map_parse(&channel_map
, optarg
)) {
850 pa_log(_("Invalid channel map '%s'"), optarg
);
854 channel_map_set
= TRUE
;
857 case ARG_FIX_CHANNELS
:
858 flags
|= PA_STREAM_FIX_CHANNELS
;
862 flags
|= PA_STREAM_FIX_RATE
;
866 flags
|= PA_STREAM_FIX_FORMAT
;
870 flags
|= PA_STREAM_NO_REMIX_CHANNELS
;
874 flags
|= PA_STREAM_NO_REMAP_CHANNELS
;
878 if (((latency
= (size_t) atoi(optarg
))) <= 0) {
879 pa_log(_("Invalid latency specification '%s'"), optarg
);
884 case ARG_PROCESS_TIME
:
885 if (((process_time
= (size_t) atoi(optarg
))) <= 0) {
886 pa_log(_("Invalid process time specification '%s'"), optarg
);
891 case ARG_LATENCY_MSEC
:
892 if (((latency_msec
= (int32_t) atoi(optarg
))) <= 0) {
893 pa_log(_("Invalid latency specification '%s'"), optarg
);
898 case ARG_PROCESS_TIME_MSEC
:
899 if (((process_time_msec
= (int32_t) atoi(optarg
))) <= 0) {
900 pa_log(_("Invalid process time specification '%s'"), optarg
);
908 if (!(t
= pa_locale_to_utf8(optarg
)) ||
909 pa_proplist_setp(proplist
, t
) < 0) {
912 pa_log(_("Invalid property '%s'"), optarg
);
924 case ARG_PASSTHROUGH
:
925 flags
|= PA_STREAM_PASSTHROUGH
;
928 case ARG_FILE_FORMAT
:
930 if ((file_format
= pa_sndfile_format_from_string(optarg
)) < 0) {
931 pa_log(_("Unknown file format %s."), optarg
);
939 case ARG_LIST_FILE_FORMATS
:
940 pa_sndfile_dump_formats();
949 if (!pa_sample_spec_valid(&sample_spec
)) {
950 pa_log(_("Invalid sample specification"));
954 if (optind
+1 == argc
) {
957 filename
= argv
[optind
];
959 if ((fd
= pa_open_cloexec(argv
[optind
], mode
== PLAYBACK
? O_RDONLY
: O_WRONLY
|O_TRUNC
|O_CREAT
, 0666)) < 0) {
960 pa_log(_("open(): %s"), strerror(errno
));
964 if (dup2(fd
, mode
== PLAYBACK
? STDIN_FILENO
: STDOUT_FILENO
) < 0) {
965 pa_log(_("dup2(): %s"), strerror(errno
));
971 } else if (optind
+1 <= argc
) {
972 pa_log(_("Too many arguments."));
980 if (mode
== RECORD
) {
981 /* This might patch up the sample spec */
982 if (pa_sndfile_write_sample_spec(&sfi
, &sample_spec
) < 0) {
983 pa_log(_("Failed to generate sample specification for file."));
987 if (file_format
<= 0) {
989 if (filename
&& (extension
= strrchr(filename
, '.')))
990 file_format
= pa_sndfile_format_from_string(extension
+1);
991 if (file_format
<= 0)
992 file_format
= SF_FORMAT_WAV
;
993 /* Transparently upgrade classic .wav to wavex for multichannel audio */
994 if (file_format
== SF_FORMAT_WAV
&&
995 (sample_spec
.channels
> 2 ||
997 !(sample_spec
.channels
== 1 && channel_map
.map
[0] == PA_CHANNEL_POSITION_MONO
) &&
998 !(sample_spec
.channels
== 2 && channel_map
.map
[0] == PA_CHANNEL_POSITION_LEFT
999 && channel_map
.map
[1] == PA_CHANNEL_POSITION_RIGHT
))))
1000 file_format
= SF_FORMAT_WAVEX
;
1003 sfi
.format
|= file_format
;
1006 if (!(sndfile
= sf_open_fd(mode
== RECORD
? STDOUT_FILENO
: STDIN_FILENO
,
1007 mode
== RECORD
? SFM_WRITE
: SFM_READ
,
1009 pa_log(_("Failed to open audio file."));
1013 if (mode
== PLAYBACK
) {
1014 if (sample_spec_set
)
1015 pa_log(_("Warning: specified sample specification will be overwritten with specification from file."));
1017 if (pa_sndfile_read_sample_spec(sndfile
, &sample_spec
) < 0) {
1018 pa_log(_("Failed to determine sample specification from file."));
1021 sample_spec_set
= TRUE
;
1023 if (!channel_map_set
) {
1024 /* Allow the user to overwrite the channel map on the command line */
1025 if (pa_sndfile_read_channel_map(sndfile
, &channel_map
) < 0) {
1026 if (sample_spec
.channels
> 2)
1027 pa_log(_("Warning: Failed to determine channel map from file."));
1029 channel_map_set
= TRUE
;
1034 if (!channel_map_set
)
1035 pa_channel_map_init_extend(&channel_map
, sample_spec
.channels
, PA_CHANNEL_MAP_DEFAULT
);
1037 if (!pa_channel_map_compatible(&channel_map
, &sample_spec
)) {
1038 pa_log(_("Channel map doesn't match sample specification"));
1045 if (mode
== PLAYBACK
)
1046 readf_function
= pa_sndfile_readf_function(&sample_spec
);
1048 if (pa_sndfile_write_channel_map(sndfile
, &channel_map
) < 0)
1049 pa_log(_("Warning: failed to write channel map to file."));
1051 writef_function
= pa_sndfile_writef_function(&sample_spec
);
1054 /* Fill in libsndfile prop list data */
1055 sfp
= pa_proplist_new();
1056 pa_sndfile_init_proplist(sndfile
, sfp
);
1057 pa_proplist_update(proplist
, PA_UPDATE_MERGE
, sfp
);
1058 pa_proplist_free(sfp
);
1062 char tss
[PA_SAMPLE_SPEC_SNPRINT_MAX
], tcm
[PA_CHANNEL_MAP_SNPRINT_MAX
];
1064 pa_log(_("Opening a %s stream with sample specification '%s' and channel map '%s'."),
1065 mode
== RECORD
? _("recording") : _("playback"),
1066 pa_sample_spec_snprint(tss
, sizeof(tss
), &sample_spec
),
1067 pa_channel_map_snprint(tcm
, sizeof(tcm
), &channel_map
));
1070 /* Fill in client name if none was set */
1071 if (!pa_proplist_contains(proplist
, PA_PROP_APPLICATION_NAME
)) {
1074 if ((t
= pa_locale_to_utf8(bn
))) {
1075 pa_proplist_sets(proplist
, PA_PROP_APPLICATION_NAME
, t
);
1080 /* Fill in media name if none was set */
1081 if (!pa_proplist_contains(proplist
, PA_PROP_MEDIA_NAME
)) {
1084 if ((t
= filename
) ||
1085 (t
= pa_proplist_gets(proplist
, PA_PROP_APPLICATION_NAME
)))
1086 pa_proplist_sets(proplist
, PA_PROP_MEDIA_NAME
, t
);
1089 /* Set up a new main loop */
1090 if (!(m
= pa_mainloop_new())) {
1091 pa_log(_("pa_mainloop_new() failed."));
1095 mainloop_api
= pa_mainloop_get_api(m
);
1097 pa_assert_se(pa_signal_init(mainloop_api
) == 0);
1098 pa_signal_new(SIGINT
, exit_signal_callback
, NULL
);
1099 pa_signal_new(SIGTERM
, exit_signal_callback
, NULL
);
1101 pa_signal_new(SIGUSR1
, sigusr1_signal_callback
, NULL
);
1103 pa_disable_sigpipe();
1106 if (!(stdio_event
= mainloop_api
->io_new(mainloop_api
,
1107 mode
== PLAYBACK
? STDIN_FILENO
: STDOUT_FILENO
,
1108 mode
== PLAYBACK
? PA_IO_EVENT_INPUT
: PA_IO_EVENT_OUTPUT
,
1109 mode
== PLAYBACK
? stdin_callback
: stdout_callback
, NULL
))) {
1110 pa_log(_("io_new() failed."));
1115 /* Create a new connection context */
1116 if (!(context
= pa_context_new_with_proplist(mainloop_api
, NULL
, proplist
))) {
1117 pa_log(_("pa_context_new() failed."));
1121 pa_context_set_state_callback(context
, context_state_callback
, NULL
);
1123 /* Connect the context */
1124 if (pa_context_connect(context
, server
, 0, NULL
) < 0) {
1125 pa_log(_("pa_context_connect() failed: %s"), pa_strerror(pa_context_errno(context
)));
1130 if (!(time_event
= pa_context_rttime_new(context
, pa_rtclock_now() + TIME_EVENT_USEC
, time_event_callback
, NULL
))) {
1131 pa_log(_("pa_context_rttime_new() failed."));
1136 /* Run the main loop */
1137 if (pa_mainloop_run(m
, &ret
) < 0) {
1138 pa_log(_("pa_mainloop_run() failed."));
1144 pa_stream_unref(stream
);
1147 pa_context_unref(context
);
1150 pa_assert(mainloop_api
);
1151 mainloop_api
->io_free(stdio_event
);
1155 pa_assert(mainloop_api
);
1156 mainloop_api
->time_free(time_event
);
1161 pa_mainloop_free(m
);
1173 pa_proplist_free(proplist
);