Handle receiving of sysex conig data
This commit is contained in:
parent
72b305830f
commit
1cdface168
5 changed files with 124 additions and 24 deletions
|
@ -184,16 +184,24 @@ void sendWLChannel(const uint8_t channel) {
|
||||||
//Only 14 LSB of int value are used (2MSB are discarded), so only works for unsigned data 0-16383
|
//Only 14 LSB of int value are used (2MSB are discarded), so only works for unsigned data 0-16383
|
||||||
|
|
||||||
//NOTE: This assumes code is running on a little-endian CPU, both for real device (Teensy) and simulator.
|
//NOTE: This assumes code is running on a little-endian CPU, both for real device (Teensy) and simulator.
|
||||||
uint16_t midi16to14(uint16_t realdata) {
|
uint16_t midi16to14(const uint16_t realdata) {
|
||||||
return (realdata & 0x3F80) >>7 | (realdata & 0x007F) <<8;
|
return (realdata & 0x3F80) >>7 | (realdata & 0x007F) <<8;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t midi14to16(uint16_t mididata) {
|
uint16_t midi14to16(const uint16_t mididata) {
|
||||||
return (mididata & 0x7F00) >> 8 | (mididata & 0x007F) <<7 ;
|
return (mididata & 0x7F00) >> 8 | (mididata & 0x007F) <<7 ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Read from a memory location, such as MIDI receive buffer
|
||||||
|
uint16_t midi14to16(const uint8_t* mididata) {
|
||||||
|
uint8_t msb = *mididata;
|
||||||
|
uint8_t lsb = *(mididata+1);
|
||||||
|
|
||||||
|
return (msb & 0x007F) <<7 | (lsb & 0x007F);
|
||||||
|
}
|
||||||
|
|
||||||
//This is a bit different. MSB of each byte is just discarded (instead of discarding MSB for whole value). Just used for CRC (easier to compare)
|
//This is a bit different. MSB of each byte is just discarded (instead of discarding MSB for whole value). Just used for CRC (easier to compare)
|
||||||
uint32_t midi32to28(uint32_t realdata) {
|
uint32_t midi32to28(const uint32_t realdata) {
|
||||||
uint8_t* p = (uint8_t*)&realdata;
|
uint8_t* p = (uint8_t*)&realdata;
|
||||||
|
|
||||||
uint32_t r=0;
|
uint32_t r=0;
|
||||||
|
|
|
@ -38,8 +38,9 @@ void sendWLChannel(const uint8_t channel);
|
||||||
|
|
||||||
|
|
||||||
//Convert things between "regular data" and MIDI data (byte order and 7-bits-per-byte)
|
//Convert things between "regular data" and MIDI data (byte order and 7-bits-per-byte)
|
||||||
uint16_t midi16to14(uint16_t realdata);
|
uint16_t midi16to14(const uint16_t realdata);
|
||||||
uint16_t midi14to16(uint16_t mididata);
|
uint16_t midi14to16(const uint16_t mididata);
|
||||||
uint32_t midi32to28(uint32_t realdata);
|
uint16_t midi14to16(const uint8_t* mididata);
|
||||||
|
uint32_t midi32to28(const uint32_t realdata);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -305,6 +305,7 @@ bool receiveSysexSettings(const uint8_t* data, const uint16_t length) {
|
||||||
//Make sure length of receive buffer is enough to read all we need to. We can accept extra junk at the end though.
|
//Make sure length of receive buffer is enough to read all we need to. We can accept extra junk at the end though.
|
||||||
if(length<expected_size) {
|
if(length<expected_size) {
|
||||||
configShowMessage("Invalid config format");
|
configShowMessage("Invalid config format");
|
||||||
|
printf("Invalid config format %d %d\n", length, expected_size);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,18 +318,20 @@ bool receiveSysexSettings(const uint8_t* data, const uint16_t length) {
|
||||||
memcpy(&crc_rcv, data+checksum_pos, 4);
|
memcpy(&crc_rcv, data+checksum_pos, 4);
|
||||||
if(crc != crc_rcv) {
|
if(crc != crc_rcv) {
|
||||||
configShowMessage("Invalid checksum");
|
configShowMessage("Invalid checksum");
|
||||||
|
printf("[crc] expected: %x got: %x\n", crc, crc_rcv);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Verify that payload size matches the size of our EEPROM config
|
//Verify that payload size matches the size of our EEPROM config
|
||||||
uint16_t payload_size = midi14to16(data[payload_pos]);
|
uint16_t payload_size = midi14to16(data+size_pos);
|
||||||
if(payload_size != EEPROM_SIZE) {
|
if(payload_size != EEPROM_SIZE) {
|
||||||
configShowMessage("Invalid config size");
|
configShowMessage("Invalid config size");
|
||||||
|
printf("[size] expected: %d got: %d (at %d)\n", EEPROM_SIZE, payload_size, size_pos);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint16_t eeprom_version_rcv = midi14to16(data[payload_pos+VERSION_ADDR]);
|
uint16_t eeprom_version_rcv = midi14to16(data+(payload_pos+VERSION_ADDR));
|
||||||
if(eeprom_version_rcv != EEPROM_VERSION) {
|
if(eeprom_version_rcv != EEPROM_VERSION) {
|
||||||
configShowMessage("Invalid config version");
|
configShowMessage("Invalid config version");
|
||||||
return false;
|
return false;
|
||||||
|
@ -338,8 +341,7 @@ bool receiveSysexSettings(const uint8_t* data, const uint16_t length) {
|
||||||
for(uint16_t i=0; i<payload_size/2; i++) {
|
for(uint16_t i=0; i<payload_size/2; i++) {
|
||||||
uint16_t addr = i*2;
|
uint16_t addr = i*2;
|
||||||
uint16_t val;
|
uint16_t val;
|
||||||
memcpy(&val, data+payload_pos+addr, 2);
|
val = midi14to16(data+(payload_pos+addr));
|
||||||
val = midi14to16(val);
|
|
||||||
|
|
||||||
//Skip sensor calibration values if they are "out of bounds". This makes it possible to send a config that does
|
//Skip sensor calibration values if they are "out of bounds". This makes it possible to send a config that does
|
||||||
//not overwrite sensor calibration.
|
//not overwrite sensor calibration.
|
||||||
|
@ -363,7 +365,7 @@ bool receiveSysexSettings(const uint8_t* data, const uint16_t length) {
|
||||||
if(val<ctouchLoLimit || val>ctouchHiLimit) continue;
|
if(val<ctouchLoLimit || val>ctouchHiLimit) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeSetting(i, val);
|
writeSetting(addr, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
//All went well
|
//All went well
|
||||||
|
@ -397,17 +399,21 @@ void configShowMessage(const char* message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* sysex_rcv_buffer = NULL;
|
uint8_t* sysex_rcv_buffer = NULL;
|
||||||
|
uint16_t sysex_buf_size = 0;
|
||||||
|
|
||||||
void handleSysexChunk(const uint8_t *data, const uint16_t length, const bool last) {
|
void handleSysexChunk(const uint8_t *data, const uint16_t length, const uint8_t last) {
|
||||||
size_t pos = 0;
|
uint16_t pos;
|
||||||
|
|
||||||
if(!sysex_rcv_buffer) {
|
if(!sysex_rcv_buffer) {
|
||||||
//Start out with an empty buffer
|
//Start out with an empty buffer
|
||||||
sysex_rcv_buffer = (uint8_t *)malloc(length);
|
pos = 0;
|
||||||
|
sysex_buf_size = length;
|
||||||
|
sysex_rcv_buffer = (uint8_t *)malloc(sysex_buf_size);
|
||||||
} else {
|
} else {
|
||||||
//Increase size of current buffer
|
//Increase size of current buffer
|
||||||
size_t pos = sizeof(sysex_rcv_buffer);
|
pos = sysex_buf_size;
|
||||||
sysex_rcv_buffer = (uint8_t *)realloc(sysex_rcv_buffer, pos + length);
|
sysex_buf_size += length;
|
||||||
|
sysex_rcv_buffer = (uint8_t *)realloc(sysex_rcv_buffer, sysex_buf_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Append this chunk to buffer
|
//Append this chunk to buffer
|
||||||
|
@ -415,9 +421,12 @@ void handleSysexChunk(const uint8_t *data, const uint16_t length, const bool las
|
||||||
|
|
||||||
//If it's the last one, call the regular handler to process it
|
//If it's the last one, call the regular handler to process it
|
||||||
if(last) {
|
if(last) {
|
||||||
handleSysex(sysex_rcv_buffer, pos+length);
|
handleSysex(sysex_rcv_buffer, sysex_buf_size);
|
||||||
|
|
||||||
|
//Discard the buffer
|
||||||
free(sysex_rcv_buffer);
|
free(sysex_rcv_buffer);
|
||||||
sysex_rcv_buffer = NULL;
|
sysex_rcv_buffer = NULL;
|
||||||
|
sysex_buf_size = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,8 +85,14 @@ public:
|
||||||
void sendPitchBend(int value, uint8_t channel, uint8_t cable=0);
|
void sendPitchBend(int value, uint8_t channel, uint8_t cable=0);
|
||||||
void sendSysEx(uint16_t length, const uint8_t *data, bool hasTerm=false, uint8_t cable=0);
|
void sendSysEx(uint16_t length, const uint8_t *data, bool hasTerm=false, uint8_t cable=0);
|
||||||
bool read(uint8_t channel=0);
|
bool read(uint8_t channel=0);
|
||||||
void setHandleSystemExclusive(__unused void (*fptr) (const uint8_t *array, uint8_t size));
|
void setHandleSystemExclusive(void (*fptr) (const uint8_t *array, unsigned int size));
|
||||||
void setHandleSystemExclusive(__unused void (*fptr) (const uint8_t *data, uint16_t length, bool complete));
|
void setHandleSystemExclusive(void (*fptr) (const uint8_t *data, uint16_t length, uint8_t complete));
|
||||||
|
|
||||||
|
void receiveMidiData(const uint8_t *data, const uint16_t length); //Send midi data "into simulator"
|
||||||
|
private:
|
||||||
|
//Handlers registered to receive MIDI
|
||||||
|
void (*usb_midi_handleSysExPartial)(const uint8_t *data, uint16_t length, uint8_t complete);
|
||||||
|
void (*usb_midi_handleSysExComplete)(const uint8_t *data, unsigned int size);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern SimSerial Serial;
|
extern SimSerial Serial;
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
* Stub simulation of Teensy usbMidi
|
* Stub simulation of Teensy usbMidi
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
void SimUsbMidi::sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t __unused cable)
|
void SimUsbMidi::sendNoteOff(uint8_t note, uint8_t velocity, uint8_t channel, uint8_t __unused cable)
|
||||||
{
|
{
|
||||||
printf( "[usbMIDI::noteOff] note %03d vel %03d ch %02d\n", note, velocity, channel);
|
printf( "[usbMIDI::noteOff] note %03d vel %03d ch %02d\n", note, velocity, channel);
|
||||||
|
@ -54,16 +53,93 @@ void SimUsbMidi::sendSysEx(uint16_t length, const uint8_t __unused *data, bool _
|
||||||
printf( "[usbMIDI::sysEx] len %d\n", length);
|
printf( "[usbMIDI::sysEx] len %d\n", length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Set a low chunk size on purpose just to let the receiver work for it
|
||||||
|
#define MIDI_SYSEX_CHUNK_SIZE 32
|
||||||
|
|
||||||
|
/* Test data for config mode
|
||||||
|
|
||||||
|
//Carefully crafted config command chunk to send via midi
|
||||||
|
static const uint8_t midimessage[] = {
|
||||||
|
0xf0, //Sysex start
|
||||||
|
0x00, 0x3e, 0x7f, //Vendor
|
||||||
|
'N', 'u', 'E', 'V', 'I', //header
|
||||||
|
'c', '0', '2', //message code
|
||||||
|
0, 102, //length
|
||||||
|
|
||||||
|
//Payload
|
||||||
|
0x00, 0x20, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, //00
|
||||||
|
0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x06, 0x07, //08
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //10
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //18
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //20
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //28
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //30
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //38
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //40
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //48
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //50
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, //58
|
||||||
|
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, //60
|
||||||
|
0x2a, 0x11, 0x32, 0x5a, //crc32
|
||||||
|
0xf7 //sysex end marker
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool midisent = false;
|
||||||
|
|
||||||
|
//On first midi read, send a message
|
||||||
|
bool SimUsbMidi::read(uint8_t __unused channel) {
|
||||||
|
if(!midisent) {
|
||||||
|
this->receiveMidiData(midimessage, sizeof(midimessage));
|
||||||
|
midisent=true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
bool SimUsbMidi::read(uint8_t __unused channel) {
|
bool SimUsbMidi::read(uint8_t __unused channel) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Regular sysex handler
|
|
||||||
void SimUsbMidi::setHandleSystemExclusive(__unused void (*fptr) (const uint8_t *array, uint8_t size)) {
|
|
||||||
|
|
||||||
|
//Provide midi data for simulation to receive
|
||||||
|
void SimUsbMidi::receiveMidiData(const uint8_t *data, const uint16_t length) {
|
||||||
|
if(this->usb_midi_handleSysExPartial) {
|
||||||
|
//Chunked sysex receiver set, use that.
|
||||||
|
if(length<=MIDI_SYSEX_CHUNK_SIZE) {
|
||||||
|
//Send all in one go
|
||||||
|
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExPartial(complete) %d B\n", length);
|
||||||
|
(this->usb_midi_handleSysExPartial)(data, length, true);
|
||||||
|
} else {
|
||||||
|
uint8_t* buf = (uint8_t*)malloc(MIDI_SYSEX_CHUNK_SIZE);
|
||||||
|
int pos=0;
|
||||||
|
while(pos<length) {
|
||||||
|
int remaining = length-pos;
|
||||||
|
int bytesToSend = min(remaining, MIDI_SYSEX_CHUNK_SIZE);
|
||||||
|
bool complete = (bytesToSend == remaining);
|
||||||
|
|
||||||
|
memcpy(buf, data+pos, bytesToSend);
|
||||||
|
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExPartial(complete: %d) %d B\n", complete, bytesToSend);
|
||||||
|
(this->usb_midi_handleSysExPartial)(buf, bytesToSend, complete);
|
||||||
|
pos=pos+bytesToSend;
|
||||||
|
}
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if(this->usb_midi_handleSysExComplete) {
|
||||||
|
printf( "[SimUsbMidi::receiveMidiData] usb_midi_handleSysExComplete() %d B\n", length);
|
||||||
|
(this->usb_midi_handleSysExComplete)(data, length);
|
||||||
|
} else {
|
||||||
|
//Nobody listening
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Regular sysex handler. For some reason the data pointer is not const, but we'll set it as such to not be dumb.
|
||||||
|
void SimUsbMidi::setHandleSystemExclusive(void (*fptr) (const uint8_t *array, unsigned int size)) {
|
||||||
|
this->usb_midi_handleSysExComplete = fptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//"Chunked" sysex handler (teensy extension), for large messages
|
//"Chunked" sysex handler (teensy extension), for large messages
|
||||||
void SimUsbMidi::setHandleSystemExclusive(__unused void (*fptr) (const uint8_t *array, uint16_t size, bool last)) {
|
void SimUsbMidi::setHandleSystemExclusive(void (*fptr) (const uint8_t *array, uint16_t size, uint8_t last)) {
|
||||||
|
this->usb_midi_handleSysExPartial = fptr;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue