123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- #include "PlaybackState.h"
- #include <memory>
- #include "CSpotContext.h"
- #include "Logger.h"
- using namespace cspot;
- PlaybackState::PlaybackState(std::shared_ptr<cspot::Context> ctx) {
- this->ctx = ctx;
- innerFrame = {};
- remoteFrame = {};
- // Prepare default state
- innerFrame.state.has_position_ms = true;
- innerFrame.state.position_ms = 0;
- innerFrame.state.status = PlayStatus_kPlayStatusStop;
- innerFrame.state.has_status = true;
- innerFrame.state.position_measured_at = 0;
- innerFrame.state.has_position_measured_at = true;
- innerFrame.state.shuffle = false;
- innerFrame.state.has_shuffle = true;
- innerFrame.state.repeat = false;
- innerFrame.state.has_repeat = true;
- innerFrame.device_state.sw_version = strdup(swVersion);
- innerFrame.device_state.is_active = false;
- innerFrame.device_state.has_is_active = true;
- innerFrame.device_state.can_play = true;
- innerFrame.device_state.has_can_play = true;
- innerFrame.device_state.volume = ctx->config.volume;
- innerFrame.device_state.has_volume = true;
- innerFrame.device_state.name = strdup(ctx->config.deviceName.c_str());
- innerFrame.state.track_count = 0;
- // Prepare player's capabilities
- addCapability(CapabilityType_kCanBePlayer, 1);
- addCapability(CapabilityType_kDeviceType, 4);
- addCapability(CapabilityType_kGaiaEqConnectId, 1);
- addCapability(CapabilityType_kSupportsLogout, 0);
- addCapability(CapabilityType_kIsObservable, 1);
- addCapability(CapabilityType_kVolumeSteps, 64);
- addCapability(CapabilityType_kSupportedContexts, -1,
- std::vector<std::string>({"album", "playlist", "search",
- "inbox", "toplist", "starred",
- "publishedstarred", "track"}));
- addCapability(CapabilityType_kSupportedTypes, -1,
- std::vector<std::string>({"audio/local", "audio/track",
- "audio/episode", "local", "track"}));
- innerFrame.device_state.capabilities_count = 8;
- }
- PlaybackState::~PlaybackState() {
- pb_release(Frame_fields, &innerFrame);
- pb_release(Frame_fields, &remoteFrame);
- }
- void PlaybackState::setPlaybackState(const PlaybackState::State state) {
- switch (state) {
- case State::Loading:
- // Prepare the playback at position 0
- innerFrame.state.status = PlayStatus_kPlayStatusPause;
- innerFrame.state.position_ms = 0;
- innerFrame.state.position_measured_at =
- ctx->timeProvider->getSyncedTimestamp();
- break;
- case State::Playing:
- innerFrame.state.status = PlayStatus_kPlayStatusPlay;
- innerFrame.state.position_measured_at =
- ctx->timeProvider->getSyncedTimestamp();
- break;
- case State::Stopped:
- break;
- case State::Paused:
- // Update state and recalculate current song position
- innerFrame.state.status = PlayStatus_kPlayStatusPause;
- uint32_t diff = ctx->timeProvider->getSyncedTimestamp() -
- innerFrame.state.position_measured_at;
- this->updatePositionMs(innerFrame.state.position_ms + diff);
- break;
- }
- }
- bool PlaybackState::isActive() {
- return innerFrame.device_state.is_active;
- }
- bool PlaybackState::nextTrack() {
- innerFrame.state.playing_track_index++;
- if (innerFrame.state.playing_track_index >= innerFrame.state.track_count) {
- innerFrame.state.playing_track_index = 0;
- if (!innerFrame.state.repeat) {
- setPlaybackState(State::Paused);
- return false;
- }
- }
- return true;
- }
- void PlaybackState::prevTrack() {
- if (innerFrame.state.playing_track_index > 0) {
- innerFrame.state.playing_track_index--;
- } else if (innerFrame.state.repeat) {
- innerFrame.state.playing_track_index = innerFrame.state.track_count - 1;
- }
- }
- void PlaybackState::setActive(bool isActive) {
- innerFrame.device_state.is_active = isActive;
- if (isActive) {
- innerFrame.device_state.became_active_at =
- ctx->timeProvider->getSyncedTimestamp();
- innerFrame.device_state.has_became_active_at = true;
- }
- }
- void PlaybackState::updatePositionMs(uint32_t position) {
- innerFrame.state.position_ms = position;
- innerFrame.state.position_measured_at =
- ctx->timeProvider->getSyncedTimestamp();
- }
- #define FREE(ptr) \
- { \
- free(ptr); \
- ptr = NULL; \
- }
- #define STRDUP(dst, src) \
- if (src != NULL) { \
- dst = strdup(src); \
- } else { \
- FREE(dst); \
- } // strdup null pointer safe
- void PlaybackState::updateTracks() {
- CSPOT_LOG(info, "---- Track count %d", remoteFrame.state.track_count);
- CSPOT_LOG(info, "---- Inner track count %d", innerFrame.state.track_count);
- CSPOT_LOG(info, "--- Context URI %s", remoteFrame.state.context_uri);
- // free unused tracks
- if (innerFrame.state.track_count > remoteFrame.state.track_count) {
- for (uint16_t i = remoteFrame.state.track_count;
- i < innerFrame.state.track_count; ++i) {
- FREE(innerFrame.state.track[i].gid);
- FREE(innerFrame.state.track[i].uri);
- FREE(innerFrame.state.track[i].context);
- }
- }
- // reallocate memory for new tracks
- innerFrame.state.track = (TrackRef*)realloc(
- innerFrame.state.track, sizeof(TrackRef) * remoteFrame.state.track_count);
- for (uint16_t i = 0; i < remoteFrame.state.track_count; ++i) {
- if (i >= innerFrame.state.track_count) {
- innerFrame.state.track[i].gid = NULL;
- innerFrame.state.track[i].uri = NULL;
- innerFrame.state.track[i].context = NULL;
- }
- if (remoteFrame.state.track[i].gid != NULL) {
- uint16_t gid_size = remoteFrame.state.track[i].gid->size;
- innerFrame.state.track[i].gid = (pb_bytes_array_t*)realloc(
- innerFrame.state.track[i].gid, PB_BYTES_ARRAY_T_ALLOCSIZE(gid_size));
- memcpy(innerFrame.state.track[i].gid->bytes,
- remoteFrame.state.track[i].gid->bytes, gid_size);
- innerFrame.state.track[i].gid->size = gid_size;
- }
- innerFrame.state.track[i].has_queued =
- remoteFrame.state.track[i].has_queued;
- innerFrame.state.track[i].queued = remoteFrame.state.track[i].queued;
- STRDUP(innerFrame.state.track[i].uri, remoteFrame.state.track[i].uri);
- STRDUP(innerFrame.state.track[i].context,
- remoteFrame.state.track[i].context);
- }
- innerFrame.state.context_uri = (char*)realloc(
- innerFrame.state.context_uri, strlen(remoteFrame.state.context_uri) + 1);
- strcpy(innerFrame.state.context_uri, remoteFrame.state.context_uri);
- innerFrame.state.track_count = remoteFrame.state.track_count;
- innerFrame.state.has_playing_track_index = true;
- innerFrame.state.playing_track_index = remoteFrame.state.playing_track_index;
- if (remoteFrame.state.repeat) {
- setRepeat(true);
- }
- if (remoteFrame.state.shuffle) {
- setShuffle(true);
- }
- }
- void PlaybackState::setVolume(uint32_t volume) {
- innerFrame.device_state.volume = volume;
- ctx->config.volume = volume;
- }
- void PlaybackState::setShuffle(bool shuffle) {
- innerFrame.state.shuffle = shuffle;
- if (shuffle) {
- // Put current song at the begining
- std::swap(innerFrame.state.track[0],
- innerFrame.state.track[innerFrame.state.playing_track_index]);
- // Shuffle current tracks
- for (int x = 1; x < innerFrame.state.track_count - 1; x++) {
- auto j = x + (std::rand() % (innerFrame.state.track_count - x));
- std::swap(innerFrame.state.track[j], innerFrame.state.track[x]);
- }
- innerFrame.state.playing_track_index = 0;
- }
- }
- void PlaybackState::setRepeat(bool repeat) {
- innerFrame.state.repeat = repeat;
- }
- TrackRef* PlaybackState::getCurrentTrackRef() {
- if (innerFrame.state.playing_track_index >= innerFrame.state.track_count) {
- return nullptr;
- }
- return &innerFrame.state.track[innerFrame.state.playing_track_index];
- }
- TrackRef* PlaybackState::getNextTrackRef() {
- if ((innerFrame.state.playing_track_index + 1) >= innerFrame.state.track_count) {
- if (innerFrame.state.repeat) {
- return &innerFrame.state.track[0];
- }
- return nullptr;
- }
- return &innerFrame.state.track[innerFrame.state.playing_track_index + 1];
- }
- std::vector<uint8_t> PlaybackState::encodeCurrentFrame(MessageType typ) {
- free(innerFrame.ident);
- free(innerFrame.protocol_version);
- // Prepare current frame info
- innerFrame.version = 1;
- innerFrame.ident = strdup(ctx->config.deviceId.c_str());
- innerFrame.seq_nr = this->seqNum;
- innerFrame.protocol_version = strdup(protocolVersion);
- innerFrame.typ = typ;
- innerFrame.state_update_id = ctx->timeProvider->getSyncedTimestamp();
- innerFrame.has_version = true;
- innerFrame.has_seq_nr = true;
- innerFrame.recipient_count = 0;
- innerFrame.has_state = true;
- innerFrame.has_device_state = true;
- innerFrame.has_typ = true;
- innerFrame.has_state_update_id = true;
- this->seqNum += 1;
- return pbEncode(Frame_fields, &innerFrame);
- }
- // Wraps messy nanopb setters. @TODO: find a better way to handle this
- void PlaybackState::addCapability(CapabilityType typ, int intValue,
- std::vector<std::string> stringValue) {
- innerFrame.device_state.capabilities[capabilityIndex].has_typ = true;
- this->innerFrame.device_state.capabilities[capabilityIndex].typ = typ;
- if (intValue != -1) {
- this->innerFrame.device_state.capabilities[capabilityIndex].intValue[0] =
- intValue;
- this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count =
- 1;
- } else {
- this->innerFrame.device_state.capabilities[capabilityIndex].intValue_count =
- 0;
- }
- for (int x = 0; x < stringValue.size(); x++) {
- pbPutString(stringValue[x],
- this->innerFrame.device_state.capabilities[capabilityIndex]
- .stringValue[x]);
- }
- this->innerFrame.device_state.capabilities[capabilityIndex]
- .stringValue_count = stringValue.size();
- this->capabilityIndex += 1;
- }
|