Spotify: end the session for OTA; app volume starts at the player's

- OTA: the player now ends the Spotify session (spotify_suspend: stop
  flag, waits until the session's tasks are gone, refuses new ones)
  instead of pausing it, and suspends HLS. Pausing was not enough (an
  upload still failed once with a paused session). Verified twice with a
  session running for minutes: "Disconnecting mercury session / session
  ended / suspended", installed in ~26 s, clean reboot.
- Volume: createFromBlob() starts cspot at volume 0, which the app
  showed while we played at 100 %. The session now starts with the
  player's volume (spotify_set_volume); volumes coming from the app are
  stored exactly, without echo. Verified: slider starts at 100 %.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 20:15:29 +10:00
parent 0aac7bc339
commit f0fe53d5d1
3 changed files with 51 additions and 9 deletions
+6
View File
@@ -30,6 +30,12 @@ esp_err_t spotify_init(spotify_pcm_cb_t pcm, spotify_event_cb_t event);
void spotify_apply(void);
// Pause/resume the running session (the Spotify app follows). No-op without a session.
void spotify_pause(bool pause);
// Suspend: end any running session (waits until its tasks are gone, max ~3 s) and refuse new ones.
// Resume: back to what the config says. Used around firmware uploads.
void spotify_suspend(bool suspend);
// The player's volume (0..100 %): reported to the Spotify app when a session starts (else the app
// shows 0 while we play at the player's level), and pushed to a running session.
void spotify_set_volume(int pct);
// Current state for status.spotify_state.
const char *spotify_state(void);
+37
View File
@@ -33,12 +33,44 @@ static std::string s_name; // advertised device nam
static std::atomic<bool> s_enabled{false};
static std::atomic<bool> s_stop{false}; // end the running session
static std::atomic<bool> s_got_blob{false};
static std::atomic<bool> s_in_session{false}; // session() running (its tasks may exist)
static std::atomic<int> s_volume{65535}; // player volume in Spotify units (0..65535)
static std::atomic<const char *> s_state{"disabled"};
static std::atomic<uint64_t> s_pcm_bytes{0};
static std::shared_ptr<cspot::SpircHandler> s_handler; // running session, else null
static spotify_pcm_cb_t s_pcm_cb;
static spotify_event_cb_t s_event_cb;
extern "C" void spotify_suspend(bool suspend)
{
if (!suspend) {
spotify_apply();
return;
}
s_enabled = false;
s_stop = true;
s_got_blob = false;
s_state = "suspended";
for (int i = 0; i < 30 && s_in_session; i++) { // session loop exits within 200 ms, then disconnect
vTaskDelay(pdMS_TO_TICKS(100));
}
ESP_LOGI(TAG, "suspended%s", s_in_session ? " (session still ending)" : "");
}
extern "C" void spotify_set_volume(int pct)
{
pct = pct < 0 ? 0 : pct > 100 ? 100 : pct;
int v = pct * 65535 / 100;
if (v == s_volume) {
return;
}
s_volume = v;
auto h = s_handler;
if (h) {
h->setRemoteVolume(v);
}
}
extern "C" void spotify_pause(bool pause)
{
auto h = s_handler;
@@ -203,6 +235,8 @@ static void session(void)
ctx->config.clientSecret = client_secret;
ctx->config.audioFormat = bitrate >= 320 ? AudioFormat_OGG_VORBIS_320
: bitrate >= 160 ? AudioFormat_OGG_VORBIS_160 : AudioFormat_OGG_VORBIS_96;
// createFromBlob() starts at volume 0, which the app would show; report what we actually play.
ctx->config.volume = s_volume;
s_state = "connecting";
ctx->session->connectWithRandomAp();
auto token = ctx->session->authenticate(login);
@@ -246,6 +280,7 @@ static void session(void)
}
break;
case E::VOLUME:
s_volume = std::get<int>(ev->data); // exact value from the app, no echo back
if (s_event_cb) {
s_event_cb(SPOTIFY_EV_VOLUME, std::get<int>(ev->data));
}
@@ -278,6 +313,7 @@ static void spotify_task(void *arg)
}
s_got_blob = false;
s_stop = false;
s_in_session = true;
try {
session();
} catch (const std::exception &e) {
@@ -285,6 +321,7 @@ static void spotify_task(void *arg)
ESP_LOGE(TAG, "session ended: %s", e.what());
}
s_handler.reset();
s_in_session = false;
vTaskDelay(pdMS_TO_TICKS(2000));
}
}