|
|
@ -3,6 +3,7 @@ |
|
|
|
#include "usbh_midi_launchpad.h" |
|
|
|
#include "usb_midi_launchpad_transport.hh" |
|
|
|
#include <cassert> |
|
|
|
#include "daisy_seed.h" |
|
|
|
|
|
|
|
extern "C" |
|
|
|
{ |
|
|
@ -30,7 +31,7 @@ public: |
|
|
|
void FlushRx() { rx_buffer_.Flush(); } |
|
|
|
void Tx(uint8_t* buffer, size_t size); |
|
|
|
|
|
|
|
void UsbToMidi(uint8_t* buffer, uint8_t length); |
|
|
|
void UsbToMidi(uint8_t); |
|
|
|
void MidiToUsb(uint8_t* buffer, size_t length); |
|
|
|
void Parse(); |
|
|
|
|
|
|
@ -75,16 +76,37 @@ private: |
|
|
|
// Global Impl
|
|
|
|
static UsbMidiLaunchpadTransport::Impl midi_usb_handle; |
|
|
|
|
|
|
|
extern daisy::DaisySeed hw; |
|
|
|
|
|
|
|
void to_string_array_uint(char* outbuf, uint8_t* arr, size_t len) |
|
|
|
{ |
|
|
|
for (uint32_t i = 0; i < len; i++) { |
|
|
|
char charbuf[100] = ""; |
|
|
|
sprintf(charbuf, "[%x]", arr[i]); |
|
|
|
strcat(outbuf, charbuf); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void ReceiveCallback(uint8_t* buffer, uint32_t* length) |
|
|
|
{ |
|
|
|
if(midi_usb_handle.RxActive()) |
|
|
|
{ |
|
|
|
for(uint16_t i = 0; i < *length; i += 4) |
|
|
|
{ |
|
|
|
size_t remaining_bytes = *length - i; |
|
|
|
uint8_t packet_length = remaining_bytes > 4 ? 4 : remaining_bytes; |
|
|
|
midi_usb_handle.UsbToMidi(buffer + i, packet_length); |
|
|
|
char strbuf[1000] = ""; |
|
|
|
to_string_array_uint(strbuf, buffer, *length); |
|
|
|
hw.PrintLine("RX RAW: %x - %s", *length, strbuf); |
|
|
|
} |
|
|
|
if (midi_usb_handle.RxActive()) { |
|
|
|
for (unsigned int notemsg = 0; notemsg < *length; notemsg += 2) { |
|
|
|
uint8_t midi_note[3] = { 0x90, 0x00, 0x00 }; |
|
|
|
midi_note[1] = buffer[notemsg]; |
|
|
|
midi_note[2] = buffer[notemsg += 1]; |
|
|
|
midi_usb_handle.UsbToMidi(midi_note[0]); |
|
|
|
midi_usb_handle.UsbToMidi(midi_note[1]); |
|
|
|
midi_usb_handle.UsbToMidi(midi_note[2]); |
|
|
|
midi_usb_handle.Parse(); |
|
|
|
|
|
|
|
char strbuf[1000] = ""; |
|
|
|
to_string_array_uint(strbuf, midi_note, 3); |
|
|
|
hw.PrintLine("RX NOTE: %s", strbuf); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -132,189 +154,47 @@ void UsbMidiLaunchpadTransport::Impl::Tx(uint8_t* buffer, size_t size) |
|
|
|
int attempt_count = config_.tx_retry_count; |
|
|
|
bool should_retry; |
|
|
|
|
|
|
|
MidiToUsb(buffer, size); |
|
|
|
do |
|
|
|
{ |
|
|
|
if(config_.periph == Config::HOST) |
|
|
|
{ |
|
|
|
// MidiToUsb(buffer, size);
|
|
|
|
do { |
|
|
|
if (config_.periph == Config::HOST) { |
|
|
|
MIDI_Launchpad_ErrorTypeDef result; |
|
|
|
result = USBH_MIDI_Launchpad_Transmit(pUSB_Host, tx_buffer_, tx_ptr_); |
|
|
|
result = USBH_MIDI_Launchpad_Transmit(pUSB_Host, buffer, size); |
|
|
|
{ |
|
|
|
char strbuf[1000] = ""; |
|
|
|
to_string_array_uint(strbuf, buffer, size); |
|
|
|
hw.PrintLine("Transport Tx() RES=%i - %s",result, strbuf); |
|
|
|
} |
|
|
|
|
|
|
|
should_retry = (result == MIDI_BUSY) && attempt_count--; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
UsbHandle::Result result; |
|
|
|
if(config_.periph == Config::EXTERNAL) |
|
|
|
result = usb_handle_.TransmitExternal(tx_buffer_, tx_ptr_); |
|
|
|
else |
|
|
|
result = usb_handle_.TransmitInternal(tx_buffer_, tx_ptr_); |
|
|
|
should_retry |
|
|
|
= (result == UsbHandle::Result::ERR) && attempt_count--; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(should_retry) |
|
|
|
if (should_retry) { |
|
|
|
System::DelayUs(100); |
|
|
|
} while(should_retry); |
|
|
|
} |
|
|
|
} while (should_retry); |
|
|
|
|
|
|
|
tx_ptr_ = 0; |
|
|
|
} |
|
|
|
|
|
|
|
void UsbMidiLaunchpadTransport::Impl::UsbToMidi(uint8_t* buffer, uint8_t length) |
|
|
|
void UsbMidiLaunchpadTransport::Impl::UsbToMidi(uint8_t buffer) |
|
|
|
{ |
|
|
|
// A length of less than four in the buffer indicates
|
|
|
|
// a garbled message, since USB MIDI packets usually*
|
|
|
|
// require 4 bytes per message
|
|
|
|
if(length < 4) |
|
|
|
return; |
|
|
|
|
|
|
|
// Right now, Daisy only supports a single cable, so we don't
|
|
|
|
// need to extract that value from the upper nibble
|
|
|
|
uint8_t code_index = buffer[0] & 0xF; |
|
|
|
if(code_index == 0x0 || code_index == 0x1) |
|
|
|
{ |
|
|
|
// 0x0 and 0x1 are reserved codes, and if they come up,
|
|
|
|
// there's probably been an error. *0xF indicates data is
|
|
|
|
// sent one byte at a time, rather than in packets of four.
|
|
|
|
// This functionality could be supported later.
|
|
|
|
// The single-byte mode does still come through as 32-bit messages
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// Only writing as many bytes as necessary
|
|
|
|
for(uint8_t i = 0; i < code_index_size_[code_index]; i++) |
|
|
|
{ |
|
|
|
if(rx_buffer_.writable() > 0) |
|
|
|
rx_buffer_.Write(buffer[1 + i]); |
|
|
|
else |
|
|
|
{ |
|
|
|
rx_active_ = false; // disable on overflow
|
|
|
|
break; |
|
|
|
} |
|
|
|
if (rx_buffer_.writable() > 0) { |
|
|
|
rx_buffer_.Write(buffer); |
|
|
|
} else { |
|
|
|
rx_active_ = false; // disable on overflow
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void UsbMidiLaunchpadTransport::Impl::MidiToUsbSingle(uint8_t* buffer, size_t size) |
|
|
|
{ |
|
|
|
if(size == 0) |
|
|
|
return; |
|
|
|
|
|
|
|
// Channel voice messages
|
|
|
|
if((buffer[0] & 0xF0) != 0xF0) |
|
|
|
{ |
|
|
|
// Check message validity
|
|
|
|
if((buffer[0] & 0xF0) == 0xC0 || (buffer[0] & 0xF0) == 0xD0) |
|
|
|
{ |
|
|
|
if(size != 2) |
|
|
|
return; // error
|
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if(size != 3) |
|
|
|
return; //error
|
|
|
|
} |
|
|
|
|
|
|
|
// CIN is the same as status byte for channel voice messages
|
|
|
|
tx_buffer_[tx_ptr_ + 0] = (buffer[0] & 0xF0) >> 4; |
|
|
|
tx_buffer_[tx_ptr_ + 1] = buffer[0]; |
|
|
|
tx_buffer_[tx_ptr_ + 2] = buffer[1]; |
|
|
|
tx_buffer_[tx_ptr_ + 3] = size == 3 ? buffer[2] : 0; |
|
|
|
|
|
|
|
tx_ptr_ += 4; |
|
|
|
} |
|
|
|
else // buffer[0] & 0xF0 == 0xF0 aka System common or realtime
|
|
|
|
{ |
|
|
|
if(0xF2 == buffer[0]) |
|
|
|
// three byte message
|
|
|
|
{ |
|
|
|
if(size != 3) |
|
|
|
return; // error
|
|
|
|
|
|
|
|
tx_buffer_[tx_ptr_ + 0] = 0x03; |
|
|
|
tx_buffer_[tx_ptr_ + 1] = buffer[0]; |
|
|
|
tx_buffer_[tx_ptr_ + 2] = buffer[1]; |
|
|
|
tx_buffer_[tx_ptr_ + 3] = buffer[2]; |
|
|
|
|
|
|
|
tx_ptr_ += 4; |
|
|
|
} |
|
|
|
if(0xF1 == buffer[0] || 0xF3 == buffer[0]) |
|
|
|
// two byte messages
|
|
|
|
{ |
|
|
|
if(size != 2) |
|
|
|
return; // error
|
|
|
|
|
|
|
|
tx_buffer_[tx_ptr_ + 0] = 0x02; |
|
|
|
tx_buffer_[tx_ptr_ + 1] = buffer[0]; |
|
|
|
tx_buffer_[tx_ptr_ + 2] = buffer[1]; |
|
|
|
tx_buffer_[tx_ptr_ + 3] = 0; |
|
|
|
|
|
|
|
tx_ptr_ += 4; |
|
|
|
} |
|
|
|
else if(0xF4 <= buffer[0]) |
|
|
|
// one byte message
|
|
|
|
{ |
|
|
|
if(size != 1) |
|
|
|
return; // error
|
|
|
|
|
|
|
|
tx_buffer_[tx_ptr_ + 0] = 0x05; |
|
|
|
tx_buffer_[tx_ptr_ + 1] = buffer[0]; |
|
|
|
tx_buffer_[tx_ptr_ + 2] = 0; |
|
|
|
tx_buffer_[tx_ptr_ + 3] = 0; |
|
|
|
|
|
|
|
tx_ptr_ += 4; |
|
|
|
} |
|
|
|
else // sysex
|
|
|
|
{ |
|
|
|
size_t i = 0; |
|
|
|
// Sysex messages are split up into several 4 bytes packets
|
|
|
|
// first ones use CIN 0x04
|
|
|
|
// but packet containing the SysEx stop byte use a different CIN
|
|
|
|
for(i = 0; i + 3 < size; i += 3, tx_ptr_ += 4) |
|
|
|
{ |
|
|
|
tx_buffer_[tx_ptr_] = 0x04; |
|
|
|
tx_buffer_[tx_ptr_ + 1] = buffer[i]; |
|
|
|
tx_buffer_[tx_ptr_ + 2] = buffer[i + 1]; |
|
|
|
tx_buffer_[tx_ptr_ + 3] = buffer[i + 2]; |
|
|
|
} |
|
|
|
|
|
|
|
// Fill CIN for terminating bytes
|
|
|
|
// 0x05 for 1 remaining byte
|
|
|
|
// 0x06 for 2
|
|
|
|
// 0x07 for 3
|
|
|
|
tx_buffer_[tx_ptr_] = 0x05 + (size - i - 1); |
|
|
|
tx_ptr_++; |
|
|
|
for(; i < size; ++i, ++tx_ptr_) |
|
|
|
tx_buffer_[tx_ptr_] = buffer[i]; |
|
|
|
for(; (tx_ptr_ % 4) != 0; ++tx_ptr_) |
|
|
|
tx_buffer_[tx_ptr_] = 0; |
|
|
|
} |
|
|
|
for (int i = 0; i < size; i++) { |
|
|
|
tx_buffer_[tx_ptr_] = buffer[i]; |
|
|
|
tx_ptr_++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void UsbMidiLaunchpadTransport::Impl::MidiToUsb(uint8_t* buffer, size_t size) |
|
|
|
{ |
|
|
|
// We'll assume your message starts with a status byte!
|
|
|
|
size_t status_index = 0; |
|
|
|
while(status_index < size) |
|
|
|
{ |
|
|
|
// Search for next status byte or end
|
|
|
|
size_t next_status = status_index; |
|
|
|
for(size_t j = status_index + 1; j < size; j++) |
|
|
|
{ |
|
|
|
if(buffer[j] & 0x80) |
|
|
|
{ |
|
|
|
next_status = j; |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
if(next_status == status_index) |
|
|
|
{ |
|
|
|
// Either we're at the end or it's malformed
|
|
|
|
next_status = size; |
|
|
|
} |
|
|
|
MidiToUsbSingle(buffer + status_index, next_status - status_index); |
|
|
|
status_index = next_status; |
|
|
|
} |
|
|
|
MidiToUsbSingle(buffer, size); |
|
|
|
} |
|
|
|
|
|
|
|
void UsbMidiLaunchpadTransport::Impl::Parse() |
|
|
|