![](/media/cache/img/default_profile.jpg.50x50_q85.jpg)
Dependencies: BLE_API mbed nRF51822_BLE_MIDI
Dependents: BLE_MIDI_one_note_TEST
Fork of BLE_MIDI by
BLEMIDI.cpp@5:7ffe438d3ed8, 2017-08-10 (annotated)
- Committer:
- mistery
- Date:
- Thu Aug 10 14:21:39 2017 +0000
- Revision:
- 5:7ffe438d3ed8
- Parent:
- 2:dbc6f81b9ba0
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 | 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 | } |