Added sleep mode for VS1053, HTTP server, tar upload, JSON status, RFID card removal debouncing, ...
This commit is contained in:
parent
7695e6196c
commit
25fa963752
@ -16,6 +16,7 @@ private:
|
|||||||
void _check_buttons();
|
void _check_buttons();
|
||||||
uint32_t _get_rfid_card_uid();
|
uint32_t _get_rfid_card_uid();
|
||||||
uint32_t _last_rfid_card_uid = 0;
|
uint32_t _last_rfid_card_uid = 0;
|
||||||
|
uint8_t _no_rfid_card_count = 0;
|
||||||
Player* _player;
|
Player* _player;
|
||||||
unsigned long _last_rfid_scan_at = 0;
|
unsigned long _last_rfid_scan_at = 0;
|
||||||
String _serial_buffer = String();
|
String _serial_buffer = String();
|
||||||
@ -26,5 +27,6 @@ private:
|
|||||||
bool _check_button(uint8_t btn);
|
bool _check_button(uint8_t btn);
|
||||||
public:
|
public:
|
||||||
Controller(Player* p, MCP* m);
|
Controller(Player* p, MCP* m);
|
||||||
|
String rfid_uid() { return String(_last_rfid_card_uid, HEX); }
|
||||||
void loop();
|
void loop();
|
||||||
};
|
};
|
||||||
|
28
include/http_server.h
Normal file
28
include/http_server.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <ESP8266WebServer.h>
|
||||||
|
#include "spi_master.h"
|
||||||
|
#include "player.h"
|
||||||
|
#include "controller.h"
|
||||||
|
#include <SD.h>
|
||||||
|
#include <WiFiClient.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESP8266mDNS.h>
|
||||||
|
|
||||||
|
class HTTPServer {
|
||||||
|
private:
|
||||||
|
ESP8266WebServer* _http_server;
|
||||||
|
void _handle_upload();
|
||||||
|
uint16_t _chunk_length;
|
||||||
|
uint8_t* _chunk;
|
||||||
|
uint32_t _file_size;
|
||||||
|
uint32_t _file_size_done;
|
||||||
|
bool _need_header;
|
||||||
|
uint32_t _upload_position;
|
||||||
|
void _handle_index();
|
||||||
|
void _handle_status();
|
||||||
|
Player* _player;
|
||||||
|
Controller* _controller;
|
||||||
|
public:
|
||||||
|
HTTPServer(Player* p, Controller* c);
|
||||||
|
void loop();
|
||||||
|
};
|
@ -10,6 +10,7 @@
|
|||||||
#define SCI_STATUS 0x01
|
#define SCI_STATUS 0x01
|
||||||
#define SCI_CLOCKF 0x03
|
#define SCI_CLOCKF 0x03
|
||||||
#define SCI_DECODE_TIME 0x04
|
#define SCI_DECODE_TIME 0x04
|
||||||
|
#define SCI_AUDATA 0x05
|
||||||
#define SCI_VOL 0x0B
|
#define SCI_VOL 0x0B
|
||||||
#define SCI_WRAMADDR 0x07
|
#define SCI_WRAMADDR 0x07
|
||||||
#define SCI_WRAM 0x06
|
#define SCI_WRAM 0x06
|
||||||
@ -30,7 +31,8 @@
|
|||||||
class Player {
|
class Player {
|
||||||
private:
|
private:
|
||||||
enum state { uninitialized, idle, playing, stopping,
|
enum state { uninitialized, idle, playing, stopping,
|
||||||
system_sound_while_playing, system_sound_while_stopped };
|
system_sound_while_playing, system_sound_while_stopped,
|
||||||
|
sleeping };
|
||||||
struct album_state {
|
struct album_state {
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
uint32_t position;
|
uint32_t position;
|
||||||
@ -51,12 +53,15 @@ private:
|
|||||||
void _flush(uint count, int8_t fill_byte);
|
void _flush(uint count, int8_t fill_byte);
|
||||||
void _set_last_track(const char* album, uint8_t track, uint32_t position);
|
void _set_last_track(const char* album, uint8_t track, uint32_t position);
|
||||||
std::map<String, album_state> _last_tracks;
|
std::map<String, album_state> _last_tracks;
|
||||||
|
String _random_album();
|
||||||
void _play_file(String filename, uint32_t offset);
|
void _play_file(String filename, uint32_t offset);
|
||||||
uint32_t _id3_tag_offset(File f);
|
uint32_t _id3_tag_offset(File f);
|
||||||
void _finish_playing();
|
void _finish_playing();
|
||||||
void _finish_stopping();
|
void _finish_stopping();
|
||||||
void _mute();
|
void _mute();
|
||||||
void _unmute();
|
void _unmute();
|
||||||
|
void _sleep();
|
||||||
|
void _wakeup();
|
||||||
|
|
||||||
SPISettings _spi_settings_slow = SPISettings(250000, MSBFIRST, SPI_MODE0);
|
SPISettings _spi_settings_slow = SPISettings(250000, MSBFIRST, SPI_MODE0);
|
||||||
SPISettings _spi_settings_fast = SPISettings(4000000, MSBFIRST, SPI_MODE0);
|
SPISettings _spi_settings_fast = SPISettings(4000000, MSBFIRST, SPI_MODE0);
|
||||||
@ -75,18 +80,25 @@ private:
|
|||||||
uint16_t _stop_delay;
|
uint16_t _stop_delay;
|
||||||
uint32_t _skip_to;
|
uint32_t _skip_to;
|
||||||
MCP* _mcp;
|
MCP* _mcp;
|
||||||
|
unsigned long _stopped_at;
|
||||||
public:
|
public:
|
||||||
Player(MCP* m);
|
Player(MCP* m);
|
||||||
void vol_up();
|
void vol_up();
|
||||||
void vol_down();
|
void vol_down();
|
||||||
void track_next();
|
void track_next();
|
||||||
void track_prev();
|
void track_prev();
|
||||||
|
bool is_playing();
|
||||||
|
|
||||||
bool play_album(String album);
|
bool play_album(String album);
|
||||||
|
void play_random_album();
|
||||||
bool play_song(String album, uint8_t song_index, uint32_t offset=0);
|
bool play_song(String album, uint8_t song_index, uint32_t offset=0);
|
||||||
void play_system_sound(String filename);
|
void play_system_sound(String filename);
|
||||||
void stop();
|
void stop();
|
||||||
bool loop();
|
bool loop();
|
||||||
void set_volume(uint8_t vol, bool save = true);
|
void set_volume(uint8_t vol, bool save = true);
|
||||||
std::list<String> ls(String path);
|
std::list<String> ls(String path, bool withFiles=true, bool withDirs=true, bool withHidden=false);
|
||||||
|
String album() { return _playing_album; }
|
||||||
|
uint8_t track() { return _playing_index; }
|
||||||
|
uint32_t position() { return _current_play_position; }
|
||||||
|
uint8_t volume() { return _volume; }
|
||||||
};
|
};
|
||||||
|
@ -53,12 +53,18 @@ void Controller::_check_rfid() {
|
|||||||
uint32_t uid = _get_rfid_card_uid();
|
uint32_t uid = _get_rfid_card_uid();
|
||||||
if (uid != _last_rfid_card_uid) {
|
if (uid != _last_rfid_card_uid) {
|
||||||
if (uid > 0) {
|
if (uid > 0) {
|
||||||
|
_no_rfid_card_count = 0;
|
||||||
INFO("New RFID card uid: %08x\n", uid);
|
INFO("New RFID card uid: %08x\n", uid);
|
||||||
String s_uid = String(uid, HEX);
|
String s_uid = String(uid, HEX);
|
||||||
_player->play_album(s_uid);
|
_player->play_album(s_uid);
|
||||||
} else {
|
} else {
|
||||||
INFO("No more RFID card.");
|
if (_no_rfid_card_count >= 1) {
|
||||||
_player->stop();
|
INFO("No more RFID card.");
|
||||||
|
_player->stop();
|
||||||
|
} else {
|
||||||
|
_no_rfid_card_count++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_last_rfid_card_uid = uid;
|
_last_rfid_card_uid = uid;
|
||||||
}
|
}
|
||||||
@ -80,12 +86,14 @@ void Controller::_check_serial() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Controller::_execute_serial_command(String cmd) {
|
void Controller::_execute_serial_command(String cmd) {
|
||||||
DEBUG("Executing command: %s", cmd.c_str());
|
DEBUG("Executing command: %s\n", cmd.c_str());
|
||||||
|
|
||||||
if (cmd.equals("ls")) {
|
if (cmd.equals("ls")) {
|
||||||
_execute_command_ls("/");
|
_execute_command_ls("/");
|
||||||
} else if (cmd.startsWith("ls ")) {
|
} else if (cmd.startsWith("ls ")) {
|
||||||
_execute_command_ls(cmd.substring(3));
|
_execute_command_ls(cmd.substring(3));
|
||||||
|
} else if (cmd.equals("play")) {
|
||||||
|
_player->play_random_album();
|
||||||
} else if (cmd.startsWith("play ")) {
|
} else if (cmd.startsWith("play ")) {
|
||||||
_player->play_album(cmd.substring(5));
|
_player->play_album(cmd.substring(5));
|
||||||
} else if (cmd.startsWith("sys ")) {
|
} else if (cmd.startsWith("sys ")) {
|
||||||
|
154
src/http_server.cpp
Normal file
154
src/http_server.cpp
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#include "http_server.h"
|
||||||
|
|
||||||
|
HTTPServer::HTTPServer(Player* p, Controller* c) {
|
||||||
|
_player = p;
|
||||||
|
_controller = c;
|
||||||
|
_http_server = new ESP8266WebServer(80);
|
||||||
|
DEBUG("Connecting to wifi \"%s\"...\n", WIFI_SSID);
|
||||||
|
WiFi.mode(WIFI_AP_STA);
|
||||||
|
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
||||||
|
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
|
||||||
|
ERROR("Could not connect to Wifi. Rebooting.");
|
||||||
|
delay(1000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
INFO("WiFi connected.\n");
|
||||||
|
MDNS.begin("esmp3");
|
||||||
|
//_http_server->onFileUpload([&]() { _handle_upload(); yield();});
|
||||||
|
_http_server->on("/upload", HTTP_POST, [&]() {
|
||||||
|
_http_server->sendHeader("Connection", "close");
|
||||||
|
_http_server->send(200, "text/plain", "OK");
|
||||||
|
}, [&]() {
|
||||||
|
_handle_upload();
|
||||||
|
yield();
|
||||||
|
});
|
||||||
|
_http_server->on("/", HTTP_GET, [&](){ _handle_index(); });
|
||||||
|
_http_server->on("/status", HTTP_GET, [&](){ _handle_status(); });
|
||||||
|
_http_server->begin();
|
||||||
|
MDNS.addService("http", "tcp", 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPServer::_handle_upload() {
|
||||||
|
// https://www.gnu.org/software/tar/manual/html_node/Standard.html
|
||||||
|
// https://www.mkssoftware.com/docs/man4/tar.4.asp
|
||||||
|
HTTPUpload* upload = &_http_server->upload();
|
||||||
|
DEBUG("_handle_upload Status: %d, length: %d\n", upload->status, upload->currentSize);
|
||||||
|
|
||||||
|
if (upload->status == UPLOAD_FILE_START) {
|
||||||
|
_chunk = new uint8_t[512];
|
||||||
|
_chunk_length = 0;
|
||||||
|
_upload_position = 0;
|
||||||
|
_file_size = 0;
|
||||||
|
_file_size_done = 0;
|
||||||
|
_need_header = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (upload->status == UPLOAD_FILE_END || upload->status == UPLOAD_FILE_ABORTED) {
|
||||||
|
// Close the file
|
||||||
|
delete _chunk;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t upload_offset = 0;
|
||||||
|
while (upload_offset < upload->currentSize) {
|
||||||
|
// Load a chunk
|
||||||
|
if (_chunk_length < 512 && upload->currentSize > upload_offset) {
|
||||||
|
uint16_t needed = 512 - _chunk_length;
|
||||||
|
if (needed > upload->currentSize - upload_offset) needed = upload->currentSize - upload_offset;
|
||||||
|
memcpy(_chunk + _chunk_length, upload->buf + upload_offset, needed);
|
||||||
|
_chunk_length += needed;
|
||||||
|
upload_offset += needed;
|
||||||
|
_upload_position += needed;
|
||||||
|
|
||||||
|
if (_chunk_length == 512) {
|
||||||
|
// Process chunk
|
||||||
|
DEBUG("Chunk.\n");
|
||||||
|
if (_need_header) {
|
||||||
|
if (_chunk[257]=='u'&&_chunk[258]=='s'&&_chunk[259]=='t'&&_chunk[260]=='a'&&_chunk[261]=='r') {
|
||||||
|
DEBUG("It is a valid header, starting at 0x%X!\n", _upload_position-512);
|
||||||
|
char filename[200];
|
||||||
|
strncpy(filename, (char*)_chunk, 100);
|
||||||
|
DEBUG("filename: %s\n", filename);
|
||||||
|
_file_size = 0;
|
||||||
|
_file_size_done = 0;
|
||||||
|
for (int i=0; i<11; i++) {
|
||||||
|
//Serial.print(_header_buffer[124 + i]);
|
||||||
|
_file_size = (_file_size<<3) + (_chunk[124 + i] - '0');
|
||||||
|
}
|
||||||
|
DEBUG("filesize: %d\n", _file_size);
|
||||||
|
uint8_t type = _chunk[156] - '0';
|
||||||
|
if (type==0) {
|
||||||
|
DEBUG("Is a file.\n");
|
||||||
|
} else if (type==5) {
|
||||||
|
DEBUG("Is a directory.\n");
|
||||||
|
} else {
|
||||||
|
ERROR("Unknown file type %d\n", type);
|
||||||
|
}
|
||||||
|
_need_header = (type==5 || _file_size==0); // No chunks needed for directories.
|
||||||
|
} else {
|
||||||
|
bool byte_found = false;
|
||||||
|
for (int i=0; i<512; i++) byte_found = byte_found || _chunk[i]>0;
|
||||||
|
if (!byte_found) {
|
||||||
|
DEBUG("Empty chunk while looking for header -> ignoring.\n");
|
||||||
|
} else {
|
||||||
|
ERROR("Invalid tar header: %c %c %c %c %c. Looking at header start offset 0x%X.\n", _chunk[257], _chunk[258], _chunk[259], _chunk[260], _chunk[261], _upload_position-512);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint16_t bytes_to_write = _file_size - _file_size_done;
|
||||||
|
if (bytes_to_write > 512) bytes_to_write=512;
|
||||||
|
// Write bytes...
|
||||||
|
_file_size_done += bytes_to_write;
|
||||||
|
if (_file_size_done >= _file_size) _need_header = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_chunk_length = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPServer::_handle_index() {
|
||||||
|
String response = String("<html><head><title>ESMP3</title><script>function play_album(e) {}</script></head><body>");
|
||||||
|
response.concat("Albums on SD card:<table>");
|
||||||
|
std::list<String> files = _player->ls("/", false, true, false);
|
||||||
|
for(std::list<String>::iterator it=files.begin(); it!=files.end(); it++) {
|
||||||
|
response.concat("<tr><td>");
|
||||||
|
response.concat(*it);
|
||||||
|
response.concat("</td><td><a href='#' onclick='play_album();'>Play</a></td></tr>\n");
|
||||||
|
}
|
||||||
|
response.concat("</table></body></html>");
|
||||||
|
_http_server->send(200, "text/html", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPServer::_handle_status() {
|
||||||
|
String response = String("{");
|
||||||
|
response.concat("\"state\": \"");
|
||||||
|
response.concat(_player->is_playing() ? "playing" : "idle");
|
||||||
|
response.concat("\", ");
|
||||||
|
if (_player->is_playing()) {
|
||||||
|
response.concat("\"album\": \"");
|
||||||
|
response.concat(_player->album());
|
||||||
|
response.concat("\", \"track\": ");
|
||||||
|
response.concat(_player->track());
|
||||||
|
response.concat(", \"position\": ");
|
||||||
|
response.concat(_player->position());
|
||||||
|
response.concat(", ");
|
||||||
|
}
|
||||||
|
response.concat("\"volume\": ");
|
||||||
|
response.concat(_player->volume());
|
||||||
|
response.concat(", \"volume_max\": ");
|
||||||
|
response.concat(VOLUME_MAX);
|
||||||
|
response.concat(", \"volume_min\": ");
|
||||||
|
response.concat(VOLUME_MIN);
|
||||||
|
response.concat(", \"rfid_uid\": ");
|
||||||
|
response.concat(_controller->rfid_uid());
|
||||||
|
response.concat("}");
|
||||||
|
_http_server->send(200, "application/json", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPServer::loop() {
|
||||||
|
_http_server->handleClient();
|
||||||
|
MDNS.update();
|
||||||
|
}
|
11
src/main.cpp
11
src/main.cpp
@ -6,10 +6,12 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "spi_master.h"
|
#include "spi_master.h"
|
||||||
|
#include "http_server.h"
|
||||||
|
|
||||||
Controller* controller;
|
Controller* controller;
|
||||||
Player* player;
|
Player* player;
|
||||||
MCP* mcp;
|
MCP* mcp;
|
||||||
|
HTTPServer* http_server;
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
delay(500);
|
delay(500);
|
||||||
@ -17,6 +19,8 @@ void setup() {
|
|||||||
INFO("Starting.\n");
|
INFO("Starting.\n");
|
||||||
INFO("Initializing...\n");
|
INFO("Initializing...\n");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
DEBUG("Setting up SPI...\n");
|
DEBUG("Setting up SPI...\n");
|
||||||
SPI.begin();
|
SPI.begin();
|
||||||
SPIMaster::init();
|
SPIMaster::init();
|
||||||
@ -35,8 +39,14 @@ void setup() {
|
|||||||
ERROR("Could not initialize SD card. Halting.\n");
|
ERROR("Could not initialize SD card. Halting.\n");
|
||||||
while(1);
|
while(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEBUG("Initializing Player and Controller...\n");
|
||||||
player = new Player(mcp);
|
player = new Player(mcp);
|
||||||
controller = new Controller(player, mcp);
|
controller = new Controller(player, mcp);
|
||||||
|
INFO("Player and controller initialized.\n");
|
||||||
|
|
||||||
|
DEBUG("Setting up WiFi and web server...\n");
|
||||||
|
http_server = new HTTPServer(player, controller);
|
||||||
|
|
||||||
INFO("Initialization completed.\n");
|
INFO("Initialization completed.\n");
|
||||||
}
|
}
|
||||||
@ -46,4 +56,5 @@ void loop() {
|
|||||||
if (more_data_needed) return;
|
if (more_data_needed) return;
|
||||||
|
|
||||||
controller->loop();
|
controller->loop();
|
||||||
|
http_server->loop();
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,27 @@ void Player::_init() {
|
|||||||
_state = idle;
|
_state = idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Player::_sleep() {
|
||||||
|
DEBUG("VS1053 going to sleep.\n");
|
||||||
|
_write_control_register(SCI_CLOCKF, 0x0000);
|
||||||
|
_spi_settings = &_spi_settings_slow;
|
||||||
|
_write_control_register(SCI_AUDATA, 0x0010);
|
||||||
|
set_volume(0, false);
|
||||||
|
_state = sleeping;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::_wakeup() {
|
||||||
|
if (_state != sleeping) return;
|
||||||
|
_stopped_at = millis();
|
||||||
|
DEBUG("Waking VS1053...\n");
|
||||||
|
set_volume(_volume, false);
|
||||||
|
_write_control_register(SCI_AUDATA, 0x0000);
|
||||||
|
_write_control_register(SCI_CLOCKF, 0x6000);
|
||||||
|
delay(10);
|
||||||
|
_spi_settings = &_spi_settings_fast;
|
||||||
|
_state = idle;
|
||||||
|
}
|
||||||
|
|
||||||
void Player::_check_system_sound(String filename) {
|
void Player::_check_system_sound(String filename) {
|
||||||
String path = String("/system/") + filename;
|
String path = String("/system/") + filename;
|
||||||
if (!SD.exists(path)) {
|
if (!SD.exists(path)) {
|
||||||
@ -159,7 +180,6 @@ void Player::set_volume(uint8_t vol, bool save) {
|
|||||||
}
|
}
|
||||||
INFO("Setting volume to %d\n", vol);
|
INFO("Setting volume to %d\n", vol);
|
||||||
vol = 0xFF - vol;
|
vol = 0xFF - vol;
|
||||||
if (vol==0xFF) vol=0xFE;
|
|
||||||
uint16_t value = (vol<<8)|vol;
|
uint16_t value = (vol<<8)|vol;
|
||||||
DEBUG("Setting volume register to 0x%04X\n", value);
|
DEBUG("Setting volume register to 0x%04X\n", value);
|
||||||
_write_control_register(SCI_VOL, value);
|
_write_control_register(SCI_VOL, value);
|
||||||
@ -178,12 +198,12 @@ void Player::vol_down() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Player::_mute() {
|
void Player::_mute() {
|
||||||
INFO("Muting.");
|
INFO("Muting.\n");
|
||||||
set_volume(0, false);
|
set_volume(1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Player::_unmute() {
|
void Player::_unmute() {
|
||||||
INFO("Unmuting.");
|
INFO("Unmuting.\n");
|
||||||
set_volume(_volume, false);
|
set_volume(_volume, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,21 +232,30 @@ void Player::track_prev() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<String> Player::ls(String path) {
|
bool Player::is_playing() {
|
||||||
|
return _state == playing;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::list<String> Player::ls(String path, bool withFiles, bool withDirs, bool withHidden) {
|
||||||
SPIMaster::enable(PIN_SD_CS);
|
SPIMaster::enable(PIN_SD_CS);
|
||||||
std::list<String> result;
|
std::list<String> result;
|
||||||
if (!SD.exists(path)) return result;
|
if (!SD.exists(path)) return result;
|
||||||
File dir = SD.open(path);
|
File dir = SD.open(path);
|
||||||
File entry;
|
File entry;
|
||||||
while (entry = dir.openNextFile()) {
|
while (entry = dir.openNextFile()) {
|
||||||
|
if (!withDirs && entry.isDirectory()) continue;
|
||||||
|
if (!withFiles && !entry.isDirectory()) continue;
|
||||||
String filename = entry.name();
|
String filename = entry.name();
|
||||||
|
if (!withHidden && filename.startsWith(".")) continue;
|
||||||
if (entry.isDirectory()) filename.concat("/");
|
if (entry.isDirectory()) filename.concat("/");
|
||||||
result.push_back(filename);
|
result.push_back(filename);
|
||||||
}
|
}
|
||||||
|
result.sort();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
String Player::_find_album_dir(String id) {
|
String Player::_find_album_dir(String id) {
|
||||||
|
if (id.endsWith("/")) id = id.substring(0, id.length() - 1);
|
||||||
String id_with_divider = id + " - ";
|
String id_with_divider = id + " - ";
|
||||||
File root = SD.open("/");
|
File root = SD.open("/");
|
||||||
File entry;
|
File entry;
|
||||||
@ -259,10 +288,10 @@ std::list<String> Player::_files_in_dir(String path) {
|
|||||||
filename.endsWith(".wma") ||
|
filename.endsWith(".wma") ||
|
||||||
filename.endsWith(".mp4") ||
|
filename.endsWith(".mp4") ||
|
||||||
filename.endsWith(".mpa"))) {
|
filename.endsWith(".mpa"))) {
|
||||||
DEBUG(" Adding entry %s\n", filename.c_str());
|
TRACE(" Adding entry %s\n", filename.c_str());
|
||||||
result.push_back(path + filename);
|
result.push_back(path + filename);
|
||||||
} else {
|
} else {
|
||||||
DEBUG(" Ignoring entry %s\n", filename.c_str());
|
TRACE(" Ignoring entry %s\n", filename.c_str());
|
||||||
}
|
}
|
||||||
entry.close();
|
entry.close();
|
||||||
}
|
}
|
||||||
@ -272,14 +301,26 @@ std::list<String> Player::_files_in_dir(String path) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String Player::_random_album() {
|
||||||
|
std::list<String> albums = ls("/", false, true, false);
|
||||||
|
uint8_t rnd = random(albums.size());
|
||||||
|
std::list<String>::iterator it = albums.begin();
|
||||||
|
for (int i=0; i<rnd; i++) { it++; }
|
||||||
|
return *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::play_random_album() {
|
||||||
|
play_album(_random_album());
|
||||||
|
}
|
||||||
|
|
||||||
bool Player::play_album(String album) {
|
bool Player::play_album(String album) {
|
||||||
//if (_state==playing) stop();
|
|
||||||
album_state s = _last_tracks[album.c_str()];
|
album_state s = _last_tracks[album.c_str()];
|
||||||
DEBUG("Last index for album %s was %d,%d\n", album.c_str(), s.index, s.position);
|
DEBUG("Last index for album %s was %d,%d\n", album.c_str(), s.index, s.position);
|
||||||
return play_song(album, s.index, s.position);
|
return play_song(album, s.index, s.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Player::play_song(String album, uint8_t index, uint32_t skip_to) {
|
bool Player::play_song(String album, uint8_t index, uint32_t skip_to) {
|
||||||
|
if (_state == sleeping) _wakeup();
|
||||||
if (_state != idle) return false;
|
if (_state != idle) return false;
|
||||||
DEBUG("Trying to play song at index %d, offset %d of album %s\n", index, skip_to, album.c_str());
|
DEBUG("Trying to play song at index %d, offset %d of album %s\n", index, skip_to, album.c_str());
|
||||||
String path = _find_album_dir(album);
|
String path = _find_album_dir(album);
|
||||||
@ -420,6 +461,7 @@ void Player::stop() {
|
|||||||
|
|
||||||
void Player::_finish_stopping() {
|
void Player::_finish_stopping() {
|
||||||
_state = idle;
|
_state = idle;
|
||||||
|
_stopped_at = millis();
|
||||||
if (_file) {
|
if (_file) {
|
||||||
_file.close();
|
_file.close();
|
||||||
}
|
}
|
||||||
@ -483,6 +525,10 @@ bool Player::loop() {
|
|||||||
_refill();
|
_refill();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_state == idle && _stopped_at < millis() - VS1053_SLEEP_DELAY) {
|
||||||
|
_sleep();
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user