Utility class for MIDI over Bluetooth LE

Dependencies:   BLE_API mbed nRF51822

Using MIDI over Bluetooth LE

This class enables sending and receiving MIDI packets.

Set up an "BLEMIDI" class instance

Create an instance, and set up to start using it.

Initialization

#include "mbed.h"
#include "BLEDevice.h"
#include "BLEMIDI.h"

BLEDevice device;
BLEMIDI bleMidi(&device);

void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) {
    bleMidi.onBleDisconnection(handle, reason);
}

void connectionCallback(Gap::Handle_t handle, Gap::addr_type_t type, const Gap::address_t addr, const Gap::ConnectionParams_t *params) {
    bleMidi.onBleConnection(handle, type, addr, params);
}

int main(void) {
    device.onConnection(connectionCallback);
    device.onDisconnection(disconnectionCallback);
    
    for (;;) {
        device.waitForEvent();
    }
}

Changing the device name

The default device name is 'MIDI'.
If the another name is preferred, use this constructor. The maximum length of device name is 4 bytes.

Constructor with device name

BLEDevice device;
// BLEMIDI bleMidi(&device);
BLEMIDI bleMidi(&device, "NAME");

Sending MIDI events

For example, send a note on event:

send a note on event

bleMidi.sendNoteOn(0, 63, 127);

About other events, see the BLEMIDI class reference.

Receiving MIDI events

For example, receive note on events:

receive note on events

void onNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {
    // do something with Note On event
}

bleMidi.attachNoteOn(onNoteOn);

About other events, see the BLEMIDI class reference.

Committer:
kshoji
Date:
Tue Apr 07 09:24:31 2015 +0000
Revision:
3:2b71bfbaa458
Parent:
2:dbc6f81b9ba0
Removed main.cpp

Who changed what in which revision?

