SpircController.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. #include "SpircController.h"
  2. #include "ConfigJSON.h"
  3. #include "Logger.h"
  4. #include "SpotifyTrack.h"
  5. SpircController::SpircController(std::shared_ptr<MercuryManager> manager,
  6. std::string username,
  7. std::shared_ptr<AudioSink> audioSink) {
  8. this->manager = manager;
  9. this->player = std::make_unique<Player>(manager, audioSink);
  10. this->state = std::make_unique<PlayerState>(manager->timeProvider);
  11. this->username = username;
  12. player->endOfFileCallback = [=]() {
  13. if (state->nextTrack()) {
  14. loadTrack();
  15. }
  16. };
  17. player->setVolume(configMan->volume);
  18. subscribe();
  19. }
  20. void SpircController::subscribe() {
  21. mercuryCallback responseLambda = [=](std::unique_ptr<MercuryResponse> res) {
  22. // this->trackInformationCallback(std::move(res));
  23. sendCmd(MessageType_kMessageTypeHello);
  24. CSPOT_LOG(debug, "Sent kMessageTypeHello!");
  25. };
  26. mercuryCallback subLambda = [=](std::unique_ptr<MercuryResponse> res) {
  27. this->handleFrame(res->parts[0]);
  28. };
  29. manager->execute(MercuryType::SUB,
  30. "hm://remote/user/" + this->username + "/", responseLambda,
  31. subLambda);
  32. }
  33. void SpircController::setPause(bool isPaused, bool notifyPlayer) {
  34. sendEvent(CSpotEventType::PLAY_PAUSE, isPaused);
  35. if (isPaused) {
  36. CSPOT_LOG(debug, "External pause command");
  37. if (notifyPlayer) player->pause();
  38. state->setPlaybackState(PlaybackState::Paused);
  39. } else {
  40. CSPOT_LOG(debug, "External play command");
  41. if (notifyPlayer) player->play();
  42. state->setPlaybackState(PlaybackState::Playing);
  43. }
  44. notify();
  45. }
  46. void SpircController::disconnect(void) {
  47. player->cancelCurrentTrack();
  48. state->setActive(false);
  49. notify();
  50. // Send the event at the end at it might be a last gasp
  51. sendEvent(CSpotEventType::DISC);
  52. }
  53. void SpircController::playToggle() {
  54. if (state->innerFrame.state.status == PlayStatus_kPlayStatusPause) {
  55. setPause(false);
  56. } else {
  57. setPause(true);
  58. }
  59. }
  60. void SpircController::adjustVolume(int by) {
  61. if (state->innerFrame.device_state.has_volume) {
  62. int volume = state->innerFrame.device_state.volume + by;
  63. if (volume < 0) volume = 0;
  64. else if (volume > MAX_VOLUME) volume = MAX_VOLUME;
  65. setVolume(volume);
  66. }
  67. }
  68. void SpircController::setVolume(int volume) {
  69. setRemoteVolume(volume);
  70. player->setVolume(volume);
  71. configMan->save();
  72. }
  73. void SpircController::setRemoteVolume(int volume) {
  74. state->setVolume(volume);
  75. notify();
  76. }
  77. void SpircController::nextSong() {
  78. if (state->nextTrack()) {
  79. loadTrack();
  80. } else {
  81. player->cancelCurrentTrack();
  82. }
  83. notify();
  84. }
  85. void SpircController::prevSong() {
  86. state->prevTrack();
  87. loadTrack();
  88. notify();
  89. }
  90. void SpircController::handleFrame(std::vector<uint8_t> &data) {
  91. pb_release(Frame_fields, state->remoteFrame);
  92. pbDecode(state->remoteFrame, Frame_fields, data);
  93. switch (state->remoteFrame.typ) {
  94. case MessageType_kMessageTypeNotify: {
  95. CSPOT_LOG(debug, "Notify frame");
  96. // Pause the playback if another player took control
  97. if (state->isActive() &&
  98. state->remoteFrame.device_state.is_active) {
  99. disconnect();
  100. }
  101. break;
  102. }
  103. case MessageType_kMessageTypeSeek: {
  104. CSPOT_LOG(debug, "Seek command");
  105. sendEvent(CSpotEventType::SEEK, (int) state->remoteFrame.position);
  106. state->updatePositionMs(state->remoteFrame.position);
  107. this->player->seekMs(state->remoteFrame.position);
  108. notify();
  109. break;
  110. }
  111. case MessageType_kMessageTypeVolume:
  112. sendEvent(CSpotEventType::VOLUME, (int) state->remoteFrame.volume);
  113. setVolume(state->remoteFrame.volume);
  114. break;
  115. case MessageType_kMessageTypePause:
  116. setPause(true);
  117. break;
  118. case MessageType_kMessageTypePlay:
  119. setPause(false);
  120. break;
  121. case MessageType_kMessageTypeNext:
  122. sendEvent(CSpotEventType::NEXT);
  123. nextSong();
  124. break;
  125. case MessageType_kMessageTypePrev:
  126. sendEvent(CSpotEventType::PREV);
  127. prevSong();
  128. break;
  129. case MessageType_kMessageTypeLoad: {
  130. CSPOT_LOG(debug, "Load frame!");
  131. state->setActive(true);
  132. // Every sane person on the planet would expect std::move to work here.
  133. // And it does... on every single platform EXCEPT for ESP32 for some
  134. // reason. For which it corrupts memory and makes printf fail. so yeah.
  135. // its cursed.
  136. state->updateTracks();
  137. // bool isPaused = (state->remoteFrame.state->status.value() ==
  138. // PlayStatus::kPlayStatusPlay) ? false : true;
  139. loadTrack(state->remoteFrame.state.position_ms, false);
  140. state->updatePositionMs(state->remoteFrame.state.position_ms);
  141. this->notify();
  142. break;
  143. }
  144. case MessageType_kMessageTypeReplace: {
  145. CSPOT_LOG(debug, "Got replace frame!");
  146. break;
  147. }
  148. case MessageType_kMessageTypeShuffle: {
  149. CSPOT_LOG(debug, "Got shuffle frame");
  150. state->setShuffle(state->remoteFrame.state.shuffle);
  151. this->notify();
  152. break;
  153. }
  154. case MessageType_kMessageTypeRepeat: {
  155. CSPOT_LOG(debug, "Got repeat frame");
  156. state->setRepeat(state->remoteFrame.state.repeat);
  157. this->notify();
  158. break;
  159. }
  160. default:
  161. break;
  162. }
  163. }
  164. void SpircController::loadTrack(uint32_t position_ms, bool isPaused) {
  165. sendEvent(CSpotEventType::LOAD, (int) position_ms);
  166. state->setPlaybackState(PlaybackState::Loading);
  167. std::function<void()> loadedLambda = [=]() {
  168. // Loading finished, notify that playback started
  169. setPause(isPaused, false);
  170. };
  171. player->handleLoad(state->getCurrentTrack(), loadedLambda, position_ms,
  172. isPaused);
  173. }
  174. void SpircController::notify() {
  175. this->sendCmd(MessageType_kMessageTypeNotify);
  176. }
  177. void SpircController::sendEvent(CSpotEventType eventType, std::variant<TrackInfo, int, bool> data) {
  178. if (eventHandler != nullptr) {
  179. CSpotEvent event = {
  180. .eventType = eventType,
  181. .data = data,
  182. };
  183. eventHandler(event);
  184. }
  185. }
  186. void SpircController::setEventHandler(cspotEventHandler callback) {
  187. this->eventHandler = callback;
  188. player->trackChanged = ([this](TrackInfo &track) {
  189. TrackInfo info;
  190. info.album = track.album;
  191. info.artist = track.artist;
  192. info.imageUrl = track.imageUrl;
  193. info.name = track.name;
  194. info.duration = track.duration;
  195. this->sendEvent(CSpotEventType::TRACK_INFO, info);
  196. });
  197. }
  198. void SpircController::stopPlayer() { this->player->stop(); }
  199. void SpircController::sendCmd(MessageType typ) {
  200. // Serialize current player state
  201. auto encodedFrame = state->encodeCurrentFrame(typ);
  202. mercuryCallback responseLambda = [=](std::unique_ptr<MercuryResponse> res) {
  203. };
  204. auto parts = mercuryParts({encodedFrame});
  205. this->manager->execute(MercuryType::SEND,
  206. "hm://remote/user/" + this->username + "/",
  207. responseLambda, parts);
  208. }