231 lines
8.4 KiB
C
231 lines
8.4 KiB
C
/*
|
|
* Espressif Modified MIT License
|
|
*
|
|
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., LTD
|
|
*
|
|
* Permission is hereby granted for use **exclusively** with Espressif Systems products.
|
|
* This includes the right to use, copy, modify, merge, publish, distribute, and sublicense
|
|
* the Software, subject to the following conditions:
|
|
*
|
|
* 1. This Software **must be used in conjunction with Espressif Systems products**.
|
|
* 2. The above copyright notice and this permission notice shall be included in all copies
|
|
* or substantial portions of the Software.
|
|
* 3. Redistribution of the Software in source or binary form **for use with non-Espressif products**
|
|
* is strictly prohibited.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
|
* FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* SPDX-License-Identifier: MIT-ESPRESSIF
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "esp_log.h"
|
|
|
|
#include "audio_pipeline.h"
|
|
#include "raw_stream.h"
|
|
#include "filter_resample.h"
|
|
#include "algorithm_stream.h"
|
|
#include "raw_stream.h"
|
|
#include "i2s_stream.h"
|
|
#include "raw_opus_decoder.h"
|
|
#include "audio_mem.h"
|
|
#include "board.h"
|
|
|
|
#include "audio_processor.h"
|
|
|
|
static char *TAG = "audio_processor";
|
|
|
|
struct audio_recorder_s {
|
|
audio_element_handle_t i2s_reader;
|
|
audio_element_handle_t raw_stream;
|
|
audio_element_handle_t algo_stream;
|
|
audio_pipeline_handle_t pipeline;
|
|
};
|
|
|
|
struct audio_player_s {
|
|
audio_element_handle_t i2s_writer;
|
|
audio_element_handle_t raw_stream;
|
|
audio_element_handle_t filter;
|
|
audio_element_handle_t opus_decoder_stream;
|
|
audio_pipeline_handle_t pipeline;
|
|
};
|
|
|
|
static int algo_read_data_callback(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context)
|
|
{
|
|
audio_element_handle_t i2s_reader = (audio_element_handle_t)context;
|
|
return audio_element_input(i2s_reader, buffer, len);
|
|
}
|
|
|
|
audio_recorder_handle_t recorder_pipeline_open()
|
|
{
|
|
struct audio_recorder_s *recorder = audio_calloc(1, sizeof(struct audio_recorder_s));
|
|
if (recorder == NULL) {
|
|
ESP_LOGE(TAG, "No mem for recorder");
|
|
return NULL;
|
|
}
|
|
audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
|
|
recorder->pipeline = audio_pipeline_init(&pipeline_cfg);
|
|
assert(recorder->pipeline);
|
|
|
|
#if CONFIG_ESP32_S3_KORVO2_V3_BOARD || CONFIG_ESP32_S3_BOX_BOARD
|
|
i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(0, 16000, I2S_DATA_BIT_WIDTH_32BIT, AUDIO_STREAM_READER);
|
|
i2s_stream_set_channel_type(&i2s_cfg, I2S_CHANNEL_TYPE_ONLY_LEFT);
|
|
#else
|
|
i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(0, 16000, I2S_DATA_BIT_WIDTH_16BIT, AUDIO_STREAM_READER);
|
|
#endif
|
|
i2s_cfg.task_stack = -1;
|
|
recorder->i2s_reader = i2s_stream_init(&i2s_cfg);
|
|
assert(recorder->i2s_reader);
|
|
|
|
algorithm_stream_cfg_t algo_config = ALGORITHM_STREAM_CFG_DEFAULT();
|
|
algo_config.sample_rate = 16000;
|
|
algo_config.out_rb_size = 26 * 1024;
|
|
algo_config.task_core = 1;
|
|
#if CONFIG_ESP32_S3_KORVO2_V3_BOARD || CONFIG_ESP32_S3_BOX_BOARD
|
|
algo_config.input_format = "RM";
|
|
#else
|
|
algo_config.input_format = "MR";
|
|
#endif
|
|
recorder->algo_stream = algo_stream_init(&algo_config);
|
|
assert(recorder->algo_stream);
|
|
audio_element_set_music_info(recorder->algo_stream, 16000, 1, 16);
|
|
audio_element_set_read_cb(recorder->algo_stream, algo_read_data_callback, (void *)recorder->i2s_reader);
|
|
audio_element_set_input_timeout(recorder->algo_stream, portMAX_DELAY);
|
|
|
|
raw_stream_cfg_t raw_cfg = RAW_STREAM_CFG_DEFAULT();
|
|
recorder->raw_stream = raw_stream_init(&raw_cfg);
|
|
assert(recorder->raw_stream);
|
|
|
|
audio_pipeline_register(recorder->pipeline, recorder->algo_stream, "algo_stream");
|
|
audio_pipeline_register(recorder->pipeline, recorder->raw_stream, "raw_read");
|
|
|
|
const char *link_tag2[2] = {"algo_stream", "raw_read"};
|
|
audio_pipeline_link(recorder->pipeline, &link_tag2[0], 2);
|
|
|
|
return (audio_recorder_handle_t)recorder;
|
|
}
|
|
|
|
esp_err_t recorder_pipeline_run(audio_recorder_handle_t recorder)
|
|
{
|
|
return audio_pipeline_run(recorder->pipeline);
|
|
}
|
|
|
|
esp_err_t recorder_pipeline_read(audio_recorder_handle_t recorder, char *buffer, int len)
|
|
{
|
|
return raw_stream_read(recorder->raw_stream, buffer, len);
|
|
}
|
|
|
|
esp_err_t recorder_pipeline_stop(audio_recorder_handle_t recorder)
|
|
{
|
|
audio_pipeline_stop(recorder->pipeline);
|
|
audio_pipeline_wait_for_stop(recorder->pipeline);
|
|
audio_pipeline_reset_elements(recorder->pipeline);
|
|
audio_pipeline_reset_ringbuffer(recorder->pipeline);
|
|
audio_pipeline_reset_items_state(recorder->pipeline);
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t recorder_pipeline_close(audio_recorder_handle_t recorder)
|
|
{
|
|
audio_pipeline_terminate(recorder->pipeline);
|
|
audio_pipeline_deinit(recorder->pipeline);
|
|
return ESP_OK;
|
|
}
|
|
|
|
static int opus_audio_data_callback(audio_element_handle_t self, char *buffer, int len, TickType_t ticks_to_wait, void *context)
|
|
{
|
|
audio_element_handle_t i2s_writer = (audio_element_handle_t)context;
|
|
return audio_element_output(i2s_writer, buffer, len);
|
|
}
|
|
|
|
audio_player_handle_t player_pipeline_open()
|
|
{
|
|
struct audio_player_s *player = audio_calloc(1, sizeof(struct audio_player_s));
|
|
if (player == NULL) {
|
|
ESP_LOGE(TAG, "No mem for player");
|
|
return NULL;
|
|
}
|
|
audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
|
|
player->pipeline = audio_pipeline_init(&pipeline_cfg);
|
|
assert(player->pipeline);
|
|
#if CONFIG_ESP32_S3_KORVO2_V3_BOARD || CONFIG_ESP32_S3_BOX_BOARD
|
|
i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(0, 16000, I2S_DATA_BIT_WIDTH_32BIT, AUDIO_STREAM_WRITER);
|
|
i2s_stream_set_channel_type(&i2s_cfg, I2S_CHANNEL_TYPE_ONLY_LEFT);
|
|
i2s_cfg.need_expand = true;
|
|
#else
|
|
i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(0, 16000, I2S_DATA_BIT_WIDTH_16BIT, AUDIO_STREAM_WRITER);
|
|
#endif
|
|
i2s_cfg.task_stack = -1;
|
|
player->i2s_writer = i2s_stream_init(&i2s_cfg);
|
|
assert(player->i2s_writer);
|
|
|
|
raw_stream_cfg_t raw_cfg = RAW_STREAM_CFG_DEFAULT();
|
|
player->raw_stream = raw_stream_init(&raw_cfg);
|
|
assert(player->raw_stream);
|
|
|
|
raw_opus_dec_cfg_t opus_dec_cfg = RAW_OPUS_DEC_CONFIG_DEFAULT();
|
|
opus_dec_cfg.enable_frame_length_prefix = true;
|
|
opus_dec_cfg.sample_rate = 16000;
|
|
opus_dec_cfg.channels = 1;
|
|
opus_dec_cfg.task_core = 1;
|
|
player->opus_decoder_stream = raw_opus_decoder_init(&opus_dec_cfg);
|
|
assert(player->opus_decoder_stream);
|
|
|
|
rsp_filter_cfg_t filter_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
|
|
filter_cfg.src_ch = 1;
|
|
filter_cfg.src_rate = 16000;
|
|
#if CONFIG_ESP32_S3_KORVO2_V3_BOARD || CONFIG_ESP32_S3_BOX_BOARD
|
|
filter_cfg.dest_ch = 1;
|
|
#else
|
|
filter_cfg.dest_ch = 2;
|
|
#endif
|
|
filter_cfg.dest_rate = 16000;
|
|
filter_cfg.stack_in_ext = true;
|
|
filter_cfg.task_core = 1;
|
|
filter_cfg.complexity = 2;
|
|
player->filter = rsp_filter_init(&filter_cfg);
|
|
assert(player->filter);
|
|
audio_element_set_write_cb(player->filter, opus_audio_data_callback, (void *)player->i2s_writer);
|
|
|
|
audio_pipeline_register(player->pipeline, player->raw_stream, "raw_stream");
|
|
audio_pipeline_register(player->pipeline, player->opus_decoder_stream, "raw_opus");
|
|
audio_pipeline_register(player->pipeline, player->filter, "filter");
|
|
|
|
const char *link_tag[3] = {"raw_stream", "raw_opus", "filter"};
|
|
audio_pipeline_link(player->pipeline, &link_tag[0], 3);
|
|
return (audio_player_handle_t)player;
|
|
}
|
|
|
|
esp_err_t player_pipeline_run(audio_player_handle_t player)
|
|
{
|
|
return audio_pipeline_run(player->pipeline);
|
|
}
|
|
|
|
esp_err_t player_pipeline_stop(audio_player_handle_t player)
|
|
{
|
|
audio_pipeline_stop(player->pipeline);
|
|
audio_pipeline_wait_for_stop(player->pipeline);
|
|
audio_pipeline_reset_elements(player->pipeline);
|
|
audio_pipeline_reset_ringbuffer(player->pipeline);
|
|
audio_pipeline_reset_items_state(player->pipeline);
|
|
return ESP_OK;
|
|
}
|
|
|
|
esp_err_t player_pipeline_write(audio_player_handle_t player, char *buffer, int len)
|
|
{
|
|
return raw_stream_write(player->raw_stream, buffer, len);
|
|
}
|
|
|
|
esp_err_t player_pipeline_close(audio_player_handle_t player)
|
|
{
|
|
audio_pipeline_terminate(player->pipeline);
|
|
audio_pipeline_deinit(player->pipeline);
|
|
return ESP_OK;
|
|
}
|