Home | History | Annotate | Download | only in bluetoothmidiservice
      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