Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: BLE_API BufferedSerial mbed nRF51822
Fork of MIDI-to-BLE-MIDI-bridge by
BLEMIDI_MIDI_Parser.h
- Committer:
- popcornell
- Date:
- 2016-08-09
- Revision:
- 0:244f1d0a3810
File content as of revision 0:244f1d0a3810:
/*
* Copyright (c) 2014 Matthias Frick
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#ifndef ____BLE_MIDI_Parser__
#define ____BLE_MIDI_Parser__
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
const int kMaxBufferSize=256; // max lenght for sysex buffer size
uint8_t midiBuffer[3];
uint8_t sysExBuffer[kMaxBufferSize];
uint8_t alterSysExBuffer[kMaxBufferSize];
int midiBufferPtr ; // int midiBufferPtr = 0;
int sysExRecBufferPtr ; // int sysExRecBufferPtr = 0;
int sysExBufferPtr ; // int sysExBufferPtr = 0;
// MIDI event messages, state & stamps
int midiEventKind;
int midiEventNote;
int midiEventVelocity;
int midiState; //int midiState = MIDI_STATE_TIMESTAMP;
int timestamp;
bool useTimestamp ; // bool useTimestamp = true;
int lastTimestamp;
long lastTimestampRecorded ; // long lastTimestampRecorded = 0;
int zeroTimestampCount; // int zeroTimestampCount = 0;
//Receiver *midiRecv;
////////////////////////////////////
static int MIDI_STATE_TIMESTAMP = 0;
static int MIDI_STATE_WAIT = 1;
static int MIDI_STATE_SIGNAL_2BYTES_2 = 21;
static int MIDI_STATE_SIGNAL_3BYTES_2 = 31;
static int MIDI_STATE_SIGNAL_3BYTES_3 = 32;
static int MIDI_STATE_SIGNAL_SYSEX = 41;
// for Timestamp
static int MAX_TIMESTAMP = 8192;
static int BUFFER_LENGTH_MILLIS = 10;
// for RPN/NRPN messages
static int PARAMETER_MODE_NONE = 0;
static int PARAMETER_MODE_RPN = 1;
static int PARAMETER_MODE_NRPN = 2;
int parameterMode = 0;
int parameterNumber = 0x3fff;
int parameterValue = 0x3fff;
void addByteToMidiBuffer(uint8_t midiEvent)
{
midiBuffer[midiBufferPtr] = midiEvent;
midiBufferPtr++;
}
void addByteToSysExBuffer(uint8_t midiEvent)
{
sysExBuffer[sysExBufferPtr] = midiEvent;
sysExBufferPtr++;
}
uint8_t replaceLastByteInSysExBuffer(uint8_t midiEvent)
{
sysExBufferPtr--;
uint8_t lastEvt = sysExBuffer[sysExBufferPtr];
sysExBuffer[sysExBufferPtr] = midiEvent;
sysExBufferPtr++;
return lastEvt;
}
void sendSysex()
{
for(int i = 0 ; i<=sysExBufferPtr; i++ ) { // send sysex message on the UART
UART.putc(sysExBuffer[i]) ;
}
//midiRecv->SendSysEx(sysExBuffer, sysExBufferPtr, 0);
}
void createSysExRecovery()
{
sysExRecBufferPtr = sysExBufferPtr;
memcpy(alterSysExBuffer, sysExBuffer, sysExBufferPtr);
}
void sendSysexRecovery()
{
for(int i = 0 ; i<=sysExRecBufferPtr; i++ ) {
UART.putc(alterSysExBuffer[i]) ;
}
// midiRecv->SendSysEx(alterSysExBuffer, sysExRecBufferPtr, 0);
}
uint8_t replaceLastByteInRecoveryBuffer(uint8_t midiEvent)
{
sysExRecBufferPtr--;
uint8_t lastEvt = alterSysExBuffer[sysExRecBufferPtr];
alterSysExBuffer[sysExRecBufferPtr] = midiEvent;
sysExRecBufferPtr++;
return lastEvt;
}
void addByteToRecoveryBuffer(uint8_t midiEvent)
{
alterSysExBuffer[sysExRecBufferPtr] = midiEvent;
sysExRecBufferPtr++;
}
void resetMidiBuffer()
{
memset(&midiBuffer[0], 0, sizeof(midiBuffer));
midiBufferPtr = 0;
}
void resetSysExBuffer()
{
memset(&sysExBuffer[0], 0, kMaxBufferSize);
sysExBufferPtr = 0;
}
void resetRecoveryBuffer()
{
memset(&alterSysExBuffer[0], 0, sizeof(alterSysExBuffer));
sysExRecBufferPtr = 0;
}
void sendMidi(uint8_t size) // send MIDI Message on the UART
{
for(int i = 0 ; i<=size ; i++ ) {
UART.putc(midiBuffer[i]) ;
}
}
void parseMidiEvent(uint8_t header, const uint8_t event)
{
uint8_t midiEvent = event & 0xff;
// printf((char*)midiEvent);
if (midiState == MIDI_STATE_TIMESTAMP)
{
// printf("Timestamp");
if ((midiEvent & 0x80) == 0)
{
// running status
midiState = MIDI_STATE_WAIT;
}
if (midiEvent == 0xf7)
{
// make sure this is the end of sysex
// and send alternative recovery stream
if (sysExRecBufferPtr > 0)
{
uint8_t removed = replaceLastByteInRecoveryBuffer(midiEvent);
sendSysexRecovery();
resetRecoveryBuffer();
}
midiState = MIDI_STATE_TIMESTAMP;
return;
}
else
{
// reset alternative sysex stream
resetRecoveryBuffer();
}
} // end of timestamp
if (midiState == MIDI_STATE_TIMESTAMP)
{
timestamp = ((header & 0x3f) << 7) | (midiEvent & 0x7f);
midiState = MIDI_STATE_WAIT;
}
else if (midiState == MIDI_STATE_WAIT)
{
switch (midiEvent & 0xf0) {
case 0xf0: {
switch (midiEvent) {
case 0xf0:
resetRecoveryBuffer();
resetSysExBuffer();
addByteToSysExBuffer(midiEvent);
midiState = MIDI_STATE_SIGNAL_SYSEX;
break;
case 0xf1:
case 0xf3:
// 0xf1 MIDI Time Code Quarter Frame. : 2bytes
// 0xf3 Song Select. : 2bytes
midiEventKind = midiEvent;
addByteToMidiBuffer(midiEvent);
midiState = MIDI_STATE_SIGNAL_2BYTES_2;
break;
case 0xf2:
// 0xf2 Song Position Pointer. : 3bytes
midiEventKind = midiEvent;
addByteToMidiBuffer(midiEvent);
midiState = MIDI_STATE_SIGNAL_3BYTES_2;
break;
case 0xf6:
// 0xf6 Tune Request : 1byte
addByteToMidiBuffer(midiEvent);
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xf8:
// 0xf8 Timing Clock : 1byte
//#pragma mark send timeclock // no on mbed OS
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xfa:
// 0xfa Start : 1byte
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xfb:
// 0xfb Continue : 1byte
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xfc:
// 0xfc Stop : 1byte
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xfe:
// 0xfe Active Sensing : 1byte
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xff:
// 0xff Reset : 1byte
midiState = MIDI_STATE_TIMESTAMP;
break;
default:
break;
}
}
break;
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
// 3bytes pattern
midiEventKind = midiEvent;
midiState = MIDI_STATE_SIGNAL_3BYTES_2;
break;
case 0xc0: // program change
case 0xd0: // channel after-touch
// 2bytes pattern
midiEventKind = midiEvent;
midiState = MIDI_STATE_SIGNAL_2BYTES_2;
break;
default:
// 0x00 - 0x70: running status
if ((midiEventKind & 0xf0) != 0xf0) {
// previous event kind is multi-bytes pattern
midiEventNote = midiEvent;
midiState = MIDI_STATE_SIGNAL_3BYTES_3;
}
break;
}
}
else if (midiState == MIDI_STATE_SIGNAL_2BYTES_2)
{
switch (midiEventKind & 0xf0)
{
// 2bytes pattern
case 0xc0: // program change
midiEventNote = midiEvent;
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xd0: // channel after-touch
midiEventNote = midiEvent;
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xf0:
{
switch (midiEventKind)
{
case 0xf1:
// 0xf1 MIDI Time Code Quarter Frame. : 2bytes
midiEventNote = midiEvent;
addByteToMidiBuffer(midiEventNote);
sendMidi(2);
resetMidiBuffer();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xf3:
// 0xf3 Song Select. : 2bytes
midiEventNote = midiEvent;
addByteToMidiBuffer(midiEventNote);
sendMidi(2);
resetMidiBuffer();
midiState = MIDI_STATE_TIMESTAMP;
break;
default:
// illegal state
midiState = MIDI_STATE_TIMESTAMP;
break;
}
}
break;
default:
// illegal state
midiState = MIDI_STATE_TIMESTAMP;
break;
}
}
else if (midiState == MIDI_STATE_SIGNAL_3BYTES_2)
{
switch (midiEventKind & 0xf0)
{
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
case 0xf0:
// 3bytes pattern
midiEventNote = midiEvent;
midiState = MIDI_STATE_SIGNAL_3BYTES_3;
break;
default:
// illegal state
midiState = MIDI_STATE_TIMESTAMP;
break;
}
}
else if (midiState == MIDI_STATE_SIGNAL_3BYTES_3)
{
switch (midiEventKind & 0xf0)
{
// 3bytes pattern
case 0x80: // note off
midiEventVelocity = midiEvent;
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(midiEventNote);
addByteToMidiBuffer(midiEventVelocity);
sendMidi(3);
resetMidiBuffer();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0x90: // note on
midiEventVelocity = midiEvent;
//timeToWait = calculateTimeToWait(timestamp);
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(midiEventNote);
addByteToMidiBuffer(midiEventVelocity);
sendMidi(3);
resetMidiBuffer();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xa0: // control polyphonic key pressure
midiEventVelocity = midiEvent;
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(midiEventNote);
addByteToMidiBuffer(midiEventVelocity);
sendMidi(3);
resetMidiBuffer();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xb0: // control change
midiEventVelocity = midiEvent;
switch (midiEventNote & 0x7f)
{
case 98:
// NRPN LSB
parameterNumber &= 0x3f80;
parameterNumber |= midiEventVelocity & 0x7f;
parameterMode = PARAMETER_MODE_NRPN;
break;
case 99:
// NRPN MSB
parameterNumber &= 0x007f;
parameterNumber |= (midiEventVelocity & 0x7f) << 7;
parameterMode = PARAMETER_MODE_NRPN;
break;
case 100:
// RPN LSB
parameterNumber &= 0x3f80;
parameterNumber |= midiEventVelocity & 0x7f;
parameterMode = PARAMETER_MODE_RPN;
break;
case 101:
// RPN MSB
parameterNumber &= 0x007f;
parameterNumber |= (midiEventVelocity & 0x7f) << 7;
parameterMode = PARAMETER_MODE_RPN;
break;
case 38:
// data LSB
parameterValue &= 0x3f80;
parameterValue |= midiEventVelocity & 0x7f;
if (parameterNumber != 0x3fff) {
if (parameterMode == PARAMETER_MODE_RPN)
{
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(parameterNumber);
addByteToMidiBuffer(parameterValue);
sendMidi(3);
resetMidiBuffer();
}
else if (parameterMode == PARAMETER_MODE_NRPN)
{
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(parameterNumber);
addByteToMidiBuffer(parameterValue);
sendMidi(3);
resetMidiBuffer();
}
}
break;
case 6:
// data MSB
parameterValue &= 0x007f;
parameterValue |= (midiEventVelocity & 0x7f) << 7;
if (parameterNumber != 0x3fff)
{
if (parameterMode == PARAMETER_MODE_RPN)
{
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(parameterNumber);
addByteToMidiBuffer(parameterValue);
sendMidi(3);
resetMidiBuffer();
}
else if (parameterMode == PARAMETER_MODE_NRPN)
{
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(parameterNumber);
addByteToMidiBuffer(parameterValue);
sendMidi(3);
resetMidiBuffer();
}
}
break;
default:
// do nothing
break;
}
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(midiEventNote);
addByteToMidiBuffer(midiEventVelocity);
sendMidi(3);
resetMidiBuffer();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xe0: // pitch bend
midiEventVelocity = midiEvent;
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(midiEventNote);
addByteToMidiBuffer(midiEventVelocity);
sendMidi(3);
resetMidiBuffer();
midiState = MIDI_STATE_TIMESTAMP;
break;
case 0xf0: // Song Position Pointer.
midiEventVelocity = midiEvent;
addByteToMidiBuffer(midiEventKind);
addByteToMidiBuffer(midiEventNote);
addByteToMidiBuffer(midiEventVelocity);
sendMidi(3);
resetMidiBuffer();
midiState = MIDI_STATE_TIMESTAMP;
break;
default:
// illegal state
midiState = MIDI_STATE_TIMESTAMP;
break;
}
}
else if (midiState == MIDI_STATE_SIGNAL_SYSEX)
{
if (midiEvent == 0xf7)
{
uint8_t repEvt = replaceLastByteInSysExBuffer(midiEvent);
resetRecoveryBuffer();
createSysExRecovery();
replaceLastByteInRecoveryBuffer(repEvt);
addByteToRecoveryBuffer(midiEvent);
sendSysex();
resetSysExBuffer();
midiState = MIDI_STATE_TIMESTAMP;
}
else
{
addByteToSysExBuffer(midiEvent);
}
}
}
#endif /* defined(____BLEParser__) */
