From 3dcd345efe9ee3b40dbde6389adffa81016e3b2a Mon Sep 17 00:00:00 2001 From: Russ Butler Date: Thu, 10 May 2018 22:39:50 -0500 Subject: [PATCH] Update the USBMIDI class Update the USB class USBMIDI from the unsupported folder. --- usb/device/USBMIDI/MIDIMessage.h | 474 +++++++++++++++++++++++++++++++ usb/device/USBMIDI/USBMIDI.cpp | 397 ++++++++++++++++++++++++++ usb/device/USBMIDI/USBMIDI.h | 184 ++++++++++++ 3 files changed, 1055 insertions(+) create mode 100644 usb/device/USBMIDI/MIDIMessage.h create mode 100644 usb/device/USBMIDI/USBMIDI.cpp create mode 100644 usb/device/USBMIDI/USBMIDI.h diff --git a/usb/device/USBMIDI/MIDIMessage.h b/usb/device/USBMIDI/MIDIMessage.h new file mode 100644 index 0000000000..ff16226a9d --- /dev/null +++ b/usb/device/USBMIDI/MIDIMessage.h @@ -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 diff --git a/usb/device/USBMIDI/USBMIDI.cpp b/usb/device/USBMIDI/USBMIDI.cpp new file mode 100644 index 0000000000..4fb902c5eb --- /dev/null +++ b/usb/device/USBMIDI/USBMIDI.cpp @@ -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 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; +} diff --git a/usb/device/USBMIDI/USBMIDI.h b/usb/device/USBMIDI/USBMIDI.h new file mode 100644 index 0000000000..c1d968d619 --- /dev/null +++ b/usb/device/USBMIDI/USBMIDI.h @@ -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 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 _callback; + + void _init(); + void _in_callback(usb_ep_t); + void _out_callback(usb_ep_t); + bool _next_message(); +}; + +#endif