xref: /aosp_15_r20/external/oboe/apps/OboeTester/app/src/main/java/com/mobileer/oboetester/AudioStreamBase.java (revision 05767d913155b055644481607e6fa1e35e2fe72c)
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