]> code.delx.au - pulseaudio/commitdiff
echo-cancel: Add a standalone test program
authorArun Raghavan <arun.raghavan@collabora.co.uk>
Mon, 26 Sep 2011 16:00:49 +0000 (21:30 +0530)
committerArun Raghavan <arun.raghavan@collabora.co.uk>
Mon, 10 Oct 2011 07:55:25 +0000 (13:25 +0530)
This is useful to test the canceller implementation on data from disk
rather than testing live. Handy for comparing implementations reliably.

src/Makefile.am
src/modules/echo-cancel/module-echo-cancel.c

index fdb2e99a6fa27b7de8b1b5e08fddf3b8613941c1..5637974f8a243b3bc82cae673590505eecf06732 100644 (file)
@@ -253,7 +253,8 @@ TESTS_norun = \
                flist-test \
                rtstutter \
                stripnul \
-               connect-stress
+               connect-stress \
+               echo-cancel-test
 
 if !OS_IS_WIN32
 TESTS += \
@@ -518,6 +519,11 @@ connect_stress_LDADD = $(AM_LDADD) libpulse.la libpulsecommon-@PA_MAJORMINOR@.la
 connect_stress_CFLAGS = $(AM_CFLAGS)
 connect_stress_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
+echo_cancel_test_SOURCES = $(module_echo_cancel_la_SOURCES)
+nodist_echo_cancel_test_SOURCES = $(nodist_module_echo_cancel_la_SOURCES)
+echo_cancel_test_LDADD = $(module_echo_cancel_la_LIBADD)
+echo_cancel_test_CFLAGS = $(module_echo_cancel_la_CFLAGS) -DECHO_CANCEL_TEST=1
+echo_cancel_test_LDFLAGS = $(AM_LDFLAGS) $(BINLDFLAGS)
 
 ###################################
 #         Common library          #
index c2db87efc2f773123245c28ab17304bc6a568367..10f411887844f965ddcc2553b68ee94153414669 100644 (file)
@@ -1322,6 +1322,37 @@ static pa_echo_canceller_method_t get_ec_method_from_string(const char *method)
         return PA_ECHO_CANCELLER_INVALID;
 }
 
