mirror of
https://github.com/sle118/squeezelite-esp32.git
synced 2025-12-06 11:36:59 +03:00
cspot fixes - release
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -229,7 +229,6 @@ void cspotPlayer::eventHandler(std::unique_ptr<cspot::SpircHandler::Event> 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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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, ...) \
|
||||
|
||||
@@ -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<uint8_t>& data);
|
||||
void notify();
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ struct TrackReference;
|
||||
class TrackPlayer : bell::Task {
|
||||
public:
|
||||
// Callback types
|
||||
typedef std::function<void(std::shared_ptr<QueuedTrack>)> TrackLoadedCallback;
|
||||
typedef std::function<void(std::shared_ptr<QueuedTrack>, bool)> TrackLoadedCallback;
|
||||
typedef std::function<size_t(uint8_t*, size_t, std::string_view)>
|
||||
DataCallback;
|
||||
typedef std::function<void()> 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<bool> pendingReset = false;
|
||||
std::atomic<bool> inFuture = false;
|
||||
std::atomic<size_t> pendingSeekPositionMs = 0;
|
||||
std::atomic<bool> startPaused = false;
|
||||
|
||||
std::mutex runningMutex;
|
||||
|
||||
|
||||
@@ -31,15 +31,15 @@ SpircHandler::SpircHandler(std::shared_ptr<cspot::Context> ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
auto trackLoadedCallback = [this](std::shared_ptr<QueuedTrack> track) {
|
||||
playbackState->setPlaybackState(PlaybackState::State::Playing);
|
||||
auto trackLoadedCallback = [this](std::shared_ptr<QueuedTrack> 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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();
|
||||
bool SpircHandler::skipSong(TrackQueue::SkipDirection dir) {
|
||||
bool skipped = trackQueue->skipTrack(dir);
|
||||
|
||||
// Reset track state
|
||||
trackPlayer->resetState();
|
||||
// Reset track state
|
||||
trackPlayer->resetState(!skipped);
|
||||
|
||||
sendEvent(EventType::PLAY_PAUSE, false);
|
||||
} else {
|
||||
playbackState->setPlaybackState(PlaybackState::State::Paused);
|
||||
playbackState->updatePositionMs(0);
|
||||
notify();
|
||||
|
||||
sendEvent(EventType::PLAY_PAUSE, true);
|
||||
}
|
||||
|
||||
notify();
|
||||
|
||||
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<TrackPlayer> SpircHandler::getTrackPlayer() {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -504,11 +504,18 @@ void TrackQueue::processTrack(std::shared_ptr<QueuedTrack> 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<QueuedTrack>(
|
||||
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;
|
||||
|
||||
if (currentTracksIndex > 0 && position < 3000) {
|
||||
queueNextTrack(-1);
|
||||
|
||||
if (preloadedTracks.size() > MAX_TRACKS_PRELOAD) {
|
||||
preloadedTracks.pop_back();
|
||||
}
|
||||
|
||||
currentTracksIndex--;
|
||||
} else {
|
||||
queueNextTrack(0);
|
||||
}
|
||||
|
||||
currentTracksIndex++;
|
||||
} else {
|
||||
queueNextTrack(-1);
|
||||
if (currentTracks.size() > currentTracksIndex + 1) {
|
||||
preloadedTracks.pop_front();
|
||||
|
||||
if (preloadedTracks.size() > MAX_TRACKS_PRELOAD) {
|
||||
preloadedTracks.pop_back();
|
||||
}
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user