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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user