1*05767d91SRobert Wu /* 2*05767d91SRobert Wu * Copyright 2019 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 #ifndef SAMPLES_DEFAULT_DATA_CALLBACK_H 18*05767d91SRobert Wu #define SAMPLES_DEFAULT_DATA_CALLBACK_H 19*05767d91SRobert Wu 20*05767d91SRobert Wu #include <vector> 21*05767d91SRobert Wu #include <oboe/AudioStreamCallback.h> 22*05767d91SRobert Wu #include <logging_macros.h> 23*05767d91SRobert Wu 24*05767d91SRobert Wu #include "IRenderableAudio.h" 25*05767d91SRobert Wu #include "IRestartable.h" 26*05767d91SRobert Wu 27*05767d91SRobert Wu /** 28*05767d91SRobert Wu * This is a callback object which will render data from an `IRenderableAudio` source. 29*05767d91SRobert Wu */ 30*05767d91SRobert Wu class DefaultDataCallback : public oboe::AudioStreamDataCallback { 31*05767d91SRobert Wu public: DefaultDataCallback()32*05767d91SRobert Wu DefaultDataCallback() {} 33*05767d91SRobert Wu virtual ~DefaultDataCallback() = default; 34*05767d91SRobert Wu 35*05767d91SRobert Wu virtual oboe::DataCallbackResult onAudioReady(oboe::AudioStream * oboeStream,void * audioData,int32_t numFrames)36*05767d91SRobert Wu onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames) override { 37*05767d91SRobert Wu 38*05767d91SRobert Wu if (mIsThreadAffinityEnabled && !mIsThreadAffinitySet) { 39*05767d91SRobert Wu setThreadAffinity(); 40*05767d91SRobert Wu mIsThreadAffinitySet = true; 41*05767d91SRobert Wu } 42*05767d91SRobert Wu 43*05767d91SRobert Wu float *outputBuffer = static_cast<float *>(audioData); 44*05767d91SRobert Wu 45*05767d91SRobert Wu std::shared_ptr<IRenderableAudio> localRenderable = mRenderable; 46*05767d91SRobert Wu if (!localRenderable) { 47*05767d91SRobert Wu LOGE("Renderable source not set!"); 48*05767d91SRobert Wu return oboe::DataCallbackResult::Stop; 49*05767d91SRobert Wu } 50*05767d91SRobert Wu localRenderable->renderAudio(outputBuffer, numFrames); 51*05767d91SRobert Wu return oboe::DataCallbackResult::Continue; 52*05767d91SRobert Wu } 53*05767d91SRobert Wu setSource(std::shared_ptr<IRenderableAudio> renderable)54*05767d91SRobert Wu void setSource(std::shared_ptr<IRenderableAudio> renderable) { 55*05767d91SRobert Wu mRenderable = renderable; 56*05767d91SRobert Wu } 57*05767d91SRobert Wu 58*05767d91SRobert Wu /** 59*05767d91SRobert Wu * Reset the callback to its initial state. 60*05767d91SRobert Wu */ reset()61*05767d91SRobert Wu void reset(){ 62*05767d91SRobert Wu mIsThreadAffinitySet = false; 63*05767d91SRobert Wu } 64*05767d91SRobert Wu getSource()65*05767d91SRobert Wu std::shared_ptr<IRenderableAudio> getSource() { 66*05767d91SRobert Wu return mRenderable; 67*05767d91SRobert Wu } 68*05767d91SRobert Wu 69*05767d91SRobert Wu /** 70*05767d91SRobert Wu * Set the CPU IDs to bind the audio callback thread to 71*05767d91SRobert Wu * 72*05767d91SRobert Wu * @param mCpuIds - the CPU IDs to bind to 73*05767d91SRobert Wu */ setCpuIds(std::vector<int> cpuIds)74*05767d91SRobert Wu void setCpuIds(std::vector<int> cpuIds){ 75*05767d91SRobert Wu mCpuIds = std::move(cpuIds); 76*05767d91SRobert Wu } 77*05767d91SRobert Wu 78*05767d91SRobert Wu /** 79*05767d91SRobert Wu * Enable or disable binding the audio callback thread to specific CPU cores. The CPU core IDs 80*05767d91SRobert Wu * can be specified using @see setCpuIds. If no CPU IDs are specified the initial core which the 81*05767d91SRobert Wu * audio thread is called on will be used. 82*05767d91SRobert Wu * 83*05767d91SRobert Wu * @param isEnabled - whether the audio callback thread should be bound to specific CPU core(s) 84*05767d91SRobert Wu */ setThreadAffinityEnabled(bool isEnabled)85*05767d91SRobert Wu void setThreadAffinityEnabled(bool isEnabled){ 86*05767d91SRobert Wu mIsThreadAffinityEnabled = isEnabled; 87*05767d91SRobert Wu LOGD("Thread affinity enabled: %s", (isEnabled) ? "true" : "false"); 88*05767d91SRobert Wu } 89*05767d91SRobert Wu 90*05767d91SRobert Wu private: 91*05767d91SRobert Wu std::shared_ptr<IRenderableAudio> mRenderable; 92*05767d91SRobert Wu std::vector<int> mCpuIds; // IDs of CPU cores which the audio callback should be bound to 93*05767d91SRobert Wu std::atomic<bool> mIsThreadAffinityEnabled { false }; 94*05767d91SRobert Wu std::atomic<bool> mIsThreadAffinitySet { false }; 95*05767d91SRobert Wu 96*05767d91SRobert Wu /** 97*05767d91SRobert Wu * Set the thread affinity for the current thread to mCpuIds. This can be useful to call on the 98*05767d91SRobert Wu * audio thread to avoid underruns caused by CPU core migrations to slower CPU cores. 99*05767d91SRobert Wu */ setThreadAffinity()100*05767d91SRobert Wu void setThreadAffinity() { 101*05767d91SRobert Wu 102*05767d91SRobert Wu pid_t current_thread_id = gettid(); 103*05767d91SRobert Wu cpu_set_t cpu_set; 104*05767d91SRobert Wu CPU_ZERO(&cpu_set); 105*05767d91SRobert Wu 106*05767d91SRobert Wu // If the callback cpu ids aren't specified then bind to the current cpu 107*05767d91SRobert Wu if (mCpuIds.empty()) { 108*05767d91SRobert Wu int current_cpu_id = sched_getcpu(); 109*05767d91SRobert Wu LOGD("Binding to current CPU ID %d", current_cpu_id); 110*05767d91SRobert Wu CPU_SET(current_cpu_id, &cpu_set); 111*05767d91SRobert Wu } else { 112*05767d91SRobert Wu LOGD("Binding to %d CPU IDs", static_cast<int>(mCpuIds.size())); 113*05767d91SRobert Wu for (size_t i = 0; i < mCpuIds.size(); i++) { 114*05767d91SRobert Wu int cpu_id = mCpuIds.at(i); 115*05767d91SRobert Wu LOGD("CPU ID %d added to cores set", cpu_id); 116*05767d91SRobert Wu CPU_SET(cpu_id, &cpu_set); 117*05767d91SRobert Wu } 118*05767d91SRobert Wu } 119*05767d91SRobert Wu 120*05767d91SRobert Wu int result = sched_setaffinity(current_thread_id, sizeof(cpu_set_t), &cpu_set); 121*05767d91SRobert Wu if (result == 0) { 122*05767d91SRobert Wu LOGV("Thread affinity set"); 123*05767d91SRobert Wu } else { 124*05767d91SRobert Wu LOGW("Error setting thread affinity. Error no: %d", result); 125*05767d91SRobert Wu } 126*05767d91SRobert Wu 127*05767d91SRobert Wu mIsThreadAffinitySet = true; 128*05767d91SRobert Wu } 129*05767d91SRobert Wu 130*05767d91SRobert Wu }; 131*05767d91SRobert Wu 132*05767d91SRobert Wu #endif //SAMPLES_DEFAULT_DATA_CALLBACK_H 133