Files
project-vesper/vesper/src/Player/Player.hpp

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
// ═══════════════════════════════════════════════════════════════════════════════════