Revert "Audio effects: define interface between EffectModule and audio framework"

This reverts commit c0abc6239e.

Reason for revert: broken build on target cf_x86_phone-userdebug_coverage

Change-Id: Ic6808bfd31bedfa85b7d7a120e4c6f6273678f73
gugelfrei
Eric Laurent 5 years ago
parent c0abc6239e
commit 3fd6a2ccfd

@ -2996,7 +2996,7 @@ std::vector<sp<AudioFlinger::EffectModule>> AudioFlinger::purgeStaleEffects_l()
for (size_t i = 0; i < chains.size(); i++) {
sp<EffectChain> ec = chains[i];
int sessionid = ec->sessionId();
sp<ThreadBase> t = ec->thread().promote();
sp<ThreadBase> t = ec->mThread.promote();
if (t == 0) {
continue;
}
@ -3019,7 +3019,7 @@ std::vector<sp<AudioFlinger::EffectModule>> AudioFlinger::purgeStaleEffects_l()
effect->unPin();
t->removeEffect_l(effect, /*release*/ true);
if (effect->purgeHandles()) {
effect->checkSuspendOnEffectEnabled(false, true /*threadLocked*/);
t->checkSuspendOnEffectEnabled_l(effect, false, effect->sessionId());
}
removedEffects.push_back(effect);
}
@ -3642,7 +3642,7 @@ status_t AudioFlinger::moveEffectChain_l(audio_session_t sessionId,
// if the move request is not received from audio policy manager, the effect must be
// re-registered with the new strategy and output
if (dstChain == 0) {
dstChain = effect->callback()->chain().promote();
dstChain = effect->chain().promote();
if (dstChain == 0) {
ALOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
status = NO_INIT;
@ -3692,7 +3692,7 @@ status_t AudioFlinger::moveAuxEffectToIo(int EffectId,
goto Exit;
}
dstChain = effect->callback()->chain().promote();
dstChain = effect->chain().promote();
if (dstChain == 0) {
thread->addEffect_l(effect);
status = INVALID_OPERATION;

@ -64,13 +64,14 @@ namespace android {
#undef LOG_TAG
#define LOG_TAG "AudioFlinger::EffectModule"
AudioFlinger::EffectModule::EffectModule(const sp<AudioFlinger::EffectCallbackInterface>& callback,
AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
const wp<AudioFlinger::EffectChain>& chain,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
bool pinned)
: mPinned(pinned),
mCallback(callback), mId(id), mSessionId(sessionId),
mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
mDescriptor(*desc),
// clear mConfig to ensure consistent initial value of buffer framecount
// in case buffers are associated by setInBuffer() or setOutBuffer()
@ -80,7 +81,8 @@ AudioFlinger::EffectModule::EffectModule(const sp<AudioFlinger::EffectCallbackIn
mMaxDisableWaitCnt(1), // set by configure(), should be >= 1
mDisableWaitCnt(0), // set by process() and updateState()
mSuspended(false),
mOffloaded(false)
mOffloaded(false),
mAudioFlinger(thread->mAudioFlinger)
#ifdef FLOAT_EFFECT_CHAIN
, mSupportsFloat(false)
#endif
@ -89,8 +91,16 @@ AudioFlinger::EffectModule::EffectModule(const sp<AudioFlinger::EffectCallbackIn
int lStatus;
// create effect engine from effect factory
mStatus = callback->createEffectHal(
&desc->uuid, sessionId, AUDIO_PORT_HANDLE_NONE, &mEffectInterface);
mStatus = -ENODEV;
sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
if (audioFlinger != 0) {
sp<EffectsFactoryHalInterface> effectsFactory = audioFlinger->getEffectsFactory();
if (effectsFactory != 0) {
mStatus = effectsFactory->createEffect(
&desc->uuid, sessionId, thread->id(), AUDIO_PORT_HANDLE_NONE, &mEffectInterface);
}
}
if (mStatus != NO_ERROR) {
return;
}
@ -100,7 +110,7 @@ AudioFlinger::EffectModule::EffectModule(const sp<AudioFlinger::EffectCallbackIn
goto Error;
}
setOffloaded(callback->isOffload(), callback->io());
setOffloaded(thread->type() == ThreadBase::OFFLOAD, thread->id());
ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface.get());
return;
@ -178,8 +188,14 @@ status_t AudioFlinger::EffectModule::updatePolicyState()
doRegister = true;
mPolicyRegistered = mHandles.size() > 0;
if (mPolicyRegistered) {
io = mCallback->io();
strategy = mCallback->strategy();
sp <EffectChain> chain = mChain.promote();
sp <ThreadBase> thread = mThread.promote();
if (thread == nullptr || chain == nullptr) {
return INVALID_OPERATION;
}
io = thread->id();
strategy = chain->strategy();
}
}
// enable effect when registered according to enable state requested by controlling handle
@ -276,16 +292,15 @@ AudioFlinger::EffectHandle *AudioFlinger::EffectModule::controlHandle_l()
ssize_t AudioFlinger::EffectModule::disconnectHandle(EffectHandle *handle, bool unpinIfLast)
{
ALOGV("disconnect() %p handle %p", this, handle);
if (mCallback->disconnectEffectHandle(handle, unpinIfLast)) {
return mHandles.size();
}
Mutex::Autolock _l(mLock);
ssize_t numHandles = removeHandle_l(handle);
if ((numHandles == 0) && (!mPinned || unpinIfLast)) {
mLock.unlock();
mCallback->updateOrphanEffectChains(this);
mLock.lock();
sp<AudioFlinger> af = mAudioFlinger.promote();
if (af != 0) {
mLock.unlock();
af->updateOrphanEffectChains(this);
mLock.lock();
}
}
return numHandles;
}
@ -527,7 +542,8 @@ void AudioFlinger::EffectModule::process()
mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) {
// If an insert effect is idle and input buffer is different from output buffer,
// accumulate input onto output
if (mCallback->activeTrackCnt() != 0) {
sp<EffectChain> chain = mChain.promote();
if (chain.get() != nullptr && chain->activeTrackCnt() != 0) {
// similar handling with data_bypass above.
if (mConfig.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE) {
accumulateInputToOutput();
@ -550,6 +566,7 @@ status_t AudioFlinger::EffectModule::configure()
{
ALOGVV("configure() started");
status_t status;
sp<ThreadBase> thread;
uint32_t size;
audio_channel_mask_t channelMask;
@ -558,11 +575,17 @@ status_t AudioFlinger::EffectModule::configure()
goto exit;
}
thread = mThread.promote();
if (thread == 0) {
status = DEAD_OBJECT;
goto exit;
}
// TODO: handle configuration of effects replacing track process
// TODO: handle configuration of input (record) SW effects above the HAL,
// similar to output EFFECT_FLAG_TYPE_INSERT/REPLACE,
// in which case input channel masks should be used here.
channelMask = mCallback->channelMask();
channelMask = thread->channelMask();
mConfig.inputCfg.channels = channelMask;
mConfig.outputCfg.channels = channelMask;
@ -599,11 +622,11 @@ status_t AudioFlinger::EffectModule::configure()
mConfig.outputCfg.format = EFFECT_BUFFER_FORMAT;
// Don't use sample rate for thread if effect isn't offloadable.
if (mCallback->isOffload() && !isOffloaded()) {
if ((thread->type() == ThreadBase::OFFLOAD) && !isOffloaded()) {
mConfig.inputCfg.samplingRate = DEFAULT_OUTPUT_SAMPLE_RATE;
ALOGV("Overriding effect input as 48kHz");
} else {
mConfig.inputCfg.samplingRate = mCallback->sampleRate();
mConfig.inputCfg.samplingRate = thread->sampleRate();
}
mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
mConfig.inputCfg.bufferProvider.cookie = NULL;
@ -629,13 +652,11 @@ status_t AudioFlinger::EffectModule::configure()
}
mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
mConfig.inputCfg.buffer.frameCount = mCallback->frameCount();
mConfig.inputCfg.buffer.frameCount = thread->frameCount();
mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
ALOGV("configure() %p chain %p buffer %p framecount %zu",
this, mCallback->chain().promote() != nullptr ? mCallback->chain().promote().get() :
nullptr,
mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
ALOGV("configure() %p thread %p buffer %p framecount %zu",
this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
status_t cmdStatus;
size = sizeof(int);
@ -650,7 +671,7 @@ status_t AudioFlinger::EffectModule::configure()
#ifdef MULTICHANNEL_EFFECT_CHAIN
if (status != NO_ERROR &&
mCallback->isOutput() &&
thread->isOutput() &&
(mConfig.inputCfg.channels != AUDIO_CHANNEL_OUT_STEREO
|| mConfig.outputCfg.channels != AUDIO_CHANNEL_OUT_STEREO)) {
// Older effects may require exact STEREO position mask.
@ -717,7 +738,11 @@ status_t AudioFlinger::EffectModule::configure()
size = sizeof(int);
*(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;
uint32_t latency = mCallback->latency();
uint32_t latency = 0;
PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
if (pbt != NULL) {
latency = pbt->latency_l();
}
*((int32_t *)p->data + 1)= latency;
mEffectInterface->command(EFFECT_CMD_SET_PARAM,
@ -764,20 +789,31 @@ void AudioFlinger::EffectModule::addEffectToHal_l()
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
(void)mCallback->addEffectToHal(mEffectInterface);
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
sp<StreamHalInterface> stream = thread->stream();
if (stream != 0) {
status_t result = stream->addEffect(mEffectInterface);
ALOGE_IF(result != OK, "Error when adding effect: %d", result);
}
}
}
}
// start() must be called with PlaybackThread::mLock or EffectChain::mLock held
status_t AudioFlinger::EffectModule::start()
{
sp<EffectChain> chain;
status_t status;
{
Mutex::Autolock _l(mLock);
status = start_l();
if (status == NO_ERROR) {
chain = mChain.promote();
}
}
if (status == NO_ERROR) {
mCallback->resetVolume();
if (chain != 0) {
chain->resetVolume_l();
}
return status;
}
@ -824,10 +860,11 @@ status_t AudioFlinger::EffectModule::stop_l()
uint32_t size = sizeof(status_t);
if (isVolumeControl() && isOffloadedOrDirect()) {
sp<EffectChain>chain = mChain.promote();
// We have the EffectChain and EffectModule lock, permit a reentrant call to setVolume:
// resetVolume_l --> setVolume_l --> EffectModule::setVolume
mSetVolumeReentrantTid = gettid();
mCallback->resetVolume();
chain->resetVolume_l();
mSetVolumeReentrantTid = INVALID_PID;
}
@ -840,7 +877,7 @@ status_t AudioFlinger::EffectModule::stop_l()
status = cmdStatus;
}
if (status == NO_ERROR) {
status = removeEffectFromHal_l();
status = remove_effect_from_hal_l();
}
return status;
}
@ -849,18 +886,25 @@ status_t AudioFlinger::EffectModule::stop_l()
void AudioFlinger::EffectModule::release_l()
{
if (mEffectInterface != 0) {
removeEffectFromHal_l();
remove_effect_from_hal_l();
// release effect engine
mEffectInterface->close();
mEffectInterface.clear();
}
}
status_t AudioFlinger::EffectModule::removeEffectFromHal_l()
status_t AudioFlinger::EffectModule::remove_effect_from_hal_l()
{
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
(mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
mCallback->removeEffectFromHal(mEffectInterface);
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
sp<StreamHalInterface> stream = thread->stream();
if (stream != 0) {
status_t result = stream->removeEffect(mEffectInterface);
ALOGE_IF(result != OK, "Error when removing effect: %d", result);
}
}
}
return NO_ERROR;
}
@ -950,29 +994,10 @@ status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
return status;
}
void AudioFlinger::EffectModule::checkSuspendOnEffectEnabled(bool enabled, bool threadLocked) {
mCallback->checkSuspendOnEffectEnabled(this, enabled, threadLocked);
}
status_t AudioFlinger::EffectModule::setEnabled(bool enabled, bool fromHandle)
status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
{
status_t status;
{
Mutex::Autolock _l(mLock);
status = setEnabled_l(enabled);
}
if (fromHandle) {
if (enabled) {
if (status != NO_ERROR) {
mCallback->checkSuspendOnEffectEnabled(this, false, false /*threadLocked*/);
} else {
mCallback->onEffectEnable(this);
}
} else {
mCallback->onEffectDisable(this);
}
}
return status;
Mutex::Autolock _l(mLock);
return setEnabled_l(enabled);
}
// must be called with EffectModule::mLock held
@ -1055,7 +1080,7 @@ bool AudioFlinger::EffectModule::isProcessEnabled() const
bool AudioFlinger::EffectModule::isOffloadedOrDirect() const
{
return mCallback->isOffloadOrDirect();
return (mThreadType == ThreadBase::OFFLOAD || mThreadType == ThreadBase::DIRECT);
}
bool AudioFlinger::EffectModule::isVolumeControlEnabled() const
@ -1099,7 +1124,9 @@ void AudioFlinger::EffectModule::setInBuffer(const sp<EffectBufferHalInterface>&
|| size > mInConversionBuffer->getSize())) {
mInConversionBuffer.clear();
ALOGV("%s: allocating mInConversionBuffer %zu", __func__, size);
(void)mCallback->allocateHalBuffer(size, &mInConversionBuffer);
sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
LOG_ALWAYS_FATAL_IF(audioFlinger == nullptr, "EM could not retrieved audioFlinger");
(void)audioFlinger->mEffectsFactoryHal->allocateBuffer(size, &mInConversionBuffer);
}
if (mInConversionBuffer.get() != nullptr) {
mInConversionBuffer->setFrameCount(inFrameCount);
@ -1143,7 +1170,9 @@ void AudioFlinger::EffectModule::setOutBuffer(const sp<EffectBufferHalInterface>
|| size > mOutConversionBuffer->getSize())) {
mOutConversionBuffer.clear();
ALOGV("%s: allocating mOutConversionBuffer %zu", __func__, size);
(void)mCallback->allocateHalBuffer(size, &mOutConversionBuffer);
sp<AudioFlinger> audioFlinger = mAudioFlinger.promote();
LOG_ALWAYS_FATAL_IF(audioFlinger == nullptr, "EM could not retrieved audioFlinger");
(void)audioFlinger->mEffectsFactoryHal->allocateBuffer(size, &mOutConversionBuffer);
}
if (mOutConversionBuffer.get() != nullptr) {
mOutConversionBuffer->setFrameCount(outFrameCount);
@ -1191,10 +1220,14 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right,
void AudioFlinger::EffectChain::setVolumeForOutput_l(uint32_t left, uint32_t right)
{
if (mEffectCallback->isOffloadOrDirect() && !isNonOffloadableEnabled_l()) {
sp<ThreadBase> thread = mThread.promote();
if (thread != 0 &&
(thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::DIRECT) &&
!isNonOffloadableEnabled_l()) {
PlaybackThread *t = (PlaybackThread *)thread.get();
float vol_l = (float)left / (1 << 24);
float vol_r = (float)right / (1 << 24);
mEffectCallback->setVolumeForOutput(vol_l, vol_r);
t->setVolumeForOutput_l(vol_l, vol_r);
}
}
@ -1613,16 +1646,38 @@ status_t AudioFlinger::EffectHandle::enable()
return status;
}
effect->checkSuspendOnEffectEnabled(true, false /*threadLocked*/);
sp<ThreadBase> thread = effect->thread().promote();
if (thread != 0) {
thread->checkSuspendOnEffectEnabled(effect, true, effect->sessionId());
}
// checkSuspendOnEffectEnabled() can suspend this same effect when enabled
if (effect->suspended()) {
return NO_ERROR;
}
status = effect->setEnabled(true, true /*fromHandle*/);
status = effect->setEnabled(true);
if (status != NO_ERROR) {
if (thread != 0) {
thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
}
mEnabled = false;
} else {
if (thread != 0) {
if (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::MMAP) {
Mutex::Autolock _l(thread->mLock);
thread->broadcast_l();
}
if (!effect->isOffloadable()) {
if (thread->type() == ThreadBase::OFFLOAD) {
PlaybackThread *t = (PlaybackThread *)thread.get();
t->invalidateTracks(AUDIO_STREAM_MUSIC);
}
if (effect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
}
}
}
}
return status;
}
@ -1650,7 +1705,17 @@ status_t AudioFlinger::EffectHandle::disable()
return NO_ERROR;
}
status_t status = effect->setEnabled(false, true /*fromHandle*/);
status_t status = effect->setEnabled(false);
sp<ThreadBase> thread = effect->thread().promote();
if (thread != 0) {
thread->checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
if (thread->type() == ThreadBase::OFFLOAD || thread->type() == ThreadBase::MMAP) {
Mutex::Autolock _l(thread->mLock);
thread->broadcast_l();
}
}
return status;
}
@ -1674,7 +1739,10 @@ void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast)
{
sp<EffectModule> effect = mEffect.promote();
if (effect != 0) {
if (effect->disconnectHandle(this, unpinIfLast) > 0) {
sp<ThreadBase> thread = effect->thread().promote();
if (thread != 0) {
thread->disconnectEffectHandle(this, unpinIfLast);
} else if (effect->disconnectHandle(this, unpinIfLast) > 0) {
ALOGW("%s Effect handle %p disconnected after thread destruction",
__func__, this);
}
@ -1894,13 +1962,12 @@ void AudioFlinger::EffectHandle::dumpToBuffer(char* buffer, size_t size)
AudioFlinger::EffectChain::EffectChain(ThreadBase *thread,
audio_session_t sessionId)
: mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
: mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX),
mEffectCallback(new EffectCallback(this, thread, thread->mAudioFlinger.get()))
mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
{
mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
if (thread == nullptr) {
if (thread == NULL) {
return;
}
mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) /
@ -1966,29 +2033,40 @@ std::vector<int> AudioFlinger::EffectChain::getEffectIds()
void AudioFlinger::EffectChain::clearInputBuffer()
{
Mutex::Autolock _l(mLock);
clearInputBuffer_l();
sp<ThreadBase> thread = mThread.promote();
if (thread == 0) {
ALOGW("clearInputBuffer(): cannot promote mixer thread");
return;
}
clearInputBuffer_l(thread);
}
// Must be called with EffectChain::mLock locked
void AudioFlinger::EffectChain::clearInputBuffer_l()
void AudioFlinger::EffectChain::clearInputBuffer_l(const sp<ThreadBase>& thread)
{
if (mInBuffer == NULL) {
return;
}
const size_t frameSize =
audio_bytes_per_sample(EFFECT_BUFFER_FORMAT) * mEffectCallback->channelCount();
audio_bytes_per_sample(EFFECT_BUFFER_FORMAT) * thread->channelCount();
memset(mInBuffer->audioBuffer()->raw, 0, mEffectCallback->frameCount() * frameSize);
memset(mInBuffer->audioBuffer()->raw, 0, thread->frameCount() * frameSize);
mInBuffer->commit();
}
// Must be called with EffectChain::mLock locked
void AudioFlinger::EffectChain::process_l()
{
sp<ThreadBase> thread = mThread.promote();
if (thread == 0) {
ALOGW("process_l(): cannot promote mixer thread");
return;
}
// never process effects when:
// - on an OFFLOAD thread
// - no more tracks are on the session and the effect tail has been rendered
bool doProcess = !mEffectCallback->isOffloadOrMmap();
bool doProcess = (thread->type() != ThreadBase::OFFLOAD)
&& (thread->type() != ThreadBase::MMAP);
if (!audio_is_global_session(mSessionId)) {
bool tracksOnSession = (trackCnt() != 0);
@ -2000,7 +2078,7 @@ void AudioFlinger::EffectChain::process_l()
// if no track is active and the effect tail has not been rendered,
// the input buffer must be cleared here as the mixer process will not do it
if (tracksOnSession || mTailBufferCount > 0) {
clearInputBuffer_l();
clearInputBuffer_l(thread);
if (mTailBufferCount > 0) {
mTailBufferCount--;
}
@ -2036,13 +2114,14 @@ void AudioFlinger::EffectChain::process_l()
// createEffect_l() must be called with ThreadBase::mLock held
status_t AudioFlinger::EffectChain::createEffect_l(sp<EffectModule>& effect,
ThreadBase *thread,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
bool pinned)
{
Mutex::Autolock _l(mLock);
effect = new EffectModule(mEffectCallback, desc, id, sessionId, pinned);
effect = new EffectModule(thread, this, desc, id, sessionId, pinned);
status_t lStatus = effect->status();
if (lStatus == NO_ERROR) {
lStatus = addEffect_ll(effect);
@ -2065,7 +2144,12 @@ status_t AudioFlinger::EffectChain::addEffect_ll(const sp<EffectModule>& effect)
effect_descriptor_t desc = effect->desc();
uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
effect->setCallback(mEffectCallback);
effect->setChain(this);
sp<ThreadBase> thread = mThread.promote();
if (thread == 0) {
return NO_INIT;
}
effect->setThread(thread);
if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
// Auxiliary effects are inserted at the beginning of mEffects vector as
@ -2076,13 +2160,13 @@ status_t AudioFlinger::EffectChain::addEffect_ll(const sp<EffectModule>& effect)
// 32 bit format. This is to avoid saturation in AudoMixer
// accumulation stage. Saturation is done in EffectModule::process() before
// calling the process in effect engine
size_t numSamples = mEffectCallback->frameCount();
size_t numSamples = thread->frameCount();
sp<EffectBufferHalInterface> halBuffer;
#ifdef FLOAT_EFFECT_CHAIN
status_t result = mEffectCallback->allocateHalBuffer(
status_t result = thread->mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
numSamples * sizeof(float), &halBuffer);
#else
status_t result = mEffectCallback->allocateHalBuffer(
status_t result = thread->mAudioFlinger->mEffectsFactoryHal->allocateBuffer(
numSamples * sizeof(int32_t), &halBuffer);
#endif
if (result != OK) return result;
@ -2400,7 +2484,7 @@ void AudioFlinger::EffectChain::setEffectSuspended_l(
if (effect != 0) {
desc->mEffect = effect;
effect->setSuspended(true);
effect->setEnabled(false, false /*fromHandle*/);
effect->setEnabled(false);
}
}
} else {
@ -2558,7 +2642,7 @@ void AudioFlinger::EffectChain::checkSuspendOnEffectEnabled(const sp<EffectModul
// if effect is requested to suspended but was not yet enabled, suspend it now.
if (desc->mEffect == 0) {
desc->mEffect = effect;
effect->setEnabled(false, false /*fromHandle*/);
effect->setEnabled(false);
effect->setSuspended(true);
}
} else {
@ -2593,7 +2677,10 @@ bool AudioFlinger::EffectChain::isNonOffloadableEnabled_l()
void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread)
{
Mutex::Autolock _l(mLock);
mEffectCallback->setThread(thread.get());
mThread = thread;
for (size_t i = 0; i < mEffects.size(); i++) {
mEffects[i]->setThread(thread);
}
}
void AudioFlinger::EffectChain::checkOutputFlagCompatibility(audio_output_flags_t *flags) const
@ -2653,224 +2740,4 @@ bool AudioFlinger::EffectChain::isCompatibleWithThread_l(const sp<ThreadBase>& t
return true;
}
// EffectCallbackInterface implementation
status_t AudioFlinger::EffectChain::EffectCallback::createEffectHal(
const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t deviceId,
sp<EffectHalInterface> *effect) {
status_t status = NO_INIT;
sp<AudioFlinger> af = mAudioFlinger.promote();
if (af == nullptr) {
return status;
}
sp<EffectsFactoryHalInterface> effectsFactory = af->getEffectsFactory();
if (effectsFactory != 0) {
status = effectsFactory->createEffect(pEffectUuid, sessionId, io(), deviceId, effect);
}
return status;
}
bool AudioFlinger::EffectChain::EffectCallback::updateOrphanEffectChains(
const sp<AudioFlinger::EffectModule>& effect) {
sp<AudioFlinger> af = mAudioFlinger.promote();
if (af == nullptr) {
return false;
}
return af->updateOrphanEffectChains(effect);
}
status_t AudioFlinger::EffectChain::EffectCallback::allocateHalBuffer(
size_t size, sp<EffectBufferHalInterface>* buffer) {
sp<AudioFlinger> af = mAudioFlinger.promote();
LOG_ALWAYS_FATAL_IF(af == nullptr, "allocateHalBuffer() could not retrieved audio flinger");
return af->mEffectsFactoryHal->allocateBuffer(size, buffer);
}
status_t AudioFlinger::EffectChain::EffectCallback::addEffectToHal(
sp<EffectHalInterface> effect) {
status_t result = NO_INIT;
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return result;
}
sp <StreamHalInterface> st = t->stream();
if (st == nullptr) {
return result;
}
result = st->addEffect(effect);
ALOGE_IF(result != OK, "Error when adding effect: %d", result);
return result;
}
status_t AudioFlinger::EffectChain::EffectCallback::removeEffectFromHal(
sp<EffectHalInterface> effect) {
status_t result = NO_INIT;
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return result;
}
sp <StreamHalInterface> st = t->stream();
if (st == nullptr) {
return result;
}
result = st->removeEffect(effect);
ALOGE_IF(result != OK, "Error when removing effect: %d", result);
return result;
}
audio_io_handle_t AudioFlinger::EffectChain::EffectCallback::io() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return AUDIO_IO_HANDLE_NONE;
}
return t->id();
}
bool AudioFlinger::EffectChain::EffectCallback::isOutput() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return true;
}
return t->isOutput();
}
bool AudioFlinger::EffectChain::EffectCallback::isOffload() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return false;
}
return t->type() == ThreadBase::OFFLOAD;
}
bool AudioFlinger::EffectChain::EffectCallback::isOffloadOrDirect() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return false;
}
return t->type() == ThreadBase::OFFLOAD || t->type() == ThreadBase::DIRECT;
}
bool AudioFlinger::EffectChain::EffectCallback::isOffloadOrMmap() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return false;
}
return t->type() == ThreadBase::OFFLOAD || t->type() == ThreadBase::MMAP;
}
uint32_t AudioFlinger::EffectChain::EffectCallback::sampleRate() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return 0;
}
return t->sampleRate();
}
audio_channel_mask_t AudioFlinger::EffectChain::EffectCallback::channelMask() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return AUDIO_CHANNEL_NONE;
}
return t->channelMask();
}
uint32_t AudioFlinger::EffectChain::EffectCallback::channelCount() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return 0;
}
return t->channelCount();
}
size_t AudioFlinger::EffectChain::EffectCallback::frameCount() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return 0;
}
return t->frameCount();
}
uint32_t AudioFlinger::EffectChain::EffectCallback::latency() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return 0;
}
return t->latency_l();
}
void AudioFlinger::EffectChain::EffectCallback::setVolumeForOutput(float left, float right) const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return;
}
t->setVolumeForOutput_l(left, right);
}
void AudioFlinger::EffectChain::EffectCallback::checkSuspendOnEffectEnabled(
const sp<EffectModule>& effect, bool enabled, bool threadLocked) {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return;
}
t->checkSuspendOnEffectEnabled(enabled, effect->sessionId(), threadLocked);
sp<EffectChain> c = mChain.promote();
if (c == nullptr) {
return;
}
c->checkSuspendOnEffectEnabled(effect, enabled);
}
void AudioFlinger::EffectChain::EffectCallback::onEffectEnable(const sp<EffectModule>& effect) {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return;
}
t->onEffectEnable(effect);
}
void AudioFlinger::EffectChain::EffectCallback::onEffectDisable(const sp<EffectModule>& effect) {
checkSuspendOnEffectEnabled(effect, false, false /*threadLocked*/);
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return;
}
t->onEffectDisable();
}
bool AudioFlinger::EffectChain::EffectCallback::disconnectEffectHandle(EffectHandle *handle,
bool unpinIfLast) {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
return false;
}
t->disconnectEffectHandle(handle, unpinIfLast);
return true;
}
void AudioFlinger::EffectChain::EffectCallback::resetVolume() {
sp<EffectChain> c = mChain.promote();
if (c == nullptr) {
return;
}
c->resetVolume_l();
}
uint32_t AudioFlinger::EffectChain::EffectCallback::strategy() const {
sp<EffectChain> c = mChain.promote();
if (c == nullptr) {
return PRODUCT_STRATEGY_NONE;
}
return c->strategy();
}
int32_t AudioFlinger::EffectChain::EffectCallback::activeTrackCnt() const {
sp<EffectChain> c = mChain.promote();
if (c == nullptr) {
return 0;
}
return c->activeTrackCnt();
}
} // namespace android

