SpircController.cpp 7.1 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. stopPlayer();
  49. state->setActive(false);
  50. notify();
  51. sendEvent(CSpotEventType::DISC);
  52. }
  53. void SpircController::playToggle() {
  54. if (state->innerFrame.state->status.value() == PlayStatus::kPlayStatusPause) {
  55. setPause(false);
  56. } else {
  57. setPause(true);
  58. }
  59. }
  60. void SpircController::adjustVolume(int by) {
  61. if (state->innerFrame.device_state->volume.has_value()) {
  62. int volume = state->innerFrame.device_state->volume.value() + 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. state->remoteFrame = decodePb<Frame>(data);
  92. switch (state->remoteFrame.typ.value()) {
  93. case MessageType::kMessageTypeNotify: {
  94. CSPOT_LOG(debug, "Notify frame");
  95. // Pause the playback if another player took control
  96. if (state->isActive() &&
  97. state->remoteFrame.device_state->is_active.value()) {
  98. disconnect();
  99. }
  100. break;
  101. }
  102. case MessageType::kMessageTypeSeek: {
  103. CSPOT_LOG(debug, "Seek command");
  104. sendEvent(CSpotEventType::SEEK, (int) state->remoteFrame.position.value());
  105. state->updatePositionMs(state->remoteFrame.position.value());
  106. this->player->seekMs(state->remoteFrame.position.value());
  107. notify();
  108. break;
  109. }
  110. case MessageType::kMessageTypeVolume:
  111. sendEvent(CSpotEventType::VOLUME, (int) state->remoteFrame.volume.value());
  112. setVolume(state->remoteFrame.volume.value());
  113. break;
  114. case MessageType::kMessageTypePause:
  115. setPause(true);
  116. break;
  117. case MessageType::kMessageTypePlay:
  118. setPause(false);
  119. break;
  120. case MessageType::kMessageTypeNext:
  121. sendEvent(CSpotEventType::NEXT);
  122. nextSong();
  123. break;
  124. case MessageType::kMessageTypePrev:
  125. sendEvent(CSpotEventType::PREV);
  126. prevSong();
  127. break;
  128. case MessageType::kMessageTypeLoad: {
  129. CSPOT_LOG(debug, "Load frame!");
  130. state->setActive(true);
  131. // Every sane person on the planet would expect std::move to work here.
  132. // And it does... on every single platform EXCEPT for ESP32 for some
  133. // reason. For which it corrupts memory and makes printf fail. so yeah.
  134. // its cursed.
  135. state->updateTracks();
  136. // bool isPaused = (state->remoteFrame.state->status.value() ==
  137. // PlayStatus::kPlayStatusPlay) ? false : true;
  138. loadTrack(state->remoteFrame.state->position_ms.value(), false);
  139. state->updatePositionMs(state->remoteFrame.state->position_ms.value());
  140. this->notify();
  141. break;
  142. }
  143. case MessageType::kMessageTypeReplace: {
  144. CSPOT_LOG(debug, "Got replace frame!");
  145. break;
  146. }
  147. case MessageType::kMessageTypeShuffle: {
  148. CSPOT_LOG(debug, "Got shuffle frame");
  149. state->setShuffle(state->remoteFrame.state->shuffle.value());
  150. this->notify();
  151. break;
  152. }
  153. case MessageType::kMessageTypeRepeat: {
  154. CSPOT_LOG(debug, "Got repeat frame");
  155. state->setRepeat(state->remoteFrame.state->repeat.value());
  156. this->notify();
  157. break;
  158. }
  159. default:
  160. break;
  161. }
  162. }
  163. void SpircController::loadTrack(uint32_t position_ms, bool isPaused) {
  164. sendEvent(CSpotEventType::LOAD, (int) position_ms);
  165. state->setPlaybackState(PlaybackState::Loading);
  166. std::function<void()> loadedLambda = [=]() {
  167. // Loading finished, notify that playback started
  168. setPause(isPaused, false);
  169. };
  170. player->handleLoad(state->getCurrentTrack(), loadedLambda, position_ms,
  171. isPaused);
  172. }
  173. void SpircController::notify() {
  174. this->sendCmd(MessageType::kMessageTypeNotify);
  175. }
  176. void SpircController::sendEvent(CSpotEventType eventType, std::variant<TrackInfo, int, bool> data) {
  177. if (eventHandler != nullptr) {
  178. CSpotEvent event = {
  179. .eventType = eventType,
  180. .data = data,
  181. };
  182. eventHandler(event);
  183. }
  184. }
  185. void SpircController::setEventHandler(cspotEventHandler callback) {
  186. this->eventHandler = callback;
  187. player->trackChanged = ([this](TrackInfo &track) {
  188. TrackInfo info;
  189. info.album = track.album;
  190. info.artist = track.artist;
  191. info.imageUrl = track.imageUrl;
  192. info.name = track.name;
  193. info.duration = track.duration;
  194. this->sendEvent(CSpotEventType::TRACK_INFO, info);
  195. });
  196. }
  197. void SpircController::stopPlayer() { this->player->stop(); }
  198. void SpircController::sendCmd(MessageType typ) {
  199. // Serialize current player state
  200. auto encodedFrame = state->encodeCurrentFrame(typ);
  201. mercuryCallback responseLambda = [=](std::unique_ptr<MercuryResponse> res) {
  202. };
  203. auto parts = mercuryParts({encodedFrame});
  204. this->manager->execute(MercuryType::SEND,
  205. "hm://remote/user/" + this->username + "/",
  206. responseLambda, parts);
  207. }