1 /*
2  * Copyright (C) 2019 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.android.cts.verifier.audio;
18 
19 import com.android.cts.verifier.CtsVerifierReportLog;
20 import com.android.cts.verifier.R;
21 import com.android.cts.verifier.audio.wavelib.*;
22 import com.android.compatibility.common.util.ResultType;
23 import com.android.compatibility.common.util.ResultUnit;
24 
25 import android.media.AudioRecord;
26 import android.media.MediaRecorder;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.util.Log;
31 import android.view.View;
32 import android.view.View.OnClickListener;
33 import android.widget.Button;
34 import android.widget.TextView;
35 import android.widget.ProgressBar;
36 
37 /**
38  * Tests Audio built in Microphone response for Voice Recognition audio source feature.
39  */
40 public class AudioFrequencyVoiceRecognitionActivity extends AudioFrequencyActivity {
41     private static final String TAG = "VoiceRecognition";
42 
43     private static final int TEST_STARTED = 900;
44     private static final int TEST_MESSAGE = 903;
45     private static final int TEST_ENDED = 904;
46     private static final int TEST_ENDED_ERROR = 905;
47     private static final double MIN_FRACTION_POINTS_IN_BAND = 0.5;
48 
49     private static final double TONE_RMS_EXPECTED = -22.35; //VOICE_RECOGNITION levels
50     private static final double TONE_RMS_MAX_ERROR = 3.0;
51     private static final double RMS_SMOOTHING_PARAM = 0.9;
52 
53     private static final double MAX_VAL = Math.pow(2, 15);
54 
55     private static final int SOURCE_TONE = 0;
56     private static final int SOURCE_NOISE = 1;
57 
58     private static final int TEST_NONE = -1;
59     private static final int TEST_TONE = 0;
60     private static final int TEST_NOISE = 1;
61     private static final int TEST_USB_BACKGROUND = 2;
62     private static final int TEST_USB_NOISE = 3;
63     private static final int TEST_COUNT = 4;
64 
65     private static final int TEST_DURATION_DEFAULT_MS = 2000;
66     private static final int TEST_DURATION_TONE_MS = TEST_DURATION_DEFAULT_MS;
67     private static final int TEST_DURATION_NOISE_MS = TEST_DURATION_DEFAULT_MS;
68     private static final int TEST_DURATION_USB_BACKGROUND_MS = TEST_DURATION_DEFAULT_MS;
69     private static final int TEST_DURATION_USB_NOISE_MS = TEST_DURATION_DEFAULT_MS;
70 
71     private static final int BLOCK_SIZE_SAMPLES = 4096;
72     private static final int SAMPLING_RATE = 48000;
73     private static final int RECORD_SOURCE = MediaRecorder.AudioSource.VOICE_RECOGNITION;
74 
75     private static final int BANDS_MIC = 3;
76     private static final int BANDS_TONE = 3;
77     private static final int BANDS_BACKGROUND = 3;
78 
79     private int mCurrentTest = TEST_NONE;
80     private boolean mTestsDone[] = new boolean[TEST_COUNT];
81     final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
82 
83     private Button mButtonTestTone;
84     private ProgressBar mProgressTone;
85     private TextView mResultTestTone;
86     private Button mButtonPlayTone;
87 
88     private Button mButtonTestNoise;
89     private ProgressBar mProgressNoise;
90     private TextView mResultTestNoise;
91     private Button mButtonPlayNoise;
92 
93     private Button mButtonTestUsbBackground;
94     private ProgressBar mProgressUsbBackground;
95     private TextView mResultTestUsbBackground;
96 
97     private Button mButtonTestUsbNoise;
98     private ProgressBar mProgressUsbNoise;
99     private TextView mResultTestUsbNoise;
100     private Button mButtonPlayUsbNoise;
101 
102     private TextView mGlobalResultText;
103 
104     private short[] mAudioShortArray2;
105 
106     private SoundPlayerObject mSPlayer;
107     private SoundRecorderObject mSRecorder;
108 
109     private DspBufferComplex mC;
110     private DspBufferDouble mData;
111 
112     private DspWindow mWindow;
113     private DspFftServer mFftServer;
114     private VectorAverage mFreqAverageTone = new VectorAverage();
115     private VectorAverage mFreqAverageNoise = new VectorAverage();
116     private VectorAverage mFreqAverageUsbBackground = new VectorAverage();
117     private VectorAverage mFreqAverageUsbNoise = new VectorAverage();
118 
119     //RMS for tone:
120     private double mRMS;
121     private double mRMSMax;
122 
123     private double mRMSTone;
124     private double mRMSMaxTone;
125 
126     private AudioBandSpecs[] mBandSpecsMic = new AudioBandSpecs[BANDS_MIC];
127     private AudioBandSpecs[] mBandSpecsTone = new AudioBandSpecs[BANDS_TONE];
128     private AudioBandSpecs[] mBandSpecsBack = new AudioBandSpecs[BANDS_BACKGROUND];
129     private Results mResultsMic;
130     private Results mResultsTone;
131     private Results mResultsBack;
132 
133     @Override
onCreate(Bundle savedInstanceState)134     protected void onCreate(Bundle savedInstanceState) {
135         super.onCreate(savedInstanceState);
136         setContentView(R.layout.audio_frequency_voice_recognition_activity);
137 
138         mSPlayer = new SoundPlayerObject();
139         playerSetSource(SOURCE_TONE);
140 
141         mSRecorder = new SoundRecorderObject(SAMPLING_RATE, BLOCK_SIZE_SAMPLES,
142                 RECORD_SOURCE) {
143             @Override
144             public void periodicNotification(AudioRecord recorder) {
145 
146                 int samplesAvailable = mPipe.availableToRead();
147                 int samplesNeeded = BLOCK_SIZE_SAMPLES;
148                 if (samplesAvailable >= samplesNeeded) {
149                     mPipe.read(mAudioShortArray2, 0, samplesNeeded);
150 
151                     //compute
152                     double maxabs = 0;
153                     int i;
154                     double rmsTempSum = 0;
155 
156                     for (i = 0; i < samplesNeeded; i++) {
157                         double value = mAudioShortArray2[i] / MAX_VAL;
158                         double valueabs = Math.abs(value);
159 
160                         if (valueabs > maxabs) {
161                             maxabs = valueabs;
162                         }
163 
164                         rmsTempSum += value * value;
165                         mData.mData[i] = value;
166                     }
167                     double rms = Math.sqrt(rmsTempSum / samplesNeeded);
168 
169                     double total_rms = rms * RMS_SMOOTHING_PARAM + mRMS * (1 - RMS_SMOOTHING_PARAM);
170                     mRMS = total_rms;
171                     if (mRMS > mRMSMax) {
172                         mRMSMax = mRMS;
173                     }
174 
175                     //for the current frame, compute FFT and send to the viewer.
176                     //apply window and pack as complex for now.
177                     DspBufferMath.mult(mData, mData, mWindow.mBuffer);
178                     DspBufferMath.set(mC, mData);
179                     mFftServer.fft(mC, 1);
180 
181                     double[] magnitude = new double[BLOCK_SIZE_SAMPLES / 2];
182                     for (i = 0; i < BLOCK_SIZE_SAMPLES / 2; i++) {
183                         magnitude[i] = Math.sqrt(mC.mReal[i] * mC.mReal[i] +
184                                 mC.mImag[i] * mC.mImag[i]);
185                     }
186 
187                     switch (mCurrentTest) {
188                         case TEST_TONE: {
189                             mFreqAverageTone.setData(magnitude, false);
190                             //Update realtime info on screen
191                             mRMSTone = mRMS;
192                             mRMSMaxTone = mRMSMax;
193                             showToneRMS();
194                         }
195                         break;
196                         case TEST_NOISE:
197                             mFreqAverageNoise.setData(magnitude, false);
198                             break;
199                         case TEST_USB_BACKGROUND:
200                             mFreqAverageUsbBackground.setData(magnitude, false);
201                             break;
202                         case TEST_USB_NOISE:
203                             mFreqAverageUsbNoise.setData(magnitude, false);
204                             break;
205                     }
206                 }
207             }
208 
209             @Override
210             public void markerReached(AudioRecord track) {
211 
212             }
213         };
214 
215         // Test tone
216         mButtonTestTone = (Button) findViewById(R.id.vr_button_test_tone);
217         mButtonTestTone.setOnClickListener(mBtnClickListener);
218         mProgressTone = (ProgressBar) findViewById(R.id.vr_test_tone_progress_bar);
219         mResultTestTone = (TextView) findViewById(R.id.vr_test_tone_result);
220         mButtonPlayTone = (Button) findViewById(R.id.vr_button_play_tone);
221         mButtonPlayTone.setOnClickListener(mBtnClickListener);
222         showWait(mProgressTone, false);
223 
224         //Test Noise
225         mButtonTestNoise = (Button) findViewById(R.id.vr_button_test_noise);
226         mButtonTestNoise.setOnClickListener(mBtnClickListener);
227         mProgressNoise = (ProgressBar) findViewById(R.id.vr_test_noise_progress_bar);
228         mResultTestNoise = (TextView) findViewById(R.id.vr_test_noise_result);
229         mButtonPlayNoise = (Button) findViewById(R.id.vr_button_play_noise);
230         mButtonPlayNoise.setOnClickListener(mBtnClickListener);
231         showWait(mProgressNoise, false);
232 
233         //USB Background
234         mButtonTestUsbBackground = (Button) findViewById(R.id.vr_button_test_usb_background);
235         mButtonTestUsbBackground.setOnClickListener(mBtnClickListener);
236         mProgressUsbBackground = (ProgressBar)
237                 findViewById(R.id.vr_test_usb_background_progress_bar);
238         mResultTestUsbBackground = (TextView)
239                 findViewById(R.id.vr_test_usb_background_result);
240         showWait(mProgressUsbBackground, false);
241 
242         mButtonTestUsbNoise = (Button) findViewById(R.id.vr_button_test_usb_noise);
243         mButtonTestUsbNoise.setOnClickListener(mBtnClickListener);
244         mProgressUsbNoise = (ProgressBar)findViewById(R.id.vr_test_usb_noise_progress_bar);
245         mResultTestUsbNoise = (TextView) findViewById(R.id.vr_test_usb_noise_result);
246         mButtonPlayUsbNoise = (Button) findViewById(R.id.vr_button_play_usb_noise);
247         mButtonPlayUsbNoise.setOnClickListener(mBtnClickListener);
248         showWait(mProgressUsbNoise, false);
249 
250         setButtonPlayStatus(-1);
251         mGlobalResultText = (TextView) findViewById(R.id.vr_test_global_result);
252 
253         //Init FFT stuff
254         mAudioShortArray2 = new short[BLOCK_SIZE_SAMPLES *2];
255         mData = new DspBufferDouble(BLOCK_SIZE_SAMPLES);
256         mC = new DspBufferComplex(BLOCK_SIZE_SAMPLES);
257         mFftServer = new DspFftServer(BLOCK_SIZE_SAMPLES);
258 
259         int overlap = BLOCK_SIZE_SAMPLES / 2;
260 
261         mWindow = new DspWindow(DspWindow.WINDOW_HANNING, BLOCK_SIZE_SAMPLES, overlap);
262 
263         setPassFailButtonClickListeners();
264         getPassButton().setEnabled(false);
265         setInfoResources(R.string.audio_frequency_voice_recognition_test,
266                 R.string.audio_frequency_voice_recognition_info, -1);
267 
268         //Init bands for Mic test
269         mBandSpecsMic[0] = new AudioBandSpecs(
270                 30, 100,         /* frequency start,stop */
271                 20.0, -20.0,     /* start top,bottom value */
272                 20.0, -20.0      /* stop top,bottom value */);
273 
274         mBandSpecsMic[1] = new AudioBandSpecs(
275                 100, 4000,       /* frequency start,stop */
276                 6.0, -6.0,     /* start top,bottom value */
277                 6.0, -6.0      /* stop top,bottom value */);
278 
279         mBandSpecsMic[2] = new AudioBandSpecs(
280                 4000, 20000,     /* frequency start,stop */
281                 30.0, -30.0,     /* start top,bottom value */
282                 30.0, -30.0      /* stop top,bottom value */);
283 
284         //Init bands for Tone test
285         mBandSpecsTone[0] = new AudioBandSpecs(
286                 5, 900,          /* frequency start,stop */
287                 -10.0, -100.0,     /* start top,bottom value */
288                 -10.0, -100.0      /* stop top,bottom value */);
289 
290         mBandSpecsTone[1] = new AudioBandSpecs(
291                 900, 1100,       /* frequency start,stop */
292                 10.0, -50.0,     /* start top,bottom value */
293                 10.0, -10.0      /* stop top,bottom value */);
294 
295         mBandSpecsTone[2] = new AudioBandSpecs(
296                 1100, 20000,     /* frequency start,stop */
297                 -30.0, -120.0,     /* start top,bottom value */
298                 -30.0, -120.0      /* stop top,bottom value */);
299 
300       //Init bands for Background test
301         mBandSpecsBack[0] = new AudioBandSpecs(
302                 5, 100,          /* frequency start,stop */
303                 10.0, -120.0,     /* start top,bottom value */
304                 -10.0, -120.0      /* stop top,bottom value */);
305 
306         mBandSpecsBack[1] = new AudioBandSpecs(
307                 100, 7000,       /* frequency start,stop */
308                 -10.0, -120.0,     /* start top,bottom value */
309                 -50.0, -120.0      /* stop top,bottom value */);
310 
311         mBandSpecsBack[2] = new AudioBandSpecs(
312                 7000, 20000,     /* frequency start,stop */
313                 -50.0, -120.0,     /* start top,bottom value */
314                 -50.0, -120.0      /* stop top,bottom value */);
315 
316         mResultsMic = new Results("mic_response", BANDS_MIC);
317         mResultsTone = new Results("tone_response", BANDS_TONE);
318         mResultsBack = new Results("background_response", BANDS_BACKGROUND);
319         connectRefMicUI();
320     }
321 
322     //
323     // Overrides
324     //
enableTestUI(boolean enable)325     void enableTestUI(boolean enable) {
326         mButtonTestTone.setEnabled(enable);
327         mButtonPlayTone.setEnabled(enable);
328 
329         mButtonTestNoise.setEnabled(enable);
330         mButtonPlayNoise.setEnabled(enable);
331 
332         mButtonTestUsbBackground.setEnabled(enable);
333 
334         mButtonTestUsbNoise.setEnabled(enable);
335         mButtonPlayUsbNoise.setEnabled(enable);
336     }
337 
playerToggleButton(int buttonId, int sourceId)338     private void playerToggleButton(int buttonId, int sourceId) {
339         if (playerIsPlaying()) {
340             playerStopAll();
341         } else {
342             playerSetSource(sourceId);
343             playerTransport(true);
344             setButtonPlayStatus(buttonId);
345         }
346     }
347 
348     private class OnBtnClickListener implements OnClickListener {
349         @Override
onClick(View v)350         public void onClick(View v) {
351             int id = v.getId();
352             switch (id) {
353             case R.id.vr_button_test_tone:
354                 startTest(TEST_TONE);
355                 break;
356             case R.id.vr_button_play_tone:
357                 playerToggleButton(id, SOURCE_TONE);
358                 break;
359             case R.id.vr_button_test_noise:
360                 startTest(TEST_NOISE);
361                 break;
362             case R.id.vr_button_play_noise:
363                 playerToggleButton(id, SOURCE_NOISE);
364                 break;
365             case R.id.vr_button_test_usb_background:
366                 startTest(TEST_USB_BACKGROUND);
367                 break;
368             case R.id.vr_button_test_usb_noise:
369                 startTest(TEST_USB_NOISE);
370                 break;
371             case R.id.vr_button_play_usb_noise:
372                 playerToggleButton(id, SOURCE_NOISE);
373                 break;
374             }
375         }
376     }
377 
setButtonPlayStatus(int playResId)378     private void setButtonPlayStatus(int playResId) {
379         String play = getResources().getText(R.string.af_button_play).toString();
380         String stop = getResources().getText(R.string.af_button_stop).toString();
381 
382         mButtonPlayTone.setText(playResId == R.id.vr_button_play_tone ? stop : play);
383         mButtonPlayNoise.setText(playResId == R.id.vr_button_play_noise ? stop : play);
384         mButtonPlayUsbNoise.setText(playResId ==
385                 R.id.vr_button_play_usb_noise ? stop : play);
386     }
387 
playerSetSource(int sourceIndex)388     private void playerSetSource(int sourceIndex) {
389         switch (sourceIndex) {
390             case SOURCE_TONE:
391                 mSPlayer.setSoundWithResId(mContext, R.raw.onekhztone);
392                 break;
393             default:
394             case SOURCE_NOISE:
395                 mSPlayer.setSoundWithResId(mContext,
396                         R.raw.stereo_mono_white_noise_48);
397                 break;
398         }
399     }
400 
playerTransport(boolean play)401     private void playerTransport(boolean play) {
402         if (!mSPlayer.isAlive()) {
403             mSPlayer.start();
404         }
405         mSPlayer.play(play);
406     }
407 
playerIsPlaying()408     private boolean playerIsPlaying() {
409        return mSPlayer.isPlaying();
410     }
411 
playerStopAll()412     private void playerStopAll() {
413         if (mSPlayer.isAlive() && mSPlayer.isPlaying()) {
414             mSPlayer.play(false);
415             setButtonPlayStatus(-1);
416         }
417     }
418 
showWait(ProgressBar pb, boolean show)419     private void showWait(ProgressBar pb, boolean show) {
420         pb.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
421     }
422 
getTestString(int testId)423     private String getTestString(int testId) {
424         String name = "undefined";
425         switch(testId) {
426             case TEST_TONE:
427                 name = "BuiltIn_tone";
428                 break;
429             case TEST_NOISE:
430                 name = "BuiltIn_noise";
431                 break;
432             case TEST_USB_BACKGROUND:
433                 name = "USB_background";
434                 break;
435             case TEST_USB_NOISE:
436                 name = "USB_noise";
437                 break;
438         }
439         return name;
440     }
441 
showWait(int testId, boolean show)442     private void showWait(int testId, boolean show) {
443         switch(testId) {
444             case TEST_TONE:
445                 showWait(mProgressTone, show);
446                 break;
447             case TEST_NOISE:
448                 showWait(mProgressNoise, show);
449                 break;
450             case TEST_USB_BACKGROUND:
451                 showWait(mProgressUsbBackground, show);
452                 break;
453             case TEST_USB_NOISE:
454                 showWait(mProgressUsbNoise, show);
455                 break;
456         }
457     }
458 
showMessage(int testId, String msg)459     private void showMessage(int testId, String msg) {
460         if (msg != null && msg.length() > 0) {
461             switch(testId) {
462                 case TEST_TONE:
463                     mResultTestTone.setText(msg);
464                     break;
465                 case TEST_NOISE:
466                     mResultTestNoise.setText(msg);
467                     break;
468                 case TEST_USB_BACKGROUND:
469                     mResultTestUsbBackground.setText(msg);
470                     break;
471                 case TEST_USB_NOISE:
472                     mResultTestUsbNoise.setText(msg);
473                     break;
474             }
475         }
476     }
477 
computeAllResults()478     private void computeAllResults() {
479         StringBuilder sb = new StringBuilder();
480 
481         boolean allDone = true;
482 
483         for (int i = 0; i < TEST_COUNT; i++) {
484             allDone = allDone & mTestsDone[i];
485             sb.append(String.format("%s : %s\n", getTestString(i),
486                     mTestsDone[i] ? "DONE" :" NOT DONE"));
487         }
488 
489         if (allDone) {
490             sb.append(computeResults());
491         } else {
492             sb.append("Please execute all tests for results\n");
493         }
494         mGlobalResultText.setText(sb.toString());
495     }
496 
processSpectrum(Results results, AudioBandSpecs[] bandsSpecs, int anchorBand)497     private void processSpectrum(Results results, AudioBandSpecs[] bandsSpecs, int anchorBand) {
498         int points = results.mValuesLog.length;
499         int bandCount = bandsSpecs.length;
500         int currentBand = 0;
501         for (int i = 0; i < points; i++) {
502             double freq = (double) SAMPLING_RATE * i / (double) BLOCK_SIZE_SAMPLES;
503             if (freq > bandsSpecs[currentBand].mFreqStop) {
504                 currentBand++;
505                 if (currentBand >= bandCount)
506                     break;
507             }
508 
509             if (freq >= bandsSpecs[currentBand].mFreqStart) {
510                 results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
511                 results.mPointsPerBand[currentBand]++;
512             }
513         }
514 
515         for (int b = 0; b < bandCount; b++) {
516             if (results.mPointsPerBand[b] > 0) {
517                 results.mAverageEnergyPerBand[b] =
518                         results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
519             }
520         }
521 
522         //set offset relative to band anchor band level
523         double offset = anchorBand > -1 && anchorBand < bandCount ?
524                 results.mAverageEnergyPerBand[anchorBand] : 0;
525         for (int b = 0; b < bandCount; b++) {
526             bandsSpecs[b].setOffset(offset);
527         }
528 
529         //test points in band.
530         currentBand = 0;
531         for (int i = 0; i < points; i++) {
532             double freq = (double) SAMPLING_RATE * i / (double) BLOCK_SIZE_SAMPLES;
533             if (freq > bandsSpecs[currentBand].mFreqStop) {
534                 currentBand++;
535                 if (currentBand >= bandCount)
536                     break;
537             }
538 
539             if (freq >= bandsSpecs[currentBand].mFreqStart) {
540                 double value = results.mValuesLog[i];
541                 if (bandsSpecs[currentBand].isInBounds(freq, value)) {
542                     results.mInBoundPointsPerBand[currentBand]++;
543                 }
544             }
545         }
546     }
547 
548     private String computeResults() {
549         StringBuilder sb = new StringBuilder();
550 
551         int points = mFreqAverageNoise.getSize();
552         //mFreqAverageNoise size is determined by the latest data written to it.
553         //Currently, this data is always BLOCK_SIZE_SAMPLES/2.
554         if (points < 1) {
555             return "error: not enough points";
556         }
557 
558         double[] tone = new double[points];
559         double[] noise = new double[points];
560         double[] reference = new double[points];
561         double[] background = new double[points];
562 
563         mFreqAverageTone.getData(tone, false);
564         mFreqAverageNoise.getData(noise, false);
565         mFreqAverageUsbNoise.getData(reference, false);
566         mFreqAverageUsbBackground.getData(background, false);
567 
568         //Convert to dB
569         double[] toneDb = new double[points];
570         double[] noiseDb = new double[points];
571         double[] referenceDb = new double[points];
572         double[] backgroundDb = new double[points];
573 
574         double[] compensatedNoiseDb = new double[points];
575 
576         for (int i = 0; i < points; i++) {
577             toneDb[i] = 20 * Math.log10(tone[i]);
578             noiseDb[i] = 20 * Math.log10(noise[i]);
579             referenceDb[i] = 20 * Math.log10(reference[i]);
580             backgroundDb[i] = 20 * Math.log10(background[i]);
581 
582             //Use reference measurement to compensate for speaker response.
583             compensatedNoiseDb[i] = noiseDb[i] - referenceDb[i];
584         }
585 
586         mResultsMic.reset();
587         mResultsTone.reset();
588         mResultsBack.reset();
589 
590         mResultsMic.mValuesLog = compensatedNoiseDb;
591         mResultsTone.mValuesLog = toneDb;
592         mResultsBack.mValuesLog = backgroundDb;
593 
594         processSpectrum(mResultsMic, mBandSpecsMic, 1);
595         processSpectrum(mResultsTone, mBandSpecsTone, 1);
596         processSpectrum(mResultsBack, mBandSpecsBack, -1); //no reference for offset
597 
598         //Tone test
599         boolean toneTestSuccess = true;
600         {
601             //rms level should be -36 dbfs +/- 3 db?
602             double rmsMaxDb = 20 * Math.log10(mRMSMaxTone);
603             sb.append(String.format("RMS level of tone: %.2f dBFS\n", rmsMaxDb));
604             sb.append(String.format("Target RMS level: %.2f dBFS +/- %.2f dB\n",
605                     TONE_RMS_EXPECTED,
606                     TONE_RMS_MAX_ERROR));
607             //check that the spectrum is really a tone around 1 khz
608             if (Math.abs(rmsMaxDb - TONE_RMS_EXPECTED) > TONE_RMS_MAX_ERROR) {
609                 toneTestSuccess = false;
610                 sb.append("RMS level test FAILED\n");
611             } else {
612                 sb.append(" RMS level test SUCCESSFUL\n");
613             }
614         }
615 
616         sb.append("\n");
617         sb.append(mResultsTone.toString());
618         if (mResultsTone.testAll()) {
619             sb.append(" 1 Khz Tone Frequency Response Test SUCCESSFUL\n");
620         } else {
621             sb.append(" 1 Khz Tone Frequency Response Test FAILED\n");
622         }
623         sb.append("\n");
624 
625         sb.append("\n");
626         sb.append(mResultsBack.toString());
627         if (mResultsBack.testAll()) {
628             sb.append(" Background environment Test SUCCESSFUL\n");
629         } else {
630             sb.append(" Background environment Test FAILED\n");
631         }
632 
633         sb.append("\n");
634         sb.append(mResultsMic.toString());
635         if (mResultsMic.testAll()) {
636             sb.append(" Frequency Response Test SUCCESSFUL\n");
637         } else {
638             sb.append(" Frequency Response Test FAILED\n");
639         }
640         sb.append("\n");
641 
642         storeTestResults(mResultsTone);
643         storeTestResults(mResultsMic);
644 
645         boolean allTestsPassed = false;
646         if (mResultsMic.testAll() && mResultsTone.testAll() && toneTestSuccess &&
647                 mResultsBack.testAll()) {
648             allTestsPassed = true;
649             String strSuccess = getResources().getString(R.string.audio_general_test_passed);
650             sb.append(strSuccess);
651         } else {
652             String strFailed = getResources().getString(R.string.audio_general_test_failed);
653             sb.append(strFailed);
654         }
655         sb.append("\n");
656 
657         getPassButton().setEnabled(allTestsPassed);
658         return sb.toString();
659     }
660 
661     Thread mTestThread;
startTest(int testId)662     private void startTest(int testId) {
663         if (mTestThread != null && mTestThread.isAlive()) {
664             Log.v(TAG, "test Thread already running.");
665             return;
666         }
667         mRMS = 0;
668         mRMSMax = 0;
669         Log.v(TAG,"Executing test Thread");
670         switch(testId) {
671             case TEST_TONE:
672                 mTestThread = new Thread(new TestRunnable(TEST_TONE) {
673                     public void run() {
674                         super.run();
675                         if (!mUsbMicConnected) {
676                             sendMessage(mTestId, TEST_MESSAGE,
677                                     "Testing Built in Microphone: Tone");
678                             mRMSTone = 0;
679                             mRMSMaxTone = 0;
680                             mFreqAverageTone.reset();
681                             mFreqAverageTone.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
682                             record(TEST_DURATION_TONE_MS);
683                             sendMessage(mTestId, TEST_ENDED, "Testing Completed");
684                             mTestsDone[mTestId] = true;
685                         } else {
686                             sendMessage(mTestId, TEST_ENDED_ERROR,
687                                     "Please Unplug USB Microphone");
688                             mTestsDone[mTestId] = false;
689                         }
690                     }
691                 });
692                 break;
693             case TEST_NOISE:
694                 mTestThread = new Thread(new TestRunnable(TEST_NOISE) {
695                     public void run() {
696                         super.run();
697                         if (!mUsbMicConnected) {
698                             sendMessage(mTestId, TEST_MESSAGE,
699                                     "Testing Built in Microphone: Noise");
700                             mFreqAverageNoise.reset();
701                             mFreqAverageNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
702                             record(TEST_DURATION_NOISE_MS);
703                             sendMessage(mTestId, TEST_ENDED, "Testing Completed");
704                             mTestsDone[mTestId] = true;
705                         } else {
706                             sendMessage(mTestId, TEST_ENDED_ERROR,
707                                     "Please Unplug USB Microphone");
708                             mTestsDone[mTestId] = false;
709                         }
710                     }
711                 });
712                 break;
713             case TEST_USB_BACKGROUND:
714                 playerStopAll();
715                 mTestThread = new Thread(new TestRunnable(TEST_USB_BACKGROUND) {
716                     public void run() {
717                         super.run();
718                         if (mUsbMicConnected) {
719                             sendMessage(mTestId, TEST_MESSAGE,
720                                     "Testing USB Microphone: background");
721                             mFreqAverageUsbBackground.reset();
722                             mFreqAverageUsbBackground.setCaptureType(
723                                     VectorAverage.CAPTURE_TYPE_AVERAGE);
724                             record(TEST_DURATION_USB_BACKGROUND_MS);
725                             sendMessage(mTestId, TEST_ENDED, "Testing Completed");
726                             mTestsDone[mTestId] = true;
727                         } else {
728                             sendMessage(mTestId, TEST_ENDED_ERROR,
729                                     "USB Microphone not detected.");
730                             mTestsDone[mTestId] = false;
731                         }
732                     }
733                 });
734                 break;
735             case TEST_USB_NOISE:
736                 mTestThread = new Thread(new TestRunnable(TEST_USB_NOISE) {
737                     public void run() {
738                         super.run();
739                         if (mUsbMicConnected) {
740                             sendMessage(mTestId, TEST_MESSAGE, "Testing USB Microphone: Noise");
741                             mFreqAverageUsbNoise.reset();
742                             mFreqAverageUsbNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
743                             record(TEST_DURATION_USB_NOISE_MS);
744                             sendMessage(mTestId, TEST_ENDED, "Testing Completed");
745                             mTestsDone[mTestId] = true;
746                         } else {
747                             sendMessage(mTestId, TEST_ENDED_ERROR,
748                                     "USB Microphone not detected.");
749                             mTestsDone[mTestId] = false;
750                         }
751                     }
752                 });
753                 break;
754         }
755         mTestThread.start();
756     }
757 
758     public class TestRunnable implements Runnable {
759         public int mTestId;
760         public boolean mUsbMicConnected;
TestRunnable(int testId)761         TestRunnable(int testId) {
762             Log.v(TAG,"New TestRunnable");
763             mTestId = testId;
764         }
run()765         public void run() {
766             mCurrentTest = mTestId;
767             sendMessage(mTestId, TEST_STARTED,"");
768             mUsbMicConnected =
769                     UsbMicrophoneTester.getIsMicrophoneConnected(mContext);
770         };
record(int durationMs)771         public void record(int durationMs) {
772             mSRecorder.startRecording();
773             try {
774                 Thread.sleep(durationMs);
775             } catch (InterruptedException e) {
776                 e.printStackTrace();
777                 //restore interrupted status
778                 Thread.currentThread().interrupt();
779             }
780             mSRecorder.stopRecording();
781         }
sendMessage(int testId, int msgType, String str)782         public void sendMessage(int testId, int msgType, String str) {
783             Message msg = Message.obtain();
784             msg.what = msgType;
785             msg.obj = str;
786             msg.arg1 = testId;
787             mMessageHandler.sendMessage(msg);
788         }
789     }
790 
791     private Handler mMessageHandler = new Handler() {
792         public void handleMessage(Message msg) {
793             super.handleMessage(msg);
794             int testId = msg.arg1; //testId
795             String str = (String) msg.obj;
796             switch (msg.what) {
797                 case TEST_STARTED:
798                     showWait(testId, true);
799                     break;
800                 case TEST_MESSAGE:
801                     showMessage(testId, str);
802                     break;
803                 case TEST_ENDED:
804                     showWait(testId, false);
805                     playerStopAll();
806                     showMessage(testId, str);
807                     appendResultsToScreen(testId, "test finished");
808                     computeAllResults();
809                     break;
810                 case TEST_ENDED_ERROR:
811                     showWait(testId, false);
812                     playerStopAll();
813                     showMessage(testId, str);
814                     computeAllResults();
815                 default:
816                     Log.e(TAG, String.format("Unknown message: %d", msg.what));
817             }
818         }
819     };
820 
821     private class Results {
822         private int mBandCount;
823         private String mLabel;
824         public double[] mValuesLog;
825         int[] mPointsPerBand; // = new int[mBands];
826         double[] mAverageEnergyPerBand;// = new double[mBands];
827         int[] mInBoundPointsPerBand;// = new int[mBands];
Results(String label, int bandCount)828         public Results(String label, int bandCount) {
829             mLabel = label;
830             mBandCount = bandCount;
831             mPointsPerBand = new int[mBandCount];
832             mAverageEnergyPerBand = new double[mBandCount];
833             mInBoundPointsPerBand = new int[mBandCount];
834         }
reset()835         public void reset() {
836             for (int i = 0; i < mBandCount; i++) {
837                 mPointsPerBand[i] = 0;
838                 mAverageEnergyPerBand[i] = 0;
839                 mInBoundPointsPerBand[i] = 0;
840             }
841         }
842 
843         //append results
toString()844         public String toString() {
845             StringBuilder sb = new StringBuilder();
846             sb.append(String.format("Channel %s\n", mLabel));
847             for (int b = 0; b < mBandCount; b++) {
848                 double percent = 0;
849                 if (mPointsPerBand[b] > 0) {
850                     percent = 100.0 * (double) mInBoundPointsPerBand[b] / mPointsPerBand[b];
851                 }
852                 sb.append(String.format(
853                         " Band %d: Av. Level: %.1f dB InBand: %d/%d (%.1f%%) %s\n",
854                         b, mAverageEnergyPerBand[b],
855                         mInBoundPointsPerBand[b],
856                         mPointsPerBand[b],
857                         percent,
858                         (testInBand(b) ? "OK" : "Not Optimal")));
859             }
860             return sb.toString();
861         }
862 
testInBand(int b)863         public boolean testInBand(int b) {
864             if (b >= 0 && b < mBandCount && mPointsPerBand[b] > 0) {
865                 if ((double) mInBoundPointsPerBand[b] / mPointsPerBand[b] >
866                     MIN_FRACTION_POINTS_IN_BAND) {
867                         return true;
868                 }
869             }
870             return false;
871         }
872 
testAll()873         public boolean testAll() {
874             for (int b = 0; b < mBandCount; b++) {
875                 if (!testInBand(b)) {
876                     return false;
877                 }
878             }
879             return true;
880         }
881     }
882 
883     //append results
appendResultsToScreen(String str, TextView text)884     private void appendResultsToScreen(String str, TextView text) {
885         String currentText = text.getText().toString();
886         text.setText(currentText + "\n" + str);
887     }
888 
appendResultsToScreen(int testId, String str)889     private void appendResultsToScreen(int testId, String str) {
890         switch(testId) {
891             case TEST_TONE:
892                 appendResultsToScreen(str, mResultTestTone);
893                 showToneRMS();
894                 break;
895             case TEST_NOISE:
896                 appendResultsToScreen(str, mResultTestNoise);
897                 break;
898             case TEST_USB_BACKGROUND:
899                 appendResultsToScreen(str, mResultTestUsbBackground);
900                 break;
901             case TEST_USB_NOISE:
902                 appendResultsToScreen(str, mResultTestUsbNoise);
903                 break;
904         }
905     }
906 
907     /**
908      * Store test results in log
909      */
storeTestResults(Results results)910     private void storeTestResults(Results results) {
911         String channelLabel = "channel_" + results.mLabel;
912 
913         CtsVerifierReportLog reportLog = getReportLog();
914         for (int b = 0; b < results.mBandCount; b++) {
915             String bandLabel = String.format(channelLabel + "_%d", b);
916             reportLog.addValue(
917                     bandLabel + "_Level",
918                     results.mAverageEnergyPerBand[b],
919                     ResultType.HIGHER_BETTER,
920                     ResultUnit.NONE);
921 
922             reportLog.addValue(
923                     bandLabel + "_pointsinbound",
924                     results.mInBoundPointsPerBand[b],
925                     ResultType.HIGHER_BETTER,
926                     ResultUnit.COUNT);
927 
928             reportLog.addValue(
929                     bandLabel + "_pointstotal",
930                     results.mPointsPerBand[b],
931                     ResultType.NEUTRAL,
932                     ResultUnit.COUNT);
933         }
934 
935         reportLog.addValues(channelLabel + "_magnitudeSpectrumLog",
936                 results.mValuesLog,
937                 ResultType.NEUTRAL,
938                 ResultUnit.NONE);
939 
940         Log.v(TAG, "Results Stored");
941     }
942 
943     @Override // PassFailButtons
recordTestResults()944     public void recordTestResults() {
945         getReportLog().submit();
946     }
947 
recordHeasetPortFound(boolean found)948     private void recordHeasetPortFound(boolean found) {
949         getReportLog().addValue(
950                 "User Reported Headset Port",
951                 found ? 1.0 : 0,
952                 ResultType.NEUTRAL,
953                 ResultUnit.NONE);
954     }
955 
showToneRMS()956     private void showToneRMS() {
957         String str = String.format("RMS: %.3f dBFS. Max RMS: %.3f dBFS",
958                 20 * Math.log10(mRMSTone),
959                 20 * Math.log10(mRMSMaxTone));
960         showMessage(TEST_TONE, str);
961     }
962 
963 }
964