Home | History | Annotate | Download | only in audio
      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
    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
    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      */
    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      */
    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      */
    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;
    339         public Results(String label) {
    340             mLabel = label;
    341         }
    342 
    343         //append results
    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 
    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 
    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 
    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      */
    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 
    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
    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      */
    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 
    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 
    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 
    566     private void stopRecording() {
    567         synchronized (mRecordingLock) {
    568             stopRecordingForReal();
    569             mIsRecording = false;
    570         }
    571     }
    572 
    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 
    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     // --------------------
    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 
    695     public void onMarkerReached(AudioRecord track) {
    696     }
    697 
    698     // ---------------------------------------------------------
    699     // Implementation of Runnable for the audio recording + playback
    700     // --------------------
    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 
    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