2004-07-16 19:56:36 +00:00
/***
2006-06-19 21:53:48 +00:00
This file is part of PulseAudio .
2007-01-04 13:43:45 +00:00
2007-02-13 15:35:19 +00:00
Copyright 2004 - 2006 Lennart Poettering
Copyright 2006 Pierre Ossman < ossman @ cendio . se > for Cendio AB
2006-06-19 21:53:48 +00:00
PulseAudio is free software ; you can redistribute it and / or modify
2004-11-14 14:58:54 +00:00
it under the terms of the GNU Lesser General Public License as published
2004-07-16 19:56:36 +00:00
by the Free Software Foundation ; either version 2 of the License ,
or ( at your option ) any later version .
2007-01-04 13:43:45 +00:00
2006-06-19 21:53:48 +00:00
PulseAudio is distributed in the hope that it will be useful , but
2004-07-16 19:56:36 +00:00
WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
2007-01-04 13:43:45 +00:00
2004-11-14 14:58:54 +00:00
You should have received a copy of the GNU Lesser General Public License
2006-06-19 21:53:48 +00:00
along with PulseAudio ; if not , write to the Free Software
2004-07-16 19:56:36 +00:00
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
2004-07-16 19:16:42 +00:00
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
2004-07-07 00:22:46 +00:00
# include <signal.h>
2004-06-23 23:17:30 +00:00
# include <string.h>
# include <errno.h>
# include <unistd.h>
# include <assert.h>
# include <stdio.h>
# include <stdlib.h>
2004-09-18 23:40:42 +00:00
# include <getopt.h>
2006-04-07 00:25:05 +00:00
# include <fcntl.h>
2008-08-06 18:54:13 +02:00
# include <locale.h>
2004-06-23 23:17:30 +00:00
2008-08-06 18:54:13 +02:00
# include <pulse/i18n.h>
2006-06-19 21:53:48 +00:00
# include <pulse/pulseaudio.h>
2006-04-12 17:18:24 +00:00
# define TIME_EVENT_USEC 50000
2004-06-23 23:17:30 +00:00
2008-05-15 23:34:41 +00:00
# define CLEAR_LINE "\x1B[K"
2004-07-10 16:50:22 +00:00
static enum { RECORD , PLAYBACK } mode = PLAYBACK ;
2006-01-11 01:17:39 +00:00
static pa_context * context = NULL ;
static pa_stream * stream = NULL ;
static pa_mainloop_api * mainloop_api = NULL ;
2004-06-23 23:17:30 +00:00
static void * buffer = NULL ;
static size_t buffer_length = 0 , buffer_index = 0 ;
2006-01-11 01:17:39 +00:00
static pa_io_event * stdio_event = NULL ;
2004-06-23 23:17:30 +00:00
2004-09-18 23:40:42 +00:00
static char * stream_name = NULL , * client_name = NULL , * device = NULL ;
static int verbose = 0 ;
static pa_volume_t volume = PA_VOLUME_NORM ;
2006-01-11 01:17:39 +00:00
static pa_sample_spec sample_spec = {
2004-09-29 22:04:44 +00:00
. format = PA_SAMPLE_S16LE ,
. rate = 44100 ,
. channels = 2
} ;
2006-04-26 16:07:33 +00:00
static pa_channel_map channel_map ;
static int channel_map_set = 0 ;
2007-11-21 01:30:40 +00:00
static pa_stream_flags_t flags = 0 ;
2008-05-15 23:34:41 +00:00
static size_t latency = 0 , process_time = 0 ;
2004-08-17 17:17:22 +00:00
/* A shortcut for terminating the application */
2004-07-07 00:22:46 +00:00
static void quit ( int ret ) {
assert ( mainloop_api ) ;
mainloop_api - > quit ( mainloop_api , ret ) ;
}
2004-08-17 17:17:22 +00:00
/* Write some data to the stream */
2004-07-10 16:50:22 +00:00
static void do_stream_write ( size_t length ) {
2004-06-23 23:17:30 +00:00
size_t l ;
2004-07-07 00:22:46 +00:00
assert ( length ) ;
if ( ! buffer | | ! buffer_length )
return ;
2007-01-04 13:43:45 +00:00
2004-06-23 23:17:30 +00:00
l = length ;
if ( l > buffer_length )
l = buffer_length ;
2007-01-04 13:43:45 +00:00
2006-02-21 01:22:42 +00:00
if ( pa_stream_write ( stream , ( uint8_t * ) buffer + buffer_index , l , NULL , 0 , PA_SEEK_RELATIVE ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_write() failed: %s \n " ) , pa_strerror ( pa_context_errno ( context ) ) ) ;
2006-02-21 01:22:42 +00:00
quit ( 1 ) ;
return ;
}
2007-01-04 13:43:45 +00:00
2004-06-23 23:17:30 +00:00
buffer_length - = l ;
buffer_index + = l ;
2007-01-04 13:43:45 +00:00
2004-06-23 23:17:30 +00:00
if ( ! buffer_length ) {
2006-05-17 19:26:54 +00:00
pa_xfree ( buffer ) ;
2004-06-23 23:17:30 +00:00
buffer = NULL ;
buffer_index = buffer_length = 0 ;
}
}
2004-08-17 17:17:22 +00:00
/* This is called whenever new data may be written to the stream */
2006-01-11 01:17:39 +00:00
static void stream_write_callback ( pa_stream * s , size_t length , void * userdata ) {
2007-11-21 01:30:40 +00:00
assert ( s ) ;
assert ( length > 0 ) ;
2004-07-07 00:22:46 +00:00
2004-08-05 19:53:57 +00:00
if ( stdio_event )
mainloop_api - > io_enable ( stdio_event , PA_IO_EVENT_INPUT ) ;
2004-06-27 17:50:02 +00:00
if ( ! buffer )
return ;
2004-07-10 16:50:22 +00:00
do_stream_write ( length ) ;
}
2004-08-17 17:17:22 +00:00
/* This is called whenever new data may is available */
2006-02-17 15:42:47 +00:00
static void stream_read_callback ( pa_stream * s , size_t length , void * userdata ) {
2006-02-20 04:05:16 +00:00
const void * data ;
2007-11-21 01:30:40 +00:00
assert ( s ) ;
assert ( length > 0 ) ;
2004-07-10 16:50:22 +00:00
2004-08-05 19:53:57 +00:00
if ( stdio_event )
mainloop_api - > io_enable ( stdio_event , PA_IO_EVENT_OUTPUT ) ;
2004-07-10 16:50:22 +00:00
2006-02-21 01:22:42 +00:00
if ( pa_stream_peek ( s , & data , & length ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_peek() failed: %s \n " ) , pa_strerror ( pa_context_errno ( context ) ) ) ;
2006-02-21 01:22:42 +00:00
quit ( 1 ) ;
return ;
}
2007-01-04 13:43:45 +00:00
2007-11-21 01:30:40 +00:00
assert ( data ) ;
assert ( length > 0 ) ;
2006-02-17 15:42:47 +00:00
2004-07-10 16:50:22 +00:00
if ( buffer ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Buffer overrun, dropping incoming data \n " ) ) ;
2006-02-21 01:22:42 +00:00
if ( pa_stream_drop ( s ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_drop() failed: %s \n " ) , pa_strerror ( pa_context_errno ( context ) ) ) ;
2006-02-21 01:22:42 +00:00
quit ( 1 ) ;
}
2004-07-10 16:50:22 +00:00
return ;
}
2006-05-17 19:26:54 +00:00
buffer = pa_xmalloc ( buffer_length = length ) ;
2004-07-10 16:50:22 +00:00
memcpy ( buffer , data , length ) ;
buffer_index = 0 ;
2006-02-17 15:42:47 +00:00
pa_stream_drop ( s ) ;
2004-06-27 17:50:02 +00:00
}
2004-08-17 17:17:22 +00:00
/* This routine is called whenever the stream state changes */
2006-01-11 01:17:39 +00:00
static void stream_state_callback ( pa_stream * s , void * userdata ) {
2004-07-06 00:08:44 +00:00
assert ( s ) ;
2004-06-23 23:17:30 +00:00
2004-08-14 20:25:32 +00:00
switch ( pa_stream_get_state ( s ) ) {
case PA_STREAM_CREATING :
2004-08-17 18:53:42 +00:00
case PA_STREAM_TERMINATED :
2004-08-14 20:25:32 +00:00
break ;
case PA_STREAM_READY :
2006-05-25 23:20:28 +00:00
if ( verbose ) {
2006-05-26 07:24:47 +00:00
const pa_buffer_attr * a ;
2007-11-21 01:30:40 +00:00
char cmt [ PA_CHANNEL_MAP_SNPRINT_MAX ] , sst [ PA_SAMPLE_SPEC_SNPRINT_MAX ] ;
2007-01-04 13:43:45 +00:00
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Stream successfully created. \n " ) ) ;
2006-05-25 23:20:28 +00:00
if ( ! ( a = pa_stream_get_buffer_attr ( s ) ) )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_get_buffer_attr() failed: %s \n " ) , pa_strerror ( pa_context_errno ( pa_stream_get_context ( s ) ) ) ) ;
2006-05-25 23:20:28 +00:00
else {
if ( mode = = PLAYBACK )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Buffer metrics: maxlength=%u, tlength=%u, prebuf=%u, minreq=%u \n " ) , a - > maxlength , a - > tlength , a - > prebuf , a - > minreq ) ;
2006-05-25 23:20:28 +00:00
else {
assert ( mode = = RECORD ) ;
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Buffer metrics: maxlength=%u, fragsize=%u \n " ) , a - > maxlength , a - > fragsize ) ;
2006-05-25 23:20:28 +00:00
}
}
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Using sample spec '%s', channel map '%s'. \n " ) ,
2007-11-21 01:30:40 +00:00
pa_sample_spec_snprint ( sst , sizeof ( sst ) , pa_stream_get_sample_spec ( s ) ) ,
pa_channel_map_snprint ( cmt , sizeof ( cmt ) , pa_stream_get_channel_map ( s ) ) ) ;
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Connected to device %s (%u, %ssuspended). \n " ) ,
2007-11-21 01:30:40 +00:00
pa_stream_get_device_name ( s ) ,
pa_stream_get_device_index ( s ) ,
pa_stream_is_suspended ( s ) ? " " : " not " ) ;
2006-05-25 23:20:28 +00:00
}
2007-01-04 13:43:45 +00:00
2004-08-14 20:25:32 +00:00
break ;
2007-01-04 13:43:45 +00:00
2004-08-14 20:25:32 +00:00
case PA_STREAM_FAILED :
default :
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Stream error: %s \n " ) , pa_strerror ( pa_context_errno ( pa_stream_get_context ( s ) ) ) ) ;
2004-08-14 20:25:32 +00:00
quit ( 1 ) ;
2004-06-23 23:17:30 +00:00
}
}
2007-11-21 01:30:40 +00:00
static void stream_suspended_callback ( pa_stream * s , void * userdata ) {
assert ( s ) ;
if ( verbose ) {
if ( pa_stream_is_suspended ( s ) )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Stream device suspended.%s \n " ) , CLEAR_LINE ) ;
2007-11-21 01:30:40 +00:00
else
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Stream device resumed.%s \n " ) , CLEAR_LINE ) ;
2007-11-21 01:30:40 +00:00
}
}
2008-05-15 23:34:41 +00:00
static void stream_underflow_callback ( pa_stream * s , void * userdata ) {
assert ( s ) ;
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Stream underrun.%s \n " ) , CLEAR_LINE ) ;
2008-05-15 23:34:41 +00:00
}
static void stream_overflow_callback ( pa_stream * s , void * userdata ) {
assert ( s ) ;
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Stream overrun.%s \n " ) , CLEAR_LINE ) ;
2008-05-15 23:34:41 +00:00
}
static void stream_started_callback ( pa_stream * s , void * userdata ) {
assert ( s ) ;
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Stream started.%s \n " ) , CLEAR_LINE ) ;
2008-05-15 23:34:41 +00:00
}
2007-11-21 01:30:40 +00:00
static void stream_moved_callback ( pa_stream * s , void * userdata ) {
assert ( s ) ;
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Stream moved to device %s (%u, %ssuspended).%s \n " ) , pa_stream_get_device_name ( s ) , pa_stream_get_device_index ( s ) , pa_stream_is_suspended ( s ) ? " " : _ ( " not " ) , CLEAR_LINE ) ;
2007-11-21 01:30:40 +00:00
}
2004-08-17 17:17:22 +00:00
/* This is called whenever the context status changes */
2006-01-11 01:17:39 +00:00
static void context_state_callback ( pa_context * c , void * userdata ) {
2004-08-14 20:25:32 +00:00
assert ( c ) ;
2004-07-07 00:22:46 +00:00
2004-08-14 20:25:32 +00:00
switch ( pa_context_get_state ( c ) ) {
case PA_CONTEXT_CONNECTING :
case PA_CONTEXT_AUTHORIZING :
case PA_CONTEXT_SETTING_NAME :
break ;
2007-01-04 13:43:45 +00:00
2006-02-21 01:22:42 +00:00
case PA_CONTEXT_READY : {
int r ;
2008-05-15 23:34:41 +00:00
pa_buffer_attr buffer_attr ;
2007-01-04 13:43:45 +00:00
2007-11-21 01:30:40 +00:00
assert ( c ) ;
assert ( ! stream ) ;
2004-08-14 20:25:32 +00:00
2004-09-18 23:40:42 +00:00
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Connection established.%s \n " ) , CLEAR_LINE ) ;
2004-09-18 23:40:42 +00:00
2006-04-26 16:07:33 +00:00
if ( ! ( stream = pa_stream_new ( c , stream_name , & sample_spec , channel_map_set ? & channel_map : NULL ) ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_new() failed: %s \n " ) , pa_strerror ( pa_context_errno ( c ) ) ) ;
2006-02-21 01:22:42 +00:00
goto fail ;
}
2004-08-14 20:25:32 +00:00
pa_stream_set_state_callback ( stream , stream_state_callback , NULL ) ;
pa_stream_set_write_callback ( stream , stream_write_callback , NULL ) ;
pa_stream_set_read_callback ( stream , stream_read_callback , NULL ) ;
2007-11-21 01:30:40 +00:00
pa_stream_set_suspended_callback ( stream , stream_suspended_callback , NULL ) ;
pa_stream_set_moved_callback ( stream , stream_moved_callback , NULL ) ;
2008-05-15 23:34:41 +00:00
pa_stream_set_underflow_callback ( stream , stream_underflow_callback , NULL ) ;
pa_stream_set_overflow_callback ( stream , stream_overflow_callback , NULL ) ;
pa_stream_set_started_callback ( stream , stream_started_callback , NULL ) ;
if ( latency > 0 ) {
memset ( & buffer_attr , 0 , sizeof ( buffer_attr ) ) ;
2008-08-19 22:39:54 +02:00
buffer_attr . tlength = ( uint32_t ) latency ;
buffer_attr . minreq = ( uint32_t ) process_time ;
2008-06-27 00:34:17 +02:00
buffer_attr . maxlength = ( uint32_t ) - 1 ;
buffer_attr . prebuf = ( uint32_t ) - 1 ;
2008-05-15 23:34:41 +00:00
flags | = PA_STREAM_ADJUST_LATENCY ;
}
2004-08-14 20:25:32 +00:00
2006-01-27 16:25:31 +00:00
if ( mode = = PLAYBACK ) {
pa_cvolume cv ;
2008-05-15 23:34:41 +00:00
if ( ( r = pa_stream_connect_playback ( stream , device , latency > 0 ? & buffer_attr : NULL , flags , pa_cvolume_set ( & cv , sample_spec . channels , volume ) , NULL ) ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_connect_playback() failed: %s \n " ) , pa_strerror ( pa_context_errno ( c ) ) ) ;
2006-02-21 01:22:42 +00:00
goto fail ;
}
2007-01-04 13:43:45 +00:00
2006-02-21 01:22:42 +00:00
} else {
2008-05-15 23:34:41 +00:00
if ( ( r = pa_stream_connect_record ( stream , device , latency > 0 ? & buffer_attr : NULL , flags ) ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_connect_record() failed: %s \n " ) , pa_strerror ( pa_context_errno ( c ) ) ) ;
2006-02-21 01:22:42 +00:00
goto fail ;
}
}
2007-01-04 13:43:45 +00:00
2004-08-14 20:25:32 +00:00
break ;
2006-02-21 01:22:42 +00:00
}
2007-01-04 13:43:45 +00:00
2004-08-14 20:25:32 +00:00
case PA_CONTEXT_TERMINATED :
quit ( 0 ) ;
break ;
case PA_CONTEXT_FAILED :
default :
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Connection failure: %s \n " ) , pa_strerror ( pa_context_errno ( c ) ) ) ;
2006-02-21 01:22:42 +00:00
goto fail ;
2004-06-23 23:17:30 +00:00
}
2006-02-21 01:22:42 +00:00
return ;
2007-01-04 13:43:45 +00:00
2006-02-21 01:22:42 +00:00
fail :
quit ( 1 ) ;
2007-01-04 13:43:45 +00:00
2004-07-07 00:22:46 +00:00
}
2004-08-17 17:17:22 +00:00
/* Connection draining complete */
2006-01-11 01:17:39 +00:00
static void context_drain_complete ( pa_context * c , void * userdata ) {
2004-08-14 20:25:32 +00:00
pa_context_disconnect ( c ) ;
2004-06-23 23:17:30 +00:00
}
2004-08-17 17:17:22 +00:00
/* Stream draining complete */
2006-01-11 01:17:39 +00:00
static void stream_drain_complete ( pa_stream * s , int success , void * userdata ) {
pa_operation * o ;
2004-08-14 20:25:32 +00:00
if ( ! success ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Failed to drain stream: %s \n " ) , pa_strerror ( pa_context_errno ( context ) ) ) ;
2004-08-14 20:25:32 +00:00
quit ( 1 ) ;
}
2007-01-04 13:43:45 +00:00
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Playback stream drained. \n " ) ) ;
2004-07-07 22:02:15 +00:00
2004-08-14 20:25:32 +00:00
pa_stream_disconnect ( stream ) ;
pa_stream_unref ( stream ) ;
2004-07-07 22:02:15 +00:00
stream = NULL ;
2007-01-04 13:43:45 +00:00
2004-08-14 20:25:32 +00:00
if ( ! ( o = pa_context_drain ( context , context_drain_complete , NULL ) ) )
pa_context_disconnect ( context ) ;
else {
2004-09-18 23:40:42 +00:00
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Draining connection to server. \n " ) ) ;
2004-08-14 20:25:32 +00:00
}
2004-07-07 22:02:15 +00:00
}
2004-08-17 17:17:22 +00:00
/* New data on STDIN **/
2006-01-27 16:25:31 +00:00
static void stdin_callback ( pa_mainloop_api * a , pa_io_event * e , int fd , pa_io_event_flags_t f , void * userdata ) {
2004-06-27 17:50:02 +00:00
size_t l , w = 0 ;
2004-06-23 23:17:30 +00:00
ssize_t r ;
2007-11-21 01:30:40 +00:00
assert ( a = = mainloop_api ) ;
assert ( e ) ;
assert ( stdio_event = = e ) ;
2004-06-23 23:17:30 +00:00
if ( buffer ) {
2004-08-05 19:53:57 +00:00
mainloop_api - > io_enable ( stdio_event , PA_IO_EVENT_NULL ) ;
2004-06-23 23:17:30 +00:00
return ;
}
2004-08-14 20:25:32 +00:00
if ( ! stream | | pa_stream_get_state ( stream ) ! = PA_STREAM_READY | | ! ( l = w = pa_stream_writable_size ( stream ) ) )
2004-06-23 23:17:30 +00:00
l = 4096 ;
2007-01-04 13:43:45 +00:00
2006-05-17 19:26:54 +00:00
buffer = pa_xmalloc ( l ) ;
2004-06-23 23:17:30 +00:00
if ( ( r = read ( fd , buffer , l ) ) < = 0 ) {
2004-07-07 00:22:46 +00:00
if ( r = = 0 ) {
2004-09-18 23:40:42 +00:00
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Got EOF. \n " ) ) ;
2006-02-21 01:22:42 +00:00
2006-07-29 15:02:24 +00:00
if ( stream ) {
pa_operation * o ;
2007-01-04 13:43:45 +00:00
2006-07-29 15:02:24 +00:00
if ( ! ( o = pa_stream_drain ( stream , stream_drain_complete , NULL ) ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_drain(): %s \n " ) , pa_strerror ( pa_context_errno ( context ) ) ) ;
2006-07-29 15:02:24 +00:00
quit ( 1 ) ;
return ;
}
2007-01-04 13:43:45 +00:00
2006-07-29 15:02:24 +00:00
pa_operation_unref ( o ) ;
} else
quit ( 0 ) ;
2007-01-04 13:43:45 +00:00
2004-07-07 00:22:46 +00:00
} else {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " read() failed: %s \n " ) , strerror ( errno ) ) ;
2004-07-07 00:22:46 +00:00
quit ( 1 ) ;
2004-06-23 23:17:30 +00:00
}
2004-08-05 19:53:57 +00:00
mainloop_api - > io_free ( stdio_event ) ;
stdio_event = NULL ;
2004-06-23 23:17:30 +00:00
return ;
}
2008-08-19 22:39:54 +02:00
buffer_length = ( uint32_t ) r ;
2004-06-23 23:17:30 +00:00
buffer_index = 0 ;
2004-06-27 17:50:02 +00:00
if ( w )
2004-07-10 16:50:22 +00:00
do_stream_write ( w ) ;
2004-06-23 23:17:30 +00:00
}
2004-08-17 17:17:22 +00:00
/* Some data may be written to STDOUT */
2006-01-27 16:25:31 +00:00
static void stdout_callback ( pa_mainloop_api * a , pa_io_event * e , int fd , pa_io_event_flags_t f , void * userdata ) {
2004-07-10 16:50:22 +00:00
ssize_t r ;
2007-11-21 01:30:40 +00:00
assert ( a = = mainloop_api ) ;
assert ( e ) ;
assert ( stdio_event = = e ) ;
2004-07-10 16:50:22 +00:00
if ( ! buffer ) {
2004-08-05 19:53:57 +00:00
mainloop_api - > io_enable ( stdio_event , PA_IO_EVENT_NULL ) ;
2004-07-10 16:50:22 +00:00
return ;
}
assert ( buffer_length ) ;
2007-01-04 13:43:45 +00:00
2004-09-01 00:46:56 +00:00
if ( ( r = write ( fd , ( uint8_t * ) buffer + buffer_index , buffer_length ) ) < = 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " write() failed: %s \n " ) , strerror ( errno ) ) ;
2004-07-10 16:50:22 +00:00
quit ( 1 ) ;
2004-08-05 19:53:57 +00:00
mainloop_api - > io_free ( stdio_event ) ;
stdio_event = NULL ;
2004-07-10 16:50:22 +00:00
return ;
}
2008-08-19 22:39:54 +02:00
buffer_length - = ( uint32_t ) r ;
buffer_index + = ( uint32_t ) r ;
2004-07-10 16:50:22 +00:00
if ( ! buffer_length ) {
2006-05-17 19:26:54 +00:00
pa_xfree ( buffer ) ;
2004-07-10 16:50:22 +00:00
buffer = NULL ;
buffer_length = buffer_index = 0 ;
}
}
2004-07-07 00:22:46 +00:00
2004-08-17 17:17:22 +00:00
/* UNIX signal to quit recieved */
2006-01-11 01:17:39 +00:00
static void exit_signal_callback ( pa_mainloop_api * m , pa_signal_event * e , int sig , void * userdata ) {
2004-09-18 23:40:42 +00:00
if ( verbose )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Got signal, exiting. \n " ) ) ;
2004-07-07 00:22:46 +00:00
quit ( 0 ) ;
}
2004-09-16 00:05:56 +00:00
/* Show the current latency */
2006-04-07 01:29:33 +00:00
static void stream_update_timing_callback ( pa_stream * s , int success , void * userdata ) {
2008-05-15 23:34:41 +00:00
pa_usec_t l , usec ;
2004-10-27 00:10:12 +00:00
int negative = 0 ;
2007-01-04 13:43:45 +00:00
2004-07-15 21:18:18 +00:00
assert ( s ) ;
2006-04-07 00:25:05 +00:00
if ( ! success | |
2006-04-12 17:18:24 +00:00
pa_stream_get_time ( s , & usec ) < 0 | |
2008-05-15 23:34:41 +00:00
pa_stream_get_latency ( s , & l , & negative ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Failed to get latency: %s \n " ) , pa_strerror ( pa_context_errno ( context ) ) ) ;
2004-07-15 21:18:18 +00:00
quit ( 1 ) ;
return ;
}
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Time: %0.3f sec; Latency: %0.0f usec. \r " ) ,
2006-04-12 17:18:24 +00:00
( float ) usec / 1000000 ,
2008-08-19 22:39:54 +02:00
( float ) l * ( negative ? - 1.0f : 1.0f ) ) ;
2004-07-15 21:18:18 +00:00
}
2004-08-17 17:17:22 +00:00
/* Someone requested that the latency is shown */
2006-01-11 01:17:39 +00:00
static void sigusr1_signal_callback ( pa_mainloop_api * m , pa_signal_event * e , int sig , void * userdata ) {
2006-04-12 17:18:24 +00:00
if ( ! stream )
return ;
2007-01-04 13:43:45 +00:00
2006-04-07 01:29:33 +00:00
pa_operation_unref ( pa_stream_update_timing_info ( stream , stream_update_timing_callback , NULL ) ) ;
2004-07-15 21:18:18 +00:00
}
2006-04-12 17:18:24 +00:00
static void time_event_callback ( pa_mainloop_api * m , pa_time_event * e , const struct timeval * tv , void * userdata ) {
struct timeval next ;
2007-01-04 13:43:45 +00:00
2006-04-12 23:19:07 +00:00
if ( stream & & pa_stream_get_state ( stream ) = = PA_STREAM_READY ) {
2006-04-12 23:12:54 +00:00
pa_operation * o ;
if ( ! ( o = pa_stream_update_timing_info ( stream , stream_update_timing_callback , NULL ) ) )
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_stream_update_timing_info() failed: %s \n " ) , pa_strerror ( pa_context_errno ( context ) ) ) ;
2006-04-12 23:12:54 +00:00
else
pa_operation_unref ( o ) ;
}
2006-04-12 17:18:24 +00:00
pa_gettimeofday ( & next ) ;
pa_timeval_add ( & next , TIME_EVENT_USEC ) ;
m - > time_restart ( e , & next ) ;
}
2004-09-18 23:40:42 +00:00
static void help ( const char * argv0 ) {
2008-08-06 18:54:13 +02:00
printf ( _ ( " %s [options] \n \n "
2004-09-18 23:40:42 +00:00
" -h, --help Show this help \n "
" --version Show version \n \n "
" -r, --record Create a connection for recording \n "
" -p, --playback Create a connection for playback \n \n "
" -v, --verbose Enable verbose operations \n \n "
" -s, --server=SERVER The name of the server to connect to \n "
" -d, --device=DEVICE The name of the sink/source to connect to \n "
" -n, --client-name=NAME How to call this client on the server \n "
" --stream-name=NAME How to call this stream on the server \n "
2006-04-26 16:27:59 +00:00
" --volume=VOLUME Specify the initial (linear) volume in range 0...65536 \n "
2004-09-29 22:04:44 +00:00
" --rate=SAMPLERATE The sample rate in Hz (defaults to 44100) \n "
" --format=SAMPLEFORMAT The sample type, one of s16le, s16be, u8, float32le, \n "
2008-09-05 00:39:36 +02:00
" float32be, ulaw, alaw, s32le, s32be (defaults to s16ne) \n "
2004-09-29 22:04:44 +00:00
" --channels=CHANNELS The number of channels, 1 for mono, 2 for stereo \n "
2006-04-26 16:07:33 +00:00
" (defaults to 2) \n "
2007-11-21 01:30:40 +00:00
" --channel-map=CHANNELMAP Channel map to use instead of the default \n "
" --fix-format Take the sample format from the sink the stream is \n "
" being connected to. \n "
" --fix-rate Take the sampling rate from the sink the stream is \n "
" being connected to. \n "
" --fix-channels Take the number of channels and the channel map \n "
" from the sink the stream is being connected to. \n "
" --no-remix Don't upmix or downmix channels. \n "
" --no-remap Map channels by index instead of name. \n "
2008-05-15 23:34:41 +00:00
" --latency=BYTES Request the specified latency in bytes. \n "
2008-08-06 18:54:13 +02:00
" --process-time=BYTES Request the specified process time per request in bytes. \n " )
2007-11-21 01:30:40 +00:00
,
2004-09-18 23:40:42 +00:00
argv0 ) ;
}
enum {
ARG_VERSION = 256 ,
ARG_STREAM_NAME ,
2004-09-29 22:04:44 +00:00
ARG_VOLUME ,
ARG_SAMPLERATE ,
ARG_SAMPLEFORMAT ,
2006-04-26 16:07:33 +00:00
ARG_CHANNELS ,
ARG_CHANNELMAP ,
2007-11-21 01:30:40 +00:00
ARG_FIX_FORMAT ,
ARG_FIX_RATE ,
ARG_FIX_CHANNELS ,
ARG_NO_REMAP ,
2008-05-15 23:34:41 +00:00
ARG_NO_REMIX ,
ARG_LATENCY ,
ARG_PROCESS_TIME
2004-09-18 23:40:42 +00:00
} ;
2004-06-23 23:17:30 +00:00
int main ( int argc , char * argv [ ] ) {
2006-01-11 01:17:39 +00:00
pa_mainloop * m = NULL ;
2004-09-18 23:40:42 +00:00
int ret = 1 , r , c ;
char * bn , * server = NULL ;
2006-04-12 17:18:24 +00:00
pa_time_event * time_event = NULL ;
2004-09-18 23:40:42 +00:00
static const struct option long_options [ ] = {
2008-05-15 23:34:41 +00:00
{ " record " , 0 , NULL , ' r ' } ,
{ " playback " , 0 , NULL , ' p ' } ,
{ " device " , 1 , NULL , ' d ' } ,
{ " server " , 1 , NULL , ' s ' } ,
{ " client-name " , 1 , NULL , ' n ' } ,
{ " stream-name " , 1 , NULL , ARG_STREAM_NAME } ,
{ " version " , 0 , NULL , ARG_VERSION } ,
{ " help " , 0 , NULL , ' h ' } ,
{ " verbose " , 0 , NULL , ' v ' } ,
{ " volume " , 1 , NULL , ARG_VOLUME } ,
{ " rate " , 1 , NULL , ARG_SAMPLERATE } ,
{ " format " , 1 , NULL , ARG_SAMPLEFORMAT } ,
{ " channels " , 1 , NULL , ARG_CHANNELS } ,
{ " channel-map " , 1 , NULL , ARG_CHANNELMAP } ,
{ " fix-format " , 0 , NULL , ARG_FIX_FORMAT } ,
{ " fix-rate " , 0 , NULL , ARG_FIX_RATE } ,
{ " fix-channels " , 0 , NULL , ARG_FIX_CHANNELS } ,
{ " no-remap " , 0 , NULL , ARG_NO_REMAP } ,
{ " no-remix " , 0 , NULL , ARG_NO_REMIX } ,
{ " latency " , 1 , NULL , ARG_LATENCY } ,
{ " process-time " , 1 , NULL , ARG_PROCESS_TIME } ,
{ NULL , 0 , NULL , 0 }
2004-09-18 23:40:42 +00:00
} ;
2004-06-23 23:17:30 +00:00
2008-08-06 18:54:13 +02:00
setlocale ( LC_ALL , " " ) ;
bindtextdomain ( GETTEXT_PACKAGE , PULSE_LOCALEDIR ) ;
2004-07-10 16:50:22 +00:00
if ( ! ( bn = strrchr ( argv [ 0 ] , ' / ' ) ) )
bn = argv [ 0 ] ;
2004-08-17 17:17:22 +00:00
else
bn + + ;
2004-07-10 16:50:22 +00:00
if ( strstr ( bn , " rec " ) | | strstr ( bn , " mon " ) )
mode = RECORD ;
else if ( strstr ( bn , " cat " ) | | strstr ( bn , " play " ) )
mode = PLAYBACK ;
2004-09-18 23:40:42 +00:00
while ( ( c = getopt_long ( argc , argv , " rpd:s:n:hv " , long_options , NULL ) ) ! = - 1 ) {
switch ( c ) {
case ' h ' :
help ( bn ) ;
ret = 0 ;
goto quit ;
2007-01-04 13:43:45 +00:00
2004-09-18 23:40:42 +00:00
case ARG_VERSION :
2008-08-06 18:54:13 +02:00
printf ( _ ( " pacat %s \n Compiled with libpulse %s \n Linked with libpulse %s \n " ) , PACKAGE_VERSION , pa_get_headers_version ( ) , pa_get_library_version ( ) ) ;
2004-09-18 23:40:42 +00:00
ret = 0 ;
goto quit ;
case ' r ' :
mode = RECORD ;
break ;
case ' p ' :
mode = PLAYBACK ;
break ;
case ' d ' :
2006-05-17 19:26:54 +00:00
pa_xfree ( device ) ;
device = pa_xstrdup ( optarg ) ;
2004-09-18 23:40:42 +00:00
break ;
case ' s ' :
2006-05-17 19:26:54 +00:00
pa_xfree ( server ) ;
server = pa_xstrdup ( optarg ) ;
2004-09-18 23:40:42 +00:00
break ;
case ' n ' :
2006-05-17 19:26:54 +00:00
pa_xfree ( client_name ) ;
client_name = pa_xstrdup ( optarg ) ;
2004-09-18 23:40:42 +00:00
break ;
case ARG_STREAM_NAME :
2006-05-17 19:26:54 +00:00
pa_xfree ( stream_name ) ;
stream_name = pa_xstrdup ( optarg ) ;
2004-09-18 23:40:42 +00:00
break ;
case ' v ' :
verbose = 1 ;
break ;
case ARG_VOLUME : {
int v = atoi ( optarg ) ;
2008-08-19 22:39:54 +02:00
volume = v < 0 ? 0U : ( pa_volume_t ) v ;
2004-09-18 23:40:42 +00:00
break ;
}
2007-01-04 13:43:45 +00:00
case ARG_CHANNELS :
2008-08-19 22:39:54 +02:00
sample_spec . channels = ( uint8_t ) atoi ( optarg ) ;
2004-09-29 22:04:44 +00:00
break ;
case ARG_SAMPLEFORMAT :
sample_spec . format = pa_parse_sample_format ( optarg ) ;
break ;
case ARG_SAMPLERATE :
2008-08-19 22:39:54 +02:00
sample_spec . rate = ( uint32_t ) atoi ( optarg ) ;
2004-09-29 22:04:44 +00:00
break ;
2006-04-26 16:07:33 +00:00
case ARG_CHANNELMAP :
if ( ! pa_channel_map_parse ( & channel_map , optarg ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Invalid channel map '%s' \n " ) , optarg ) ;
2006-04-26 16:07:33 +00:00
goto quit ;
}
channel_map_set = 1 ;
break ;
2007-01-04 13:43:45 +00:00
2007-11-21 01:30:40 +00:00
case ARG_FIX_CHANNELS :
flags | = PA_STREAM_FIX_CHANNELS ;
break ;
case ARG_FIX_RATE :
flags | = PA_STREAM_FIX_RATE ;
break ;
case ARG_FIX_FORMAT :
flags | = PA_STREAM_FIX_FORMAT ;
break ;
case ARG_NO_REMIX :
flags | = PA_STREAM_NO_REMIX_CHANNELS ;
break ;
case ARG_NO_REMAP :
flags | = PA_STREAM_NO_REMAP_CHANNELS ;
break ;
2008-05-15 23:34:41 +00:00
case ARG_LATENCY :
2008-08-19 22:39:54 +02:00
if ( ( ( latency = ( size_t ) atoi ( optarg ) ) ) < = 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Invalid latency specification '%s' \n " ) , optarg ) ;
2008-05-15 23:34:41 +00:00
goto quit ;
}
break ;
case ARG_PROCESS_TIME :
2008-08-19 22:39:54 +02:00
if ( ( ( process_time = ( size_t ) atoi ( optarg ) ) ) < = 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Invalid process time specification '%s' \n " ) , optarg ) ;
2008-05-15 23:34:41 +00:00
goto quit ;
}
break ;
2004-09-18 23:40:42 +00:00
default :
goto quit ;
2004-09-06 17:47:04 +00:00
}
}
2004-09-29 22:04:44 +00:00
if ( ! pa_sample_spec_valid ( & sample_spec ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Invalid sample specification \n " ) ) ;
2004-09-29 22:04:44 +00:00
goto quit ;
}
2006-04-26 16:07:33 +00:00
2008-09-05 01:31:17 +02:00
if ( channel_map_set & & pa_channel_map_compatible ( & channel_map , & sample_spec ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Channel map doesn't match sample specification \n " ) ) ;
2006-04-26 16:07:33 +00:00
goto quit ;
}
2007-01-04 13:43:45 +00:00
2004-09-29 22:04:44 +00:00
if ( verbose ) {
char t [ PA_SAMPLE_SPEC_SNPRINT_MAX ] ;
pa_sample_spec_snprint ( t , sizeof ( t ) , & sample_spec ) ;
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Opening a %s stream with sample specification '%s'. \n " ) , mode = = RECORD ? _ ( " recording " ) : _ ( " playback " ) , t ) ;
2004-09-29 22:04:44 +00:00
}
2004-08-17 17:17:22 +00:00
2006-04-10 19:42:14 +00:00
if ( ! ( optind > = argc ) ) {
if ( optind + 1 = = argc ) {
int fd ;
2007-01-04 13:43:45 +00:00
2006-04-12 23:55:21 +00:00
if ( ( fd = open ( argv [ optind ] , mode = = PLAYBACK ? O_RDONLY : O_WRONLY | O_TRUNC | O_CREAT , 0666 ) ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " open(): %s \n " ) , strerror ( errno ) ) ;
2006-04-10 19:42:14 +00:00
goto quit ;
}
2007-01-04 13:43:45 +00:00
2006-04-10 19:42:14 +00:00
if ( dup2 ( fd , mode = = PLAYBACK ? 0 : 1 ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " dup2(): %s \n " ) , strerror ( errno ) ) ;
2006-04-10 19:42:14 +00:00
goto quit ;
}
2007-01-04 13:43:45 +00:00
2006-04-10 19:42:14 +00:00
close ( fd ) ;
2006-05-14 16:17:17 +00:00
if ( ! stream_name )
2006-05-17 19:26:54 +00:00
stream_name = pa_xstrdup ( argv [ optind ] ) ;
2007-01-04 13:43:45 +00:00
2006-04-10 19:42:14 +00:00
} else {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " Too many arguments. \n " ) ) ;
2006-04-07 00:25:05 +00:00
goto quit ;
}
}
2006-05-14 16:17:17 +00:00
if ( ! client_name )
2006-05-17 19:26:54 +00:00
client_name = pa_xstrdup ( bn ) ;
2006-05-14 16:17:17 +00:00
if ( ! stream_name )
2006-05-17 19:26:54 +00:00
stream_name = pa_xstrdup ( client_name ) ;
2006-05-14 16:17:17 +00:00
2004-08-17 17:17:22 +00:00
/* Set up a new main loop */
2004-06-23 23:17:30 +00:00
if ( ! ( m = pa_mainloop_new ( ) ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_mainloop_new() failed. \n " ) ) ;
2004-06-23 23:17:30 +00:00
goto quit ;
}
mainloop_api = pa_mainloop_get_api ( m ) ;
2004-07-07 00:22:46 +00:00
r = pa_signal_init ( mainloop_api ) ;
assert ( r = = 0 ) ;
2004-08-05 19:53:57 +00:00
pa_signal_new ( SIGINT , exit_signal_callback , NULL ) ;
2004-12-12 22:58:53 +00:00
pa_signal_new ( SIGTERM , exit_signal_callback , NULL ) ;
2006-01-10 17:51:06 +00:00
# ifdef SIGUSR1
2004-08-05 19:53:57 +00:00
pa_signal_new ( SIGUSR1 , sigusr1_signal_callback , NULL ) ;
2006-01-10 17:51:06 +00:00
# endif
# ifdef SIGPIPE
2004-07-07 00:22:46 +00:00
signal ( SIGPIPE , SIG_IGN ) ;
2006-01-10 17:51:06 +00:00
# endif
2007-01-04 13:43:45 +00:00
2004-08-05 19:53:57 +00:00
if ( ! ( stdio_event = mainloop_api - > io_new ( mainloop_api ,
mode = = PLAYBACK ? STDIN_FILENO : STDOUT_FILENO ,
mode = = PLAYBACK ? PA_IO_EVENT_INPUT : PA_IO_EVENT_OUTPUT ,
mode = = PLAYBACK ? stdin_callback : stdout_callback , NULL ) ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " io_new() failed. \n " ) ) ;
2004-06-23 23:17:30 +00:00
goto quit ;
}
2004-08-17 17:17:22 +00:00
/* Create a new connection context */
2004-09-18 23:40:42 +00:00
if ( ! ( context = pa_context_new ( mainloop_api , client_name ) ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_context_new() failed. \n " ) ) ;
2004-06-23 23:17:30 +00:00
goto quit ;
}
2004-08-14 20:25:32 +00:00
pa_context_set_state_callback ( context , context_state_callback , NULL ) ;
2004-08-17 17:17:22 +00:00
/* Connect the context */
2006-02-20 23:29:46 +00:00
pa_context_connect ( context , server , 0 , NULL ) ;
2004-06-23 23:17:30 +00:00
2006-04-12 17:18:24 +00:00
if ( verbose ) {
struct timeval tv ;
pa_gettimeofday ( & tv ) ;
pa_timeval_add ( & tv , TIME_EVENT_USEC ) ;
2007-01-04 13:43:45 +00:00
2006-04-12 17:18:24 +00:00
if ( ! ( time_event = mainloop_api - > time_new ( mainloop_api , & tv , time_event_callback , NULL ) ) ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " time_new() failed. \n " ) ) ;
2006-04-12 17:18:24 +00:00
goto quit ;
}
}
2004-08-17 17:17:22 +00:00
/* Run the main loop */
2004-06-23 23:17:30 +00:00
if ( pa_mainloop_run ( m , & ret ) < 0 ) {
2008-08-06 18:54:13 +02:00
fprintf ( stderr , _ ( " pa_mainloop_run() failed. \n " ) ) ;
2004-06-23 23:17:30 +00:00
goto quit ;
}
2007-01-04 13:43:45 +00:00
2004-06-23 23:17:30 +00:00
quit :
if ( stream )
2004-08-14 20:25:32 +00:00
pa_stream_unref ( stream ) ;
2004-06-23 23:17:30 +00:00
if ( context )
2004-08-14 20:25:32 +00:00
pa_context_unref ( context ) ;
2004-07-10 16:50:22 +00:00
2004-08-14 20:25:32 +00:00
if ( stdio_event ) {
assert ( mainloop_api ) ;
mainloop_api - > io_free ( stdio_event ) ;
}
2006-04-12 17:18:24 +00:00
if ( time_event ) {
assert ( mainloop_api ) ;
mainloop_api - > time_free ( time_event ) ;
}
2007-01-04 13:43:45 +00:00
2004-07-15 20:51:55 +00:00
if ( m ) {
pa_signal_done ( ) ;
2004-06-23 23:17:30 +00:00
pa_mainloop_free ( m ) ;
2004-07-15 20:51:55 +00:00
}
2004-08-14 20:25:32 +00:00
2006-05-17 19:26:54 +00:00
pa_xfree ( buffer ) ;
2004-09-18 23:40:42 +00:00
2006-05-17 19:26:54 +00:00
pa_xfree ( server ) ;
pa_xfree ( device ) ;
pa_xfree ( client_name ) ;
pa_xfree ( stream_name ) ;
2007-01-04 13:43:45 +00:00
2004-06-23 23:17:30 +00:00
return ret ;
}