SpircController.cpp 7.4 KB

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