implemented parser for incoming messages
This commit is contained in:
parent
e91e4209b1
commit
26c4973e12
|
|
@ -132,9 +132,9 @@ public:
|
||||||
|
|
||||||
inline void read()
|
inline void read()
|
||||||
{
|
{
|
||||||
// n/a, data comes in async (see onWrite callbacks)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void sendMIDI(StatusByte, DataByte data1 = 0, DataByte data2 = 0);
|
||||||
inline void receive(uint8_t *buffer, uint8_t bufferSize);
|
inline void receive(uint8_t *buffer, uint8_t bufferSize);
|
||||||
|
|
||||||
void onConnected(void(*fptr)()) {
|
void onConnected(void(*fptr)()) {
|
||||||
|
|
@ -219,6 +219,39 @@ bool BleMidiInterface::begin(const char* deviceName)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BleMidiInterface::sendMIDI(StatusByte status, DataByte data1, DataByte data2)
|
||||||
|
{
|
||||||
|
MidiType type = getTypeFromStatusByte(status);
|
||||||
|
Channel channel = getChannelFromStatusByte(status);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case NoteOff:
|
||||||
|
if (_noteOffCallback) _noteOffCallback(channel, data1, data2);
|
||||||
|
break;
|
||||||
|
case NoteOn:
|
||||||
|
if (_noteOnCallback) _noteOnCallback(channel, data1, data2);
|
||||||
|
break;
|
||||||
|
case AfterTouchPoly:
|
||||||
|
if (_afterTouchPolyCallback) _afterTouchPolyCallback(channel, data1, data2);
|
||||||
|
break;
|
||||||
|
case ControlChange:
|
||||||
|
if (_controlChangeCallback) _controlChangeCallback(channel, data1, data2);
|
||||||
|
break;
|
||||||
|
case ProgramChange:
|
||||||
|
if (_programChangeCallback) _programChangeCallback(channel, data1);
|
||||||
|
break;
|
||||||
|
case AfterTouchChannel:
|
||||||
|
if (_afterTouchChannelCallback) _afterTouchChannelCallback(channel, data1);
|
||||||
|
break;
|
||||||
|
case PitchBend:
|
||||||
|
if (_pitchBendCallback) {
|
||||||
|
int value = (int) ((data1 & 0x7f) | ((data2 & 0x7f) << 7)) + MIDI_PITCHBEND_MIN;
|
||||||
|
_pitchBendCallback(channel, value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void BleMidiInterface::receive(uint8_t *buffer, uint8_t bufferSize)
|
void BleMidiInterface::receive(uint8_t *buffer, uint8_t bufferSize)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
@ -251,26 +284,24 @@ void BleMidiInterface::receive(uint8_t *buffer, uint8_t bufferSize)
|
||||||
MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved
|
MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved
|
||||||
from other messages – except for System Exclusive messages.
|
from other messages – except for System Exclusive messages.
|
||||||
*/
|
*/
|
||||||
Channel channel;
|
|
||||||
MidiType command;
|
|
||||||
|
|
||||||
//Pointers used to search through payload.
|
//Pointers used to search through payload.
|
||||||
uint8_t lPtr = 0;
|
uint8_t lPtr = 0;
|
||||||
uint8_t rPtr = 0;
|
uint8_t rPtr = 0;
|
||||||
|
|
||||||
|
//lastStatus used to capture runningStatus
|
||||||
|
uint8_t lastStatus;
|
||||||
|
|
||||||
//Decode first packet -- SHALL be "Full MIDI message"
|
//Decode first packet -- SHALL be "Full MIDI message"
|
||||||
lPtr = 2; //Start at first MIDI status -- SHALL be "MIDI status"
|
lPtr = 2; //Start at first MIDI status -- SHALL be "MIDI status"
|
||||||
|
|
||||||
//While statement contains incrementing pointers and breaks when buffer size exceeded.
|
//While statement contains incrementing pointers and breaks when buffer size exceeded.
|
||||||
while(1){
|
while(1){
|
||||||
//lastStatus used to capture runningStatus
|
lastStatus = buffer[lPtr];
|
||||||
auto lastStatus = buffer[lPtr];
|
|
||||||
if( (buffer[lPtr] < 0x80) ){
|
if( (buffer[lPtr] < 0x80) ){
|
||||||
//Status message not present, bail
|
//Status message not present, bail
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
command = getTypeFromStatusByte(lastStatus);
|
|
||||||
channel = getChannelFromStatusByte(lastStatus);
|
|
||||||
|
|
||||||
//Point to next non-data byte
|
//Point to next non-data byte
|
||||||
rPtr = lPtr;
|
rPtr = lPtr;
|
||||||
while( (buffer[rPtr + 1] < 0x80)&&(rPtr < (bufferSize - 1)) ){
|
while( (buffer[rPtr + 1] < 0x80)&&(rPtr < (bufferSize - 1)) ){
|
||||||
|
|
@ -279,36 +310,28 @@ void BleMidiInterface::receive(uint8_t *buffer, uint8_t bufferSize)
|
||||||
//look at l and r pointers and decode by size.
|
//look at l and r pointers and decode by size.
|
||||||
if( rPtr - lPtr < 1 ){
|
if( rPtr - lPtr < 1 ){
|
||||||
//Time code or system
|
//Time code or system
|
||||||
// MIDI.send(command, 0, 0, channel);
|
sendMIDI(lastStatus);
|
||||||
} else if( rPtr - lPtr < 2 ) {
|
} else if( rPtr - lPtr < 2 ) {
|
||||||
// MIDI.send(command, buffer[lPtr + 1], 0, channel);
|
sendMIDI(lastStatus, buffer[lPtr + 1]);
|
||||||
} else if( rPtr - lPtr < 3 ) {
|
} else if( rPtr - lPtr < 3 ) {
|
||||||
|
sendMIDI(lastStatus, buffer[lPtr + 1], buffer[lPtr + 2]);
|
||||||
// TODO: switch for type
|
|
||||||
|
|
||||||
if (_noteOnCallback) // if an attached function exisist, call it here
|
|
||||||
_noteOnCallback(0, 1, 2);
|
|
||||||
|
|
||||||
// MIDI.send(command, buffer[lPtr + 1], buffer[lPtr + 2], channel);
|
|
||||||
} else {
|
} else {
|
||||||
//Too much data
|
//Too much data
|
||||||
//If not System Common or System Real-Time, send it as running status
|
//If not System Common or System Real-Time, send it as running status
|
||||||
switch( buffer[lPtr] & 0xF0 )
|
switch( buffer[lPtr] & 0xF0 )
|
||||||
{
|
{
|
||||||
case 0x80:
|
case NoteOff:
|
||||||
case 0x90:
|
case NoteOn:
|
||||||
case 0xA0:
|
case AfterTouchPoly:
|
||||||
case 0xB0:
|
case ControlChange:
|
||||||
case 0xE0:
|
case PitchBend:
|
||||||
for (int i = lPtr; i < rPtr; i = i + 2) {
|
for(int i = lPtr; i < rPtr; i = i + 2)
|
||||||
// MIDI.send(command, buffer[i + 1], buffer[i + 2], channel);
|
sendMIDI(lastStatus, buffer[i + 1], buffer[i + 2]);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 0xC0:
|
case ProgramChange:
|
||||||
case 0xD0:
|
case AfterTouchChannel:
|
||||||
for (int i = lPtr; i < rPtr; i = i + 1) {
|
for(int i = lPtr; i < rPtr; i = i + 1)
|
||||||
// MIDI.send(command, buffer[i + 1], 0, channel);
|
sendMIDI(lastStatus, buffer[i + 1]);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
@ -321,6 +344,7 @@ if (_noteOnCallback) // if an attached function exisist, call it here
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
END_BLEMIDI_NAMESPACE
|
END_BLEMIDI_NAMESPACE
|
||||||
|
|
|
||||||
|
|
@ -277,24 +277,24 @@ protected:
|
||||||
public:
|
public:
|
||||||
// sending
|
// sending
|
||||||
void sendNoteOn(DataByte note, DataByte velocity, Channel channel) {
|
void sendNoteOn(DataByte note, DataByte velocity, Channel channel) {
|
||||||
send(MidiType::NoteOn, channel, note, velocity);
|
sendChannelMessage(MidiType::NoteOn, channel, note, velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendNoteOff(DataByte note, DataByte velocity, Channel channel) {
|
void sendNoteOff(DataByte note, DataByte velocity, Channel channel) {
|
||||||
send(MidiType::NoteOff, channel, note, velocity);
|
sendChannelMessage(MidiType::NoteOff, channel, note, velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendProgramChange(DataByte number, Channel channel) {
|
void sendProgramChange(DataByte number, Channel channel) {
|
||||||
send(MidiType::ProgramChange, number, 0, channel);
|
sendChannelMessage(MidiType::ProgramChange, number, 0, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendControlChange(DataByte number, DataByte value, Channel channel) {
|
void sendControlChange(DataByte number, DataByte value, Channel channel) {
|
||||||
send(MidiType::ControlChange, number, value, channel);
|
sendChannelMessage(MidiType::ControlChange, number, value, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendPitchBend(int value, Channel channel) {
|
void sendPitchBend(int value, Channel channel) {
|
||||||
const unsigned bend = unsigned(value - int(MIDI_PITCHBEND_MIN));
|
const unsigned bend = unsigned(value - int(MIDI_PITCHBEND_MIN));
|
||||||
send(MidiType::PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, channel);
|
sendChannelMessage(MidiType::PitchBend, (bend & 0x7f), (bend >> 7) & 0x7f, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendPitchBend(double pitchValue, Channel channel) {
|
void sendPitchBend(double pitchValue, Channel channel) {
|
||||||
|
|
@ -304,15 +304,15 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendPolyPressure(DataByte note, DataByte pressure, Channel channel) {
|
void sendPolyPressure(DataByte note, DataByte pressure, Channel channel) {
|
||||||
send(MidiType::AfterTouchPoly, note, pressure, channel);
|
sendChannelMessage(MidiType::AfterTouchPoly, note, pressure, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendAfterTouch(DataByte pressure, Channel channel) {
|
void sendAfterTouch(DataByte pressure, Channel channel) {
|
||||||
send(MidiType::AfterTouchChannel, pressure, 0, channel);
|
sendChannelMessage(MidiType::AfterTouchChannel, pressure, 0, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendAfterTouch(DataByte note, DataByte pressure, Channel channel) {
|
void sendAfterTouch(DataByte note, DataByte pressure, Channel channel) {
|
||||||
send(MidiType::AfterTouchChannel, note, pressure, channel);
|
sendChannelMessage(MidiType::AfterTouchChannel, note, pressure, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -322,62 +322,66 @@ public:
|
||||||
|
|
||||||
|
|
||||||
void sendTimeCodeQuarterFrame(DataByte typeNibble, DataByte valuesNibble) {
|
void sendTimeCodeQuarterFrame(DataByte typeNibble, DataByte valuesNibble) {
|
||||||
// TODO f(typeNibble, valuesNibble);
|
const byte data = byte((((typeNibble & 0x07) << 4) | (valuesNibble & 0x0f)));
|
||||||
send(MidiType::TimeCodeQuarterFrame);
|
sendTimeCodeQuarterFrame(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendTimeCodeQuarterFrame(DataByte data) {
|
void sendTimeCodeQuarterFrame(DataByte data) {
|
||||||
send(MidiType::TimeCodeQuarterFrame, data);
|
sendSystemCommonMessage(MidiType::TimeCodeQuarterFrame, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendSongPosition(unsigned short beats) {
|
void sendSongPosition(unsigned short beats) {
|
||||||
send(MidiType::SongPosition, beats);
|
byte data1 = beats & 0x7f;
|
||||||
|
byte data2 = (beats >> 7) & 0x7f;
|
||||||
|
|
||||||
|
sendSystemCommonMessage(MidiType::SongPosition, data1, data2);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendSongSelect(DataByte number) {
|
void sendSongSelect(DataByte number) {
|
||||||
send(MidiType::SongSelect, number);
|
sendSystemCommonMessage(MidiType::SongSelect, number & 0x7f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendTuneRequest() {
|
void sendTuneRequest() {
|
||||||
send(MidiType::TuneRequest);
|
sendSystemCommonMessage(MidiType::TuneRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendActiveSensing() {
|
void sendActiveSensing() {
|
||||||
send(MidiType::ActiveSensing);
|
sendSystemCommonMessage(MidiType::ActiveSensing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void sendStart() {
|
void sendStart() {
|
||||||
send(MidiType::Start);
|
sendRealTimeMessage(MidiType::Start);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendContinue() {
|
void sendContinue() {
|
||||||
send(MidiType::Continue);
|
sendRealTimeMessage(MidiType::Continue);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendStop() {
|
void sendStop() {
|
||||||
send(MidiType::Stop);
|
sendRealTimeMessage(MidiType::Stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendClock() {
|
void sendClock() {
|
||||||
send(MidiType::Clock);
|
sendRealTimeMessage(MidiType::Clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendTick() {
|
void sendTick() {
|
||||||
send(MidiType::Tick);
|
sendRealTimeMessage(MidiType::Tick);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sendReset() {
|
void sendReset() {
|
||||||
send(MidiType::SystemReset);
|
sendRealTimeMessage(MidiType::SystemReset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//receiving
|
//receiving
|
||||||
void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) {
|
|
||||||
_noteOnCallback = fptr;
|
|
||||||
}
|
|
||||||
void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) {
|
void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) {
|
||||||
_noteOffCallback = fptr;
|
_noteOffCallback = fptr;
|
||||||
}
|
}
|
||||||
|
void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) {
|
||||||
|
_noteOnCallback = fptr;
|
||||||
|
}
|
||||||
void setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) {
|
void setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) {
|
||||||
_afterTouchPolyCallback = fptr;
|
_afterTouchPolyCallback = fptr;
|
||||||
}
|
}
|
||||||
|
|
@ -428,8 +432,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// Channel messages
|
// Channel messages
|
||||||
virtual void send(MidiType type, DataByte data1, DataByte data2, Channel channel)
|
virtual void sendChannelMessage(MidiType type, DataByte data1, DataByte data2, Channel channel)
|
||||||
{
|
{
|
||||||
// Then test if channel is valid
|
// Then test if channel is valid
|
||||||
if (channel >= MIDI_CHANNEL_OFF ||
|
if (channel >= MIDI_CHANNEL_OFF ||
|
||||||
|
|
@ -460,27 +465,42 @@ protected:
|
||||||
}
|
}
|
||||||
else if (type >= MidiType::Clock && type <= MidiType::SystemReset)
|
else if (type >= MidiType::Clock && type <= MidiType::SystemReset)
|
||||||
{
|
{
|
||||||
send(type); // System Real-time and 1 byte.
|
sendRealTimeMessage(type); // System Real-time and 1 byte.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SystemCommon message
|
// SystemCommon message
|
||||||
virtual void send(MidiType type, DataByte data1)
|
virtual void sendSystemCommonMessage(MidiType type, DataByte data1 = 0, DataByte data2 = 0)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// realTime messages
|
// RealTime messages
|
||||||
virtual void send(MidiType type)
|
virtual void sendRealTimeMessage(MidiType type)
|
||||||
{
|
{
|
||||||
|
// Do not invalidate Running Status for real-time messages
|
||||||
|
// as they can be interleaved within any message.
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case Clock:
|
||||||
|
case Start:
|
||||||
|
case Stop:
|
||||||
|
case Continue:
|
||||||
|
case ActiveSensing:
|
||||||
|
case SystemReset:
|
||||||
|
serialize(type);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Invalid Real Time marker
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool begin(const char*) = 0;
|
virtual bool begin(const char*) = 0;
|
||||||
|
|
||||||
virtual void read() = 0;
|
|
||||||
|
|
||||||
// serialize from the hardware
|
// serialize from the hardware
|
||||||
|
virtual void read() = 0;
|
||||||
|
|
||||||
// serialize towards to hardware
|
// serialize towards to hardware
|
||||||
virtual void serialize(DataByte) = 0;
|
virtual void serialize(DataByte) = 0;
|
||||||
|
|
@ -491,3 +511,4 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue