Dependencies:   BLE_API mbed nRF51822_BLE_MIDI

Dependents:   BLE_MIDI_one_note_TEST

Fork of BLE_MIDI by Kaoru Shoji

Committer:
kshoji
Date:
Thu Apr 02 05:17:14 2015 +0000
Revision:
0:83889dc90473
Child:
1:cba2eba64f5c
Tested MIDI receiving

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 // read characteristic and write serial
kshoji 0:83889dc90473 33 uint16_t length;
kshoji 0:83889dc90473 34 uint8_t rxBuffer[20];
kshoji 0:83889dc90473 35
kshoji 0:83889dc90473 36 device->readCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), rxBuffer, &length);
kshoji 0:83889dc90473 37
kshoji 0:83889dc90473 38 if (length > 1) {
kshoji 0:83889dc90473 39 // parse BLE message
kshoji 0:83889dc90473 40 uint8_t header = rxBuffer[0];
kshoji 0:83889dc90473 41 for (int i = 1; i < length; i++) {
kshoji 0:83889dc90473 42 uint8_t midiEvent = rxBuffer[i];
kshoji 0:83889dc90473 43
kshoji 0:83889dc90473 44 if (midiState == MIDI_STATE_TIMESTAMP) {
kshoji 0:83889dc90473 45 if ((midiEvent & 0x80) == 0) {
kshoji 0:83889dc90473 46 // running status
kshoji 0:83889dc90473 47 midiState = MIDI_STATE_WAIT;
kshoji 0:83889dc90473 48 }
kshoji 0:83889dc90473 49
kshoji 0:83889dc90473 50 if (midiEvent == 0xf7) {
kshoji 0:83889dc90473 51 // maybe error
kshoji 0:83889dc90473 52 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 53 continue;
kshoji 0:83889dc90473 54 }
kshoji 0:83889dc90473 55 }
kshoji 0:83889dc90473 56 if (midiState == MIDI_STATE_TIMESTAMP) {
kshoji 0:83889dc90473 57 timestamp = ((header & 0x3f) << 7) | (midiEvent & 0x7f);
kshoji 0:83889dc90473 58 midiState = MIDI_STATE_WAIT;
kshoji 0:83889dc90473 59 } else if (midiState == MIDI_STATE_WAIT) {
kshoji 0:83889dc90473 60 switch (midiEvent & 0xf0) {
kshoji 0:83889dc90473 61 case 0xf0: {
kshoji 0:83889dc90473 62 switch (midiEvent) {
kshoji 0:83889dc90473 63 case 0xf0:
kshoji 0:83889dc90473 64 sysExBuffer[sysExBufferPos++] = midiEvent;
kshoji 0:83889dc90473 65 midiState = MIDI_STATE_SIGNAL_SYSEX;
kshoji 0:83889dc90473 66 break;
kshoji 0:83889dc90473 67
kshoji 0:83889dc90473 68 case 0xf1:
kshoji 0:83889dc90473 69 case 0xf3:
kshoji 0:83889dc90473 70 // 0xf1 MIDI Time Code Quarter Frame. : 2bytes
kshoji 0:83889dc90473 71 // 0xf3 Song Select. : 2bytes
kshoji 0:83889dc90473 72 midiEventKind = midiEvent;
kshoji 0:83889dc90473 73 midiState = MIDI_STATE_SIGNAL_2BYTES_2;
kshoji 0:83889dc90473 74 break;
kshoji 0:83889dc90473 75
kshoji 0:83889dc90473 76 case 0xf2:
kshoji 0:83889dc90473 77 // 0xf2 Song Position Pointer. : 3bytes
kshoji 0:83889dc90473 78 midiEventKind = midiEvent;
kshoji 0:83889dc90473 79 midiState = MIDI_STATE_SIGNAL_3BYTES_2;
kshoji 0:83889dc90473 80 break;
kshoji 0:83889dc90473 81
kshoji 0:83889dc90473 82 case 0xf6:
kshoji 0:83889dc90473 83 // 0xf6 Tune Request : 1byte
kshoji 0:83889dc90473 84 onTuneRequest();
kshoji 0:83889dc90473 85 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 86 break;
kshoji 0:83889dc90473 87 case 0xf8:
kshoji 0:83889dc90473 88 // 0xf8 Timing Clock : 1byte
kshoji 0:83889dc90473 89 onTimingClock();
kshoji 0:83889dc90473 90 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 91 break;
kshoji 0:83889dc90473 92 case 0xfa:
kshoji 0:83889dc90473 93 // 0xfa Start : 1byte
kshoji 0:83889dc90473 94 onStart();
kshoji 0:83889dc90473 95 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 96 break;
kshoji 0:83889dc90473 97 case 0xfb:
kshoji 0:83889dc90473 98 // 0xfb Continue : 1byte
kshoji 0:83889dc90473 99 onContinue();
kshoji 0:83889dc90473 100 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 101 break;
kshoji 0:83889dc90473 102 case 0xfc:
kshoji 0:83889dc90473 103 // 0xfc Stop : 1byte
kshoji 0:83889dc90473 104 onStop();
kshoji 0:83889dc90473 105 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 106 break;
kshoji 0:83889dc90473 107 case 0xfe:
kshoji 0:83889dc90473 108 // 0xfe Active Sensing : 1byte
kshoji 0:83889dc90473 109 onActiveSensing();
kshoji 0:83889dc90473 110 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 111 break;
kshoji 0:83889dc90473 112 case 0xff:
kshoji 0:83889dc90473 113 // 0xff Reset : 1byte
kshoji 0:83889dc90473 114 onReset();
kshoji 0:83889dc90473 115 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 116 break;
kshoji 0:83889dc90473 117
kshoji 0:83889dc90473 118 default:
kshoji 0:83889dc90473 119 break;
kshoji 0:83889dc90473 120 }
kshoji 0:83889dc90473 121 }
kshoji 0:83889dc90473 122 break;
kshoji 0:83889dc90473 123 case 0x80:
kshoji 0:83889dc90473 124 case 0x90:
kshoji 0:83889dc90473 125 case 0xa0:
kshoji 0:83889dc90473 126 case 0xb0:
kshoji 0:83889dc90473 127 case 0xe0:
kshoji 0:83889dc90473 128 // 3bytes pattern
kshoji 0:83889dc90473 129 midiEventKind = midiEvent;
kshoji 0:83889dc90473 130 midiState = MIDI_STATE_SIGNAL_3BYTES_2;
kshoji 0:83889dc90473 131 break;
kshoji 0:83889dc90473 132 case 0xc0: // program change
kshoji 0:83889dc90473 133 case 0xd0: // channel after-touch
kshoji 0:83889dc90473 134 // 2bytes pattern
kshoji 0:83889dc90473 135 midiEventKind = midiEvent;
kshoji 0:83889dc90473 136 midiState = MIDI_STATE_SIGNAL_2BYTES_2;
kshoji 0:83889dc90473 137 break;
kshoji 0:83889dc90473 138 default:
kshoji 0:83889dc90473 139 // 0x00 - 0x70: running status
kshoji 0:83889dc90473 140 if ((midiEventKind & 0xf0) != 0xf0) {
kshoji 0:83889dc90473 141 // previous event kind is multi-bytes pattern
kshoji 0:83889dc90473 142 midiEventNote = midiEvent;
kshoji 0:83889dc90473 143 midiState = MIDI_STATE_SIGNAL_3BYTES_3;
kshoji 0:83889dc90473 144 }
kshoji 0:83889dc90473 145 break;
kshoji 0:83889dc90473 146 }
kshoji 0:83889dc90473 147 } else if (midiState == MIDI_STATE_SIGNAL_2BYTES_2) {
kshoji 0:83889dc90473 148 switch (midiEventKind & 0xf0) {
kshoji 0:83889dc90473 149 // 2bytes pattern
kshoji 0:83889dc90473 150 case 0xc0: // program change
kshoji 0:83889dc90473 151 midiEventNote = midiEvent;
kshoji 0:83889dc90473 152 onProgramChange(midiEventKind & 0xf, midiEventNote);
kshoji 0:83889dc90473 153 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 154 break;
kshoji 0:83889dc90473 155 case 0xd0: // channel after-touch
kshoji 0:83889dc90473 156 midiEventNote = midiEvent;
kshoji 0:83889dc90473 157 onChannelAftertouch(midiEventKind & 0xf, midiEventNote);
kshoji 0:83889dc90473 158 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 159 break;
kshoji 0:83889dc90473 160 case 0xf0: {
kshoji 0:83889dc90473 161 switch (midiEventKind) {
kshoji 0:83889dc90473 162 case 0xf1:
kshoji 0:83889dc90473 163 // 0xf1 MIDI Time Code Quarter Frame. : 2bytes
kshoji 0:83889dc90473 164 midiEventNote = midiEvent;
kshoji 0:83889dc90473 165 onTimeCodeQuarterFrame(midiEventNote);
kshoji 0:83889dc90473 166 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 167 break;
kshoji 0:83889dc90473 168 case 0xf3:
kshoji 0:83889dc90473 169 // 0xf3 Song Select. : 2bytes
kshoji 0:83889dc90473 170 midiEventNote = midiEvent;
kshoji 0:83889dc90473 171 onSongSelect(midiEventNote);
kshoji 0:83889dc90473 172 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 173 break;
kshoji 0:83889dc90473 174 default:
kshoji 0:83889dc90473 175 // illegal state
kshoji 0:83889dc90473 176 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 177 break;
kshoji 0:83889dc90473 178 }
kshoji 0:83889dc90473 179 }
kshoji 0:83889dc90473 180 break;
kshoji 0:83889dc90473 181 default:
kshoji 0:83889dc90473 182 // illegal state
kshoji 0:83889dc90473 183 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 184 break;
kshoji 0:83889dc90473 185 }
kshoji 0:83889dc90473 186 } else if (midiState == MIDI_STATE_SIGNAL_3BYTES_2) {
kshoji 0:83889dc90473 187 switch (midiEventKind & 0xf0) {
kshoji 0:83889dc90473 188 case 0x80:
kshoji 0:83889dc90473 189 case 0x90:
kshoji 0:83889dc90473 190 case 0xa0:
kshoji 0:83889dc90473 191 case 0xb0:
kshoji 0:83889dc90473 192 case 0xe0:
kshoji 0:83889dc90473 193 case 0xf0:
kshoji 0:83889dc90473 194 // 3bytes pattern
kshoji 0:83889dc90473 195 midiEventNote = midiEvent;
kshoji 0:83889dc90473 196 midiState = MIDI_STATE_SIGNAL_3BYTES_3;
kshoji 0:83889dc90473 197 break;
kshoji 0:83889dc90473 198 default:
kshoji 0:83889dc90473 199 // illegal state
kshoji 0:83889dc90473 200 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 201 break;
kshoji 0:83889dc90473 202 }
kshoji 0:83889dc90473 203 } else if (midiState == MIDI_STATE_SIGNAL_3BYTES_3) {
kshoji 0:83889dc90473 204 switch (midiEventKind & 0xf0) {
kshoji 0:83889dc90473 205 // 3bytes pattern
kshoji 0:83889dc90473 206 case 0x80: // note off
kshoji 0:83889dc90473 207 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 208 onNoteOff(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 209 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 210 break;
kshoji 0:83889dc90473 211 case 0x90: // note on
kshoji 0:83889dc90473 212 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 213 if (midiEventVelocity == 0) {
kshoji 0:83889dc90473 214 onNoteOff(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 215 } else {
kshoji 0:83889dc90473 216 onNoteOn(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 217 }
kshoji 0:83889dc90473 218 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 219 break;
kshoji 0:83889dc90473 220 case 0xa0: // control polyphonic key pressure
kshoji 0:83889dc90473 221 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 222 onPolyphonicAftertouch(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 223 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 224 break;
kshoji 0:83889dc90473 225 case 0xb0: // control change
kshoji 0:83889dc90473 226 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 227 onControlChange(midiEventKind & 0xf, midiEventNote, midiEventVelocity);
kshoji 0:83889dc90473 228 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 229 break;
kshoji 0:83889dc90473 230 case 0xe0: // pitch bend
kshoji 0:83889dc90473 231 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 232 onPitchWheel(midiEventKind & 0xf, (midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
kshoji 0:83889dc90473 233 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 234 break;
kshoji 0:83889dc90473 235 case 0xf0: // Song Position Pointer.
kshoji 0:83889dc90473 236 midiEventVelocity = midiEvent;
kshoji 0:83889dc90473 237 onSongPositionPointer((midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7));
kshoji 0:83889dc90473 238 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 239 break;
kshoji 0:83889dc90473 240 default:
kshoji 0:83889dc90473 241 // illegal state
kshoji 0:83889dc90473 242 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 243 break;
kshoji 0:83889dc90473 244 }
kshoji 0:83889dc90473 245 } else if (midiState == MIDI_STATE_SIGNAL_SYSEX) {
kshoji 0:83889dc90473 246 if (midiEvent == 0xf7) {
kshoji 0:83889dc90473 247 // the end of message
kshoji 0:83889dc90473 248 // last written uint8_t is for timestamp
kshoji 0:83889dc90473 249 sysExBuffer[sysExBufferPos - 1] = midiEvent;
kshoji 0:83889dc90473 250 onSystemExclusive(sysExBuffer, sysExBufferPos);
kshoji 0:83889dc90473 251
kshoji 0:83889dc90473 252 sysExBufferPos = 0;
kshoji 0:83889dc90473 253 midiState = MIDI_STATE_TIMESTAMP;
kshoji 0:83889dc90473 254 } else {
kshoji 0:83889dc90473 255 sysExBuffer[sysExBufferPos++] = midiEvent;
kshoji 0:83889dc90473 256 }
kshoji 0:83889dc90473 257 }
kshoji 0:83889dc90473 258 }
kshoji 0:83889dc90473 259 }
kshoji 0:83889dc90473 260 }
kshoji 0:83889dc90473 261
kshoji 0:83889dc90473 262 BLEMIDI::BLEMIDI(BLEDevice *dev) {
kshoji 0:83889dc90473 263 device = dev;
kshoji 0:83889dc90473 264 isConnected = false;
kshoji 0:83889dc90473 265 sysExBufferPos = 0;
kshoji 0:83889dc90473 266
kshoji 0:83889dc90473 267 // Advertise packet length is 31 bytes.
kshoji 0:83889dc90473 268 // device name is upto 4 bytes if the service UUID included
kshoji 0:83889dc90473 269 const char DEVICE_NAME[] = "MIDI";
kshoji 0:83889dc90473 270
kshoji 0:83889dc90473 271 // MIDI characteristic
kshoji 0:83889dc90473 272 uint8_t midiPayload[20];
kshoji 0:83889dc90473 273 LongUUIDBytes_t characteristicUuid = {
kshoji 0:83889dc90473 274 0x77, 0x72, 0xe5, 0xdb, 0x38, 0x68, 0x41, 0x12,
kshoji 0:83889dc90473 275 0xa1, 0xa9, 0xf2, 0x66, 0x9d, 0x10, 0x6b, 0xf3
kshoji 0:83889dc90473 276 };
kshoji 0:83889dc90473 277 GattCharacteristic midiChar(UUID(characteristicUuid), midiPayload, 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 278 midiCharacteristic = &midiChar;
kshoji 0:83889dc90473 279
kshoji 0:83889dc90473 280 GattCharacteristic *midiChars[] = {midiCharacteristic};
kshoji 0:83889dc90473 281
kshoji 0:83889dc90473 282 // MIDI service
kshoji 0:83889dc90473 283 LongUUIDBytes_t serviceUuid = {
kshoji 0:83889dc90473 284 0x03, 0xb8, 0x0e, 0x5a, 0xed, 0xe8, 0x4b, 0x33,
kshoji 0:83889dc90473 285 0xa7, 0x51, 0x6c, 0xe3, 0x4e, 0xc4, 0xc7, 0x00
kshoji 0:83889dc90473 286 };
kshoji 0:83889dc90473 287
kshoji 0:83889dc90473 288 GattService midiService(UUID(serviceUuid), midiChars, sizeof(midiChars) / sizeof(GattCharacteristic *));
kshoji 0:83889dc90473 289 uint8_t uuid128_list[] = {
kshoji 0:83889dc90473 290 0x00, 0xc7, 0xc4, 0x4e, 0xe3, 0x6c, 0x51, 0xa7,
kshoji 0:83889dc90473 291 0x33, 0x4b, 0xe8, 0xed, 0x5a, 0x0e, 0xb8, 0x03
kshoji 0:83889dc90473 292 };
kshoji 0:83889dc90473 293
kshoji 0:83889dc90473 294 device->init();
kshoji 0:83889dc90473 295 device->reset();
kshoji 0:83889dc90473 296
kshoji 0:83889dc90473 297 /* setup callbacks */
kshoji 0:83889dc90473 298 device->onDataWritten(this, &BLEMIDI::dataWrittenCallback);
kshoji 0:83889dc90473 299
kshoji 0:83889dc90473 300 device->addService(midiService);
kshoji 0:83889dc90473 301
kshoji 0:83889dc90473 302 /* setup advertising */
kshoji 0:83889dc90473 303 device->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t*)uuid128_list, sizeof(uuid128_list));
kshoji 0:83889dc90473 304 device->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
kshoji 0:83889dc90473 305 device->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
kshoji 0:83889dc90473 306
kshoji 0:83889dc90473 307 device->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
kshoji 0:83889dc90473 308 device->setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */
kshoji 0:83889dc90473 309
kshoji 0:83889dc90473 310 device->startAdvertising();
kshoji 0:83889dc90473 311 tick.start();
kshoji 0:83889dc90473 312 }
kshoji 0:83889dc90473 313
kshoji 0:83889dc90473 314 bool BLEMIDI::connected() {
kshoji 0:83889dc90473 315 return isConnected;
kshoji 0:83889dc90473 316 }
kshoji 0:83889dc90473 317
kshoji 0:83889dc90473 318 void BLEMIDI::sendMidiMessage(uint8_t data0) {
kshoji 0:83889dc90473 319 if (isConnected) {
kshoji 0:83889dc90473 320 uint8_t midi[3];
kshoji 0:83889dc90473 321
kshoji 0:83889dc90473 322 unsigned int ticks = tick.read_ms() & 0x1fff;
kshoji 0:83889dc90473 323 midi[0] = 0x80 | (ticks >> 7) & 0x3f;
kshoji 0:83889dc90473 324 midi[1] = ticks & 0x7f;
kshoji 0:83889dc90473 325 midi[2] = data0;
kshoji 0:83889dc90473 326
kshoji 0:83889dc90473 327 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 3);
kshoji 0:83889dc90473 328 }
kshoji 0:83889dc90473 329 }
kshoji 0:83889dc90473 330
kshoji 0:83889dc90473 331 void BLEMIDI::sendMidiMessage(uint8_t data0, uint8_t data1) {
kshoji 0:83889dc90473 332 if (isConnected) {
kshoji 0:83889dc90473 333 uint8_t midi[4];
kshoji 0:83889dc90473 334
kshoji 0:83889dc90473 335 unsigned int ticks = tick.read_ms() & 0x1fff;
kshoji 0:83889dc90473 336 midi[0] = 0x80 | (ticks >> 7) & 0x3f;
kshoji 0:83889dc90473 337 midi[1] = ticks & 0x7f;
kshoji 0:83889dc90473 338 midi[2] = data0;
kshoji 0:83889dc90473 339 midi[3] = data1;
kshoji 0:83889dc90473 340
kshoji 0:83889dc90473 341 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 4);
kshoji 0:83889dc90473 342 }
kshoji 0:83889dc90473 343 }
kshoji 0:83889dc90473 344
kshoji 0:83889dc90473 345 void BLEMIDI::sendMidiMessage(uint8_t data0, uint8_t data1, uint8_t data2) {
kshoji 0:83889dc90473 346 if (isConnected) {
kshoji 0:83889dc90473 347 uint8_t midi[5];
kshoji 0:83889dc90473 348
kshoji 0:83889dc90473 349 unsigned int ticks = tick.read_ms() & 0x1fff;
kshoji 0:83889dc90473 350 midi[0] = 0x80 | (ticks >> 7) & 0x3f;
kshoji 0:83889dc90473 351 midi[1] = ticks & 0x7f;
kshoji 0:83889dc90473 352 midi[2] = data0;
kshoji 0:83889dc90473 353 midi[3] = data1;
kshoji 0:83889dc90473 354 midi[4] = data2;
kshoji 0:83889dc90473 355
kshoji 0:83889dc90473 356 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 5);
kshoji 0:83889dc90473 357 }
kshoji 0:83889dc90473 358 }
kshoji 0:83889dc90473 359
kshoji 0:83889dc90473 360 void BLEMIDI::sendTuneRequest() {
kshoji 0:83889dc90473 361 sendMidiMessage(0xf6);
kshoji 0:83889dc90473 362 }
kshoji 0:83889dc90473 363 void BLEMIDI::sendTimingClock() {
kshoji 0:83889dc90473 364 sendMidiMessage(0xf8);
kshoji 0:83889dc90473 365 }
kshoji 0:83889dc90473 366 void BLEMIDI::sendStart() {
kshoji 0:83889dc90473 367 sendMidiMessage(0xfa);
kshoji 0:83889dc90473 368 }
kshoji 0:83889dc90473 369 void BLEMIDI::sendContinue() {
kshoji 0:83889dc90473 370 sendMidiMessage(0xfb);
kshoji 0:83889dc90473 371 }
kshoji 0:83889dc90473 372 void BLEMIDI::sendStop() {
kshoji 0:83889dc90473 373 sendMidiMessage(0xfc);
kshoji 0:83889dc90473 374 }
kshoji 0:83889dc90473 375 void BLEMIDI::sendActiveSensing() {
kshoji 0:83889dc90473 376 sendMidiMessage(0xfe);
kshoji 0:83889dc90473 377 }
kshoji 0:83889dc90473 378 void BLEMIDI::sendReset() {
kshoji 0:83889dc90473 379 sendMidiMessage(0xff);
kshoji 0:83889dc90473 380 }
kshoji 0:83889dc90473 381 void BLEMIDI::sendProgramChange(uint8_t channel, uint8_t program) {
kshoji 0:83889dc90473 382 sendMidiMessage(0xc0 | (channel & 0xf), program);
kshoji 0:83889dc90473 383 }
kshoji 0:83889dc90473 384 void BLEMIDI::sendChannelAftertouch(uint8_t channel, uint8_t pressure) {
kshoji 0:83889dc90473 385 sendMidiMessage(0xd0 | (channel & 0xf), pressure);
kshoji 0:83889dc90473 386 }
kshoji 0:83889dc90473 387 void BLEMIDI::sendTimeCodeQuarterFrame(uint8_t timing) {
kshoji 0:83889dc90473 388 sendMidiMessage(0xf1, timing & 0x7f);
kshoji 0:83889dc90473 389 }
kshoji 0:83889dc90473 390 void BLEMIDI::sendSongSelect(uint8_t song) {
kshoji 0:83889dc90473 391 sendMidiMessage(0xf3, song & 0x7f);
kshoji 0:83889dc90473 392 }
kshoji 0:83889dc90473 393 void BLEMIDI::sendNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) {
kshoji 0:83889dc90473 394 sendMidiMessage(0x80 | (channel & 0xf), note, velocity);
kshoji 0:83889dc90473 395 }
kshoji 0:83889dc90473 396 void BLEMIDI::sendNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {
kshoji 0:83889dc90473 397 sendMidiMessage(0x90 | (channel & 0xf), note, velocity);
kshoji 0:83889dc90473 398 }
kshoji 0:83889dc90473 399 void BLEMIDI::sendPolyphonicAftertouch(uint8_t channel, uint8_t note, uint8_t pressure) {
kshoji 0:83889dc90473 400 sendMidiMessage(0xa0 | (channel & 0xf), note, pressure);
kshoji 0:83889dc90473 401 }
kshoji 0:83889dc90473 402 void BLEMIDI::sendControlChange(uint8_t channel, uint8_t function, uint8_t value) {
kshoji 0:83889dc90473 403 sendMidiMessage(0xb0 | (channel & 0xf), function, value);
kshoji 0:83889dc90473 404 }
kshoji 0:83889dc90473 405 void BLEMIDI::sendPitchWheel(uint8_t channel, uint16_t amount) {
kshoji 0:83889dc90473 406 sendMidiMessage(0xe0 | (channel & 0xf), amount & 0x7f, (amount >> 7) & 0x7f);
kshoji 0:83889dc90473 407 }
kshoji 0:83889dc90473 408 void BLEMIDI::sendSongPositionPointer(uint16_t position) {
kshoji 0:83889dc90473 409 sendMidiMessage(0xf2, position & 0x7f, (position >> 7) & 0x7f);
kshoji 0:83889dc90473 410 }
kshoji 0:83889dc90473 411 void BLEMIDI::sendSystemExclusive(uint8_t * sysex, uint16_t length) {
kshoji 0:83889dc90473 412 if (isConnected) {
kshoji 0:83889dc90473 413 uint8_t midi[20];
kshoji 0:83889dc90473 414 uint8_t position = 0;
kshoji 0:83889dc90473 415
kshoji 0:83889dc90473 416 // header
kshoji 0:83889dc90473 417 unsigned int ticks = tick.read_ms() & 0x1fff;
kshoji 0:83889dc90473 418 midi[position++] = 0x80 | (ticks >> 7) & 0x3f;
kshoji 0:83889dc90473 419 midi[position++] = ticks & 0x7f;
kshoji 0:83889dc90473 420
kshoji 0:83889dc90473 421 unsigned int timestamp = tick.read_ms() & 0x1fff;
kshoji 0:83889dc90473 422 for (int i = 0; i < length; i++) {
kshoji 0:83889dc90473 423 if (i == length - 1) {
kshoji 0:83889dc90473 424 // last byte
kshoji 0:83889dc90473 425 midi[position++] = 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 }