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