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