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