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