implemented parser for incoming messages
This commit is contained in:
parent
e91e4209b1
commit
26c4973e12
102
src/Ble_esp32.h
102
src/Ble_esp32.h
|
|
@ -132,9 +132,9 @@ public:
|
|||
|
||||
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);
|
||||
|
||||
void onConnected(void(*fptr)()) {
|
||||
|
|
@ -219,6 +219,39 @@ bool BleMidiInterface::begin(const char* deviceName)
|
|||
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)
|
||||
{
|
||||
/*
|
||||
|
|
@ -251,64 +284,54 @@ void BleMidiInterface::receive(uint8_t *buffer, uint8_t bufferSize)
|
|||
MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved
|
||||
from other messages – except for System Exclusive messages.
|
||||
*/
|
||||
Channel channel;
|
||||
MidiType command;
|
||||
|
||||
//Pointers used to search through payload.
|
||||
uint8_t lPtr = 0;
|
||||
uint8_t rPtr = 0;
|
||||
|
||||
//lastStatus used to capture runningStatus
|
||||
uint8_t lastStatus;
|
||||
|
||||
//Decode first packet -- SHALL be "Full MIDI message"
|
||||
lPtr = 2; //Start at first MIDI status -- SHALL be "MIDI status"
|
||||
|
||||
//While statement contains incrementing pointers and breaks when buffer size exceeded.
|
||||
while (1) {
|
||||
//lastStatus used to capture runningStatus
|
||||
auto lastStatus = buffer[lPtr];
|
||||
if ( (buffer[lPtr] < 0x80) ) {
|
||||
while(1){
|
||||
lastStatus = buffer[lPtr];
|
||||
if( (buffer[lPtr] < 0x80) ){
|
||||
//Status message not present, bail
|
||||
return;
|
||||
}
|
||||
|
||||
command = getTypeFromStatusByte(lastStatus);
|
||||
channel = getChannelFromStatusByte(lastStatus);
|
||||
|
||||
//Point to next non-data byte
|
||||
rPtr = lPtr;
|
||||
while ( (buffer[rPtr + 1] < 0x80) && (rPtr < (bufferSize - 1)) ) {
|
||||
while( (buffer[rPtr + 1] < 0x80)&&(rPtr < (bufferSize - 1)) ){
|
||||
rPtr++;
|
||||
}
|
||||
//look at l and r pointers and decode by size.
|
||||
if ( rPtr - lPtr < 1 ) {
|
||||
if( rPtr - lPtr < 1 ){
|
||||
//Time code or system
|
||||
// MIDI.send(command, 0, 0, channel);
|
||||
} else if ( rPtr - lPtr < 2 ) {
|
||||
// MIDI.send(command, buffer[lPtr + 1], 0, channel);
|
||||
} else if ( rPtr - lPtr < 3 ) {
|
||||
|
||||
// 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);
|
||||
sendMIDI(lastStatus);
|
||||
} else if( rPtr - lPtr < 2 ) {
|
||||
sendMIDI(lastStatus, buffer[lPtr + 1]);
|
||||
} else if( rPtr - lPtr < 3 ) {
|
||||
sendMIDI(lastStatus, buffer[lPtr + 1], buffer[lPtr + 2]);
|
||||
} else {
|
||||
//Too much data
|
||||
//If not System Common or System Real-Time, send it as running status
|
||||
switch ( buffer[lPtr] & 0xF0 )
|
||||
switch( buffer[lPtr] & 0xF0 )
|
||||
{
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
case 0xA0:
|
||||
case 0xB0:
|
||||
case 0xE0:
|
||||
for (int i = lPtr; i < rPtr; i = i + 2) {
|
||||
// MIDI.send(command, buffer[i + 1], buffer[i + 2], channel);
|
||||
}
|
||||
case NoteOff:
|
||||
case NoteOn:
|
||||
case AfterTouchPoly:
|
||||
case ControlChange:
|
||||
case PitchBend:
|
||||
for(int i = lPtr; i < rPtr; i = i + 2)
|
||||
sendMIDI(lastStatus, buffer[i + 1], buffer[i + 2]);
|
||||
break;
|
||||
case 0xC0:
|
||||
case 0xD0:
|
||||
for (int i = lPtr; i < rPtr; i = i + 1) {
|
||||
// MIDI.send(command, buffer[i + 1], 0, channel);
|
||||
}
|
||||
case ProgramChange:
|
||||
case AfterTouchChannel:
|
||||
for(int i = lPtr; i < rPtr; i = i + 1)
|
||||
sendMIDI(lastStatus, buffer[i + 1]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -316,11 +339,12 @@ if (_noteOnCallback) // if an attached function exisist, call it here
|
|||
}
|
||||
//Point to next status
|
||||
lPtr = rPtr + 2;
|
||||
if (lPtr >= bufferSize) {
|
||||
if(lPtr >= bufferSize){
|
||||
//end of packet
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
END_BLEMIDI_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Midi {
|
|||
#define MIDI_SAMPLING_RATE_192KHZ 192000
|
||||
#define MIDI_SAMPLING_RATE_DEFAULT 10000
|
||||
|
||||
// Channel Voice Messages
|
||||
// Channel Voice Messages
|
||||
#define MIDI_STATUS_NOTE_OFF 0x80
|
||||
#define MIDI_STATUS_NOTE_ON 0x90
|
||||
#define MIDI_STATUS_POLYPHONIC_KEY_PRESSURE 0xA0
|
||||
|
|
@ -28,7 +28,7 @@ namespace Midi {
|
|||
#define MIDI_STATUS_CHANNEL_PRESSURE 0xd0
|
||||
#define MIDI_STATUS_PITCH_WHEEL_CHANGE 0xe0
|
||||
|
||||
// MIDI Channel enumeration values
|
||||
// MIDI Channel enumeration values
|
||||
#define MIDI_CHANNEL_OMNI 0x0
|
||||
#define MIDI_CHANNEL_1 0x0
|
||||
#define MIDI_CHANNEL_2 0x1
|
||||
|
|
@ -53,23 +53,23 @@ namespace Midi {
|
|||
#define MIDI_LSB( v ) (v) & 0x7F
|
||||
#define MIDI_MSB( v ) ((v)>> 7) & 0x7F
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Type definitions
|
||||
// -----------------------------------------------------------------------------
|
||||
// Type definitions
|
||||
|
||||
typedef uint8_t byte;
|
||||
typedef uint8_t byte;
|
||||
|
||||
typedef byte StatusByte;
|
||||
typedef byte DataByte;
|
||||
typedef byte Channel;
|
||||
typedef byte FilterMode;
|
||||
typedef byte StatusByte;
|
||||
typedef byte DataByte;
|
||||
typedef byte Channel;
|
||||
typedef byte FilterMode;
|
||||
|
||||
typedef byte MIDI_CHANNEL;
|
||||
typedef byte MIDI_VELOCITY;
|
||||
typedef byte MIDI_PRESSURE;
|
||||
typedef byte MIDI_CHANNEL;
|
||||
typedef byte MIDI_VELOCITY;
|
||||
typedef byte MIDI_PRESSURE;
|
||||
|
||||
/*! Enumeration of MIDI types */
|
||||
enum MidiType : uint8_t
|
||||
{
|
||||
/*! Enumeration of MIDI types */
|
||||
enum MidiType : uint8_t
|
||||
{
|
||||
InvalidType = 0x00, ///< For notifying errors
|
||||
|
||||
NoteOff = 0x80, ///< Note Off
|
||||
|
|
@ -96,11 +96,11 @@ enum MidiType : uint8_t
|
|||
Stop = 0xFC, ///< System Real Time - Stop
|
||||
ActiveSensing = 0xFE, ///< System Real Time - Active Sensing
|
||||
SystemReset = 0xFF, ///< System Real Time - System Reset
|
||||
};
|
||||
};
|
||||
|
||||
/*! Enumeration of Thru filter modes */
|
||||
struct Thru
|
||||
{
|
||||
/*! Enumeration of Thru filter modes */
|
||||
struct Thru
|
||||
{
|
||||
enum Mode
|
||||
{
|
||||
Off = 0, ///< Thru disabled (nothing passes through).
|
||||
|
|
@ -108,14 +108,14 @@ struct Thru
|
|||
SameChannel = 2, ///< Only the messages on the Input Channel will be sent back.
|
||||
DifferentChannel = 3, ///< All the messages but the ones on the Input Channel will be sent back.
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/*! \brief Enumeration of Control Change command numbers.
|
||||
/*! \brief Enumeration of Control Change command numbers.
|
||||
See the detailed controllers numbers & description here:
|
||||
http://www.somascape.org/midi/tech/spec.html#ctrlnums
|
||||
*/
|
||||
enum MidiControlChangeNumber : uint8_t
|
||||
{
|
||||
enum MidiControlChangeNumber : uint8_t
|
||||
{
|
||||
// High resolution Continuous Controllers MSB (+32 for LSB) ----------------
|
||||
BankSelect = 0,
|
||||
ModulationWheel = 1,
|
||||
|
|
@ -178,10 +178,10 @@ enum MidiControlChangeNumber : uint8_t
|
|||
OmniModeOn = 125,
|
||||
MonoModeOn = 126,
|
||||
PolyModeOn = 127
|
||||
};
|
||||
};
|
||||
|
||||
struct RPN
|
||||
{
|
||||
struct RPN
|
||||
{
|
||||
enum RegisteredParameterNumbers
|
||||
{
|
||||
PitchBendSensitivity = 0x0000,
|
||||
|
|
@ -192,12 +192,12 @@ struct RPN
|
|||
ModulationDepthRange = 0x0005,
|
||||
NullFunction = (0x7f << 7) + 0x7f,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/*! \brief Extract an enumerated MIDI type from a status byte
|
||||
/*! \brief Extract an enumerated MIDI type from a status byte
|
||||
*/
|
||||
static MidiType getTypeFromStatusByte(byte status)
|
||||
{
|
||||
static MidiType getTypeFromStatusByte(byte status)
|
||||
{
|
||||
if ((status < 0x80) ||
|
||||
(status == 0xf4) ||
|
||||
(status == 0xf5) ||
|
||||
|
|
@ -214,26 +214,26 @@ static MidiType getTypeFromStatusByte(byte status)
|
|||
}
|
||||
|
||||
return MidiType(status);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Returns type + channel
|
||||
/*! \brief Returns type + channel
|
||||
*/
|
||||
static StatusByte getStatus(MidiType type, Channel channel)
|
||||
{
|
||||
static StatusByte getStatus(MidiType type, Channel channel)
|
||||
{
|
||||
return ( type & 0xf0) | ((channel - 1) & 0x0f);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Returns channel in the range 1-16
|
||||
/*! \brief Returns channel in the range 1-16
|
||||
*/
|
||||
static Channel getChannelFromStatusByte(byte status)
|
||||
{
|
||||
static Channel getChannelFromStatusByte(byte status)
|
||||
{
|
||||
return Channel((status & 0x0f) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief check if channel is in the range 1-16
|
||||
/*! \brief check if channel is in the range 1-16
|
||||
*/
|
||||
static bool isChannelMessage(MidiType type)
|
||||
{
|
||||
static bool isChannelMessage(MidiType type)
|
||||
{
|
||||
return (type == MidiType::NoteOff ||
|
||||
type == MidiType::NoteOn ||
|
||||
type == MidiType::ControlChange ||
|
||||
|
|
@ -241,20 +241,20 @@ static bool isChannelMessage(MidiType type)
|
|||
type == MidiType::AfterTouchChannel ||
|
||||
type == MidiType::PitchBend ||
|
||||
type == MidiType::ProgramChange);
|
||||
}
|
||||
}
|
||||
|
||||
class AbstractMidiInterface
|
||||
{
|
||||
protected:
|
||||
class AbstractMidiInterface
|
||||
{
|
||||
protected:
|
||||
int _runningStatus;
|
||||
bool _thruActivated;
|
||||
|
||||
public:
|
||||
public:
|
||||
AbstractMidiInterface()
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
void (*_noteOnCallback)(byte channel, byte note, byte velocity) = NULL;
|
||||
void (*_noteOffCallback)(byte channel, byte note, byte velocity) = NULL;
|
||||
void (*_afterTouchPolyCallback)(byte channel, byte note, byte velocity) = NULL;
|
||||
|
|
@ -274,27 +274,27 @@ protected:
|
|||
void (*_activeSensingCallback)(void) = NULL;
|
||||
void (*_resetCallback)(void) = NULL;
|
||||
|
||||
public:
|
||||
public:
|
||||
// sending
|
||||
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) {
|
||||
send(MidiType::NoteOff, channel, note, velocity);
|
||||
sendChannelMessage(MidiType::NoteOff, channel, note, velocity);
|
||||
}
|
||||
|
||||
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) {
|
||||
send(MidiType::ControlChange, number, value, channel);
|
||||
sendChannelMessage(MidiType::ControlChange, number, value, channel);
|
||||
}
|
||||
|
||||
void sendPitchBend(int value, Channel channel) {
|
||||
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) {
|
||||
|
|
@ -304,15 +304,15 @@ public:
|
|||
}
|
||||
|
||||
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) {
|
||||
send(MidiType::AfterTouchChannel, pressure, 0, channel);
|
||||
sendChannelMessage(MidiType::AfterTouchChannel, pressure, 0, 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) {
|
||||
// TODO f(typeNibble, valuesNibble);
|
||||
send(MidiType::TimeCodeQuarterFrame);
|
||||
const byte data = byte((((typeNibble & 0x07) << 4) | (valuesNibble & 0x0f)));
|
||||
sendTimeCodeQuarterFrame(data);
|
||||
}
|
||||
|
||||
void sendTimeCodeQuarterFrame(DataByte data) {
|
||||
send(MidiType::TimeCodeQuarterFrame, data);
|
||||
sendSystemCommonMessage(MidiType::TimeCodeQuarterFrame, data);
|
||||
}
|
||||
|
||||
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) {
|
||||
send(MidiType::SongSelect, number);
|
||||
sendSystemCommonMessage(MidiType::SongSelect, number & 0x7f);
|
||||
}
|
||||
|
||||
void sendTuneRequest() {
|
||||
send(MidiType::TuneRequest);
|
||||
sendSystemCommonMessage(MidiType::TuneRequest);
|
||||
}
|
||||
|
||||
void sendActiveSensing() {
|
||||
send(MidiType::ActiveSensing);
|
||||
sendSystemCommonMessage(MidiType::ActiveSensing);
|
||||
}
|
||||
|
||||
|
||||
void sendStart() {
|
||||
send(MidiType::Start);
|
||||
sendRealTimeMessage(MidiType::Start);
|
||||
}
|
||||
|
||||
void sendContinue() {
|
||||
send(MidiType::Continue);
|
||||
sendRealTimeMessage(MidiType::Continue);
|
||||
}
|
||||
|
||||
void sendStop() {
|
||||
send(MidiType::Stop);
|
||||
sendRealTimeMessage(MidiType::Stop);
|
||||
}
|
||||
|
||||
void sendClock() {
|
||||
send(MidiType::Clock);
|
||||
sendRealTimeMessage(MidiType::Clock);
|
||||
}
|
||||
|
||||
void sendTick() {
|
||||
send(MidiType::Tick);
|
||||
sendRealTimeMessage(MidiType::Tick);
|
||||
}
|
||||
|
||||
void sendReset() {
|
||||
send(MidiType::SystemReset);
|
||||
sendRealTimeMessage(MidiType::SystemReset);
|
||||
}
|
||||
|
||||
|
||||
//receiving
|
||||
void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) {
|
||||
_noteOnCallback = fptr;
|
||||
}
|
||||
void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity)) {
|
||||
_noteOffCallback = fptr;
|
||||
}
|
||||
void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity)) {
|
||||
_noteOnCallback = fptr;
|
||||
}
|
||||
void setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure)) {
|
||||
_afterTouchPolyCallback = fptr;
|
||||
}
|
||||
|
|
@ -427,9 +431,10 @@ public:
|
|||
_resetCallback = fptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
protected:
|
||||
|
||||
// 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
|
||||
if (channel >= MIDI_CHANNEL_OFF ||
|
||||
|
|
@ -460,34 +465,50 @@ protected:
|
|||
}
|
||||
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
|
||||
virtual void send(MidiType type, DataByte data1)
|
||||
virtual void sendSystemCommonMessage(MidiType type, DataByte data1 = 0, DataByte data2 = 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// realTime messages
|
||||
virtual void send(MidiType type)
|
||||
// RealTime messages
|
||||
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 void read() = 0;
|
||||
|
||||
// serialize from the hardware
|
||||
virtual void read() = 0;
|
||||
|
||||
// serialize towards to hardware
|
||||
virtual void serialize(DataByte) = 0;
|
||||
virtual void serialize(DataByte, DataByte) = 0;
|
||||
virtual void serialize(DataByte, DataByte, DataByte) = 0;
|
||||
|
||||
protected:
|
||||
};
|
||||
protected:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue