1 /* 2 * Copyright 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.mobileer.oboetester; 18 19 import java.io.IOException; 20 import java.util.Locale; 21 22 /** 23 * Base class for any audio input or output. 24 */ 25 public abstract class AudioStreamBase { 26 27 private StreamConfiguration mRequestedStreamConfiguration; 28 private StreamConfiguration mActualStreamConfiguration; 29 private AudioStreamBase.DoubleStatistics mLatencyStatistics; 30 private SampleRateMonitor mSampleRateMonitor = new SampleRateMonitor(); 31 private int mBufferSizeInFrames; 32 33 private class SampleRateMonitor { 34 private static final int SIZE = 16; // power of 2 35 private static final long MASK = SIZE - 1L; 36 private long[] times = new long[SIZE]; 37 private long[] frames = new long[SIZE]; 38 private long cursor; 39 add(long numFrames)40 void add(long numFrames) { 41 int index = (int) (cursor & MASK); 42 frames[index] = numFrames; 43 times[index] = System.currentTimeMillis(); 44 cursor++; 45 } 46 getRate()47 int getRate() { 48 if (cursor < 2) return 0; 49 long numValid = Math.min((long)SIZE, cursor); 50 int oldestIndex = (int)((cursor - numValid) & MASK); 51 int newestIndex = (int)((cursor - 1) & MASK); 52 long deltaTime = times[newestIndex] - times[oldestIndex]; 53 long deltaFrames = frames[newestIndex] - frames[oldestIndex]; 54 if (deltaTime <= 0) { 55 return -1; 56 } 57 long sampleRate = (deltaFrames * 1000) / deltaTime; 58 return (int) sampleRate; 59 } 60 reset()61 void reset() { 62 cursor = 0; 63 } 64 } 65 getStreamStatus()66 public StreamStatus getStreamStatus() { 67 StreamStatus status = new StreamStatus(); 68 status.bufferSize = getBufferSizeInFrames(); 69 status.xRunCount = getXRunCount(); 70 status.framesRead = getFramesRead(); 71 status.framesWritten = getFramesWritten(); 72 status.callbackCount = getCallbackCount(); 73 status.latency = getLatency(); 74 mLatencyStatistics.add(status.latency); 75 status.callbackTimeStr = getCallbackTimeStr(); 76 status.cpuLoad = getCpuLoad(); 77 status.state = getState(); 78 mSampleRateMonitor.add(status.framesRead); 79 status.measuredRate = mSampleRateMonitor.getRate(); 80 return status; 81 } 82 getLatencyStatistics()83 public DoubleStatistics getLatencyStatistics() { 84 return mLatencyStatistics; 85 } 86 setPerformanceHintEnabled(boolean checked)87 public void setPerformanceHintEnabled(boolean checked) { 88 } setHearWorkload(boolean checked)89 public void setHearWorkload(boolean checked) { 90 } 91 92 public static class DoubleStatistics { 93 private double sum; 94 private int count; 95 private double minimum = Double.MAX_VALUE; 96 private double maximum = Double.MIN_VALUE; 97 add(double statistic)98 void add(double statistic) { 99 if (statistic <= 0.0) return; 100 sum += statistic; 101 count++; 102 minimum = Math.min(statistic, minimum); 103 maximum = Math.max(statistic, maximum); 104 } 105 getAverage()106 double getAverage() { 107 return sum / count; 108 } 109 dump()110 public String dump() { 111 if (count == 0) return "?"; 112 return String.format(Locale.getDefault(), "%3.1f/%3.1f/%3.1f ms", minimum, getAverage(), maximum); 113 } 114 } 115 116 /** 117 * Changes dynamic at run-time. 118 */ 119 public static class StreamStatus { 120 public int bufferSize; 121 public int xRunCount; 122 public long framesWritten; 123 public long framesRead; 124 public double latency; // msec 125 public int state; 126 public long callbackCount; 127 public int framesPerCallback; 128 public float cpuLoad; 129 public String callbackTimeStr; 130 public int measuredRate; 131 132 // These are constantly changing. dump(int framesPerBurst)133 String dump(int framesPerBurst) { 134 if (bufferSize < 0 || framesWritten < 0) { 135 return "idle"; 136 } 137 StringBuffer buffer = new StringBuffer(); 138 139 buffer.append("time between callbacks = " + callbackTimeStr + "\n"); 140 141 buffer.append("wr " 142 + String.format(Locale.getDefault(), "%Xh", framesWritten) 143 + " - rd " + String.format(Locale.getDefault(), "%Xh", framesRead) 144 + " = " + (framesWritten - framesRead) + " fr" 145 + ", SR = " + ((measuredRate <= 0) ? "?" : measuredRate) + "\n"); 146 147 String cpuLoadText = String.format(Locale.getDefault(), "%2d%c", (int)(cpuLoad * 100), '%'); 148 buffer.append( 149 convertStateToString(state) 150 + ", #cb=" + callbackCount 151 + ", f/cb=" + String.format(Locale.getDefault(), "%3d", framesPerCallback) 152 + ", " + cpuLoadText + " CPU" 153 + "\n"); 154 155 buffer.append("buffer size = "); 156 if (bufferSize <= 0 || framesPerBurst <= 0) { 157 buffer.append("?"); 158 } else { 159 int numBuffers = bufferSize / framesPerBurst; 160 int remainder = bufferSize - (numBuffers * framesPerBurst); 161 buffer.append(bufferSize + " = (" + numBuffers + " * " + framesPerBurst + ") + " + remainder); 162 } 163 buffer.append(", xRun# = " + ((xRunCount < 0) ? "?" : xRunCount)); 164 165 return buffer.toString(); 166 } 167 /** 168 * Converts ints from Oboe index to human-readable stream state 169 */ convertStateToString(int stateId)170 private String convertStateToString(int stateId) { 171 final String[] STATE_ARRAY = {"Uninit.", "Unknown", "Open", "Starting", "Started", 172 "Pausing", "Paused", "Flushing", "Flushed", 173 "Stopping", "Stopped", "Closing", "Closed", "Disconn."}; 174 if (stateId < 0 || stateId >= STATE_ARRAY.length) { 175 return "Invalid - " + stateId; 176 } 177 return STATE_ARRAY[stateId]; 178 } 179 } 180 181 /** 182 * 183 * @param requestedConfiguration 184 * @param actualConfiguration 185 * @param bufferSizeInFrames 186 * @throws IOException 187 */ open(StreamConfiguration requestedConfiguration, StreamConfiguration actualConfiguration, int bufferSizeInFrames)188 public void open(StreamConfiguration requestedConfiguration, 189 StreamConfiguration actualConfiguration, 190 int bufferSizeInFrames) throws IOException { 191 mRequestedStreamConfiguration = requestedConfiguration; 192 mActualStreamConfiguration = actualConfiguration; 193 mBufferSizeInFrames = bufferSizeInFrames; 194 mLatencyStatistics = new AudioStreamBase.DoubleStatistics(); 195 } 196 onStart()197 public void onStart() { 198 mSampleRateMonitor.reset(); 199 } onStop()200 public void onStop() { 201 mSampleRateMonitor.reset(); 202 } 203 isInput()204 public abstract boolean isInput(); 205 startPlayback()206 public void startPlayback() throws IOException {} 207 stopPlayback()208 public void stopPlayback() throws IOException {} 209 close()210 public abstract void close(); 211 getChannelCount()212 public int getChannelCount() { 213 return mActualStreamConfiguration.getChannelCount(); 214 } 215 getSampleRate()216 public int getSampleRate() { 217 return mActualStreamConfiguration.getSampleRate(); 218 } 219 getFramesPerBurst()220 public int getFramesPerBurst() { 221 return mActualStreamConfiguration.getFramesPerBurst(); 222 } 223 getBufferCapacityInFrames()224 public int getBufferCapacityInFrames() { 225 return mBufferSizeInFrames; 226 } 227 getBufferSizeInFrames()228 public int getBufferSizeInFrames() { 229 return mBufferSizeInFrames; 230 } 231 setBufferSizeInFrames(int bufferSize)232 public int setBufferSizeInFrames(int bufferSize) { 233 throw new UnsupportedOperationException("bufferSize cannot be changed"); 234 } 235 getCallbackCount()236 public long getCallbackCount() { return -1; } 237 getLastErrorCallbackResult()238 public int getLastErrorCallbackResult() { return 0; } 239 getFramesWritten()240 public long getFramesWritten() { return -1; } 241 getFramesRead()242 public long getFramesRead() { return -1; } 243 getLatency()244 public double getLatency() { return -1.0; } 245 getCpuLoad()246 public float getCpuLoad() { return 0.0f; } getAndResetMaxCpuLoad()247 public float getAndResetMaxCpuLoad() { return 0.0f; } getAndResetCpuMask()248 public int getAndResetCpuMask() { return 0; } 249 getCallbackTimeStr()250 public String getCallbackTimeStr() { return "?"; }; 251 getState()252 public int getState() { return -1; } 253 setWorkload(int workload)254 public void setWorkload(int workload) {} 255 getXRunCount()256 public abstract int getXRunCount(); 257 258 } 259