SOC-ESP32S3部分:33-聲學前端模型ESP-SR

飛書文檔https://x509p6c8to.feishu.cn/wiki/YnbmwtqI5iBwE3kHA7AcZ3yTnLf

ESP-SR 是樂鑫官方開發的一個音頻組件,支持以下模塊:

  • 聲學前端算法 AFE
  • 喚醒詞檢測 WakeNet
  • 命令詞識別 MultiNet
  • 語音合成(目前只支持中文)

組件地址:https://components.espressif.com/components/espressif/esp-sr/versions/2.0.2

聲學前端 (Audio Front-End, AFE) 算法

由于語音交互類設備需要保證能夠采集干凈的音頻,所以在讀取麥克風的音頻后,需要進行一系列的算法處理,例如AEC、NS、BSS、MISO、VAD、AGC等

名稱簡介
AEC (Acoustic Echo Cancellation)回聲消除算法,最多支持雙麥處理,能夠有效的去除 mic 輸入信號中的自身播放聲音,從而可以在自身播放音樂的情況下很好的完成語音識別。
NS (Noise Suppression)噪聲抑制算法,支持單通道處理,能夠對單通道音頻中的非人聲噪聲進行抑制,尤其針對穩態噪聲,具有很好的抑制效果。
BSS (Blind Source Separation)盲信號分離算法,支持雙通道處理,能夠很好的將目標聲源和其余干擾音進行盲源分離,從而提取出有用音頻信號,保證了后級語音的質量。
MISO (Multi Input Single Output)多輸入單輸出算法,支持雙通道輸入,單通道輸出。用于在雙麥場景,沒有喚醒使能的情況下,選擇信噪比高的一路音頻輸出。
VAD (Voice Activity Detection)語音活動檢測算法,支持實時輸出當前幀的語音活動狀態。
AGC (Automatic Gain Control)自動增益控制算法,可以動態調整輸出音頻的幅值,當弱信號輸入時,放大輸出幅度;當輸入信號達到一定強度時,壓縮輸出幅度。

例如語音通過使用的算法

WakeNet 喚醒詞檢測

WakeNet 是一個基于神經網絡,為低功耗嵌入式 MCU 設計的喚醒詞模型,目前支持 5 個以內的喚醒詞識別,對于需要支持喚醒詞功能的應用,我們可以把經過AFE算法處理的音頻輸入給WakeNet模型,得到喚醒狀態,模型支持的音頻格式如下:輸入的音頻文件采樣率為 16 KHz,單聲道,編碼方式為 signed 16-bit。。

例如語音識別使用的算法:

MultiNet 是為了在 ESP32-S3 系列上離線實現多命令詞識別而設計的輕量化模型,目前支持 200 個以內的自定義命令詞識別。

  • 支持中文和英文命令詞識別
  • 支持用戶自定義命令詞
  • 支持運行過程中 增加/刪除/修改 命令詞語
  • 最多支持 200 個命令詞
  • 支持單次識別和連續識別兩種模式
  • 輕量化,低資源消耗
  • 低延時,延時 500 ms內
  • 支持在線中英文模型切換
  • 模型單獨分區,支持用戶應用 OTA

樂鑫 TTS 語音合成模型是一個為嵌入式系統設計的輕量化語音合成系統,具有如下主要特性:

  • 目前 僅支持中文
  • 輸入文本采用 UTF-8 編碼
  • 輸出格式采用流輸出,可減少延時
  • 多音詞發音自動識別
  • 可調節合成語速
  • 數字播報優化
  • 自定義聲音集(敬請期待)

如何使用esp-sr組件的相關功能呢?官方也給我們提供了示例工程ESP-Skainet。

ESP-Skainet 是樂鑫推出的智能語音助手應用,內置了很多例程,例如喚醒詞識別、命令詞識別、中文文字轉語音,USBmic等,詳見:https://github.com/espressif/esp-skainet/blob/master/README_cn.md

喚醒功能實現

參考

https://github.com/espressif/esp-skainet/tree/master/examples/wake_word_detection

新建工程,添加sr組件

idf.py add-dependency "espressif/esp-sr^2.0.2"
idf.py add-dependency "espressif/es8311^1.0.0"

修改工程配置Flash大小

因為加入sr聲學模型后,需要更大的存儲空間

添加自定義分區表

參考分區表章節22-分區表

