X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/935826f4f318a89a0a570f766deb54808a4f9683..fa499dad06ba6558111cdef64c18f2401e803cff:/polyp/module-combine.c diff --git a/polyp/module-combine.c b/polyp/module-combine.c index 49a6951e..95bd958f 100644 --- a/polyp/module-combine.c +++ b/polyp/module-combine.c @@ -4,7 +4,7 @@ This file is part of polypaudio. polypaudio is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published + it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. @@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Lesser General Public License along with polypaudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. @@ -36,22 +36,25 @@ #include "xmalloc.h" #include "modargs.h" #include "namereg.h" +#include "module-combine-symdef.h" PA_MODULE_AUTHOR("Lennart Poettering") PA_MODULE_DESCRIPTION("Combine multiple sinks to one") PA_MODULE_VERSION(PACKAGE_VERSION) -PA_MODULE_USAGE("sink_name= master= slave=") +PA_MODULE_USAGE("sink_name= master= slaves= adjust_time= resample_method=") #define DEFAULT_SINK_NAME "combined" #define MEMBLOCKQ_MAXLENGTH (1024*170) #define RENDER_SIZE (1024*10) -#define ADJUST_TIME 5 +#define DEFAULT_ADJUST_TIME 20 static const char* const valid_modargs[] = { "sink_name", "master", "slaves", + "adjust_time", + "resample_method", NULL }; @@ -60,7 +63,7 @@ struct output { struct pa_sink_input *sink_input; size_t counter; struct pa_memblockq *memblockq; - pa_usec_t sink_latency; + pa_usec_t total_latency; PA_LLIST_FIELDS(struct output); }; @@ -71,6 +74,7 @@ struct userdata { unsigned n_outputs; struct output *master; struct pa_time_event *time_event; + uint32_t adjust_time; PA_LLIST_HEAD(struct output, outputs); }; @@ -87,38 +91,44 @@ static void update_usage(struct userdata *u) { static void adjust_rates(struct userdata *u) { struct output *o; - pa_usec_t max = 0; + pa_usec_t max_sink_latency = 0, min_total_latency = (pa_usec_t) -1, target_latency; uint32_t base_rate; assert(u && u->sink); for (o = u->outputs; o; o = o->next) { - o->sink_latency = o->sink_input->sink ? pa_sink_get_latency(o->sink_input->sink) : 0; + 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; - if (o->sink_latency > max) - max = o->sink_latency; + if (o->total_latency < min_total_latency) + min_total_latency = o->total_latency; } - pa_log(__FILE__": [%s] maximum latency is %0.0f usec.\n", u->sink->name, (float) max); + assert(min_total_latency != (pa_usec_t) -1); + + target_latency = max_sink_latency > min_total_latency ? max_sink_latency : min_total_latency; + + pa_log(__FILE__": [%s] target latency is %0.0f usec.\n", u->sink->name, (float) target_latency); base_rate = u->sink->sample_spec.rate; for (o = u->outputs; o; o = o->next) { - pa_usec_t l; - uint32_t r = base_rate; - - l = o->sink_latency + pa_sink_input_get_latency(o->sink_input); - - if (l < max) - r -= (uint32_t) (((((double) max-l))/ADJUST_TIME)*r/ 1000000); - else if (l > max) - r += (uint32_t) (((((double) l-max))/ADJUST_TIME)*r/ 1000000); + uint32_t r = base_rate; + + 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); if (r < (uint32_t) (base_rate*0.9) || r > (uint32_t) (base_rate*1.1)) pa_log(__FILE__": [%s] sample rates too different, not adjusting (%u vs. %u).\n", o->sink_input->name, base_rate, r); - else - pa_log(__FILE__": [%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.\n", o->sink_input->name, r, (double) r / base_rate, (float) l); - - pa_sink_input_set_rate(o->sink_input, r); + else { + pa_log(__FILE__": [%s] new rate is %u Hz; ratio is %0.3f; latency is %0.0f usec.\n", o->sink_input->name, r, (double) r / base_rate, (float) o->total_latency); + pa_sink_input_set_rate(o->sink_input, r); + } } } @@ -146,7 +156,7 @@ static void time_callback(struct pa_mainloop_api*a, struct pa_time_event* e, con adjust_rates(u); gettimeofday(&n, NULL); - n.tv_sec += ADJUST_TIME; + n.tv_sec += u->adjust_time; u->sink->core->mainloop->time_restart(e, &n); } @@ -192,7 +202,7 @@ static pa_usec_t sink_get_latency_cb(struct pa_sink *s) { return pa_sink_input_get_latency(u->master->sink_input); } -static struct output *output_new(struct userdata *u, struct pa_sink *sink) { +static struct output *output_new(struct userdata *u, struct pa_sink *sink, int resample_method) { struct output *o = NULL; char t[256]; assert(u && sink && u->sink); @@ -204,7 +214,7 @@ static struct output *output_new(struct userdata *u, struct pa_sink *sink) { o->memblockq = pa_memblockq_new(MEMBLOCKQ_MAXLENGTH, MEMBLOCKQ_MAXLENGTH, pa_frame_size(&u->sink->sample_spec), 0, 0, sink->core->memblock_stat); snprintf(t, sizeof(t), "%s: output #%u", u->sink->name, u->n_outputs+1); - if (!(o->sink_input = pa_sink_input_new(sink, t, &u->sink->sample_spec, 1))) + if (!(o->sink_input = pa_sink_input_new(sink, t, &u->sink->sample_spec, 1, resample_method))) goto fail; o->sink_input->get_latency = sink_input_get_latency_cb; @@ -269,17 +279,25 @@ static void clear_up(struct userdata *u) { int pa__init(struct pa_core *c, struct pa_module*m) { struct userdata *u; struct pa_modargs *ma = NULL; - const char *master_name, *slaves; + const char *master_name, *slaves, *rm; struct pa_sink *master_sink; char *n = NULL; const char*split_state; struct timeval tv; + int resample_method = -1; assert(c && m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log(__FILE__": failed to parse module arguments\n"); goto fail; } + + if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) { + if ((resample_method = pa_parse_resample_method(rm)) < 0) { + pa_log(__FILE__": invalid resample method '%s'\n", rm); + goto fail; + } + } u = pa_xmalloc(sizeof(struct userdata)); m->userdata = u; @@ -289,8 +307,14 @@ int pa__init(struct pa_core *c, struct pa_module*m) { u->module = m; u->core = c; u->time_event = NULL; + u->adjust_time = DEFAULT_ADJUST_TIME; PA_LLIST_HEAD_INIT(struct output, u->outputs); + if (pa_modargs_get_value_u32(ma, "adjust_time", &u->adjust_time) < 0) { + pa_log(__FILE__": failed to parse adjust_time value\n"); + goto fail; + } + if (!(master_name = pa_modargs_get_value(ma, "master", NULL)) || !(slaves = pa_modargs_get_value(ma, "slaves", NULL))) { pa_log(__FILE__": no master or slave sinks specified\n"); goto fail; @@ -311,7 +335,7 @@ int pa__init(struct pa_core *c, struct pa_module*m) { u->sink->get_latency = sink_get_latency_cb; u->sink->userdata = u; - if (!(u->master = output_new(u, master_sink))) { + if (!(u->master = output_new(u, master_sink, resample_method))) { pa_log(__FILE__": failed to create master sink input on sink '%s'.\n", u->sink->name); goto fail; } @@ -327,7 +351,7 @@ int pa__init(struct pa_core *c, struct pa_module*m) { pa_xfree(n); - if (!output_new(u, slave_sink)) { + if (!output_new(u, slave_sink, resample_method)) { pa_log(__FILE__": failed to create slave sink input on sink '%s'.\n", slave_sink->name); goto fail; } @@ -336,9 +360,11 @@ int pa__init(struct pa_core *c, struct pa_module*m) { if (u->n_outputs <= 1) pa_log(__FILE__": WARNING: no slave sinks specified.\n"); - gettimeofday(&tv, NULL); - tv.tv_sec += ADJUST_TIME; - u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u); + if (u->adjust_time > 0) { + gettimeofday(&tv, NULL); + tv.tv_sec += u->adjust_time; + u->time_event = c->mainloop->time_new(c->mainloop, &tv, time_callback, u); + } pa_modargs_free(ma); return 0;