+/* Common initialisation bits between module-echo-cancel and the standalone test program */
+static int init_common(pa_modargs *ma, struct userdata *u, pa_sample_spec *source_ss, pa_channel_map *source_map) {
+    pa_echo_canceller_method_t ec_method;
+
+    if (pa_modargs_get_sample_spec_and_channel_map(ma, source_ss, source_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+        pa_log("Invalid sample format specification or channel map");
+        goto fail;
+    }
+
+    u->ec = pa_xnew0(pa_echo_canceller, 1);
+    if (!u->ec) {
+        pa_log("Failed to alloc echo canceller");
+        goto fail;
+    }
+
+    if ((ec_method = get_ec_method_from_string(pa_modargs_get_value(ma, "aec_method", DEFAULT_ECHO_CANCELLER))) < 0) {
+        pa_log("Invalid echo canceller implementation");
+        goto fail;
+    }
+
+    u->ec->init = ec_table[ec_method].init;
+    u->ec->run = ec_table[ec_method].run;
+    u->ec->done = ec_table[ec_method].done;
+
+    return 0;
+
+fail:
+    return -1;
+}
+
+
 int pa__init(pa_module*m) {
     struct userdata *u;
     pa_sample_spec source_ss, sink_ss;
@@ -1334,7 +1365,6 @@ int pa__init(pa_module*m) {
     pa_source_new_data source_data;
     pa_sink_new_data sink_data;
     pa_memchunk silence;
-    pa_echo_canceller_method_t ec_method;
     uint32_t adjust_time_sec;
     pa_bool_t use_volume_sharing = TRUE;
 
@@ -1366,10 +1396,6 @@ int pa__init(pa_module*m) {
     source_ss.rate = DEFAULT_RATE;
     source_ss.channels = DEFAULT_CHANNELS;
     pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT);
-    if (pa_modargs_get_sample_spec_and_channel_map(ma, &source_ss, &source_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
-        pa_log("Invalid sample format specification or channel map");
-        goto fail;
-    }
 
     sink_ss = sink_master->sample_spec;
     sink_map = sink_master->channel_map;
@@ -1389,21 +1415,6 @@ int pa__init(pa_module*m) {
     m->userdata = u;
     u->dead = FALSE;
 
-    u->ec = pa_xnew0(pa_echo_canceller, 1);
-    if (!u->ec) {
-        pa_log("Failed to alloc echo canceller");
-        goto fail;
-    }
-
-    if ((ec_method = get_ec_method_from_string(pa_modargs_get_value(ma, "aec_method", DEFAULT_ECHO_CANCELLER))) < 0) {
-        pa_log("Invalid echo canceller implementation");
-        goto fail;
-    }
-
-    u->ec->init = ec_table[ec_method].init;
-    u->ec->run = ec_table[ec_method].run;
-    u->ec->done = ec_table[ec_method].done;
-
     adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC;
     if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) {
         pa_log("Failed to parse adjust_time value");
@@ -1427,8 +1438,12 @@ int pa__init(pa_module*m) {
         goto fail;
     }
 
+    if (init_common(ma, u, &source_ss, &source_map))
+        goto fail;
+
     u->asyncmsgq = pa_asyncmsgq_new(0);
     u->need_realign = TRUE;
+
     if (u->ec->init) {
         if (!u->ec->init(u->core, u->ec, &source_ss, &source_map, &sink_ss, &sink_map, &u->blocksize, pa_modargs_get_value(ma, "aec_args", NULL))) {
             pa_log("Failed to init AEC engine");
@@ -1725,3 +1740,108 @@ void pa__done(pa_module*m) {
 
     pa_xfree(u);
 }
+
+#ifdef ECHO_CANCEL_TEST
+/*
+ * Stand-alone test program for running in the canceller on pre-recorded files.
+ */
+int main(int argc, char* argv[]) {
+    struct userdata u;
+    pa_sample_spec source_ss, sink_ss;
+    pa_channel_map source_map, sink_map;
+    pa_modargs *ma = NULL;
+    uint8_t *rdata = NULL, *pdata = NULL, *cdata = NULL;
+    int ret = 0, unused;
+
+    pa_memzero(&u, sizeof(u));
+
+    if (argc < 4 || argc > 6) {
+        goto usage;
+    }
+
+    u.ec = pa_xnew0(pa_echo_canceller, 1);
+    if (!u.ec) {
+        pa_log("Failed to alloc echo canceller");
+        goto fail;
+    }
+
+    u.captured_file = fopen(argv[2], "r");
+    if (u.captured_file == NULL) {
+        perror ("fopen failed");
+        goto fail;
+    }
+    u.played_file = fopen(argv[1], "r");
+    if (u.played_file == NULL) {
+        perror ("fopen failed");
+        goto fail;
+    }
+    u.canceled_file = fopen(argv[3], "wb");
+    if (u.canceled_file == NULL) {
+        perror ("fopen failed");
+        goto fail;
+    }
+
+    u.core = pa_xnew0(pa_core, 1);
+    u.core->cpu_info.cpu_type = PA_CPU_X86;
+    u.core->cpu_info.flags.x86 |= PA_CPU_X86_SSE;
+
+    if (!(ma = pa_modargs_new(argc > 4 ? argv[4] : NULL, valid_modargs))) {
+        pa_log("Failed to parse module arguments.");
+        goto fail;
+    }
+
+    source_ss.format = PA_SAMPLE_S16LE;
+    source_ss.rate = DEFAULT_RATE;
+    source_ss.channels = DEFAULT_CHANNELS;
+    pa_channel_map_init_auto(&source_map, source_ss.channels, PA_CHANNEL_MAP_DEFAULT);
+
+    init_common(ma, &u, &source_ss, &source_map);
+
+    if (!u.ec->init(u.core, u.ec, &source_ss, &source_map, &sink_ss, &sink_map, &u.blocksize,
+                     (argc > 4) ? argv[5] : NULL )) {
+        pa_log("Failed to init AEC engine");
+        goto fail;
+    }
+
+    rdata = pa_xmalloc(u.blocksize);
+    pdata = pa_xmalloc(u.blocksize);
+    cdata = pa_xmalloc(u.blocksize);
+
+    while (fread(rdata, u.blocksize, 1, u.captured_file) > 0) {
+        if (fread(pdata, u.blocksize, 1, u.played_file) == 0) {
+            perror("played file ended before captured file");
+            break;
+        }
+
+        u.ec->run(u.ec, rdata, pdata, cdata);
+
+        unused = fwrite(cdata, u.blocksize, 1, u.canceled_file);
+    }
+
+    u.ec->done(u.ec);
+
+    fclose(u.captured_file);
+    fclose(u.played_file);
+    fclose(u.canceled_file);
+
+out:
+    pa_xfree(rdata);
+    pa_xfree(pdata);
+    pa_xfree(cdata);
+
+    pa_xfree(u.ec);
+    pa_xfree(u.core);
+
+    if (ma)
+        pa_modargs_free(ma);
+
+    return ret;
+
+usage:
+    pa_log("Usage: %s play_file rec_file out_file [module args] [aec_args]",argv[0]);
+
+fail:
+    ret = -1;
+    goto out;
+}
+#endif /* ECHO_CANCEL_TEST */