mirror of https://github.com/ARMmbed/mbed-os.git
Update the USBMIDI class
Update the USB class USBMIDI from the unsupported folder.pull/9768/head
parent
9f94e4651a
commit
3dcd345efe
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue