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 org.drrickorang.loopback; 18 19 import android.os.Bundle; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 import android.util.Log; 23 24 import java.util.Arrays; 25 26 27 /** 28 * This class records the buffer period of the audio player or recorder when in Java mode. 29 * Currently the accuracy is in 1ms. 30 */ 31 32 //TODO for native mode, should use a scale more accurate than the current 1ms 33 public class BufferPeriod implements Parcelable { 34 private static final String TAG = "BufferPeriod"; 35 36 private long mStartTimeNs = 0; // first time collectBufferPeriod() is called 37 private long mPreviousTimeNs = 0; 38 private long mCurrentTimeNs = 0; 39 40 private int mMeasurements = 0; 41 private long mVar; // variance in nanoseconds^2 42 private long mSDM = 0; // sum of squares of deviations from the expected mean 43 private int mMaxBufferPeriod = 0; 44 45 private int mCount = 0; 46 // Must match constant 'RANGE' in jni/loopback.h 47 private final int range = 1002; // store counts for 0ms to 1000ms, and for > 1000ms 48 private int mExpectedBufferPeriod = 0; 49 50 private int[] mBufferPeriod = new int[range]; 51 private BufferCallbackTimes mCallbackTimes; 52 private CaptureHolder mCaptureHolder; 53 BufferPeriod()54 public BufferPeriod() { 55 // Default constructor for when no data will be restored 56 } 57 58 /** 59 * For player, this function is called before every AudioTrack.write(). 60 * For recorder, this function is called after every AudioRecord.read() with read > 0. 61 */ collectBufferPeriod()62 public void collectBufferPeriod() { 63 mCurrentTimeNs = System.nanoTime(); 64 mCount++; 65 66 // if mPreviousTimeNs = 0, it's the first time this function is called 67 if (mPreviousTimeNs == 0) { 68 mStartTimeNs = mCurrentTimeNs; 69 } 70 71 if (mPreviousTimeNs != 0 && mCount > Constant.BUFFER_PERIOD_DISCARD) { 72 mMeasurements++; 73 74 long diffInNano = mCurrentTimeNs - mPreviousTimeNs; 75 // diffInMilli is rounded up 76 int diffInMilli = (int) ((diffInNano + Constant.NANOS_PER_MILLI - 1) / 77 Constant.NANOS_PER_MILLI); 78 79 long timeStampInNano = mCurrentTimeNs - mStartTimeNs; 80 int timeStampInMilli = (int) ((timeStampInNano + Constant.NANOS_PER_MILLI - 1) / 81 Constant.NANOS_PER_MILLI); 82 83 if (diffInMilli > mMaxBufferPeriod) { 84 mMaxBufferPeriod = diffInMilli; 85 } 86 87 // from 0 ms to 1000 ms, plus a sum of all occurrences > 1000ms 88 if (diffInMilli >= (range - 1)) { 89 mBufferPeriod[range - 1]++; 90 } else if (diffInMilli >= 0) { 91 mBufferPeriod[diffInMilli]++; 92 } else { // for diffInMilli < 0 93 log("Having negative BufferPeriod."); 94 } 95 96 long delta = diffInNano - (long) mExpectedBufferPeriod * Constant.NANOS_PER_MILLI; 97 mSDM += delta * delta; 98 if (mCount > 1) { 99 mVar = mSDM / mMeasurements; 100 } 101 102 mCallbackTimes.recordCallbackTime(timeStampInMilli, (short) diffInMilli); 103 104 // If diagnosing specific Java thread callback behavior set a conditional here and use 105 // mCaptureHolder.captureState(rank); to capture systraces and bugreport and/or wav file 106 } 107 108 mPreviousTimeNs = mCurrentTimeNs; 109 } 110 111 112 /** Reset all variables, called if wants to start a new buffer period's record. */ resetRecord()113 public void resetRecord() { 114 mPreviousTimeNs = 0; 115 mCurrentTimeNs = 0; 116 Arrays.fill(mBufferPeriod, 0); 117 mMaxBufferPeriod = 0; 118 mMeasurements = 0; 119 mExpectedBufferPeriod = 0; 120 mCount = 0; 121 mCallbackTimes = null; 122 } 123 prepareMemberObjects(int maxRecords, int expectedBufferPeriod, CaptureHolder captureHolder)124 public void prepareMemberObjects(int maxRecords, int expectedBufferPeriod, 125 CaptureHolder captureHolder) { 126 mCallbackTimes = new BufferCallbackTimes(maxRecords, expectedBufferPeriod); 127 mCaptureHolder = captureHolder; 128 mExpectedBufferPeriod = expectedBufferPeriod; 129 } 130 getBufferPeriodArray()131 public int[] getBufferPeriodArray() { 132 return mBufferPeriod; 133 } 134 getStdDevBufferPeriod()135 public double getStdDevBufferPeriod() { 136 return Math.sqrt(mVar) / (double) Constant.NANOS_PER_MILLI; 137 } 138 getMaxBufferPeriod()139 public int getMaxBufferPeriod() { 140 return mMaxBufferPeriod; 141 } 142 getCallbackTimes()143 public BufferCallbackTimes getCallbackTimes() { 144 return mCallbackTimes; 145 } 146 147 @Override describeContents()148 public int describeContents() { 149 return 0; 150 } 151 152 // Only save values which represent the results. Any ongoing timing would not give useful 153 // results after a save/restore. 154 @Override writeToParcel(Parcel dest, int flags)155 public void writeToParcel(Parcel dest, int flags) { 156 Bundle out = new Bundle(); 157 out.putInt("mMaxBufferPeriod", mMaxBufferPeriod); 158 out.putIntArray("mBufferPeriod", mBufferPeriod); 159 out.putInt("mExpectedBufferPeriod", mExpectedBufferPeriod); 160 out.putParcelable("mCallbackTimes", mCallbackTimes); 161 dest.writeBundle(out); 162 } 163 BufferPeriod(Parcel source)164 private BufferPeriod(Parcel source) { 165 Bundle in = source.readBundle(getClass().getClassLoader()); 166 mMaxBufferPeriod = in.getInt("mMaxBufferPeriod"); 167 mBufferPeriod = in.getIntArray("mBufferPeriod"); 168 mExpectedBufferPeriod = in.getInt("mExpectedBufferPeriod"); 169 mCallbackTimes = in.getParcelable("mCallbackTimes"); 170 } 171 172 public static final Parcelable.Creator<BufferPeriod> CREATOR 173 = new Parcelable.Creator<BufferPeriod>() { 174 public BufferPeriod createFromParcel(Parcel in) { 175 return new BufferPeriod(in); 176 } 177 178 public BufferPeriod[] newArray(int size) { 179 return new BufferPeriod[size]; 180 } 181 }; 182 log(String msg)183 private static void log(String msg) { 184 Log.v(TAG, msg); 185 } 186 187 } 188