Home | History | Annotate | Download | only in audio
      1 /*
      2  * Copyright (C) 2011 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 android.content.Context;
     23 import android.media.AudioFormat;
     24 import android.media.AudioManager;
     25 import android.media.AudioTrack;
     26 import android.os.AsyncTask;
     27 import android.os.Bundle;
     28 import android.text.method.ScrollingMovementMethod;
     29 import android.util.Log;
     30 import android.view.Gravity;
     31 import android.view.LayoutInflater;
     32 import android.view.View;
     33 import android.widget.Button;
     34 import android.widget.LinearLayout;
     35 import android.widget.LinearLayout.LayoutParams;
     36 import android.widget.PopupWindow;
     37 import android.widget.TextView;
     38 import java.util.Arrays;
     39 
     40 import com.androidplot.xy.SimpleXYSeries;
     41 import com.androidplot.xy.XYSeries;
     42 import com.androidplot.xy.*;
     43 
     44 public class HifiUltrasoundTestActivity extends PassFailButtons.Activity {
     45 
     46   public enum Status {
     47     START, RECORDING, DONE, PLAYER
     48   }
     49 
     50   private static final String TAG = "HifiUltrasoundTestActivity";
     51 
     52   private Status status = Status.START;
     53   private boolean onPlotScreen = false;
     54   private TextView info;
     55   private Button playerButton;
     56   private Button recorderButton;
     57   private AudioTrack audioTrack;
     58   private LayoutInflater layoutInflater;
     59   private View popupView;
     60   private PopupWindow popupWindow;
     61   private boolean micSupport = true;
     62   private boolean spkrSupport = true;
     63 
     64   @Override
     65   public void onBackPressed () {
     66     if (onPlotScreen) {
     67       popupWindow.dismiss();
     68       onPlotScreen = false;
     69       recorderButton.setEnabled(true);
     70     } else {
     71       super.onBackPressed();
     72     }
     73   }
     74 
     75   @Override
     76   protected void onCreate(Bundle savedInstanceState) {
     77     super.onCreate(savedInstanceState);
     78     setContentView(R.layout.hifi_ultrasound);
     79     setInfoResources(R.string.hifi_ultrasound_test, R.string.hifi_ultrasound_test_info, -1);
     80     setPassFailButtonClickListeners();
     81     getPassButton().setEnabled(false);
     82 
     83     info = (TextView) findViewById(R.id.info_text);
     84     info.setMovementMethod(new ScrollingMovementMethod());
     85     info.setText(R.string.hifi_ultrasound_test_instruction1);
     86 
     87     AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
     88     String micSupportString = audioManager.getProperty(
     89         AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND);
     90     String spkrSupportString = audioManager.getProperty(
     91         AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND);
     92     Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupportString);
     93     Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupportString);
     94 
     95     if (micSupportString == null) {
     96       micSupportString = "null";
     97     }
     98     if (spkrSupportString == null) {
     99       spkrSupportString = "null";
    100     }
    101     if (micSupportString.equalsIgnoreCase(getResources().getString(
    102         R.string.hifi_ultrasound_test_default_false_string))) {
    103       micSupport = false;
    104       getPassButton().setEnabled(true);
    105       getPassButton().performClick();
    106       info.append(getResources().getString(R.string.hifi_ultrasound_test_mic_no_support));
    107     }
    108     if (spkrSupportString.equalsIgnoreCase(getResources().getString(
    109         R.string.hifi_ultrasound_test_default_false_string))) {
    110       spkrSupport = false;
    111       info.append(getResources().getString(R.string.hifi_ultrasound_test_spkr_no_support));
    112     }
    113 
    114     layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
    115         LAYOUT_INFLATER_SERVICE);
    116     popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
    117     popupWindow = new PopupWindow(
    118         popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    119 
    120     final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
    121     final int recordRate = audioRecorder.getSampleRate();
    122 
    123     recorderButton = (Button) findViewById(R.id.recorder_button);
    124     recorderButton.setEnabled(micSupport);
    125     recorderButton.setOnClickListener(new View.OnClickListener() {
    126       private WavAnalyzerTask wavAnalyzerTask = null;
    127       private void stopRecording() {
    128         audioRecorder.stop();
    129         wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
    130         wavAnalyzerTask.execute();
    131         status = Status.DONE;
    132       }
    133       @Override
    134       public void onClick(View v) {
    135         switch (status) {
    136           case START:
    137             info.append("Recording at " + recordRate + "Hz using ");
    138             final int source = audioRecorder.getAudioSource();
    139             switch (source) {
    140               case 1:
    141                 info.append("MIC");
    142                 break;
    143               case 6:
    144                 info.append("VOICE_RECOGNITION");
    145                 break;
    146               default:
    147                 info.append("UNEXPECTED " + source);
    148                 break;
    149             }
    150             info.append("\n");
    151             status = Status.RECORDING;
    152             playerButton.setEnabled(false);
    153             recorderButton.setEnabled(false);
    154             audioRecorder.start();
    155 
    156             final View finalV = v;
    157             new Thread() {
    158               @Override
    159               public void run() {
    160                 Double recordingDuration_millis = new Double(1000 * (2.5
    161                     + Common.PREFIX_LENGTH_S
    162                     + Common.PAUSE_BEFORE_PREFIX_DURATION_S
    163                     + Common.PAUSE_AFTER_PREFIX_DURATION_S
    164                     + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
    165                     * Common.REPETITIONS));
    166                 Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
    167                 try {
    168                   Thread.sleep(recordingDuration_millis.intValue());
    169                 } catch (InterruptedException e) {
    170                   throw new RuntimeException(e);
    171                 }
    172                 runOnUiThread(new Runnable() {
    173                   @Override
    174                   public void run() {
    175                     stopRecording();
    176                   }
    177                 });
    178               }
    179             }.start();
    180 
    181             break;
    182 
    183           case DONE:
    184             plotResponse(wavAnalyzerTask);
    185             break;
    186 
    187           default: break;
    188         }
    189       }
    190     });
    191 
    192     playerButton = (Button) findViewById(R.id.player_button);
    193     playerButton.setEnabled(spkrSupport);
    194     playerButton.setOnClickListener(new View.OnClickListener() {
    195       @Override
    196       public void onClick(View v) {
    197         recorderButton.setEnabled(false);
    198         status = Status.PLAYER;
    199         play();
    200       }
    201     });
    202   }
    203 
    204   private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
    205     Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
    206     dismissButton.setOnClickListener(new Button.OnClickListener(){
    207       @Override
    208       public void onClick(View v) {
    209         popupWindow.dismiss();
    210         onPlotScreen = false;
    211         recorderButton.setEnabled(true);
    212       }});
    213     popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
    214     onPlotScreen = true;
    215 
    216     recorderButton.setEnabled(false);
    217 
    218     XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
    219     plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);
    220 
    221     Double[] frequencies = new Double[Common.PIP_NUM];
    222     for (int i = 0; i < Common.PIP_NUM; i++) {
    223       frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
    224     }
    225 
    226     if (wavAnalyzerTask != null) {
    227 
    228       double[][] power = wavAnalyzerTask.getPower();
    229       for(int i = 0; i < Common.REPETITIONS; i++) {
    230         Double[] powerWrap = new Double[Common.PIP_NUM];
    231         for (int j = 0; j < Common.PIP_NUM; j++) {
    232           powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
    233         }
    234         XYSeries series = new SimpleXYSeries(
    235             Arrays.asList(frequencies),
    236             Arrays.asList(powerWrap),
    237             "");
    238         LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
    239         seriesFormat.configure(getApplicationContext(),
    240             R.xml.ultrasound_line_formatter_trials);
    241         seriesFormat.setPointLabelFormatter(null);
    242         plot.addSeries(series, seriesFormat);
    243       }
    244 
    245       double[] noiseDB = wavAnalyzerTask.getNoiseDB();
    246       Double[] noiseDBWrap = new Double[Common.PIP_NUM];
    247       for (int i = 0; i < Common.PIP_NUM; i++) {
    248         noiseDBWrap[i] = new Double(noiseDB[i]);
    249       }
    250 
    251       XYSeries noiseSeries = new SimpleXYSeries(
    252           Arrays.asList(frequencies),
    253           Arrays.asList(noiseDBWrap),
    254           "background noise");
    255       LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
    256       noiseSeriesFormat.configure(getApplicationContext(),
    257           R.xml.ultrasound_line_formatter_noise);
    258       noiseSeriesFormat.setPointLabelFormatter(null);
    259       plot.addSeries(noiseSeries, noiseSeriesFormat);
    260 
    261       double[] dB = wavAnalyzerTask.getDB();
    262       Double[] dBWrap = new Double[Common.PIP_NUM];
    263       for (int i = 0; i < Common.PIP_NUM; i++) {
    264         dBWrap[i] = new Double(dB[i]);
    265       }
    266 
    267       XYSeries series = new SimpleXYSeries(
    268           Arrays.asList(frequencies),
    269           Arrays.asList(dBWrap),
    270           "median");
    271       LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
    272       seriesFormat.configure(getApplicationContext(),
    273           R.xml.ultrasound_line_formatter_median);
    274       seriesFormat.setPointLabelFormatter(null);
    275       plot.addSeries(series, seriesFormat);
    276 
    277       Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
    278       Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
    279       XYSeries passSeries = new SimpleXYSeries(
    280           Arrays.asList(passX), Arrays.asList(passY), "passing");
    281       LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
    282       passSeriesFormat.configure(getApplicationContext(),
    283           R.xml.ultrasound_line_formatter_pass);
    284       passSeriesFormat.setPointLabelFormatter(null);
    285       plot.addSeries(passSeries, passSeriesFormat);
    286     }
    287   }
    288 
    289   /**
    290    * Plays the generated pips.
    291    */
    292   private void play() {
    293     play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
    294   }
    295 
    296   /**
    297    * Plays the sound data.
    298    */
    299   private void play(byte[] data, int sampleRate) {
    300     if (audioTrack != null) {
    301       audioTrack.stop();
    302       audioTrack.release();
    303     }
    304     audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
    305         sampleRate, AudioFormat.CHANNEL_OUT_MONO,
    306         AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
    307         sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
    308         AudioTrack.MODE_STATIC);
    309     audioTrack.write(data, 0, data.length);
    310     audioTrack.play();
    311   }
    312 
    313   /**
    314    * AsyncTask class for the analyzing.
    315    */
    316   private class WavAnalyzerTask extends AsyncTask<Void, String, String>
    317       implements WavAnalyzer.Listener {
    318 
    319     private static final String TAG = "WavAnalyzerTask";
    320     WavAnalyzer wavAnalyzer;
    321 
    322     public WavAnalyzerTask(byte[] recording) {
    323       wavAnalyzer = new WavAnalyzer(recording, Common.RECORDING_SAMPLE_RATE_HZ,
    324           WavAnalyzerTask.this);
    325     }
    326 
    327     double[] getDB() {
    328       return wavAnalyzer.getDB();
    329     }
    330 
    331     double[][] getPower() {
    332       return wavAnalyzer.getPower();
    333     }
    334 
    335     double[] getNoiseDB() {
    336       return wavAnalyzer.getNoiseDB();
    337     }
    338 
    339     double getThreshold() {
    340       return wavAnalyzer.getThreshold();
    341     }
    342 
    343     @Override
    344     protected String doInBackground(Void... params) {
    345       boolean result = wavAnalyzer.doWork();
    346       if (result) {
    347         return getString(R.string.hifi_ultrasound_test_pass);
    348       }
    349       return getString(R.string.hifi_ultrasound_test_fail);
    350     }
    351 
    352     @Override
    353     protected void onPostExecute(String result) {
    354       info.append(result);
    355       recorderButton.setEnabled(true);
    356       if (wavAnalyzer.getResult()) {
    357         getPassButton().setEnabled(true);
    358       }
    359       recorderButton.setText(R.string.hifi_ultrasound_test_plot);
    360     }
    361 
    362     @Override
    363     protected void onProgressUpdate(String... values) {
    364       for (String message : values) {
    365         info.append(message);
    366         Log.d(TAG, message);
    367       }
    368     }
    369 
    370     @Override
    371     public void sendMessage(String message) {
    372       publishProgress(message);
    373     }
    374   }
    375 }
    376