cspot_sink.c 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <ctype.h>
  4. #include <stdlib.h>
  5. #include "mdns.h"
  6. #include "nvs.h"
  7. #include "tcpip_adapter.h"
  8. // IDF-V4++ #include "esp_netif.h"
  9. #include "esp_log.h"
  10. #include "esp_console.h"
  11. #include "esp_pthread.h"
  12. #include "esp_system.h"
  13. #include "freertos/timers.h"
  14. #include "platform_config.h"
  15. #include "audio_controls.h"
  16. #include "display.h"
  17. #include "accessors.h"
  18. #include "cspot_private.h"
  19. #include "cspot_sink.h"
  20. static EXT_RAM_ATTR struct cspot_cb_s {
  21. cspot_cmd_vcb_t cmd;
  22. cspot_data_cb_t data;
  23. } cspot_cbs;
  24. static const char TAG[] = "cspot";
  25. static struct cspot_s *cspot;
  26. static cspot_cmd_vcb_t cmd_handler_chain;
  27. static void cspot_volume_up(bool pressed) {
  28. if (!pressed) return;
  29. cspot_cmd(cspot, CSPOT_VOLUME_UP, NULL);
  30. ESP_LOGI(TAG, "CSpot volume up");
  31. }
  32. static void cspot_volume_down(bool pressed) {
  33. if (!pressed) return;
  34. cspot_cmd(cspot, CSPOT_VOLUME_DOWN, NULL);
  35. ESP_LOGI(TAG, "CSpot volume down");
  36. }
  37. static void cspot_toggle(bool pressed) {
  38. if (!pressed) return;
  39. cspot_cmd(cspot, CSPOT_TOGGLE, NULL);
  40. ESP_LOGI(TAG, "CSpot play/pause");
  41. }
  42. static void cspot_pause(bool pressed) {
  43. if (!pressed) return;
  44. cspot_cmd(cspot, CSPOT_PAUSE, NULL);
  45. ESP_LOGI(TAG, "CSpot pause");
  46. }
  47. static void cspot_play(bool pressed) {
  48. if (!pressed) return;
  49. cspot_cmd(cspot, CSPOT_PLAY, NULL);
  50. ESP_LOGI(TAG, "CSpot play");
  51. }
  52. static void cspot_stop(bool pressed) {
  53. if (!pressed) return;
  54. cspot_cmd(cspot, CSPOT_STOP, NULL);
  55. ESP_LOGI(TAG, "CSpot stop");
  56. }
  57. static void cspot_prev(bool pressed) {
  58. if (!pressed) return;
  59. cspot_cmd(cspot, CSPOT_PREV, NULL);
  60. ESP_LOGI(TAG, "CSpot previous");
  61. }
  62. static void cspot_next(bool pressed) {
  63. if (!pressed) return;
  64. cspot_cmd(cspot, CSPOT_NEXT, NULL);
  65. ESP_LOGI(TAG, "CSpot next");
  66. }
  67. const static actrls_t controls = {
  68. NULL, // power
  69. cspot_volume_up, cspot_volume_down, // volume up, volume down
  70. cspot_toggle, cspot_play, // toggle, play
  71. cspot_pause, cspot_stop, // pause, stop
  72. NULL, NULL, // rew, fwd
  73. cspot_prev, cspot_next, // prev, next
  74. NULL, NULL, NULL, NULL, // left, right, up, down
  75. NULL, NULL, NULL, NULL, NULL, NULL, // pre1-6
  76. cspot_volume_down, cspot_volume_up, cspot_toggle// knob left, knob_right, knob push
  77. };
  78. /****************************************************************************************
  79. * Command handler
  80. */
  81. static bool cmd_handler(cspot_event_t event, ...) {
  82. va_list args;
  83. va_start(args, event);
  84. // handle audio event and stop if forbidden
  85. if (!cmd_handler_chain(event, args)) {
  86. va_end(args);
  87. return false;
  88. }
  89. // now handle events for display
  90. switch(event) {
  91. case CSPOT_SETUP:
  92. actrls_set(controls, false, NULL, actrls_ir_action);
  93. displayer_control(DISPLAYER_ACTIVATE, "SPOTIFY");
  94. break;
  95. case CSPOT_PLAY:
  96. displayer_control(DISPLAYER_TIMER_RUN);
  97. break;
  98. case CSPOT_PAUSE:
  99. displayer_control(DISPLAYER_TIMER_PAUSE);
  100. break;
  101. case CSPOT_DISC:
  102. actrls_unset();
  103. displayer_control(DISPLAYER_SUSPEND);
  104. break;
  105. case CSPOT_SEEK:
  106. displayer_timer(DISPLAYER_ELAPSED, va_arg(args, int), -1);
  107. break;
  108. case CSPOT_TRACK: {
  109. uint32_t sample_rate = va_arg(args, uint32_t);
  110. char *artist = va_arg(args, char*), *album = va_arg(args, char*), *title = va_arg(args, char*);
  111. displayer_metadata(artist, album, title);
  112. displayer_timer(DISPLAYER_ELAPSED, 0, -1);
  113. break;
  114. }
  115. // nothing to do on CSPOT_FLUSH
  116. default:
  117. break;
  118. }
  119. va_end(args);
  120. return true;
  121. }
  122. /****************************************************************************************
  123. * CSpot sink de-initialization
  124. */
  125. void cspot_sink_deinit(void) {
  126. mdns_free();
  127. }
  128. /****************************************************************************************
  129. * CSpot sink startup
  130. */
  131. static bool cspot_sink_start(cspot_cmd_vcb_t cmd_cb, cspot_data_cb_t data_cb) {
  132. const char *hostname = NULL;
  133. tcpip_adapter_ip_info_t ipInfo = { };
  134. tcpip_adapter_if_t ifs[] = { TCPIP_ADAPTER_IF_ETH, TCPIP_ADAPTER_IF_STA, TCPIP_ADAPTER_IF_AP };
  135. // get various IP info
  136. for (int i = 0; i < sizeof(ifs) / sizeof(tcpip_adapter_if_t); i++)
  137. if (tcpip_adapter_get_ip_info(ifs[i], &ipInfo) == ESP_OK && ipInfo.ip.addr != IPADDR_ANY) {
  138. tcpip_adapter_get_hostname(ifs[i], &hostname);
  139. break;
  140. }
  141. if (!hostname) {
  142. ESP_LOGI(TAG, "No hostname/IP found, can't start CSpot (will retry)");
  143. return false;
  144. }
  145. cmd_handler_chain = cmd_cb;
  146. cspot = cspot_create(hostname, cmd_handler, data_cb);
  147. return true;
  148. }
  149. /****************************************************************************************
  150. * CSpot sink timer handler
  151. */
  152. static void cspot_start_handler( TimerHandle_t xTimer ) {
  153. if (cspot_sink_start(cspot_cbs.cmd, cspot_cbs.data)) {
  154. xTimerDelete(xTimer, portMAX_DELAY);
  155. }
  156. }
  157. /****************************************************************************************
  158. * CSpot sink initialization
  159. */
  160. void cspot_sink_init(cspot_cmd_vcb_t cmd_cb, cspot_data_cb_t data_cb) {
  161. if (!cspot_sink_start(cmd_cb, data_cb)) {
  162. cspot_cbs.cmd = cmd_cb;
  163. cspot_cbs.data = data_cb;
  164. TimerHandle_t timer = xTimerCreate("cspotStart", 5000 / portTICK_RATE_MS, pdTRUE, NULL, cspot_start_handler);
  165. xTimerStart(timer, portMAX_DELAY);
  166. ESP_LOGI(TAG, "Delaying CSPOT start");
  167. }
  168. }
  169. /****************************************************************************************
  170. * CSpot forced disconnection
  171. */
  172. void cspot_disconnect(void) {
  173. ESP_LOGI(TAG, "forced disconnection");
  174. displayer_control(DISPLAYER_SHUTDOWN);
  175. cspot_cmd(cspot, CSPOT_FLUSH, NULL);
  176. actrls_unset();
  177. }