mirror of https://github.com/ARMmbed/mbed-os.git
commit
1b9e8d686f
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
#include "mbed.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
|
// MIDI Message Format
|
||||||
//
|
//
|
||||||
// [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ]
|
// [ msg(4) | channel(4) ] [ 0 | n(7) ] [ 0 | m(7) ]
|
||||||
|
@ -49,6 +51,16 @@ public:
|
||||||
data[i] = buf[i];
|
data[i] = buf[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New constructor, buf is a true MIDI message (not USBMidi message) and buf_len true message length.
|
||||||
|
MIDIMessage(uint8_t *buf, int buf_len) {
|
||||||
|
length=buf_len+1;
|
||||||
|
// first byte keeped for retro-compatibility
|
||||||
|
data[0]=0;
|
||||||
|
|
||||||
|
for (int i = 0; i < buf_len; i++)
|
||||||
|
data[i+1] = buf[i];
|
||||||
|
}
|
||||||
|
|
||||||
// create messages
|
// create messages
|
||||||
|
|
||||||
/** Create a NoteOff message
|
/** Create a NoteOff message
|
||||||
|
@ -162,6 +174,16 @@ public:
|
||||||
return ControlChange(123, 0, channel);
|
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=MIDIMessage(data,len);
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
// decode messages
|
// decode messages
|
||||||
|
|
||||||
/** MIDI Message Types */
|
/** MIDI Message Types */
|
||||||
|
@ -174,7 +196,8 @@ public:
|
||||||
ProgramChangeType,
|
ProgramChangeType,
|
||||||
ChannelAftertouchType,
|
ChannelAftertouchType,
|
||||||
PitchWheelType,
|
PitchWheelType,
|
||||||
AllNotesOffType
|
AllNotesOffType,
|
||||||
|
SysExType
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Read the message type
|
/** Read the message type
|
||||||
|
@ -196,6 +219,7 @@ public:
|
||||||
case 0xC: return ProgramChangeType;
|
case 0xC: return ProgramChangeType;
|
||||||
case 0xD: return ChannelAftertouchType;
|
case 0xD: return ChannelAftertouchType;
|
||||||
case 0xE: return PitchWheelType;
|
case 0xE: return PitchWheelType;
|
||||||
|
case 0xF: return SysExType;
|
||||||
default: return ErrorType;
|
default: return ErrorType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +269,8 @@ public:
|
||||||
return p - 8192; // 0 - 16383, 8192 is center
|
return p - 8192; // 0 - 16383, 8192 is center
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data[4];
|
uint8_t data[MAX_MIDI_MESSAGE_SIZE+1];
|
||||||
|
uint8_t length=4;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,8 +25,49 @@ USBMIDI::USBMIDI(uint16_t vendor_id, uint16_t product_id, uint16_t product_relea
|
||||||
USBDevice::connect();
|
USBDevice::connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write plain MIDIMessage that will be converted to USBMidi event packet
|
||||||
void USBMIDI::write(MIDIMessage m) {
|
void USBMIDI::write(MIDIMessage m) {
|
||||||
USBDevice::write(EPBULK_IN, m.data, 4, MAX_PACKET_SIZE_EPBULK);
|
// 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;
|
||||||
|
|
||||||
|
USBDevice::write(EPBULK_IN, buf, 4, MAX_PACKET_SIZE_EPBULK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -34,7 +75,6 @@ void USBMIDI::attach(void (*fptr)(MIDIMessage)) {
|
||||||
midi_evt = fptr;
|
midi_evt = fptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool USBMIDI::EPBULK_OUT_callback() {
|
bool USBMIDI::EPBULK_OUT_callback() {
|
||||||
uint8_t buf[64];
|
uint8_t buf[64];
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
|
@ -42,7 +82,53 @@ bool USBMIDI::EPBULK_OUT_callback() {
|
||||||
|
|
||||||
if (midi_evt != NULL) {
|
if (midi_evt != NULL) {
|
||||||
for (uint32_t i=0; i<len; i+=4) {
|
for (uint32_t i=0; i<len; i+=4) {
|
||||||
midi_evt(MIDIMessage(buf+i));
|
uint8_t data_read;
|
||||||
|
data_end=true;
|
||||||
|
switch(buf[i]) {
|
||||||
|
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++) {
|
||||||
|
data[cur_data]=buf[i+j];
|
||||||
|
cur_data++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(data_end) {
|
||||||
|
midi_evt(MIDIMessage(data,cur_data));
|
||||||
|
cur_data=0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,8 +137,6 @@ bool USBMIDI::EPBULK_OUT_callback() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Called in ISR context
|
// Called in ISR context
|
||||||
// Set configuration. Return false if the
|
// Set configuration. Return false if the
|
||||||
// configuration is not supported.
|
// configuration is not supported.
|
||||||
|
|
|
@ -102,8 +102,11 @@ protected:
|
||||||
virtual uint8_t * configurationDesc();
|
virtual uint8_t * configurationDesc();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void (*midi_evt)(MIDIMessage);
|
uint8_t data[MAX_MIDI_MESSAGE_SIZE+1];
|
||||||
|
uint8_t cur_data=0;
|
||||||
|
bool data_end = true;
|
||||||
|
|
||||||
|
void (*midi_evt)(MIDIMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -4,7 +4,70 @@
|
||||||
USBMIDI midi;
|
USBMIDI midi;
|
||||||
Serial pc(USBTX, USBRX);
|
Serial pc(USBTX, USBRX);
|
||||||
|
|
||||||
|
// MIDI IN
|
||||||
|
void transmitMessage(MIDIMessage msg) {
|
||||||
|
switch (msg.type()) {
|
||||||
|
case MIDIMessage::NoteOnType:
|
||||||
|
wait(0.1);
|
||||||
|
midi.write(MIDIMessage::NoteOn(msg.key()));
|
||||||
|
break;
|
||||||
|
case MIDIMessage::NoteOffType:
|
||||||
|
wait(0.1);
|
||||||
|
midi.write(MIDIMessage::NoteOff(msg.key()));
|
||||||
|
break;
|
||||||
|
case MIDIMessage::ProgramChangeType:
|
||||||
|
wait(0.1);
|
||||||
|
midi.write(MIDIMessage::ProgramChange(msg.program()));
|
||||||
|
break;
|
||||||
|
case MIDIMessage::SysExType:
|
||||||
|
wait(0.1);
|
||||||
|
unsigned char tmp[64];
|
||||||
|
for(int i=0;i<msg.length-1;i++) {
|
||||||
|
tmp[i]=msg.data[i+1];
|
||||||
|
}
|
||||||
|
midi.write(MIDIMessage::SysEx(tmp,msg.length-1));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
wait(5);
|
||||||
|
// MIDI OUT
|
||||||
|
|
||||||
|
// set piano
|
||||||
|
midi.write(MIDIMessage::ProgramChange(1));
|
||||||
|
wait(0.1);
|
||||||
|
|
||||||
|
// play A
|
||||||
|
midi.write(MIDIMessage::NoteOn(21));
|
||||||
|
wait(0.1);
|
||||||
|
midi.write(MIDIMessage::NoteOff(21));
|
||||||
|
wait(0.1);
|
||||||
|
|
||||||
|
// GM reset
|
||||||
|
unsigned char gm_reset[]={0xF0,0x7E,0x7F,0x09,0x01,0xF7};
|
||||||
|
midi.write(MIDIMessage::SysEx(gm_reset,6));
|
||||||
|
wait(0.1);
|
||||||
|
|
||||||
|
// GM Master volume max
|
||||||
|
unsigned char gm_master_vol_max[]={0xF0,0x7F,0x7F,0x04,0x01,0x7F,0x7F,0xF7};
|
||||||
|
midi.write(MIDIMessage::SysEx(gm_master_vol_max,8));
|
||||||
|
wait(0.1);
|
||||||
|
|
||||||
|
// GS reset
|
||||||
|
unsigned char gs_reset[]={0xF0,0x41,0x10,0x42,0x12,0x40,0x00,0x7F,0x00,0x41,0xF7};
|
||||||
|
midi.write(MIDIMessage::SysEx(gs_reset,11));
|
||||||
|
wait(0.1);
|
||||||
|
|
||||||
|
// GS Master volume max
|
||||||
|
unsigned char gs_master_vol_max[]={0xF0,0x41,0x10,0x42,0x12,0x40,0x00,0x04,0x7F,0x3D,0xF7};
|
||||||
|
midi.write(MIDIMessage::SysEx(gs_master_vol_max,11));
|
||||||
|
wait(0.1);
|
||||||
|
|
||||||
|
midi.attach(transmitMessage);
|
||||||
|
|
||||||
while(1);
|
while(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
import time
|
||||||
|
import mido
|
||||||
|
from mido import Message
|
||||||
|
|
||||||
|
|
||||||
|
def test_midi_in(port):
|
||||||
|
expected_messages_count=0
|
||||||
|
while expected_messages_count < 7:
|
||||||
|
for message in port.iter_pending():
|
||||||
|
if message.type in ('note_on', 'note_off', 'program_change', 'sysex'):
|
||||||
|
yield message
|
||||||
|
expected_messages_count+=1
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
def test_midi_loopback(input_port):
|
||||||
|
expected_messages_count=0
|
||||||
|
while expected_messages_count < 1:
|
||||||
|
for message in input_port.iter_pending():
|
||||||
|
print('Test MIDI OUT loopback received {}'.format(message.hex()))
|
||||||
|
expected_messages_count+=1
|
||||||
|
|
||||||
|
def test_midi_out_loopback(output_port,input_port):
|
||||||
|
print("Test MIDI OUT loopback")
|
||||||
|
output_port.send(Message('program_change', program=1))
|
||||||
|
test_midi_loopback(input_port)
|
||||||
|
|
||||||
|
output_port.send(Message('note_on', note=21))
|
||||||
|
test_midi_loopback(input_port)
|
||||||
|
|
||||||
|
output_port.send(Message('note_off', note=21))
|
||||||
|
test_midi_loopback(input_port)
|
||||||
|
|
||||||
|
output_port.send(Message('sysex', data=[0x7E,0x7F,0x09,0x01]))
|
||||||
|
test_midi_loopback(input_port)
|
||||||
|
|
||||||
|
output_port.send(Message('sysex', data=[0x7F,0x7F,0x04,0x01,0x7F,0x7F]))
|
||||||
|
test_midi_loopback(input_port)
|
||||||
|
|
||||||
|
output_port.send(Message('sysex', data=[0x41,0x10,0x42,0x12,0x40,0x00,0x7F,0x00,0x41]))
|
||||||
|
test_midi_loopback(input_port)
|
||||||
|
|
||||||
|
output_port.send(Message('sysex', data=[0x41,0x10,0x42,0x12,0x40,0x00,0x04,0x7F,0x3D]))
|
||||||
|
test_midi_loopback(input_port)
|
||||||
|
|
||||||
|
portname=""
|
||||||
|
|
||||||
|
while portname=="":
|
||||||
|
print("Wait for MIDI IN plug ...")
|
||||||
|
for name in mido.get_input_names():
|
||||||
|
matchObj = re.match( r'Mbed', name)
|
||||||
|
|
||||||
|
if matchObj:
|
||||||
|
portname=name
|
||||||
|
time.sleep( 1 )
|
||||||
|
|
||||||
|
try:
|
||||||
|
input_port = mido.open_input(portname)
|
||||||
|
output_port = mido.open_output(portname)
|
||||||
|
|
||||||
|
print('Using {}'.format(input_port))
|
||||||
|
|
||||||
|
print("Test MIDI IN")
|
||||||
|
|
||||||
|
for message in test_midi_in(input_port):
|
||||||
|
print('Test MIDI IN received {}'.format(message.hex()))
|
||||||
|
|
||||||
|
test_midi_out_loopback(output_port,input_port)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
pass
|
Loading…
Reference in New Issue