1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetoothmidiservice; 18 19 /** 20 * Convert MIDI over BTLE timestamps to system nanotime. 21 */ 22 public class MidiBtleTimeTracker { 23 24 public final static long NANOS_PER_MILLI = 1000000L; 25 26 private final static long RANGE_MILLIS = 0x2000; // per MIDI / BTLE standard 27 private final static long RANGE_NANOS = RANGE_MILLIS * NANOS_PER_MILLI; 28 29 private int mWindowMillis = 20; // typical max connection interval 30 private long mWindowNanos = mWindowMillis * NANOS_PER_MILLI; 31 32 private int mPreviousTimestamp; // Used to calculate deltas. 33 private long mPreviousNow; 34 // Our model of the peripherals millisecond clock. 35 private long mPeripheralTimeMillis; 36 // Host time that corresponds to time=0 on the peripheral. 37 private long mBaseHostTimeNanos; 38 private long mPreviousResult; // To prevent retrograde timestamp 39 40 public MidiBtleTimeTracker(long now) { 41 mPeripheralTimeMillis = 0; 42 mBaseHostTimeNanos = now; 43 mPreviousNow = now; 44 } 45 46 /** 47 * @param timestamp 48 * 13-bit millis in range of 0 to 8191 49 * @param now 50 * current time in nanoseconds 51 * @return nanoseconds corresponding to the timestamp 52 */ 53 public long convertTimestampToNanotime(int timestamp, long now) { 54 long deltaMillis = timestamp - mPreviousTimestamp; 55 // will be negative when timestamp wraps 56 if (deltaMillis < 0) { 57 deltaMillis += RANGE_MILLIS; 58 } 59 mPeripheralTimeMillis += deltaMillis; 60 61 // If we have not been called for a long time then 62 // make sure we have not wrapped multiple times. 63 if ((now - mPreviousNow) > (RANGE_NANOS / 2)) { 64 // Handle missed wraps. 65 long minimumTimeNanos = (now - mBaseHostTimeNanos) 66 - (RANGE_NANOS / 2); 67 long minimumTimeMillis = minimumTimeNanos / NANOS_PER_MILLI; 68 while (mPeripheralTimeMillis < minimumTimeMillis) { 69 mPeripheralTimeMillis += RANGE_MILLIS; 70 } 71 } 72 73 // Convert peripheral time millis to host time nanos. 74 long timestampHostNanos = (mPeripheralTimeMillis * NANOS_PER_MILLI) 75 + mBaseHostTimeNanos; 76 77 // The event cannot be in the future. So move window if we hit that. 78 if (timestampHostNanos > now) { 79 mPeripheralTimeMillis = 0; 80 mBaseHostTimeNanos = now; 81 timestampHostNanos = now; 82 } else { 83 // Timestamp should not be older than our window time. 84 long windowBottom = now - mWindowNanos; 85 if (timestampHostNanos < windowBottom) { 86 mPeripheralTimeMillis = 0; 87 mBaseHostTimeNanos = windowBottom; 88 timestampHostNanos = windowBottom; 89 } 90 } 91 // prevent retrograde timestamp 92 if (timestampHostNanos < mPreviousResult) { 93 timestampHostNanos = mPreviousResult; 94 } 95 mPreviousResult = timestampHostNanos; 96 mPreviousTimestamp = timestamp; 97 mPreviousNow = now; 98 return timestampHostNanos; 99 } 100 101 public int getWindowMillis() { 102 return mWindowMillis; 103 } 104 105 public void setWindowMillis(int window) { 106 this.mWindowMillis = window; 107 } 108 109 } 110