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