// 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() { pinMode(XRESET, OUTPUT); digitalWrite(XRESET, HIGH); pinMode(DREQ, INPUT); _init(); } void Player::_reset() { digitalWrite(XRESET, LOW); delay(100); digitalWrite(XRESET, HIGH); delay(100); _state = uninitialized; _spi_settings = &_spi_settings_slow; // After reset, communication has to be slow } void Player::_init() { SPIMaster::disable(); _reset(); uint16_t result = _read_control_register(SCI_MODE); Serial.printf("SCI_MODE: 0x%04X\n", result); if (result != 0x4800) { Serial.printf("Error: SCI_MODE was 0x%04X, expected was 0x4800.\n", result); return; } result = _read_control_register(SCI_STATUS); Serial.printf("SCI_STATUS: 0x%04X\n", result); if (result != 0x0040 && result != 0x0048) { Serial.printf("Error: SCI_STATUS was 0x%04X, expected was 0x0040 or 0x0048.\n", result); return; } result = _read_control_register(SCI_CLOCKF); Serial.printf("SCI_CLOCKF: 0x%04X\n", result); Serial.println("VS1053 Init looking good."); Serial.println("Upping VS1053 multiplier..."); _write_control_register(SCI_CLOCKF, 0x6000); delay(10); _spi_settings = &_spi_settings_fast; result = _read_control_register(SCI_CLOCKF); Serial.printf("SCI_CLOCKF: 0x%04X\n", result); if (result != 0x6000) { Serial.printf("Error: SCI_CLOCKF was 0x%04X, expected was 0x6000.\n", result); return; } _end_byte = (int8_t)(_read_wram(ADDR_ENDBYTE) & 0xFF); set_volume(VOLUME_DEFAULT); Serial.println("VS1053 initialization completed."); _state = idle; } 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 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 set_volume(VOLUME_MIN); } void Player::_mute() { Serial.println("Muting."); set_volume(0, false); } void Player::_unmute() { Serial.println("Unmuting."); set_volume(_volume, false); } void Player::track_next() { if (_state != playing) return; stop(); play_song(_playing_album, _playing_index + 1); } void Player::track_prev() { if (_state != playing) return; if (_playing_index == 0) _playing_index=1; stop(); play_song(_playing_album, _playing_index - 1); } 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 Player::_files_in_dir(String path) { Serial.printf("Examining folder %s...\n", path.c_str()); if (!path.startsWith("/")) path = String("/") + path; if (!path.endsWith("/")) path.concat("/"); std::list 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"))) { //Serial.printf("Adding file %s\n", filename.c_str()); result.push_back(path + filename); } else { //Serial.printf("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()]; Serial.printf("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; Serial.printf("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) { Serial.println("Could not find album."); return false; } else { Serial.printf("Found album in dir '%s'.\n", path.c_str()); } std::list files = _files_in_dir(path); Serial.printf("Found %d songs in album\n", files.size()); if (index >= files.size()) { Serial.println("No matching file found - not playing."); return false; } //std::list::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)) { Serial.printf("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) { Serial.printf("play_file('%s', %d)\n", file.c_str(), file_offset); _file = SD.open(file); if (!_file) return; Serial.println("Resetting SCI_DECODE_TIME..."); _write_control_register(SCI_DECODE_TIME, 0); Serial.println("Resetting SS_DO_NOT_JUMP..."); _write_control_register(SCI_STATUS, _read_control_register(SCI_STATUS) & ~SS_DO_NOT_JUMP); delay(100); _refills = 0; _skip_to = file_offset; if (_skip_to>0) _mute(); Serial.println("Now playing."); } void Player::_flush(uint bytes) { SPIMaster::enable(XDCS); SPI.beginTransaction(*_spi_settings); for(uint i=0; i 2048) { init(); break; } _stop_delay++; } } void Player::_finish_stopping() { _state = idle; if (_file) { _file.close(); } Serial.println("Stopped."); } void Player::_refill() { SPIMaster::enable(PIN_SD_CS); _refills++; if (_refills % 1000 == 0) Serial.print("."); uint8_t result = _file.read(_buffer, sizeof(_buffer)); if (result == 0) { // File is over. Serial.println("EOF reached."); _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; } _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) { Serial.printf("Skipping to %d.\n", _skip_to); _flush(2048); _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) { Serial.printf("Setting _last_track[%s]=%d,%d.\n", album, index, position); _last_tracks[album] = {index, position}; }