blob: fbec57a2fb4f9549b6c926a3ec0bd8eb1800c795 [file] [log] [blame]
Ricardo Garciae908b092015-07-13 19:10:50 -07001/*
2 * Copyright (C) 2015 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
17package com.android.cts.verifier.audio;
18
19import com.android.cts.verifier.PassFailButtons;
20import com.android.cts.verifier.R;
21import com.android.compatibility.common.util.ReportLog;
22import com.android.compatibility.common.util.ResultType;
23import com.android.compatibility.common.util.ResultUnit;
24import android.content.Context;
25
26import android.media.AudioDeviceCallback;
27import android.media.AudioDeviceInfo;
28import android.media.AudioManager;
29import android.media.AudioTrack;
30
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Message;
34
35import android.util.Log;
36
37import android.view.View;
38import android.view.View.OnClickListener;
39
40import android.widget.Button;
41import android.widget.TextView;
42import android.widget.SeekBar;
43import android.widget.LinearLayout;
44import android.widget.ProgressBar;
45
46/**
47 * Tests Audio Device roundtrip latency by using a loopback plug.
48 */
49public class AudioLoopbackActivity extends PassFailButtons.Activity {
50 private static final String TAG = "AudioLoopbackActivity";
51
52 public static final int BYTES_PER_FRAME = 2;
53
54 NativeAudioThread nativeAudioThread = null;
55
56 private int mSamplingRate = 44100;
57 private int mMinBufferSizeInFrames = 0;
58 private static final double CONFIDENCE_THRESHOLD = 0.6;
59 private Correlation mCorrelation = new Correlation();
60
61 OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
62 Context mContext;
63
Ricardo Garcia28f89202015-09-29 15:23:16 -050064 Button mHeadsetPortYes;
65 Button mHeadsetPortNo;
66
Ricardo Garciae908b092015-07-13 19:10:50 -070067 Button mLoopbackPlugReady;
68 TextView mAudioLevelText;
69 SeekBar mAudioLevelSeekbar;
70 LinearLayout mLinearLayout;
71 Button mTestButton;
72 TextView mResultText;
73 ProgressBar mProgressBar;
74
75 int mMaxLevel;
76 private class OnBtnClickListener implements OnClickListener {
77 @Override
78 public void onClick(View v) {
79 switch (v.getId()) {
80 case R.id.audio_loopback_plug_ready_btn:
81 Log.i(TAG, "audio loopback plug ready");
82 //enable all the other views.
83 enableLayout(true);
84 break;
85 case R.id.audio_loopback_test_btn:
86 Log.i(TAG, "audio loopback test");
87 startAudioTest();
88 break;
Ricardo Garcia28f89202015-09-29 15:23:16 -050089 case R.id.audio_general_headset_yes:
90 Log.i(TAG, "User confirms Headset Port existence");
91 mLoopbackPlugReady.setEnabled(true);
92 recordHeasetPortFound(true);
93 mHeadsetPortYes.setEnabled(false);
94 mHeadsetPortNo.setEnabled(false);
95 break;
96 case R.id.audio_general_headset_no:
97 Log.i(TAG, "User denies Headset Port existence");
98 recordHeasetPortFound(false);
99 getPassButton().setEnabled(true);
100 mHeadsetPortYes.setEnabled(false);
101 mHeadsetPortNo.setEnabled(false);
102 break;
Ricardo Garciae908b092015-07-13 19:10:50 -0700103 }
104 }
105 }
106
107 @Override
108 protected void onCreate(Bundle savedInstanceState) {
109 super.onCreate(savedInstanceState);
110 setContentView(R.layout.audio_loopback_activity);
111
112 mContext = this;
113
Ricardo Garcia28f89202015-09-29 15:23:16 -0500114 mHeadsetPortYes = (Button)findViewById(R.id.audio_general_headset_yes);
115 mHeadsetPortYes.setOnClickListener(mBtnClickListener);
116 mHeadsetPortNo = (Button)findViewById(R.id.audio_general_headset_no);
117 mHeadsetPortNo.setOnClickListener(mBtnClickListener);
118
Ricardo Garciae908b092015-07-13 19:10:50 -0700119 mLoopbackPlugReady = (Button)findViewById(R.id.audio_loopback_plug_ready_btn);
120 mLoopbackPlugReady.setOnClickListener(mBtnClickListener);
Ricardo Garcia28f89202015-09-29 15:23:16 -0500121 mLoopbackPlugReady.setEnabled(false);
Ricardo Garciae908b092015-07-13 19:10:50 -0700122 mLinearLayout = (LinearLayout)findViewById(R.id.audio_loopback_layout);
123 mAudioLevelText = (TextView)findViewById(R.id.audio_loopback_level_text);
124 mAudioLevelSeekbar = (SeekBar)findViewById(R.id.audio_loopback_level_seekbar);
125 mTestButton =(Button)findViewById(R.id.audio_loopback_test_btn);
126 mTestButton.setOnClickListener(mBtnClickListener);
127 mResultText = (TextView)findViewById(R.id.audio_loopback_results_text);
128 mProgressBar = (ProgressBar)findViewById(R.id.audio_loopback_progress_bar);
129 showWait(false);
130
131 enableLayout(false); //disabled all content
132 AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
133 mMaxLevel = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
134 mAudioLevelSeekbar.setMax(mMaxLevel);
135 am.setStreamVolume(AudioManager.STREAM_MUSIC, (int)(0.7 * mMaxLevel), 0);
136 refreshLevel();
137
138 mAudioLevelSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
139 @Override
140 public void onStopTrackingTouch(SeekBar seekBar) {
141 }
142
143 @Override
144 public void onStartTrackingTouch(SeekBar seekBar) {
145 }
146
147 @Override
148 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
149
150 AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
151 am.setStreamVolume(AudioManager.STREAM_MUSIC,
152 progress, 0);
153 refreshLevel();
154 Log.i(TAG,"Changed stream volume to: " + progress);
155 }
156 });
157
158 setPassFailButtonClickListeners();
159 getPassButton().setEnabled(false);
Ricardo Garcia28f89202015-09-29 15:23:16 -0500160 setInfoResources(R.string.audio_loopback_test, R.string.audio_loopback_info, -1);
Ricardo Garciae908b092015-07-13 19:10:50 -0700161 }
162
163 /**
164 * refresh Audio Level seekbar and text
165 */
166 private void refreshLevel() {
167 AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
168
169 int currentLevel = am.getStreamVolume(AudioManager.STREAM_MUSIC);
170 mAudioLevelSeekbar.setProgress(currentLevel);
171
172 String levelText = String.format("%s: %d/%d",
173 getResources().getString(R.string.audio_loopback_level_text),
174 currentLevel, mMaxLevel);
175 mAudioLevelText.setText(levelText);
176 }
177
178 /**
179 * enable test ui elements
180 */
181 private void enableLayout(boolean enable) {
182 for (int i = 0; i<mLinearLayout.getChildCount(); i++) {
183 View view = mLinearLayout.getChildAt(i);
184 view.setEnabled(enable);
185 }
186 }
187
188 /**
189 * show active progress bar
190 */
191 private void showWait(boolean show) {
192 if (show) {
193 mProgressBar.setVisibility(View.VISIBLE) ;
194 } else {
195 mProgressBar.setVisibility(View.INVISIBLE) ;
196 }
197 }
198
199 /**
200 * Start the loopback audio test
201 */
202 private void startAudioTest() {
203 getPassButton().setEnabled(false);
204
205 //get system defaults for sampling rate, buffers.
206 AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
207 String value = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
208 mMinBufferSizeInFrames = Integer.parseInt(value);
209
210 int minBufferSizeInBytes = BYTES_PER_FRAME * mMinBufferSizeInFrames;
211
212 mSamplingRate = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
213
214 Log.i(TAG, String.format("startAudioTest sr:%d , buffer:%d frames",
215 mSamplingRate, mMinBufferSizeInFrames));
216
217 nativeAudioThread = new NativeAudioThread();
218 if (nativeAudioThread != null) {
219 nativeAudioThread.setMessageHandler(mMessageHandler);
220 nativeAudioThread.mSessionId = 0;
221 nativeAudioThread.setParams(mSamplingRate,
222 minBufferSizeInBytes,
223 minBufferSizeInBytes,
224 0x03 /*voice recognition*/);
225 nativeAudioThread.start();
226
227 try {
228 Thread.sleep(200);
229 } catch (InterruptedException e) {
230 e.printStackTrace();
231 }
232
233 nativeAudioThread.runTest();
234
235 }
236 }
237
238 /**
239 * handler for messages from audio thread
240 */
241 private Handler mMessageHandler = new Handler() {
242 public void handleMessage(Message msg) {
243 super.handleMessage(msg);
244 switch(msg.what) {
245 case NativeAudioThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED:
246 Log.v(TAG,"got message native rec started!!");
247 showWait(true);
248 mResultText.setText("Test Running...");
249 break;
250 case NativeAudioThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR:
251 Log.v(TAG,"got message native rec can't start!!");
252 showWait(false);
253 mResultText.setText("Test Error.");
254 break;
255 case NativeAudioThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE:
256 case NativeAudioThread.NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS:
257 if (nativeAudioThread != null) {
258 Log.v(TAG,"Finished recording.");
259 double [] waveData = nativeAudioThread.getWaveData();
260 mCorrelation.computeCorrelation(waveData, mSamplingRate);
261 mResultText.setText(String.format(
262 "Test Finished\nLatency:%.2f ms\nConfidence: %.2f",
263 mCorrelation.mEstimatedLatencyMs,
264 mCorrelation.mEstimatedLatencyConfidence));
265
266 recordTestResults();
267 if (mCorrelation.mEstimatedLatencyConfidence >= CONFIDENCE_THRESHOLD) {
268 getPassButton().setEnabled(true);
269 }
270
271 //close
272 if (nativeAudioThread != null) {
273 nativeAudioThread.isRunning = false;
274 try {
275 nativeAudioThread.finish();
276 nativeAudioThread.join();
277 } catch (InterruptedException e) {
278 e.printStackTrace();
279 }
280 nativeAudioThread = null;
281 }
282 showWait(false);
283 }
284 break;
285 default:
286 break;
287 }
288 }
289 };
290
291 /**
292 * Store test results in log
293 */
294 private void recordTestResults() {
295
296 getReportLog().addValue(
297 "Estimated Latency",
298 mCorrelation.mEstimatedLatencyMs,
299 ResultType.LOWER_BETTER,
300 ResultUnit.MS);
301
302 getReportLog().addValue(
303 "Confidence",
304 mCorrelation.mEstimatedLatencyConfidence,
305 ResultType.HIGHER_BETTER,
306 ResultUnit.NONE);
307
308 int audioLevel = mAudioLevelSeekbar.getProgress();
309 getReportLog().addValue(
310 "Audio Level",
311 audioLevel,
312 ResultType.NEUTRAL,
313 ResultUnit.NONE);
314
315 getReportLog().addValue(
316 "Frames Buffer Size",
317 mMinBufferSizeInFrames,
318 ResultType.NEUTRAL,
319 ResultUnit.NONE);
320
321 getReportLog().addValue(
322 "Sampling Rate",
323 mSamplingRate,
324 ResultType.NEUTRAL,
325 ResultUnit.NONE);
326
327 Log.v(TAG,"Results Recorded");
328 }
Ricardo Garcia28f89202015-09-29 15:23:16 -0500329
330 private void recordHeasetPortFound(boolean found) {
331 getReportLog().addValue(
332 "User Reported Headset Port",
333 found ? 1.0 : 0,
334 ResultType.NEUTRAL,
335 ResultUnit.NONE);
336 }
Ricardo Garciae908b092015-07-13 19:10:50 -0700337}