#pragma once extern std::vector melody_steps; void PublishMqtt(const char * data); class Player { public: uint16_t id; // The (internal) ID of the selected melody. Not specificly used anywhere atm. Might be used later. std::string name = "melody1"; // Name of the Melody saved. Will be used to read the file: /name.bin uint16_t speed = 500; // Time to wait per beat. (In Miliseconds) uint32_t duration = 15000; // Total Duration that program will run (In Miliseconds) uint32_t loop_duration = 0; // Duration of the playback per segment uint32_t interval = 0; // Indicates the Duration of the Interval between finished segments, IF "inf" is true bool infinite_play = false; // Infinite Loop Indicator (If True the melody will loop forever or until stoped, with pauses of "interval" in between loops) bool isPlaying = false; // Indicates if the Melody is actually Playing right now. bool isPaused = false; // If playing, indicates if the Melody is Paused uint64_t startTime = 0; // The time-point the Melody started Playing uint64_t loopStartTime = 0; // The time-point the current segment started Playing bool hardStop = false; // Flags a hardstop, immediately. uint64_t pauseTime = 0; // The time-point the melody paused void play() { isPlaying = true; hardStop = false; startTime = loopStartTime = millis(); LOG_DEBUG("Plbck: PLAY"); } void forceStop() { hardStop = true; isPlaying = false; LOG_DEBUG("Plbck: FORCE STOP"); } void stop() { hardStop = false; isPlaying = false; LOG_DEBUG("Plbck: STOP"); } void pause() { isPaused = true; LOG_DEBUG("Plbck: PAUSE"); } void unpause() { isPaused = false; loopStartTime = millis(); LOG_DEBUG("Plbck: RESUME"); } // Handles Incoming Commands to PLAY or STOP void command(JsonVariant content){ String action = content["action"]; LOG_DEBUG("Incoming Command: %s", action); if (action == "play") { play(); PublishMqtt("OK - PLAY"); } else if (action == "stop") { forceStop(); PublishMqtt("OK - STOP"); } } // Sets incoming Attributes for the Melody, into the class' variables. void setMelodyAttributes(JsonVariant doc){ if (doc.containsKey("name")) { name = doc["name"].as(); } if (doc.containsKey("id")) { id = doc["id"].as(); } if (doc.containsKey("duration")) { duration = doc["duration"].as(); } if (doc.containsKey("infinite")) { infinite_play = doc["infinite"].as(); } if (doc.containsKey("interval")) { interval = doc["interval"].as(); } if (doc.containsKey("speed")) { speed = doc["speed"].as(); } if (doc.containsKey("loop_dur")) { loop_duration = doc["loop_dur"].as(); } // Print Just for Debugging Purposes LOG_DEBUG("Name: %s, ID: %d, Total Duration: %lu, Loop Duration: %lu, Interval: %d, Speed: %d, Inf: %s\n", name.c_str(), id, duration, loop_duration, interval, speed, infinite_play ? "true" : "false" ); } // Loads the Selected melody from a .bin file on SD into RAM void loadMelodyInRAM(std::vector &melody_steps) { String filePath = "/melodies/" + String(name.c_str()) + ".bin"; File bin_file = SD.open(filePath.c_str(), FILE_READ); if (!bin_file) { LOG_ERROR("Failed to open file: %s", filePath.c_str()); return; } size_t fileSize = bin_file.size(); if (fileSize % 2 != 0) { LOG_ERROR("Invalid file size: %u (not a multiple of 2)", fileSize); bin_file.close(); return; } melody_steps.resize(fileSize / 2); for (size_t i = 0; i < melody_steps.size(); i++) { uint8_t high = bin_file.read(); uint8_t low = bin_file.read(); melody_steps[i] = (high << 8) | low; } LOG_INFO("Melody Load Successful"); bin_file.close(); } };