Update the USBMIDI class

Update the USB class USBMIDI from the unsupported folder.
pull/9768/head
Russ Butler 2018-05-10 22:39:50 -05:00 committed by Russ Butler
parent 9f94e4651a
commit 3dcd345efe
3 changed files with 1055 additions and 0 deletions

View File

@ -0,0 +1,474 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MIDIMESSAGE_H
#define MIDIMESSAGE_H
#include "mbed.h"
#define MAX_MIDI_MESSAGE_SIZE 256 // Max message size. SysEx can be up to 65536 but 256 should be fine for most usage
// MIDI Message Format
//
// [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ]
//
// MIDI Data Messages (Channel Specific)
//
// Message msg n m
// ---------------------------------------------
// Note Off 0x8 Key Velocity
// Note On 0x9 Key Velocity
// Polyphonic Aftertouch 0xA Key Pressure
// Control Change 0xB Controller Value
// Program Change 0xC Program -
// Channel Aftertouch 0xD Pressure -
// Pitch Wheel 0xE LSB MSB
#define CABLE_NUM (0<<4)
/** A MIDI message container */
class MIDIMessage {
public:
MIDIMessage() : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0) {}
MIDIMessage(uint8_t *buf) : data(new uint8_t[MAX_MIDI_MESSAGE_SIZE + 1]), length(0)
{
for (int i = 0; i < 4; i++) {
data[i] = buf[i];
}
}
/**
* Copy constructor
*/
MIDIMessage(const MIDIMessage &other)
{
*this = other;
}
/**
* Assignment operator
*/
MIDIMessage &operator=(const MIDIMessage &other)
{
length = other.length;
for (int i = 0; i < length; i++) {
data[i] = other.data[i];
}
return *this;
}
~MIDIMessage()
{
delete[] data;
}
/**
* Set this MIDIMessage to a raw MIDI message
*
* @param buf is a true MIDI message (not USBMidi message)
* @param buf_len size of message
*/
void from_raw(uint8_t *buf, int buf_len)
{
length = buf_len + 1;
if (length > MAX_MIDI_MESSAGE_SIZE) {
// Message is too big
length = 0;
return;
}
// first byte keeped for retro-compatibility
data[0] = CABLE_NUM | 0x08;
for (int i = 0; i < buf_len; i++) {
data[i + 1] = buf[i];
}
}
// create messages
/** Create a NoteOff message
* @param key Key ID
* @param velocity Key velocity (0-127, default = 127)
* @param channel Key channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage NoteOff(int key, int velocity = 127, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x08;
msg.data[1] = 0x80 | (channel & 0x0F);
msg.data[2] = key & 0x7F;
msg.data[3] = velocity & 0x7F;
msg.length = 4;
return msg;
}
/** Create a NoteOn message
* @param key Key ID
* @param velocity Key velocity (0-127, default = 127)
* @param channel Key channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage NoteOn(int key, int velocity = 127, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x09;
msg.data[1] = 0x90 | (channel & 0x0F);
msg.data[2] = key & 0x7F;
msg.data[3] = velocity & 0x7F;
msg.length = 4;
return msg;
}
/** Create a PolyPhonic Aftertouch message
* @param key Key ID
* @param pressure Aftertouch pressure (0-127)
* @param channel Key channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage PolyphonicAftertouch(int key, int pressure, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x0A;
msg.data[1] = 0xA0 | (channel & 0x0F);
msg.data[2] = key & 0x7F;
msg.data[3] = pressure & 0x7F;
msg.length = 4;
return msg;
}
/** Create a Control Change message
* @param control Controller ID
* @param value Controller value (0-127)
* @param channel Controller channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage ControlChange(int control, int value, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x0B;
msg.data[1] = 0xB0 | (channel & 0x0F);
msg.data[2] = control & 0x7F;
msg.data[3] = value & 0x7F;
msg.length = 4;
return msg;
}
/** Create a Program Change message
* @param program Program ID
* @param channel Channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage ProgramChange(int program, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x0C;
msg.data[1] = 0xC0 | (channel & 0x0F);
msg.data[2] = program & 0x7F;
msg.data[3] = 0x00;
msg.length = 4;
return msg;
}
/** Create a Channel Aftertouch message
* @param pressure Pressure
* @param channel Key channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage ChannelAftertouch(int pressure, int channel = 0)
{
MIDIMessage msg;
msg.data[0] = CABLE_NUM | 0x0D;
msg.data[1] = 0xD0 | (channel & 0x0F);
msg.data[2] = pressure & 0x7F;
msg.data[3] = 0x00;
msg.length = 4;
return msg;
}
/** Create a Pitch Wheel message
* @param pitch Pitch (-8192 - 8191, default = 0)
* @param channel Channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage PitchWheel(int pitch = 0, int channel = 0)
{
MIDIMessage msg;
int p = pitch + 8192; // 0 - 16383, 8192 is center
msg.data[0] = CABLE_NUM | 0x0E;
msg.data[1] = 0xE0 | (channel & 0x0F);
msg.data[2] = p & 0x7F;
msg.data[3] = (p >> 7) & 0x7F;
msg.length = 4;
return msg;
}
/** Create an All Notes Off message
* @param channel Channel (0-15, default 0)
* @returns A MIDIMessage
*/
static MIDIMessage AllNotesOff(int channel = 0)
{
return ControlChange(123, 0, channel);
}
/** Create a SysEx message
* @param data SysEx data (including 0xF0 .. 0xF7)
* @param len SysEx data length
* @returns A MIDIMessage
*/
static MIDIMessage SysEx(uint8_t *data, int len)
{
MIDIMessage msg;
msg.from_raw(data, len);
return msg;
}
// decode messages
/** MIDI Message Types */
enum MIDIMessageType {
ErrorType,
NoteOffType,
NoteOnType,
PolyphonicAftertouchType,
ControlChangeType,
ProgramChangeType,
ChannelAftertouchType,
PitchWheelType,
ResetAllControllersType,
AllNotesOffType,
SysExType
};
/** Read the message type
*
* @returns MIDIMessageType
*/
MIDIMessageType type()
{
MIDIMessageType message_type;
uint8_t min_size;
switch ((data[1] >> 4) & 0xF) {
case 0x8:
// message, channel
// key
// velocity
min_size = 3;
message_type = NoteOffType;
break;
case 0x9:
// message, channel
// key
// velocity
min_size = 3;
message_type = NoteOnType;
break;
case 0xA:
// message, channel
// key
// pressure
min_size = 3;
message_type = PolyphonicAftertouchType;
break;
case 0xB:
// message, channel
// controller
min_size = 2;
if ((data[2] & 0x7F) < 120) { // standard controllers
message_type = ControlChangeType;
} else if ((data[2] & 0x7F) == 121) {
message_type = ResetAllControllersType;
} else if ((data[2] & 0x7F) == 123) {
message_type = AllNotesOffType;
} else {
message_type = ErrorType; // unsupported atm
}
break;
case 0xC:
// message, channel
// program
min_size = 2;
message_type = ProgramChangeType;
break;
case 0xD:
// message, channel
// pressure
min_size = 2;
message_type = ChannelAftertouchType;
break;
case 0xE:
// message, channel
// pitch lsb
// pitch msb
min_size = 3;
message_type = PitchWheelType;
break;
case 0xF:
min_size = 2;
message_type = SysExType;
break;
default:
message_type = ErrorType;
break;
}
if (length < min_size) {
// too small to be a valid message
message_type = ErrorType;
}
return message_type;
}
/**
* Read the channel number
*
* @return channel number or -1 on error
*/
int channel()
{
return (data[1] & 0x0F);
}
/**
* Read the key ID
*
* @return key ID or -1 on error
*/
int key()
{
MIDIMessageType msg_type = type();
if ((msg_type != NoteOffType) &&
(msg_type != NoteOnType) &&
(msg_type != PolyphonicAftertouchType)) {
return -1;
}
return data[2] & 0x7F;
}
/**
* Read the velocity
*
* @return velocity or -1 on error
*/
int velocity()
{
MIDIMessageType msg_type = type();
if ((msg_type != NoteOffType) &&
(msg_type != NoteOnType)) {
return -1;
}
return data[3] & 0x7F;
}
/**
* Read the controller value
*
* @return controller value or -1 on error
*/
int value()
{
MIDIMessageType msg_type = type();
if ((msg_type != ControlChangeType) &&
(msg_type != ResetAllControllersType) &&
(msg_type != AllNotesOffType)) {
return -1;
}
return data[3] & 0x7F;
}
/**
* Read the aftertouch pressure
*
* @return aftertouch pressure or -1 on error
*/
int pressure()
{
MIDIMessageType msg_type = type();
if ((msg_type != PolyphonicAftertouchType) &&
(msg_type != ChannelAftertouchType)) {
return -1;
}
if (type() == PolyphonicAftertouchType) {
return data[3] & 0x7F;
} else {
return data[2] & 0x7F;
}
}
/**
* Read the controller number
*
* @return controller number or -1 on error
*/
int controller()
{
MIDIMessageType msg_type = type();
if ((msg_type != ControlChangeType) &&
(msg_type != ResetAllControllersType) &&
(msg_type != AllNotesOffType)) {
return -1;
}
return data[2] & 0x7F;
}
/**
* Read the program number
*
* @return program number or -1 on error
*/
int program()
{
MIDIMessageType msg_type = type();
if (msg_type != ProgramChangeType) {
return -1;
}
return data[2] & 0x7F;
}
/**
* Read the pitch value
*
* @return pitch value or -1 on error
*/
int pitch()
{
MIDIMessageType msg_type = type();
if (msg_type != PitchWheelType) {
return -1;
}
int p = ((data[3] & 0x7F) << 7) | (data[2] & 0x7F);
return p - 8192; // 0 - 16383, 8192 is center
}
uint8_t *data;
uint16_t length;
};
#endif

