Hi,
I'm trying to write a patch editor and librarian for Roland's CM-32L
synth module. This was a repackaged version of the popular MT-32 with 8
part multimbral support, separate drums and a basic reverb facility. The
only controls on the front panel are a power switch and master volume
knob - everything else has to be done via MIDI.
I have written a simple program to change which preset each part uses,
and can also trigger notes. Now I want to get a SysEx dump so I can put
together the patch editor. However, whenever I send a Request Data
message the unit doesn't appear to respond. It definitely receives the
message as the MIDI activity light comes on. But when I try to read from
the MIDI device file my call blocks forever.
Has anyone had succes in getting SysEx data flowing back and forth
between Roland sound modules and their computer? I have attached my
simple test program in case I'm doing something obviously wrong. By the
way, I'm working on NetBSD which has slightly different MIDI device
filenames to Linux.
Chris
--
http://www.btinternet.com/~chris.wareham/
/* get_sysex_dump.c
* cc -Wall -Wformat -g -o get_sysex_dump get_sysex_dump.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#define SYSEX_START 0xf0
#define SYSEX_END 0xf7
struct sysex {
unsigned int length;
unsigned char *data;
};
static struct sysex *sysex_new(void);
static void sysex_delete(struct sysex *);
static void sysex_set_length(struct sysex *, unsigned int);
static void sysex_read(int, struct sysex *, int);
static void sysex_write(int, struct sysex *);
static int sysex_checksum(struct sysex *, int, int);
int
main(int argc, char *argv[])
{
int fd, i;
struct sysex *se;
fd = open("/dev/rmidi2", O_RDWR|O_SYNC);
if (fd == -1) {
fprintf(stderr, "open() failed: %s\n", strerror(errno));
return 1;
}
se = sysex_new();
sysex_set_length(se, 11);
se->data[0] = 0x41; /* Roland's ID */
se->data[1] = 0x10; /* Device Number - default appears to be 0x10 */
se->data[2] = 0x16; /* Model ID */
se->data[3] = 0x11; /* Command ID - "Request Data" */
se->data[4] = 0x04; /* Address MSB */
se->data[5] = 0x01; /* Address */
se->data[6] = 0x76; /* Address LSB */
se->data[7] = 0x00; /* Size MSB */
se->data[8] = 0x01; /* Size */
se->data[9] = 0x76; /* Size LSB */
se->data[10] = 0x0e; /* Checksum */
sysex_write(fd, se);
sysex_set_length(se, 252); /* 8 bytes to wrap data, and 246 bytes of data */
sysex_read(fd, se, 1);
printf("Read %d bytes\n", se->length);
for (i = 0; i < se->length; i++)
printf("0x%0x\n", se->data[i]);
sysex_delete(se);
if (close(fd) == -1) {
fprintf(stderr, "close() failed: %s\n", strerror(errno));
return 1;
}
return 0;
}
/* Receives a MIDI System Exclusive message */
void
sysex_read(int fd, struct sysex *se, int autostop)
{
int n, length = 0;
unsigned char c;
if (fd == -1) {
fprintf(stderr, "sysex_read() failed: invalid fd\n");
return;
}
if (se == NULL) {
fprintf(stderr, "sysex_read() failed: null sysex argument\n");
return;
}
while ((n = read(fd, &c, 1)) == 1 && c != SYSEX_START)
usleep(40);
if (n != 1)
return;
while (length < se->length) {
n = read(fd, &c, 1);
if (n == 1) {
se->data[length++] = c;
if (autostop && se->data[length - 1] == SYSEX_END)
break;
}
}
if (length < se->length) {
se->length = length;
se->data = realloc(se->data, se->length);
}
}
/* Sends a MIDI System Exclusive message */
void
sysex_write(int fd, struct sysex *se)
{
unsigned char *data;
if (fd == -1) {
fprintf(stderr, "sysex_write() failed: invalid fd\n");
return;
}
if (se == NULL) {
fprintf(stderr, "sysex_write() failed: null sysex argument\n");
return;
}
data = calloc(se->length + 2, sizeof(unsigned char));
data[0] = SYSEX_START;
memcpy(data + 1, se->data, se->length);
data[se->length + 1] = SYSEX_END;
write(fd, data, se->length + 2);
free(data);
}
static struct sysex *sysex_new(void)
{
struct sysex *se;
se = calloc(1, sizeof(struct sysex));
return se;
}
static void sysex_delete(struct sysex *se)
{
if (se->length != 0)
free(se->data);
free(se);
}
/* Sets the length of a MIDI System Exclusive message */
void
sysex_set_length(struct sysex *se, unsigned int length)
{
if (se == NULL) {
fprintf(stderr, "sysex_set_length() failed: null sysex argument\n");
return;
}
se->length = length;
se->data = realloc(se->data, se->length);
}
/* Determine a Roland System Exclusive checksum */
int
sysex_checksum(struct sysex *se, int start, int length)
{
int checksum, i;
if (se == NULL) {
fprintf(stderr, "sysex_checksum() failed: null sysex argument\n");
return 0;
}
checksum = 0;
for (i = start; i < start + length; i++) {
checksum += se->data[i];
if (checksum > 127)
checksum -= 128;
}
checksum = 128 - checksum;
return checksum;
}