# Name,?? Type, SubType, Offset,? Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs,????? data, nvs,???? ,??????? 0x6000,
phy_init, data, phy,???? ,??????? 0x1000,
factory,? app,? factory, ,??????? 1M,
model,? data, spiffs,??? ,??????? 5168K,

開啟PSRAM

選擇模型

ESP-SR允許您通過 menuconfig 界面選擇所需的模型。要配置模型:
運行
idf.py set-target esp32s3
idf.py menuconfig
導航到 ESP Speech Recognition
可支持配置以下選項:
- NS噪聲抑制模型
- VAD語音活動檢測模型
- WakeNet喚醒詞識別模型
- MultiNet命令詞識別模型模型存儲位置
(Top) → ESP Speech Recognition → model data path
(X) Read model data from flash
( ) Read model data from SD CardAFE回聲消除模型
→ ESP Speech Recognition → Select voice activity detection
(X) voice activity detection (WebRTC)
( ) voice activity detection (vadnet1 medium)??NS噪聲抑制模型
→ ESP Speech Recognition → Select noise suppression model
(X) noise suppression (WebRTC)
( ) Deep noise suppression v2 (nsnet2)VAD語音活動檢測模型
-> ESP Speech Recognition ->Select voice activity detection
(X) voice activity detection (WebRTC)
( ) voice activity detection (vadnet1 medium)喚醒詞配置:
→ ESP Speech Recognition → Load Multiple Wake Words
[ ] Hi,樂鑫 (wn9_hilexin)
[ ] 小愛同學 (wn9_xiaoaitongxue)
[*] 你好小智 (wn9_nihaoxiaozhi_tts)
可選擇單個,最多可選擇兩個中文命令詞識別模型
→ ESP Speech Recognition → Chinese Speech Commands Model
(X) None
( ) chinese recognition (mn5q8_cn)
( ) general chinese recognition (mn6_cn)
( ) chinese recognition for air conditioner controller (mn6_cn_ac)
( ) general chinese recognition (mn7_cn)
( ) chinese recognition for air conditioner controller (mn7_cn_ac)英文命令詞識別模型
→ ESP Speech Recognition → English Speech Commands Model
(X) None
( ) english recognition (mn5q8_en)
( ) general english recognition (mn6_en)
( ) general english recognition (mn7_en)

為了使得前端模型運行效果更佳,建議參考例程進行配置,ESP32S3其它配置如下

CONFIG_IDF_TARGET="esp32s3"
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y? 4線spi
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y 16M flash
CONFIG_PARTITION_TABLE_CUSTOM=y? 自定義分區表
CONFIG_SR_VADN_VADNET1_MEDIUM=y? vad檢測模型
CONFIG_SPIRAM=y????????????????? 開啟spiram
CONFIG_SPIRAM_MODE_OCT=y???????? 8線sprram
CONFIG_SPIRAM_SPEED_80M=y??????? 80M速率
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y 240M CPU主頻
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y 緩存配置
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y

修改demo/main/CMakeLists.txt

idf_component_register(SRCS "main.c""driver_es8311.c"INCLUDE_DIRS ".")

添加es8311驅動

demo/main/driver_es8311.h

#ifndef _DERIVER_ES8311_H_
#define _DERIVER_ES8311_H_int es8311_get_feed_channel();esp_err_t es8311_get_feed_data(bool is_get_raw_channel, int16_t *buffer, int buffer_len);void init_driver_es8311();#endif

demo/main/driver_es8311.c

