diff options
author | Clyne Sullivan <tullivan99@gmail.com> | 2019-02-28 17:04:22 -0500 |
---|---|---|
committer | Clyne Sullivan <tullivan99@gmail.com> | 2019-02-28 17:04:22 -0500 |
commit | d6869d1ec4bd24cd2c3eafa534f0849b25ec5607 (patch) | |
tree | 79e54ed27b39c31864895535d11399708d5a45c0 /arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp | |
parent | 614ee97bf3a2270c413527a7f35c54cbecd9e601 (diff) |
added basic code
Diffstat (limited to 'arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp')
-rwxr-xr-x | arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp b/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp new file mode 100755 index 0000000..cc16f6d --- /dev/null +++ b/arduino/libraries/Bluefruit52Lib/src/services/BLEMidi.cpp @@ -0,0 +1,395 @@ +/**************************************************************************/ +/*! + @file BLEMidi.cpp + @author hathach (tinyusb.org) & toddtreece + + @section LICENSE + + Software License Agreement (BSD License) + + Copyright (c) 2018, Adafruit Industries (adafruit.com) + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/**************************************************************************/ + +#include "bluefruit.h" + +// GCC 5x new feature to detect optional include +#ifdef __has_include +#if __has_include("MIDI.h") + #include <MIDI.h> + #define MIDI_LIB_INCLUDED +#endif +#endif + + +/* MIDI Service: 03B80E5A-EDE8-4B33-A751-6CE34EC4C700 + * MIDI I/O : 7772E5DB-3868-4112-A1A9-F2669D106BF3 + */ + +const uint8_t BLEMIDI_UUID_SERVICE[] = +{ + 0x00, 0xC7, 0xC4, 0x4E, 0xE3, 0x6C, 0x51, 0xA7, + 0x33, 0x4B, 0xE8, 0xED, 0x5A, 0x0E, 0xB8, 0x03 +}; + +const uint8_t BLEMIDI_UUID_CHR_IO[] = +{ + 0xF3, 0x6B, 0x10, 0x9D, 0x66, 0xF2, 0xA9, 0xA1, + 0x12, 0x41, 0x68, 0x38, 0xDB, 0xE5, 0x72, 0x77 +}; + +/*------------------------------------------------------------------*/ +/* MIDI Data Type + *------------------------------------------------------------------*/ +typedef union ATTR_PACKED +{ + struct { + uint8_t timestamp_hi : 6; + uint8_t : 1; + uint8_t start_bit : 1; + }; + + uint8_t byte; +} midi_header_t; + +VERIFY_STATIC ( sizeof(midi_header_t) == 1 ); + +typedef union ATTR_PACKED +{ + struct { + uint8_t timestamp_low : 7; + uint8_t start_bit : 1; + }; + + uint8_t byte; +} midi_timestamp_t; + +VERIFY_STATIC ( sizeof(midi_timestamp_t) == 1 ); + +typedef struct ATTR_PACKED +{ + midi_header_t header; + midi_timestamp_t timestamp; + uint8_t data[BLE_MIDI_TX_BUFFER_SIZE]; +} midi_event_packet_t; + +VERIFY_STATIC ( sizeof(midi_event_packet_t) == (BLE_MIDI_TX_BUFFER_SIZE + 2) ); + +typedef struct ATTR_PACKED +{ + midi_header_t header; + uint8_t data[BLE_MIDI_TX_BUFFER_SIZE]; +} midi_split_packet_t; + +VERIFY_STATIC ( sizeof(midi_split_packet_t) == (BLE_MIDI_TX_BUFFER_SIZE + 1) ); + +void blemidi_write_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset); + +/*------------------------------------------------------------------*/ +/* IMPLEMENTATION + *------------------------------------------------------------------*/ +BLEMidi::BLEMidi(uint16_t fifo_depth) + : BLEService(BLEMIDI_UUID_SERVICE), _io(BLEMIDI_UUID_CHR_IO), _rxd_fifo(1, fifo_depth) +{ + _write_cb = NULL; + _midilib_obj = NULL; +} + +bool BLEMidi::notifyEnabled(void) +{ + return Bluefruit.connPaired() && _io.notifyEnabled(); +} + +void BLEMidi::setWriteCallback(midi_write_cb_t fp) +{ + _write_cb = fp; +} + +void BLEMidi::autoMIDIread(void* midi_obj) +{ + _midilib_obj = midi_obj; +} +void BLEMidi::begin(int baudrate) +{ + (void) baudrate; + begin(); +} + +err_t BLEMidi::begin(void) +{ + _rxd_fifo.begin(); + + // Invoke base class begin() + VERIFY_STATUS( BLEService::begin() ); + + // IO characteristic + _io.setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE | CHR_PROPS_WRITE_WO_RESP | CHR_PROPS_NOTIFY); + _io.setPermission(SECMODE_ENC_NO_MITM, SECMODE_ENC_NO_MITM); + _io.setWriteCallback(blemidi_write_cb); + + VERIFY_STATUS( _io.begin() ); + + // Attempt to change the connection interval to 11.25-15 ms when starting HID + Bluefruit.setConnInterval(9, 12); + + return ERROR_NONE; +} + +/*------------------------------------------------------------------*/ +/* Callbacks + *------------------------------------------------------------------*/ +void blemidi_write_cb(BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset) +{ + (void) offset; + if ( len < 3 ) return; + + BLEMidi& midi_svc = (BLEMidi&) chr.parentService(); + midi_svc._write_handler(data, len); +} + +void BLEMidi::_write_handler(uint8_t* data, uint16_t len) +{ + // drop the BLE MIDI header byte + data++; + len--; + + while (len) + { + + // timestamp low byte followed by a MIDI status, + // so we drop the timestamp low byte + if ( isStatusByte(data[0]) && isStatusByte(data[1]) ) + { + data++; + len--; + } + // timestamp low byte on it's own (running status), + // so we drop the timestamp low byte + else if ( isStatusByte(data[0]) && ! isStatusByte(data[1]) ) + { + data++; + len--; + } + + // write the status or MIDI data to the FIFO + _rxd_fifo.write(data++, 1); + len--; + } + + // Call write callback if configured + if ( _write_cb ) _write_cb(); + +#ifdef MIDI_LIB_INCLUDED + // read while possible if configured + if ( _midilib_obj ) + { + while( ((midi::MidiInterface<BLEMidi>*)_midilib_obj)->read() ) { } + } +#endif + +} + +/*------------------------------------------------------------------*/ +/* Stream API + *------------------------------------------------------------------*/ +int BLEMidi::read ( void ) +{ + uint8_t ch; + return _rxd_fifo.read(&ch) ? (int) ch : EOF; +} + + +size_t BLEMidi::write ( uint8_t b ) +{ + // MIDI Library will write event byte by byte. + // We need to buffer the data until we have a full event, + // or until we reach the TX buffer limit. + static uint8_t count = 0; + static uint8_t buf[BLE_MIDI_TX_BUFFER_SIZE] = { 0 }; + + // the current byte is a sysex end status message, + // and we still have an existing buffer. send the + // existing buffer and clear it so we can send + // the sysex end status message with the appropriate + // BLE header and timestamp bytes. + if(b == 0xF7 && count > 0) + { + // send and clear the last of the existing buffer. + // it should contain the final bytes in the sysex payload. + if (isStatusByte(buf[0])) + send(buf, count); + else + sendSplit(buf, count); + + // reset buffer + buf[0] = 0; + count = 0; + } + + // add the current byte to the buffer + buf[count++] = b; + + // send matching 1, 2, or 3 byte messages + // and clear the buffer + if ( (oneByteMessage(buf[0]) && count == 1) || + (twoByteMessage(buf[0]) && count == 2) || + (threeByteMessage(buf[0]) && count == 3) ) + { + send(buf, count); + // reset buffer + buf[0] = 0; + count = 0; + } + + // do we have a full buffer at this point? + if(count == BLE_MIDI_TX_BUFFER_SIZE) + { + // send a full or split message depending + // on the type of the first byte in the buffer + if (isStatusByte(buf[0])) + send(buf, count); + else + sendSplit(buf, count); + + // reset buffer + buf[0] = 0; + count = 0; + } + + return 1; +} + +int BLEMidi::available ( void ) +{ + return _rxd_fifo.count(); +} + +int BLEMidi::peek ( void ) +{ + uint8_t ch; + return _rxd_fifo.peek(&ch) ? (int) ch : EOF; +} + +void BLEMidi::flush ( void ) +{ + _rxd_fifo.clear(); +} + +/*------------------------------------------------------------------*/ +/* Message Type Helpers + *------------------------------------------------------------------*/ +bool BLEMidi::isStatusByte( uint8_t b ) +{ + // if the bit 7 is set, then it's a MIDI status message + if (bitRead(b, 7)) + return true; + else + return false; +} + +bool BLEMidi::oneByteMessage( uint8_t status ) +{ + // system messages + if (status >= 0xF4 && status <= 0xFF) return true; + + // system common + if (status == 0xF1) return true; + + // sysex end + if (status == 0xF7) return true; + + return false; +} + +bool BLEMidi::twoByteMessage( uint8_t status ) +{ + // program change, aftertouch + if (status >= 0xC0 && status <= 0xDF) return true; + + // song select + if (status == 0xF3) return true; + + return false; +} + +bool BLEMidi::threeByteMessage( uint8_t status ) +{ + // note off, note on, aftertouch, control change + if (status >= 0x80 && status <= 0xBF) return true; + + // pitch wheel change + if (status >= 0xE0 && status <= 0xEF) return true; + + // song position pointer + if (status == 0xF2) return true; + + return false; +} + +/*------------------------------------------------------------------*/ +/* Send Event (notify) + *------------------------------------------------------------------*/ +bool BLEMidi::send(uint8_t data[], uint8_t len) +{ + uint32_t tstamp = millis(); + + midi_event_packet_t event = + { + .header = {{ + .timestamp_hi = (uint8_t) ((tstamp & 0x1F80UL) >> 7), + .start_bit = 1 + }}, + + .timestamp = {{ + .timestamp_low = (uint8_t) (tstamp & 0x7FUL), + .start_bit = 1 + }} + }; + + memcpy(event.data, data, len); + + // send data length + 1 byte for header + 1 byte for timestamp + return _io.notify(&event, len + 2); +} + +bool BLEMidi::sendSplit(uint8_t data[], uint8_t len) +{ + uint32_t tstamp = millis(); + + midi_split_packet_t event = + { + .header = {{ + .timestamp_hi = (uint8_t) ((tstamp & 0x1F80UL) >> 7), + .start_bit = 1 + }} + }; + + memcpy(event.data, data, len); + + // send data length + 1 byte for header + // don't include the second timestamp byte + return _io.notify(&event, len + 1); +} |