|
|
|
|
|
|
|
|
#define print USBHost::print_ |
|
|
#define print USBHost::print_ |
|
|
#define println USBHost::println_ |
|
|
#define println USBHost::println_ |
|
|
|
|
|
|
|
|
void MIDIDevice::init() |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::init() |
|
|
{ |
|
|
{ |
|
|
contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); |
|
|
contribute_Pipes(mypipes, sizeof(mypipes)/sizeof(Pipe_t)); |
|
|
contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); |
|
|
contribute_Transfers(mytransfers, sizeof(mytransfers)/sizeof(Transfer_t)); |
|
|
|
|
|
|
|
|
// EP_CONTROL_UNDEFINED 0x00 |
|
|
// EP_CONTROL_UNDEFINED 0x00 |
|
|
// ASSOCIATION_CONTROL 0x01 |
|
|
// ASSOCIATION_CONTROL 0x01 |
|
|
|
|
|
|
|
|
bool MIDIDevice::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) |
|
|
|
|
|
|
|
|
bool MIDIDeviceBase::claim(Device_t *dev, int type, const uint8_t *descriptors, uint32_t len) |
|
|
{ |
|
|
{ |
|
|
// only claim at interface level |
|
|
// only claim at interface level |
|
|
if (type != 1) return false; |
|
|
if (type != 1) return false; |
|
|
|
|
|
|
|
|
if (p[0] != 9 || p[1] != 4) return false; // interface descriptor |
|
|
if (p[0] != 9 || p[1] != 4) return false; // interface descriptor |
|
|
//println(" bInterfaceClass=", p[5]); |
|
|
//println(" bInterfaceClass=", p[5]); |
|
|
//println(" bInterfaceSubClass=", p[6]); |
|
|
//println(" bInterfaceSubClass=", p[6]); |
|
|
if (p[5] != 1) return false; // bInterfaceClass: 1 = Audio class |
|
|
|
|
|
if (p[6] != 3) return false; // bInterfaceSubClass: 3 = MIDI |
|
|
|
|
|
|
|
|
bool ismidi = false; |
|
|
|
|
|
if (p[5] == 1 && p[6] == 3) { |
|
|
|
|
|
println(" Interface is MIDI"); // p[5] is bInterfaceClass: 1 = Audio class |
|
|
|
|
|
ismidi = true; // p[6] is bInterfaceSubClass: 3 = MIDI |
|
|
|
|
|
} else { |
|
|
|
|
|
if (p[5] >= 2 && p[5] <= 18) return false; // definitely not MIDI |
|
|
|
|
|
// Yamaha uses vendor specific class, but can be |
|
|
|
|
|
// identified as MIDI from CS_INTERFACE descriptors. |
|
|
|
|
|
// https://forum.pjrc.com/threads/55142?p=199162&viewfull=1#post199162 |
|
|
|
|
|
println(" Interface is unknown (might be Yahama)"); |
|
|
|
|
|
} |
|
|
p += 9; |
|
|
p += 9; |
|
|
println(" Interface is MIDI"); |
|
|
|
|
|
rx_ep = 0; |
|
|
rx_ep = 0; |
|
|
tx_ep = 0; |
|
|
tx_ep = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (subtype == 1) { |
|
|
if (subtype == 1) { |
|
|
// Interface Header, midi 1.0, page 21 |
|
|
// Interface Header, midi 1.0, page 21 |
|
|
println(" MIDI Header (ignored)"); |
|
|
println(" MIDI Header (ignored)"); |
|
|
|
|
|
ismidi = true; |
|
|
} else if (subtype == 2) { |
|
|
} else if (subtype == 2) { |
|
|
// MIDI IN Jack, midi 1.0, page 22 |
|
|
// MIDI IN Jack, midi 1.0, page 22 |
|
|
println(" MIDI IN Jack (ignored)"); |
|
|
println(" MIDI IN Jack (ignored)"); |
|
|
|
|
|
ismidi = true; |
|
|
} else if (subtype == 3) { |
|
|
} else if (subtype == 3) { |
|
|
// MIDI OUT Jack, midi 1.0, page 22 |
|
|
// MIDI OUT Jack, midi 1.0, page 22 |
|
|
println(" MIDI OUT Jack (ignored)"); |
|
|
println(" MIDI OUT Jack (ignored)"); |
|
|
|
|
|
ismidi = true; |
|
|
} else if (subtype == 4) { |
|
|
} else if (subtype == 4) { |
|
|
// Element Descriptor, midi 1.0, page 23-24 |
|
|
// Element Descriptor, midi 1.0, page 23-24 |
|
|
println(" MIDI Element (ignored)"); |
|
|
println(" MIDI Element (ignored)"); |
|
|
|
|
|
ismidi = true; |
|
|
|
|
|
} else if (subtype == 0xF1 && p[3] == 2) { |
|
|
|
|
|
// see Linux sound/usb/quirks.c create_roland_midi_quirk() |
|
|
|
|
|
println(" Roland vendor-specific (ignored)"); |
|
|
|
|
|
ismidi = true; |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
println(" Unknown MIDI CS_INTERFACE descriptor!"); |
|
|
return false; // unknown |
|
|
return false; // unknown |
|
|
} |
|
|
} |
|
|
} else if (type == 5) { |
|
|
} else if (type == 5) { |
|
|
|
|
|
|
|
|
// MIDI endpoint info, midi 1.0: 6.2.2, page 26 |
|
|
// MIDI endpoint info, midi 1.0: 6.2.2, page 26 |
|
|
println(" MIDI Endpoint Jack Association (ignored)"); |
|
|
println(" MIDI Endpoint Jack Association (ignored)"); |
|
|
} else { |
|
|
} else { |
|
|
|
|
|
println(" Unknown descriptor, type=", type); |
|
|
return false; // unknown |
|
|
return false; // unknown |
|
|
} |
|
|
} |
|
|
p += len; |
|
|
p += len; |
|
|
} |
|
|
} |
|
|
|
|
|
if (!ismidi) { |
|
|
|
|
|
println("This interface is not MIDI"); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
// if an IN endpoint was found, create its pipe |
|
|
// if an IN endpoint was found, create its pipe |
|
|
if (rx_ep && rx_size <= MAX_PACKET_SIZE) { |
|
|
|
|
|
|
|
|
if (rx_ep && rx_size <= max_packet_size) { |
|
|
rxpipe = new_Pipe(dev, rx_ep_type, rx_ep, 1, rx_size); |
|
|
rxpipe = new_Pipe(dev, rx_ep_type, rx_ep, 1, rx_size); |
|
|
if (rxpipe) { |
|
|
if (rxpipe) { |
|
|
rxpipe->callback_function = rx_callback; |
|
|
rxpipe->callback_function = rx_callback; |
|
|
|
|
|
|
|
|
rxpipe = NULL; |
|
|
rxpipe = NULL; |
|
|
} |
|
|
} |
|
|
// if an OUT endpoint was found, create its pipe |
|
|
// if an OUT endpoint was found, create its pipe |
|
|
if (tx_ep && tx_size <= MAX_PACKET_SIZE) { |
|
|
|
|
|
|
|
|
if (tx_ep && tx_size <= max_packet_size) { |
|
|
txpipe = new_Pipe(dev, tx_ep_type, tx_ep, 0, tx_size); |
|
|
txpipe = new_Pipe(dev, tx_ep_type, tx_ep, 0, tx_size); |
|
|
if (txpipe) { |
|
|
if (txpipe) { |
|
|
txpipe->callback_function = tx_callback; |
|
|
txpipe->callback_function = tx_callback; |
|
|
|
|
|
|
|
|
return (rxpipe || txpipe); |
|
|
return (rxpipe || txpipe); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void MIDIDevice::rx_callback(const Transfer_t *transfer) |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::rx_callback(const Transfer_t *transfer) |
|
|
{ |
|
|
{ |
|
|
if (transfer->driver) { |
|
|
if (transfer->driver) { |
|
|
((MIDIDevice *)(transfer->driver))->rx_data(transfer); |
|
|
((MIDIDevice *)(transfer->driver))->rx_data(transfer); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void MIDIDevice::tx_callback(const Transfer_t *transfer) |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::tx_callback(const Transfer_t *transfer) |
|
|
{ |
|
|
{ |
|
|
if (transfer->driver) { |
|
|
if (transfer->driver) { |
|
|
((MIDIDevice *)(transfer->driver))->tx_data(transfer); |
|
|
((MIDIDevice *)(transfer->driver))->tx_data(transfer); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void MIDIDevice::rx_data(const Transfer_t *transfer) |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::rx_data(const Transfer_t *transfer) |
|
|
{ |
|
|
{ |
|
|
println("MIDIDevice Receive"); |
|
|
println("MIDIDevice Receive"); |
|
|
print(" MIDI Data: "); |
|
|
print(" MIDI Data: "); |
|
|
|
|
|
|
|
|
for (uint32_t i=0; i < len; i++) { |
|
|
for (uint32_t i=0; i < len; i++) { |
|
|
uint32_t msg = rx_buffer[i]; |
|
|
uint32_t msg = rx_buffer[i]; |
|
|
if (msg) { |
|
|
if (msg) { |
|
|
if (++head >= RX_QUEUE_SIZE) head = 0; |
|
|
|
|
|
|
|
|
if (++head >= rx_queue_size) head = 0; |
|
|
rx_queue[head] = msg; |
|
|
rx_queue[head] = msg; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
rx_head = head; |
|
|
rx_head = head; |
|
|
rx_tail = tail; |
|
|
rx_tail = tail; |
|
|
uint32_t avail = (head < tail) ? tail - head - 1 : RX_QUEUE_SIZE - 1 - head + tail; |
|
|
|
|
|
|
|
|
uint32_t avail = (head < tail) ? tail - head - 1 : rx_queue_size - 1 - head + tail; |
|
|
//println("rx_size = ", rx_size); |
|
|
//println("rx_size = ", rx_size); |
|
|
println("avail = ", avail); |
|
|
println("avail = ", avail); |
|
|
if (avail >= (uint32_t)(rx_size>>2)) { |
|
|
if (avail >= (uint32_t)(rx_size>>2)) { |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void MIDIDevice::tx_data(const Transfer_t *transfer) |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::tx_data(const Transfer_t *transfer) |
|
|
{ |
|
|
{ |
|
|
println("MIDIDevice transmit complete"); |
|
|
println("MIDIDevice transmit complete"); |
|
|
print(" MIDI Data: "); |
|
|
print(" MIDI Data: "); |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MIDIDevice::disconnect() |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::disconnect() |
|
|
{ |
|
|
{ |
|
|
// should rx_queue be cleared? |
|
|
// should rx_queue be cleared? |
|
|
// as-is, the user can still read MIDI messages |
|
|
// as-is, the user can still read MIDI messages |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void MIDIDevice::write_packed(uint32_t data) |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::write_packed(uint32_t data) |
|
|
{ |
|
|
{ |
|
|
if (!txpipe) return; |
|
|
if (!txpipe) return; |
|
|
uint32_t tx_max = tx_size / 4; |
|
|
uint32_t tx_max = tx_size / 4; |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void MIDIDevice::send_sysex_buffer_has_term(const uint8_t *data, uint32_t length, uint8_t cable) |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::send_sysex_buffer_has_term(const uint8_t *data, uint32_t length, uint8_t cable) |
|
|
{ |
|
|
{ |
|
|
cable = (cable & 0x0F) << 4; |
|
|
cable = (cable & 0x0F) << 4; |
|
|
while (length > 3) { |
|
|
while (length > 3) { |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void MIDIDevice::send_sysex_add_term_bytes(const uint8_t *data, uint32_t length, uint8_t cable) |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::send_sysex_add_term_bytes(const uint8_t *data, uint32_t length, uint8_t cable) |
|
|
{ |
|
|
{ |
|
|
cable = (cable & 0x0F) << 4; |
|
|
cable = (cable & 0x0F) << 4; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool MIDIDevice::read(uint8_t channel) |
|
|
|
|
|
|
|
|
bool MIDIDeviceBase::read(uint8_t channel) |
|
|
{ |
|
|
{ |
|
|
uint32_t n, head, tail, avail, ch, type1, type2, b1; |
|
|
uint32_t n, head, tail, avail, ch, type1, type2, b1; |
|
|
|
|
|
|
|
|
head = rx_head; |
|
|
head = rx_head; |
|
|
tail = rx_tail; |
|
|
tail = rx_tail; |
|
|
if (head == tail) return false; |
|
|
if (head == tail) return false; |
|
|
if (++tail >= RX_QUEUE_SIZE) tail = 0; |
|
|
|
|
|
|
|
|
if (++tail >= rx_queue_size) tail = 0; |
|
|
n = rx_queue[tail]; |
|
|
n = rx_queue[tail]; |
|
|
rx_tail = tail; |
|
|
rx_tail = tail; |
|
|
if (!rx_packet_queued && rxpipe) { |
|
|
if (!rx_packet_queued && rxpipe) { |
|
|
avail = (head < tail) ? tail - head - 1 : RX_QUEUE_SIZE - 1 - head + tail; |
|
|
|
|
|
|
|
|
avail = (head < tail) ? tail - head - 1 : rx_queue_size - 1 - head + tail; |
|
|
if (avail >= (uint32_t)(rx_size>>2)) { |
|
|
if (avail >= (uint32_t)(rx_size>>2)) { |
|
|
__disable_irq(); |
|
|
__disable_irq(); |
|
|
queue_Data_Transfer(rxpipe, rx_buffer, rx_size, this); |
|
|
queue_Data_Transfer(rxpipe, rx_buffer, rx_size, this); |
|
|
|
|
|
|
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void MIDIDevice::sysex_byte(uint8_t b) |
|
|
|
|
|
|
|
|
void MIDIDeviceBase::sysex_byte(uint8_t b) |
|
|
{ |
|
|
{ |
|
|
if (handleSysExPartial && msg_sysex_len >= SYSEX_MAX_LEN) { |
|
|
if (handleSysExPartial && msg_sysex_len >= SYSEX_MAX_LEN) { |
|
|
// when buffer is full, send another chunk to partial handler. |
|
|
// when buffer is full, send another chunk to partial handler. |