1. 添加了esp-idf的几个组件,基于main/idf_component.yml的配置,用于应用层快速开发

2. 测试了音频播放功能,测试通过
This commit is contained in:
Misaki
2025-08-24 23:23:25 +08:00
parent fa066408e2
commit ec33aa0483
14 changed files with 984 additions and 33 deletions
+203
View File
@@ -0,0 +1,203 @@
#include "PCM5101.h"
static const char *TAG = "AUDIO PCM5101";
static i2s_chan_handle_t i2s_tx_chan;
static i2s_chan_handle_t i2s_rx_chan;
uint8_t Volume = Volume_MAX - 2;
bool Music_Next_Flag = 0;
// static esp_err_t bsp_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms) { // I2S Write Init
// return i2s_channel_write(i2s_tx_chan, (char *)audio_buffer, len, bytes_written, timeout_ms);
// }
static esp_err_t bsp_i2s_write(void *audio_buffer, size_t len, size_t *bytes_written, uint32_t timeout_ms) {
int16_t *samples = (int16_t *)audio_buffer;
size_t sample_count = len / sizeof(int16_t);
// Calculate the volume scaling factor to convert the volume level from 0-100 to the 0.0-1.0 range
float volume_factor = Volume / 100.0f;
for (size_t i = 0; i < sample_count; i++) {
samples[i] = (int16_t)(samples[i] * volume_factor);
}
return i2s_channel_write(i2s_tx_chan, (char *)audio_buffer, len, bytes_written, timeout_ms);
}
static esp_err_t bsp_i2s_reconfig_clk(uint32_t rate, uint32_t bits_cfg, i2s_slot_mode_t ch) { // I2S Init
esp_err_t ret = ESP_OK;
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(rate),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG((i2s_data_bit_width_t)bits_cfg, ch),
.gpio_cfg = BSP_I2S_GPIO_CFG,
};
ret |= i2s_channel_disable(i2s_tx_chan);
ret |= i2s_channel_reconfig_std_clock(i2s_tx_chan, &std_cfg.clk_cfg);
ret |= i2s_channel_reconfig_std_slot(i2s_tx_chan, &std_cfg.slot_cfg);
ret |= i2s_channel_enable(i2s_tx_chan);
return ret;
}
static esp_err_t audio_mute_function(AUDIO_PLAYER_MUTE_SETTING setting) { // audio mute function
ESP_LOGI(TAG, "mute setting %d", setting);
return ESP_OK;
}
static esp_err_t bsp_audio_init(const i2s_std_config_t *i2s_config, i2s_chan_handle_t *tx_channel, i2s_chan_handle_t *rx_channel) { // Audio Init
i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(CONFIG_BSP_I2S_NUM, I2S_ROLE_MASTER);
chan_cfg.auto_clear = true;
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, tx_channel, rx_channel));
const i2s_std_config_t std_cfg_default = BSP_I2S_DUPLEX_MONO_CFG(22050);
const i2s_std_config_t *p_i2s_cfg = (i2s_config != NULL) ? i2s_config : &std_cfg_default;
if (tx_channel) {
ESP_ERROR_CHECK(i2s_channel_init_std_mode(*tx_channel, p_i2s_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(*tx_channel));
}
if (rx_channel) {
ESP_ERROR_CHECK(i2s_channel_init_std_mode(*rx_channel, p_i2s_cfg));
ESP_ERROR_CHECK(i2s_channel_enable(*rx_channel));
}
return ESP_OK;
}
static FILE * Music_File = NULL;
static audio_player_callback_event_t expected_event;
static QueueHandle_t event_queue;
static audio_player_callback_event_t event;
static void audio_player_callback(audio_player_cb_ctx_t *ctx) {
if (ctx->audio_event == AUDIO_PLAYER_CALLBACK_EVENT_IDLE) {
ESP_LOGI(TAG, "Playback finished");
Music_Next_Flag = 1;
}
if (ctx->audio_event == expected_event) {
xQueueSend(event_queue, &(ctx->audio_event), 0);
}
}
void Audio_Init(void)
{
i2s_std_config_t std_cfg = {
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(44100),
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
.gpio_cfg = BSP_I2S_GPIO_CFG,
};
esp_err_t ret = bsp_audio_init(&std_cfg, &i2s_tx_chan, &i2s_rx_chan);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize audio: %s", esp_err_to_name(ret));
return;
}
audio_player_config_t config = {
.mute_fn = audio_mute_function,
.write_fn = bsp_i2s_write,
.clk_set_fn = bsp_i2s_reconfig_clk,
.priority = 3,
.coreID = 1
};
ret = audio_player_new(config);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to create audio player: %s", esp_err_to_name(ret));
return;
}
event_queue = xQueueCreate(1, sizeof(audio_player_callback_event_t));
if (!event_queue) {
ESP_LOGE(TAG, "Failed to create event queue");
return;
}
ret = audio_player_callback_register(audio_player_callback, NULL);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to register callback: %s", esp_err_to_name(ret));
return;
}
if (audio_player_get_state() != AUDIO_PLAYER_STATE_IDLE) {
ESP_LOGE(TAG, "Expected state to be IDLE"); // The player is not idle
return;
}
}
void Play_Music(const char* directory, const char* fileName)
{
Music_pause();
const int maxPathLength = 100;
char filePath[maxPathLength];
if (strcmp(directory, "/") == 0) {
snprintf(filePath, maxPathLength, "%s%s", directory, fileName);
} else {
snprintf(filePath, maxPathLength, "%s/%s", directory, fileName);
}
Music_File = Open_File(filePath);
if (!Music_File) {
ESP_LOGE(TAG, "Failed to open MP3 file: %s", filePath);
return;
}
expected_event = AUDIO_PLAYER_CALLBACK_EVENT_PLAYING;
esp_err_t ret = audio_player_play(Music_File);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to play audio: %s", esp_err_to_name(ret));
fclose(Music_File);
return;
}
if (xQueueReceive(event_queue, &event, pdMS_TO_TICKS(100)) != pdPASS) {
ESP_LOGE(TAG, "Failed to receive playing event");
fclose(Music_File);
return;
}
if (audio_player_get_state() != AUDIO_PLAYER_STATE_PLAYING) {
ESP_LOGE(TAG, "Expected state to be PLAYING");
fclose(Music_File);
return;
}
}
void Music_resume(void)
{
if (audio_player_get_state() != AUDIO_PLAYER_STATE_PLAYING){
expected_event = AUDIO_PLAYER_CALLBACK_EVENT_PLAYING;
esp_err_t ret = audio_player_resume();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to resume audio: %s", esp_err_to_name(ret));
fclose(Music_File);
return;
}
if (xQueueReceive(event_queue, &event, pdMS_TO_TICKS(100)) != pdPASS) {
ESP_LOGE(TAG, "Failed to receive playing event after resume");
fclose(Music_File);
return;
}
if (audio_player_get_state() != AUDIO_PLAYER_STATE_PLAYING) {
ESP_LOGE(TAG, "Expected state to be RESUME");
fclose(Music_File);
return;
}
}
}
void Music_pause(void)
{
if (audio_player_get_state() == AUDIO_PLAYER_STATE_PLAYING){
expected_event = AUDIO_PLAYER_CALLBACK_EVENT_PAUSE;
esp_err_t ret = audio_player_pause();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to pause audio: %s", esp_err_to_name(ret));
fclose(Music_File);
return;
}
if (xQueueReceive(event_queue, &event, pdMS_TO_TICKS(100)) != pdPASS) {
ESP_LOGE(TAG, "Failed to receive pause event");
fclose(Music_File);
return;
}
if (audio_player_get_state() != AUDIO_PLAYER_STATE_PAUSE) {
ESP_LOGE(TAG, "Expected state to be PAUSE");
fclose(Music_File);
return;
}
}
}
void Volume_adjustment(uint8_t Vol) {
if(Vol > Volume_MAX )
printf("Audio : The volume value is incorrect. Please enter 0 to 21\r\n");
else
Volume = Vol;
ESP_LOGI(TAG, "Volume set to %d", Volume);
}
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "esp_log.h"
#include "esp_check.h"
#include "unity.h"
#include "audio_player.h"
#include "driver/gpio.h"
#include "freertos/semphr.h"
#include "SD_MMC.h"
#define CONFIG_BSP_I2S_NUM 0
#define BSP_I2S_SCLK (GPIO_NUM_48)
#define BSP_I2S_MCLK (GPIO_NUM_NC)
#define BSP_I2S_LCLK (GPIO_NUM_38)
#define BSP_I2S_DOUT (GPIO_NUM_47)
#define BSP_I2S_DSIN (GPIO_NUM_NC)
#define BSP_I2S_GPIO_CFG \
{ \
.mclk = BSP_I2S_MCLK, \
.bclk = BSP_I2S_SCLK, \
.ws = BSP_I2S_LCLK, \
.dout = BSP_I2S_DOUT, \
.din = BSP_I2S_DSIN, \
.invert_flags = { \
.mclk_inv = false, \
.bclk_inv = false, \
.ws_inv = false, \
}, \
}
#define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate) \
{ \
.clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sample_rate), \
.slot_cfg = I2S_STD_PHILIP_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \
.gpio_cfg = BSP_I2S_GPIO_CFG, \
}
#define Volume_MAX 100
extern bool Music_Next_Flag;
extern uint8_t Volume;
void Audio_Init(void);
void Play_Music(const char* directory, const char* fileName);
void Music_resume(void);
void Music_pause(void);
uint32_t Music_Duration(void);
uint32_t Music_Elapsed(void);
uint16_t Music_Energy(void);
void Volume_adjustment(uint8_t Volume);