mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-07 20:00:53 +00:00
feat(vad): lp vad driver and wakeup feature
This commit is contained in:

committed by
Armando (Dou Yiwen)

parent
39430c1404
commit
1792aba1dc
@@ -30,6 +30,10 @@ if(CONFIG_SOC_LP_I2S_SUPPORTED)
|
||||
list(APPEND srcs "lp_i2s.c" "lp_i2s_std.c" "lp_i2s_pdm.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_LP_I2S_SUPPORT_VAD)
|
||||
list(APPEND srcs "lp_i2s_vad.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS ${include}
|
||||
PRIV_REQUIRES esp_driver_gpio esp_pm esp_mm
|
||||
|
153
components/esp_driver_i2s/include/driver/lp_i2s_vad.h
Normal file
153
components/esp_driver_i2s/include/driver/lp_i2s_vad.h
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/i2s_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief State Machine
|
||||
┌──────────────────────────────────┐
|
||||
│ │
|
||||
┌─────────────┤ speak-activity-listening-state │ ◄───────────────┐
|
||||
│ │ │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
│ ▲ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
detected speak activity │ │ detected speak activity │ detected speak activity
|
||||
>= │ │ >= │ >=
|
||||
'speak_activity_thresh' │ │ 'min_speak_activity_thresh' │ 'max_speak_activity_thresh'
|
||||
│ │ │
|
||||
│ │ && │
|
||||
│ │ │
|
||||
│ │ detected non-speak activity │
|
||||
│ │ < │
|
||||
│ │ 'non_speak_activity_thresh' │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ │ │
|
||||
│ ┌───────────┴─────────────────────┐ │
|
||||
│ │ │ │
|
||||
└───────────► │ speak-activity-detected-state ├─────────────────┘
|
||||
│ │
|
||||
└─┬───────────────────────────────┘
|
||||
│
|
||||
│ ▲
|
||||
│ │
|
||||
│ │
|
||||
│ │ detected speak activity
|
||||
│ │ >=
|
||||
│ │ 'min_speak_activity_thresh'
|
||||
│ │
|
||||
│ │ &&
|
||||
│ │
|
||||
│ │ detected non-speak activity
|
||||
│ │ <
|
||||
└─────────────────────┘ 'non_speak_activity_thresh'
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief LP VAD peripheral
|
||||
*/
|
||||
typedef uint32_t lp_vad_t;
|
||||
|
||||
/**
|
||||
* @brief Type of VAD unit handle
|
||||
*/
|
||||
typedef struct vad_unit_ctx_t *vad_unit_handle_t;
|
||||
|
||||
/**
|
||||
* @brief LP VAD configurations
|
||||
*/
|
||||
typedef struct {
|
||||
int init_frame_num; /**< Number of init frames that are used for VAD to denoise, this helps the VAD to decrease the accidental trigger ratio.
|
||||
Note too big values may lead to voice activity miss */
|
||||
int min_energy_thresh; ///< Min energy threshold.
|
||||
bool skip_band_energy_thresh; ///< Skip band energy threshold or not
|
||||
|
||||
int speak_activity_thresh; /**< When in speak-activity-listening-state, if number of the detected speak activity is higher than this value, VAD runs into speak-activity-detected-state */
|
||||
|
||||
int non_speak_activity_thresh; /**< When in speak-activity-detected-state, if the number of the detected speak activity is higher than this value, but lower than `max_speak_activity_thresh`:
|
||||
- if the number of the detected non-speak activity is higher than this value, VAD runs into speak-activity-listening-state
|
||||
- if the number of the detected non-speak activity is lower than this value, VAD keeps in speak-activity-detected-state */
|
||||
|
||||
int min_speak_activity_thresh; /**< When in speak-activity-detected-state, if the number of the detected speak activity is higher than this value, but lower than `max_speak_activity_thresh`,
|
||||
then the VAD state machine will depends on the value of `non_speak_activity_thresh` */
|
||||
|
||||
int max_speak_activity_thresh; /**< When in speak-activity-detected-state, if the number of the detected speak activity is higher than this value, VAD runs into speak-activity-listening-state */
|
||||
} lp_vad_config_t;
|
||||
|
||||
typedef struct {
|
||||
lp_i2s_chan_handle_t lp_i2s_chan; ///< LP I2S channel handle
|
||||
lp_vad_config_t vad_config; ///< LP VAD config
|
||||
} lp_vad_init_config_t;
|
||||
|
||||
/**
|
||||
* @brief New LP VAD unit
|
||||
* @param[in] vad_id VAD id
|
||||
* @param[in] init_config Initial configurations
|
||||
* @param[out] ret_unit Unit handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: On success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment
|
||||
*/
|
||||
esp_err_t lp_i2s_vad_new_unit(lp_vad_t vad_id, const lp_vad_init_config_t *init_config, vad_unit_handle_t *ret_unit);
|
||||
|
||||
/**
|
||||
* @brief Enable LP VAD
|
||||
*
|
||||
* @param[in] unit VAD handle
|
||||
* @param[in] init_config Initial configurations
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: On success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment
|
||||
*/
|
||||
esp_err_t lp_i2s_vad_enable(vad_unit_handle_t unit);
|
||||
|
||||
/**
|
||||
* @brief Disable LP VAD
|
||||
*
|
||||
* @param[in] unit VAD handle
|
||||
* @param[in] init_config Initial configurations
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: On success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment
|
||||
*/
|
||||
esp_err_t lp_i2s_vad_disable(vad_unit_handle_t unit);
|
||||
|
||||
/**
|
||||
* @brief Delete LP VAD unit
|
||||
* @param[in] unit VAD handle
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: On success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
* - ESP_ERR_INVALID_STATE: Driver state is invalid, you shouldn't call this API at this moment
|
||||
*/
|
||||
esp_err_t lp_i2s_vad_del_unit(vad_unit_handle_t unit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/lp_i2s_hal.h"
|
||||
#include "driver/i2s_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get LP I2S soc handle
|
||||
*
|
||||
* @param[in] chan LP I2S channel handle
|
||||
*
|
||||
* @return LP I2S soc handle
|
||||
*/
|
||||
lp_i2s_soc_handle_t lp_i2s_get_soc_handle(lp_i2s_chan_handle_t chan);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -25,6 +25,7 @@
|
||||
#include "driver/lp_i2s.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#include "esp_private/i2s_platform.h"
|
||||
#include "esp_private/lp_i2s_private.h"
|
||||
#include "i2s_private.h"
|
||||
#include "soc/i2s_periph.h"
|
||||
|
||||
@@ -329,3 +330,15 @@ static void IRAM_ATTR s_i2s_default_isr(void *arg)
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------
|
||||
HELPERS
|
||||
---------------------------------------------------------------*/
|
||||
lp_i2s_soc_handle_t lp_i2s_get_soc_handle(lp_i2s_chan_handle_t chan)
|
||||
{
|
||||
if (!chan) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return chan->ctlr->hal.dev;
|
||||
}
|
||||
|
112
components/esp_driver_i2s/lp_i2s_vad.c
Normal file
112
components/esp_driver_i2s/lp_i2s_vad.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "stdatomic.h"
|
||||
#if SOC_LP_VAD_SUPPORTED
|
||||
#include "esp_check.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/lp_i2s_vad.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "hal/lp_i2s_ll.h"
|
||||
#include "hal/lp_i2s_hal.h"
|
||||
#include "esp_private/lp_i2s_private.h"
|
||||
|
||||
#define LP_VAD_MEM_ALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
|
||||
static const char *LP_VAD_TAG = "LP_VAD";
|
||||
|
||||
typedef enum {
|
||||
VAD_FSM_INIT,
|
||||
VAD_FSM_ENABLE,
|
||||
} vad_fsm_t;
|
||||
|
||||
typedef struct vad_unit_ctx_t {
|
||||
lp_i2s_soc_handle_t hw;
|
||||
lp_vad_t vad_id;
|
||||
vad_fsm_t fsm;
|
||||
} vad_unit_ctx_t;
|
||||
|
||||
static atomic_bool s_vad_id_claimed[SOC_ADC_PERIPH_NUM] = {ATOMIC_VAR_INIT(false)};
|
||||
|
||||
static bool s_vad_claim(lp_vad_t vad_id)
|
||||
{
|
||||
bool false_var = false;
|
||||
return atomic_compare_exchange_strong(&s_vad_id_claimed[vad_id], &false_var, true);
|
||||
}
|
||||
|
||||
static bool s_vad_free(lp_vad_t vad_id)
|
||||
{
|
||||
bool true_var = true;
|
||||
return atomic_compare_exchange_strong(&s_vad_id_claimed[vad_id], &true_var, false);
|
||||
}
|
||||
|
||||
esp_err_t lp_i2s_vad_new_unit(lp_vad_t vad_id, const lp_vad_init_config_t *init_config, vad_unit_handle_t *ret_unit)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(init_config, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid arg");
|
||||
ESP_RETURN_ON_FALSE(init_config->lp_i2s_chan, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "LP I2S not initialised");
|
||||
ESP_RETURN_ON_FALSE(init_config->vad_config.init_frame_num >= LP_VAD_LL_INIT_FRAME_MIN && init_config->vad_config.init_frame_num <= LP_VAD_LL_INIT_FRAME_MAX, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid init frame num");
|
||||
|
||||
bool success_claim = s_vad_claim(vad_id);
|
||||
ESP_RETURN_ON_FALSE(success_claim, ESP_ERR_NOT_FOUND, LP_VAD_TAG, "vad%"PRId32" is already in use", vad_id);
|
||||
|
||||
vad_unit_ctx_t *unit = heap_caps_calloc(1, sizeof(vad_unit_ctx_t), LP_VAD_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, LP_VAD_TAG, "no mem for unit");
|
||||
|
||||
unit->hw = lp_i2s_get_soc_handle(init_config->lp_i2s_chan);
|
||||
ESP_LOGD(LP_VAD_TAG, "unit->hw: %p", unit->hw);
|
||||
lp_vad_ll_set_init_frame_num(unit->hw, init_config->vad_config.init_frame_num);
|
||||
lp_vad_ll_set_init_min_energy(unit->hw, init_config->vad_config.min_energy_thresh);
|
||||
lp_vad_ll_set_speak_activity_thresh(unit->hw, init_config->vad_config.speak_activity_thresh);
|
||||
lp_vad_ll_set_non_speak_activity_thresh(unit->hw, init_config->vad_config.non_speak_activity_thresh);
|
||||
lp_vad_ll_set_min_speak_activity_thresh(unit->hw, init_config->vad_config.min_speak_activity_thresh);
|
||||
lp_vad_ll_set_max_speak_activity_thresh(unit->hw, init_config->vad_config.max_speak_activity_thresh);
|
||||
lp_vad_ll_skip_band_energy(unit->hw, init_config->vad_config.skip_band_energy_thresh);
|
||||
unit->fsm = VAD_FSM_INIT;
|
||||
*ret_unit = unit;
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
bool success_free = s_vad_free(vad_id);
|
||||
assert(success_free);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lp_i2s_vad_enable(vad_unit_handle_t unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid arg");
|
||||
ESP_RETURN_ON_FALSE(unit->fsm == VAD_FSM_INIT, ESP_ERR_INVALID_STATE, LP_VAD_TAG, "The driver is enabled already");
|
||||
|
||||
lp_vad_ll_enable(unit->hw, true);
|
||||
unit->fsm = VAD_FSM_ENABLE;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lp_i2s_vad_disable(vad_unit_handle_t unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid arg");
|
||||
ESP_RETURN_ON_FALSE(unit->fsm == VAD_FSM_ENABLE, ESP_ERR_INVALID_STATE, LP_VAD_TAG, "The driver is not enabled yet");
|
||||
|
||||
lp_vad_ll_enable(unit->hw, false);
|
||||
unit->fsm = VAD_FSM_INIT;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t lp_i2s_vad_del_unit(vad_unit_handle_t unit)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, LP_VAD_TAG, "invalid arg");
|
||||
ESP_RETURN_ON_FALSE(unit->fsm == VAD_FSM_INIT, ESP_ERR_INVALID_STATE, LP_VAD_TAG, "The driver is still in enabled state");
|
||||
|
||||
bool success_free = s_vad_free(unit->vad_id);
|
||||
ESP_RETURN_ON_FALSE(success_free, ESP_ERR_NOT_FOUND, LP_VAD_TAG, "vad%"PRId32" isn't in use", unit->vad_id);
|
||||
|
||||
free(unit);
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif /* SOC_LP_VAD_SUPPORTED */
|
Reference in New Issue
Block a user