Player: seek and volume on /api/player

seek {ms} runs on the Spotify session task like a seek from the app (buffer
flushed, app notified). volume {value}|{delta} sets the player volume and
pushes it to the session, so the app's slider follows; setRemoteVolume now
also runs on the session task.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 23:04:11 +10:00
parent 8485ecb5b7
commit a55a3ce14a
4 changed files with 94 additions and 18 deletions
+5 -2
View File
@@ -32,10 +32,13 @@ esp_err_t spotify_init(spotify_pcm_cb_t pcm, spotify_event_cb_t event, spotify_p
// the client credentials are set; otherwise not advertised and a running session is ended.
// A new device name ends the session and re-advertises.
void spotify_apply(void);
typedef enum { SPOTIFY_CMD_PLAY, SPOTIFY_CMD_PAUSE, SPOTIFY_CMD_NEXT, SPOTIFY_CMD_PREV } spotify_cmd_t;
typedef enum {
SPOTIFY_CMD_PLAY, SPOTIFY_CMD_PAUSE, SPOTIFY_CMD_NEXT, SPOTIFY_CMD_PREV,
SPOTIFY_CMD_SEEK, // arg: position in ms
} spotify_cmd_t;
// Control the running session (the Spotify app follows). Runs on the session task; waits until it
// has run (max 1 s). Returns false without a session.
bool spotify_command(spotify_cmd_t cmd);
bool spotify_command(spotify_cmd_t cmd, uint32_t arg);
// 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);
+28 -9
View File
@@ -57,7 +57,12 @@ static std::string s_last_id; // track of the most recent data callbac
static std::atomic<bool> s_depleted{false};
static std::mutex s_cmd_mutex;
static std::deque<spotify_cmd_t> s_cmds; // from spotify_command(), run by the session loop
enum { CMD_VOLUME = 100 }; // internal: push s_volume to the app
struct Command {
int cmd; // spotify_cmd_t or CMD_VOLUME
uint32_t arg;
};
static std::deque<Command> s_cmds; // from spotify_command(), run by the session loop
static uint32_t s_cmd_queued; // commands queued so far (s_cmd_mutex)
static std::atomic<uint32_t> s_cmd_done{0}; // commands run so far
@@ -123,13 +128,14 @@ extern "C" void spotify_set_volume(int pct)
return;
}
s_volume = v;
auto h = s_handler;
if (h) {
h->setRemoteVolume(v);
if (s_handler) {
std::lock_guard<std::mutex> lock(s_cmd_mutex);
s_cmds.push_back({ CMD_VOLUME, 0 });
++s_cmd_queued;
}
}
extern "C" bool spotify_command(spotify_cmd_t cmd)
extern "C" bool spotify_command(spotify_cmd_t cmd, uint32_t arg)
{
if (!s_handler) {
return false;
@@ -137,7 +143,7 @@ extern "C" bool spotify_command(spotify_cmd_t cmd)
uint32_t seq;
{
std::lock_guard<std::mutex> lock(s_cmd_mutex);
s_cmds.push_back(cmd);
s_cmds.push_back({ cmd, arg });
seq = ++s_cmd_queued;
}
// Wait until it has run (the loop runs every <= 200 ms), so callers can report the new state.
@@ -150,14 +156,14 @@ extern "C" bool spotify_command(spotify_cmd_t cmd)
// Session loop: run queued commands on the task that also handles Spotify's frames.
static void run_commands(const std::shared_ptr<cspot::SpircHandler> &handler)
{
std::deque<spotify_cmd_t> cmds;
std::deque<Command> cmds;
uint32_t seq;
{
std::lock_guard<std::mutex> lock(s_cmd_mutex);
cmds.swap(s_cmds);
seq = s_cmd_queued;
}
for (auto cmd : cmds) {
for (auto [cmd, arg] : cmds) {
switch (cmd) {
case SPOTIFY_CMD_PLAY:
case SPOTIFY_CMD_PAUSE:
@@ -170,8 +176,21 @@ static void run_commands(const std::shared_ptr<cspot::SpircHandler> &handler)
ESP_LOGW(TAG, "no %s track", cmd == SPOTIFY_CMD_NEXT ? "next" : "previous");
}
break;
case SPOTIFY_CMD_SEEK:
// As for a seek from the app (SpircHandler::handleFrame): the buffered audio is void.
handler->getTrackPlayer()->seekMs(arg ? arg : 1); // 0 means "no seek" to TrackPlayer
handler->updatePositionMs(arg); // notifies the app
clear_boundaries(false);
s_depleted = false;
if (s_event_cb) {
s_event_cb(SPOTIFY_EV_FLUSH, 0);
}
break;
case CMD_VOLUME:
handler->setRemoteVolume(s_volume); // notifies the app
break;
}
ESP_LOGI(TAG, "command %d", (int)cmd);
ESP_LOGI(TAG, "command %d %lu", cmd, (unsigned long)arg);
}
s_cmd_done = seq;
}