Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: BLE_API mbed nRF51822
BLEMIDI.cpp@1:cba2eba64f5c, 2015-04-03 (annotated)
- Committer:
- kshoji
- Date:
- Fri Apr 03 04:24:01 2015 +0000
- Revision:
- 1:cba2eba64f5c
- Parent:
- 0:83889dc90473
- Child:
- 2:dbc6f81b9ba0
Tested MIDI sending
Who changed what in which revision?
| User | Revision | Line number | New 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 | 1:cba2eba64f5c | 249 | if (sysExBufferPos > 0) { | 
| kshoji | 1:cba2eba64f5c | 250 | sysExBuffer[sysExBufferPos - 1] = midiEvent; | 
| kshoji | 1:cba2eba64f5c | 251 | onSystemExclusive(sysExBuffer, sysExBufferPos); | 
| kshoji | 1:cba2eba64f5c | 252 | } | 
| kshoji | 0:83889dc90473 | 253 | |
| kshoji | 0:83889dc90473 | 254 | sysExBufferPos = 0; | 
| kshoji | 0:83889dc90473 | 255 | midiState = MIDI_STATE_TIMESTAMP; | 
| kshoji | 0:83889dc90473 | 256 | } else { | 
| kshoji | 0:83889dc90473 | 257 | sysExBuffer[sysExBufferPos++] = midiEvent; | 
| kshoji | 0:83889dc90473 | 258 | } | 
| kshoji | 0:83889dc90473 | 259 | } | 
| kshoji | 0:83889dc90473 | 260 | } | 
| kshoji | 0:83889dc90473 | 261 | } | 
| kshoji | 0:83889dc90473 | 262 | } | 
| kshoji | 0:83889dc90473 | 263 | |
| kshoji | 0:83889dc90473 | 264 | BLEMIDI::BLEMIDI(BLEDevice *dev) { | 
| kshoji | 0:83889dc90473 | 265 | device = dev; | 
| kshoji | 0:83889dc90473 | 266 | isConnected = false; | 
| kshoji | 0:83889dc90473 | 267 | sysExBufferPos = 0; | 
| kshoji | 0:83889dc90473 | 268 | |
| kshoji | 1:cba2eba64f5c | 269 | timestamp = 0; | 
| kshoji | 1:cba2eba64f5c | 270 | midiEventKind = 0; | 
| kshoji | 1:cba2eba64f5c | 271 | midiEventNote = 0; | 
| kshoji | 1:cba2eba64f5c | 272 | midiEventVelocity = 0; | 
| kshoji | 1:cba2eba64f5c | 273 | |
| kshoji | 0:83889dc90473 | 274 | // Advertise packet length is 31 bytes. | 
| kshoji | 0:83889dc90473 | 275 | // device name is upto 4 bytes if the service UUID included | 
| kshoji | 0:83889dc90473 | 276 | const char DEVICE_NAME[] = "MIDI"; | 
| kshoji | 0:83889dc90473 | 277 | |
| kshoji | 0:83889dc90473 | 278 | // MIDI characteristic | 
| kshoji | 0:83889dc90473 | 279 | LongUUIDBytes_t characteristicUuid = { | 
| kshoji | 0:83889dc90473 | 280 | 0x77, 0x72, 0xe5, 0xdb, 0x38, 0x68, 0x41, 0x12, | 
| kshoji | 0:83889dc90473 | 281 | 0xa1, 0xa9, 0xf2, 0x66, 0x9d, 0x10, 0x6b, 0xf3 | 
| kshoji | 0:83889dc90473 | 282 | }; | 
| kshoji | 0:83889dc90473 | 283 | |
| kshoji | 1:cba2eba64f5c | 284 | 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 | 285 | GattCharacteristic *midiChars[] = {midiCharacteristic}; | 
| kshoji | 0:83889dc90473 | 286 | |
| kshoji | 0:83889dc90473 | 287 | // MIDI service | 
| kshoji | 0:83889dc90473 | 288 | LongUUIDBytes_t serviceUuid = { | 
| kshoji | 0:83889dc90473 | 289 | 0x03, 0xb8, 0x0e, 0x5a, 0xed, 0xe8, 0x4b, 0x33, | 
| kshoji | 0:83889dc90473 | 290 | 0xa7, 0x51, 0x6c, 0xe3, 0x4e, 0xc4, 0xc7, 0x00 | 
| kshoji | 0:83889dc90473 | 291 | }; | 
| kshoji | 0:83889dc90473 | 292 | |
| kshoji | 0:83889dc90473 | 293 | GattService midiService(UUID(serviceUuid), midiChars, sizeof(midiChars) / sizeof(GattCharacteristic *)); | 
| kshoji | 0:83889dc90473 | 294 | uint8_t uuid128_list[] = { | 
| kshoji | 0:83889dc90473 | 295 | 0x00, 0xc7, 0xc4, 0x4e, 0xe3, 0x6c, 0x51, 0xa7, | 
| kshoji | 0:83889dc90473 | 296 | 0x33, 0x4b, 0xe8, 0xed, 0x5a, 0x0e, 0xb8, 0x03 | 
| kshoji | 0:83889dc90473 | 297 | }; | 
| kshoji | 0:83889dc90473 | 298 | |
| kshoji | 0:83889dc90473 | 299 | device->init(); | 
| kshoji | 0:83889dc90473 | 300 | device->reset(); | 
| kshoji | 0:83889dc90473 | 301 | |
| kshoji | 0:83889dc90473 | 302 | /* setup callbacks */ | 
| kshoji | 0:83889dc90473 | 303 | device->onDataWritten(this, &BLEMIDI::dataWrittenCallback); | 
| kshoji | 0:83889dc90473 | 304 | |
| kshoji | 0:83889dc90473 | 305 | device->addService(midiService); | 
| kshoji | 0:83889dc90473 | 306 | |
| kshoji | 0:83889dc90473 | 307 | /* setup advertising */ | 
| kshoji | 0:83889dc90473 | 308 | device->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t*)uuid128_list, sizeof(uuid128_list)); | 
| kshoji | 0:83889dc90473 | 309 | device->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); | 
| kshoji | 0:83889dc90473 | 310 | device->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); | 
| kshoji | 0:83889dc90473 | 311 | |
| kshoji | 0:83889dc90473 | 312 | device->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); | 
| kshoji | 0:83889dc90473 | 313 | device->setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */ | 
| kshoji | 0:83889dc90473 | 314 | |
| kshoji | 0:83889dc90473 | 315 | device->startAdvertising(); | 
| kshoji | 0:83889dc90473 | 316 | tick.start(); | 
| kshoji | 0:83889dc90473 | 317 | } | 
| kshoji | 0:83889dc90473 | 318 | |
| kshoji | 0:83889dc90473 | 319 | bool BLEMIDI::connected() { | 
| kshoji | 0:83889dc90473 | 320 | return isConnected; | 
| kshoji | 0:83889dc90473 | 321 | } | 
| kshoji | 0:83889dc90473 | 322 | |
| kshoji | 0:83889dc90473 | 323 | void BLEMIDI::sendMidiMessage(uint8_t data0) { | 
| kshoji | 0:83889dc90473 | 324 | if (isConnected) { | 
| kshoji | 0:83889dc90473 | 325 | unsigned int ticks = tick.read_ms() & 0x1fff; | 
| kshoji | 1:cba2eba64f5c | 326 | midi[0] = 0x80 | ((ticks >> 7) & 0x3f); | 
| kshoji | 1:cba2eba64f5c | 327 | midi[1] = 0x80 | (ticks & 0x7f); | 
| kshoji | 0:83889dc90473 | 328 | midi[2] = data0; | 
| kshoji | 0:83889dc90473 | 329 | |
| kshoji | 0:83889dc90473 | 330 | device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 3); | 
| kshoji | 0:83889dc90473 | 331 | } | 
| kshoji | 0:83889dc90473 | 332 | } | 
| kshoji | 0:83889dc90473 | 333 | |
| kshoji | 0:83889dc90473 | 334 | void BLEMIDI::sendMidiMessage(uint8_t data0, uint8_t data1) { | 
| kshoji | 0:83889dc90473 | 335 | if (isConnected) { | 
| kshoji | 0:83889dc90473 | 336 | unsigned int ticks = tick.read_ms() & 0x1fff; | 
| kshoji | 1:cba2eba64f5c | 337 | midi[0] = 0x80 | ((ticks >> 7) & 0x3f); | 
| kshoji | 1:cba2eba64f5c | 338 | midi[1] = 0x80 | (ticks & 0x7f); | 
| kshoji | 0:83889dc90473 | 339 | midi[2] = data0; | 
| kshoji | 0:83889dc90473 | 340 | midi[3] = data1; | 
| kshoji | 0:83889dc90473 | 341 | |
| kshoji | 0:83889dc90473 | 342 | device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 4); | 
| kshoji | 0:83889dc90473 | 343 | } | 
| kshoji | 0:83889dc90473 | 344 | } | 
| kshoji | 0:83889dc90473 | 345 | |
| kshoji | 0:83889dc90473 | 346 | void BLEMIDI::sendMidiMessage(uint8_t data0, uint8_t data1, uint8_t data2) { | 
| kshoji | 0:83889dc90473 | 347 | if (isConnected) { | 
| kshoji | 0:83889dc90473 | 348 | unsigned int ticks = tick.read_ms() & 0x1fff; | 
| kshoji | 1:cba2eba64f5c | 349 | midi[0] = 0x80 | ((ticks >> 7) & 0x3f); | 
| kshoji | 1:cba2eba64f5c | 350 | midi[1] = 0x80 | (ticks & 0x7f); | 
| kshoji | 0:83889dc90473 | 351 | midi[2] = data0; | 
| kshoji | 0:83889dc90473 | 352 | midi[3] = data1; | 
| kshoji | 0:83889dc90473 | 353 | midi[4] = data2; | 
| kshoji | 0:83889dc90473 | 354 | |
| kshoji | 0:83889dc90473 | 355 | device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 5); | 
| kshoji | 0:83889dc90473 | 356 | } | 
| kshoji | 0:83889dc90473 | 357 | } | 
| kshoji | 0:83889dc90473 | 358 | |
| kshoji | 0:83889dc90473 | 359 | void BLEMIDI::sendTuneRequest() { | 
| kshoji | 0:83889dc90473 | 360 | sendMidiMessage(0xf6); | 
| kshoji | 0:83889dc90473 | 361 | } | 
| kshoji | 0:83889dc90473 | 362 | void BLEMIDI::sendTimingClock() { | 
| kshoji | 0:83889dc90473 | 363 | sendMidiMessage(0xf8); | 
| kshoji | 0:83889dc90473 | 364 | } | 
| kshoji | 0:83889dc90473 | 365 | void BLEMIDI::sendStart() { | 
| kshoji | 0:83889dc90473 | 366 | sendMidiMessage(0xfa); | 
| kshoji | 0:83889dc90473 | 367 | } | 
| kshoji | 0:83889dc90473 | 368 | void BLEMIDI::sendContinue() { | 
| kshoji | 0:83889dc90473 | 369 | sendMidiMessage(0xfb); | 
| kshoji | 0:83889dc90473 | 370 | } | 
| kshoji | 0:83889dc90473 | 371 | void BLEMIDI::sendStop() { | 
| kshoji | 0:83889dc90473 | 372 | sendMidiMessage(0xfc); | 
| kshoji | 0:83889dc90473 | 373 | } | 
| kshoji | 0:83889dc90473 | 374 | void BLEMIDI::sendActiveSensing() { | 
| kshoji | 0:83889dc90473 | 375 | sendMidiMessage(0xfe); | 
| kshoji | 0:83889dc90473 | 376 | } | 
| kshoji | 0:83889dc90473 | 377 | void BLEMIDI::sendReset() { | 
| kshoji | 0:83889dc90473 | 378 | sendMidiMessage(0xff); | 
| kshoji | 0:83889dc90473 | 379 | } | 
| kshoji | 0:83889dc90473 | 380 | void BLEMIDI::sendProgramChange(uint8_t channel, uint8_t program) { | 
| kshoji | 0:83889dc90473 | 381 | sendMidiMessage(0xc0 | (channel & 0xf), program); | 
| kshoji | 0:83889dc90473 | 382 | } | 
| kshoji | 0:83889dc90473 | 383 | void BLEMIDI::sendChannelAftertouch(uint8_t channel, uint8_t pressure) { | 
| kshoji | 0:83889dc90473 | 384 | sendMidiMessage(0xd0 | (channel & 0xf), pressure); | 
| kshoji | 0:83889dc90473 | 385 | } | 
| kshoji | 0:83889dc90473 | 386 | void BLEMIDI::sendTimeCodeQuarterFrame(uint8_t timing) { | 
| kshoji | 0:83889dc90473 | 387 | sendMidiMessage(0xf1, timing & 0x7f); | 
| kshoji | 0:83889dc90473 | 388 | } | 
| kshoji | 0:83889dc90473 | 389 | void BLEMIDI::sendSongSelect(uint8_t song) { | 
| kshoji | 0:83889dc90473 | 390 | sendMidiMessage(0xf3, song & 0x7f); | 
| kshoji | 0:83889dc90473 | 391 | } | 
| kshoji | 0:83889dc90473 | 392 | void BLEMIDI::sendNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) { | 
| kshoji | 0:83889dc90473 | 393 | sendMidiMessage(0x80 | (channel & 0xf), note, velocity); | 
| kshoji | 0:83889dc90473 | 394 | } | 
| kshoji | 0:83889dc90473 | 395 | void BLEMIDI::sendNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { | 
| kshoji | 0:83889dc90473 | 396 | sendMidiMessage(0x90 | (channel & 0xf), note, velocity); | 
| kshoji | 0:83889dc90473 | 397 | } | 
| kshoji | 0:83889dc90473 | 398 | void BLEMIDI::sendPolyphonicAftertouch(uint8_t channel, uint8_t note, uint8_t pressure) { | 
| kshoji | 0:83889dc90473 | 399 | sendMidiMessage(0xa0 | (channel & 0xf), note, pressure); | 
| kshoji | 0:83889dc90473 | 400 | } | 
| kshoji | 0:83889dc90473 | 401 | void BLEMIDI::sendControlChange(uint8_t channel, uint8_t function, uint8_t value) { | 
| kshoji | 0:83889dc90473 | 402 | sendMidiMessage(0xb0 | (channel & 0xf), function, value); | 
| kshoji | 0:83889dc90473 | 403 | } | 
| kshoji | 0:83889dc90473 | 404 | void BLEMIDI::sendPitchWheel(uint8_t channel, uint16_t amount) { | 
| kshoji | 0:83889dc90473 | 405 | sendMidiMessage(0xe0 | (channel & 0xf), amount & 0x7f, (amount >> 7) & 0x7f); | 
| kshoji | 0:83889dc90473 | 406 | } | 
| kshoji | 0:83889dc90473 | 407 | void BLEMIDI::sendSongPositionPointer(uint16_t position) { | 
| kshoji | 0:83889dc90473 | 408 | sendMidiMessage(0xf2, position & 0x7f, (position >> 7) & 0x7f); | 
| kshoji | 0:83889dc90473 | 409 | } | 
| kshoji | 0:83889dc90473 | 410 | void BLEMIDI::sendSystemExclusive(uint8_t * sysex, uint16_t length) { | 
| kshoji | 0:83889dc90473 | 411 | if (isConnected) { | 
| kshoji | 0:83889dc90473 | 412 | uint8_t position = 0; | 
| kshoji | 0:83889dc90473 | 413 | |
| kshoji | 0:83889dc90473 | 414 | // header | 
| kshoji | 1:cba2eba64f5c | 415 | uint16_t ticks = tick.read_ms() & 0x1fff; | 
| kshoji | 1:cba2eba64f5c | 416 | midi[position++] = 0x80 | ((ticks >> 7) & 0x3f); | 
| kshoji | 1:cba2eba64f5c | 417 | midi[position++] = 0x80 | (ticks & 0x7f); | 
| kshoji | 0:83889dc90473 | 418 | |
| kshoji | 0:83889dc90473 | 419 | for (int i = 0; i < length; i++) { | 
| kshoji | 0:83889dc90473 | 420 | if (i == length - 1) { | 
| kshoji | 1:cba2eba64f5c | 421 | // modify last byte | 
| kshoji | 1:cba2eba64f5c | 422 | midi[position++] = 0x80 | (ticks & 0x7f); | 
| kshoji | 0:83889dc90473 | 423 | |
| kshoji | 0:83889dc90473 | 424 | if (position == 20) { | 
| kshoji | 0:83889dc90473 | 425 | device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 20); | 
| kshoji | 0:83889dc90473 | 426 | |
| kshoji | 0:83889dc90473 | 427 | position = 0; | 
| kshoji | 0:83889dc90473 | 428 | // header | 
| kshoji | 0:83889dc90473 | 429 | midi[position++] = 0x80 | (ticks >> 7) & 0x3f; | 
| kshoji | 0:83889dc90473 | 430 | } | 
| kshoji | 0:83889dc90473 | 431 | } | 
| kshoji | 0:83889dc90473 | 432 | midi[position++] = sysex[i]; | 
| kshoji | 0:83889dc90473 | 433 | if (position == 20) { | 
| kshoji | 0:83889dc90473 | 434 | device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 20); | 
| kshoji | 0:83889dc90473 | 435 | |
| kshoji | 0:83889dc90473 | 436 | position = 0; | 
| kshoji | 0:83889dc90473 | 437 | // header | 
| kshoji | 0:83889dc90473 | 438 | midi[position++] = 0x80 | (ticks >> 7) & 0x3f; | 
| kshoji | 0:83889dc90473 | 439 | } | 
| kshoji | 0:83889dc90473 | 440 | |
| kshoji | 0:83889dc90473 | 441 | ticks = tick.read_ms() & 0x1fff; | 
| kshoji | 0:83889dc90473 | 442 | } | 
| kshoji | 0:83889dc90473 | 443 | |
| kshoji | 0:83889dc90473 | 444 | if (position > 0) { | 
| kshoji | 0:83889dc90473 | 445 | // send remains | 
| kshoji | 0:83889dc90473 | 446 | device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, position); | 
| kshoji | 0:83889dc90473 | 447 | } | 
| kshoji | 0:83889dc90473 | 448 | } | 
| kshoji | 0:83889dc90473 | 449 | } |