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