diff --git a/CHANGELOG b/CHANGELOG index 1cbb39cbd..8069f55dc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +2023-10-06 + - fix cspot PREV on first track, NEXT on last track and normal ending + - use DMA_AUTO for SPI + - cspot share same time log + 2023-10-06 - Fix bootswatch bug that caused difficult to read UI ( issue #319) diff --git a/components/platform_console/cmd_i2ctools.c b/components/platform_console/cmd_i2ctools.c index 46c662497..54e56bc8c 100644 --- a/components/platform_console/cmd_i2ctools.c +++ b/components/platform_console/cmd_i2ctools.c @@ -477,7 +477,7 @@ static int do_spiconfig_cmd(int argc, char **argv){ if(!nerrors){ fprintf(f,"Configuring SPI data=%d clock=%d host=%u dc: %d\n", spi_config.mosi_io_num, spi_config.sclk_io_num, host, dc); - err=spi_bus_initialize( host, &spi_config, 1 ); + err=spi_bus_initialize( host, &spi_config, SPI_DMA_CH_AUTO ); if(err!=ESP_OK){ if(err==ESP_ERR_INVALID_STATE){ // if user is changing the host number, we need to try freeing both hosts @@ -485,7 +485,7 @@ static int do_spiconfig_cmd(int argc, char **argv){ fprintf(f,"SPI bus init failed. Please clear SPI configuration, restart the device and try again. %s\n", esp_err_to_name(err)); nerrors++; } - else if((err=spi_bus_initialize( host, &spi_config, 1 ))!=ESP_OK){ + else if((err=spi_bus_initialize( host, &spi_config, SPI_DMA_CH_AUTO ))!=ESP_OK){ fprintf(f,"Failed to initialize SPI Bus. %s\n", esp_err_to_name(err)); nerrors++; } diff --git a/components/services/services.c b/components/services/services.c index ba1e93df9..ae654ad85 100644 --- a/components/services/services.c +++ b/components/services/services.c @@ -380,7 +380,7 @@ void services_init(void) { ESP_LOGI(TAG,"Configuring SPI mosi:%d miso:%d clk:%d host:%u dc:%d", spi_config->mosi_io_num, spi_config->miso_io_num, spi_config->sclk_io_num, spi_system_host, spi_system_dc_gpio); if (spi_config->mosi_io_num != -1 && spi_config->sclk_io_num != -1) { - spi_bus_initialize( spi_system_host, spi_config, 1 ); + spi_bus_initialize( spi_system_host, spi_config, SPI_DMA_CH_AUTO ); if (spi_system_dc_gpio != -1) { gpio_reset_pin(spi_system_dc_gpio); gpio_set_direction( spi_system_dc_gpio, GPIO_MODE_OUTPUT ); diff --git a/components/spotify/Shim.cpp b/components/spotify/Shim.cpp index df0cbf0f0..aa33d0917 100644 --- a/components/spotify/Shim.cpp +++ b/components/spotify/Shim.cpp @@ -229,7 +229,6 @@ void cspotPlayer::eventHandler(std::unique_ptr event case cspot::SpircHandler::EventType::NEXT: case cspot::SpircHandler::EventType::PREV: case cspot::SpircHandler::EventType::FLUSH: { - // FLUSH is sent when there is no next, just clean everything cmdHandler(CSPOT_FLUSH); break; } @@ -424,7 +423,7 @@ void cspotPlayer::runTask() { CSPOT_LOG(info, "last track finished"); trackStatus = TRACK_INIT; cmdHandler(CSPOT_STOP); - spirc->setPause(true); + spirc->notifyAudioEnded(); } } @@ -461,6 +460,7 @@ void cspotPlayer::runTask() { */ struct cspot_s* cspot_create(const char *name, httpd_handle_t server, int port, cspot_cmd_cb_t cmd_cb, cspot_data_cb_t data_cb) { bell::setDefaultLogger(); + bell::enableTimestampLogging(true); player = new cspotPlayer(name, server, port, cmd_cb, data_cb); player->startTask(); return (cspot_s*) player; diff --git a/components/spotify/cspot/bell/main/utilities/BellLogger.cpp b/components/spotify/cspot/bell/main/utilities/BellLogger.cpp index 46ec6d92c..165bdd838 100644 --- a/components/spotify/cspot/bell/main/utilities/BellLogger.cpp +++ b/components/spotify/cspot/bell/main/utilities/BellLogger.cpp @@ -10,6 +10,7 @@ void bell::enableSubmoduleLogging() { bell::bellGlobalLogger->enableSubmodule = true; } -void bell::enableTimestampLogging() { +void bell::enableTimestampLogging(bool local) { bell::bellGlobalLogger->enableTimestamp = true; + bell::bellGlobalLogger->shortTime = local; } diff --git a/components/spotify/cspot/bell/main/utilities/include/BellLogger.h b/components/spotify/cspot/bell/main/utilities/include/BellLogger.h index ba6694c3d..d53e705a3 100644 --- a/components/spotify/cspot/bell/main/utilities/include/BellLogger.h +++ b/components/spotify/cspot/bell/main/utilities/include/BellLogger.h @@ -14,6 +14,7 @@ class AbstractLogger { public: bool enableSubmodule = false; bool enableTimestamp = false; + bool shortTime = false; virtual void debug(std::string filename, int line, std::string submodule, const char* format, ...) = 0; @@ -94,10 +95,18 @@ class BellLogger : public bell::AbstractLogger { now.time_since_epoch()) % 1000; - auto gmt_time = gmtime(&now_time); printf(colorReset); - std::cout << std::put_time(gmt_time, "[%Y-%m-%d %H:%M:%S") << '.' - << std::setfill('0') << std::setw(3) << nowMs.count() << "] "; + struct tm* gmt_time; + if (shortTime) { + gmt_time = localtime(&now_time); + std::cout << std::put_time(gmt_time, "[%H:%M:%S") << '.' + << std::setfill('0') << std::setw(3) << nowMs.count() << "] "; + } + else { + gmt_time = gmtime(&now_time); + std::cout << std::put_time(gmt_time, "[%Y-%m-%d %H:%M:%S") << '.' + << std::setfill('0') << std::setw(3) << nowMs.count() << "] "; + } } } @@ -129,7 +138,7 @@ class BellLogger : public bell::AbstractLogger { void setDefaultLogger(); void enableSubmoduleLogging(); -void enableTimestampLogging(); +void enableTimestampLogging(bool local = false); } // namespace bell #define BELL_LOG(type, ...) \ diff --git a/components/spotify/cspot/include/SpircHandler.h b/components/spotify/cspot/include/SpircHandler.h index 7a4545364..7d558cd8a 100644 --- a/components/spotify/cspot/include/SpircHandler.h +++ b/components/spotify/cspot/include/SpircHandler.h @@ -48,11 +48,12 @@ class SpircHandler { void setPause(bool pause); - void previousSong(); + bool previousSong(); - void nextSong(); + bool nextSong(); void notifyAudioReachedPlayback(); + void notifyAudioEnded(); void updatePositionMs(uint32_t position); void setRemoteVolume(int volume); void loadTrackFromURI(const std::string& uri); @@ -74,7 +75,7 @@ class SpircHandler { void sendEvent(EventType type); void sendEvent(EventType type, EventData data); - void skipSong(TrackQueue::SkipDirection dir); + bool skipSong(TrackQueue::SkipDirection dir); void handleFrame(std::vector& data); void notify(); }; diff --git a/components/spotify/cspot/include/TrackPlayer.h b/components/spotify/cspot/include/TrackPlayer.h index 9ec100a17..0a409865f 100644 --- a/components/spotify/cspot/include/TrackPlayer.h +++ b/components/spotify/cspot/include/TrackPlayer.h @@ -32,7 +32,7 @@ struct TrackReference; class TrackPlayer : bell::Task { public: // Callback types - typedef std::function)> TrackLoadedCallback; + typedef std::function, bool)> TrackLoadedCallback; typedef std::function DataCallback; typedef std::function EOFCallback; @@ -49,7 +49,7 @@ class TrackPlayer : bell::Task { // CDNTrackStream::TrackInfo getCurrentTrackInfo(); void seekMs(size_t ms); - void resetState(); + void resetState(bool paused = false); // Vorbis codec callbacks size_t _vorbisRead(void* ptr, size_t size, size_t nmemb); @@ -89,6 +89,7 @@ class TrackPlayer : bell::Task { std::atomic pendingReset = false; std::atomic inFuture = false; std::atomic pendingSeekPositionMs = 0; + std::atomic startPaused = false; std::mutex runningMutex; diff --git a/components/spotify/cspot/src/SpircHandler.cpp b/components/spotify/cspot/src/SpircHandler.cpp index 52032aedb..cd5da7a61 100644 --- a/components/spotify/cspot/src/SpircHandler.cpp +++ b/components/spotify/cspot/src/SpircHandler.cpp @@ -31,15 +31,15 @@ SpircHandler::SpircHandler(std::shared_ptr ctx) { } }; - auto trackLoadedCallback = [this](std::shared_ptr track) { - playbackState->setPlaybackState(PlaybackState::State::Playing); + auto trackLoadedCallback = [this](std::shared_ptr track, bool paused = false) { + playbackState->setPlaybackState(paused ? PlaybackState::State::Paused : PlaybackState::State::Playing); playbackState->updatePositionMs(track->requestedPosition); this->notify(); - // Send playback start event, unpause + // Send playback start event, pause/unpause per request sendEvent(EventType::PLAYBACK_START, (int)track->requestedPosition); - sendEvent(EventType::PLAY_PAUSE, false); + sendEvent(EventType::PLAY_PAUSE, paused); }; this->ctx = ctx; @@ -77,6 +77,12 @@ void SpircHandler::subscribeToMercury() { void SpircHandler::loadTrackFromURI(const std::string& uri) {} +void SpircHandler::notifyAudioEnded() { + playbackState->updatePositionMs(0); + notify(); + trackPlayer->resetState(true); +} + void SpircHandler::notifyAudioReachedPlayback() { int offset = 0; @@ -142,7 +148,6 @@ void SpircHandler::handleFrame(std::vector& data) { notify(); sendEvent(EventType::SEEK, (int)playbackState->remoteFrame.position); - //sendEvent(EventType::FLUSH); break; } case MessageType_kMessageTypeVolume: @@ -157,12 +162,14 @@ void SpircHandler::handleFrame(std::vector& data) { setPause(false); break; case MessageType_kMessageTypeNext: - nextSong(); - sendEvent(EventType::NEXT); + if (nextSong()) { + sendEvent(EventType::NEXT); + } break; case MessageType_kMessageTypePrev: - previousSong(); - sendEvent(EventType::PREV); + if (previousSong()) { + sendEvent(EventType::PREV); + } break; case MessageType_kMessageTypeLoad: { this->trackPlayer->start(); @@ -199,8 +206,8 @@ void SpircHandler::handleFrame(std::vector& data) { false); this->notify(); - trackPlayer->resetState(); sendEvent(EventType::FLUSH); + trackPlayer->resetState(); break; } case MessageType_kMessageTypeShuffle: { @@ -227,34 +234,22 @@ void SpircHandler::notify() { this->sendCmd(MessageType_kMessageTypeNotify); } -void SpircHandler::skipSong(TrackQueue::SkipDirection dir) { - if (trackQueue->skipTrack(dir)) { - playbackState->setPlaybackState(PlaybackState::State::Playing); - notify(); - - // Reset track state - trackPlayer->resetState(); - - sendEvent(EventType::PLAY_PAUSE, false); - } else { - playbackState->setPlaybackState(PlaybackState::State::Paused); - playbackState->updatePositionMs(0); - notify(); - - sendEvent(EventType::PLAY_PAUSE, true); - } +bool SpircHandler::skipSong(TrackQueue::SkipDirection dir) { + bool skipped = trackQueue->skipTrack(dir); - notify(); + // Reset track state + trackPlayer->resetState(!skipped); - sendEvent(EventType::FLUSH); + // send NEXT or PREV event only when successful + return skipped; } -void SpircHandler::nextSong() { - skipSong(TrackQueue::SkipDirection::NEXT); +bool SpircHandler::nextSong() { + return skipSong(TrackQueue::SkipDirection::NEXT); } -void SpircHandler::previousSong() { - skipSong(TrackQueue::SkipDirection::PREV); +bool SpircHandler::previousSong() { + return skipSong(TrackQueue::SkipDirection::PREV); } std::shared_ptr SpircHandler::getTrackPlayer() { diff --git a/components/spotify/cspot/src/TrackPlayer.cpp b/components/spotify/cspot/src/TrackPlayer.cpp index d310caa5d..a252b3cbf 100644 --- a/components/spotify/cspot/src/TrackPlayer.cpp +++ b/components/spotify/cspot/src/TrackPlayer.cpp @@ -87,10 +87,11 @@ void TrackPlayer::stop() { std::scoped_lock lock(runningMutex); } -void TrackPlayer::resetState() { +void TrackPlayer::resetState(bool paused) { // Mark for reset this->pendingReset = true; this->currentSongPlaying = false; + this->startPaused = paused; std::scoped_lock lock(dataOutMutex); @@ -119,7 +120,7 @@ void TrackPlayer::runTask() { while (isRunning) { // Ensure we even have any tracks to play if (!this->trackQueue->hasTracks() || - (endOfQueueReached && trackQueue->isFinished())) { + (!pendingReset && endOfQueueReached && trackQueue->isFinished())) { this->trackQueue->playableSemaphore->twait(300); continue; } @@ -184,7 +185,8 @@ void TrackPlayer::runTask() { } if (trackOffset == 0 && pendingSeekPositionMs == 0) { - this->trackLoaded(track); + this->trackLoaded(track, startPaused); + startPaused = false; } int32_t r = diff --git a/components/spotify/cspot/src/TrackQueue.cpp b/components/spotify/cspot/src/TrackQueue.cpp index f2e440d6e..4369f3cfd 100644 --- a/components/spotify/cspot/src/TrackQueue.cpp +++ b/components/spotify/cspot/src/TrackQueue.cpp @@ -504,11 +504,18 @@ void TrackQueue::processTrack(std::shared_ptr track) { bool TrackQueue::queueNextTrack(int offset, uint32_t positionMs) { const int requestedRefIndex = offset + currentTracksIndex; + if (requestedRefIndex < 0 || requestedRefIndex >= currentTracks.size()) { return false; } - if (offset < 0) { + // in case we re-queue current track, make sure position is updated (0) + if (offset == 0 && preloadedTracks.size() && + preloadedTracks[0]->ref == currentTracks[currentTracksIndex]) { + preloadedTracks.pop_front(); + } + + if (offset <= 0) { preloadedTracks.push_front(std::make_shared( currentTracks[requestedRefIndex], ctx, positionMs)); } else { @@ -520,42 +527,51 @@ bool TrackQueue::queueNextTrack(int offset, uint32_t positionMs) { } bool TrackQueue::skipTrack(SkipDirection dir, bool expectNotify) { - bool canSkipNext = currentTracks.size() > currentTracksIndex + 1; - bool canSkipPrev = currentTracksIndex > 0; - - if ((dir == SkipDirection::NEXT && canSkipNext) || - (dir == SkipDirection::PREV && canSkipPrev)) { + bool skipped = true; std::scoped_lock lock(tracksMutex); - if (dir == SkipDirection::NEXT) { - preloadedTracks.pop_front(); - if (!queueNextTrack(preloadedTracks.size() + 1)) { - CSPOT_LOG(info, "Failed to queue next track"); - } + if (dir == SkipDirection::PREV) { + uint64_t position = !playbackState->innerFrame.state.has_position_ms ? 0 : + playbackState->innerFrame.state.position_ms + + ctx->timeProvider->getSyncedTimestamp() - + playbackState->innerFrame.state.position_measured_at; - currentTracksIndex++; - } else { - queueNextTrack(-1); + if (currentTracksIndex > 0 && position < 3000) { + queueNextTrack(-1); - if (preloadedTracks.size() > MAX_TRACKS_PRELOAD) { - preloadedTracks.pop_back(); + if (preloadedTracks.size() > MAX_TRACKS_PRELOAD) { + preloadedTracks.pop_back(); + } + + currentTracksIndex--; + } else { + queueNextTrack(0); } + } else { + if (currentTracks.size() > currentTracksIndex + 1) { + preloadedTracks.pop_front(); + + if (!queueNextTrack(preloadedTracks.size() + 1)) { + CSPOT_LOG(info, "Failed to queue next track"); + } - currentTracksIndex--; + currentTracksIndex++; + } else { + skipped = false; + } } - // Update frame data - playbackState->innerFrame.state.playing_track_index = currentTracksIndex; + if (skipped) { + // Update frame data + playbackState->innerFrame.state.playing_track_index = currentTracksIndex; - if (expectNotify) { - // Reset position to zero - notifyPending = true; + if (expectNotify) { + // Reset position to zero + notifyPending = true; + } } - return true; - } - - return false; + return skipped; } bool TrackQueue::hasTracks() {