这次使用轻量 红外线 解码,加入两段eq均衡器,通过红外遥控 可以控制 ,对

pcm5102a 的初始化做了调整,补齐了,系统崩溃重启的短板。本来想搞5段eq均衡

但是 就是播放卡顿,实在郁闷,大神们谁有好eq算法,交流一下啊。

#include <Arduino.h>
#include <driver/i2s.h>
#include <esp_heap_caps.h>
#include "flac.h"
#include "usb/usb_host.h"
#include "msc_host.h"
#include "msc_host_vfs.h"
#include <dirent.h>
#include "esp_random.h"
#include "bootloader_random.h"
#include <MP3DecoderMAD.h>
#include <vector>
#include <String>
#include <cstring>
#include <cmath>
#include "driver/rtc_io.h"

// ========== 硬件引脚定义(语义化命名) ==========
#define IR_RECEIVE_PIN 2       // 红外接收引脚
#define I2S_BIT_CLOCK_PIN 17   // I2S位时钟引脚

#define I2S_DATA_OUT_PIN 18   // I2S数据输出引脚

#define I2S_LR_CLOCK_PIN 8    // I2S左右声道时钟引脚
#define I2S_PORT_NUM I2S_NUM_0 // I2S端口号

// ========== 基础配置常量(语义化命名) ==========
#define USB_MOUNT_PATH "/usb"                    // USB挂载路径
#define MAD_DECODE_BUFFER_SIZE 8192              // MP3解码缓冲区大小
#define MAD_PCM_BUFFER_SIZE 4096                 // MP3 PCM输出缓冲区大小
#define MP3_READ_BUFFER_SIZE 8192                // MP3文件读取缓冲区大小
#define FLAC_READ_BUFFER_SIZE 8192               // FLAC文件读取缓冲区大小
#define EQ_FREQ_55HZ 62.0f                       // EQ 55Hz中心频率
#define EQ_FREQ_8000HZ 8000.0f                   // EQ 8000Hz中心频率
#define EQ_Q_FACTOR 0.707f                       // EQ Q值
#define AUDIO_SAMPLE_RATE 44100                  // 默认音频采样率
#define MAX_RANDOM_RETRY_COUNT 1000              // 随机选曲最大重试次数
#define DECODE_TASK_STACK_SIZE 48 * 1024         // 解码任务栈大小
#define AUDIO_LIST_CACHE "/usb/audio_list.cache" // 音频列表缓存路径

// ========== 深度睡眠配置 ==========
#define PAUSE_SLEEP_TIMEOUT_MS 10000 // 暂停超时深度睡眠时间(10分钟)
#define WAKEUP_PIN IR_RECEIVE_PIN    // 唤醒引脚

// ========== 事件常量定义(语义化命名) ==========
#define USB_EVENT_DISCONNECTED 0 // USB断开事件
#define USB_EVENT_CONNECTED 1    // USB连接事件
#define USB_EVENT_QUIT 2         // USB退出事件

#define MSC_EVENT_CONNECTED 0    // MSC设备连接事件
#define MSC_EVENT_DISCONNECTED 1 // MSC设备断开事件

// ========== 枚举定义(语义化命名) ==========
// 播放模式枚举
enum PlayMode
{
    PLAY_MODE_STOP = 0, // 停止模式
    PLAY_MODE_PLAY = 1, // 播放模式
    PLAY_MODE_NEXT = 2  // 下一曲模式
};

// 音频文件类型枚举
enum AudioFileType
{
    AUDIO_TYPE_UNKNOWN, // 未知类型
    AUDIO_TYPE_MP3,     // MP3类型
    AUDIO_TYPE_FLAC     // FLAC类型
};

// ========== 全局变量定义(规范化命名) ==========
uint32_t g_ir_receive_code = 0; // 红外接收码
i2s_config_t g_i2s_config = {}; // I2S配置结构体

// USB相关状态
bool g_usb_mounted = false;                    // USB是否挂载
uint8_t g_usb_device_address = 0;              // USB设备地址
uint8_t g_usb_event_id = 5;                    // USB事件ID
msc_host_device_handle_t g_msc_device = NULL;  // MSC设备句柄
msc_host_vfs_handle_t g_msc_vfs_handle = NULL; // MSC VFS句柄
bool g_msc_device_present = false;             // MSC设备是否存在

// EQ相关配置
bool g_eq_55hz_adjust_up = true;                // 55Hz EQ增益上调标志
bool g_eq_8000hz_adjust_up = true;              // 8000Hz EQ增益上调标志
bool g_eq_enabled = true;                       // EQ功能使能
bool g_i2s_configured = false;                  // I2S是否已配置
float g_audio_volume = 0.01f;                   // 当前音量(0.0-1.0)
volatile PlayMode g_play_mode = PLAY_MODE_STOP; // 当前播放模式
volatile int g_current_play_index = -1;         // 当前播放曲目索引
int g_prev_play_index = -1;                     // 上一曲目索引(避免随机重复)
bool g_is_random_play = true;                   // 是否随机播放

// 解码器相关
FLAC__StreamDecoder *g_flac_decoder = nullptr;  // FLAC解码器指针
libmad::MP3DecoderMAD *g_mp3_decoder = nullptr; // MP3解码器指针
uint8_t g_audio_read_buffer[8192];              // 音频文件读取缓冲区
int16_t g_pcm_output_buffer[4096];              // PCM输出缓冲区

// 播放控制
bool g_is_paused = false;              // 暂停标志
std::vector<String> g_audio_file_list; // 音频文件列表
String g_audio_list_cache_file_path;   // 音频列表缓存文件路径
bool g_device_mounted = false;         // 设备挂载标志
uint32_t g_pause_start_timestamp = 0;  // 暂停开始时间戳
bool g_enter_sleep_flag = false;       // 进入睡眠标志

// EQ参数(语义化命名)
int g_eq_55hz_gain = 0;                            // 55Hz EQ增益值
int g_eq_8000hz_gain = 0;                          // 8000Hz EQ增益值
float g_eq_55hz_b0 = 0.0f;                         // 55Hz滤波器b0系数
float g_eq_55hz_a1 = 0.0f;                         // 55Hz滤波器a1系数
float g_eq_55hz_a2 = 0.0f;                         // 55Hz滤波器a2系数
float g_eq_8000hz_b0 = 0.0f;                       // 8000Hz滤波器b0系数
float g_eq_8000hz_a1 = 0.0f;                       // 8000Hz滤波器a1系数
float g_eq_8000hz_a2 = 0.0f;                       // 8000Hz滤波器a2系数
bool g_eq_coeffs_calculated = true;                // EQ系数是否已计算
static float g_eq_55hz_left_buffer[4] = {0.0f};    // 55Hz左声道滤波缓存
static float g_eq_55hz_right_buffer[4] = {0.0f};   // 55Hz右声道滤波缓存
static float g_eq_8000hz_left_buffer[4] = {0.0f};  // 8000Hz左声道滤波缓存
static float g_eq_8000hz_right_buffer[4] = {0.0f}; // 8000Hz右声道滤波缓存
float g_eq_55hz_gain_linear = 0.0f;                // 55Hz增益线性值
float g_eq_55hz_prev_gain = 10;                    // 55Hz上一次增益值
float g_eq_8000hz_gain_linear = 0.0f;              // 8000Hz增益线性值
float g_eq_8000hz_prev_gain = 10;                  // 8000Hz上一次增益值
bool g_is_mp3_playing = true;                      // 是否正在播放MP3
int g_random_retry_count = 0;                      // 随机选曲重试计数

