/* * Copyright 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "common/OboeDebug.h" #include "OboeStreamCallbackProxy.h" bool OboeStreamCallbackProxy::mCallbackReturnStop = false; oboe::DataCallbackResult OboeStreamCallbackProxy::onAudioReady( oboe::AudioStream *audioStream, void *audioData, int numFrames) { oboe::DataCallbackResult callbackResult = oboe::DataCallbackResult::Stop; int64_t startTimeNanos = getNanoseconds(); int32_t numWorkloadVoices = mNumWorkloadVoices; // Record which CPU this is running on. orCurrentCpuMask(sched_getcpu()); // Tell ADPF in advance what our workload will be. if (mWorkloadReportingEnabled) { audioStream->reportWorkload(numWorkloadVoices); } // Change affinity if app requested a change. uint32_t mask = mCpuAffinityMask; if (mask != mPreviousMask) { int err = applyCpuAffinityMask(mask); if (err != 0) { } mPreviousMask = mask; } mCallbackCount++; mFramesPerCallback = numFrames; if (mCallbackReturnStop) { return oboe::DataCallbackResult::Stop; } if (mCallback != nullptr) { callbackResult = mCallback->onAudioReady(audioStream, audioData, numFrames); } mSynthWorkload.onCallback(numWorkloadVoices); if (numWorkloadVoices > 0) { // Render into the buffer or discard the synth voices. float *buffer = (audioStream->getChannelCount() == 2 && mHearWorkload) ? static_cast(audioData) : nullptr; mSynthWorkload.renderStereo(buffer, numFrames); } // Measure CPU load. int64_t currentTimeNanos = getNanoseconds(); // Sometimes we get a short callback when doing sample rate conversion. // Just ignore those to avoid noise. if (numFrames > (getFramesPerCallback() / 2)) { int64_t calculationTime = currentTimeNanos - startTimeNanos; float currentCpuLoad = calculationTime * 0.000000001f * audioStream->getSampleRate() / numFrames; mCpuLoad = (mCpuLoad * 0.95f) + (currentCpuLoad * 0.05f); // simple low pass filter mMaxCpuLoad = std::max(currentCpuLoad, mMaxCpuLoad.load()); } if (mPreviousCallbackTimeNs != 0) { mStatistics.add((currentTimeNanos - mPreviousCallbackTimeNs) * kNsToMsScaler); } mPreviousCallbackTimeNs = currentTimeNanos; return callbackResult; } int OboeStreamCallbackProxy::applyCpuAffinityMask(uint32_t mask) { int err = 0; // Capture original CPU set so we can restore it. if (!mIsOriginalCpuSetValid) { err = sched_getaffinity((pid_t) 0, sizeof(mOriginalCpuSet), &mOriginalCpuSet); if (err) { LOGE("%s(0x%02X) - sched_getaffinity(), errno = %d\n", __func__, mask, errno); return -errno; } mIsOriginalCpuSetValid = true; } if (mask) { cpu_set_t cpu_set; CPU_ZERO(&cpu_set); int cpuCount = sysconf(_SC_NPROCESSORS_CONF); for (int cpuIndex = 0; cpuIndex < cpuCount; cpuIndex++) { if (mask & (1 << cpuIndex)) { CPU_SET(cpuIndex, &cpu_set); } } err = sched_setaffinity((pid_t) 0, sizeof(cpu_set_t), &cpu_set); } else { // Restore original mask. err = sched_setaffinity((pid_t) 0, sizeof(mOriginalCpuSet), &mOriginalCpuSet); } if (err) { LOGE("%s(0x%02X) - sched_setaffinity(), errno = %d\n", __func__, mask, errno); return -errno; } return 0; }