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