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