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