Utility class for MIDI over Bluetooth LE

Dependencies:   BLE_API mbed nRF51822

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers BLEMIDI.cpp Source File

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 }