// 线程安全互斥锁
portMUX_TYPE g_audio_mux = portMUX_INITIALIZER_UNLOCKED;

/**
 * @brief 计算单频段二阶IIR滤波器系数
 * @param freq 中心频率
 * @param b0 输出系数b0
 * @param a1 输出系数a1
 * @param a2 输出系数a2
 */
static void calculate_eq_filter_coefficients(float freq, float *b0, float *a1, float *a2)
{
    float omega = 2.0f * M_PI * freq / AUDIO_SAMPLE_RATE;
    float sin_omega = sinf(omega);
    float cos_omega = cosf(omega);
    float alpha = sin_omega / (2.0f * EQ_Q_FACTOR);

    float b0_ = alpha;
    float a0_ = 1.0f + alpha;
    float a1_ = -2.0f * cos_omega;
    float a2_ = 1.0f - alpha;

    *b0 = b0_ / a0_;
    *a1 = a1_ / a0_;
    *a2 = a2_ / a0_;
}

/**
 * @brief 重置所有滤波器缓存(切歌或切换曲目时调用)
 */
void reset_eq_buffers(void)
{
    portENTER_CRITICAL(&g_audio_mux);
    memset(g_eq_55hz_left_buffer, 0, sizeof(g_eq_55hz_left_buffer));
    memset(g_eq_55hz_right_buffer, 0, sizeof(g_eq_55hz_right_buffer));
    memset(g_eq_8000hz_left_buffer, 0, sizeof(g_eq_8000hz_left_buffer));
    memset(g_eq_8000hz_right_buffer, 0, sizeof(g_eq_8000hz_right_buffer));
    portEXIT_CRITICAL(&g_audio_mux);
}

/**
 * @brief 双频段浮点精度EQ处理函数(修正参数类型)
 * @param pcm_float 输入输出浮点PCM数据(范围[-1.0, 1.0])
 * @param sample_count 总采样点数(包含左右声道)
 */
void process_audio_eq(float *pcm_float, size_t sample_count)
{
    if (!pcm_float || sample_count == 0)
        return;

    // 首次计算EQ系数
    if (g_eq_coeffs_calculated)
    {
        g_eq_coeffs_calculated = false;
        calculate_eq_filter_coefficients(EQ_FREQ_55HZ, &g_eq_55hz_b0, &g_eq_55hz_a1, &g_eq_55hz_a2);
        calculate_eq_filter_coefficients(EQ_FREQ_8000HZ, &g_eq_8000hz_b0, &g_eq_8000hz_a1, &g_eq_8000hz_a2);
        Serial.println("EQ系数已计算完成");
    }

    // 转换增益dB为线性系数
    if (g_eq_55hz_prev_gain != g_eq_55hz_gain)
    {
        g_eq_55hz_gain_linear = powf(10.0f, (float)g_eq_55hz_gain * 0.05f);
        g_eq_55hz_prev_gain = g_eq_55hz_gain;
        Serial.printf("55Hz EQ增益: %d dB (线性值: %.3f)\n", g_eq_55hz_gain, g_eq_55hz_gain_linear);
    }

    if (g_eq_8000hz_prev_gain != g_eq_8000hz_gain)
    {
        g_eq_8000hz_gain_linear = powf(10.0f, (float)g_eq_8000hz_gain * 0.05f);
        g_eq_8000hz_prev_gain = g_eq_8000hz_gain;
        Serial.printf("8000Hz EQ增益: %d dB (线性值: %.3f)\n", g_eq_8000hz_gain, g_eq_8000hz_gain_linear);
    }

    // 处理立体声PCM数据(每次处理左右两个采样)
    for (size_t i = 0; i < sample_count; i += 2)
    {
        float in_left = pcm_float[i];
        float in_right = (i + 1 < sample_count) ? pcm_float[i + 1] : in_left;

        // 55Hz频段处理
        float out_left_55hz = g_eq_55hz_b0 * (in_left - g_eq_55hz_left_buffer[1]) - g_eq_55hz_a1 * g_eq_55hz_left_buffer[2] - g_eq_55hz_a2 * g_eq_55hz_left_buffer[3];
        g_eq_55hz_left_buffer[1] = g_eq_55hz_left_buffer[0];
        g_eq_55hz_left_buffer[0] = in_left;
        g_eq_55hz_left_buffer[3] = g_eq_55hz_left_buffer[2];
        g_eq_55hz_left_buffer[2] = out_left_55hz;

        float out_right_55hz = g_eq_55hz_b0 * (in_right - g_eq_55hz_right_buffer[1]) - g_eq_55hz_a1 * g_eq_55hz_right_buffer[2] - g_eq_55hz_a2 * g_eq_55hz_right_buffer[3];
        g_eq_55hz_right_buffer[1] = g_eq_55hz_right_buffer[0];
        g_eq_55hz_right_buffer[0] = in_right;
        g_eq_55hz_right_buffer[3] = g_eq_55hz_right_buffer[2];
        g_eq_55hz_right_buffer[2] = out_right_55hz;

        // 8000Hz频段处理
        float out_left_8000hz = g_eq_8000hz_b0 * (in_left - g_eq_8000hz_left_buffer[1]) - g_eq_8000hz_a1 * g_eq_8000hz_left_buffer[2] - g_eq_8000hz_a2 * g_eq_8000hz_left_buffer[3];
        g_eq_8000hz_left_buffer[1] = g_eq_8000hz_left_buffer[0];
        g_eq_8000hz_left_buffer[0] = in_left;
        g_eq_8000hz_left_buffer[3] = g_eq_8000hz_left_buffer[2];
        g_eq_8000hz_left_buffer[2] = out_left_8000hz;

        float out_right_8000hz = g_eq_8000hz_b0 * (in_right - g_eq_8000hz_right_buffer[1]) - g_eq_8000hz_a1 * g_eq_8000hz_right_buffer[2] - g_eq_8000hz_a2 * g_eq_8000hz_right_buffer[3];
        g_eq_8000hz_right_buffer[1] = g_eq_8000hz_right_buffer[0];
        g_eq_8000hz_right_buffer[0] = in_right;
        g_eq_8000hz_right_buffer[3] = g_eq_8000hz_right_buffer[2];
        g_eq_8000hz_right_buffer[2] = out_right_8000hz;

        // 信号混合
        float final_left = in_left + out_left_55hz * g_eq_55hz_gain_linear + out_left_8000hz * g_eq_8000hz_gain_linear;
        float final_right = in_right + out_right_55hz * g_eq_55hz_gain_linear + out_right_8000hz * g_eq_8000hz_gain_linear;

        // 限幅保护
        final_left = constrain(final_left, -1.0f, 1.0f);
        final_right = constrain(final_right, -1.0f, 1.0f);

        // 输出赋值
        pcm_float[i] = final_left;
        if (i + 1 < sample_count)
        {
            pcm_float[i + 1] = final_right;
        }
    }
}

/**
 * @brief 生成指定范围随机数(增加边界检查)
 * @param min 最小值
 * @param max 最大值
 * @return 指定范围内的随机数
 */
uint32_t generate_random_number(uint32_t min, uint32_t max)
{
    if (min > max)
        return min;
    return min + (esp_random() % (max - min + 1));
}

/**
 * @brief 过滤音频文件(判断是否为支持的音频格式)
 * @param file_name 文件名
 * @return true-是音频文件,false-不是
 */
