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