This program enables a RedBearlab nRF51822 to be used as a "bridge device" with full bi-directional communication between MIDI through standard MIDI cables and BLE-MIDI. MIDI cables are connected to the UART. This allows for example to send MIDI data to synthesizers directly from a computer or a tablet via BLE-MIDI and vice-versa, "wires-free" . The Midi Manufacturers Association BLE-MIDI Specification is used. This project is inspired by Matthias Frick's "blidino" project which implements a USB-MIDI to BLE-MIDI bridge with the nRF51822 and is available at https://github.com/sieren/blidino. I owe to him all the BLE-MIDI to MIDI parsing part.

Dependencies:   BLE_API BufferedSerial mbed nRF51822

/media/uploads/popcornell/wp_20160713_22_30_15_rich.jpg

Video

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__) */