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 import com.android.cts.verifier.audio.wavelib.*;
22 import com.android.compatibility.common.util.ReportLog;
23 import com.android.compatibility.common.util.ResultType;
24 import com.android.compatibility.common.util.ResultUnit;
25 import android.content.Context;
26 import android.content.BroadcastReceiver;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 
30 import android.media.AudioDeviceCallback;
31 import android.media.AudioDeviceInfo;
32 import android.media.AudioFormat;
33 import android.media.AudioManager;
34 import android.media.AudioTrack;
35 import android.media.AudioRecord;
36 import android.media.MediaRecorder;
37 
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.Message;
41 import android.os.SystemClock;
42 
43 import android.util.Log;
44 
45 import android.view.View;
46 import android.view.View.OnClickListener;
47 
48 import android.widget.Button;
49 import android.widget.TextView;
50 import android.widget.SeekBar;
51 import android.widget.LinearLayout;
52 import android.widget.ProgressBar;
53 
54 /**
55  * Tests Audio Device roundtrip latency by using a loopback plug.
56  */
57 public class AudioFrequencySpeakerActivity extends AudioFrequencyActivity implements Runnable,
58     AudioRecord.OnRecordPositionUpdateListener {
59     private static final String TAG = "AudioFrequencySpeakerActivity";
60 
61     static final int TEST_STARTED = 900;
62     static final int TEST_ENDED = 901;
63     static final int TEST_MESSAGE = 902;
64     static final double MIN_ENERGY_BAND_1 = -50.0;          //dB Full Scale
65     static final double MAX_ENERGY_BAND_1_BASE = -60.0;     //dB Full Scale
66     static final double MIN_FRACTION_POINTS_IN_BAND = 0.3;
67 
68     final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
69     Context mContext;
70 
71     Button mLoopbackPlugReady;          //user signal to have connected USB Microphone
72     Button mTestButton;                 //user to start test
73     String mUsbDevicesInfo;             //usb device info for report
74     LinearLayout mLinearLayout;
75     TextView mResultText;
76     TextView mUsbStatusText;
77     ProgressBar mProgressBar;
78 
79     private boolean mIsRecording = false;
80     private final Object mRecordingLock = new Object();
81     private AudioRecord mRecorder;
82     private int mMinRecordBufferSizeInSamples = 0;
83     private short[] mAudioShortArray;
84     private short[] mAudioShortArray2;
85 
86     private final int mBlockSizeSamples = 1024;
87     private final int mSamplingRate = 48000;
88     private final int mSelectedRecordSource = MediaRecorder.AudioSource.VOICE_RECOGNITION;
89     private final int mChannelConfig = AudioFormat.CHANNEL_IN_MONO;
90     private final int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
91     private Thread mRecordThread;
92     private boolean mRecordThreadShutdown = false;
93 
94     PipeShort mPipe = new PipeShort(65536);
95     SoundPlayerObject mSPlayer;
96 
97     private DspBufferComplex mC;
98     private DspBufferDouble mData;
99 
100     private DspWindow mWindow;
101     private DspFftServer mFftServer;
102     private VectorAverage mFreqAverageMain = new VectorAverage();
103 
104     private VectorAverage mFreqAverageBase = new VectorAverage();
105     private VectorAverage mFreqAverageLeft = new VectorAverage();
106     private VectorAverage mFreqAverageRight = new VectorAverage();
107 
108     private int mCurrentTest = -1;
109     int mBands = 4;
110     AudioBandSpecs[] bandSpecsArray = new AudioBandSpecs[mBands];
111     AudioBandSpecs[] baseBandSpecsArray = new AudioBandSpecs[mBands];
112 
113     private class OnBtnClickListener implements OnClickListener {
114         @Override
onClick(View v)115         public void onClick(View v) {
116             switch (v.getId()) {
117             case R.id.audio_frequency_speaker_mic_ready_btn:
118                 testUSB();
119                 setMaxLevel();
120                 testMaxLevel();
121                 break;
122             case R.id.audio_frequency_speaker_test_btn:
123                 startAudioTest();
124                 break;
125             }
126         }
127     }
128 
129     @Override
onCreate(Bundle savedInstanceState)130     protected void onCreate(Bundle savedInstanceState) {
131         super.onCreate(savedInstanceState);
132         setContentView(R.layout.audio_frequency_speaker_activity);
133 
134         mContext = this;
135 
136         mLoopbackPlugReady = (Button)findViewById(R.id.audio_frequency_speaker_mic_ready_btn);
137         mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
138         mLinearLayout = (LinearLayout)findViewById(R.id.audio_frequency_speaker_layout);
139         mUsbStatusText = (TextView)findViewById(R.id.audio_frequency_speaker_usb_status);
140         mTestButton = (Button)findViewById(R.id.audio_frequency_speaker_test_btn);
141         mTestButton.setOnClickListener(mBtnClickListener);
142         mResultText = (TextView)findViewById(R.id.audio_frequency_speaker_results_text);
143         mProgressBar = (ProgressBar)findViewById(R.id.audio_frequency_speaker_progress_bar);
144         showWait(false);
145         enableLayout(false);         //disabled all content
146 
147         mSPlayer = new SoundPlayerObject();
148         mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.stereo_mono_white_noise_48);
149         mSPlayer.setBalance(0.5f);
150 
151         //Init FFT stuff
152         mAudioShortArray2 = new short[mBlockSizeSamples*2];
153         mData = new DspBufferDouble(mBlockSizeSamples);
154         mC = new DspBufferComplex(mBlockSizeSamples);
155         mFftServer = new DspFftServer(mBlockSizeSamples);
156 
157         int overlap = mBlockSizeSamples / 2;
158 
159         mWindow = new DspWindow(DspWindow.WINDOW_HANNING, mBlockSizeSamples, overlap);
160 
161         setPassFailButtonClickListeners();
162         getPassButton().setEnabled(false);
163         setInfoResources(R.string.audio_frequency_speaker_test,
164                 R.string.audio_frequency_speaker_info, -1);
165 
166         //Init bands for Left/Right test
167         bandSpecsArray[0] = new AudioBandSpecs(
168                 50, 500,        /* frequency start,stop */
169                 4.0, -50,     /* start top,bottom value */
170                 4.0, -4.0       /* stop top,bottom value */);
171 
172         bandSpecsArray[1] = new AudioBandSpecs(
173                 500,4000,       /* frequency start,stop */
174                 4.0, -4.0,      /* start top,bottom value */
175                 4.0, -4.0        /* stop top,bottom value */);
176 
177         bandSpecsArray[2] = new AudioBandSpecs(
178                 4000, 12000,    /* frequency start,stop */
179                 4.0, -4.0,      /* start top,bottom value */
180                 5.0, -5.0       /* stop top,bottom value */);
181 
182         bandSpecsArray[3] = new AudioBandSpecs(
183                 12000, 20000,   /* frequency start,stop */
184                 5.0, -5.0,      /* start top,bottom value */
185                 5.0, -30.0      /* stop top,bottom value */);
186 
187         //Init base bands for silence
188         baseBandSpecsArray[0] = new AudioBandSpecs(
189                 50, 500,        /* frequency start,stop */
190                 40.0, -50.0,     /* start top,bottom value */
191                 5.0, -50.0       /* stop top,bottom value */);
192 
193         baseBandSpecsArray[1] = new AudioBandSpecs(
194                 500,4000,       /* frequency start,stop */
195                 5.0, -50.0,      /* start top,bottom value */
196                 5.0, -50.0        /* stop top,bottom value */);
197 
198         baseBandSpecsArray[2] = new AudioBandSpecs(
199                 4000, 12000,    /* frequency start,stop */
200                 5.0, -50.0,      /* start top,bottom value */
201                 5.0, -50.0       /* stop top,bottom value */);
202 
203         baseBandSpecsArray[3] = new AudioBandSpecs(
204                 12000, 20000,   /* frequency start,stop */
205                 5.0, -50.0,      /* start top,bottom value */
206                 5.0, -50.0      /* stop top,bottom value */);
207 
208     }
209 
210     /**
211      * enable test ui elements
212      */
enableLayout(boolean enable)213     private void enableLayout(boolean enable) {
214         for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
215             View view = mLinearLayout.getChildAt(i);
216             view.setEnabled(enable);
217         }
218     }
219 
220     /**
221      * show active progress bar
222      */
showWait(boolean show)223     private void showWait(boolean show) {
224         if (show) {
225             mProgressBar.setVisibility(View.VISIBLE);
226         } else {
227             mProgressBar.setVisibility(View.INVISIBLE);
228         }
229     }
230 
231     /**
232      *  Start the loopback audio test
233      */
startAudioTest()234     private void startAudioTest() {
235         if (mTestThread != null && !mTestThread.isAlive()) {
236             mTestThread = null; //kill it.
237         }
238 
239         if (mTestThread == null) {
240             Log.v(TAG,"Executing test Thread");
241             mTestThread = new Thread(mPlayRunnable);
242             getPassButton().setEnabled(false);
243             if (!mSPlayer.isAlive())
244                 mSPlayer.start();
245             mTestThread.start();
246         } else {
247             Log.v(TAG,"test Thread already running.");
248         }
249     }
250 
251     Thread mTestThread;
252     Runnable mPlayRunnable = new Runnable() {
253         public void run() {
254             Message msg = Message.obtain();
255             msg.what = TEST_STARTED;
256             mMessageHandler.sendMessage(msg);
257 
258             setMinLevel();
259             sendMessage("Testing Background Environment");
260             mCurrentTest = 0;
261             mSPlayer.setBalance(0.5f);
262             mFreqAverageBase.reset();
263             play();
264 
265             setMaxLevel();
266             sendMessage("Testing Left Capture");
267             mCurrentTest = 1;
268             mFreqAverageLeft.reset();
269             mSPlayer.setBalance(0.0f);
270             play();
271 
272             sendMessage("Testing Right Capture");
273             mCurrentTest = 2;
274             mFreqAverageRight.reset();
275             mSPlayer.setBalance(1.0f);
276             play();
277 
278             mCurrentTest = -1;
279             sendMessage("Testing Completed");
280 
281             Message msg2 = Message.obtain();
282             msg2.what = TEST_ENDED;
283             mMessageHandler.sendMessage(msg2);
284         }
285 
286         private void play() {
287             startRecording();
288             mSPlayer.play(true);
289 
290             try {
291                 Thread.sleep(2000);
292             } catch (InterruptedException e) {
293                 e.printStackTrace();
294             }
295 
296             mSPlayer.play(false);
297             stopRecording();
298         }
299 
300         private void sendMessage(String str) {
301             Message msg = Message.obtain();
302             msg.what = TEST_MESSAGE;
303             msg.obj = str;
304             mMessageHandler.sendMessage(msg);
305         }
306     };
307 
308     private Handler mMessageHandler = new Handler() {
309         public void handleMessage(Message msg) {
310             super.handleMessage(msg);
311             switch (msg.what) {
312             case TEST_STARTED:
313                 showWait(true);
314                 getPassButton().setEnabled(false);
315                 break;
316             case TEST_ENDED:
317                 showWait(false);
318                 computeResults();
319                 break;
320             case TEST_MESSAGE:
321                 String str = (String)msg.obj;
322                 if (str != null) {
323                     mResultText.setText(str);
324                 }
325                 break;
326             default:
327                 Log.e(TAG, String.format("Unknown message: %d", msg.what));
328             }
329         }
330     };
331 
332     private class Results {
333         private String mLabel;
334         public double[] mValuesLog;
335         int[] mPointsPerBand = new int[mBands];
336         double[] mAverageEnergyPerBand = new double[mBands];
337         int[] mInBoundPointsPerBand = new int[mBands];
338         public boolean mIsBaseMeasurement = false;
Results(String label)339         public Results(String label) {
340             mLabel = label;
341         }
342 
343         //append results
toString()344         public String toString() {
345             StringBuilder sb = new StringBuilder();
346             sb.append(String.format("Channel %s\n", mLabel));
347             sb.append("Level in Band 1 : " + (testLevel() ? "OK" :"Not Optimal") +
348                     (mIsBaseMeasurement ? " (Base Meas.)" : "") + "\n");
349             for (int b = 0; b < mBands; b++) {
350                 double percent = 0;
351                 if (mPointsPerBand[b] > 0) {
352                     percent = 100.0 * (double)mInBoundPointsPerBand[b] / mPointsPerBand[b];
353                 }
354                 sb.append(String.format(
355                         " Band %d: Av. Level: %.1f dB InBand: %d/%d (%.1f%%) %s\n",
356                         b, mAverageEnergyPerBand[b],
357                         mInBoundPointsPerBand[b],
358                         mPointsPerBand[b],
359                         percent,
360                         (testInBand(b) ? "OK" : "Not Optimal")));
361             }
362             return sb.toString();
363         }
364 
testLevel()365         public boolean testLevel() {
366             if (mIsBaseMeasurement && mAverageEnergyPerBand[1] <= MAX_ENERGY_BAND_1_BASE) {
367                 return true;
368             } else if (mAverageEnergyPerBand[1] >= MIN_ENERGY_BAND_1) {
369                 return true;
370             }
371             return false;
372         }
373 
testInBand(int b)374         public boolean testInBand(int b) {
375             if (b >= 0 && b < mBands && mPointsPerBand[b] > 0) {
376                 if ((double)mInBoundPointsPerBand[b] / mPointsPerBand[b] >
377                 MIN_FRACTION_POINTS_IN_BAND)
378                     return true;
379             }
380             return false;
381         }
382 
testAll()383         public boolean testAll() {
384             if (!testLevel()) {
385                 return false;
386             }
387             for (int b = 0; b < mBands; b++) {
388                 if (!testInBand(b)) {
389                     return false;
390                 }
391             }
392             return true;
393         }
394     }
395 
396     /**
397      * compute test results
398      */
computeResults()399     private void computeResults() {
400 
401         Results resultsBase = new Results("Base");
402         computeResultsForVector(mFreqAverageBase, resultsBase, true, baseBandSpecsArray);
403         Results resultsLeft = new Results("Left");
404         computeResultsForVector(mFreqAverageLeft, resultsLeft, false, bandSpecsArray);
405         Results resultsRight = new Results("Right");
406         computeResultsForVector(mFreqAverageRight, resultsRight, false, bandSpecsArray);
407         if (resultsLeft.testAll() && resultsRight.testAll() && resultsBase.testAll()) {
408             //enable button
409             String strSuccess = getResources().getString(R.string.audio_general_test_passed);
410             appendResultsToScreen(strSuccess);
411         } else {
412             String strFailed = getResources().getString(R.string.audio_general_test_failed);
413             appendResultsToScreen(strFailed + "\n");
414             String strWarning = getResources().getString(R.string.audio_general_deficiency_found);
415             appendResultsToScreen(strWarning);
416         }
417         getPassButton().setEnabled(true); //Everybody passes! (for now...)
418     }
419 
computeResultsForVector(VectorAverage freqAverage,Results results, boolean isBase, AudioBandSpecs[] bandSpecs)420     private void computeResultsForVector(VectorAverage freqAverage,Results results, boolean isBase,
421             AudioBandSpecs[] bandSpecs) {
422 
423         results.mIsBaseMeasurement = isBase;
424         int points = freqAverage.getSize();
425         if (points > 0) {
426             //compute vector in db
427             double[] values = new double[points];
428             freqAverage.getData(values, false);
429             results.mValuesLog = new double[points];
430             for (int i = 0; i < points; i++) {
431                 results.mValuesLog[i] = 20 * Math.log10(values[i]);
432             }
433 
434             int currentBand = 0;
435             for (int i = 0; i < points; i++) {
436                 double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
437                 if (freq > bandSpecs[currentBand].mFreqStop) {
438                     currentBand++;
439                     if (currentBand >= mBands)
440                         break;
441                 }
442 
443                 if (freq >= bandSpecs[currentBand].mFreqStart) {
444                     results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
445                     results.mPointsPerBand[currentBand]++;
446                 }
447             }
448 
449             for (int b = 0; b < mBands; b++) {
450                 if (results.mPointsPerBand[b] > 0) {
451                     results.mAverageEnergyPerBand[b] =
452                             results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
453                 }
454             }
455 
456             //set offset relative to band 1 level
457             for (int b = 0; b < mBands; b++) {
458                 bandSpecs[b].setOffset(results.mAverageEnergyPerBand[1]);
459             }
460 
461             //test points in band.
462             currentBand = 0;
463             for (int i = 0; i < points; i++) {
464                 double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
465                 if (freq >  bandSpecs[currentBand].mFreqStop) {
466                     currentBand++;
467                     if (currentBand >= mBands)
468                         break;
469                 }
470 
471                 if (freq >= bandSpecs[currentBand].mFreqStart) {
472                     double value = results.mValuesLog[i];
473                     if (bandSpecs[currentBand].isInBounds(freq, value)) {
474                         results.mInBoundPointsPerBand[currentBand]++;
475                     }
476                 }
477             }
478 
479             appendResultsToScreen(results.toString());
480             //store results
481             recordTestResults(results);
482         } else {
483             appendResultsToScreen("Failed testing channel " + results.mLabel);
484         }
485     }
486 
487     //append results
appendResultsToScreen(String str)488     private void appendResultsToScreen(String str) {
489         String currentText = mResultText.getText().toString();
490         mResultText.setText(currentText + "\n" + str);
491     }
492 
493     /**
494      * Store test results in log
495      */
recordTestResults(Results results)496     private void recordTestResults(Results results) {
497         String channelLabel = "channel_" + results.mLabel;
498 
499         for (int b = 0; b < mBands; b++) {
500             String bandLabel = String.format(channelLabel + "_%d", b);
501             getReportLog().addValue(
502                     bandLabel + "_Level",
503                     results.mAverageEnergyPerBand[b],
504                     ResultType.HIGHER_BETTER,
505                     ResultUnit.NONE);
506 
507             getReportLog().addValue(
508                     bandLabel + "_pointsinbound",
509                     results.mInBoundPointsPerBand[b],
510                     ResultType.HIGHER_BETTER,
511                     ResultUnit.COUNT);
512 
513             getReportLog().addValue(
514                     bandLabel + "_pointstotal",
515                     results.mPointsPerBand[b],
516                     ResultType.NEUTRAL,
517                     ResultUnit.COUNT);
518         }
519 
520         getReportLog().addValues(channelLabel + "_magnitudeSpectrumLog",
521                 results.mValuesLog,
522                 ResultType.NEUTRAL,
523                 ResultUnit.NONE);
524 
525         Log.v(TAG, "Results Recorded");
526     }
527 
startRecording()528     private void startRecording() {
529         synchronized (mRecordingLock) {
530             mIsRecording = true;
531         }
532 
533         boolean successful = initRecord();
534         if (successful) {
535             startRecordingForReal();
536         } else {
537             Log.v(TAG, "Recorder initialization error.");
538             synchronized (mRecordingLock) {
539                 mIsRecording = false;
540             }
541         }
542     }
543 
startRecordingForReal()544     private void startRecordingForReal() {
545         // start streaming
546         if (mRecordThread == null) {
547             mRecordThread = new Thread(AudioFrequencySpeakerActivity.this);
548             mRecordThread.setName("FrequencyAnalyzerThread");
549             mRecordThreadShutdown = false;
550         }
551         if (!mRecordThread.isAlive()) {
552             mRecordThread.start();
553         }
554 
555         mPipe.flush();
556 
557         long startTime = SystemClock.uptimeMillis();
558         mRecorder.startRecording();
559         if (mRecorder.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
560             stopRecording();
561             return;
562         }
563         Log.v(TAG, "Start time: " + (long) (SystemClock.uptimeMillis() - startTime) + " ms");
564     }
565 
stopRecording()566     private void stopRecording() {
567         synchronized (mRecordingLock) {
568             stopRecordingForReal();
569             mIsRecording = false;
570         }
571     }
572 
stopRecordingForReal()573     private void stopRecordingForReal() {
574 
575         // stop streaming
576         Thread zeThread = mRecordThread;
577         mRecordThread = null;
578         mRecordThreadShutdown = true;
579         if (zeThread != null) {
580             zeThread.interrupt();
581             try {
582                 zeThread.join();
583             } catch(InterruptedException e) {
584                 Log.v(TAG,"Error shutting down recording thread " + e);
585                 //we don't really care about this error, just logging it.
586             }
587         }
588          // release recording resources
589         if (mRecorder != null) {
590             mRecorder.stop();
591             mRecorder.release();
592             mRecorder = null;
593         }
594     }
595 
initRecord()596     private boolean initRecord() {
597         int minRecordBuffSizeInBytes = AudioRecord.getMinBufferSize(mSamplingRate,
598                 mChannelConfig, mAudioFormat);
599         Log.v(TAG,"FrequencyAnalyzer: min buff size = " + minRecordBuffSizeInBytes + " bytes");
600         if (minRecordBuffSizeInBytes <= 0) {
601             return false;
602         }
603 
604         mMinRecordBufferSizeInSamples = minRecordBuffSizeInBytes / 2;
605         // allocate the byte array to read the audio data
606 
607         mAudioShortArray = new short[mMinRecordBufferSizeInSamples];
608 
609         Log.v(TAG, "Initiating record:");
610         Log.v(TAG, "      using source " + mSelectedRecordSource);
611         Log.v(TAG, "      at " + mSamplingRate + "Hz");
612 
613         try {
614             mRecorder = new AudioRecord(mSelectedRecordSource, mSamplingRate,
615                     mChannelConfig, mAudioFormat, 2 * minRecordBuffSizeInBytes);
616         } catch (IllegalArgumentException e) {
617             return false;
618         }
619         if (mRecorder.getState() != AudioRecord.STATE_INITIALIZED) {
620             mRecorder.release();
621             mRecorder = null;
622             return false;
623         }
624         mRecorder.setRecordPositionUpdateListener(this);
625         mRecorder.setPositionNotificationPeriod(mBlockSizeSamples / 2);
626         return true;
627     }
628 
629     // ---------------------------------------------------------
630     // Implementation of AudioRecord.OnPeriodicNotificationListener
631     // --------------------
onPeriodicNotification(AudioRecord recorder)632     public void onPeriodicNotification(AudioRecord recorder) {
633         int samplesAvailable = mPipe.availableToRead();
634         int samplesNeeded = mBlockSizeSamples;
635         if (samplesAvailable >= samplesNeeded) {
636             mPipe.read(mAudioShortArray2, 0, samplesNeeded);
637 
638             //compute stuff.
639             double maxval = Math.pow(2, 15);
640             int clipcount = 0;
641             double cliplevel = (maxval-10) / maxval;
642             double sum = 0;
643             double maxabs = 0;
644             int i;
645             int index = 0;
646 
647             for (i = 0; i < samplesNeeded; i++) {
648                 double value = mAudioShortArray2[i] / maxval;
649                 double valueabs = Math.abs(value);
650 
651                 if (valueabs > maxabs) {
652                     maxabs = valueabs;
653                 }
654 
655                 if (valueabs > cliplevel) {
656                     clipcount++;
657                 }
658 
659                 sum += value * value;
660                 //fft stuff
661                 if (index < mBlockSizeSamples) {
662                     mData.mData[index] = value;
663                 }
664                 index++;
665             }
666 
667             //for the current frame, compute FFT and send to the viewer.
668 
669             //apply window and pack as complex for now.
670             DspBufferMath.mult(mData, mData, mWindow.mBuffer);
671             DspBufferMath.set(mC, mData);
672             mFftServer.fft(mC, 1);
673 
674             double[] halfMagnitude = new double[mBlockSizeSamples / 2];
675             for (i = 0; i < mBlockSizeSamples / 2; i++) {
676                 halfMagnitude[i] = Math.sqrt(mC.mReal[i] * mC.mReal[i] + mC.mImag[i] * mC.mImag[i]);
677             }
678 
679             mFreqAverageMain.setData(halfMagnitude, false); //average all of them!
680 
681             switch(mCurrentTest) {
682                 case 0:
683                     mFreqAverageBase.setData(halfMagnitude, false);
684                     break;
685                 case 1:
686                     mFreqAverageLeft.setData(halfMagnitude, false);
687                     break;
688                 case 2:
689                     mFreqAverageRight.setData(halfMagnitude, false);
690                     break;
691             }
692         }
693     }
694 
onMarkerReached(AudioRecord track)695     public void onMarkerReached(AudioRecord track) {
696     }
697 
698     // ---------------------------------------------------------
699     // Implementation of Runnable for the audio recording + playback
700     // --------------------
run()701     public void run() {
702         int nSamplesRead = 0;
703 
704         Thread thisThread = Thread.currentThread();
705         while (mRecordThread == thisThread && !mRecordThreadShutdown) {
706             // read from native recorder
707             nSamplesRead = mRecorder.read(mAudioShortArray, 0, mMinRecordBufferSizeInSamples);
708             if (nSamplesRead > 0) {
709                 mPipe.write(mAudioShortArray, 0, nSamplesRead);
710             }
711         }
712     }
713 
testUSB()714     private void testUSB() {
715         boolean isConnected = UsbMicrophoneTester.getIsMicrophoneConnected(getApplicationContext());
716         mUsbDevicesInfo = UsbMicrophoneTester.getUSBDeviceListString(getApplicationContext());
717 
718         if (isConnected) {
719             mUsbStatusText.setText(
720                     getResources().getText(R.string.audio_frequency_speaker_mic_ready_text));
721             enableLayout(true);
722         } else {
723             mUsbStatusText.setText(
724                     getResources().getText(R.string.audio_frequency_speaker_mic_not_ready_text));
725             enableLayout(false);
726         }
727     }
728 
729 }
730