// 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 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 Player::ls(String path) { SPIMaster::enable(PIN_SD_CS); 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.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 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 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 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::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 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}; }