renamed BLEMIDI to BLE-MIDI
This commit is contained in:
parent
e2e97d17e6
commit
7dec0b1165
|
|
@ -1,16 +1,15 @@
|
||||||
#define DEBUG 4
|
#define DEBUG 4
|
||||||
#include <midi_bleTransport.h>
|
#include <BLE-MIDI.h>
|
||||||
#include <Ble_esp32.h>
|
#include <hardware/BLE-MIDI_ESP32.h>
|
||||||
|
|
||||||
|
BLEMIDI_CREATE_DEFAULT_ESP32_INSTANCE()
|
||||||
typedef BLEMIDI_NAMESPACE::BleMidiTransport<BLEMIDI_NAMESPACE::BluetoothEsp32> bleMIDI_t;
|
|
||||||
bleMIDI_t bm("Huzzah BLE MIDI");
|
|
||||||
MIDI_NAMESPACE::MidiInterface<bleMIDI_t> MIDI((bleMIDI_t &)bm);
|
|
||||||
|
|
||||||
USING_NAMESPACE_BLEMIDI
|
USING_NAMESPACE_BLEMIDI
|
||||||
|
|
||||||
unsigned long t0 = millis();
|
unsigned long t0 = millis();
|
||||||
|
#ifdef ESP32
|
||||||
bool isConnected = false;
|
bool isConnected = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
#include "BLE-MIDI.h"
|
||||||
|
|
@ -0,0 +1,314 @@
|
||||||
|
/*!
|
||||||
|
* @file BLEMIDI.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "utility/Logging.h"
|
||||||
|
|
||||||
|
#include <MIDI.h>
|
||||||
|
using namespace MIDI_NAMESPACE;
|
||||||
|
|
||||||
|
#include "BLE-MIDI_Settings.h"
|
||||||
|
#include "BLE-MIDI_Defs.h"
|
||||||
|
#include "BLE-MIDI_Namespace.h"
|
||||||
|
|
||||||
|
BEGIN_BLEMIDI_NAMESPACE
|
||||||
|
|
||||||
|
template<class T, class _Settings = DefaultSettings>
|
||||||
|
class BLEMIDI
|
||||||
|
{
|
||||||
|
typedef _Settings Settings;
|
||||||
|
|
||||||
|
private:
|
||||||
|
byte mRxBuffer[Settings::MaxBufferSize];
|
||||||
|
unsigned mRxIndex = 0;
|
||||||
|
|
||||||
|
byte mTxBuffer[Settings::MaxBufferSize];
|
||||||
|
unsigned mTxIndex = 0;
|
||||||
|
|
||||||
|
char mDeviceName[24];
|
||||||
|
|
||||||
|
private:
|
||||||
|
T mBleClass;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BLEMIDI(const char* deviceName)
|
||||||
|
{
|
||||||
|
strncpy(mDeviceName, deviceName, 24);
|
||||||
|
|
||||||
|
mRxIndex = 0;
|
||||||
|
mTxIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin(MIDI_NAMESPACE::Channel inChannel = 1)
|
||||||
|
{
|
||||||
|
mBleClass.begin(mDeviceName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool beginTransmission(MidiType)
|
||||||
|
{
|
||||||
|
getMidiTimestamp(&mTxBuffer[0], &mTxBuffer[1]);
|
||||||
|
mTxIndex = 2;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(byte inData)
|
||||||
|
{
|
||||||
|
// check for size! SysEx!!!
|
||||||
|
if (false)
|
||||||
|
{
|
||||||
|
// should only happen from SysEx!
|
||||||
|
// if we approach the end of the buffer, chop-up in segments until
|
||||||
|
// we reach F7 (end of SysEx)
|
||||||
|
}
|
||||||
|
|
||||||
|
mTxBuffer[mTxIndex++] = inData;
|
||||||
|
}
|
||||||
|
|
||||||
|
void endTransmission()
|
||||||
|
{
|
||||||
|
mBleClass.write(mTxBuffer, mTxIndex);
|
||||||
|
mTxIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned available()
|
||||||
|
{
|
||||||
|
return mRxIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte read()
|
||||||
|
{
|
||||||
|
return mRxBuffer[--mRxIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void reverse(byte arr[], int n)
|
||||||
|
{
|
||||||
|
for (int low = 0, high = n - 1; low < high; low++, high--)
|
||||||
|
{
|
||||||
|
int temp = arr[low];
|
||||||
|
arr[low] = arr[high];
|
||||||
|
arr[high] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
The general form of a MIDI message follows:
|
||||||
|
n-byte MIDI Message
|
||||||
|
Byte 0 MIDI message Status byte, Bit 7 is Set to 1.
|
||||||
|
Bytes 1 to n-1 MIDI message Data bytes, if n > 1. Bit 7 is Set to 0
|
||||||
|
There are two types of MIDI messages that can appear in a single packet: full MIDI messages and
|
||||||
|
Running Status MIDI messages. Each is encoded differently.
|
||||||
|
A full MIDI message is simply the MIDI message with the Status byte included.
|
||||||
|
A Running Status MIDI message is a MIDI message with the Status byte omitted. Running Status
|
||||||
|
MIDI messages may only be placed in the data stream if the following criteria are met:
|
||||||
|
1. The original MIDI message is 2 bytes or greater and is not a System Common or System
|
||||||
|
Real-Time message.
|
||||||
|
2. The omitted Status byte matches the most recently preceding full MIDI message’s Status
|
||||||
|
byte within the same BLE packet.
|
||||||
|
In addition, the following rules apply with respect to Running Status:
|
||||||
|
1. A Running Status MIDI message is allowed within the packet after at least one full MIDI
|
||||||
|
message.
|
||||||
|
2. Every MIDI Status byte must be preceded by a timestamp byte. Running Status MIDI
|
||||||
|
messages may be preceded by a timestamp byte. If a Running Status MIDI message is not
|
||||||
|
preceded by a timestamp byte, the timestamp byte of the most recently preceding message
|
||||||
|
in the same packet is used.
|
||||||
|
3. System Common and System Real-Time messages do not cancel Running Status if
|
||||||
|
interspersed between Running Status MIDI messages. However, a timestamp byte must
|
||||||
|
precede the Running Status MIDI message that follows.
|
||||||
|
4. The end of a BLE packet does cancel Running Status.
|
||||||
|
In the MIDI 1.0 protocol, System Real-Time messages can be sent at any time and may be
|
||||||
|
inserted anywhere in a MIDI data stream, including between Status and Data bytes of any other
|
||||||
|
MIDI messages. In the MIDI BLE protocol, the System Real-Time messages must be deinterleaved
|
||||||
|
from other messages – except for System Exclusive messages.
|
||||||
|
*/
|
||||||
|
void receive(byte* buffer, size_t bufferSize)
|
||||||
|
{
|
||||||
|
size_t index = 0;
|
||||||
|
size_t i = 0;
|
||||||
|
|
||||||
|
N_DEBUG_PRINT("Received (");
|
||||||
|
N_DEBUG_PRINT(bufferSize);
|
||||||
|
N_DEBUG_PRINTLN(") :");
|
||||||
|
for (int j = 0; j < bufferSize; j++) {
|
||||||
|
N_DEBUG_PRINT("0x");
|
||||||
|
N_DEBUG_PRINT(buffer[j], HEX);
|
||||||
|
N_DEBUG_PRINT(" ");
|
||||||
|
}
|
||||||
|
N_DEBUG_PRINTLN();
|
||||||
|
N_DEBUG_PRINTLN();
|
||||||
|
|
||||||
|
// 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 (true)
|
||||||
|
{
|
||||||
|
lastStatus = buffer[lPtr];
|
||||||
|
if( (buffer[lPtr] < 0x80) )
|
||||||
|
break; // Status message not present, bail
|
||||||
|
|
||||||
|
// Point to next non-data byte
|
||||||
|
rPtr = lPtr;
|
||||||
|
while ((buffer[rPtr + 1] < 0x80)
|
||||||
|
&& (rPtr < (bufferSize - 1)) )
|
||||||
|
rPtr++;
|
||||||
|
|
||||||
|
// look at l and r pointers and decode by size.
|
||||||
|
if( rPtr - lPtr < 1 )
|
||||||
|
{
|
||||||
|
mRxBuffer[i++] = lastStatus;
|
||||||
|
}
|
||||||
|
else if( rPtr - lPtr < 2 ) {
|
||||||
|
mRxBuffer[i++] = lastStatus;
|
||||||
|
mRxBuffer[i++] = buffer[lPtr + 1];
|
||||||
|
}
|
||||||
|
else if( rPtr - lPtr < 3 ) {
|
||||||
|
mRxBuffer[i++] = lastStatus;
|
||||||
|
mRxBuffer[i++] = buffer[lPtr + 1];
|
||||||
|
mRxBuffer[i++] = buffer[lPtr + 2];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* //Too much data
|
||||||
|
//If not System Common or System Real-Time, send it as running status
|
||||||
|
switch( buffer[lPtr] & 0xF0 )
|
||||||
|
{
|
||||||
|
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 ProgramChange:
|
||||||
|
case AfterTouchChannel:
|
||||||
|
for(int i = lPtr; i < rPtr; i = i + 1)
|
||||||
|
sendMIDI(lastStatus, buffer[i + 1]);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Point to next status
|
||||||
|
lPtr = rPtr + 2;
|
||||||
|
|
||||||
|
if (lPtr >= bufferSize)
|
||||||
|
break; //end of packet
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
reverse(mRxBuffer, i);
|
||||||
|
|
||||||
|
N_DEBUG_PRINT("mRxBuffer (");
|
||||||
|
N_DEBUG_PRINT(i);
|
||||||
|
N_DEBUG_PRINTLN(") :");
|
||||||
|
for (int j = 0; j < i; j++) {
|
||||||
|
N_DEBUG_PRINT("0x");
|
||||||
|
N_DEBUG_PRINT(mRxBuffer[j], HEX);
|
||||||
|
N_DEBUG_PRINT(" ");
|
||||||
|
}
|
||||||
|
N_DEBUG_PRINTLN();
|
||||||
|
N_DEBUG_PRINTLN();
|
||||||
|
|
||||||
|
mRxIndex = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/*
|
||||||
|
The first byte of all BLE packets must be a header byte. This is followed by timestamp bytes and MIDI messages.
|
||||||
|
|
||||||
|
Header Byte
|
||||||
|
bit 7 Set to 1.
|
||||||
|
bit 6 Set to 0. (Reserved for future use)
|
||||||
|
bits 5-0 timestampHigh:Most significant 6 bits of timestamp information.
|
||||||
|
The header byte contains the topmost 6 bits of timing information for MIDI events in the BLE
|
||||||
|
packet. The remaining 7 bits of timing information for individual MIDI messages encoded in a
|
||||||
|
packet is expressed by timestamp bytes.
|
||||||
|
|
||||||
|
Timestamp Byte
|
||||||
|
bit 7 Set to 1.
|
||||||
|
bits 6-0 timestampLow: Least Significant 7 bits of timestamp information.
|
||||||
|
|
||||||
|
The 13-bit timestamp for the first MIDI message in a packet is calculated using 6 bits from the
|
||||||
|
header byte and 7 bits from the timestamp byte.
|
||||||
|
|
||||||
|
Timestamps are 13-bit values in milliseconds, and therefore the maximum value is 8,191 ms.
|
||||||
|
Timestamps must be issued by the sender in a monotonically increasing fashion.
|
||||||
|
timestampHigh is initially set using the lower 6 bits from the header byte while the timestampLow is
|
||||||
|
formed of the lower 7 bits from the timestamp byte. Should the timestamp value of a subsequent
|
||||||
|
MIDI message in the same packet overflow/wrap (i.e., the timestampLow is smaller than a
|
||||||
|
preceding timestampLow), the receiver is responsible for tracking this by incrementing the
|
||||||
|
timestampHigh by one (the incremented value is not transmitted, only understood as a result of the
|
||||||
|
overflow condition).
|
||||||
|
|
||||||
|
In practice, the time difference between MIDI messages in the same BLE packet should not span
|
||||||
|
more than twice the connection interval. As a result, a maximum of one overflow/wrap may occur
|
||||||
|
per BLE packet.
|
||||||
|
|
||||||
|
Timestamps are in the sender’s clock domain and are not allowed to be scheduled in the future.
|
||||||
|
Correlation between the receiver’s clock and the received timestamps must be performed to
|
||||||
|
ensure accurate rendering of MIDI messages, and is not addressed in this document.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Calculating a Timestamp
|
||||||
|
To calculate the timestamp, the built-in millis() is used.
|
||||||
|
The BLE standard only specifies 13 bits worth of millisecond data though,
|
||||||
|
so it’s bitwise anded with 0x1FFF for an ever repeating cycle of 13 bits.
|
||||||
|
This is done right after a MIDI message is detected. It’s split into a 6 upper bits, 7 lower bits,
|
||||||
|
and the MSB of both bytes are set to indicate that this is a header byte.
|
||||||
|
Both bytes are placed into the first two position of an array in preparation for a MIDI message.
|
||||||
|
*/
|
||||||
|
static void getMidiTimestamp (uint8_t *header, uint8_t *timestamp)
|
||||||
|
{
|
||||||
|
auto currentTimeStamp = millis() & 0x01FFF;
|
||||||
|
|
||||||
|
*header = ((currentTimeStamp >> 7) & 0x3F) | 0x80; // 6 bits plus MSB
|
||||||
|
*timestamp = (currentTimeStamp & 0x7F) | 0x80; // 7 bits plus MSB
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setMidiTimestamp (uint8_t header, uint8_t *timestamp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// callbacks
|
||||||
|
void(*_connectedCallback)() = nullptr;
|
||||||
|
void(*_disconnectedCallback)() = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void onConnected(void(*fptr)()) {
|
||||||
|
_connectedCallback = fptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDisconnected(void(*fptr)()) {
|
||||||
|
_disconnectedCallback = fptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
END_BLEMIDI_NAMESPACE
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BLE-MIDI_Namespace.h"
|
||||||
|
|
||||||
|
// As specified in
|
||||||
|
// Specification for MIDI over Bluetooth Low Energy (BLE-MIDI)
|
||||||
|
// Version 1.0a, NOvember 1, 2015
|
||||||
|
// 3. BLE Service and Characteristics Definitions
|
||||||
|
#define SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700"
|
||||||
|
#define CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3"
|
||||||
|
|
||||||
|
#if ARDUINO
|
||||||
|
#include <Arduino.h>
|
||||||
|
#else
|
||||||
|
#include <inttypes.h>
|
||||||
|
typedef uint8_t byte;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
BEGIN_BLEMIDI_NAMESPACE
|
||||||
|
|
||||||
|
/*! \brief Create an instance of the library
|
||||||
|
*/
|
||||||
|
#define BLEMIDI_CREATE_INSTANCE(Type, DeviceName, Name) \
|
||||||
|
typedef BLEMIDI_NAMESPACE::BLEMIDI<BLEMIDI_NAMESPACE::BLEMIDI_ESP32> BLEMIDI_t; \
|
||||||
|
BLEMIDI_t Name(DeviceName); \
|
||||||
|
MIDI_NAMESPACE::MidiInterface<BLEMIDI_t> MIDI((BLEMIDI_t &)Name);
|
||||||
|
|
||||||
|
/*! \brief Create an instance for ESP32 named <DeviceName>
|
||||||
|
*/
|
||||||
|
#define BLEMIDI_CREATE_ESP32_INSTANCE(DeviceName) \
|
||||||
|
BLEMIDI_CREATE_INSTANCE(BLEMIDI_NAMESPACE::BLEMIDI_ESP32, DeviceName, bm);
|
||||||
|
|
||||||
|
/*! \brief Create a default instance for ESP32 named BLE-MIDI
|
||||||
|
*/
|
||||||
|
#define BLEMIDI_CREATE_DEFAULT_ESP32_INSTANCE() \
|
||||||
|
BLEMIDI_CREATE_ESP32_INSTANCE("BLE-MIDI")
|
||||||
|
|
||||||
|
END_BLEMIDI_NAMESPACE
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BLE-MIDI_Namespace.h"
|
||||||
|
|
||||||
|
BEGIN_BLEMIDI_NAMESPACE
|
||||||
|
|
||||||
|
struct DefaultSettings
|
||||||
|
{
|
||||||
|
static const size_t MaxBufferSize = 64;
|
||||||
|
};
|
||||||
|
|
||||||
|
END_BLEMIDI_NAMESPACE
|
||||||
|
|
@ -8,43 +8,40 @@
|
||||||
|
|
||||||
BEGIN_BLEMIDI_NAMESPACE
|
BEGIN_BLEMIDI_NAMESPACE
|
||||||
|
|
||||||
#define SERVICE_UUID "03b80e5a-ede8-4b33-a751-6ce34ec4c700"
|
class BLEMIDI_ESP32
|
||||||
#define CHARACTERISTIC_UUID "7772e5db-3868-4112-a1a9-f2669d106bf3"
|
|
||||||
|
|
||||||
class BluetoothEsp32
|
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
BLEServer* _server = nullptr;
|
BLEServer* _server = nullptr;
|
||||||
BLEAdvertising* _advertising = nullptr;
|
BLEAdvertising* _advertising = nullptr;
|
||||||
BLECharacteristic* _characteristic = nullptr;
|
BLECharacteristic* _characteristic = nullptr;
|
||||||
|
|
||||||
BleMidiTransport<class BluetoothEsp32>* _bleMidiTransport = nullptr;
|
BLEMIDI<class BLEMIDI_ESP32>* _bleMidiTransport = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BluetoothEsp32()
|
BLEMIDI_ESP32()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool begin(const char*, BleMidiTransport<class BluetoothEsp32>*);
|
bool begin(const char*, BLEMIDI<class BLEMIDI_ESP32>*);
|
||||||
|
|
||||||
inline void write(uint8_t* data, uint8_t length)
|
void write(uint8_t* data, uint8_t length)
|
||||||
{
|
{
|
||||||
_characteristic->setValue(data, length);
|
_characteristic->setValue(data, length);
|
||||||
_characteristic->notify();
|
_characteristic->notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void receive(uint8_t* buffer, uint8_t length)
|
void receive(uint8_t* buffer, uint8_t length)
|
||||||
{
|
{
|
||||||
_bleMidiTransport->receive(buffer, length);
|
_bleMidiTransport->receive(buffer, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void connected()
|
void connected()
|
||||||
{
|
{
|
||||||
if (_bleMidiTransport->_connectedCallback)
|
if (_bleMidiTransport->_connectedCallback)
|
||||||
_bleMidiTransport->_connectedCallback();
|
_bleMidiTransport->_connectedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void disconnected()
|
void disconnected()
|
||||||
{
|
{
|
||||||
if (_bleMidiTransport->_disconnectedCallback)
|
if (_bleMidiTransport->_disconnectedCallback)
|
||||||
_bleMidiTransport->_disconnectedCallback();
|
_bleMidiTransport->_disconnectedCallback();
|
||||||
|
|
@ -53,12 +50,12 @@ public:
|
||||||
|
|
||||||
class MyServerCallbacks: public BLEServerCallbacks {
|
class MyServerCallbacks: public BLEServerCallbacks {
|
||||||
public:
|
public:
|
||||||
MyServerCallbacks(BluetoothEsp32* bluetoothEsp32)
|
MyServerCallbacks(BLEMIDI_ESP32* bluetoothEsp32)
|
||||||
: _bluetoothEsp32(bluetoothEsp32) {
|
: _bluetoothEsp32(bluetoothEsp32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BluetoothEsp32* _bluetoothEsp32 = nullptr;
|
BLEMIDI_ESP32* _bluetoothEsp32 = nullptr;
|
||||||
|
|
||||||
void onConnect(BLEServer* server) {
|
void onConnect(BLEServer* server) {
|
||||||
_bluetoothEsp32->connected();
|
_bluetoothEsp32->connected();
|
||||||
|
|
@ -71,22 +68,22 @@ protected:
|
||||||
|
|
||||||
class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {
|
class MyCharacteristicCallbacks: public BLECharacteristicCallbacks {
|
||||||
public:
|
public:
|
||||||
MyCharacteristicCallbacks(BluetoothEsp32* bluetoothEsp32)
|
MyCharacteristicCallbacks(BLEMIDI_ESP32* bluetoothEsp32)
|
||||||
: _bluetoothEsp32(bluetoothEsp32 ) {
|
: _bluetoothEsp32(bluetoothEsp32 ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BluetoothEsp32* _bluetoothEsp32 = nullptr;
|
BLEMIDI_ESP32* _bluetoothEsp32 = nullptr;
|
||||||
|
|
||||||
void onWrite(BLECharacteristic * characteristic) {
|
void onWrite(BLECharacteristic * characteristic) {
|
||||||
std::string rxValue = characteristic->getValue();
|
std::string rxValue = characteristic->getValue();
|
||||||
if (rxValue.length() > 2) {
|
if (rxValue.length() > 0) {
|
||||||
_bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length());
|
_bluetoothEsp32->receive((uint8_t *)(rxValue.c_str()), rxValue.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
bool BluetoothEsp32::begin(const char* deviceName, BleMidiTransport<class BluetoothEsp32>* bleMidiTransport)
|
bool BLEMIDI_ESP32::begin(const char* deviceName, BLEMIDI<class BLEMIDI_ESP32>* bleMidiTransport)
|
||||||
{
|
{
|
||||||
_bleMidiTransport = bleMidiTransport;
|
_bleMidiTransport = bleMidiTransport;
|
||||||
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "midi_bleNamespace.h"
|
|
||||||
|
|
||||||
#if ARDUINO
|
|
||||||
#include <Arduino.h>
|
|
||||||
#else
|
|
||||||
#include <inttypes.h>
|
|
||||||
typedef uint8_t byte;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BEGIN_BLEMIDI_NAMESPACE
|
|
||||||
|
|
||||||
#define BleBuffer_t Deque<byte, 44>
|
|
||||||
|
|
||||||
/*! \brief Create an instance of the library
|
|
||||||
*/
|
|
||||||
#define BLEMIDI_CREATE_INSTANCE(Type, Name) \
|
|
||||||
BLEMIDI_NAMESPACE::BleMidiTransport<Type> Name((Type&)SerialPort);
|
|
||||||
|
|
||||||
|
|
||||||
/*! \brief
|
|
||||||
*/
|
|
||||||
#define BLEMIDI_CREATE_DEFAULT_INSTANCE() \
|
|
||||||
BLEMIDI_CREATE_INSTANCE(bm);
|
|
||||||
|
|
||||||
END_BLEMIDI_NAMESPACE
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "midi_bleNamespace.h"
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
#include "midi_bleTransport.h"
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
/*!
|
|
||||||
* @file midi_bleTransport.h
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utility/midi_feat4_4_0/MIDI.h"
|
|
||||||
|
|
||||||
#include "utility/Logging.h"
|
|
||||||
|
|
||||||
#include "midi_bleSettings.h"
|
|
||||||
#include "midi_bleDefs.h"
|
|
||||||
|
|
||||||
#include "utility/Deque.h"
|
|
||||||
|
|
||||||
BEGIN_BLEMIDI_NAMESPACE
|
|
||||||
|
|
||||||
template<class BleClass>
|
|
||||||
class BleMidiTransport
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
byte mRxBuffer[44];
|
|
||||||
unsigned mRxIndex = 0;
|
|
||||||
|
|
||||||
byte mTxBuffer[44];
|
|
||||||
unsigned mTxIndex = 0;
|
|
||||||
|
|
||||||
char mDeviceName[24];
|
|
||||||
|
|
||||||
private:
|
|
||||||
BleClass mBleClass;
|
|
||||||
|
|
||||||
public:
|
|
||||||
BleMidiTransport(const char* deviceName)
|
|
||||||
{
|
|
||||||
strncpy(mDeviceName, deviceName, 24);
|
|
||||||
|
|
||||||
mRxIndex = 0;
|
|
||||||
mTxIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void begin(MIDI_NAMESPACE::Channel inChannel = 1)
|
|
||||||
{
|
|
||||||
mBleClass.begin(mDeviceName, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool beginTransmission()
|
|
||||||
{
|
|
||||||
getMidiTimestamp(&mTxBuffer[0], &mTxBuffer[1]);
|
|
||||||
mTxIndex = 2;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(byte inData)
|
|
||||||
{
|
|
||||||
// check for size! SysEx!!!
|
|
||||||
if (false)
|
|
||||||
{
|
|
||||||
// should only happen from SysEx!
|
|
||||||
// if we approach the end of the buffer, chop-up in segments until
|
|
||||||
// we reach F7 (end of SysEx)
|
|
||||||
}
|
|
||||||
|
|
||||||
mTxBuffer[mTxIndex++] = inData;
|
|
||||||
}
|
|
||||||
|
|
||||||
void endTransmission()
|
|
||||||
{
|
|
||||||
mBleClass.write(mTxBuffer, mTxIndex);
|
|
||||||
mTxIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned available()
|
|
||||||
{
|
|
||||||
return mRxIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
byte read()
|
|
||||||
{
|
|
||||||
return mRxBuffer[--mRxIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
void receive(uint8_t* buffer, uint8_t length)
|
|
||||||
{
|
|
||||||
// drop the first 2 bytes
|
|
||||||
|
|
||||||
for (int i = 2; i < length; i++)
|
|
||||||
mRxBuffer[length - i - 1] = buffer[i];
|
|
||||||
|
|
||||||
mRxIndex = (length - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/*
|
|
||||||
The first byte of all BLE packets must be a header byte. This is followed by timestamp bytes and MIDI messages.
|
|
||||||
Header Byte
|
|
||||||
bit 7 Set to 1.
|
|
||||||
bit 6 Set to 0. (Reserved for future use)
|
|
||||||
bits 5-0 timestampHigh:Most significant 6 bits of timestamp information.
|
|
||||||
The header byte contains the topmost 6 bits of timing information for MIDI events in the BLE
|
|
||||||
packet. The remaining 7 bits of timing information for individual MIDI messages encoded in a
|
|
||||||
packet is expressed by timestamp bytes.
|
|
||||||
Timestamp Byte
|
|
||||||
bit 7 Set to 1.
|
|
||||||
bits 6-0 timestampLow: Least Significant 7 bits of timestamp information.
|
|
||||||
The 13-bit timestamp for the first MIDI message in a packet is calculated using 6 bits from the
|
|
||||||
header byte and 7 bits from the timestamp byte.
|
|
||||||
Timestamps are 13-bit values in milliseconds, and therefore the maximum value is 8,191 ms.
|
|
||||||
Timestamps must be issued by the sender in a monotonically increasing fashion.
|
|
||||||
timestampHigh is initially set using the lower 6 bits from the header byte while the timestampLow is
|
|
||||||
formed of the lower 7 bits from the timestamp byte. Should the timestamp value of a subsequent
|
|
||||||
MIDI message in the same packet overflow/wrap (i.e., the timestampLow is smaller than a
|
|
||||||
preceding timestampLow), the receiver is responsible for tracking this by incrementing the
|
|
||||||
timestampHigh by one (the incremented value is not transmitted, only understood as a result of the
|
|
||||||
overflow condition).
|
|
||||||
In practice, the time difference between MIDI messages in the same BLE packet should not span
|
|
||||||
more than twice the connection interval. As a result, a maximum of one overflow/wrap may occur
|
|
||||||
per BLE packet.
|
|
||||||
Timestamps are in the sender’s clock domain and are not allowed to be scheduled in the future.
|
|
||||||
Correlation between the receiver’s clock and the received timestamps must be performed to
|
|
||||||
ensure accurate rendering of MIDI messages, and is not addressed in this document.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
Calculating a Timestamp
|
|
||||||
To calculate the timestamp, the built-in millis() is used.
|
|
||||||
The BLE standard only specifies 13 bits worth of millisecond data though,
|
|
||||||
so it’s bitwise anded with 0x1FFF for an ever repeating cycle of 13 bits.
|
|
||||||
This is done right after a MIDI message is detected. It’s split into a 6 upper bits, 7 lower bits,
|
|
||||||
and the MSB of both bytes are set to indicate that this is a header byte.
|
|
||||||
Both bytes are placed into the first two position of an array in preparation for a MIDI message.
|
|
||||||
*/
|
|
||||||
inline static void getMidiTimestamp (uint8_t *header, uint8_t *timestamp)
|
|
||||||
{
|
|
||||||
auto currentTimeStamp = millis() & 0x01FFF;
|
|
||||||
|
|
||||||
*header = ((currentTimeStamp >> 7) & 0x3F) | 0x80; // 6 bits plus MSB
|
|
||||||
*timestamp = (currentTimeStamp & 0x7F) | 0x80; // 7 bits plus MSB
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
// callbacks
|
|
||||||
void(*_connectedCallback)() = nullptr;
|
|
||||||
void(*_disconnectedCallback)() = nullptr;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void onConnected(void(*fptr)()) {
|
|
||||||
_connectedCallback = fptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDisconnected(void(*fptr)()) {
|
|
||||||
_disconnectedCallback = fptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
END_BLEMIDI_NAMESPACE
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef BYTE_ORDER
|
|
||||||
|
|
||||||
#ifndef BIG_ENDIAN
|
|
||||||
#define BIG_ENDIAN 4321
|
|
||||||
#endif
|
|
||||||
#ifndef LITTLE_ENDIAN
|
|
||||||
#define LITTLE_ENDIAN 1234
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TEST_LITTLE_ENDIAN (((union { unsigned x; unsigned char c; }){1}).c)
|
|
||||||
|
|
||||||
#ifdef TEST_LITTLE_ENDIAN
|
|
||||||
#define BYTE_ORDER LITTLE_ENDIAN
|
|
||||||
#else
|
|
||||||
#define BYTE_ORDER BIG_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef TEST_LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#ifndef __bswap16
|
|
||||||
#define __bswap16(x) ((uint16_t)((((uint16_t)(x)&0xff00) >> 8) | (((uint16_t)(x)&0x00ff) << 8)))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __bswap32
|
|
||||||
#define __bswap32(x) \
|
|
||||||
((uint32_t)((((uint32_t)(x)&0xff000000) >> 24) | (((uint32_t)(x)&0x00ff0000) >> 8) | \
|
|
||||||
(((uint32_t)(x)&0x0000ff00) << 8) | (((uint32_t)(x)&0x000000ff) << 24)))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __bswap64
|
|
||||||
#define __bswap64(x) \
|
|
||||||
((uint64_t)((((uint64_t)(x)&0xff00000000000000ULL) >> 56) | \
|
|
||||||
(((uint64_t)(x)&0x00ff000000000000ULL) >> 40) | \
|
|
||||||
(((uint64_t)(x)&0x0000ff0000000000ULL) >> 24) | \
|
|
||||||
(((uint64_t)(x)&0x000000ff00000000ULL) >> 8) | \
|
|
||||||
(((uint64_t)(x)&0x00000000ff000000ULL) << 8) | \
|
|
||||||
(((uint64_t)(x)&0x0000000000ff0000ULL) << 24) | \
|
|
||||||
(((uint64_t)(x)&0x000000000000ff00ULL) << 40) | \
|
|
||||||
(((uint64_t)(x)&0x00000000000000ffULL) << 56)))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
union conversionBuffer
|
|
||||||
{
|
|
||||||
uint8_t value8;
|
|
||||||
uint16_t value16;
|
|
||||||
uint32_t value32;
|
|
||||||
uint64_t value64;
|
|
||||||
byte buffer[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
#if BYTE_ORDER == LITTLE_ENDIAN
|
|
||||||
|
|
||||||
// Definitions from musl libc
|
|
||||||
#define htobe16(x) __bswap16(x)
|
|
||||||
#define be16toh(x) __bswap16(x)
|
|
||||||
#define betoh16(x) __bswap16(x)
|
|
||||||
#define htobe32(x) __bswap32(x)
|
|
||||||
#define be32toh(x) __bswap32(x)
|
|
||||||
#define betoh32(x) __bswap32(x)
|
|
||||||
#define htobe64(x) __bswap64(x)
|
|
||||||
#define be64toh(x) __bswap64(x)
|
|
||||||
#define betoh64(x) __bswap64(x)
|
|
||||||
#define htole16(x) (uint16_t)(x)
|
|
||||||
#define le16toh(x) (uint16_t)(x)
|
|
||||||
#define letoh16(x) (uint16_t)(x)
|
|
||||||
#define htole32(x) (uint32_t)(x)
|
|
||||||
#define le32toh(x) (uint32_t)(x)
|
|
||||||
#define letoh32(x) (uint32_t)(x)
|
|
||||||
#define htole64(x) (uint64_t)(x)
|
|
||||||
#define le64toh(x) (uint64_t)(x)
|
|
||||||
#define letoh64(x) (uint64_t)(x)
|
|
||||||
|
|
||||||
// From Apple Open Source Libc
|
|
||||||
#define ntohs(x) __bswap16(x)
|
|
||||||
#define htons(x) __bswap16(x)
|
|
||||||
#define ntohl(x) __bswap32(x)
|
|
||||||
#define htonl(x) __bswap32(x)
|
|
||||||
#define ntohll(x) __bswap64(x)
|
|
||||||
#define htonll(x) __bswap64(x)
|
|
||||||
|
|
||||||
#define NTOHL(x) (x) = ntohl((uint32_t)x)
|
|
||||||
#define NTOHS(x) (x) = ntohs((uint16_t)x)
|
|
||||||
#define NTOHLL(x) (x) = ntohll((uint64_t)x)
|
|
||||||
#define HTONL(x) (x) = htonl((uint32_t)x)
|
|
||||||
#define HTONS(x) (x) = htons((uint16_t)x)
|
|
||||||
#define HTONLL(x) (x) = htonll((uint64_t)x)
|
|
||||||
|
|
||||||
#else // BIG_ENDIAN
|
|
||||||
|
|
||||||
// Definitions from musl libc
|
|
||||||
|
|
||||||
#define htobe16(x) (uint16_t)(x)
|
|
||||||
#define be16toh(x) (uint16_t)(x)
|
|
||||||
#define betoh16(x) (uint16_t)(x)
|
|
||||||
#define htobe32(x) (uint32_t)(x)
|
|
||||||
#define be32toh(x) (uint32_t)(x)
|
|
||||||
#define betoh32(x) (uint32_t)(x)
|
|
||||||
#define htobe64(x) (uint64_t)(x)
|
|
||||||
#define be64toh(x) (uint64_t)(x)
|
|
||||||
#define betoh64(x) (uint64_t)(x)
|
|
||||||
#define htole16(x) __bswap16(x)
|
|
||||||
#define le16toh(x) __bswap16(x)
|
|
||||||
#define letoh16(x) __bswap16(x)
|
|
||||||
#define htole32(x) __bswap32(x)
|
|
||||||
#define le32toh(x) __bswap32(x)
|
|
||||||
#define letoh32(x) __bswap32(x)
|
|
||||||
#define htole64(x) __bswap64(x)
|
|
||||||
#define le64toh(x) __bswap64(x)
|
|
||||||
#define letoh64(x) __bswap64(x)
|
|
||||||
|
|
||||||
// From Apple Open Source libc
|
|
||||||
#define ntohl(x) ((uint32_t)(x))
|
|
||||||
#define ntohs(x) ((uint16_t)(x))
|
|
||||||
#define htonl(x) ((uint32_t)(x))
|
|
||||||
#define htons(x) ((uint16_t)(x))
|
|
||||||
#define ntohll(x) ((uint64_t)(x))
|
|
||||||
#define htonll(x) ((uint64_t)(x))
|
|
||||||
|
|
||||||
#define NTOHL(x) (x)
|
|
||||||
#define NTOHS(x) (x)
|
|
||||||
#define NTOHLL(x) (x)
|
|
||||||
#define HTONL(x) (x)
|
|
||||||
#define HTONS(x) (x)
|
|
||||||
#define HTONLL(x) (x)
|
|
||||||
|
|
||||||
|
|
||||||
void aa(uint64_t value)
|
|
||||||
{
|
|
||||||
if ( value >= 10 )
|
|
||||||
aa(value / 10);
|
|
||||||
N_DEBUG_PRINT((uint32_t)(value % 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
/*!
|
|
||||||
* @file MIDI.cpp
|
|
||||||
* Project Arduino MIDI Library
|
|
||||||
* @brief MIDI Library for the Arduino
|
|
||||||
* @author Francois Best
|
|
||||||
* @date 24/02/11
|
|
||||||
* @license MIT - Copyright (c) 2015 Francois Best
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "MIDI.h"
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
BEGIN_MIDI_NAMESPACE
|
|
||||||
|
|
||||||
/*! \brief Encode System Exclusive messages.
|
|
||||||
SysEx messages are encoded to guarantee transmission of data bytes higher than
|
|
||||||
127 without breaking the MIDI protocol. Use this static method to convert the
|
|
||||||
data you want to send.
|
|
||||||
\param inData The data to encode.
|
|
||||||
\param outSysEx The output buffer where to store the encoded message.
|
|
||||||
\param inLength The lenght of the input buffer.
|
|
||||||
\param inFlipHeaderBits True for Korg and other who store MSB in reverse order
|
|
||||||
\return The lenght of the encoded output buffer.
|
|
||||||
@see decodeSysEx
|
|
||||||
Code inspired from Ruin & Wesen's SysEx encoder/decoder - http://ruinwesen.com
|
|
||||||
*/
|
|
||||||
unsigned encodeSysEx(const byte* inData,
|
|
||||||
byte* outSysEx,
|
|
||||||
unsigned inLength,
|
|
||||||
bool inFlipHeaderBits)
|
|
||||||
{
|
|
||||||
unsigned outLength = 0; // Num bytes in output array.
|
|
||||||
byte count = 0; // Num 7bytes in a block.
|
|
||||||
outSysEx[0] = 0;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < inLength; ++i)
|
|
||||||
{
|
|
||||||
const byte data = inData[i];
|
|
||||||
const byte msb = data >> 7;
|
|
||||||
const byte body = data & 0x7f;
|
|
||||||
|
|
||||||
outSysEx[0] |= (msb << (inFlipHeaderBits ? count : (6 - count)));
|
|
||||||
outSysEx[1 + count] = body;
|
|
||||||
|
|
||||||
if (count++ == 6)
|
|
||||||
{
|
|
||||||
outSysEx += 8;
|
|
||||||
outLength += 8;
|
|
||||||
outSysEx[0] = 0;
|
|
||||||
count = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return outLength + count + (count != 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! \brief Decode System Exclusive messages.
|
|
||||||
SysEx messages are encoded to guarantee transmission of data bytes higher than
|
|
||||||
127 without breaking the MIDI protocol. Use this static method to reassemble
|
|
||||||
your received message.
|
|
||||||
\param inSysEx The SysEx data received from MIDI in.
|
|
||||||
\param outData The output buffer where to store the decrypted message.
|
|
||||||
\param inLength The lenght of the input buffer.
|
|
||||||
\param inFlipHeaderBits True for Korg and other who store MSB in reverse order
|
|
||||||
\return The lenght of the output buffer.
|
|
||||||
@see encodeSysEx @see getSysExArrayLength
|
|
||||||
Code inspired from Ruin & Wesen's SysEx encoder/decoder - http://ruinwesen.com
|
|
||||||
*/
|
|
||||||
unsigned decodeSysEx(const byte* inSysEx,
|
|
||||||
byte* outData,
|
|
||||||
unsigned inLength,
|
|
||||||
bool inFlipHeaderBits)
|
|
||||||
{
|
|
||||||
unsigned count = 0;
|
|
||||||
byte msbStorage = 0;
|
|
||||||
byte byteIndex = 0;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < inLength; ++i)
|
|
||||||
{
|
|
||||||
if ((i % 8) == 0)
|
|
||||||
{
|
|
||||||
msbStorage = inSysEx[i];
|
|
||||||
byteIndex = 6;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const byte body = inSysEx[i];
|
|
||||||
const byte shift = inFlipHeaderBits ? 6 - byteIndex : byteIndex;
|
|
||||||
const byte msb = byte(((msbStorage >> shift) & 1) << 7);
|
|
||||||
byteIndex--;
|
|
||||||
outData[count++] = msb | body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
END_MIDI_NAMESPACE
|
|
||||||
|
|
@ -1,266 +0,0 @@
|
||||||
/*!
|
|
||||||
* @file MIDI.h
|
|
||||||
* Project Arduino MIDI Library
|
|
||||||
* @brief MIDI Library for the Arduino
|
|
||||||
* @author Francois Best
|
|
||||||
* @date 24/02/11
|
|
||||||
* @license MIT - Copyright (c) 2015 Francois Best
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "midi_Defs.h"
|
|
||||||
#include "midi_Settings.h"
|
|
||||||
#include "midi_Message.h"
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
BEGIN_MIDI_NAMESPACE
|
|
||||||
|
|
||||||
/*! \brief The main class for MIDI handling.
|
|
||||||
It is templated over the type of serial port to provide abstraction from
|
|
||||||
the hardware interface, meaning you can use HardwareSerial, SoftwareSerial
|
|
||||||
or ak47's Uart classes. The only requirement is that the class implements
|
|
||||||
the begin, read, write and available methods.
|
|
||||||
*/
|
|
||||||
template<class Encoder, class _Settings = DefaultSettings>
|
|
||||||
class MidiInterface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
typedef _Settings Settings;
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline MidiInterface(Encoder&);
|
|
||||||
inline ~MidiInterface();
|
|
||||||
|
|
||||||
public:
|
|
||||||
void begin(Channel inChannel = 1);
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// MIDI Output
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline void sendNoteOn(DataByte inNoteNumber,
|
|
||||||
DataByte inVelocity,
|
|
||||||
Channel inChannel);
|
|
||||||
|
|
||||||
inline void sendNoteOff(DataByte inNoteNumber,
|
|
||||||
DataByte inVelocity,
|
|
||||||
Channel inChannel);
|
|
||||||
|
|
||||||
inline void sendProgramChange(DataByte inProgramNumber,
|
|
||||||
Channel inChannel);
|
|
||||||
|
|
||||||
inline void sendControlChange(DataByte inControlNumber,
|
|
||||||
DataByte inControlValue,
|
|
||||||
Channel inChannel);
|
|
||||||
|
|
||||||
inline void sendPitchBend(int inPitchValue, Channel inChannel);
|
|
||||||
inline void sendPitchBend(double inPitchValue, Channel inChannel);
|
|
||||||
|
|
||||||
inline void sendPolyPressure(DataByte inNoteNumber,
|
|
||||||
DataByte inPressure,
|
|
||||||
Channel inChannel) __attribute__ ((deprecated));
|
|
||||||
|
|
||||||
inline void sendAfterTouch(DataByte inPressure,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendAfterTouch(DataByte inNoteNumber,
|
|
||||||
DataByte inPressure,
|
|
||||||
Channel inChannel);
|
|
||||||
|
|
||||||
inline void sendSysEx(unsigned inLength,
|
|
||||||
const byte* inArray,
|
|
||||||
bool inArrayContainsBoundaries = false);
|
|
||||||
|
|
||||||
inline void sendTimeCodeQuarterFrame(DataByte inTypeNibble,
|
|
||||||
DataByte inValuesNibble);
|
|
||||||
inline void sendTimeCodeQuarterFrame(DataByte inData);
|
|
||||||
|
|
||||||
inline void sendSongPosition(unsigned inBeats);
|
|
||||||
inline void sendSongSelect(DataByte inSongNumber);
|
|
||||||
inline void sendTuneRequest();
|
|
||||||
inline void sendRealTime(MidiType inType);
|
|
||||||
|
|
||||||
inline void beginRpn(unsigned inNumber,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendRpnValue(unsigned inValue,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendRpnValue(byte inMsb,
|
|
||||||
byte inLsb,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendRpnIncrement(byte inAmount,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendRpnDecrement(byte inAmount,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void endRpn(Channel inChannel);
|
|
||||||
|
|
||||||
inline void beginNrpn(unsigned inNumber,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendNrpnValue(unsigned inValue,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendNrpnValue(byte inMsb,
|
|
||||||
byte inLsb,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendNrpnIncrement(byte inAmount,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void sendNrpnDecrement(byte inAmount,
|
|
||||||
Channel inChannel);
|
|
||||||
inline void endNrpn(Channel inChannel);
|
|
||||||
|
|
||||||
public:
|
|
||||||
void send(MidiType inType,
|
|
||||||
DataByte inData1,
|
|
||||||
DataByte inData2,
|
|
||||||
Channel inChannel);
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// MIDI Input
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline bool read();
|
|
||||||
inline bool read(Channel inChannel);
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline MidiType getType() const;
|
|
||||||
inline Channel getChannel() const;
|
|
||||||
inline DataByte getData1() const;
|
|
||||||
inline DataByte getData2() const;
|
|
||||||
inline const byte* getSysExArray() const;
|
|
||||||
inline unsigned getSysExArrayLength() const;
|
|
||||||
inline bool check() const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline Channel getInputChannel() const;
|
|
||||||
inline void setInputChannel(Channel inChannel);
|
|
||||||
|
|
||||||
public:
|
|
||||||
static inline MidiType getTypeFromStatusByte(byte inStatus);
|
|
||||||
static inline Channel getChannelFromStatusByte(byte inStatus);
|
|
||||||
static inline bool isChannelMessage(MidiType inType);
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// Input Callbacks
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline void setHandleNoteOff(void (*fptr)(byte channel, byte note, byte velocity));
|
|
||||||
inline void setHandleNoteOn(void (*fptr)(byte channel, byte note, byte velocity));
|
|
||||||
inline void setHandleAfterTouchPoly(void (*fptr)(byte channel, byte note, byte pressure));
|
|
||||||
inline void setHandleControlChange(void (*fptr)(byte channel, byte number, byte value));
|
|
||||||
inline void setHandleProgramChange(void (*fptr)(byte channel, byte number));
|
|
||||||
inline void setHandleAfterTouchChannel(void (*fptr)(byte channel, byte pressure));
|
|
||||||
inline void setHandlePitchBend(void (*fptr)(byte channel, int bend));
|
|
||||||
inline void setHandleSystemExclusive(void (*fptr)(byte * array, unsigned size));
|
|
||||||
inline void setHandleTimeCodeQuarterFrame(void (*fptr)(byte data));
|
|
||||||
inline void setHandleSongPosition(void (*fptr)(unsigned beats));
|
|
||||||
inline void setHandleSongSelect(void (*fptr)(byte songnumber));
|
|
||||||
inline void setHandleTuneRequest(void (*fptr)(void));
|
|
||||||
inline void setHandleClock(void (*fptr)(void));
|
|
||||||
inline void setHandleStart(void (*fptr)(void));
|
|
||||||
inline void setHandleContinue(void (*fptr)(void));
|
|
||||||
inline void setHandleStop(void (*fptr)(void));
|
|
||||||
inline void setHandleActiveSensing(void (*fptr)(void));
|
|
||||||
inline void setHandleSystemReset(void (*fptr)(void));
|
|
||||||
|
|
||||||
inline void disconnectCallbackFromType(MidiType inType);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void launchCallback();
|
|
||||||
|
|
||||||
void (*mNoteOffCallback)(byte channel, byte note, byte velocity);
|
|
||||||
void (*mNoteOnCallback)(byte channel, byte note, byte velocity);
|
|
||||||
void (*mAfterTouchPolyCallback)(byte channel, byte note, byte velocity);
|
|
||||||
void (*mControlChangeCallback)(byte channel, byte, byte);
|
|
||||||
void (*mProgramChangeCallback)(byte channel, byte);
|
|
||||||
void (*mAfterTouchChannelCallback)(byte channel, byte);
|
|
||||||
void (*mPitchBendCallback)(byte channel, int);
|
|
||||||
void (*mSystemExclusiveCallback)(byte * array, unsigned size);
|
|
||||||
void (*mTimeCodeQuarterFrameCallback)(byte data);
|
|
||||||
void (*mSongPositionCallback)(unsigned beats);
|
|
||||||
void (*mSongSelectCallback)(byte songnumber);
|
|
||||||
void (*mTuneRequestCallback)(void);
|
|
||||||
void (*mClockCallback)(void);
|
|
||||||
void (*mStartCallback)(void);
|
|
||||||
void (*mContinueCallback)(void);
|
|
||||||
void (*mStopCallback)(void);
|
|
||||||
void (*mActiveSensingCallback)(void);
|
|
||||||
void (*mSystemResetCallback)(void);
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
|
||||||
// MIDI Soft Thru
|
|
||||||
|
|
||||||
public:
|
|
||||||
inline Thru::Mode getFilterMode() const;
|
|
||||||
inline bool getThruState() const;
|
|
||||||
|
|
||||||
inline void turnThruOn(Thru::Mode inThruFilterMode = Thru::Full);
|
|
||||||
inline void turnThruOff();
|
|
||||||
inline void setThruFilterMode(Thru::Mode inThruFilterMode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void thruFilter(byte inChannel);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool parse();
|
|
||||||
inline void handleNullVelocityNoteOnAsNoteOff();
|
|
||||||
inline bool inputFilter(Channel inChannel);
|
|
||||||
inline void resetInput();
|
|
||||||
|
|
||||||
private:
|
|
||||||
typedef Message<Settings::SysExMaxSize> MidiMessage;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Encoder& mEncoder;
|
|
||||||
|
|
||||||
private:
|
|
||||||
Channel mInputChannel;
|
|
||||||
StatusByte mRunningStatus_RX;
|
|
||||||
StatusByte mRunningStatus_TX;
|
|
||||||
byte mPendingMessage[3];
|
|
||||||
unsigned mPendingMessageExpectedLenght;
|
|
||||||
unsigned mPendingMessageIndex;
|
|
||||||
unsigned mCurrentRpnNumber;
|
|
||||||
unsigned mCurrentNrpnNumber;
|
|
||||||
bool mThruActivated : 1;
|
|
||||||
Thru::Mode mThruFilterMode : 7;
|
|
||||||
MidiMessage mMessage;
|
|
||||||
|
|
||||||
unsigned long mLastMessageSentTime;
|
|
||||||
bool mSenderActiveSensingActivated;
|
|
||||||
|
|
||||||
private:
|
|
||||||
inline StatusByte getStatus(MidiType inType,
|
|
||||||
Channel inChannel) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
unsigned encodeSysEx(const byte* inData,
|
|
||||||
byte* outSysEx,
|
|
||||||
unsigned inLenght,
|
|
||||||
bool inFlipHeaderBits = false);
|
|
||||||
unsigned decodeSysEx(const byte* inSysEx,
|
|
||||||
byte* outData,
|
|
||||||
unsigned inLenght,
|
|
||||||
bool inFlipHeaderBits = false);
|
|
||||||
|
|
||||||
END_MIDI_NAMESPACE
|
|
||||||
|
|
||||||
#include "MIDI.hpp"
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,209 +0,0 @@
|
||||||
/*!
|
|
||||||
* @file midi_Defs.h
|
|
||||||
* Project Arduino MIDI Library
|
|
||||||
* @brief MIDI Library for the Arduino - Definitions
|
|
||||||
* @author Francois Best
|
|
||||||
* @date 24/02/11
|
|
||||||
* @license MIT - Copyright (c) 2015 Francois Best
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "midi_Namespace.h"
|
|
||||||
|
|
||||||
#if ARDUINO
|
|
||||||
#include <Arduino.h>
|
|
||||||
#else
|
|
||||||
#include <inttypes.h>
|
|
||||||
typedef uint8_t byte;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BEGIN_MIDI_NAMESPACE
|
|
||||||
|
|
||||||
#define MIDI_LIBRARY_VERSION 0x040400
|
|
||||||
#define MIDI_LIBRARY_VERSION_MAJOR 4
|
|
||||||
#define MIDI_LIBRARY_VERSION_MINOR 4
|
|
||||||
#define MIDI_LIBRARY_VERSION_PATCH 0
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#define MIDI_CHANNEL_OMNI 0
|
|
||||||
#define MIDI_CHANNEL_OFF 17 // and over
|
|
||||||
|
|
||||||
#define MIDI_PITCHBEND_MIN -8192
|
|
||||||
#define MIDI_PITCHBEND_MAX 8191
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Type definitions
|
|
||||||
|
|
||||||
typedef byte StatusByte;
|
|
||||||
typedef byte DataByte;
|
|
||||||
typedef byte Channel;
|
|
||||||
typedef byte FilterMode;
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*! Enumeration of MIDI types */
|
|
||||||
enum MidiType: uint8_t
|
|
||||||
{
|
|
||||||
InvalidType = 0x00, ///< For notifying errors
|
|
||||||
NoteOff = 0x80, ///< Note Off
|
|
||||||
NoteOn = 0x90, ///< Note On
|
|
||||||
AfterTouchPoly = 0xA0, ///< Polyphonic AfterTouch
|
|
||||||
ControlChange = 0xB0, ///< Control Change / Channel Mode
|
|
||||||
ProgramChange = 0xC0, ///< Program Change
|
|
||||||
AfterTouchChannel = 0xD0, ///< Channel (monophonic) AfterTouch
|
|
||||||
PitchBend = 0xE0, ///< Pitch Bend
|
|
||||||
SystemExclusive = 0xF0, ///< System Exclusive
|
|
||||||
SystemExclusiveStart = SystemExclusive, ///< System Exclusive Start
|
|
||||||
TimeCodeQuarterFrame = 0xF1, ///< System Common - MIDI Time Code Quarter Frame
|
|
||||||
SongPosition = 0xF2, ///< System Common - Song Position Pointer
|
|
||||||
SongSelect = 0xF3, ///< System Common - Song Select
|
|
||||||
TuneRequest = 0xF6, ///< System Common - Tune Request
|
|
||||||
SystemExclusiveEnd = 0xF7, ///< System Exclusive End
|
|
||||||
Clock = 0xF8, ///< System Real Time - Timing Clock
|
|
||||||
Start = 0xFA, ///< System Real Time - Start
|
|
||||||
Continue = 0xFB, ///< System Real Time - Continue
|
|
||||||
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
|
|
||||||
{
|
|
||||||
enum Mode
|
|
||||||
{
|
|
||||||
Off = 0, ///< Thru disabled (nothing passes through).
|
|
||||||
Full = 1, ///< Fully enabled Thru (every incoming message is sent back).
|
|
||||||
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.
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/*! Deprecated: use Thru::Mode instead.
|
|
||||||
Will be removed in v5.0.
|
|
||||||
*/
|
|
||||||
enum __attribute__ ((deprecated)) MidiFilterMode
|
|
||||||
{
|
|
||||||
Off = Thru::Off,
|
|
||||||
Full = Thru::Full,
|
|
||||||
SameChannel = Thru::SameChannel,
|
|
||||||
DifferentChannel = Thru::DifferentChannel,
|
|
||||||
};
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/*! \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
|
|
||||||
{
|
|
||||||
// High resolution Continuous Controllers MSB (+32 for LSB) ----------------
|
|
||||||
BankSelect = 0,
|
|
||||||
ModulationWheel = 1,
|
|
||||||
BreathController = 2,
|
|
||||||
// CC3 undefined
|
|
||||||
FootController = 4,
|
|
||||||
PortamentoTime = 5,
|
|
||||||
DataEntryMSB = 6,
|
|
||||||
ChannelVolume = 7,
|
|
||||||
Balance = 8,
|
|
||||||
// CC9 undefined
|
|
||||||
Pan = 10,
|
|
||||||
ExpressionController = 11,
|
|
||||||
EffectControl1 = 12,
|
|
||||||
EffectControl2 = 13,
|
|
||||||
// CC14 undefined
|
|
||||||
// CC15 undefined
|
|
||||||
GeneralPurposeController1 = 16,
|
|
||||||
GeneralPurposeController2 = 17,
|
|
||||||
GeneralPurposeController3 = 18,
|
|
||||||
GeneralPurposeController4 = 19,
|
|
||||||
|
|
||||||
DataEntryLSB = 38,
|
|
||||||
|
|
||||||
// Switches ----------------------------------------------------------------
|
|
||||||
Sustain = 64,
|
|
||||||
Portamento = 65,
|
|
||||||
Sostenuto = 66,
|
|
||||||
SoftPedal = 67,
|
|
||||||
Legato = 68,
|
|
||||||
Hold = 69,
|
|
||||||
|
|
||||||
// Low resolution continuous controllers -----------------------------------
|
|
||||||
SoundController1 = 70, ///< Synth: Sound Variation FX: Exciter On/Off
|
|
||||||
SoundController2 = 71, ///< Synth: Harmonic Content FX: Compressor On/Off
|
|
||||||
SoundController3 = 72, ///< Synth: Release Time FX: Distortion On/Off
|
|
||||||
SoundController4 = 73, ///< Synth: Attack Time FX: EQ On/Off
|
|
||||||
SoundController5 = 74, ///< Synth: Brightness FX: Expander On/Off
|
|
||||||
SoundController6 = 75, ///< Synth: Decay Time FX: Reverb On/Off
|
|
||||||
SoundController7 = 76, ///< Synth: Vibrato Rate FX: Delay On/Off
|
|
||||||
SoundController8 = 77, ///< Synth: Vibrato Depth FX: Pitch Transpose On/Off
|
|
||||||
SoundController9 = 78, ///< Synth: Vibrato Delay FX: Flange/Chorus On/Off
|
|
||||||
SoundController10 = 79, ///< Synth: Undefined FX: Special Effects On/Off
|
|
||||||
GeneralPurposeController5 = 80,
|
|
||||||
GeneralPurposeController6 = 81,
|
|
||||||
GeneralPurposeController7 = 82,
|
|
||||||
GeneralPurposeController8 = 83,
|
|
||||||
PortamentoControl = 84,
|
|
||||||
// CC85 to CC90 undefined
|
|
||||||
Effects1 = 91, ///< Reverb send level
|
|
||||||
Effects2 = 92, ///< Tremolo depth
|
|
||||||
Effects3 = 93, ///< Chorus send level
|
|
||||||
Effects4 = 94, ///< Celeste depth
|
|
||||||
Effects5 = 95, ///< Phaser depth
|
|
||||||
DataIncrement = 96,
|
|
||||||
DataDecrement = 97,
|
|
||||||
NRPNLSB = 98, ///< Non-Registered Parameter Number (LSB)
|
|
||||||
NRPNMSB = 99, ///< Non-Registered Parameter Number (MSB)
|
|
||||||
RPNLSB = 100, ///< Registered Parameter Number (LSB)
|
|
||||||
RPNMSB = 101, ///< Registered Parameter Number (MSB)
|
|
||||||
|
|
||||||
// Channel Mode messages ---------------------------------------------------
|
|
||||||
AllSoundOff = 120,
|
|
||||||
ResetAllControllers = 121,
|
|
||||||
LocalControl = 122,
|
|
||||||
AllNotesOff = 123,
|
|
||||||
OmniModeOff = 124,
|
|
||||||
OmniModeOn = 125,
|
|
||||||
MonoModeOn = 126,
|
|
||||||
PolyModeOn = 127
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RPN
|
|
||||||
{
|
|
||||||
enum RegisteredParameterNumbers: uint16_t
|
|
||||||
{
|
|
||||||
PitchBendSensitivity = 0x0000,
|
|
||||||
ChannelFineTuning = 0x0001,
|
|
||||||
ChannelCoarseTuning = 0x0002,
|
|
||||||
SelectTuningProgram = 0x0003,
|
|
||||||
SelectTuningBank = 0x0004,
|
|
||||||
ModulationDepthRange = 0x0005,
|
|
||||||
NullFunction = (0x7f << 7) + 0x7f,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
END_MIDI_NAMESPACE
|
|
||||||
|
|
@ -1,101 +0,0 @@
|
||||||
/*!
|
|
||||||
* @file midi_Message.h
|
|
||||||
* Project Arduino MIDI Library
|
|
||||||
* @brief MIDI Library for the Arduino - Message struct definition
|
|
||||||
* @author Francois Best
|
|
||||||
* @date 11/06/14
|
|
||||||
* @license MIT - Copyright (c) 2015 Francois Best
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "midi_Namespace.h"
|
|
||||||
#include "midi_Defs.h"
|
|
||||||
#ifndef ARDUINO
|
|
||||||
#include <string.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
BEGIN_MIDI_NAMESPACE
|
|
||||||
|
|
||||||
/*! The Message structure contains decoded data of a MIDI message
|
|
||||||
read from the serial port with read()
|
|
||||||
*/
|
|
||||||
template<unsigned SysExMaxSize>
|
|
||||||
struct Message
|
|
||||||
{
|
|
||||||
/*! Default constructor
|
|
||||||
\n Initializes the attributes with their default values.
|
|
||||||
*/
|
|
||||||
inline Message()
|
|
||||||
: channel(0)
|
|
||||||
, type(MIDI_NAMESPACE::InvalidType)
|
|
||||||
, data1(0)
|
|
||||||
, data2(0)
|
|
||||||
, valid(false)
|
|
||||||
{
|
|
||||||
memset(sysexArray, 0, sSysExMaxSize * sizeof(DataByte));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*! The maximum size for the System Exclusive array.
|
|
||||||
*/
|
|
||||||
static const unsigned sSysExMaxSize = SysExMaxSize;
|
|
||||||
|
|
||||||
/*! The MIDI channel on which the message was recieved.
|
|
||||||
\n Value goes from 1 to 16.
|
|
||||||
*/
|
|
||||||
Channel channel;
|
|
||||||
|
|
||||||
/*! The type of the message
|
|
||||||
(see the MidiType enum for types reference)
|
|
||||||
*/
|
|
||||||
MidiType type;
|
|
||||||
|
|
||||||
/*! The first data byte.
|
|
||||||
\n Value goes from 0 to 127.
|
|
||||||
*/
|
|
||||||
DataByte data1;
|
|
||||||
|
|
||||||
/*! The second data byte.
|
|
||||||
If the message is only 2 bytes long, this one is null.
|
|
||||||
\n Value goes from 0 to 127.
|
|
||||||
*/
|
|
||||||
DataByte data2;
|
|
||||||
|
|
||||||
/*! System Exclusive dedicated byte array.
|
|
||||||
\n Array length is stocked on 16 bits,
|
|
||||||
in data1 (LSB) and data2 (MSB)
|
|
||||||
*/
|
|
||||||
DataByte sysexArray[sSysExMaxSize];
|
|
||||||
|
|
||||||
/*! This boolean indicates if the message is valid or not.
|
|
||||||
There is no channel consideration here,
|
|
||||||
validity means the message respects the MIDI norm.
|
|
||||||
*/
|
|
||||||
bool valid;
|
|
||||||
|
|
||||||
inline unsigned getSysExSize() const
|
|
||||||
{
|
|
||||||
const unsigned size = unsigned(data2) << 8 | data1;
|
|
||||||
return size > sSysExMaxSize ? sSysExMaxSize : size;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
END_MIDI_NAMESPACE
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
/*!
|
|
||||||
* @file midi_Namespace.h
|
|
||||||
* Project Arduino MIDI Library
|
|
||||||
* @brief MIDI Library for the Arduino - Namespace declaration
|
|
||||||
* @author Francois Best
|
|
||||||
* @date 24/02/11
|
|
||||||
* @license MIT - Copyright (c) 2015 Francois Best
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define MIDI_NAMESPACE midi_v440
|
|
||||||
#define BEGIN_MIDI_NAMESPACE namespace MIDI_NAMESPACE {
|
|
||||||
#define END_MIDI_NAMESPACE }
|
|
||||||
|
|
||||||
#define USING_NAMESPACE_MIDI using namespace MIDI_NAMESPACE;
|
|
||||||
|
|
||||||
BEGIN_MIDI_NAMESPACE
|
|
||||||
|
|
||||||
END_MIDI_NAMESPACE
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
/*!
|
|
||||||
* @file midi_Settings.h
|
|
||||||
* Project Arduino MIDI Library
|
|
||||||
* @brief MIDI Library for the Arduino - Settings
|
|
||||||
* @author Francois Best
|
|
||||||
* @date 24/02/11
|
|
||||||
* @license MIT - Copyright (c) 2015 Francois Best
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "midi_Defs.h"
|
|
||||||
|
|
||||||
BEGIN_MIDI_NAMESPACE
|
|
||||||
|
|
||||||
/*! \brief Default Settings for the MIDI Library.
|
|
||||||
|
|
||||||
To change the default settings, don't edit them there, create a subclass and
|
|
||||||
override the values in that subclass, then use the MIDI_CREATE_CUSTOM_INSTANCE
|
|
||||||
macro to create your instance. The settings you don't override will keep their
|
|
||||||
default value. Eg:
|
|
||||||
\code{.cpp}
|
|
||||||
struct MySettings : public MIDI::DefaultSettings
|
|
||||||
{
|
|
||||||
static const unsigned SysExMaxSize = 1024; // Accept SysEx messages up to 1024 bytes long.
|
|
||||||
};
|
|
||||||
|
|
||||||
MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial2, MIDI, MySettings);
|
|
||||||
\endcode
|
|
||||||
*/
|
|
||||||
struct DefaultSettings
|
|
||||||
{
|
|
||||||
/*! Running status enables short messages when sending multiple values
|
|
||||||
of the same type and channel.\n
|
|
||||||
Must be disabled to send USB MIDI messages to a computer
|
|
||||||
Warning: does not work with some hardware, enable with caution.
|
|
||||||
*/
|
|
||||||
static const bool UseRunningStatus = false;
|
|
||||||
|
|
||||||
/*! NoteOn with 0 velocity should be handled as NoteOf.\n
|
|
||||||
Set to true to get NoteOff events when receiving null-velocity NoteOn messages.\n
|
|
||||||
Set to false to get NoteOn events when receiving null-velocity NoteOn messages.
|
|
||||||
*/
|
|
||||||
static const bool HandleNullVelocityNoteOnAsNoteOff = true;
|
|
||||||
|
|
||||||
/*! Active Sensing is intended to be sent
|
|
||||||
repeatedly by the sender to tell the receiver that a connection is alive. Use
|
|
||||||
of this message is optional. When initially received, the
|
|
||||||
receiver will expect to receive another Active Sensing
|
|
||||||
message each 300ms (max), and if it does not then it will
|
|
||||||
assume that the connection has been terminated. At
|
|
||||||
termination, the receiver will turn off all voices and return to
|
|
||||||
normal (non- active sensing) operation..
|
|
||||||
*/
|
|
||||||
static const bool UseSenderActiveSensing = false;
|
|
||||||
|
|
||||||
/*! Setting this to true will make MIDI.read parse only one byte of data for each
|
|
||||||
call when data is available. This can speed up your application if receiving
|
|
||||||
a lot of traffic, but might induce MIDI Thru and treatment latency.
|
|
||||||
*/
|
|
||||||
static const bool Use1ByteParsing = false;
|
|
||||||
|
|
||||||
/*! Maximum size of SysEx receivable. Decrease to save RAM if you don't expect
|
|
||||||
to receive SysEx, or adjust accordingly.
|
|
||||||
*/
|
|
||||||
static const unsigned SysExMaxSize = 128;
|
|
||||||
};
|
|
||||||
|
|
||||||
END_MIDI_NAMESPACE
|
|
||||||
|
|
@ -87,4 +87,9 @@ template <class T> const T& min(const T& a, const T& b) {
|
||||||
return !(b < a) ? a : b; // or: return !comp(b,a)?a:b; for version (2)
|
return !(b < a) ? a : b; // or: return !comp(b,a)?a:b; for version (2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool bitRead(byte value, uint8_t bitToCheck)
|
||||||
|
{
|
||||||
|
return value & (1 << bitToCheck)
|
||||||
|
}
|
||||||
|
|
||||||
#define F(x) x
|
#define F(x) x
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#define DEBUG 7
|
#define DEBUG 7
|
||||||
#define APPLEMIDI_INITIATOR
|
#define APPLEMIDI_INITIATOR
|
||||||
|
|
||||||
#include "../src/midi_bleTransport.h"
|
#include "../src/BLEMIDI.h"
|
||||||
|
|
||||||
void begin()
|
void begin()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,78 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1130"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CCE329B423C2037C00A197D1"
|
||||||
|
BuildableName = "rtpMidi"
|
||||||
|
BlueprintName = "rtpMidi"
|
||||||
|
ReferencedContainer = "container:bleMidi.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CCE329B423C2037C00A197D1"
|
||||||
|
BuildableName = "rtpMidi"
|
||||||
|
BlueprintName = "rtpMidi"
|
||||||
|
ReferencedContainer = "container:bleMidi.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "CCE329B423C2037C00A197D1"
|
||||||
|
BuildableName = "rtpMidi"
|
||||||
|
BlueprintName = "rtpMidi"
|
||||||
|
ReferencedContainer = "container:bleMidi.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildableProductRunnable>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
||||||
|
|
@ -10,5 +10,13 @@
|
||||||
<integer>0</integer>
|
<integer>0</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
|
<key>SuppressBuildableAutocreation</key>
|
||||||
|
<dict>
|
||||||
|
<key>CCE329B423C2037C00A197D1</key>
|
||||||
|
<dict>
|
||||||
|
<key>primary</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue