2004-09-14 17:52:11 +00:00
/* $Id$ */
/***
2006-06-19 21:53:48 +00:00
This file is part of PulseAudio .
2004-09-14 17:52:11 +00:00
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-09-14 17:52:11 +00:00
by the Free Software Foundation ; either version 2 of the License ,
or ( at your option ) any later version .
2006-06-19 21:53:48 +00:00
PulseAudio is distributed in the hope that it will be useful , but
2004-09-14 17:52:11 +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 .
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-09-14 17:52:11 +00:00
Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307
USA .
* * */
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include <assert.h>
# include <stdio.h>
2006-06-19 21:53:48 +00:00
# include <pulse/timeval.h>
# include <pulse/xmalloc.h>
2006-05-17 16:34:18 +00:00
2006-06-19 21:53:48 +00:00
# include <pulsecore/module.h>
# include <pulsecore/llist.h>
# include <pulsecore/sink.h>
# include <pulsecore/sink-input.h>
# include <pulsecore/memblockq.h>
# include <pulsecore/log.h>
# include <pulsecore/core-util.h>
# include <pulsecore/modargs.h>
# include <pulsecore/namereg.h>
2006-02-16 19:19:58 +00:00
2004-10-30 01:55:16 +00:00
# include "module-combine-symdef.h"
2004-09-14 17:52:11 +00:00
PA_MODULE_AUTHOR ( " Lennart Poettering " )
2004-09-14 20:53:25 +00:00
PA_MODULE_DESCRIPTION ( " Combine multiple sinks to one " )
2004-09-14 17:52:11 +00:00
PA_MODULE_VERSION ( PACKAGE_VERSION )
2006-04-26 15:40:14 +00:00
PA_MODULE_USAGE (
" sink_name=<name for the sink> "
" master=<master sink> "
" slaves=<slave sinks> "
" adjust_time=<seconds> "
" resample_method=<method> "
" format=<sample format> "
" channels=<number of channels> "
" rate=<sample rate> "
" channel_map=<channel map> " )
2004-09-14 17:52:11 +00:00
2004-09-14 23:08:39 +00:00
# define DEFAULT_SINK_NAME "combined"
2004-09-14 17:52:11 +00:00
# define MEMBLOCKQ_MAXLENGTH (1024*170)
# define RENDER_SIZE (1024*10)
2004-09-15 14:05:28 +00:00
# define DEFAULT_ADJUST_TIME 20
2004-09-14 17:52:11 +00:00
static const char * const valid_modargs [ ] = {
" sink_name " ,
" master " ,
" slaves " ,
2004-09-15 14:05:28 +00:00
" adjust_time " ,
2004-09-17 21:10:05 +00:00
" resample_method " ,
2006-04-26 15:40:14 +00:00
" format " ,
" channels " ,
" rate " ,
" channel_map " ,
2004-09-14 17:52:11 +00:00
NULL
} ;
struct output {
struct userdata * userdata ;
2006-01-11 01:17:39 +00:00
pa_sink_input * sink_input ;
2004-09-14 17:52:11 +00:00
size_t counter ;
2006-01-11 01:17:39 +00:00
pa_memblockq * memblockq ;
2004-09-16 22:07:41 +00:00
pa_usec_t total_latency ;
2004-09-14 17:52:11 +00:00
PA_LLIST_FIELDS ( struct output ) ;
} ;
struct userdata {
2006-01-11 01:17:39 +00:00
pa_module * module ;
pa_core * core ;
pa_sink * sink ;
2004-09-14 17:52:11 +00:00
unsigned n_outputs ;
struct output * master ;
2006-01-11 01:17:39 +00:00
pa_time_event * time_event ;
2004-09-15 14:05:28 +00:00
uint32_t adjust_time ;
2004-09-14 17:52:11 +00:00
PA_LLIST_HEAD ( struct output , outputs ) ;
} ;
static void output_free ( struct output * o ) ;
static void clear_up ( struct userdata * u ) ;
2004-09-14 23:08:39 +00:00
static void update_usage ( struct userdata * u ) {
2006-08-12 17:06:05 +00:00
pa_module_set_used ( u - > module , u - > sink ? pa_sink_used_by ( u - > sink ) : 0 ) ;
2004-09-14 23:08:39 +00:00
}
2004-09-14 17:52:11 +00:00
static void adjust_rates ( struct userdata * u ) {
struct output * o ;
2004-09-16 22:07:41 +00:00
pa_usec_t max_sink_latency = 0 , min_total_latency = ( pa_usec_t ) - 1 , target_latency ;
2004-09-14 17:52:11 +00:00
uint32_t base_rate ;
assert ( u & & u - > sink ) ;
for ( o = u - > outputs ; o ; o = o - > next ) {
2004-09-16 22:07:41 +00:00
uint32_t sink_latency = o - > sink_input - > sink ? pa_sink_get_latency ( o - > sink_input - > sink ) : 0 ;
o - > total_latency = sink_latency + pa_sink_input_get_latency ( o - > sink_input ) ;
if ( sink_latency > max_sink_latency )
max_sink_latency = sink_latency ;
2004-09-14 17:52:11 +00:00
2004-09-16 22:07:41 +00:00
if ( o - > total_latency < min_total_latency )
min_total_latency = o - > total_latency ;
2004-09-14 17:52:11 +00:00
}
2004-09-19 23:12:41 +00:00
assert ( min_total_latency ! = ( pa_usec_t ) - 1 ) ;
2004-09-16 22:07:41 +00:00
target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency ;
2006-02-23 02:27:19 +00:00
pa_log_info ( __FILE__ " : [%s] target latency is %0.0f usec. " , u - > sink - > name , ( float ) target_latency ) ;
2004-09-14 17:52:11 +00:00
base_rate = u - > sink - > sample_spec . rate ;
for ( o = u - > outputs ; o ; o = o - > next ) {
2004-09-26 22:27:04 +00:00
uint32_t r = base_rate ;
2004-09-16 22:07:41 +00:00
if ( o - > total_latency < target_latency )
r - = ( uint32_t ) ( ( ( ( ( double ) target_latency - o - > total_latency ) ) / u - > adjust_time ) * r / 1000000 ) ;
else if ( o - > total_latency > target_latency )
r + = ( uint32_t ) ( ( ( ( ( double ) o - > total_latency - target_latency ) ) / u - > adjust_time ) * r / 1000000 ) ;
2004-09-14 17:52:11 +00:00
if ( r < ( uint32_t ) ( base_rate * 0.9 ) | | r > ( uint32_t ) ( base_rate * 1.1 ) )
2006-02-23 02:27:19 +00:00
pa_log_warn ( __FILE__ " : [%s] sample rates too different, not adjusting (%u vs. %u). " , o - > sink_input - > name , base_rate , r ) ;
2004-09-16 22:07:41 +00:00
else {
2006-02-23 02:27:19 +00:00
pa_log_info ( __FILE__ " : [%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec. " , o - > sink_input - > name , r , ( double ) r / base_rate , ( float ) o - > total_latency ) ;
2004-09-16 22:07:41 +00:00
pa_sink_input_set_rate ( o - > sink_input , r ) ;
}
2004-09-14 17:52:11 +00:00
}
}
static void request_memblock ( struct userdata * u ) {
2006-01-11 01:17:39 +00:00
pa_memchunk chunk ;
2004-09-14 17:52:11 +00:00
struct output * o ;
assert ( u & & u - > sink ) ;
2004-09-14 23:08:39 +00:00
update_usage ( u ) ;
2004-09-14 17:52:11 +00:00
if ( pa_sink_render ( u - > sink , RENDER_SIZE , & chunk ) < 0 )
return ;
for ( o = u - > outputs ; o ; o = o - > next )
2006-02-20 04:05:16 +00:00
pa_memblockq_push_align ( o - > memblockq , & chunk ) ;
2004-09-14 17:52:11 +00:00
pa_memblock_unref ( chunk . memblock ) ;
}
2006-01-11 01:17:39 +00:00
static void time_callback ( pa_mainloop_api * a , pa_time_event * e , PA_GCC_UNUSED const struct timeval * tv , void * userdata ) {
2004-09-14 17:52:11 +00:00
struct userdata * u = userdata ;
struct timeval n ;
assert ( u & & a & & u - > time_event = = e ) ;
adjust_rates ( u ) ;
2006-01-10 17:51:06 +00:00
pa_gettimeofday ( & n ) ;
2004-09-15 14:05:28 +00:00
n . tv_sec + = u - > adjust_time ;
2004-09-14 17:52:11 +00:00
u - > sink - > core - > mainloop - > time_restart ( e , & n ) ;
}
2006-01-11 01:17:39 +00:00
static int sink_input_peek_cb ( pa_sink_input * i , pa_memchunk * chunk ) {
2004-09-14 17:52:11 +00:00
struct output * o = i - > userdata ;
assert ( i & & o & & o - > sink_input & & chunk ) ;
if ( pa_memblockq_peek ( o - > memblockq , chunk ) > = 0 )
return 0 ;
/* Try harder */
request_memblock ( o - > userdata ) ;
return pa_memblockq_peek ( o - > memblockq , chunk ) ;
}
2006-01-11 01:17:39 +00:00
static void sink_input_drop_cb ( pa_sink_input * i , const pa_memchunk * chunk , size_t length ) {
2004-09-14 17:52:11 +00:00
struct output * o = i - > userdata ;
assert ( i & & o & & o - > sink_input & & chunk & & length ) ;
pa_memblockq_drop ( o - > memblockq , chunk , length ) ;
o - > counter + = length ;
}
2006-01-11 01:17:39 +00:00
static void sink_input_kill_cb ( pa_sink_input * i ) {
2004-09-14 17:52:11 +00:00
struct output * o = i - > userdata ;
assert ( i & & o & & o - > sink_input ) ;
2004-09-14 20:53:25 +00:00
pa_module_unload_request ( o - > userdata - > module ) ;
2004-09-14 17:52:11 +00:00
clear_up ( o - > userdata ) ;
}
2006-01-11 01:17:39 +00:00
static pa_usec_t sink_input_get_latency_cb ( pa_sink_input * i ) {
2004-09-14 17:52:11 +00:00
struct output * o = i - > userdata ;
assert ( i & & o & & o - > sink_input ) ;
return pa_bytes_to_usec ( pa_memblockq_get_length ( o - > memblockq ) , & i - > sample_spec ) ;
}
2006-01-11 01:17:39 +00:00
static pa_usec_t sink_get_latency_cb ( pa_sink * s ) {
2004-09-14 17:52:11 +00:00
struct userdata * u = s - > userdata ;
assert ( s & & u & & u - > sink & & u - > master ) ;
2006-04-13 18:20:05 +00:00
return
pa_sink_input_get_latency ( u - > master - > sink_input ) +
pa_sink_get_latency ( u - > master - > sink_input - > sink ) ;
2004-09-14 17:52:11 +00:00
}
2006-01-11 01:17:39 +00:00
static struct output * output_new ( struct userdata * u , pa_sink * sink , int resample_method ) {
2004-09-14 17:52:11 +00:00
struct output * o = NULL ;
char t [ 256 ] ;
2006-08-13 16:19:56 +00:00
pa_sink_input_new_data data ;
2004-09-14 17:52:11 +00:00
assert ( u & & sink & & u - > sink ) ;
o = pa_xmalloc ( sizeof ( struct output ) ) ;
o - > userdata = u ;
o - > counter = 0 ;
2006-02-20 04:05:16 +00:00
o - > memblockq = pa_memblockq_new (
0 ,
MEMBLOCKQ_MAXLENGTH ,
MEMBLOCKQ_MAXLENGTH ,
pa_frame_size ( & u - > sink - > sample_spec ) ,
1 ,
0 ,
2006-08-18 19:55:18 +00:00
NULL ) ;
2004-09-14 17:52:11 +00:00
snprintf ( t , sizeof ( t ) , " %s: output #%u " , u - > sink - > name , u - > n_outputs + 1 ) ;
2006-08-13 16:19:56 +00:00
pa_sink_input_new_data_init ( & data ) ;
data . sink = sink ;
data . driver = __FILE__ ;
data . name = t ;
pa_sink_input_new_data_set_sample_spec ( & data , & u - > sink - > sample_spec ) ;
pa_sink_input_new_data_set_channel_map ( & data , & u - > sink - > channel_map ) ;
data . module = u - > module ;
if ( ! ( o - > sink_input = pa_sink_input_new ( u - > core , & data , PA_SINK_INPUT_VARIABLE_RATE ) ) )
2004-09-14 17:52:11 +00:00
goto fail ;
o - > sink_input - > get_latency = sink_input_get_latency_cb ;
o - > sink_input - > peek = sink_input_peek_cb ;
o - > sink_input - > drop = sink_input_drop_cb ;
o - > sink_input - > kill = sink_input_kill_cb ;
o - > sink_input - > userdata = o ;
PA_LLIST_PREPEND ( struct output , u - > outputs , o ) ;
u - > n_outputs + + ;
return o ;
fail :
if ( o ) {
2004-09-14 20:53:25 +00:00
if ( o - > sink_input ) {
pa_sink_input_disconnect ( o - > sink_input ) ;
pa_sink_input_unref ( o - > sink_input ) ;
}
2004-09-14 17:52:11 +00:00
if ( o - > memblockq )
pa_memblockq_free ( o - > memblockq ) ;
pa_xfree ( o ) ;
}
return NULL ;
}
static void output_free ( struct output * o ) {
assert ( o ) ;
PA_LLIST_REMOVE ( struct output , o - > userdata - > outputs , o ) ;
o - > userdata - > n_outputs - - ;
pa_memblockq_free ( o - > memblockq ) ;
2004-09-14 20:53:25 +00:00
pa_sink_input_disconnect ( o - > sink_input ) ;
pa_sink_input_unref ( o - > sink_input ) ;
2004-09-14 17:52:11 +00:00
pa_xfree ( o ) ;
}
static void clear_up ( struct userdata * u ) {
struct output * o ;
assert ( u ) ;
if ( u - > time_event ) {
u - > core - > mainloop - > time_free ( u - > time_event ) ;
u - > time_event = NULL ;
}
while ( ( o = u - > outputs ) )
output_free ( o ) ;
u - > master = NULL ;
if ( u - > sink ) {
2004-09-14 20:53:25 +00:00
pa_sink_disconnect ( u - > sink ) ;
pa_sink_unref ( u - > sink ) ;
2004-09-14 17:52:11 +00:00
u - > sink = NULL ;
}
}
2006-01-11 01:17:39 +00:00
int pa__init ( pa_core * c , pa_module * m ) {
2004-09-14 17:52:11 +00:00
struct userdata * u ;
2006-01-11 01:17:39 +00:00
pa_modargs * ma = NULL ;
2004-09-17 21:10:05 +00:00
const char * master_name , * slaves , * rm ;
2006-01-11 01:17:39 +00:00
pa_sink * master_sink ;
2004-09-14 17:52:11 +00:00
char * n = NULL ;
const char * split_state ;
struct timeval tv ;
2004-09-17 21:10:05 +00:00
int resample_method = - 1 ;
2006-04-26 15:40:14 +00:00
pa_sample_spec ss ;
pa_channel_map map ;
2004-09-14 17:52:11 +00:00
assert ( c & & m ) ;
if ( ! ( ma = pa_modargs_new ( m - > argument , valid_modargs ) ) ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : failed to parse module arguments " ) ;
2004-09-14 17:52:11 +00:00
goto fail ;
}
2004-09-17 21:10:05 +00:00
if ( ( rm = pa_modargs_get_value ( ma , " resample_method " , NULL ) ) ) {
if ( ( resample_method = pa_parse_resample_method ( rm ) ) < 0 ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : invalid resample method '%s' " , rm ) ;
2004-09-17 21:10:05 +00:00
goto fail ;
}
}
2004-09-14 17:52:11 +00:00
2006-04-26 15:40:14 +00:00
u = pa_xnew ( struct userdata , 1 ) ;
2004-09-14 17:52:11 +00:00
m - > userdata = u ;
u - > sink = NULL ;
u - > n_outputs = 0 ;
u - > master = NULL ;
u - > module = m ;
u - > core = c ;
u - > time_event = NULL ;
2004-09-15 14:05:28 +00:00
u - > adjust_time = DEFAULT_ADJUST_TIME ;
2004-09-14 17:52:11 +00:00
PA_LLIST_HEAD_INIT ( struct output , u - > outputs ) ;
2004-09-15 14:05:28 +00:00
if ( pa_modargs_get_value_u32 ( ma , " adjust_time " , & u - > adjust_time ) < 0 ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : failed to parse adjust_time value " ) ;
2004-09-15 14:05:28 +00:00
goto fail ;
}
2004-09-14 17:52:11 +00:00
if ( ! ( master_name = pa_modargs_get_value ( ma , " master " , NULL ) ) | | ! ( slaves = pa_modargs_get_value ( ma , " slaves " , NULL ) ) ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : no master or slave sinks specified " ) ;
2004-09-14 17:52:11 +00:00
goto fail ;
}
if ( ! ( master_sink = pa_namereg_get ( c , master_name , PA_NAMEREG_SINK , 1 ) ) ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : invalid master sink '%s' " , master_name ) ;
2004-09-14 17:52:11 +00:00
goto fail ;
}
2006-04-26 15:40:14 +00:00
ss = master_sink - > sample_spec ;
if ( ( pa_modargs_get_sample_spec ( ma , & ss ) < 0 ) ) {
pa_log ( __FILE__ " : invalid sample specification. " ) ;
goto fail ;
}
if ( ss . channels = = master_sink - > sample_spec . channels )
map = master_sink - > channel_map ;
else
2006-05-16 23:47:38 +00:00
pa_channel_map_init_auto ( & map , ss . channels , PA_CHANNEL_MAP_DEFAULT ) ;
2006-04-26 15:40:14 +00:00
if ( ( pa_modargs_get_channel_map ( ma , & map ) < 0 ) ) {
pa_log ( __FILE__ " : invalid channel map. " ) ;
goto fail ;
}
2006-04-26 16:50:36 +00:00
if ( ss . channels ! = map . channels ) {
pa_log ( __FILE__ " : channel map and sample specification don't match. " ) ;
goto fail ;
}
2006-04-26 15:40:14 +00:00
if ( ! ( u - > sink = pa_sink_new ( c , __FILE__ , pa_modargs_get_value ( ma , " sink_name " , DEFAULT_SINK_NAME ) , 0 , & ss , & map ) ) ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : failed to create sink " ) ;
2004-09-14 17:52:11 +00:00
goto fail ;
}
pa_sink_set_owner ( u - > sink , m ) ;
2006-08-11 23:58:55 +00:00
pa_sink_set_description ( u - > sink , " Combined sink " ) ;
2004-09-14 17:52:11 +00:00
u - > sink - > get_latency = sink_get_latency_cb ;
u - > sink - > userdata = u ;
2004-09-17 21:10:05 +00:00
if ( ! ( u - > master = output_new ( u , master_sink , resample_method ) ) ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : failed to create master sink input on sink '%s'. " , u - > sink - > name ) ;
2004-09-14 17:52:11 +00:00
goto fail ;
}
split_state = NULL ;
while ( ( n = pa_split ( slaves , " , " , & split_state ) ) ) {
2006-01-11 01:17:39 +00:00
pa_sink * slave_sink ;
2004-09-14 17:52:11 +00:00
if ( ! ( slave_sink = pa_namereg_get ( c , n , PA_NAMEREG_SINK , 1 ) ) ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : invalid slave sink '%s' " , n ) ;
2004-09-14 17:52:11 +00:00
goto fail ;
}
pa_xfree ( n ) ;
2004-09-17 21:10:05 +00:00
if ( ! output_new ( u , slave_sink , resample_method ) ) {
2006-02-23 02:27:19 +00:00
pa_log ( __FILE__ " : failed to create slave sink input on sink '%s'. " , slave_sink - > name ) ;
2004-09-14 17:52:11 +00:00
goto fail ;
}
}
if ( u - > n_outputs < = 1 )
2006-02-23 02:27:19 +00:00
pa_log_warn ( __FILE__ " : WARNING: no slave sinks specified. " ) ;
2004-09-14 17:52:11 +00:00
2004-09-15 14:05:28 +00:00
if ( u - > adjust_time > 0 ) {
2006-01-10 17:51:06 +00:00
pa_gettimeofday ( & tv ) ;
2004-09-15 14:05:28 +00:00
tv . tv_sec + = u - > adjust_time ;
u - > time_event = c - > mainloop - > time_new ( c - > mainloop , & tv , time_callback , u ) ;
}
2004-09-14 17:52:11 +00:00
pa_modargs_free ( ma ) ;
return 0 ;
fail :
pa_xfree ( n ) ;
if ( ma )
pa_modargs_free ( ma ) ;
pa__done ( c , m ) ;
return - 1 ;
}
2006-01-11 01:17:39 +00:00
void pa__done ( pa_core * c , pa_module * m ) {
2004-09-14 17:52:11 +00:00
struct userdata * u ;
assert ( c & & m ) ;
if ( ! ( u = m - > userdata ) )
return ;
clear_up ( u ) ;
pa_xfree ( u ) ;
}