├── lib_agc ├── doc │ └── rst │ │ ├── xdoc.conf │ │ ├── index.rst │ │ └── agc-internals.rst ├── .xproject ├── .makefile ├── module_build_info ├── generate_config.py ├── .project ├── api │ ├── agc_control_map.h │ ├── agc.h │ └── agc_control.h ├── config │ └── agc_2ch.json └── src │ ├── agc_ch_state.h │ ├── agc_control_map.xc │ ├── agc_control.xc │ └── agc.xc ├── python ├── __init__.py ├── setup.py ├── test_wav_agc.py └── agc.py ├── .github └── ISSUE_TEMPLATE │ ├── missing-c-api.md │ ├── missing-xcore-test.md │ ├── missing-application-note.md │ ├── top-level-readme-not-compliant.md │ ├── test-not-compliant.md │ ├── example-not-compliant.md │ └── application-note-not-compliant.md ├── cmake_utils ├── xc │ ├── CMakeTestXCCompiler.cmake │ ├── CMakeXCCompiler.cmake.in │ ├── CMakeDetermineXCCompiler.cmake │ └── CMakeXCInformation.cmake ├── xmos_toolchain.cmake ├── agc.cmake └── xmos_utils.cmake ├── .gitignore ├── tests ├── test_wav_agc │ ├── src │ │ ├── agc_conf.h │ │ ├── process_wav_conf.h │ │ └── test_wav_agc.xc │ ├── README.md │ ├── config.xscope │ ├── .project │ ├── CMakeLists.txt │ └── wscript └── agc_unit_tests │ ├── src │ ├── agc_unit_tests.h │ ├── agc_conf.h │ └── test_agc.xc │ ├── README.md │ ├── config.xscope │ ├── CMakeLists.txt │ ├── generate_unity_runners.py │ ├── conftest.py │ └── wscript ├── README.rst ├── requirements.txt ├── Jenkinsfile ├── CHANGELOG.rst └── LICENSE.rst /lib_agc/doc/rst/xdoc.conf: -------------------------------------------------------------------------------- 1 | XMOSNEWSTYLE = 1 2 | DOXYGEN_DIRS=../../api 3 | -------------------------------------------------------------------------------- /python/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2021 XMOS LIMITED. 2 | # This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | -------------------------------------------------------------------------------- /lib_agc/.xproject: -------------------------------------------------------------------------------- 1 | 2 | lib_agc 3 | 0.0.1dbbe4eb2632 4 | -------------------------------------------------------------------------------- /lib_agc/.makefile: -------------------------------------------------------------------------------- 1 | all: 2 | @echo "** Module only - only builds as part of application **" 3 | 4 | 5 | clean: 6 | @echo "** Module only - only builds as part of application **" 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/missing-c-api.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Missing C API Issue 3 | about: Template for a Missing C API issue report 4 | title: Missing C API 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /cmake_utils/xc/CMakeTestXCCompiler.cmake: -------------------------------------------------------------------------------- 1 | if(CMAKE_XC_COMPILER_FORCED) 2 | # The compiler configuration was forced by the user. 3 | # Assume the user has configured all compiler information. 4 | set(CMAKE_XC_COMPILER_WORKS TRUE CACHE INTERNAL "") 5 | return() 6 | endif() 7 | -------------------------------------------------------------------------------- /lib_agc/doc/rst/index.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../../README.rst 2 | 3 | Usage 4 | ----- 5 | 6 | .. include:: agc-internals.rst 7 | 8 | |newpage| 9 | 10 | |appendix| 11 | 12 | Known Issues 13 | ------------ 14 | 15 | None 16 | 17 | .. include:: ../../../CHANGELOG.rst 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/missing-xcore-test.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Missing xCore Test Issue 3 | about: Template for a Missing xCore test issue report 4 | title: Missing xCore test 5 | labels: testing 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/missing-application-note.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Missing Application Note 3 | about: Template for a Missing Application Note issue report 4 | title: Missing Application Note 5 | labels: documentation 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /cmake_utils/xc/CMakeXCCompiler.cmake.in: -------------------------------------------------------------------------------- 1 | set(CMAKE_XC_COMPILER "@CMAKE_XC_COMPILER@") 2 | set(CMAKE_XC_COMPILER_LOADED 1) 3 | set(CMAKE_XC_SOURCE_FILE_EXTENSIONS @CMAKE_XC_SOURCE_FILE_EXTENSIONS@) 4 | set(CMAKE_XC_OUTPUT_EXTENSION @CMAKE_XC_OUTPUT_EXTENSION@) 5 | set(CMAKE_XC_COMPILER_ENV_VAR "@CMAKE_XC_COMPILER_ENV_VAR@") 6 | 7 | set(CMAKE_LINKER "@CMAKE_LINKER@") 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/top-level-readme-not-compliant.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Top-level README Issue 3 | about: Template for the top-level README issue report 4 | title: Top-level README not compliant 5 | labels: documentation 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | **Issues:** 12 | 13 | - xdoc-custom RST directives used in the README 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.build*/* 2 | **/bin/* 3 | *.o 4 | *.xe 5 | *.vcd 6 | *.xi 7 | *.i 8 | *.a 9 | **/_build/ 10 | **/build/ 11 | **/run_*.log 12 | **/pdf/* 13 | **/html/* 14 | *.pyc 15 | **/.settings* 16 | Installs/* 17 | test_results.csv 18 | *.log 19 | .DS_Store 20 | xscope.xmt 21 | *.xc.pca.xml 22 | *.xc.pca.xml.decouple 23 | *.xc.d 24 | **/.lock-waf_darwin_build 25 | **/*.egg-info/ 26 | */.venv/* 27 | Pipfile.lock 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/test-not-compliant.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test Issue 3 | about: Template for a Test issue report 4 | title: tests/XXX does not build or fails 5 | labels: testing 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | **Issues:** 14 | 15 | - Test not compiling for xcore-ai 16 | - Test not running successfully on xcore-ai 17 | -------------------------------------------------------------------------------- /tests/test_wav_agc/src/agc_conf.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | 4 | #ifndef AGC_CONF_H_ 5 | #define AGC_CONF_H_ 6 | 7 | #define AGC_FRAME_ADVANCE (240) 8 | #define AGC_PROC_FRAME_LENGTH (AGC_FRAME_ADVANCE) 9 | 10 | #define AGC_INPUT_CHANNELS (2) 11 | #define AGC_CHANNEL_PAIRS ((AGC_INPUT_CHANNELS+1)/2) 12 | 13 | #endif /* AGC_CONF_H_ */ 14 | -------------------------------------------------------------------------------- /lib_agc/module_build_info: -------------------------------------------------------------------------------- 1 | VERSION = 8.2.0 2 | 3 | DEPENDENT_MODULES = lib_dsp(>=6.0.1) \ 4 | lib_voice_toolbox(>=8.0.0) \ 5 | lib_vad(>=1.0.3)) \ 6 | audio_test_tools(>=4.2.0) 7 | 8 | MODULE_XCC_FLAGS = $(XCC_FLAGS) \ 9 | -O2 10 | 11 | OPTIONAL_HEADERS += agc_conf.h 12 | 13 | EXPORT_INCLUDE_DIRS = api \ 14 | src 15 | 16 | INCLUDE_DIRS = $(EXPORT_INCLUDE_DIRS) 17 | 18 | SOURCE_DIRS = src 19 | -------------------------------------------------------------------------------- /tests/test_wav_agc/src/process_wav_conf.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #ifndef _PROCESS_WAV_CONF_H 4 | #define _PROCESS_WAV_CONF_H 5 | 6 | #define ATT_PW_INPUT_CHANNELS (2) 7 | #define ATT_PW_OUTPUT_CHANNELS (2) 8 | #define ATT_PW_PROC_FRAME_LENGTH (480) 9 | #define ATT_PW_FRAME_ADVANCE (240) 10 | #define ATT_PW_INPUT_FILE_NAME "input.wav" 11 | #define ATT_PW_OUTPUT_FILE_NAME "output.wav" 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/example-not-compliant.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Example Issue 3 | about: Template for an example issue report 4 | title: examples/xxx Example not compliant 5 | labels: documentation 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | **Issues:** 14 | 15 | - convert example to Application Note 16 | - xdoc-custom RST directives used in the README 17 | - example code not compiling for xcore-ai 18 | - example code not running successfully on xcore-ai 19 | -------------------------------------------------------------------------------- /lib_agc/generate_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright 2019-2021 XMOS LIMITED. 3 | # This Software is subject to the terms of the XMOS Public Licence: Version 1. 4 | from pathlib import Path 5 | from json_utils import JsonHandler 6 | 7 | module_dir = Path(__file__).parent 8 | file_conv = JsonHandler(str((module_dir / 'config/agc_2ch.json').resolve()), 9 | False) 10 | file_conv.create_header_file( 11 | str((module_dir / 'api/agc.h').resolve()), 12 | [str((module_dir / '../tests/test_wav_agc/src/agc_conf.h').resolve())] 13 | ) 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/application-note-not-compliant.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Application Note Issue 3 | about: Template for an Application note issue report 4 | title: examples/ANXXXXX Application Note not compliant 5 | labels: documentation 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | 12 | 13 | **Issues:** 14 | 15 | - xdoc-custom RST directives used in the Application Note README 16 | - application note code not compiling for xcore-ai 17 | - application note code not running successfully on xcore-ai 18 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/src/agc_unit_tests.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #ifndef AGC_UNIT_TESTS_H_ 4 | #define AGC_UNIT_TESTS_H_ 5 | 6 | #include "unity.h" 7 | 8 | #ifdef __XC__ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include "audio_test_tools.h" 17 | #include "voice_toolbox.h" 18 | 19 | #include "agc.h" 20 | #include "agc_control.h" 21 | 22 | #define TEST_ASM 1 23 | 24 | #endif // __XC__ 25 | 26 | #endif /* AGC_UNIT_TESTS_H_ */ 27 | -------------------------------------------------------------------------------- /cmake_utils/xc/CMakeDetermineXCCompiler.cmake: -------------------------------------------------------------------------------- 1 | if(DEFINED XMOS_TOOLS_PATH) 2 | set(CMAKE_XC_COMPILER "${XMOS_TOOLS_PATH}/xcc") 3 | else() 4 | set(CMAKE_XC_COMPILER "$ENV{XMOS_TOOL_PATH}/bin/xcc") 5 | endif() 6 | mark_as_advanced(CMAKE_XC_COMPILER) 7 | 8 | set(CMAKE_XC_SOURCE_FILE_EXTENSIONS xc;XC CACHE INTERNAL "") 9 | set(CMAKE_XC_OUTPUT_EXTENSION .obj CACHE INTERNAL "") 10 | set(CMAKE_XC_COMPILER_ENV_VAR "XC" CACHE INTERNAL "") 11 | set(CMAKE_XC_LINKER_PREFERENCE 99 CACHE INTERNAL "") 12 | set(CMAKE_XC_LINKER_PREFERENCE_PROPAGATES 1 CACHE INTERNAL "") 13 | 14 | # Configure variables set in this file for fast reload later on 15 | configure_file(${CMAKE_CURRENT_LIST_DIR}/CMakeXCCompiler.cmake.in 16 | ${CMAKE_PLATFORM_INFO_DIR}/CMakeXCCompiler.cmake) 17 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Automatic Gain Control Library 2 | ============================== 3 | 4 | Summary 5 | ------- 6 | 7 | This library implements an automatic gain control algorithm. Given a stream 8 | of audio samples in the time domain, it dynamically adapts the gain such that 9 | voice content maintains a specified output level. 10 | 11 | Features 12 | ........ 13 | 14 | * Supports multiple audio channels. 15 | * Configurable adaptive or fixed gain. 16 | * Configurable initial and maximum gain values. 17 | * Soft limiter applied to output. 18 | 19 | Software version and dependencies 20 | ................................. 21 | 22 | The CHANGELOG contains information about the current and previous versions. 23 | For a list of direct dependencies, look for DEPENDENT_MODULES in lib_agc/module_build_info. 24 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/README.md: -------------------------------------------------------------------------------- 1 | # agc_unit_tests test application 2 | 3 | This example builds the agc_unit_tests application 4 | 5 | ## Prerequisites for building 6 | 7 | [XMOS Toolchain 15.0.3](https://www.xmos.com/software/tools/) or newer. 8 | 9 | Install [CMake](https://cmake.org/download/) version 3.13 or newer. 10 | 11 | ## Building for xCORE 12 | 13 | Set environment variable for lib_agc path: 14 | 15 | > export AGC_PATH= 16 | 17 | cd to lib_agc/tests/agc_unit_tests 18 | 19 | Run cmake and build 20 | 21 | > cmake . 22 | > make 23 | 24 | ## Run on hardware 25 | 26 | Ensure your XCORE AI EXPLORER board is powered up and connected to the XTAG debugger. 27 | Make sure the input.wav file is copied into the build directory 28 | 29 | > pytest -n 1 30 | 31 | 32 | You should see the tests collected by pytest pass 33 | -------------------------------------------------------------------------------- /tests/test_wav_agc/README.md: -------------------------------------------------------------------------------- 1 | # test_wav_agc test application 2 | 3 | This example builds the test_wav_agc.xe application 4 | 5 | ## Prerequisites for building 6 | 7 | [XMOS Toolchain 15.0.3](https://www.xmos.com/software/tools/) or newer. 8 | 9 | Install [CMake](https://cmake.org/download/) version 3.13 or newer. 10 | 11 | ## Building for xCORE 12 | 13 | Set environment variable for lib_agc path: 14 | 15 | > export AGC_PATH= 16 | 17 | cd to lib_agc/tests/test_wav_agc 18 | 19 | Run cmake and build 20 | 21 | > cmake . -B build 22 | > cd build 23 | > make 24 | 25 | ## Run on hardware 26 | 27 | Ensure your XCORE AI EXPLORER board is powered up and connected to the XTAG debugger. 28 | Make sure the input.wav file is copied into the build directory 29 | 30 | > xrun --xscope test_wav_agc.xe 31 | 32 | 33 | You should see an output.wav file generated with the test_wav_agc output. 34 | -------------------------------------------------------------------------------- /tests/test_wav_agc/config.xscope: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/config.xscope: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /lib_agc/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | lib_agc 4 | 5 | 6 | 7 | 8 | 9 | com.xmos.cdt.core.ModulePathBuilder 10 | 11 | 12 | 13 | 14 | com.xmos.cdt.core.LegacyProjectCheckerBuilder 15 | 16 | 17 | 18 | 19 | com.xmos.cdt.core.ProjectInfoSyncBuilder 20 | 21 | 22 | 23 | 24 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 25 | clean,full,incremental, 26 | 27 | 28 | 29 | 30 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 31 | full,incremental, 32 | 33 | 34 | 35 | 36 | 37 | org.eclipse.cdt.core.cnature 38 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 39 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 40 | com.xmos.cdt.core.XdeProjectNature 41 | 42 | 43 | -------------------------------------------------------------------------------- /tests/test_wav_agc/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | test_wav_agc 4 | 5 | 6 | 7 | 8 | 9 | com.xmos.cdt.core.ProjectInfoSyncBuilder 10 | 11 | 12 | 13 | 14 | com.xmos.cdt.core.LegacyProjectCheckerBuilder 15 | 16 | 17 | 18 | 19 | com.xmos.cdt.core.ModulePathBuilder 20 | 21 | 22 | 23 | 24 | org.eclipse.cdt.managedbuilder.core.genmakebuilder 25 | clean,full,incremental, 26 | 27 | 28 | 29 | 30 | org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder 31 | full,incremental, 32 | 33 | 34 | 35 | 36 | 37 | org.eclipse.cdt.core.cnature 38 | org.eclipse.cdt.managedbuilder.core.managedBuildNature 39 | org.eclipse.cdt.managedbuilder.core.ScannerConfigNature 40 | com.xmos.cdt.core.XdeProjectNature 41 | 42 | 43 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019-2024 XMOS LIMITED. 2 | # This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | import setuptools 4 | 5 | # Another repository might depend on python code defined in this one. The 6 | # procedure to set up a suitable python environment for that repository may 7 | # pip-install this one as editable using this setup.py file. To minimise the 8 | # chance of version conflicts while ensuring a minimal degree of conformity, 9 | # the 3rd-party modules listed here require the same major version and at 10 | # least the same minor version as specified in the requirements.txt file. 11 | # The same modules should appear in the requirements.txt file as given below. 12 | setuptools.setup( 13 | name='lib_agc', 14 | packages=setuptools.find_packages(), 15 | install_requires=[ 16 | 'flake8~=3.8', 17 | 'matplotlib~=3.3', 18 | 'numpy~=1.18', 19 | 'pylint~=2.5', 20 | 'pytest~=6.0', 21 | 'pytest-xdist~=1.34', 22 | 'scipy~=1.4', 23 | 'webrtcvad~=2.0', 24 | 'audio_test_tools', 25 | 'lib_vad', 26 | 'lib_voice_toolbox', 27 | ], 28 | dependency_links=[ 29 | './../../audio_test_tools/python#egg=audio_test_tools', 30 | './../../lib_voice_toolbox#egg=lib_voice_toolbox', 31 | './../../lib_vad/python#egg=lib_vad', 32 | ], 33 | ) 34 | -------------------------------------------------------------------------------- /cmake_utils/xc/CMakeXCInformation.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeLanguageInformation) 2 | include(CMakeCommonLanguageInclude) 3 | 4 | if(NOT CMAKE_INCLUDE_FLAG_XC) 5 | set(CMAKE_INCLUDE_FLAG_XC ${CMAKE_INCLUDE_FLAG_C}) 6 | endif() 7 | 8 | set(CMAKE_XC_FLAGS_INIT "$ENV{XCFLAGS} ${CMAKE_XC_FLAGS_INIT}") 9 | 10 | # Create a static archive incrementally for large object file counts. 11 | # If CMAKE_CXX_CREATE_STATIC_LIBRARY is set it will override these. 12 | if(NOT DEFINED CMAKE_XC_ARCHIVE_CREATE) 13 | set(CMAKE_XC_ARCHIVE_CREATE " qc ") 14 | endif() 15 | if(NOT DEFINED CMAKE_XC_ARCHIVE_APPEND) 16 | set(CMAKE_XC_ARCHIVE_APPEND " q ") 17 | endif() 18 | if(NOT DEFINED CMAKE_XC_ARCHIVE_FINISH) 19 | set(CMAKE_XC_ARCHIVE_FINISH " ") 20 | endif() 21 | 22 | # Compile an XC file into an object file 23 | if(NOT CMAKE_XC_COMPILE_OBJECT) 24 | set(CMAKE_XC_COMPILE_OBJECT 25 | " -o -x xc -c ") 26 | endif() 27 | 28 | if(NOT CMAKE_XC_LINK_EXECUTABLE) 29 | set(CMAKE_XC_LINK_EXECUTABLE 30 | " -o ") 31 | endif() 32 | 33 | set(CMAKE_XC_CREATE_SHARED_LIBRARY " ") 34 | set(CMAKE_XC_CREATE_SHARED_MODULE "XC_NO_CREATE_SHARED_MODULE") 35 | 36 | set(CMAKE_XC_INFORMATION_LOADED 1) 37 | -------------------------------------------------------------------------------- /cmake_utils/xmos_toolchain.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME XMOS) 2 | 3 | list(APPEND CMAKE_MODULE_PATH "$ENV{AGC_PATH}/cmake_utils/xc") 4 | 5 | if(DEFINED XMOS_TOOLS_PATH) 6 | set(CMAKE_C_COMPILER "${XMOS_TOOLS_PATH}/xcc") 7 | set(CMAKE_XC_COMPILER "${XMOS_TOOLS_PATH}/xcc") 8 | set(CMAKE_CXX_COMPILER "${XMOS_TOOLS_PATH}/xcc") 9 | set(CMAKE_ASM_COMPILER "${XMOS_TOOLS_PATH}/xcc") 10 | set(CMAKE_AR "${XMOS_TOOLS_PATH}/xmosar") 11 | set(CMAKE_C_COMPILER_AR "${XMOS_TOOLS_PATH}/xmosar") 12 | set(CMAKE_XC_COMPILER_AR "${XMOS_TOOLS_PATH}/xmosar") 13 | set(CMAKE_CXX_COMPILER_AR "${XMOS_TOOLS_PATH}/xmosar") 14 | set(CMAKE_ASM_COMPILER_AR "${XMOS_TOOLS_PATH}/xmosar") 15 | else() 16 | message(WARNING "XMOS_TOOLS_PATH not specified. CMake will assume tools have been added to PATH.") 17 | set(CMAKE_C_COMPILER "xcc") 18 | set(CMAKE_XC_COMPILER "xcc") 19 | set(CMAKE_CXX_COMPILER "xcc") 20 | set(CMAKE_ASM_COMPILER "xcc") 21 | set(CMAKE_AR "xmosar") 22 | set(CMAKE_C_COMPILER_AR "xmosar") 23 | set(CMAKE_XC_COMPILER_AR "xmosar") 24 | set(CMAKE_CXX_COMPILER_AR "xmosar") 25 | set(CMAKE_ASM_COMPILER_AR "xmosar") 26 | endif() 27 | 28 | set(CMAKE_RANLIB "") 29 | set(CMAKE_C_COMPILER_FORCED TRUE) 30 | set(CMAKE_XC_COMPILER_FORCED TRUE) 31 | set(CMAKE_CXX_COMPILER_FORCED TRUE) 32 | set(CMAKE_ASM_COMPILER_FORCED TRUE) 33 | 34 | set(CMAKE_C_FLAGS "" CACHE STRING "C Compiler Base Flags" FORCE) 35 | set(CMAKE_XC_FLAGS "" CACHE STRING "XC Compiler Base Flags" FORCE) 36 | set(CMAKE_CXX_FLAGS "-std=c++11" CACHE STRING "C++ Compiler Base Flags" FORCE) 37 | set(CMAKE_EXE_LINKER_FLAGS "" CACHE INTERNAL "" FORCE) 38 | -------------------------------------------------------------------------------- /lib_agc/api/agc_control_map.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #ifndef AGC_CONTROL_MAP_H_ 4 | #define AGC_CONTROL_MAP_H_ 5 | #include "vtb_control.h" 6 | 7 | #define LC_N_FRAMES_NUM 2 8 | #define LC_GAMMAS_NUM 3 9 | #define LC_DELTAS_NUM 3 10 | #define LC_GAINS_NUM 4 11 | 12 | typedef enum { 13 | // get: MSB == 1 14 | agc_cmd_get_gain = 0x80, 15 | agc_cmd_get_max_gain, 16 | agc_cmd_get_adapt, 17 | agc_cmd_get_desired_upper_threshold, 18 | agc_cmd_get_desired_lower_threshold, 19 | agc_cmd_get_gain_increment_stepsize, 20 | agc_cmd_get_gain_decrement_stepsize, 21 | agc_cmd_get_lc_enabled, 22 | agc_cmd_get_adapt_on_vad, 23 | agc_cmd_get_soft_clipping, 24 | agc_cmd_get_min_gain, 25 | agc_cmd_get_lc_n_frames, 26 | agc_cmd_get_lc_corr_threshold, 27 | agc_cmd_get_lc_deltas, 28 | agc_cmd_get_lc_gains, 29 | agc_cmd_get_lc_gammas, 30 | agc_num_get_commands, 31 | 32 | // set: MSB == 0 33 | agc_cmd_set_gain = 0, 34 | agc_cmd_set_max_gain, 35 | agc_cmd_set_adapt, 36 | agc_cmd_set_desired_upper_threshold, 37 | agc_cmd_set_desired_lower_threshold, 38 | agc_cmd_set_gain_increment_stepsize, 39 | agc_cmd_set_gain_decrement_stepsize, 40 | agc_cmd_set_lc_enabled, 41 | agc_cmd_set_adapt_on_vad, 42 | agc_cmd_set_soft_clipping, 43 | agc_cmd_set_min_gain, 44 | agc_cmd_set_lc_n_frames, 45 | agc_cmd_set_lc_corr_threshold, 46 | agc_cmd_set_lc_deltas, 47 | agc_cmd_set_lc_gains, 48 | agc_cmd_set_lc_gammas, 49 | agc_num_set_commands, 50 | } agc_control_commands; 51 | 52 | #define agc_num_commands ((agc_num_get_commands - 0x80) + agc_num_set_commands) 53 | extern unsigned agc_control_map[agc_num_commands][3]; 54 | 55 | #endif // agc_CONTROL_MAP_H 56 | -------------------------------------------------------------------------------- /lib_agc/config/agc_2ch.json: -------------------------------------------------------------------------------- 1 | { 2 | "agc_init_config": { 3 | "ch_init_config": [ 4 | { 5 | "gain_dec": 0.87, 6 | "max_gain": 1000, 7 | "min_gain": 0, 8 | "gain_inc": 1.197, 9 | "adapt": 1, 10 | "adapt_on_vad": 1, 11 | "soft_clipping": 1, 12 | "init_gain": 500, 13 | "upper_threshold": 0.7079, 14 | "lower_threshold": 0.1905, 15 | "lc_enabled": 0, 16 | "lc_n_frame_near": 0, 17 | "lc_n_frame_far": 0, 18 | "lc_corr_threshold": 0, 19 | "lc_gamma_inc": 0, 20 | "lc_gamma_dec": 0, 21 | "lc_bg_power_gamma": 0, 22 | "lc_near_delta_far_act": 0, 23 | "lc_near_delta": 0, 24 | "lc_far_delta": 0, 25 | "lc_gain_max": 0, 26 | "lc_gain_dt": 0, 27 | "lc_gain_silence": 0, 28 | "lc_gain_min": 0 29 | 30 | }, 31 | { 32 | "gain_dec": 0.98804, 33 | "max_gain": 1000, 34 | "min_gain": 0, 35 | "gain_inc": 1.0034, 36 | "adapt": 1, 37 | "adapt_on_vad": 1, 38 | "soft_clipping": 1, 39 | "init_gain": 500, 40 | "upper_threshold": 0.4, 41 | "lower_threshold": 0.4, 42 | "lc_enabled": 0, 43 | "lc_n_frame_near": 34, 44 | "lc_n_frame_far": 17, 45 | "lc_corr_threshold": 0.993, 46 | "lc_gamma_inc": 1.005, 47 | "lc_gamma_dec": 0.995, 48 | "lc_bg_power_gamma": 1.002, 49 | "lc_near_delta_far_act": 100, 50 | "lc_near_delta": 50, 51 | "lc_far_delta": 300, 52 | "lc_gain_max": 1, 53 | "lc_gain_dt": 0.9, 54 | "lc_gain_silence": 0.1, 55 | "lc_gain_min": 0.022387 56 | } 57 | ] 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # python_version 3.7.6 2 | # pip_version 20.* 3 | # 4 | # The parse_version_from_requirements() function in the installPipfile.groovy 5 | # file of the Jenkins Shared Library uses the python_version comment to set 6 | # the version of python used. 7 | 8 | # Distributed (released) dependencies 9 | # 10 | # The python modules listed below specify a known working combination required 11 | # by the python code in this repository. The procedure used to set up a 12 | # suitable python environment for it installs the version of each module in 13 | # the list. Using a specific version ensures a controlled infrastructure for 14 | # development, testing and release of this repository. 15 | # 16 | # Another repository might depend on python code defined in this one. The 17 | # procedure to set up a suitable python environment for that repository may 18 | # pip-install this one as editable using this repository's setup.py file. The 19 | # same modules should appear in the setup.py list as given below. 20 | flake8==3.8.3 21 | 22 | # Pin importlib-metadata to <5 due to https://github.com/python/importlib_metadata/issues/409. 23 | importlib-metadata==4.13.0 24 | matplotlib==3.3.1 25 | 26 | # Pin numpy to 1.18.5 due to tensorflow v2.1.1 hard pinning it to that version. 27 | numpy==1.18.5 28 | pylint==2.5.3 29 | pytest==6.0.0 30 | pytest-xdist==1.34.0 31 | 32 | # Pin scipy to 1.4.1 due to tensorflow v2.1.1 hard pinning it to that version. 33 | scipy==1.4.1 34 | webrtcvad==2.0.10 35 | 36 | # Development dependencies 37 | # 38 | # Each link listed below specifies the path to a setup.py file which are 39 | # installed in editable mode with '-e $PATH' (without the quotes). 40 | # 41 | # If python code in this repository depends on python code under development 42 | # in another repository, then an entry for that other respository should 43 | # appear in this list instead of the released dependencies list. 44 | # 45 | # If this repository uses the setup functionality (e.g., script entry points) 46 | # of its own setup.py file, then this list must include an entry for that 47 | # setup.py file, e.g., '-e .' or '-e ./python' (without the quotes). 48 | -e ./../audio_test_tools/python 49 | -e ./../lib_voice_toolbox/python 50 | -e ./../lib_vad/python 51 | -e ./python 52 | -------------------------------------------------------------------------------- /lib_agc/src/agc_ch_state.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #ifndef _AGC_STATE_H_ 4 | #define _AGC_STATE_H_ 5 | 6 | #include 7 | #include "voice_toolbox.h" 8 | #include "agc_conf.h" 9 | 10 | #define AGC_ALPHA_SLOW_RISE VTB_UQ0_32(0.8869) 11 | #define AGC_ALPHA_SLOW_FALL VTB_UQ0_32(0.9646) 12 | #define AGC_ALPHA_FAST_RISE VTB_UQ0_32(0.3814) 13 | #define AGC_ALPHA_FAST_FALL VTB_UQ0_32(0.8869) 14 | #define AGC_ALPHA_PEAK_RISE VTB_UQ0_32(0.5480) 15 | #define AGC_ALPHA_PEAK_FALL VTB_UQ0_32(0.9646) 16 | 17 | #define AGC_LC_EST_ALPHA_INC VTB_UQ0_32(0) 18 | #define AGC_LC_EST_ALPHA_DEC VTB_UQ0_32(0.6973) 19 | #define AGC_LC_BG_POWER_EST_ALPHA_DEC VTB_UQ0_32(0.5480) 20 | 21 | #define AGC_LC_NEAR_POWER_EST VTB_UQ0_32(0.00001) 22 | #define AGC_LC_BG_POWER_EST_INIT VTB_UQ0_32(0.01) 23 | #define AGC_LC_FAR_BG_POWER_EST_INIT VTB_UQ0_32(0.01) 24 | 25 | /** 26 | * Structure to hold AGC state, only used internally. 27 | */ 28 | typedef struct { 29 | int adapt; 30 | int adapt_on_vad; 31 | int soft_clipping; 32 | vtb_u32_float_t gain; 33 | vtb_u32_float_t max_gain; 34 | vtb_u32_float_t min_gain; 35 | vtb_u32_float_t upper_threshold; 36 | vtb_u32_float_t lower_threshold; 37 | vtb_u32_float_t x_slow; 38 | vtb_u32_float_t x_fast; 39 | vtb_u32_float_t x_peak; 40 | vtb_u32_float_t gain_inc; 41 | vtb_u32_float_t gain_dec; 42 | int lc_enabled; 43 | int lc_t_far; 44 | int lc_t_near; 45 | int lc_n_frame_near; 46 | int lc_n_frame_far; 47 | vtb_u32_float_t lc_corr_threshold; 48 | vtb_u32_float_t lc_near_power_est; 49 | vtb_u32_float_t lc_far_power_est; 50 | vtb_u32_float_t lc_bg_power_est; 51 | vtb_u32_float_t lc_gain; 52 | vtb_u32_float_t lc_far_bg_power_est; 53 | vtb_uq0_32_t lc_corr_factor; 54 | vtb_u32_float_t lc_bg_power_gamma; 55 | vtb_u32_float_t lc_gamma_inc; 56 | vtb_u32_float_t lc_gamma_dec; 57 | vtb_u32_float_t lc_near_delta_far_act; 58 | vtb_u32_float_t lc_near_delta; 59 | vtb_u32_float_t lc_far_delta; 60 | vtb_u32_float_t lc_gain_max; 61 | vtb_u32_float_t lc_gain_dt; 62 | vtb_u32_float_t lc_gain_silence; 63 | vtb_u32_float_t lc_gain_min; 64 | 65 | } agc_ch_state_t; 66 | 67 | /** 68 | * Structure containing AGC state unique to each input channel. 69 | */ 70 | typedef struct { 71 | agc_ch_state_t ch_state[AGC_INPUT_CHANNELS]; ///< Channel states 72 | } agc_state_t; 73 | 74 | 75 | #endif // _AGC_STATE_H_ 76 | -------------------------------------------------------------------------------- /tests/test_wav_agc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | set(XMOS_TOOLS_PATH $ENV{XMOS_TOOL_PATH}/bin) 4 | 5 | #********************** 6 | # Setup XMOS toolchain 7 | #********************** 8 | if(NOT DEFINED ENV{AGC_PATH}) 9 | message(FATAL_ERROR "AGC_PATH environment variable not defined") 10 | # some more commands 11 | endif() 12 | include("$ENV{AGC_PATH}/cmake_utils/xmos_toolchain.cmake") 13 | 14 | #********************** 15 | # Project 16 | #********************** 17 | # Disable in-source build. 18 | if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") 19 | message(FATAL_ERROR "In-source build is not allowed! Please specify a build folder.\n\tex:cmake -B build") 20 | endif() 21 | 22 | 23 | ## Define project 24 | project(test_wav_agc VERSION 0.1.0) 25 | 26 | ## Enable languages for project 27 | enable_language(CXX XC C ASM) 28 | 29 | #********************** 30 | # Build flags 31 | #********************** 32 | add_executable(test_wav_agc) 33 | 34 | 35 | set(APP_COMPILER_FLAGS 36 | "-O2" 37 | "-g" 38 | "-Wall" 39 | "-report" 40 | "-fxscope" 41 | "-target=XCORE-AI-EXPLORER" 42 | "${CMAKE_CURRENT_SOURCE_DIR}/config.xscope" 43 | "-D__process_wav_conf_h_exists__" 44 | ) 45 | 46 | set(APP_SRCS 47 | "./src/test_wav_agc.xc" 48 | ) 49 | 50 | set(APP_INCLUDES 51 | "src" 52 | "autogenerated" 53 | ) 54 | 55 | set(APP_DEPENDENT_MODULES 56 | "lib_agc(>=7.0.2)" 57 | "lib_dsp(>=6.0.1)" 58 | "lib_voice_toolbox(>=8.0.0)" 59 | "lib_vad(>=1.0.3)" 60 | "audio_test_tools(>=4.2.0)" 61 | ) 62 | 63 | include("$ENV{AGC_PATH}/cmake_utils/agc.cmake") 64 | 65 | set_target_properties(test_wav_agc PROPERTIES OUTPUT_NAME test_wav_agc.xe) 66 | 67 | target_compile_options(test_wav_agc PRIVATE ${APP_COMPILER_FLAGS}) 68 | 69 | target_include_directories(test_wav_agc 70 | PRIVATE ${APP_INCLUDES} 71 | PRIVATE ${AGC_INCLUDES_ALL} 72 | ) 73 | 74 | target_sources(test_wav_agc 75 | PRIVATE ${APP_SRCS} 76 | PRIVATE ${AGC_SRCS_ALL}) 77 | 78 | 79 | add_custom_target( 80 | always_run_target ALL 81 | COMMAND python $ENV{AGC_PATH}/lib_agc/generate_config.py 82 | COMMAND mv autogenerated ${CMAKE_CURRENT_SOURCE_DIR}/. 83 | COMMENT "Generating AGC config" 84 | ) 85 | 86 | add_dependencies(test_wav_agc always_run_target) 87 | 88 | target_link_options(test_wav_agc PRIVATE ${APP_COMPILER_FLAGS}) 89 | 90 | 91 | ## Set any additional flags only for C++ 92 | set(CMAKE_CXX_FLAGS "-std=c++11") 93 | 94 | ## Register the application 95 | #XMOS_REGISTER_APP() 96 | -------------------------------------------------------------------------------- /lib_agc/src/agc_control_map.xc: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #include "agc_conf.h" 4 | #include "agc_control_map.h" 5 | 6 | unsigned agc_control_map[agc_num_commands][3] = { 7 | {agc_cmd_set_gain, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 8 | {agc_cmd_get_gain, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 9 | {agc_cmd_set_max_gain, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 10 | {agc_cmd_get_max_gain, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 11 | {agc_cmd_set_min_gain, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 12 | {agc_cmd_get_min_gain, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 13 | {agc_cmd_set_adapt, AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 14 | {agc_cmd_get_adapt, AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 15 | {agc_cmd_set_adapt_on_vad, AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 16 | {agc_cmd_get_adapt_on_vad, AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 17 | {agc_cmd_set_soft_clipping, AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 18 | {agc_cmd_get_soft_clipping, AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 19 | {agc_cmd_set_lc_enabled, AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 20 | {agc_cmd_get_lc_enabled, AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 21 | {agc_cmd_set_desired_upper_threshold, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_1_31}, 22 | {agc_cmd_set_desired_lower_threshold, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_1_31}, 23 | {agc_cmd_get_desired_upper_threshold, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_1_31}, 24 | {agc_cmd_get_desired_lower_threshold, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_1_31}, 25 | {agc_cmd_get_gain_increment_stepsize, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 26 | {agc_cmd_get_gain_decrement_stepsize, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 27 | {agc_cmd_set_gain_increment_stepsize, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 28 | {agc_cmd_set_gain_decrement_stepsize, AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 29 | {agc_cmd_get_lc_n_frames, LC_N_FRAMES_NUM*AGC_INPUT_CHANNELS, vtb_ctrl_uint32}, 30 | {agc_cmd_set_lc_n_frames, LC_N_FRAMES_NUM+1, vtb_ctrl_uint32}, 31 | {agc_cmd_get_lc_corr_threshold, 1*AGC_INPUT_CHANNELS, vtb_ctrl_fixed_0_32}, 32 | {agc_cmd_set_lc_corr_threshold, 1+1, vtb_ctrl_fixed_0_32}, 33 | {agc_cmd_get_lc_gammas, LC_GAMMAS_NUM*AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 34 | {agc_cmd_set_lc_gammas, 1+LC_GAMMAS_NUM, vtb_ctrl_fixed_16_16}, 35 | {agc_cmd_get_lc_deltas, LC_DELTAS_NUM*AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 36 | {agc_cmd_set_lc_deltas, 1+LC_DELTAS_NUM, vtb_ctrl_fixed_16_16}, 37 | {agc_cmd_get_lc_gains, LC_GAINS_NUM*AGC_INPUT_CHANNELS, vtb_ctrl_fixed_16_16}, 38 | {agc_cmd_set_lc_gains, 1+LC_GAINS_NUM, vtb_ctrl_fixed_16_16}, 39 | }; 40 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/src/agc_conf.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | 4 | #ifndef AGC_CONF_H_ 5 | #define AGC_CONF_H_ 6 | 7 | #define AGC_FRAME_ADVANCE (240) 8 | #define AGC_PROC_FRAME_LENGTH (AGC_FRAME_ADVANCE) 9 | 10 | #define AGC_INPUT_CHANNELS (2) 11 | #define AGC_CHANNEL_PAIRS ((AGC_INPUT_CHANNELS+1)/2) 12 | 13 | 14 | #define AGC_CH0_ADAPT (0) 15 | #define AGC_CH0_ADAPT_ON_VAD (0) 16 | #define AGC_CH0_SOFT_CLIPPING (0) 17 | #define AGC_CH0_LC_ENABLED (0) 18 | 19 | 20 | #define AGC_CH0_GAIN (2) 21 | #define AGC_CH0_MAX_GAIN (1000) 22 | #define AGC_CH0_MIN_GAIN (100) 23 | #define AGC_CH0_DESIRED_LEVEL_FS (0.1) 24 | 25 | #define AGC_CH0_GAIN_INC VTB_UQ16_16(1.0121) 26 | #define AGC_CH0_GAIN_DEC VTB_UQ16_16(0.9880) 27 | 28 | #define AGC_CH1_GAIN_INC VTB_UQ16_16(1.0121) 29 | #define AGC_CH1_GAIN_DEC VTB_UQ16_16(0.9880) 30 | 31 | #define AGC_CH1_ADAPT (1) 32 | #define AGC_CH1_ADAPT_ON_VAD (1) 33 | #define AGC_CH1_SOFT_CLIPPING (1) 34 | #define AGC_CH1_LC_ENABLED (1) 35 | 36 | #define AGC_CH1_GAIN (30) 37 | #define AGC_CH1_MAX_GAIN (100) 38 | #define AGC_CH1_MIN_GAIN (0) 39 | 40 | #define AGC_CH1_DESIRED_LEVEL_FS (0.001) 41 | 42 | #define AGC_CH0_UPPER_THRESHOLD (0.002) 43 | #define AGC_CH0_LOWER_THRESHOLD (0.0001) 44 | 45 | #define AGC_CH1_UPPER_THRESHOLD (0.002) 46 | #define AGC_CH1_LOWER_THRESHOLD (0.0001) 47 | 48 | #define AGC_CH0_LC_N_FRAME_NEAR (10) 49 | #define AGC_CH0_LC_N_FRAME_FAR (20) 50 | #define AGC_CH0_LC_NEAR_DELTA_FAR_ACT (0.11) 51 | #define AGC_CH0_LC_NEAR_DELTA (0.21) 52 | #define AGC_CH0_LC_FAR_DELTA (0.31) 53 | #define AGC_CH0_LC_GAMMA_INC (0.12) 54 | #define AGC_CH0_LC_GAMMA_DEC (0.22) 55 | #define AGC_CH0_LC_BG_POWER_GAMMA (0.32) 56 | #define AGC_CH0_LC_CORR_THRESHOLD (0.13) 57 | #define AGC_CH0_LC_GAIN_MAX (0.43) 58 | #define AGC_CH0_LC_GAIN_DT (0.33) 59 | #define AGC_CH0_LC_GAIN_SILENCE (0.23) 60 | #define AGC_CH0_LC_GAIN_MIN (0.13) 61 | 62 | #define AGC_CH1_LC_N_FRAME_NEAR (60) 63 | #define AGC_CH1_LC_N_FRAME_FAR (70) 64 | #define AGC_CH1_LC_NEAR_DELTA_FAR_ACT (0.51) 65 | #define AGC_CH1_LC_NEAR_DELTA (0.61) 66 | #define AGC_CH1_LC_FAR_DELTA (0.71) 67 | #define AGC_CH1_LC_GAMMA_INC (0.52) 68 | #define AGC_CH1_LC_GAMMA_DEC (0.62) 69 | #define AGC_CH1_LC_BG_POWER_GAMMA (0.72) 70 | #define AGC_CH1_LC_CORR_THRESHOLD (0.53) 71 | #define AGC_CH1_LC_GAIN_MAX (0.83) 72 | #define AGC_CH1_LC_GAIN_DT (0.73) 73 | #define AGC_CH1_LC_GAIN_SILENCE (0.63) 74 | #define AGC_CH1_LC_GAIN_MIN (0.53) 75 | 76 | #endif /* AGC_CONF_H_ */ 77 | -------------------------------------------------------------------------------- /cmake_utils/agc.cmake: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE LIB_AGC_SOURCES "$ENV{AGC_PATH}/lib_agc/src/*.xc") 2 | file(GLOB_RECURSE LIB_VAD_SOURCES "$ENV{AGC_PATH}/../lib_vad/lib_vad/src/*.xc") 3 | file(GLOB_RECURSE LIB_DSP_SOURCES "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/bfp/*.S" 4 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/bfp/*.xc" 5 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/fft/*.S" 6 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/fft/*.xc" 7 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/dsp_dct.xc" 8 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/dsp_math.c" 9 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/dsp_logistics.S" 10 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/dsp_biquad.S" 11 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/src/dsp_tables.c") 12 | file(GLOB_RECURSE LIB_VTB_SOURCES "$ENV{AGC_PATH}/../lib_voice_toolbox/lib_voice_toolbox/src/bfp/*.xc" 13 | "$ENV{AGC_PATH}/../lib_voice_toolbox/lib_voice_toolbox/src/bfp/*.S") 14 | set_source_files_properties(${LIB_VTB_SOURCES} PROPERTIES COMPILE_FLAGS -O2) 15 | file(GLOB_RECURSE LIB_AI_SOURCES "$ENV{AGC_PATH}/../lib_ai/lib_ai/src/*.xc" 16 | "$ENV{AGC_PATH}/../lib_ai/ai/src/*.S" 17 | "$ENV{AGC_PATH}/../lib_ai/ai/src/*.c") 18 | file(GLOB_RECURSE AUDIO_TEST_TOOLS_SOURCES "$ENV{AGC_PATH}/../audio_test_tools/audio_test_tools/src/*.xc" 19 | "$ENV{AGC_PATH}/../audio_test_tools/audio_test_tools/src/*.S") 20 | 21 | set(AGC_SRCS_ALL ${LIB_AGC_SOURCES} 22 | ${LIB_VAD_SOURCES} 23 | ${LIB_DSP_SOURCES} 24 | ${LIB_VTB_SOURCES} 25 | ${LIB_AI_SOURCES} 26 | ${AUDIO_TEST_TOOLS_SOURCES}) 27 | 28 | 29 | set(AGC_INCLUDES_ALL "$ENV{AGC_PATH}/lib_agc/api" 30 | "$ENV{AGC_PATH}/lib_agc/src" 31 | "$ENV{AGC_PATH}/../lib_vad/lib_vad/api" 32 | "$ENV{AGC_PATH}/../lib_dsp/lib_dsp/api" 33 | "$ENV{AGC_PATH}/../lib_voice_toolbox/lib_voice_toolbox/api" 34 | "$ENV{AGC_PATH}/../audio_test_tools/audio_test_tools/api" 35 | "$ENV{AGC_PATH}/../lib_ai/lib_ai/api") 36 | 37 | #Add all paths from sources to includes. It's a bit overkill but still builds fast :) 38 | FOREACH(SRC ${AGC_SRCS_ALL}) 39 | get_filename_component(DIR ${SRC} DIRECTORY) 40 | list(APPEND AGC_INCLUDES_ALL ${DIR}) 41 | ENDFOREACH() 42 | 43 | #message( ${VFE_SRCS_ALL}) 44 | #message( ${VFE_INCLUDES_ALL}) 45 | -------------------------------------------------------------------------------- /python/test_wav_agc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2021 XMOS LIMITED. 2 | # This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | from builtins import range 4 | from math import sqrt 5 | 6 | import numpy as np 7 | import scipy.io.wavfile 8 | import audio_wav_utils as awu 9 | from json_utils import json_to_dict 10 | from agc import agc 11 | import vad 12 | import argparse 13 | 14 | FRAME_ADVANCE = 240 15 | 16 | 17 | ADAPT_DEFAULT = False 18 | DESIRED_LEVEL_DEFAULT = 0.1 #-20 dB 19 | MAX_GAIN_DEFAULT = 100000.0 20 | INIT_GAIN_DEFAULT = 10.0 21 | 22 | VAD_THRESHOLD = 0.8 23 | 24 | def parse_arguments(): 25 | parser = argparse.ArgumentParser() 26 | parser.add_argument("input", help="input wav file") 27 | parser.add_argument("output", help="output wav file") 28 | parser.add_argument("--config-file", help="json config file", default="../lib_agc/config/agc_2ch.json") 29 | parser.add_argument('--plot', action='store_true') 30 | args = parser.parse_args() 31 | return args 32 | 33 | if __name__ == "__main__": 34 | args = parse_arguments() 35 | agc_parameters = json_to_dict(args.config_file) 36 | rate, mix_wav_file = scipy.io.wavfile.read(args.input, 'r') 37 | wav_data, channel_count, file_length = awu.parse_audio(mix_wav_file) 38 | 39 | vad = vad.vad() 40 | 41 | x_slow = [] 42 | x_fast = [] 43 | x_peak = [] 44 | frame_gain_dB = [] 45 | vad_results = [] 46 | 47 | 48 | # treat the key-value pairs of the dictionary as additional named arguments to the constructor. 49 | agc = agc(**agc_parameters["agc_init_config"]) 50 | output = np.zeros((len(agc.ch_state), file_length)) 51 | 52 | for frame_start in range(0, file_length-FRAME_ADVANCE, FRAME_ADVANCE): 53 | x = awu.get_frame(wav_data, frame_start, FRAME_ADVANCE) 54 | vad_result = vad.run(x[0]) 55 | ref_power = 0 56 | 57 | output[:, frame_start: frame_start + FRAME_ADVANCE] = agc.process_frame(x, ref_power, vad_result > VAD_THRESHOLD) 58 | 59 | x_slow.append(20.0 * np.log10(agc.ch_state[0].x_slow)) 60 | x_fast.append(20.0 * np.log10(agc.ch_state[0].x_fast)) 61 | x_peak.append(20.0 * np.log10(agc.ch_state[0].x_peak) if agc.ch_state[0].x_peak > 0 else np.NaN) 62 | frame_gain_dB.append(20.0 * np.log10(agc.ch_state[0].gain)) 63 | vad_results.append(vad_result) 64 | 65 | 66 | scipy.io.wavfile.write(args.output, rate, output.T) 67 | 68 | if args.plot: 69 | import matplotlib.pyplot as plt 70 | plt.figure(1) 71 | plt.plot(x_slow, label = 'Slow') 72 | plt.plot(x_fast, label = 'Fast') 73 | plt.plot(x_peak, label = 'Peak') 74 | plt.title('Envelope Trackers') 75 | plt.xlabel('Frame Index') 76 | plt.ylabel('Level') 77 | plt.legend() 78 | plt.grid() 79 | 80 | plt.figure(2) 81 | plt.plot(frame_gain_dB) 82 | plt.title('Applied Gain') 83 | plt.xlabel('Frame Index') 84 | plt.ylabel('Gain (dB)') 85 | plt.grid() 86 | 87 | plt.figure(3) 88 | plt.plot(vad_results, label = "VAD") 89 | plt.title('VAD') 90 | plt.xlabel('Frame Index') 91 | plt.ylabel('Output') 92 | plt.grid() 93 | 94 | 95 | plt.show() 96 | -------------------------------------------------------------------------------- /tests/test_wav_agc/wscript: -------------------------------------------------------------------------------- 1 | import sys 2 | import subprocess 3 | import os 4 | from waflib import Options, Errors 5 | from waflib.Build import BuildContext, CleanContext 6 | sys.path.append('../../../lib_voice_toolbox/python') 7 | 8 | xcore_ai = os.environ.get('XCOREAI', '0') 9 | print("xcore_ai = ",xcore_ai) 10 | 11 | TARGETS = ['xcore200', 'xcoreai'] 12 | # This creates build and clean commands for each BUILD_CONFIG 13 | def create_waf_contexts(targets): 14 | for test_name in targets: 15 | for ctx in (BuildContext, CleanContext): 16 | raw_context = ctx.__name__.replace('Context', '').lower() 17 | 18 | class tmp(ctx): 19 | cmd = raw_context + '_' + test_name 20 | variant = os.path.split(os.getcwd())[1] + '_' + test_name 21 | config = test_name 22 | target = test_name 23 | 24 | create_waf_contexts(TARGETS) 25 | 26 | def options(opt): 27 | opt.add_option('--target', action='store', default='xcore200') 28 | opt.add_option('--config_file', default='../../lib_agc/config/agc_2ch.json') 29 | opt.load('xwaf.xcommon') 30 | 31 | def configure(conf): 32 | conf.load('xwaf.compiler_xcc') 33 | conf.env.PROJECT_ROOT = '../../..' 34 | conf.load('xwaf.xcommon') 35 | 36 | def build(bld): 37 | print('bld.variant = ', bld.variant) 38 | print('bld.options.target = ', bld.options.target) 39 | if not bld.variant: 40 | build_configs = [ 41 | c for c in TARGETS if c == bld.options.target 42 | ] 43 | 44 | if len(build_configs) == 0: 45 | bld.fatal('specify a target with --target.\nAvailable targets: {}'.format(', '.join(TARGETS))) 46 | return 47 | 48 | build_commands = ['{}_{}'.format(bld.cmd, c) for c in build_configs] 49 | print("build_commands = {}".format(build_commands)) 50 | 51 | if not build_commands: 52 | bld.fatal/( 53 | '{} does not match any configs'.format(bld.options.ap_config)) 54 | 55 | cmd_str = {'build': 'Building', 'clean': 'Cleaning', 'list': "Listing"}[bld.cmd] 56 | print('{} configs:\n {}'.format(cmd_str, '\n '.join( 57 | build_configs))) 58 | if not bld.cmd == 'list': 59 | Options.commands = build_commands + Options.commands 60 | return 61 | 62 | subprocess.call(['../../.venv/bin/python', '../../lib_agc/generate_config.py']) 63 | target_path = 'bin/' + bld.options.target 64 | #print('target_path ',target_path) 65 | bld.env.XSCOPE = bld.path.find_resource('config.xscope') 66 | depends_on = ['lib_agc', 'audio_test_tools', 'lib_voice_toolbox'] 67 | makefile_opts = {} 68 | makefile_opts['SOURCE_DIRS'] = ['src'] 69 | if(bld.target == 'xcoreai'): 70 | print('TARGET XCOREAI') 71 | makefile_opts['TARGET'] = ['XCORE-AI-EXPLORER'] 72 | else: 73 | print('TARGET XCORE200') 74 | makefile_opts['TARGET'] = ['XCORE-200-EXPLORER'] 75 | 76 | makefile_opts['INCLUDE_DIRS'] = ['src', 'autogenerated'] 77 | makefile_opts['XCC_FLAGS'] = ['-O2', '-g', '-Wall', '-report'] 78 | makefile_opts['APP_NAME'] = [bld.options.target] 79 | makefile_opts['USED_MODULES'] = depends_on 80 | makefile_opts['XCOMMON_MAKEFILE'] = ['Makefile.common'] 81 | bld.do_xcommon(makefile_opts) 82 | 83 | def dist(ctx): 84 | ctx.load('xwaf.xcommon') 85 | 86 | def distcheck(ctx): 87 | ctx.load('xwaf.xcommon') 88 | 89 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | set(XMOS_TOOLS_PATH $ENV{XMOS_TOOL_PATH}/bin) 4 | 5 | #********************** 6 | # Setup XMOS toolchain 7 | #********************** 8 | if(NOT DEFINED ENV{AGC_PATH}) 9 | message(FATAL_ERROR "AGC_PATH environment variable not defined") 10 | # some more commands 11 | endif() 12 | include("$ENV{AGC_PATH}/cmake_utils/xmos_toolchain.cmake") 13 | 14 | #********************** 15 | # Project 16 | #********************** 17 | # Disable in-source build. 18 | #if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_BINARY_DIR}") 19 | # message(FATAL_ERROR "In-source build is not allowed! Please specify a build folder.\n\tex:cmake -B build") 20 | #endif() 21 | 22 | 23 | ## Define project 24 | project(agc_unit_tests VERSION 0.1.0) 25 | 26 | ## Enable languages for project 27 | enable_language(CXX XC C ASM) 28 | 29 | message(STATUS "CAME HERE") 30 | add_custom_target("runners" ALL) 31 | add_custom_command( 32 | TARGET runners 33 | COMMAND python generate_unity_runners.py 34 | COMMENT "generate unity runners" 35 | ) 36 | 37 | message(STATUS "CAME HERE 1") 38 | file( GLOB APP_SOURCES src/test*.xc ) 39 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) 40 | foreach( testsourcefile ${APP_SOURCES} ) 41 | get_filename_component(ITEM_NAME ${testsourcefile} NAME_WE) 42 | message(STATUS "item_name " ${ITEM_NAME}) 43 | add_executable(${ITEM_NAME}) 44 | set(APP_COMPILER_FLAGS 45 | "-O2" 46 | "-g" 47 | "-Wall" 48 | "-report" 49 | "-fxscope" 50 | "-target=XCORE-AI-EXPLORER" 51 | "${CMAKE_CURRENT_SOURCE_DIR}/config.xscope" 52 | "-DUNITY_SUPPORT_64" 53 | "-DUNITY_INCLUDE_DOUBLE" 54 | ) 55 | set_source_files_properties( 56 | "runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c" 57 | PROPERTIES GENERATED TRUE 58 | ) 59 | 60 | set(APP_SRCS 61 | ${testsourcefile} 62 | "runners/${ITEM_NAME}/${ITEM_NAME}_Runner.c" 63 | "${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src/unity.c" 64 | ) 65 | set(APP_INCLUDES 66 | "src" 67 | "${CMAKE_CURRENT_SOURCE_DIR}/../../../Unity/src" 68 | ) 69 | set(APP_DEPENDENT_MODULES 70 | "lib_agc(>=7.0.2)" 71 | "lib_dsp(>=6.0.1)" 72 | "lib_voice_toolbox(>=8.0.0)" 73 | "lib_vad(>=1.0.3)" 74 | "audio_test_tools(>=4.2.0)" 75 | ) 76 | 77 | include("$ENV{AGC_PATH}/cmake_utils/agc.cmake") 78 | set_target_properties(${ITEM_NAME} PROPERTIES OUTPUT_NAME ${ITEM_NAME}.xe) 79 | target_compile_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS}) 80 | 81 | 82 | 83 | target_include_directories(${ITEM_NAME} 84 | PRIVATE ${APP_INCLUDES} 85 | PRIVATE ${AGC_INCLUDES_ALL} 86 | ) 87 | 88 | target_sources(${ITEM_NAME} 89 | PRIVATE ${APP_SRCS} 90 | PRIVATE ${AGC_SRCS_ALL} 91 | ) 92 | add_dependencies(${ITEM_NAME} runners) 93 | target_link_options(${ITEM_NAME} PRIVATE ${APP_COMPILER_FLAGS}) 94 | ## Set any additional flags only for C++ 95 | set(CMAKE_CXX_FLAGS "-std=c++11") 96 | 97 | endforeach( testsourcefile ${APP_SOURCES} ) 98 | 99 | message(STATUS ${APP_SOURCES}) 100 | 101 | message(STATUS "CAME HERE 2") 102 | ## Register the application 103 | #XMOS_REGISTER_APP() 104 | -------------------------------------------------------------------------------- /tests/test_wav_agc/src/test_wav_agc.xc: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #include 4 | 5 | #include "voice_toolbox.h" 6 | #include "agc.h" 7 | #include "audio_test_tools.h" 8 | #include "vad.h" 9 | #include 10 | #include "agc_init_config.h" 11 | 12 | 13 | void app_control(chanend c_control_to_wav, chanend c_control_to_dsp){ 14 | 15 | //Play for 5 seconds 16 | att_pw_play_until_sample_passes(c_control_to_wav, 16000*5); 17 | 18 | //command the DSP to do something 19 | //aec_... 20 | 21 | //Play the rest of the file 22 | att_pw_play(c_control_to_wav); 23 | } 24 | 25 | 26 | //This must be more than AGC_FRAME_ADVANCE to work around vtb_rx_tx doesnt support advance==length. 27 | #define INPUT_FRAME_LENGTH 480 28 | 29 | #define STATE_SIZE VTB_RX_STATE_UINT64_SIZE(AGC_CHANNEL_PAIRS*2, INPUT_FRAME_LENGTH, AGC_FRAME_ADVANCE, 0) 30 | 31 | #define AGC_VAD_THRESHOLD (205) 32 | 33 | void agc_test_task(chanend c_data_input, chanend c_data_output, 34 | chanend ?c_control){ 35 | uint64_t state[STATE_SIZE]; 36 | vtb_md_t md; 37 | vtb_md_init(md); 38 | 39 | vtb_rx_state_init(state, AGC_CHANNEL_PAIRS*2, INPUT_FRAME_LENGTH, AGC_FRAME_ADVANCE, null, STATE_SIZE); 40 | 41 | agc_state_t [[aligned(8)]] agc_state; 42 | int32_t vad_data_window[VAD_PROC_FRAME_LENGTH]; 43 | for(int i = 0; i= 0;s--){ 66 | vad_data_window[s + AGC_FRAME_ADVANCE] = vad_data_window[s]; 67 | } 68 | for(unsigned s=0;s AGC_VAD_THRESHOLD; 72 | vtb_uq0_32_t aec_corr = 0; 73 | 74 | agc_process_frame(agc_state, input_frame, ref_power, vad, aec_corr); 75 | 76 | vtb_tx_notification_and_data(c_data_output, (vtb_ch_pair_t*)input_frame, 77 | 2*AGC_CHANNEL_PAIRS, 78 | AGC_FRAME_ADVANCE, md); 79 | } 80 | } 81 | 82 | 83 | 84 | int main(){ 85 | chan app_to_dsp; 86 | chan dsp_to_app; 87 | chan c_control_to_dsp; 88 | chan c_control_to_wav; 89 | 90 | par { 91 | on tile[0]:{ 92 | app_control(c_control_to_wav, c_control_to_dsp); 93 | } 94 | on tile[0]:{ 95 | att_process_wav(app_to_dsp, dsp_to_app, c_control_to_wav); 96 | _Exit(0); 97 | } 98 | on tile[1]: agc_test_task(app_to_dsp, dsp_to_app, c_control_to_dsp); 99 | } 100 | return 0; 101 | } 102 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | @Library('xmos_jenkins_shared_library@v0.32.0') _ 2 | 3 | getApproval() 4 | 5 | pipeline { 6 | agent none 7 | 8 | //Tools for AI verif stage. Tools for standard stage in view file 9 | environment { 10 | REPO = 'lib_agc' 11 | VIEW = getViewName(REPO) 12 | } 13 | stages { 14 | stage('Standard build and XS2 tests') { 15 | agent { 16 | label 'x86_64 && linux' 17 | } 18 | options { 19 | skipDefaultCheckout() 20 | } 21 | stages { 22 | stage('Get View') { 23 | steps { 24 | xcorePrepareSandbox("${VIEW}", "${REPO}") 25 | } 26 | } 27 | stage('Library Checks') { 28 | steps { 29 | xcoreLibraryChecks("${REPO}") 30 | } 31 | } 32 | stage('Unit Tests') { 33 | steps { 34 | dir("${REPO}") { 35 | dir('tests') { 36 | dir('agc_unit_tests') { 37 | withVenv { 38 | runWaf('.', "configure clean build --target=xcore200") 39 | runWaf('.', "configure clean build --target=xcoreai") 40 | stash name: 'agc_unit_tests', includes: 'bin/*xcoreai.xe, ' 41 | viewEnv() { 42 | runPython("TARGET=XCORE200 pytest -n 1") 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | stage('Build test_wav_agc') { 51 | steps { 52 | dir("${REPO}") { 53 | dir('tests/test_wav_agc') { 54 | withVenv { 55 | runWaf('.', "configure clean build --target=xcore200") 56 | runWaf('.', "configure clean build --target=xcoreai") 57 | } 58 | } 59 | } 60 | } 61 | } 62 | stage('Build docs') { 63 | steps { 64 | runXdoc("${REPO}/${REPO}/doc") 65 | // Archive all the generated .pdf docs 66 | archiveArtifacts artifacts: "${REPO}/**/pdf/*.pdf", fingerprint: true, allowEmptyArchive: true 67 | } 68 | } 69 | } 70 | post { 71 | cleanup { 72 | xcoreCleanSandbox() 73 | } 74 | } 75 | } 76 | stage('xcore.ai Verification'){ 77 | agent { 78 | label 'xcore.ai' 79 | } 80 | options { 81 | skipDefaultCheckout() 82 | } 83 | stages { 84 | stage('Get View') { 85 | steps { 86 | xcorePrepareSandbox("${VIEW}", "${REPO}") 87 | } 88 | } 89 | stage('xs3 agc_unit_tests') 90 | { 91 | steps { 92 | dir("${REPO}") { 93 | dir('tests') { 94 | dir('agc_unit_tests') { 95 | withVenv { 96 | unstash 'agc_unit_tests' 97 | viewEnv() { 98 | runPython("TARGET=XCOREAI pytest -s") 99 | } 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | } //stages 107 | post { 108 | cleanup { 109 | cleanWs() 110 | } 111 | } 112 | }//xcore.ai 113 | stage('Update view files') { 114 | agent { 115 | label 'x86_64 && linux' 116 | } 117 | when { 118 | expression { return currentBuild.currentResult == "SUCCESS" } 119 | } 120 | steps { 121 | updateViewfiles() 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | AGC library change log 2 | ====================== 3 | 4 | 8.2.0 5 | ----- 6 | 7 | * CHANGED: Handling of the loss control near-end audio timer 8 | * ADDED: Control commands to configure loss-control 9 | * CHANGED: Use XMOS Public Licence Version 1 10 | 11 | 8.1.0 12 | ----- 13 | 14 | * CHANGED: updated hard-coded values used in loss control algorithm 15 | * ADDED: Support for building and running tests on XS3 target 16 | 17 | 8.0.0 18 | ----- 19 | 20 | * ADDED: Control commands map and handler 21 | * ADDED: Control parameters for min gain, adapt on vad and soft clipping 22 | 23 | 7.0.2 24 | ----- 25 | 26 | * CHANGED: Pin Python package versions 27 | * REMOVED: not necessary cpanfile 28 | 29 | 7.0.1 30 | ----- 31 | 32 | * CHANGED: Use virtualenv instead of pipenv. 33 | 34 | 7.0.0 35 | ----- 36 | 37 | * CHANGED: Loss control requires an AEC correlation value. 38 | * CHANGED: Removed unnecessary internal state in agc_ch_state_t. 39 | * CHANGED: Switch from pipenv to virtualenv 40 | * CHANGED: Update Jenkins shared library to 0.14.1 41 | 42 | 6.0.2 43 | ----- 44 | 45 | * CHANGED: Further updates to loss control parameters. 46 | 47 | 6.0.1 48 | ----- 49 | 50 | * CHANGED: Updated loss control parameters for communications channel. 51 | 52 | 6.0.0 53 | ----- 54 | 55 | * ADDED: support for loss control. 56 | * UPDATED: API updated to accept reference audio frame power. 57 | * CHANGED: Update dependency on lib_dsp to v6.0.0 58 | 59 | * Changes to dependencies: 60 | 61 | - lib_logging: Added dependency 3.0.0 62 | 63 | 5.1.0 64 | ----- 65 | 66 | * CHANGED: Update .json config for test_wav_agc due to lib_vtb v7.1.0 67 | 68 | 5.0.0 69 | ----- 70 | 71 | * CHANGED: Build files updated to support new "xcommon" behaviour in xwaf. 72 | 73 | 4.1.0 74 | ----- 75 | 76 | * CHANGED: Use pipenv to set up python environment. 77 | * CHANGED: Get wavfile processing related functions from audio_wav_utils in 78 | audio_test_tools 79 | 80 | * Changes to dependencies: 81 | 82 | - lib_ai: Added dependency 0.0.1 83 | 84 | - lib_vad: Added dependency 0.4.2 85 | 86 | 4.0.0 87 | ----- 88 | 89 | * ADDED: support for JSON config file 90 | * UPDATED: Removed VAD threshold. API updated to accept VAD flag instead. 91 | 92 | 3.1.1 93 | ----- 94 | 95 | * CHANGED: VAD threshold increased to 80%. 96 | 97 | 3.1.0 98 | ----- 99 | 100 | * CHANGED: upper and lower threshold parameters in python from dB to non-dB. 101 | 102 | 3.0.0 103 | ----- 104 | 105 | * ADDED: Range constrainer like functionality within AGC. 106 | * ADDED: Parameters for upper and lower desired voice level thresholds. 107 | 108 | 2.3.0 109 | ----- 110 | 111 | * ADDED: Pipfile + setup.py for pipenv support 112 | * ADDED: Python 3 support 113 | 114 | 2.2.0 115 | ----- 116 | 117 | * CHANGED: Updated lib_voice_toolbox dependency to v5.0.0 118 | 119 | 2.1.0 120 | ----- 121 | 122 | * CHANGE: Fixed channel index bug. 123 | * CHANGE: Extended unit tests. 124 | 125 | 2.0.0 126 | ----- 127 | 128 | * CHANGED: AGC adaptive algorithm. 129 | * CHANGED: Processing a frame requires VAD. 130 | * CHANGED: Renamed AGC_CHANNELS to AGC_INPUT_CHANNELS. 131 | * ADDED: Parameter get and set functions. 132 | * ADDED: Initial AGC config structure. 133 | 134 | 1.0.0 135 | ----- 136 | 137 | * ADDED: Multiple channel support 138 | * ADDED: Gain and adaption control 139 | 140 | 0.0.3 141 | ----- 142 | 143 | * ADDED: Unit tests 144 | * ADDED: Python and XC implementations 145 | * ADDED: Jenkinsfile 146 | 147 | 0.0.2 148 | ----- 149 | 150 | * ADDED: Support for xmake 151 | * Copyrights, licences and dependencies 152 | 153 | * Changes to dependencies: 154 | 155 | - lib_dsp: Added dependency 4.1.0 156 | 157 | - lib_voice_toolbox: Added dependency 1.0.2 158 | 159 | 0.0.1 160 | ----- 161 | 162 | * Initial version 163 | 164 | -------------------------------------------------------------------------------- /lib_agc/doc/rst/agc-internals.rst: -------------------------------------------------------------------------------- 1 | At the core of the AGC is the ``agc_state_t`` structure, which holds the state 2 | of the AGC for each audio channel to be processed. 3 | 4 | The AGC is initialised using the ``agc_init`` function. This function requires 5 | an array of the ``agc_init_config_t`` structure, which contains the user-defined 6 | configuration of the AGC (for each channel) at initialisation. This per-channel 7 | configuration includes; 8 | 9 | * Whether the channel has adaptive gain (normal AGC behaviour) or fixed gain. 10 | 11 | * The channel's initial gain value. 12 | 13 | * The desired output level of voice content in the channel. 14 | 15 | 16 | Users are also required to include a file named ``agc_conf.h`` at compilation, 17 | which must contain the following pre-processor directives: 18 | 19 | * AGC_INPUT_CHANNELS - the number of input channels given to the AGC. 20 | 21 | * AGC_CHANNEL_PAIRS - the number of input channel pairs given to the AGC. The 22 | first AGC_INPUT_CHANNELS channels within this number of pairs will be operated 23 | on. Must be equal to or greater than ((AGC_INPUT_CHANNELS+1)/2). 24 | 25 | * AGC_PROC_FRAME_LENGTH - the length of the frames to be processed by 26 | the AGC. 27 | 28 | 29 | After the structure is initialised, each frame is processed using a call to 30 | ``agc_process_frame``. This function requires the ``agc_state_t`` structure, 31 | a frame of samples of the correct length and the output level of a Voice 32 | Activity Detector (VAD). The VAD level indicates the probability that there 33 | is voice content in the frame of samples to be processed and is required for 34 | adaptive gain. 35 | 36 | 37 | The AGC state can be configured to apply a fixed gain to a channel. In these 38 | cases the gain will remain it's initial value. This fixed gain can be adjusted 39 | by the ``agc_set_ch_gain`` function. A channel can be switched between 40 | adaptive and fixed gain behaviour by the ``agc_set_ch_adapt`` function. 41 | 42 | 43 | A limiter is applied to the output of the AGC through the use of a gain 44 | compressor. If applying gain to a frame results in sample values greater than 45 | INT_MAX/2 or less than -INT_MAX/2 (i.e. greater than -6dBFS) then the gain is 46 | reduced. This removes hard clipping in the AGC output. 47 | 48 | 49 | |newpage| 50 | 51 | Example Usage 52 | ............. 53 | 54 | In its simplest form the AGC can be used as follows:: 55 | 56 | agc_state_t agc_state; 57 | agc_init_config_t agc_config[AGC_INPUT_CHANNELS] = { // AGC_INPUT_CHANNELS = 2 58 | { 59 | 1, // Adaptive gain. 60 | VTB_UQ16_16(40), // Initial gain value (linear). 61 | VTB_UQ16_16(1000), // Max gain value (linear). 62 | (0.1 * INT32_MAX) // Desired voice output level 63 | }. 64 | { 65 | 0, // Fixed gain. 66 | VTB_UQ16_16(40), // Gain value (linear). 67 | VTB_UQ16_16(1000), // Max gain (linear). No impact whilst gain is fixed. 68 | 0 // Desired voice output level. No impact whilst gain is fixed. 69 | } 70 | }; 71 | 72 | agc_init_state(agc_state, agc_config); 73 | 74 | while(1) { 75 | dsp_complex_t samples[AGC_CHANNEL_PAIRS][AGC_PROC_FRAME_LENGTH]; 76 | int32_t vad_output; 77 | 78 | ... get_data(samples) ... 79 | ... get_vad_output(vad_output) ... 80 | 81 | agc_process_frame(agc_state, samples, vad_output); 82 | 83 | ... put_data(samples) ... 84 | } 85 | 86 | 87 | |newpage| 88 | 89 | API 90 | ... 91 | 92 | Supporting Types 93 | ................ 94 | 95 | .. doxygenstruct:: agc_state_t 96 | 97 | .. doxygenstruct:: agc_init_config_t 98 | 99 | |newpage| 100 | 101 | Creating an AGC Instance 102 | '''''''''''''''''''''''' 103 | 104 | .. doxygenfunction:: agc_init 105 | 106 | 107 | Processing Time Domain Data 108 | ''''''''''''''''''''''''''' 109 | 110 | .. doxygenfunction:: agc_process_frame 111 | 112 | |newpage| 113 | 114 | Controlling an AGC instance 115 | ''''''''''''''''''''''''''' 116 | 117 | .. doxygenfunction:: agc_set_ch_gain 118 | .. doxygenfunction:: agc_get_ch_gain 119 | .. doxygenfunction:: agc_set_ch_max_gain 120 | .. doxygenfunction:: agc_get_ch_max_gain 121 | .. doxygenfunction:: agc_set_ch_adapt 122 | .. doxygenfunction:: agc_get_ch_adapt 123 | .. doxygenfunction:: agc_set_ch_desired_level 124 | .. doxygenfunction:: agc_get_ch_desired_level 125 | 126 | |newpage| 127 | -------------------------------------------------------------------------------- /lib_agc/api/agc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #ifndef _AGC_H_ 4 | #define _AGC_H_ 5 | 6 | #include 7 | #include "dsp.h" 8 | #include "vtb_q_format.h" 9 | #include "agc_ch_state.h" 10 | 11 | 12 | /** 13 | * Structure containing AGC configuration unique to each input channel. 14 | * Used to initialise AGC channel state. 15 | */ 16 | typedef struct { 17 | int adapt; ///< 0 for fixed gain, or 1 for AGC. 18 | int adapt_on_vad; ///< 0 if always adapt AGC, or 1 if adapt AGC only when voice is detection. 19 | int soft_clipping; ///< 0 for no soft clipping, or 1 for soft clipping. 20 | vtb_uq16_16_t init_gain; ///< Initial channel gain. Linear UQ16_16. 21 | vtb_uq16_16_t max_gain; ///< Maximum channel gain. Linear UQ16_16. 22 | vtb_uq16_16_t min_gain; ///< Minimum channel gain. Linear UQ16_16. 23 | vtb_uq1_31_t upper_threshold; ///< Upper threshold for desired output voice level [0, INT32_MAX]. 24 | vtb_uq1_31_t lower_threshold; ///< Lower threshold for desired output voice level [0, INT32_MAX]. 25 | vtb_uq16_16_t gain_inc; ///< Step value to increment the channel gain. 26 | vtb_uq16_16_t gain_dec; ///< Step value to decrement the channel gain. 27 | int lc_enabled; ///< 0 for loss control disabled, or 1 for enabled. 28 | int lc_n_frame_far; ///< number of frames when far-field audio is considered active 29 | int lc_n_frame_near; ///< number of frames when near-field audio is considered active 30 | vtb_uq0_32_t lc_corr_threshold; ///< loss control correlation threshold to detect double talk 31 | vtb_uq16_16_t lc_bg_power_gamma;///< loss control background power gamma coefficient 32 | vtb_uq16_16_t lc_gamma_inc; ///< loss control gamma increment coefficient 33 | vtb_uq16_16_t lc_gamma_dec; ///< loss control gamma decrement coefficient 34 | vtb_uq16_16_t lc_far_delta; ///< delta multiplier used by loss control when only far-end activity is present 35 | vtb_uq16_16_t lc_near_delta; ///< delta multiplier used by loss control when only near-end activity is present 36 | vtb_uq16_16_t lc_near_delta_far_act; ///< delta multiplier used by loss control when both near and far-end activities are present 37 | vtb_uq16_16_t lc_gain_max; ///< gain applied by loss control when only near-end activity is present 38 | vtb_uq16_16_t lc_gain_dt; ///< gain applied by loss control when both near and far-end activities are present (double talk) 39 | vtb_uq16_16_t lc_gain_silence; ///< gain applied by loss control when only silence is present 40 | vtb_uq16_16_t lc_gain_min; ///< gain applied by loss control when only far-end activity is present 41 | } agc_ch_init_config_t; 42 | 43 | /** 44 | * Structure containing AGC configuration 45 | * Used to initialise AGC state. 46 | */ 47 | typedef struct { 48 | agc_ch_init_config_t ch_init_config[AGC_INPUT_CHANNELS]; 49 | } agc_init_config_t; 50 | 51 | /** Initialise AGC state given an initial configuration. 52 | * 53 | * \param[out] agc AGC state to be initialised. 54 | * 55 | * \param[in] config Array containing AGC configuration for each channel. 56 | * Must be of length AGC_INPUT_CHANNELS. 57 | */ 58 | void agc_init(REFERENCE_PARAM(agc_state_t, agc), agc_init_config_t config); 59 | 60 | /** Process a multi-channel frame of time-domain sample data. 61 | * 62 | * \param[in,out] agc AGC state. 63 | * 64 | * \param[in,out] frame_in_out On input this array contains the sample data. 65 | * On output this array contains the data with AGC 66 | * applied. 67 | * 68 | * \param[in] far_power Frame power of reference audio. 69 | * 70 | * \param[in] vad_flag VAD flag for input sample data. Non-zero indicates 71 | * that the sample data contains voice activity. 72 | * 73 | * \param[in] aec_corr AEC correlation value. A value close to 1 indicates 74 | * that the mic energy is dominated by reference audio. 75 | */ 76 | void agc_process_frame(REFERENCE_PARAM(agc_state_t, agc), 77 | vtb_ch_pair_t frame_in_out[AGC_CHANNEL_PAIRS][AGC_PROC_FRAME_LENGTH], 78 | vtb_u32_float_t far_power, 79 | int vad_flag, 80 | vtb_uq0_32_t aec_corr); 81 | 82 | 83 | #endif // _AGC_H_ 84 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/generate_unity_runners.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020-2021 XMOS LIMITED. 2 | # This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | import glob 4 | import os.path 5 | import subprocess 6 | import sys 7 | 8 | UNITY_TEST_DIR = 'src' 9 | UNITY_TEST_PREFIX = 'test_' 10 | UNITY_RUNNER_DIR = 'runners' 11 | UNITY_RUNNER_SUFFIX = '_Runner' 12 | project_root = os.path.join('..', '..', '..') 13 | 14 | def get_ruby(): 15 | """ 16 | Check ruby is avaliable and return the command to invoke it. 17 | """ 18 | interpreter_name = 'ruby' 19 | try: 20 | dev_null = open(os.devnull, 'w') 21 | # Call the version command to check the interpreter can be run 22 | subprocess.check_call([interpreter_name, '--version'], 23 | stdout=dev_null, 24 | close_fds=True) 25 | except OSError as e: 26 | print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) 27 | exit(1) # TODO: Check this is the correct way to kill xwaf on error 28 | 29 | return interpreter_name 30 | 31 | def get_unity_runner_generator(project_root_path): 32 | """ 33 | Check the Unity generate_test_runner script is avaliable, and return the 34 | path to it. 35 | """ 36 | unity_runner_generator = os.path.join( 37 | project_root_path, 'Unity', 'auto', 'generate_test_runner.rb') 38 | if not os.path.exists(unity_runner_generator): 39 | print("Unity repo not found in workspace", file=sys.stderr) 40 | exit(1) # TODO: Check this is the correct way to kill xwaf on error 41 | return unity_runner_generator 42 | 43 | 44 | def get_test_name(test_path): 45 | """ 46 | Return the test name by removing the extension from the filename. 47 | """ 48 | return os.path.splitext(os.path.basename(test_path))[0] 49 | 50 | 51 | def get_file_type(filename): 52 | """ 53 | Return the extension from the filename. 54 | """ 55 | return filename.rsplit('.')[-1:][0] 56 | 57 | 58 | def generate_unity_runner(project_root_path, unity_test_path, unity_runner_dir, 59 | unity_runner_suffix): 60 | """ 61 | Invoke the Unity runner generation script for the given test file, and 62 | return the path to the generated file. The output directory will be created 63 | if it does not already exist. 64 | """ 65 | runner_path = os.path.join(os.path.join(unity_runner_dir, get_test_name(unity_test_path))) 66 | if not os.path.exists(runner_path): 67 | os.makedirs(runner_path) 68 | 69 | unity_runner_path = os.path.join( 70 | runner_path, get_test_name(unity_test_path) + unity_runner_suffix 71 | + '.' + 'c') 72 | 73 | try: 74 | subprocess.check_call([get_ruby(), 75 | get_unity_runner_generator(project_root_path), 76 | unity_test_path, 77 | unity_runner_path]) 78 | except OSError as e: 79 | print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e), 80 | file=sys.stderr) 81 | exit(1) # TODO: Check this is the correct way to kill xwaf on error 82 | 83 | 84 | def find_unity_test_paths(unity_test_dir, unity_test_prefix): 85 | """ 86 | Return a list of all file paths with the unity_test_prefix found in the 87 | unity_test_dir. 88 | """ 89 | return glob.glob(os.path.join(unity_test_dir, unity_test_prefix+'*')) 90 | 91 | 92 | def find_unity_tests(unity_test_dir, unity_test_prefix): 93 | """ 94 | Return a dictionary of all {test names, test language} pairs with the 95 | unity_test_prefix found in the unity_test_dir. 96 | """ 97 | unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) 98 | print('unity_test_paths = ', unity_test_paths) 99 | return {get_test_name(path): get_file_type(path) 100 | for path in unity_test_paths} 101 | 102 | def find_unity_test_paths(unity_test_dir, unity_test_prefix): 103 | """ 104 | Return a list of all file paths with the unity_test_prefix found in the 105 | unity_test_dir. 106 | """ 107 | return glob.glob(os.path.join(unity_test_dir, unity_test_prefix+'*')) 108 | 109 | 110 | def generate_runners(): 111 | UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX) 112 | print('UNITY_TESTS = ',UNITY_TESTS) 113 | unity_test_paths = find_unity_test_paths(UNITY_TEST_DIR, UNITY_TEST_PREFIX) 114 | print('unity_test_paths = ',unity_test_paths) 115 | for unity_test_path in unity_test_paths: 116 | generate_unity_runner(project_root, unity_test_path, UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX) 117 | 118 | 119 | if __name__ == "__main__": 120 | generate_runners() 121 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/conftest.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2021 XMOS LIMITED. 2 | # This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | from __future__ import print_function 4 | from builtins import str 5 | import os.path 6 | import pytest 7 | import subprocess 8 | 9 | target = os.environ.get('TARGET', 'all_possible') 10 | print("target = ", target) 11 | 12 | def pytest_collect_file(parent, path): 13 | if(path.ext == ".xe"): 14 | if(target == 'all_possible'): 15 | return UnityTestSource.from_parent(parent, fspath=path) 16 | if(target == 'XCOREAI' and ('xcoreai' in path.basename)): 17 | return UnityTestSource.from_parent(parent, fspath=path) 18 | if(target == 'XCORE200' and ('xcore200' in path.basename)): 19 | return UnityTestSource.from_parent(parent, fspath=path) 20 | 21 | 22 | class UnityTestSource(pytest.File): 23 | def collect(self): 24 | # Find the binary built from the runner for this test file 25 | # 26 | # Assume the following directory layout: 27 | # unit_tests/ <- Test root directory 28 | # |-- bin/ <- Compiled binaries of the test runners 29 | # |-- conftest.py <- This file 30 | # |-- runners/ <- Auto-generated buildable source of test binaries 31 | # |-- src/ <- Unity test functions 32 | # `-- wscript <- Build system file used to generate/build runners 33 | xe_name = ((os.path.basename(self.name)).split("."))[0] + ".xe" 34 | test_bin_path = os.path.join('bin', xe_name) 35 | 36 | yield UnityTestExecutable.from_parent(self, name=self.name) 37 | 38 | 39 | class UnityTestExecutable(pytest.Item): 40 | def __init__(self, name, parent): 41 | super(UnityTestExecutable, self).__init__(name, parent) 42 | self._nodeid = self.name # Override the naming to suit C better 43 | 44 | def runtest(self): 45 | # Run the binary in the simulator 46 | simulator_fail = False 47 | test_output = None 48 | try: 49 | if('xcore200' in self.name): 50 | print("run axe for executable ", self.name) 51 | test_output = subprocess.check_output(['axe', self.name], text=True) 52 | else: 53 | print("run xrun for executable ", self.name) 54 | test_output = subprocess.check_output(['xrun', '--io', '--id', '0', self.name], text=True, stderr=subprocess.STDOUT) 55 | except subprocess.CalledProcessError as e: 56 | # Unity exits non-zero if an assertion fails 57 | simulator_fail = True 58 | test_output = e.output 59 | 60 | # Parse the Unity output 61 | unity_pass = False 62 | test_output = test_output.split('\n') 63 | for line in test_output: 64 | if 'test' in line: 65 | test_report = line.split(':') 66 | # Unity output is as follows: 67 | # :::PASS 68 | # :::FAIL: 69 | test_source = test_report[0] 70 | line_number = test_report[1] 71 | test_case = test_report[2] 72 | result = test_report[3] 73 | failure_reason = None 74 | print(('\n {}()'.format(test_case)), end=' ') 75 | if result == 'PASS': 76 | unity_pass = True 77 | continue 78 | if result == 'FAIL': 79 | failure_reason = test_report[4] 80 | print('') # Insert line break after test_case print 81 | raise UnityTestException(self, {'test_source': test_source, 82 | 'line_number': line_number, 83 | 'test_case': test_case, 84 | 'failure_reason': 85 | failure_reason}) 86 | 87 | if simulator_fail: 88 | raise Exception(self, "Simulation failed.") 89 | if not unity_pass: 90 | raise Exception(self, "Unity test output not found.") 91 | print('') # Insert line break after final test_case which passed 92 | 93 | def repr_failure(self, excinfo): 94 | if isinstance(excinfo.value, UnityTestException): 95 | return '\n'.join([str(self.parent).strip('<>'), 96 | '{}:{}:{}()'.format( 97 | excinfo.value[1]['test_source'], 98 | excinfo.value[1]['line_number'], 99 | excinfo.value[1]['test_case']), 100 | 'Failure reason:', 101 | excinfo.value[1]['failure_reason']]) 102 | else: 103 | return str(excinfo.value) 104 | 105 | def reportinfo(self): 106 | # It's not possible to give sensible line number info for an executable 107 | # so we return it as 0. 108 | # 109 | # The source line number will instead be recovered from the Unity print 110 | # statements. 111 | return self.fspath, 0, self.name 112 | 113 | 114 | class UnityTestException(Exception): 115 | pass 116 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/wscript: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import glob 3 | import os.path 4 | import subprocess 5 | import sys 6 | from waflib import Options 7 | from waflib.Build import BuildContext, CleanContext 8 | 9 | TARGETS = ['xcore200', 'xcoreai'] 10 | 11 | def get_ruby(): 12 | """ 13 | Check ruby is avaliable and return the command to invoke it. 14 | """ 15 | interpreter_name = 'ruby' 16 | try: 17 | dev_null = open(os.devnull, 'w') 18 | # Call the version command to check the interpreter can be run 19 | subprocess.check_call([interpreter_name, '--version'], 20 | stdout=dev_null, 21 | close_fds=True) 22 | except OSError as e: 23 | print("Failed to run Ruby interpreter: {}".format(e), file=sys.stderr) 24 | exit(1) # TODO: Check this is the correct way to kill xwaf on error 25 | 26 | return interpreter_name 27 | 28 | 29 | def get_unity_runner_generator(project_root_path): 30 | """ 31 | Check the Unity generate_test_runner script is avaliable, and return the 32 | path to it. 33 | """ 34 | unity_runner_generator = os.path.join( 35 | project_root_path, 'Unity', 'auto', 'generate_test_runner.rb') 36 | if not os.path.exists(unity_runner_generator): 37 | print("Unity repo not found in workspace", file=sys.stderr) 38 | exit(1) # TODO: Check this is the correct way to kill xwaf on error 39 | return unity_runner_generator 40 | 41 | 42 | def get_test_name(test_path): 43 | """ 44 | Return the test name by removing the extension from the filename. 45 | """ 46 | return os.path.splitext(os.path.basename(test_path))[0] 47 | 48 | 49 | def get_file_type(filename): 50 | """ 51 | Return the extension from the filename. 52 | """ 53 | return filename.rsplit('.')[-1:][0] 54 | 55 | 56 | def generate_unity_runner(project_root_path, unity_test_path, unity_runner_dir, 57 | unity_runner_suffix): 58 | """ 59 | Invoke the Unity runner generation script for the given test file, and 60 | return the path to the generated file. The output directory will be created 61 | if it does not already exist. 62 | """ 63 | runner_path = os.path.join(os.path.join(unity_runner_dir, get_test_name(unity_test_path))) 64 | if not os.path.exists(runner_path): 65 | os.makedirs(runner_path) 66 | 67 | unity_runner_path = os.path.join( 68 | runner_path, get_test_name(unity_test_path) + unity_runner_suffix 69 | + '.' + 'c') 70 | 71 | try: 72 | subprocess.check_call([get_ruby(), 73 | get_unity_runner_generator(project_root_path), 74 | unity_test_path, 75 | unity_runner_path]) 76 | except OSError as e: 77 | print("Ruby generator failed for {}\n\t{}".format(unity_test_path, e), 78 | file=sys.stderr) 79 | exit(1) # TODO: Check this is the correct way to kill xwaf on error 80 | 81 | 82 | def add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path, 83 | unity_runner_build_flags, target): 84 | """ 85 | Add a config to xwaf to build each Unity test runner into an xCORE 86 | executable. 87 | """ 88 | print(f"get_test_name(unity_test_path) = {get_test_name(unity_test_path)}. target = {target}") 89 | waf_conf.setenv(get_test_name(unity_test_path) + '_' + target) 90 | waf_conf.load('xwaf.compiler_xcc') 91 | waf_conf.env.XCC_FLAGS = unity_runner_build_flags 92 | waf_conf.env.PROJECT_ROOT = project_root_path 93 | # TODO: can the xwaf boilerplate help here? 94 | 95 | 96 | def prepare_unity_test_for_build(waf_conf, project_root_path, unity_test_path, 97 | unity_runner_dir, unity_runner_suffix, target): 98 | generate_unity_runner(project_root_path, unity_test_path, 99 | unity_runner_dir, unity_runner_suffix) 100 | runner_build_flags = '' # Could extract flags from the test name 101 | add_unity_runner_build_config(waf_conf, project_root_path, unity_test_path, 102 | runner_build_flags, target) 103 | 104 | 105 | def find_unity_test_paths(unity_test_dir, unity_test_prefix): 106 | """ 107 | Return a list of all file paths with the unity_test_prefix found in the 108 | unity_test_dir. 109 | """ 110 | return glob.glob(os.path.join(unity_test_dir, unity_test_prefix+'*')) 111 | 112 | 113 | def find_unity_tests(unity_test_dir, unity_test_prefix): 114 | """ 115 | Return a dictionary of all {test names, test language} pairs with the 116 | unity_test_prefix found in the unity_test_dir. 117 | """ 118 | unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) 119 | return {get_test_name(path): get_file_type(path) 120 | for path in unity_test_paths} 121 | 122 | 123 | def generate_all_unity_runners(waf_conf, project_root_path, 124 | unity_test_dir, unity_test_prefix, 125 | unity_runner_dir, unity_runner_suffix): 126 | """ 127 | Generate a runner and a build config for each test file in the 128 | unity_test_dir. 129 | """ 130 | # FIXME: pass unity_tests in? 131 | unity_test_paths = find_unity_test_paths(unity_test_dir, unity_test_prefix) 132 | for trgt in TARGETS: 133 | for unity_test_path in unity_test_paths: 134 | prepare_unity_test_for_build(waf_conf, project_root_path, 135 | unity_test_path, 136 | unity_runner_dir, unity_runner_suffix, trgt) 137 | 138 | 139 | # TODO: can the xwaf boilerplate help here? 140 | def create_waf_contexts(configs): 141 | for trgt in TARGETS: 142 | for test_name, test_language in configs.items(): 143 | print(f"test_name {test_name}, test_language {test_language}") 144 | for ctx in (BuildContext, CleanContext): 145 | raw_context = ctx.__name__.replace('Context', '').lower() 146 | 147 | class tmp(ctx): 148 | cmd = raw_context + '_' + test_name + '_' + trgt 149 | variant = test_name + '_' + trgt 150 | #cmd = raw_context + '_' + test_name 151 | #variant = test_name 152 | language = test_language 153 | target = trgt 154 | runner = test_name 155 | print(f"cmd {cmd}, variant {variant}, language {language}") 156 | 157 | 158 | UNITY_TEST_DIR = 'src' 159 | UNITY_TEST_PREFIX = 'test_' 160 | UNITY_RUNNER_DIR = 'runners' 161 | UNITY_RUNNER_SUFFIX = '_Runner' 162 | UNITY_TESTS = find_unity_tests(UNITY_TEST_DIR, UNITY_TEST_PREFIX) 163 | 164 | create_waf_contexts(UNITY_TESTS) 165 | 166 | def options(opt): 167 | opt.add_option('--target', action='store', default='xcore200') 168 | opt.load('xwaf.xcommon') 169 | 170 | def configure(conf): 171 | # TODO: move the call to generate_all_unity_runners() to build() 172 | project_root = os.path.join('..', '..', '..') 173 | generate_all_unity_runners(conf, project_root, 174 | UNITY_TEST_DIR, UNITY_TEST_PREFIX, 175 | UNITY_RUNNER_DIR, UNITY_RUNNER_SUFFIX) 176 | conf.load('xwaf.xcommon') 177 | 178 | 179 | def build(bld): 180 | if not bld.variant: 181 | print('Adding test runners to build queue') 182 | trgt = [ 183 | c for c in TARGETS if c == bld.options.target 184 | ] 185 | 186 | if len(trgt) == 0: 187 | bld.fatal('specify a target with --target.\nAvailable targets: {}'.format(', '.join(TARGETS))) 188 | return 189 | 190 | for name in UNITY_TESTS: 191 | Options.commands.insert(0, 'build_' + name + '_' + trgt[0]) 192 | #Options.commands.insert(0, 'build_' + name) 193 | print('Build queue {}'.format(Options.commands)) 194 | else: 195 | print('Building runner {}'.format(bld.runner)) 196 | bld.env.XSCOPE = bld.path.find_resource('config.xscope') 197 | 198 | depends_on = ['lib_agc', 'lib_voice_toolbox', 'lib_dsp', 'audio_test_tools', 199 | 'Unity'] 200 | makefile_opts = {} 201 | makefile_opts['SOURCE_DIRS'] = ['src', os.path.join('runners',bld.runner)] 202 | if(bld.target == 'xcoreai'): 203 | print('TARGET XCOREAI') 204 | makefile_opts['TARGET'] = ['XCORE-AI-EXPLORER'] 205 | else: 206 | print('TARGET XCORE200') 207 | makefile_opts['TARGET'] = ['XCORE-200-EXPLORER'] 208 | makefile_opts['INCLUDE_DIRS'] = ['src'] 209 | makefile_opts['XCC_FLAGS'] = ['-O2', '-g', '-Wall', '-DUNITY_SUPPORT_64', '-DUNITY_INCLUDE_DOUBLE'] 210 | makefile_opts['APP_NAME'] = [bld.variant] 211 | makefile_opts['USED_MODULES'] = depends_on 212 | makefile_opts['XCOMMON_MAKEFILE'] = ['Makefile.common'] 213 | bld.do_xcommon(makefile_opts) 214 | 215 | 216 | def test(bld): 217 | # Call pytest to run Unity tests inside axe or xsim 218 | try: 219 | test_output = subprocess.check_output(['pytest']) 220 | except subprocess.CalledProcessError as e: 221 | # pytest exits non-zero if an assertion fails 222 | test_output = e.output 223 | print(test_output) 224 | 225 | 226 | # TODO: ensure clean deletes the runners dir/ 227 | def dist(ctx): 228 | ctx.load('xwaf.xcommon') 229 | 230 | def distcheck(ctx): 231 | ctx.load('xwaf.xcommon') 232 | -------------------------------------------------------------------------------- /python/agc.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2021 XMOS LIMITED. 2 | # This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | from __future__ import division 4 | from builtins import object 5 | import numpy as np 6 | from math import sqrt 7 | 8 | class agc_ch(object): 9 | def __init__(self, adapt, adapt_on_vad, soft_clipping, init_gain, 10 | max_gain, min_gain, upper_threshold, lower_threshold, 11 | gain_inc, gain_dec, lc_enabled, 12 | lc_n_frame_near, lc_n_frame_far, 13 | lc_corr_threshold, 14 | lc_gamma_inc, lc_gamma_dec, lc_bg_power_gamma, 15 | lc_near_delta_far_act, lc_near_delta, lc_far_delta, 16 | lc_gain_max, lc_gain_dt, lc_gain_silence, lc_gain_min): 17 | if init_gain < 0: 18 | raise Exception("init_gain must be greater than 0.") 19 | if max_gain < 0: 20 | raise Exception("max_gain must be greater than 0.") 21 | if upper_threshold > 1.0: 22 | raise Exception("upper_threshold must be less than or equal to 1.") 23 | if lower_threshold > 1.0: 24 | raise Exception("lower_threshold must be less than or equal to 1.") 25 | self.adapt = adapt 26 | self.adapt_on_vad = adapt_on_vad 27 | self.soft_clipping = soft_clipping 28 | 29 | self.gain = init_gain 30 | self.max_gain = max_gain 31 | self.min_gain = min_gain 32 | 33 | self.gain_inc = gain_inc 34 | self.gain_dec = gain_dec 35 | 36 | self.threshold_upper = float(upper_threshold) 37 | self.threshold_lower = float(lower_threshold) 38 | 39 | self.lc_enabled = lc_enabled 40 | self.lc_gain = 1 41 | 42 | self.lc_n_frame_near = lc_n_frame_near 43 | self.lc_n_frame_far = lc_n_frame_far 44 | self.lc_corr_threshold = lc_corr_threshold 45 | self.lc_gamma_inc = lc_gamma_inc 46 | self.lc_gamma_dec = lc_gamma_dec 47 | self.lc_bg_power_gamma = lc_bg_power_gamma 48 | self.lc_near_delta_far_act = lc_near_delta_far_act 49 | self.lc_near_delta = lc_near_delta 50 | self.lc_far_delta = lc_far_delta 51 | self.lc_gain_max = lc_gain_max 52 | self.lc_gain_dt = lc_gain_dt 53 | self.lc_gain_silence = lc_gain_silence 54 | self.lc_gain_min = lc_gain_min 55 | 56 | self.lc_near_bg_power_est = agc.LC_BG_POWER_EST_INIT 57 | self.lc_near_power_est = agc.LC_POWER_EST_INIT 58 | self.lc_far_bg_power_est = agc.LC_FAR_BG_POWER_EST_INIT 59 | self.lc_far_power_est = agc.LC_FAR_BG_POWER_EST_INIT 60 | 61 | self.lc_t_far = 0 62 | self.lc_t_near = 0 63 | self.corr_val = 0 64 | 65 | 66 | 67 | def process_channel(self, input_frame, ref_power, vad, aec_corr_factor): 68 | if self.adapt_on_vad == 0: 69 | vad = True 70 | if(self.adapt): 71 | peak_sample = np.absolute(input_frame).max() #Sample input from Ch0 72 | rising = peak_sample > abs(self.x_slow) 73 | 74 | alpha_slow = agc.ALPHA_SLOW_RISE if rising else agc.ALPHA_SLOW_FALL 75 | self.x_slow = (1 - alpha_slow) * peak_sample + alpha_slow * self.x_slow 76 | 77 | alpha_fast = agc.ALPHA_FAST_RISE if rising else agc.ALPHA_FAST_FALL 78 | self.x_fast = (1 - alpha_fast) * peak_sample + alpha_fast * self.x_fast 79 | 80 | exceed_desired_level = (peak_sample * self.gain) > self.threshold_upper 81 | 82 | if vad or exceed_desired_level: 83 | alpha_peak = agc.ALPHA_PEAK_RISE if self.x_fast > self.x_peak else agc.ALPHA_PEAK_FALL 84 | self.x_peak = (1 - alpha_peak) * self.x_fast + alpha_peak * self.x_peak 85 | 86 | g_mod = 1 87 | near_only = (self.lc_t_far == 0) and (self.lc_t_near > 0) 88 | if (self.x_peak * self.gain < self.threshold_lower) and (not self.lc_enabled or near_only): 89 | g_mod = self.gain_inc 90 | elif self.x_peak * self.gain > self.threshold_upper: 91 | g_mod = self.gain_dec 92 | 93 | self.gain = min(g_mod * self.gain, self.max_gain) 94 | self.gain = max(g_mod * self.gain, self.min_gain) 95 | 96 | 97 | # Loss Control 98 | far_power_alpha = agc.LC_EST_ALPHA_INC 99 | if ref_power < self.lc_far_power_est: 100 | far_power_alpha = agc.LC_EST_ALPHA_DEC 101 | self.lc_far_power_est = (far_power_alpha) * self.lc_far_power_est + (1 - far_power_alpha) * ref_power 102 | 103 | self.lc_far_bg_power_est = min(self.lc_bg_power_gamma * self.lc_far_bg_power_est, self.lc_far_power_est) 104 | self.lc_far_bg_power_est = max(self.lc_far_bg_power_est, agc.LC_FAR_BG_POWER_EST_MIN) 105 | 106 | frame_power = np.mean(input_frame**2.0) 107 | near_power_alpha = agc.LC_EST_ALPHA_INC 108 | if frame_power < self.lc_near_power_est: 109 | near_power_alpha = agc.LC_EST_ALPHA_DEC 110 | 111 | self.lc_near_power_est = (near_power_alpha) * self.lc_near_power_est + (1 - near_power_alpha) * frame_power 112 | 113 | if(self.lc_near_bg_power_est > self.lc_near_power_est): 114 | self.lc_near_bg_power_est = (agc.LC_BG_POWER_EST_ALPHA_DEC) * self.lc_near_bg_power_est + (1 - agc.LC_BG_POWER_EST_ALPHA_DEC) * self.lc_near_power_est 115 | else: 116 | self.lc_near_bg_power_est = self.lc_bg_power_gamma * self.lc_near_bg_power_est 117 | 118 | 119 | gained_input = input_frame 120 | if(self.lc_enabled): 121 | 122 | if aec_corr_factor > self.corr_val: 123 | self.corr_val = aec_corr_factor 124 | else: 125 | self.corr_val = 0.98 * self.corr_val + 0.02 * aec_corr_factor 126 | 127 | # Update far-end activity timer 128 | if(self.lc_far_power_est > self.lc_far_delta * self.lc_far_bg_power_est): 129 | self.lc_t_far = self.lc_n_frame_far 130 | else: 131 | self.lc_t_far = max(0, self.lc_t_far - 1) 132 | delta = self.lc_near_delta 133 | if self.lc_t_far > 0: 134 | delta = self.lc_near_delta_far_act 135 | 136 | # Update near-end activity timer 137 | if(self.lc_near_power_est > (delta * self.lc_near_bg_power_est)): 138 | if self.lc_t_far == 0 or (self.lc_t_far > 0 and self.corr_val < self.lc_corr_threshold): 139 | # Near speech only or Double talk 140 | self.lc_t_near = self.lc_n_frame_near 141 | elif self.lc_t_far > 0 and self.corr_val >= self.lc_corr_threshold: 142 | # Far end speech only 143 | # Do nothing 144 | pass 145 | else: 146 | raise Exception("Reached here!") 147 | 148 | else: 149 | # Silence 150 | self.lc_t_near = max(0, self.lc_t_near - 1) 151 | 152 | # Adapt loss control gain 153 | if(self.lc_t_far <= 0 and self.lc_t_near > 0): 154 | # Near speech only 155 | target_gain = self.lc_gain_max 156 | elif(self.lc_t_far <= 0 and self.lc_t_near <= 0): 157 | # Silence 158 | target_gain = self.lc_gain_silence 159 | elif(self.lc_t_far > 0 and self.lc_t_near <= 0): 160 | # Far end only 161 | target_gain = self.lc_gain_min 162 | elif(self.lc_t_far > 0 and self.lc_t_near > 0): 163 | # Double talk 164 | target_gain = self.lc_gain_dt 165 | 166 | 167 | if(self.lc_gain > target_gain): 168 | for i, sample in enumerate(input_frame): 169 | self.lc_gain = max(target_gain, self.lc_gain * self.lc_gamma_dec) 170 | gained_input[i] = (self.lc_gain * self.gain) * sample 171 | else: 172 | for i, sample in enumerate(input_frame): 173 | self.lc_gain = min(target_gain, self.lc_gain * self.lc_gamma_inc) 174 | gained_input[i] = (self.lc_gain * self.gain) * sample 175 | 176 | else: 177 | gained_input = self.gain * input_frame 178 | 179 | def limit_gain(x): 180 | NONLINEAR_POINT = 0.5 181 | return x if (self.soft_clipping == 0 or abs(x) < NONLINEAR_POINT) else (np.sign(x) * 2 * NONLINEAR_POINT - NONLINEAR_POINT ** 2 / x) 182 | 183 | output_frame = [limit_gain(sample) for sample in gained_input] 184 | return output_frame 185 | 186 | 187 | class agc(object): 188 | # Adaption coefficients, do not change 189 | ALPHA_SLOW_RISE = 0.8869 190 | ALPHA_SLOW_FALL = 0.9646 191 | ALPHA_FAST_RISE = 0.3814 192 | ALPHA_FAST_FALL = 0.8869 193 | ALPHA_PEAK_RISE = 0.5480 194 | ALPHA_PEAK_FALL = 0.9646 195 | 196 | # Alpha values for EWMA calcuations 197 | LC_EST_ALPHA_INC = 0.5480 198 | LC_EST_ALPHA_DEC = 0.6973 199 | LC_BG_POWER_EST_ALPHA_DEC = 0.5480 200 | 201 | LC_CORR_PK_HOLD = 1 202 | 203 | LC_POWER_EST_INIT = 0.00001 204 | LC_BG_POWER_EST_INIT = 0.01 205 | LC_FAR_BG_POWER_EST_INIT = 0.01 206 | LC_FAR_BG_POWER_EST_MIN = 0.00001 207 | 208 | 209 | 210 | def __init__(self, ch_init_config): 211 | self.ch_state = [] 212 | self.input_ch_count = len(ch_init_config) 213 | for ch_idx in range(self.input_ch_count): 214 | self.ch_state.append(agc_ch(**ch_init_config[ch_idx])) 215 | self.ch_state[ch_idx].x_slow = 0 216 | self.ch_state[ch_idx].x_fast = 0 217 | self.ch_state[ch_idx].x_peak = 0 218 | 219 | 220 | def process_frame(self, input_frame, ref_power_est, vad, aec_corr_factor): 221 | output = np.zeros((self.input_ch_count, len(input_frame[0]))) 222 | for i in range(self.input_ch_count): 223 | output[i] = self.ch_state[i].process_channel(input_frame[i], ref_power_est, vad, aec_corr_factor) 224 | 225 | return output 226 | -------------------------------------------------------------------------------- /LICENSE.rst: -------------------------------------------------------------------------------- 1 | ******************************* 2 | XMOS PUBLIC LICENCE: Version 1 3 | ******************************* 4 | 5 | Subject to the conditions and limitations below, permission is hereby granted by XMOS LIMITED (“XMOS”), free of charge, to any person or entity obtaining a copy of the XMOS Software. 6 | 7 | **1. Definitions** 8 | 9 | **“Applicable Patent Rights”** means: (a) where XMOS is the grantor of the rights, (i) claims of patents that are now or in future owned by or assigned to XMOS and (ii) that cover subject matter contained in the Software, but only to the extent it is necessary to use, reproduce or distribute the Software without infringement; and (b) where you are the grantor of the rights, (i) claims of patents that are now or in future owned by or assigned to you and (ii) that cover the subject matter contained in your Derivatives, taken alone or in combination with the Software. 10 | 11 | **“Compiled Code”** means any compiled, binary, machine readable or executable version of the Source Code. 12 | 13 | **“Contributor”** means any person or entity that creates or contributes to the creation of Derivatives. 14 | 15 | **“Derivatives”** means any addition to, deletion from and/or change to the substance, structure of the Software, any previous Derivatives, the combination of the Derivatives and the Software and/or any respective portions thereof. 16 | 17 | **“Source Code”** means the human readable code that is suitable for making modifications but excluding any Compiled Code. 18 | 19 | **“Software”** means the software and associated documentation files which XMOS makes available and which contain a notice identifying the software as original XMOS software and referring to the software being subject to the terms of this XMOS Public Licence. 20 | 21 | This Licence refers to XMOS Software and does not relate to any XMOS hardware or devices which are protected by intellectual property rights (including patent and trade marks) which may be sold to you under a separate agreement. 22 | 23 | 24 | **2. Licence** 25 | 26 | **Permitted Uses, Conditions and Restrictions.** Subject to the conditions below, XMOS grants you a worldwide, royalty free, non-exclusive licence, to the extent of any Patent Rights to do the following: 27 | 28 | 2.1 **Unmodified Software.** You may use, copy, display, publish, distribute and make available unmodified copies of the Software: 29 | 30 | 2.1.1 for personal or academic, non-commercial purposes; or 31 | 32 | 2.1.2 for commercial purposes provided the Software is at all times used on a device designed, licensed or developed by XMOS and, provided that in each instance (2.1.1 and 2.1.2): 33 | 34 | (a) you must retain and reproduce in all copies of the Software the copyright and proprietary notices and disclaimers of XMOS as they appear in the Software, and keep intact all notices and disclaimers in the Software files that refer to this Licence; and 35 | 36 | (b) you must include a copy of this Licence with every copy of the Software and documentation you publish, distribute and make available and you may not offer or impose any terms on such Software that alter or restrict this Licence or the intent of such Licence, except as permitted below (Additional Terms). 37 | 38 | The licence above does not include any Compiled Code which XMOS may make available under a separate support and licence agreement. 39 | 40 | 2.2 **Derivatives.** You may create and modify Derivatives and use, copy, display, publish, distribute and make available Derivatives: 41 | 42 | 2.2.1 for personal or academic, non-commercial purposes; or 43 | 44 | 2.2.2 for commercial purposes, provided the Derivatives are at all times used on a device designed, licensed or developed by XMOS and, provided that in each instance (2.2.1 and 2.2.2): 45 | 46 | (a) you must comply with the terms of clause 2.1 with respect to the Derivatives; 47 | 48 | (b) you must copy (to the extent it doesn’t already exist) the notice below in each file of the Derivatives, and ensure all the modified files carry prominent notices stating that you have changed the files and the date of any change; and 49 | 50 | (c) if you sublicence, distribute or otherwise make the Software and/or the Derivatives available for commercial purposes, you must provide that the Software and Derivatives are at all times used on a device designed, licensed or developed by XMOS. 51 | 52 | Without limitation to these terms and clause 3 below, the Source Code and Compiled Code to your Derivatives may at your discretion (but without obligation) be released, copied, displayed, published, distributed and made available; and if you elect to do so, it must be under the terms of this Licence including the terms of the licence at clauses 2.2.1, 2.2.2 and clause 3 below. 53 | 54 | 2.3 **Distribution of Executable Versions.** If you distribute or make available Derivatives, you must include a prominent notice in the code itself as well as in all related documentation, stating that the Source Code of the Software from which the Derivatives are based is available under the terms of this Licence, with information on how and where to obtain such Source Code. 55 | 56 | **3. Your Grant of Rights.** In consideration and as a condition to this Licence, you grant to any person or entity receiving or distributing any Derivatives, a non-exclusive, royalty free, perpetual, irrevocable license under your Applicable Patent Rights and all other intellectual property rights owned or controlled by you, to use, copy, display, publish, distribute and make available your Derivatives of the same scope and extent as XMOS’s licence under clause 2.2 above. 57 | 58 | **4. Combined Products.** You may create a combined product by combining Software, Derivatives and other code not covered by this Licence as a single application or product. In such instance, you must comply with the requirements of this Licence for any portion of the Software and/or Derivatives. 59 | 60 | **5. Additional Terms.** You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations and/or other rights consistent with the term of this Licence (“Additional Terms”) to any legitimate recipients of the Software and/or Derivatives. The terms on which you provide such Additional Terms are on your sole responsibility and you shall indemnify, defend and hold XMOS harmless against any claims asserted against XMOS. 61 | 62 | **6. New Versions.** XMOS may publish revised and/or new versions of this Licence from time to time to accommodate changes to the Licence terms, new versions, updates and bug fixes of the Software. Each version will be given a distinguishing version number. Once Software has been published under a particular version of this Licence, you may continue to use it under the terms of that version. You may also choose to use the latest version of the Software under any subsequent version published by XMOS. Only XMOS shall have the right to modify these terms. 63 | 64 | **7. IPR and Ownership** 65 | Any rights, including all intellectual property rights and all trademarks not expressly granted herein are reserved in full by the authors or copyright holders. Any requests for additional permissions by XMOS including any rights to use XMOS trademarks, should be made (without obligation) to XMOS at **support@xmos.com** 66 | 67 | Nothing herein shall limit any rights that XMOS is otherwise entitled to under the doctrines of patent exhaustion, implied license, or legal estoppel. Neither the name of the authors, the copyright holders or any contributors may be used to endorse or promote any Derivatives from this Software without specific written permission. Any attempt to deal with the Software which does not comply with this Licence shall be void and shall automatically terminate any rights granted under this licence (including any licence of any intellectual property rights granted herein). 68 | Subject to the licences granted under this Licence any Contributor retains all rights, title and interest in and to any Derivatives made by Contributor subject to the underlying rights of XMOS in the Software. XMOS shall retain all rights, title and interest in the Software and any Derivatives made by XMOS (“XMOS Derivatives”). XMOS Derivatives will not automatically be subject to this Licence and XMOS shall be entitled to licence such rights on any terms (without obligation) as it sees fit. 69 | 70 | **8. Termination** 71 | 72 | 8.1 This Licence will automatically terminate immediately, without notice to you, if: 73 | 74 | (a) you fail to comply with the terms of this Licence; and/or 75 | 76 | (b) you directly or indirectly commence any action for patent or intellectual property right infringement against XMOS, or any parent, group, affiliate or subsidiary of XMOS; provided XMOS did not first commence an action or patent infringement against you in that instance; and/or 77 | 78 | (c) the terms of this Licence are held by any court of competent jurisdiction to be unenforceable in whole or in part. 79 | 80 | **9. Critical Applications.** Unless XMOS has agreed in writing with you an agreement specifically governing use of the Goods in military, aerospace, automotive or medically related functions (collectively and individually hereinafter referred to as "Special Use"), any permitted use of the Software excludes Special Use. Notwithstanding any agreement between XMOS and you for Special Use, Special Use shall be at your own risk, and you shall fully indemnify XMOS against any damages, losses, costs and claims (direct and indirect) arising out of any Special Use. 81 | 82 | **10. NO WARRANTY OR SUPPORT.** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL XMOS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, WARRANTY, CIVIL TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE INCLUDING GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES EVEN IF SUCH PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES AND NOT WITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE. IN SOME JURISDICTIONS PARTIES ARE UNABLE TO LIMIT LIABILTY IN THIS WAY, IF THIS APPLIES TO YOUR JURISDICTION THIS LIABILITY CLAUSE ABOVE MAY NOT APPLY. NOTWITHSTANDING THE ABOVE, IN NO EVENT SHALL XMOS’s TOTAL LIABILITY TO YOU FOR ALL DAMAGES, LOSS OR OTHERWISE EXCEED $50. 83 | 84 | **11. Governing Law and Jurisdiction.** This Licence constitutes the entire agreement between the parties with respect to the subject matter hereof. The Licence shall be governed by the laws of England and the conflict of laws and UN Convention on Contracts for the International Sale of Goods, shall not apply. 85 | -------------------------------------------------------------------------------- /cmake_utils/xmos_utils.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | IF(NOT DEFINED ENV{AGC_PATH}) 4 | message(FATAL_ERROR "Environment var AGC_PATH must be set before including xmos_utils.cmake") 5 | endif() 6 | 7 | # Set up compiler 8 | include("$ENV{AGC_PATH}/cmake_utils/xmos_toolchain.cmake") 9 | 10 | set(XMOS_MODULES_ROOT_DIR "$ENV{AGC_PATH}") 11 | 12 | if(PROJECT_SOURCE_DIR) 13 | message(FATAL_ERROR "xmos_utils.cmake must be included before a project definition") 14 | endif() 15 | 16 | # Check that supported bitstream has been specified 17 | include("bitstream_src/supported_hw.cmake") 18 | if(DEFINED BOARD) 19 | if(${BOARD} IN_LIST SUPPORTED_HW) 20 | include("bitstream_src/${BOARD}/board.cmake") 21 | else() 22 | message("\nConfiguration for ${BOARD} not found.\nPreconfigured bitstreams are:") 23 | foreach(HW ${SUPPORTED_HW}) 24 | message("\t${HW}") 25 | endforeach() 26 | message(FATAL_ERROR "") 27 | endif() 28 | else() 29 | message("\n-DBOARD must be specified.\nPreconfigured bitstreams are:") 30 | foreach(HW ${SUPPORTED_HW}) 31 | message("\t${HW}") 32 | endforeach() 33 | message(FATAL_ERROR "") 34 | endif() 35 | 36 | ## Setup at caller scope 37 | 38 | # Set up some XMOS specific variables 39 | set(FULL 1 ) 40 | set(BITSTREAM_ONLY 2 ) 41 | set(BSP_ONLY 3 ) 42 | define_property(TARGET PROPERTY OPTIONAL_HEADERS BRIEF_DOCS "Contains list of optional headers." FULL_DOCS "Contains a list of optional headers. The application level should search through all app includes and define D__[header]_h_exists__ for each header that is in both the app and optional headers.") 43 | define_property(GLOBAL PROPERTY XMOS_TARGETS_LIST BRIEF_DOCS "brief" FULL_DOCS "full") 44 | 45 | # Setup build output 46 | file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/bin") 47 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin") 48 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/libs") 49 | 50 | function(XMOS_ADD_FILE_COMPILER_FLAGS) 51 | if(NOT ${ARGC} EQUAL 2) 52 | message(FATAL_ERROR "XMOS_ADD_FILE_COMPILER_FLAGS requires 2 arguments, a file and string of flags") 53 | else() 54 | if(NOT EXISTS ${ARGV0} AND NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${ARGV0}) 55 | message(FATAL_ERROR "arg 0 must be a file relative to the caller.") 56 | endif() 57 | if(NOT ${ARGV1} STRGREATER "") 58 | message(FATAL_ERROR "arg 1 must be a non empty string.") 59 | endif() 60 | endif() 61 | 62 | set_source_files_properties(${ARGV0} PROPERTIES COMPILE_FLAGS ${ARGV1}) 63 | endfunction() 64 | 65 | 66 | ## Registers an application and it's dependencies 67 | function(XMOS_REGISTER_APP) 68 | if(NOT APP_HW_TARGET) 69 | set(APP_HW_TARGET ${BOARD_HW_TARGET}) 70 | endif() 71 | 72 | ## Populate build flag for hardware target 73 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${APP_HW_TARGET}) 74 | get_filename_component(HW_ABS_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${APP_HW_TARGET} ABSOLUTE) 75 | set(APP_TARGET_COMPILER_FLAG ${HW_ABS_PATH}) 76 | else() 77 | set(APP_TARGET_COMPILER_FLAG "-target=${APP_HW_TARGET}") 78 | endif() 79 | 80 | if(DEFINED THIS_XCORE_TILE) 81 | list(APPEND APP_COMPILER_FLAGS "-DTHIS_XCORE_TILE=${THIS_XCORE_TILE}") 82 | endif() 83 | 84 | set(LIB_NAME ${PROJECT_NAME}_LIB) 85 | set(LIB_VERSION ${PROJECT_VERSION}) 86 | set(LIB_ADD_COMPILER_FLAGS ${APP_COMPILER_FLAGS} ${BOARD_COMPILE_FLAGS}) 87 | set(LIB_XC_SRCS ${APP_XC_SRCS} ${BOARD_XC_SRCS}) 88 | set(LIB_CXX_SRCS ${APP_CXX_SRCS} ${BOARD_CXX_SRCS}) 89 | set(LIB_C_SRCS ${APP_C_SRCS} ${BOARD_C_SRCS}) 90 | set(LIB_ASM_SRCS ${APP_ASM_SRCS} ${BOARD_ASM_SRCS}) 91 | set(LIB_INCLUDES ${APP_INCLUDES} ${BOARD_INCLUDES}) 92 | set(LIB_DEPENDENT_MODULES ${APP_DEPENDENT_MODULES}) 93 | set(LIB_OPTIONAL_HEADERS "") 94 | set(LIB_FILE_FLAGS "") 95 | 96 | XMOS_REGISTER_MODULE("silent") 97 | 98 | get_target_property(${PROJECT_NAME}_LIB_SRCS ${PROJECT_NAME}_LIB SOURCES) 99 | get_target_property(${PROJECT_NAME}_LIB_INCS ${PROJECT_NAME}_LIB INCLUDE_DIRECTORIES) 100 | get_target_property(${PROJECT_NAME}_LIB_OPTINCS ${PROJECT_NAME}_LIB OPTIONAL_HEADERS) 101 | get_target_property(${PROJECT_NAME}_LIB_FILE_FLAGS ${PROJECT_NAME}_LIB FILE_FLAGS) 102 | 103 | set(APP_SOURCES ${APP_XC_SRCS} ${BOARD_XC_SRCS} ${APP_CXX_SRCS} ${BOARD_CXX_SRCS} ${APP_C_SRCS} ${BOARD_C_SRCS} ${APP_ASM_SRCS} ${BOARD_ASM_SRCS}) 104 | set(APP_INCLUDES ${APP_INCLUDES} ${BOARD_INCLUDES}) 105 | 106 | get_property(XMOS_TARGETS_LIST GLOBAL PROPERTY XMOS_TARGETS_LIST) 107 | 108 | foreach(lib ${XMOS_TARGETS_LIST}) 109 | get_target_property(inc ${lib} INCLUDE_DIRECTORIES) 110 | list(APPEND APP_INCLUDES ${inc}) 111 | endforeach() 112 | 113 | list(REMOVE_DUPLICATES APP_SOURCES) 114 | list(REMOVE_DUPLICATES APP_INCLUDES) 115 | 116 | # Only define header exists, if header optional 117 | foreach(inc ${APP_INCLUDES}) 118 | file(GLOB headers ${inc}/*.h) 119 | foreach(header ${headers}) 120 | get_filename_component(name ${header} NAME) 121 | list(FIND ${PROJECT_NAME}_LIB_OPTINCS ${name} FOUND) 122 | if(${FOUND} GREATER -1) 123 | get_filename_component(name_we ${header} NAME_WE) 124 | list(APPEND HEADER_EXIST_FLAGS -D__${name_we}_h_exists__) 125 | endif() 126 | endforeach() 127 | endforeach() 128 | 129 | if(DEFINED THIS_XCORE_TILE) 130 | set(TARGET_NAME "${PROJECT_NAME}_${THIS_XCORE_TILE}.xe") 131 | file(MAKE_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/tile${THIS_XCORE_TILE}") 132 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/tile${THIS_XCORE_TILE}") 133 | else() 134 | set(TARGET_NAME "${PROJECT_NAME}.xe") 135 | endif() 136 | 137 | set(APP_COMPILE_FLAGS ${APP_TARGET_COMPILER_FLAG} ${LIB_ADD_COMPILER_FLAGS} ${APP_COMPILER_C_FLAGS} ${HEADER_EXIST_FLAGS}) 138 | 139 | add_executable(${TARGET_NAME}) 140 | target_sources(${TARGET_NAME} PRIVATE ${APP_SOURCES}) 141 | 142 | target_include_directories(${TARGET_NAME} PRIVATE ${APP_INCLUDES}) 143 | target_compile_options(${TARGET_NAME} PRIVATE ${APP_COMPILE_FLAGS}) 144 | 145 | set(DEPS_TO_LINK "") 146 | foreach(target ${XMOS_TARGETS_LIST}) 147 | target_include_directories(${target} PRIVATE ${APP_INCLUDES}) 148 | target_compile_options(${target} BEFORE PRIVATE ${APP_COMPILE_FLAGS}) 149 | add_dependencies(${TARGET_NAME} ${target}) 150 | list(APPEND DEPS_TO_LINK ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/lib${target}.a) 151 | endforeach() 152 | list(REMOVE_DUPLICATES DEPS_TO_LINK) 153 | 154 | target_link_libraries(${TARGET_NAME} PRIVATE ${DEPS_TO_LINK}) 155 | target_link_options(${TARGET_NAME} PRIVATE ${APP_COMPILE_FLAGS}) 156 | endfunction() 157 | 158 | ## Registers a module and it's dependencies 159 | function(XMOS_REGISTER_MODULE) 160 | if(ARGC GREATER 0) 161 | ## Do not output added library message 162 | string(FIND ${ARGV0} "silent" ${LIB_NAME}_SILENT_FLAG) 163 | if(${LIB_NAME}_SILENT_FLAG GREATER -1) 164 | set(${LIB_NAME}_SILENT_FLAG True) 165 | endif() 166 | endif() 167 | 168 | set(DEP_MODULE_LIST "") 169 | if(NOT TARGET ${LIB_NAME}) 170 | if(NOT ${LIB_NAME}_SILENT_FLAG) 171 | add_library(${LIB_NAME} STATIC) 172 | set_property(TARGET ${LIB_NAME} PROPERTY VERSION ${LIB_VERSION}) 173 | else() 174 | add_library(${LIB_NAME} OBJECT EXCLUDE_FROM_ALL) 175 | set_property(TARGET ${LIB_NAME} PROPERTY VERSION ${LIB_VERSION}) 176 | endif() 177 | 178 | set(DEP_OPTIONAL_HEADERS "") 179 | set(DEP_FILE_FLAGS "") 180 | foreach(DEP_MODULE ${LIB_DEPENDENT_MODULES}) 181 | string(REGEX MATCH "^[A-Za-z0-9_ -]+" DEP_NAME ${DEP_MODULE}) 182 | string(REGEX REPLACE "^[A-Za-z0-9_ -]+" "" DEP_FULL_REQ ${DEP_MODULE}) 183 | 184 | list(APPEND DEP_MODULE_LIST ${DEP_NAME}) 185 | if("${DEP_FULL_REQ}" STREQUAL "") 186 | message(FATAL_ERROR "Missing dependency version requirement for ${DEP_NAME} in ${LIB_NAME}.\nA version requirement must be specified for all dependencies.") 187 | endif() 188 | 189 | string(REGEX MATCH "[0-9.]+" VERSION_REQ ${DEP_FULL_REQ} ) 190 | string(REGEX MATCH "[<>=]+" VERSION_QUAL_REQ ${DEP_FULL_REQ} ) 191 | 192 | # Add dependencies directories 193 | if(NOT TARGET ${DEP_NAME}) 194 | if(EXISTS ${XMOS_MODULES_ROOT_DIR}/${DEP_NAME}) 195 | add_subdirectory("${XMOS_MODULES_ROOT_DIR}/${DEP_NAME}" "${CMAKE_BINARY_DIR}/${DEP_NAME}") 196 | elseif(EXISTS ${XMOS_APP_MODULES_ROOT_DIR}/${DEP_NAME}) 197 | add_subdirectory("${XMOS_APP_MODULES_ROOT_DIR}/${DEP_NAME}" "${CMAKE_BINARY_DIR}/${DEP_NAME}") 198 | else() 199 | message(FATAL_ERROR "Missing dependency ${DEP_NAME}") 200 | endif() 201 | 202 | get_target_property(${DEP_NAME}_optinc ${DEP_NAME} OPTIONAL_HEADERS) 203 | list(APPEND DEP_OPTIONAL_HEADERS ${${DEP_NAME}_optinc}) 204 | endif() 205 | 206 | # Check dependency version 207 | get_target_property(DEP_VERSION ${DEP_NAME} VERSION) 208 | 209 | if(DEP_VERSION VERSION_EQUAL VERSION_REQ) 210 | string(FIND ${VERSION_QUAL_REQ} "=" DEP_VERSION_CHECK) 211 | elseif(DEP_VERSION VERSION_LESS VERSION_REQ) 212 | string(FIND ${VERSION_QUAL_REQ} "<" DEP_VERSION_CHECK) 213 | elseif(DEP_VERSION VERSION_GREATER VERSION_REQ) 214 | string(FIND ${VERSION_QUAL_REQ} ">" DEP_VERSION_CHECK) 215 | endif() 216 | 217 | if(${DEP_VERSION_CHECK} EQUAL "-1") 218 | message(WARNING "${LIB_NAME} dependency ${DEP_MODULE} not met. Found ${DEP_NAME}(${DEP_VERSION}).") 219 | endif() 220 | endforeach() 221 | 222 | if(NOT ${LIB_NAME}_SILENT_FLAG) 223 | get_property(XMOS_TARGETS_LIST GLOBAL PROPERTY XMOS_TARGETS_LIST) 224 | set_property(GLOBAL PROPERTY XMOS_TARGETS_LIST "${XMOS_TARGETS_LIST};${LIB_NAME}") 225 | endif() 226 | 227 | ## Set optional headers 228 | if(${LIB_NAME}_DEPS_ONLY_FLAG) 229 | set(LIB_OPTIONAL_HEADERS ${DEP_OPTIONAL_HEADERS}) 230 | else() 231 | list(APPEND LIB_OPTIONAL_HEADERS ${DEP_OPTIONAL_HEADERS}) 232 | endif() 233 | list(REMOVE_DUPLICATES LIB_OPTIONAL_HEADERS) 234 | list(FILTER LIB_OPTIONAL_HEADERS EXCLUDE REGEX "^.+-NOTFOUND$") 235 | set_property(TARGET ${LIB_NAME} PROPERTY OPTIONAL_HEADERS ${LIB_OPTIONAL_HEADERS}) 236 | 237 | if(NOT ${LIB_NAME}_SILENT_FLAG) 238 | if("${LIB_ADD_COMPILER_FLAGS}" STREQUAL "") 239 | else() 240 | foreach(file ${LIB_XC_SRCS}) 241 | get_filename_component(ABS_PATH ${file} ABSOLUTE) 242 | string(REPLACE ";" " " NEW_FLAGS "${LIB_ADD_COMPILER_FLAGS}") 243 | set_source_files_properties(${ABS_PATH} PROPERTIES COMPILE_FLAGS ${NEW_FLAGS}) 244 | endforeach() 245 | 246 | foreach(file ${LIB_CXX_SRCS}) 247 | get_filename_component(ABS_PATH ${file} ABSOLUTE) 248 | string(REPLACE ";" " " NEW_FLAGS "${LIB_ADD_COMPILER_FLAGS}") 249 | set_source_files_properties(${ABS_PATH} PROPERTIES COMPILE_FLAGS ${NEW_FLAGS}) 250 | endforeach() 251 | 252 | foreach(file ${LIB_C_SRCS}) 253 | get_filename_component(ABS_PATH ${file} ABSOLUTE) 254 | string(REPLACE ";" " " NEW_FLAGS "${LIB_ADD_COMPILER_FLAGS}") 255 | set_source_files_properties(${ABS_PATH} PROPERTIES COMPILE_FLAGS ${NEW_FLAGS}) 256 | endforeach() 257 | endif() 258 | endif() 259 | target_sources(${LIB_NAME} PUBLIC ${LIB_XC_SRCS} ${LIB_CXX_SRCS} ${LIB_ASM_SRCS} ${LIB_C_SRCS}) 260 | target_include_directories(${LIB_NAME} PRIVATE ${LIB_INCLUDES}) 261 | 262 | if(NOT ${LIB_NAME}_SILENT_FLAG) 263 | set(DEPS_TO_LINK "") 264 | foreach(module ${DEP_MODULE_LIST}) 265 | list(APPEND DEPS_TO_LINK "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/lib${module}.a") 266 | add_dependencies(${LIB_NAME} ${module}) 267 | endforeach() 268 | 269 | target_link_libraries( 270 | ${LIB_NAME} 271 | PUBLIC 272 | ${DEPS_TO_LINK} 273 | ) 274 | target_compile_options(${LIB_NAME} PUBLIC ${LIB_ADD_COMPILER_FLAGS}) 275 | endif() 276 | 277 | if(NOT ${LIB_NAME}_SILENT_FLAG) 278 | message("Added ${LIB_NAME} (${LIB_VERSION})") 279 | endif() 280 | endif() 281 | endfunction() 282 | -------------------------------------------------------------------------------- /lib_agc/src/agc_control.xc: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #include "agc_control.h" 4 | void agc_command_handler(chanend c_command, agc_state_t &agc_state){ 5 | uint8_t cmd; 6 | c_command :> cmd; 7 | switch(cmd){ 8 | case agc_cmd_get_gain: 9 | uint32_t vals[AGC_INPUT_CHANNELS]; 10 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 11 | vals[i] = (uint32_t)agc_get_ch_gain(agc_state, i); 12 | } 13 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 14 | break; 15 | case agc_cmd_set_gain: 16 | uint32_t instructions[2]; 17 | vtb_control_handle_set_n_words(c_command, 2, instructions); 18 | if (instructions[1] < AGC_INPUT_CHANNELS) { 19 | agc_set_ch_gain(agc_state, instructions[1], instructions[0]); 20 | } 21 | break; 22 | case agc_cmd_get_max_gain: 23 | uint32_t vals[AGC_INPUT_CHANNELS]; 24 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 25 | vals[i] = (uint32_t)agc_get_ch_max_gain(agc_state, i); 26 | } 27 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 28 | break; 29 | case agc_cmd_set_max_gain: 30 | uint32_t instructions[2]; 31 | vtb_control_handle_set_n_words(c_command, 2, instructions); 32 | if (instructions[1] < AGC_INPUT_CHANNELS) { 33 | agc_set_ch_max_gain(agc_state, instructions[1], instructions[0]); 34 | } 35 | break; 36 | case agc_cmd_get_min_gain: 37 | uint32_t vals[AGC_INPUT_CHANNELS]; 38 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 39 | vals[i] = (uint32_t)agc_get_ch_min_gain(agc_state, i); 40 | } 41 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 42 | break; 43 | case agc_cmd_set_min_gain: 44 | uint32_t instructions[2]; 45 | vtb_control_handle_set_n_words(c_command, 2, instructions); 46 | if (instructions[1] < AGC_INPUT_CHANNELS) { 47 | agc_set_ch_min_gain(agc_state, instructions[1], instructions[0]); 48 | } 49 | break; 50 | 51 | case agc_cmd_get_adapt: 52 | uint32_t vals[AGC_INPUT_CHANNELS]; 53 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 54 | vals[i] = (uint32_t)agc_get_ch_adapt(agc_state, i); 55 | } 56 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 57 | break; 58 | case agc_cmd_set_adapt: 59 | uint32_t instructions[2]; 60 | vtb_control_handle_set_n_words(c_command, 2, instructions); 61 | if (instructions[1] < AGC_INPUT_CHANNELS) { 62 | agc_set_ch_adapt(agc_state, instructions[1], instructions[0]); 63 | } 64 | break; 65 | case agc_cmd_get_adapt_on_vad: 66 | uint32_t vals[AGC_INPUT_CHANNELS]; 67 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 68 | vals[i] = (uint32_t)agc_get_ch_adapt_on_vad(agc_state, i); 69 | } 70 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 71 | break; 72 | case agc_cmd_set_adapt_on_vad: 73 | uint32_t instructions[2]; 74 | vtb_control_handle_set_n_words(c_command, 2, instructions); 75 | if (instructions[1] < AGC_INPUT_CHANNELS) { 76 | agc_set_ch_adapt_on_vad(agc_state, instructions[1], instructions[0]); 77 | } 78 | break; 79 | case agc_cmd_get_soft_clipping: 80 | uint32_t vals[AGC_INPUT_CHANNELS]; 81 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 82 | vals[i] = (uint32_t)agc_get_ch_soft_clipping(agc_state, i); 83 | } 84 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 85 | break; 86 | case agc_cmd_set_soft_clipping: 87 | uint32_t instructions[2]; 88 | vtb_control_handle_set_n_words(c_command, 2, instructions); 89 | if (instructions[1] < AGC_INPUT_CHANNELS) { 90 | agc_set_ch_soft_clipping(agc_state, instructions[1], instructions[0]); 91 | } 92 | break; 93 | 94 | case agc_cmd_get_lc_enabled: 95 | uint32_t vals[AGC_INPUT_CHANNELS]; 96 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 97 | vals[i] = (uint32_t)agc_get_ch_lc_enable(agc_state, i); 98 | } 99 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 100 | break; 101 | case agc_cmd_set_lc_enabled: 102 | uint32_t instructions[2]; 103 | vtb_control_handle_set_n_words(c_command, 2, instructions); 104 | if (instructions[1] < AGC_INPUT_CHANNELS) { 105 | agc_set_ch_lc_enable(agc_state, instructions[1], instructions[0]); 106 | } 107 | break; 108 | case agc_cmd_get_desired_upper_threshold: 109 | uint32_t vals[AGC_INPUT_CHANNELS]; 110 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 111 | vals[i] = (uint32_t)agc_get_ch_upper_threshold(agc_state, i); 112 | } 113 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 114 | break; 115 | case agc_cmd_get_desired_lower_threshold: 116 | uint32_t vals[AGC_INPUT_CHANNELS]; 117 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 118 | vals[i] = (uint32_t)agc_get_ch_lower_threshold(agc_state, i); 119 | } 120 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 121 | break; 122 | case agc_cmd_set_desired_upper_threshold: 123 | uint32_t instructions[2]; 124 | vtb_control_handle_set_n_words(c_command, 2, instructions); 125 | if (instructions[1] < AGC_INPUT_CHANNELS) { 126 | agc_set_ch_upper_threshold(agc_state, instructions[1], instructions[0]); 127 | } 128 | break; 129 | case agc_cmd_set_desired_lower_threshold: 130 | uint32_t instructions[2]; 131 | vtb_control_handle_set_n_words(c_command, 2, instructions); 132 | if (instructions[1] < AGC_INPUT_CHANNELS) { 133 | agc_set_ch_lower_threshold(agc_state, instructions[1], instructions[0]); 134 | } 135 | break; 136 | case agc_cmd_get_gain_increment_stepsize: 137 | uint32_t vals[AGC_INPUT_CHANNELS]; 138 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 139 | vals[i] = (uint32_t)agc_get_ch_gain_inc(agc_state, i); 140 | } 141 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 142 | break; 143 | case agc_cmd_set_gain_increment_stepsize: 144 | uint32_t instructions[2]; 145 | vtb_control_handle_set_n_words(c_command, 2, instructions); 146 | if (instructions[1] < AGC_INPUT_CHANNELS) { 147 | agc_set_ch_gain_inc(agc_state, instructions[1], instructions[0]); 148 | } 149 | break; 150 | case agc_cmd_get_gain_decrement_stepsize: 151 | uint32_t vals[AGC_INPUT_CHANNELS]; 152 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 153 | vals[i] = (uint32_t)agc_get_ch_gain_dec(agc_state, i); 154 | } 155 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 156 | break; 157 | case agc_cmd_set_gain_decrement_stepsize: 158 | uint32_t instructions[2]; 159 | vtb_control_handle_set_n_words(c_command, 2, instructions); 160 | if (instructions[1] < AGC_INPUT_CHANNELS) { 161 | agc_set_ch_gain_dec(agc_state, instructions[1], instructions[0]); 162 | } 163 | break; 164 | case agc_cmd_get_lc_n_frames: 165 | uint32_t vals[AGC_INPUT_CHANNELS*LC_N_FRAMES_NUM]; 166 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 167 | { vals[LC_N_FRAMES_NUM*i], vals[LC_N_FRAMES_NUM*i+1] } = agc_get_ch_lc_n_frames(agc_state, i); 168 | } 169 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS*2, vals); 170 | break; 171 | case agc_cmd_set_lc_n_frames: 172 | uint32_t instructions[LC_N_FRAMES_NUM+1]; 173 | vtb_control_handle_set_n_words(c_command, LC_N_FRAMES_NUM+1, instructions); 174 | if (instructions[LC_N_FRAMES_NUM] < AGC_INPUT_CHANNELS) { 175 | uint32_t vals[LC_N_FRAMES_NUM]; 176 | vals[0] = instructions[0]; 177 | vals[1] = instructions[1]; 178 | 179 | agc_set_ch_lc_n_frames(agc_state, instructions[LC_N_FRAMES_NUM], vals); 180 | } 181 | break; 182 | case agc_cmd_get_lc_corr_threshold: 183 | uint32_t vals[AGC_INPUT_CHANNELS]; 184 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 185 | vals[i] = (uint32_t)agc_get_ch_lc_corr_threshold(agc_state, i); 186 | } 187 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS, vals); 188 | break; 189 | case agc_cmd_set_lc_corr_threshold: 190 | uint32_t instructions[2]; 191 | vtb_control_handle_set_n_words(c_command, 2, instructions); 192 | if (instructions[1] < AGC_INPUT_CHANNELS) { 193 | agc_set_ch_lc_corr_threshold(agc_state, instructions[1], instructions[0]); 194 | } 195 | break; 196 | case agc_cmd_get_lc_gammas: 197 | uint32_t vals[AGC_INPUT_CHANNELS*LC_GAMMAS_NUM]; 198 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 199 | { vals[LC_GAMMAS_NUM*i], vals[LC_GAMMAS_NUM*i+1], vals[LC_GAMMAS_NUM*i+2] } = agc_get_ch_lc_gammas(agc_state, i); 200 | } 201 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS*LC_GAMMAS_NUM, vals); 202 | break; 203 | case agc_cmd_set_lc_gammas: 204 | uint32_t instructions[LC_GAMMAS_NUM+1]; 205 | vtb_control_handle_set_n_words(c_command, LC_GAMMAS_NUM+1, instructions); 206 | if (instructions[LC_GAMMAS_NUM] < AGC_INPUT_CHANNELS) { 207 | uint32_t vals[LC_GAMMAS_NUM]; 208 | vals[0] = instructions[0]; 209 | vals[1] = instructions[1]; 210 | vals[2] = instructions[2]; 211 | 212 | agc_set_ch_lc_gammas(agc_state, instructions[LC_GAMMAS_NUM], vals); 213 | } 214 | break; 215 | case agc_cmd_get_lc_deltas: 216 | uint32_t vals[AGC_INPUT_CHANNELS*LC_DELTAS_NUM]; 217 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 218 | { vals[LC_DELTAS_NUM*i], vals[LC_DELTAS_NUM*i+1], vals[LC_DELTAS_NUM*i+2] } = agc_get_ch_lc_deltas(agc_state, i); 219 | } 220 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS*LC_DELTAS_NUM, vals); 221 | break; 222 | case agc_cmd_set_lc_deltas: 223 | uint32_t instructions[LC_DELTAS_NUM+1]; 224 | vtb_control_handle_set_n_words(c_command, LC_DELTAS_NUM+1, instructions); 225 | if (instructions[LC_DELTAS_NUM] < AGC_INPUT_CHANNELS) { 226 | uint32_t vals[LC_DELTAS_NUM]; 227 | vals[0] = instructions[0]; 228 | vals[1] = instructions[1]; 229 | vals[2] = instructions[2]; 230 | 231 | agc_set_ch_lc_deltas(agc_state, instructions[LC_DELTAS_NUM], vals); 232 | } 233 | break; 234 | case agc_cmd_get_lc_gains: 235 | uint32_t vals[AGC_INPUT_CHANNELS*LC_GAINS_NUM]; 236 | for(int i = 0; i < AGC_INPUT_CHANNELS; i ++) { 237 | { vals[LC_GAINS_NUM*i], vals[LC_GAINS_NUM*i+1], vals[LC_GAINS_NUM*i+2], vals[LC_GAINS_NUM*i+3] } = agc_get_ch_lc_gains(agc_state, i); 238 | } 239 | vtb_control_handle_get_n_words(c_command, AGC_INPUT_CHANNELS*LC_GAINS_NUM, vals); 240 | break; 241 | case agc_cmd_set_lc_gains: 242 | uint32_t instructions[LC_GAINS_NUM+1]; 243 | vtb_control_handle_set_n_words(c_command, LC_GAINS_NUM+1, instructions); 244 | if (instructions[LC_GAINS_NUM] < AGC_INPUT_CHANNELS) { 245 | uint32_t vals[LC_GAINS_NUM]; 246 | vals[0] = instructions[0]; 247 | vals[1] = instructions[1]; 248 | vals[2] = instructions[2]; 249 | vals[3] = instructions[3]; 250 | 251 | agc_set_ch_lc_gains(agc_state, instructions[LC_GAINS_NUM], vals); 252 | } 253 | break; 254 | 255 | default: 256 | c_command :> unsigned; // Receive payload length 257 | c_command <: 0; 258 | break; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /lib_agc/api/agc_control.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #ifndef AGC_CONTROL_H_ 4 | #define AGC_CONTROL_H_ 5 | 6 | #include 7 | #include "vtb_control.h" 8 | #include "agc_ch_state.h" 9 | #include "agc_control_map.h" 10 | 11 | void agc_command_handler(chanend c_command, agc_state_t &state); 12 | 13 | /** Set AGC channel gain. 14 | * 15 | * \param[in,out] agc AGC state. 16 | * 17 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 18 | * 19 | * \param[in] gain Gain value in linear UQ16_16 format. 20 | */ 21 | void agc_set_ch_gain(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, vtb_uq16_16_t gain); 22 | 23 | 24 | /** Get AGC channel gain. 25 | * 26 | * \param[in] agc AGC state. 27 | * 28 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 29 | * 30 | * \returns Channel gain in linear UQ16_16 format. 31 | */ 32 | vtb_uq16_16_t agc_get_ch_gain(agc_state_t agc, unsigned ch_index); 33 | 34 | 35 | /** Set AGC channel gain increase value. 36 | * 37 | * \param[in,out] agc AGC state. 38 | * 39 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 40 | * 41 | * \param[in] gain_inc Gain increase value in linear UQ16_16 format. 42 | * Must be greater than 1. 43 | */ 44 | void agc_set_ch_gain_inc(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, vtb_uq16_16_t gain_inc); 45 | 46 | 47 | /** Get AGC channel gain increase value. 48 | * 49 | * \param[in] agc AGC state. 50 | * 51 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 52 | * 53 | * \returns Channel gain_inc in linear UQ16_16 format. 54 | */ 55 | vtb_uq16_16_t agc_get_ch_gain_inc(agc_state_t agc, unsigned ch_index); 56 | 57 | 58 | /** Set AGC channel gain decrease value. 59 | * 60 | * \param[in,out] agc AGC state. 61 | * 62 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 63 | * 64 | * \param[in] gain_dec Gain decrease value in linear UQ16_16 format. 65 | * Must be between 0 and 1. 66 | */ 67 | void agc_set_ch_gain_dec(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, vtb_uq16_16_t gain_dec); 68 | 69 | 70 | /** Get AGC channel gain decrease value. 71 | * 72 | * \param[in] agc AGC state. 73 | * 74 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 75 | * 76 | * \returns Channel gain_dec in linear UQ16_16 format. 77 | */ 78 | vtb_uq16_16_t agc_get_ch_gain_dec(agc_state_t agc, unsigned ch_index); 79 | 80 | 81 | /** Set AGC channel max gain. 82 | * 83 | * \param[in,out] agc AGC state. 84 | * 85 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 86 | * 87 | * \param[in] max_gain Max gain value in linear UQ16_16 format. 88 | */ 89 | void agc_set_ch_max_gain(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, 90 | vtb_uq16_16_t max_gain); 91 | 92 | 93 | /** Get AGC channel max gain. 94 | * 95 | * \param[in] agc AGC state. 96 | * 97 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 98 | * 99 | * \returns Channel max gain in linear UQ16_16 format. 100 | */ 101 | vtb_uq16_16_t agc_get_ch_max_gain(agc_state_t agc, unsigned ch_index); 102 | 103 | 104 | /** Set AGC channel min gain. 105 | * 106 | * \param[in,out] agc AGC state. 107 | * 108 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 109 | * 110 | * \param[in] min_gain Min gain value in linear UQ16_16 format. 111 | */ 112 | void agc_set_ch_min_gain(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, 113 | vtb_uq16_16_t min_gain); 114 | 115 | 116 | /** Get AGC channel min gain. 117 | * 118 | * \param[in] agc AGC state. 119 | * 120 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 121 | * 122 | * \returns Channel min gain in linear UQ16_16 format. 123 | */ 124 | vtb_uq16_16_t agc_get_ch_min_gain(agc_state_t agc, unsigned ch_index); 125 | 126 | 127 | /** Set AGC channel adapt flag. 128 | * 129 | * \param[in,out] agc AGC state. 130 | * 131 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 132 | * 133 | * \param[in] adapt AGC adapt flag: 0 for fixed gain, 1 for adapt. 134 | */ 135 | void agc_set_ch_adapt(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, uint32_t adapt); 136 | 137 | 138 | /** Get AGC channel adapt flag. 139 | * 140 | * \param[in] agc AGC state. 141 | * 142 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 143 | * 144 | * \returns 0 for fixed gain, 1 for adapt. 145 | */ 146 | int agc_get_ch_adapt(agc_state_t agc, unsigned ch_index); 147 | 148 | 149 | 150 | /** Set AGC channel adapt on VAD flag. 151 | * 152 | * \param[in,out] agc AGC state. 153 | * 154 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 155 | * 156 | * \param[in] adapt AGC on VAD adapt flag: 0 for adapting when voice is detected, 1 if always adapting. 157 | */ 158 | void agc_set_ch_adapt_on_vad(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, uint32_t adapt); 159 | 160 | 161 | /** Get AGC channel adapt on VAD flag. 162 | * 163 | * \param[in] agc AGC state. 164 | * 165 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 166 | * 167 | * \returns 0 for adapting when voice is detected, 1 if always adapting. 168 | */ 169 | int agc_get_ch_adapt_on_vad(agc_state_t agc, unsigned ch_index); 170 | 171 | /** Set AGC channel soft clipping flag. 172 | * 173 | * \param[in,out] agc AGC state. 174 | * 175 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 176 | * 177 | * \param[in] soft_clipping AGC soft clipping flag: 0 for soft clipping disabled, 1 for for soft clipping enabled. 178 | */ 179 | void agc_set_ch_soft_clipping(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, uint32_t soft_clipping); 180 | 181 | 182 | /** Get AGC channel soft clipping flag. 183 | * 184 | * \param[in] agc AGC state. 185 | * 186 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 187 | * 188 | * \returns 0 for soft clipping disabled, 1 for for soft clipping enabled. 189 | */ 190 | int agc_get_ch_soft_clipping(agc_state_t agc, unsigned soft_clipping); 191 | 192 | 193 | /** Set AGC channel loss control enabled flag. 194 | * 195 | * \param[in,out] agc AGC state. 196 | * 197 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 198 | * 199 | * \param[in] enable AGC LC enable flag: 0 for disabled, 1 for enabled. 200 | */ 201 | void agc_set_ch_lc_enable(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, uint32_t enable); 202 | 203 | 204 | /** Get AGC channel loss control enabled flag. 205 | * 206 | * \param[in] agc AGC state. 207 | * 208 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 209 | * 210 | * \returns 0 for disabled, 1 for enabled. 211 | */ 212 | int agc_get_ch_lc_enable(agc_state_t agc, unsigned ch_index); 213 | 214 | 215 | /** Set upper threshold of desired output voice level for AGC channel. 216 | * 217 | * \param[in,out] agc AGC state. 218 | * 219 | * \param[in] ch_index Channel index. Must be less than 220 | * AGC_INPUT_CHANNELS. 221 | * 222 | * \param[in] upper_threshold Upper threshold of desired output voice level 223 | * [0, INT32_MAX]. 224 | */ 225 | void agc_set_ch_upper_threshold(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, 226 | int32_t upper_threshold); 227 | 228 | 229 | /** Set lower threshold of desired output voice level for AGC channel. 230 | * 231 | * \param[in,out] agc AGC state. 232 | * 233 | * \param[in] ch_index Channel index. Must be less than 234 | * AGC_INPUT_CHANNELS. 235 | * 236 | * \param[in] lower_threshold Lower threshold of desired output voice level 237 | * [0, INT32_MAX]. 238 | */ 239 | void agc_set_ch_lower_threshold(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, 240 | int32_t lower_threshold); 241 | 242 | 243 | /** Get upper threshold of desired output voice level for AGC channel. 244 | * 245 | * \param[in] agc AGC state. 246 | * 247 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 248 | * 249 | * \returns Lower threshold of esired output voice level for AGC channel. 250 | */ 251 | int32_t agc_get_ch_upper_threshold(agc_state_t agc, unsigned ch_index); 252 | 253 | 254 | /** Get lower threshold of desired output voice level for AGC channel. 255 | * 256 | * \param[in] agc AGC state. 257 | * 258 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 259 | * 260 | * \returns Lower threshold of desired output voice level for AGC channel. 261 | */ 262 | int32_t agc_get_ch_lower_threshold(agc_state_t agc, unsigned ch_index); 263 | 264 | /** Set loss control timers in frames for AGC channel 265 | * 266 | * \param[in,out] agc AGC state. 267 | * 268 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 269 | * 270 | * \param[in] frames 1. Number of frames to consider far-end audio active for AGC channel 271 | * 2. Number of frames to consider near-end audio active for AGC channel 272 | */ 273 | void agc_set_ch_lc_n_frames(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, uint32_t frames[2]); 274 | 275 | /** Get loss control timers in frames for AGC channel 276 | * 277 | * \param[in] agc AGC state. 278 | * 279 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 280 | * 281 | * \returns 1. Number of frames to consider far-end audio active for AGC channel 282 | * 2. Number of frames to consider near-end audio active for AGC channel 283 | */ 284 | {uint32_t, uint32_t} agc_get_ch_lc_n_frames(agc_state_t agc, unsigned ch_index); 285 | 286 | /** Get loss control correlation threshold for AGC channel. 287 | * 288 | * \param[in] agc AGC state. 289 | * 290 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 291 | * 292 | * \returns Loss control correlation threshold for AGC channel. 293 | */ 294 | uint32_t agc_get_ch_lc_corr_threshold(agc_state_t agc, unsigned ch_index); 295 | 296 | /** Set loss control correlation threshold for AGC channel. 297 | * \param[in,out] agc AGC state. 298 | * 299 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 300 | * 301 | * \param[in] lc_corr_threshold Loss control correlation threshold for AGC channel. 302 | * [0, INT32_MAX]. 303 | */ 304 | void agc_set_ch_lc_corr_threshold(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, 305 | uint32_t lc_corr_threshold); 306 | 307 | /** Get loss control gamma coefficients for AGC channel. 308 | * 309 | * \param[in] agc AGC state. 310 | * 311 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 312 | * 313 | * \returns 1. Loss control background power gamma coefficient for AGC channel 314 | * 2. Loss control gamma increment for AGC channel 315 | * 3. Loss control gamma decrement for AGC channel 316 | */ 317 | {uint32_t, uint32_t, uint32_t} agc_get_ch_lc_gammas(agc_state_t agc, unsigned ch_index); 318 | 319 | /** Set loss control gamma coefficients for AGC channel. 320 | * \param[in,out] agc AGC state. 321 | * 322 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 323 | * 324 | * \param[in] gammas 1. Loss control background power gamma coefficient for AGC channel 325 | * 2. Loss control gamma increment for AGC channel 326 | * 3. Loss control gamma decrement for AGC channel 327 | */ 328 | void agc_set_ch_lc_gammas(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, uint32_t gammas[3]); 329 | 330 | /** Set delta coefficients applied by loss control for AGC channel. 331 | * \param[in,out] agc AGC state. 332 | * 333 | * \param[in] ch_index Channel index. Must be less than 334 | * AGC_INPUT_CHANNELS. 335 | * 336 | * \param[in] deltas 1. Delta value applied by loss control when only far-end audio is present 337 | * 2. Delta value applied by loss control when only near-end audio is present 338 | * 3. Delta value applied by loss control when both near-end and far-end audio are present 339 | */ 340 | void agc_set_ch_lc_deltas(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, uint32_t deltas[3]); 341 | 342 | /** Set delta value applied by loss control when both near-end and far-end audio are present for AGC channel. 343 | * 344 | * \param[in] agc AGC state. 345 | * 346 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 347 | * 348 | * \returns 1. Delta value applied by loss control when only far-end audio is present 349 | * 2. Delta value applied by loss control when only near-end audio is present 350 | * 3. Delta value applied by loss control when both near-end and far-end audio are present 351 | */ 352 | {uint32_t, uint32_t, uint32_t} agc_get_ch_lc_deltas(agc_state_t agc, unsigned ch_index); 353 | 354 | /** Set gains applied by loss control for AGC channel. 355 | * 356 | * \param[in,out] agc AGC state. 357 | * 358 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 359 | * 360 | * \param[in] gains 1. Maximum gain applied by loss control for AGC channel 361 | * 2. Gain applied by loss control when double-talk is detected for AGC channel 362 | * 2. Gain applied by loss control when silence is detected for AGC channel 363 | * 4. Minimum gain applied by loss control for AGC channel 364 | 365 | */ 366 | void agc_set_ch_lc_gains(REFERENCE_PARAM(agc_state_t, agc), unsigned ch_index, uint32_t gains[4]); 367 | 368 | /** Get maximum gain applied by loss control for AGC channel. 369 | * 370 | * \param[in] agc AGC state. 371 | * 372 | * \param[in] ch_index Channel index. Must be less than AGC_INPUT_CHANNELS. 373 | * 374 | * \returns 1. Maximum gain applied by loss control for AGC channel 375 | * 2. Gain applied by loss control when double-talk is detected for AGC channel 376 | * 2. Gain applied by loss control when silence is detected for AGC channel 377 | * 4. Minimum gain applied by loss control for AGC channel 378 | */ 379 | {uint32_t, uint32_t, uint32_t, uint32_t} agc_get_ch_lc_gains(agc_state_t agc, unsigned ch_index); 380 | 381 | #endif /* AGC_CONTROL_H_ */ 382 | -------------------------------------------------------------------------------- /lib_agc/src/agc.xc: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #include 4 | #include "agc.h" 5 | #include "voice_toolbox.h" 6 | 7 | /* 8 | * AGC_DEBUG_MODE Will enable all self checking, it will make it 9 | * MUCH slower however. 10 | * AGC_DEBUG_PRINT Will enable printing of internal state to be 11 | * compared to higher models. 12 | * AGC_WARNING_PRINT This enables warnings (events that might be bad 13 | * but not catastrophic). 14 | */ 15 | #ifndef AGC_DEBUG_MODE 16 | #define AGC_DEBUG_MODE 0 17 | #endif 18 | 19 | #ifndef AGC_DEBUG_PRINT 20 | #define AGC_DEBUG_PRINT 0 21 | #endif 22 | 23 | #ifndef AGC_WARNING_PRINT 24 | #define AGC_WARNING_PRINT 0 25 | #endif 26 | 27 | #if AGC_DEBUG_MODE | AGC_DEBUG_PRINT | AGC_WARNING_PRINT 28 | #include 29 | #include "audio_test_tools.h" 30 | #endif 31 | 32 | 33 | 34 | #if AGC_DEBUG_PRINT 35 | static int frame_counter = 0; 36 | #endif 37 | 38 | #define VTB_UQ16_16_EXP (-16) 39 | #define VTB_UQ0_32_EXP (-32) 40 | 41 | const vtb_s32_float_t S_ONE = {INT_MAX, -31}; 42 | const vtb_u32_float_t U_ONE = {UINT_MAX, -32}; 43 | const vtb_u32_float_t U_HALF = {UINT_MAX, -32-1}; 44 | const vtb_s32_float_t S_QUARTER = {INT_MAX, -31-2}; 45 | 46 | const vtb_u32_float_t AGC_LC_FAR_BG_POWER_EST_MIN = {2814705664, -48}; //0.00001 47 | 48 | static void agc_process_channel(agc_ch_state_t &agc_state, vtb_ch_pair_t samples[AGC_PROC_FRAME_LENGTH], unsigned ch_index, vtb_u32_float_t far_power, int vad_flag, vtb_uq0_32_t aec_corr); 49 | 50 | 51 | void agc_init(agc_state_t &agc, agc_init_config_t config){ 52 | for(unsigned ch = 0; ch < AGC_INPUT_CHANNELS; ch++){ 53 | agc.ch_state[ch].adapt = config.ch_init_config[ch].adapt; 54 | agc.ch_state[ch].adapt_on_vad = config.ch_init_config[ch].adapt_on_vad; 55 | agc.ch_state[ch].soft_clipping = config.ch_init_config[ch].soft_clipping; 56 | 57 | agc.ch_state[ch].gain.m = config.ch_init_config[ch].init_gain; 58 | agc.ch_state[ch].gain.e = VTB_UQ16_16_EXP; 59 | vtb_normalise_u32(agc.ch_state[ch].gain); 60 | 61 | agc.ch_state[ch].max_gain.m = config.ch_init_config[ch].max_gain; 62 | agc.ch_state[ch].max_gain.e = VTB_UQ16_16_EXP; 63 | vtb_normalise_u32(agc.ch_state[ch].max_gain); 64 | 65 | agc.ch_state[ch].min_gain.m = config.ch_init_config[ch].min_gain; 66 | agc.ch_state[ch].min_gain.e = VTB_UQ16_16_EXP; 67 | vtb_normalise_u32(agc.ch_state[ch].min_gain); 68 | 69 | agc.ch_state[ch].upper_threshold.m = (uint32_t)config.ch_init_config[ch].upper_threshold; 70 | agc.ch_state[ch].upper_threshold.e = 0; 71 | vtb_normalise_u32(agc.ch_state[ch].upper_threshold); 72 | 73 | agc.ch_state[ch].lower_threshold.m = (uint32_t)config.ch_init_config[ch].lower_threshold; 74 | agc.ch_state[ch].lower_threshold.e = 0; 75 | vtb_normalise_u32(agc.ch_state[ch].lower_threshold); 76 | 77 | vtb_u32_float_t vtb_float_u32_zero = VTB_FLOAT_U32_ZERO; 78 | agc.ch_state[ch].x_slow = vtb_float_u32_zero; 79 | agc.ch_state[ch].x_fast = vtb_float_u32_zero; 80 | agc.ch_state[ch].x_peak = vtb_float_u32_zero; 81 | 82 | agc.ch_state[ch].gain_inc.m = config.ch_init_config[ch].gain_inc; 83 | agc.ch_state[ch].gain_inc.e = VTB_UQ16_16_EXP; 84 | vtb_normalise_u32(agc.ch_state[ch].gain_inc); 85 | 86 | agc.ch_state[ch].gain_dec.m = config.ch_init_config[ch].gain_dec; 87 | agc.ch_state[ch].gain_dec.e = VTB_UQ16_16_EXP; 88 | vtb_normalise_u32(agc.ch_state[ch].gain_dec); 89 | 90 | agc.ch_state[ch].lc_enabled = config.ch_init_config[ch].lc_enabled; 91 | 92 | agc.ch_state[ch].lc_n_frame_near = config.ch_init_config[ch].lc_n_frame_near; 93 | agc.ch_state[ch].lc_n_frame_far = config.ch_init_config[ch].lc_n_frame_far; 94 | 95 | agc.ch_state[ch].lc_near_power_est.m = AGC_LC_NEAR_POWER_EST; 96 | agc.ch_state[ch].lc_near_power_est.e = VTB_UQ0_32_EXP; 97 | vtb_normalise_u32(agc.ch_state[ch].lc_near_power_est); 98 | 99 | agc.ch_state[ch].lc_far_power_est.m = AGC_LC_FAR_BG_POWER_EST_INIT; 100 | agc.ch_state[ch].lc_far_power_est.e = VTB_UQ0_32_EXP; 101 | vtb_normalise_u32(agc.ch_state[ch].lc_far_power_est); 102 | 103 | agc.ch_state[ch].lc_gain = U_ONE; 104 | 105 | agc.ch_state[ch].lc_bg_power_est.m = AGC_LC_BG_POWER_EST_INIT; 106 | agc.ch_state[ch].lc_bg_power_est.e = VTB_UQ0_32_EXP; 107 | vtb_normalise_u32(agc.ch_state[ch].lc_bg_power_est); 108 | 109 | agc.ch_state[ch].lc_bg_power_gamma.m = (uint32_t)config.ch_init_config[ch].lc_bg_power_gamma; 110 | agc.ch_state[ch].lc_bg_power_gamma.e = VTB_UQ16_16_EXP; 111 | vtb_normalise_u32(agc.ch_state[ch].lc_bg_power_gamma); 112 | 113 | agc.ch_state[ch].lc_gamma_inc.m = (uint32_t)config.ch_init_config[ch].lc_gamma_inc; 114 | agc.ch_state[ch].lc_gamma_inc.e = VTB_UQ16_16_EXP; 115 | vtb_normalise_u32(agc.ch_state[ch].lc_gamma_inc); 116 | 117 | agc.ch_state[ch].lc_gamma_dec.m = (uint32_t)config.ch_init_config[ch].lc_gamma_dec; 118 | agc.ch_state[ch].lc_gamma_dec.e = VTB_UQ16_16_EXP; 119 | vtb_normalise_u32(agc.ch_state[ch].lc_gamma_dec); 120 | 121 | agc.ch_state[ch].lc_far_bg_power_est.m = AGC_LC_FAR_BG_POWER_EST_INIT; 122 | agc.ch_state[ch].lc_far_bg_power_est.e = VTB_UQ0_32_EXP; 123 | vtb_normalise_u32(agc.ch_state[ch].lc_far_bg_power_est); 124 | 125 | agc.ch_state[ch].lc_corr_threshold.m = (uint32_t)config.ch_init_config[ch].lc_corr_threshold; 126 | agc.ch_state[ch].lc_corr_threshold.e = VTB_UQ0_32_EXP; 127 | vtb_normalise_u32(agc.ch_state[ch].lc_corr_threshold); 128 | 129 | agc.ch_state[ch].lc_near_delta_far_act.m = (uint32_t)config.ch_init_config[ch].lc_near_delta_far_act; 130 | agc.ch_state[ch].lc_near_delta_far_act.e = VTB_UQ16_16_EXP; 131 | vtb_normalise_u32(agc.ch_state[ch].lc_near_delta_far_act); 132 | 133 | agc.ch_state[ch].lc_near_delta.m = (uint32_t)config.ch_init_config[ch].lc_near_delta; 134 | agc.ch_state[ch].lc_near_delta.e = VTB_UQ16_16_EXP; 135 | vtb_normalise_u32(agc.ch_state[ch].lc_near_delta); 136 | 137 | agc.ch_state[ch].lc_far_delta.m = (uint32_t)config.ch_init_config[ch].lc_far_delta; 138 | agc.ch_state[ch].lc_far_delta.e = VTB_UQ16_16_EXP; 139 | vtb_normalise_u32(agc.ch_state[ch].lc_far_delta); 140 | 141 | agc.ch_state[ch].lc_gain_max.m = (uint32_t)config.ch_init_config[ch].lc_gain_max; 142 | agc.ch_state[ch].lc_gain_max.e = VTB_UQ16_16_EXP; 143 | vtb_normalise_u32(agc.ch_state[ch].lc_gain_max); 144 | 145 | agc.ch_state[ch].lc_gain_dt.m = (uint32_t)config.ch_init_config[ch].lc_gain_dt; 146 | agc.ch_state[ch].lc_gain_dt.e = VTB_UQ16_16_EXP; 147 | vtb_normalise_u32(agc.ch_state[ch].lc_gain_dt); 148 | 149 | agc.ch_state[ch].lc_gain_silence.m = (uint32_t)config.ch_init_config[ch].lc_gain_silence; 150 | agc.ch_state[ch].lc_gain_silence.e = VTB_UQ16_16_EXP; 151 | vtb_normalise_u32(agc.ch_state[ch].lc_gain_silence); 152 | 153 | 154 | agc.ch_state[ch].lc_gain_min.m = (uint32_t)config.ch_init_config[ch].lc_gain_min; 155 | agc.ch_state[ch].lc_gain_min.e = VTB_UQ16_16_EXP; 156 | vtb_normalise_u32(agc.ch_state[ch].lc_gain_min); 157 | 158 | 159 | agc.ch_state[ch].lc_t_far = 0; 160 | agc.ch_state[ch].lc_t_near = 0; 161 | 162 | agc.ch_state[ch].lc_corr_factor = VTB_UQ0_32(0); 163 | } 164 | } 165 | 166 | 167 | void agc_set_ch_gain(agc_state_t &agc, unsigned ch_index, vtb_uq16_16_t gain){ 168 | if(ch_index < AGC_INPUT_CHANNELS){ 169 | agc.ch_state[ch_index].gain.m = gain; 170 | agc.ch_state[ch_index].gain.e = VTB_UQ16_16_EXP; 171 | vtb_normalise_u32(agc.ch_state[ch_index].gain); 172 | } 173 | } 174 | 175 | 176 | vtb_uq16_16_t agc_get_ch_gain(agc_state_t agc, unsigned ch_index){ 177 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 178 | return vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].gain, VTB_UQ16_16_EXP); 179 | } 180 | 181 | 182 | void agc_set_ch_gain_inc(agc_state_t &agc, unsigned ch_index, vtb_uq16_16_t gain_inc){ 183 | if(ch_index < AGC_INPUT_CHANNELS){ 184 | vtb_u32_float_t new_gain_inc; 185 | new_gain_inc.m = gain_inc; 186 | new_gain_inc.e = VTB_UQ16_16_EXP; 187 | vtb_normalise_u32(new_gain_inc); 188 | if(vtb_gte_u32_u32(new_gain_inc, U_ONE)){ 189 | agc.ch_state[ch_index].gain_inc.m = new_gain_inc.m; 190 | agc.ch_state[ch_index].gain_inc.e = new_gain_inc.e; 191 | } 192 | } 193 | } 194 | 195 | 196 | vtb_uq16_16_t agc_get_ch_gain_inc(agc_state_t agc, unsigned ch_index){ 197 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 198 | return vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].gain_inc, VTB_UQ16_16_EXP); 199 | } 200 | 201 | 202 | void agc_set_ch_gain_dec(agc_state_t &agc, unsigned ch_index, vtb_uq16_16_t gain_dec){ 203 | if(ch_index < AGC_INPUT_CHANNELS){ 204 | vtb_u32_float_t new_gain_dec; 205 | new_gain_dec.m = gain_dec; 206 | new_gain_dec.e = VTB_UQ16_16_EXP; 207 | vtb_normalise_u32(new_gain_dec); 208 | if(!vtb_gte_u32_u32(new_gain_dec, U_ONE)){ 209 | agc.ch_state[ch_index].gain_dec.m = new_gain_dec.m; 210 | agc.ch_state[ch_index].gain_dec.e = new_gain_dec.e; 211 | } 212 | } 213 | } 214 | 215 | vtb_uq16_16_t agc_get_ch_gain_dec(agc_state_t agc, unsigned ch_index){ 216 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 217 | return vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].gain_dec, VTB_UQ16_16_EXP); 218 | } 219 | 220 | 221 | void agc_set_ch_max_gain(agc_state_t &agc, unsigned ch_index, vtb_uq16_16_t max_gain){ 222 | if(ch_index < AGC_INPUT_CHANNELS){ 223 | agc.ch_state[ch_index].max_gain.m = max_gain; 224 | agc.ch_state[ch_index].max_gain.e = VTB_UQ16_16_EXP; 225 | vtb_normalise_u32(agc.ch_state[ch_index].max_gain); 226 | } 227 | } 228 | 229 | vtb_uq16_16_t agc_get_ch_max_gain(agc_state_t agc, unsigned ch_index){ 230 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 231 | return vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].max_gain, VTB_UQ16_16_EXP); 232 | } 233 | 234 | void agc_set_ch_min_gain(agc_state_t &agc, unsigned ch_index, vtb_uq16_16_t min_gain){ 235 | if(ch_index < AGC_INPUT_CHANNELS){ 236 | agc.ch_state[ch_index].min_gain.m = min_gain; 237 | agc.ch_state[ch_index].min_gain.e = VTB_UQ16_16_EXP; 238 | vtb_normalise_u32(agc.ch_state[ch_index].min_gain); 239 | } 240 | } 241 | 242 | vtb_uq16_16_t agc_get_ch_min_gain(agc_state_t agc, unsigned ch_index){ 243 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 244 | return vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].min_gain, VTB_UQ16_16_EXP); 245 | } 246 | 247 | void agc_set_ch_adapt(agc_state_t &agc, unsigned ch_index, uint32_t adapt){ 248 | if(ch_index < AGC_INPUT_CHANNELS){ 249 | agc.ch_state[ch_index].adapt = (int)(adapt > 0); 250 | } 251 | } 252 | 253 | int agc_get_ch_adapt(agc_state_t agc, unsigned ch_index){ 254 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 255 | return agc.ch_state[ch_index].adapt; 256 | } 257 | 258 | void agc_set_ch_adapt_on_vad(agc_state_t &agc, unsigned ch_index, uint32_t adapt){ 259 | if(ch_index < AGC_INPUT_CHANNELS){ 260 | agc.ch_state[ch_index].adapt_on_vad = (int)(adapt > 0); 261 | } 262 | } 263 | 264 | int agc_get_ch_adapt_on_vad(agc_state_t agc, unsigned ch_index){ 265 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 266 | return agc.ch_state[ch_index].adapt_on_vad; 267 | } 268 | 269 | void agc_set_ch_soft_clipping(agc_state_t &agc, unsigned ch_index, uint32_t soft_clipping){ 270 | if(ch_index < AGC_INPUT_CHANNELS){ 271 | agc.ch_state[ch_index].soft_clipping = (int)(soft_clipping > 0); 272 | } 273 | } 274 | 275 | int agc_get_ch_soft_clipping(agc_state_t agc, unsigned ch_index){ 276 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 277 | return agc.ch_state[ch_index].soft_clipping; 278 | } 279 | 280 | void agc_set_ch_lc_enable(agc_state_t &agc, unsigned ch_index, uint32_t enable){ 281 | if(ch_index < AGC_INPUT_CHANNELS){ 282 | agc.ch_state[ch_index].lc_enabled = (int)(enable > 0); 283 | } 284 | } 285 | 286 | int agc_get_ch_lc_enable(agc_state_t agc, unsigned ch_index){ 287 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 288 | return agc.ch_state[ch_index].lc_enabled; 289 | } 290 | 291 | void agc_set_ch_upper_threshold(agc_state_t &agc, unsigned ch_index, int32_t upper_threshold){ 292 | if(ch_index < AGC_INPUT_CHANNELS){ 293 | int32_t abs_input = upper_threshold; 294 | if (abs_input < 0) abs_input = -abs_input; 295 | 296 | agc.ch_state[ch_index].upper_threshold.m = (uint32_t)abs_input; 297 | agc.ch_state[ch_index].upper_threshold.e = 0; 298 | vtb_normalise_u32(agc.ch_state[ch_index].upper_threshold); 299 | 300 | if (vtb_gte_u32_u32(agc.ch_state[ch_index].lower_threshold, agc.ch_state[ch_index].upper_threshold)){ 301 | agc.ch_state[ch_index].upper_threshold = agc.ch_state[ch_index].lower_threshold; 302 | } 303 | } 304 | } 305 | 306 | void agc_set_ch_lower_threshold(agc_state_t &agc, unsigned ch_index, int32_t lower_threshold){ 307 | if(ch_index < AGC_INPUT_CHANNELS){ 308 | int32_t abs_input = lower_threshold; 309 | if (abs_input < 0) abs_input = -abs_input; 310 | 311 | agc.ch_state[ch_index].lower_threshold.m = (uint32_t)abs_input; 312 | agc.ch_state[ch_index].lower_threshold.e = 0; 313 | vtb_normalise_u32(agc.ch_state[ch_index].lower_threshold); 314 | 315 | if (vtb_gte_u32_u32(agc.ch_state[ch_index].lower_threshold, agc.ch_state[ch_index].upper_threshold)){ 316 | agc.ch_state[ch_index].lower_threshold = agc.ch_state[ch_index].upper_threshold; 317 | } 318 | } 319 | } 320 | 321 | int32_t agc_get_ch_upper_threshold(agc_state_t agc, unsigned ch_index){ 322 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 323 | uint32_t upper_threshold = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].upper_threshold, 0); 324 | return (int32_t)upper_threshold; 325 | } 326 | 327 | int32_t agc_get_ch_lower_threshold(agc_state_t agc, unsigned ch_index){ 328 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 329 | uint32_t lower_threshold = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lower_threshold, 0); 330 | return (int32_t)lower_threshold; 331 | } 332 | 333 | void agc_set_ch_lc_n_frames(agc_state_t &agc, unsigned ch_index, uint32_t frames[2]){ 334 | if(ch_index < AGC_INPUT_CHANNELS){ 335 | agc.ch_state[ch_index].lc_n_frame_far = frames[0]; 336 | agc.ch_state[ch_index].lc_n_frame_near = frames[1]; 337 | } 338 | } 339 | 340 | {uint32_t, uint32_t} agc_get_ch_lc_n_frames(agc_state_t agc, unsigned ch_index){ 341 | if(ch_index >= AGC_INPUT_CHANNELS) return {0, 0}; 342 | return { agc.ch_state[ch_index].lc_n_frame_far, agc.ch_state[ch_index].lc_n_frame_near}; 343 | } 344 | 345 | void agc_set_ch_lc_corr_threshold(agc_state_t &agc, unsigned ch_index, uint32_t lc_corr_threshold){ 346 | if(ch_index < AGC_INPUT_CHANNELS){ 347 | agc.ch_state[ch_index].lc_corr_threshold.m = lc_corr_threshold; 348 | agc.ch_state[ch_index].lc_corr_threshold.e = VTB_UQ0_32_EXP; 349 | vtb_normalise_u32(agc.ch_state[ch_index].lc_corr_threshold); 350 | } 351 | } 352 | 353 | uint32_t agc_get_ch_lc_corr_threshold(agc_state_t agc, unsigned ch_index){ 354 | if(ch_index >= AGC_INPUT_CHANNELS) return 0; 355 | uint32_t lc_corr_threshold = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_corr_threshold, VTB_UQ0_32_EXP); 356 | return (int32_t)lc_corr_threshold; 357 | } 358 | 359 | void agc_set_ch_lc_gammas(agc_state_t &agc, unsigned ch_index, uint32_t gammas[3]){ 360 | if(ch_index < AGC_INPUT_CHANNELS){ 361 | agc.ch_state[ch_index].lc_bg_power_gamma.m = gammas[0]; 362 | agc.ch_state[ch_index].lc_bg_power_gamma.e = VTB_UQ16_16_EXP; 363 | 364 | agc.ch_state[ch_index].lc_gamma_inc.m = gammas[1]; 365 | agc.ch_state[ch_index].lc_gamma_inc.e = VTB_UQ16_16_EXP; 366 | 367 | agc.ch_state[ch_index].lc_gamma_dec.m = gammas[2]; 368 | agc.ch_state[ch_index].lc_gamma_dec.e = VTB_UQ16_16_EXP; 369 | } 370 | } 371 | 372 | {uint32_t, uint32_t, uint32_t} agc_get_ch_lc_gammas(agc_state_t agc, unsigned ch_index){ 373 | if(ch_index >= AGC_INPUT_CHANNELS) return {0, 0, 0}; 374 | 375 | uint32_t lc_bg_power_gamma = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_bg_power_gamma, VTB_UQ16_16_EXP); 376 | uint32_t lc_gamma_inc = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_gamma_inc, VTB_UQ16_16_EXP); 377 | uint32_t lc_gamma_dec = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_gamma_dec, VTB_UQ16_16_EXP); 378 | 379 | 380 | return { lc_bg_power_gamma, lc_gamma_inc, lc_gamma_dec}; 381 | } 382 | 383 | void agc_set_ch_lc_deltas(agc_state_t &agc, unsigned ch_index, uint32_t deltas[3]){ 384 | if(ch_index < AGC_INPUT_CHANNELS){ 385 | 386 | agc.ch_state[ch_index].lc_far_delta.m = deltas[0]; 387 | agc.ch_state[ch_index].lc_far_delta.e = VTB_UQ16_16_EXP; 388 | 389 | agc.ch_state[ch_index].lc_near_delta.m = deltas[1]; 390 | agc.ch_state[ch_index].lc_near_delta.e = VTB_UQ16_16_EXP; 391 | 392 | agc.ch_state[ch_index].lc_near_delta_far_act.m = deltas[2]; 393 | agc.ch_state[ch_index].lc_near_delta_far_act.e = VTB_UQ16_16_EXP; 394 | } 395 | } 396 | 397 | {uint32_t, uint32_t, uint32_t} agc_get_ch_lc_deltas(agc_state_t agc, unsigned ch_index){ 398 | if(ch_index >= AGC_INPUT_CHANNELS) return {0, 0, 0}; 399 | uint32_t lc_far_delta = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_far_delta, VTB_UQ16_16_EXP); 400 | uint32_t lc_near_delta = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_near_delta, VTB_UQ16_16_EXP); 401 | uint32_t lc_near_delta_far_act = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_near_delta_far_act, VTB_UQ16_16_EXP); 402 | 403 | return { lc_far_delta, lc_near_delta, lc_near_delta_far_act}; 404 | } 405 | 406 | void agc_set_ch_lc_gains(agc_state_t &agc, unsigned ch_index, uint32_t gains[4]){ 407 | if(ch_index < AGC_INPUT_CHANNELS){ 408 | agc.ch_state[ch_index].lc_gain_max.m = gains[0]; 409 | agc.ch_state[ch_index].lc_gain_max.e = VTB_UQ16_16_EXP; 410 | 411 | agc.ch_state[ch_index].lc_gain_dt.m = gains[1]; 412 | agc.ch_state[ch_index].lc_gain_dt.e = VTB_UQ16_16_EXP; 413 | 414 | agc.ch_state[ch_index].lc_gain_silence.m = gains[2]; 415 | agc.ch_state[ch_index].lc_gain_silence.e = VTB_UQ16_16_EXP; 416 | 417 | agc.ch_state[ch_index].lc_gain_min.m = gains[3]; 418 | agc.ch_state[ch_index].lc_gain_min.e = VTB_UQ16_16_EXP; 419 | 420 | } 421 | } 422 | 423 | {uint32_t, uint32_t, uint32_t, uint32_t} agc_get_ch_lc_gains(agc_state_t agc, unsigned ch_index){ 424 | if(ch_index >= AGC_INPUT_CHANNELS) return {0, 0, 0, 0}; 425 | uint32_t lc_gain_max = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_gain_max, VTB_UQ16_16_EXP); 426 | uint32_t lc_gain_dt = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_gain_dt, VTB_UQ16_16_EXP); 427 | uint32_t lc_gain_silence = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_gain_silence, VTB_UQ16_16_EXP); 428 | uint32_t lc_gain_min = vtb_denormalise_and_saturate_u32(agc.ch_state[ch_index].lc_gain_min, VTB_UQ16_16_EXP); 429 | 430 | return { lc_gain_max, lc_gain_dt, lc_gain_silence, lc_gain_min }; 431 | } 432 | 433 | uint32_t get_max_abs_sample(vtb_ch_pair_t samples[AGC_PROC_FRAME_LENGTH], unsigned ch_index){ 434 | uint32_t max_abs_value = 0; 435 | for(unsigned n = 0; n < AGC_PROC_FRAME_LENGTH; n++){ 436 | int32_t sample = (samples[n], int32_t[2])[ch_index&1]; 437 | uint32_t abs_sample = 0; 438 | if(sample < 0){ 439 | abs_sample = (uint32_t)(-sample); 440 | } else { 441 | abs_sample = (uint32_t)sample; 442 | } 443 | 444 | if(abs_sample > max_abs_value){ 445 | max_abs_value = abs_sample; 446 | } 447 | } 448 | return max_abs_value; 449 | } 450 | 451 | 452 | void agc_process_frame(agc_state_t &agc, vtb_ch_pair_t frame[AGC_CHANNEL_PAIRS][AGC_PROC_FRAME_LENGTH], vtb_u32_float_t far_power, int vad_flag, vtb_uq0_32_t aec_corr){ 453 | #if AGC_DEBUG_PRINT 454 | printf("\n#%u\n", frame_counter++); 455 | #endif 456 | for(unsigned ch=0;ch state.lc_corr_factor){ 556 | state.lc_corr_factor = aec_corr; 557 | } 558 | else{ 559 | // Exponential decay 560 | state.lc_corr_factor = (vtb_uq0_32_t)(((uint64_t)VTB_UQ0_32(0.98) * (uint64_t)state.lc_corr_factor + (uint64_t)VTB_UQ0_32(0.02) * (uint64_t)aec_corr)>>32); 561 | } 562 | 563 | // Update activity timers 564 | if(vtb_gte_u32_u32(state.lc_far_power_est, vtb_mul_u32_u32(state.lc_far_delta, state.lc_far_bg_power_est))){ 565 | state.lc_t_far = state.lc_n_frame_far; 566 | } 567 | else{ 568 | state.lc_t_far = state.lc_t_far - 1; 569 | if (state.lc_t_far < 0) state.lc_t_far = 0; 570 | } 571 | 572 | vtb_u32_float_t delta = state.lc_near_delta; 573 | if(state.lc_t_far != 0){ 574 | delta = state.lc_near_delta_far_act; 575 | } 576 | 577 | // Update near-end-activity timer 578 | if(vtb_gte_u32_u32(state.lc_near_power_est, vtb_mul_u32_u32(delta, state.lc_bg_power_est))){ 579 | if(state.lc_t_far == 0 || (state.lc_t_far != 0 && (state.lc_corr_factor < state.lc_corr_threshold.m))){ 580 | // Near-end speech only or Double talk 581 | state.lc_t_near = state.lc_n_frame_near; 582 | } 583 | else { 584 | // Far-end speech only 585 | // Do nothing 586 | ; 587 | } 588 | } 589 | else{ 590 | // Silence 591 | state.lc_t_near = state.lc_t_near - 1; 592 | if (state.lc_t_near < 0) state.lc_t_near = 0; 593 | } 594 | 595 | // Adapt loss control gain 596 | if(state.lc_t_far == 0 && state.lc_t_near != 0){ 597 | // Near speech only 598 | lc_target_gain = state.lc_gain_max; 599 | } 600 | else if(state.lc_t_far != 0 && state.lc_t_near == 0){ 601 | // Far end only 602 | lc_target_gain = state.lc_gain_min; 603 | } 604 | else if(state.lc_t_far != 0 && state.lc_t_near != 0){ 605 | // Both near-end and far -end 606 | lc_target_gain = state.lc_gain_dt; 607 | } 608 | else{ 609 | // Silence 610 | lc_target_gain = state.lc_gain_silence; 611 | } 612 | } 613 | 614 | 615 | for(unsigned n = 0; n < AGC_PROC_FRAME_LENGTH; n++){ 616 | vtb_s32_float_t input_sample = {(samples[n], int32_t[2])[ch_index&1], s32_exponent}; 617 | vtb_normalise_s32(input_sample); 618 | 619 | vtb_s32_float_t gained_sample; 620 | if(state.lc_enabled){ 621 | if(vtb_gte_u32_u32(state.lc_gain, lc_target_gain)){ 622 | state.lc_gain = vtb_mul_u32_u32(state.lc_gain, state.lc_gamma_dec); 623 | // TODO hold lc_gain if equal? 624 | } 625 | else{ 626 | state.lc_gain = vtb_mul_u32_u32(state.lc_gain, state.lc_gamma_inc); 627 | } 628 | 629 | gained_sample = vtb_mul_s32_u32(input_sample, vtb_mul_u32_u32(state.lc_gain, state.gain)); 630 | } 631 | else{ 632 | gained_sample = vtb_mul_s32_u32(input_sample, state.gain); 633 | } 634 | 635 | #if AGC_DEBUG_PRINT 636 | printf("input_sample[%u] = %d\n", ch_index, (samples[n], int32_t[2])[ch_index&1]); 637 | printf("input_sample_float[%u] = %.22f\n", ch_index, att_uint32_to_double(input_sample.m, input_sample.e)); 638 | printf("gained_sample[%u] = %.22f\n", ch_index, att_uint32_to_double(gained_sample.m, gained_sample.e)); 639 | #endif 640 | 641 | vtb_u32_float_t abs_gained_sample = vtb_abs_s32_to_u32(gained_sample); 642 | 643 | if(state.soft_clipping && vtb_gte_u32_u32(abs_gained_sample, agc_limit_point)){ 644 | vtb_s32_float_t div_result = vtb_div_s32_u32(S_QUARTER, abs_gained_sample); 645 | vtb_s32_float_t output_normalised = vtb_sub_s32_s32(S_ONE, div_result); 646 | int32_t output_sample = vtb_denormalise_and_saturate_s32(output_normalised, s32_exponent); 647 | 648 | #if AGC_DEBUG_PRINT 649 | printf("output_sample_float[%u] = %.22f\n", ch_index, att_uint32_to_double(output_normalised.m, output_normalised.e)); 650 | printf("output_sample[%u] = %d\n", ch_index, output_sample); 651 | #endif 652 | 653 | if(input_sample.m < 0){ 654 | output_sample =- output_sample; 655 | } 656 | (samples[n], int32_t[2])[ch_index&1] = output_sample; 657 | 658 | } else{ 659 | int32_t output_sample = vtb_denormalise_and_saturate_s32(gained_sample, s32_exponent); 660 | (samples[n], int32_t[2])[ch_index&1] = output_sample; 661 | 662 | #if AGC_DEBUG_PRINT 663 | printf("output_sample_float[%u] = %.22f\n", ch_index, att_uint32_to_double(gained_sample.m, s32_exponent)); 664 | printf("output_sample[%u] = %d\n", ch_index, output_sample); 665 | #endif 666 | } 667 | } 668 | } 669 | -------------------------------------------------------------------------------- /tests/agc_unit_tests/src/test_agc.xc: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 XMOS LIMITED. 2 | // This Software is subject to the terms of the XMOS Public Licence: Version 1. 3 | #include "agc_unit_tests.h" 4 | 5 | #define TEST_COUNT (1<<10) 6 | #define VTB_UQ16_16_EXP (-16) 7 | 8 | void test_agc_init(){ 9 | int expected_adapt[AGC_INPUT_CHANNELS] = {AGC_CH0_ADAPT, AGC_CH1_ADAPT}; 10 | int expected_adapt_on_vad[AGC_INPUT_CHANNELS] = {AGC_CH0_ADAPT_ON_VAD, AGC_CH1_ADAPT_ON_VAD}; 11 | int expected_soft_clipping[AGC_INPUT_CHANNELS] = {AGC_CH0_SOFT_CLIPPING, AGC_CH1_SOFT_CLIPPING}; 12 | 13 | vtb_uq16_16_t expected_init_gain[AGC_INPUT_CHANNELS] = {VTB_UQ16_16(AGC_CH0_GAIN), VTB_UQ16_16(AGC_CH1_GAIN)}; 14 | vtb_uq16_16_t expected_max_gain[AGC_INPUT_CHANNELS] = {VTB_UQ16_16(AGC_CH0_MAX_GAIN), VTB_UQ16_16(AGC_CH1_MAX_GAIN)}; 15 | vtb_uq16_16_t expected_min_gain[AGC_INPUT_CHANNELS] = {VTB_UQ16_16(AGC_CH0_MIN_GAIN), VTB_UQ16_16(AGC_CH1_MIN_GAIN)}; 16 | 17 | uint32_t expected_upper_threshold[AGC_INPUT_CHANNELS] = {VTB_UQ1_31(AGC_CH0_UPPER_THRESHOLD), VTB_UQ1_31(AGC_CH1_UPPER_THRESHOLD)}; 18 | uint32_t expected_lower_threshold[AGC_INPUT_CHANNELS] = {VTB_UQ1_31(AGC_CH0_LOWER_THRESHOLD), VTB_UQ1_31(AGC_CH1_LOWER_THRESHOLD)}; 19 | vtb_uq16_16_t expected_gain_inc[AGC_INPUT_CHANNELS] = {VTB_UQ16_16(AGC_CH0_GAIN_INC), VTB_UQ16_16(AGC_CH1_GAIN_INC)}; 20 | vtb_uq16_16_t expected_gain_dec[AGC_INPUT_CHANNELS] = {VTB_UQ16_16(AGC_CH0_GAIN_DEC), VTB_UQ16_16(AGC_CH1_GAIN_DEC)}; 21 | int expected_lc_enabled[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_ENABLED, AGC_CH1_LC_ENABLED}; 22 | 23 | 24 | int expected_lc_n_frame_near[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_N_FRAME_NEAR, AGC_CH1_LC_N_FRAME_NEAR}; 25 | int expected_lc_n_frame_far[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_N_FRAME_FAR, AGC_CH1_LC_N_FRAME_FAR}; 26 | int expected_lc_near_delta_far_act[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_NEAR_DELTA_FAR_ACT, AGC_CH1_LC_NEAR_DELTA_FAR_ACT}; 27 | int expected_lc_near_delta[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_NEAR_DELTA, AGC_CH1_LC_NEAR_DELTA}; 28 | int expected_lc_far_delta[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_FAR_DELTA, AGC_CH1_LC_FAR_DELTA}; 29 | int expected_lc_gamma_inc[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_GAMMA_INC, AGC_CH1_LC_GAMMA_INC}; 30 | int expected_lc_gamma_dec[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_GAMMA_DEC, AGC_CH1_LC_GAMMA_DEC}; 31 | int expected_lc_bg_power_gamma[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_BG_POWER_GAMMA, AGC_CH1_LC_BG_POWER_GAMMA}; 32 | int expected_lc_corr_threshold[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_CORR_THRESHOLD, AGC_CH1_LC_CORR_THRESHOLD}; 33 | int expected_lc_gain_max[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_GAIN_MAX, AGC_CH1_LC_GAIN_MAX}; 34 | int expected_lc_gain_dt[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_GAIN_DT, AGC_CH1_LC_GAIN_DT}; 35 | int expected_lc_gain_silence[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_GAIN_SILENCE, AGC_CH1_LC_GAIN_SILENCE}; 36 | int expected_lc_gain_min[AGC_INPUT_CHANNELS] = {AGC_CH0_LC_GAIN_MIN, AGC_CH1_LC_GAIN_MIN}; 37 | 38 | agc_state_t agc; 39 | agc_init_config_t config = { 40 | { 41 | { 42 | expected_adapt[0], 43 | expected_adapt_on_vad[0], 44 | expected_soft_clipping[0], 45 | expected_init_gain[0], 46 | expected_max_gain[0], 47 | expected_min_gain[0], 48 | expected_upper_threshold[0], 49 | expected_lower_threshold[0], 50 | expected_gain_inc[0], 51 | expected_gain_dec[0], 52 | expected_lc_enabled[0], 53 | expected_lc_n_frame_far[0], 54 | expected_lc_n_frame_near[0], 55 | expected_lc_corr_threshold[0], 56 | expected_lc_bg_power_gamma[0], 57 | expected_lc_gamma_inc[0], 58 | expected_lc_gamma_dec[0], 59 | expected_lc_far_delta[0], 60 | expected_lc_near_delta[0], 61 | expected_lc_near_delta_far_act[0], 62 | expected_lc_gain_max[0], 63 | expected_lc_gain_dt[0], 64 | expected_lc_gain_silence[0], 65 | expected_lc_gain_min[0] 66 | }, 67 | { 68 | expected_adapt[1], 69 | expected_adapt_on_vad[1], 70 | expected_soft_clipping[1], 71 | expected_init_gain[1], 72 | expected_max_gain[1], 73 | expected_min_gain[1], 74 | expected_upper_threshold[1], 75 | expected_lower_threshold[1], 76 | expected_gain_inc[1], 77 | expected_gain_dec[1], 78 | expected_lc_enabled[1], 79 | expected_lc_n_frame_far[1], 80 | expected_lc_n_frame_near[1], 81 | expected_lc_corr_threshold[1], 82 | expected_lc_bg_power_gamma[1], 83 | expected_lc_gamma_inc[1], 84 | expected_lc_gamma_dec[1], 85 | expected_lc_far_delta[1], 86 | expected_lc_near_delta[1], 87 | expected_lc_near_delta_far_act[1], 88 | expected_lc_gain_max[1], 89 | expected_lc_gain_dt[1], 90 | expected_lc_gain_silence[1], 91 | expected_lc_gain_min[1] 92 | 93 | } 94 | } 95 | }; 96 | 97 | agc_init(agc, config); 98 | 99 | for(unsigned ch = 0; ch < AGC_INPUT_CHANNELS; ++ch){ 100 | TEST_ASSERT_EQUAL_INT_MESSAGE(expected_adapt[ch], agc.ch_state[ch].adapt, "Incorrect adapt flag"); 101 | TEST_ASSERT_EQUAL_INT_MESSAGE(expected_adapt_on_vad[ch], agc.ch_state[ch].adapt_on_vad, "Incorrect adapt on vad flag"); 102 | TEST_ASSERT_EQUAL_INT_MESSAGE(expected_soft_clipping[ch], agc.ch_state[ch].soft_clipping, "Incorrect soft clipping flag"); 103 | 104 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_init_gain[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].gain, -16), "Incorrect init gain"); 105 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_max_gain[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].max_gain, -16), "Incorrect max gain"); 106 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_min_gain[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].min_gain, -16), "Incorrect min gain"); 107 | 108 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_upper_threshold[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].upper_threshold, 0), "Incorrect threshold upper"); 109 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lower_threshold[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lower_threshold, 0), "Incorrect threshold lower"); 110 | 111 | vtb_u32_float_t vtb_float_u32_zero = VTB_FLOAT_U32_ZERO; 112 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(vtb_float_u32_zero.m, agc.ch_state[ch].x_slow.m, "Incorrect x_slow m"); 113 | TEST_ASSERT_EQUAL_INT32_MESSAGE(vtb_float_u32_zero.e, agc.ch_state[ch].x_slow.e, "Incorrect x_slow e"); 114 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(vtb_float_u32_zero.m, agc.ch_state[ch].x_fast.m, "Incorrect x_fast m"); 115 | TEST_ASSERT_EQUAL_INT32_MESSAGE(vtb_float_u32_zero.e, agc.ch_state[ch].x_fast.e, "Incorrect x_fast e"); 116 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(vtb_float_u32_zero.m, agc.ch_state[ch].x_peak.m, "Incorrect x_peak m"); 117 | TEST_ASSERT_EQUAL_INT32_MESSAGE(vtb_float_u32_zero.e, agc.ch_state[ch].x_peak.e, "Incorrect x_peak e"); 118 | 119 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_gain_inc[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].gain_inc, -16), "Incorrect gain inc"); 120 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_gain_dec[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].gain_dec, -16), "Incorrect gain inc"); 121 | 122 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_n_frame_far[ch], agc.ch_state[ch].lc_n_frame_far, "Incorrect LC N frame far"); 123 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_n_frame_near[ch], agc.ch_state[ch].lc_n_frame_near, "Incorrect LC N frame near"); 124 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_corr_threshold[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_corr_threshold, -16), "Incorrect LC corr threshold"); 125 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_bg_power_gamma[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_bg_power_gamma, -16), "Incorrect LC bg power gamma"); 126 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_gamma_inc[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_gamma_inc, -16), "Incorrect LC gamma inc"); 127 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_gamma_dec[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_gamma_dec, -16), "Incorrect LC gamma dec"); 128 | 129 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_near_delta_far_act[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_near_delta_far_act, -16), "Incorrect LC near delta far activity"); 130 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_near_delta[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_near_delta, -16), "Incorrect LC near delta"); 131 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_far_delta[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_far_delta, -16), "Incorrect LC far delta"); 132 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_gain_max[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_gain_max, -16), "Incorrect LC gain max"); 133 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_gain_dt[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_gain_dt, -16), "Incorrect LC gain double talk"); 134 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_gain_silence[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_gain_silence, -16), "Incorrect LC gain silence"); 135 | TEST_ASSERT_EQUAL_UINT32_MESSAGE(expected_lc_gain_min[ch], vtb_denormalise_and_saturate_u32(agc.ch_state[ch].lc_gain_min, -16), "Incorrect LC gain min"); 136 | } 137 | } 138 | 139 | void test_agc_set_get_ch_adapt(){ 140 | srand((unsigned) 2); 141 | 142 | agc_init_config_t config; 143 | memset(&config, 0xFF, sizeof(agc_init_config_t)); 144 | 145 | agc_state_t agc; 146 | agc_init(agc, config); 147 | 148 | 149 | uint32_t expected_adapt = 0; 150 | for(unsigned i=0; i> 16){ 313 | TEST_ASSERT_EQUAL_INT32_MESSAGE(expected_gain_inc[i], actual_gain, "Incorrect channel gain_inc"); 314 | } 315 | else { 316 | TEST_ASSERT_EQUAL_INT32_MESSAGE(initial_gain_inc[i], actual_gain, "Incorrect channel gain_inc"); 317 | } 318 | } 319 | } 320 | } 321 | 322 | 323 | void test_agc_set_get_ch_gain_dec(){ 324 | srand((unsigned) 2); 325 | vtb_uq16_16_t initial_gain_dec[AGC_INPUT_CHANNELS] = {VTB_UQ16_16(AGC_CH0_GAIN_DEC), VTB_UQ16_16(AGC_CH1_GAIN_DEC)}; 326 | 327 | agc_init_config_t config; 328 | memset(&config, 0xFF, sizeof(agc_init_config_t)); 329 | config.ch_init_config[0].gain_dec = initial_gain_dec[0]; 330 | config.ch_init_config[1].gain_dec = initial_gain_dec[1]; 331 | 332 | for(unsigned i=0;i> 16){ 349 | TEST_ASSERT_EQUAL_INT32_MESSAGE(initial_gain_dec[i], actual_gain, "Incorrect channel gain_dec"); 350 | } 351 | else { 352 | TEST_ASSERT_EQUAL_INT32_MESSAGE(expected_gain_dec[i], actual_gain, "Incorrect channel gain_dec"); 353 | } 354 | } 355 | } 356 | } 357 | 358 | void test_agc_set_get_ch_max_gain(){ 359 | srand((unsigned) 1); 360 | 361 | agc_init_config_t config; 362 | memset(&config, 0xFF, sizeof(agc_init_config_t)); 363 | 364 | for(unsigned i=0;i config.ch_init_config[i].upper_threshold){ 482 | expected = config.ch_init_config[i].upper_threshold; 483 | } 484 | 485 | TEST_ASSERT_EQUAL_INT32_MESSAGE(expected, actual, "Incorrect upper threshold"); 486 | } 487 | } 488 | } 489 | 490 | void test_agc_set_get_lc_corr_threshold(){ 491 | srand((unsigned) 2); 492 | 493 | agc_init_config_t config; 494 | memset(&config, 0xFF, sizeof(agc_init_config_t)); 495 | 496 | agc_state_t agc; 497 | agc_init(agc, config); 498 | 499 | 500 | uint32_t expected_lc_corr_threshold = 0; 501 | for(unsigned ch=0; ch> 7; 856 | 857 | for(int ch_pair=0; ch_pair