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