2019-11-14 19:42:02 +00:00
|
|
|
#include <playlist.h>
|
|
|
|
#include "spi_master.h"
|
|
|
|
#include "config.h"
|
|
|
|
#include <SD.h>
|
|
|
|
#include <algorithm>
|
2019-11-16 22:03:13 +00:00
|
|
|
#include <ArduinoJson.h>
|
2019-11-14 19:42:02 +00:00
|
|
|
|
|
|
|
Playlist::Playlist(String path) {
|
|
|
|
// Add files to _files
|
|
|
|
SPIMaster::select_sd();
|
|
|
|
TRACE("Examining folder %s...\n", path.c_str());
|
|
|
|
if (!path.startsWith("/")) path = String("/") + path;
|
|
|
|
if (!SD.exists(path)) {
|
|
|
|
DEBUG("Could not open path '%s'.\n", path.c_str());
|
|
|
|
SPIMaster::select_sd(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
File dir = SD.open(path);
|
|
|
|
File entry;
|
|
|
|
while (entry = dir.openNextFile()) {
|
|
|
|
String filename = entry.name();
|
|
|
|
filename = filename.substring(path.length() + 1);
|
|
|
|
String ext = filename.substring(filename.length() - 4);
|
|
|
|
if (!entry.isDirectory() &&
|
|
|
|
!filename.startsWith(".") &&
|
|
|
|
( ext.equals(".mp3") ||
|
|
|
|
ext.equals(".ogg") ||
|
|
|
|
ext.equals(".wma") ||
|
|
|
|
ext.equals(".mp4") ||
|
|
|
|
ext.equals(".mpa"))) {
|
|
|
|
TRACE(" Adding entry %s\n", entry.name());
|
|
|
|
_files.push_back(entry.name());
|
2019-11-16 22:03:13 +00:00
|
|
|
bool non_ascii_chars = false;
|
|
|
|
for(int i=0; i<filename.length(); i++) {
|
|
|
|
char c = filename.charAt(i);
|
|
|
|
if (c < 0x20 || c >= 0x7F) {
|
|
|
|
non_ascii_chars = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (non_ascii_chars) {
|
|
|
|
ERROR("WARNING: File '%s' contains non-ascii chars!\n", filename.c_str());
|
|
|
|
}
|
2019-11-14 19:42:02 +00:00
|
|
|
} else {
|
|
|
|
TRACE(" Ignoring entry %s\n", filename.c_str());
|
|
|
|
}
|
|
|
|
entry.close();
|
|
|
|
}
|
|
|
|
dir.close();
|
|
|
|
SPIMaster::select_sd(false);
|
|
|
|
std::sort(_files.begin(), _files.end());
|
|
|
|
}
|
|
|
|
|
2019-11-16 22:03:13 +00:00
|
|
|
void Playlist::start() {
|
|
|
|
_started = true;
|
|
|
|
}
|
|
|
|
|
2019-11-14 19:42:02 +00:00
|
|
|
bool Playlist::has_track_prev() {
|
|
|
|
return _current_track > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Playlist::has_track_next() {
|
|
|
|
return _current_track < _files.size()-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Playlist::track_prev() {
|
|
|
|
if (_current_track > 0) {
|
|
|
|
_current_track--;
|
|
|
|
_position = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Playlist::track_next() {
|
|
|
|
if (_current_track < _files.size()-1) {
|
|
|
|
_current_track++;
|
|
|
|
_position = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-11-16 22:03:13 +00:00
|
|
|
bool Playlist::set_track(uint8_t track) {
|
|
|
|
if (track < _files.size()) {
|
|
|
|
_current_track = track;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-11-14 19:42:02 +00:00
|
|
|
void Playlist::track_restart() {
|
|
|
|
_position = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::shuffle(uint8_t random_offset) {
|
|
|
|
DEBUG("Shuffling the playlist with an offset of %d...\n", random_offset);
|
|
|
|
for (int i=random_offset; i<_files.size(); i++) {
|
|
|
|
int j = random(random_offset, _files.size()-1);
|
|
|
|
if (i!=j) {
|
|
|
|
TRACE(" Swapping elements %d and %d.\n", i, j);
|
|
|
|
String temp = _files[i];
|
|
|
|
_files[i] = _files[j];
|
|
|
|
_files[j] = temp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_shuffled = true;
|
|
|
|
TRACE("Done.\n");
|
|
|
|
}
|
|
|
|
|
2019-11-16 22:03:13 +00:00
|
|
|
void Playlist::advent_shuffle(uint8_t day) {
|
|
|
|
if (day > 24) day = 24;
|
|
|
|
|
|
|
|
if (day > _files.size()) return;
|
|
|
|
|
|
|
|
_files.insert(_files.begin(), _files[day - 1]);
|
|
|
|
_files.erase(_files.begin() + day - 1, _files.end());
|
|
|
|
}
|
|
|
|
|
2019-11-14 19:42:02 +00:00
|
|
|
void Playlist::reset() {
|
|
|
|
std::sort(_files.begin(), _files.end());
|
|
|
|
_current_track = 0;
|
|
|
|
_position = 0;
|
|
|
|
_shuffled = false;
|
2019-11-16 22:03:13 +00:00
|
|
|
_started = false;
|
2019-11-14 19:42:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
String Playlist::get_current_file() {
|
|
|
|
return _files[_current_track];
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Playlist::get_position() {
|
|
|
|
return _position;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::set_position(uint32_t p) {
|
|
|
|
_position = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Playlist::is_fresh() {
|
2019-11-16 22:03:13 +00:00
|
|
|
return !_shuffled && !_started && _position==0 && _current_track==0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::dump() {
|
|
|
|
for (int i=0; i<_files.size(); i++) {
|
|
|
|
DEBUG(" %02d %2s %s\n", i+1, (i==_current_track) ? "->" : "", _files[i].c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Playlist::json(JsonObject json) {
|
|
|
|
json["_type"] = "playlist";
|
|
|
|
JsonArray files = json.createNestedArray("files");
|
|
|
|
for (String file: _files) {
|
|
|
|
files.add(file);
|
|
|
|
}
|
|
|
|
json["current_track"] = _current_track;
|
|
|
|
json["has_track_next"] = has_track_next();
|
|
|
|
json["has_track_prev"] = has_track_prev();
|
|
|
|
}
|