KORG microX utility.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

246 lines
7.5 KiB

  1. /*
  2. * asysex - A SysEx Utility
  3. * Copyright (C) 2012 Damien Goutte-Gattat
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /** @file sysex.c
  19. * System Exclusive support module.
  20. *
  21. * This module provides helper functions to handle System Exclusive
  22. * (SysEx) MIDI messages.
  23. */
  24. #ifdef HAVE_CONFIG_H
  25. #include <config.h>
  26. #endif
  27. #include <sysex.h>
  28. /**
  29. * Read a System Exclusive message.
  30. * This function reads a single System Exclusive message.
  31. * Realtime messages possibly interspersed inside the System
  32. * Exclusive message are discarded. Incomplete SysEx messages,
  33. * interrupted from the sending side before the terminating
  34. * byte is received, are also discarded.
  35. *
  36. * @param[in] midi The MIDI port to read from.
  37. * @param[out] data The buffer where to store the message.
  38. * @param[in] len The size of the @a data buffer.
  39. *
  40. * @return
  41. * - The number of bytes read and stored in the @a data buffer.
  42. *
  43. * If this value equals the size of the buffer, the caller should
  44. * check whether the last byte in the buffer is a SysEx terminating
  45. * byte (0xF7); if it is not, the buffer was not large enough to
  46. * hold the entire message, and the caller may call the function
  47. * again to get the rest of the message.
  48. * - 0 if there was no SysEx message, or if it was abruptely
  49. * interrupted.
  50. * - -1 if an I/O error occured.
  51. */
  52. ssize_t
  53. sysex_read(midi_io_t *midi, unsigned char *data, size_t len)
  54. {
  55. int byte, loop, n, p;
  56. loop = 1;
  57. n = p = 0;
  58. while ( n < len && loop ) {
  59. if ( (byte = midi_next(midi)) == -1 )
  60. return byte;
  61. /*
  62. * We're not interested in System Realtime messages and should
  63. * ignore them. However, when using a MIDI device which
  64. * regularly emits such messages, this function can end up
  65. * blocked until the next SysEx message. The workaround here is
  66. * to count System Realtime messages: if we read more than 10
  67. * of them in a row (arbitrary value), it's likely that there
  68. * is no SysEx message in queue, and we can tell the caller
  69. * that we read nothing.
  70. */
  71. if ( byte >= 0xF8 ) {
  72. if ( ++p >= 10 )
  73. return 0;
  74. continue;
  75. }
  76. else
  77. p = 0;
  78. if ( midi_status(midi) == 0xF0 ) {
  79. if ( byte == 0xF0 && n > 0 ) {
  80. /* Discard previous, incomplete SysEx message. */
  81. n = 0;
  82. }
  83. data[n++] = byte;
  84. }
  85. else if ( n > 0 ) {
  86. /* We were reading a SysEx message, what happened? */
  87. if ( byte == 0xF7 ) {
  88. /* Normal termination of SysEx message,
  89. * stop reading and return data to caller. */
  90. data[n++] = byte;
  91. loop = 0;
  92. }
  93. else if ( byte >= 0x80 && byte < 0xF7 ) {
  94. /* Abnormal termination of SysEx message,
  95. * abort reading and report failure to caller. */
  96. n = 0;
  97. loop = 0;
  98. }
  99. }
  100. }
  101. return n;
  102. }
  103. /**
  104. * Send a MIDI message and get a SysEx reply.
  105. * This function writes the provided data to the MIDI port and
  106. * read a single SysEx message in reply if any.
  107. *
  108. * @param[in] midi The MIDI port.
  109. * @param[in] query The data to write.
  110. * @param[in] query_len The number of bytes from the @a query
  111. * buffer to write.
  112. * @param[out] reply The buffer where to store the reply.
  113. * @param[in] reply_len The size of the @a reply buffer.
  114. *
  115. * @return
  116. * - The number of bytes read and stored in the @a reply buffer;
  117. * - 0 if the reply message was abruptly terminated;
  118. * - -1 if an I/O error occured.
  119. */
  120. ssize_t
  121. sysex_query(midi_io_t *midi,
  122. unsigned char *query,
  123. size_t query_len,
  124. unsigned char *reply,
  125. size_t reply_len)
  126. {
  127. ssize_t n;
  128. if ( (n = midi_write(midi, query, query_len)) != -1 )
  129. n = sysex_read(midi, reply, reply_len);
  130. return n;
  131. }
  132. /**
  133. * Identify a connected MIDI device.
  134. * This function sends an universal Device Inquiry System
  135. * Exclusive message to the MIDI port and read the reply,
  136. * which is expected to contain informations about the
  137. * connected MIDI device.
  138. *
  139. * @param[in] midi The MIDI port.
  140. * @param[out] devid A midi_device_id structure which is
  141. * to be filled in with the received
  142. * informations.
  143. *
  144. * @return
  145. * - 1 if the identify attempt was successful;
  146. * - 0 if the reply was not a valid Device Inquiry Reply;
  147. * - -1 if an I/O error occured.
  148. */
  149. int
  150. sysex_identify(midi_io_t *midi, midi_device_id_t *devid)
  151. {
  152. ssize_t n;
  153. unsigned char reply[64], *p;
  154. unsigned char query[] = { 0xF0, /* SysEx message begin */
  155. 0x7E, /* Non-realtime message */
  156. 0x7F, /* SysEx channel (Any) */
  157. 0x06, /* General information */
  158. 0x01, /* Identity request */
  159. 0xF7 /* SysEx message end */ };
  160. if ( (n = sysex_query(midi, query, sizeof(query),
  161. reply, sizeof(reply))) == -1 )
  162. return n;
  163. if ( n != 15 && n != 17 ) /* invalid reply length */
  164. return 0;
  165. if ( reply[1] != 0x7E || reply[3] != 0x06 || reply[4] != 0x02 )
  166. /* invalid reply */
  167. return 0;
  168. p = &reply[5];
  169. devid->manufacturer = *p++;
  170. if ( devid->manufacturer == 0x00 ) { /* extended manufacturer ID */
  171. devid->manufacturer = 1 << 16;
  172. devid->manufacturer += *p++ << 8;
  173. devid->manufacturer += *p++;
  174. }
  175. devid->family = *p++;
  176. devid->family += *p++ << 8;
  177. devid->model = *p++;
  178. devid->model += *p++ << 8;
  179. devid->version = *p++;
  180. devid->version += *p++ << 8;
  181. devid->version += *p++ << 16;
  182. devid->version += *p++ << 24;
  183. return 1;
  184. }
  185. struct manufacturer
  186. {
  187. unsigned int id;
  188. const char *name;
  189. };
  190. /**
  191. * Get the name of the MIDI manufacturer with the specified ID.
  192. * This function lookups a internal list of MIDI manufacturers
  193. * and return the name of the manufacturer with the specified
  194. * identifier.
  195. *
  196. * @param[in] id A positive integer identifying a MIDI
  197. * manufacturer.
  198. *
  199. * @return
  200. * A static string containing the name of the MIDI manufacturer
  201. * with the specified @a id.
  202. */
  203. const char *
  204. sysex_get_manufacturer(unsigned int id)
  205. {
  206. struct manufacturer *cursor;
  207. static struct manufacturer manufacturers[] = {
  208. #define MANUFACTURER(id, name) { id, name },
  209. #define MANUFACTURER_EXTENDED(high_id, low_id, name) \
  210. { (1 << 16) + (high_id << 8) + low_id, name },
  211. #include <manufacturers.h>
  212. #undef MANUFACTURER
  213. #undef MANUFACTURER_EXTENDED
  214. { 0x0, NULL }
  215. };
  216. cursor = manufacturers;
  217. while ( cursor->name ) {
  218. if ( cursor->id == id )
  219. return cursor->name;
  220. cursor += 1;
  221. }
  222. return "Unknown manufacturer";
  223. }