1 /*
2 * Copyright 2017 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 #include "common/OboeDebug.h"
18 #include "OboeStreamCallbackProxy.h"
19
20 bool OboeStreamCallbackProxy::mCallbackReturnStop = false;
21
onAudioReady(oboe::AudioStream * audioStream,void * audioData,int numFrames)22 oboe::DataCallbackResult OboeStreamCallbackProxy::onAudioReady(
23 oboe::AudioStream *audioStream,
24 void *audioData,
25 int numFrames) {
26 oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Stop;
27 int64_t startTimeNanos = getNanoseconds();
28 int32_t numWorkloadVoices = mNumWorkloadVoices;
29
30 // Record which CPU this is running on.
31 orCurrentCpuMask(sched_getcpu());
32
33 // Tell ADPF in advance what our workload will be.
34 if (mWorkloadReportingEnabled) {
35 audioStream->reportWorkload(numWorkloadVoices);
36 }
37
38 // Change affinity if app requested a change.
39 uint32_t mask = mCpuAffinityMask;
40 if (mask != mPreviousMask) {
41 int err = applyCpuAffinityMask(mask);
42 if (err != 0) {
43 }
44 mPreviousMask = mask;
45 }
46
47 mCallbackCount++;
48 mFramesPerCallback = numFrames;
49
50 if (mCallbackReturnStop) {
51 return oboe::DataCallbackResult::Stop;
52 }
53
54 if (mCallback != nullptr) {
55 callbackResult = mCallback->onAudioReady(audioStream, audioData, numFrames);
56 }
57
58 mSynthWorkload.onCallback(numWorkloadVoices);
59 if (numWorkloadVoices > 0) {
60 // Render into the buffer or discard the synth voices.
61 float *buffer = (audioStream->getChannelCount() == 2 && mHearWorkload)
62 ? static_cast<float *>(audioData) : nullptr;
63 mSynthWorkload.renderStereo(buffer, numFrames);
64 }
65
66 // Measure CPU load.
67 int64_t currentTimeNanos = getNanoseconds();
68 // Sometimes we get a short callback when doing sample rate conversion.
69 // Just ignore those to avoid noise.
70 if (numFrames > (getFramesPerCallback() / 2)) {
71 int64_t calculationTime = currentTimeNanos - startTimeNanos;
72 float currentCpuLoad = calculationTime * 0.000000001f * audioStream->getSampleRate() / numFrames;
73 mCpuLoad = (mCpuLoad * 0.95f) + (currentCpuLoad * 0.05f); // simple low pass filter
74 mMaxCpuLoad = std::max(currentCpuLoad, mMaxCpuLoad.load());
75 }
76
77 if (mPreviousCallbackTimeNs != 0) {
78 mStatistics.add((currentTimeNanos - mPreviousCallbackTimeNs) * kNsToMsScaler);
79 }
80 mPreviousCallbackTimeNs = currentTimeNanos;
81
82 return callbackResult;
83 }
84
applyCpuAffinityMask(uint32_t mask)85 int OboeStreamCallbackProxy::applyCpuAffinityMask(uint32_t mask) {
86 int err = 0;
87 // Capture original CPU set so we can restore it.
88 if (!mIsOriginalCpuSetValid) {
89 err = sched_getaffinity((pid_t) 0,
90 sizeof(mOriginalCpuSet),
91 &mOriginalCpuSet);
92 if (err) {
93 LOGE("%s(0x%02X) - sched_getaffinity(), errno = %d\n", __func__, mask, errno);
94 return -errno;
95 }
96 mIsOriginalCpuSetValid = true;
97 }
98 if (mask) {
99 cpu_set_t cpu_set;
100 CPU_ZERO(&cpu_set);
101 int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
102 for (int cpuIndex = 0; cpuIndex < cpuCount; cpuIndex++) {
103 if (mask & (1 << cpuIndex)) {
104 CPU_SET(cpuIndex, &cpu_set);
105 }
106 }
107 err = sched_setaffinity((pid_t) 0, sizeof(cpu_set_t), &cpu_set);
108 } else {
109 // Restore original mask.
110 err = sched_setaffinity((pid_t) 0, sizeof(mOriginalCpuSet), &mOriginalCpuSet);
111 }
112 if (err) {
113 LOGE("%s(0x%02X) - sched_setaffinity(), errno = %d\n", __func__, mask, errno);
114 return -errno;
115 }
116 return 0;
117 }
118