1 /* 2 * Copyright (C) 2021 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 static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode; 20 import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix; 21 22 import android.os.Bundle; 23 import android.util.Log; 24 import android.view.View; 25 import android.widget.TextView; 26 27 import com.android.cts.verifier.R; 28 import com.android.cts.verifier.audio.audiolib.AudioSystemParams; 29 30 import org.hyphonate.megaaudio.recorder.AudioSinkProvider; 31 import org.hyphonate.megaaudio.recorder.Recorder; 32 import org.hyphonate.megaaudio.recorder.RecorderBuilder; 33 import org.hyphonate.megaaudio.recorder.sinks.AppCallback; 34 import org.hyphonate.megaaudio.recorder.sinks.AppCallbackAudioSink; 35 import org.hyphonate.megaaudio.recorder.sinks.AppCallbackAudioSinkProvider; 36 37 /** 38 * CTS-Test for cold-start latency measurements 39 */ 40 public class AudioInColdStartLatencyActivity 41 extends AudioColdStartBaseActivity { 42 private static final String TAG = "AudioInColdStartLatencyActivity"; 43 private static final boolean DEBUG = false; 44 45 private static final int LATENCY_MS_MUST = 500; // CDD C-3-2 46 private static final int LATENCY_MS_RECOMMEND = 100; // CDD C-SR 47 48 // MegaAudio 49 private Recorder mRecorder; 50 51 // private TextView mCallbackDeltaTxt; 52 53 private long mPreviousCallbackTime; 54 private long mCallbackDeltaTime; 55 56 private long mNominalCallbackDelta; 57 private long mCallbackThresholdTime; 58 private long mAccumulatedTime; 59 private long mNumCallbacks; 60 61 @Override onCreate(Bundle savedInstanceState)62 protected void onCreate(Bundle savedInstanceState) { 63 setContentView(R.layout.audio_coldstart_in_activity); 64 super.onCreate(savedInstanceState); 65 66 // mCallbackDeltaTxt = (TextView) findViewById(R.id.coldstart_inCallbackDeltaTxt); 67 68 setPassFailButtonClickListeners(); 69 getPassButton().setEnabled(false); 70 setInfoResources( 71 R.string.audio_coldstart_inputlbl, R.string.audio_coldstart_input_info, -1); 72 } 73 74 @Override getTestId()75 public String getTestId() { 76 return setTestNameSuffix(sCurrentDisplayMode, getClass().getName()); 77 } 78 calcTestResult()79 boolean calcTestResult() { 80 boolean pass = mColdStartlatencyMS <= LATENCY_MS_MUST; 81 getPassButton().setEnabled(pass); 82 return pass; 83 } 84 calcColdStartLatency()85 double calcColdStartLatency() { 86 mColdStartlatencyMS = nanosToMs(mPreviousCallbackTime - mPreOpenTime); 87 return mColdStartlatencyMS; 88 } 89 showInResults()90 void showInResults() { 91 calcTestResult(); 92 showColdStartLatency(); 93 } 94 stopAudio()95 protected void stopAudio() { 96 stopAudioTest(); 97 } 98 99 @Override getRequiredTimeMS()100 int getRequiredTimeMS() { 101 return LATENCY_MS_MUST; 102 } 103 104 @Override getRecommendedTimeMS()105 int getRecommendedTimeMS() { 106 return LATENCY_MS_RECOMMEND; 107 } 108 109 // 110 // Audio Streaming 111 // 112 @Override startAudioTest()113 boolean startAudioTest() { 114 AudioSystemParams audioSystemParams = new AudioSystemParams(); 115 audioSystemParams.init(this); 116 117 mSampleRate = audioSystemParams.getSystemSampleRate(); 118 mNumBufferFrames = audioSystemParams.getSystemBurstFrames(); 119 120 mAccumulatedTime = 0; 121 mNumCallbacks = 0; 122 123 AudioSinkProvider sinkProvider = 124 new AppCallbackAudioSinkProvider(new ColdStartAppCallback()); 125 try { 126 mPreOpenTime = System.nanoTime(); 127 mRecorder = (new RecorderBuilder()) 128 .setRecorderType(mAudioApi) 129 .setAudioSinkProvider(sinkProvider) 130 .build(); 131 mRecorder.setupStream(NUM_CHANNELS, mSampleRate, mNumBufferFrames); 132 mPostOpenTime = System.nanoTime(); 133 134 mIsTestRunning = true; 135 } catch (RecorderBuilder.BadStateException badStateException) { 136 mLatencyTxt.setText("Can't Start Recorder."); 137 Log.e(TAG, "BadStateException: " + badStateException); 138 mIsTestRunning = false; 139 } 140 141 mPreStartTime = System.nanoTime(); 142 mRecorder.startStream(); 143 mPostStartTime = System.nanoTime(); 144 145 showOpenTime(); 146 showStartTime(); 147 148 if (mIsTestRunning) { 149 mStartBtn.setEnabled(false); 150 mStopBtn.setEnabled(true); 151 } 152 return mIsTestRunning; 153 } 154 155 @Override stopAudioTest()156 void stopAudioTest() { 157 if (!mIsTestRunning) { 158 return; 159 } 160 161 mRecorder.stopStream(); 162 mRecorder.teardownStream(); 163 164 mIsTestRunning = false; 165 166 mStartBtn.setEnabled(true); 167 mStopBtn.setEnabled(false); 168 169 calcColdStartLatency(); 170 171 showInResults(); 172 } 173 174 // Callback for Recorder 175 /* 176 * Monitor callbacks until they become consistent (i.e. delta between callbacks is below 177 * some threshold like 1/8 the "nominal" callback time). This is defined as the "cold start 178 * latency". Calculate that time and display the results. 179 */ 180 class ColdStartAppCallback implements AppCallback { onDataReady(float[] audioData, int numFrames)181 public void onDataReady(float[] audioData, int numFrames) { 182 mNumCallbacks++; 183 184 long time = System.nanoTime(); 185 if (mPreviousCallbackTime == 0) { 186 mNumBufferFrames = numFrames; 187 mNominalCallbackDelta 188 = (long)((1000000000.0 * (double) mNumBufferFrames) / (double) mSampleRate); 189 mCallbackThresholdTime = mNominalCallbackDelta + (mNominalCallbackDelta / 8); 190 // update attributes with actual buffer size 191 // showAttributes(); 192 mPreviousCallbackTime = time; 193 } else { 194 mCallbackDeltaTime = time - mPreviousCallbackTime; 195 196 mPreviousCallbackTime = time; 197 mAccumulatedTime += mCallbackDeltaTime; 198 199 if (mCallbackDeltaTime < mCallbackThresholdTime) { 200 runOnUiThread(new Runnable() { 201 @Override 202 public void run() { 203 stopAudioTest(); 204 } 205 }); 206 } 207 } 208 } 209 } 210 } 211