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