/* * ═══════════════════════════════════════════════════════════════════════════════════ * 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 core functionality #include // STL vector for melody data storage #include // STL string for melody metadata #include // Fixed-width integer types #include // JSON parsing for melody configuration #include // WebSocket client handling #include // 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 _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 // ═══════════════════════════════════════════════════════════════════════════════════