diff options
Diffstat (limited to 'services/audioflinger/AudioFlinger.cpp')
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 537 |
1 files changed, 365 insertions, 172 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 771d885..b88e69d 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -63,6 +63,8 @@ // ---------------------------------------------------------------------------- +extern const char * const gEffectLibPath; + namespace android { static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n"; @@ -127,8 +129,7 @@ static bool settingsAllowed() { AudioFlinger::AudioFlinger() : BnAudioFlinger(), - mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1), - mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0) + mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1) { mHardwareStatus = AUDIO_HW_IDLE; @@ -321,13 +322,19 @@ sp<IAudioTrack> AudioFlinger::createTrack( mClients.add(pid, client); } - // If no audio session id is provided, create one here - // TODO: enforce same stream type for all tracks in same audio session? - // TODO: prevent same audio session on different output threads LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId); - if (sessionId != NULL && *sessionId != 0) { + if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) { + // prevent same audio session on different output threads + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.keyAt(i) != output && + mPlaybackThreads.valueAt(i)->hasAudioSession(*sessionId)) { + lStatus = BAD_VALUE; + goto Exit; + } + } lSessionId = *sessionId; } else { + // if no audio session id is provided, create one here lSessionId = nextUniqueId(); if (sessionId != NULL) { *sessionId = lSessionId; @@ -1141,6 +1148,23 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra { // scope for mLock Mutex::Autolock _l(mLock); + + // all tracks in same audio session must share the same routing strategy otherwise + // conflicts will happen when tracks are moved from one output to another by audio policy + // manager + uint32_t strategy = + AudioSystem::getStrategyForStream((AudioSystem::stream_type)streamType); + for (size_t i = 0; i < mTracks.size(); ++i) { + sp<Track> t = mTracks[i]; + if (t != 0) { + if (sessionId == t->sessionId() && + strategy != AudioSystem::getStrategyForStream((AudioSystem::stream_type)t->type())) { + lStatus = BAD_VALUE; + goto Exit; + } + } + } + track = new Track(this, client, streamType, sampleRate, format, channelCount, frameCount, sharedBuffer, sessionId); if (track->getCblk() == NULL || track->name() < 0) { @@ -1153,6 +1177,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra if (chain != 0) { LOGV("createTrack_l() setting main buffer %p", chain->inBuffer()); track->setMainBuffer(chain->inBuffer()); + chain->setStrategy(AudioSystem::getStrategyForStream((AudioSystem::stream_type)track->type())); } } lStatus = NO_ERROR; @@ -1344,7 +1369,16 @@ void AudioFlinger::PlaybackThread::readOutputParameters() mMixBuffer = new int16_t[mFrameCount * 2]; memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); - //TODO handle effects reconfig + // force reconfiguration of effect chains and engines to take new buffer size and audio + // parameters into account + // Note that mLock is not held when readOutputParameters() is called from the constructor + // but in this case nothing is done below as no audio sessions have effect yet so it doesn't + // matter. + // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains + Vector< sp<EffectChain> > effectChains = mEffectChains; + for (size_t i = 0; i < effectChains.size(); i ++) { + mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this); + } } status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames) @@ -1369,7 +1403,8 @@ bool AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) for (size_t i = 0; i < mTracks.size(); ++i) { sp<Track> track = mTracks[i]; - if (sessionId == track->sessionId()) { + if (sessionId == track->sessionId() && + !(track->mCblk->flags & CBLK_INVALID_MSK)) { return true; } } @@ -1377,6 +1412,23 @@ bool AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) return false; } +uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId) +{ + // session AudioSystem::SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that + // it is moved to correct output by audio policy manager when A2DP is connected or disconnected + if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) { + return AudioSystem::getStrategyForStream(AudioSystem::MUSIC); + } + for (size_t i = 0; i < mTracks.size(); i++) { + sp<Track> track = mTracks[i]; + if (sessionId == track->sessionId() && + !(track->mCblk->flags & CBLK_INVALID_MSK)) { + return AudioSystem::getStrategyForStream((AudioSystem::stream_type) track->type()); + } + } + return AudioSystem::getStrategyForStream(AudioSystem::MUSIC); +} + sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId) { Mutex::Autolock _l(mLock); @@ -1402,7 +1454,7 @@ void AudioFlinger::PlaybackThread::setMode(uint32_t mode) Mutex::Autolock _l(mLock); size_t size = mEffectChains.size(); for (size_t i = 0; i < size; i++) { - mEffectChains[i]->setMode(mode); + mEffectChains[i]->setMode_l(mode); } } @@ -1503,8 +1555,7 @@ bool AudioFlinger::MixerThread::threadLoop() // prevent any changes in effect chain list and in each effect chain // during mixing and effect process as the audio buffers could be deleted // or modified if an effect is created or deleted - effectChains = mEffectChains; - lockEffectChains_l(); + lockEffectChains_l(effectChains); } if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { @@ -1540,7 +1591,7 @@ bool AudioFlinger::MixerThread::threadLoop() effectChains[i]->process_l(); } // enable changes in effect chain - unlockEffectChains(); + unlockEffectChains(effectChains); #ifdef LVMX int audioOutputType = LifeVibes::getMixerType(mId, mType); if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { @@ -1571,7 +1622,7 @@ bool AudioFlinger::MixerThread::threadLoop() mStandby = false; } else { // enable changes in effect chain - unlockEffectChains(); + unlockEffectChains(effectChains); usleep(sleepTime); } @@ -1625,10 +1676,10 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track } #endif // Delegate master volume control to effect in output mix effect chain if needed - sp<EffectChain> chain = getEffectChain_l(0); + sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX); if (chain != 0) { uint32_t v = (uint32_t)(masterVolume * (1 << 24)); - chain->setVolume(&v, &v); + chain->setVolume_l(&v, &v); masterVolume = (float)((v + (1 << 23)) >> 24); chain.clear(); } @@ -1706,7 +1757,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track uint32_t vr = (uint32_t)(v * cblk->volume[1]) << 12; // Delegate volume control to effect in track effect chain if needed - if (chain != 0 && chain->setVolume(&vl, &vr)) { + if (chain != 0 && chain->setVolume_l(&vl, &vr)) { // Do not ramp volume is volume is controlled by effect param = AudioMixer::VOLUME; } @@ -1814,8 +1865,10 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track void AudioFlinger::MixerThread::invalidateTracks(int streamType) { - LOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d", this, streamType, mTracks.size()); + LOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d", + this, streamType, mTracks.size()); Mutex::Autolock _l(mLock); + size_t size = mTracks.size(); for (size_t i = 0; i < size; i++) { sp<Track> t = mTracks[i]; @@ -1885,7 +1938,7 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() // aware of attached audio device. mDevice = (uint32_t)value; for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setDevice(mDevice); + mEffectChains[i]->setDevice_l(mDevice); } } @@ -2070,7 +2123,6 @@ bool AudioFlinger::DirectOutputThread::threadLoop() // hardware resources as soon as possible nsecs_t standbyDelay = microseconds(activeSleepTime*2); - while (!exitPending()) { bool rampVolume; @@ -2198,7 +2250,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() // there is one, the track is connected to it if (!effectChains.isEmpty()) { // Do not ramp volume is volume is controlled by effect - if(effectChains[0]->setVolume(&vl, &vr)) { + if(effectChains[0]->setVolume_l(&vl, &vr)) { rampVolume = false; } } @@ -2246,7 +2298,8 @@ bool AudioFlinger::DirectOutputThread::threadLoop() if (UNLIKELY(trackToRemove != 0)) { mActiveTracks.remove(trackToRemove); if (!effectChains.isEmpty()) { - LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(), trackToRemove->sessionId()); + LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(), + trackToRemove->sessionId()); effectChains[0]->stopTrack(); } if (trackToRemove->isTerminated()) { @@ -2255,7 +2308,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } } - lockEffectChains_l(); + lockEffectChains_l(effectChains); } if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { @@ -2301,7 +2354,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() for (size_t i = 0; i < effectChains.size(); i ++) { effectChains[i]->process_l(); } - unlockEffectChains(); + unlockEffectChains(effectChains); mLastWriteTime = systemTime(); mInWrite = true; @@ -2312,7 +2365,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() mInWrite = false; mStandby = false; } else { - unlockEffectChains(); + unlockEffectChains(effectChains); usleep(sleepTime); } @@ -2505,8 +2558,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() // prevent any changes in effect chain list and in each effect chain // during mixing and effect process as the audio buffers could be deleted // or modified if an effect is created or deleted - effectChains = mEffectChains; - lockEffectChains_l(); + lockEffectChains_l(effectChains); } if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { @@ -2547,7 +2599,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() effectChains[i]->process_l(); } // enable changes in effect chain - unlockEffectChains(); + unlockEffectChains(effectChains); standbyTime = systemTime() + kStandbyTimeInNsecs; for (size_t i = 0; i < outputTracks.size(); i++) { @@ -2557,7 +2609,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() mBytesWritten += mixBufferSize; } else { // enable changes in effect chain - unlockEffectChains(); + unlockEffectChains(effectChains); usleep(sleepTime); } @@ -2859,7 +2911,9 @@ void AudioFlinger::PlaybackThread::Track::destroy() if (thread != 0) { if (!isOutputTrack()) { if (mState == ACTIVE || mState == RESUMING) { - AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + AudioSystem::stopOutput(thread->id(), + (AudioSystem::stream_type)mStreamType, + mSessionId); } AudioSystem::releaseOutput(thread->id()); } @@ -2948,7 +3002,8 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const { status_t AudioFlinger::PlaybackThread::Track::start() { status_t status = NO_ERROR; - LOGV("start(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid()); + LOGV("start(%d), calling thread %d session %d", + mName, IPCThreadState::self()->getCallingPid(), mSessionId); sp<ThreadBase> thread = mThread.promote(); if (thread != 0) { Mutex::Autolock _l(thread->mLock); @@ -2965,7 +3020,9 @@ status_t AudioFlinger::PlaybackThread::Track::start() if (!isOutputTrack() && state != ACTIVE && state != RESUMING) { thread->mLock.unlock(); - status = AudioSystem::startOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + status = AudioSystem::startOutput(thread->id(), + (AudioSystem::stream_type)mStreamType, + mSessionId); thread->mLock.lock(); } if (status == NO_ERROR) { @@ -2998,7 +3055,9 @@ void AudioFlinger::PlaybackThread::Track::stop() } if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) { thread->mLock.unlock(); - AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + AudioSystem::stopOutput(thread->id(), + (AudioSystem::stream_type)mStreamType, + mSessionId); thread->mLock.lock(); } } @@ -3015,7 +3074,9 @@ void AudioFlinger::PlaybackThread::Track::pause() LOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get()); if (!isOutputTrack()) { thread->mLock.unlock(); - AudioSystem::stopOutput(thread->id(), (AudioSystem::stream_type)mStreamType); + AudioSystem::stopOutput(thread->id(), + (AudioSystem::stream_type)mStreamType, + mSessionId); thread->mLock.lock(); } } @@ -3584,7 +3645,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( } // If no audio session id is provided, create one here - if (sessionId != NULL && *sessionId != 0) { + if (sessionId != NULL && *sessionId != AudioSystem::SESSION_OUTPUT_MIX) { lSessionId = *sessionId; } else { lSessionId = nextUniqueId(); @@ -4415,8 +4476,8 @@ status_t AudioFlinger::setStreamOutput(uint32_t stream, int output) thread->type() != PlaybackThread::DIRECT) { MixerThread *srcThread = (MixerThread *)thread; srcThread->invalidateTracks(stream); - } } + } return NO_ERROR; } @@ -4471,12 +4532,26 @@ int AudioFlinger::nextUniqueId() status_t AudioFlinger::loadEffectLibrary(const char *libPath, int *handle) { + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + // only allow libraries loaded from /system/lib/soundfx for now + if (strncmp(gEffectLibPath, libPath, strlen(gEffectLibPath)) != 0) { + return PERMISSION_DENIED; + } + Mutex::Autolock _l(mLock); return EffectLoadLibrary(libPath, handle); } status_t AudioFlinger::unloadEffectLibrary(int handle) { + // check calling permissions + if (!settingsAllowed()) { + return PERMISSION_DENIED; + } + Mutex::Autolock _l(mLock); return EffectUnloadLibrary(handle); } @@ -4521,7 +4596,8 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, sp<Client> client; wp<Client> wclient; - LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d", pid, effectClient.get(), priority, sessionId, output); + LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d", + pid, effectClient.get(), priority, sessionId, output); if (pDesc == NULL) { lStatus = BAD_VALUE; @@ -4576,7 +4652,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, // an auxiliary version of this effect type is available found = true; memcpy(&d, &desc, sizeof(effect_descriptor_t)); - if (sessionId != 0 || + if (sessionId != AudioSystem::SESSION_OUTPUT_MIX || (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { break; } @@ -4589,22 +4665,23 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, } // For same effect type, chose auxiliary version over insert version if // connect to output mix (Compliance to OpenSL ES) - if (sessionId == 0 && + if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && (d.flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_AUXILIARY) { memcpy(&desc, &d, sizeof(effect_descriptor_t)); } } // Do not allow auxiliary effects on a session different from 0 (output mix) - if (sessionId != 0 && + if (sessionId != AudioSystem::SESSION_OUTPUT_MIX && (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { lStatus = INVALID_OPERATION; goto Exit; } - // Session -1 is reserved for output stage effects that can only be created - // by audio policy manager (running in same process) - if (sessionId == -1 && getpid() != IPCThreadState::self()->getCallingPid()) { + // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects + // that can only be created by audio policy manager (running in same process) + if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && + getpid() != IPCThreadState::self()->getCallingPid()) { lStatus = INVALID_OPERATION; goto Exit; } @@ -4616,13 +4693,14 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, // output threads. // TODO: allow attachment of effect to inputs if (output == 0) { - if (sessionId <= 0) { - // default to first output - // TODO: define criteria to choose output when not specified. Or - // receive output from audio policy manager - if (mPlaybackThreads.size() != 0) { - output = mPlaybackThreads.keyAt(0); - } + if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) { + // output must be specified by AudioPolicyManager when using session + // AudioSystem::SESSION_OUTPUT_STAGE + lStatus = BAD_VALUE; + goto Exit; + } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) { + output = AudioSystem::getOutputForEffect(&desc); + LOGV("createEffect() got output %d for effect %s", output, desc.name); } else { // look for the thread where the specified audio session is present for (size_t i = 0; i < mPlaybackThreads.size(); i++) { @@ -4635,7 +4713,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, } PlaybackThread *thread = checkPlaybackThread_l(output); if (thread == NULL) { - LOGE("unknown output thread"); + LOGE("createEffect() unknown output thread"); lStatus = BAD_VALUE; goto Exit; } @@ -4650,7 +4728,8 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, } // create effect on selected output trhead - handle = thread->createEffect_l(client, effectClient, priority, sessionId, &desc, enabled, &lStatus); + handle = thread->createEffect_l(client, effectClient, priority, sessionId, + &desc, enabled, &lStatus); if (handle != 0 && id != NULL) { *id = handle->id(); } @@ -4663,31 +4742,64 @@ Exit: return handle; } -status_t AudioFlinger::registerEffectResource_l(effect_descriptor_t *desc) { - if (mTotalEffectsCpuLoad + desc->cpuLoad > MAX_EFFECTS_CPU_LOAD) { - LOGW("registerEffectResource() CPU Load limit exceeded for Fx %s, CPU %f MIPS", - desc->name, (float)desc->cpuLoad/10); - return INVALID_OPERATION; +status_t AudioFlinger::moveEffects(int session, int srcOutput, int dstOutput) +{ + LOGV("moveEffects() session %d, srcOutput %d, dstOutput %d", + session, srcOutput, dstOutput); + Mutex::Autolock _l(mLock); + if (srcOutput == dstOutput) { + LOGW("moveEffects() same dst and src outputs %d", dstOutput); + return NO_ERROR; } - if (mTotalEffectsMemory + desc->memoryUsage > MAX_EFFECTS_MEMORY) { - LOGW("registerEffectResource() memory limit exceeded for Fx %s, Memory %d KB", - desc->name, desc->memoryUsage); - return INVALID_OPERATION; + PlaybackThread *srcThread = checkPlaybackThread_l(srcOutput); + if (srcThread == NULL) { + LOGW("moveEffects() bad srcOutput %d", srcOutput); + return BAD_VALUE; + } + PlaybackThread *dstThread = checkPlaybackThread_l(dstOutput); + if (dstThread == NULL) { + LOGW("moveEffects() bad dstOutput %d", dstOutput); + return BAD_VALUE; } - mTotalEffectsCpuLoad += desc->cpuLoad; - mTotalEffectsMemory += desc->memoryUsage; - LOGV("registerEffectResource_l() effect %s, CPU %d, memory %d", - desc->name, desc->cpuLoad, desc->memoryUsage); - LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory); + + Mutex::Autolock _dl(dstThread->mLock); + Mutex::Autolock _sl(srcThread->mLock); + moveEffectChain_l(session, srcThread, dstThread); + return NO_ERROR; } -void AudioFlinger::unregisterEffectResource_l(effect_descriptor_t *desc) { - mTotalEffectsCpuLoad -= desc->cpuLoad; - mTotalEffectsMemory -= desc->memoryUsage; - LOGV("unregisterEffectResource_l() effect %s, CPU %d, memory %d", - desc->name, desc->cpuLoad, desc->memoryUsage); - LOGV(" total CPU %d, total memory %d", mTotalEffectsCpuLoad, mTotalEffectsMemory); +// moveEffectChain_l mustbe called with both srcThread and dstThread mLocks held +status_t AudioFlinger::moveEffectChain_l(int session, + AudioFlinger::PlaybackThread *srcThread, + AudioFlinger::PlaybackThread *dstThread) +{ + LOGV("moveEffectChain_l() session %d from thread %p to thread %p", + session, srcThread, dstThread); + + sp<EffectChain> chain = srcThread->getEffectChain_l(session); + if (chain == 0) { + LOGW("moveEffectChain_l() effect chain for session %d not on source thread %p", + session, srcThread); + return INVALID_OPERATION; + } + + // remove chain first. This is usefull only if reconfiguring effect chain on same output thread, + // so that a new chain is created with correct parameters when first effect is added. This is + // otherwise unecessary as removeEffect_l() will remove the chain when last effect is + // removed. + srcThread->removeEffectChain_l(chain); + + // transfer all effects one by one so that new effect chain is created on new thread with + // correct buffer sizes and audio parameters and effect engines reconfigured accordingly + sp<EffectModule> effect = chain->getEffectFromId_l(0); + while (effect != 0) { + srcThread->removeEffect_l(effect); + dstThread->addEffect_l(effect); + effect = chain->getEffectFromId_l(0); + } + + return NO_ERROR; } // PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held @@ -4706,6 +4818,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l( status_t lStatus; sp<Track> track; sp<EffectChain> chain; + bool chainCreated = false; bool effectCreated = false; bool effectRegistered = false; @@ -4717,16 +4830,18 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l( // Do not allow auxiliary effect on session other than 0 if ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY && - sessionId != 0) { - LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", desc->name, sessionId); + sessionId != AudioSystem::SESSION_OUTPUT_MIX) { + LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", + desc->name, sessionId); lStatus = BAD_VALUE; goto Exit; } // Do not allow effects with session ID 0 on direct output or duplicating threads // TODO: add rule for hw accelerated effects on direct outputs with non PCM format - if (sessionId == 0 && mType != MIXER) { - LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", desc->name, sessionId); + if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && mType != MIXER) { + LOGW("createEffect_l() Cannot add auxiliary effect %s to session %d", + desc->name, sessionId); lStatus = BAD_VALUE; goto Exit; } @@ -4743,26 +4858,29 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l( LOGV("createEffect_l() new effect chain for session %d", sessionId); chain = new EffectChain(this, sessionId); addEffectChain_l(chain); + chain->setStrategy(getStrategyForSession_l(sessionId)); + chainCreated = true; } else { - effect = chain->getEffectFromDesc(desc); + effect = chain->getEffectFromDesc_l(desc); } LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get()); if (effect == 0) { + int id = mAudioFlinger->nextUniqueId(); // Check CPU and memory usage - lStatus = mAudioFlinger->registerEffectResource_l(desc); + lStatus = AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id); if (lStatus != NO_ERROR) { goto Exit; } effectRegistered = true; // create a new effect module if none present in the chain - effect = new EffectModule(this, chain, desc, mAudioFlinger->nextUniqueId(), sessionId); + effect = new EffectModule(this, chain, desc, id, sessionId); lStatus = effect->status(); if (lStatus != NO_ERROR) { goto Exit; } - lStatus = chain->addEffect(effect); + lStatus = chain->addEffect_l(effect); if (lStatus != NO_ERROR) { goto Exit; } @@ -4781,13 +4899,15 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l( Exit: if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) { + Mutex::Autolock _l(mLock); if (effectCreated) { - if (chain->removeEffect(effect) == 0) { - removeEffectChain_l(chain); - } + chain->removeEffect_l(effect); } if (effectRegistered) { - mAudioFlinger->unregisterEffectResource_l(desc); + AudioSystem::unregisterEffect(effect->id()); + } + if (chainCreated) { + removeEffectChain_l(chain); } handle.clear(); } @@ -4798,26 +4918,71 @@ Exit: return handle; } -void AudioFlinger::PlaybackThread::disconnectEffect(const sp< EffectModule>& effect, - const wp<EffectHandle>& handle) { +// PlaybackThread::addEffect_l() must be called with AudioFlinger::mLock and +// PlaybackThread::mLock held +status_t AudioFlinger::PlaybackThread::addEffect_l(const sp<EffectModule>& effect) +{ + // check for existing effect chain with the requested audio session + int sessionId = effect->sessionId(); + sp<EffectChain> chain = getEffectChain_l(sessionId); + bool chainCreated = false; + + if (chain == 0) { + // create a new chain for this session + LOGV("addEffect_l() new effect chain for session %d", sessionId); + chain = new EffectChain(this, sessionId); + addEffectChain_l(chain); + chain->setStrategy(getStrategyForSession_l(sessionId)); + chainCreated = true; + } + LOGV("addEffect_l() %p chain %p effect %p", this, chain.get(), effect.get()); + + if (chain->getEffectFromId_l(effect->id()) != 0) { + LOGW("addEffect_l() %p effect %s already present in chain %p", + this, effect->desc().name, chain.get()); + return BAD_VALUE; + } + + status_t status = chain->addEffect_l(effect); + if (status != NO_ERROR) { + if (chainCreated) { + removeEffectChain_l(chain); + } + return status; + } + + effect->setDevice(mDevice); + effect->setMode(mAudioFlinger->getMode()); + return NO_ERROR; +} + +void AudioFlinger::PlaybackThread::removeEffect_l(const sp<EffectModule>& effect) { + + LOGV("removeEffect_l() %p effect %p", this, effect.get()); effect_descriptor_t desc = effect->desc(); + if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + detachAuxEffect_l(effect->id()); + } + + sp<EffectChain> chain = effect->chain().promote(); + if (chain != 0) { + // remove effect chain if removing last effect + if (chain->removeEffect_l(effect) == 0) { + removeEffectChain_l(chain); + } + } else { + LOGW("removeEffect_l() %p cannot promote chain for effect %p", this, effect.get()); + } +} + +void AudioFlinger::PlaybackThread::disconnectEffect(const sp<EffectModule>& effect, + const wp<EffectHandle>& handle) { Mutex::Autolock _l(mLock); + LOGV("disconnectEffect() %p effect %p", this, effect.get()); // delete the effect module if removing last handle on it if (effect->removeHandle(handle) == 0) { - if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { - detachAuxEffect_l(effect->id()); - } - sp<EffectChain> chain = effect->chain().promote(); - if (chain != 0) { - // remove effect chain if remove last effect - if (chain->removeEffect(effect) == 0) { - removeEffectChain_l(chain); - } - } - mLock.unlock(); - mAudioFlinger->mLock.lock(); - mAudioFlinger->unregisterEffectResource_l(&desc); - mAudioFlinger->mLock.unlock(); + removeEffect_l(effect); + AudioSystem::unregisterEffect(effect->id()); } } @@ -4861,13 +5026,16 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& c chain->setInBuffer(buffer, ownsBuffer); chain->setOutBuffer(mMixBuffer); - // Effect chain for session -1 is inserted at end of effect chains list - // in order to be processed last as it contains output stage effects - // Effect chain for session 0 is inserted before session -1 to be processed + // Effect chain for session AudioSystem::SESSION_OUTPUT_STAGE is inserted at end of effect + // chains list in order to be processed last as it contains output stage effects + // Effect chain for session AudioSystem::SESSION_OUTPUT_MIX is inserted before + // session AudioSystem::SESSION_OUTPUT_STAGE to be processed // after track specific effects and before output stage - // Effect chain for session other than 0 is inserted at beginning of effect - // chains list to be processed before output mix effects. Relative order between - // sessions other than 0 is not important + // It is therefore mandatory that AudioSystem::SESSION_OUTPUT_MIX == 0 and + // that AudioSystem::SESSION_OUTPUT_STAGE < AudioSystem::SESSION_OUTPUT_MIX + // Effect chain for other sessions are inserted at beginning of effect + // chains list to be processed before output mix effects. Relative order between other + // sessions is not important size_t size = mEffectChains.size(); size_t i = 0; for (i = 0; i < size; i++) { @@ -4894,52 +5062,58 @@ size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& track->setMainBuffer(mMixBuffer); } } + break; } } return mEffectChains.size(); } -void AudioFlinger::PlaybackThread::lockEffectChains_l() +void AudioFlinger::PlaybackThread::lockEffectChains_l( + Vector<sp <AudioFlinger::EffectChain> >& effectChains) { + effectChains = mEffectChains; for (size_t i = 0; i < mEffectChains.size(); i++) { mEffectChains[i]->lock(); } } -void AudioFlinger::PlaybackThread::unlockEffectChains() +void AudioFlinger::PlaybackThread::unlockEffectChains( + Vector<sp <AudioFlinger::EffectChain> >& effectChains) { - Mutex::Autolock _l(mLock); - for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->unlock(); + for (size_t i = 0; i < effectChains.size(); i++) { + effectChains[i]->unlock(); } } + sp<AudioFlinger::EffectModule> AudioFlinger::PlaybackThread::getEffect_l(int sessionId, int effectId) { sp<EffectModule> effect; sp<EffectChain> chain = getEffectChain_l(sessionId); if (chain != 0) { - effect = chain->getEffectFromId(effectId); + effect = chain->getEffectFromId_l(effectId); } return effect; } -status_t AudioFlinger::PlaybackThread::attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId) +status_t AudioFlinger::PlaybackThread::attachAuxEffect( + const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId) { Mutex::Autolock _l(mLock); return attachAuxEffect_l(track, EffectId); } -status_t AudioFlinger::PlaybackThread::attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId) +status_t AudioFlinger::PlaybackThread::attachAuxEffect_l( + const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId) { status_t status = NO_ERROR; if (EffectId == 0) { track->setAuxBuffer(0, NULL); } else { - // Auxiliary effects are always in audio session 0 - sp<EffectModule> effect = getEffect_l(0, EffectId); + // Auxiliary effects are always in audio session AudioSystem::SESSION_OUTPUT_MIX + sp<EffectModule> effect = getEffect_l(AudioSystem::SESSION_OUTPUT_MIX, EffectId); if (effect != 0) { if ((effect->desc().flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { track->setAuxBuffer(EffectId, (int32_t *)effect->inBuffer()); @@ -5135,7 +5309,7 @@ void AudioFlinger::EffectModule::process() if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32, mConfig.inputCfg.buffer.s32, - mConfig.inputCfg.buffer.frameCount); + mConfig.inputCfg.buffer.frameCount/2); } // do the actual processing in the effect engine @@ -5212,7 +5386,8 @@ status_t AudioFlinger::EffectModule::configure() mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; // Insert effect: - // - in session 0 or -1, always overwrites output buffer: input buffer == output buffer + // - in session AudioSystem::SESSION_OUTPUT_MIX or AudioSystem::SESSION_OUTPUT_STAGE, + // always overwrites output buffer: input buffer == output buffer // - in other sessions: // last effect in the chain accumulates in output buffer: input buffer != output buffer // other effect: overwrites output buffer: input buffer == output buffer @@ -5229,6 +5404,9 @@ status_t AudioFlinger::EffectModule::configure() mConfig.inputCfg.buffer.frameCount = thread->frameCount(); mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount; + LOGV("configure() %p thread %p buffer %p framecount %d", + this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount); + status_t cmdStatus; int size = sizeof(int); status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_CONFIGURE, sizeof(effect_config_t), &mConfig, &size, &cmdStatus); @@ -5365,7 +5543,9 @@ status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, // Send volume indication if EFFECT_FLAG_VOLUME_IND is set and read back altered volume // if controller flag is set (Note that controller == TRUE => EFFECT_FLAG_VOLUME_CTRL set) - if ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) & (EFFECT_FLAG_VOLUME_CTRL|EFFECT_FLAG_VOLUME_IND)) { + if ((mState >= ACTIVE) && + ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL || + (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) { status_t cmdStatus; uint32_t volume[2]; uint32_t *pVolume = NULL; @@ -5745,9 +5925,11 @@ void AudioFlinger::EffectHandle::dump(char* buffer, size_t size) AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread, int sessionId) - : mThread(wThread), mSessionId(sessionId), mVolumeCtrlIdx(-1), mActiveTrackCnt(0), mOwnInBuffer(false) + : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mOwnInBuffer(false), + mVolumeCtrlIdx(-1), mLeftVolume(0), mRightVolume(0), + mNewLeftVolume(0), mNewRightVolume(0) { - + mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC); } AudioFlinger::EffectChain::~EffectChain() @@ -5758,7 +5940,8 @@ AudioFlinger::EffectChain::~EffectChain() } -sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc(effect_descriptor_t *descriptor) +// getEffectFromDesc_l() must be called with PlaybackThread::mLock held +sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc_l(effect_descriptor_t *descriptor) { sp<EffectModule> effect; size_t size = mEffects.size(); @@ -5772,13 +5955,15 @@ sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc(effe return effect; } -sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId(int id) +// getEffectFromId_l() must be called with PlaybackThread::mLock held +sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId_l(int id) { sp<EffectModule> effect; size_t size = mEffects.size(); for (size_t i = 0; i < size; i++) { - if (mEffects[i]->id() == id) { + // by convention, return first effect if id provided is 0 (0 is never a valid id) + if (id == 0 || mEffects[i]->id() == id) { effect = mEffects[i]; break; } @@ -5807,21 +5992,25 @@ void AudioFlinger::EffectChain::process_l() } } -status_t AudioFlinger::EffectChain::addEffect(sp<EffectModule>& effect) +// addEffect_l() must be called with PlaybackThread::mLock held +status_t AudioFlinger::EffectChain::addEffect_l(const sp<EffectModule>& effect) { effect_descriptor_t desc = effect->desc(); uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK; Mutex::Autolock _l(mLock); + 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 // they are processed first and accumulated in chain input buffer mEffects.insertAt(effect, 0); - sp<ThreadBase> thread = mThread.promote(); - if (thread == 0) { - return NO_INIT; - } + // the input buffer for auxiliary effect contains mono samples in // 32 bit format. This is to avoid saturation in AudoMixer // accumulation stage. Saturation is done in EffectModule::process() before @@ -5860,7 +6049,7 @@ status_t AudioFlinger::EffectChain::addEffect(sp<EffectModule>& effect) // check invalid effect chaining combinations if (insertPref == EFFECT_FLAG_INSERT_EXCLUSIVE || iPref == EFFECT_FLAG_INSERT_EXCLUSIVE) { - LOGW("addEffect() could not insert effect %s: exclusive conflict with %s", desc.name, d.name); + LOGW("addEffect_l() could not insert effect %s: exclusive conflict with %s", desc.name, d.name); return INVALID_OPERATION; } // remember position of first insert effect and by default @@ -5910,22 +6099,17 @@ status_t AudioFlinger::EffectChain::addEffect(sp<EffectModule>& effect) effect->setOutBuffer(mInBuffer); } mEffects.insertAt(effect, idx_insert); - // Always give volume control to last effect in chain with volume control capability - if (((desc.flags & EFFECT_FLAG_VOLUME_MASK) & EFFECT_FLAG_VOLUME_CTRL) && - mVolumeCtrlIdx < idx_insert) { - mVolumeCtrlIdx = idx_insert; - } - LOGV("addEffect() effect %p, added in chain %p at rank %d", effect.get(), this, idx_insert); + LOGV("addEffect_l() effect %p, added in chain %p at rank %d", effect.get(), this, idx_insert); } effect->configure(); return NO_ERROR; } -size_t AudioFlinger::EffectChain::removeEffect(const sp<EffectModule>& effect) +// removeEffect_l() must be called with PlaybackThread::mLock held +size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect) { Mutex::Autolock _l(mLock); - int size = (int)mEffects.size(); int i; uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK; @@ -5941,26 +6125,16 @@ size_t AudioFlinger::EffectChain::removeEffect(const sp<EffectModule>& effect) } } mEffects.removeAt(i); - LOGV("removeEffect() effect %p, removed from chain %p at rank %d", effect.get(), this, i); + LOGV("removeEffect_l() effect %p, removed from chain %p at rank %d", effect.get(), this, i); break; } } - // Return volume control to last effect in chain with volume control capability - if (mVolumeCtrlIdx == i) { - size = (int)mEffects.size(); - for (i = size; i > 0; i--) { - if ((mEffects[i - 1]->desc().flags & EFFECT_FLAG_VOLUME_MASK) & EFFECT_FLAG_VOLUME_CTRL) { - break; - } - } - // mVolumeCtrlIdx reset to -1 if no effect found with volume control flag set - mVolumeCtrlIdx = i - 1; - } return mEffects.size(); } -void AudioFlinger::EffectChain::setDevice(uint32_t device) +// setDevice_l() must be called with PlaybackThread::mLock held +void AudioFlinger::EffectChain::setDevice_l(uint32_t device) { size_t size = mEffects.size(); for (size_t i = 0; i < size; i++) { @@ -5968,7 +6142,8 @@ void AudioFlinger::EffectChain::setDevice(uint32_t device) } } -void AudioFlinger::EffectChain::setMode(uint32_t mode) +// setMode_l() must be called with PlaybackThread::mLock held +void AudioFlinger::EffectChain::setMode_l(uint32_t mode) { size_t size = mEffects.size(); for (size_t i = 0; i < size; i++) { @@ -5976,27 +6151,56 @@ void AudioFlinger::EffectChain::setMode(uint32_t mode) } } -bool AudioFlinger::EffectChain::setVolume(uint32_t *left, uint32_t *right) +// setVolume_l() must be called with PlaybackThread::mLock held +bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right) { uint32_t newLeft = *left; uint32_t newRight = *right; bool hasControl = false; + int ctrlIdx = -1; + size_t size = mEffects.size(); + + // first update volume controller + for (size_t i = size; i > 0; i--) { + if ((mEffects[i - 1]->state() >= EffectModule::ACTIVE) && + (mEffects[i - 1]->desc().flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL) { + ctrlIdx = i - 1; + hasControl = true; + break; + } + } - // first get volume update from volume controller - if (mVolumeCtrlIdx >= 0) { - mEffects[mVolumeCtrlIdx]->setVolume(&newLeft, &newRight, true); + if (ctrlIdx == mVolumeCtrlIdx && *left == mLeftVolume && *right == mRightVolume) { + if (hasControl) { + *left = mNewLeftVolume; + *right = mNewRightVolume; + } + return hasControl; + } + + if (mVolumeCtrlIdx != -1) { hasControl = true; } + mVolumeCtrlIdx = ctrlIdx; + mLeftVolume = newLeft; + mRightVolume = newRight; + + // second get volume update from volume controller + if (ctrlIdx >= 0) { + mEffects[ctrlIdx]->setVolume(&newLeft, &newRight, true); + mNewLeftVolume = newLeft; + mNewRightVolume = newRight; + } // then indicate volume to all other effects in chain. // Pass altered volume to effects before volume controller // and requested volume to effects after controller uint32_t lVol = newLeft; uint32_t rVol = newRight; - size_t size = mEffects.size(); + for (size_t i = 0; i < size; i++) { - if ((int)i == mVolumeCtrlIdx) continue; - // this also works for mVolumeCtrlIdx == -1 when there is no volume controller - if ((int)i > mVolumeCtrlIdx) { + if ((int)i == ctrlIdx) continue; + // this also works for ctrlIdx == -1 when there is no volume controller + if ((int)i > ctrlIdx) { lVol = *left; rVol = *right; } @@ -6008,16 +6212,6 @@ bool AudioFlinger::EffectChain::setVolume(uint32_t *left, uint32_t *right) return hasControl; } -sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getVolumeController() -{ - sp<EffectModule> effect; - if (mVolumeCtrlIdx >= 0) { - effect = mEffects[mVolumeCtrlIdx]; - } - return effect; -} - - status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args) { const size_t SIZE = 256; @@ -6033,12 +6227,11 @@ status_t AudioFlinger::EffectChain::dump(int fd, const Vector<String16>& args) result.append("\tCould not lock mutex:\n"); } - result.append("\tNum fx In buffer Out buffer Vol ctrl Active tracks:\n"); - snprintf(buffer, SIZE, "\t%02d 0x%08x 0x%08x %02d %d\n", + result.append("\tNum fx In buffer Out buffer Active tracks:\n"); + snprintf(buffer, SIZE, "\t%02d 0x%08x 0x%08x %d\n", mEffects.size(), (uint32_t)mInBuffer, (uint32_t)mOutBuffer, - (mVolumeCtrlIdx == -1) ? 0 : mEffects[mVolumeCtrlIdx]->id(), mActiveTrackCnt); result.append(buffer); write(fd, result.string(), result.size()); |