@ -21,61 +21,11 @@
//--- Audio Effect Management
// Interface implemented by the EffectModule parent or owner (e.g an EffectChain) to abstract
// interactions between the EffectModule and the reset of the audio framework.
class EffectCallbackInterface : public RefBase {
public:
~EffectCallbackInterface() override = default;
// Trivial methods usually implemented with help from ThreadBase
virtual audio_io_handle_t io() const = 0;
virtual bool isOutput() const = 0;
virtual bool isOffload() const = 0;
virtual bool isOffloadOrDirect() const = 0;
virtual bool isOffloadOrMmap() const = 0;
virtual uint32_t sampleRate() const = 0;
virtual audio_channel_mask_t channelMask() const = 0;
virtual uint32_t channelCount() const = 0;
virtual size_t frameCount() const = 0;
// Non trivial methods usually implemented with help from ThreadBase:
// pay attention to mutex locking order
virtual uint32_t latency() const { return 0; }
virtual status_t addEffectToHal(sp<EffectHalInterface> effect) = 0;
virtual status_t removeEffectFromHal(sp<EffectHalInterface> effect) = 0;
virtual void setVolumeForOutput(float left, float right) const;
virtual bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) = 0;
virtual void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
bool enabled,
bool threadLocked) = 0;
virtual void onEffectEnable(const sp<EffectModule>& effect) = 0;
virtual void onEffectDisable(const sp<EffectModule>& effect) = 0;
// Methods usually implemented with help from AudioFlinger: pay attention to mutex locking order
virtual status_t createEffectHal(const effect_uuid_t *pEffectUuid,
int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) = 0;
virtual status_t allocateHalBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) = 0;
virtual bool updateOrphanEffectChains(const sp<EffectModule>& effect) = 0;
// Methods usually implemented with help from EffectChain: pay attention to mutex locking order
virtual uint32_t strategy() const = 0;
virtual int32_t activeTrackCnt() const = 0;
virtual void resetVolume() = 0;
virtual wp<EffectChain> chain() const = 0;
};
// EffectModule and EffectChain classes both have their own mutex to protect
// state changes or resource modifications. Always respect the following order
// if multiple mutexes must be acquired to avoid cross deadlock:
// AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
// AudioHandle -> ThreadBase -> EffectChain -> EffectModule
// NOTE: When implementing the EffectCallbackInterface, in an EffectChain or other, it is important
// to pay attention to this locking order as some callback methods can be called from a state where
// EffectModule and/or EffectChain mutexes are held.
// In addition, methods that lock the AudioPolicyService mutex (getOutputForEffect(),
// startOutput(), getInputForAttr(), releaseInput()...) should never be called with AudioFlinger or
// Threadbase mutex locked to avoid cross deadlock with other clients calling AudioPolicyService
@ -92,7 +42,8 @@ public:
// the attached track(s) to accumulate their auxiliary channel.
class EffectModule : public RefBase {
public:
EffectModule(const sp<EffectCallbackInterface>& chain,
EffectModule(ThreadBase *thread,
const wp<AudioFlinger::EffectChain>& chain,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@ -130,7 +81,7 @@ public:
audio_session_t sessionId() const {
return mSessionId;
}
status_t setEnabled(bool enabled, bool fromHandle);
status_t setEnabled(bool enabled);
status_t setEnabled_l(bool enabled);
bool isEnabled() const;
bool isProcessEnabled() const;
@ -145,7 +96,10 @@ public:
int16_t *outBuffer() const {
return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL;
}
void setCallback(const sp<EffectCallbackInterface>& callback) { mCallback = callback; }
void setChain(const wp<EffectChain>& chain) { mChain = chain; }
void setThread(const wp<ThreadBase>& thread)
{ mThread = thread; mThreadType = thread.promote()->type(); }
const wp<ThreadBase>& thread() { return mThread; }
status_t addHandle(EffectHandle *handle);
ssize_t disconnectHandle(EffectHandle *handle, bool unpinIfLast);
@ -153,7 +107,7 @@ public:
ssize_t removeHandle_l(EffectHandle *handle);
const effect_descriptor_t& desc() const { return mDescriptor; }
sp<EffectCallbackInterface>& callback() { return mCallback; }
wp<EffectChain>& chain() { return mChain; }
status_t setDevices(const AudioDeviceTypeAddrVector &devices);
status_t setInputDevice(const AudioDeviceTypeAddr &device);
@ -190,7 +144,6 @@ public:
void release_l();
status_t updatePolicyState();
void checkSuspendOnEffectEnabled(bool enabled, bool threadLocked);
void dump(int fd, const Vector<String16>& args);
@ -205,11 +158,13 @@ private:
status_t start_l();
status_t stop_l();
status_t removeEffectFromHal_l();
status_t remove_effect_from_hal_l();
status_t sendSetAudioDevicesCommand(const AudioDeviceTypeAddrVector &devices, uint32_t cmdCode);
mutable Mutex mLock; // mutex for process, commands and handles list protection
sp<EffectCallbackInterface> mCallback; // parent effect chain
wp<ThreadBase> mThread; // parent thread
ThreadBase::type_t mThreadType; // parent thread type
wp<EffectChain> mChain; // parent effect chain
const int mId; // this instance unique ID
const audio_session_t mSessionId; // audio session ID
const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
@ -226,6 +181,7 @@ mutable Mutex mLock; // mutex for process, commands and handl
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mSuspended; // effect is suspended: temporarily disabled by framework
bool mOffloaded; // effect is currently offloaded to the audio DSP
wp<AudioFlinger> mAudioFlinger;
#ifdef FLOAT_EFFECT_CHAIN
bool mSupportsFloat; // effect supports float processing
@ -377,6 +333,7 @@ public:
}
status_t createEffect_l(sp<EffectModule>& effect,
ThreadBase *thread,
effect_descriptor_t *desc,
int id,
audio_session_t sessionId,
@ -432,8 +389,9 @@ public:
bool suspend);
// suspend all eligible effects
void setEffectSuspendedAll_l(bool suspend);
// check if effects should be suspended or restored when a given effect is enable or disabled
void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect, bool enabled);
// check if effects should be suspend or restored when a given effect is enable or disabled
void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
bool enabled);
void clearInputBuffer();
@ -458,60 +416,9 @@ public:
// isCompatibleWithThread_l() must be called with thread->mLock held
bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
sp<EffectCallbackInterface> effectCallback() const { return mEffectCallback; }
wp<ThreadBase> thread() const { return mEffectCallback->thread(); }
void dump(int fd, const Vector<String16>& args);
private:
class EffectCallback : public EffectCallbackInterface {
public:
EffectCallback(EffectChain *chain, ThreadBase *thread, AudioFlinger *audioFlinger)
: mChain(chain), mThread(thread), mAudioFlinger(audioFlinger) {}
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
int32_t sessionId, int32_t deviceId, sp<EffectHalInterface> *effect) override;
status_t allocateHalBuffer(size_t size, sp<EffectBufferHalInterface>* buffer) override;
bool updateOrphanEffectChains(const sp<EffectModule>& effect) override;
audio_io_handle_t io() const override;
bool isOutput() const override;
bool isOffload() const override;
bool isOffloadOrDirect() const override;
bool isOffloadOrMmap() const override;
uint32_t sampleRate() const override;
audio_channel_mask_t channelMask() const override;
uint32_t channelCount() const override;
size_t frameCount() const override;
uint32_t latency() const override;
status_t addEffectToHal(sp<EffectHalInterface> effect) override;
status_t removeEffectFromHal(sp<EffectHalInterface> effect) override;
bool disconnectEffectHandle(EffectHandle *handle, bool unpinIfLast) override;
void setVolumeForOutput(float left, float right) const override;
// check if effects should be suspended/restored when a given effect is enable/disabled
void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
bool enabled, bool threadLocked) override;
void resetVolume() override;
uint32_t strategy() const override;
int32_t activeTrackCnt() const override;
void onEffectEnable(const sp<EffectModule>& effect) override;
void onEffectDisable(const sp<EffectModule>& effect) override;
wp<EffectChain> chain() const override { return mChain; }
wp<ThreadBase> thread() { return mThread; }
void setThread(ThreadBase *thread) { mThread = thread; };
private:
wp<EffectChain> mChain;
wp<ThreadBase> mThread;
wp<AudioFlinger> mAudioFlinger;
};
friend class AudioFlinger; // for mThread, mEffects
DISALLOW_COPY_AND_ASSIGN(EffectChain);
@ -537,12 +444,13 @@ private:
static bool isEffectEligibleForBtNrecSuspend(const effect_uuid_t *type);
void clearInputBuffer_l();
void clearInputBuffer_l(const sp<ThreadBase>& thread);
void setThread(const sp<ThreadBase>& thread);
void setVolumeForOutput_l(uint32_t left, uint32_t right);
wp<ThreadBase> mThread; // parent mixer thread
mutable Mutex mLock; // mutex protecting effect list
Vector< sp<EffectModule> > mEffects; // list of effect modules
audio_session_t mSessionId; // audio session ID
@ -566,6 +474,4 @@ private:
// timeLow fields among effect type UUIDs.
// Updated by setEffectSuspended_l() and setEffectSuspendedAll_l() only.
KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
const sp<EffectCallback> mEffectCallback;
};

