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