1 /*
2  * Copyright (C) 2016 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 #define LOG_TAG "IsochronousClockModel"
18 //#define LOG_NDEBUG 0
19 #include <log/log.h>
20 
21 #include <inttypes.h>
22 #include <stdint.h>
23 #include <algorithm>
24 
25 #include "utility/AudioClock.h"
26 #include "utility/AAudioUtilities.h"
27 #include "IsochronousClockModel.h"
28 
29 using namespace aaudio;
30 
31 using namespace android::audio_utils;
32 
33 #ifndef ICM_LOG_DRIFT
34 #define ICM_LOG_DRIFT   0
35 #endif // ICM_LOG_DRIFT
36 
37 // To enable the timestamp histogram, enter this before opening the stream:
38 //    adb root
39 //    adb shell setprop aaudio.log_mask 1
40 // A histogram of the lateness of the timestamps will be cleared when the stream is started.
41 // It will be updated when the model is stable and receives a timestamp,
42 // and dumped to the log when the stream is stopped.
43 
IsochronousClockModel()44 IsochronousClockModel::IsochronousClockModel()
45 {
46     if ((AAudioProperty_getLogMask() & AAUDIO_LOG_CLOCK_MODEL_HISTOGRAM) != 0) {
47         mHistogramMicros = std::make_unique<Histogram>(kHistogramBinCount,
48                 kHistogramBinWidthMicros);
49     }
50     update();
51 }
52 
setPositionAndTime(int64_t framePosition,int64_t nanoTime)53 void IsochronousClockModel::setPositionAndTime(int64_t framePosition, int64_t nanoTime) {
54     ALOGV("setPositionAndTime, %lld, %lld", (long long) framePosition, (long long) nanoTime);
55     mMarkerFramePosition = framePosition;
56     mMarkerNanoTime = nanoTime;
57 }
58 
start(int64_t nanoTime)59 void IsochronousClockModel::start(int64_t nanoTime) {
60     ALOGV("start(nanos = %lld)\n", (long long) nanoTime);
61     mMarkerNanoTime = nanoTime;
62     mState = STATE_STARTING;
63     mConsecutiveVeryLateCount = 0;
64     mDspStallCount = 0;
65     if (mHistogramMicros) {
66         mHistogramMicros->clear();
67     }
68 }
69 
stop(int64_t nanoTime)70 void IsochronousClockModel::stop(int64_t nanoTime) {
71     ALOGD("stop(nanos = %lld) max lateness = %d micros, DSP stalled %d times",
72           (long long) nanoTime,
73           (int) (mMaxMeasuredLatenessNanos / 1000),
74           mDspStallCount
75     );
76     setPositionAndTime(convertTimeToPosition(nanoTime), nanoTime);
77     // TODO should we set position?
78     mState = STATE_STOPPED;
79     if (mHistogramMicros) {
80         dumpHistogram();
81     }
82 }
83 
isStarting() const84 bool IsochronousClockModel::isStarting() const {
85     return mState == STATE_STARTING;
86 }
87 
isRunning() const88 bool IsochronousClockModel::isRunning() const {
89     return mState == STATE_RUNNING;
90 }
91 
processTimestamp(int64_t framePosition,int64_t nanoTime)92 void IsochronousClockModel::processTimestamp(int64_t framePosition, int64_t nanoTime) {
93     mTimestampCount++;
94 // Log position and time in CSV format so we can import it easily into spreadsheets.
95     //ALOGD("%s() CSV, %d, %lld, %lld", __func__,
96           //mTimestampCount, (long long)framePosition, (long long)nanoTime);
97     int64_t framesDelta = framePosition - mMarkerFramePosition;
98     int64_t nanosDelta = nanoTime - mMarkerNanoTime;
99     if (nanosDelta < 1000) {
100         return;
101     }
102 
103 //    ALOGD("processTimestamp() - mMarkerFramePosition = %lld at mMarkerNanoTime %llu",
104 //         (long long)mMarkerFramePosition,
105 //         (long long)mMarkerNanoTime);
106 
107     int64_t expectedNanosDelta = convertDeltaPositionToTime(framesDelta);
108 //    ALOGD("processTimestamp() - expectedNanosDelta = %lld, nanosDelta = %llu",
109 //         (long long)expectedNanosDelta,
110 //         (long long)nanosDelta);
111 
112 //    ALOGD("processTimestamp() - mSampleRate = %d", mSampleRate);
113 //    ALOGD("processTimestamp() - mState = %d", mState);
114     // Lateness relative to the start of the window.
115     int64_t latenessNanos = nanosDelta - expectedNanosDelta;
116     int32_t nextConsecutiveVeryLateCount = 0;
117     switch (mState) {
118     case STATE_STOPPED:
119         break;
120     case STATE_STARTING:
121         setPositionAndTime(framePosition, nanoTime);
122         mState = STATE_SYNCING;
123         break;
124     case STATE_SYNCING:
125         // This will handle a burst of rapid transfer at the beginning.
126         if (latenessNanos < 0) {
127             setPositionAndTime(framePosition, nanoTime);
128         } else {
129 //            ALOGD("processTimestamp() - advance to STATE_RUNNING");
130             mState = STATE_RUNNING;
131         }
132         break;
133     case STATE_RUNNING:
134         if (mHistogramMicros) {
135             mHistogramMicros->add(latenessNanos / AAUDIO_NANOS_PER_MICROSECOND);
136         }
137         // Modify estimated position based on lateness.
138         // This affects the "early" side of the window, which controls output glitches.
139         if (latenessNanos < 0) {
140             // Earlier than expected timestamp.
141             // This data is probably more accurate, so use it.
142             // Or we may be drifting due to a fast HW clock.
143             setPositionAndTime(framePosition, nanoTime);
144 #if ICM_LOG_DRIFT
145             int earlyDeltaMicros = (int) ((expectedNanosDelta - nanosDelta)
146                     / AAUDIO_NANOS_PER_MICROSECOND);
147             ALOGD("%s() - STATE_RUNNING - #%d, %5d micros EARLY",
148                 __func__, mTimestampCount, earlyDeltaMicros);
149 #endif
150         } else if (latenessNanos > mLatenessForJumpNanos) {
151             ALOGD("%s() - STATE_RUNNING - #%d, %5d micros VERY LATE, %d times",
152                   __func__,
153                   mTimestampCount,
154                   (int) (latenessNanos / AAUDIO_NANOS_PER_MICROSECOND),
155                   mConsecutiveVeryLateCount
156             );
157             // A lateness this large is probably due to a stall in the DSP.
158             // A pause causes a persistent lateness so we can detect it by counting
159             // consecutive late timestamps.
160             if (mConsecutiveVeryLateCount >= kVeryLateCountsNeededToTriggerJump) {
161                 // Assume the timestamp is valid and let subsequent EARLY timestamps
162                 // move the window quickly to the correct place.
163                 setPositionAndTime(framePosition, nanoTime); // JUMP!
164                 mDspStallCount++;
165                 // Throttle the warnings but do not silence them.
166                 // They indicate a bug that needs to be fixed!
167                 if ((nanoTime - mLastJumpWarningTimeNanos) > AAUDIO_NANOS_PER_SECOND) {
168                     ALOGW("%s() - STATE_RUNNING - #%d, %5d micros VERY LATE! Force window jump"
169                           ", mDspStallCount = %d",
170                           __func__,
171                           mTimestampCount,
172                           (int) (latenessNanos / AAUDIO_NANOS_PER_MICROSECOND),
173                           mDspStallCount
174                     );
175                     mLastJumpWarningTimeNanos = nanoTime;
176                 }
177             } else {
178                 nextConsecutiveVeryLateCount = mConsecutiveVeryLateCount + 1;
179                 driftForward(latenessNanos, expectedNanosDelta, framePosition);
180             }
181         } else if (latenessNanos > mLatenessForDriftNanos) {
182             driftForward(latenessNanos, expectedNanosDelta, framePosition);
183         }
184         mConsecutiveVeryLateCount = nextConsecutiveVeryLateCount;
185 
186         // Modify mMaxMeasuredLatenessNanos.
187         // This affects the "late" side of the window, which controls input glitches.
188         if (latenessNanos > mMaxMeasuredLatenessNanos) { // increase
189 #if ICM_LOG_DRIFT
190             ALOGD("%s() - STATE_RUNNING - #%d, newmax %d, oldmax %d micros LATE",
191                     __func__,
192                     mTimestampCount,
193                     (int) (latenessNanos / AAUDIO_NANOS_PER_MICROSECOND),
194                     (int) (mMaxMeasuredLatenessNanos / AAUDIO_NANOS_PER_MICROSECOND)
195                     );
196 #endif
197             mMaxMeasuredLatenessNanos = (int32_t) latenessNanos;
198         }
199 
200         break;
201     default:
202         break;
203     }
204 }
205 
206 // When we are on the late side, it may be because of preemption in the kernel,
207 // or timing jitter caused by resampling in the DSP,
208 // or we may be drifting due to a slow HW clock.
209 // We add slight drift value just in case there is actual long term drift
210 // forward caused by a slower clock.
211 // If the clock is faster than the model will get pushed earlier
212 // by the code in the earlier branch.
213 // The two opposing forces should allow the model to track the real clock
214 // over a long time.
driftForward(int64_t latenessNanos,int64_t expectedNanosDelta,int64_t framePosition)215 void IsochronousClockModel::driftForward(int64_t latenessNanos,
216                                          int64_t expectedNanosDelta,
217                                          int64_t framePosition) {
218     const int64_t driftNanos = (latenessNanos - mLatenessForDriftNanos) >> kShifterForDrift;
219     const int64_t minDriftNanos = std::min(driftNanos, kMaxDriftNanos);
220     const int64_t expectedMarkerNanoTime = mMarkerNanoTime + expectedNanosDelta;
221     const int64_t driftedTime = expectedMarkerNanoTime + minDriftNanos;
222     setPositionAndTime(framePosition, driftedTime);
223 #if ICM_LOG_DRIFT
224     ALOGD("%s() - STATE_RUNNING - #%d, %5d micros LATE, nudge window forward by %d micros",
225           __func__,
226           mTimestampCount,
227           (int) (latenessNanos / AAUDIO_NANOS_PER_MICROSECOND),
228           (int) (minDriftNanos / AAUDIO_NANOS_PER_MICROSECOND)
229     );
230 #endif
231 }
232 
setSampleRate(int32_t sampleRate)233 void IsochronousClockModel::setSampleRate(int32_t sampleRate) {
234     mSampleRate = sampleRate;
235     update();
236 }
237 
setFramesPerBurst(int32_t framesPerBurst)238 void IsochronousClockModel::setFramesPerBurst(int32_t framesPerBurst) {
239     mFramesPerBurst = framesPerBurst;
240     update();
241     ALOGD("%s() - mFramesPerBurst = %d - mBurstPeriodNanos = %" PRId64,
242           __func__,
243           mFramesPerBurst,
244           mBurstPeriodNanos
245           );
246 }
247 
248 // Update expected lateness based on sampleRate and framesPerBurst
update()249 void IsochronousClockModel::update() {
250     mBurstPeriodNanos = convertDeltaPositionToTime(mFramesPerBurst);
251     mLatenessForDriftNanos = mBurstPeriodNanos + kLatenessMarginForSchedulingJitter;
252     mLatenessForJumpNanos = mLatenessForDriftNanos * kScalerForJumpLateness;
253 }
254 
convertDeltaPositionToTime(int64_t framesDelta) const255 int64_t IsochronousClockModel::convertDeltaPositionToTime(int64_t framesDelta) const {
256     return (AAUDIO_NANOS_PER_SECOND * framesDelta) / mSampleRate;
257 }
258 
convertDeltaTimeToPosition(int64_t nanosDelta) const259 int64_t IsochronousClockModel::convertDeltaTimeToPosition(int64_t nanosDelta) const {
260     return (mSampleRate * nanosDelta) / AAUDIO_NANOS_PER_SECOND;
261 }
262 
convertPositionToTime(int64_t framePosition) const263 int64_t IsochronousClockModel::convertPositionToTime(int64_t framePosition) const {
264     if (mState == STATE_STOPPED) {
265         return mMarkerNanoTime;
266     }
267     int64_t nextBurstIndex = (framePosition + mFramesPerBurst - 1) / mFramesPerBurst;
268     int64_t nextBurstPosition = mFramesPerBurst * nextBurstIndex;
269     int64_t framesDelta = nextBurstPosition - mMarkerFramePosition;
270     int64_t nanosDelta = convertDeltaPositionToTime(framesDelta);
271     int64_t time = mMarkerNanoTime + nanosDelta;
272 //    ALOGD("convertPositionToTime: pos = %llu --> time = %llu",
273 //         (unsigned long long)framePosition,
274 //         (unsigned long long)time);
275     return time;
276 }
277 
convertTimeToPosition(int64_t nanoTime) const278 int64_t IsochronousClockModel::convertTimeToPosition(int64_t nanoTime) const {
279     if (mState == STATE_STOPPED) {
280         return mMarkerFramePosition;
281     }
282     int64_t nanosDelta = nanoTime - mMarkerNanoTime;
283     int64_t framesDelta = convertDeltaTimeToPosition(nanosDelta);
284     int64_t nextBurstPosition = mMarkerFramePosition + framesDelta;
285     int64_t nextBurstIndex = nextBurstPosition / mFramesPerBurst;
286     int64_t position = nextBurstIndex * mFramesPerBurst;
287 //    ALOGD("convertTimeToPosition: time = %llu --> pos = %llu",
288 //         (unsigned long long)nanoTime,
289 //         (unsigned long long)position);
290 //    ALOGD("convertTimeToPosition: framesDelta = %llu, mFramesPerBurst = %d",
291 //         (long long) framesDelta, mFramesPerBurst);
292     return position;
293 }
294 
getLateTimeOffsetNanos() const295 int32_t IsochronousClockModel::getLateTimeOffsetNanos() const {
296     return mMaxMeasuredLatenessNanos + kExtraLatenessNanos;
297 }
298 
convertPositionToLatestTime(int64_t framePosition) const299 int64_t IsochronousClockModel::convertPositionToLatestTime(int64_t framePosition) const {
300     return convertPositionToTime(framePosition) + getLateTimeOffsetNanos();
301 }
302 
convertLatestTimeToPosition(int64_t nanoTime) const303 int64_t IsochronousClockModel::convertLatestTimeToPosition(int64_t nanoTime) const {
304     return convertTimeToPosition(nanoTime - getLateTimeOffsetNanos());
305 }
306 
dump() const307 void IsochronousClockModel::dump() const {
308     ALOGD("mMarkerFramePosition = %" PRId64, mMarkerFramePosition);
309     ALOGD("mMarkerNanoTime      = %" PRId64, mMarkerNanoTime);
310     ALOGD("mSampleRate          = %6d", mSampleRate);
311     ALOGD("mFramesPerBurst      = %6d", mFramesPerBurst);
312     ALOGD("mMaxMeasuredLatenessNanos = %6" PRId64, mMaxMeasuredLatenessNanos);
313     ALOGD("mState               = %6d", mState);
314 }
315 
dumpHistogram() const316 void IsochronousClockModel::dumpHistogram() const {
317     if (!mHistogramMicros) return;
318     std::istringstream istr(mHistogramMicros->dump());
319     std::string line;
320     while (std::getline(istr, line)) {
321         ALOGD("lateness, %s", line.c_str());
322     }
323 }
324