• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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.google.sample.oboe.manualtest;
18 
19 import android.os.Bundle;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.view.View;
23 import android.widget.Button;
24 import android.widget.TextView;
25 
26 import java.io.IOException;
27 
28 /**
29  * Activity to measure the number of glitches.
30  */
31 public class GlitchActivity extends AnalyzerActivity {
32     private TextView mAnalyzerTextView;
33     private Button mStartButton;
34     private Button mStopButton;
35     private Button mShareButton;
36 
37     // These must match the values in LatencyAnalyzer.h
38     final static int STATE_IDLE = 0;
39     final static int STATE_IMMUNE = 1;
40     final static int STATE_WAITING_FOR_SIGNAL = 2;
41     final static int STATE_WAITING_FOR_LOCK = 3;
42     final static int STATE_LOCKED = 4;
43     final static int STATE_GLITCHING = 5;
44     String mLastGlitchReport;
45 
getStateFrameCount(int state)46     native int getStateFrameCount(int state);
getGlitchCount()47     native int getGlitchCount();
getSignalToNoiseDB()48     native double getSignalToNoiseDB();
getPeakAmplitude()49     native double getPeakAmplitude();
50 
51     // Note that these strings must match the enum result_code in LatencyAnalyzer.h
stateToString(int resultCode)52     String stateToString(int resultCode) {
53         switch (resultCode) {
54             case STATE_IDLE:
55                 return "IDLE";
56             case STATE_IMMUNE:
57                 return "IMMUNE";
58             case STATE_WAITING_FOR_SIGNAL:
59                 return "WAITING_FOR_SIGNAL";
60             case STATE_WAITING_FOR_LOCK:
61                 return "WAITING_FOR_LOCK";
62             case STATE_LOCKED:
63                 return "LOCKED";
64             case STATE_GLITCHING:
65                 return "GLITCHING";
66             default:
67                 return "UNKNOWN";
68         }
69     }
70 
71     // Periodically query for glitches from the native detector.
72     protected class GlitchSniffer {
73         public static final int SNIFFER_UPDATE_PERIOD_MSEC = 100;
74         public static final int SNIFFER_UPDATE_DELAY_MSEC = 200;
75 
76         private long mTimeAtStart;
77         private long mTimeOfLastGlitch;
78         private double mSecondsWithoutGlitches;
79         private double mMaxSecondsWithoutGlitches;
80         private int mLastGlitchCount;
81         private int mLastUnlockedFrames;
82         private int mLastLockedFrames;
83         private int mLastGlitchFrames;
84 
85         private int mStartResetCount;
86         private int mLastResetCount;
87         private int mPreviousState;
88 
89         private double mSignalToNoiseDB;
90         private double mPeakAmplitude;
91         private Handler mHandler = new Handler(Looper.getMainLooper()); // UI thread
92         private volatile boolean mEnabled = true;
93 
startSniffer()94         private void startSniffer() {
95             long now = System.currentTimeMillis();
96             mTimeAtStart = now;
97             mTimeOfLastGlitch = now;
98             mLastUnlockedFrames = 0;
99             mLastLockedFrames = 0;
100             mLastGlitchFrames = 0;
101             mSecondsWithoutGlitches = 0.0;
102             mMaxSecondsWithoutGlitches = 0.0;
103             mLastGlitchCount = 0;
104             mStartResetCount = mLastResetCount;
105             // Start the initial runnable task by posting through the handler
106             mEnabled = true;
107             mHandler.postDelayed(runnableCode, SNIFFER_UPDATE_DELAY_MSEC);
108         }
109 
stopSniffer()110         private void stopSniffer() {
111             mEnabled = false;
112             if (mHandler != null) {
113                 mHandler.removeCallbacks(runnableCode);
114             }
115 
116             runOnUiThread(new Runnable() {
117                 @Override
118                 public void run() {
119                     updateStatusText();
120                 }
121             });
122         }
123 
124         private Runnable runnableCode = new Runnable() {
125             @Override
126             public void run() {
127                 int state = getAnalyzerState();
128                 mSignalToNoiseDB = getSignalToNoiseDB();
129                 mPeakAmplitude = getPeakAmplitude();
130                 mPreviousState = state;
131 
132                 long now = System.currentTimeMillis();
133                 int glitchCount = getGlitchCount();
134                 int resetCount = getResetCount();
135                 mLastUnlockedFrames = getStateFrameCount(STATE_WAITING_FOR_LOCK);
136                 int lockedFrames = getStateFrameCount(STATE_LOCKED);
137                 int glitchFrames = getStateFrameCount(STATE_GLITCHING);
138 
139                 if (glitchFrames > mLastGlitchFrames || glitchCount > mLastGlitchCount) {
140                     mTimeOfLastGlitch = now;
141                     mSecondsWithoutGlitches = 0.0;
142                     onGlitchDetected();
143                 } else if (lockedFrames > mLastLockedFrames) {
144                     mSecondsWithoutGlitches = (now - mTimeOfLastGlitch) / 1000.0;
145                 }
146 
147                 if (resetCount > mLastResetCount) {
148                     mLastResetCount = resetCount;
149                 }
150 
151                 if (mSecondsWithoutGlitches > mMaxSecondsWithoutGlitches) {
152                     mMaxSecondsWithoutGlitches = mSecondsWithoutGlitches;
153                 }
154 
155                 mLastGlitchCount = glitchCount;
156                 mLastGlitchFrames = glitchFrames;
157                 mLastLockedFrames = lockedFrames;
158                 mLastResetCount = resetCount;
159 
160                 updateStatusText();
161 
162                 // Reschedule so this task repeats
163                 if (mEnabled) {
164                     mHandler.postDelayed(runnableCode, SNIFFER_UPDATE_PERIOD_MSEC);
165                 }
166             }
167         };
168 
getCurrentStatusReport()169         String getCurrentStatusReport() {
170             long now = System.currentTimeMillis();
171             double totalSeconds = (now - mTimeAtStart) / 1000.0;
172 
173             StringBuffer message = new StringBuffer();
174             message.append("state = " + stateToString(mPreviousState) + "\n");
175             message.append(String.format("unlocked.frames = %d\n", mLastUnlockedFrames));
176             message.append(String.format("locked.frames = %d\n", mLastLockedFrames));
177             message.append(String.format("glitch.frames = %d\n", mLastGlitchFrames));
178             message.append(String.format("reset.count = %d\n", mLastResetCount - mStartResetCount));
179             message.append(String.format("peak.amplitude = %8.6f\n", mPeakAmplitude));
180             if (mLastLockedFrames > 0) {
181                 message.append(String.format("signal.noise.ratio.db = %5.1f\n", mSignalToNoiseDB));
182             }
183             message.append(String.format("time.total = %8.2f seconds\n", totalSeconds));
184             if (mLastLockedFrames > 0) {
185                 message.append(String.format("time.no.glitches = %8.2f\n", mSecondsWithoutGlitches));
186                 message.append(String.format("max.time.no.glitches = %8.2f\n",
187                         mMaxSecondsWithoutGlitches));
188                 message.append(String.format("glitch.count = %d\n", mLastGlitchCount));
189             }
190             return message.toString();
191         }
192 
getShortReport()193         public String getShortReport() {
194             String resultText = "#glitches = " + getLastGlitchCount()
195                     + ", #resets = " + getLastResetCount()
196                     + ", max no glitch = " + getMaxSecondsWithNoGlitch() + " secs\n";
197             resultText += String.format("SNR = %5.1f db", mSignalToNoiseDB);
198             resultText += ", #locked = " + mLastLockedFrames;
199             return resultText;
200         }
201 
updateStatusText()202         private void updateStatusText() {
203             mLastGlitchReport = getCurrentStatusReport();
204             setAnalyzerText(mLastGlitchReport);
205         }
206 
getMaxSecondsWithNoGlitch()207         public double getMaxSecondsWithNoGlitch() {
208             return mMaxSecondsWithoutGlitches;
209         }
210 
getLastGlitchCount()211         public int getLastGlitchCount() {
212             return mLastGlitchCount;
213         }
getLastResetCount()214         public int getLastResetCount() {
215             return mLastResetCount;
216         }
217     }
218 
219     // Called on UI thread
onGlitchDetected()220     protected void onGlitchDetected() {
221     }
222 
223     private GlitchSniffer mGlitchSniffer = new GlitchSniffer();
224 
setAnalyzerText(String s)225     private void setAnalyzerText(String s) {
226         mAnalyzerTextView.setText(s);
227     }
228 
229     /**
230      * Set tolerance to deviations from expected value.
231      * The normalized value will be converted in the native code.
232      * @param tolerance normalized between 0.0 and 1.0
233      */
setTolerance(float tolerance)234     public native void setTolerance(float tolerance);
235 
236     @Override
onCreate(Bundle savedInstanceState)237     protected void onCreate(Bundle savedInstanceState) {
238         super.onCreate(savedInstanceState);
239         mStartButton = (Button) findViewById(R.id.button_start);
240         mStopButton = (Button) findViewById(R.id.button_stop);
241         mStopButton.setEnabled(false);
242         mShareButton = (Button) findViewById(R.id.button_share);
243         mShareButton.setEnabled(false);
244         mAnalyzerTextView = (TextView) findViewById(R.id.text_status);
245         updateEnabledWidgets();
246         hideSettingsViews();
247         // TODO hide sample rate menu
248         StreamContext streamContext = getFirstInputStreamContext();
249         if (streamContext != null) {
250             if (streamContext.configurationView != null) {
251                 streamContext.configurationView.hideSampleRateMenu();
252             }
253         }
254     }
255 
256     @Override
onStart()257     protected void onStart() {
258         super.onStart();
259         setActivityType(ACTIVITY_GLITCHES);
260         mStartButton.setEnabled(true);
261         mStopButton.setEnabled(false);
262         mShareButton.setEnabled(false);
263     }
264 
265     @Override
onStop()266     protected void onStop() {
267         stopAudioTest();
268         super.onStop();
269     }
270 
271     // Called on UI thread
onStartAudioTest(View view)272     public void onStartAudioTest(View view) throws IOException {
273         startAudioTest();
274         mStartButton.setEnabled(false);
275         mStopButton.setEnabled(true);
276         mShareButton.setEnabled(false);
277         keepScreenOn(true);
278     }
279 
startAudioTest()280     public void startAudioTest() throws IOException {
281         openAudio();
282         startAudio();
283         mGlitchSniffer.startSniffer();
284         onTestBegan();
285     }
286 
onCancel(View view)287     public void onCancel(View view) {
288         stopAudioTest();
289         onTestFinished();
290     }
291 
292     // Called on UI thread
onStopAudioTest(View view)293     public void onStopAudioTest(View view) {
294         stopAudioTest();
295         onTestFinished();
296         keepScreenOn(false);
297     }
298 
299     // Must be called on UI thread.
onTestBegan()300     public void onTestBegan() {
301     }
302 
303     // Must be called on UI thread.
onTestFinished()304     public void onTestFinished() {
305         mStartButton.setEnabled(true);
306         mStopButton.setEnabled(false);
307         mShareButton.setEnabled(true);
308     }
309 
stopAudioTest()310     public void stopAudioTest() {
311         mGlitchSniffer.stopSniffer();
312         stopAudio();
313         closeAudio();
314     }
315 
316     @Override
isOutput()317     boolean isOutput() {
318         return false;
319     }
320 
321     @Override
setupEffects(int sessionId)322     public void setupEffects(int sessionId) {
323     }
324 
getMaxSecondsWithNoGlitch()325     public double getMaxSecondsWithNoGlitch() {
326         return mGlitchSniffer.getMaxSecondsWithNoGlitch();
327     }
328 
getShortReport()329     public String getShortReport() {
330         return mGlitchSniffer.getShortReport();
331     }
332 
333     @Override
getWaveTag()334     String getWaveTag() {
335         return "glitches";
336     }
337 }
338