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