UserRevisionLine numberNew contents of line
kshoji 0:83889dc90473 1 /* Copyright (c) 2014 mbed.org, MIT License
kshoji 0:83889dc90473 2 *
kshoji 0:83889dc90473 3 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
kshoji 0:83889dc90473 4 * and associated documentation files (the "Software"), to deal in the Software without
kshoji 0:83889dc90473 5 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
kshoji 0:83889dc90473 6 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
kshoji 0:83889dc90473 7 * Software is furnished to do so, subject to the following conditions:
kshoji 0:83889dc90473 8 *
kshoji 0:83889dc90473 9 * The above copyright notice and this permission notice shall be included in all copies or
kshoji 0:83889dc90473 10 * substantial portions of the Software.
kshoji 0:83889dc90473 11 *
kshoji 0:83889dc90473 12 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
kshoji 0:83889dc90473 13 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
kshoji 0:83889dc90473 14 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
kshoji 0:83889dc90473 15 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
kshoji 0:83889dc90473 16 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
kshoji 0:83889dc90473 17 */
kshoji 0:83889dc90473 18
kshoji 0:83889dc90473 19 #include "mbed.h"
kshoji 0:83889dc90473 20 #include "BLEMIDI.h"
kshoji 0:83889dc90473 21
kshoji 0:83889dc90473 22 void BLEMIDI::onBleDisconnection(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) {
kshoji 0:83889dc90473 23 device->startAdvertising();
kshoji 0:83889dc90473 24 isConnected = false;
kshoji 0:83889dc90473 25 }
kshoji 0:83889dc90473 26
kshoji 0:83889dc90473 27 void BLEMIDI::onBleConnection(Gap::Handle_t handle, Gap::addr_type_t type, const Gap::address_t addr, const Gap::ConnectionParams_t *params) {
kshoji 0:83889dc90473 28 isConnected = true;
kshoji 0:83889dc90473 29 }
kshoji 0:83889dc90473 30
kshoji 0:83889dc90473 31 void BLEMIDI::dataWrittenCallback(const GattCharacteristicWriteCBParams *params) {
kshoji 0:83889dc90473 32 uint16_t length;
kshoji 0:83889dc90473 33 uint8_t rxBuffer[20];
kshoji 0:83889dc90473 34
kshoji 0:83889dc90473 35 device->readCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), rxBuffer, &length);
kshoji 0:83889dc90473 36
kshoji 0:83889dc90473 37 if (length > 1) {
kshoji 0:83889dc90473 38 // parse BLE message
kshoji 0:83889dc90473 39 uint8_t header = rxBuffer[0];
kshoji 0:83889dc90473 40 for (int i = 1; i < length; i++) {
kshoji 0:83889dc90473 41 uint8_t midiEvent = rxBuffer[i];
kshoji 0:83889dc90473 42
kshoji 0:83889dc90473 43 if (midiState == MIDI_STATE_TIMESTAMP) {
kshoji 0:83889dc90473 44 if ((midiEvent & 0x80) == 0) {
kshoji 0:83889dc90473 45 // running status
kshoji 0:83889dc90473 46 midiState = MIDI_STATE_WAIT;
kshoji 0:83889dc90473 47 }
kshoji 0:83889dc90473 48
kshoji 0:83889dc90473 49 if (midiEvent == 0xf7) {
kshoji 0:83889dc90473 50 // maybe error
kshoji 0:83889dc90473 51 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 52 continue;
kshoji 0:83889dc90473 53 }
kshoji 0:83889dc90473 54 }
kshoji 0:83889dc90473 55 if (midiState == MIDI_STATE_TIMESTAMP) {
kshoji 0:83889dc90473 56 timestamp = ((header & 0x3f) << 7) | (midiEvent & 0x7f);
kshoji 0:83889dc90473 57 midiState = MIDI_STATE_WAIT;
kshoji 0:83889dc90473 58 } else if (midiState == MIDI_STATE_WAIT) {
kshoji 0:83889dc90473 59 switch (midiEvent & 0xf0) {
kshoji 0:83889dc90473 60 case 0xf0: {
kshoji 0:83889dc90473 61 switch (midiEvent) {
kshoji 0:83889dc90473 62 case 0xf0:
kshoji 0:83889dc90473 63 sysExBuffer[sysExBufferPos++] = midiEvent;
kshoji 0:83889dc90473 64 midiState = MIDI_STATE_SIGNAL_SYSEX;
kshoji 0:83889dc90473 65 break;
kshoji 0:83889dc90473 66
kshoji 0:83889dc90473 67 case 0xf1:
kshoji 0:83889dc90473 68 case 0xf3:
kshoji 0:83889dc90473 69 // 0xf1 MIDI Time Code Quarter Frame. : 2bytes
kshoji 0:83889dc90473 70 // 0xf3 Song Select. : 2bytes
kshoji 0:83889dc90473 71 midiEventKind = midiEvent;
kshoji 0:83889dc90473 72 midiState = MIDI_STATE_SIGNAL_2BYTES_2;
kshoji 0:83889dc90473 73 break;
kshoji 0:83889dc90473 74
kshoji 0:83889dc90473 75 case 0xf2:
kshoji 0:83889dc90473 76 // 0xf2 Song Position Pointer. : 3bytes
kshoji 0:83889dc90473 77 midiEventKind = midiEvent;
kshoji 0:83889dc90473 78 midiState = MIDI_STATE_SIGNAL_3BYTES_2;
kshoji 0:83889dc90473 79 break;
kshoji 0:83889dc90473 80
kshoji 0:83889dc90473 81 case 0xf6:
kshoji 0:83889dc90473 82 // 0xf6 Tune Request : 1byte
kshoji 0:83889dc90473 83 onTuneRequest();
kshoji 0:83889dc90473 84 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 85 break;
kshoji 0:83889dc90473 86 case 0xf8:
kshoji 0:83889dc90473 87 // 0xf8 Timing Clock : 1byte
kshoji 0:83889dc90473 88 onTimingClock();
kshoji 0:83889dc90473 89 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 90 break;
kshoji 0:83889dc90473 91 case 0xfa:
kshoji 0:83889dc90473 92 // 0xfa Start : 1byte
kshoji 0:83889dc90473 93 onStart();
kshoji 0:83889dc90473 94 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 95 break;
kshoji 0:83889dc90473 96 case 0xfb:
kshoji 0:83889dc90473 97 // 0xfb Continue : 1byte
kshoji 0:83889dc90473 98 onContinue();
kshoji 0:83889dc90473 99 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 100 break;
kshoji 0:83889dc90473 101 case 0xfc:
kshoji 0:83889dc90473 102 // 0xfc Stop : 1byte
kshoji 0:83889dc90473 103 onStop();
kshoji 0:83889dc90473 104 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 105 break;
kshoji 0:83889dc90473 106 case 0xfe:
kshoji 0:83889dc90473 107 // 0xfe Active Sensing : 1byte
kshoji 0:83889dc90473 108 onActiveSensing();
kshoji 0:83889dc90473 109 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 110 break;
kshoji 0:83889dc90473 111 case 0xff:
kshoji 0:83889dc90473 112 // 0xff Reset : 1byte
kshoji 0:83889dc90473 113 onReset();
kshoji 0:83889dc90473 114 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 115 break;
kshoji 0:83889dc90473 116
kshoji 0:83889dc90473 117 default:
kshoji 0:83889dc90473 118 break;
kshoji 0:83889dc90473 119 }
kshoji 0:83889dc90473 120 }
kshoji 0:83889dc90473 121 break;
kshoji 0:83889dc90473 122 case 0x80:
kshoji 0:83889dc90473 123 case 0x90:
kshoji 0:83889dc90473 124 case 0xa0:
kshoji 0:83889dc90473 125 case 0xb0:
kshoji 0:83889dc90473 126 case 0xe0:
kshoji 0:83889dc90473 127 // 3bytes pattern
kshoji 0:83889dc90473 128 midiEventKind = midiEvent;
kshoji 0:83889dc90473 129 midiState = MIDI_STATE_SIGNAL_3BYTES_2;
kshoji 0:83889dc90473 130 break;
kshoji 0:83889dc90473 131 case 0xc0: // program change
kshoji 0:83889dc90473 132 case 0xd0: // channel after-touch
kshoji 0:83889dc90473 133 // 2bytes pattern
kshoji 0:83889dc90473 134 midiEventKind = midiEvent;
kshoji 0:83889dc90473 135 midiState = MIDI_STATE_SIGNAL_2BYTES_2;
kshoji 0:83889dc90473 136 break;
kshoji 0:83889dc90473 137 default:
kshoji 0:83889dc90473 138 // 0x00 - 0x70: running status
kshoji 0:83889dc90473 139 if ((midiEventKind & 0xf0) != 0xf0) {
kshoji 0:83889dc90473 140 // previous event kind is multi-bytes pattern
kshoji 0:83889dc90473 141 midiEventNote = midiEvent;
kshoji 0:83889dc90473 142 midiState = MIDI_STATE_SIGNAL_3BYTES_3;
kshoji 0:83889dc90473 143 }
kshoji 0:83889dc90473 144 break;
kshoji 0:83889dc90473 145 }
kshoji 0:83889dc90473 146 } else if (midiState == MIDI_STATE_SIGNAL_2BYTES_2) {
kshoji 0:83889dc90473 147 switch (midiEventKind & 0xf0) {
kshoji 0:83889dc90473 148 // 2bytes pattern
kshoji 0:83889dc90473 149 case 0xc0: // program change
kshoji 0:83889dc90473 150 midiEventNote = midiEvent;
kshoji 0:83889dc90473 151 onProgramChange(midiEventKind & 0xf, midiEventNote);
kshoji 0:83889dc90473 152 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 153 break;
kshoji 0:83889dc90473 154 case 0xd0: // channel after-touch
kshoji 0:83889dc90473 155 midiEventNote = midiEvent;
kshoji 0:83889dc90473 156 onChannelAftertouch(midiEventKind & 0xf, midiEventNote);
kshoji 0:83889dc90473 157 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 158 break;
kshoji 0:83889dc90473 159 case 0xf0: {
kshoji 0:83889dc90473 160 switch (midiEventKind) {
kshoji 0:83889dc90473 161 case 0xf1:
kshoji 0:83889dc90473 162 // 0xf1 MIDI Time Code Quarter Frame. : 2bytes
kshoji 0:83889dc90473 163 midiEventNote = midiEvent;
kshoji 0:83889dc90473 164 onTimeCodeQuarterFrame(midiEventNote);
kshoji 0:83889dc90473 165 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 166 break;
kshoji 0:83889dc90473 167 case 0xf3:
kshoji 0:83889dc90473 168 // 0xf3 Song Select. : 2bytes
kshoji 0:83889dc90473 169 midiEventNote = midiEvent;
kshoji 0:83889dc90473 170 onSongSelect(midiEventNote);
kshoji 0:83889dc90473 171 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 172 break;
kshoji 0:83889dc90473 173 default:
kshoji 0:83889dc90473 174 // illegal state
kshoji 0:83889dc90473 175 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 176 break;
kshoji 0:83889dc90473 177 }
kshoji 0:83889dc90473 178 }
kshoji 0:83889dc90473 179 break;
kshoji 0:83889dc90473 180 default:
kshoji 0:83889dc90473 181 // illegal state
kshoji 0:83889dc90473 182 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 183 break;
kshoji 0:83889dc90473 184 }
kshoji 0:83889dc90473 185 } else if (midiState == MIDI_STATE_SIGNAL_3BYTES_2) {
kshoji 0:83889dc90473 186 switch (midiEventKind & 0xf0) {
kshoji 0:83889dc90473 187 case 0x80:
kshoji 0:83889dc90473 188 case 0x90:
kshoji 0:83889dc90473 189 case 0xa0:
kshoji 0:83889dc90473 190 case 0xb0:
kshoji 0:83889dc90473 191 case 0xe0:
kshoji 0:83889dc90473 192 case 0xf0:
kshoji 0:83889dc90473 193 // 3bytes pattern
kshoji 0:83889dc90473 194 midiEventNote = midiEvent;
kshoji 0:83889dc90473 195 midiState = MIDI_STATE_SIGNAL_3BYTES_3;
kshoji 0:83889dc90473 196 break;
kshoji 0:83889dc90473 197 default:
kshoji 0:83889dc90473 198 // illegal state
kshoji 0:83889dc90473 199 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 200 break;
kshoji 0:83889dc90473 201 }
kshoji 0:83889dc90473 202 } else if (midiState == MIDI_STATE_SIGNAL_3BYTES_3) {
kshoji 0:83889dc90473 203 switch (midiEventKind & 0xf0) {
kshoji 0:83889dc90473 204 // 3bytes pattern
kshoji 0:83889dc90473 205 case 0x80: // note off
kshoji 0:83889dc90473 206 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 207 onNoteOff(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 208 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 209 break;
kshoji 0:83889dc90473 210 case 0x90: // note on
kshoji 0:83889dc90473 211 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 212 if (midiEventVelocity == 0) {
kshoji 0:83889dc90473 213 onNoteOff(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 214 } else {
kshoji 0:83889dc90473 215 onNoteOn(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 216 }
kshoji 0:83889dc90473 217 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 218 break;
kshoji 0:83889dc90473 219 case 0xa0: // control polyphonic key pressure
kshoji 0:83889dc90473 220 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 221 onPolyphonicAftertouch(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 222 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 223 break;
kshoji 0:83889dc90473 224 case 0xb0: // control change
kshoji 0:83889dc90473 225 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 226 onControlChange(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 227 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 228 break;
kshoji 0:83889dc90473 229 case 0xe0: // pitch bend
kshoji 0:83889dc90473 230 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 231 onPitchWheel(midiEventKind & 0xf, (midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
kshoji 0:83889dc90473 232 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 233 break;
kshoji 0:83889dc90473 234 case 0xf0: // Song Position Pointer.
kshoji 0:83889dc90473 235 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 236 onSongPositionPointer((midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
kshoji 0:83889dc90473 237 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 238 break;
kshoji 0:83889dc90473 239 default:
kshoji 0:83889dc90473 240 // illegal state
kshoji 0:83889dc90473 241 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 242 break;
kshoji 0:83889dc90473 243 }
kshoji 0:83889dc90473 244 } else if (midiState == MIDI_STATE_SIGNAL_SYSEX) {
kshoji 0:83889dc90473 245 if (midiEvent == 0xf7) {
kshoji 0:83889dc90473 246 // the end of message
kshoji 0:83889dc90473 247 // last written uint8_t is for timestamp
kshoji 1:cba2eba64f5c 248 if (sysExBufferPos > 0) {
kshoji 1:cba2eba64f5c 249 sysExBuffer[sysExBufferPos - 1] = midiEvent;
kshoji 2:dbc6f81b9ba0 250 onSystemExclusive(sysExBuffer, sysExBufferPos, false);
kshoji 1:cba2eba64f5c 251 }
kshoji 0:83889dc90473 252
kshoji 0:83889dc90473 253 sysExBufferPos = 0;
kshoji 0:83889dc90473 254 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 255 } else {
kshoji 2:dbc6f81b9ba0 256 if (sysExBufferPos == 128) {
kshoji 2:dbc6f81b9ba0 257 onSystemExclusive(sysExBuffer, sysExBufferPos, true);
kshoji 2:dbc6f81b9ba0 258 sysExBufferPos = 0;
kshoji 2:dbc6f81b9ba0 259 }
kshoji 0:83889dc90473 260 sysExBuffer[sysExBufferPos++] = midiEvent;
kshoji 0:83889dc90473 261 }
kshoji 0:83889dc90473 262 }
kshoji 0:83889dc90473 263 }
kshoji 0:83889dc90473 264 }
kshoji 0:83889dc90473 265 }
kshoji 0:83889dc90473 266
kshoji 0:83889dc90473 267 BLEMIDI::BLEMIDI(BLEDevice *dev) {
kshoji 2:dbc6f81b9ba0 268 BLEMIDI(dev, "MIDI");
kshoji 2:dbc6f81b9ba0 269 }
kshoji 2:dbc6f81b9ba0 270
kshoji 2:dbc6f81b9ba0 271 BLEMIDI::BLEMIDI(BLEDevice *dev, char *deviceName) {
kshoji 0:83889dc90473 272 device = dev;
kshoji 0:83889dc90473 273 isConnected = false;
kshoji 0:83889dc90473 274 sysExBufferPos = 0;
kshoji 0:83889dc90473 275
kshoji 1:cba2eba64f5c 276 timestamp = 0;
kshoji 1:cba2eba64f5c 277 midiEventKind = 0;
kshoji 1:cba2eba64f5c 278 midiEventNote = 0;
kshoji 1:cba2eba64f5c 279 midiEventVelocity = 0;
kshoji 1:cba2eba64f5c 280
kshoji 0:83889dc90473 281 // MIDI characteristic
kshoji 0:83889dc90473 282 LongUUIDBytes_t characteristicUuid = {
kshoji 0:83889dc90473 283 0x77, 0x72, 0xe5, 0xdb, 0x38, 0x68, 0x41, 0x12,
kshoji 0:83889dc90473 284 0xa1, 0xa9, 0xf2, 0x66, 0x9d, 0x10, 0x6b, 0xf3
kshoji 0:83889dc90473 285 };
kshoji 0:83889dc90473 286
kshoji 1:cba2eba64f5c 287 midiCharacteristic = new GattCharacteristic(UUID(characteristicUuid), midi, 0, 20, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
kshoji 0:83889dc90473 288 GattCharacteristic *midiChars[] = {midiCharacteristic};
kshoji 0:83889dc90473 289
kshoji 0:83889dc90473 290 // MIDI service
kshoji 0:83889dc90473 291 LongUUIDBytes_t serviceUuid = {
kshoji 0:83889dc90473 292 0x03, 0xb8, 0x0e, 0x5a, 0xed, 0xe8, 0x4b, 0x33,
kshoji 0:83889dc90473 293 0xa7, 0x51, 0x6c, 0xe3, 0x4e, 0xc4, 0xc7, 0x00
kshoji 0:83889dc90473 294 };
kshoji 0:83889dc90473 295
kshoji 0:83889dc90473 296 GattService midiService(UUID(serviceUuid), midiChars, sizeof(midiChars) / sizeof(GattCharacteristic *));
kshoji 0:83889dc90473 297 uint8_t uuid128_list[] = {
kshoji 0:83889dc90473 298 0x00, 0xc7, 0xc4, 0x4e, 0xe3, 0x6c, 0x51, 0xa7,
kshoji 0:83889dc90473 299 0x33, 0x4b, 0xe8, 0xed, 0x5a, 0x0e, 0xb8, 0x03
kshoji 0:83889dc90473 300 };
kshoji 0:83889dc90473 301
kshoji 0:83889dc90473 302 device->init();
kshoji 0:83889dc90473 303 device->reset();
kshoji 0:83889dc90473 304
kshoji 0:83889dc90473 305 /* setup callbacks */
kshoji 0:83889dc90473 306 device->onDataWritten(this, &BLEMIDI::dataWrittenCallback);
kshoji 0:83889dc90473 307
kshoji 0:83889dc90473 308 device->addService(midiService);
kshoji 0:83889dc90473 309
kshoji 0:83889dc90473 310 /* setup advertising */
kshoji 0:83889dc90473 311 device->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t*)uuid128_list, sizeof(uuid128_list));
kshoji 0:83889dc90473 312 device->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
kshoji 2:dbc6f81b9ba0 313 device->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)deviceName, sizeof(deviceName));
kshoji 0:83889dc90473 314
kshoji 0:83889dc90473 315 device->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
kshoji 0:83889dc90473 316 device->setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */
kshoji 0:83889dc90473 317
kshoji 0:83889dc90473 318 device->startAdvertising();
kshoji 0:83889dc90473 319 tick.start();
kshoji 0:83889dc90473 320 }
kshoji 0:83889dc90473 321
kshoji 0:83889dc90473 322 bool BLEMIDI::connected() {
kshoji 0:83889dc90473 323 return isConnected;
kshoji 0:83889dc90473 324 }
kshoji 0:83889dc90473 325
kshoji 0:83889dc90473 326 void BLEMIDI::sendMidiMessage(uint8_t data0) {
kshoji 0:83889dc90473 327 if (isConnected) {
kshoji 0:83889dc90473 328 unsigned int ticks = tick.read_ms() & 0x1fff;
kshoji 1:cba2eba64f5c 329 midi[0] = 0x80 | ((ticks >> 7) & 0x3f);
kshoji 1:cba2eba64f5c 330 midi[1] = 0x80 | (ticks & 0x7f);
kshoji 0:83889dc90473 331 midi[2] = data0;
kshoji 0:83889dc90473 332
kshoji 0:83889dc90473 333 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 3);
kshoji 0:83889dc90473 334 }
kshoji 0:83889dc90473 335 }
kshoji 0:83889dc90473 336
kshoji 0:83889dc90473 337 void BLEMIDI::sendMidiMessage(uint8_t data0, uint8_t data1) {
kshoji 0:83889dc90473 338 if (isConnected) {
kshoji 0:83889dc90473 339 unsigned int ticks = tick.read_ms() & 0x1fff;
kshoji 1:cba2eba64f5c 340 midi[0] = 0x80 | ((ticks >> 7) & 0x3f);
kshoji 1:cba2eba64f5c 341 midi[1] = 0x80 | (ticks & 0x7f);
kshoji 0:83889dc90473 342 midi[2] = data0;
kshoji 0:83889dc90473 343 midi[3] = data1;
kshoji 0:83889dc90473 344
kshoji 0:83889dc90473 345 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 4);
kshoji 0:83889dc90473 346 }
kshoji 0:83889dc90473 347 }
kshoji 0:83889dc90473 348
kshoji 0:83889dc90473 349 void BLEMIDI::sendMidiMessage(uint8_t data0, uint8_t data1, uint8_t data2) {
kshoji 0:83889dc90473 350 if (isConnected) {
kshoji 0:83889dc90473 351 unsigned int ticks = tick.read_ms() & 0x1fff;
kshoji 1:cba2eba64f5c 352 midi[0] = 0x80 | ((ticks >> 7) & 0x3f);
kshoji 1:cba2eba64f5c 353 midi[1] = 0x80 | (ticks & 0x7f);
kshoji 0:83889dc90473 354 midi[2] = data0;
kshoji 0:83889dc90473 355 midi[3] = data1;
kshoji 0:83889dc90473 356 midi[4] = data2;
kshoji 0:83889dc90473 357
kshoji 0:83889dc90473 358 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 5);
kshoji 0:83889dc90473 359 }
kshoji 0:83889dc90473 360 }
kshoji 0:83889dc90473 361
kshoji 0:83889dc90473 362 void BLEMIDI::sendTuneRequest() {
kshoji 0:83889dc90473 363 sendMidiMessage(0xf6);
kshoji 0:83889dc90473 364 }
kshoji 0:83889dc90473 365 void BLEMIDI::sendTimingClock() {
kshoji 0:83889dc90473 366 sendMidiMessage(0xf8);
kshoji 0:83889dc90473 367 }
kshoji 0:83889dc90473 368 void BLEMIDI::sendStart() {
kshoji 0:83889dc90473 369 sendMidiMessage(0xfa);
kshoji 0:83889dc90473 370 }
kshoji 0:83889dc90473 371 void BLEMIDI::sendContinue() {
kshoji 0:83889dc90473 372 sendMidiMessage(0xfb);
kshoji 0:83889dc90473 373 }
kshoji 0:83889dc90473 374 void BLEMIDI::sendStop() {
kshoji 0:83889dc90473 375 sendMidiMessage(0xfc);
kshoji 0:83889dc90473 376 }
kshoji 0:83889dc90473 377 void BLEMIDI::sendActiveSensing() {
kshoji 0:83889dc90473 378 sendMidiMessage(0xfe);
kshoji 0:83889dc90473 379 }
kshoji 0:83889dc90473 380 void BLEMIDI::sendReset() {
kshoji 0:83889dc90473 381 sendMidiMessage(0xff);
kshoji 0:83889dc90473 382 }
kshoji 0:83889dc90473 383 void BLEMIDI::sendProgramChange(uint8_t channel, uint8_t program) {
kshoji 0:83889dc90473 384 sendMidiMessage(0xc0 | (channel & 0xf), program);
kshoji 0:83889dc90473 385 }
kshoji 0:83889dc90473 386 void BLEMIDI::sendChannelAftertouch(uint8_t channel, uint8_t pressure) {
kshoji 0:83889dc90473 387 sendMidiMessage(0xd0 | (channel & 0xf), pressure);
kshoji 0:83889dc90473 388 }
kshoji 0:83889dc90473 389 void BLEMIDI::sendTimeCodeQuarterFrame(uint8_t timing) {
kshoji 0:83889dc90473 390 sendMidiMessage(0xf1, timing & 0x7f);
kshoji 0:83889dc90473 391 }
kshoji 0:83889dc90473 392 void BLEMIDI::sendSongSelect(uint8_t song) {
kshoji 0:83889dc90473 393 sendMidiMessage(0xf3, song & 0x7f);
kshoji 0:83889dc90473 394 }
kshoji 0:83889dc90473 395 void BLEMIDI::sendNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) {
kshoji 0:83889dc90473 396 sendMidiMessage(0x80 | (channel & 0xf), note, velocity);
kshoji 0:83889dc90473 397 }
kshoji 0:83889dc90473 398 void BLEMIDI::sendNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {
kshoji 0:83889dc90473 399 sendMidiMessage(0x90 | (channel & 0xf), note, velocity);
kshoji 0:83889dc90473 400 }
kshoji 0:83889dc90473 401 void BLEMIDI::sendPolyphonicAftertouch(uint8_t channel, uint8_t note, uint8_t pressure) {
kshoji 0:83889dc90473 402 sendMidiMessage(0xa0 | (channel & 0xf), note, pressure);
kshoji 0:83889dc90473 403 }
kshoji 0:83889dc90473 404 void BLEMIDI::sendControlChange(uint8_t channel, uint8_t function, uint8_t value) {
kshoji 0:83889dc90473 405 sendMidiMessage(0xb0 | (channel & 0xf), function, value);
kshoji 0:83889dc90473 406 }
kshoji 0:83889dc90473 407 void BLEMIDI::sendPitchWheel(uint8_t channel, uint16_t amount) {
kshoji 0:83889dc90473 408 sendMidiMessage(0xe0 | (channel & 0xf), amount & 0x7f, (amount >> 7) & 0x7f);
kshoji 0:83889dc90473 409 }
kshoji 0:83889dc90473 410 void BLEMIDI::sendSongPositionPointer(uint16_t position) {
kshoji 0:83889dc90473 411 sendMidiMessage(0xf2, position & 0x7f, (position >> 7) & 0x7f);
kshoji 0:83889dc90473 412 }
kshoji 0:83889dc90473 413 void BLEMIDI::sendSystemExclusive(uint8_t * sysex, uint16_t length) {
kshoji 0:83889dc90473 414 if (isConnected) {
kshoji 0:83889dc90473 415 uint8_t position = 0;
kshoji 0:83889dc90473 416
kshoji 0:83889dc90473 417 // header
kshoji 1:cba2eba64f5c 418 uint16_t ticks = tick.read_ms() & 0x1fff;
kshoji 1:cba2eba64f5c 419 midi[position++] = 0x80 | ((ticks >> 7) & 0x3f);
kshoji 1:cba2eba64f5c 420 midi[position++] = 0x80 | (ticks & 0x7f);
kshoji 0:83889dc90473 421
kshoji 0:83889dc90473 422 for (int i = 0; i < length; i++) {
kshoji 0:83889dc90473 423 if (i == length - 1) {
kshoji 1:cba2eba64f5c 424 // modify last byte
kshoji 1:cba2eba64f5c 425 midi[position++] = 0x80 | (ticks & 0x7f);
kshoji 0:83889dc90473 426
kshoji 0:83889dc90473 427 if (position == 20) {
kshoji 0:83889dc90473 428 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 20);
kshoji 0:83889dc90473 429
kshoji 0:83889dc90473 430 position = 0;
kshoji 0:83889dc90473 431 // header
kshoji 0:83889dc90473 432 midi[position++] = 0x80 | (ticks >> 7) & 0x3f;
kshoji 0:83889dc90473 433 }
kshoji 0:83889dc90473 434 }
kshoji 0:83889dc90473 435 midi[position++] = sysex[i];
kshoji 0:83889dc90473 436 if (position == 20) {
kshoji 0:83889dc90473 437 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 20);
kshoji 0:83889dc90473 438
kshoji 0:83889dc90473 439 position = 0;
kshoji 0:83889dc90473 440 // header
kshoji 0:83889dc90473 441 midi[position++] = 0x80 | (ticks >> 7) & 0x3f;
kshoji 0:83889dc90473 442 }
kshoji 0:83889dc90473 443
kshoji 0:83889dc90473 444 ticks = tick.read_ms() & 0x1fff;
kshoji 0:83889dc90473 445 }
kshoji 0:83889dc90473 446
kshoji 0:83889dc90473 447 if (position > 0) {
kshoji 0:83889dc90473 448 // send remains
kshoji 0:83889dc90473 449 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, position);
kshoji 0:83889dc90473 450 }
kshoji 0:83889dc90473 451 }
kshoji 0:83889dc90473 452 }