| /* |
| * 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; |
| } |
| |
| } |