View File

@ -0,0 +1,397 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "stdint.h"
#include "USBMIDI.h"
#include "EndpointResolver.h"
#include "usb_phy_api.h"
#define FLAG_WRITE_DONE (1 << 0)
#define FLAG_DISCONNECT (1 << 1)
#define FLAG_CONNECT (1 << 2)
USBMIDI::USBMIDI(bool connect_blocking, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(get_usb_phy(), vendor_id, product_id, product_release)
{
_init();
if (connect_blocking) {
USBDevice::connect();
wait_ready();
} else {
init();
}
}
USBMIDI::USBMIDI(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release)
: USBDevice(phy, vendor_id, product_id, product_release)
{
_init();
// User or child responsible for calling connect or init
}
USBMIDI::~USBMIDI()
{
deinit();
}
void USBMIDI::_init()
{
_bulk_buf_pos = 0;
_bulk_buf_size = 0;
_data_ready = false;
_cur_data = 0;
EndpointResolver resolver(endpoint_table());
resolver.endpoint_ctrl(64);
_bulk_in = resolver.endpoint_in(USB_EP_TYPE_BULK, MaxSize);
_bulk_out = resolver.endpoint_out(USB_EP_TYPE_BULK, MaxSize);
MBED_ASSERT(resolver.valid());
}
bool USBMIDI::ready()
{
return _flags.get() & FLAG_CONNECT ? true : false;
}
void USBMIDI::wait_ready()
{
_flags.wait_any(FLAG_CONNECT, osWaitForever, false);
}
// write plain MIDIMessage that will be converted to USBMidi event packet
bool USBMIDI::write(MIDIMessage m)
{
_write_mutex.lock();
bool ret = true;
// first byte keeped for retro-compatibility
for (int p = 1; p < m.length; p += 3) {
uint8_t buf[4];
// Midi message to USBMidi event packet
buf[0] = m.data[1] >> 4;
// SysEx
if (buf[0] == 0xF) {
if ((m.length - p) > 3) {
// SysEx start or continue
buf[0] = 0x4;
} else {
switch (m.length - p) {
case 1:
// SysEx end with one byte
buf[0] = 0x5;
break;
case 2:
// SysEx end with two bytes
buf[0] = 0x6;
break;
case 3:
// SysEx end with three bytes
buf[0] = 0x7;
break;
}
}
}
buf[1] = m.data[p];
if (p + 1 < m.length) {
buf[2] = m.data[p + 1];
} else {
buf[2] = 0;
}
if (p + 2 < m.length) {
buf[3] = m.data[p + 2];
} else {
buf[3] = 0;
}
_flags.clear(FLAG_WRITE_DONE);
USBDevice::write_start(_bulk_in, buf, 4);
uint32_t flags = _flags.wait_any(FLAG_WRITE_DONE | FLAG_DISCONNECT, osWaitForever, false);
if (flags & FLAG_DISCONNECT) {
ret = false;
break;
}
USBDevice::write_finish(_bulk_in);
}
_write_mutex.unlock();
return ret;
}
bool USBMIDI::readable()
{
lock();
bool ret = _data_ready;
unlock();
return ret;
}
bool USBMIDI::read(MIDIMessage *m)
{
lock();
// Invalidate message
m->length = 0;
if (!_data_ready) {
unlock();
return false;
}
m->from_raw(_data, _cur_data);
_cur_data = 0;
_next_message();
if (!_data_ready) {
read_start(_bulk_out, _bulk_buf, MaxSize);
}
unlock();
return true;
}
void USBMIDI::attach(Callback<void()> callback)
{
lock();
_callback = callback;
unlock();
}
void USBMIDI::callback_state_change(DeviceState new_state)
{
assert_locked();
if (new_state == Configured) {
_flags.set(FLAG_CONNECT);
_flags.clear(FLAG_DISCONNECT);
} else {
_flags.set(FLAG_DISCONNECT);
_flags.clear(FLAG_CONNECT | FLAG_WRITE_DONE);
}
}
void USBMIDI::callback_request(const setup_packet_t *setup)
{
assert_locked();
RequestResult result = PassThrough;
uint8_t *data = NULL;
uint32_t size = 0;
complete_request(result, data, size);
}
void USBMIDI::callback_request_xfer_done(const setup_packet_t *setup, bool aborted)
{
assert_locked();
complete_request_xfer_done(false);
}
void USBMIDI::callback_set_configuration(uint8_t configuration)
{
assert_locked();
if (configuration == DEFAULT_CONFIGURATION) {
complete_set_configuration(false);
}
endpoint_remove_all();
endpoint_add(_bulk_in, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_in_callback);
endpoint_add(_bulk_out, MaxSize, USB_EP_TYPE_BULK, &USBMIDI::_out_callback);
read_start(_bulk_out, _bulk_buf, MaxSize);
complete_set_configuration(true);
}
void USBMIDI::callback_set_interface(uint16_t interface, uint8_t alternate)
{
assert_locked();
complete_set_interface(true);
}
const uint8_t *USBMIDI::string_iinterface_desc()
{
static const uint8_t string_iinterface_descriptor[] = {
0x0c, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iInterface - Audio
};
return string_iinterface_descriptor;
}
const uint8_t *USBMIDI::string_iproduct_desc()
{
static const uint8_t string_iproduct_descriptor[] = {
0x16, //bLength
STRING_DESCRIPTOR, //bDescriptorType 0x03
'M', 0, 'b', 0, 'e', 0, 'd', 0, ' ', 0, 'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0 //bString iProduct - Mbed Audio
};
return string_iproduct_descriptor;
}
const uint8_t *USBMIDI::configuration_desc(uint8_t index)
{
if (index != 0) {
return NULL;
}
uint8_t config_descriptor_temp[] = {
// configuration descriptor
0x09, 0x02, 0x65, 0x00, 0x02, 0x01, 0x00, 0xc0, 0x50,
// The Audio Interface Collection
0x09, 0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, // Standard AC Interface Descriptor
0x09, 0x24, 0x01, 0x00, 0x01, 0x09, 0x00, 0x01, 0x01, // Class-specific AC Interface Descriptor
0x09, 0x04, 0x01, 0x00, 0x02, 0x01, 0x03, 0x00, 0x00, // MIDIStreaming Interface Descriptors
0x07, 0x24, 0x01, 0x00, 0x01, 0x41, 0x00, // Class-Specific MS Interface Header Descriptor
// MIDI IN JACKS
0x06, 0x24, 0x02, 0x01, 0x01, 0x00,
0x06, 0x24, 0x02, 0x02, 0x02, 0x00,
// MIDI OUT JACKS
0x09, 0x24, 0x03, 0x01, 0x03, 0x01, 0x02, 0x01, 0x00,
0x09, 0x24, 0x03, 0x02, 0x06, 0x01, 0x01, 0x01, 0x00,
// OUT endpoint - Standard MS Bulk Data Endpoint Descriptor
0x09, // bLength
0x05, // bDescriptorType
_bulk_out, // bEndpointAddress
0x02, // bmAttributes
0x40, // wMaxPacketSize (LSB)
0x00, // wMaxPacketSize (MSB)
0x00, // bInterval (milliseconds)
0x00, // bRefresh
0x00, // bSynchAddress
0x05, 0x25, 0x01, 0x01, 0x01,
// IN endpoint - Standard MS Bulk Data Endpoint Descriptor
0x09, // bLength
0x05, // bDescriptorType
_bulk_in, // bEndpointAddress
0x02, // bmAttributes
0x40, // wMaxPacketSize (LSB)
0x00, // wMaxPacketSize (MSB)
0x00, // bInterval (milliseconds)
0x00, // bRefresh
0x00, // bSynchAddress
0x05, 0x25, 0x01, 0x01, 0x03,
};
MBED_ASSERT(sizeof(config_descriptor_temp) == sizeof(_config_descriptor));
memcpy(_config_descriptor, config_descriptor_temp, sizeof(config_descriptor_temp));
return _config_descriptor;
}
void USBMIDI::_in_callback(usb_ep_t ep)
{
assert_locked();
_flags.set(FLAG_WRITE_DONE);
}
void USBMIDI::_out_callback(usb_ep_t ep)
{
assert_locked();
_bulk_buf_size = read_finish(_bulk_out);
_bulk_buf_pos = 0;
if (_callback && _next_message()) {
_callback();
return;
}
read_start(_bulk_out, _bulk_buf, MaxSize);
}
bool USBMIDI::_next_message()
{
assert_locked();
bool data_ready = false;
while (_bulk_buf_pos < _bulk_buf_size) {
uint8_t data_read;
bool data_end = true;
switch (_bulk_buf[_bulk_buf_pos]) {
case 0x2:
// Two-bytes System Common Message - undefined in USBMidi 1.0
data_read = 2;
break;
case 0x4:
// SysEx start or continue
data_end = false;
data_read = 3;
break;
case 0x5:
// Single-byte System Common Message or SysEx end with one byte
data_read = 1;
break;
case 0x6:
// SysEx end with two bytes
data_read = 2;
break;
case 0xC:
// Program change
data_read = 2;
break;
case 0xD:
// Channel pressure
data_read = 2;
break;
case 0xF:
// Single byte
data_read = 1;
break;
default:
// Others three-bytes messages
data_read = 3;
break;
}
for (uint8_t j = 1; j < data_read + 1; j++) {
if (_cur_data < sizeof(_data)) {
_data[_cur_data] = _bulk_buf[_bulk_buf_pos + j];
}
_cur_data++;
}
_bulk_buf_pos += 4;
if (data_end) {
// Message is ready to be read
data_ready = true;
break;
}
}
_data_ready = data_ready;
return data_ready;
}

View File

@ -0,0 +1,184 @@
/* mbed Microcontroller Library
* Copyright (c) 2018-2018 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef USBMIDI_H
#define USBMIDI_H
/* These headers are included for child class. */
#include "USBDescriptor.h"
#include "USBDevice_Types.h"
#include "USBDevice.h"
#include "MIDIMessage.h"
#include "EventFlags.h"
#include "Mutex.h"
#include "Callback.h"
#define DEFAULT_CONFIGURATION (1)
/**
* USBMIDI example
*
* @code
* #include "mbed.h"
* #include "USBMIDI.h"
*
* USBMIDI midi;
*
* int main() {
* while (1) {
* for(int i=48; i<83; i++) { // send some messages!
* midi.write(MIDIMessage::NoteOn(i));
* wait(0.25);
* midi.write(MIDIMessage::NoteOff(i));
* wait(0.5);
* }
* }
* }
* @endcode
*/
class USBMIDI: public USBDevice {
public:
/**
* Basic constructor
*
* Construct this object optionally connecting and blocking until it is ready.
*
* @note Do not use this constructor in derived classes.
*
* @param connect_blocking true to perform a blocking connect, false to start in a disconnected state
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBMIDI(bool connect_blocking = true, uint16_t vendor_id = 0x0700, uint16_t product_id = 0x0101, uint16_t product_release = 0x0001);
/**
* Fully featured constructor
*
* Construct this object with the supplied USBPhy and parameters. The user
* this object is responsible for calling connect() or init().
*
* @note Derived classes must use this constructor and call init() or
* connect() themselves. Derived classes should also call deinit() in
* their destructor. This ensures that no interrupts can occur when the
* object is partially constructed or destroyed.
*
* @param phy USB phy to use
* @param vendor_id Your vendor_id
* @param product_id Your product_id
* @param product_release Your product_release
*/
USBMIDI(USBPhy *phy, uint16_t vendor_id, uint16_t product_id, uint16_t product_release);
/**
* Destroy this object
*
* Any classes which inherit from this class must call deinit
* before this destructor runs.
*/
virtual ~USBMIDI();
/**
* Check if this class is ready
*
* @return true if configured, false otherwise
*/
bool ready();
/**
* Block until this device is configured
*/
void wait_ready();
/**
* Send a MIDIMessage
*
* @param m The MIDIMessage to send
* @return true if the message was sent, false otherwise
*/
bool write(MIDIMessage m);
/**
* Check if a message can be read
*
* @return true if a packet can be read false otherwise
* @note USBMIDI::attach must be called to enable the receiver
*/
bool readable();
/**
* Read a message
*
* @param m The MIDIMessage to fill
* @return true if a message was read, false otherwise
*/
bool read(MIDIMessage *m);
/**
* Attach a callback for when a MIDIEvent is received
*
* @param callback code to call when a packet is received
*/
void attach(Callback<void()> callback);
protected:
virtual void callback_state_change(DeviceState new_state);
virtual void callback_request(const setup_packet_t *setup);
virtual void callback_request_xfer_done(const setup_packet_t *setup, bool aborted);
virtual void callback_set_configuration(uint8_t configuration);
virtual void callback_set_interface(uint16_t interface, uint8_t alternate);
virtual const uint8_t *string_iproduct_desc();
virtual const uint8_t *string_iinterface_desc();
virtual const uint8_t *configuration_desc(uint8_t index);
private:
static const uint32_t MaxSize = 64;
uint8_t _bulk_buf[MaxSize];
uint32_t _bulk_buf_pos;
uint32_t _bulk_buf_size;
bool _data_ready;
uint8_t _data[MAX_MIDI_MESSAGE_SIZE + 1];
uint32_t _cur_data;
rtos::EventFlags _flags;
rtos::Mutex _write_mutex;
usb_ep_t _bulk_in;
usb_ep_t _bulk_out;
uint8_t _config_descriptor[0x65];
Callback<void()> _callback;
void _init();
void _in_callback(usb_ep_t);
void _out_callback(usb_ep_t);
bool _next_message();
};
#endif