#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2s_std.h"
#include "esp_system.h"
#include "esp_check.h"
#include "es8311.h"/* Example configurations */
#define EXAMPLE_SAMPLE_RATE (16000)??????????????????????????????????????? // 音頻采樣率,采樣率被設置為16000 Hz,即每秒采樣16000次。
#define EXAMPLE_DATA_BIT_WIDTH I2S_DATA_BIT_WIDTH_16BIT??????????????????? // 音頻采樣位寬 16bit
#define EXAMPLE_SLOT_MODE_MONO I2S_SLOT_MODE_STEREO?????????? ?????????????// 音頻采樣聲道 雙聲道#define EXAMPLE_MCLK_MULTIPLE (384)??????????????????????????????????????? // 主時鐘頻率是采樣率的倍數,用于驅動I2S接口。MCLK的倍數被設置為384。這意味著主時鐘頻率將是采樣率的384倍。如果數據寬度不是24位,256倍數可能已經足夠。
#define EXAMPLE_MCLK_FREQ_HZ (EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE) // 主時鐘的頻率
#define EXAMPLE_VOICE_VOLUME 90??????????????????????????????????????????? // 音量,控制輸出音量的大小。
#define EXAMPLE_MIC_GAIN ES8311_MIC_GAIN_0DB?????????????????????????????? // 麥克風增益
#define EXAMPLE_RECV_BUF_SIZE?? (2400)??????????????????????? ?????????????// MIC接收緩沖區大小/* I2C port and GPIOs */
#define I2C_NUM (0)
#define I2C_SCL_IO (GPIO_NUM_5)
#define I2C_SDA_IO (GPIO_NUM_7)
/* I2S port and GPIOs */
#define I2S_NUM (0)
#define I2S_MCK_IO (GPIO_NUM_6)
#define I2S_BCK_IO (GPIO_NUM_14)
#define I2S_WS_IO (GPIO_NUM_12)
#define I2S_DO_IO (GPIO_NUM_11)
#define I2S_DI_IO (GPIO_NUM_13)
#define SPKER_CTRL_PIN??? GPIO_NUM_10
#define SPKER_CTRL_PIN_SEL? (1ULL<<SPKER_CTRL_PIN)static const char *TAG = "i2s_es8311";
static i2s_chan_handle_t tx_handle = NULL;
static i2s_chan_handle_t rx_handle = NULL;static esp_err_t i2s_driver_init(void)
{// 指定I2S編號和主模式i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM, I2S_ROLE_MASTER);// 啟用自動清除DMA緩沖區中的遺留數據chan_cfg.auto_clear = true;// 創建一個新的I2S通道,并將返回的發送和接收通道句柄分別存儲在tx_handle和rx_handle中ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, &rx_handle));// 配置標準I2S模式i2s_std_config_t std_cfg = {// 設置時鐘配置,使用默認的標準I2S時鐘配置,并根據EXAMPLE_SAMPLE_RATE配置采樣率.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(EXAMPLE_SAMPLE_RATE),// 設置槽位配置,使用默認的Philips標準槽位配置,16位數據寬度和立體聲模式.slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(EXAMPLE_DATA_BIT_WIDTH, EXAMPLE_SLOT_MODE_MONO),// 設置GPIO配置,指定各個I2S信號的GPIO引腳,并配置信號不反轉.gpio_cfg = {.mclk = I2S_MCK_IO, // 主時鐘引腳.bclk = I2S_BCK_IO, // 位時鐘引腳.ws = I2S_WS_IO,??? // 左右聲道選擇引腳.dout = I2S_DO_IO,? // 數據輸出引腳.din = I2S_DI_IO,?? // 數據輸入引腳.invert_flags = {.mclk_inv = false, // 主時鐘不反轉.bclk_inv = false, // 位時鐘不反轉.ws_inv = false,?? // 左右聲道選擇信號不反轉},},};// 設置主時鐘的倍數std_cfg.clk_cfg.mclk_multiple = EXAMPLE_MCLK_MULTIPLE;// 初始化發送通道為標準I2S模式ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg));// 初始化接收通道為標準I2S模式ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle, &std_cfg));// 啟用發送通道ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));// 啟用接收通道ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));return ESP_OK;
}static esp_err_t es8311_codec_init(void)
{/* 初始化I2C外設 */const i2c_config_t es_i2c_cfg = {.sda_io_num = I2C_SDA_IO,??????????? // SDA引腳編號.scl_io_num = I2C_SCL_IO,??????????? // SCL引腳編號.mode = I2C_MODE_MASTER,???????????? // I2C模式為主模式.sda_pullup_en = GPIO_PULLUP_ENABLE, // 啟用SDA引腳的上拉電阻.scl_pullup_en = GPIO_PULLUP_ENABLE, // 啟用SCL引腳的上拉電阻.master.clk_speed = 100000,??????? ??// I2C主時鐘速度為100 kHz};// 配置I2C參數ESP_RETURN_ON_ERROR(i2c_param_config(I2C_NUM, &es_i2c_cfg), TAG, "config i2c failed");// 安裝I2C驅動ESP_RETURN_ON_ERROR(i2c_driver_install(I2C_NUM, I2C_MODE_MASTER, 0, 0, 0), TAG, "install i2c driver failed");// 初始化ES8311編解碼器 創建ES8311句柄,使用I2C_NUM和ES8311的地址es8311_handle_t es_handle = es8311_create(I2C_NUM, ES8311_ADDRRES_0);ESP_RETURN_ON_FALSE(es_handle, ESP_FAIL, TAG, "es8311 create failed");// 配置ES8311的時鐘const es8311_clock_config_t es_clk = {.mclk_inverted = false,???????????????? // 主時鐘不反轉.sclk_inverted = false,???????????????? // 位時鐘不反轉.mclk_from_mclk_pin = true,???????????? // 主時鐘從MCLK引腳獲取.mclk_frequency = EXAMPLE_MCLK_FREQ_HZ, // 主時鐘頻率.sample_frequency = EXAMPLE_SAMPLE_RATE // 采樣頻率};// 初始化ES8311編解碼器ESP_ERROR_CHECK(es8311_init(es_handle, &es_clk, ES8311_RESOLUTION_16, ES8311_RESOLUTION_16));// 配置ES8311的采樣頻率ESP_RETURN_ON_ERROR(es8311_sample_frequency_config(es_handle, EXAMPLE_SAMPLE_RATE * EXAMPLE_MCLK_MULTIPLE, EXAMPLE_SAMPLE_RATE), TAG, "set es8311 sample frequency failed");// 設置ES8311的音量ESP_RETURN_ON_ERROR(es8311_voice_volume_set(es_handle, EXAMPLE_VOICE_VOLUME, NULL), TAG, "set es8311 volume failed");// 配置ES8311的麥克風ESP_RETURN_ON_ERROR(es8311_microphone_config(es_handle, false), TAG, "set es8311 microphone failed");// 設置ES8311的麥克風增益ESP_RETURN_ON_ERROR(es8311_microphone_gain_set(es_handle, EXAMPLE_MIC_GAIN), TAG, "set es8311 microphone gain failed");return ESP_OK;
}int es8311_get_feed_channel(void)
{return EXAMPLE_SLOT_MODE_MONO;
}esp_err_t es8311_get_feed_data(bool is_get_raw_channel, int16_t *buffer, int buffer_len){size_t bytes_read = 0;esp_err_t ret = ESP_OK;ret = i2s_channel_read(rx_handle, buffer, buffer_len, &bytes_read, 1000);if (ret != ESP_OK) {ESP_LOGE(TAG, "[echo] i2s read failed");abort(); // 終止程序}return ret;
}esp_err_t es8311_play_data(const int16_t *buffer, int buffer_len)
{size_t bytes_written = 0;esp_err_t ret = i2s_channel_write(tx_handle, buffer, buffer_len, &bytes_written, 1000);if (ret != ESP_OK) {ESP_LOGE(TAG, "i2s write failed");return ret;}return ESP_OK;
}void init_driver_es8311(void)
{gpio_config_t io_conf = {};io_conf.intr_type = GPIO_INTR_DISABLE;io_conf.mode = GPIO_MODE_OUTPUT;io_conf.pin_bit_mask = SPKER_CTRL_PIN_SEL;io_conf.pull_down_en = 0;io_conf.pull_up_en = 0;gpio_config(&io_conf);gpio_set_level(SPKER_CTRL_PIN, 1);printf("i2s es8311 codec example start\n-----------------------------\n");/* 初始化I2S外設 */if (i2s_driver_init() != ESP_OK){ESP_LOGE(TAG, "i2s driver init failed");abort(); // 終止程序}else{ESP_LOGI(TAG, "i2s driver init success");}/* 初始化I2C外設并配置ES8311編解碼器 */if (es8311_codec_init() != ESP_OK){ESP_LOGE(TAG, "es8311 codec init failed");abort(); // 終止程序}else{ESP_LOGI(TAG, "es8311 codec init success");}
}

main.c

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wn_iface.h"
#include "esp_wn_models.h"
#include "esp_afe_sr_models.h"
#include "esp_mn_iface.h"
#include "esp_mn_models.h"
#include "model_path.h"
#include "string.h"#include "driver_es8311.h"int detect_flag = 0;????????????????????????? // 檢測標志,初始為0
static esp_afe_sr_iface_t *afe_handle = NULL; // AFE 處理接口句柄
static volatile int task_flag = 0;??????????? // 任務標志,用于控制任務的運行// 任務:從麥克風獲取音頻數據并喂給 AFE 處理
void feed_Task(void *arg)
{esp_afe_sr_data_t *afe_data = arg;????????????????????????????? // 獲取 AFE 數據結構int audio_chunksize = afe_handle->get_feed_chunksize(afe_data); // 獲取每次喂給 AFE 的音頻塊大小int nch = afe_handle->get_feed_channel_num(afe_data);?????????? // 獲取音頻通道數int feed_channel = es8311_get_feed_channel(); // 獲取實際的音頻通道數assert(nch == feed_channel);????????????????? // 確保通道數匹配// 分配內存以存儲音頻數據塊int16_t *i2s_buff = malloc(audio_chunksize * sizeof(int16_t) * feed_channel);assert(i2s_buff);while (task_flag){ // 當任務標志為1時,持續運行// 從麥克風獲取音頻數據es8311_get_feed_data(true, i2s_buff, audio_chunksize * sizeof(int16_t) * feed_channel);// 將音頻數據喂給 AFE 處理afe_handle->feed(afe_data, i2s_buff);}// 釋放內存if (i2s_buff){free(i2s_buff);i2s_buff = NULL;}// 刪除任務vTaskDelete(NULL);
}// 任務:檢測喚醒詞
void detect_Task(void *arg)
{esp_afe_sr_data_t *afe_data = arg;???????????????????????????? // 獲取 AFE 數據結構int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data); // 獲取每次從 AFE 獲取的音頻塊大小int16_t *buff = malloc(afe_chunksize * sizeof(int16_t));?????? // 分配內存以存儲音頻數據塊assert(buff);printf("------------detect start------------\n");while (task_flag){ // 當任務標志為1時,持續運行// 從 AFE 獲取處理結果afe_fetch_result_t *res = afe_handle->fetch(afe_data);if (!res || res->ret_value == ESP_FAIL){printf("fetch error!\n");break;}if (res->wakeup_state == WAKENET_DETECTED) {printf("wakeword detected\n");printf("model index:%d, word index:%d\n", res->wakenet_model_index, res->wake_word_index);printf("-----------LISTENING-----------\n");}// if (res->vad_state == VAD_SILENCE)// {//???? printf("VAD_SILENCE\n");// }// else if (res->vad_state == VAD_SPEECH)// {//???? printf("VAD_SPEECH\n");// }}// 釋放內存if (buff){free(buff);buff = NULL;}// 刪除任務vTaskDelete(NULL);
}void app_main()
{// 初始化音頻板,設置采樣率為16000 Hz,單聲道,位深為16位init_driver_es8311();// 初始化聲學前端(AFE)模型,存儲到分區表的model分區srmodel_list_t *models = esp_srmodel_init("model");if (models){for (int i = 0; i < models->num; i++){if (strstr(models->model_name[i], ESP_WN_PREFIX) != NULL){printf("wakenet model in flash: %s\n", models->model_name[i]);}}}//"MR":聲學前端模型名稱,模型具體參考https://docs.espressif.com/projects/esp-sr/zh_CN/latest/esp32s3/benchmark/README.html// M:麥克風通道 R:播放參考通道 N:未使用或未知通道 MRNN代表一個麥克風通道、一個播放通道// models:聲學前端和麥克風喚醒模型列表。// AFE_TYPE_VC :用于語音通話降噪 AFE_TYPE_SR:用于語音識別。// AFE_MODE_LOW_COST:AFE 模式,低功耗模式。afe_config_t *afe_config = afe_config_init("MR", models, AFE_TYPE_SR, AFE_MODE_LOW_COST);// print/modify wake word model.if (afe_config->wakenet_model_name){printf("wakeword model in AFE config: %s\n", afe_config->wakenet_model_name);}if (afe_config->wakenet_model_name_2){printf("wakeword model in AFE config: %s\n", afe_config->wakenet_model_name_2);}afe_handle = esp_afe_handle_from_config(afe_config);esp_afe_sr_data_t *afe_data = afe_handle->create_from_config(afe_config);afe_config_free(afe_config); // 釋放 AFE 配置內存task_flag = 1; // 設置任務標志為1,啟動任務// 創建音頻采集任務,運行在核心0xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void *)afe_data, 5, NULL, 0);// 創建檢測喚醒詞任務,運行在核心1xTaskCreatePinnedToCore(&detect_Task, "detect", 4 * 1024, (void *)afe_data, 5, NULL, 1);
}

音頻減噪功能實現

新建工程,添加sr組件

idf.py add-dependency "espressif/esp-sr^2.0.2"
idf.py add-dependency "espressif/es8311^1.0.0"

添加自定義分區表

# Espressif ESP32 Partition Table
# Name,? Type, SubType, Offset,? Size
factory, app,? factory, 0x010000, 2500k
model,? data, spiffs,???????? , 5168K,

最終main.c

#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_wn_iface.h"
#include "esp_wn_models.h"
#include "esp_afe_sr_models.h"
#include "esp_mn_iface.h"
#include "esp_mn_models.h"
#include "model_path.h"
#include "string.h"#include "driver_es8311.h"int detect_flag = 0;? // 檢測標志,初始為0
static esp_afe_sr_iface_t *afe_handle = NULL;? // AFE 處理接口句柄
static volatile int task_flag = 0;? // 任務標志,用于控制任務的運行// 任務:從麥克風獲取音頻數據并喂給 AFE 處理
void feed_Task(void *arg) {esp_afe_sr_data_t *afe_data = arg;? // 獲取 AFE 數據結構int audio_chunksize = afe_handle->get_feed_chunksize(afe_data);? // 獲取每次喂給 AFE 的音頻塊大小int nch = afe_handle->get_feed_channel_num(afe_data);? // 獲取音頻通道數int feed_channel = es8311_get_feed_channel();? // 獲取實際的音頻通道數assert(nch == feed_channel);? // 確保通道數匹配// 分配內存以存儲音頻數據塊int16_t *i2s_buff = malloc(audio_chunksize * sizeof(int16_t) * feed_channel);assert(i2s_buff);while (task_flag) {? // 當任務標志為1時,持續運行// 從麥克風獲取音頻數據es8311_get_feed_data(true, i2s_buff, audio_chunksize * sizeof(int16_t) * feed_channel);// 將音頻數據喂給 AFE 處理afe_handle->feed(afe_data, i2s_buff);}// 釋放內存if (i2s_buff) {free(i2s_buff);i2s_buff = NULL;}// 刪除任務vTaskDelete(NULL);
}// 任務:音頻處理結果
void detect_Task(void *arg) {esp_afe_sr_data_t *afe_data = arg;? // 獲取 AFE 數據結構int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data);? // 獲取每次從 AFE 獲取的音頻塊大小int16_t *buff = malloc(afe_chunksize * sizeof(int16_t));? // 分配內存以存儲音頻數據塊assert(buff);printf("------------detect start------------\n");while (task_flag) {? // 當任務標志為1時,持續運行// 從 AFE 獲取處理結果afe_fetch_result_t* res = afe_handle->fetch(afe_data);if (res && res->ret_value != ESP_FAIL) {memcpy(buff, res->data, afe_chunksize * sizeof(int16_t));// 在這里使用算法處理后的音頻,存儲到本地或者上傳云端//data = buff,len =? afe_chunksize * sizeof(int16_t)}}// 釋放內存if (buff) {free(buff);buff = NULL;}// 刪除任務vTaskDelete(NULL);
}void app_main() {// 初始化音頻板,設置采樣率為16000 Hz,單聲道,位深為16位init_driver_es8311();// 初始化聲學前端(AFE)模型,存儲到分區表的model分區srmodel_list_t *models = esp_srmodel_init("model");if (models) {for (int i=0; i<models->num; i++) {if (strstr(models->model_name[i], ESP_WN_PREFIX) != NULL) {printf("wakenet model in flash: %s\n", models->model_name[i]);}}}//"MR":聲學前端模型名稱,模型具體參考https://docs.espressif.com/projects/esp-sr/zh_CN/latest/esp32s3/benchmark/README.html。//models:聲學前端和麥克風喚醒模型列表。//AFE_TYPE_VC :用于語音降噪。//AFE_MODE_LOW_COST:AFE 模式,低功耗模式。afe_config_t *afe_config = afe_config_init("MR", models, AFE_TYPE_VC, AFE_MODE_LOW_COST);afe_handle = esp_afe_handle_from_config(afe_config);esp_afe_sr_data_t *afe_data = afe_handle->create_from_config(afe_config);afe_config_free(afe_config);? // 釋放 AFE 配置內存task_flag = 1;? // 設置任務標志為1,啟動任務// 創建音頻采集任務,運行在核心0xTaskCreatePinnedToCore(&feed_Task, "feed", 8 * 1024, (void*)afe_data, 5, NULL, 0);// 創建檢測喚醒詞任務,運行在核心1xTaskCreatePinnedToCore(&detect_Task, "detect", 4 * 1024, (void*)afe_data, 5, NULL, 1);
}
默認啟用:
如果應用涉及 Wi-Fi 或網絡通信,
建議默認開啟 WIFI_IRAM_OPT 和 LWIP_IRAM_OPTIMIZATION。
按需啟用 WIFI_RX_IRAM_OPT:僅在需要 極低延遲接收
或 Wi-Fi/BLE 并發 時啟用WIFI_STATIC_RX_BUFFER_NUM 16? 24 靜態 Wi-Fi 接收緩沖區數量
WIFI_DYNAMIC_RX_BUFFER_NUM 32 64 動態 Wi-Fi 接收緩沖區數量
WIFI_STATIC_TX_BUFFER_NUM 16 24 靜態 Wi-Fi 發送緩沖區數量
WIFI_RX_BA_WIN 16 32 Block Ack 窗口大小 影響吞吐量,不占內存
LWIP_UDP_RECVMBOX_SIZE 6 64? 16

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/82808.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/82808.shtml
英文地址,請注明出處:http://en.pswp.cn/web/82808.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于vscode,idea,java,html,css,vue,echart,maven,springboot,mysql數據庫,在線考試系統

詳細視頻&#xff1a;【基于vscode,idea,java,html,css,vue,echart,maven,springboot,mysql數據庫&#xff0c;在線考試系統-嗶哩嗶哩】 https://b23.tv/7hwmwmQ

【Linux】shell中的運行流程控制

目錄 一.什么是運行流程控制 二.條件允許流程控制--if 2.1.單分支 2.2.雙分支 2.3.多分支 if多分支練習 三.循環運行流程控制 無判定循環--for 判斷循環--while&#xff0c;until 四.選擇運行流程控制 五.自動應答--expect 5.1.固定位置的交互應答 5.2.非固定位置的…

新能源汽車熱管理核心技術解析:冬季續航提升40%的行業方案

新能源汽車熱管理核心技術解析&#xff1a;冬季續航提升40%的行業方案 摘要&#xff1a;突破續航焦慮的關鍵在熱能循環&#xff01; &#x1f449; 本文耗時72小時梳理行業前沿方案&#xff0c;含特斯拉/比亞迪等8家車企熱管理系統原理圖 一、熱管理為何成新能源車決勝關鍵&am…

OCR MLLM Evaluation

為什么需要評測體系&#xff1f;——背景與矛盾 ?? 能干的事&#xff1a;?? 看清楚發票、身份證上的字&#xff08;準確率>90%&#xff09;&#xff0c;速度飛快&#xff08;眨眼間完成&#xff09;。??干不了的事&#xff1a;?? 碰到復雜表格&#xff08;合并單元…

深入解析JVM工作原理:從字節碼到機器指令的全過程

一、JVM概述 Java虛擬機(JVM)是Java平臺的核心組件&#xff0c;它實現了Java"一次編寫&#xff0c;到處運行"的理念。JVM是一個抽象的計算機器&#xff0c;它有自己的指令集和運行時內存管理機制。 JVM的主要職責&#xff1a; 加載&#xff1a;讀取.class文件并驗…

Python繪圖庫及圖像類型之特殊領域可視化

Python繪圖庫及圖像類型之基礎圖表-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/148433762?spm1001.2014.3001.5501 Python繪圖庫及圖像類型之高級可視化-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/148450750?spm1001.2014.3001.…

04 APP 自動化- Appium toast 元素定位列表滑動

文章目錄 一、toast 元素的定位二、滑屏操作 一、toast 元素的定位 toast 元素就是簡易的消息提示框&#xff0c;toast 顯示窗口顯示的時間有限&#xff0c;一般3秒左右 # -*- codingutf-8 -*- from time import sleep from appium import webdriver from appium.options.an…

C/C++ OpenCV 矩陣運算

C/C OpenCV 矩陣運算詳解 &#x1f4a1; OpenCV 是一個強大的開源計算機視覺和機器學習庫&#xff0c;它提供了豐富的矩陣運算功能&#xff0c;這對于圖像處理和計算機視覺算法至關重要。本文將詳細介紹如何使用 C/C 和 OpenCV 進行常見的矩陣運算。 矩陣的創建與初始化 在進…

基于大模型的 UI 自動化系統

基于大模型的 UI 自動化系統 下面是一個完整的 Python 系統,利用大模型實現智能 UI 自動化,結合計算機視覺和自然語言處理技術,實現"看屏操作"的能力。 系統架構設計 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…

USB擴展器與USB服務器的2個主要區別

在現代辦公和IT環境中&#xff0c;連接和管理USB設備是常見需求。USB擴展器&#xff08;常稱USB集線器&#xff09;與USB服務器&#xff08;如朝天椒USB服務器&#xff09;是兩類功能定位截然不同的解決方案。前者主要解決物理接口數量不足的“近身”連接擴展問題&#xff0c;而…

Nuxt.js 中的路由配置詳解

Nuxt.js 通過其內置的路由系統簡化了應用的路由配置&#xff0c;使得開發者可以輕松地管理頁面導航和 URL 結構。路由配置主要涉及頁面組件的組織、動態路由的設置以及路由元信息的配置。 自動路由生成 Nuxt.js 會根據 pages 目錄下的文件結構自動生成路由配置。每個文件都會對…

驗證負載均衡與彈性伸縮

什么是彈性伸縮&#xff08;Auto Scaling&#xff09;&#xff1f; 彈性伸縮是指 云計算平臺根據實時負載自動調整計算資源&#xff08;如服務器實例、容器Pod&#xff09;數量&#xff0c;以確保系統在高峰時保持穩定&#xff0c;在低谷時節省成本。 什么時候會觸發彈性伸縮&…

區分viewmodel和model職責的方法

gpt回答挺好的&#xff0c;我就分享一下。 1. 最經典的一句話區分 Model&#xff08;Repository/數據層&#xff09;&#xff1a;只負責**“數據獲取/存儲/持久化”和“核心業務算法”**&#xff0c;不依賴UI層和Android框架&#xff0c;可以脫離界面獨立存在。 ViewModel&…

C語言數據結構筆記3:Union聯合體+結構體取8位Bool量

本文銜接上文要求&#xff0c;新增8位bool量的獲取方式。 目錄 問題提出&#xff1a; Union聯合體struct結構體(方式1)&#xff1a; Union聯合體struct結構體(方式2)&#xff1a; BYTE方式讀取&#xff1a; 問題提出&#xff1a; 在STM32單片機的編程中&#xff0c;無法定義Boo…

三種讀寫傳統xls格式文件開源庫libxls、xlslib、BasicExcel的比較

最近準備讀寫傳統xls格式文件&#xff0c;而不是較新的xlsx&#xff0c;詢問DeepSeek有哪些開源庫&#xff0c;他給出了如下的簡介和建議&#xff0c;還給出了相應鏈接&#xff0c;不過有的鏈接已失效。最后還不忘提醒&#xff0c;現在該用xlsx格式了。 以下是幾個可以處理傳統…

從測試角度看待CI/CD,敏捷開發

什么是敏捷開發&#xff1f; 是在高強度反饋的情況下&#xff0c;短周期&#xff0c;不斷的迭代產品&#xff0c;滿足用戶需求&#xff0c;搶占更多的市場 敏捷開發是什么&#xff1f; 是一種產品快速迭代的情況下&#xff0c;降低出錯的概率&#xff0c;具體會落實到公司的…

figma MCP + cursor如何將設計稿生成前端頁面

一、準備工作 figma MCP需要通過figma key來獲取設計稿權限&#xff0c;key的生成步驟如下 1. 打開figma網頁版/APP&#xff0c;進入賬戶設定 2. 點擊生成token 3. 填寫內容生成token(一定要確認復制了&#xff0c;不然關閉彈窗后就不會顯示了) 二、配置MCP 4. 進入到cursor…

git互聯GitHub 使用教程

一、下載git Git 公司 右鍵 git config --global user.name "name" git config --global user.email "email" ssh-keygen -t rsa -C email &#xff1a;生成的ssh密鑰需要到github 網站中保存ssh 二、GitHub新建repository 三、本地git互聯GitHub 找…

“輕量應用服務器” vs. “云服務器CVM”:小白入門騰訊云,哪款“云機”更適合你?(場景、配置、價格對比解析)

更多云服務器知識&#xff0c;盡在hostol.com 當你第一次踏入騰訊云這個“數字百貨大樓”&#xff0c;面對琳瑯滿目的“云產品”&#xff0c;是不是有點眼花繚亂&#xff0c;特別是看到“輕量應用服務器”和“云服務器CVM”這兩位都號稱能幫你“安家落戶”的“云主機”時&…

MongoDB學習和應用(高效的非關系型數據庫)

一丶 MongoDB簡介 對于社交類軟件的功能&#xff0c;我們需要對它的功能特點進行分析&#xff1a; 數據量會隨著用戶數增大而增大讀多寫少價值較低非好友看不到其動態信息地理位置的查詢… 針對以上特點進行分析各大存儲工具&#xff1a; mysql&#xff1a;關系型數據庫&am…