Files
ESP32_ZACUS/ui_freenove_allinone/include/audio/audio_manager.h
T
2026-03-11 00:03:58 +01:00

129 lines
4.3 KiB
C++

// audio_manager.h - Freenove audio playback manager backed by ESP32-audioI2S.
#pragma once
#include <Arduino.h>
#include <memory>
#include "ui_freenove_config.h"
class Audio;
class AudioManager {
public:
using AudioDoneCallback = void (*)(const char* track, void* ctx);
using AudioMetadataCallback = void (*)(const char* key, const char* value, void* ctx);
AudioManager();
~AudioManager();
AudioManager(const AudioManager&) = delete;
AudioManager& operator=(const AudioManager&) = delete;
AudioManager(AudioManager&&) = delete;
AudioManager& operator=(AudioManager&&) = delete;
bool begin();
bool play(const char* filename);
bool playUrl(const char* url);
bool playDiagnosticTone();
void stop();
void update();
bool isPlaying() const;
void releaseOutputResources();
bool restoreOutputResources();
void setVolume(uint8_t volume);
uint8_t volume() const;
const char* currentTrack() const;
bool setOutputProfile(uint8_t profile_index);
uint8_t outputProfile() const;
uint8_t outputProfileCount() const;
const char* outputProfileLabel(uint8_t profile_index) const;
bool setFxProfile(uint8_t fx_profile_index);
uint8_t fxProfile() const;
uint8_t fxProfileCount() const;
const char* fxProfileLabel(uint8_t fx_profile_index) const;
const char* activeCodec() const;
uint16_t activeBitrateKbps() const;
uint32_t underrunCount() const;
void setAudioDoneCallback(AudioDoneCallback cb, void* ctx);
void setMetadataCallback(AudioMetadataCallback cb, void* ctx);
void notifyMetadata(const char* key, const char* value);
private:
enum class AudioCodec : uint8_t {
kUnknown = 0U,
kMp3 = 1U,
kWav = 2U,
kAac = 3U,
kFlac = 4U,
};
bool ensurePlayer();
bool requestPlay(const char* filename, bool diagnostic_tone);
bool requestPlayUrl(const char* url);
void applyOutputProfile();
void applyFxProfile();
bool normalizeTrackPath(const char* input, String& out_path, bool& out_use_sd) const;
bool trackExists(const String& path, bool use_sd) const;
bool detectTrackCodecAndBitrate(const String& path, bool use_sd, AudioCodec& codec, uint16_t& bitrate_kbps) const;
const char* codecLabel(AudioCodec codec) const;
bool openTrack(const String& path, bool use_sd);
bool beginTrackPlayback(const String& path,
bool use_sd,
AudioCodec codec,
uint16_t bitrate_kbps,
bool diagnostic_tone);
void scheduleTrackStart(const String& path,
bool use_sd,
AudioCodec codec,
uint16_t bitrate_kbps,
bool diagnostic_tone,
uint32_t earliest_ms);
void tryStartPendingTrack(uint32_t now_ms);
void createRtosState();
void destroyRtosState();
bool startAudioPump();
void stopAudioPump();
void audioPumpLoop();
static void audioPumpTaskEntry(void* arg);
void processPendingPlaybackEvents();
void enqueuePlaybackDone(const char* track);
bool takeStateLock(uint32_t timeout_ms) const;
void releaseStateLock() const;
void clearTrackState();
void finishPlaybackAndNotify();
std::unique_ptr<Audio> player_;
struct AudioRtosState;
std::unique_ptr<AudioRtosState> rtos_state_;
bool pump_task_enabled_ = false;
bool begun_ = false;
bool playing_ = false;
bool using_diagnostic_tone_ = false;
uint8_t volume_ = FREENOVE_AUDIO_MAX_VOLUME;
uint8_t fx_profile_ = 0U;
uint8_t output_profile_ = 0U;
String current_track_;
AudioCodec active_codec_ = AudioCodec::kUnknown;
uint16_t active_bitrate_kbps_ = 0U;
bool active_use_sd_ = false;
bool pending_start_ = false;
String pending_track_;
AudioCodec pending_codec_ = AudioCodec::kUnknown;
uint16_t pending_bitrate_kbps_ = 0U;
bool pending_use_sd_ = false;
bool pending_diagnostic_tone_ = false;
uint32_t reopen_earliest_ms_ = 0U;
bool restore_pending_ = false;
uint32_t restore_retry_not_before_ms_ = 0U;
uint32_t underrun_count_ = 0U;
uint32_t underrun_last_note_ms_ = 0U;
uint32_t underrun_last_log_ms_ = 0U;
mutable char current_track_snapshot_[96] = {0};
AudioDoneCallback done_cb_ = nullptr;
void* done_ctx_ = nullptr;
AudioMetadataCallback metadata_cb_ = nullptr;
void* metadata_ctx_ = nullptr;
String last_stream_url_;
};