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 
21 /**
22  * Base class for any audio input or output.
23  */
24 public abstract class AudioStreamBase {
25 
26     private StreamConfiguration mRequestedStreamConfiguration;
27     private StreamConfiguration mActualStreamConfiguration;
28     AudioStreamBase.DoubleStatistics mLatencyStatistics;
29 
30     private int mBufferSizeInFrames;
31 
getStreamStatus()32     public StreamStatus getStreamStatus() {
33         StreamStatus status = new StreamStatus();
34         status.bufferSize = getBufferSizeInFrames();
35         status.xRunCount = getXRunCount();
36         status.framesRead = getFramesRead();
37         status.framesWritten = getFramesWritten();
38         status.callbackCount = getCallbackCount();
39         status.latency = getLatency();
40         mLatencyStatistics.add(status.latency);
41         status.cpuLoad = getCpuLoad();
42         status.state = getState();
43         return status;
44     }
45 
getLatencyStatistics()46     public DoubleStatistics getLatencyStatistics() {
47         return mLatencyStatistics;
48     }
49 
50     public static class DoubleStatistics {
51         private double sum;
52         private int count;
53         private double minimum = Double.MAX_VALUE;
54         private double maximum = Double.MIN_VALUE;
55 
add(double latency)56         void add(double latency) {
57             if (latency <= 0.0) return;
58             sum += latency;
59             count++;
60             minimum = Math.min(latency, minimum);
61             maximum = Math.max(latency, maximum);
62         }
63 
getAverage()64         double getAverage() {
65             return sum / count;
66         }
67 
dump()68         public String dump() {
69             if (count == 0) return "?";
70             return String.format("%3.1f/%3.1f/%3.1f ms", minimum, getAverage(), maximum);
71         }
72     }
73 
74     /**
75      * Changes dynamic at run-time.
76      */
77     public static class StreamStatus {
78         public int bufferSize;
79         public int xRunCount;
80         public long framesWritten;
81         public long framesRead;
82         public double latency; // msec
83         public int state;
84         public long callbackCount;
85         public int framesPerCallback;
86         public double cpuLoad;
87 
88         // These are constantly changing.
dump(int framesPerBurst)89         String dump(int framesPerBurst) {
90             if (bufferSize < 0 || framesWritten < 0) {
91                 return "idle";
92             }
93             StringBuffer buffer = new StringBuffer();
94 
95             buffer.append("frames written " + framesWritten + " - read " + framesRead
96                     + " = " + (framesWritten - framesRead) + "\n");
97 
98             String cpuLoadText = String.format("%2d%c", (int)(cpuLoad * 100), '%');
99             buffer.append(
100                     convertStateToString(state)
101                     + ", #cb=" + callbackCount
102                     + ", f/cb=" + String.format("%3d", framesPerCallback)
103                     + ", " + cpuLoadText + " cpu"
104                     + "\n");
105 
106             buffer.append("buffer size = ");
107             if (bufferSize < 0) {
108                 buffer.append("?");
109             } else {
110                 int numBuffers = bufferSize / framesPerBurst;
111                 int remainder = bufferSize - (numBuffers * framesPerBurst);
112                 buffer.append(bufferSize + " = (" + numBuffers + " * " + framesPerBurst + ") + " + remainder);
113             }
114             buffer.append(",   xRun# = " + ((xRunCount < 0) ? "?" : xRunCount) + "\n");
115 
116             return buffer.toString();
117         }
118         /**
119          * Converts ints from Oboe index to human-readable stream state
120          */
convertStateToString(int stateId)121         private String convertStateToString(int stateId) {
122             final String[] STATE_ARRAY = {"Uninit.", "Unknown", "Open", "Starting", "Started",
123                     "Pausing", "Paused", "Flushing", "Flushed",
124                     "Stopping", "Stopped", "Closing", "Closed", "Disconn."};
125             if (stateId < 0 || stateId >= STATE_ARRAY.length) {
126                 return "Invalid - " + stateId;
127             }
128             return STATE_ARRAY[stateId];
129         }
130     }
131 
132     /**
133      *
134      * @param requestedConfiguration
135      * @param actualConfiguration
136      * @param bufferSizeInFrames
137      * @throws IOException
138      */
open(StreamConfiguration requestedConfiguration, StreamConfiguration actualConfiguration, int bufferSizeInFrames)139     public void open(StreamConfiguration requestedConfiguration,
140                      StreamConfiguration actualConfiguration,
141                      int bufferSizeInFrames) throws IOException {
142         mRequestedStreamConfiguration = requestedConfiguration;
143         mActualStreamConfiguration = actualConfiguration;
144         mBufferSizeInFrames = bufferSizeInFrames;
145         mLatencyStatistics = new AudioStreamBase.DoubleStatistics();
146     }
147 
isInput()148     public abstract boolean isInput();
149 
startPlayback()150     public void startPlayback() throws IOException {}
151 
stopPlayback()152     public void stopPlayback() throws IOException {}
153 
write(float[] buffer, int offset, int length)154     public abstract int write(float[] buffer, int offset, int length);
155 
close()156     public abstract void close();
157 
getChannelCount()158     public int getChannelCount() {
159         return mActualStreamConfiguration.getChannelCount();
160     }
161 
getSampleRate()162     public int getSampleRate() {
163         return mActualStreamConfiguration.getSampleRate();
164     }
165 
getFramesPerBurst()166     public int getFramesPerBurst() {
167         return mActualStreamConfiguration.getFramesPerBurst();
168     }
169 
getBufferCapacityInFrames()170     public int getBufferCapacityInFrames() {
171         return mBufferSizeInFrames;
172     }
173 
getBufferSizeInFrames()174     public int getBufferSizeInFrames() {
175         return mBufferSizeInFrames;
176     }
177 
setBufferSizeInFrames(int bufferSize)178     public int setBufferSizeInFrames(int bufferSize) {
179         throw new UnsupportedOperationException("bufferSize cannot be changed");
180     }
181 
getCallbackCount()182     public long getCallbackCount() { return -1; }
183 
getLastErrorCallbackResult()184     public int getLastErrorCallbackResult() { return 0; }
185 
getFramesWritten()186     public long getFramesWritten() { return -1; }
187 
getFramesRead()188     public long getFramesRead() { return -1; }
189 
getLatency()190     public double getLatency() { return -1.0; }
191 
getCpuLoad()192     public double getCpuLoad() { return 0.0; }
193 
getState()194     public int getState() { return -1; }
195 
isThresholdSupported()196     public boolean isThresholdSupported() {
197         return false;
198     }
199 
setWorkload(double workload)200     public void setWorkload(double workload) {}
201 
getXRunCount()202     public abstract int getXRunCount();
203 
204 }
205