bool is_supported_audio_file(const String &file_name)
{
    int dot_index = file_name.lastIndexOf('.');
    if (dot_index == -1)
        return false;

    String extension = file_name.substring(dot_index + 1);
    extension.toLowerCase();
    return (extension == "mp3" || extension == "wav" || extension == "flac");
}

/**
 * @brief 遍历目录并执行回调函数
 * @param dir_path 目录路径
 * @param callback 回调函数
 * @return true-遍历成功,false-失败
 */
static bool traverse_directory(const String &dir_path, bool (*callback)(const String &, bool))
{
    DIR *dir = opendir(dir_path.c_str());
    if (!dir)
    {
        Serial.printf("打开目录失败:%s\n", dir_path.c_str());
        return false;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL)
    {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;

        String full_path = dir_path + "/" + entry->d_name;
        if (full_path.startsWith("//"))
            full_path = full_path.substring(1);

        bool is_directory = (entry->d_type == DT_DIR);
        if (!callback(full_path, is_directory))
        {
            closedir(dir);
            return false;
        }
    }

    closedir(dir);
    return true;
}

/**
 * @brief 保存音频列表到缓存文件
 * @return true-保存成功,false-失败
 */
static bool save_audio_list_to_cache()
{
    if (!g_device_mounted)
        return false;

    FILE *cache_file = fopen(g_audio_list_cache_file_path.c_str(), "w");
    if (!cache_file)
    {
        Serial.printf("创建缓存文件失败:%s\n", g_audio_list_cache_file_path.c_str());
        return false;
    }

    fprintf(cache_file, "%d\n", g_audio_file_list.size());
    for (size_t i = 0; i < g_audio_file_list.size(); i++)
    {
        String file_name = g_audio_file_list[i];
        if (!file_name.startsWith("/"))
            file_name = "/" + file_name;
        fprintf(cache_file, "%s\n", file_name.c_str());
    }

    fclose(cache_file);
    Serial.printf("音频列表已缓存到USB,共%d个文件\n", g_audio_file_list.size());
    return true;
}

/**
 * @brief 从缓存文件加载音频列表
 * @return true-加载成功,false-失败
 */
static bool load_audio_list_from_cache()
{
    if (!g_device_mounted)
        return false;

    FILE *cache_file = fopen(g_audio_list_cache_file_path.c_str(), "r");
    if (!cache_file)
    {
        Serial.println("缓存文件不存在,需要扫描USB");
        return false;
    }

    g_audio_file_list.clear();
    int file_count = 0;
    if (fscanf(cache_file, "%d\n", &file_count) != 1)
    {
        Serial.println("读取缓存文件数量失败");
        fclose(cache_file);
        return false;
    }

    char buffer[256];
    for (int i = 0; i < file_count; i++)
    {
        if (fgets(buffer, sizeof(buffer), cache_file) == NULL)
            break;

        String file_name = String(buffer);
        file_name.trim();
        if (file_name.length() > 0)
        {
            if (!file_name.startsWith("/"))
                file_name = "/" + file_name;
            g_audio_file_list.push_back(file_name);
        }
    }

    fclose(cache_file);
    Serial.printf("从USB缓存加载音频列表,共%d个文件\n", g_audio_file_list.size());
    return true;
}

/**
 * @brief 扫描音频文件(优先加载缓存)
 * @param scan_path 扫描路径
 * @param is_mounted 设备是否挂载
 * @param cache_file_path 缓存文件路径
 */
void scan_audio_files(String scan_path, bool is_mounted, String cache_file_path)
{
    g_audio_list_cache_file_path = cache_file_path;
    g_device_mounted = is_mounted;

    if (!g_device_mounted)
    {
        g_audio_file_list.clear();
        return;
    }

    // 先尝试加载缓存
    if (load_audio_list_from_cache())
        return;

    // 缓存加载失败,扫描USB
    g_audio_file_list.clear();
    traverse_directory(scan_path, [](const String &full_path, bool is_dir) -> bool
                       {
    if (!is_dir && is_supported_audio_file(full_path))
    {
      String file_name = full_path;
      if (!file_name.startsWith("/")) 
        file_name = "/" + file_name;
      g_audio_file_list.push_back(file_name);
      Serial.printf("发现音频文件:%s\n", file_name.c_str());
    }
    return true; });

    save_audio_list_to_cache();
    Serial.printf("扫描USB完成,共%d个音频文件\n", g_audio_file_list.size());
}

/**
 * @brief 检查文件是否存在
 * @param file_path 文件路径
 * @return true-存在,false-不存在
 */
bool file_exists(const String &file_path)
{
    struct stat file_stat;
    return (stat(file_path.c_str(), &file_stat) == 0);
}

/**
 * @brief MP3 PCM 输出回调函数(修复类型转换+内存管理)
 * @param info 音频信息
 * @param pcm_buffer PCM数据缓冲区
 * @param length 数据长度
 */
