268 lines
17 KiB
C++
268 lines
17 KiB
C++
/*
|
|
* ═══════════════════════════════════════════════════════════════════════════════════
|
|
* PLAYER.HPP - Melody Playback and Control System
|
|
* ═══════════════════════════════════════════════════════════════════════════════════
|
|
*
|
|
* 🎵 THE MELODY MAESTRO OF VESPER 🎵
|
|
*
|
|
* This class manages melody playback, timing control, and coordination with
|
|
* the BellEngine for precise bell activation. It handles melody loading,
|
|
* duration management, and playback state control.
|
|
*
|
|
* 🏗️ ARCHITECTURE:
|
|
* • Clean separation between playback logic and timing engine
|
|
* • FreeRTOS timer-based duration control (saves 4KB RAM vs tasks!)
|
|
* • Dependency injection for loose coupling
|
|
* • Thread-safe state management
|
|
* • Comprehensive melody metadata handling
|
|
*
|
|
* 🎶 KEY FEATURES:
|
|
* • Multi-format melody support with note assignments
|
|
* • Flexible timing control (speed, segments, loops)
|
|
* • Pause/resume functionality
|
|
* • Duration-based automatic stopping
|
|
* • Continuous and finite loop modes
|
|
* • Real-time playback status tracking
|
|
*
|
|
* ⏱️ TIMING MANAGEMENT:
|
|
* • Segment-based playback with configurable pauses
|
|
* • Total duration limiting
|
|
* • Precision timing coordination with BellEngine
|
|
* • Memory-efficient timer implementation
|
|
*
|
|
* 🔗 INTEGRATION:
|
|
* The Player coordinates with BellEngine for precise timing,
|
|
* Communication for command handling, and FileManager for
|
|
* melody file operations.
|
|
*
|
|
* 📋 VERSION: 2.0 (Modular architecture with dependency injection)
|
|
* 📅 DATE: 2025
|
|
* 👨💻 AUTHOR: Advanced Bell Systems
|
|
* ═══════════════════════════════════════════════════════════════════════════════════
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
// ═════════════════════════════════════════════════════════════════════════════════
|
|
// SYSTEM INCLUDES - Core libraries for melody playback
|
|
// ═════════════════════════════════════════════════════════════════════════════════
|
|
#include <Arduino.h> // Arduino core functionality
|
|
#include <vector> // STL vector for melody data storage
|
|
#include <string> // STL string for melody metadata
|
|
#include <cstdint> // Fixed-width integer types
|
|
#include <ArduinoJson.h> // JSON parsing for melody configuration
|
|
#include <ESPAsyncWebServer.h> // WebSocket client handling
|
|
#include <SD.h> // SD card operations for melody files
|
|
#include "freertos/FreeRTOS.h" // FreeRTOS kernel
|
|
#include "freertos/task.h" // FreeRTOS task management
|
|
#include "../Logging/Logging.hpp" // Centralized logging system
|
|
#include "../FileManager/FileManager.hpp" // File operations abstraction
|
|
|
|
// ═════════════════════════════════════════════════════════════════════════════════
|
|
// FORWARD DECLARATIONS - Dependencies injected at runtime
|
|
// ═════════════════════════════════════════════════════════════════════════════════
|
|
class CommunicationRouter; // Command handling and communication
|
|
class BellEngine; // High-precision timing engine
|
|
|
|
// ═════════════════════════════════════════════════════════════════════════════════
|
|
// PLAYER STATUS ENUMERATION
|
|
// ═════════════════════════════════════════════════════════════════════════════════
|
|
/**
|
|
* @enum PlayerStatus
|
|
* @brief Defines the current state of the player
|
|
*/
|
|
enum class PlayerStatus {
|
|
STOPPED, // ⏹️ Not playing, engine stopped
|
|
PLAYING, // ▶️ Actively playing melody
|
|
PAUSED, // ⏸️ Temporarily paused between segments
|
|
STOPPING // 🔄 Soft stop triggered, waiting for melody to complete
|
|
};
|
|
|
|
/**
|
|
* @class Player
|
|
* @brief Melody playback and timing control system
|
|
*
|
|
* The Player class manages all aspects of melody playback including timing,
|
|
* duration control, pause/resume functionality, and coordination with the
|
|
* BellEngine for precise bell activation.
|
|
*
|
|
* Key responsibilities:
|
|
* - Melody metadata management (name, speed, duration, etc.)
|
|
* - Playback state control (play, pause, stop)
|
|
* - Duration-based automatic stopping
|
|
* - Note assignment mapping for bell activation
|
|
* - Integration with BellEngine for precision timing
|
|
*/
|
|
class Player {
|
|
public:
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// CONSTRUCTORS & DEPENDENCY INJECTION
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* @brief Constructor with dependency injection
|
|
* @param comm Pointer to communication manager
|
|
* @param fm Pointer to file manager
|
|
*/
|
|
Player(CommunicationRouter* comm, FileManager* fm);
|
|
|
|
/**
|
|
* @brief Default constructor for backward compatibility
|
|
*
|
|
* When using this constructor, must call setDependencies() before use.
|
|
*/
|
|
Player();
|
|
|
|
/**
|
|
* @brief Set dependencies after construction
|
|
* @param comm Pointer to communication manager
|
|
* @param fm Pointer to file manager
|
|
*/
|
|
void setDependencies(CommunicationRouter* comm, FileManager* fm);
|
|
|
|
/**
|
|
* @brief Set BellEngine reference for precision timing
|
|
* @param engine Pointer to BellEngine instance
|
|
*/
|
|
void setBellEngine(BellEngine* engine) { _bellEngine = engine; }
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// MELODY METADATA - Public access for compatibility
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
uint16_t id; // 🏷️ Internal ID of the selected melody
|
|
std::string name; // 🏵️ Display name of the melody
|
|
std::string uid; // 🆔 Unique identifier from Firestore
|
|
std::string url; // 🌐 Download URL for melody binary
|
|
uint16_t noteAssignments[16]; // 🎹 Note-to-bell mapping configuration
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// TIMING CONFIGURATION
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
uint16_t speed; // ⏱️ Time per beat in milliseconds
|
|
uint32_t segment_duration; // ⏳ Duration per loop segment (milliseconds)
|
|
uint32_t pause_duration; // ⏸️ Pause between segments (milliseconds)
|
|
uint32_t total_duration; // ⏰ Total runtime limit (milliseconds)
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// RUNTIME STATE TRACKING
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
uint64_t segmentCmpltTime; // ✅ Timestamp of last segment completion
|
|
uint64_t segmentStartTime; // 🚀 Timestamp when current segment started
|
|
uint64_t startTime; // 🏁 Timestamp when melody playback began
|
|
uint64_t pauseTime; // ⏸️ Timestamp when melody was paused
|
|
bool isPlaying; // ▶️ Currently playing indicator
|
|
bool isPaused; // ⏸️ Currently paused indicator
|
|
bool hardStop; // 🚑 Emergency stop flag
|
|
bool continuous_loop; // 🔄 Continuous loop mode flag
|
|
bool infinite_play; // ∞ Infinite playback mode flag
|
|
PlayerStatus _status; // 📊 Current player status
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// DESTRUCTOR
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
/**
|
|
* @brief Destructor - Clean up resources
|
|
*
|
|
* Ensures proper cleanup of timers and resources.
|
|
*/
|
|
~Player();
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// INITIALIZATION & CONTROL METHODS
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
/** @brief Initialize playback control system */
|
|
void begin();
|
|
|
|
/** @brief Start melody playback */
|
|
void play();
|
|
|
|
/** @brief Stop melody playback gracefully */
|
|
void stop();
|
|
|
|
/** @brief Force immediate stop */
|
|
void forceStop();
|
|
|
|
/** @brief Pause current playback */
|
|
void pause();
|
|
|
|
/** @brief Resume paused playback */
|
|
void unpause();
|
|
|
|
/** @brief Handle JSON commands from communication layer */
|
|
bool command(JsonVariant data);
|
|
|
|
/** @brief Set melody attributes from JSON configuration */
|
|
void setMelodyAttributes(JsonVariant doc);
|
|
|
|
/** @brief Load melody data into RAM for playback */
|
|
void loadMelodyInRAM();
|
|
|
|
/** @brief Static timer callback for FreeRTOS duration control */
|
|
static void durationTimerCallback(TimerHandle_t xTimer);
|
|
|
|
// ═════════════════════════════════════════════════════════════════════════════════
|
|
// STATUS QUERY METHODS
|
|
// ═════════════════════════════════════════════════════════════════════════════════
|
|
|
|
/** @brief Get current player status */
|
|
PlayerStatus getStatus() const { return _status; }
|
|
|
|
/** @brief Check if player is currently playing */
|
|
bool isCurrentlyPlaying() const { return _status == PlayerStatus::PLAYING; }
|
|
|
|
/** @brief Check if player is currently paused */
|
|
bool isCurrentlyPaused() const { return _status == PlayerStatus::PAUSED; }
|
|
|
|
/** @brief Check if player is in stopping state (soft stop triggered) */
|
|
bool isCurrentlyStopping() const { return _status == PlayerStatus::STOPPING; }
|
|
|
|
/** @brief Check if player is completely stopped */
|
|
bool isCurrentlyStopped() const { return _status == PlayerStatus::STOPPED; }
|
|
|
|
/** @brief BellEngine callback when melody loop actually completes */
|
|
void onMelodyLoopCompleted();
|
|
|
|
/** @brief Calculate the projected total run time of current playback */
|
|
uint64_t calculateProjectedRunTime() const;
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// HEALTH CHECK METHOD
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
/** @brief Check if Player is in healthy state */
|
|
bool isHealthy() const;
|
|
|
|
private:
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// PRIVATE DEPENDENCIES AND DATA
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
CommunicationRouter* _commManager; // 📡 Communication system reference
|
|
FileManager* _fileManager; // 📁 File operations reference
|
|
BellEngine* _bellEngine; // 🔥 High-precision timing engine reference
|
|
|
|
std::vector<uint16_t> _melodySteps; // 🎵 Melody data owned by Player
|
|
TimerHandle_t _durationTimerHandle = NULL; // ⏱️ FreeRTOS timer (saves 4KB vs task!)
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
// PRIVATE HELPER METHODS
|
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
/** @brief Check if it's time to stop based on duration */
|
|
bool timeToStop(unsigned long now);
|
|
|
|
/** @brief Check if it's time to pause based on segment timing */
|
|
bool timeToPause(unsigned long now);
|
|
|
|
/** @brief Check if it's time to resume from pause */
|
|
bool timeToResume(unsigned long now);
|
|
|
|
/** @brief Update player status and notify clients if changed */
|
|
void setStatus(PlayerStatus newStatus);
|
|
};
|
|
|
|
// ═══════════════════════════════════════════════════════════════════════════════════
|
|
// END OF PLAYER.HPP
|
|
// ═══════════════════════════════════════════════════════════════════════════════════
|