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