xref: /aosp_15_r20/external/oboe/src/common/StabilizedCallback.cpp (revision 05767d913155b055644481607e6fa1e35e2fe72c)
1*05767d91SRobert Wu /*
2*05767d91SRobert Wu  * Copyright (C) 2018 The Android Open Source Project
3*05767d91SRobert Wu  *
4*05767d91SRobert Wu  * Licensed under the Apache License, Version 2.0 (the "License");
5*05767d91SRobert Wu  * you may not use this file except in compliance with the License.
6*05767d91SRobert Wu  * You may obtain a copy of the License at
7*05767d91SRobert Wu  *
8*05767d91SRobert Wu  *      http://www.apache.org/licenses/LICENSE-2.0
9*05767d91SRobert Wu  *
10*05767d91SRobert Wu  * Unless required by applicable law or agreed to in writing, software
11*05767d91SRobert Wu  * distributed under the License is distributed on an "AS IS" BASIS,
12*05767d91SRobert Wu  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*05767d91SRobert Wu  * See the License for the specific language governing permissions and
14*05767d91SRobert Wu  * limitations under the License.
15*05767d91SRobert Wu  */
16*05767d91SRobert Wu 
17*05767d91SRobert Wu #include "oboe/StabilizedCallback.h"
18*05767d91SRobert Wu #include "common/AudioClock.h"
19*05767d91SRobert Wu #include "common/Trace.h"
20*05767d91SRobert Wu 
21*05767d91SRobert Wu constexpr int32_t kLoadGenerationStepSizeNanos = 20000;
22*05767d91SRobert Wu constexpr float kPercentageOfCallbackToUse = 0.8;
23*05767d91SRobert Wu 
24*05767d91SRobert Wu using namespace oboe;
25*05767d91SRobert Wu 
StabilizedCallback(AudioStreamCallback * callback)26*05767d91SRobert Wu StabilizedCallback::StabilizedCallback(AudioStreamCallback *callback) : mCallback(callback){
27*05767d91SRobert Wu     Trace::initialize();
28*05767d91SRobert Wu }
29*05767d91SRobert Wu 
30*05767d91SRobert Wu /**
31*05767d91SRobert Wu  * An audio callback which attempts to do work for a fixed amount of time.
32*05767d91SRobert Wu  *
33*05767d91SRobert Wu  * @param oboeStream
34*05767d91SRobert Wu  * @param audioData
35*05767d91SRobert Wu  * @param numFrames
36*05767d91SRobert Wu  * @return
37*05767d91SRobert Wu  */
38*05767d91SRobert Wu DataCallbackResult
onAudioReady(AudioStream * oboeStream,void * audioData,int32_t numFrames)39*05767d91SRobert Wu StabilizedCallback::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) {
40*05767d91SRobert Wu 
41*05767d91SRobert Wu     int64_t startTimeNanos = AudioClock::getNanoseconds();
42*05767d91SRobert Wu 
43*05767d91SRobert Wu     if (mFrameCount == 0){
44*05767d91SRobert Wu         mEpochTimeNanos = startTimeNanos;
45*05767d91SRobert Wu     }
46*05767d91SRobert Wu 
47*05767d91SRobert Wu     int64_t durationSinceEpochNanos = startTimeNanos - mEpochTimeNanos;
48*05767d91SRobert Wu 
49*05767d91SRobert Wu     // In an ideal world the callback start time will be exactly the same as the duration of the
50*05767d91SRobert Wu     // frames already read/written into the stream. In reality the callback can start early
51*05767d91SRobert Wu     // or late. By finding the delta we can calculate the target duration for our stabilized
52*05767d91SRobert Wu     // callback.
53*05767d91SRobert Wu     int64_t idealStartTimeNanos = (mFrameCount * kNanosPerSecond) / oboeStream->getSampleRate();
54*05767d91SRobert Wu     int64_t lateStartNanos = durationSinceEpochNanos - idealStartTimeNanos;
55*05767d91SRobert Wu 
56*05767d91SRobert Wu     if (lateStartNanos < 0){
57*05767d91SRobert Wu         // This was an early start which indicates that our previous epoch was a late callback.
58*05767d91SRobert Wu         // Update our epoch to this more accurate time.
59*05767d91SRobert Wu         mEpochTimeNanos = startTimeNanos;
60*05767d91SRobert Wu         mFrameCount = 0;
61*05767d91SRobert Wu     }
62*05767d91SRobert Wu 
63*05767d91SRobert Wu     int64_t numFramesAsNanos = (numFrames * kNanosPerSecond) / oboeStream->getSampleRate();
64*05767d91SRobert Wu     int64_t targetDurationNanos = static_cast<int64_t>(
65*05767d91SRobert Wu             (numFramesAsNanos * kPercentageOfCallbackToUse) - lateStartNanos);
66*05767d91SRobert Wu 
67*05767d91SRobert Wu     Trace::beginSection("Actual load");
68*05767d91SRobert Wu     DataCallbackResult result = mCallback->onAudioReady(oboeStream, audioData, numFrames);
69*05767d91SRobert Wu     Trace::endSection();
70*05767d91SRobert Wu 
71*05767d91SRobert Wu     int64_t executionDurationNanos = AudioClock::getNanoseconds() - startTimeNanos;
72*05767d91SRobert Wu     int64_t stabilizingLoadDurationNanos = targetDurationNanos - executionDurationNanos;
73*05767d91SRobert Wu 
74*05767d91SRobert Wu     Trace::beginSection("Stabilized load for %lldns", stabilizingLoadDurationNanos);
75*05767d91SRobert Wu     generateLoad(stabilizingLoadDurationNanos);
76*05767d91SRobert Wu     Trace::endSection();
77*05767d91SRobert Wu 
78*05767d91SRobert Wu     // Wraparound: At 48000 frames per second mFrameCount wraparound will occur after 6m years,
79*05767d91SRobert Wu     // significantly longer than the average lifetime of an Android phone.
80*05767d91SRobert Wu     mFrameCount += numFrames;
81*05767d91SRobert Wu     return result;
82*05767d91SRobert Wu }
83*05767d91SRobert Wu 
generateLoad(int64_t durationNanos)84*05767d91SRobert Wu void StabilizedCallback::generateLoad(int64_t durationNanos) {
85*05767d91SRobert Wu 
86*05767d91SRobert Wu     int64_t currentTimeNanos = AudioClock::getNanoseconds();
87*05767d91SRobert Wu     int64_t deadlineTimeNanos = currentTimeNanos + durationNanos;
88*05767d91SRobert Wu 
89*05767d91SRobert Wu     // opsPerStep gives us an estimated number of operations which need to be run to fully utilize
90*05767d91SRobert Wu     // the CPU for a fixed amount of time (specified by kLoadGenerationStepSizeNanos).
91*05767d91SRobert Wu     // After each step the opsPerStep value is re-calculated based on the actual time taken to
92*05767d91SRobert Wu     // execute those operations.
93*05767d91SRobert Wu     auto opsPerStep = (int)(mOpsPerNano * kLoadGenerationStepSizeNanos);
94*05767d91SRobert Wu     int64_t stepDurationNanos = 0;
95*05767d91SRobert Wu     int64_t previousTimeNanos = 0;
96*05767d91SRobert Wu 
97*05767d91SRobert Wu     while (currentTimeNanos <= deadlineTimeNanos){
98*05767d91SRobert Wu 
99*05767d91SRobert Wu         for (int i = 0; i < opsPerStep; i++) cpu_relax();
100*05767d91SRobert Wu 
101*05767d91SRobert Wu         previousTimeNanos = currentTimeNanos;
102*05767d91SRobert Wu         currentTimeNanos = AudioClock::getNanoseconds();
103*05767d91SRobert Wu         stepDurationNanos = currentTimeNanos - previousTimeNanos;
104*05767d91SRobert Wu 
105*05767d91SRobert Wu         // Calculate exponential moving average to smooth out values, this acts as a low pass filter.
106*05767d91SRobert Wu         // @see https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
107*05767d91SRobert Wu         static const float kFilterCoefficient = 0.1;
108*05767d91SRobert Wu         auto measuredOpsPerNano = (double) opsPerStep / stepDurationNanos;
109*05767d91SRobert Wu         mOpsPerNano = kFilterCoefficient * measuredOpsPerNano + (1.0 - kFilterCoefficient) * mOpsPerNano;
110*05767d91SRobert Wu         opsPerStep = (int) (mOpsPerNano * kLoadGenerationStepSizeNanos);
111*05767d91SRobert Wu     }
112*05767d91SRobert Wu }
113