diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/audioflinger/Android.mk | 3 | ||||
-rw-r--r-- | libs/audioflinger/AudioFlinger.cpp | 2074 | ||||
-rw-r--r-- | libs/audioflinger/AudioFlinger.h | 305 | ||||
-rw-r--r-- | libs/audioflinger/AudioMixer.cpp | 694 | ||||
-rw-r--r-- | libs/audioflinger/AudioMixer.h | 50 |
5 files changed, 2796 insertions, 330 deletions
diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk index 870c0b8..22ecc54 100644 --- a/libs/audioflinger/Android.mk +++ b/libs/audioflinger/Android.mk @@ -87,7 +87,8 @@ LOCAL_SHARED_LIBRARIES := \ libutils \ libbinder \ libmedia \ - libhardware_legacy + libhardware_legacy \ + libeffects ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true) LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 3b38d83..1860793 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -37,7 +37,7 @@ #include <media/AudioRecord.h> #include <private/media/AudioTrackShared.h> - +#include <private/media/AudioEffectShared.h> #include <hardware_legacy/AudioHardwareInterface.h> #include "AudioMixer.h" @@ -51,6 +51,8 @@ #include "lifevibes.h" #endif +#include <media/EffectFactoryApi.h> + // ---------------------------------------------------------------------------- // the sim build doesn't have gettid @@ -67,6 +69,7 @@ static const char* kHardwareLockedString = "Hardware lock is taken\n"; //static const nsecs_t kStandbyTimeInNsecs = seconds(3); static const float MAX_GAIN = 4096.0f; +static const float MAX_GAIN_INT = 0x1000; // retry counts for buffer fill timeout // 50 * ~20msecs = 1 second @@ -123,7 +126,7 @@ static bool settingsAllowed() { AudioFlinger::AudioFlinger() : BnAudioFlinger(), - mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextThreadId(0) + mAudioHardware(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1) { mHardwareStatus = AUDIO_HW_IDLE; @@ -282,6 +285,7 @@ sp<IAudioTrack> AudioFlinger::createTrack( uint32_t flags, const sp<IMemory>& sharedBuffer, int output, + int *sessionId, status_t *status) { sp<PlaybackThread::Track> track; @@ -289,6 +293,7 @@ sp<IAudioTrack> AudioFlinger::createTrack( sp<Client> client; wp<Client> wclient; status_t lStatus; + int lSessionId; if (streamType >= AudioSystem::NUM_STREAM_TYPES) { LOGE("invalid stream type"); @@ -313,8 +318,23 @@ sp<IAudioTrack> AudioFlinger::createTrack( client = new Client(this, pid); 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) { + lSessionId = *sessionId; + } else { + lSessionId = nextUniqueId(); + if (sessionId != NULL) { + *sessionId = lSessionId; + } + } + LOGV("createTrack() lSessionId: %d", lSessionId); + track = thread->createTrack_l(client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer, &lStatus); + channelCount, frameCount, sharedBuffer, lSessionId, &lStatus); } if (lStatus == NO_ERROR) { trackHandle = new TrackHandle(track); @@ -940,10 +960,11 @@ status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args // ---------------------------------------------------------------------------- -AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) +AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device) : ThreadBase(audioFlinger, id), mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output), - mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false) + mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false), + mDevice(device) { readOutputParameters(); @@ -965,6 +986,7 @@ status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args { dumpInternals(fd, args); dumpTracks(fd, args); + dumpEffectChains(fd, args); return NO_ERROR; } @@ -976,7 +998,7 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16> snprintf(buffer, SIZE, "Output thread %p tracks\n", this); result.append(buffer); - result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + result.append(" Name Clien Typ Fmt Chn Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n"); for (size_t i = 0; i < mTracks.size(); ++i) { sp<Track> track = mTracks[i]; if (track != 0) { @@ -987,7 +1009,7 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16> snprintf(buffer, SIZE, "Output thread %p active tracks\n", this); result.append(buffer); - result.append(" Name Clien Typ Fmt Chn Buf S M F SRate LeftV RighV Serv User\n"); + result.append(" Name Clien Typ Fmt Chn Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n"); for (size_t i = 0; i < mActiveTracks.size(); ++i) { wp<Track> wTrack = mActiveTracks[i]; if (wTrack != 0) { @@ -1002,6 +1024,24 @@ status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16> return NO_ERROR; } +status_t AudioFlinger::PlaybackThread::dumpEffectChains(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "\n- %d Effect Chains:\n", mEffectChains.size()); + write(fd, buffer, strlen(buffer)); + + for (size_t i = 0; i < mEffectChains.size(); ++i) { + sp<EffectChain> chain = mEffectChains[i]; + if (chain != 0) { + chain->dump(fd, args); + } + } + return NO_ERROR; +} + status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { const size_t SIZE = 256; @@ -1020,6 +1060,8 @@ status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String result.append(buffer); snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended); result.append(buffer); + snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer); + result.append(buffer); write(fd, result.string(), result.size()); dumpBase(fd, args); @@ -1057,6 +1099,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra int channelCount, int frameCount, const sp<IMemory>& sharedBuffer, + int sessionId, status_t *status) { sp<Track> track; @@ -1087,12 +1130,18 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra { // scope for mLock Mutex::Autolock _l(mLock); track = new Track(this, client, streamType, sampleRate, format, - channelCount, frameCount, sharedBuffer); + channelCount, frameCount, sharedBuffer, sessionId); if (track->getCblk() == NULL || track->name() < 0) { lStatus = NO_MEMORY; goto Exit; } mTracks.add(track); + + sp<EffectChain> chain = getEffectChain_l(sessionId); + if (chain != 0) { + LOGV("createTrack_l() setting main buffer %p", chain->inBuffer()); + track->setMainBuffer(chain->inBuffer()); + } } lStatus = NO_ERROR; @@ -1209,6 +1258,14 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) track->mFillingUpStatus = Track::FS_FILLING; track->mResetDone = false; mActiveTracks.add(track); + if (track->mainBuffer() != mMixBuffer) { + sp<EffectChain> chain = getEffectChain_l(track->sessionId()); + if (chain != 0) { + LOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId()); + chain->startTrack(); + } + } + status = NO_ERROR; } @@ -1271,9 +1328,11 @@ void AudioFlinger::PlaybackThread::readOutputParameters() // FIXME - Current mixer implementation only supports stereo output: Always // Allocate a stereo buffer even if HW output is mono. - if (mMixBuffer != NULL) delete mMixBuffer; + if (mMixBuffer != NULL) delete[] mMixBuffer; mMixBuffer = new int16_t[mFrameCount * 2]; memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); + + //TODO handle effects reconfig } status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames) @@ -1289,10 +1348,47 @@ status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, ui return mOutput->getRenderPosition(dspFrames); } +bool AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) +{ + Mutex::Autolock _l(mLock); + if (getEffectChain_l(sessionId) != 0) { + return true; + } + + for (size_t i = 0; i < mTracks.size(); ++i) { + sp<Track> track = mTracks[i]; + if (sessionId == track->sessionId()) { + return true; + } + } + + return false; +} + +sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId) +{ + Mutex::Autolock _l(mLock); + return getEffectChain_l(sessionId); +} + +sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain_l(int sessionId) +{ + sp<EffectChain> chain; + + size_t size = mEffectChains.size(); + for (size_t i = 0; i < size; i++) { + if (mEffectChains[i]->sessionId() == sessionId) { + chain = mEffectChains[i]; + break; + } + } + return chain; +} + // ---------------------------------------------------------------------------- -AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) - : PlaybackThread(audioFlinger, output, id), +AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device) + : PlaybackThread(audioFlinger, output, id, device), mAudioMixer(0) { mType = PlaybackThread::MIXER; @@ -1311,7 +1407,6 @@ AudioFlinger::MixerThread::~MixerThread() bool AudioFlinger::MixerThread::threadLoop() { - int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; uint32_t mixerStatus = MIXER_IDLE; nsecs_t standbyTime = systemTime(); @@ -1324,6 +1419,7 @@ bool AudioFlinger::MixerThread::threadLoop() uint32_t activeSleepTime = activeSleepTimeUs(); uint32_t idleSleepTime = idleSleepTimeUs(); uint32_t sleepTime = idleSleepTime; + Vector< sp<EffectChain> > effectChains; while (!exitPending()) { @@ -1382,13 +1478,20 @@ bool AudioFlinger::MixerThread::threadLoop() } mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); + + // 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(); } if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { // mix buffers... - mAudioMixer->process(curBuf); + mAudioMixer->process(); sleepTime = 0; standbyTime = systemTime() + kStandbyTimeInNsecs; + //TODO: delay standby when effects have a tail } else { // If no tracks are ready, sleep once for the duration of an output // buffer size, then write 0s to the output @@ -1400,10 +1503,11 @@ bool AudioFlinger::MixerThread::threadLoop() } } else if (mBytesWritten != 0 || (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) { - memset (curBuf, 0, mixBufferSize); + memset (mMixBuffer, 0, mixBufferSize); sleepTime = 0; LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start"); } + // TODO add standby time extension fct of effect tail } if (mSuspended) { @@ -1411,16 +1515,22 @@ bool AudioFlinger::MixerThread::threadLoop() } // sleepTime == 0 means we must write to audio hardware if (sleepTime == 0) { - mLastWriteTime = systemTime(); - mInWrite = true; - mBytesWritten += mixBufferSize; + for (size_t i = 0; i < effectChains.size(); i ++) { + effectChains[i]->process_l(); + } + // enable changes in effect chain + unlockEffectChains(); #ifdef LVMX int audioOutputType = LifeVibes::getMixerType(mId, mType); if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { - LifeVibes::process(audioOutputType, curBuf, mixBufferSize); + LifeVibes::process(audioOutputType, mMixBuffer, mixBufferSize); } #endif - int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); + mLastWriteTime = systemTime(); + mInWrite = true; + mBytesWritten += mixBufferSize; + + int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize); if (bytesWritten < 0) mBytesWritten -= mixBufferSize; mNumWrites++; mInWrite = false; @@ -1439,6 +1549,8 @@ bool AudioFlinger::MixerThread::threadLoop() } mStandby = false; } else { + // enable changes in effect chain + unlockEffectChains(); usleep(sleepTime); } @@ -1446,6 +1558,10 @@ bool AudioFlinger::MixerThread::threadLoop() // since we can't guarantee the destructors won't acquire that // same lock. tracksToRemove.clear(); + + // Effect chains will be actually deleted here if they were removed from + // mEffectChains list during mixing or effects processing + effectChains.clear(); } if (!mStandby) { @@ -1463,6 +1579,8 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track uint32_t mixerStatus = MIXER_IDLE; // find out which tracks need to be processed size_t count = activeTracks.size(); + size_t mixedTracks = 0; + size_t tracksWithEffect = 0; float masterVolume = mMasterVolume; bool masterMute = mMasterMute; @@ -1485,6 +1603,14 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute); } #endif + // Delegate master volume control to effect in output mix effect chain if needed + sp<EffectChain> chain = getEffectChain_l(0); + if (chain != 0) { + uint32_t v = (uint32_t)(masterVolume * (1 << 24)); + chain->setVolume(&v, &v); + masterVolume = (float)((v + (1 << 23)) >> 24); + chain.clear(); + } for (size_t i=0 ; i<count ; i++) { sp<Track> t = activeTracks[i].promote(); @@ -1501,11 +1627,42 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track { //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this); + mixedTracks++; + + // track->mainBuffer() != mMixBuffer means there is an effect chain + // connected to the track + chain.clear(); + if (track->mainBuffer() != mMixBuffer) { + chain = getEffectChain_l(track->sessionId()); + // Delegate volume control to effect in track effect chain if needed + if (chain != 0) { + tracksWithEffect++; + } else { + LOGW("prepareTracks_l(): track %08x attached to effect but no chain found on session %d", + track->name(), track->sessionId()); + } + } + + + int param = AudioMixer::VOLUME; + if (track->mFillingUpStatus == Track::FS_FILLED) { + // no ramp for the first volume setting + track->mFillingUpStatus = Track::FS_ACTIVE; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + param = AudioMixer::RAMP_VOLUME; + } + } else if (cblk->server != 0) { + // If the track is stopped before the first frame was mixed, + // do not apply ramp + param = AudioMixer::RAMP_VOLUME; + } + // compute volume for this track - int16_t left, right; + int16_t left, right, aux; if (track->isMuted() || masterMute || track->isPausing() || mStreamTypes[track->type()].mute) { - left = right = 0; + left = right = aux = 0; if (track->isPausing()) { track->setPaused(); } @@ -1524,31 +1681,28 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track } #endif float v = masterVolume * typeVolume; - float v_clamped = v * cblk->volume[0]; - if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + uint32_t vl = (uint32_t)(v * cblk->volume[0]) << 12; + 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)) { + // Do not ramp volume is volume is controlled by effect + param = AudioMixer::VOLUME; + } + + // Convert volumes from 8.24 to 4.12 format + uint32_t v_clamped = (vl + (1 << 11)) >> 12; + if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; left = int16_t(v_clamped); - v_clamped = v * cblk->volume[1]; - if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; + v_clamped = (vr + (1 << 11)) >> 12; + if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; right = int16_t(v_clamped); - } - - // XXX: these things DON'T need to be done each time - mAudioMixer->setBufferProvider(track); - mAudioMixer->enable(AudioMixer::MIXING); - int param = AudioMixer::VOLUME; - if (track->mFillingUpStatus == Track::FS_FILLED) { - // no ramp for the first volume setting - track->mFillingUpStatus = Track::FS_ACTIVE; - if (track->mState == TrackBase::RESUMING) { - track->mState = TrackBase::ACTIVE; - param = AudioMixer::RAMP_VOLUME; - } - } else if (cblk->server != 0) { - // If the track is stopped before the first frame was mixed, - // do not apply ramp - param = AudioMixer::RAMP_VOLUME; + v_clamped = (uint32_t)(v * cblk->sendLevel); + if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; + aux = int16_t(v_clamped); } + #ifdef LVMX if ( tracksConnectedChanged || stateChanged ) { @@ -1556,18 +1710,30 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track param = AudioMixer::VOLUME; } #endif - mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); - mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); + + // XXX: these things DON'T need to be done each time + mAudioMixer->setBufferProvider(track); + mAudioMixer->enable(AudioMixer::MIXING); + + mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left); + mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right); + mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux); mAudioMixer->setParameter( AudioMixer::TRACK, - AudioMixer::FORMAT, track->format()); + AudioMixer::FORMAT, (void *)track->format()); mAudioMixer->setParameter( AudioMixer::TRACK, - AudioMixer::CHANNEL_COUNT, track->channelCount()); + AudioMixer::CHANNEL_COUNT, (void *)track->channelCount()); mAudioMixer->setParameter( AudioMixer::RESAMPLE, AudioMixer::SAMPLE_RATE, - int(cblk->sampleRate)); + (void *)(cblk->sampleRate)); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer()); + mAudioMixer->setParameter( + AudioMixer::TRACK, + AudioMixer::AUX_BUFFER, (void *)track->auxBuffer()); // reset retry count track->mRetryCount = kMaxTrackRetries; @@ -1581,7 +1747,6 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track // We have consumed all the buffers of this track. // Remove it from the list of active tracks. tracksToRemove->add(track); - mAudioMixer->disable(AudioMixer::MIXING); } else { // No buffers for this track. Give it a few chances to // fill a buffer, then remove it from active list. @@ -1591,9 +1756,8 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track } else if (mixerStatus != MIXER_TRACKS_READY) { mixerStatus = MIXER_TRACKS_ENABLED; } - - mAudioMixer->disable(AudioMixer::MIXING); } + mAudioMixer->disable(AudioMixer::MIXING); } } @@ -1603,6 +1767,13 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track for (size_t i=0 ; i<count ; i++) { const sp<Track>& track = tracksToRemove->itemAt(i); mActiveTracks.remove(track); + if (track->mainBuffer() != mMixBuffer) { + chain = getEffectChain_l(track->sessionId()); + if (chain != 0) { + LOGV("stopping track on chain %p for session Id: %d", chain.get(), track->sessionId()); + chain->stopTrack(); + } + } if (track->isTerminated()) { mTracks.remove(track); deleteTrackName_l(track->mName); @@ -1610,6 +1781,13 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track } } + // mix buffer must be cleared if all tracks are connected to an + // effect chain as in this case the mixer will not write to + // mix buffer and track effects will accumulate into it + if (mixedTracks != 0 && mixedTracks == tracksWithEffect) { + memset(mMixBuffer, 0, mFrameCount * mChannelCount * sizeof(int16_t)); + } + return mixerStatus; } @@ -1681,6 +1859,15 @@ bool AudioFlinger::MixerThread::checkForNewParameters_l() reconfig = true; } } + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { + // forward device change to effects that have requested to be + // aware of attached audio device. + mDevice = (uint32_t)value; + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice(mDevice); + } + } + if (status == NO_ERROR) { status = mOutput->setParameters(keyValuePair); if (!mStandby && status == INVALID_OPERATION) { @@ -1740,9 +1927,8 @@ uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() } // ---------------------------------------------------------------------------- -AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id) - : PlaybackThread(audioFlinger, output, id), - mLeftVolume (1.0), mRightVolume(1.0) +AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device) + : PlaybackThread(audioFlinger, output, id, device) { mType = PlaybackThread::DIRECT; } @@ -1752,6 +1938,102 @@ AudioFlinger::DirectOutputThread::~DirectOutputThread() } +static inline int16_t clamp16(int32_t sample) +{ + if ((sample>>15) ^ (sample>>31)) + sample = 0x7FFF ^ (sample>>31); + return sample; +} + +static inline +int32_t mul(int16_t in, int16_t v) +{ +#if defined(__arm__) && !defined(__thumb__) + int32_t out; + asm( "smulbb %[out], %[in], %[v] \n" + : [out]"=r"(out) + : [in]"%r"(in), [v]"r"(v) + : ); + return out; +#else + return in * int32_t(v); +#endif +} + +void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp) +{ + // Do not apply volume on compressed audio + if (!AudioSystem::isLinearPCM(mFormat)) { + return; + } + + // convert to signed 16 bit before volume calculation + if (mFormat == AudioSystem::PCM_8_BIT) { + size_t count = mFrameCount * mChannelCount; + uint8_t *src = (uint8_t *)mMixBuffer + count-1; + int16_t *dst = mMixBuffer + count-1; + while(count--) { + *dst-- = (int16_t)(*src--^0x80) << 8; + } + } + + size_t frameCount = mFrameCount; + int16_t *out = mMixBuffer; + if (ramp) { + if (mChannelCount == 1) { + int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16; + int32_t vlInc = d / (int32_t)frameCount; + int32_t vl = ((int32_t)mLeftVolShort << 16); + do { + out[0] = clamp16(mul(out[0], vl >> 16) >> 12); + out++; + vl += vlInc; + } while (--frameCount); + + } else { + int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16; + int32_t vlInc = d / (int32_t)frameCount; + d = ((int32_t)rightVol - (int32_t)mRightVolShort) << 16; + int32_t vrInc = d / (int32_t)frameCount; + int32_t vl = ((int32_t)mLeftVolShort << 16); + int32_t vr = ((int32_t)mRightVolShort << 16); + do { + out[0] = clamp16(mul(out[0], vl >> 16) >> 12); + out[1] = clamp16(mul(out[1], vr >> 16) >> 12); + out += 2; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + } + } else { + if (mChannelCount == 1) { + do { + out[0] = clamp16(mul(out[0], leftVol) >> 12); + out++; + } while (--frameCount); + } else { + do { + out[0] = clamp16(mul(out[0], leftVol) >> 12); + out[1] = clamp16(mul(out[1], rightVol) >> 12); + out += 2; + } while (--frameCount); + } + } + + // convert back to unsigned 8 bit after volume calculation + if (mFormat == AudioSystem::PCM_8_BIT) { + size_t count = mFrameCount * mChannelCount; + int16_t *src = mMixBuffer; + uint8_t *dst = (uint8_t *)mMixBuffer; + while(count--) { + *dst++ = (uint8_t)(((int32_t)*src++ + (1<<7)) >> 8)^0x80; + } + } + + mLeftVolShort = leftVol; + mRightVolShort = rightVol; +} + bool AudioFlinger::DirectOutputThread::threadLoop() { uint32_t mixerStatus = MIXER_IDLE; @@ -1770,6 +2052,11 @@ bool AudioFlinger::DirectOutputThread::threadLoop() while (!exitPending()) { + bool rampVolume; + uint16_t leftVol; + uint16_t rightVol; + Vector< sp<EffectChain> > effectChains; + processConfigEvents(); mixerStatus = MIXER_IDLE; @@ -1821,6 +2108,8 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } } + effectChains = mEffectChains; + // find out which tracks need to be processed if (mActiveTracks.size() != 0) { sp<Track> t = mActiveTracks[0].promote(); @@ -1836,6 +2125,19 @@ bool AudioFlinger::DirectOutputThread::threadLoop() { //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); + if (track->mFillingUpStatus == Track::FS_FILLED) { + track->mFillingUpStatus = Track::FS_ACTIVE; + mLeftVolFloat = mRightVolFloat = 0; + mLeftVolShort = mRightVolShort = 0; + if (track->mState == TrackBase::RESUMING) { + track->mState = TrackBase::ACTIVE; + rampVolume = true; + } + } else if (cblk->server != 0) { + // If the track is stopped before the first frame was mixed, + // do not apply ramp + rampVolume = true; + } // compute volume for this track float left, right; if (track->isMuted() || mMasterMute || track->isPausing() || @@ -1855,17 +2157,42 @@ bool AudioFlinger::DirectOutputThread::threadLoop() right = v_clamped/MAX_GAIN; } - if (left != mLeftVolume || right != mRightVolume) { - mOutput->setVolume(left, right); - left = mLeftVolume; - right = mRightVolume; - } + if (left != mLeftVolFloat || right != mRightVolFloat) { + mLeftVolFloat = left; + mRightVolFloat = right; - if (track->mFillingUpStatus == Track::FS_FILLED) { - track->mFillingUpStatus = Track::FS_ACTIVE; - if (track->mState == TrackBase::RESUMING) { - track->mState = TrackBase::ACTIVE; + // If audio HAL implements volume control, + // force software volume to nominal value + if (mOutput->setVolume(left, right) == NO_ERROR) { + left = 1.0f; + right = 1.0f; } + + // Convert volumes from float to 8.24 + uint32_t vl = (uint32_t)(left * (1 << 24)); + uint32_t vr = (uint32_t)(right * (1 << 24)); + + // Delegate volume control to effect in track effect chain if needed + // only one effect chain can be present on DirectOutputThread, so if + // 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)) { + rampVolume = false; + } + } + + // Convert volumes from 8.24 to 4.12 format + uint32_t v_clamped = (vl + (1 << 11)) >> 12; + if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; + leftVol = (uint16_t)v_clamped; + v_clamped = (vr + (1 << 11)) >> 12; + if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; + rightVol = (uint16_t)v_clamped; + } else { + leftVol = mLeftVolShort; + rightVol = mRightVolShort; + rampVolume = false; } // reset retry count @@ -1897,11 +2224,17 @@ bool AudioFlinger::DirectOutputThread::threadLoop() // remove all the tracks that need to be... 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()); + effectChains[0]->stopTrack(); + } if (trackToRemove->isTerminated()) { mTracks.remove(trackToRemove); deleteTrackName_l(trackToRemove->mName); } } + + lockEffectChains_l(); } if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { @@ -1909,7 +2242,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() size_t frameCount = mFrameCount; curBuf = (int8_t *)mMixBuffer; // output audio to hardware - while(frameCount) { + while (frameCount) { buffer.frameCount = frameCount; activeTrack->getNextBuffer(&buffer); if (UNLIKELY(buffer.raw == 0)) { @@ -1941,6 +2274,14 @@ bool AudioFlinger::DirectOutputThread::threadLoop() } // sleepTime == 0 means we must write to audio hardware if (sleepTime == 0) { + if (mixerStatus == MIXER_TRACKS_READY) { + applyVolume(leftVol, rightVol, rampVolume); + } + for (size_t i = 0; i < effectChains.size(); i ++) { + effectChains[i]->process_l(); + } + unlockEffectChains(); + mLastWriteTime = systemTime(); mInWrite = true; mBytesWritten += mixBufferSize; @@ -1950,6 +2291,7 @@ bool AudioFlinger::DirectOutputThread::threadLoop() mInWrite = false; mStandby = false; } else { + unlockEffectChains(); usleep(sleepTime); } @@ -1958,6 +2300,10 @@ bool AudioFlinger::DirectOutputThread::threadLoop() // same lock. trackToRemove.clear(); activeTrack.clear(); + + // Effect chains will be actually deleted here if they were removed from + // mEffectChains list during mixing or effects processing + effectChains.clear(); } if (!mStandby) { @@ -2048,7 +2394,7 @@ uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs() // ---------------------------------------------------------------------------- AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id) - : MixerThread(audioFlinger, mainThread->getOutput(), id), mWaitTimeMs(UINT_MAX) + : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX) { mType = PlaybackThread::DUPLICATING; addOutputTrack(mainThread); @@ -2064,7 +2410,6 @@ AudioFlinger::DuplicatingThread::~DuplicatingThread() bool AudioFlinger::DuplicatingThread::threadLoop() { - int16_t* curBuf = mMixBuffer; Vector< sp<Track> > tracksToRemove; uint32_t mixerStatus = MIXER_IDLE; nsecs_t standbyTime = systemTime(); @@ -2074,6 +2419,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() uint32_t activeSleepTime = activeSleepTimeUs(); uint32_t idleSleepTime = idleSleepTimeUs(); uint32_t sleepTime = idleSleepTime; + Vector< sp<EffectChain> > effectChains; while (!exitPending()) { @@ -2134,14 +2480,20 @@ bool AudioFlinger::DuplicatingThread::threadLoop() } mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); + + // 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(); } if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { // mix buffers... if (outputsReady(outputTracks)) { - mAudioMixer->process(curBuf); + mAudioMixer->process(); } else { - memset(curBuf, 0, mixBufferSize); + memset(mMixBuffer, 0, mixBufferSize); } sleepTime = 0; writeFrames = mFrameCount; @@ -2158,6 +2510,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() if (outputTracks[i]->isActive()) { sleepTime = 0; writeFrames = 0; + memset(mMixBuffer, 0, mixBufferSize); break; } } @@ -2169,13 +2522,21 @@ bool AudioFlinger::DuplicatingThread::threadLoop() } // sleepTime == 0 means we must write to audio hardware if (sleepTime == 0) { + for (size_t i = 0; i < effectChains.size(); i ++) { + effectChains[i]->process_l(); + } + // enable changes in effect chain + unlockEffectChains(); + standbyTime = systemTime() + kStandbyTimeInNsecs; for (size_t i = 0; i < outputTracks.size(); i++) { - outputTracks[i]->write(curBuf, writeFrames); + outputTracks[i]->write(mMixBuffer, writeFrames); } mStandby = false; mBytesWritten += mixBufferSize; } else { + // enable changes in effect chain + unlockEffectChains(); usleep(sleepTime); } @@ -2184,6 +2545,10 @@ bool AudioFlinger::DuplicatingThread::threadLoop() // same lock. tracksToRemove.clear(); outputTracks.clear(); + + // Effect chains will be actually deleted here if they were removed from + // mEffectChains list during mixing or effects processing + effectChains.clear(); } return false; @@ -2268,7 +2633,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( int channelCount, int frameCount, uint32_t flags, - const sp<IMemory>& sharedBuffer) + const sp<IMemory>& sharedBuffer, + int sessionId) : RefBase(), mThread(thread), mClient(client), @@ -2277,7 +2643,8 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase( mState(IDLE), mClientTid(-1), mFormat(format), - mFlags(flags & ~SYSTEM_FLAGS_MASK) + mFlags(flags & ~SYSTEM_FLAGS_MASK), + mSessionId(sessionId) { LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); @@ -2420,15 +2787,17 @@ AudioFlinger::PlaybackThread::Track::Track( int format, int channelCount, int frameCount, - const sp<IMemory>& sharedBuffer) - : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer), - mMute(false), mSharedBuffer(sharedBuffer), mName(-1) + const sp<IMemory>& sharedBuffer, + int sessionId) + : TrackBase(thread, client, sampleRate, format, channelCount, frameCount, 0, sharedBuffer, sessionId), + mMute(false), mSharedBuffer(sharedBuffer), mName(-1), mMainBuffer(NULL), mAuxBuffer(NULL), mAuxEffectId(0) { if (mCblk != NULL) { sp<ThreadBase> baseThread = thread.promote(); if (baseThread != 0) { PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get(); mName = playbackThread->getTrackName_l(); + mMainBuffer = playbackThread->mixBuffer(); } LOGV("Track constructor name %d, calling thread %d", mName, IPCThreadState::self()->getCallingPid()); if (mName < 0) { @@ -2482,12 +2851,13 @@ void AudioFlinger::PlaybackThread::Track::destroy() void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) { - snprintf(buffer, size, " %5d %5d %3u %3u %3u %04u %1d %1d %1d %5u %5u %5u %08x %08x\n", + snprintf(buffer, size, " %05d %05d %03u %03u %03u %05u %04u %1d %1d %1d %05u %05u %05u 0x%08x 0x%08x 0x%08x 0x%08x\n", mName - AudioMixer::TRACK0, (mClient == NULL) ? getpid() : mClient->pid(), mStreamType, mFormat, mCblk->channelCount, + mSessionId, mFrameCount, mState, mMute, @@ -2496,7 +2866,9 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) mCblk->volume[0], mCblk->volume[1], mCblk->server, - mCblk->user); + mCblk->user, + (int)mMainBuffer, + (int)mAuxBuffer); } status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) @@ -2679,6 +3051,23 @@ void AudioFlinger::PlaybackThread::Track::setVolume(float left, float right) mVolume[1] = right; } +status_t AudioFlinger::PlaybackThread::Track::attachAuxEffect(int EffectId) +{ + status_t status = DEAD_OBJECT; + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + status = playbackThread->attachAuxEffect(this, EffectId); + } + return status; +} + +void AudioFlinger::PlaybackThread::Track::setAuxBuffer(int EffectId, int32_t *buffer) +{ + mAuxEffectId = EffectId; + mAuxBuffer = buffer; +} + // ---------------------------------------------------------------------------- // RecordTrack constructor must be called with AudioFlinger::mLock held @@ -2689,9 +3078,10 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack( int format, int channelCount, int frameCount, - uint32_t flags) + uint32_t flags, + int sessionId) : TrackBase(thread, client, sampleRate, format, - channelCount, frameCount, flags, 0), + channelCount, frameCount, flags, 0, sessionId), mOverflow(false) { if (mCblk != NULL) { @@ -2779,10 +3169,11 @@ void AudioFlinger::RecordThread::RecordTrack::stop() void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) { - snprintf(buffer, size, " %05d %03u %03u %04u %01d %05u %08x %08x\n", + snprintf(buffer, size, " %05d %03u %03u %05d %04u %01d %05u %08x %08x\n", (mClient == NULL) ? getpid() : mClient->pid(), mFormat, mCblk->channelCount, + mSessionId, mFrameCount, mState, mCblk->sampleRate, @@ -2800,7 +3191,7 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( int format, int channelCount, int frameCount) - : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL), + : Track(thread, NULL, AudioSystem::NUM_STREAM_TYPES, sampleRate, format, channelCount, frameCount, NULL, 0), mActive(false), mSourceThread(sourceThread) { @@ -3115,6 +3506,11 @@ sp<IMemory> AudioFlinger::TrackHandle::getCblk() const { return mTrack->getCblk(); } +status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId) +{ + return mTrack->attachAuxEffect(EffectId); +} + status_t AudioFlinger::TrackHandle::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -3131,6 +3527,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( int channelCount, int frameCount, uint32_t flags, + int *sessionId, status_t *status) { sp<RecordThread::RecordTrack> recordTrack; @@ -3140,6 +3537,7 @@ sp<IAudioRecord> AudioFlinger::openRecord( status_t lStatus; RecordThread *thread; size_t inFrameCount; + int lSessionId; // check calling permissions if (!recordingAllowed()) { @@ -3164,9 +3562,18 @@ sp<IAudioRecord> AudioFlinger::openRecord( mClients.add(pid, client); } + // If no audio session id is provided, create one here + if (sessionId != NULL && *sessionId != 0) { + lSessionId = *sessionId; + } else { + lSessionId = nextUniqueId(); + if (sessionId != NULL) { + *sessionId = lSessionId; + } + } // create new record track. The record track uses one track in mHardwareMixerThread by convention. recordTrack = new RecordThread::RecordTrack(thread, client, sampleRate, - format, channelCount, frameCount, flags); + format, channelCount, frameCount, flags, lSessionId); } if (recordTrack->getCblk() == NULL) { // remove local strong reference to Client before deleting the RecordTrack so that the Client @@ -3504,7 +3911,7 @@ status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) if (mActiveTrack != 0) { result.append("Active Track:\n"); - result.append(" Clien Fmt Chn Buf S SRate Serv User\n"); + result.append(" Clien Fmt Chn Session Buf S SRate Serv User\n"); mActiveTrack->dump(buffer, SIZE); result.append(buffer); @@ -3753,14 +4160,15 @@ int AudioFlinger::openOutput(uint32_t *pDevices, mHardwareStatus = AUDIO_HW_IDLE; if (output != 0) { + int id = nextUniqueId(); if ((flags & AudioSystem::OUTPUT_FLAG_DIRECT) || (format != AudioSystem::PCM_16_BIT) || (channels != AudioSystem::CHANNEL_OUT_STEREO)) { - thread = new DirectOutputThread(this, output, ++mNextThreadId); - LOGV("openOutput() created direct output: ID %d thread %p", mNextThreadId, thread); + thread = new DirectOutputThread(this, output, id, *pDevices); + LOGV("openOutput() created direct output: ID %d thread %p", id, thread); } else { - thread = new MixerThread(this, output, ++mNextThreadId); - LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread); + thread = new MixerThread(this, output, id, *pDevices); + LOGV("openOutput() created mixer output: ID %d thread %p", id, thread); #ifdef LVMX unsigned bitsPerSample = @@ -3774,7 +4182,7 @@ int AudioFlinger::openOutput(uint32_t *pDevices, #endif } - mPlaybackThreads.add(mNextThreadId, thread); + mPlaybackThreads.add(id, thread); if (pSamplingRate) *pSamplingRate = samplingRate; if (pFormat) *pFormat = format; @@ -3783,7 +4191,7 @@ int AudioFlinger::openOutput(uint32_t *pDevices, // notify client processes of the new output creation thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); - return mNextThreadId; + return id; } return 0; @@ -3800,13 +4208,13 @@ int AudioFlinger::openDuplicateOutput(int output1, int output2) return 0; } - - DuplicatingThread *thread = new DuplicatingThread(this, thread1, ++mNextThreadId); + int id = nextUniqueId(); + DuplicatingThread *thread = new DuplicatingThread(this, thread1, id); thread->addOutputTrack(thread2); - mPlaybackThreads.add(mNextThreadId, thread); + mPlaybackThreads.add(id, thread); // notify client processes of the new output creation thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); - return mNextThreadId; + return id; } status_t AudioFlinger::closeOutput(int output) @@ -3925,10 +4333,11 @@ int AudioFlinger::openInput(uint32_t *pDevices, } if (input != 0) { + int id = nextUniqueId(); // Start record thread - thread = new RecordThread(this, input, reqSamplingRate, reqChannels, ++mNextThreadId); - mRecordThreads.add(mNextThreadId, thread); - LOGV("openInput() created record thread: ID %d thread %p", mNextThreadId, thread); + thread = new RecordThread(this, input, reqSamplingRate, reqChannels, id); + mRecordThreads.add(id, thread); + LOGV("openInput() created record thread: ID %d thread %p", id, thread); if (pSamplingRate) *pSamplingRate = reqSamplingRate; if (pFormat) *pFormat = format; if (pChannels) *pChannels = reqChannels; @@ -3937,7 +4346,7 @@ int AudioFlinger::openInput(uint32_t *pDevices, // notify client processes of the new input creation thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED); - return mNextThreadId; + return id; } return 0; @@ -3991,6 +4400,12 @@ status_t AudioFlinger::setStreamOutput(uint32_t stream, int output) return NO_ERROR; } + +int AudioFlinger::newAudioSessionId() +{ + return nextUniqueId(); +} + // checkPlaybackThread_l() must be called with AudioFlinger::mLock held AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(int output) const { @@ -4023,6 +4438,1475 @@ AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(int input) const return thread; } +int AudioFlinger::nextUniqueId() +{ + return android_atomic_inc(&mNextUniqueId); +} + +// ---------------------------------------------------------------------------- +// Effect management +// ---------------------------------------------------------------------------- + + +status_t AudioFlinger::loadEffectLibrary(const char *libPath, int *handle) +{ + Mutex::Autolock _l(mLock); + return EffectLoadLibrary(libPath, handle); +} + +status_t AudioFlinger::unloadEffectLibrary(int handle) +{ + Mutex::Autolock _l(mLock); + return EffectUnloadLibrary(handle); +} + +status_t AudioFlinger::queryNumberEffects(uint32_t *numEffects) +{ + Mutex::Autolock _l(mLock); + return EffectQueryNumberEffects(numEffects); +} + +status_t AudioFlinger::queryNextEffect(effect_descriptor_t *descriptor) +{ + Mutex::Autolock _l(mLock); + return EffectQueryNext(descriptor); +} + +status_t AudioFlinger::getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor) +{ + Mutex::Autolock _l(mLock); + return EffectGetDescriptor(pUuid, descriptor); +} + +sp<IEffect> AudioFlinger::createEffect(pid_t pid, + effect_descriptor_t *pDesc, + const sp<IEffectClient>& effectClient, + int32_t priority, + int output, + int sessionId, + status_t *status, + int *id, + int *enabled) +{ + status_t lStatus = NO_ERROR; + sp<EffectHandle> handle; + effect_interface_t itfe; + effect_descriptor_t desc; + sp<Client> client; + wp<Client> wclient; + + LOGV("createEffect pid %d, client %p, priority %d, sessionId %d, output %d", pid, effectClient.get(), priority, sessionId, output); + + if (pDesc == NULL) { + lStatus = BAD_VALUE; + goto Exit; + } + + { + Mutex::Autolock _l(mLock); + + if (!EffectIsNullUuid(&pDesc->uuid)) { + // if uuid is specified, request effect descriptor + lStatus = EffectGetDescriptor(&pDesc->uuid, &desc); + if (lStatus < 0) { + LOGW("createEffect() error %d from EffectGetDescriptor", lStatus); + goto Exit; + } + } else { + // if uuid is not specified, look for an available implementation + // of the required type in effect factory + if (EffectIsNullUuid(&pDesc->type)) { + LOGW("createEffect() no effect type"); + lStatus = BAD_VALUE; + goto Exit; + } + uint32_t numEffects = 0; + effect_descriptor_t d; + bool found = false; + + lStatus = EffectQueryNumberEffects(&numEffects); + if (lStatus < 0) { + LOGW("createEffect() error %d from EffectQueryNumberEffects", lStatus); + goto Exit; + } + for (; numEffects > 0; numEffects--) { + lStatus = EffectQueryNext(&desc); + if (lStatus < 0) { + LOGW("createEffect() error %d from EffectQueryNext", lStatus); + continue; + } + if (memcmp(&desc.type, &pDesc->type, sizeof(effect_uuid_t)) == 0) { + // If matching type found save effect descriptor. If the session is + // 0 and the effect is not auxiliary, continue enumeration in case + // an auxiliary version of this effect type is available + found = true; + memcpy(&d, &desc, sizeof(effect_descriptor_t)); + if (sessionId != 0 || + (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + break; + } + } + } + if (!found) { + lStatus = BAD_VALUE; + LOGW("createEffect() effect not found"); + goto Exit; + } + // For same effect type, chose auxiliary version over insert version if + // connect to output mix (Compliance to OpenSL ES) + if (sessionId == 0 && + (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 && + (desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + lStatus = INVALID_OPERATION; + goto Exit; + } + + // return effect descriptor + memcpy(pDesc, &desc, sizeof(effect_descriptor_t)); + + // If output is not specified try to find a matching audio session ID in one of the + // 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); + } + } else { + // look for the thread where the specified audio session is present + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId)) { + output = mPlaybackThreads.keyAt(i); + break; + } + } + } + } + PlaybackThread *thread = checkPlaybackThread_l(output); + if (thread == NULL) { + LOGE("unknown output thread"); + lStatus = BAD_VALUE; + goto Exit; + } + + wclient = mClients.valueFor(pid); + + if (wclient != NULL) { + client = wclient.promote(); + } else { + client = new Client(this, pid); + mClients.add(pid, client); + } + + // create effect on selected output trhead + handle = thread->createEffect_l(client, effectClient, priority, sessionId, &desc, enabled, &lStatus); + if (handle != 0 && id != NULL) { + *id = handle->id(); + } + } + +Exit: + if(status) { + *status = lStatus; + } + return handle; +} + +// PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held +sp<AudioFlinger::EffectHandle> AudioFlinger::PlaybackThread::createEffect_l( + const sp<AudioFlinger::Client>& client, + const sp<IEffectClient>& effectClient, + int32_t priority, + int sessionId, + effect_descriptor_t *desc, + int *enabled, + status_t *status + ) +{ + sp<EffectModule> effect; + sp<EffectHandle> handle; + status_t lStatus; + sp<Track> track; + sp<EffectChain> chain; + bool effectCreated = false; + + if (mOutput == 0) { + LOGW("createEffect_l() Audio driver not initialized."); + lStatus = NO_INIT; + goto Exit; + } + + // 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); + 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); + lStatus = BAD_VALUE; + goto Exit; + } + + LOGV("createEffect_l() thread %p effect %s on session %d", this, desc->name, sessionId); + + { // scope for mLock + Mutex::Autolock _l(mLock); + + // check for existing effect chain with the requested audio session + chain = getEffectChain_l(sessionId); + if (chain == 0) { + // create a new chain for this session + LOGV("createEffect_l() new effect chain for session %d", sessionId); + chain = new EffectChain(this, sessionId); + addEffectChain_l(chain); + } else { + effect = chain->getEffectFromDesc(desc); + } + + LOGV("createEffect_l() got effect %p on chain %p", effect == 0 ? 0 : effect.get(), chain.get()); + + if (effect == 0) { + // create a new effect module if none present in the chain + effectCreated = true; + effect = new EffectModule(this, chain, desc, mAudioFlinger->nextUniqueId(), sessionId); + lStatus = effect->status(); + if (lStatus != NO_ERROR) { + goto Exit; + } + //TODO: handle CPU load and memory usage here + lStatus = chain->addEffect(effect); + if (lStatus != NO_ERROR) { + goto Exit; + } + + effect->setDevice(mDevice); + } + // create effect handle and connect it to effect module + handle = new EffectHandle(effect, client, effectClient, priority); + lStatus = effect->addHandle(handle); + if (enabled) { + *enabled = (int)effect->isEnabled(); + } + } + +Exit: + if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) { + if (chain != 0 && effectCreated) { + if (chain->removeEffect(effect) == 0) { + removeEffectChain_l(chain); + } + } + handle.clear(); + } + + if(status) { + *status = lStatus; + } + return handle; +} + +status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain) +{ + int session = chain->sessionId(); + int16_t *buffer = mMixBuffer; + + LOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session); + if (session == 0) { + chain->setInBuffer(buffer, false); + chain->setOutBuffer(buffer); + // Effect chain for session 0 is inserted at end of effect chains list + // to be processed last as it contains output mix effects to apply after + // all track specific effects + mEffectChains.add(chain); + } else { + bool ownsBuffer = false; + // Only one effect chain can be present in direct output thread and it uses + // the mix buffer as input + if (mType != DIRECT) { + size_t numSamples = mFrameCount * mChannelCount; + buffer = new int16_t[numSamples]; + memset(buffer, 0, numSamples * sizeof(int16_t)); + LOGV("addEffectChain_l() creating new input buffer %p session %d", buffer, session); + ownsBuffer = true; + } + chain->setInBuffer(buffer, ownsBuffer); + chain->setOutBuffer(mMixBuffer); + // 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 + mEffectChains.insertAt(chain, 0); + } + + // Attach all tracks with same session ID to this chain. + for (size_t i = 0; i < mTracks.size(); ++i) { + sp<Track> track = mTracks[i]; + if (session == track->sessionId()) { + LOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer); + track->setMainBuffer(buffer); + } + } + + // indicate all active tracks in the chain + for (size_t i = 0 ; i < mActiveTracks.size() ; ++i) { + sp<Track> track = mActiveTracks[i].promote(); + if (track == 0) continue; + if (session == track->sessionId()) { + LOGV("addEffectChain_l() activating track %p on session %d", track.get(), session); + chain->startTrack(); + } + } + + return NO_ERROR; +} + +size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& chain) +{ + int session = chain->sessionId(); + + LOGV("removeEffectChain_l() %p from thread %p for session %d", chain.get(), this, session); + + for (size_t i = 0; i < mEffectChains.size(); i++) { + if (chain == mEffectChains[i]) { + mEffectChains.removeAt(i); + // detach all tracks with same session ID from this chain + for (size_t i = 0; i < mTracks.size(); ++i) { + sp<Track> track = mTracks[i]; + if (session == track->sessionId()) { + track->setMainBuffer(mMixBuffer); + } + } + } + } + return mEffectChains.size(); +} + +void AudioFlinger::PlaybackThread::lockEffectChains_l() +{ + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->lock(); + } +} + +void AudioFlinger::PlaybackThread::unlockEffectChains() +{ + Mutex::Autolock _l(mLock); + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[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); + } + return effect; +} + +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 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); + if (effect != 0) { + if ((effect->desc().flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + track->setAuxBuffer(EffectId, (int32_t *)effect->inBuffer()); + } else { + status = INVALID_OPERATION; + } + } else { + status = BAD_VALUE; + } + } + return status; +} + +void AudioFlinger::PlaybackThread::detachAuxEffect_l(int effectId) +{ + for (size_t i = 0; i < mTracks.size(); ++i) { + sp<Track> track = mTracks[i]; + if (track->auxEffectId() == effectId) { + attachAuxEffect_l(track, 0); + } + } +} + +// ---------------------------------------------------------------------------- +// EffectModule implementation +// ---------------------------------------------------------------------------- + +#undef LOG_TAG +#define LOG_TAG "AudioFlinger::EffectModule" + +AudioFlinger::EffectModule::EffectModule(const wp<ThreadBase>& wThread, + const wp<AudioFlinger::EffectChain>& chain, + effect_descriptor_t *desc, + int id, + int sessionId) + : mThread(wThread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL), + mStatus(NO_INIT), mState(IDLE) +{ + LOGV("Constructor %p", this); + int lStatus; + sp<ThreadBase> thread = mThread.promote(); + if (thread == 0) { + return; + } + PlaybackThread *p = (PlaybackThread *)thread.get(); + + memcpy(&mDescriptor, desc, sizeof(effect_descriptor_t)); + + // create effect engine from effect factory + mStatus = EffectCreate(&desc->uuid, &mEffectInterface); + if (mStatus != NO_ERROR) { + return; + } + lStatus = init(); + if (lStatus < 0) { + mStatus = lStatus; + goto Error; + } + + LOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface); + return; +Error: + EffectRelease(mEffectInterface); + mEffectInterface = NULL; + LOGV("Constructor Error %d", mStatus); +} + +AudioFlinger::EffectModule::~EffectModule() +{ + LOGV("Destructor %p", this); + if (mEffectInterface != NULL) { + // release effect engine + EffectRelease(mEffectInterface); + } +} + +status_t AudioFlinger::EffectModule::addHandle(sp<EffectHandle>& handle) +{ + status_t status; + + Mutex::Autolock _l(mLock); + // First handle in mHandles has highest priority and controls the effect module + int priority = handle->priority(); + size_t size = mHandles.size(); + sp<EffectHandle> h; + size_t i; + for (i = 0; i < size; i++) { + h = mHandles[i].promote(); + if (h == 0) continue; + if (h->priority() <= priority) break; + } + // if inserted in first place, move effect control from previous owner to this handle + if (i == 0) { + if (h != 0) { + h->setControl(false, true); + } + handle->setControl(true, false); + status = NO_ERROR; + } else { + status = ALREADY_EXISTS; + } + mHandles.insertAt(handle, i); + return status; +} + +size_t AudioFlinger::EffectModule::removeHandle(const wp<EffectHandle>& handle) +{ + Mutex::Autolock _l(mLock); + size_t size = mHandles.size(); + size_t i; + for (i = 0; i < size; i++) { + if (mHandles[i] == handle) break; + } + if (i == size) { + return size; + } + mHandles.removeAt(i); + size = mHandles.size(); + // if removed from first place, move effect control from this handle to next in line + if (i == 0 && size != 0) { + sp<EffectHandle> h = mHandles[0].promote(); + if (h != 0) { + h->setControl(true, true); + } + } + + return size; +} + +void AudioFlinger::EffectModule::disconnect(const wp<EffectHandle>& handle) +{ + // keep a strong reference on this EffectModule to avoid calling the + // destructor before we exit + sp<EffectModule> keep(this); + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + Mutex::Autolock _l(thread->mLock); + // delete the effect module if removing last handle on it + if (removeHandle(handle) == 0) { + PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); + if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + playbackThread->detachAuxEffect_l(mId); + } + sp<EffectChain> chain = mChain.promote(); + if (chain != 0) { + // remove effect chain if remove last effect + if (chain->removeEffect(keep) == 0) { + playbackThread->removeEffectChain_l(chain); + } + } + } + } +} + +void AudioFlinger::EffectModule::process() +{ + Mutex::Autolock _l(mLock); + + if (mEffectInterface == NULL || mConfig.inputCfg.buffer.raw == NULL || mConfig.outputCfg.buffer.raw == NULL) { + return; + } + + if (mState != IDLE) { + // do 32 bit to 16 bit conversion for auxiliary effect input buffer + 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); + } + + // TODO: handle effects with buffer provider + if (mState != ACTIVE) { + uint32_t count = mConfig.inputCfg.buffer.frameCount; + int32_t amp = 32767L << 16; + int32_t step = amp / count; + int16_t *pIn = mConfig.inputCfg.buffer.s16; + int16_t *pOut = mConfig.outputCfg.buffer.s16; + int inChannels; + int outChannels; + + if (mConfig.inputCfg.channels == CHANNEL_MONO) { + inChannels = 1; + } else { + inChannels = 2; + } + if (mConfig.outputCfg.channels == CHANNEL_MONO) { + outChannels = 1; + } else { + outChannels = 2; + } + + switch (mState) { + case RESET: + reset(); + // clear auxiliary effect input buffer for next accumulation + if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t)); + } + step = -step; + mState = STARTING; + break; + case STARTING: + start(); + amp = 0; + pOut = mConfig.inputCfg.buffer.s16; + outChannels = inChannels; + mState = ACTIVE; + break; + case STOPPING: + step = -step; + pOut = mConfig.inputCfg.buffer.s16; + outChannels = inChannels; + mState = STOPPED; + break; + case STOPPED: + stop(); + amp = 0; + mState = IDLE; + break; + } + + // ramp volume down or up before activating or deactivating the effect + if (inChannels == 1) { + if (outChannels == 1) { + while (count--) { + *pOut++ = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15); + amp += step; + } + } else { + while (count--) { + int32_t smp = (int16_t)(((int32_t)*pIn++ * (amp >> 16)) >> 15); + *pOut++ = smp; + *pOut++ = smp; + amp += step; + } + } + } else { + if (outChannels == 1) { + while (count--) { + int32_t smp = (((int32_t)*pIn * (amp >> 16)) >> 16) + + (((int32_t)*(pIn + 1) * (amp >> 16)) >> 16); + pIn += 2; + *pOut++ = (int16_t)smp; + amp += step; + } + } else { + while (count--) { + *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15; + *pOut++ = (int16_t)((int32_t)*pIn++ * (amp >> 16)) >> 15; + amp += step; + } + } + } + if (mState == STARTING || mState == IDLE) { + return; + } + } + + // do the actual processing in the effect engine + (*mEffectInterface)->process(mEffectInterface, &mConfig.inputCfg.buffer, &mConfig.outputCfg.buffer); + + // clear auxiliary effect input buffer for next accumulation + if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + memset(mConfig.inputCfg.buffer.raw, 0, mConfig.inputCfg.buffer.frameCount*sizeof(int32_t)); + } + } else if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_INSERT && + mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw){ + // If an insert effect is idle and input buffer is different from output buffer, copy input to + // output + sp<EffectChain> chain = mChain.promote(); + if (chain != 0 && chain->activeTracks() != 0) { + size_t size = mConfig.inputCfg.buffer.frameCount * sizeof(int16_t); + if (mConfig.inputCfg.channels == CHANNEL_STEREO) { + size *= 2; + } + memcpy(mConfig.outputCfg.buffer.raw, mConfig.inputCfg.buffer.raw, size); + } + } +} + +void AudioFlinger::EffectModule::reset() +{ + if (mEffectInterface == NULL) { + return; + } + (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL); +} + +status_t AudioFlinger::EffectModule::configure() +{ + uint32_t channels; + if (mEffectInterface == NULL) { + return NO_INIT; + } + + sp<ThreadBase> thread = mThread.promote(); + if (thread == 0) { + return DEAD_OBJECT; + } + + // TODO: handle configuration of effects replacing track process + if (thread->channelCount() == 1) { + channels = CHANNEL_MONO; + } else { + channels = CHANNEL_STEREO; + } + + if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { + mConfig.inputCfg.channels = CHANNEL_MONO; + } else { + mConfig.inputCfg.channels = channels; + } + mConfig.outputCfg.channels = channels; + mConfig.inputCfg.format = PCM_FORMAT_S15; + mConfig.outputCfg.format = PCM_FORMAT_S15; + mConfig.inputCfg.samplingRate = thread->sampleRate(); + mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate; + mConfig.inputCfg.bufferProvider.cookie = NULL; + mConfig.inputCfg.bufferProvider.getBuffer = NULL; + mConfig.inputCfg.bufferProvider.releaseBuffer = NULL; + mConfig.outputCfg.bufferProvider.cookie = NULL; + mConfig.outputCfg.bufferProvider.getBuffer = NULL; + mConfig.outputCfg.bufferProvider.releaseBuffer = NULL; + mConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ; + // Insert effect: + // - in session 0, 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 + // Auxiliary effect: + // accumulates in output buffer: input buffer != output buffer + // Therefore: accumulate <=> input buffer != output buffer + if (mConfig.inputCfg.buffer.raw != mConfig.outputCfg.buffer.raw) { + mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE; + } else { + mConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE; + } + mConfig.inputCfg.mask = EFFECT_CONFIG_ALL; + mConfig.outputCfg.mask = EFFECT_CONFIG_ALL; + mConfig.inputCfg.buffer.frameCount = thread->frameCount(); + mConfig.outputCfg.buffer.frameCount = 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); + if (status == 0) { + status = cmdStatus; + } + return status; +} + +status_t AudioFlinger::EffectModule::init() +{ + if (mEffectInterface == NULL) { + return NO_INIT; + } + status_t cmdStatus; + int size = sizeof(status_t); + status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_INIT, 0, NULL, &size, &cmdStatus); + if (status == 0) { + status = cmdStatus; + } + return status; +} + +status_t AudioFlinger::EffectModule::start() +{ + if (mEffectInterface == NULL) { + return NO_INIT; + } + status_t cmdStatus; + int size = sizeof(status_t); + status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_ENABLE, 0, NULL, &size, &cmdStatus); + if (status == 0) { + status = cmdStatus; + } + return status; +} + +status_t AudioFlinger::EffectModule::stop() +{ + if (mEffectInterface == NULL) { + return NO_INIT; + } + status_t cmdStatus; + int size = sizeof(status_t); + status_t status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_DISABLE, 0, NULL, &size, &cmdStatus); + if (status == 0) { + status = cmdStatus; + } + return status; +} + +status_t AudioFlinger::EffectModule::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData) +{ + LOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface); + + if (mEffectInterface == NULL) { + return NO_INIT; + } + status_t status = (*mEffectInterface)->command(mEffectInterface, cmdCode, cmdSize, pCmdData, replySize, pReplyData); + if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) { + int size = (replySize == NULL) ? 0 : *replySize; + Mutex::Autolock _l(mLock); + for (size_t i = 1; i < mHandles.size(); i++) { + sp<EffectHandle> h = mHandles[i].promote(); + if (h != 0) { + h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData); + } + } + } + return status; +} + +status_t AudioFlinger::EffectModule::setEnabled(bool enabled) +{ + Mutex::Autolock _l(mLock); + LOGV("setEnabled %p enabled %d", this, enabled); + + if (enabled != isEnabled()) { + switch (mState) { + // going from disabled to enabled + case IDLE: + mState = RESET; + break; + case STOPPING: + mState = ACTIVE; + break; + case STOPPED: + mState = STARTING; + break; + + // going from enabled to disabled + case RESET: + mState = IDLE; + break; + case STARTING: + mState = STOPPED; + break; + case ACTIVE: + mState = STOPPING; + break; + } + for (size_t i = 1; i < mHandles.size(); i++) { + sp<EffectHandle> h = mHandles[i].promote(); + if (h != 0) { + h->setEnabled(enabled); + } + } + } + return NO_ERROR; +} + +bool AudioFlinger::EffectModule::isEnabled() +{ + switch (mState) { + case RESET: + case STARTING: + case ACTIVE: + return true; + case IDLE: + case STOPPING: + case STOPPED: + default: + return false; + } +} + +status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller) +{ + status_t status = NO_ERROR; + + // 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)) { + status_t cmdStatus; + uint32_t volume[2]; + uint32_t *pVolume = NULL; + int size = sizeof(volume); + volume[0] = *left; + volume[1] = *right; + if (controller) { + pVolume = volume; + } + status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_VOLUME, size, volume, &size, pVolume); + if (controller && status == NO_ERROR && size == sizeof(volume)) { + *left = volume[0]; + *right = volume[1]; + } + } + return status; +} + +status_t AudioFlinger::EffectModule::setDevice(uint32_t device) +{ + status_t status = NO_ERROR; + if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_MASK) { + status_t cmdStatus; + int size = sizeof(status_t); + status = (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_SET_DEVICE, sizeof(uint32_t), &device, &size, &cmdStatus); + if (status == NO_ERROR) { + status = cmdStatus; + } + } + return status; +} + + +status_t AudioFlinger::EffectModule::dump(int fd, const Vector<String16>& args) +{ + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "\tEffect ID %d:\n", mId); + result.append(buffer); + + bool locked = tryLock(mLock); + // failed to lock - AudioFlinger is probably deadlocked + if (!locked) { + result.append("\t\tCould not lock Fx mutex:\n"); + } + + result.append("\t\tSession Status State Engine:\n"); + snprintf(buffer, SIZE, "\t\t%05d %03d %03d 0x%08x\n", + mSessionId, mStatus, mState, (uint32_t)mEffectInterface); + result.append(buffer); + + result.append("\t\tDescriptor:\n"); + snprintf(buffer, SIZE, "\t\t- UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n", + mDescriptor.uuid.timeLow, mDescriptor.uuid.timeMid, mDescriptor.uuid.timeHiAndVersion, + mDescriptor.uuid.clockSeq, mDescriptor.uuid.node[0], mDescriptor.uuid.node[1],mDescriptor.uuid.node[2], + mDescriptor.uuid.node[3],mDescriptor.uuid.node[4],mDescriptor.uuid.node[5]); + result.append(buffer); + snprintf(buffer, SIZE, "\t\t- TYPE: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X%02X\n", + mDescriptor.type.timeLow, mDescriptor.type.timeMid, mDescriptor.type.timeHiAndVersion, + mDescriptor.type.clockSeq, mDescriptor.type.node[0], mDescriptor.type.node[1],mDescriptor.type.node[2], + mDescriptor.type.node[3],mDescriptor.type.node[4],mDescriptor.type.node[5]); + result.append(buffer); + snprintf(buffer, SIZE, "\t\t- apiVersion: %04X\n\t\t- flags: %08X\n", + mDescriptor.apiVersion, + mDescriptor.flags); + result.append(buffer); + snprintf(buffer, SIZE, "\t\t- name: %s\n", + mDescriptor.name); + result.append(buffer); + snprintf(buffer, SIZE, "\t\t- implementor: %s\n", + mDescriptor.implementor); + result.append(buffer); + + result.append("\t\t- Input configuration:\n"); + result.append("\t\t\tBuffer Frames Smp rate Channels Format\n"); + snprintf(buffer, SIZE, "\t\t\t0x%08x %05d %05d %08x %d\n", + (uint32_t)mConfig.inputCfg.buffer.raw, + mConfig.inputCfg.buffer.frameCount, + mConfig.inputCfg.samplingRate, + mConfig.inputCfg.channels, + mConfig.inputCfg.format); + result.append(buffer); + + result.append("\t\t- Output configuration:\n"); + result.append("\t\t\tBuffer Frames Smp rate Channels Format\n"); + snprintf(buffer, SIZE, "\t\t\t0x%08x %05d %05d %08x %d\n", + (uint32_t)mConfig.outputCfg.buffer.raw, + mConfig.outputCfg.buffer.frameCount, + mConfig.outputCfg.samplingRate, + mConfig.outputCfg.channels, + mConfig.outputCfg.format); + result.append(buffer); + + snprintf(buffer, SIZE, "\t\t%d Clients:\n", mHandles.size()); + result.append(buffer); + result.append("\t\t\tPid Priority Ctrl Locked client server\n"); + for (size_t i = 0; i < mHandles.size(); ++i) { + sp<EffectHandle> handle = mHandles[i].promote(); + if (handle != 0) { + handle->dump(buffer, SIZE); + result.append(buffer); + } + } + + result.append("\n"); + + write(fd, result.string(), result.length()); + + if (locked) { + mLock.unlock(); + } + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- +// EffectHandle implementation +// ---------------------------------------------------------------------------- + +#undef LOG_TAG +#define LOG_TAG "AudioFlinger::EffectHandle" + +AudioFlinger::EffectHandle::EffectHandle(const sp<EffectModule>& effect, + const sp<AudioFlinger::Client>& client, + const sp<IEffectClient>& effectClient, + int32_t priority) + : BnEffect(), + mEffect(effect), mEffectClient(effectClient), mClient(client), mPriority(priority), mHasControl(false) +{ + LOGV("constructor %p", this); + + int bufOffset = ((sizeof(effect_param_cblk_t) - 1) / sizeof(int) + 1) * sizeof(int); + mCblkMemory = client->heap()->allocate(EFFECT_PARAM_BUFFER_SIZE + bufOffset); + if (mCblkMemory != 0) { + mCblk = static_cast<effect_param_cblk_t *>(mCblkMemory->pointer()); + + if (mCblk) { + new(mCblk) effect_param_cblk_t(); + mBuffer = (uint8_t *)mCblk + bufOffset; + } + } else { + LOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE + sizeof(effect_param_cblk_t)); + return; + } +} + +AudioFlinger::EffectHandle::~EffectHandle() +{ + LOGV("Destructor %p", this); + disconnect(); +} + +status_t AudioFlinger::EffectHandle::enable() +{ + if (!mHasControl) return INVALID_OPERATION; + if (mEffect == 0) return DEAD_OBJECT; + + return mEffect->setEnabled(true); +} + +status_t AudioFlinger::EffectHandle::disable() +{ + if (!mHasControl) return INVALID_OPERATION; + if (mEffect == NULL) return DEAD_OBJECT; + + return mEffect->setEnabled(false); +} + +void AudioFlinger::EffectHandle::disconnect() +{ + if (mEffect == 0) { + return; + } + mEffect->disconnect(this); + // release sp on module => module destructor can be called now + mEffect.clear(); + if (mCblk) { + mCblk->~effect_param_cblk_t(); // destroy our shared-structure. + } + mCblkMemory.clear(); // and free the shared memory + if (mClient != 0) { + Mutex::Autolock _l(mClient->audioFlinger()->mLock); + mClient.clear(); + } +} + +status_t AudioFlinger::EffectHandle::command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData) +{ + LOGV("command(), cmdCode: %d, mHasControl: %d, mEffect: %p", cmdCode, mHasControl, (mEffect == 0) ? 0 : mEffect.get()); + + // only get parameter command is permitted for applications not controlling the effect + if (!mHasControl && cmdCode != EFFECT_CMD_GET_PARAM) { + return INVALID_OPERATION; + } + if (mEffect == 0) return DEAD_OBJECT; + + // handle commands that are not forwarded transparently to effect engine + if (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) { + // No need to trylock() here as this function is executed in the binder thread serving a particular client process: + // no risk to block the whole media server process or mixer threads is we are stuck here + Mutex::Autolock _l(mCblk->lock); + if (mCblk->clientIndex > EFFECT_PARAM_BUFFER_SIZE || + mCblk->serverIndex > EFFECT_PARAM_BUFFER_SIZE) { + mCblk->serverIndex = 0; + mCblk->clientIndex = 0; + return BAD_VALUE; + } + status_t status = NO_ERROR; + while (mCblk->serverIndex < mCblk->clientIndex) { + int reply; + int rsize = sizeof(int); + int *p = (int *)(mBuffer + mCblk->serverIndex); + int size = *p++; + effect_param_t *param = (effect_param_t *)p; + int psize = sizeof(effect_param_t) + ((param->psize - 1) / sizeof(int) + 1) * sizeof(int) + param->vsize; + status_t ret = mEffect->command(EFFECT_CMD_SET_PARAM, psize, p, &rsize, &reply); + if (ret == NO_ERROR) { + if (reply != NO_ERROR) { + status = reply; + } + } else { + status = ret; + } + mCblk->serverIndex += size; + } + mCblk->serverIndex = 0; + mCblk->clientIndex = 0; + return status; + } else if (cmdCode == EFFECT_CMD_ENABLE) { + return enable(); + } else if (cmdCode == EFFECT_CMD_DISABLE) { + return disable(); + } + + return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData); +} + +sp<IMemory> AudioFlinger::EffectHandle::getCblk() const { + return mCblkMemory; +} + +void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal) +{ + LOGV("setControl %p control %d", this, hasControl); + + mHasControl = hasControl; + if (signal && mEffectClient != 0) { + mEffectClient->controlStatusChanged(hasControl); + } +} + +void AudioFlinger::EffectHandle::commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData) +{ + if (mEffectClient != 0) { + mEffectClient->commandExecuted(cmdCode, cmdSize, pCmdData, replySize, pReplyData); + } +} + + + +void AudioFlinger::EffectHandle::setEnabled(bool enabled) +{ + if (mEffectClient != 0) { + mEffectClient->enableStatusChanged(enabled); + } +} + +status_t AudioFlinger::EffectHandle::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) +{ + return BnEffect::onTransact(code, data, reply, flags); +} + + +void AudioFlinger::EffectHandle::dump(char* buffer, size_t size) +{ + bool locked = tryLock(mCblk->lock); + + snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n", + (mClient == NULL) ? getpid() : mClient->pid(), + mPriority, + mHasControl, + !locked, + mCblk->clientIndex, + mCblk->serverIndex + ); + + if (locked) { + mCblk->lock.unlock(); + } +} + +#undef LOG_TAG +#define LOG_TAG "AudioFlinger::EffectChain" + +AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread, + int sessionId) + : mThread(wThread), mSessionId(sessionId), mVolumeCtrlIdx(-1), mActiveTrackCnt(0), mOwnInBuffer(false) +{ + +} + +AudioFlinger::EffectChain::~EffectChain() +{ + if (mOwnInBuffer) { + delete mInBuffer; + } + +} + +sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc(effect_descriptor_t *descriptor) +{ + sp<EffectModule> effect; + size_t size = mEffects.size(); + + for (size_t i = 0; i < size; i++) { + if (memcmp(&mEffects[i]->desc().uuid, &descriptor->uuid, sizeof(effect_uuid_t)) == 0) { + effect = mEffects[i]; + break; + } + } + return effect; +} + +sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId(int id) +{ + sp<EffectModule> effect; + size_t size = mEffects.size(); + + for (size_t i = 0; i < size; i++) { + if (mEffects[i]->id() == id) { + effect = mEffects[i]; + break; + } + } + return effect; +} + +// Must be called with EffectChain::mLock locked +void AudioFlinger::EffectChain::process_l() +{ + size_t size = mEffects.size(); + for (size_t i = 0; i < size; i++) { + mEffects[i]->process(); + } + // if no track is active, input buffer must be cleared here as the mixer process + // will not do it + if (mSessionId != 0 && activeTracks() == 0) { + sp<ThreadBase> thread = mThread.promote(); + if (thread != 0) { + size_t numSamples = thread->frameCount() * thread->channelCount(); + memset(mInBuffer, 0, numSamples * sizeof(int16_t)); + } + } +} + +status_t AudioFlinger::EffectChain::addEffect(sp<EffectModule>& effect) +{ + effect_descriptor_t desc = effect->desc(); + uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK; + + Mutex::Autolock _l(mLock); + + 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 + // calling the process in effect engine + size_t numSamples = thread->frameCount(); + int32_t *buffer = new int32_t[numSamples]; + memset(buffer, 0, numSamples * sizeof(int32_t)); + effect->setInBuffer((int16_t *)buffer); + // auxiliary effects output samples to chain input buffer for further processing + // by insert effects + effect->setOutBuffer(mInBuffer); + } else { + // Insert effects are inserted at the end of mEffects vector as they are processed + // after track and auxiliary effects. + // Insert effect order: + // if EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_EXCLUSIVE insert as first insert effect + // else if EFFECT_FLAG_INSERT_ANY insert after first or before last + // else insert as last insert effect + // Reject insertion if: + // - EFFECT_FLAG_INSERT_EXCLUSIVE and another effect is present + // - an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is present + // - EFFECT_FLAG_INSERT_FIRST or EFFECT_FLAG_INSERT_LAST and an effect with same + // preference is present + + int size = (int)mEffects.size(); + int idx_insert = size; + int idx_insert_first = -1; + int idx_insert_last = -1; + + for (int i = 0; i < size; i++) { + effect_descriptor_t d = mEffects[i]->desc(); + uint32_t iMode = d.flags & EFFECT_FLAG_TYPE_MASK; + uint32_t iPref = d.flags & EFFECT_FLAG_INSERT_MASK; + if (iMode == EFFECT_FLAG_TYPE_INSERT) { + // check invalid effect chaining combinations + if (insertPref == EFFECT_FLAG_INSERT_EXCLUSIVE || + iPref == EFFECT_FLAG_INSERT_EXCLUSIVE || + (insertPref != EFFECT_FLAG_INSERT_ANY + && insertPref == iPref)) { + return INVALID_OPERATION; + } + // remember position of first insert effect + if (idx_insert == size) { + idx_insert = i; + } + // remember position of insert effect claiming + // first place + if (iPref == EFFECT_FLAG_INSERT_FIRST) { + idx_insert_first = i; + } + // remember position of insert effect claiming + // last place + if (iPref == EFFECT_FLAG_INSERT_LAST) { + idx_insert_last = i; + } + } + } + + // modify idx_insert from first place if needed + if (idx_insert_first != -1) { + idx_insert = idx_insert_first + 1; + } else if (idx_insert_last != -1) { + idx_insert = idx_insert_last; + } else if (insertPref == EFFECT_FLAG_INSERT_LAST) { + idx_insert = size; + } + + // always read samples from chain input buffer + effect->setInBuffer(mInBuffer); + + // if last effect in the chain, output samples to chain + // output buffer, otherwise to chain input buffer + if (idx_insert == size) { + if (idx_insert != 0) { + mEffects[idx_insert-1]->setOutBuffer(mInBuffer); + mEffects[idx_insert-1]->configure(); + } + effect->setOutBuffer(mOutBuffer); + } else { + effect->setOutBuffer(mInBuffer); + } + status_t status = 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 status %d", effect.get(), this, idx_insert, status); + } + effect->configure(); + return NO_ERROR; +} + +size_t AudioFlinger::EffectChain::removeEffect(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; + + for (i = 0; i < size; i++) { + if (effect == mEffects[i]) { + if (type == EFFECT_FLAG_TYPE_AUXILIARY) { + delete[] effect->inBuffer(); + } else { + if (i == size - 1 && i != 0) { + mEffects[i - 1]->setOutBuffer(mOutBuffer); + mEffects[i - 1]->configure(); + } + } + mEffects.removeAt(i); + LOGV("removeEffect() 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) +{ + size_t size = mEffects.size(); + for (size_t i = 0; i < size; i++) { + mEffects[i]->setDevice(device); + } +} + +bool AudioFlinger::EffectChain::setVolume(uint32_t *left, uint32_t *right) +{ + uint32_t newLeft = *left; + uint32_t newRight = *right; + bool hasControl = false; + + // first get volume update from volume controller + if (mVolumeCtrlIdx >= 0) { + mEffects[mVolumeCtrlIdx]->setVolume(&newLeft, &newRight, true); + hasControl = true; + } + // 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) { + lVol = *left; + rVol = *right; + } + mEffects[i]->setVolume(&lVol, &rVol, false); + } + *left = newLeft; + *right = newRight; + + 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; + char buffer[SIZE]; + String8 result; + + snprintf(buffer, SIZE, "Effects for session %d:\n", mSessionId); + result.append(buffer); + + bool locked = tryLock(mLock); + // failed to lock - AudioFlinger is probably deadlocked + if (!locked) { + 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", + mEffects.size(), + (uint32_t)mInBuffer, + (uint32_t)mOutBuffer, + (mVolumeCtrlIdx == -1) ? 0 : mEffects[mVolumeCtrlIdx]->id(), + mActiveTrackCnt); + result.append(buffer); + write(fd, result.string(), result.size()); + + for (size_t i = 0; i < mEffects.size(); ++i) { + sp<EffectModule> effect = mEffects[i]; + if (effect != 0) { + effect->dump(fd, args); + } + } + + if (locked) { + mLock.unlock(); + } + + return NO_ERROR; +} + +#undef LOG_TAG +#define LOG_TAG "AudioFlinger" + // ---------------------------------------------------------------------------- status_t AudioFlinger::onTransact( diff --git a/libs/audioflinger/AudioFlinger.h b/libs/audioflinger/AudioFlinger.h index f35f38b..e543334 100644 --- a/libs/audioflinger/AudioFlinger.h +++ b/libs/audioflinger/AudioFlinger.h @@ -42,6 +42,7 @@ namespace android { class audio_track_cblk_t; +class effect_param_cblk_t; class AudioMixer; class AudioBuffer; class AudioResampler; @@ -75,6 +76,7 @@ public: uint32_t flags, const sp<IMemory>& sharedBuffer, int output, + int *sessionId, status_t *status); virtual uint32_t sampleRate(int output) const; @@ -139,6 +141,28 @@ public: virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output); + virtual int newAudioSessionId(); + + virtual status_t loadEffectLibrary(const char *libPath, int *handle); + + virtual status_t unloadEffectLibrary(int handle); + + virtual status_t queryNumberEffects(uint32_t *numEffects); + + virtual status_t queryNextEffect(effect_descriptor_t *descriptor); + + virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor); + + virtual sp<IEffect> createEffect(pid_t pid, + effect_descriptor_t *pDesc, + const sp<IEffectClient>& effectClient, + int32_t priority, + int output, + int sessionId, + status_t *status, + int *id, + int *enabled); + enum hardware_call_state { AUDIO_HW_IDLE = 0, AUDIO_HW_INIT, @@ -167,6 +191,7 @@ public: int channelCount, int frameCount, uint32_t flags, + int *sessionId, status_t *status); virtual status_t onTransact( @@ -233,6 +258,9 @@ private: class DuplicatingThread; class Track; class RecordTrack; + class EffectModule; + class EffectHandle; + class EffectChain; class ThreadBase : public Thread { public: @@ -268,13 +296,15 @@ private: int channelCount, int frameCount, uint32_t flags, - const sp<IMemory>& sharedBuffer); + const sp<IMemory>& sharedBuffer, + int sessionId); ~TrackBase(); virtual status_t start() = 0; virtual void stop() = 0; sp<IMemory> getCblk() const; audio_track_cblk_t* cblk() const { return mCblk; } + int sessionId() { return mSessionId; } protected: friend class ThreadBase; @@ -323,6 +353,7 @@ private: int mClientTid; uint8_t mFormat; uint32_t mFlags; + int mSessionId; }; class ConfigEvent { @@ -405,7 +436,8 @@ private: int format, int channelCount, int frameCount, - const sp<IMemory>& sharedBuffer); + const sp<IMemory>& sharedBuffer, + int sessionId); ~Track(); void dump(char* buffer, size_t size); @@ -424,6 +456,12 @@ private: int type() const { return mStreamType; } + status_t attachAuxEffect(int EffectId); + void setAuxBuffer(int EffectId, int32_t *buffer); + int32_t *auxBuffer() { return mAuxBuffer; } + void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; } + int16_t *mainBuffer() { return mMainBuffer; } + int auxEffectId() { return mAuxEffectId; } protected: @@ -464,6 +502,9 @@ private: bool mResetDone; int mStreamType; int mName; + int16_t *mMainBuffer; + int32_t *mAuxBuffer; + int mAuxEffectId; }; // end of Track @@ -505,7 +546,7 @@ private: DuplicatingThread* mSourceThread; }; // end of OutputTrack - PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); + PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device); virtual ~PlaybackThread(); virtual status_t dump(int fd, const Vector<String16>& args); @@ -538,6 +579,7 @@ private: int channelCount, int frameCount, const sp<IMemory>& sharedBuffer, + int sessionId, status_t *status); AudioStreamOut* getOutput() { return mOutput; } @@ -549,6 +591,29 @@ private: virtual String8 getParameters(const String8& keys); virtual void audioConfigChanged_l(int event, int param = 0); virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); + int16_t *mixBuffer() { return mMixBuffer; }; + + sp<EffectHandle> createEffect_l( + const sp<AudioFlinger::Client>& client, + const sp<IEffectClient>& effectClient, + int32_t priority, + int sessionId, + effect_descriptor_t *desc, + int *enabled, + status_t *status); + + bool hasAudioSession(int sessionId); + sp<EffectChain> getEffectChain(int sessionId); + sp<EffectChain> getEffectChain_l(int sessionId); + status_t addEffectChain_l(const sp<EffectChain>& chain); + size_t removeEffectChain_l(const sp<EffectChain>& chain); + void lockEffectChains_l(); + void unlockEffectChains(); + + sp<AudioFlinger::EffectModule> getEffect_l(int sessionId, int effectId); + void detachAuxEffect_l(int effectId); + status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId); + status_t attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId); struct stream_type_t { stream_type_t() @@ -591,8 +656,11 @@ private: void readOutputParameters(); + uint32_t device() { return mDevice; } + virtual status_t dumpInternals(int fd, const Vector<String16>& args); status_t dumpTracks(int fd, const Vector<String16>& args); + status_t dumpEffectChains(int fd, const Vector<String16>& args); SortedVector< sp<Track> > mTracks; // mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread @@ -603,11 +671,13 @@ private: int mNumWrites; int mNumDelayedWrites; bool mInWrite; + Vector< sp<EffectChain> > mEffectChains; + uint32_t mDevice; }; class MixerThread : public PlaybackThread { public: - MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); + MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device); virtual ~MixerThread(); // Thread virtuals @@ -630,7 +700,7 @@ private: class DirectOutputThread : public PlaybackThread { public: - DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id); + DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device); ~DirectOutputThread(); // Thread virtuals @@ -645,8 +715,12 @@ private: virtual uint32_t idleSleepTimeUs(); private: - float mLeftVolume; - float mRightVolume; + void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp); + + float mLeftVolFloat; + float mRightVolFloat; + uint16_t mLeftVolShort; + uint16_t mRightVolShort; }; class DuplicatingThread : public MixerThread { @@ -676,6 +750,8 @@ private: float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; } void audioConfigChanged_l(int event, int ioHandle, void *param2); + int nextUniqueId(); + friend class AudioBuffer; class TrackHandle : public android::BnAudioTrack { @@ -689,6 +765,7 @@ private: virtual void pause(); virtual void setVolume(float left, float right); virtual sp<IMemory> getCblk() const; + virtual status_t attachAuxEffect(int effectId); virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: @@ -717,7 +794,8 @@ private: int format, int channelCount, int frameCount, - uint32_t flags); + uint32_t flags, + int sessionId); ~RecordTrack(); virtual status_t start(); @@ -792,6 +870,215 @@ private: sp<RecordThread::RecordTrack> mRecordTrack; }; + //--- 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 + + // 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(const wp<ThreadBase>& wThread, + const wp<AudioFlinger::EffectChain>& chain, + effect_descriptor_t *desc, + int id, + int sessionId); + ~EffectModule(); + + enum effect_state { + IDLE, + RESET, + STARTING, + ACTIVE, + STOPPING, + STOPPED + }; + + int id() { return mId; } + void process(); + status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData); + + void reset(); + status_t configure(); + status_t init(); + uint32_t state() { + return mState; + } + uint32_t status() { + return mStatus; + } + status_t setEnabled(bool enabled); + bool isEnabled(); + + void setInBuffer(int16_t *buffer) { mConfig.inputCfg.buffer.s16 = buffer; } + int16_t *inBuffer() { return mConfig.inputCfg.buffer.s16; } + void setOutBuffer(int16_t *buffer) { mConfig.outputCfg.buffer.s16 = buffer; } + int16_t *outBuffer() { return mConfig.outputCfg.buffer.s16; } + + status_t addHandle(sp<EffectHandle>& handle); + void disconnect(const wp<EffectHandle>& handle); + size_t removeHandle (const wp<EffectHandle>& handle); + + effect_descriptor_t& desc() { return mDescriptor; } + + status_t setDevice(uint32_t device); + status_t setVolume(uint32_t *left, uint32_t *right, bool controller); + + status_t dump(int fd, const Vector<String16>& args); + + protected: + + EffectModule(const EffectModule&); + EffectModule& operator = (const EffectModule&); + + status_t start(); + status_t stop(); + + Mutex mLock; // mutex for process, commands and handles list protection + wp<ThreadBase> mThread; // parent thread + wp<EffectChain> mChain; // parent effect chain + int mId; // this instance unique ID + int mSessionId; // audio session ID + effect_descriptor_t mDescriptor;// effect descriptor received from effect engine + effect_config_t mConfig; // input and output audio configuration + effect_interface_t mEffectInterface; // Effect module C API + status_t mStatus; // initialization status + uint32_t mState; // current activation state (effect_state) + Vector< wp<EffectHandle> > mHandles; // list of client handles + }; + + // 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(); + + // IEffect + virtual status_t enable(); + virtual status_t disable(); + virtual status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData); + virtual void disconnect(); + virtual sp<IMemory> getCblk() const; + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags); + + + // Give or take control of effect module + void setControl(bool hasControl, bool signal); + void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData); + void setEnabled(bool enabled); + + // Getters + int id() { return mEffect->id(); } + int priority() { return mPriority; } + bool hasControl() { return mHasControl; } + sp<EffectModule> effect() { return mEffect; } + + void dump(char* buffer, size_t size); + + protected: + + EffectHandle(const EffectHandle&); + EffectHandle& operator =(const EffectHandle&); + + sp<EffectModule> mEffect; // pointer to controlled EffectModule + sp<IEffectClient> mEffectClient; // callback interface for client notifications + sp<Client> mClient; // client for shared memory allocation + 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 + }; + + // 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 EffecChain with session ID 0 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 != 0), 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, int sessionId); + ~EffectChain(); + + void process_l(); + + void lock() { + mLock.lock(); + } + void unlock() { + mLock.unlock(); + } + + status_t addEffect(sp<EffectModule>& handle); + size_t removeEffect(const sp<EffectModule>& handle); + + int sessionId() { + return mSessionId; + } + sp<EffectModule> getEffectFromDesc(effect_descriptor_t *descriptor); + sp<EffectModule> getEffectFromId(int id); + sp<EffectModule> getVolumeController(); + bool setVolume(uint32_t *left, uint32_t *right); + void setDevice(uint32_t device); + + void setInBuffer(int16_t *buffer, bool ownsBuffer = false) { + mInBuffer = buffer; + mOwnInBuffer = ownsBuffer; + } + int16_t *inBuffer() { + return mInBuffer; + } + void setOutBuffer(int16_t *buffer) { + mOutBuffer = buffer; + } + int16_t *outBuffer() { + return mOutBuffer; + } + + void startTrack() {mActiveTrackCnt++;} + void stopTrack() {mActiveTrackCnt--;} + int activeTracks() { return mActiveTrackCnt;} + + status_t dump(int fd, const Vector<String16>& args); + + protected: + + EffectChain(const EffectChain&); + EffectChain& operator =(const EffectChain&); + + wp<ThreadBase> mThread; // parent mixer thread + Mutex mLock; // mutex protecting effect list + Vector<sp<EffectModule> > mEffects; // list of effect modules + int mSessionId; // audio session ID + int16_t *mInBuffer; // chain input buffer + int16_t *mOutBuffer; // chain output buffer + int mVolumeCtrlIdx; // index of insert effect having control over volume + int mActiveTrackCnt; // number of active tracks connected + bool mOwnInBuffer; // true if the chain owns its input buffer + }; + friend class RecordThread; friend class PlaybackThread; @@ -813,7 +1100,7 @@ private: DefaultKeyedVector< int, sp<RecordThread> > mRecordThreads; DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients; - int mNextThreadId; + volatile int32_t mNextUniqueId; #ifdef LVMX int mLifeVibesClientPid; #endif diff --git a/libs/audioflinger/AudioMixer.cpp b/libs/audioflinger/AudioMixer.cpp index 19a442a..8aaa325 100644 --- a/libs/audioflinger/AudioMixer.cpp +++ b/libs/audioflinger/AudioMixer.cpp @@ -56,6 +56,8 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) t->volume[1] = UNITY_GAIN; t->volumeInc[0] = 0; t->volumeInc[1] = 0; + t->auxLevel = 0; + t->auxInc = 0; t->channelCount = 2; t->enabled = 0; t->format = 16; @@ -65,6 +67,8 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) t->resampler = 0; t->sampleRate = mSampleRate; t->in = 0; + t->mainBuffer = NULL; + t->auxBuffer = NULL; t++; } } @@ -169,28 +173,48 @@ status_t AudioMixer::setActiveTrack(int track) return NO_ERROR; } -status_t AudioMixer::setParameter(int target, int name, int value) +status_t AudioMixer::setParameter(int target, int name, void *value) { + int valueInt = (int)value; + int32_t *valueBuf = (int32_t *)value; + switch (target) { case TRACK: if (name == CHANNEL_COUNT) { - if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) { - if (mState.tracks[ mActiveTrack ].channelCount != value) { - mState.tracks[ mActiveTrack ].channelCount = value; - LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value); + if ((uint32_t(valueInt) <= MAX_NUM_CHANNELS) && (valueInt)) { + if (mState.tracks[ mActiveTrack ].channelCount != valueInt) { + mState.tracks[ mActiveTrack ].channelCount = valueInt; + LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", valueInt); invalidateState(1<<mActiveTrack); } return NO_ERROR; } } + if (name == MAIN_BUFFER) { + if (mState.tracks[ mActiveTrack ].mainBuffer != valueBuf) { + mState.tracks[ mActiveTrack ].mainBuffer = valueBuf; + LOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf); + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } + if (name == AUX_BUFFER) { + if (mState.tracks[ mActiveTrack ].auxBuffer != valueBuf) { + mState.tracks[ mActiveTrack ].auxBuffer = valueBuf; + LOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf); + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } + break; case RESAMPLE: if (name == SAMPLE_RATE) { - if (value > 0) { + if (valueInt > 0) { track_t& track = mState.tracks[ mActiveTrack ]; - if (track.setResampler(uint32_t(value), mSampleRate)) { + if (track.setResampler(uint32_t(valueInt), mSampleRate)) { LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)", - uint32_t(value)); + uint32_t(valueInt)); invalidateState(1<<mActiveTrack); } return NO_ERROR; @@ -201,18 +225,39 @@ status_t AudioMixer::setParameter(int target, int name, int value) case VOLUME: if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) { track_t& track = mState.tracks[ mActiveTrack ]; - if (track.volume[name-VOLUME0] != value) { + if (track.volume[name-VOLUME0] != valueInt) { + LOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt); track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16; - track.volume[name-VOLUME0] = value; + track.volume[name-VOLUME0] = valueInt; if (target == VOLUME) { - track.prevVolume[name-VOLUME0] = value << 16; + track.prevVolume[name-VOLUME0] = valueInt << 16; track.volumeInc[name-VOLUME0] = 0; } else { - int32_t d = (value<<16) - track.prevVolume[name-VOLUME0]; + int32_t d = (valueInt<<16) - track.prevVolume[name-VOLUME0]; int32_t volInc = d / int32_t(mState.frameCount); track.volumeInc[name-VOLUME0] = volInc; if (volInc == 0) { - track.prevVolume[name-VOLUME0] = value << 16; + track.prevVolume[name-VOLUME0] = valueInt << 16; + } + } + invalidateState(1<<mActiveTrack); + } + return NO_ERROR; + } else if (name == AUXLEVEL) { + track_t& track = mState.tracks[ mActiveTrack ]; + if (track.auxLevel != valueInt) { + LOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt); + track.prevAuxLevel = track.auxLevel << 16; + track.auxLevel = valueInt; + if (target == VOLUME) { + track.prevAuxLevel = valueInt << 16; + track.auxInc = 0; + } else { + int32_t d = (valueInt<<16) - track.prevAuxLevel; + int32_t volInc = d / int32_t(mState.frameCount); + track.auxInc = volInc; + if (volInc == 0) { + track.prevAuxLevel = valueInt << 16; } } invalidateState(1<<mActiveTrack); @@ -245,7 +290,7 @@ bool AudioMixer::track_t::doesResample() const } inline -void AudioMixer::track_t::adjustVolumeRamp() +void AudioMixer::track_t::adjustVolumeRamp(bool aux) { for (int i=0 ; i<2 ; i++) { if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) || @@ -254,6 +299,13 @@ void AudioMixer::track_t::adjustVolumeRamp() prevVolume[i] = volume[i]<<16; } } + if (aux) { + if (((auxInc>0) && (((prevAuxLevel+auxInc)>>16) >= auxLevel)) || + ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) { + auxInc = 0; + prevAuxLevel = auxLevel<<16; + } + } } @@ -265,13 +317,13 @@ status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer) -void AudioMixer::process(void* output) +void AudioMixer::process() { - mState.hook(&mState, output); + mState.hook(&mState); } -void AudioMixer::process__validate(state_t* state, void* output) +void AudioMixer::process__validate(state_t* state) { LOGW_IF(!state->needsChanged, "in process__validate() but nothing's invalid"); @@ -308,7 +360,10 @@ void AudioMixer::process__validate(state_t* state, void* output) n |= NEEDS_CHANNEL_1 + t.channelCount - 1; n |= NEEDS_FORMAT_16; n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED; - + if (t.auxLevel != 0 && t.auxBuffer != NULL) { + n |= NEEDS_AUX_ENABLED; + } + if (t.volumeInc[0]|t.volumeInc[1]) { volumeRamp = 1; } else if (!t.doesResample() && t.volumeRL == 0) { @@ -319,6 +374,9 @@ void AudioMixer::process__validate(state_t* state, void* output) if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) { t.hook = track__nop; } else { + if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) { + all16BitsStereoNoResample = 0; + } if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { all16BitsStereoNoResample = 0; resampling = 1; @@ -369,7 +427,7 @@ void AudioMixer::process__validate(state_t* state, void* output) countActiveTracks, state->enabledTracks, all16BitsStereoNoResample, resampling, volumeRamp); - state->hook(state, output); + state->hook(state); // Now that the volume ramp has been done, set optimal state and // track hooks for subsequent mixer process @@ -390,7 +448,7 @@ void AudioMixer::process__validate(state_t* state, void* output) } if (allMuted) { state->hook = process__nop; - } else if (!resampling && all16BitsStereoNoResample) { + } else if (all16BitsStereoNoResample) { if (countActiveTracks == 1) { state->hook = process__OneTrack16BitsStereoNoResampling; } @@ -481,30 +539,44 @@ int32_t mulRL(int left, uint32_t inRL, uint32_t vRL) } -void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux) { t->resampler->setSampleRate(t->sampleRate); // ramp gain - resample to temp buffer and scale/mix in 2nd step - if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + if (aux != NULL) { + // always resample with unity gain when sending to auxiliary buffer to be able + // to apply send level after resampling + // TODO: modify each resampler to support aux channel? t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); t->resampler->resample(temp, outFrameCount, t->bufferProvider); - volumeRampStereo(t, out, outFrameCount, temp); - } + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) { + volumeRampStereo(t, out, outFrameCount, temp, aux); + } else { + volumeStereo(t, out, outFrameCount, temp, aux); + } + } else { + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN); + memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t)); + t->resampler->resample(temp, outFrameCount, t->bufferProvider); + volumeRampStereo(t, out, outFrameCount, temp, aux); + } - // constant gain - else { - t->resampler->setVolume(t->volume[0], t->volume[1]); - t->resampler->resample(out, outFrameCount, t->bufferProvider); + // constant gain + else { + t->resampler->setVolume(t->volume[0], t->volume[1]); + t->resampler->resample(out, outFrameCount, t->bufferProvider); + } } } -void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp) +void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux) { } -void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux) { int32_t vl = t->prevVolume[0]; int32_t vr = t->prevVolume[1]; @@ -514,98 +586,238 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i //LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], // (vl + vlInc*frameCount)/65536.0f, frameCount); - + // ramp volume - do { - *out++ += (vl >> 16) * (*temp++ >> 12); - *out++ += (vr >> 16) * (*temp++ >> 12); - vl += vlInc; - vr += vrInc; - } while (--frameCount); + if UNLIKELY(aux != NULL) { + int32_t va = t->prevAuxLevel; + const int32_t vaInc = t->auxInc; + int32_t l; + int32_t r; + do { + l = (*temp++ >> 12); + r = (*temp++ >> 12); + *out++ += (vl >> 16) * l; + *out++ += (vr >> 16) * r; + *aux++ += (va >> 17) * (l + r); + vl += vlInc; + vr += vrInc; + va += vaInc; + } while (--frameCount); + t->prevAuxLevel = va; + } else { + do { + *out++ += (vl >> 16) * (*temp++ >> 12); + *out++ += (vr >> 16) * (*temp++ >> 12); + vl += vlInc; + vr += vrInc; + } while (--frameCount); + } t->prevVolume[0] = vl; t->prevVolume[1] = vr; - t->adjustVolumeRamp(); + t->adjustVolumeRamp((aux != NULL)); } -void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux) { - int16_t const *in = static_cast<int16_t const *>(t->in); - - // ramp gain - if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { - int32_t vl = t->prevVolume[0]; - int32_t vr = t->prevVolume[1]; - const int32_t vlInc = t->volumeInc[0]; - const int32_t vrInc = t->volumeInc[1]; - - // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", - // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], - // (vl + vlInc*frameCount)/65536.0f, frameCount); + const int16_t vl = t->volume[0]; + const int16_t vr = t->volume[1]; + if UNLIKELY(aux != NULL) { + const int16_t va = (int16_t)t->auxLevel; do { - *out++ += (vl >> 16) * (int32_t) *in++; - *out++ += (vr >> 16) * (int32_t) *in++; - vl += vlInc; - vr += vrInc; + int16_t l = (int16_t)(*temp++ >> 12); + int16_t r = (int16_t)(*temp++ >> 12); + out[0] = mulAdd(l, vl, out[0]); + int16_t a = (int16_t)(((int32_t)l + r) >> 1); + out[1] = mulAdd(r, vr, out[1]); + out += 2; + aux[0] = mulAdd(a, va, aux[0]); + aux++; } while (--frameCount); - - t->prevVolume[0] = vl; - t->prevVolume[1] = vr; - t->adjustVolumeRamp(); - } - - // constant gain - else { - const uint32_t vrl = t->volumeRL; + } else { do { - uint32_t rl = *reinterpret_cast<uint32_t const *>(in); - in += 2; - out[0] = mulAddRL(1, rl, vrl, out[0]); - out[1] = mulAddRL(0, rl, vrl, out[1]); + int16_t l = (int16_t)(*temp++ >> 12); + int16_t r = (int16_t)(*temp++ >> 12); + out[0] = mulAdd(l, vl, out[0]); + out[1] = mulAdd(r, vr, out[1]); out += 2; } while (--frameCount); } +} + +void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux) +{ + int16_t const *in = static_cast<int16_t const *>(t->in); + + if UNLIKELY(aux != NULL) { + int32_t l; + int32_t r; + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + int32_t va = t->prevAuxLevel; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + const int32_t vaInc = t->auxInc; + // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + l = (int32_t)*in++; + r = (int32_t)*in++; + *out++ += (vl >> 16) * l; + *out++ += (vr >> 16) * r; + *aux++ += (va >> 17) * (l + r); + vl += vlInc; + vr += vrInc; + va += vaInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->prevAuxLevel = va; + t->adjustVolumeRamp(true); + } + + // constant gain + else { + const uint32_t vrl = t->volumeRL; + const int16_t va = (int16_t)t->auxLevel; + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1); + in += 2; + out[0] = mulAddRL(1, rl, vrl, out[0]); + out[1] = mulAddRL(0, rl, vrl, out[1]); + out += 2; + aux[0] = mulAdd(a, va, aux[0]); + aux++; + } while (--frameCount); + } + } else { + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + // LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + *out++ += (vl >> 16) * (int32_t) *in++; + *out++ += (vr >> 16) * (int32_t) *in++; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(false); + } + + // constant gain + else { + const uint32_t vrl = t->volumeRL; + do { + uint32_t rl = *reinterpret_cast<uint32_t const *>(in); + in += 2; + out[0] = mulAddRL(1, rl, vrl, out[0]); + out[1] = mulAddRL(0, rl, vrl, out[1]); + out += 2; + } while (--frameCount); + } + } t->in = in; } -void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp) +void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux) { int16_t const *in = static_cast<int16_t const *>(t->in); - // ramp gain - if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { - int32_t vl = t->prevVolume[0]; - int32_t vr = t->prevVolume[1]; - const int32_t vlInc = t->volumeInc[0]; - const int32_t vrInc = t->volumeInc[1]; + if UNLIKELY(aux != NULL) { + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + int32_t va = t->prevAuxLevel; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + const int32_t vaInc = t->auxInc; - // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", - // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], - // (vl + vlInc*frameCount)/65536.0f, frameCount); + // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); - do { - int32_t l = *in++; - *out++ += (vl >> 16) * l; - *out++ += (vr >> 16) * l; - vl += vlInc; - vr += vrInc; - } while (--frameCount); - - t->prevVolume[0] = vl; - t->prevVolume[1] = vr; - t->adjustVolumeRamp(); - } - // constant gain - else { - const int16_t vl = t->volume[0]; - const int16_t vr = t->volume[1]; - do { - int16_t l = *in++; - out[0] = mulAdd(l, vl, out[0]); - out[1] = mulAdd(l, vr, out[1]); - out += 2; - } while (--frameCount); + do { + int32_t l = *in++; + *out++ += (vl >> 16) * l; + *out++ += (vr >> 16) * l; + *aux++ += (va >> 16) * l; + vl += vlInc; + vr += vrInc; + va += vaInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->prevAuxLevel = va; + t->adjustVolumeRamp(true); + } + // constant gain + else { + const int16_t vl = t->volume[0]; + const int16_t vr = t->volume[1]; + const int16_t va = (int16_t)t->auxLevel; + do { + int16_t l = *in++; + out[0] = mulAdd(l, vl, out[0]); + out[1] = mulAdd(l, vr, out[1]); + out += 2; + aux[0] = mulAdd(l, va, aux[0]); + aux++; + } while (--frameCount); + } + } else { + // ramp gain + if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) { + int32_t vl = t->prevVolume[0]; + int32_t vr = t->prevVolume[1]; + const int32_t vlInc = t->volumeInc[0]; + const int32_t vrInc = t->volumeInc[1]; + + // LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d", + // t, vlInc/65536.0f, vl/65536.0f, t->volume[0], + // (vl + vlInc*frameCount)/65536.0f, frameCount); + + do { + int32_t l = *in++; + *out++ += (vl >> 16) * l; + *out++ += (vr >> 16) * l; + vl += vlInc; + vr += vrInc; + } while (--frameCount); + + t->prevVolume[0] = vl; + t->prevVolume[1] = vr; + t->adjustVolumeRamp(false); + } + // constant gain + else { + const int16_t vl = t->volume[0]; + const int16_t vr = t->volume[1]; + do { + int16_t l = *in++; + out[0] = mulAdd(l, vl, out[0]); + out[1] = mulAdd(l, vr, out[1]); + out += 2; + } while (--frameCount); + } } t->in = in; } @@ -624,37 +836,56 @@ void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c) } // no-op case -void AudioMixer::process__nop(state_t* state, void* output) +void AudioMixer::process__nop(state_t* state) { - // this assumes output 16 bits stereo, no resampling - memset(output, 0, state->frameCount*4); - uint32_t en = state->enabledTracks; - while (en) { - const int i = 31 - __builtin_clz(en); - en &= ~(1<<i); - track_t& t = state->tracks[i]; - size_t outFrames = state->frameCount; - while (outFrames) { - t.buffer.frameCount = outFrames; - t.bufferProvider->getNextBuffer(&t.buffer); - if (!t.buffer.raw) break; - outFrames -= t.buffer.frameCount; - t.bufferProvider->releaseBuffer(&t.buffer); + uint32_t e0 = state->enabledTracks; + size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS; + while (e0) { + // process by group of tracks with same output buffer to + // avoid multiple memset() on same buffer + uint32_t e1 = e0, e2 = e0; + int i = 31 - __builtin_clz(e1); + track_t& t1 = state->tracks[i]; + e2 &= ~(1<<i); + while (e2) { + i = 31 - __builtin_clz(e2); + e2 &= ~(1<<i); + track_t& t2 = state->tracks[i]; + if UNLIKELY(t2.mainBuffer != t1.mainBuffer) { + e1 &= ~(1<<i); + } + } + e0 &= ~(e1); + + memset(t1.mainBuffer, 0, bufSize); + + while (e1) { + i = 31 - __builtin_clz(e1); + e1 &= ~(1<<i); + t1 = state->tracks[i]; + size_t outFrames = state->frameCount; + while (outFrames) { + t1.buffer.frameCount = outFrames; + t1.bufferProvider->getNextBuffer(&t1.buffer); + if (!t1.buffer.raw) break; + outFrames -= t1.buffer.frameCount; + t1.bufferProvider->releaseBuffer(&t1.buffer); + } } } } // generic code without resampling -void AudioMixer::process__genericNoResampling(state_t* state, void* output) +void AudioMixer::process__genericNoResampling(state_t* state) { int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32))); // acquire each track's buffer uint32_t enabledTracks = state->enabledTracks; - uint32_t en = enabledTracks; - while (en) { - const int i = 31 - __builtin_clz(en); - en &= ~(1<<i); + uint32_t e0 = enabledTracks; + while (e0) { + const int i = 31 - __builtin_clz(e0); + e0 &= ~(1<<i); track_t& t = state->tracks[i]; t.buffer.frameCount = state->frameCount; t.bufferProvider->getNextBuffer(&t.buffer); @@ -666,110 +897,156 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output) enabledTracks &= ~(1<<i); } - // this assumes output 16 bits stereo, no resampling - int32_t* out = static_cast<int32_t*>(output); - size_t numFrames = state->frameCount; - do { - memset(outTemp, 0, sizeof(outTemp)); - - en = enabledTracks; - while (en) { - const int i = 31 - __builtin_clz(en); - en &= ~(1<<i); - track_t& t = state->tracks[i]; - size_t outFrames = BLOCKSIZE; - - while (outFrames) { - size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; - if (inFrames) { - (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp); - t.frameCount -= inFrames; - outFrames -= inFrames; + e0 = enabledTracks; + while (e0) { + // process by group of tracks with same output buffer to + // optimize cache use + uint32_t e1 = e0, e2 = e0; + int j = 31 - __builtin_clz(e1); + track_t& t1 = state->tracks[j]; + e2 &= ~(1<<j); + while (e2) { + j = 31 - __builtin_clz(e2); + e2 &= ~(1<<j); + track_t& t2 = state->tracks[j]; + if UNLIKELY(t2.mainBuffer != t1.mainBuffer) { + e1 &= ~(1<<j); + } + } + e0 &= ~(e1); + // this assumes output 16 bits stereo, no resampling + int32_t *out = t1.mainBuffer; + size_t numFrames = 0; + do { + memset(outTemp, 0, sizeof(outTemp)); + e2 = e1; + while (e2) { + const int i = 31 - __builtin_clz(e2); + e2 &= ~(1<<i); + track_t& t = state->tracks[i]; + size_t outFrames = BLOCKSIZE; + int32_t *aux = NULL; + if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) { + aux = t.auxBuffer + numFrames; } - if (t.frameCount == 0 && outFrames) { - t.bufferProvider->releaseBuffer(&t.buffer); - t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames); - t.bufferProvider->getNextBuffer(&t.buffer); - t.in = t.buffer.raw; - if (t.in == NULL) { - enabledTracks &= ~(1<<i); - break; + while (outFrames) { + size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; + if (inFrames) { + (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux); + t.frameCount -= inFrames; + outFrames -= inFrames; + if UNLIKELY(aux != NULL) { + aux += inFrames; + } } - t.frameCount = t.buffer.frameCount; - } + if (t.frameCount == 0 && outFrames) { + t.bufferProvider->releaseBuffer(&t.buffer); + t.buffer.frameCount = (state->frameCount - numFrames) - (BLOCKSIZE - outFrames); + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + if (t.in == NULL) { + enabledTracks &= ~(1<<i); + e1 &= ~(1<<i); + break; + } + t.frameCount = t.buffer.frameCount; + } + } } - } - - ditherAndClamp(out, outTemp, BLOCKSIZE); - out += BLOCKSIZE; - numFrames -= BLOCKSIZE; - } while (numFrames); - + ditherAndClamp(out, outTemp, BLOCKSIZE); + out += BLOCKSIZE; + numFrames += BLOCKSIZE; + } while (numFrames < state->frameCount); + } // release each track's buffer - en = enabledTracks; - while (en) { - const int i = 31 - __builtin_clz(en); - en &= ~(1<<i); + e0 = enabledTracks; + while (e0) { + const int i = 31 - __builtin_clz(e0); + e0 &= ~(1<<i); track_t& t = state->tracks[i]; t.bufferProvider->releaseBuffer(&t.buffer); } } -// generic code with resampling -void AudioMixer::process__genericResampling(state_t* state, void* output) + + // generic code with resampling +void AudioMixer::process__genericResampling(state_t* state) { int32_t* const outTemp = state->outputTemp; const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount; memset(outTemp, 0, size); - int32_t* out = static_cast<int32_t*>(output); size_t numFrames = state->frameCount; - uint32_t en = state->enabledTracks; - while (en) { - const int i = 31 - __builtin_clz(en); - en &= ~(1<<i); - track_t& t = state->tracks[i]; + uint32_t e0 = state->enabledTracks; + while (e0) { + // process by group of tracks with same output buffer + // to optimize cache use + uint32_t e1 = e0, e2 = e0; + int j = 31 - __builtin_clz(e1); + track_t& t1 = state->tracks[j]; + e2 &= ~(1<<j); + while (e2) { + j = 31 - __builtin_clz(e2); + e2 &= ~(1<<j); + track_t& t2 = state->tracks[j]; + if UNLIKELY(t2.mainBuffer != t1.mainBuffer) { + e1 &= ~(1<<j); + } + } + e0 &= ~(e1); + int32_t *out = t1.mainBuffer; + while (e1) { + const int i = 31 - __builtin_clz(e1); + e1 &= ~(1<<i); + track_t& t = state->tracks[i]; + int32_t *aux = NULL; + if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) { + aux = t.auxBuffer; + } - // this is a little goofy, on the resampling case we don't - // acquire/release the buffers because it's done by - // the resampler. - if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { - (t.hook)(&t, outTemp, numFrames, state->resampleTemp); - } else { + // this is a little goofy, on the resampling case we don't + // acquire/release the buffers because it's done by + // the resampler. + if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { + (t.hook)(&t, outTemp, numFrames, state->resampleTemp, aux); + } else { - size_t outFrames = numFrames; - - while (outFrames) { - t.buffer.frameCount = outFrames; - t.bufferProvider->getNextBuffer(&t.buffer); - t.in = t.buffer.raw; - // t.in == NULL can happen if the track was flushed just after having - // been enabled for mixing. - if (t.in == NULL) break; - - (t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp); - outFrames -= t.buffer.frameCount; - t.bufferProvider->releaseBuffer(&t.buffer); + size_t outFrames = 0; + + while (outFrames < numFrames) { + t.buffer.frameCount = numFrames - outFrames; + t.bufferProvider->getNextBuffer(&t.buffer); + t.in = t.buffer.raw; + // t.in == NULL can happen if the track was flushed just after having + // been enabled for mixing. + if (t.in == NULL) break; + + if UNLIKELY(aux != NULL) { + aux += outFrames; + } + (t.hook)(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux); + outFrames += t.buffer.frameCount; + t.bufferProvider->releaseBuffer(&t.buffer); + } } } + ditherAndClamp(out, outTemp, numFrames); } - - ditherAndClamp(out, outTemp, numFrames); } // one track, 16 bits stereo without resampling is the most common case -void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output) +void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state) { const int i = 31 - __builtin_clz(state->enabledTracks); const track_t& t = state->tracks[i]; AudioBufferProvider::Buffer& b(t.buffer); - - int32_t* out = static_cast<int32_t*>(output); + + int32_t* out = t.mainBuffer; size_t numFrames = state->frameCount; - + const int16_t vl = t.volume[0]; const int16_t vr = t.volume[1]; const uint32_t vrl = t.volumeRL; @@ -787,7 +1064,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* return; } size_t outFrames = b.frameCount; - + if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) { // volume is boosted, so we might need to clamp even though // we process only one track. @@ -816,7 +1093,9 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* } // 2 tracks is also a common case -void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output) +// NEVER used in current implementation of process__validate() +// only use if the 2 tracks have the same output buffer +void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state) { int i; uint32_t en = state->enabledTracks; @@ -829,24 +1108,25 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void i = 31 - __builtin_clz(en); const track_t& t1 = state->tracks[i]; AudioBufferProvider::Buffer& b1(t1.buffer); - + int16_t const *in0; const int16_t vl0 = t0.volume[0]; const int16_t vr0 = t0.volume[1]; size_t frameCount0 = 0; - + int16_t const *in1; const int16_t vl1 = t1.volume[0]; const int16_t vr1 = t1.volume[1]; size_t frameCount1 = 0; - - int32_t* out = static_cast<int32_t*>(output); + + //FIXME: only works if two tracks use same buffer + int32_t* out = t0.mainBuffer; size_t numFrames = state->frameCount; int16_t const *buff = NULL; - + while (numFrames) { - + if (frameCount0 == 0) { b0.frameCount = numFrames; t0.bufferProvider->getNextBuffer(&b0); @@ -875,13 +1155,13 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void } frameCount1 = b1.frameCount; } - + size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1; numFrames -= outFrames; frameCount0 -= outFrames; frameCount1 -= outFrames; - + do { int32_t l0 = *in0++; int32_t r0 = *in0++; @@ -896,17 +1176,17 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void r = clamp16(r); *out++ = (r<<16) | (l & 0xFFFF); } while (--outFrames); - + if (frameCount0 == 0) { t0.bufferProvider->releaseBuffer(&b0); } if (frameCount1 == 0) { t1.bufferProvider->releaseBuffer(&b1); } - } - + } + if (buff != NULL) { - delete [] buff; + delete [] buff; } } diff --git a/libs/audioflinger/AudioMixer.h b/libs/audioflinger/AudioMixer.h index 15766cd..aee3e17 100644 --- a/libs/audioflinger/AudioMixer.h +++ b/libs/audioflinger/AudioMixer.h @@ -63,11 +63,14 @@ public: // for target TRACK CHANNEL_COUNT = 0x4000, FORMAT = 0x4001, + MAIN_BUFFER = 0x4002, + AUX_BUFFER = 0x4003, // for TARGET RESAMPLE SAMPLE_RATE = 0x4100, // for TARGET VOLUME (8 channels max) VOLUME0 = 0x4200, VOLUME1 = 0x4201, + AUXLEVEL = 0x4210, }; @@ -78,10 +81,10 @@ public: status_t disable(int name); status_t setActiveTrack(int track); - status_t setParameter(int target, int name, int value); + status_t setParameter(int target, int name, void *value); status_t setBufferProvider(AudioBufferProvider* bufferProvider); - void process(void* output); + void process(); uint32_t trackNames() const { return mTrackNames; } @@ -94,6 +97,7 @@ private: NEEDS_FORMAT__MASK = 0x000000F0, NEEDS_MUTE__MASK = 0x00000100, NEEDS_RESAMPLE__MASK = 0x00001000, + NEEDS_AUX__MASK = 0x00010000, }; enum { @@ -107,6 +111,9 @@ private: NEEDS_RESAMPLE_DISABLED = 0x00000000, NEEDS_RESAMPLE_ENABLED = 0x00001000, + + NEEDS_AUX_DISABLED = 0x00000000, + NEEDS_AUX_ENABLED = 0x00010000, }; static inline int32_t applyVolume(int32_t in, int32_t v) { @@ -115,9 +122,10 @@ private: struct state_t; + struct track_t; - typedef void (*mix_t)(state_t* state, void* output); - + typedef void (*mix_t)(state_t* state); + typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux); static const int BLOCKSIZE = 16; // 4 cache lines struct track_t { @@ -131,6 +139,9 @@ private: int32_t prevVolume[2]; int32_t volumeInc[2]; + int32_t auxLevel; + int32_t auxInc; + int32_t prevAuxLevel; uint16_t frameCount; @@ -142,15 +153,17 @@ private: AudioBufferProvider* bufferProvider; mutable AudioBufferProvider::Buffer buffer; - void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp); + hook_t hook; void const* in; // current location in buffer AudioResampler* resampler; uint32_t sampleRate; + int32_t* mainBuffer; + int32_t* auxBuffer; bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); bool doesResample() const; - void adjustVolumeRamp(); + void adjustVolumeRamp(bool aux); }; // pad to 32-bytes to fill cache line @@ -173,18 +186,19 @@ private: void invalidateState(uint32_t mask); - static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp); - static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp); - - static void process__validate(state_t* state, void* output); - static void process__nop(state_t* state, void* output); - static void process__genericNoResampling(state_t* state, void* output); - static void process__genericResampling(state_t* state, void* output); - static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output); - static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output); + static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); + static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); + static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); + static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); + static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux); + static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux); + + static void process__validate(state_t* state); + static void process__nop(state_t* state); + static void process__genericNoResampling(state_t* state); + static void process__genericResampling(state_t* state); + static void process__OneTrack16BitsStereoNoResampling(state_t* state); + static void process__TwoTracks16BitsStereoNoResampling(state_t* state); }; // ---------------------------------------------------------------------------- |