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