mirror of
https://github.com/espressif/esp-sr.git
synced 2025-09-15 15:28:44 +08:00
feat: support AEC on esp32c5
This commit is contained in:
parent
0e62b69a71
commit
fc1a922c5b
@ -95,6 +95,24 @@ if((${IDF_TARGET} STREQUAL "esp32s3") OR (${IDF_TARGET} STREQUAL "esp32p4") OR (
|
||||
endif()
|
||||
endif()
|
||||
|
||||
elseif(${IDF_TARGET} STREQUAL "esp32c5")
|
||||
|
||||
set(srcs
|
||||
"lib/${IDF_TARGET}/dummy.c"
|
||||
)
|
||||
|
||||
set(include_dirs
|
||||
"include/${IDF_TARGET}"
|
||||
)
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS ${include_dirs}
|
||||
)
|
||||
|
||||
component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
|
||||
add_prebuilt_library(esp_audio_processor "${CMAKE_CURRENT_SOURCE_DIR}/lib/${IDF_TARGET}/libesp_audio_processor.a")
|
||||
target_link_libraries(${COMPONENT_LIB} PRIVATE esp_audio_processor)
|
||||
|
||||
elseif((${IDF_TARGET} STREQUAL "esp32s2") OR (${IDF_TARGET} STREQUAL "esp32c3") OR (${IDF_TARGET} STREQUAL "esp32c6"))
|
||||
#Only support TTS on esp32s2, esp32c3 and esp32c6
|
||||
|
||||
|
||||
105
include/esp32c5/esp_aec.h
Normal file
105
include/esp32c5/esp_aec.h
Normal file
@ -0,0 +1,105 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License
|
||||
#ifndef _ESP_AEC_H_
|
||||
#define _ESP_AEC_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define USE_AEC_FFT // Not kiss_fft
|
||||
#define AEC_SAMPLE_RATE 16000 // Only Support 16000Hz
|
||||
#define AEC_FRAME_LENGTH_MS 32
|
||||
|
||||
typedef struct aec_handle_t aec_handle_t;
|
||||
typedef enum {
|
||||
AEC_MODE_SR_LOW_COST = 0, // Low Cost AEC fro speech recognition
|
||||
AEC_MODE_SR_HIGH_PERF = 1, // High Perforamce AEC for speech recognition
|
||||
AEC_MODE_VOIP_LOW_COST = 3, // Low Cost AEC for voice communication
|
||||
AEC_MODE_VOIP_HIGH_PERF = 4, // High Perforamce AEC for voice communication
|
||||
} aec_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Creates an instance to the AEC structure.
|
||||
* Please get frame size by aec_get_chunksize() function
|
||||
*
|
||||
* @param sample_rate The Sampling frequency (Hz) must be 16000.
|
||||
* @param filter_length Number of filter, recommend to set 4. The larger the filter_length, the more resource consumption.
|
||||
* @param channel_num The input microphone channel number
|
||||
* @param mode The mode of AEC, recommend to set AEC_MODE_SR_LOW_COST
|
||||
* @return
|
||||
* - NULL: Create failed
|
||||
* - Others: The instance of AEC
|
||||
*/
|
||||
aec_handle_t *aec_create(int sample_rate, int filter_length, int channel_num, aec_mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Creates an instance to the AEC structure, same with aec_create().
|
||||
*
|
||||
* @param filter_length Number of filter, recommend to set 4. The larger the filter_length, the more resource consumption.
|
||||
* @param channel_num The input microphone channel number
|
||||
* @param mode The mode of AEC, recommend to set AEC_MODE_SR_LOW_COST
|
||||
* @return
|
||||
* - NULL: Create failed
|
||||
* - Others: The instance of AEC
|
||||
*/
|
||||
aec_handle_t *aec_pro_create(int filter_length, int channel_num, aec_mode_t mode);
|
||||
|
||||
/**
|
||||
* @brief Performs echo cancellation a frame, based on the audio sent to the speaker and frame from mic.
|
||||
*
|
||||
* @warning The indata, refdata and outdata must be 16-bit signed. please allocate memory by heap_caps_aligned_alloc().
|
||||
*
|
||||
* @param inst The instance of AEC. Format for multi-channel data is "ch0 ch0 ch0 ..., ch1 ch1 ch1 ..."
|
||||
* @param indata An array of 16-bit signed audio samples from mic.
|
||||
* @param refdata An array of 16-bit signed audio samples sent to the speaker.
|
||||
* @param outdata Returns near-end signal with echo removed. Format for multi-channel data is "ch0 ch0 ch0..., ch1 ch1 ch1 ..."
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
void aec_process(const aec_handle_t *handel, int16_t *indata, int16_t *refdata, int16_t *outdata);
|
||||
|
||||
/**
|
||||
* @brief Get frame size of AEC (the samples of one frame)
|
||||
* @param handle The instance of AEC.
|
||||
* @return Frame size
|
||||
*/
|
||||
int aec_get_chunksize(const aec_handle_t *handle);
|
||||
|
||||
/**
|
||||
* @brief Get AEC mode string
|
||||
*
|
||||
* @param aec_mode The mode of AEC.
|
||||
*
|
||||
* @return AEC mode string
|
||||
*/
|
||||
char * aec_get_mode_string(aec_mode_t aec_mode);
|
||||
|
||||
/**
|
||||
* @brief Free the AEC instance
|
||||
*
|
||||
* @param inst The instance of AEC.
|
||||
*
|
||||
* @return None
|
||||
*
|
||||
*/
|
||||
void aec_destroy(aec_handle_t *handel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //_ESP_AEC_H_
|
||||
0
lib/esp32c5/dummy.c
Normal file
0
lib/esp32c5/dummy.c
Normal file
BIN
lib/esp32c5/libesp_audio_processor.a
Normal file
BIN
lib/esp32c5/libesp_audio_processor.a
Normal file
Binary file not shown.
10
test_apps/esp32c5/CMakeLists.txt
Normal file
10
test_apps/esp32c5/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# Include the components directory of the main application:
|
||||
#
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
|
||||
"../../../esp-sr")
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(esp32c5_test)
|
||||
11
test_apps/esp32c5/main/CMakeLists.txt
Normal file
11
test_apps/esp32c5/main/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
set(srcs
|
||||
"app_main.cpp"
|
||||
"test_aec.cpp"
|
||||
)
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES unity esp-sr esp_timer
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
47
test_apps/esp32c5/main/app_main.cpp
Normal file
47
test_apps/esp32c5/main/app_main.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
/* Example test application for testable component.
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
/* These are the different ways of running registered tests.
|
||||
* In practice, only one of them is usually needed.
|
||||
*
|
||||
* UNITY_BEGIN() and UNITY_END() calls tell Unity to print a summary
|
||||
* (number of tests executed/failed/ignored) of tests executed between these calls.
|
||||
*/
|
||||
// print_banner("Executing one test by its name");
|
||||
// UNITY_BEGIN();
|
||||
// unity_run_test_by_name("Mean of an empty array is zero");
|
||||
// UNITY_END();
|
||||
|
||||
// print_banner("Running tests with [mean] tag");
|
||||
// UNITY_BEGIN();
|
||||
// unity_run_tests_by_tag("[mean]", false);
|
||||
// UNITY_END();
|
||||
|
||||
// print_banner("Running tests without [fails] tag");
|
||||
// UNITY_BEGIN();
|
||||
// unity_run_tests_by_tag("[fails]", true);
|
||||
// UNITY_END();
|
||||
|
||||
// print_banner("Running all the registered tests");
|
||||
// UNITY_BEGIN();
|
||||
// unity_run_all_tests();
|
||||
// UNITY_END();
|
||||
|
||||
// print_banner("Starting interactive test menu");
|
||||
/* This function will not return, and will be busy waiting for UART input.
|
||||
* Make sure that task watchdog is disabled if you use this function.
|
||||
*/
|
||||
unity_run_menu();
|
||||
}
|
||||
47
test_apps/esp32c5/main/audio_test_file.h
Normal file
47
test_apps/esp32c5/main/audio_test_file.h
Normal file
File diff suppressed because one or more lines are too long
70
test_apps/esp32c5/main/test_aec.cpp
Normal file
70
test_apps/esp32c5/main/test_aec.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/* test_mean.c: Implementation of a testable component.
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_aec.h"
|
||||
#include "audio_test_file.h"
|
||||
#include "unity.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
TEST_CASE("test esp32c5 aec", "[aec]")
|
||||
{
|
||||
// vad_handle_t vad_handle = (vad_handle_t)arg;
|
||||
heap_caps_print_heap_info(MALLOC_CAP_8BIT);
|
||||
int start_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
int start_internal_size = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
int sample_rate = 16000;
|
||||
|
||||
aec_handle_t *aec_handle = aec_create(sample_rate, 2, 1, AEC_MODE_SR_LOW_COST);
|
||||
aec_destroy(aec_handle);
|
||||
int first_end_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
printf("memory leak for first init: %d\n", start_size - first_end_size);
|
||||
|
||||
aec_handle = aec_create(sample_rate, 2, 1, AEC_MODE_SR_LOW_COST);
|
||||
int audio_chunksize = aec_get_chunksize(aec_handle);
|
||||
printf("audio chunksize:%d\n", audio_chunksize); //512
|
||||
int16_t *buffer = (int16_t *)malloc(audio_chunksize * sizeof(int16_t));
|
||||
int16_t *ref_buffer = (int16_t *)malloc(audio_chunksize * sizeof(int16_t));
|
||||
int16_t *out_buffer = (int16_t *)malloc(audio_chunksize * sizeof(int16_t));
|
||||
|
||||
int chunks = 0;
|
||||
uint32_t c0, c1, c_res = 0;
|
||||
while (1) {
|
||||
if ((chunks + 1)*audio_chunksize * sizeof(int16_t) <= sizeof(audio_mic_file)) {
|
||||
memcpy(buffer, audio_mic_file + chunks * audio_chunksize , audio_chunksize * sizeof(int16_t));
|
||||
memcpy(ref_buffer, audio_ref_file + chunks * audio_chunksize , audio_chunksize * sizeof(int16_t));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
c0 = esp_timer_get_time();
|
||||
aec_process(aec_handle, buffer, ref_buffer, out_buffer);
|
||||
c1 = esp_timer_get_time();
|
||||
|
||||
c_res += c1 - c0;
|
||||
chunks++;
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
free(ref_buffer);
|
||||
free(out_buffer);
|
||||
printf("RAM size after vad detection: total:%d, internal:%d\n",
|
||||
start_size - heap_caps_get_free_size(MALLOC_CAP_8BIT),
|
||||
start_internal_size - heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
|
||||
printf("Done! Took %ld ms to parse %d ms worth of samples in %d iterations.\n",
|
||||
c_res/1000, chunks*audio_chunksize*1000/sample_rate, chunks);
|
||||
aec_destroy(aec_handle);
|
||||
|
||||
int end_size = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
printf("memory leak:%d\n", start_size-end_size);
|
||||
TEST_ASSERT_EQUAL(true, end_size == start_size);
|
||||
}
|
||||
6
test_apps/esp32c5/sdkconfig.defaults
Normal file
6
test_apps/esp32c5/sdkconfig.defaults
Normal file
@ -0,0 +1,6 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_IDF_TARGET="esp32c5"
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=148584
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
Loading…
Reference in New Issue
Block a user