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