You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
346 lines
10 KiB
C
346 lines
10 KiB
C
/*
|
|
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
|
*
|
|
* SPDX-License-Identifier: CC0-1.0
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/queue.h"
|
|
#include "freertos/event_groups.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_task_wdt.h"
|
|
#include "esp_check.h"
|
|
#include "esp_err.h"
|
|
#include "esp_log.h"
|
|
#include "esp_check.h"
|
|
#include "esp_timer.h"
|
|
#include "esp_spiffs.h"
|
|
#include "esp_vfs.h"
|
|
#include "app_sr.h"
|
|
#include "app_audio.h"
|
|
#include "bsp_board.h"
|
|
#include "bsp/esp-bsp.h"
|
|
#include "audio_player.h"
|
|
#include "file_iterator.h"
|
|
#include "app_ui_ctrl.h"
|
|
#include "app_wifi.h"
|
|
|
|
static const char *TAG = "app_audio";
|
|
|
|
#if !CONFIG_BSP_BOARD_ESP32_S3_BOX_Lite
|
|
static bool mute_flag = true;
|
|
#endif
|
|
bool record_flag = false;
|
|
uint32_t record_total_len = 0;
|
|
uint32_t file_total_len = 0;
|
|
static uint8_t *record_audio_buffer = NULL;
|
|
uint8_t *audio_rx_buffer = NULL;
|
|
audio_play_finish_cb_t audio_play_finish_cb = NULL;
|
|
|
|
extern sr_data_t *g_sr_data;
|
|
extern esp_err_t start_openai(uint8_t *audio, int audio_len);
|
|
extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size);
|
|
|
|
/* main function */
|
|
void mute_btn_handler(void *handle, void *arg)
|
|
{
|
|
#if !CONFIG_BSP_BOARD_ESP32_S3_BOX_Lite
|
|
button_event_t event = (button_event_t)arg;
|
|
|
|
if (BUTTON_PRESS_DOWN == event) {
|
|
esp_rom_printf(DRAM_STR("Audio Mute On\r\n"));
|
|
mute_flag = true;
|
|
} else {
|
|
esp_rom_printf(DRAM_STR("Audio Mute Off\r\n"));
|
|
mute_flag = false;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static esp_err_t audio_mute_function(AUDIO_PLAYER_MUTE_SETTING setting)
|
|
{
|
|
bsp_codec_mute_set(setting == AUDIO_PLAYER_MUTE ? true : false);
|
|
// restore the voice volume upon unmuting
|
|
if (setting == AUDIO_PLAYER_UNMUTE) {
|
|
bsp_codec_volume_set(CONFIG_VOLUME_LEVEL, NULL);
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
static esp_err_t audio_codec_set_fs(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch)
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
ret = bsp_codec_set_fs(rate, bits_cfg, ch);
|
|
|
|
bsp_codec_mute_set(true);
|
|
bsp_codec_mute_set(false);
|
|
bsp_codec_volume_set(CONFIG_VOLUME_LEVEL, NULL);
|
|
vTaskDelay(pdMS_TO_TICKS(50));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void audio_player_cb(audio_player_cb_ctx_t *ctx)
|
|
{
|
|
switch (ctx->audio_event) {
|
|
case AUDIO_PLAYER_CALLBACK_EVENT_IDLE:
|
|
ESP_LOGI(TAG, "Player IDLE");
|
|
bsp_codec_set_fs(16000, 16, 2);
|
|
if (audio_play_finish_cb) {
|
|
audio_play_finish_cb();
|
|
}
|
|
break;
|
|
case AUDIO_PLAYER_CALLBACK_EVENT_COMPLETED_PLAYING_NEXT:
|
|
ESP_LOGI(TAG, "Player NEXT");
|
|
break;
|
|
case AUDIO_PLAYER_CALLBACK_EVENT_PLAYING:
|
|
ESP_LOGI(TAG, "Player PLAYING");
|
|
break;
|
|
case AUDIO_PLAYER_CALLBACK_EVENT_PAUSE:
|
|
ESP_LOGI(TAG, "Player PAUSE");
|
|
break;
|
|
case AUDIO_PLAYER_CALLBACK_EVENT_SHUTDOWN:
|
|
ESP_LOGI(TAG, "Player SHUTDOWN");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void audio_record_init()
|
|
{
|
|
/* Create file if record to SD card enabled*/
|
|
#if DEBUG_SAVE_PCM
|
|
record_audio_buffer = heap_caps_calloc(1, FILE_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
|
assert(record_audio_buffer);
|
|
printf("successfully created record_audio_buffer with a size: %zu\n", FILE_SIZE);
|
|
audio_rx_buffer = heap_caps_calloc(1, MAX_FILE_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
|
assert(audio_rx_buffer);
|
|
printf("audio_rx_buffer with a size: %zu\n", MAX_FILE_SIZE);
|
|
#endif
|
|
|
|
if (record_audio_buffer == NULL || audio_rx_buffer == NULL) {
|
|
printf("Error: Failed to allocate memory for buffers\n");
|
|
return; // Return or handle the error condition appropriately
|
|
}
|
|
|
|
file_iterator_instance_t *file_iterator = file_iterator_new(BSP_SPIFFS_MOUNT_POINT);
|
|
assert(file_iterator != NULL);
|
|
|
|
audio_player_config_t config = { .mute_fn = audio_mute_function,
|
|
.write_fn = bsp_i2s_write,
|
|
.clk_set_fn = audio_codec_set_fs,
|
|
.priority = 5
|
|
};
|
|
ESP_ERROR_CHECK(audio_player_new(config));
|
|
audio_player_callback_register(audio_player_cb, NULL);
|
|
}
|
|
|
|
void audio_record_save(int16_t *audio_buffer, int audio_chunksize)
|
|
{
|
|
#if DEBUG_SAVE_PCM
|
|
if (record_flag) {
|
|
uint16_t *record_buff = (uint16_t *)(record_audio_buffer + sizeof(wav_header_t));
|
|
record_buff += record_total_len;
|
|
for (int i = 0; i < (audio_chunksize - 1); i++) {
|
|
if (record_total_len < (MAX_FILE_SIZE - sizeof(wav_header_t)) / 2) {
|
|
#if PCM_ONE_CHANNEL
|
|
record_buff[ i * 1 + 0] = audio_buffer[i * 3 + 0];
|
|
record_total_len += 1;
|
|
#else
|
|
record_buff[ i * 2 + 0] = audio_buffer[i * 3 + 0];
|
|
record_buff[ i * 2 + 1] = audio_buffer[i * 3 + 1];
|
|
record_total_len += 2;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void audio_register_play_finish_cb(audio_play_finish_cb_t cb)
|
|
{
|
|
audio_play_finish_cb = cb;
|
|
}
|
|
|
|
static void audio_record_start()
|
|
{
|
|
#if DEBUG_SAVE_PCM
|
|
ESP_LOGI(TAG, "### record Start");
|
|
audio_player_stop();
|
|
|
|
record_flag = true;
|
|
record_total_len = 0;
|
|
file_total_len = sizeof(wav_header_t);
|
|
#endif
|
|
}
|
|
|
|
static esp_err_t audio_record_stop()
|
|
{
|
|
esp_err_t ret = ESP_OK;
|
|
#if DEBUG_SAVE_PCM
|
|
record_flag = false;
|
|
#if PCM_ONE_CHANNEL
|
|
record_total_len *= 1;
|
|
#else
|
|
record_total_len *= 2;
|
|
#endif
|
|
file_total_len += record_total_len;
|
|
ESP_LOGI(TAG, "### record Stop, %" PRIu32 " %" PRIu32 "K", \
|
|
record_total_len, \
|
|
record_total_len / 1024);
|
|
|
|
FILE *fp = fopen("/spiffs/echo_en_wake.wav", "r");
|
|
ESP_GOTO_ON_FALSE(NULL != fp, ESP_FAIL, err, TAG, "Failed create record file");
|
|
|
|
wav_header_t wav_head;
|
|
int len = fread(&wav_head, 1, sizeof(wav_header_t), fp);
|
|
ESP_GOTO_ON_FALSE(len > 0, ESP_FAIL, err, TAG, "Failed create record file");
|
|
|
|
wav_head.SampleRate = 16000;
|
|
#if PCM_ONE_CHANNEL
|
|
wav_head.NumChannels = 1;
|
|
#else
|
|
wav_head.NumChannels = 2;
|
|
#endif
|
|
wav_head.BitsPerSample = 16;
|
|
wav_head.ChunkSize = file_total_len - 8;
|
|
wav_head.ByteRate = wav_head.SampleRate * wav_head.BitsPerSample * wav_head.NumChannels / 8;
|
|
wav_head.Subchunk2ID[0] = 'd';
|
|
wav_head.Subchunk2ID[1] = 'a';
|
|
wav_head.Subchunk2ID[2] = 't';
|
|
wav_head.Subchunk2ID[3] = 'a';
|
|
wav_head.Subchunk2Size = record_total_len;
|
|
memcpy((void *)record_audio_buffer, &wav_head, sizeof(wav_header_t));
|
|
Cache_WriteBack_Addr((uint32_t)record_audio_buffer, record_total_len);
|
|
|
|
#endif
|
|
err:
|
|
if (fp) {
|
|
fclose(fp);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
esp_err_t audio_play_task(void *filepath)
|
|
{
|
|
FILE *fp = NULL;
|
|
struct stat file_stat;
|
|
esp_err_t ret = ESP_OK;
|
|
|
|
const size_t chunk_size = 4096;
|
|
uint8_t *buffer = malloc(chunk_size);
|
|
ESP_GOTO_ON_FALSE(NULL != buffer, ESP_FAIL, EXIT, TAG, "buffer malloc failed");
|
|
|
|
ESP_GOTO_ON_FALSE(-1 != stat(filepath, &file_stat), ESP_FAIL, EXIT, TAG, "Failed to stat file");
|
|
|
|
fp = fopen(filepath, "r");
|
|
ESP_GOTO_ON_FALSE(NULL != fp, ESP_FAIL, EXIT, TAG, "Failed create record file");
|
|
|
|
wav_header_t wav_head;
|
|
int len = fread(&wav_head, 1, sizeof(wav_header_t), fp);
|
|
ESP_GOTO_ON_FALSE(len > 0, ESP_FAIL, EXIT, TAG, "Read wav header failed");
|
|
|
|
if (NULL == strstr((char *)wav_head.Subchunk1ID, "fmt") &&
|
|
NULL == strstr((char *)wav_head.Subchunk2ID, "data")) {
|
|
ESP_LOGI(TAG, "PCM format");
|
|
fseek(fp, 0, SEEK_SET);
|
|
wav_head.SampleRate = 16000;
|
|
wav_head.NumChannels = 2;
|
|
wav_head.BitsPerSample = 16;
|
|
}
|
|
|
|
ESP_LOGI(TAG, "frame_rate= %" PRIi32 ", ch=%d, width=%d", wav_head.SampleRate, wav_head.NumChannels, wav_head.BitsPerSample);
|
|
bsp_codec_set_fs(wav_head.SampleRate, wav_head.BitsPerSample, I2S_SLOT_MODE_STEREO);
|
|
|
|
bsp_codec_mute_set(true);
|
|
bsp_codec_mute_set(false);
|
|
bsp_codec_volume_set(CONFIG_VOLUME_LEVEL, NULL);
|
|
|
|
size_t cnt, total_cnt = 0;
|
|
do {
|
|
/* Read file in chunks into the scratch buffer */
|
|
len = fread(buffer, 1, chunk_size, fp);
|
|
if (len <= 0) {
|
|
break;
|
|
} else if (len > 0) {
|
|
bsp_i2s_write(buffer, len, &cnt, portMAX_DELAY);
|
|
total_cnt += cnt;
|
|
}
|
|
} while (1);
|
|
|
|
EXIT:
|
|
if (fp) {
|
|
fclose(fp);
|
|
}
|
|
if (buffer) {
|
|
free(buffer);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void sr_handler_task(void *pvParam)
|
|
{
|
|
#if !CONFIG_BSP_BOARD_ESP32_S3_BOX_Lite
|
|
static bool mute_state = false;
|
|
mute_flag = gpio_get_level(BSP_BUTTON_MUTE_IO);
|
|
printf("sr handle task, mute:%d\n", mute_flag);
|
|
#endif
|
|
|
|
while (true) {
|
|
if (NEED_DELETE && xEventGroupGetBits(g_sr_data->event_group)) {
|
|
xEventGroupSetBits(g_sr_data->event_group, HANDLE_DELETED);
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
sr_result_t result = {
|
|
.wakenet_mode = WAKENET_NO_DETECT,
|
|
.state = ESP_MN_STATE_DETECTING,
|
|
};
|
|
|
|
app_sr_get_result(&result, pdMS_TO_TICKS(1 * 1000));
|
|
|
|
#if !CONFIG_BSP_BOARD_ESP32_S3_BOX_Lite
|
|
if (mute_state != mute_flag) {
|
|
mute_state = mute_flag;
|
|
if (false == mute_state) {
|
|
bsp_codec_set_fs(16000, 16, 2);
|
|
}
|
|
}
|
|
#endif
|
|
if (ESP_MN_STATE_TIMEOUT == result.state) {
|
|
ESP_LOGI(TAG, "ESP_MN_STATE_TIMEOUT");
|
|
audio_record_stop();
|
|
FILE *fp = fopen("/spiffs/waitPlease.mp3", "r");
|
|
if (fp) {
|
|
audio_player_play(fp);
|
|
}
|
|
if (WIFI_STATUS_CONNECTED_OK == wifi_connected_already()) {
|
|
start_openai((uint8_t *)record_audio_buffer, record_total_len);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (WAKENET_DETECTED == result.wakenet_mode) {
|
|
audio_record_start();
|
|
|
|
// UI show listen
|
|
ui_ctrl_guide_jump();
|
|
ui_ctrl_show_panel(UI_CTRL_PANEL_LISTEN, 0);
|
|
|
|
audio_play_task("/spiffs/echo_en_wake.wav");
|
|
continue;
|
|
}
|
|
|
|
if (ESP_MN_STATE_DETECTED & result.state) {
|
|
ESP_LOGI(TAG, "STOP:%d", result.command_id);
|
|
audio_record_stop();
|
|
audio_play_task("/spiffs/echo_en_ok.wav");
|
|
//How to stop the transmission, when start_openai begins.
|
|
continue;
|
|
}
|
|
}
|
|
vTaskDelete(NULL);
|
|
}
|