bug fix
Dependencies: BLE_API mbed nRF51822
Fork of BLE_MIDI by
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 | } |