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