esmp3/src/player.cpp

493 lines
12 KiB
C++
Raw Normal View History

// Based on https://github.com/mpflaga/Arduino_Library-vs1053_for_SdFat/blob/master/src/vs1053_SdFat.cpp
#include "player.h"
#include "spi_master.h"
//Player::_spi_settings
Player::Player(MCP* m) {
_mcp = m;
_mcp->pinMode(XRESET, OUTPUT);
_mcp->digitalWrite(XRESET, HIGH);
pinMode(DREQ, INPUT);
_init();
}
void Player::_reset() {
_mcp->digitalWrite(XRESET, LOW);
delay(100);
_mcp->digitalWrite(XRESET, HIGH);
delay(100);
_state = uninitialized;
_spi_settings = &_spi_settings_slow; // After reset, communication has to be slow
}
void Player::_init() {
SPIMaster::disable();
DEBUG("Resetting VS1053...\n");
_reset();
uint16_t result = _read_control_register(SCI_MODE);
DEBUG("SCI_MODE: 0x%04X\n", result);
if (result != 0x4800) {
ERROR("SCI_MODE was 0x%04X, expected was 0x4800.\n", result);
return;
}
result = _read_control_register(SCI_STATUS);
DEBUG("SCI_STATUS: 0x%04X\n", result);
if (result != 0x0040 && result != 0x0048) {
ERROR("SCI_STATUS was 0x%04X, expected was 0x0040 or 0x0048.\n", result);
return;
}
result = _read_control_register(SCI_CLOCKF);
DEBUG("SCI_CLOCKF: 0x%04X\n", result);
DEBUG("VS1053 Init looking good.\n");
DEBUG("Upping VS1053 multiplier...\n");
_write_control_register(SCI_CLOCKF, 0x6000);
delay(10);
_spi_settings = &_spi_settings_fast;
result = _read_control_register(SCI_CLOCKF);
DEBUG("SCI_CLOCKF: 0x%04X\n", result);
if (result != 0x6000) {
ERROR("Error: SCI_CLOCKF was 0x%04X, expected was 0x6000.\n", result);
return;
}
set_volume(VOLUME_DEFAULT);
INFO("VS1053 initialization completed.\n");
INFO("Checking system sounds...\n");
SPIMaster::enable(PIN_SD_CS);
_check_system_sound("no_prev_song.mp3");
_check_system_sound("no_next_song.mp3");
_check_system_sound("volume_max.mp3");
_check_system_sound("volume_min.mp3");
_state = idle;
}
void Player::_check_system_sound(String filename) {
String path = String("/system/") + filename;
if (!SD.exists(path)) {
ERROR("System sound %s is missing on the sd card!\n", path.c_str());
} else {
DEBUG("%s found.\n", path.c_str());
}
}
inline void Player::_wait() {
while(!digitalRead(DREQ));
}
uint16_t Player::_read_control_register(uint8_t address) {
_wait();
SPIMaster::enable(XCS);
SPI.beginTransaction(*_spi_settings);
SPI.transfer(CMD_READ);
SPI.transfer(address);
uint8_t b1 = SPI.transfer(0xFF);
_wait();
uint8_t b2 = SPI.transfer(0xFF);
_wait();
SPI.endTransaction();
SPIMaster::disable();
return (b1 << 8) | b2;
}
void Player::_write_control_register(uint8_t address, uint16_t value) {
uint8_t b1 = value >> 8;
uint8_t b2 = value & 0xFF;
_wait();
SPIMaster::enable(XCS);
SPI.beginTransaction(*_spi_settings);
SPI.transfer(CMD_WRITE);
SPI.transfer(address);
SPI.transfer(b1);
SPI.transfer(b2);
_wait();
SPI.endTransaction();
SPIMaster::disable();
}
void Player::_write_data(uint8_t* buffer) {
SPIMaster::enable(XDCS);
SPI.beginTransaction(*_spi_settings);
for (uint i=0; i<sizeof(_buffer); i++) {
SPI.transfer(_buffer[i]);
}
SPI.endTransaction();
SPIMaster::disable();
}
uint16_t Player::_read_wram(uint16_t address) {
DEBUG("Reading WRAM address 0x%04X...\n", address);
_write_control_register(SCI_WRAMADDR, address);
uint16_t r1 = _read_control_register(SCI_WRAM);
_write_control_register(SCI_WRAMADDR, address);
uint16_t r2 = _read_control_register(SCI_WRAM);
if (r1 == r2) return r1;
DEBUG("Reading WRAM resulted in different values: 0x%04X and 0x%04X.\n", r1, r2);
_write_control_register(SCI_WRAMADDR, address);
r1 = _read_control_register(SCI_WRAM);
if (r1 == r2) return r1;
DEBUG("Reading WRAM resulted in different values: 0x%04X and 0x%04X.\n", r1, r2);
_write_control_register(SCI_WRAMADDR, address);
r2 = _read_control_register(SCI_WRAM);
if (r1 == r2) return r1;
DEBUG("Reading WRAM resulted in different values: 0x%04X and 0x%04X.\n", r1, r2);
DEBUG("Returning last value (0x%04X)...\n", r2);
return r2;
}
int8_t Player::_get_endbyte() {
int8_t endbyte = _read_wram(ADDR_ENDBYTE) & 0xFF;
DEBUG("Endbyte is 0x%02X.\n", endbyte);
return endbyte;
}
void Player::set_volume(uint8_t vol, bool save) {
if (save) {
_volume = vol;
}
INFO("Setting volume to %d\n", vol);
vol = 0xFF - vol;
if (vol==0xFF) vol=0xFE;
uint16_t value = (vol<<8)|vol;
DEBUG("Setting volume register to 0x%04X\n", value);
_write_control_register(SCI_VOL, value);
}
void Player::vol_up() {
if (_volume == VOLUME_MAX) play_system_sound("volume_max.mp3");
else if (_volume + VOLUME_STEP > VOLUME_MAX) set_volume(VOLUME_MAX);
else set_volume(_volume + VOLUME_STEP);
}
void Player::vol_down() {
if (_volume >= VOLUME_MIN + VOLUME_STEP) set_volume(_volume - VOLUME_STEP);
else if (_volume == VOLUME_MIN) play_system_sound("volume_min.mp3");
else set_volume(VOLUME_MIN);
}
void Player::_mute() {
INFO("Muting.");
set_volume(0, false);
}
void Player::_unmute() {
INFO("Unmuting.");
set_volume(_volume, false);
}
void Player::track_next() {
if (_state != playing) return;
if (_playing_index + 1 >= _playing_album_songs) {
play_system_sound("no_next_song.mp3");
return;
}
stop();
play_song(_playing_album, _playing_index + 1);
}
void Player::track_prev() {
if (_state != playing) return;
if (_current_play_position > 100000) {
stop();
play_song(_playing_album, _playing_index);
} else {
if (_playing_index == 0) {
play_system_sound("no_prev_song.mp3");
return;
}
stop();
play_song(_playing_album, _playing_index - 1);
}
}
std::list<String> Player::ls(String path) {
SPIMaster::enable(PIN_SD_CS);
std::list<String> result;
if (!SD.exists(path)) return result;
File dir = SD.open(path);
File entry;
while (entry = dir.openNextFile()) {
String filename = entry.name();
if (entry.isDirectory()) filename.concat("/");
result.push_back(filename);
}
return result;
}
String Player::_find_album_dir(String id) {
String id_with_divider = id + " - ";
File root = SD.open("/");
File entry;
String result = String("");
while ((result.length()==0) && (entry = root.openNextFile())) {
String name = entry.name();
if (entry.isDirectory() && (name.startsWith(id_with_divider) || name.equals(id))) {
result = name;
}
entry.close();
}
root.close();
return result;
}
std::list<String> Player::_files_in_dir(String path) {
DEBUG("Examining folder %s...\n", path.c_str());
if (!path.startsWith("/")) path = String("/") + path;
if (!path.endsWith("/")) path.concat("/");
std::list<String> result;
if (!SD.exists(path)) return result;
File dir = SD.open(path);
File entry;
while (entry = dir.openNextFile()) {
String filename = entry.name();
if (!entry.isDirectory() &&
!filename.startsWith(".") &&
( filename.endsWith(".mp3") ||
filename.endsWith(".ogg") ||
filename.endsWith(".wma") ||
filename.endsWith(".mp4") ||
filename.endsWith(".mpa"))) {
DEBUG(" Adding entry %s\n", filename.c_str());
result.push_back(path + filename);
} else {
DEBUG(" Ignoring entry %s\n", filename.c_str());
}
entry.close();
}
dir.close();
result.sort();
return result;
}
bool Player::play_album(String album) {
//if (_state==playing) stop();
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);
return play_song(album, s.index, s.position);
}
bool Player::play_song(String album, uint8_t index, uint32_t skip_to) {
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());
String path = _find_album_dir(album);
if (path.length()==0) {
ERROR("Could not find album.\n");
return false;
} else {
INFO("Found album in dir '%s'.\n", path.c_str());
}
std::list<String> files = _files_in_dir(path);
_playing_album_songs = files.size();
DEBUG("Found %d songs in album\n", files.size());
if (index >= files.size()) {
ERROR("No matching file found - not playing.\n");
return false;
}
//std::list<String>::iterator it = files.begin();
//std::advance(it, index);
String file = *(std::next(files.begin(), index));
_state = playing;
_playing_album = album;
_playing_index = index;
_set_last_track(album.c_str(), index, skip_to);
_play_file(file, skip_to);
return true;
}
void Player::play_system_sound(String filename) {
//String file = String("/system/") + filename;
String file = filename;
if (!SD.exists(file)) {
ERROR("File %s does not exist!\n", file.c_str());
return;
}
if (_state == playing) {
stop();
_state = system_sound_while_playing;
} else {
_state = system_sound_while_stopped;
}
_play_file(file, 0);
}
void Player::_play_file(String file, uint32_t file_offset) {
INFO("play_file('%s', %d)\n", file.c_str(), file_offset);
_file = SD.open(file);
if (!_file) return;
DEBUG("Resetting SCI_DECODE_TIME...\n");
_write_control_register(SCI_DECODE_TIME, 0);
DEBUG("Resetting SS_DO_NOT_JUMP...\n");
_write_control_register(SCI_STATUS, _read_control_register(SCI_STATUS) & ~SS_DO_NOT_JUMP);
delay(100);
if (file_offset == 0) {
_file.seek(_id3_tag_offset(_file));
}
_refills = 0;
_current_play_position = _file.position();
_skip_to = file_offset;
if (_skip_to>0) _mute();
INFO("Now playing.\n");
}
uint32_t Player::_id3_tag_offset(File f) {
uint32_t original_position = f.position();
uint32_t offset = 0;
if (f.read()=='I' && f.read()=='D' && f.read()=='3') {
DEBUG("ID3 tag found\n");
// Skip ID3 tag version
f.read(); f.read();
byte tags = f.read();
bool footer_present = tags & 0x10;
DEBUG("ID3 footer found: %d\n", footer_present);
for (byte i=0; i<4; i++) {
offset <<= 7;
offset |= (0x7F & f.read());
}
offset += 10;
if (footer_present) offset += 10;
DEBUG("ID3 tag length is %d bytes.\n", offset);
} else {
DEBUG("No ID3 tag found\n");
}
f.seek(original_position);
return offset;
}
void Player::_flush(uint count, int8_t byte) {
SPIMaster::enable(XDCS);
SPI.beginTransaction(*_spi_settings);
for(uint i=0; i<count; i++) {
_wait();
SPI.transfer(byte);
}
SPI.endTransaction();
}
void Player::_finish_playing() {
uint8_t endbyte = _get_endbyte();
_flush(2052, endbyte);
_write_control_register(SCI_MODE, _read_control_register(SCI_MODE) | SM_CANCEL);
for (int i=0; i<64; i++) {
_flush(32, endbyte);
uint16_t mode = _read_control_register(SCI_MODE);
if ((mode & SM_CANCEL) == 0) return;
}
// If we reached this, the Chip didn't stop. That should not happen.
// (That's written in the manual.)
// Reset the chip.
_init();
}
void Player::stop() {
if (_state != playing /* && _state != system_sound_while_playing && _state != system_sound_while_stopped*/) return;
INFO("Stopping...\n");
if (_state == playing) {
_set_last_track(_playing_album.c_str(), _playing_index, (uint32_t)_file.position());
}
_state = stopping;
_stop_delay = 0;
_write_control_register(SCI_MODE, _read_control_register(SCI_MODE) | SM_CANCEL);
uint8_t endbyte = _get_endbyte();
while (true) {
_refill();
uint16_t mode = _read_control_register(SCI_MODE);
if ((mode & SM_CANCEL) == 0) {
_flush(2052, endbyte);
_finish_stopping();
break;
} else if (_stop_delay > 2048) {
init();
break;
}
_stop_delay++;
}
}
void Player::_finish_stopping() {
_state = idle;
if (_file) {
_file.close();
}
INFO("Stopped.\n");
}
void Player::_refill() {
SPIMaster::enable(PIN_SD_CS);
_refills++;
if (_refills % 1000 == 0) DEBUG(".");
uint8_t result = _file.read(_buffer, sizeof(_buffer));
if (result == 0) {
// File is over.
DEBUG("EOF reached.\n");
_finish_playing();
if (_state == system_sound_while_playing) {
_finish_stopping();
play_album(_playing_album);
return;
} else if (_state == system_sound_while_stopped) {
_finish_stopping();
return;
}
_finish_stopping();
bool result = play_song(_playing_album, _playing_index + 1);
if (!result) {
_set_last_track(_playing_album.c_str(), 0, 0);
}
return;
}
_current_play_position+=result;
_write_data(_buffer);
if (_skip_to > 0) {
if (_skip_to > _file.position()) {
uint16_t status = _read_control_register(SCI_STATUS);
if ((status & SS_DO_NOT_JUMP) == 0) {
DEBUG("Skipping to %d.\n", _skip_to);
_flush(2048, _get_endbyte());
_file.seek(_skip_to);
_skip_to = 0;
_unmute();
}
} else {
_skip_to = 0;
_unmute();
}
}
}
bool Player::_refill_needed() {
return _state==playing ||
_state==stopping ||
_state==system_sound_while_playing ||
_state==system_sound_while_stopped;
}
bool Player::loop() {
if (digitalRead(DREQ) && _refill_needed()) {
_refill();
return true;
}
return false;
}
void Player::_set_last_track(const char* album, uint8_t index, uint32_t position) {
DEBUG("Setting _last_track[%s]=%d,%d.\n", album, index, position);
_last_tracks[album] = {index, position};
}