static void on_mp3_pcm_data(libmad::MadAudioInfo &info, short *pcm_buffer, size_t length)
{
    // 1. 分配浮点缓冲区
    float *pcm_float = (float *)heap_caps_malloc(length * sizeof(float), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
    if (!pcm_float)
    {
        Serial.println("MP3浮点缓冲区分配失败");
        return;
    }

    // 2. 整数转浮点(带音量,修正范围为32768)
    portENTER_CRITICAL(&g_audio_mux);
    float volume = g_audio_volume;
    portEXIT_CRITICAL(&g_audio_mux);

    for (size_t i = 0; i < length; i++)
    {
        float sample = ((float)pcm_buffer[i] / 32768.0f) * volume;
        pcm_float[i] = sample;
    }

    // 3. EQ处理
    portENTER_CRITICAL(&g_audio_mux);
    bool eq_enabled = g_eq_enabled;
    portEXIT_CRITICAL(&g_audio_mux);

    if (eq_enabled)
    {
        process_audio_eq(pcm_float, length);
    }

    // 4. 浮点转回整数(修正范围+限幅)
    for (size_t i = 0; i < length; i++)
    {
        int32_t sample = (int32_t)(pcm_float[i] * 32768.0f);
        g_pcm_output_buffer[i] = constrain(sample, -32768, 32767);
    }

    // 释放浮点缓冲区
    heap_caps_free(pcm_float);

    // 首次设置I2S采样率
    static bool first_config = true;
    if (first_config)
    {
        i2s_set_clk(I2S_PORT_NUM, info.sample_rate, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
        first_config = false;
        Serial.printf("I2S配置: %d Hz, %d 声道\n", info.sample_rate, info.channels);
    }

    // 写入I2S输出
    size_t bytes_written;
    i2s_write(I2S_PORT_NUM, g_pcm_output_buffer, length * 2, &bytes_written, portMAX_DELAY);
}

/**
 * @brief MP3音频信息回调函数
 * @param info 音频信息
 */
static bool first_audio_info = true;
static void on_mp3_audio_info(libmad::MadAudioInfo &info)
{
    if (first_audio_info)
    {
        Serial.printf("MP3信息: %d Hz, %d 声道, %d 位\n", info.sample_rate, info.channels, info.bits_per_sample);
        first_audio_info = false;
    }
}

/**
 * @brief 获取音频文件类型
 * @param file_path 文件路径
 * @return 音频文件类型枚举
 */
AudioFileType get_audio_file_type(const String &file_path)
{
    String path_lower = file_path;
    path_lower.toLowerCase();

    if (path_lower.endsWith(".mp3"))
        return AUDIO_TYPE_MP3;
    if (path_lower.endsWith(".flac"))
        return AUDIO_TYPE_FLAC;

    return AUDIO_TYPE_UNKNOWN;
}

/**
 * @brief 销毁音频解码器(防止内存泄漏)
 */
void destroy_audio_decoders()
{
    portENTER_CRITICAL(&g_audio_mux);
    if (g_mp3_decoder)
    {
        delete g_mp3_decoder;
        g_mp3_decoder = nullptr;
    }
    if (g_flac_decoder)
    {
        FLAC__stream_decoder_delete(g_flac_decoder);
        g_flac_decoder = nullptr;
    }
    portEXIT_CRITICAL(&g_audio_mux);
    Serial.println("音频解码器已销毁");
}

/**
 * @brief 初始化解码器指针
 */
void init_audio_decoders()
{
    // 初始化MP3解码器(仅初始化一次)
    if (g_mp3_decoder == nullptr)
    {
        g_mp3_decoder = new libmad::MP3DecoderMAD();
        g_mp3_decoder->setBufferSize(MAD_DECODE_BUFFER_SIZE);
        g_mp3_decoder->setResultBufferSize(MAD_PCM_BUFFER_SIZE);
        g_mp3_decoder->setDataCallback(on_mp3_pcm_data);
        g_mp3_decoder->setInfoCallback(on_mp3_audio_info);
        g_mp3_decoder->begin();
    }

    // 初始化FLAC解码器
    if (g_flac_decoder == nullptr)
        g_flac_decoder = FLAC__stream_decoder_new();

    vTaskDelay(pdMS_TO_TICKS(500));

    if (g_flac_decoder)
        Serial.println("FLAC解码器初始化完成");
    if (g_mp3_decoder)
        Serial.println("MP3解码器初始化完成");
}

/**
 * @brief 播放MP3文件
 * @param file_path 文件路径
 * @return true-播放成功,false-失败
 */
bool play_mp3_file(const String &file_path)
{
    portENTER_CRITICAL(&g_audio_mux);
    g_is_paused = false;
    portEXIT_CRITICAL(&g_audio_mux);

    // 检查解码器是否初始化
    if (g_mp3_decoder == nullptr)
    {
        Serial.println("MP3解码器未初始化");
        return false;
    }

    // 打开文件(增加错误处理)
    FILE *audio_file = fopen(file_path.c_str(), "rb");
    if (!audio_file)
    {
        Serial.printf("文件不存在:%s\n", file_path.c_str());
        return false;
    }

    Serial.printf("开始播放MP3: %s\n", file_path.c_str());
    first_audio_info = true;

    // 播放循环
    while (!feof(audio_file))
    {
        // 检查播放模式(加锁保护)
        portENTER_CRITICAL(&g_audio_mux);
        PlayMode current_mode = g_play_mode;
        bool is_paused = g_is_paused;
        portEXIT_CRITICAL(&g_audio_mux);

        if (current_mode != PLAY_MODE_PLAY)
        {
            break;
        }

        size_t bytes_read = fread(g_audio_read_buffer, 1, MP3_READ_BUFFER_SIZE, audio_file);
        if (bytes_read > 0)
        {
            g_mp3_decoder->write(g_audio_read_buffer, bytes_read);
        }

        // 暂停处理
        while (is_paused && current_mode == PLAY_MODE_PLAY)
        {
            vTaskDelay(pdMS_TO_TICKS(200));
            portENTER_CRITICAL(&g_audio_mux);
            is_paused = g_is_paused;
            current_mode = g_play_mode;
            portEXIT_CRITICAL(&g_audio_mux);
        }

        vTaskDelay(pdMS_TO_TICKS(1));
    }

    Serial.println("MP3播放循环退出");

    // 安全关闭文件
    if (audio_file != NULL)
    {
        fclose(audio_file);
        audio_file = NULL;
    }

    vTaskDelay(pdMS_TO_TICKS(50));
    Serial.printf("MP3文件已关闭:%s\n", file_path.c_str());
    return true;
}

/**
 * @brief FLAC解码写入回调函数(修复类型转换+内存泄漏+goto作用域)
 * @param decoder FLAC解码器指针
 * @param frame FLAC帧数据
 * @param buffer PCM数据缓冲区
 * @param data 用户数据
 * @return 解码状态
 */
static FLAC__StreamDecoderWriteStatus flac_write_callback(
    const FLAC__StreamDecoder *decoder,
    const FLAC__Frame *frame,
    const FLAC__int32 *const buffer[],
    void *data)
{
    if (!frame || !buffer)
        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;

    uint32_t sample_rate = frame->header.sample_rate;
    uint32_t channels = frame->header.channels;
    uint32_t bits_per_sample = frame->header.bits_per_sample;
    uint32_t block_size = frame->header.blocksize;

    // 提前定义所有变量,避免goto跨初始化
    float *pcm_float = NULL;
    int16_t *pcm_int16 = NULL;
    float volume = 0.0f;
    bool eq_enabled = false;
    PlayMode current_mode = PLAY_MODE_STOP;

    // 配置I2S采样率
    if (!g_i2s_configured || g_i2s_config.sample_rate != sample_rate)
    {
        g_i2s_config.sample_rate = sample_rate;
        i2s_set_clk(I2S_PORT_NUM, sample_rate, I2S_BITS_PER_SAMPLE_16BIT,
                    channels == 1 ? I2S_CHANNEL_MONO : I2S_CHANNEL_STEREO);
        g_i2s_configured = true;
        Serial.printf("FLAC信息: %d Hz, %d 声道, %d 位\n", sample_rate, channels, bits_per_sample);
    }

    // 1. 分配浮点缓冲区(用于EQ计算)
    pcm_float = (float *)heap_caps_malloc(block_size * channels * sizeof(float),
                                          MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
    if (!pcm_float)
    {
        Serial.println("FLAC浮点缓冲区分配失败");
        goto cleanup;
    }

    // 2. 分配16位整数缓冲区(用于I2S输出)
    pcm_int16 = (int16_t *)heap_caps_malloc(block_size * channels * sizeof(int16_t),
                                            MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
    if (!pcm_int16)
    {
        Serial.println("FLAC整数缓冲区分配失败");
        goto cleanup;
    }

    // 3. 获取音量(加锁保护)
    portENTER_CRITICAL(&g_audio_mux);
    volume = g_audio_volume;
    portEXIT_CRITICAL(&g_audio_mux);

    // 4. FLAC数据转换为浮点(先缩放至[-1.0, 1.0]范围)
    for (uint32_t i = 0; i < block_size; i++)
    {
        for (uint32_t c = 0; c < channels; c++)
        {
            if (c >= frame->header.channels || i >= frame->header.blocksize)
                continue;

            // FLAC原始数据(可能是24/32位)→ 缩放到[-1.0, 1.0]浮点
            int32_t sample = buffer[c][i];
            if (bits_per_sample > 16)
            {
                sample >>= (bits_per_sample - 16); // 先降为16位精度
            }

            // 应用音量 + 转换为浮点(修正为32768范围)
            float sample_float = ((float)sample / 32768.0f) * volume;
            pcm_float[i * channels + c] = sample_float;
        }
    }

    // 5. 应用EQ处理(直接操作浮点数组)
    portENTER_CRITICAL(&g_audio_mux);
    eq_enabled = g_eq_enabled;
    portEXIT_CRITICAL(&g_audio_mux);

    if (eq_enabled)
    {
        process_audio_eq(pcm_float, block_size * channels);
    }

    // 6. 浮点转回16位整数(限幅保护)
    for (uint32_t i = 0; i < block_size * channels; i++)
    {
        // 缩放回16位整数范围 + 限幅(修正为32768)
        int32_t sample = (int32_t)(pcm_float[i] * 32768.0f);
        pcm_int16[i] = constrain(sample, -32768, 32767);
    }

    // 7. 写入I2S(16位整数数据)
    size_t bytes_written;
    i2s_write(I2S_PORT_NUM, pcm_int16, block_size * channels * 2, &bytes_written, pdMS_TO_TICKS(100));

cleanup: // 统一清理内存
    if (pcm_float)
        heap_caps_free(pcm_float);
    if (pcm_int16)
        heap_caps_free(pcm_int16);

    // 检查播放模式
    portENTER_CRITICAL(&g_audio_mux);
    current_mode = g_play_mode;
    portEXIT_CRITICAL(&g_audio_mux);

    // 根据是否分配成功返回状态
    if (!pcm_float || !pcm_int16)
    {
        return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
    }

    return (current_mode == PLAY_MODE_PLAY) ? FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE
                                            : FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}

/**
 * @brief FLAC解码错误回调函数
 * @param decoder FLAC解码器指针
 * @param status 错误状态
 * @param data 用户数据
 */
static void flac_error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *data)
{
    if (status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
    {
        Serial.printf("FLAC解码错误: %s\n", FLAC__StreamDecoderErrorStatusString[status]);
    }
}

/**
 * @brief 播放FLAC文件
 * @param file_path 文件路径
 * @return true-播放成功,false-失败
 */
bool play_flac_file(const String &file_path)
{
    portENTER_CRITICAL(&g_audio_mux);
    g_is_paused = false;
    portEXIT_CRITICAL(&g_audio_mux);

    if (g_flac_decoder == nullptr)
    {
        Serial.println("FLAC解码器未初始化");
        return false;
    }

    // 打开FLAC文件
    FILE *audio_file = fopen(file_path.c_str(), "rb");
    if (!audio_file)
    {
        Serial.printf("文件不存在:%s\n", file_path.c_str());
        return false;
    }

    // 清理旧解码器状态
    if (g_flac_decoder)
    {
        FLAC__stream_decoder_finish(g_flac_decoder);
    }

    // 初始化解码器
    FLAC__StreamDecoderInitStatus init_status = FLAC__stream_decoder_init_FILE(
        g_flac_decoder, audio_file, flac_write_callback, nullptr, flac_error_callback, nullptr);

    if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
    {
        Serial.printf("FLAC解码器初始化失败:%d\n", init_status);
        FLAC__stream_decoder_delete(g_flac_decoder);
        g_flac_decoder = nullptr;
        fclose(audio_file);
        return false;
    }

    FLAC__stream_decoder_set_metadata_ignore_all(g_flac_decoder);
    Serial.printf("开始播放FLAC: %s\n", file_path.c_str());

    // 解码循环
    while (true)
    {
        // 检查播放模式
        portENTER_CRITICAL(&g_audio_mux);
        PlayMode current_mode = g_play_mode;
        bool is_paused = g_is_paused;
        portEXIT_CRITICAL(&g_audio_mux);

        if (current_mode != PLAY_MODE_PLAY)
        {
            break;
        }

        if (!g_flac_decoder)
        {
            Serial.println("FLAC解码器已释放");
            break;
        }

        FLAC__bool decode_ok = FLAC__stream_decoder_process_single(g_flac_decoder);
        FLAC__StreamDecoderState decoder_state = FLAC__stream_decoder_get_state(g_flac_decoder);

        if (!decode_ok || decoder_state == FLAC__STREAM_DECODER_END_OF_STREAM)
        {
            Serial.printf("FLAC解码结束:状态=%d\n", decoder_state);
            break;
        }

        // 暂停处理
        while (is_paused && current_mode == PLAY_MODE_PLAY)
        {
            vTaskDelay(pdMS_TO_TICKS(200));
            portENTER_CRITICAL(&g_audio_mux);
            is_paused = g_is_paused;
            current_mode = g_play_mode;
            portEXIT_CRITICAL(&g_audio_mux);
        }

        vTaskDelay(pdMS_TO_TICKS(1));
    }

    Serial.println("FLAC播放循环退出");

    // 清理资源
    if (g_flac_decoder)
        FLAC__stream_decoder_finish(g_flac_decoder);

    fclose(audio_file);
    audio_file = nullptr;

    return true;
}

/**
 * @brief 音频解码任务
 * @param param 任务参数
 */
void audio_decode_task(void *param)
{
    while (true)
    {
        // 检查曲目列表是否为空
        if (g_audio_file_list.empty())
        {
            portENTER_CRITICAL(&g_audio_mux);
            g_play_mode = PLAY_MODE_STOP;
            portEXIT_CRITICAL(&g_audio_mux);
            vTaskDelay(pdMS_TO_TICKS(1000));
            continue;
        }

        // 切歌逻辑
        portENTER_CRITICAL(&g_audio_mux);
        PlayMode current_mode = g_play_mode;
        portEXIT_CRITICAL(&g_audio_mux);

        if (current_mode == PLAY_MODE_NEXT)
        {
            g_random_retry_count = 0;
            int new_index = generate_random_number(0, g_audio_file_list.size() - 1);

            // 增加重试次数限制,防止死循环(避免重复选曲)
            while (g_random_retry_count < MAX_RANDOM_RETRY_COUNT)
            {
                // 边界检查,防止数组越界
                if (new_index < 0 || new_index >= (int)g_audio_file_list.size())
                {
                    new_index = generate_random_number(0, g_audio_file_list.size() - 1);
                    g_random_retry_count++;
                    continue;
                }

                // 避免和上一曲重复(列表长度>1时生效)
                if (g_audio_file_list.size() > 1 && new_index == g_prev_play_index)
                {
                    new_index = generate_random_number(0, g_audio_file_list.size() - 1);
                    g_random_retry_count++;
                    continue;
                }

                AudioFileType file_type = get_audio_file_type(g_audio_file_list[new_index]);
                if (file_type == AUDIO_TYPE_MP3 || file_type == AUDIO_TYPE_FLAC)
                {
                    reset_eq_buffers();
                    i2s_zero_dma_buffer(I2S_PORT_NUM); // 清空I2S缓冲区,消除咔哒声
                    g_prev_play_index = new_index;     // 记录当前索引
                    break;
                }

                g_random_retry_count++;
                new_index = generate_random_number(0, g_audio_file_list.size() - 1);
            }

            if (g_random_retry_count >= MAX_RANDOM_RETRY_COUNT)
            {
                Serial.println("没有能播放的音频文件");
                portENTER_CRITICAL(&g_audio_mux);
                g_play_mode = PLAY_MODE_STOP;
                portEXIT_CRITICAL(&g_audio_mux);
                vTaskDelay(pdMS_TO_TICKS(1000));
                continue;
            }

            portENTER_CRITICAL(&g_audio_mux);
            g_current_play_index = new_index;
            g_play_mode = PLAY_MODE_PLAY;
            portEXIT_CRITICAL(&g_audio_mux);

            Serial.printf("切换到下一曲: 索引=%d\n", new_index);
            continue;
        }

        // 首次播放
        if (g_current_play_index == -1)
        {
            int retry_count = 0;
            int new_index = -1;

            do
            {
                new_index = generate_random_number(0, g_audio_file_list.size() - 1);
                retry_count++;

                // 防止死循环
                if (retry_count > MAX_RANDOM_RETRY_COUNT)
                {
                    Serial.println("没有找到可播放的音频文件");
                    portENTER_CRITICAL(&g_audio_mux);
                    g_play_mode = PLAY_MODE_STOP;
                    portEXIT_CRITICAL(&g_audio_mux);
                    g_current_play_index = -1;
                    break;
                }

                // 避免重复(首次播放无历史,仅检查文件类型)
            } while (new_index < 0 || new_index >= (int)g_audio_file_list.size() ||
                     get_audio_file_type(g_audio_file_list[new_index]) == AUDIO_TYPE_UNKNOWN);

            if (new_index != -1)
            {
                portENTER_CRITICAL(&g_audio_mux);
                g_current_play_index = new_index;
                g_play_mode = PLAY_MODE_PLAY;
                portEXIT_CRITICAL(&g_audio_mux);
                g_prev_play_index = new_index;
            }
            else
            {
                continue;
            }
        }

        // 边界检查,防止数组越界
        if (g_current_play_index < 0 || g_current_play_index >= (int)g_audio_file_list.size())
        {
            portENTER_CRITICAL(&g_audio_mux);
            g_play_mode = PLAY_MODE_NEXT;
            portEXIT_CRITICAL(&g_audio_mux);
            continue;
        }

        // 获取文件路径并播放
        String file_path = g_audio_file_list[g_current_play_index];
        AudioFileType file_type = get_audio_file_type(file_path);

        // 播放逻辑
        if (file_type == AUDIO_TYPE_MP3)
            play_mp3_file(file_path);
        else if (file_type == AUDIO_TYPE_FLAC)
            play_flac_file(file_path);
        else
        {
            portENTER_CRITICAL(&g_audio_mux);
            g_play_mode = PLAY_MODE_NEXT;
            portEXIT_CRITICAL(&g_audio_mux);
            continue;
        }

        // 播放完成自动切歌
        portENTER_CRITICAL(&g_audio_mux);
        if (g_play_mode == PLAY_MODE_PLAY)
        {
            g_play_mode = PLAY_MODE_NEXT;
        }
        portEXIT_CRITICAL(&g_audio_mux);

        // 增加延时,防止任务独占CPU
        vTaskDelay(pdMS_TO_TICKS(10));
    }
}

/**
 * @brief 进入深度睡眠模式
 */
/**
 * @brief 进入深度睡眠模式(新增USB安全卸载逻辑)
 */
void enter_deep_sleep_mode()
{
    Serial.println("=== 进入深度睡眠模式 ===");

    // ========== 第一步:安全卸载USB设备(核心新增逻辑) ==========
    Serial.println("开始卸载USB设备...");

    // 1. 卸载VFS挂载(对应原代码的 msc_host_vfs_unregister)
    if (g_msc_vfs_handle)
    {
        esp_err_t err = msc_host_vfs_unregister(g_msc_vfs_handle);
        if (err == ESP_OK)
        {
            Serial.println("✅ USB VFS挂载已卸载");
            g_msc_vfs_handle = NULL;
            g_usb_mounted = false;
        }
        else
        {
            Serial.printf("❌ USB VFS卸载失败: %s\n", esp_err_to_name(err));
        }
    }

    // 2. 卸载MSC设备(对应原代码的 msc_host_uninstall_device)
    if (g_msc_device)
    {
        esp_err_t err = msc_host_uninstall_device(g_msc_device);
        if (err == ESP_OK)
        {
            Serial.println("✅ MSC设备已卸载");
            g_msc_device = NULL;
            g_msc_device_present = false;
        }
        else
        {
            Serial.printf("❌ MSC设备卸载失败: %s\n", esp_err_to_name(err));
        }
    }

    // 3. 卸载MSC主机驱动(对应原代码的 msc_host_uninstall)
    // 注意:驱动卸载后下次唤醒需要重新初始化,因此只在休眠时执行
    esp_err_t msc_uninstall_err = msc_host_uninstall();
    if (msc_uninstall_err == ESP_OK)
    {
        Serial.println("✅ MSC主机驱动已卸载");
    }
    else
    {
        Serial.printf("❌ MSC驱动卸载失败: %s\n", esp_err_to_name(msc_uninstall_err));
    }

    // 4. 重置USB相关状态
    g_usb_device_address = 0;
    g_usb_event_id = 5;
    Serial.println("✅ USB设备卸载完成");

    // ========== 第二步:清理播放资源(原有逻辑保留) ==========
    portENTER_CRITICAL(&g_audio_mux);
    g_play_mode = PLAY_MODE_STOP;
    g_is_paused = false;
    portEXIT_CRITICAL(&g_audio_mux);

    // ========== 第三步:销毁解码器(原有逻辑保留) ==========
    destroy_audio_decoders();

    // ========== 第四步:卸载I2S驱动(原有逻辑保留) ==========
    i2s_driver_uninstall(I2S_PORT_NUM);
    Serial.println("✅ I2S驱动已卸载");

    // ========== 第五步:配置深度睡眠唤醒源(原有逻辑保留) ==========
    // 配置唤醒源:红外接收引脚低电平唤醒
    esp_sleep_enable_ext0_wakeup(GPIO_NUM_2, 0);

    // 将GPIO2切换到RTC GPIO模式(必须)
    rtc_gpio_deinit(GPIO_NUM_2);
    rtc_gpio_init(GPIO_NUM_2);
    rtc_gpio_set_direction(GPIO_NUM_2, RTC_GPIO_MODE_INPUT_ONLY);
    rtc_gpio_pullup_en(GPIO_NUM_2); // 启用上拉

    // ========== 第六步:进入深度睡眠 ==========
    Serial.println("✅ 所有资源已清理,进入深度睡眠...");
    esp_deep_sleep_start();
}

/**
 * @brief 检查暂停超时,超时则进入深度睡眠
 */
void check_pause_timeout()
{
    // 加锁读取状态
    portENTER_CRITICAL(&g_audio_mux);
    bool is_paused = g_is_paused;
    PlayMode current_mode = g_play_mode;
    portEXIT_CRITICAL(&g_audio_mux);

    // 如果处于暂停状态
    if (is_paused && current_mode == PLAY_MODE_PLAY)
    {
        // 记录暂停开始时间
        if (g_pause_start_timestamp == 0)
        {
            g_pause_start_timestamp = millis();
            Serial.printf("暂停计时开始:%lu ms\n", g_pause_start_timestamp);
        }
        // 检查是否超时(10分钟)
        else if (millis() - g_pause_start_timestamp >= PAUSE_SLEEP_TIMEOUT_MS)
        {
            g_enter_sleep_flag = true;
            Serial.println("暂停超时,准备进入深度睡眠...");
        }
    }
    else
    {
        // 非暂停状态,重置计时
        g_pause_start_timestamp = 0;
        g_enter_sleep_flag = false;
    }

    // 触发深度睡眠
    if (g_enter_sleep_flag)
    {
        enter_deep_sleep_mode();
    }
}

/**
 * @brief 处理红外按键事件(修复switch case作用域)
 */
void process_ir_key_events()
{
    // 提前定义所有变量,避免case跨初始化
    uint32_t ir_code = 0;
    float vol_plus = 0.0f;
    float vol_minus = 0.0f;
    bool paused = false;
    bool eq_enabled = false;
    int gain_55 = 0;
    int gain_8000 = 0;

    // 加锁读取红外码
    portENTER_CRITICAL(&g_audio_mux);
    ir_code = g_ir_receive_code;
    portEXIT_CRITICAL(&g_audio_mux);

    if (ir_code != 0 && ir_code != 0x55)
    {
        Serial.printf("接收到红外按键码: 0x%04X\n", ir_code);

        switch (ir_code)
        {
        case 0x4054: // 音量+
            portENTER_CRITICAL(&g_audio_mux);
            g_audio_volume = constrain(g_audio_volume + 0.01f, 0.0f, 1.0f);
            vol_plus = g_audio_volume;
            portEXIT_CRITICAL(&g_audio_mux);
            Serial.printf("当前音量: %.2f\n", vol_plus);
            break;

        case 0x4445: // 音量-
            portENTER_CRITICAL(&g_audio_mux);
            g_audio_volume = constrain(g_audio_volume - 0.01f, 0.0f, 1.0f);
            vol_minus = g_audio_volume;
            portEXIT_CRITICAL(&g_audio_mux);
            Serial.printf("当前音量: %.2f\n", vol_minus);
            break;

        case 0x4444: // 下一曲
            portENTER_CRITICAL(&g_audio_mux);
            g_play_mode = PLAY_MODE_NEXT;
            // 重置暂停计时
            g_pause_start_timestamp = 0;
            g_enter_sleep_flag = false;
            portEXIT_CRITICAL(&g_audio_mux);
            Serial.println("切换到下一曲");
            break;

        case 0x4050: // 暂停/播放
            portENTER_CRITICAL(&g_audio_mux);
            g_is_paused = !g_is_paused;
            paused = g_is_paused;
            portEXIT_CRITICAL(&g_audio_mux);

            // 暂停时开始计时,播放时重置计时
            if (paused)
            {
                g_pause_start_timestamp = millis();
                Serial.println("暂停播放(开始计时)");
            }
            else
            {
                g_pause_start_timestamp = 0;
                g_enter_sleep_flag = false;
                Serial.println("恢复播放(重置计时)");
            }
            break;

        case 0x4014: // EQ开关
            portENTER_CRITICAL(&g_audio_mux);
            g_eq_enabled = !g_eq_enabled;
            eq_enabled = g_eq_enabled;
            portEXIT_CRITICAL(&g_audio_mux);
            Serial.printf("EQ功能: %s\n", eq_enabled ? "开启" : "关闭");
            break;

        case 0x0411: // 55Hz增益调节
            portENTER_CRITICAL(&g_audio_mux);
            if (g_eq_55hz_adjust_up)
            {
                g_eq_55hz_gain++;
                if (g_eq_55hz_gain > 12)
                {
                    g_eq_55hz_gain = 12;
                    g_eq_55hz_adjust_up = false;
                }
            }
            else
            {
                g_eq_55hz_gain--;
                if (g_eq_55hz_gain < -12)
                {
                    g_eq_55hz_gain = -12;
                    g_eq_55hz_adjust_up = true;
                }
            }
            gain_55 = g_eq_55hz_gain;
            portEXIT_CRITICAL(&g_audio_mux);
            Serial.printf("55Hz EQ增益: %d dB\n", gain_55);
            break;

        case 0x0441: // 8000Hz增益调节
            portENTER_CRITICAL(&g_audio_mux);
            if (g_eq_8000hz_adjust_up)
            {
                g_eq_8000hz_gain++;
                if (g_eq_8000hz_gain > 12)
                {
                    g_eq_8000hz_gain = 12;
                    g_eq_8000hz_adjust_up = false;
                }
            }
            else
            {
                g_eq_8000hz_gain--;
                if (g_eq_8000hz_gain < -12)
                {
                    g_eq_8000hz_gain = -12;
                    g_eq_8000hz_adjust_up = true;
                }
            }
            gain_8000 = g_eq_8000hz_gain;
            portEXIT_CRITICAL(&g_audio_mux);
            Serial.printf("8000Hz EQ增益: %d dB\n", gain_8000);
            break;
        }

        // 清除按键状态(加锁)
        portENTER_CRITICAL(&g_audio_mux);
        g_ir_receive_code = 0;
        portEXIT_CRITICAL(&g_audio_mux);
    }
}

/**
 * @brief 红外接收中断处理函数(修复线程安全)
 */
void IRAM_ATTR ir_receive_isr()
{
    static uint32_t time_prev = 0, time_curr = 0, bit_count = 0, ir_data = 0;
    time_curr = micros();
    uint32_t time_diff = time_curr - time_prev;
    time_prev = time_curr;

    // 起始码检测
    if (time_diff > 8500 && time_diff < 9500)
    {
        bit_count = 0;
        ir_data = 0;
        return;
    }

    // 数据位接收
    if (bit_count < 32 && time_diff > 400 && time_diff < 1800)
    {
        ir_data = (ir_data << 1) | (time_diff > 1000 ? 1 : 0);
        bit_count++;

        if (bit_count == 32)
        {
            uint32_t temp_code = (ir_data >> 8) & 0xFFFF;
            // 加锁赋值,避免主循环读到不完整数据
            portENTER_CRITICAL_ISR(&g_audio_mux);
            g_ir_receive_code = temp_code;
            portEXIT_CRITICAL_ISR(&g_audio_mux);
            bit_count = 0;
        }
    }
}

/**
 * @brief 初始化红外接收功能
 */
void init_ir_receiver()
{
    pinMode(IR_RECEIVE_PIN, INPUT);
    attachInterrupt(IR_RECEIVE_PIN, ir_receive_isr, CHANGE);
}
void init_i2s_audio(uint8_t BCLK, uint8_t LRC, uint8_t DOUT)
{

    g_i2s_config.sample_rate = 44100;
    g_i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;
    g_i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
    g_i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; // interrupt priority
    g_i2s_config.dma_buf_count = 16;
    g_i2s_config.dma_buf_len = 512;
    g_i2s_config.use_apll = 0;
    g_i2s_config.tx_desc_auto_clear = true;
    g_i2s_config.fixed_mclk = true;
    g_i2s_config.mclk_multiple = I2S_MCLK_MULTIPLE_128;

    g_i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
    g_i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_STAND_I2S); // Arduino vers. > 2.0.0

    esp_err_t err = i2s_driver_install((i2s_port_t)I2S_PORT_NUM, &g_i2s_config, 0, NULL);
    if (err != ESP_OK)
    {
        Serial.printf("I2S驱动安装失败: %s\n", esp_err_to_name(err));
        while (1)
            vTaskDelay(pdMS_TO_TICKS(100));
    }
    i2s_zero_dma_buffer((i2s_port_t)I2S_PORT_NUM);
    i2s_pin_config_t m_pin_config = {};
    m_pin_config.bck_io_num = BCLK;
    m_pin_config.ws_io_num = LRC; //  wclk = lrc
    m_pin_config.data_out_num = DOUT;
    m_pin_config.data_in_num = I2S_PIN_NO_CHANGE;
    m_pin_config.mck_io_num = I2S_PIN_NO_CHANGE;
    esp_err_t result = i2s_set_pin((i2s_port_t)I2S_PORT_NUM, &m_pin_config);

    if (result != ESP_OK)
    {
        Serial.printf("I2S引脚配置失败: %s\n", esp_err_to_name(result));
        while (1)
            vTaskDelay(pdMS_TO_TICKS(100));
    }
    g_i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT;
    i2s_set_clk((i2s_port_t)I2S_PORT_NUM, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_STEREO);
    Serial.println("I2S音频输出初始化完成");
}

/**
 * @brief MSC设备事件回调函数
 * @param event 事件结构体
 * @param arg 用户参数
 */
static void msc_device_event_callback(const msc_host_event_t *event, void *arg)
{
    if (event->event == MSC_EVENT_CONNECTED)
    {
        Serial.println("MSC存储设备已连接");
        g_usb_device_address = event->device.address;
        g_usb_event_id = USB_EVENT_CONNECTED;
    }
    else if (event->event == MSC_EVENT_DISCONNECTED)
    {
        Serial.println("MSC存储设备已断开");
        g_usb_event_id = USB_EVENT_DISCONNECTED;
    }
}

/**
 * @brief USB主机任务
 * @param args 任务参数
 */
static void usb_host_task(void *args)
{
    printf("USB主机任务运行在核心: %d\n", xPortGetCoreID());

    // 初始化USB主机
    const usb_host_config_t host_config = {.intr_flags = ESP_INTR_FLAG_LEVEL1};
    ESP_ERROR_CHECK(usb_host_install(&host_config));

    // 初始化MSC主机驱动
    const msc_host_driver_config_t msc_config = {
        .create_backround_task = true,
        .task_priority = 1,
        .stack_size = 4096,
        .core_id = 1,
        .callback = msc_device_event_callback,
    };
    ESP_ERROR_CHECK(msc_host_install(&msc_config));

    bool has_clients = true;
    while (true)
    {
        uint32_t event_flags;
        usb_host_lib_handle_events(portMAX_DELAY, &event_flags);

        if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
        {
            has_clients = false;
            if (usb_host_device_free_all() == ESP_OK)
                break;
        }

        if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE && !has_clients)
            break;

        vTaskDelay(pdMS_TO_TICKS(100));
    }

    vTaskDelay(pdMS_TO_TICKS(10));
    Serial.println("正在卸载USB主机驱动");
    ESP_ERROR_CHECK(usb_host_uninstall());
    vTaskDelete(NULL);
}

/**
 * @brief 初始化USB主机功能
 */
void init_usb_host()
{
    printf("USB初始化运行在核心: %d\n", xPortGetCoreID());

    // 创建USB主机任务
    BaseType_t task_created = xTaskCreatePinnedToCore(
        usb_host_task, "usb_host_task", 4096, NULL, 2, NULL, 1);
    assert(task_created == pdPASS);

    int wait_count = 0;
    Serial.println("等待USB存储设备连接...");

    // 等待USB设备连接
    while (true)
    {
        if (g_usb_event_id == USB_EVENT_CONNECTED)
        {
            g_usb_event_id = 5;

            if (!g_msc_device_present)
            {
                g_msc_device_present = true;

                // 安装MSC设备
                esp_err_t err = msc_host_install_device(g_usb_device_address, &g_msc_device);
                if (err != ESP_OK)
                {
                    Serial.printf("MSC设备安装失败: %s\n", esp_err_to_name(err));
                    continue;
                }

                // 配置VFS挂载参数
                const esp_vfs_fat_mount_config_t mount_config = {
                    .format_if_mount_failed = false,
                    .max_files = 3,
                    .allocation_unit_size = 8192,
                };

                // 挂载USB设备
                err = msc_host_vfs_register(g_msc_device, USB_MOUNT_PATH, &mount_config, &g_msc_vfs_handle);
                if (err == ESP_OK)
                {
                    g_usb_mounted = true;
                    Serial.println("USB存储设备挂载成功");
                    break;
                }
                else
                {
                    Serial.printf("USB挂载失败: %s\n", esp_err_to_name(err));
                }
            }
        }

        // 超时处理
        if (wait_count++ > 10)
        {
            Serial.println("USB设备连接超时");
            break;
        }

        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

/**
 * @brief 系统初始化函数
 */
void setup()
{
    Serial.begin(115200);
    vTaskDelay(pdMS_TO_TICKS(500)); // 替换delay为vTaskDelay,更适合FreeRTOS

    // 初始化红外接收
    init_ir_receiver();

    int wakeup_wait_count = 0;

    // 检查是否从深度睡眠唤醒
    esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
    if (wakeup_reason == ESP_SLEEP_WAKEUP_EXT0)
    {
        // 等待用户确认唤醒
        while (1)
        {
            portENTER_CRITICAL(&g_audio_mux);
            uint32_t ir_code = g_ir_receive_code;
            portEXIT_CRITICAL(&g_audio_mux);

            if (ir_code != 0 && ir_code != 0x55)
            {
                Serial.printf("唤醒按键码: 0x%04X\n", ir_code);

                if (ir_code == 0x4050) // OK键确认唤醒
                {
                    portENTER_CRITICAL(&g_audio_mux);
                    g_ir_receive_code = 0;
                    portEXIT_CRITICAL(&g_audio_mux);
                    break;
                }
            }

            vTaskDelay(pdMS_TO_TICKS(1000));
            Serial.printf("请按OK键唤醒,%d秒后将再次进入深度睡眠\n", 10 - wakeup_wait_count);

            if (wakeup_wait_count++ > 10)
                enter_deep_sleep_mode();
        }
        Serial.println("\n=== 从深度睡眠唤醒 ===");
    }
    else
    {
        Serial.println("\n=== 系统首次启动 ===");
    }

    // 初始化USB主机
    init_usb_host();
    vTaskDelay(pdMS_TO_TICKS(500));

    // 初始化I2S音频输出
    init_i2s_audio(I2S_BIT_CLOCK_PIN, I2S_LR_CLOCK_PIN, I2S_DATA_OUT_PIN);
    vTaskDelay(pdMS_TO_TICKS(500));

    // 初始化解码器
    init_audio_decoders();

    // 扫描音频文件
    scan_audio_files(USB_MOUNT_PATH, g_usb_mounted, AUDIO_LIST_CACHE);

    // 重置睡眠状态
    g_pause_start_timestamp = 0;
    g_enter_sleep_flag = false;

    // 创建解码任务
    BaseType_t decode_task_created = xTaskCreatePinnedToCore(
        audio_decode_task, "AudioDecodeTask", DECODE_TASK_STACK_SIZE, nullptr, 5, nullptr, 1);
    assert(decode_task_created == pdPASS);

    Serial.println("音频播放器初始化完成(按暂停/播放键开始播放)");
}

/**
 * @brief 主循环函数
 */

void loop()
{
    // 检查暂停超时
    check_pause_timeout();

    // 处理红外按键事件
    process_ir_key_events();

    vTaskDelay(pdMS_TO_TICKS(10));
}
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:esp32-s3-devkitc-1]
platform = espressif32
board = esp32-s3-devkitc-1
framework = arduino
board_upload.psram_size = 8MB
board_upload.flash_size = 16MB
board_build.arduino.partitions = default_16MB.csv
board_build.arduino.memory_type = qio_opi
build_flags = 
	-DBOARD_HAS_PSRAM
	-mfix-esp32-psram-cache-issue
	-DFLAC__NO_ASM
	-DFLAC__HAS_OGG=0
	-DFLAC__USE_STDINT_H=1
	-O2
	-ffast-math
	-DNDEBUG
	-DARDUINO_LOOP_STACK_SIZE=8192
	-DARDUINO_RUNNING_CORE=1
monitor_speed = 115200
lib_deps = 
	firechip/usb-host-msc@^1.1.4

 # 指定版本的Git库(tag/分支/提交哈希)
 ;https://github.com/pschatzmann/arduino-libflac.git#1.0.1
 ; https://github.com/pschatzmann/arduino-libmad.git#0.7.1
 ; https://github.com/pschatzmann/arduino-libopus.git#main  # main分支

更多推荐