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