controls.c 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. /*
  2. * (c) Philippe G. 2019, philippe_44@outlook.com
  3. *
  4. * This software is released under the MIT License.
  5. * https://opensource.org/licenses/MIT
  6. *
  7. */
  8. #include "squeezelite.h"
  9. // #include "Config.h"
  10. #pragma message("fixme: look for TODO below")
  11. #include "audio_controls.h"
  12. static log_level loglevel = lINFO;
  13. #define DOWN_OFS 0x10000
  14. #define UP_OFS 0x20000
  15. // numbers are simply 0..9 but are not used
  16. // arrow_right.down = 0001000e seems to be missing ...
  17. enum { BUTN_POWER_FRONT = 0X0A, BUTN_ARROW_UP, BUTN_ARROW_DOWN, BUTN_ARROW_LEFT, BUTN_KNOB_PUSH, BUTN_SEARCH,
  18. BUTN_REW, BUTN_FWD, BUTN_PLAY, BUTN_ADD, BUTN_BRIGHTNESS, BUTN_NOW_PLAYING,
  19. BUTN_PAUSE = 0X17, BUTN_BROWSE, BUTN_VOLUP_FRONT, BUTN_VOLDOWN_FRONT, BUTN_SIZE, BUTN_VISUAL, BUTN_VOLUMEMODE,
  20. BUTN_PRESET_0 = 0x22, BUTN_PRESET_1, BUTN_PRESET_2, BUTN_PRESET_3, BUTN_PRESET_4, BUTN_PRESET_5, BUTN_PRESET_6, BUTN_PRESET_7, BUTN_PRESET_8, BUTN_PRESET_9,
  21. BUTN_SNOOZE,
  22. BUTN_KNOB_LEFT = 0X5A, BUTN_KNOB_RIGHT };
  23. #define BUTN_ARROW_RIGHT BUTN_KNOB_PUSH
  24. #pragma pack(push, 1)
  25. struct BUTN_header {
  26. char opcode[4];
  27. u32_t length;
  28. u32_t jiffies;
  29. u32_t button;
  30. };
  31. struct IR_header {
  32. char opcode[4];
  33. u32_t length;
  34. u32_t jiffies;
  35. u8_t format; // unused
  36. u8_t bits; // unused
  37. u32_t code;
  38. };
  39. #pragma pack(pop)
  40. static in_addr_t server_ip;
  41. static u16_t server_hport;
  42. static u16_t server_cport;
  43. static int cli_sock = -1;
  44. static u8_t mac[6];
  45. static void (*chained_notify)(in_addr_t, u16_t, u16_t);
  46. static bool raw_mode=false;
  47. static void cli_send_cmd(char *cmd);
  48. /****************************************************************************************
  49. * Send BUTN
  50. */
  51. static void sendBUTN(int code, bool pressed) {
  52. struct BUTN_header pkt_header;
  53. memset(&pkt_header, 0, sizeof(pkt_header));
  54. memcpy(&pkt_header.opcode, "BUTN", 4);
  55. pkt_header.length = htonl(sizeof(pkt_header) - 8);
  56. pkt_header.jiffies = htonl(gettime_ms());
  57. pkt_header.button = htonl(code + (pressed ? DOWN_OFS : UP_OFS));
  58. LOG_DEBUG("sending BUTN code %04x %s", code, pressed ? "down" : "up");
  59. LOCK_P;
  60. send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
  61. UNLOCK_P;
  62. }
  63. /****************************************************************************************
  64. * Send IR
  65. */
  66. static void sendIR(u16_t addr, u16_t cmd) {
  67. struct IR_header pkt_header;
  68. memset(&pkt_header, 0, sizeof(pkt_header));
  69. memcpy(&pkt_header.opcode, "IR ", 4);
  70. pkt_header.length = htonl(sizeof(pkt_header) - 8);
  71. pkt_header.jiffies = htonl(gettime_ms());
  72. pkt_header.format = pkt_header.bits = 0;
  73. pkt_header.code = htonl((addr << 16) | cmd);
  74. LOG_INFO("sending IR code %04x", (addr << 16) | cmd);
  75. LOCK_P;
  76. send_packet((uint8_t *) &pkt_header, sizeof(pkt_header));
  77. UNLOCK_P;
  78. }
  79. static void lms_toggle(bool pressed) {
  80. if (raw_mode) {
  81. sendBUTN(BUTN_PAUSE, pressed);
  82. } else {
  83. cli_send_cmd("pause");
  84. }
  85. }
  86. static void lms_pause(bool pressed) {
  87. if (raw_mode) {
  88. sendBUTN(BUTN_PAUSE, pressed);
  89. } else {
  90. cli_send_cmd("pause 1");
  91. }
  92. }
  93. static void lms_stop(bool pressed) {
  94. cli_send_cmd("button stop");
  95. }
  96. #define LMS_CALLBACK(N,B,E) \
  97. static void lms_##N (bool pressed) { \
  98. if (raw_mode) { \
  99. sendBUTN( BUTN_##B , pressed ); \
  100. } else { \
  101. cli_send_cmd("button" " " #E); \
  102. } \
  103. }
  104. LMS_CALLBACK(power, POWER_FRONT, power)
  105. LMS_CALLBACK(play, PLAY, play.single)
  106. LMS_CALLBACK(volup, VOLUP_FRONT, volup)
  107. LMS_CALLBACK(voldown, VOLDOWN_FRONT, voldown)
  108. LMS_CALLBACK(rew, REW, rew.repeat)
  109. LMS_CALLBACK(fwd, FWD, fwd.repeat)
  110. LMS_CALLBACK(prev, REW, rew)
  111. LMS_CALLBACK(next, FWD, fwd)
  112. LMS_CALLBACK(up, ARROW_UP, arrow_up)
  113. LMS_CALLBACK(down, ARROW_DOWN, arrow_down)
  114. LMS_CALLBACK(left, ARROW_LEFT, arrow_left)
  115. LMS_CALLBACK(right, ARROW_RIGHT, arrow_right)
  116. LMS_CALLBACK(pre0, PRESET_0, preset_0.single)
  117. LMS_CALLBACK(pre1, PRESET_1, preset_1.single)
  118. LMS_CALLBACK(pre2, PRESET_2, preset_2.single)
  119. LMS_CALLBACK(pre3, PRESET_3, preset_3.single)
  120. LMS_CALLBACK(pre4, PRESET_4, preset_4.single)
  121. LMS_CALLBACK(pre5, PRESET_5, preset_5.single)
  122. LMS_CALLBACK(pre6, PRESET_6, preset_6.single)
  123. LMS_CALLBACK(pre7, PRESET_7, preset_7.single)
  124. LMS_CALLBACK(pre8, PRESET_8, preset_8.single)
  125. LMS_CALLBACK(pre9, PRESET_9, preset_9.single)
  126. LMS_CALLBACK(knob_left, KNOB_LEFT, knob_left)
  127. LMS_CALLBACK(knob_right, KNOB_RIGHT, knob_right)
  128. LMS_CALLBACK(knob_push, KNOB_PUSH, knob_push)
  129. const actrls_t LMS_controls = {
  130. lms_power,
  131. lms_volup, lms_voldown, // volume up, volume down
  132. lms_toggle, lms_play, // toggle, play
  133. lms_pause, lms_stop, // pause, stop
  134. lms_rew, lms_fwd, // rew, fwd
  135. lms_prev, lms_next, // prev, next
  136. lms_up, lms_down,
  137. lms_left, lms_right,
  138. lms_pre0, lms_pre1, lms_pre2, lms_pre3, lms_pre4, lms_pre5, lms_pre6, lms_pre7, lms_pre8, lms_pre9,
  139. lms_knob_left, lms_knob_right, lms_knob_push,
  140. };
  141. /****************************************************************************************
  142. *
  143. */
  144. static void connect_cli_socket(void) {
  145. struct sockaddr_in addr = {
  146. .sin_family = AF_INET,
  147. .sin_addr.s_addr = server_ip,
  148. .sin_port = htons(server_cport),
  149. };
  150. socklen_t addrlen = sizeof(addr);
  151. cli_sock = socket(AF_INET, SOCK_STREAM, 0);
  152. if (connect(cli_sock, (struct sockaddr *) &addr, addrlen) < 0) {
  153. LOG_ERROR("unable to connect to server %s:%hu with cli", inet_ntoa(server_ip), server_cport);
  154. closesocket(cli_sock);
  155. cli_sock = -1;
  156. }
  157. }
  158. /****************************************************************************************
  159. *
  160. */
  161. static void cli_send_cmd(char *cmd) {
  162. char packet[96];
  163. int len;
  164. len = sprintf(packet, "%02x:%02x:%02x:%02x:%02x:%02x %s\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], cmd);
  165. LOG_DEBUG("sending command %s at %s:%hu", packet, inet_ntoa(server_ip), server_cport);
  166. if (cli_sock < 0) connect_cli_socket();
  167. if (send(cli_sock, packet, len, MSG_DONTWAIT) < 0) {
  168. closesocket(cli_sock);
  169. cli_sock = -1;
  170. LOG_WARN("cannot send CLI %s", packet);
  171. }
  172. // need to empty the RX buffer otherwise we'll lock the TCP/IP stack
  173. len = recv(cli_sock, packet, 96, MSG_DONTWAIT);
  174. }
  175. /****************************************************************************************
  176. * Notification when server changes
  177. */
  178. static void notify(in_addr_t ip, u16_t hport, u16_t cport) {
  179. server_ip = ip;
  180. server_hport = hport;
  181. server_cport = cport;
  182. // close existing CLI connection and open new one
  183. if (cli_sock >= 0) closesocket(cli_sock);
  184. connect_cli_socket();
  185. LOG_INFO("notified server %s hport %hu cport %hu", inet_ntoa(ip), hport, cport);
  186. if (chained_notify) (*chained_notify)(ip, hport, cport);
  187. }
  188. /****************************************************************************************
  189. * IR handler
  190. */
  191. static bool ir_handler(u16_t addr, u16_t cmd) {
  192. sendIR(addr, cmd);
  193. return true;
  194. }
  195. /****************************************************************************************
  196. * Initialize controls - shall be called once from output_init_embedded
  197. */
  198. void sb_controls_init(void) {
  199. // TODO: Add support for the commented code
  200. #pragma message("sb_controls_init needs to be implemented")
  201. // char *p = config_alloc_get_default(NVS_TYPE_STR, "lms_ctrls_raw", "n", 0);
  202. // raw_mode = p && (*p == '1' || *p == 'Y' || *p == 'y');
  203. // free(p);
  204. LOG_INFO("initializing audio (buttons/rotary/ir) controls (raw:%u)", raw_mode);
  205. get_mac(mac);
  206. actrls_set_default(LMS_controls, raw_mode, NULL, ir_handler);
  207. chained_notify = server_notify;
  208. server_notify = notify;
  209. }