]> code.delx.au - pulseaudio/blobdiff - polyp/module-combine.c
Make the whole stuff LGPL only
[pulseaudio] / polyp / module-combine.c
index 49a6951e299649ee04c59dac2b7e3a26ae0062d6..95bd958f9cad45319e86a64f7d69f71df69420f1 100644 (file)
@@ -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.
 #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=<name for the sink> master=<master sink> slave=<slave sinks>")
+PA_MODULE_USAGE("sink_name=<name for the sink> master=<master sink> slaves=<slave sinks> adjust_time=<seconds> resample_method=<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;