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