1. 完成了对音频播放类的完整C++封装,测试通过

2. 修复了LVGL渲染类当中的一些小bug
3. 增加了一些CPU资源占用的日志打印函数,运行在主线程当中
4. 完善了底层通信类的封装,基于websocket,尚未测试
This commit is contained in:
Misaki
2025-09-12 02:11:50 +08:00
parent 4985fee7c2
commit 97fe13da26
16 changed files with 1297 additions and 85 deletions
@@ -0,0 +1,248 @@
//
// Created by misaki on 2025/9/9.
//
#include "AudioOutput.h"
#include "esp_log.h"
#include <memory>
#include "PCM5101.h"
// 初始化静态成员
AudioOutput* AudioOutput::instance = nullptr;
std::mutex AudioOutput::instanceMutex;
AudioOutput* AudioOutput::getInstance() {
std::lock_guard<std::mutex> lock(instanceMutex);
if (instance == nullptr) {
instance = new AudioOutput();
}
return instance;
}
AudioOutput::AudioOutput()
: currentState(AudioState::IDLE),
currentVolume(Volume_MAX - 2),
hardwareInitialized(false) {
// 初始化硬件
init();
}
AudioOutput::~AudioOutput() {
// 停止播放并清理资源
stop();
}
bool AudioOutput::init() {
std::lock_guard<std::mutex> lock(stateMutex);
if (hardwareInitialized) {
ESP_LOGI("AudioOutput", "Audio hardware already initialized");
return true;
}
// 初始化SD卡
SDFileManager::getInstance()->tryInitSDCard();
// 初始化音频硬件
Audio_Init();
hardwareInitialized = true;
ESP_LOGI("AudioOutput", "Audio hardware initialized successfully");
return hardwareInitialized;
}
void AudioOutput::tryInitAudioOutput() {
ESP_LOGI("AudioOutput", "Trying to initialize audio output......");
}
bool AudioOutput::playSync(const char* directory, const char* fileName) {
std::lock_guard<std::mutex> lock(stateMutex);
if (!hardwareInitialized) {
ESP_LOGE("AudioOutput", "Audio hardware not initialized");
return false;
}
// 停止当前播放 TODO:有bug,需要fix
// stop();
// 播放新文件
Play_Music(directory, fileName);
if (audio_player_get_state() == AUDIO_PLAYER_STATE_PLAYING) {
currentState = AudioState::PLAYING;
currentFilePath = std::string(directory) + "/" + fileName;
return true;
}
currentState = AudioState::ERROR;
return false;
}
void AudioOutput::playAsync(const char* directory, const char* fileName, AudioCallback callback) {
ThreadConfig config = getThreadConfig("play_async");
ThreadManager::createThread(config, [this, directory = std::string(directory),
fileName = std::string(fileName), callback]() {
this->playInternal(directory.c_str(), fileName.c_str(), callback);
}).detach();
}
void AudioOutput::playInternal(const char* directory, const char* fileName, AudioCallback callback) {
bool success = false;
AudioState finalState = AudioState::ERROR;
{
std::lock_guard<std::mutex> lock(stateMutex);
if (!hardwareInitialized) {
ESP_LOGE("AudioOutput", "Audio hardware not initialized");
finalState = AudioState::ERROR;
} else {
// 停止当前播放
// stop();
// 播放新文件
Play_Music(directory, fileName);
if (audio_player_get_state() == AUDIO_PLAYER_STATE_PLAYING) {
currentState = AudioState::PLAYING;
currentFilePath = std::string(directory) + "/" + fileName;
success = true;
finalState = AudioState::PLAYING;
} else {
currentState = AudioState::ERROR;
finalState = AudioState::ERROR;
}
}
}
if (callback) {
callback(finalState, success ? currentFilePath.c_str() : nullptr);
}
}
bool AudioOutput::pause() {
std::lock_guard<std::mutex> lock(stateMutex);
if (currentState != AudioState::PLAYING) {
return false;
}
Music_pause();
if (audio_player_get_state() == AUDIO_PLAYER_STATE_PAUSE) {
currentState = AudioState::PAUSED;
return true;
}
return false;
}
bool AudioOutput::resume() {
std::lock_guard<std::mutex> lock(stateMutex);
if (currentState != AudioState::PAUSED) {
return false;
}
Music_resume();
if (audio_player_get_state() == AUDIO_PLAYER_STATE_PLAYING) {
currentState = AudioState::PLAYING;
return true;
}
return false;
}
bool AudioOutput::stop() {
std::lock_guard<std::mutex> lock(stateMutex);
if (currentState == AudioState::IDLE || currentState == AudioState::STOPPED) { // 如果当前状态为IDLE或STOPPED,则直接返回成功
return true;
}
// 暂停播放
Music_pause(); // 内部已经完成关闭文件的操作了
currentState = AudioState::STOPPED;
currentFilePath.clear();
return true;
}
bool AudioOutput::setVolume(uint8_t volume) {
std::lock_guard<std::mutex> lock(stateMutex);
if (volume > Volume_MAX) {
ESP_LOGE("AudioOutput", "Volume value %d is out of range (0-%d)", volume, Volume_MAX);
return false;
}
Volume_adjustment(volume);
currentVolume = volume;
return true;
}
uint8_t AudioOutput::getVolume() const {
std::lock_guard<std::mutex> lock(stateMutex);
return currentVolume;
}
uint8_t AudioOutput::getMaxVolume() const {
return Volume_MAX;
}
uint32_t AudioOutput::getDuration() const {
return Music_Duration();
}
uint32_t AudioOutput::getElapsed() const {
return Music_Elapsed();
}
uint16_t AudioOutput::getEnergy() const {
return Music_Energy();
}
AudioState AudioOutput::getState() const {
std::lock_guard<std::mutex> lock(stateMutex);
return currentState;
}
bool AudioOutput::isPlaying() const {
std::lock_guard<std::mutex> lock(stateMutex);
return currentState == AudioState::PLAYING;
}
bool AudioOutput::isPaused() const {
std::lock_guard<std::mutex> lock(stateMutex);
return currentState == AudioState::PAUSED;
}
bool AudioOutput::isStopped() const {
std::lock_guard<std::mutex> lock(stateMutex);
return currentState == AudioState::STOPPED || currentState == AudioState::IDLE;
}
bool AudioOutput::isFinished() const {
return Music_Next_Flag;
}
ThreadConfig AudioOutput::getThreadConfig(const char* operation) {
ThreadConfig config;
config.name = "audio_" + std::string(operation);
config.core_id = -1; // 不绑定核心
config.stack_size = 4096;
config.priority = 5;
config.inherit_cfg = false;
return config;
}
void AudioOutput::setState(AudioState newState) {
std::lock_guard<std::mutex> lock(stateMutex);
currentState = newState;
}
@@ -0,0 +1,202 @@
//
// Created by misaki on 2025/9/9.
//
#pragma once
#include <mutex>
#include <functional>
#include <string>
#include "ThreadManager.h"
#include "SDFileManager.h"
// 音频播放状态枚举
enum class AudioState {
IDLE, // 音频未播放
PLAYING, // 音频播放中
PAUSED, // 音频暂停中
STOPPED, // 音频已停止
ERROR // 音频播放错误
};
// 音频播放回调函数类型
using AudioCallback = std::function<void(AudioState state, const char* filePath)>;
/*
* 回调函数示例:
void audioCallback(AudioState state, const char* filePath) {
switch (state) {
case AudioState::PLAYING:
ESP_LOGI("Example", "Started playing: %s", filePath);
break;
case AudioState::PAUSED:
ESP_LOGI("Example", "Paused: %s", filePath);
break;
case AudioState::STOPPED:
ESP_LOGI("Example", "Stopped: %s", filePath);
break;
case AudioState::ERROR:
ESP_LOGE("Example", "Error playing: %s", filePath);
break;
default:
break;
}
}
*/
/**
* 本模块为音频输出模块
* 支持同步,异步音频输出
*
* 注意:底层C驱动任务运行在核0, 请不要把例如lvgl这种高CPU占比的任务放在核0中,避免资源抢占导致播放卡顿
*/
class AudioOutput {
public:
// 删除拷贝构造函数和赋值运算符
AudioOutput(AudioOutput const&) = delete;
AudioOutput& operator=(AudioOutput const&) = delete;
/**
* 获取单例实例
* @return AudioOutput实例
*/
static AudioOutput* getInstance();
/**
* 初始化音频输出
* @return 是否成功
*/
bool init();
// try to init AudioOutput
void tryInitAudioOutput();
/**
* 同步播放音频文件
* @param directory 目录路径
* @param fileName 文件名
* @return 是否成功
*/
bool playSync(const char* directory, const char* fileName);
/**
* 异步播放音频文件
* @param directory 目录路径
* @param fileName 文件名
* @param callback 回调函数
*/
void playAsync(const char* directory, const char* fileName, AudioCallback callback = nullptr);
/**
* 暂停播放
* @return 是否成功
*/
bool pause();
/**
* 恢复播放
* @return 是否成功
*/
bool resume();
/**
* 停止播放
* @return 是否成功
*/
bool stop();
/**
* 设置音量
* @param volume 音量值 (0-100)
* @return 是否成功
*/
bool setVolume(uint8_t volume);
/**
* 获取当前音量
* @return 音量值
*/
uint8_t getVolume() const;
/**
* 获取最大音量
* @return 最大音量值
*/
uint8_t getMaxVolume() const;
/**
* 获取音频总时长
* @return 总时长(毫秒)
*/
uint32_t getDuration() const;
/**
* 获取已播放时长
* @return 已播放时长(毫秒)
*/
uint32_t getElapsed() const;
/**
* 获取音频能量值
* @return 能量值
*/
uint16_t getEnergy() const;
/**
* 获取当前播放状态
* @return 播放状态
*/
AudioState getState() const;
/**
* 检查是否正在播放
* @return 是否正在播放
*/
bool isPlaying() const;
/**
* 检查是否暂停
* @return 是否暂停
*/
bool isPaused() const;
/**
* 检查是否停止
* @return 是否停止
*/
bool isStopped() const;
/**
* 检查播放是否完成
* @return 是否完成
*/
bool isFinished() const;
private:
// 私有构造函数
AudioOutput();
~AudioOutput();
// 初始化线程配置
ThreadConfig getThreadConfig(const char* operation);
// 内部播放实现
void playInternal(const char* directory, const char* fileName, AudioCallback callback);
// 状态转换辅助方法
void setState(AudioState newState);
// 单例实例指针
static AudioOutput* instance;
static std::mutex instanceMutex;
// 成员变量
mutable std::mutex stateMutex;
AudioState currentState;
std::string currentFilePath;
uint8_t currentVolume;
bool hardwareInitialized;
};