• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
20 import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
21 
22 import android.media.AudioFormat;
23 import android.media.AudioManager;
24 import android.media.AudioRecord;
25 import android.media.MediaRecorder;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.os.SystemClock;
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.ProgressBar;
35 import android.widget.TextView;
36 
37 import com.android.compatibility.common.util.CddTest;
38 import com.android.cts.verifier.CtsVerifierReportLog;
39 import com.android.cts.verifier.R;
40 import com.android.cts.verifier.audio.wavelib.DspBufferComplex;
41 import com.android.cts.verifier.audio.wavelib.DspBufferDouble;
42 import com.android.cts.verifier.audio.wavelib.DspBufferMath;
43 import com.android.cts.verifier.audio.wavelib.DspFftServer;
44 import com.android.cts.verifier.audio.wavelib.DspWindow;
45 import com.android.cts.verifier.audio.wavelib.PipeShort;
46 import com.android.cts.verifier.audio.wavelib.VectorAverage;
47 
48 import org.json.JSONArray;
49 import org.json.JSONObject;
50 
51 /**
52  * Tests Audio built in Microphone response for Unprocessed audio source feature.
53  */
54 @CddTest(requirement = "5.11/C-1-1,C-1-2,C-1-3,C-1-4,C-1-5")
55 public class AudioFrequencyUnprocessedActivity extends AudioFrequencyActivity implements Runnable,
56     AudioRecord.OnRecordPositionUpdateListener {
57     private static final String TAG = "AudioFrequencyUnprocessedActivity";
58 
59     private static final int TEST_STARTED = 900;
60     private static final int TEST_MESSAGE = 903;
61     private static final int TEST_ENDED = 904;
62     private static final int TEST_ENDED_ERROR = 905;
63     private static final double MIN_FRACTION_POINTS_IN_BAND = 0.5;
64 
65     private static final double TONE_RMS_EXPECTED = -36.0;
66     private static final double TONE_RMS_MAX_ERROR = 3.0;
67 
68     private static final double MAX_VAL = Math.pow(2, 15);
69     private static final double CLIP_LEVEL = (MAX_VAL-10) / MAX_VAL;
70 
71     private static final int SOURCE_TONE = 0;
72     private static final int SOURCE_NOISE = 1;
73 
74     private static final int TEST_NONE = -1;
75     private static final int TEST_TONE = 0;
76     private static final int TEST_NOISE = 1;
77     private static final int TEST_USB_BACKGROUND = 2;
78     private static final int TEST_USB_NOISE = 3;
79     private static final int TEST_COUNT = 4;
80     private int mCurrentTest = TEST_NONE;
81     private boolean mTestsDone[] = new boolean[TEST_COUNT];
82 
83     private static final int TEST_DURATION_DEFAULT = 2000;
84     private static final int TEST_DURATION_TONE = TEST_DURATION_DEFAULT;
85     private static final int TEST_DURATION_NOISE = TEST_DURATION_DEFAULT;
86     private static final int TEST_DURATION_USB_BACKGROUND = TEST_DURATION_DEFAULT;
87     private static final int TEST_DURATION_USB_NOISE = TEST_DURATION_DEFAULT;
88 
89     final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
90 
91     Button mButtonTestTone;
92     ProgressBar mProgressTone;
93     TextView mResultTestTone;
94     Button mButtonPlayTone;
95 
96     Button mButtonTestNoise;
97     ProgressBar mProgressNoise;
98     TextView mResultTestNoise;
99     Button mButtonPlayNoise;
100 
101     Button mButtonTestUsbBackground;
102     ProgressBar mProgressUsbBackground;
103     TextView mResultTestUsbBackground;
104 
105     Button mButtonTestUsbNoise;
106     ProgressBar mProgressUsbNoise;
107     TextView mResultTestUsbNoise;
108     Button mButtonPlayUsbNoise;
109 
110     TextView mGlobalResultText;
111     TextView mTextViewUnprocessedStatus;
112 
113     private boolean mIsRecording = false;
114     private final Object mRecordingLock = new Object();
115     private AudioRecord mRecorder;
116     private int mMinRecordBufferSizeInSamples = 0;
117     private short[] mAudioShortArray;
118     private short[] mAudioShortArray2;
119 
120     private final int mBlockSizeSamples = 4096;
121     private final int mSamplingRate = 48000;
122     private final int mSelectedRecordSource = MediaRecorder.AudioSource.UNPROCESSED;
123     private final int mChannelConfig = AudioFormat.CHANNEL_IN_MONO;
124     private final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
125     private Thread mRecordThread;
126 
127     PipeShort mPipe = new PipeShort(65536);
128 
129     SoundPlayerObject mSPlayer;
130 
131     private boolean mSupportsUnprocessed = false;
132 
133     private DspBufferComplex mC;
134     private DspBufferDouble mData;
135 
136     private DspWindow mWindow;
137     private DspFftServer mFftServer;
138     private VectorAverage mFreqAverageTone = new VectorAverage();
139     private VectorAverage mFreqAverageNoise = new VectorAverage();
140     private VectorAverage mFreqAverageUsbBackground = new VectorAverage();
141     private VectorAverage mFreqAverageUsbNoise = new VectorAverage();
142 
143     //RMS for tone:
144     private double mRMS;
145     private double mRMSMax;
146 
147     private double mRMSTone;
148     private double mRMSMaxTone;
149 
150     int mBands = 3;
151     int mBandsTone = 3;
152     int mBandsBack = 3;
153     AudioBandSpecs[] mBandSpecsMic = new AudioBandSpecs[mBands];
154     AudioBandSpecs[] mBandSpecsTone = new AudioBandSpecs[mBandsTone];
155     AudioBandSpecs[] mBandSpecsBack = new AudioBandSpecs[mBandsBack];
156     private Results mResultsMic;
157     private Results mResultsTone;
158     private Results mResultsBack;
159 
160     @Override
onCreate(Bundle savedInstanceState)161     protected void onCreate(Bundle savedInstanceState) {
162         super.onCreate(savedInstanceState);
163         setContentView(R.layout.audio_frequency_unprocessed_activity);
164         mTextViewUnprocessedStatus = (TextView) findViewById(
165                 R.id.audio_frequency_unprocessed_defined);
166         //unprocessed test
167         mSupportsUnprocessed = supportsUnprocessed();
168         if (mSupportsUnprocessed) {
169             mTextViewUnprocessedStatus.setText(
170                     getResources().getText(R.string.audio_frequency_unprocessed_defined));
171         } else {
172             mTextViewUnprocessedStatus.setText(
173                     getResources().getText(R.string.audio_frequency_unprocessed_not_defined));
174         }
175 
176         mSPlayer = new SoundPlayerObject();
177         playerSetSource(SOURCE_TONE);
178 
179         // Test tone
180         mButtonTestTone = (Button) findViewById(R.id.unprocessed_test_tone_btn);
181         mButtonTestTone.setOnClickListener(mBtnClickListener);
182         mProgressTone = (ProgressBar) findViewById(R.id.unprocessed_test_tone_progress_bar);
183         mResultTestTone = (TextView) findViewById(R.id.unprocessed_test_tone_result);
184         mButtonPlayTone = (Button) findViewById(R.id.unprocessed_play_tone_btn);
185         mButtonPlayTone.setOnClickListener(mBtnClickListener);
186         showWait(mProgressTone, false);
187 
188         //Test Noise
189         mButtonTestNoise = (Button) findViewById(R.id.unprocessed_test_noise_btn);
190         mButtonTestNoise.setOnClickListener(mBtnClickListener);
191         mProgressNoise = (ProgressBar) findViewById(R.id.unprocessed_test_noise_progress_bar);
192         mResultTestNoise = (TextView) findViewById(R.id.unprocessed_test_noise_result);
193         mButtonPlayNoise = (Button) findViewById(R.id.unprocessed_play_noise_btn);
194         mButtonPlayNoise.setOnClickListener(mBtnClickListener);
195         showWait(mProgressNoise, false);
196 
197         //USB Background
198         mButtonTestUsbBackground = (Button) findViewById(R.id.unprocessed_test_usb_background_btn);
199         mButtonTestUsbBackground.setOnClickListener(mBtnClickListener);
200         mProgressUsbBackground = (ProgressBar)
201                 findViewById(R.id.unprocessed_test_usb_background_progress_bar);
202         mResultTestUsbBackground = (TextView)
203                 findViewById(R.id.unprocessed_test_usb_background_result);
204         showWait(mProgressUsbBackground, false);
205 
206         mButtonTestUsbNoise = (Button) findViewById(R.id.unprocessed_test_usb_noise_btn);
207         mButtonTestUsbNoise.setOnClickListener(mBtnClickListener);
208         mProgressUsbNoise = (ProgressBar)findViewById(R.id.unprocessed_test_usb_noise_progress_bar);
209         mResultTestUsbNoise = (TextView) findViewById(R.id.unprocessed_test_usb_noise_result);
210         mButtonPlayUsbNoise = (Button) findViewById(R.id.unprocessed_play_usb_noise_btn);
211         mButtonPlayUsbNoise.setOnClickListener(mBtnClickListener);
212         showWait(mProgressUsbNoise, false);
213 
214         setButtonPlayStatus(-1);
215         mGlobalResultText = (TextView) findViewById(R.id.unprocessed_test_global_result);
216 
217         //Init FFT stuff
218         mAudioShortArray2 = new short[mBlockSizeSamples*2];
219         mData = new DspBufferDouble(mBlockSizeSamples);
220         mC = new DspBufferComplex(mBlockSizeSamples);
221         mFftServer = new DspFftServer(mBlockSizeSamples);
222 
223         int overlap = mBlockSizeSamples / 2;
224 
225         mWindow = new DspWindow(DspWindow.WINDOW_HANNING, mBlockSizeSamples, overlap);
226 
227         setPassFailButtonClickListeners();
228         getPassButton().setEnabled(false);
229         setInfoResources(R.string.audio_frequency_unprocessed_test,
230                 R.string.audio_frequency_unprocessed_info, -1);
231 
232         //Init bands for Mic test
233         mBandSpecsMic[0] = new AudioBandSpecs(
234                 30, 100,          /* frequency start,stop */
235                 20.0, -20.0,     /* start top,bottom value */
236                 20.0, -20.0      /* stop top,bottom value */);
237 
238         mBandSpecsMic[1] = new AudioBandSpecs(
239                 100, 7000,       /* frequency start,stop */
240                 10.0, -10.0,     /* start top,bottom value */
241                 10.0, -10.0      /* stop top,bottom value */);
242 
243         mBandSpecsMic[2] = new AudioBandSpecs(
244                 7000, 20000,     /* frequency start,stop */
245                 30.0, -30.0,     /* start top,bottom value */
246                 30.0, -30.0      /* stop top,bottom value */);
247 
248         //Init bands for Tone test
249         mBandSpecsTone[0] = new AudioBandSpecs(
250                 30, 900,          /* frequency start,stop */
251                 -10.0, -100.0,     /* start top,bottom value */
252                 -10.0, -100.0      /* stop top,bottom value */);
253 
254         mBandSpecsTone[1] = new AudioBandSpecs(
255                 900, 1100,       /* frequency start,stop */
256                 10.0, -50.0,     /* start top,bottom value */
257                 10.0, -10.0      /* stop top,bottom value */);
258 
259         mBandSpecsTone[2] = new AudioBandSpecs(
260                 1100, 20000,     /* frequency start,stop */
261                 -30.0, -120.0,     /* start top,bottom value */
262                 -30.0, -120.0      /* stop top,bottom value */);
263 
264       //Init bands for Background test
265         mBandSpecsBack[0] = new AudioBandSpecs(
266                 30, 100,          /* frequency start,stop */
267                 10.0, -120.0,     /* start top,bottom value */
268                 -10.0, -120.0      /* stop top,bottom value */);
269 
270         mBandSpecsBack[1] = new AudioBandSpecs(
271                 100, 7000,       /* frequency start,stop */
272                 -10.0, -120.0,     /* start top,bottom value */
273                 -50.0, -120.0      /* stop top,bottom value */);
274 
275         mBandSpecsBack[2] = new AudioBandSpecs(
276                 7000, 20000,     /* frequency start,stop */
277                 -50.0, -120.0,     /* start top,bottom value */
278                 -50.0, -120.0      /* stop top,bottom value */);
279 
280         mSupportsUnprocessed = supportsUnprocessed();
281         Log.v(TAG, "Supports unprocessed: " + mSupportsUnprocessed);
282 
283         mResultsMic =  new Results("mic_response", TEST_NOISE, mBands);
284         mResultsTone = new Results("tone_response", TEST_TONE, mBandsTone);
285         mResultsBack = new Results("background_response", TEST_USB_BACKGROUND, mBandsBack);
286 
287         connectRefMicUI();
288     }
289 
290     //
291     // Overrides
292     //
enableTestUI(boolean enable)293     void enableTestUI(boolean enable) {
294         mButtonTestTone.setEnabled(enable);
295         mButtonPlayTone.setEnabled(enable);
296 
297         mButtonTestNoise.setEnabled(enable);
298         mButtonPlayNoise.setEnabled(enable);
299 
300         mButtonTestUsbBackground.setEnabled(enable);
301 
302         mButtonTestUsbNoise.setEnabled(enable);
303         mButtonPlayUsbNoise.setEnabled(enable);
304     }
305 
playerToggleButton(int buttonId, int sourceId)306     private void playerToggleButton(int buttonId, int sourceId) {
307         if (playerIsPlaying()) {
308             playerStopAll();
309         } else {
310             playerSetSource(sourceId);
311             playerTransport(true);
312             setButtonPlayStatus(buttonId);
313         }
314     }
315 
316     private class OnBtnClickListener implements OnClickListener {
317         @Override
onClick(View v)318         public void onClick(View v) {
319             int id = v.getId();
320             if (id == R.id.unprocessed_test_tone_btn) {
321                 startTest(TEST_TONE);
322             } else if (id == R.id.unprocessed_play_tone_btn) {
323                 playerToggleButton(id, SOURCE_TONE);
324             } else if (id == R.id.unprocessed_test_noise_btn) {
325                 startTest(TEST_NOISE);
326             } else if (id == R.id.unprocessed_play_noise_btn) {
327                 playerToggleButton(id, SOURCE_NOISE);
328             } else if (id == R.id.unprocessed_test_usb_background_btn) {
329                 startTest(TEST_USB_BACKGROUND);
330             } else if (id == R.id.unprocessed_test_usb_noise_btn) {
331                 startTest(TEST_USB_NOISE);
332             } else if (id == R.id.unprocessed_play_usb_noise_btn) {
333                 playerToggleButton(id, SOURCE_NOISE);
334             }
335         }
336     }
337 
setButtonPlayStatus(int playResId)338     private void setButtonPlayStatus(int playResId) {
339         String play = getResources().getText(R.string.unprocessed_play).toString();
340         String stop = getResources().getText(R.string.unprocessed_stop).toString();
341 
342         mButtonPlayTone.setText(playResId == R.id.unprocessed_play_tone_btn ? stop : play);
343         mButtonPlayNoise.setText(playResId == R.id.unprocessed_play_noise_btn ? stop : play);
344         mButtonPlayUsbNoise.setText(playResId ==
345                 R.id.unprocessed_play_usb_noise_btn ? stop : play);
346     }
347 
playerSetSource(int sourceIndex)348     private void playerSetSource(int sourceIndex) {
349         switch (sourceIndex) {
350             case SOURCE_TONE:
351                 mSPlayer.setSoundWithResId(mContext, R.raw.onekhztone);
352                 break;
353             default:
354             case SOURCE_NOISE:
355                 mSPlayer.setSoundWithResId(mContext,
356                         R.raw.stereo_mono_white_noise_48);
357                 break;
358         }
359     }
360 
playerTransport(boolean play)361     private void playerTransport(boolean play) {
362         if (!mSPlayer.isAlive()) {
363             mSPlayer.start();
364         }
365         mSPlayer.play(play);
366     }
367 
playerIsPlaying()368     private boolean playerIsPlaying() {
369        return mSPlayer.isPlaying();
370     }
371 
playerStopAll()372     private void playerStopAll() {
373         if (mSPlayer.isAlive() && mSPlayer.isPlaying()) {
374             mSPlayer.play(false);
375             setButtonPlayStatus(-1);
376         }
377     }
378 
showWait(ProgressBar pb, boolean show)379     private void showWait(ProgressBar pb, boolean show) {
380         if (show) {
381             pb.setVisibility(View.VISIBLE);
382         } else {
383             pb.setVisibility(View.INVISIBLE);
384         }
385     }
386 
getTestString(int testId)387     private String getTestString(int testId) {
388         String name = "undefined";
389         switch(testId) {
390             case TEST_TONE:
391                 name = "built_in_tone";
392                 break;
393             case TEST_NOISE:
394                 name = "built_in_noise";
395                 break;
396             case TEST_USB_BACKGROUND:
397                 name = "usb_background";
398                 break;
399             case TEST_USB_NOISE:
400                 name = "usb_noise";
401                 break;
402         }
403         return name;
404     }
405 
showWait(int testId, boolean show)406     private void showWait(int testId, boolean show) {
407         switch(testId) {
408             case TEST_TONE:
409                 showWait(mProgressTone, show);
410                 break;
411             case TEST_NOISE:
412                 showWait(mProgressNoise, show);
413                 break;
414             case TEST_USB_BACKGROUND:
415                 showWait(mProgressUsbBackground, show);
416                 break;
417             case TEST_USB_NOISE:
418                 showWait(mProgressUsbNoise, show);
419                 break;
420         }
421     }
422 
showMessage(int testId, String msg)423     private void showMessage(int testId, String msg) {
424         if (msg != null && msg.length() > 0) {
425             switch(testId) {
426                 case TEST_TONE:
427                     mResultTestTone.setText(msg);
428                     break;
429                 case TEST_NOISE:
430                     mResultTestNoise.setText(msg);
431                     break;
432                 case TEST_USB_BACKGROUND:
433                     mResultTestUsbBackground.setText(msg);
434                     break;
435                 case TEST_USB_NOISE:
436                     mResultTestUsbNoise.setText(msg);
437                     break;
438             }
439         }
440     }
441 
supportsUnprocessed()442     private boolean supportsUnprocessed() {
443         boolean unprocessedSupport = false;
444         String unprocessedSupportString = mAudioManager.getProperty(
445                 AudioManager.PROPERTY_SUPPORT_AUDIO_SOURCE_UNPROCESSED);
446         Log.v(TAG,"unprocessed support: " + unprocessedSupportString);
447         if (unprocessedSupportString == null ||
448                 unprocessedSupportString.equalsIgnoreCase(getResources().getString(
449                         R.string.audio_general_default_false_string))) {
450             unprocessedSupport = false;
451         } else {
452             unprocessedSupport = true;
453         }
454         return unprocessedSupport;
455     }
456 
computeAllResults()457     private void computeAllResults() {
458         StringBuilder sb = new StringBuilder();
459 
460         boolean allDone = true;
461 
462         for (int i=0; i<TEST_COUNT; i++) {
463             allDone = allDone & mTestsDone[i];
464             sb.append(String.format("%s : %s\n", getTestString(i),
465                     mTestsDone[i] ? "DONE" :" NOT DONE"));
466         }
467 
468         if (allDone) {
469             sb.append(computeResults());
470         } else {
471             sb.append("Please execute all tests for results\n");
472         }
473         mGlobalResultText.setText(sb.toString());
474     }
475 
processSpectrum(Results results, AudioBandSpecs[] bandsSpecs, int anchorBand)476     private void processSpectrum(Results results, AudioBandSpecs[] bandsSpecs, int anchorBand) {
477         int points = results.mValuesLog.length;
478         int bandCount = bandsSpecs.length;
479         int currentBand = 0;
480         for (int i = 0; i < points; i++) {
481             double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
482             if (freq > bandsSpecs[currentBand].mFreqStop) {
483                 currentBand++;
484                 if (currentBand >= bandCount)
485                     break;
486             }
487 
488             if (freq >= bandsSpecs[currentBand].mFreqStart) {
489                 results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
490                 results.mPointsPerBand[currentBand]++;
491             }
492         }
493 
494         for (int b = 0; b < bandCount; b++) {
495             if (results.mPointsPerBand[b] > 0) {
496                 results.mAverageEnergyPerBand[b] =
497                         results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
498             }
499         }
500 
501         //set offset relative to band anchor band level
502         for (int b = 0; b < bandCount; b++) {
503             if (anchorBand > -1 && anchorBand < bandCount) {
504                 bandsSpecs[b].setOffset(results.mAverageEnergyPerBand[anchorBand]);
505             } else {
506                 bandsSpecs[b].setOffset(0);
507             }
508         }
509 
510         //test points in band.
511         currentBand = 0;
512         for (int i = 0; i < points; i++) {
513             double freq = (double) mSamplingRate * i / (double) mBlockSizeSamples;
514             if (freq >  bandsSpecs[currentBand].mFreqStop) {
515                 currentBand++;
516                 if (currentBand >= bandCount)
517                     break;
518             }
519 
520             if (freq >= bandsSpecs[currentBand].mFreqStart) {
521                 double value = results.mValuesLog[i];
522                 if (bandsSpecs[currentBand].isInBounds(freq, value)) {
523                     results.mInBoundPointsPerBand[currentBand]++;
524                 }
525             }
526         }
527     }
528 
computeResults()529     private String computeResults() {
530         StringBuilder sb = new StringBuilder();
531 
532         int points = mFreqAverageNoise.getSize();
533         //mFreqAverageNoise size is determined by the latest data writen to it.
534         //Currently, this data is always mBlockSizeSamples/2.
535         if (points < 1) {
536             return "error: not enough points";
537         }
538 
539         double[] tone = new double[points];
540         double[] noise = new double[points];
541         double[] reference = new double[points];
542         double[] background = new double[points];
543 
544         mFreqAverageTone.getData(tone, false);
545         mFreqAverageNoise.getData(noise, false);
546         mFreqAverageUsbNoise.getData(reference, false);
547         mFreqAverageUsbBackground.getData(background, false);
548 
549         //Convert to dB
550         double[] toneDb = new double[points];
551         double[] noiseDb = new double[points];
552         double[] referenceDb = new double[points];
553         double[] backgroundDb = new double[points];
554 
555         double[] compensatedNoiseDb = new double[points];
556 
557         for (int i = 0; i < points; i++) {
558             toneDb[i] = 20 * Math.log10(tone[i]);
559             noiseDb[i] = 20 * Math.log10(noise[i]);
560             referenceDb[i] = 20 * Math.log10(reference[i]);
561             backgroundDb[i] = 20 * Math.log10(background[i]);
562 
563             compensatedNoiseDb[i] = noiseDb[i] - referenceDb[i];
564         }
565 
566         mResultsMic.reset();
567         mResultsTone.reset();
568         mResultsBack.reset();
569 
570         mResultsMic.mValuesLog = compensatedNoiseDb;
571         mResultsTone.mValuesLog = toneDb;
572         mResultsBack.mValuesLog = backgroundDb;
573 
574         processSpectrum(mResultsMic, mBandSpecsMic, 1);
575         processSpectrum(mResultsTone, mBandSpecsTone, 1);
576         processSpectrum(mResultsBack, mBandSpecsBack, -1); //no reference for offset
577 
578         //Tone test
579         boolean toneTestSuccess = true;
580         {
581             //rms level should be -36 dbfs +/- 3 db?
582             double rmsMaxDb =  20 * Math.log10(mRMSMaxTone);
583             sb.append(String.format("RMS level of tone: %.2f dBFS\n", rmsMaxDb));
584             sb.append(String.format("Target RMS level: %.2f dBFS +/- %.2f dB\n",
585                     TONE_RMS_EXPECTED,
586                     TONE_RMS_MAX_ERROR));
587             if (Math.abs(rmsMaxDb - TONE_RMS_EXPECTED) > TONE_RMS_MAX_ERROR) {
588                 toneTestSuccess = false;
589                 sb.append("RMS level test FAILED\n");
590             } else {
591                 sb.append(" RMS level test SUCCESSFUL\n");
592             }
593             //check the spectrum is really a tone around 1 khz
594         }
595 
596         sb.append("\n");
597         sb.append(mResultsTone.toString());
598         if (mResultsTone.testAll()) {
599             sb.append(" 1 Khz Tone Frequency Response Test SUCCESSFUL\n");
600         } else {
601             sb.append(" 1 Khz Tone Frequency Response Test FAILED\n");
602         }
603         sb.append("\n");
604 
605         sb.append("\n");
606         sb.append(mResultsBack.toString());
607         if (mResultsBack.testAll()) {
608             sb.append(" Background environment Test SUCCESSFUL\n");
609         } else {
610             sb.append(" Background environment Test FAILED\n");
611         }
612 
613         sb.append("\n");
614         sb.append(mResultsMic.toString());
615         if (mResultsMic.testAll()) {
616             sb.append(" Frequency Response Test SUCCESSFUL\n");
617         } else {
618             sb.append(" Frequency Response Test FAILED\n");
619         }
620         sb.append("\n");
621 
622         storeTestResults(mResultsTone);
623         storeTestResults(mResultsMic);
624         storeTestResults(mResultsBack);
625 
626         boolean allTestsPassed = false;
627         if (mResultsMic.testAll() && mResultsTone.testAll() && toneTestSuccess &&
628                 mResultsBack.testAll()) {
629             allTestsPassed = true;
630             String strSuccess = getResources().getString(R.string.audio_general_test_passed);
631             sb.append(strSuccess);
632         } else {
633             String strFailed = getResources().getString(R.string.audio_general_test_failed);
634             sb.append(strFailed);
635         }
636 
637         sb.append("\n");
638         if (mSupportsUnprocessed) { //test is mandatory
639             sb.append(getResources().getText(
640                     R.string.audio_frequency_unprocessed_defined).toString());
641             if (allTestsPassed) {
642                 getPassButton().setEnabled(true);
643             } else {
644                 getPassButton().setEnabled(false);
645             }
646         } else {
647             //test optional
648             sb.append(getResources().getText(
649                     R.string.audio_frequency_unprocessed_not_defined).toString());
650             getPassButton().setEnabled(true);
651         }
652         return sb.toString();
653     }
654 
655     Thread mTestThread;
startTest(int testId)656     private void startTest(int testId) {
657         if (mTestThread != null && !mTestThread.isAlive()) {
658             mTestThread = null; //kill it.
659         }
660 
661         if (mTestThread == null) {
662             mRMS = 0;
663             mRMSMax = 0;
664             Log.v(TAG,"Executing test Thread");
665             switch(testId) {
666                 case TEST_TONE:
667                     mTestThread = new Thread(new TestRunnable(TEST_TONE) {
668                         public void run() {
669                             super.run();
670                             if (!mUsbMicConnected) {
671                                 sendMessage(mTestId, TEST_MESSAGE,
672                                         "Testing Built in Microphone: Tone");
673                                 mRMSTone = 0;
674                                 mRMSMaxTone = 0;
675                                 mFreqAverageTone.reset();
676                                 mFreqAverageTone.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
677                                 record(TEST_DURATION_TONE);
678                                 sendMessage(mTestId, TEST_ENDED, "Testing Completed");
679                                 mTestsDone[mTestId] = true;
680                             } else {
681                                 sendMessage(mTestId, TEST_ENDED_ERROR,
682                                         "Please Unplug USB Microphone");
683                                 mTestsDone[mTestId] = false;
684                             }
685                         }
686                     });
687                 break;
688                 case TEST_NOISE:
689                     mTestThread = new Thread(new TestRunnable(TEST_NOISE) {
690                         public void run() {
691                             super.run();
692                             if (!mUsbMicConnected) {
693                                 sendMessage(mTestId, TEST_MESSAGE,
694                                         "Testing Built in Microphone: Noise");
695                                 mFreqAverageNoise.reset();
696                                 mFreqAverageNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
697                                 record(TEST_DURATION_NOISE);
698                                 sendMessage(mTestId, TEST_ENDED, "Testing Completed");
699                                 mTestsDone[mTestId] = true;
700                             } else {
701                                 sendMessage(mTestId, TEST_ENDED_ERROR,
702                                         "Please Unplug USB Microphone");
703                                 mTestsDone[mTestId] = false;
704                             }
705                         }
706                     });
707                 break;
708                 case TEST_USB_BACKGROUND:
709                     playerStopAll();
710                     mTestThread = new Thread(new TestRunnable(TEST_USB_BACKGROUND) {
711                         public void run() {
712                             super.run();
713                             if (mUsbMicConnected) {
714                                 sendMessage(mTestId, TEST_MESSAGE,
715                                         "Testing USB Microphone: background");
716                                 mFreqAverageUsbBackground.reset();
717                                 mFreqAverageUsbBackground.setCaptureType(
718                                         VectorAverage.CAPTURE_TYPE_AVERAGE);
719                                 record(TEST_DURATION_USB_BACKGROUND);
720                                 sendMessage(mTestId, TEST_ENDED, "Testing Completed");
721                                 mTestsDone[mTestId] = true;
722                             } else {
723                                 sendMessage(mTestId, TEST_ENDED_ERROR,
724                                         "USB Microphone not detected.");
725                                 mTestsDone[mTestId] = false;
726                             }
727                         }
728                     });
729                 break;
730                 case TEST_USB_NOISE:
731                     mTestThread = new Thread(new TestRunnable(TEST_USB_NOISE) {
732                         public void run() {
733                             super.run();
734                             if (mUsbMicConnected) {
735                                 sendMessage(mTestId, TEST_MESSAGE, "Testing USB Microphone: Noise");
736                                 mFreqAverageUsbNoise.reset();
737                                 mFreqAverageUsbNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
738                                 record(TEST_DURATION_USB_NOISE);
739                                 sendMessage(mTestId, TEST_ENDED, "Testing Completed");
740                                 mTestsDone[mTestId] = true;
741                             } else {
742                                 sendMessage(mTestId, TEST_ENDED_ERROR,
743                                         "USB Microphone not detected.");
744                                 mTestsDone[mTestId] = false;
745                             }
746                         }
747                     });
748                 break;
749             }
750             mTestThread.start();
751         } else {
752             Log.v(TAG,"test Thread already running.");
753         }
754     }
755     public class TestRunnable implements Runnable {
756         public int mTestId;
757         public boolean mUsbMicConnected;
TestRunnable(int testId)758         TestRunnable(int testId) {
759             Log.v(TAG,"New TestRunnable");
760             mTestId = testId;
761         }
run()762         public void run() {
763             mCurrentTest = mTestId;
764             sendMessage(mTestId, TEST_STARTED,"");
765             mUsbMicConnected =
766                     UsbMicrophoneTester.getIsMicrophoneConnected(mContext);
767         };
record(int durationMs)768         public void record(int durationMs) {
769             startRecording();
770             try {
771                 Thread.sleep(durationMs);
772             } catch (InterruptedException e) {
773                 e.printStackTrace();
774                 //restore interrupted status
775                 Thread.currentThread().interrupt();
776             }
777             stopRecording();
778         }
sendMessage(int testId, int msgType, String str)779         public void sendMessage(int testId, int msgType, String str) {
780             Message msg = Message.obtain();
781             msg.what = msgType;
782             msg.obj = str;
783             msg.arg1 = testId;
784             mMessageHandler.sendMessage(msg);
785         }
786     }
787 
788     private Handler mMessageHandler = new Handler() {
789         public void handleMessage(Message msg) {
790             super.handleMessage(msg);
791             int testId = msg.arg1; //testId
792             String str = (String) msg.obj;
793             switch (msg.what) {
794             case TEST_STARTED:
795                 showWait(testId, true);
796 //                getPassButton().setEnabled(false);
797                 break;
798             case TEST_MESSAGE:
799                     showMessage(testId, str);
800                 break;
801             case TEST_ENDED:
802                 showWait(testId, false);
803                 playerStopAll();
804                 showMessage(testId, str);
805                 appendResultsToScreen(testId, "test finished");
806                 computeAllResults();
807                 break;
808             case TEST_ENDED_ERROR:
809                 showWait(testId, false);
810                 playerStopAll();
811                 showMessage(testId, str);
812                 computeAllResults();
813             default:
814                 Log.e(TAG, String.format("Unknown message: %d", msg.what));
815             }
816         }
817     };
818 
819     private class Results {
820         private int mTestId;
821         private int mBandCount;
822         private String mLabel;
823         public double[] mValuesLog;
824         int[] mPointsPerBand; // = new int[mBands];
825         double[] mAverageEnergyPerBand;// = new double[mBands];
826         int[] mInBoundPointsPerBand;// = new int[mBands];
Results(String label, int testId, int bandCount)827         Results(String label, int testId, int bandCount) {
828             mTestId = testId;
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 
884 //    /**
885 //     * compute test results
886 //     */
887 //    private void computeTestResults(int testId) {
888 //        String testName = getTestString(testId);
889 //        appendResultsToScreen(testId, "test finished");
890 //    }
891 
892     //append results
appendResultsToScreen(String str, TextView text)893     private void appendResultsToScreen(String str, TextView text) {
894         String currentText = text.getText().toString();
895         text.setText(currentText + "\n" + str);
896     }
897 
appendResultsToScreen(int testId, String str)898     private void appendResultsToScreen(int testId, String str) {
899         switch(testId) {
900             case TEST_TONE:
901                 appendResultsToScreen(str, mResultTestTone);
902                 showToneRMS();
903                 break;
904             case TEST_NOISE:
905                 appendResultsToScreen(str, mResultTestNoise);
906                 break;
907             case TEST_USB_BACKGROUND:
908                 appendResultsToScreen(str, mResultTestUsbBackground);
909                 break;
910             case TEST_USB_NOISE:
911                 appendResultsToScreen(str, mResultTestUsbNoise);
912                 break;
913         }
914     }
915 
916     /**
917      * Store test results in log
918      */
919     private static final String SECTION_AUDIOFREQUENCYUNPROCESSED =
920             "audio_frequency_unprocessed";
921     @Override
getReportSectionName()922     public final String getReportSectionName() {
923         return setTestNameSuffix(sCurrentDisplayMode, SECTION_AUDIOFREQUENCYUNPROCESSED);
924     }
925 
storeTestResults(Results results)926     private void storeTestResults(Results results) {
927         try {
928             CtsVerifierReportLog reportLog = getReportLog();
929             JSONArray bandsArray = new JSONArray();
930             JSONObject resultsObject = new JSONObject();
931             for (int b = 0; b < mBands; b++) {
932                 JSONObject bandObject = new JSONObject();
933                 bandObject.put(KEY_LEVEL, results.mAverageEnergyPerBand[b]);
934                 bandObject.put(KEY_POINTS_IN_BOUND, results.mInBoundPointsPerBand[b]);
935                 bandObject.put(KEY_POINTS_TOTAL, results.mPointsPerBand[b]);
936                 bandsArray.put(bandObject);
937             }
938             resultsObject.put(KEY_BANDS, bandsArray);
939             resultsObject.put(KEY_MAGNITUDE_SPECTRUM_LOG, new JSONArray(results.mValuesLog));
940             reportLog.addValue(results.mLabel, resultsObject);
941         } catch (Exception e) {
942             Log.e(TAG, LOG_ERROR_STR, e);
943             appendResultsToScreen(results.mTestId, LOG_ERROR_STR);
944         }
945 
946         Log.v(TAG, "Results Recorded");
947     }
948 
949     @Override // PassFailButtons
recordTestResults()950     public void recordTestResults() {
951         getReportLog().submit();
952     }
953 
startRecording()954     private void startRecording() {
955         synchronized (mRecordingLock) {
956             mIsRecording = true;
957         }
958 
959         boolean successful = initRecord();
960         if (successful) {
961             startRecordingForReal();
962         } else {
963             Log.v(TAG, "Recorder initialization error.");
964             synchronized (mRecordingLock) {
965                 mIsRecording = false;
966             }
967         }
968     }
969 
startRecordingForReal()970     private void startRecordingForReal() {
971         // start streaming
972         if (mRecordThread == null) {
973             mRecordThread = new Thread(AudioFrequencyUnprocessedActivity.this);
974             mRecordThread.setName("FrequencyAnalyzerThread");
975         }
976         if (!mRecordThread.isAlive()) {
977             mRecordThread.start();
978         }
979 
980         mPipe.flush();
981 
982         long startTime = SystemClock.uptimeMillis();
983         mRecorder.startRecording();
984         if (mRecorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
985             stopRecording();
986             return;
987         }
988         Log.v(TAG, "Start time: " + (long) (SystemClock.uptimeMillis() - startTime) + " ms");
989     }
990 
stopRecording()991     private void stopRecording() {
992         synchronized (mRecordingLock) {
993             stopRecordingForReal();
994             mIsRecording = false;
995         }
996     }
997 
stopRecordingForReal()998     private void stopRecordingForReal() {
999 
1000         // stop streaming
1001         Thread zeThread = mRecordThread;
1002         mRecordThread = null;
1003         if (zeThread != null) {
1004             zeThread.interrupt();
1005             try {
1006                 zeThread.join();
1007             } catch(InterruptedException e) {
1008                 //restore interrupted status of recording thread
1009                 zeThread.interrupt();
1010             }
1011         }
1012          // release recording resources
1013         if (mRecorder != null) {
1014             mRecorder.stop();
1015             mRecorder.release();
1016             mRecorder = null;
1017         }
1018     }
1019 
initRecord()1020     private boolean initRecord() {
1021         int minRecordBuffSizeInBytes = AudioRecord.getMinBufferSize(mSamplingRate,
1022                 mChannelConfig, mAudioFormat);
1023         Log.v(TAG,"FrequencyAnalyzer: min buff size = " + minRecordBuffSizeInBytes + " bytes");
1024         if (minRecordBuffSizeInBytes <= 0) {
1025             return false;
1026         }
1027 
1028         mMinRecordBufferSizeInSamples = minRecordBuffSizeInBytes / 2;
1029         // allocate the byte array to read the audio data
1030 
1031         mAudioShortArray = new short[mMinRecordBufferSizeInSamples];
1032 
1033         Log.v(TAG, "Initiating record:");
1034         Log.v(TAG, "      using source " + mSelectedRecordSource);
1035         Log.v(TAG, "      at " + mSamplingRate + "Hz");
1036 
1037         try {
1038             mRecorder = new AudioRecord(mSelectedRecordSource, mSamplingRate,
1039                     mChannelConfig, mAudioFormat, 2 * minRecordBuffSizeInBytes);
1040         } catch (IllegalArgumentException e) {
1041             return false;
1042         }
1043         if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
1044             mRecorder.release();
1045             mRecorder = null;
1046             return false;
1047         }
1048         mRecorder.setRecordPositionUpdateListener(this);
1049         mRecorder.setPositionNotificationPeriod(mBlockSizeSamples / 2);
1050         return true;
1051     }
showToneRMS()1052     private void showToneRMS() {
1053         String str = String.format("RMS: %.3f dBFS. Max RMS: %.3f dBFS",
1054                 20 * Math.log10(mRMSTone),
1055                 20 * Math.log10(mRMSMaxTone));
1056         showMessage(TEST_TONE, str);
1057     }
1058 
1059     // ---------------------------------------------------------
1060     // Implementation of AudioRecord.OnPeriodicNotificationListener
1061     // --------------------
onPeriodicNotification(AudioRecord recorder)1062     public void onPeriodicNotification(AudioRecord recorder) {
1063         int samplesAvailable = mPipe.availableToRead();
1064         int samplesNeeded = mBlockSizeSamples;
1065         if (samplesAvailable >= samplesNeeded) {
1066             mPipe.read(mAudioShortArray2, 0, samplesNeeded);
1067 
1068             //compute stuff.
1069             int clipcount = 0;
1070 //            double sum = 0;
1071             double maxabs = 0;
1072             int i;
1073             double rmsTempSum = 0;
1074 
1075             for (i = 0; i < samplesNeeded; i++) {
1076                 double value = mAudioShortArray2[i] / MAX_VAL;
1077                 double valueabs = Math.abs(value);
1078 
1079                 if (valueabs > maxabs) {
1080                     maxabs = valueabs;
1081                 }
1082 
1083                 if (valueabs > CLIP_LEVEL) {
1084                     clipcount++;
1085                 }
1086 
1087                 rmsTempSum += value * value;
1088                 //fft stuff
1089                 mData.mData[i] = value;
1090             }
1091             double rms = Math.sqrt(rmsTempSum / samplesNeeded);
1092 
1093             double alpha = 0.9;
1094             double total_rms = rms * alpha + mRMS *(1-alpha);
1095             mRMS = total_rms;
1096             if (mRMS > mRMSMax) {
1097                 mRMSMax = mRMS;
1098             }
1099 
1100             //for the current frame, compute FFT and send to the viewer.
1101 
1102             //apply window and pack as complex for now.
1103             DspBufferMath.mult(mData, mData, mWindow.mBuffer);
1104             DspBufferMath.set(mC, mData);
1105             mFftServer.fft(mC, 1);
1106 
1107             double[] halfMagnitude = new double[mBlockSizeSamples / 2];
1108             for (i = 0; i < mBlockSizeSamples / 2; i++) {
1109                 halfMagnitude[i] = Math.sqrt(mC.mReal[i] * mC.mReal[i] + mC.mImag[i] * mC.mImag[i]);
1110             }
1111 
1112             switch(mCurrentTest) {
1113                 case TEST_TONE: {
1114                     mFreqAverageTone.setData(halfMagnitude, false);
1115                     //Update realtime info on screen
1116                     mRMSTone = mRMS;
1117                     mRMSMaxTone = mRMSMax;
1118                    showToneRMS();
1119                 }
1120                     break;
1121                 case TEST_NOISE:
1122                     mFreqAverageNoise.setData(halfMagnitude, false);
1123                     break;
1124                 case TEST_USB_BACKGROUND:
1125                     mFreqAverageUsbBackground.setData(halfMagnitude, false);
1126                     break;
1127                 case TEST_USB_NOISE:
1128                     mFreqAverageUsbNoise.setData(halfMagnitude, false);
1129                     break;
1130             }
1131         }
1132     }
1133 
onMarkerReached(AudioRecord track)1134     public void onMarkerReached(AudioRecord track) {
1135     }
1136 
1137     // ---------------------------------------------------------
1138     // Implementation of Runnable for the audio recording + playback
1139     // --------------------
run()1140     public void run() {
1141         Thread thisThread = Thread.currentThread();
1142         while (!thisThread.isInterrupted()) {
1143             // read from native recorder
1144             int nSamplesRead = mRecorder.read(mAudioShortArray, 0, mMinRecordBufferSizeInSamples);
1145             if (nSamplesRead > 0) {
1146                 mPipe.write(mAudioShortArray, 0, nSamplesRead);
1147             }
1148         }
1149     }
1150 }
1151