/* ** ** Copyright 2012, 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. */ #ifndef INCLUDING_FROM_AUDIOFLINGER_H #error This header file should only be included from AudioFlinger.h #endif //--- Audio Effect Management // 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 // 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 // methods that in turn call AudioFlinger thus locking the same mutexes in the reverse order. // The EffectModule class is a wrapper object controlling the effect engine implementation // in the effect library. It prevents concurrent calls to process() and command() functions // from different client threads. It keeps a list of EffectHandle objects corresponding // to all client applications using this effect and notifies applications of effect state, // control or parameter changes. It manages the activation state machine to send appropriate // reset, enable, disable commands to effect engine and provide volume // ramping when effects are activated/deactivated. // When controlling an auxiliary effect, the EffectModule also provides an input buffer used by // the attached track(s) to accumulate their auxiliary channel. class EffectModule : public RefBase { public: EffectModule(ThreadBase *thread, const wp<AudioFlinger::EffectChain>& chain, effect_descriptor_t *desc, int id, audio_session_t sessionId, bool pinned); virtual ~EffectModule(); enum effect_state { IDLE, RESTART, STARTING, ACTIVE, STOPPING, STOPPED, DESTROYED }; int id() const { return mId; } void process(); bool updateState(); status_t command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t *replySize, void *pReplyData); void reset_l(); status_t configure(); status_t init(); effect_state state() const { return mState; } uint32_t status() { return mStatus; } audio_session_t sessionId() const { return mSessionId; } status_t setEnabled(bool enabled); status_t setEnabled_l(bool enabled); bool isEnabled() const; bool isProcessEnabled() const; void setInBuffer(const sp<EffectBufferHalInterface>& buffer); int16_t *inBuffer() const { return mInBuffer != 0 ? reinterpret_cast<int16_t*>(mInBuffer->ptr()) : NULL; } void setOutBuffer(const sp<EffectBufferHalInterface>& buffer); int16_t *outBuffer() const { return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL; } void setChain(const wp<EffectChain>& chain) { mChain = chain; } void setThread(const wp<ThreadBase>& thread) { mThread = thread; } const wp<ThreadBase>& thread() { return mThread; } status_t addHandle(EffectHandle *handle); ssize_t disconnectHandle(EffectHandle *handle, bool unpinIfLast); ssize_t removeHandle(EffectHandle *handle); ssize_t removeHandle_l(EffectHandle *handle); const effect_descriptor_t& desc() const { return mDescriptor; } wp<EffectChain>& chain() { return mChain; } status_t setDevice(audio_devices_t device); status_t setVolume(uint32_t *left, uint32_t *right, bool controller); status_t setMode(audio_mode_t mode); status_t setAudioSource(audio_source_t source); status_t start(); status_t stop(); void setSuspended(bool suspended); bool suspended() const; EffectHandle* controlHandle_l(); bool isPinned() const { return mPinned; } void unPin() { mPinned = false; } bool purgeHandles(); void lock() { mLock.lock(); } void unlock() { mLock.unlock(); } bool isOffloadable() const { return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; } bool isImplementationSoftware() const { return (mDescriptor.flags & EFFECT_FLAG_HW_ACC_MASK) == 0; } bool isProcessImplemented() const { return (mDescriptor.flags & EFFECT_FLAG_NO_PROCESS) == 0; } status_t setOffloaded(bool offloaded, audio_io_handle_t io); bool isOffloaded() const; void addEffectToHal_l(); void release_l(); void dump(int fd, const Vector<String16>& args); protected: friend class AudioFlinger; // for mHandles bool mPinned; // Maximum time allocated to effect engines to complete the turn off sequence static const uint32_t MAX_DISABLE_TIME_MS = 10000; EffectModule(const EffectModule&); EffectModule& operator = (const EffectModule&); status_t start_l(); status_t stop_l(); status_t remove_effect_from_hal_l(); mutable Mutex mLock; // mutex for process, commands and handles list protection wp<ThreadBase> mThread; // parent thread 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 effect_config_t mConfig; // input and output audio configuration sp<EffectHalInterface> mEffectInterface; // Effect module HAL sp<EffectBufferHalInterface> mInBuffer; // Buffers for interacting with HAL sp<EffectBufferHalInterface> mOutBuffer; status_t mStatus; // initialization status effect_state mState; // current activation state Vector<EffectHandle *> mHandles; // list of client handles // First handle in mHandles has highest priority and controls the effect module uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after // sending disable command. 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; }; // The EffectHandle class implements the IEffect interface. It provides resources // to receive parameter updates, keeps track of effect control // ownership and state and has a pointer to the EffectModule object it is controlling. // There is one EffectHandle object for each application controlling (or using) // an effect module. // The EffectHandle is obtained by calling AudioFlinger::createEffect(). class EffectHandle: public android::BnEffect { public: EffectHandle(const sp<EffectModule>& effect, const sp<AudioFlinger::Client>& client, const sp<IEffectClient>& effectClient, int32_t priority); virtual ~EffectHandle(); virtual status_t initCheck(); // IEffect virtual status_t enable(); virtual status_t disable(); virtual status_t command(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t *replySize, void *pReplyData); virtual void disconnect(); private: void disconnect(bool unpinIfLast); public: virtual sp<IMemory> getCblk() const { return mCblkMemory; } virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); // Give or take control of effect module // - hasControl: true if control is given, false if removed // - signal: true client app should be signaled of change, false otherwise // - enabled: state of the effect when control is passed void setControl(bool hasControl, bool signal, bool enabled); void commandExecuted(uint32_t cmdCode, uint32_t cmdSize, void *pCmdData, uint32_t replySize, void *pReplyData); void setEnabled(bool enabled); bool enabled() const { return mEnabled; } // Getters wp<EffectModule> effect() const { return mEffect; } int id() const { sp<EffectModule> effect = mEffect.promote(); if (effect == 0) { return 0; } return effect->id(); } int priority() const { return mPriority; } bool hasControl() const { return mHasControl; } bool disconnected() const { return mDisconnected; } void dumpToBuffer(char* buffer, size_t size); protected: friend class AudioFlinger; // for mEffect, mHasControl, mEnabled EffectHandle(const EffectHandle&); EffectHandle& operator =(const EffectHandle&); Mutex mLock; // protects IEffect method calls wp<EffectModule> mEffect; // pointer to controlled EffectModule sp<IEffectClient> mEffectClient; // callback interface for client notifications /*const*/ sp<Client> mClient; // client for shared memory allocation, see disconnect() sp<IMemory> mCblkMemory; // shared memory for control block effect_param_cblk_t* mCblk; // control block for deferred parameter setting via // shared memory uint8_t* mBuffer; // pointer to parameter area in shared memory int mPriority; // client application priority to control the effect bool mHasControl; // true if this handle is controlling the effect bool mEnabled; // cached enable state: needed when the effect is // restored after being suspended bool mDisconnected; // Set to true by disconnect() }; // the EffectChain class represents a group of effects associated to one audio session. // There can be any number of EffectChain objects per output mixer thread (PlaybackThread). // The EffectChain with session ID AUDIO_SESSION_OUTPUT_MIX contains global effects applied // to the output mix. // Effects in this chain can be insert or auxiliary. Effects in other chains (attached to // tracks) are insert only. The EffectChain maintains an ordered list of effect module, the // order corresponding in the effect process order. When attached to a track (session ID != // AUDIO_SESSION_OUTPUT_MIX), // it also provide it's own input buffer used by the track as accumulation buffer. class EffectChain : public RefBase { public: EffectChain(const wp<ThreadBase>& wThread, audio_session_t sessionId); EffectChain(ThreadBase *thread, audio_session_t sessionId); virtual ~EffectChain(); // special key used for an entry in mSuspendedEffects keyed vector // corresponding to a suspend all request. static const int kKeyForSuspendAll = 0; // minimum duration during which we force calling effect process when last track on // a session is stopped or removed to allow effect tail to be rendered static const int kProcessTailDurationMs = 1000; void process_l(); void lock() { mLock.lock(); } void unlock() { mLock.unlock(); } status_t createEffect_l(sp<EffectModule>& effect, ThreadBase *thread, effect_descriptor_t *desc, int id, audio_session_t sessionId, bool pinned); status_t addEffect_l(const sp<EffectModule>& handle); status_t addEffect_ll(const sp<EffectModule>& handle); size_t removeEffect_l(const sp<EffectModule>& handle, bool release = false); audio_session_t sessionId() const { return mSessionId; } void setSessionId(audio_session_t sessionId) { mSessionId = sessionId; } sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor); sp<EffectModule> getEffectFromId_l(int id); sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type); // FIXME use float to improve the dynamic range bool setVolume_l(uint32_t *left, uint32_t *right, bool force = false); void resetVolume_l(); void setDevice_l(audio_devices_t device); void setMode_l(audio_mode_t mode); void setAudioSource_l(audio_source_t source); void setInBuffer(const sp<EffectBufferHalInterface>& buffer) { mInBuffer = buffer; } int16_t *inBuffer() const { return mInBuffer != 0 ? reinterpret_cast<int16_t*>(mInBuffer->ptr()) : NULL; } void setOutBuffer(const sp<EffectBufferHalInterface>& buffer) { mOutBuffer = buffer; } int16_t *outBuffer() const { return mOutBuffer != 0 ? reinterpret_cast<int16_t*>(mOutBuffer->ptr()) : NULL; } void incTrackCnt() { android_atomic_inc(&mTrackCnt); } void decTrackCnt() { android_atomic_dec(&mTrackCnt); } int32_t trackCnt() const { return android_atomic_acquire_load(&mTrackCnt); } void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt); mTailBufferCount = mMaxTailBuffers; } void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); } int32_t activeTrackCnt() const { return android_atomic_acquire_load(&mActiveTrackCnt); } uint32_t strategy() const { return mStrategy; } void setStrategy(uint32_t strategy) { mStrategy = strategy; } // suspend effect of the given type void setEffectSuspended_l(const effect_uuid_t *type, bool suspend); // suspend all eligible effects void setEffectSuspendedAll_l(bool suspend); // 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(); // At least one non offloadable effect in the chain is enabled bool isNonOffloadableEnabled(); void syncHalEffectsState(); // flags is an ORed set of audio_output_flags_t which is updated on return. void checkOutputFlagCompatibility(audio_output_flags_t *flags) const; // flags is an ORed set of audio_input_flags_t which is updated on return. void checkInputFlagCompatibility(audio_input_flags_t *flags) const; // Is this EffectChain compatible with the RAW audio flag. bool isRawCompatible() const; // Is this EffectChain compatible with the FAST audio flag. bool isFastCompatible() const; // isCompatibleWithThread_l() must be called with thread->mLock held bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const; void dump(int fd, const Vector<String16>& args); protected: friend class AudioFlinger; // for mThread, mEffects EffectChain(const EffectChain&); EffectChain& operator =(const EffectChain&); class SuspendedEffectDesc : public RefBase { public: SuspendedEffectDesc() : mRefCount(0) {} int mRefCount; effect_uuid_t mType; wp<EffectModule> mEffect; }; // get a list of effect modules to suspend when an effect of the type // passed is enabled. void getSuspendEligibleEffects(Vector< sp<EffectModule> > &effects); // get an effect module if it is currently enable sp<EffectModule> getEffectIfEnabled(const effect_uuid_t *type); // true if the effect whose descriptor is passed can be suspended // OEMs can modify the rules implemented in this method to exclude specific effect // types or implementations from the suspend/restore mechanism. bool isEffectEligibleForSuspend(const effect_descriptor_t& desc); void clearInputBuffer_l(const sp<ThreadBase>& thread); void setThread(const sp<ThreadBase>& thread); 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 sp<EffectBufferHalInterface> mInBuffer; // chain input buffer sp<EffectBufferHalInterface> mOutBuffer; // chain output buffer // 'volatile' here means these are accessed with atomic operations instead of mutex volatile int32_t mActiveTrackCnt; // number of active tracks connected volatile int32_t mTrackCnt; // number of tracks connected int32_t mTailBufferCount; // current effect tail buffer count int32_t mMaxTailBuffers; // maximum effect tail buffers int mVolumeCtrlIdx; // index of insert effect having control over volume uint32_t mLeftVolume; // previous volume on left channel uint32_t mRightVolume; // previous volume on right channel uint32_t mNewLeftVolume; // new volume on left channel uint32_t mNewRightVolume; // new volume on right channel uint32_t mStrategy; // strategy for this effect chain // mSuspendedEffects lists all effects currently suspended in the chain. // Use effect type UUID timelow field as key. There is no real risk of identical // timeLow fields among effect type UUIDs. // Updated by updateSuspendedSessions_l() only. KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects; };