summaryrefslogtreecommitdiffstats
path: root/media/packages
diff options
context:
space:
mode:
authorMike Lockwood <lockwood@google.com>2015-04-21 09:33:09 -0700
committerMike Lockwood <lockwood@google.com>2015-04-21 13:28:08 -0700
commitff001809f60b937c63d2db39e99a567af54414ac (patch)
treea72f1e10e81a55308fe96bd5554f0c438071521e /media/packages
parent93ea09a37696e60c9659c51868091b6b492c3bc3 (diff)
downloadframeworks_base-ff001809f60b937c63d2db39e99a567af54414ac.zip
frameworks_base-ff001809f60b937c63d2db39e99a567af54414ac.tar.gz
frameworks_base-ff001809f60b937c63d2db39e99a567af54414ac.tar.bz2
BluetoothMidiService: Use MidiBtleTimeTracker to interpret incoming Bluetooth MIDI timestamps
Also fixed some problems handling timestamp wrapping. Change-Id: Ic0aefc54f2560425bea6d07ca0c4529d16699eaa
Diffstat (limited to 'media/packages')
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java27
-rw-r--r--media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/MidiBtleTimeTracker.java109
2 files changed, 127 insertions, 9 deletions
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java
index c5bfb5f..1bce9fb 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothPacketDecoder.java
@@ -30,6 +30,7 @@ public class BluetoothPacketDecoder extends PacketDecoder {
private static final String TAG = "BluetoothPacketDecoder";
private final byte[] mBuffer;
+ private MidiBtleTimeTracker mTimeTracker;
private final int TIMESTAMP_MASK_HIGH = 0x1F80;
private final int TIMESTAMP_MASK_LOW = 0x7F;
@@ -41,6 +42,10 @@ public class BluetoothPacketDecoder extends PacketDecoder {
@Override
public void decodePacket(byte[] buffer, MidiReceiver receiver) {
+ if (mTimeTracker == null) {
+ mTimeTracker = new MidiBtleTimeTracker(System.nanoTime());
+ }
+
int length = buffer.length;
// NOTE his code allows running status across packets,
@@ -57,10 +62,12 @@ public class BluetoothPacketDecoder extends PacketDecoder {
}
// shift bits 0 - 5 to bits 7 - 12
- int timestamp = (header & HEADER_TIMESTAMP_MASK) << 7;
+ int highTimestamp = (header & HEADER_TIMESTAMP_MASK) << 7;
boolean lastWasTimestamp = false;
int dataCount = 0;
int previousLowTimestamp = 0;
+ long nanoTimestamp = 0;
+ int currentTimestamp = 0;
// iterate through the rest of the packet, separating MIDI data from timestamps
for (int i = 1; i < buffer.length; i++) {
@@ -69,25 +76,28 @@ public class BluetoothPacketDecoder extends PacketDecoder {
if ((b & 0x80) != 0 && !lastWasTimestamp) {
lastWasTimestamp = true;
int lowTimestamp = b & TIMESTAMP_MASK_LOW;
- int newTimestamp = (timestamp & TIMESTAMP_MASK_HIGH) | lowTimestamp;
if (lowTimestamp < previousLowTimestamp) {
- newTimestamp = (newTimestamp + 0x0080) & TIMESTAMP_MASK_HIGH;
+ highTimestamp = (highTimestamp + 0x0080) & TIMESTAMP_MASK_HIGH;
}
previousLowTimestamp = lowTimestamp;
- if (newTimestamp != timestamp) {
+ int newTimestamp = highTimestamp | lowTimestamp;
+ if (newTimestamp != currentTimestamp) {
if (dataCount > 0) {
// send previous message separately since it has a different timestamp
try {
- // FIXME use sendWithTimestamp
- receiver.send(mBuffer, 0, dataCount);
+ receiver.sendWithTimestamp(mBuffer, 0, dataCount, nanoTimestamp);
} catch (IOException e) {
// ???
}
dataCount = 0;
}
+ currentTimestamp = newTimestamp;
}
- timestamp = newTimestamp;
+
+ // calculate nanoTimestamp
+ long now = System.nanoTime();
+ nanoTimestamp = mTimeTracker.convertTimestampToNanotime(currentTimestamp, now);
} else {
lastWasTimestamp = false;
mBuffer[dataCount++] = b;
@@ -96,8 +106,7 @@ public class BluetoothPacketDecoder extends PacketDecoder {
if (dataCount > 0) {
try {
- // FIXME use sendWithTimestamp
- receiver.send(mBuffer, 0, dataCount);
+ receiver.sendWithTimestamp(mBuffer, 0, dataCount, nanoTimestamp);
} catch (IOException e) {
// ???
}
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/MidiBtleTimeTracker.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/MidiBtleTimeTracker.java
new file mode 100644
index 0000000..5202f9a
--- /dev/null
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/MidiBtleTimeTracker.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bluetoothmidiservice;
+
+/**
+ * Convert MIDI over BTLE timestamps to system nanotime.
+ */
+public class MidiBtleTimeTracker {
+
+ public final static long NANOS_PER_MILLI = 1000000L;
+
+ private final static long RANGE_MILLIS = 0x2000; // per MIDI / BTLE standard
+ private final static long RANGE_NANOS = RANGE_MILLIS * NANOS_PER_MILLI;
+
+ private int mWindowMillis = 20; // typical max connection interval
+ private long mWindowNanos = mWindowMillis * NANOS_PER_MILLI;
+
+ private int mPreviousTimestamp; // Used to calculate deltas.
+ private long mPreviousNow;
+ // Our model of the peripherals millisecond clock.
+ private long mPeripheralTimeMillis;
+ // Host time that corresponds to time=0 on the peripheral.
+ private long mBaseHostTimeNanos;
+ private long mPreviousResult; // To prevent retrograde timestamp
+
+ public MidiBtleTimeTracker(long now) {
+ mPeripheralTimeMillis = 0;
+ mBaseHostTimeNanos = now;
+ mPreviousNow = now;
+ }
+
+ /**
+ * @param timestamp
+ * 13-bit millis in range of 0 to 8191
+ * @param now
+ * current time in nanoseconds
+ * @return nanoseconds corresponding to the timestamp
+ */
+ public long convertTimestampToNanotime(int timestamp, long now) {
+ long deltaMillis = timestamp - mPreviousTimestamp;
+ // will be negative when timestamp wraps
+ if (deltaMillis < 0) {
+ deltaMillis += RANGE_MILLIS;
+ }
+ mPeripheralTimeMillis += deltaMillis;
+
+ // If we have not been called for a long time then
+ // make sure we have not wrapped multiple times.
+ if ((now - mPreviousNow) > (RANGE_NANOS / 2)) {
+ // Handle missed wraps.
+ long minimumTimeNanos = (now - mBaseHostTimeNanos)
+ - (RANGE_NANOS / 2);
+ long minimumTimeMillis = minimumTimeNanos / NANOS_PER_MILLI;
+ while (mPeripheralTimeMillis < minimumTimeMillis) {
+ mPeripheralTimeMillis += RANGE_MILLIS;
+ }
+ }
+
+ // Convert peripheral time millis to host time nanos.
+ long timestampHostNanos = (mPeripheralTimeMillis * NANOS_PER_MILLI)
+ + mBaseHostTimeNanos;
+
+ // The event cannot be in the future. So move window if we hit that.
+ if (timestampHostNanos > now) {
+ mPeripheralTimeMillis = 0;
+ mBaseHostTimeNanos = now;
+ timestampHostNanos = now;
+ } else {
+ // Timestamp should not be older than our window time.
+ long windowBottom = now - mWindowNanos;
+ if (timestampHostNanos < windowBottom) {
+ mPeripheralTimeMillis = 0;
+ mBaseHostTimeNanos = windowBottom;
+ timestampHostNanos = windowBottom;
+ }
+ }
+ // prevent retrograde timestamp
+ if (timestampHostNanos < mPreviousResult) {
+ timestampHostNanos = mPreviousResult;
+ }
+ mPreviousResult = timestampHostNanos;
+ mPreviousTimestamp = timestamp;
+ mPreviousNow = now;
+ return timestampHostNanos;
+ }
+
+ public int getWindowMillis() {
+ return mWindowMillis;
+ }
+
+ public void setWindowMillis(int window) {
+ this.mWindowMillis = window;
+ }
+
+}