Utility class for MIDI over Bluetooth LE
Dependencies: BLE_API mbed nRF51822
BLEMIDI.cpp
00001 /* Copyright (c) 2014 mbed.org, MIT License 00002 * 00003 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 00004 * and associated documentation files (the "Software"), to deal in the Software without 00005 * restriction, including without limitation the rights to use, copy, modify, merge, publish, 00006 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 00007 * Software is furnished to do so, subject to the following conditions: 00008 * 00009 * The above copyright notice and this permission notice shall be included in all copies or 00010 * substantial portions of the Software. 00011 * 00012 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 00013 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 00014 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 00015 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 00016 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00017 */ 00018 00019 #include "mbed.h" 00020 #include "BLEMIDI.h" 00021 00022 void BLEMIDI::onBleDisconnection(Gap::Handle_t handle, Gap::DisconnectionReason_t reason) { 00023 device->startAdvertising(); 00024 isConnected = false; 00025 } 00026 00027 void BLEMIDI::onBleConnection(Gap::Handle_t handle, Gap::addr_type_t type, const Gap::address_t addr, const Gap::ConnectionParams_t *params) { 00028 isConnected = true; 00029 } 00030 00031 void BLEMIDI::dataWrittenCallback(const GattCharacteristicWriteCBParams *params) { 00032 uint16_t length; 00033 uint8_t rxBuffer[20]; 00034 00035 device->readCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), rxBuffer, &length); 00036 00037 if (length > 1) { 00038 // parse BLE message 00039 uint8_t header = rxBuffer[0]; 00040 for (int i = 1; i < length; i++) { 00041 uint8_t midiEvent = rxBuffer[i]; 00042 00043 if (midiState == MIDI_STATE_TIMESTAMP) { 00044 if ((midiEvent & 0x80) == 0) { 00045 // running status 00046 midiState = MIDI_STATE_WAIT; 00047 } 00048 00049 if (midiEvent == 0xf7) { 00050 // maybe error 00051 midiState = MIDI_STATE_TIMESTAMP; 00052 continue; 00053 } 00054 } 00055 if (midiState == MIDI_STATE_TIMESTAMP) { 00056 timestamp = ((header & 0x3f) << 7) | (midiEvent & 0x7f); 00057 midiState = MIDI_STATE_WAIT; 00058 } else if (midiState == MIDI_STATE_WAIT) { 00059 switch (midiEvent & 0xf0) { 00060 case 0xf0: { 00061 switch (midiEvent) { 00062 case 0xf0: 00063 sysExBuffer[sysExBufferPos++] = midiEvent; 00064 midiState = MIDI_STATE_SIGNAL_SYSEX; 00065 break; 00066 00067 case 0xf1: 00068 case 0xf3: 00069 // 0xf1 MIDI Time Code Quarter Frame. : 2bytes 00070 // 0xf3 Song Select. : 2bytes 00071 midiEventKind = midiEvent; 00072 midiState = MIDI_STATE_SIGNAL_2BYTES_2; 00073 break; 00074 00075 case 0xf2: 00076 // 0xf2 Song Position Pointer. : 3bytes 00077 midiEventKind = midiEvent; 00078 midiState = MIDI_STATE_SIGNAL_3BYTES_2; 00079 break; 00080 00081 case 0xf6: 00082 // 0xf6 Tune Request : 1byte 00083 onTuneRequest(); 00084 midiState = MIDI_STATE_TIMESTAMP; 00085 break; 00086 case 0xf8: 00087 // 0xf8 Timing Clock : 1byte 00088 onTimingClock(); 00089 midiState = MIDI_STATE_TIMESTAMP; 00090 break; 00091 case 0xfa: 00092 // 0xfa Start : 1byte 00093 onStart(); 00094 midiState = MIDI_STATE_TIMESTAMP; 00095 break; 00096 case 0xfb: 00097 // 0xfb Continue : 1byte 00098 onContinue(); 00099 midiState = MIDI_STATE_TIMESTAMP; 00100 break; 00101 case 0xfc: 00102 // 0xfc Stop : 1byte 00103 onStop(); 00104 midiState = MIDI_STATE_TIMESTAMP; 00105 break; 00106 case 0xfe: 00107 // 0xfe Active Sensing : 1byte 00108 onActiveSensing(); 00109 midiState = MIDI_STATE_TIMESTAMP; 00110 break; 00111 case 0xff: 00112 // 0xff Reset : 1byte 00113 onReset(); 00114 midiState = MIDI_STATE_TIMESTAMP; 00115 break; 00116 00117 default: 00118 break; 00119 } 00120 } 00121 break; 00122 case 0x80: 00123 case 0x90: 00124 case 0xa0: 00125 case 0xb0: 00126 case 0xe0: 00127 // 3bytes pattern 00128 midiEventKind = midiEvent; 00129 midiState = MIDI_STATE_SIGNAL_3BYTES_2; 00130 break; 00131 case 0xc0: // program change 00132 case 0xd0: // channel after-touch 00133 // 2bytes pattern 00134 midiEventKind = midiEvent; 00135 midiState = MIDI_STATE_SIGNAL_2BYTES_2; 00136 break; 00137 default: 00138 // 0x00 - 0x70: running status 00139 if ((midiEventKind & 0xf0) != 0xf0) { 00140 // previous event kind is multi-bytes pattern 00141 midiEventNote = midiEvent; 00142 midiState = MIDI_STATE_SIGNAL_3BYTES_3; 00143 } 00144 break; 00145 } 00146 } else if (midiState == MIDI_STATE_SIGNAL_2BYTES_2) { 00147 switch (midiEventKind & 0xf0) { 00148 // 2bytes pattern 00149 case 0xc0: // program change 00150 midiEventNote = midiEvent; 00151 onProgramChange(midiEventKind & 0xf, midiEventNote); 00152 midiState = MIDI_STATE_TIMESTAMP; 00153 break; 00154 case 0xd0: // channel after-touch 00155 midiEventNote = midiEvent; 00156 onChannelAftertouch(midiEventKind & 0xf, midiEventNote); 00157 midiState = MIDI_STATE_TIMESTAMP; 00158 break; 00159 case 0xf0: { 00160 switch (midiEventKind) { 00161 case 0xf1: 00162 // 0xf1 MIDI Time Code Quarter Frame. : 2bytes 00163 midiEventNote = midiEvent; 00164 onTimeCodeQuarterFrame(midiEventNote); 00165 midiState = MIDI_STATE_TIMESTAMP; 00166 break; 00167 case 0xf3: 00168 // 0xf3 Song Select. : 2bytes 00169 midiEventNote = midiEvent; 00170 onSongSelect(midiEventNote); 00171 midiState = MIDI_STATE_TIMESTAMP; 00172 break; 00173 default: 00174 // illegal state 00175 midiState = MIDI_STATE_TIMESTAMP; 00176 break; 00177 } 00178 } 00179 break; 00180 default: 00181 // illegal state 00182 midiState = MIDI_STATE_TIMESTAMP; 00183 break; 00184 } 00185 } else if (midiState == MIDI_STATE_SIGNAL_3BYTES_2) { 00186 switch (midiEventKind & 0xf0) { 00187 case 0x80: 00188 case 0x90: 00189 case 0xa0: 00190 case 0xb0: 00191 case 0xe0: 00192 case 0xf0: 00193 // 3bytes pattern 00194 midiEventNote = midiEvent; 00195 midiState = MIDI_STATE_SIGNAL_3BYTES_3; 00196 break; 00197 default: 00198 // illegal state 00199 midiState = MIDI_STATE_TIMESTAMP; 00200 break; 00201 } 00202 } else if (midiState == MIDI_STATE_SIGNAL_3BYTES_3) { 00203 switch (midiEventKind & 0xf0) { 00204 // 3bytes pattern 00205 case 0x80: // note off 00206 midiEventVelocity = midiEvent; 00207 onNoteOff(midiEventKind & 0xf, midiEventNote, midiEventVelocity); 00208 midiState = MIDI_STATE_TIMESTAMP; 00209 break; 00210 case 0x90: // note on 00211 midiEventVelocity = midiEvent; 00212 if (midiEventVelocity == 0) { 00213 onNoteOff(midiEventKind & 0xf, midiEventNote, midiEventVelocity); 00214 } else { 00215 onNoteOn(midiEventKind & 0xf, midiEventNote, midiEventVelocity); 00216 } 00217 midiState = MIDI_STATE_TIMESTAMP; 00218 break; 00219 case 0xa0: // control polyphonic key pressure 00220 midiEventVelocity = midiEvent; 00221 onPolyphonicAftertouch(midiEventKind & 0xf, midiEventNote, midiEventVelocity); 00222 midiState = MIDI_STATE_TIMESTAMP; 00223 break; 00224 case 0xb0: // control change 00225 midiEventVelocity = midiEvent; 00226 onControlChange(midiEventKind & 0xf, midiEventNote, midiEventVelocity); 00227 midiState = MIDI_STATE_TIMESTAMP; 00228 break; 00229 case 0xe0: // pitch bend 00230 midiEventVelocity = midiEvent; 00231 onPitchWheel(midiEventKind & 0xf, (midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7)); 00232 midiState = MIDI_STATE_TIMESTAMP; 00233 break; 00234 case 0xf0: // Song Position Pointer. 00235 midiEventVelocity = midiEvent; 00236 onSongPositionPointer((midiEventNote & 0x7f) | ((midiEventVelocity & 0x7f) << 7)); 00237 midiState = MIDI_STATE_TIMESTAMP; 00238 break; 00239 default: 00240 // illegal state 00241 midiState = MIDI_STATE_TIMESTAMP; 00242 break; 00243 } 00244 } else if (midiState == MIDI_STATE_SIGNAL_SYSEX) { 00245 if (midiEvent == 0xf7) { 00246 // the end of message 00247 // last written uint8_t is for timestamp 00248 if (sysExBufferPos > 0) { 00249 sysExBuffer[sysExBufferPos - 1] = midiEvent; 00250 onSystemExclusive(sysExBuffer, sysExBufferPos, false); 00251 } 00252 00253 sysExBufferPos = 0; 00254 midiState = MIDI_STATE_TIMESTAMP; 00255 } else { 00256 if (sysExBufferPos == 128) { 00257 onSystemExclusive(sysExBuffer, sysExBufferPos, true); 00258 sysExBufferPos = 0; 00259 } 00260 sysExBuffer[sysExBufferPos++] = midiEvent; 00261 } 00262 } 00263 } 00264 } 00265 } 00266 00267 BLEMIDI::BLEMIDI(BLEDevice *dev) { 00268 BLEMIDI(dev, "MIDI"); 00269 } 00270 00271 BLEMIDI::BLEMIDI(BLEDevice *dev, char *deviceName) { 00272 device = dev; 00273 isConnected = false; 00274 sysExBufferPos = 0; 00275 00276 timestamp = 0; 00277 midiEventKind = 0; 00278 midiEventNote = 0; 00279 midiEventVelocity = 0; 00280 00281 // MIDI characteristic 00282 LongUUIDBytes_t characteristicUuid = { 00283 0x77, 0x72, 0xe5, 0xdb, 0x38, 0x68, 0x41, 0x12, 00284 0xa1, 0xa9, 0xf2, 0x66, 0x9d, 0x10, 0x6b, 0xf3 00285 }; 00286 00287 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); 00288 GattCharacteristic *midiChars[] = {midiCharacteristic}; 00289 00290 // MIDI service 00291 LongUUIDBytes_t serviceUuid = { 00292 0x03, 0xb8, 0x0e, 0x5a, 0xed, 0xe8, 0x4b, 0x33, 00293 0xa7, 0x51, 0x6c, 0xe3, 0x4e, 0xc4, 0xc7, 0x00 00294 }; 00295 00296 GattService midiService(UUID(serviceUuid), midiChars, sizeof(midiChars) / sizeof(GattCharacteristic *)); 00297 uint8_t uuid128_list[] = { 00298 0x00, 0xc7, 0xc4, 0x4e, 0xe3, 0x6c, 0x51, 0xa7, 00299 0x33, 0x4b, 0xe8, 0xed, 0x5a, 0x0e, 0xb8, 0x03 00300 }; 00301 00302 device->init(); 00303 device->reset(); 00304 00305 /* setup callbacks */ 00306 device->onDataWritten(this, &BLEMIDI::dataWrittenCallback); 00307 00308 device->addService(midiService); 00309 00310 /* setup advertising */ 00311 device->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t*)uuid128_list, sizeof(uuid128_list)); 00312 device->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); 00313 device->accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)deviceName, sizeof(deviceName)); 00314 00315 device->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); 00316 device->setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */ 00317 00318 device->startAdvertising(); 00319 tick.start(); 00320 } 00321 00322 bool BLEMIDI::connected() { 00323 return isConnected; 00324 } 00325 00326 void BLEMIDI::sendMidiMessage(uint8_t data0) { 00327 if (isConnected) { 00328 unsigned int ticks = tick.read_ms() & 0x1fff; 00329 midi[0] = 0x80 | ((ticks >> 7) & 0x3f); 00330 midi[1] = 0x80 | (ticks & 0x7f); 00331 midi[2] = data0; 00332 00333 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 3); 00334 } 00335 } 00336 00337 void BLEMIDI::sendMidiMessage(uint8_t data0, uint8_t data1) { 00338 if (isConnected) { 00339 unsigned int ticks = tick.read_ms() & 0x1fff; 00340 midi[0] = 0x80 | ((ticks >> 7) & 0x3f); 00341 midi[1] = 0x80 | (ticks & 0x7f); 00342 midi[2] = data0; 00343 midi[3] = data1; 00344 00345 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 4); 00346 } 00347 } 00348 00349 void BLEMIDI::sendMidiMessage(uint8_t data0, uint8_t data1, uint8_t data2) { 00350 if (isConnected) { 00351 unsigned int ticks = tick.read_ms() & 0x1fff; 00352 midi[0] = 0x80 | ((ticks >> 7) & 0x3f); 00353 midi[1] = 0x80 | (ticks & 0x7f); 00354 midi[2] = data0; 00355 midi[3] = data1; 00356 midi[4] = data2; 00357 00358 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 5); 00359 } 00360 } 00361 00362 void BLEMIDI::sendTuneRequest() { 00363 sendMidiMessage(0xf6); 00364 } 00365 void BLEMIDI::sendTimingClock() { 00366 sendMidiMessage(0xf8); 00367 } 00368 void BLEMIDI::sendStart() { 00369 sendMidiMessage(0xfa); 00370 } 00371 void BLEMIDI::sendContinue() { 00372 sendMidiMessage(0xfb); 00373 } 00374 void BLEMIDI::sendStop() { 00375 sendMidiMessage(0xfc); 00376 } 00377 void BLEMIDI::sendActiveSensing() { 00378 sendMidiMessage(0xfe); 00379 } 00380 void BLEMIDI::sendReset() { 00381 sendMidiMessage(0xff); 00382 } 00383 void BLEMIDI::sendProgramChange(uint8_t channel, uint8_t program) { 00384 sendMidiMessage(0xc0 | (channel & 0xf), program); 00385 } 00386 void BLEMIDI::sendChannelAftertouch(uint8_t channel, uint8_t pressure) { 00387 sendMidiMessage(0xd0 | (channel & 0xf), pressure); 00388 } 00389 void BLEMIDI::sendTimeCodeQuarterFrame(uint8_t timing) { 00390 sendMidiMessage(0xf1, timing & 0x7f); 00391 } 00392 void BLEMIDI::sendSongSelect(uint8_t song) { 00393 sendMidiMessage(0xf3, song & 0x7f); 00394 } 00395 void BLEMIDI::sendNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) { 00396 sendMidiMessage(0x80 | (channel & 0xf), note, velocity); 00397 } 00398 void BLEMIDI::sendNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) { 00399 sendMidiMessage(0x90 | (channel & 0xf), note, velocity); 00400 } 00401 void BLEMIDI::sendPolyphonicAftertouch(uint8_t channel, uint8_t note, uint8_t pressure) { 00402 sendMidiMessage(0xa0 | (channel & 0xf), note, pressure); 00403 } 00404 void BLEMIDI::sendControlChange(uint8_t channel, uint8_t function, uint8_t value) { 00405 sendMidiMessage(0xb0 | (channel & 0xf), function, value); 00406 } 00407 void BLEMIDI::sendPitchWheel(uint8_t channel, uint16_t amount) { 00408 sendMidiMessage(0xe0 | (channel & 0xf), amount & 0x7f, (amount >> 7) & 0x7f); 00409 } 00410 void BLEMIDI::sendSongPositionPointer(uint16_t position) { 00411 sendMidiMessage(0xf2, position & 0x7f, (position >> 7) & 0x7f); 00412 } 00413 void BLEMIDI::sendSystemExclusive(uint8_t * sysex, uint16_t length) { 00414 if (isConnected) { 00415 uint8_t position = 0; 00416 00417 // header 00418 uint16_t ticks = tick.read_ms() & 0x1fff; 00419 midi[position++] = 0x80 | ((ticks >> 7) & 0x3f); 00420 midi[position++] = 0x80 | (ticks & 0x7f); 00421 00422 for (int i = 0; i < length; i++) { 00423 if (i == length - 1) { 00424 // modify last byte 00425 midi[position++] = 0x80 | (ticks & 0x7f); 00426 00427 if (position == 20) { 00428 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 20); 00429 00430 position = 0; 00431 // header 00432 midi[position++] = 0x80 | (ticks >> 7) & 0x3f; 00433 } 00434 } 00435 midi[position++] = sysex[i]; 00436 if (position == 20) { 00437 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, 20); 00438 00439 position = 0; 00440 // header 00441 midi[position++] = 0x80 | (ticks >> 7) & 0x3f; 00442 } 00443 00444 ticks = tick.read_ms() & 0x1fff; 00445 } 00446 00447 if (position > 0) { 00448 // send remains 00449 device->updateCharacteristicValue(midiCharacteristic->getValueAttribute().getHandle(), midi, position); 00450 } 00451 } 00452 }
Generated on Tue Jul 12 2022 14:18:15 by 1.7.2