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 com.android.cts.verifier.PassFailButtons; 20 import com.android.cts.verifier.R; 21 22 import com.android.cts.verifier.audio.wavelib.*; 23 import com.android.compatibility.common.util.ReportLog; 24 import com.android.compatibility.common.util.ResultType; 25 import com.android.compatibility.common.util.ResultUnit; 26 import android.content.Context; 27 28 import android.content.BroadcastReceiver; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 32 import android.media.AudioDeviceCallback; 33 import android.media.AudioDeviceInfo; 34 import android.media.AudioFormat; 35 import android.media.AudioManager; 36 import android.media.AudioTrack; 37 import android.media.AudioRecord; 38 import android.media.MediaRecorder; 39 40 import android.os.Bundle; 41 import android.os.Handler; 42 import android.os.Message; 43 import android.os.SystemClock; 44 45 import android.util.Log; 46 47 import android.view.View; 48 import android.view.View.OnClickListener; 49 50 import android.widget.Button; 51 import android.widget.TextView; 52 import android.widget.SeekBar; 53 import android.widget.LinearLayout; 54 import android.widget.ProgressBar; 55 56 /** 57 * Tests Audio built in Microphone response using external speakers and USB reference microphone. 58 */ 59 public class AudioFrequencyMicActivity extends AudioFrequencyActivity implements Runnable, 60 AudioRecord.OnRecordPositionUpdateListener { 61 private static final String TAG = "AudioFrequencyMicActivity"; 62 63 private static final int TEST_STARTED = 900; 64 private static final int TEST_MESSAGE = 903; 65 private static final int TEST_ENDED = 904; 66 private static final int TEST_ENDED_ERROR = 905; 67 68 private static final double MIN_ENERGY_BAND_1 = -50.0; //dB Full Scale 69 private static final double MAX_ENERGY_BAND_1_BASE = -60.0; //dB Full Scale 70 private static final double MIN_FRACTION_POINTS_IN_BAND = 0.3; 71 private static final double MAX_VAL = Math.pow(2, 15); 72 private static final double CLIP_LEVEL = (MAX_VAL-10) / MAX_VAL; 73 74 private static final int TEST_NONE = -1; 75 private static final int TEST_NOISE = 0; 76 private static final int TEST_USB_BACKGROUND = 1; 77 private static final int TEST_USB_NOISE = 2; 78 private static final int TEST_COUNT = 3; 79 private int mCurrentTest = TEST_NONE; 80 private boolean mTestsDone[] = new boolean[TEST_COUNT]; 81 82 private static final int TEST_DURATION_DEFAULT = 2000; 83 private static final int TEST_DURATION_NOISE = TEST_DURATION_DEFAULT; 84 private static final int TEST_DURATION_USB_BACKGROUND = TEST_DURATION_DEFAULT; 85 private static final int TEST_DURATION_USB_NOISE = TEST_DURATION_DEFAULT; 86 87 final OnBtnClickListener mBtnClickListener = new OnBtnClickListener(); 88 Context mContext; 89 90 LinearLayout mLayoutTestNoise; 91 Button mButtonTestNoise; 92 ProgressBar mProgressNoise; 93 TextView mResultTestNoise; 94 Button mButtonPlayNoise; 95 96 LinearLayout mLayoutTestUsbBackground; 97 Button mButtonTestUsbBackground; 98 ProgressBar mProgressUsbBackground; 99 TextView mResultTestUsbBackground; 100 101 LinearLayout mLayoutTestUsbNoise; 102 Button mButtonTestUsbNoise; 103 ProgressBar mProgressUsbNoise; 104 TextView mResultTestUsbNoise; 105 Button mButtonPlayUsbNoise; 106 107 TextView mGlobalResultText; 108 109 private boolean mIsRecording = false; 110 private final Object mRecordingLock = new Object(); 111 private AudioRecord mRecorder; 112 private int mMinRecordBufferSizeInSamples = 0; 113 private short[] mAudioShortArray; 114 private short[] mAudioShortArray2; 115 116 private final int mBlockSizeSamples = 1024; 117 private final int mSamplingRate = 48000; 118 private final int mSelectedRecordSource = MediaRecorder.AudioSource.VOICE_RECOGNITION; 119 private final int mChannelConfig = AudioFormat.CHANNEL_IN_MONO; 120 private final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; 121 private Thread mRecordThread; 122 123 PipeShort mPipe = new PipeShort(65536); 124 SoundPlayerObject mSPlayer; 125 126 private DspBufferComplex mC; 127 private DspBufferDouble mData; 128 129 private DspWindow mWindow; 130 private DspFftServer mFftServer; 131 private VectorAverage mFreqAverageUsbBackground = new VectorAverage(); 132 private VectorAverage mFreqAverageNoise = new VectorAverage(); 133 private VectorAverage mFreqAverageUsbNoise = new VectorAverage(); 134 135 136 int mBands = 4; 137 AudioBandSpecs[] bandSpecsArray = new AudioBandSpecs[mBands]; 138 AudioBandSpecs[] baseBandSpecsArray = new AudioBandSpecs[mBands]; 139 140 @Override 141 protected void onCreate(Bundle savedInstanceState) { 142 super.onCreate(savedInstanceState); 143 setContentView(R.layout.audio_frequency_mic_activity); 144 mContext = this; 145 146 //Test Noise 147 mLayoutTestNoise = (LinearLayout) findViewById(R.id.frequency_mic_layout_test_noise); 148 mButtonTestNoise = (Button) findViewById(R.id.frequency_mic_test_noise_btn); 149 mButtonTestNoise.setOnClickListener(mBtnClickListener); 150 mProgressNoise = (ProgressBar) findViewById(R.id.frequency_mic_test_noise_progress_bar); 151 mResultTestNoise = (TextView) findViewById(R.id.frequency_mic_test_noise_result); 152 mButtonPlayNoise = (Button) findViewById(R.id.frequency_mic_play_noise_btn); 153 mButtonPlayNoise.setOnClickListener(mBtnClickListener); 154 showWait(mProgressNoise, false); 155 156 //USB Background 157 mLayoutTestUsbBackground = (LinearLayout) 158 findViewById(R.id.frequency_mic_layout_test_usb_background); 159 mButtonTestUsbBackground = (Button) 160 findViewById(R.id.frequency_mic_test_usb_background_btn); 161 mButtonTestUsbBackground.setOnClickListener(mBtnClickListener); 162 mProgressUsbBackground = (ProgressBar) 163 findViewById(R.id.frequency_mic_test_usb_background_progress_bar); 164 mResultTestUsbBackground = (TextView) 165 findViewById(R.id.frequency_mic_test_usb_background_result); 166 showWait(mProgressUsbBackground, false); 167 168 mLayoutTestUsbNoise = (LinearLayout) findViewById(R.id.frequency_mic_layout_test_usb_noise); 169 mButtonTestUsbNoise = (Button) findViewById(R.id.frequency_mic_test_usb_noise_btn); 170 mButtonTestUsbNoise.setOnClickListener(mBtnClickListener); 171 mProgressUsbNoise = (ProgressBar) 172 findViewById(R.id.frequency_mic_test_usb_noise_progress_bar); 173 mResultTestUsbNoise = (TextView) findViewById(R.id.frequency_mic_test_usb_noise_result); 174 mButtonPlayUsbNoise = (Button) findViewById(R.id.frequency_mic_play_usb_noise_btn); 175 mButtonPlayUsbNoise.setOnClickListener(mBtnClickListener); 176 showWait(mProgressUsbNoise, false); 177 178 mGlobalResultText = (TextView) findViewById(R.id.frequency_mic_test_global_result); 179 180 mSPlayer = new SoundPlayerObject(); 181 mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.stereo_mono_white_noise_48); 182 mSPlayer.setBalance(0.5f); 183 184 //Init FFT stuff 185 mAudioShortArray2 = new short[mBlockSizeSamples*2]; 186 mData = new DspBufferDouble(mBlockSizeSamples); 187 mC = new DspBufferComplex(mBlockSizeSamples); 188 mFftServer = new DspFftServer(mBlockSizeSamples); 189 190 int overlap = mBlockSizeSamples / 2; 191 192 mWindow = new DspWindow(DspWindow.WINDOW_HANNING, mBlockSizeSamples, overlap); 193 194 setPassFailButtonClickListeners(); 195 getPassButton().setEnabled(false); 196 setInfoResources(R.string.audio_frequency_mic_test, 197 R.string.frequency_mic_info, -1); 198 199 //Init bands for BuiltIn/Reference test 200 bandSpecsArray[0] = new AudioBandSpecs( 201 50, 500, /* frequency start,stop */ 202 4.0, -50, /* start top,bottom value */ 203 4.0, -4.0 /* stop top,bottom value */); 204 205 bandSpecsArray[1] = new AudioBandSpecs( 206 500,4000, /* frequency start,stop */ 207 4.0, -4.0, /* start top,bottom value */ 208 4.0, -4.0 /* stop top,bottom value */); 209 210 bandSpecsArray[2] = new AudioBandSpecs( 211 4000, 12000, /* frequency start,stop */ 212 4.0, -4.0, /* start top,bottom value */ 213 5.0, -5.0 /* stop top,bottom value */); 214 215 bandSpecsArray[3] = new AudioBandSpecs( 216 12000, 20000, /* frequency start,stop */ 217 5.0, -5.0, /* start top,bottom value */ 218 5.0, -30.0 /* stop top,bottom value */); 219 220 //Init base bands for silence 221 baseBandSpecsArray[0] = new AudioBandSpecs( 222 50, 500, /* frequency start,stop */ 223 40.0, -50.0, /* start top,bottom value */ 224 5.0, -50.0 /* stop top,bottom value */); 225 226 baseBandSpecsArray[1] = new AudioBandSpecs( 227 500,4000, /* frequency start,stop */ 228 5.0, -50.0, /* start top,bottom value */ 229 5.0, -50.0 /* stop top,bottom value */); 230 231 baseBandSpecsArray[2] = new AudioBandSpecs( 232 4000, 12000, /* frequency start,stop */ 233 5.0, -50.0, /* start top,bottom value */ 234 5.0, -50.0 /* stop top,bottom value */); 235 236 baseBandSpecsArray[3] = new AudioBandSpecs( 237 12000, 20000, /* frequency start,stop */ 238 5.0, -50.0, /* start top,bottom value */ 239 5.0, -50.0 /* stop top,bottom value */); 240 241 } 242 private void playerToggleButton(int buttonId) { 243 if (playerIsPlaying()) { 244 playerStopAll(); 245 } else { 246 playerTransport(true); 247 setButtonPlayStatus(buttonId); 248 } 249 } 250 251 private class OnBtnClickListener implements OnClickListener { 252 @Override 253 public void onClick(View v) { 254 int id = v.getId(); 255 switch (id) { 256 case R.id.frequency_mic_test_noise_btn: 257 startTest(TEST_NOISE); 258 break; 259 case R.id.frequency_mic_play_noise_btn: 260 playerToggleButton(id); 261 break; 262 case R.id.frequency_mic_test_usb_background_btn: 263 startTest(TEST_USB_BACKGROUND); 264 break; 265 case R.id.frequency_mic_test_usb_noise_btn: 266 startTest(TEST_USB_NOISE); 267 break; 268 case R.id.frequency_mic_play_usb_noise_btn: 269 playerToggleButton(id); 270 break; 271 } 272 } 273 } 274 275 private void setButtonPlayStatus(int playResId) { 276 String play = getResources().getText(R.string.frequency_mic_play).toString(); 277 String stop = getResources().getText(R.string.frequency_mic_stop).toString(); 278 279 mButtonPlayNoise.setText(playResId == R.id.frequency_mic_play_noise_btn ? stop : play); 280 mButtonPlayUsbNoise.setText(playResId == 281 R.id.frequency_mic_play_usb_noise_btn ? stop : play); 282 } 283 284 private void playerTransport(boolean play) { 285 if (!mSPlayer.isAlive()) { 286 mSPlayer.start(); 287 } 288 mSPlayer.play(play); 289 } 290 291 private boolean playerIsPlaying() { 292 return mSPlayer.isPlaying(); 293 } 294 295 private void playerStopAll() { 296 if (mSPlayer.isAlive() && mSPlayer.isPlaying()) { 297 mSPlayer.play(false); 298 setButtonPlayStatus(-1); 299 } 300 } 301 302 /** 303 * enable test ui elements 304 */ 305 private void enableLayout(LinearLayout layout, boolean enable) { 306 for (int i = 0; i < layout.getChildCount(); i++) { 307 View view = layout.getChildAt(i); 308 view.setEnabled(enable); 309 } 310 } 311 312 private void showWait(ProgressBar pb, boolean show) { 313 if (show) { 314 pb.setVisibility(View.VISIBLE); 315 } else { 316 pb.setVisibility(View.INVISIBLE); 317 } 318 } 319 320 private void showWait(int testId, boolean show) { 321 switch(testId) { 322 case TEST_NOISE: 323 showWait(mProgressNoise, show); 324 break; 325 case TEST_USB_BACKGROUND: 326 showWait(mProgressUsbBackground, show); 327 break; 328 case TEST_USB_NOISE: 329 showWait(mProgressUsbNoise, show); 330 break; 331 } 332 } 333 334 private String getTestString(int testId) { 335 String name = "undefined"; 336 switch(testId) { 337 case TEST_NOISE: 338 name = "BuiltIn_noise"; 339 break; 340 case TEST_USB_BACKGROUND: 341 name = "USB_background"; 342 break; 343 case TEST_USB_NOISE: 344 name = "USB_noise"; 345 break; 346 } 347 return name; 348 } 349 350 private void showMessage(int testId, String msg) { 351 if (msg != null && msg.length() > 0) { 352 switch(testId) { 353 case TEST_NOISE: 354 mResultTestNoise.setText(msg); 355 break; 356 case TEST_USB_BACKGROUND: 357 mResultTestUsbBackground.setText(msg); 358 break; 359 case TEST_USB_NOISE: 360 mResultTestUsbNoise.setText(msg); 361 break; 362 } 363 } 364 } 365 366 Thread mTestThread; 367 private void startTest(int testId) { 368 if (mTestThread != null && !mTestThread.isAlive()) { 369 mTestThread = null; //kill it. 370 } 371 372 if (mTestThread == null) { 373 Log.v(TAG,"Executing test Thread"); 374 switch(testId) { 375 case TEST_NOISE: 376 mTestThread = new Thread(new TestRunnable(TEST_NOISE) { 377 public void run() { 378 super.run(); 379 if (!mUsbMicConnected) { 380 sendMessage(mTestId, TEST_MESSAGE, 381 "Testing Built in Microphone: Noise"); 382 mFreqAverageNoise.reset(); 383 mFreqAverageNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX); 384 record(TEST_DURATION_NOISE); 385 sendMessage(mTestId, TEST_ENDED, "Testing Completed"); 386 mTestsDone[mTestId] = true; 387 } else { 388 sendMessage(mTestId, TEST_ENDED_ERROR, 389 "Please Unplug USB Microphone"); 390 mTestsDone[mTestId] = false; 391 } 392 } 393 }); 394 break; 395 case TEST_USB_BACKGROUND: 396 playerStopAll(); 397 mTestThread = new Thread(new TestRunnable(TEST_USB_BACKGROUND) { 398 public void run() { 399 super.run(); 400 if (mUsbMicConnected) { 401 sendMessage(mTestId, TEST_MESSAGE, 402 "Testing USB Microphone: background"); 403 mFreqAverageUsbBackground.reset(); 404 mFreqAverageUsbBackground.setCaptureType( 405 VectorAverage.CAPTURE_TYPE_AVERAGE); 406 record(TEST_DURATION_USB_BACKGROUND); 407 sendMessage(mTestId, TEST_ENDED, "Testing Completed"); 408 mTestsDone[mTestId] = true; 409 } else { 410 sendMessage(mTestId, TEST_ENDED_ERROR, 411 "USB Microphone not detected."); 412 mTestsDone[mTestId] = false; 413 } 414 } 415 }); 416 break; 417 case TEST_USB_NOISE: 418 mTestThread = new Thread(new TestRunnable(TEST_USB_NOISE) { 419 public void run() { 420 super.run(); 421 if (mUsbMicConnected) { 422 sendMessage(mTestId, TEST_MESSAGE, "Testing USB Microphone: Noise"); 423 mFreqAverageUsbNoise.reset(); 424 mFreqAverageUsbNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX); 425 record(TEST_DURATION_USB_NOISE); 426 sendMessage(mTestId, TEST_ENDED, "Testing Completed"); 427 mTestsDone[mTestId] = true; 428 } else { 429 sendMessage(mTestId, TEST_ENDED_ERROR, 430 "USB Microphone not detected."); 431 mTestsDone[mTestId] = false; 432 } 433 } 434 }); 435 break; 436 } 437 mTestThread.start(); 438 } else { 439 Log.v(TAG,"test Thread already running."); 440 } 441 } 442 public class TestRunnable implements Runnable { 443 public int mTestId; 444 public boolean mUsbMicConnected; 445 TestRunnable(int testId) { 446 Log.v(TAG,"New TestRunnable"); 447 mTestId = testId; 448 } 449 public void run() { 450 mCurrentTest = mTestId; 451 sendMessage(mTestId, TEST_STARTED,""); 452 mUsbMicConnected = 453 UsbMicrophoneTester.getIsMicrophoneConnected(getApplicationContext()); 454 }; 455 public void record(int durationMs) { 456 startRecording(); 457 try { 458 Thread.sleep(durationMs); 459 } catch (InterruptedException e) { 460 e.printStackTrace(); 461 //restore interrupted status 462 Thread.currentThread().interrupt(); 463 } 464 stopRecording(); 465 } 466 public void sendMessage(int testId, int msgType, String str) { 467 Message msg = Message.obtain(); 468 msg.what = msgType; 469 msg.obj = str; 470 msg.arg1 = testId; 471 mMessageHandler.sendMessage(msg); 472 } 473 } 474 475 private Handler mMessageHandler = new Handler() { 476 public void handleMessage(Message msg) { 477 super.handleMessage(msg); 478 int testId = msg.arg1; //testId 479 String str = (String) msg.obj; 480 switch (msg.what) { 481 case TEST_STARTED: 482 showWait(testId, true); 483 break; 484 case TEST_MESSAGE: 485 showMessage(testId, str); 486 break; 487 case TEST_ENDED: 488 showWait(testId, false); 489 playerStopAll(); 490 showMessage(testId, str); 491 appendResultsToScreen(testId, "test finished"); 492 computeAllResults(); 493 break; 494 case TEST_ENDED_ERROR: 495 showWait(testId, false); 496 playerStopAll(); 497 showMessage(testId, str); 498 computeAllResults(); 499 default: 500 Log.e(TAG, String.format("Unknown message: %d", msg.what)); 501 } 502 } 503 }; 504 505 private class Results { 506 private String mLabel; 507 public double[] mValuesLog; 508 int[] mPointsPerBand = new int[mBands]; 509 double[] mAverageEnergyPerBand = new double[mBands]; 510 int[] mInBoundPointsPerBand = new int[mBands]; 511 public boolean mIsBaseMeasurement = false; 512 public Results(String label) { 513 mLabel = label; 514 } 515 516 //append results 517 public String toString() { 518 StringBuilder sb = new StringBuilder(); 519 sb.append(String.format("Channel %s\n", mLabel)); 520 sb.append("Level in Band 1 : " + (testLevel() ? "OK" :"Not Optimal") + 521 (mIsBaseMeasurement ? " (Base Meas.)" : "") + "\n"); 522 for (int b = 0; b < mBands; b++) { 523 double percent = 0; 524 if (mPointsPerBand[b] > 0) { 525 percent = 100.0 * (double) mInBoundPointsPerBand[b] / mPointsPerBand[b]; 526 } 527 sb.append(String.format( 528 " Band %d: Av. Level: %.1f dB InBand: %d/%d (%.1f%%) %s\n", 529 b, mAverageEnergyPerBand[b], 530 mInBoundPointsPerBand[b], 531 mPointsPerBand[b], 532 percent, 533 (testInBand(b) ? "OK" : "Not Optimal"))); 534 } 535 return sb.toString(); 536 } 537 538 public boolean testLevel() { 539 if (mIsBaseMeasurement && mAverageEnergyPerBand[1] <= MAX_ENERGY_BAND_1_BASE) { 540 return true; 541 } else if (mAverageEnergyPerBand[1] >= MIN_ENERGY_BAND_1) { 542 return true; 543 } 544 return false; 545 } 546 547 public boolean testInBand(int b) { 548 if (b >= 0 && b < mBands && mPointsPerBand[b] > 0) { 549 if ((double) mInBoundPointsPerBand[b] / mPointsPerBand[b] > 550 MIN_FRACTION_POINTS_IN_BAND) { 551 return true; 552 } 553 } 554 return false; 555 } 556 557 public boolean testAll() { 558 if (!testLevel()) { 559 return false; 560 } 561 for (int b = 0; b < mBands; b++) { 562 if (!testInBand(b)) { 563 return false; 564 } 565 } 566 return true; 567 } 568 } 569 570 private void computeAllResults() { 571 StringBuilder sb = new StringBuilder(); 572 573 boolean allDone = true; 574 575 for (int i = 0; i < TEST_COUNT; i++) { 576 allDone = allDone & mTestsDone[i]; 577 sb.append(String.format("%s : %s\n", getTestString(i), 578 mTestsDone[i] ? "DONE" :" NOT DONE")); 579 } 580 581 if (allDone) { 582 sb.append(computeResults()); 583 } else { 584 sb.append("Please execute all tests for results\n"); 585 } 586 mGlobalResultText.setText(sb.toString()); 587 } 588 589 private String computeResults() { 590 StringBuilder sb = new StringBuilder(); 591 592 sb.append("\n"); 593 594 Results resultsBuiltIn = new Results(getTestString(TEST_NOISE)); 595 if (computeResultsForVector(mFreqAverageNoise, resultsBuiltIn, false, bandSpecsArray)) { 596 sb.append(resultsBuiltIn.toString()); 597 sb.append("\n"); 598 recordTestResults(resultsBuiltIn); 599 } 600 601 Results resultsBase = new Results(getTestString(TEST_USB_BACKGROUND)); 602 if (computeResultsForVector(mFreqAverageUsbBackground, resultsBase, true, 603 baseBandSpecsArray)) { 604 sb.append(resultsBase.toString()); 605 sb.append("\n"); 606 recordTestResults(resultsBase); 607 } 608 609 Results resultsUsbNoise = new Results(getTestString(TEST_USB_NOISE)); 610 if (computeResultsForVector(mFreqAverageUsbNoise, resultsUsbNoise, false, 611 bandSpecsArray)) { 612 sb.append(resultsUsbNoise.toString()); 613 sb.append("\n"); 614 recordTestResults(resultsUsbNoise); 615 getPassButton().setEnabled(true); 616 } 617 return sb.toString(); 618 } 619 620 private boolean computeResultsForVector(VectorAverage freqAverage, Results results, 621 boolean isBase, AudioBandSpecs[] bandSpecs) { 622 623 results.mIsBaseMeasurement = isBase; 624 int points = freqAverage.getSize(); 625 if (points > 0) { 626 //compute vector in db 627 double[] values = new double[points]; 628 freqAverage.getData(values, false); 629 results.mValuesLog = new double[points]; 630 for (int i = 0; i < points; i++) { 631 results.mValuesLog[i] = 20 * Math.log10(values[i]); 632 } 633 634 int currentBand = 0; 635 for (int i = 0; i < points; i++) { 636 double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples; 637 if (freq > bandSpecs[currentBand].mFreqStop) { 638 currentBand++; 639 if (currentBand >= mBands) 640 break; 641 } 642 643 if (freq >= bandSpecs[currentBand].mFreqStart) { 644 results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i]; 645 results.mPointsPerBand[currentBand]++; 646 } 647 } 648 649 for (int b = 0; b < mBands; b++) { 650 if (results.mPointsPerBand[b] > 0) { 651 results.mAverageEnergyPerBand[b] = 652 results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b]; 653 } 654 } 655 656 //set offset relative to band 1 level 657 for (int b = 0; b < mBands; b++) { 658 bandSpecs[b].setOffset(results.mAverageEnergyPerBand[1]); 659 } 660 661 //test points in band. 662 currentBand = 0; 663 for (int i = 0; i < points; i++) { 664 double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples; 665 if (freq > bandSpecs[currentBand].mFreqStop) { 666 currentBand++; 667 if (currentBand >= mBands) 668 break; 669 } 670 671 if (freq >= bandSpecs[currentBand].mFreqStart) { 672 double value = results.mValuesLog[i]; 673 if (bandSpecs[currentBand].isInBounds(freq, value)) { 674 results.mInBoundPointsPerBand[currentBand]++; 675 } 676 } 677 } 678 return true; 679 } else { 680 return false; 681 } 682 } 683 684 //append results 685 private void appendResultsToScreen(String str, TextView text) { 686 String currentText = text.getText().toString(); 687 text.setText(currentText + "\n" + str); 688 } 689 690 private void appendResultsToScreen(int testId, String str) { 691 switch(testId) { 692 case TEST_NOISE: 693 appendResultsToScreen(str, mResultTestNoise); 694 break; 695 case TEST_USB_BACKGROUND: 696 appendResultsToScreen(str, mResultTestUsbBackground); 697 break; 698 case TEST_USB_NOISE: 699 appendResultsToScreen(str, mResultTestUsbNoise); 700 break; 701 } 702 } 703 704 /** 705 * Store test results in log 706 */ 707 private void recordTestResults(Results results) { 708 String channelLabel = "channel_" + results.mLabel; 709 710 for (int b = 0; b < mBands; b++) { 711 String bandLabel = String.format(channelLabel + "_%d", b); 712 getReportLog().addValue( 713 bandLabel + "_Level", 714 results.mAverageEnergyPerBand[b], 715 ResultType.HIGHER_BETTER, 716 ResultUnit.NONE); 717 718 getReportLog().addValue( 719 bandLabel + "_pointsinbound", 720 results.mInBoundPointsPerBand[b], 721 ResultType.HIGHER_BETTER, 722 ResultUnit.COUNT); 723 724 getReportLog().addValue( 725 bandLabel + "_pointstotal", 726 results.mPointsPerBand[b], 727 ResultType.NEUTRAL, 728 ResultUnit.COUNT); 729 } 730 731 getReportLog().addValues(channelLabel + "_magnitudeSpectrumLog", 732 results.mValuesLog, 733 ResultType.NEUTRAL, 734 ResultUnit.NONE); 735 736 Log.v(TAG, "Results Recorded"); 737 } 738 739 private void recordHeasetPortFound(boolean found) { 740 getReportLog().addValue( 741 "User Reported Headset Port", 742 found ? 1.0 : 0, 743 ResultType.NEUTRAL, 744 ResultUnit.NONE); 745 } 746 747 private void startRecording() { 748 synchronized (mRecordingLock) { 749 mIsRecording = true; 750 } 751 752 boolean successful = initRecord(); 753 if (successful) { 754 startRecordingForReal(); 755 } else { 756 Log.v(TAG, "Recorder initialization error."); 757 synchronized (mRecordingLock) { 758 mIsRecording = false; 759 } 760 } 761 } 762 763 private void startRecordingForReal() { 764 // start streaming 765 if (mRecordThread == null) { 766 mRecordThread = new Thread(AudioFrequencyMicActivity.this); 767 mRecordThread.setName("FrequencyAnalyzerThread"); 768 } 769 if (!mRecordThread.isAlive()) { 770 mRecordThread.start(); 771 } 772 773 mPipe.flush(); 774 775 long startTime = SystemClock.uptimeMillis(); 776 mRecorder.startRecording(); 777 if (mRecorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) { 778 stopRecording(); 779 return; 780 } 781 Log.v(TAG, "Start time: " + (long) (SystemClock.uptimeMillis() - startTime) + " ms"); 782 } 783 784 private void stopRecording() { 785 synchronized (mRecordingLock) { 786 stopRecordingForReal(); 787 mIsRecording = false; 788 } 789 } 790 791 private void stopRecordingForReal() { 792 793 // stop streaming 794 Thread zeThread = mRecordThread; 795 mRecordThread = null; 796 if (zeThread != null) { 797 zeThread.interrupt(); 798 try { 799 zeThread.join(); 800 } catch(InterruptedException e) { 801 //restore interrupted status of recording thread 802 zeThread.interrupt(); 803 } 804 } 805 // release recording resources 806 if (mRecorder != null) { 807 mRecorder.stop(); 808 mRecorder.release(); 809 mRecorder = null; 810 } 811 } 812 813 private boolean initRecord() { 814 int minRecordBuffSizeInBytes = AudioRecord.getMinBufferSize(mSamplingRate, 815 mChannelConfig, mAudioFormat); 816 Log.v(TAG,"FrequencyAnalyzer: min buff size = " + minRecordBuffSizeInBytes + " bytes"); 817 if (minRecordBuffSizeInBytes <= 0) { 818 return false; 819 } 820 821 mMinRecordBufferSizeInSamples = minRecordBuffSizeInBytes / 2; 822 // allocate the byte array to read the audio data 823 824 mAudioShortArray = new short[mMinRecordBufferSizeInSamples]; 825 826 Log.v(TAG, "Initiating record:"); 827 Log.v(TAG, " using source " + mSelectedRecordSource); 828 Log.v(TAG, " at " + mSamplingRate + "Hz"); 829 830 try { 831 mRecorder = new AudioRecord(mSelectedRecordSource, mSamplingRate, 832 mChannelConfig, mAudioFormat, 2 * minRecordBuffSizeInBytes); 833 } catch (IllegalArgumentException e) { 834 return false; 835 } 836 if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) { 837 mRecorder.release(); 838 mRecorder = null; 839 return false; 840 } 841 mRecorder.setRecordPositionUpdateListener(this); 842 mRecorder.setPositionNotificationPeriod(mBlockSizeSamples / 2); 843 return true; 844 } 845 846 // --------------------------------------------------------- 847 // Implementation of AudioRecord.OnPeriodicNotificationListener 848 // -------------------- 849 public void onPeriodicNotification(AudioRecord recorder) { 850 int samplesAvailable = mPipe.availableToRead(); 851 int samplesNeeded = mBlockSizeSamples; 852 if (samplesAvailable >= samplesNeeded) { 853 mPipe.read(mAudioShortArray2, 0, samplesNeeded); 854 855 //compute stuff. 856 int clipcount = 0; 857 double sum = 0; 858 double maxabs = 0; 859 int i; 860 861 for (i = 0; i < samplesNeeded; i++) { 862 double value = mAudioShortArray2[i] / MAX_VAL; 863 double valueabs = Math.abs(value); 864 865 if (valueabs > maxabs) { 866 maxabs = valueabs; 867 } 868 869 if (valueabs > CLIP_LEVEL) { 870 clipcount++; 871 } 872 873 sum += value * value; 874 //fft stuff 875 mData.mData[i] = value; 876 } 877 878 //for the current frame, compute FFT and send to the viewer. 879 880 //apply window and pack as complex for now. 881 DspBufferMath.mult(mData, mData, mWindow.mBuffer); 882 DspBufferMath.set(mC, mData); 883 mFftServer.fft(mC, 1); 884 885 double[] halfMagnitude = new double[mBlockSizeSamples / 2]; 886 for (i = 0; i < mBlockSizeSamples / 2; i++) { 887 halfMagnitude[i] = Math.sqrt(mC.mReal[i] * mC.mReal[i] + mC.mImag[i] * mC.mImag[i]); 888 } 889 890 switch(mCurrentTest) { 891 case TEST_NOISE: 892 mFreqAverageNoise.setData(halfMagnitude, false); 893 break; 894 case TEST_USB_BACKGROUND: 895 mFreqAverageUsbBackground.setData(halfMagnitude, false); 896 break; 897 case TEST_USB_NOISE: 898 mFreqAverageUsbNoise.setData(halfMagnitude, false); 899 break; 900 } 901 } 902 } 903 904 public void onMarkerReached(AudioRecord track) { 905 } 906 907 // --------------------------------------------------------- 908 // Implementation of Runnable for the audio recording + playback 909 // -------------------- 910 public void run() { 911 Thread thisThread = Thread.currentThread(); 912 while (!thisThread.isInterrupted()) { 913 // read from native recorder 914 int nSamplesRead = mRecorder.read(mAudioShortArray, 0, mMinRecordBufferSizeInSamples); 915 if (nSamplesRead > 0) { 916 mPipe.write(mAudioShortArray, 0, nSamplesRead); 917 } 918 } 919 } 920 921 } 922