Add recorder support.
This commit is contained in:
parent
36558e2252
commit
5c5dcb40fa
|
|
@ -1,4 +1,4 @@
|
|||
idf_component_register(SRCS "img_bilibili120.c" "app_sr.c" "esp32_s3_szp.c" "main.c" "app_ui.c"
|
||||
idf_component_register(SRCS "app_sr.c" "esp32_s3_szp.c" "main.c" "app_ui.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
spiffs_create_partition_image(storage ../spiffs FLASH_IN_PROJECT)
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ void detect_Task(void* arg) {
|
|||
// play_voice = -1;
|
||||
afe_handle->disable_wakenet(afe_data); // 关闭唤醒词识别
|
||||
detect_flag = 1; // 标记已检测到唤醒词
|
||||
ai_gui_in(); // AI人出现
|
||||
printf("AFE_FETCH_CHANNEL_VERIFIED, channel index: %d\n",
|
||||
res->trigger_channel_id);
|
||||
}
|
||||
|
|
@ -148,7 +147,6 @@ void detect_Task(void* arg) {
|
|||
afe_handle->enable_wakenet(afe_data); // 重新打开唤醒词识别
|
||||
detect_flag = 0; // 清除标记
|
||||
printf("\n-----------awaits to be waken up-----------\n");
|
||||
ai_gui_out(); // AI人退出
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ void mp3_player_init(void) {
|
|||
player_config.coreID = 1;
|
||||
|
||||
// ESP_ERROR_CHECK(audio_player_new(player_config));
|
||||
// ESP_ERROR_CHECK(audio_player_callback_register(_audio_player_callback, NULL));
|
||||
// ESP_ERROR_CHECK(audio_player_callback_register(_audio_player_callback,
|
||||
// NULL));
|
||||
|
||||
// 显示界面
|
||||
music_ui();
|
||||
|
|
@ -396,27 +397,8 @@ void music_ui(void) {
|
|||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
// AI人动画
|
||||
LV_IMG_DECLARE(img_bilibili120);
|
||||
|
||||
lv_obj_t* gif_start;
|
||||
|
||||
// AI人出现在屏幕
|
||||
void ai_gui_in(void) {
|
||||
lvgl_port_lock(0);
|
||||
gif_start = lv_gif_create(lv_scr_act());
|
||||
lv_gif_set_src(gif_start, &img_bilibili120);
|
||||
lv_obj_align(gif_start, LV_ALIGN_CENTER, 0, 0);
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
// AI人退出屏幕
|
||||
void ai_gui_out(void) {
|
||||
lvgl_port_lock(0);
|
||||
lv_obj_del(gif_start);
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
// AI播放音乐
|
||||
void ai_play(void) {
|
||||
int index = file_iterator_get_index(file_iterator);
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@
|
|||
void mp3_player_init(void);
|
||||
void music_ui(void);
|
||||
|
||||
void ai_gui_in(void);
|
||||
void ai_gui_out(void);
|
||||
|
||||
void ai_play(void);
|
||||
void ai_pause(void);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Header structure for WAV file with only one data chunk
|
||||
*
|
||||
* @note See this for reference: http://soundfile.sapp.org/doc/WaveFormat/
|
||||
*
|
||||
* @note Assignment to variables in this struct directly is only possible for
|
||||
* little endian architectures (including Xtensa & RISC-V)
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
char chunk_id[4]; /*!< Contains the letters "RIFF" in ASCII form */
|
||||
uint32_t chunk_size; /*!< This is the size of the rest of the chunk
|
||||
following this number */
|
||||
char chunk_format[4]; /*!< Contains the letters "WAVE" */
|
||||
} descriptor_chunk; /*!< Canonical WAVE format starts with the RIFF header */
|
||||
struct {
|
||||
char subchunk_id[4]; /*!< Contains the letters "fmt " */
|
||||
uint32_t subchunk_size; /*!< This is the size of the rest of the Subchunk
|
||||
which follows this number */
|
||||
uint16_t audio_format; /*!< PCM = 1, values other than 1 indicate some form
|
||||
of compression */
|
||||
uint16_t num_of_channels; /*!< Mono = 1, Stereo = 2, etc. */
|
||||
uint32_t sample_rate; /*!< 8000, 44100, etc. */
|
||||
uint32_t byte_rate; /*!< ==SampleRate * NumChannels * BitsPerSample s/ 8 */
|
||||
uint16_t block_align; /*!< ==NumChannels * BitsPerSample / 8 */
|
||||
uint16_t bits_per_sample; /*!< 8 bits = 8, 16 bits = 16, etc. */
|
||||
} fmt_chunk; /*!< The "fmt " subchunk describes the sound data's format */
|
||||
struct {
|
||||
char subchunk_id[4]; /*!< Contains the letters "data" */
|
||||
uint32_t
|
||||
subchunk_size; /*!< ==NumSamples * NumChannels * BitsPerSample / 8 */
|
||||
int16_t data[0]; /*!< Holds raw audio data */
|
||||
} data_chunk; /*!< The "data" subchunk contains the size of the data and the
|
||||
actual sound */
|
||||
} wav_header_t;
|
||||
|
||||
/**
|
||||
* @brief Default header for PCM format WAV files
|
||||
*
|
||||
*/
|
||||
#define WAV_HEADER_PCM_DEFAULT(wav_sample_size, wav_sample_bits, \
|
||||
wav_sample_rate, wav_channel_num) \
|
||||
{ \
|
||||
.descriptor_chunk = {.chunk_id = {'R', 'I', 'F', 'F'}, \
|
||||
.chunk_size = \
|
||||
(wav_sample_size) + sizeof(wav_header_t) - 8, \
|
||||
.chunk_format = {'W', 'A', 'V', 'E'}}, \
|
||||
.fmt_chunk = {.subchunk_id = {'f', 'm', 't', ' '}, \
|
||||
.subchunk_size = 16, /* 16 for PCM */ \
|
||||
.audio_format = 1, /* 1 for PCM */ \
|
||||
.num_of_channels = (wav_channel_num), \
|
||||
.sample_rate = (wav_sample_rate), \
|
||||
.byte_rate = (wav_sample_bits) * (wav_sample_rate) * \
|
||||
(wav_channel_num) / 8, \
|
||||
.block_align = (wav_sample_bits) * (wav_channel_num) / 8, \
|
||||
.bits_per_sample = (wav_sample_bits)}, \
|
||||
.data_chunk = { \
|
||||
.subchunk_id = {'d', 'a', 't', 'a'}, \
|
||||
.subchunk_size = (wav_sample_size) \
|
||||
} \
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
espressif/es7210: "^1.0.0"
|
||||
# espressif/esp32-camera: "^2.0.10" # 摄像头驱动
|
||||
lvgl/lvgl: "~8.3.0"
|
||||
espressif/esp_lvgl_port: "~1.4.0" # LVGL接口
|
||||
|
|
@ -9,3 +10,6 @@ dependencies:
|
|||
espressif/esp_codec_dev: "~1.3.0" # 音频驱动
|
||||
espressif/esp-sr: "~1.6.0" # 语音识别
|
||||
|
||||
idf:
|
||||
version: ">=5.4.0"
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
220
main/main.c
220
main/main.c
|
|
@ -1,8 +1,208 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "app_sr.h"
|
||||
#include "app_ui.h"
|
||||
#include "driver/i2c.h"
|
||||
#include "driver/i2s_std.h"
|
||||
#include "driver/i2s_tdm.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
#include "es7210.h"
|
||||
#include "esp32_s3_szp.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "format_wav.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
|
||||
/* I2C port and GPIOs */
|
||||
#define EXAMPLE_I2C_NUM (0)
|
||||
#define EXAMPLE_I2C_SDA_IO (1)
|
||||
#define EXAMPLE_I2C_SCL_IO (2)
|
||||
|
||||
/* I2S port and GPIOs */
|
||||
#define EXAMPLE_I2S_NUM (0)
|
||||
#define EXAMPLE_I2S_MCK_IO (38)
|
||||
#define EXAMPLE_I2S_BCK_IO (14)
|
||||
#define EXAMPLE_I2S_WS_IO (13)
|
||||
#define EXAMPLE_I2S_DI_IO (12)
|
||||
|
||||
/* SD card GPIOs */
|
||||
#define EXAMPLE_SD_CMD_IO (48)
|
||||
#define EXAMPLE_SD_CLK_IO (47)
|
||||
#define EXAMPLE_SD_DAT0_IO (21)
|
||||
|
||||
/* I2S configurations */
|
||||
#define EXAMPLE_I2S_TDM_FORMAT (ES7210_I2S_FMT_I2S)
|
||||
#define EXAMPLE_I2S_CHAN_NUM (2)
|
||||
#define EXAMPLE_I2S_SAMPLE_RATE (48000)
|
||||
#define EXAMPLE_I2S_MCLK_MULTIPLE (I2S_MCLK_MULTIPLE_256)
|
||||
#define EXAMPLE_I2S_SAMPLE_BITS (I2S_DATA_BIT_WIDTH_16BIT)
|
||||
#define EXAMPLE_I2S_TDM_SLOT_MASK (I2S_TDM_SLOT0 | I2S_TDM_SLOT1)
|
||||
|
||||
/* ES7210 configurations */
|
||||
#define EXAMPLE_ES7210_I2C_ADDR (0x41)
|
||||
#define EXAMPLE_ES7210_I2C_CLK (100000)
|
||||
#define EXAMPLE_ES7210_MIC_GAIN (ES7210_MIC_GAIN_30DB)
|
||||
#define EXAMPLE_ES7210_MIC_BIAS (ES7210_MIC_BIAS_2V87)
|
||||
#define EXAMPLE_ES7210_ADC_VOLUME (0)
|
||||
|
||||
/* SD card & recording configurations */
|
||||
#define EXAMPLE_RECORD_TIME_SEC (10)
|
||||
#define EXAMPLE_SD_MOUNT_POINT "/sdcard"
|
||||
#define EXAMPLE_RECORD_FILE_PATH "/RECORD.WAV"
|
||||
|
||||
static const char* TAG = "example";
|
||||
|
||||
static i2s_chan_handle_t es7210_i2s_init(void) {
|
||||
i2s_chan_handle_t i2s_rx_chan = NULL; // 定义接收通道句柄
|
||||
ESP_LOGI(TAG, "Create I2S receive channel");
|
||||
i2s_chan_config_t i2s_rx_conf = I2S_CHANNEL_DEFAULT_CONFIG(
|
||||
I2S_NUM_AUTO, I2S_ROLE_MASTER); // 配置接收通道
|
||||
ESP_ERROR_CHECK(
|
||||
i2s_new_channel(&i2s_rx_conf, NULL, &i2s_rx_chan)); // 创建i2s通道
|
||||
|
||||
ESP_LOGI(TAG, "Configure I2S receive channel to TDM mode");
|
||||
// 定义接收通道为I2S TDM模式 并配置
|
||||
i2s_tdm_config_t i2s_tdm_rx_conf = {
|
||||
.slot_cfg = I2S_TDM_PHILIPS_SLOT_DEFAULT_CONFIG(
|
||||
EXAMPLE_I2S_SAMPLE_BITS, I2S_SLOT_MODE_STEREO,
|
||||
EXAMPLE_I2S_TDM_SLOT_MASK),
|
||||
.clk_cfg = {.clk_src = I2S_CLK_SRC_DEFAULT,
|
||||
.sample_rate_hz = EXAMPLE_I2S_SAMPLE_RATE,
|
||||
.mclk_multiple = EXAMPLE_I2S_MCLK_MULTIPLE},
|
||||
.gpio_cfg = {.mclk = EXAMPLE_I2S_MCK_IO,
|
||||
.bclk = EXAMPLE_I2S_BCK_IO,
|
||||
.ws = EXAMPLE_I2S_WS_IO,
|
||||
.dout = -1, // ES7210 only has ADC capability
|
||||
.din = EXAMPLE_I2S_DI_IO},
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_init_tdm_mode(
|
||||
i2s_rx_chan, &i2s_tdm_rx_conf)); // 初始化I2S通道为TDM模式
|
||||
|
||||
return i2s_rx_chan;
|
||||
}
|
||||
|
||||
sdmmc_card_t* mount_sdcard(void) {
|
||||
sdmmc_card_t* sdmmc_card = NULL;
|
||||
|
||||
ESP_LOGI(TAG, "Mounting SD card");
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = true,
|
||||
.max_files = 2,
|
||||
.allocation_unit_size = 8 * 1024};
|
||||
|
||||
ESP_LOGI(TAG, "Initializing SD card");
|
||||
ESP_LOGI(TAG, "Using SDMMC peripheral");
|
||||
|
||||
sdmmc_host_t sdmmc_host = SDMMC_HOST_DEFAULT(); // SDMMC主机接口配置
|
||||
sdmmc_slot_config_t slot_config =
|
||||
SDMMC_SLOT_CONFIG_DEFAULT(); // SDMMC插槽配置
|
||||
slot_config.width = 1; // 设置为1线SD模式
|
||||
slot_config.clk = EXAMPLE_SD_CLK_IO;
|
||||
slot_config.cmd = EXAMPLE_SD_CMD_IO;
|
||||
slot_config.d0 = EXAMPLE_SD_DAT0_IO;
|
||||
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // 打开内部上拉电阻
|
||||
|
||||
ESP_LOGI(TAG, "Mounting filesystem");
|
||||
|
||||
esp_err_t ret;
|
||||
while (1) {
|
||||
ret = esp_vfs_fat_sdmmc_mount(EXAMPLE_SD_MOUNT_POINT, &sdmmc_host,
|
||||
&slot_config, &mount_config, &sdmmc_card);
|
||||
if (ret == ESP_OK) {
|
||||
break;
|
||||
} else if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount filesystem.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize the card (%s). ",
|
||||
esp_err_to_name(ret));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
ESP_LOGI(
|
||||
TAG, "Card size: %lluMB, speed: %dMHz",
|
||||
(((uint64_t)sdmmc_card->csd.capacity) * sdmmc_card->csd.sector_size) >>
|
||||
20,
|
||||
sdmmc_card->max_freq_khz / 1000);
|
||||
|
||||
return sdmmc_card;
|
||||
}
|
||||
|
||||
static void es7210_codec_init(void) {
|
||||
// 创建es7210器件句柄
|
||||
es7210_dev_handle_t es7210_handle = NULL;
|
||||
es7210_i2c_config_t es7210_i2c_conf = {.i2c_port = EXAMPLE_I2C_NUM,
|
||||
.i2c_addr = EXAMPLE_ES7210_I2C_ADDR};
|
||||
ESP_ERROR_CHECK(es7210_new_codec(&es7210_i2c_conf, &es7210_handle));
|
||||
|
||||
// 初始化es7210芯片
|
||||
ESP_LOGI(TAG, "Configure ES7210 codec parameters");
|
||||
es7210_codec_config_t codec_conf = {
|
||||
.i2s_format = EXAMPLE_I2S_TDM_FORMAT,
|
||||
.mclk_ratio = EXAMPLE_I2S_MCLK_MULTIPLE,
|
||||
.sample_rate_hz = EXAMPLE_I2S_SAMPLE_RATE,
|
||||
.bit_width = (es7210_i2s_bits_t)EXAMPLE_I2S_SAMPLE_BITS,
|
||||
.mic_bias = EXAMPLE_ES7210_MIC_BIAS,
|
||||
.mic_gain = EXAMPLE_ES7210_MIC_GAIN,
|
||||
.flags.tdm_enable = true};
|
||||
ESP_ERROR_CHECK(es7210_config_codec(es7210_handle, &codec_conf));
|
||||
ESP_ERROR_CHECK(
|
||||
es7210_config_volume(es7210_handle, EXAMPLE_ES7210_ADC_VOLUME));
|
||||
}
|
||||
|
||||
static esp_err_t record_wav(i2s_chan_handle_t i2s_rx_chan) {
|
||||
ESP_RETURN_ON_FALSE(i2s_rx_chan, ESP_FAIL, TAG,
|
||||
"invalid i2s channel handle pointer");
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
uint32_t byte_rate = EXAMPLE_I2S_SAMPLE_RATE * EXAMPLE_I2S_CHAN_NUM *
|
||||
EXAMPLE_I2S_SAMPLE_BITS / 8;
|
||||
uint32_t wav_size = byte_rate * EXAMPLE_RECORD_TIME_SEC;
|
||||
|
||||
const wav_header_t wav_header =
|
||||
WAV_HEADER_PCM_DEFAULT(wav_size, EXAMPLE_I2S_SAMPLE_BITS,
|
||||
EXAMPLE_I2S_SAMPLE_RATE, EXAMPLE_I2S_CHAN_NUM);
|
||||
|
||||
ESP_LOGI(TAG, "Opening file %s", EXAMPLE_RECORD_FILE_PATH);
|
||||
FILE* f = fopen(EXAMPLE_SD_MOUNT_POINT EXAMPLE_RECORD_FILE_PATH, "w");
|
||||
ESP_RETURN_ON_FALSE(f, ESP_FAIL, TAG, "error while opening wav file");
|
||||
|
||||
/* Write wav header */
|
||||
ESP_GOTO_ON_FALSE(fwrite(&wav_header, sizeof(wav_header_t), 1, f), ESP_FAIL,
|
||||
err, TAG, "error while writing wav header");
|
||||
|
||||
/* Start recording */
|
||||
size_t wav_written = 0;
|
||||
static int16_t i2s_readraw_buff[4096];
|
||||
ESP_GOTO_ON_ERROR(i2s_channel_enable(i2s_rx_chan), err, TAG,
|
||||
"error while starting i2s rx channel");
|
||||
while (wav_written < wav_size) {
|
||||
if (wav_written % byte_rate < sizeof(i2s_readraw_buff)) {
|
||||
ESP_LOGI(TAG, "Recording: %" PRIu32 "/%ds", wav_written / byte_rate + 1,
|
||||
EXAMPLE_RECORD_TIME_SEC);
|
||||
}
|
||||
size_t bytes_read = 0;
|
||||
/* Read RAW samples from ES7210 */
|
||||
ESP_GOTO_ON_ERROR(i2s_channel_read(i2s_rx_chan, i2s_readraw_buff,
|
||||
sizeof(i2s_readraw_buff), &bytes_read,
|
||||
pdMS_TO_TICKS(1000)),
|
||||
err, TAG, "error while reading samples from i2s");
|
||||
/* Write the samples to the WAV file */
|
||||
ESP_GOTO_ON_FALSE(fwrite(i2s_readraw_buff, bytes_read, 1, f), ESP_FAIL, err,
|
||||
TAG, "error while writing samples to wav file");
|
||||
wav_written += bytes_read;
|
||||
}
|
||||
|
||||
err:
|
||||
i2s_channel_disable(i2s_rx_chan);
|
||||
ESP_LOGI(TAG, "Recording done! Flushing file buffer");
|
||||
fclose(f);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void app_main(void) {
|
||||
bsp_i2c_init(); // I2C初始化
|
||||
|
|
@ -14,4 +214,24 @@ void app_main(void) {
|
|||
mp3_player_init(); // MP3播放器初始化
|
||||
|
||||
app_sr_init(); // 语音识别初始化
|
||||
|
||||
/* 初始化I2S接口 */
|
||||
i2s_chan_handle_t i2s_rx_chan = es7210_i2s_init();
|
||||
/* 初始化es7210芯片 */
|
||||
es7210_codec_init();
|
||||
|
||||
/* 挂载SD卡 */
|
||||
sdmmc_card_t* sdmmc_card = mount_sdcard();
|
||||
/* 录音 */
|
||||
esp_err_t err = record_wav(i2s_rx_chan);
|
||||
/* 弹出SD卡 */
|
||||
esp_vfs_fat_sdcard_unmount(EXAMPLE_SD_MOUNT_POINT, sdmmc_card);
|
||||
if (err == ESP_OK) {
|
||||
ESP_LOGI(TAG,
|
||||
"Audio was successfully recorded into " EXAMPLE_RECORD_FILE_PATH
|
||||
". You can now remove the SD card safely");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Record failed, " EXAMPLE_RECORD_FILE_PATH
|
||||
" on SD card may not be playable.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue