diff options
author | Phil Burk <philburk@google.com> | 2020-02-20 14:23:39 -0800 |
---|---|---|
committer | Phil Burk <philburk@google.com> | 2020-03-26 19:04:46 +0000 |
commit | 51aaf2e89e4f982bef55f940524203e6a81cc226 (patch) | |
tree | e65c44c386b22be22de0d2f9a92d20326fac878e /media/packages | |
parent | e55493727d91d42ab51694c3930aa45d4eecf6a7 (diff) |
BLE-MIDI: fix timestamps for sysex data
Blocks of data in the middle of a SysEx used to get a zero timestamp.
Now they get a proper timestamp.
Thanks to Teenage Engineering for suggesting this fix.
I also cleaned up the code to make it easier to understand.
Bug: 140638458
Test: Send long, >50 byte, SysEx over BLE to an Android
Test: device running the MIDI scope. Check timestamps
Test: for the data blocks. See bug for more details.
Change-Id: I1a553d54cc76025f854e53229d233a033ab7c297
Merged-In: I1a553d54cc76025f854e53229d233a033ab7c297
(cherry picked from commit 5664e50ac9fd04e169006e821110a2bc73f59e80)
Diffstat (limited to 'media/packages')
-rw-r--r-- | media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java | 80 |
1 files changed, 44 insertions, 36 deletions
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java index ea95a01c1f78..c51c8fa73c4e 100644 --- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java +++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java @@ -22,24 +22,41 @@ import android.util.Log; import java.io.IOException; /** - * This is an abstract base class that decodes a packet buffer and passes it to a - * {@link android.media.midi.MidiReceiver} + * This is an abstract base class that decodes a BLE-MIDI packet + * buffer and passes it to a {@link android.media.midi.MidiReceiver} */ public class BluetoothPacketDecoder extends PacketDecoder { private static final String TAG = "BluetoothPacketDecoder"; private final byte[] mBuffer; + private int mBytesInBuffer; private MidiBtleTimeTracker mTimeTracker; - private final int TIMESTAMP_MASK_HIGH = 0x1F80; - private final int TIMESTAMP_MASK_LOW = 0x7F; - private final int HEADER_TIMESTAMP_MASK = 0x3F; + private int mLowTimestamp; + private long mNanoTimestamp; + + private static final int TIMESTAMP_MASK_HIGH = 0x1F80; // top 7 bits + private static final int TIMESTAMP_MASK_LOW = 0x7F; // bottom 7 bits + private static final int HEADER_TIMESTAMP_MASK = 0x3F; // bottom 6 bits public BluetoothPacketDecoder(int maxPacketSize) { mBuffer = new byte[maxPacketSize]; } + private void flushOutput(MidiReceiver receiver) { + if (mBytesInBuffer > 0) { + try { + receiver.send(mBuffer, 0, mBytesInBuffer, mNanoTimestamp); + } catch (IOException e) { + // ??? + } + mBytesInBuffer = 0; + } + } + + // NOTE: this code allows running status across packets, + // although the specification does not allow that. @Override public void decodePacket(byte[] buffer, MidiReceiver receiver) { if (mTimeTracker == null) { @@ -47,14 +64,11 @@ public class BluetoothPacketDecoder extends PacketDecoder { } int length = buffer.length; - - // NOTE his code allows running status across packets, - // although the specification does not allow that. - if (length < 1) { Log.e(TAG, "empty packet"); return; } + byte header = buffer[0]; if ((header & 0xC0) != 0x80) { Log.e(TAG, "packet does not start with header"); @@ -64,52 +78,46 @@ public class BluetoothPacketDecoder extends PacketDecoder { // shift bits 0 - 5 to bits 7 - 12 int highTimestamp = (header & HEADER_TIMESTAMP_MASK) << 7; boolean lastWasTimestamp = false; - int dataCount = 0; int previousLowTimestamp = 0; - long nanoTimestamp = 0; - int currentTimestamp = 0; + int currentTimestamp = highTimestamp | mLowTimestamp; - // iterate through the rest of the packet, separating MIDI data from timestamps + // Iterate through the rest of the packet, separating MIDI data from timestamps. for (int i = 1; i < buffer.length; i++) { byte b = buffer[i]; + // Is this a timestamp byte? if ((b & 0x80) != 0 && !lastWasTimestamp) { lastWasTimestamp = true; - int lowTimestamp = b & TIMESTAMP_MASK_LOW; - if (lowTimestamp < previousLowTimestamp) { + mLowTimestamp = b & TIMESTAMP_MASK_LOW; + + // If the low timestamp byte wraps within the packet then + // increment the high timestamp byte. + if (mLowTimestamp < previousLowTimestamp) { highTimestamp = (highTimestamp + 0x0080) & TIMESTAMP_MASK_HIGH; } - previousLowTimestamp = lowTimestamp; + previousLowTimestamp = mLowTimestamp; - int newTimestamp = highTimestamp | lowTimestamp; + // If the timestamp advances then send any pending data. + int newTimestamp = highTimestamp | mLowTimestamp; if (newTimestamp != currentTimestamp) { - if (dataCount > 0) { - // send previous message separately since it has a different timestamp - try { - receiver.send(mBuffer, 0, dataCount, nanoTimestamp); - } catch (IOException e) { - // ??? - } - dataCount = 0; - } + // Send previous message separately since it has a different timestamp. + flushOutput(receiver); currentTimestamp = newTimestamp; } - // calculate nanoTimestamp + // Calculate MIDI nanosecond timestamp from BLE timestamp. long now = System.nanoTime(); - nanoTimestamp = mTimeTracker.convertTimestampToNanotime(currentTimestamp, now); + mNanoTimestamp = mTimeTracker.convertTimestampToNanotime(currentTimestamp, now); } else { lastWasTimestamp = false; - mBuffer[dataCount++] = b; + // Flush if full before adding more data. + if (mBytesInBuffer == mBuffer.length) { + flushOutput(receiver); + } + mBuffer[mBytesInBuffer++] = b; } } - if (dataCount > 0) { - try { - receiver.send(mBuffer, 0, dataCount, nanoTimestamp); - } catch (IOException e) { - // ??? - } - } + flushOutput(receiver); } } |