Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions tests/lib_aec/aec_unit_tests_new/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@

cmake_minimum_required(VERSION 3.21)
include($ENV{XMOS_CMAKE_PATH}/xcommon.cmake)
project(aec_unit_tests_new)

set(APP_HW_TARGET XK-EVK-XU316)
set(XMOS_SANDBOX_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../../)
set(CONFIG_XSCOPE_PATH ${XMOS_SANDBOX_DIR}/lib_voice/tests/shared/file_utils)

file(GLOB CONFIG_XSCOPE_REL_PATH
RELATIVE ${CMAKE_CURRENT_LIST_DIR}
CONFIGURE_DEPENDS
${CONFIG_XSCOPE_PATH}/config.xscope)

file(GLOB APP_C_SRCS
RELATIVE ${CMAKE_CURRENT_LIST_DIR}
CONFIGURE_DEPENDS
${XMOS_SANDBOX_DIR}/lib_voice/tests/lib_vnr/vnr_unit_tests/src/main.c
${XMOS_SANDBOX_DIR}/lib_voice/tests/shared/file_utils/src/*.c
${CMAKE_CURRENT_LIST_DIR}/src/*.c)

file(GLOB APP_XC_SRCS
RELATIVE ${CMAKE_CURRENT_LIST_DIR}
CONFIGURE_DEPENDS
${XMOS_SANDBOX_DIR}/lib_voice/tests/lib_vnr/vnr_unit_tests/src/main.xc)

set(APP_INCLUDES
${CMAKE_CURRENT_LIST_DIR}/src
${CMAKE_CURRENT_LIST_DIR}/../../shared/file_utils/src)

file(GLOB_RECURSE tests RELATIVE ${CMAKE_CURRENT_LIST_DIR} CONFIGURE_DEPENDS src/test*.c)

foreach(test_file ${tests})
get_filename_component(test_name ${test_file} NAME_WE)
set(SOURCE_FILES_${test_name} ${test_file})
if (NOT BUILD_NATIVE)
set(APP_DEPENDENT_MODULES "lib_voice"
"xscope_fileio")
set(APP_COMPILER_FLAGS_${test_name}
-report
-Os
-g
-Wno-xcore-fptrgroup
-DSPEEDUP_FACTOR=${TEST_SPEEDUP_FACTOR}
-DTEST_WAV_XSCOPE=1)

set(APP_XSCOPE_SRCS ${CONFIG_XSCOPE_REL_PATH})
else()
set(APP_DEPENDENT_MODULES "lib_voice")

set(APP_COMPILER_FLAGS_${test_name}
-Os
-DBUILD_NATIVE=1
-DX86_BUILD=1)
endif()
endforeach()

XMOS_REGISTER_APP()

85 changes: 85 additions & 0 deletions tests/lib_aec/aec_unit_tests_new/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Copyright 2022-2026 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import pytest
from pathlib import Path
import py_voice.modules.aec as aec
import py_voice.config.config as pv_config
import py_voice.core.fdaf_controller as fdc
import py_voice
import numpy as np
import py_vs_c_utils as pvc
from run_dut import run_dut

PY_VOICE_ROOT = Path(py_voice.__file__).resolve().parent
default_conf_path = PY_VOICE_ROOT / "config" / "defaults.json"
default_conf = pv_config.get_config_dict(default_conf_path)
bin_dir_path = Path(__file__).parent / "bin"

def gen_bfp(rng, len, hr_max, exps=(-31, 31)):
data = pvc.rand_int32_arr(rng, len, hr_max)
exp = rng.integers(exps[0], exps[1] + 1, size=1, dtype=np.int32)
data_fl64 = pvc.int32_to_double(data, exp)
data_int = np.concatenate([exp, data])
return data_int, data_fl64

def gen_bfps(rng, hr_max, shape, exps=(-31, 31)):
bfp_len = shape[-1]
bfp_num = 1
data_int = np.empty(0, dtype=np.int32)
data_fl64 = np.empty(0, dtype=np.float64)
for dim in range(len(shape) - 1):
bfp_num *= shape[dim]

for _ in range(bfp_num):
bfp_int, arr_fl64 = gen_bfp(rng, bfp_len, hr_max, exps)
data_int = np.append(data_int, bfp_int)
data_fl64 = np.append(data_fl64, arr_fl64)

return data_int, data_fl64

def set_aec_conf(y_ch, x_ch, main_ph, shadow_ph):
test_conf = default_conf
test_conf["general"]["modules"] = ["aec"]
test_conf["general"]["input_channel_count"] = y_ch + x_ch
test_conf["general"]["output_channel_count"] = y_ch
test_conf["aec"]["input_channel_count"] = y_ch + x_ch
test_conf["aec"]["output_channel_count"] = y_ch
test_conf["aec"]["y_channel_count"] = y_ch
test_conf["aec"]["x_channel_count"] = x_ch
test_conf["aec"]["phases"] = main_ph
test_conf["aec"]["phases_shadow"] = shadow_ph

return test_conf

@pytest.fixture
def aec_obj(y_ch, x_ch, main_ph, shadow_ph):
test_conf = set_aec_conf(y_ch, x_ch, main_ph, shadow_ph)

return aec.aec(test_conf)

@pytest.fixture
def fdaf_obj(y_ch, x_ch, main_ph, shadow_ph):
test_conf = set_aec_conf(y_ch, x_ch, main_ph, shadow_ph)

return fdc.fdaf_controller(test_conf, "aec")

@pytest.fixture
def dut_runner(request, target):
exe_path = bin_dir_path / request.node.originalname / f"aec_unit_tests_new_{request.node.originalname}"

def _run_dut(input_bin):
op, _ = run_dut(input_bin, exe_path, target)
return op

return _run_dut

@pytest.fixture
def rng():
return np.random.default_rng(1243)

def pytest_generate_tests(metafunc):
if "target" in metafunc.fixturenames:
metafunc.parametrize("target", [
'native',
'xs3a'
])
78 changes: 78 additions & 0 deletions tests/lib_aec/aec_unit_tests_new/src/test_calc_Error_and_Y_hat.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2022-2026 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "aec.h"
#include "aec_priv.h"

#define Y_CH 1
#define X_CH 2
#define MAIN_PH 5
#define SHADOW_PH 3

#define NUM_BINS ((AEC_PROC_FRAME_LENGTH / 2) + 1)
#define BFP_LEN (NUM_BINS * 2)
#define BFP_BIN_LEN (BFP_LEN + 1) // for exponent
#define Y_LEN (Y_CH * BFP_BIN_LEN)
#define X_LEN (X_CH * MAIN_PH * BFP_BIN_LEN)
#define H_HAT_LEN (Y_CH * X_CH * MAIN_PH * BFP_BIN_LEN)

static aec_state_t aec_state;
void test_init()
{
#if BUILD_NATIVE
aec_task_distribution_t tdist = aec_tdist_chans2_threads1;
#else
aec_task_distribution_t tdist = aec_tdist_chans2_threads2;
#endif

aec_init(&aec_state, Y_CH, X_CH, MAIN_PH, SHADOW_PH, &tdist);
}

void test(int32_t *output, int32_t *input)
{
aec_filter_state_t *state_ptr = &aec_state.main_state;
for(unsigned ch = 0; ch < Y_CH; ch++){
unsigned offset = ch * BFP_BIN_LEN;
bfp_complex_s32_init(&state_ptr->shared_state->Y[ch], (complex_s32_t *)&input[offset + 1], input[offset], NUM_BINS, 1);
}

for(unsigned ch = 0; ch < X_CH; ch++) {
for(unsigned ph = 0; ph < MAIN_PH; ph++) {
unsigned offset = (ch * MAIN_PH + ph) * BFP_BIN_LEN + Y_LEN;
bfp_complex_s32_init(&state_ptr->shared_state->X_fifo[ch][ph], (complex_s32_t *)&input[offset + 1], input[offset], NUM_BINS, 1);
}
}
//aec init only initialises the 2d Xfifo. Since we're using the 1d fifo for error computation, call aec_update_X_fifo_1d()
//to update the 1d Fifo
aec_update_X_fifo_1d(state_ptr);

for(unsigned ch = 0; ch < Y_CH; ch++) {
for(unsigned ph = 0; ph < (X_CH * MAIN_PH); ph++) {
unsigned offset = (ch * (X_CH * MAIN_PH) + ph) * BFP_BIN_LEN + Y_LEN + X_LEN;
bfp_complex_s32_init(&state_ptr->H_hat[ch][ph], (complex_s32_t *)&input[offset + 1], input[offset], NUM_BINS, 1);
}
}

for(unsigned ch = 0; ch < Y_CH; ch++) {
// Y_hat is accumulated via bfp_complex_s32_macc() inside aec_l2_calc_Error_and_Y_hat().
// The production pipeline clears Y_hat each frame in aec_process_frame(); do the same here
// to keep frames independent and prevent drift.
state_ptr->Y_hat[ch].exp = AEC_ZEROVAL_EXP;
state_ptr->Y_hat[ch].hr = AEC_ZEROVAL_HR;
memset(&state_ptr->Y_hat[ch].data[0], 0, NUM_BINS * sizeof(complex_s32_t));

aec_calc_Error_and_Y_hat(state_ptr, ch);

unsigned offset = ch * BFP_BIN_LEN * 2;
unsigned offset2 = offset + BFP_BIN_LEN;

memcpy(&output[offset], &state_ptr->Y_hat[ch].exp, sizeof(int32_t));
memcpy(&output[offset + 1], state_ptr->Y_hat[ch].data, BFP_LEN * sizeof(int32_t));
memcpy(&output[offset2], &state_ptr->Error[ch].exp, sizeof(int32_t));
memcpy(&output[offset2 + 1], state_ptr->Error[ch].data, BFP_LEN * sizeof(int32_t));
}
}
40 changes: 40 additions & 0 deletions tests/lib_aec/aec_unit_tests_new/src/test_calc_coherence.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2022-2026 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "aec.h"
#include "aec_priv.h"

static aec_state_t aec_state;
void test_init()
{
#if BUILD_NATIVE
aec_task_distribution_t tdist = aec_tdist_chans2_threads1;
#else
aec_task_distribution_t tdist = aec_tdist_chans2_threads2;
#endif

aec_init(&aec_state, 1, 1, 9, 0, &tdist);
aec_state.main_state.shared_state->ref_active_flag = 1;
}

void test(int32_t *output, int32_t *input)
{
// test_calc_coherence is interested in y_hat[240:480] and prev_y[0:240]
aec_state.main_state.shared_state->prev_y[0].data = &input[1];
aec_state.main_state.shared_state->prev_y[0].exp = input[0];

bfp_s32_init(&aec_state.main_state.y_hat[0], (int32_t*)&aec_state.main_state.Y_hat[0].data[0], 0, AEC_PROC_FRAME_LENGTH, 0);
memcpy(&aec_state.main_state.y_hat[0].data[AEC_FRAME_ADVANCE], &input[AEC_FRAME_ADVANCE + 1 + 1], AEC_FRAME_ADVANCE * sizeof(int32_t));
aec_state.main_state.y_hat[0].exp = input[AEC_FRAME_ADVANCE + 1];

aec_calc_coherence(&aec_state.main_state, 0);

coherence_mu_params_t *coh_mu_state_ptr = &aec_state.main_state.shared_state->coh_mu_state[0];

memcpy(output, &coh_mu_state_ptr->coh, sizeof(float_s32_t));
memcpy((int8_t *)output + sizeof(float_s32_t), &coh_mu_state_ptr->coh_slow, sizeof(float_s32_t));
}
38 changes: 38 additions & 0 deletions tests/lib_aec/aec_unit_tests_new/src/test_calc_corr_factor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2022-2026 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "aec.h"
#include "aec_priv.h"

#define TEST_FRAME_LEN (AEC_FRAME_ADVANCE - 32)

static aec_state_t aec_state;
void test_init()
{
#if BUILD_NATIVE
aec_task_distribution_t tdist = aec_tdist_chans2_threads1;
#else
aec_task_distribution_t tdist = aec_tdist_chans2_threads2;
#endif

aec_init(&aec_state, 1, 1, 10, 0, &tdist);
}

void test(int32_t *output, int32_t *input)
{
// aec_calc_corr_factor uses y_hat[240:480-32] and prev_y[0:240-32]
aec_state.main_state.shared_state->prev_y[0].data = &input[1];
aec_state.main_state.shared_state->prev_y[0].exp = input[0];

bfp_s32_init(&aec_state.main_state.y_hat[0], (int32_t*)&aec_state.main_state.Y_hat[0].data[0], 0, AEC_PROC_FRAME_LENGTH, 0);
memcpy(&aec_state.main_state.y_hat[0].data[AEC_FRAME_ADVANCE], &input[TEST_FRAME_LEN + 1 + 1], TEST_FRAME_LEN * sizeof(int32_t));
aec_state.main_state.y_hat[0].exp = input[AEC_PROC_FRAME_LENGTH + 1];

float_s32_t corr = aec_calc_corr_factor(&aec_state.main_state, 0);

memcpy(output, &corr, sizeof(float_s32_t));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2022-2026 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "aec.h"
#include "aec_priv.h"

#define TEST_FRAME_LEN (AEC_PROC_FRAME_LENGTH / 2 + 1)

static aec_state_t aec_state;
void test_init()
{
#if BUILD_NATIVE
aec_task_distribution_t tdist = aec_tdist_chans2_threads1;
#else
aec_task_distribution_t tdist = aec_tdist_chans2_threads2;
#endif

aec_init(&aec_state, 1, 1, 10, 0, &tdist);
}

void test(int32_t *output, int32_t *input)
{
bfp_complex_s32_t data;
float_s32_t energy;
bfp_complex_s32_init(&data, (int32_t *)&input[1], input[0], TEST_FRAME_LEN, 1);

aec_calc_freq_domain_energy(&energy, &data);

memcpy(output, &energy, sizeof(float_s32_t));
}
54 changes: 54 additions & 0 deletions tests/lib_aec/aec_unit_tests_new/src/test_calc_inv_X_energy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2022-2026 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#include "aec.h"
#include "aec_priv.h"

#define Y_CH 1
#define X_CH 2
#define MAIN_PH 6
#define SHADOW_PH 2

#define NUM_BINS ((AEC_PROC_FRAME_LENGTH / 2) + 1)
#define BFP_BIN_LEN (NUM_BINS + 1) // for exponent

static aec_state_t aec_state;
void test_init()
{
#if BUILD_NATIVE
aec_task_distribution_t tdist = aec_tdist_chans2_threads1;
#else
aec_task_distribution_t tdist = aec_tdist_chans2_threads2;
#endif

aec_init(&aec_state, Y_CH, X_CH, MAIN_PH, SHADOW_PH, &tdist);
}

void test(int32_t *output, int32_t *input)
{
int32_t is_shadow = input[0];
float_s32_t delta = {input[1], input[2]};

aec_filter_state_t *state_ptr = (is_shadow) ? &aec_state.shadow_state : &aec_state.main_state;

state_ptr->delta = delta;
for(unsigned ch = 0; ch < X_CH; ch++) {
unsigned offset = 3 + ch * BFP_BIN_LEN;
// unsigned offset2 = 3
int32_t scratch[NUM_BINS] = {0};
bfp_s32_init(&state_ptr->shared_state->sigma_XX[ch], &input[offset + 1], input[offset], NUM_BINS, 1);
offset += BFP_BIN_LEN * 2;
bfp_s32_init(&state_ptr->X_energy[ch], &input[offset + 1], input[offset], NUM_BINS, 1);
bfp_s32_init(&state_ptr->inv_X_energy[ch], scratch, 0, NUM_BINS, 0);

aec_calc_normalisation_spectrum(state_ptr, ch, is_shadow);

unsigned out_offset = ch * BFP_BIN_LEN;
memcpy(&output[out_offset], &state_ptr->inv_X_energy[ch].exp, sizeof(int32_t));
memcpy(&output[out_offset + 1], state_ptr->inv_X_energy[ch].data, NUM_BINS * sizeof(int32_t));
}
}
Loading