@ -1146,13 +1146,18 @@ void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *ty
}
}
void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(bool enabled,
audio_session_t sessionId,
bool threadLocked) {
if (!threadLocked) {
mLock.lock();
}
void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
bool enabled,
audio_session_t sessionId)
{
Mutex::Autolock _l(mLock);
checkSuspendOnEffectEnabled_l(effect, enabled, sessionId);
}
void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
bool enabled,
audio_session_t sessionId)
{
if (mType != RECORD) {
// suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
// another session. This gives the priority to well behaved effect control panels
@ -1164,8 +1169,9 @@ void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(bool enabled,
}
}
if (!threadLocked) {
mLock.unlock();
sp<EffectChain> chain = getEffectChain_l(sessionId);
if (chain != 0) {
chain->checkSuspendOnEffectEnabled(effect, enabled);
}
}
@ -1373,7 +1379,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l(
if (effect == 0) {
effectId = mAudioFlinger->nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT);
// create a new effect module if none present in the chain
lStatus = chain->createEffect_l(effect, desc, effectId, sessionId, pinned);
lStatus = chain->createEffect_l(effect, this, desc, effectId, sessionId, pinned);
if (lStatus != NO_ERROR) {
goto Exit;
}
@ -1421,7 +1427,7 @@ void AudioFlinger::ThreadBase::disconnectEffectHandle(EffectHandle *handle,
Mutex::Autolock _l(mLock);
effect = handle->effect().promote();
if (effect == nullptr) {
if (effect == 0) {
return;
}
// restore suspended effects if the disconnected handle was enabled and the last one.
@ -1433,34 +1439,11 @@ void AudioFlinger::ThreadBase::disconnectEffectHandle(EffectHandle *handle,
if (remove) {
mAudioFlinger->updateOrphanEffectChains(effect);
if (handle->enabled()) {
effect->checkSuspendOnEffectEnabled(false, false /*threadLocked*/);
}
}
}
void AudioFlinger::ThreadBase::onEffectEnable(const sp<EffectModule>& effect) {
if (mType == OFFLOAD || mType == MMAP) {
Mutex::Autolock _l(mLock);
broadcast_l();
}
if (!effect->isOffloadable()) {
if (mType == ThreadBase::OFFLOAD) {
PlaybackThread *t = (PlaybackThread *)this;
t->invalidateTracks(AUDIO_STREAM_MUSIC);
}
if (effect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
mAudioFlinger->onNonOffloadableGlobalEffectEnable();
checkSuspendOnEffectEnabled(effect, false, effect->sessionId());
}
}
}
void AudioFlinger::ThreadBase::onEffectDisable() {
if (mType == OFFLOAD || mType == MMAP) {
Mutex::Autolock _l(mLock);
broadcast_l();
}
}
sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect(audio_session_t sessionId,
int effectId)
{
@ -1536,7 +1519,7 @@ void AudioFlinger::ThreadBase::removeEffect_l(const sp<EffectModule>& effect, bo
detachAuxEffect_l(effect->id());
}
sp<EffectChain> chain = effect->callback()->chain().promote();
sp<EffectChain> chain = effect->chain().promote();
if (chain != 0) {
// remove effect chain if removing last effect
if (chain->removeEffect_l(effect, release) == 0) {

@ -271,8 +271,6 @@ public:
// Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
// and returns the [normal mix] buffer's frame count.
virtual size_t frameCount() const = 0;
virtual uint32_t latency_l() const { return 0; }
virtual void setVolumeForOutput_l(float left __unused, float right __unused) const {}
// Return's the HAL's frame count i.e. fast mixer buffer size.
size_t frameCountHAL() const { return mFrameCount; }
@ -426,9 +424,14 @@ public:
// check if some effects must be suspended/restored when an effect is enabled
// or disabled
void checkSuspendOnEffectEnabled(bool enabled,
audio_session_t sessionId,
bool threadLocked);
void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
bool enabled,
audio_session_t sessionId =
AUDIO_SESSION_OUTPUT_MIX);
void checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
bool enabled,
audio_session_t sessionId =
AUDIO_SESSION_OUTPUT_MIX);
virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0;
virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0;
@ -462,9 +465,6 @@ public:
mutable Mutex mLock;
void onEffectEnable(const sp<EffectModule>& effect);
void onEffectDisable();
protected:
// entry describing an effect being suspended in mSuspendedSessions keyed vector
@ -814,7 +814,7 @@ public:
// return estimated latency in milliseconds, as reported by HAL
uint32_t latency() const;
// same, but lock must already be held
uint32_t latency_l() const override;
uint32_t latency_l() const;
// VolumeInterface
virtual void setMasterVolume(float value);
@ -824,7 +824,7 @@ public:
virtual void setStreamMute(audio_stream_type_t stream, bool muted);
virtual float streamVolume(audio_stream_type_t stream) const;
void setVolumeForOutput_l(float left, float right) const override;
void setVolumeForOutput_l(float left, float right) const;
sp<Track> createTrack_l(
const sp<AudioFlinger::Client>& client,

Loading…
Cancel
Save