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.

847 lines
27 KiB

  1. /*
  2. * kmxtool - KORG microX utility
  3. * Copyright (C) 2012,2013,2016 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 microx.c
  19. * MicroX-specific functions.
  20. *
  21. * This module provides helper functions to deal with a Korg microX
  22. * synthesizer.
  23. */
  24. #ifdef HAVE_CONFIG_H
  25. #include <config.h>
  26. #endif
  27. #include <stdio.h>
  28. #include <microx.h>
  29. #include <sysex.h>
  30. /**
  31. * Identify a connected microX device.
  32. * This function check whether the device connected to the MIDI
  33. * port is a Korg microX synthesizer.
  34. *
  35. * @param[in] midi The MIDI port.
  36. * @param[out] version A pointer to a kmx_microx_version structure
  37. * to be filled in with informations from the
  38. * device; may be @c NULL.
  39. * @param[out] global A pointer to an integer to receive the
  40. * global MIDI channel used by the microX;
  41. * may be @c NULL.
  42. *
  43. * @return
  44. * - @e KMX_OK if the port is connected to a device which
  45. * successfully identified itself as a microX device;
  46. * - @e KMX_IO_ERROR if an I/O error occured;
  47. * - @e KMX_INVALID_REPLY if the device replied incorrectly:
  48. * - @e KMX_NOT_KORG is the device did not identify itself as
  49. * a Korg device;
  50. * - @e KMX_NOT_MICROX is the device did not identify itself
  51. * as a microX device.
  52. */
  53. int
  54. kmx_microx_identify(midi_io_t *midi,
  55. struct kmx_microx_version *version,
  56. unsigned int *global)
  57. {
  58. int n;
  59. midi_device_id_t devid;
  60. if ( (n = sysex_identify(midi, &devid)) == -1 )
  61. return KMX_IO_ERROR;
  62. else if ( n == 0 )
  63. return KMX_INVALID_REPLY;
  64. if ( devid.manufacturer != KORG_ID )
  65. return KMX_NOT_KORG;
  66. if ( devid.family != MICROX_ID || devid.model != MICROX_SUB_ID )
  67. return KMX_NOT_MICROX;
  68. if ( version ) {
  69. version->major = devid.version & 0xFF;
  70. version->minor = (devid.version >> 8) & 0xFF;
  71. version->release = (devid.version >> 16) & 0xFF;
  72. version->build = (devid.version >> 24) & 0xFF;
  73. }
  74. if ( global )
  75. *global = devid.global_channel;
  76. return KMX_OK;
  77. }
  78. /**
  79. * Get the microX current status.
  80. * This function gets the current status of the connected microX
  81. * synthesizer.
  82. *
  83. * @param[in] midi The MIDI port.
  84. * @param[int] chan The global MIDI channel.
  85. * @param[out] status A pointer to a kmx_microx_status structure
  86. * to be filled in with the status data
  87. * received from the microX; may be @c NULL.
  88. *
  89. * @return
  90. * - @e KMX_OK if the query was successful;
  91. * - @e KMX_IO_ERROR if an I/O error occured;
  92. * - @e KMX_NOT_KORG if the reply did not come from a Korg device;
  93. * - @e KMX_INVALID_REPLY if the device replied incorrectly.
  94. */
  95. int
  96. kmx_microx_query_status(midi_io_t *midi,
  97. unsigned int chan,
  98. struct kmx_microx_status *s)
  99. {
  100. ssize_t n;
  101. unsigned char reply[64];
  102. unsigned char query[] = { 0xF0, /* SysEx message begin */
  103. 0x42, /* KORG manufacturer ID */
  104. 0x30, /* SysEx channel (1) */
  105. 0x7A, /* End of SysEx header */
  106. 0x12, /* Mode and state request */
  107. 0xF7 /* SysEx message end */ };
  108. fprintf(stderr, "querying status on channel %d\n", chan);
  109. query[2] |= chan & 0xF;
  110. if ( (n = sysex_query(midi, query, sizeof(query),
  111. reply, sizeof(reply))) == -1 )
  112. return KMX_IO_ERROR;
  113. /*
  114. * Expected Mode and state Reply (27 bytes):
  115. * Byte # Value Meaning
  116. * 0 0xF0 SysEx message begin
  117. * 1 0x42 KORG manufacturer ID
  118. * 2 0x3n Global channel (n = 0x0 ~ 0xF)
  119. * 3 0x7A End of SysEx header
  120. * 4 0x42 Mode and state reply
  121. * 5 0xnn Current mode (one of kmx_microx_mode)
  122. * 6 0x06 Status of Global mode
  123. * 7 0xnn Global mode setting data 1
  124. * bit 0: Local Control Off/On
  125. * bits 1,2: MIDI Clock (one of kmx_microx_clock)
  126. * bit 3: (Reserved)
  127. * bit 4: Bank MAP KORG/GM
  128. * bit 5: Power On Mode Reset/Memorize
  129. * bit 6: Receive Ext.Realtime Command Off/On
  130. * 8 0xnn Global mode setting data 2
  131. * bit 0: Enable Program Change Off/On
  132. * bit 1: Enable Bank Change Off/On
  133. * bit 2: Enable Combination Change Off/On
  134. * bit 3: Enable Aftertouch Off/On
  135. * bit 4: Enable Control Change Off/On
  136. * bits 5,6: Note Filter All/Even/Odd
  137. * 9 0xnn Global mode setting data 3 (Memory Protection)
  138. * bit 0: Program
  139. * bit 1: Combination
  140. * bit 2: Multi
  141. * bit 3: Drums
  142. * bit 4: ArpeggioPattern
  143. * bit 5: Ext.Control
  144. * 10 0x02 Status of Program mode
  145. * 11 0x0n Current Bank (n = 0x0 ~ 0x4, bank A ~ E)
  146. * 12 0xnn Current Program (n = 0x0 ~ 0x7F)
  147. * 13 0x00 (Reserved)
  148. * 14 0x00 Status of Combination mode
  149. * 15 0x0n Current Bank (n = 0x0 ~ 0x2, bank A ~ C)
  150. * 16 0xnn Current Combination (n = 0x0 ~ 0x7F)
  151. * 17 0x00 (Reserved)
  152. * 18 0x04 Status of Multi mode
  153. * 19 0x00 (Reserved)
  154. * 20 0xnn Current Multi (n = 0x0 ~ 0x7F)
  155. * 21 0x0n Control Track (n = 0x0 ~ 0xF, track 1 ~ 16)
  156. * 22 0x21 Status of Ext.Controller
  157. * 23 0x0n Ext.Controller Off/On
  158. * 24 0xnn Current Controller (n = 0x0 ~ 0x3F)
  159. * 25 0x00 (Reserved)
  160. * 26 0xF7 SysEx message end
  161. */
  162. if ( n != 27 || reply[4] != 0x42 )
  163. return KMX_INVALID_REPLY;
  164. if ( reply[1] != KORG_ID )
  165. return KMX_NOT_KORG;
  166. if ( s ) {
  167. s->global_channel = reply[2] & 0xF;
  168. s->active_mode = reply[5];
  169. s->global.local_control = reply[7] & 0x1;
  170. s->global.clock_source = reply[7] >> 1 & 0x3;
  171. s->global.bank_map = reply[7] >> 4 & 0x1;
  172. s->global.memorize_mode = reply[7] >> 5 & 0x1;
  173. s->global.rec_ext_rt_cmd = reply[7] >> 6 & 0x1;
  174. s->global.prgm_change = reply[8] & 0x1;
  175. s->global.bank_change = reply[8] >> 1 & 0x1;
  176. s->global.comb_change = reply[8] >> 2 & 0x1;
  177. s->global.aftertouch = reply[8] >> 3 & 0x1;
  178. s->global.ctrl_change = reply[8] >> 4 & 0x1;
  179. s->global.note_filter = reply[8] >> 5 & 0x3;
  180. s->global.protected_memory.program = reply[9] & 0x1;
  181. s->global.protected_memory.bank = reply[9] >> 1 & 0x1;
  182. s->global.protected_memory.multi = reply[9] >> 2 & 0x1;
  183. s->global.protected_memory.drum = reply[9] >> 3 & 0x1;
  184. s->global.protected_memory.arpeggio = reply[9] >> 4 & 0x1;
  185. s->global.protected_memory.ext_ctrl = reply[9] >> 5 & 0x1;
  186. s->program.current_bank = reply[11];
  187. s->program.current_program = reply[12];
  188. s->combination.current_bank = reply[15];
  189. s->combination.current_combination = reply[16];
  190. s->multi.current_multi = reply[20];
  191. s->multi.control_track = reply[21];
  192. s->ext_ctrl.enabled = reply[23];
  193. s->ext_ctrl.current = reply[24];
  194. }
  195. return KMX_OK;
  196. }
  197. /**
  198. * Get an error message.
  199. * This function translates an error code returned by other
  200. * functions of this module to an error message.
  201. *
  202. * If the error happens to be a MIDI I/O error, this function
  203. * calls the midi_error() function to get a meaningful
  204. * message from the underlying MIDI API.
  205. *
  206. * @param[in] midi The MIDI port used to talk to the microX.
  207. * @param[in] e The error code to lookup.
  208. *
  209. * @return
  210. * A static string containing the user-readable error message.
  211. */
  212. const char *
  213. kmx_microx_error(midi_io_t *midi, int e)
  214. {
  215. const char *msg;
  216. switch ( e ) {
  217. case KMX_IO_ERROR:
  218. msg = midi_error(midi);
  219. break;
  220. case KMX_INVALID_REPLY:
  221. msg = "invalid reply";
  222. break;
  223. case KMX_NOT_KORG:
  224. msg = "not a KORG device";
  225. break;
  226. case KMX_NOT_MICROX:
  227. msg = "not a microX device";
  228. break;
  229. case KMX_INVALID_QUERY:
  230. msg = "invalid query";
  231. break;
  232. case KMX_BUFFER_TOO_SMALL:
  233. msg = "output buffer too small";
  234. break;
  235. case KMX_DEST_MEM_PROTECTED:
  236. msg = "destination memory is write-protected";
  237. break;
  238. case KMX_DEST_PRG_MISSING:
  239. msg = "destination bank/program/parameter does not exist";
  240. break;
  241. case KMX_WRONG_MODE:
  242. msg = "the mode is wrong";
  243. break;
  244. case KMX_MEM_OVERFLOW:
  245. msg = "memory overflow";
  246. break;
  247. case KMX_OTHER_ERROR:
  248. msg = "another error type";
  249. break;
  250. default: /* Should not happen. */
  251. msg = "unknown error";
  252. break;
  253. }
  254. return msg;
  255. }
  256. static struct {
  257. unsigned char query;
  258. unsigned char reply;
  259. } dump_codes[] = {
  260. { 0x1C, 0x4C },
  261. { 0x1D, 0x4D },
  262. { 0x18, 0x48 },
  263. { 0x0D, 0x52 },
  264. { 0x34, 0x69 },
  265. { 0x1E, 0x6E }
  266. };
  267. /**
  268. * Parse a string as a positive integer.
  269. * This function parses the given string as a positive integer
  270. * value.
  271. *
  272. * @param[in] s The string to parse.
  273. *
  274. * @return
  275. * - The positive integer value contained in @a s;
  276. * - -1 if @a s could not be parsed as a positive integer value.
  277. */
  278. static int
  279. parse_int(const char *s)
  280. {
  281. int val;
  282. val = 0;
  283. while ( *s && val >= 0 ) {
  284. if ( *s >= '0' && *s <= '9' )
  285. val = val * 10 + *s - '0';
  286. else
  287. val = -1;
  288. s += 1;
  289. }
  290. return val;
  291. }
  292. /**
  293. * Parse a string as a dump/load request to the microX.
  294. * This function translates a string to a formal representation
  295. * of a dump or load parameter request to a microX device.
  296. *
  297. * If the request string is valid and successfully parsed, the
  298. * function fills in a kmx_microx_dump structure which may then
  299. * be used in a call to kmx_microx_dump() or kmx_microx_load().
  300. *
  301. * The request string should start with a single letter
  302. * indicating the type of memory to dump or load: 'P' for programs,
  303. * 'C' for combinations, 'M' for multis, 'D' for drumkits,
  304. * 'A' for arpeggio patterns, or 'E' for external controllers sets.
  305. * It must be followed by a single uppercase letter indicating the
  306. * bank (only for programs and combinations), and up to three
  307. * decimal digits indicating the slot number (counting from one).
  308. *
  309. * An asterisk may be used instead of the program number, in that
  310. * case the microX will be instructed to dump all slots in the
  311. * bank. For programs and combinations, an asterisk may also be
  312. * used instead of the bank letter @e and the program number, in
  313. * that case the microX will be instructed to dump all slots in
  314. * all program or combination banks.
  315. *
  316. * For example:
  317. *
  318. * - @c "PA123" means "dump program #123 of bank A";
  319. * - @c "M17" means "dump multi #17";
  320. * - @c "CB*" means "dump all combinations in bank B";
  321. * - @c "C*" means "dump all combinations in all banks";
  322. * - @c "D*" means "dump all drumkits".
  323. *
  324. * @param[in] what The request string to parse.
  325. * @param[out] The kmx_microx_dump structure to fill in.
  326. *
  327. * @return
  328. * - @e KMX_OK if the dump request was successfully parsed.
  329. * - @e KMX_INVALID_QUERY if the dump request is invalid.
  330. */
  331. int
  332. kmx_microx_parse_dump_request(const char *what, struct kmx_microx_dump *dump)
  333. {
  334. int val, ret;
  335. char bank;
  336. ret = KMX_OK;
  337. switch ( *what++ ) {
  338. case 'P':
  339. dump->type = PROGRAM_DATA;
  340. bank = *what++;
  341. if ( bank == '*' )
  342. dump->bank = KMX_MICROX_DUMP_ALL;
  343. else if ( bank >= 'A' && bank <= 'A' + MICROX_N_PROG_BANK ) {
  344. dump->bank = bank - 'A';
  345. if ( *what == '*' )
  346. dump->program = KMX_MICROX_DUMP_ALL;
  347. else if ( (val = parse_int(what)) >= 1 && val <= MICROX_BANK_SIZE )
  348. dump->program = val - 1;
  349. else
  350. ret = KMX_INVALID_QUERY;
  351. }
  352. else
  353. ret = KMX_INVALID_QUERY;
  354. break;
  355. case 'C':
  356. dump->type = COMBINATION_DATA;
  357. bank = *what++;
  358. if ( bank == '*' )
  359. dump->bank = KMX_MICROX_DUMP_ALL;
  360. else if ( bank >= 'A' && bank <= 'A' + MICROX_N_COMB_BANK ) {
  361. dump->bank = bank - 'A';
  362. if ( *what == '*' )
  363. dump->program = KMX_MICROX_DUMP_ALL;
  364. else if ( (val = parse_int(what)) >= 1 && val <= MICROX_BANK_SIZE )
  365. dump->program = val - 1;
  366. else
  367. ret = KMX_INVALID_QUERY;
  368. }
  369. else
  370. ret = KMX_INVALID_QUERY;
  371. break;
  372. case 'M':
  373. dump->type = MULTI_DATA;
  374. if ( *what == '*' )
  375. dump->program = KMX_MICROX_DUMP_ALL;
  376. else if ( (val = parse_int(what)) >= 1 && val <= MICROX_N_MULTI )
  377. dump->program = val - 1;
  378. else
  379. ret = KMX_INVALID_QUERY;
  380. break;
  381. case 'D':
  382. dump->type = DRUMKIT_DATA;
  383. if ( *what == '*' )
  384. dump->program = KMX_MICROX_DUMP_ALL;
  385. else if ( (val = parse_int(what)) >= 1 && val <= MICROX_N_DRUMKIT )
  386. dump->program = val - 1;
  387. else
  388. ret = KMX_INVALID_QUERY;
  389. break;
  390. case 'A':
  391. dump->type = ARPEGGIO_DATA;
  392. if ( *what == '*' )
  393. dump->program = KMX_MICROX_DUMP_ALL;
  394. else if ( (val = parse_int(what)) >= 1 && val <= MICROX_N_ARPEGGIO )
  395. dump->program = val - 1;
  396. else
  397. ret = KMX_INVALID_QUERY;
  398. break;
  399. case 'E':
  400. dump->type = EXTSET_DATA;
  401. if ( *what == '*' )
  402. dump->program = KMX_MICROX_DUMP_ALL;
  403. else if ( (val = parse_int(what)) >= 1 && val <= MICROX_N_EXTSET )
  404. dump->program = val - 1;
  405. else
  406. ret = KMX_INVALID_QUERY;
  407. break;
  408. default:
  409. ret = KMX_INVALID_QUERY;
  410. }
  411. return ret;
  412. }
  413. /**
  414. * Get the size of a microX data dump.
  415. * Given a formal dump specification as returned by the
  416. * kmx_microx_parse_dump_request() function, this function
  417. * returns the expected size of the corresponding dump.
  418. *
  419. * @param[in] dump A kmx_microx_dump structure representing
  420. * a dump request.
  421. *
  422. * @return
  423. * The expected size of the specified data dump.
  424. */
  425. size_t
  426. kmx_microx_get_dump_size(struct kmx_microx_dump *dump)
  427. {
  428. size_t s;
  429. switch ( dump->type ) {
  430. case PROGRAM_DATA:
  431. s = MICROX_PROGRAM_SIZE;
  432. if ( dump->bank == KMX_MICROX_DUMP_ALL )
  433. s *= MICROX_N_PROGRAM;
  434. else if ( dump->program == KMX_MICROX_DUMP_ALL )
  435. s *= MICROX_BANK_SIZE;
  436. break;
  437. case COMBINATION_DATA:
  438. s = MICROX_COMBINATION_SIZE;
  439. if ( dump->bank == KMX_MICROX_DUMP_ALL )
  440. s *= MICROX_N_COMBINATION;
  441. else if ( dump->program == KMX_MICROX_DUMP_ALL )
  442. s *= MICROX_BANK_SIZE;
  443. break;
  444. case MULTI_DATA:
  445. s = MICROX_MULTI_SIZE;
  446. if ( dump->program == KMX_MICROX_DUMP_ALL )
  447. s *= MICROX_N_MULTI;
  448. break;
  449. case DRUMKIT_DATA:
  450. s = MICROX_DRUMKIT_SIZE;
  451. if ( dump->program == KMX_MICROX_DUMP_ALL )
  452. s *= MICROX_N_DRUMKIT;
  453. break;
  454. case ARPEGGIO_DATA:
  455. s = MICROX_ARPEGGIO_SIZE;
  456. if ( dump->program == KMX_MICROX_DUMP_ALL )
  457. s *= MICROX_N_ARPEGGIO;
  458. break;
  459. case EXTSET_DATA:
  460. s = MICROX_EXTSET_SIZE;
  461. if ( dump->program == KMX_MICROX_DUMP_ALL )
  462. s *= MICROX_N_EXTSET;
  463. break;
  464. default: /* Should never happen. */
  465. s = 0;
  466. break;
  467. }
  468. return s;
  469. }
  470. /**
  471. * Translate a dump request into actual MIDI bytes.
  472. * Given a formal dump specification as returned by the
  473. * kmx_microx_parse_dump_request() function, this function
  474. * translates the request into the appropriate MIDI bytes
  475. * that can be received by a microX synthesizer.
  476. *
  477. * @param[in] dump A kmx_microx_dump structure
  478. * representing a dump request.
  479. * @param[out] buffer A buffer to be filled in with the
  480. * appropriate MIDI bytes; this buffer
  481. * should be at least 4 four bytes long.
  482. *
  483. * @return
  484. * - @e KMX_OK if the operation was successful;
  485. * - @e KMX_INVALID_QUERY if an error occured.
  486. *
  487. * The current implementation is always successful.
  488. */
  489. static int
  490. get_dump_parameters(struct kmx_microx_dump *dump,
  491. unsigned char *buffer)
  492. {
  493. buffer[0] = dump->load ? dump_codes[dump->type].reply
  494. : dump_codes[dump->type].query;
  495. switch ( dump->type ) {
  496. case PROGRAM_DATA:
  497. if ( dump->bank != KMX_MICROX_DUMP_ALL ) {
  498. if ( dump->program == KMX_MICROX_DUMP_ALL )
  499. buffer[1] = 0x10 + dump->bank;
  500. else {
  501. if ( dump->load ) {
  502. buffer[1] = 1;
  503. buffer[2] = 0x20 + dump->bank;
  504. buffer[3] = dump->program;
  505. }
  506. else {
  507. buffer[1] = 0x20 + dump->bank;
  508. buffer[2] = dump->program;
  509. }
  510. }
  511. }
  512. break;
  513. case COMBINATION_DATA:
  514. if ( dump->bank != KMX_MICROX_DUMP_ALL ) {
  515. if ( dump->program == KMX_MICROX_DUMP_ALL )
  516. buffer[1] = 0x10 + dump->bank;
  517. else {
  518. if ( dump->load ) {
  519. buffer[2] = 0x20 + dump->bank;
  520. buffer[3] = dump->program;
  521. }
  522. else {
  523. buffer[1] = 0x20 + dump->bank;
  524. buffer[2] = dump->program;
  525. }
  526. }
  527. }
  528. break;
  529. case MULTI_DATA:
  530. if ( dump->program != KMX_MICROX_DUMP_ALL ) {
  531. buffer[1] = 1;
  532. buffer[2] = dump->program;
  533. }
  534. break;
  535. case DRUMKIT_DATA:
  536. if ( dump->program != KMX_MICROX_DUMP_ALL ) {
  537. buffer[1] = 1;
  538. buffer[2] = dump->program;
  539. }
  540. break;
  541. case ARPEGGIO_DATA:
  542. if ( dump->program != KMX_MICROX_DUMP_ALL ) {
  543. buffer[1] = 0x40;
  544. buffer[2] = dump->program >> 7;
  545. buffer[3] = dump->program & 0x7F;
  546. }
  547. break;
  548. case EXTSET_DATA:
  549. if ( dump->program != KMX_MICROX_DUMP_ALL ) {
  550. buffer[1] = 1;
  551. buffer[2] = dump->program;
  552. }
  553. break;
  554. }
  555. return KMX_OK;
  556. }
  557. /**
  558. * Read a microX data dump.
  559. * This function reads data from a MIDI port and, if it corresponds
  560. * to a SysEx message announcing a Korg microX data dump, translates
  561. * it from the MIDI encoding used to transmit it.
  562. *
  563. * @param[in] midi The MIDI port to read from.
  564. * @param[in] reply_code The expected function code of the SysEx
  565. * message to read.
  566. * @param[out] data The buffer where to store the dump.
  567. * @param[in] len The size of the @a data buffer.
  568. *
  569. * @return
  570. * - The count of data bytes read and stored in the @a data buffer;
  571. * - @e KMX_IO_ERROR if an I/O error occured;
  572. * - @e KMX_NOT_KORG if the reply did not come from a Korg device;
  573. * - @e KMX_NOT_MICROX if the reply did not come from a microX device;
  574. * - @e KMX_INVALID_REPLY if the device replied incorrectly.
  575. */
  576. int
  577. kmx_microx_read_dump(midi_io_t *midi,
  578. unsigned char reply_code,
  579. unsigned char *data,
  580. size_t len)
  581. {
  582. ssize_t n;
  583. unsigned i, j, k, m;
  584. unsigned char reply[1024];
  585. j = k = m = 0;
  586. do {
  587. if ( (n = sysex_read(midi, reply, sizeof(reply))) == -1 )
  588. return KMX_IO_ERROR;
  589. i = 0;
  590. if ( j == 0 ) {
  591. /* Check reply header. */
  592. if ( reply[0] != 0xF0 || reply[4] != reply_code )
  593. return KMX_INVALID_REPLY;
  594. if ( reply[1] != KORG_ID )
  595. return KMX_NOT_KORG;
  596. if ( reply[3] != MICROX_ID )
  597. return KMX_NOT_MICROX;
  598. /* Skip reply header. */
  599. i = 8;
  600. }
  601. /*
  602. * Convert data back to the microX internal representation
  603. * and copy it to the caller's buffer. When transmitting its
  604. * data, the microX converts a group of seven 8-bits bytes
  605. * into eight MIDI bytes, where the first byte is used to
  606. * encode the value of the 8th bit of each following byte.
  607. *
  608. * For example, consider the following byte sequence in the
  609. * transmitted MIDI data (all numbers hexadecimal):
  610. *
  611. * 20 00 00 00 06 0e 16 00
  612. *
  613. * The first byte has the 6th bit set. It means that in the
  614. * microX internal representation, the sixth byte of that
  615. * group has its 8th bit set; so the real value of that byte
  616. * is not 16, as transmitted, but 96.
  617. */
  618. for ( ; i < n; i++, k++ ) {
  619. if ( k % 8 == 0 )
  620. for ( m = 0; m < 7; m++ )
  621. data[j+m] = (reply[i] >> m) << 7;
  622. else
  623. data[j++] += reply[i];
  624. }
  625. } while ( reply[n-1] != 0xF7 );
  626. return j - 1; /* Remove SysEx terminating byte. */
  627. }
  628. /**
  629. * Dump memory slot(s) from a microX device.
  630. * This function sends a parameter dump request to the
  631. * connected microX synthesizer and gets the dump data
  632. * in reply.
  633. *
  634. * @param[in] midi The MIDI port.
  635. * @param[in] chan The global MIDI channel.
  636. * @param[in] dump A kmx_microx_dump structure representing
  637. * the dump request.
  638. * @param[out] data The buffer where to store the dump.
  639. * @param[in] len The size of the @a data buffer.
  640. *
  641. * @return
  642. * - The number of bytes read and stored in the @a data buffer;
  643. * - @e KMX_IO_ERROR if an I/O error occured;
  644. * - @e KMX_INVALID_QUERY if the dump query was invalid;
  645. * - @e KMX_BUFFER_TOO_SMALL if the provided buffer was not large
  646. * enough to hold the expected data dump;
  647. * - @e KMX_NOT_KORG if the reply did not come from a Korg device;
  648. * - @e KMX_NOT_MICROX if the reply did not come from a microX device;
  649. * - @e KMX_INVALID_REPLY if the device replied incorrectly.
  650. */
  651. int
  652. kmx_microx_dump(midi_io_t *midi,
  653. unsigned int chan,
  654. struct kmx_microx_dump *dump,
  655. unsigned char *data,
  656. size_t len)
  657. {
  658. ssize_t n;
  659. unsigned char query[] = { 0xF0, /* SysEx message begin */
  660. 0x42, /* KORG manufacturer ID */
  661. 0x30, /* Global channel */
  662. 0x7A, /* End of SysEx header */
  663. 0x00, /* Dump type */
  664. 0x00, /* Dump parameter 1 */
  665. 0x00, /* Dump parameter 2 */
  666. 0x00, /* Dump parameter 3 */
  667. 0xF7 /* SysEx message end */ };
  668. query[2] |= chan & 0xF;
  669. if ( get_dump_parameters(dump, &(query[4])) == KMX_INVALID_QUERY )
  670. return KMX_INVALID_QUERY;
  671. if ( len < kmx_microx_get_dump_size(dump) )
  672. return KMX_BUFFER_TOO_SMALL;
  673. if ( (n = midi_write(midi, query, sizeof(query))) == -1 )
  674. return KMX_IO_ERROR;
  675. return kmx_microx_read_dump(midi, dump_codes[dump->type].reply, data, len);
  676. }
  677. /**
  678. * Load a memory slot to a microX device.
  679. * This function loads the provided data to a memory slot of
  680. * the microX synthesizer.
  681. *
  682. * @param[in] midi The MIDI port.
  683. * @param[in] chan The global MIDI channel.
  684. * @param[in] dump A kmx_microx_dump structure indicating the
  685. * memory slot to load the data into.
  686. * @param[in] data The data to load.
  687. * @param[in] len The number of bytes from the @a data buffer
  688. * to send to the device.
  689. *
  690. * @return
  691. * - @e KMX_OK if the load operation completed successfully;
  692. * - @e KMX_IO_ERROR if an I/O error occured;
  693. * - @e KMX_INVALID_QUERY if the load query was invalid;
  694. * - @e KMX_BUFFER_TOO_SMALL if the size of the @a data buffer did
  695. * not match the expected size for the specified memory slot;
  696. * - @e KMX_INVALID_REPLY if the device replied unexpectedly;
  697. * - @e KMX_DEST_MEM_PROTECTED if the specified memory slot was
  698. * write-protected;
  699. * - @e KMX_DEST_PRG_MISSING if the specified memory slot did not
  700. * exist;
  701. * - @e KMX_MEM_OVERFLOW if a memory error occured on the device;
  702. * - @e KMX_OTHER_ERROR if an unspecified error occured on the
  703. * device.
  704. */
  705. int
  706. kmx_microx_load(midi_io_t *midi,
  707. unsigned int chan,
  708. struct kmx_microx_dump *dump,
  709. unsigned char *data,
  710. size_t len)
  711. {
  712. size_t n, m;
  713. int status;
  714. unsigned char buffer[256] = { 0xF0, /* Begin SysEx message */
  715. 0x42, /* KORG manufacturer ID */
  716. 0x30, /* Global channel */
  717. 0x7A, /* End of SysEx header */
  718. 0x00, /* Dump type */
  719. 0x00, /* Dump parameter 1 */
  720. 0x00, /* Dump parameter 2 */
  721. 0x00, /* Dump parameter 3 */ };
  722. unsigned char reply[7];
  723. buffer[2] |= chan & 0xF;
  724. if ( get_dump_parameters(dump, &(buffer[4])) == KMX_INVALID_QUERY )
  725. return KMX_INVALID_QUERY;
  726. if ( len < kmx_microx_get_dump_size(dump) )
  727. return KMX_BUFFER_TOO_SMALL;
  728. n = 8;
  729. m = status = 0;
  730. while ( m < len && status == 0 ) {
  731. /* Convert data from the microX internal representation
  732. * to MIDI format. See comment in kmx_micro_dump above. */
  733. if ( m % 7 == 0 )
  734. buffer[n++] = 0;
  735. buffer[n - (m % 7) - 1] += (data[m] & 0x80) >> (7 - m % 7);
  736. buffer[n++] = data[m++] & 0x7F;
  737. if ( n == sizeof(buffer) ) {
  738. if ( midi_write(midi, buffer, n) == -1 )
  739. status = KMX_IO_ERROR;
  740. n = 0;
  741. }
  742. }
  743. buffer[n++] = 0xF7; /* SysEx message end */
  744. if ( (n = sysex_query(midi, buffer, n, reply, sizeof(reply))) < 6 )
  745. status = KMX_IO_ERROR;
  746. if ( reply[0] != 0xF0 || reply[1] != KORG_ID || reply[3] != 0x7A )
  747. status = KMX_INVALID_REPLY;
  748. else if ( reply[4] == 0x24 ) /* DATA LOAD ERROR (NAC) */
  749. status = KMX_ERROR_CODE - reply[5];
  750. else if ( reply[4] == 0x23 ) /* DATA LOAD COMPLETED (ACK) */
  751. status = KMX_OK;
  752. return status;
  753. }