mirror of
				https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
				synced 2025-11-03 09:01:50 -05:00 
			
		
		
		
	echo-cancel: Add a standalone test program
This is useful to test the canceller implementation on data from disk rather than testing live. Handy for comparing implementations reliably.
This commit is contained in:
		
							parent
							
								
									a103e82029
								
							
						
					
					
						commit
						3f5c5582f4
					
				
					 2 changed files with 147 additions and 21 deletions
				
			
		| 
						 | 
				
			
			@ -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          #
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue