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