diff options
author | Eric Laurent <elaurent@google.com> | 2010-06-01 23:49:17 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2010-06-03 03:21:53 -0700 |
commit | 65b65459e6ac59f8a257009df8014467ae0838ee (patch) | |
tree | 5f9ba01afd1e0ffe85f4b700c1a16f9b998c807d /libs | |
parent | 501b2b400a15a43849ff84c5da4e68d23ed726cf (diff) | |
download | frameworks_base-65b65459e6ac59f8a257009df8014467ae0838ee.zip frameworks_base-65b65459e6ac59f8a257009df8014467ae0838ee.tar.gz frameworks_base-65b65459e6ac59f8a257009df8014467ae0838ee.tar.bz2 |
Issue 2667801: [Audio Effect Framework] AudioFlinger, AudioMixer AudioTrack modifications.
First drop of audio framework modifications for audio effects support.
- AudioTrack/AudioRecord:
Added support for auxiliary effects in AudioTrack
Added support for audio sessions
Fixed left right channel inversion in setVolume()
- IAudioFlinger:
Added interface methods for effect enumeraiton and instantiation
Added support for audio sessions.
- IAudioTrack:
Added method to attach auxiliary effect.
- AudioFlinger
Created new classes to control effect engines in effect library and manage effect connections to tracks or
output mix:
EffectModule: wrapper object controlling the effect engine implementation in the effect library. There
is one EffectModule per instance of an effect in a given audio session
EffectChain: group of effects associated to one audio session. There is one EffectChain per audio session.
EffectChain for session 0 is for output mix effects, other chains are attached to audio tracks
with same session ID. Each chain contains a variable number of EffectModules
EffectHandle: implements the IEffect interface. There is one EffectHandle object for each application
controlling (or using) an effect module. THe EffectModule maintians a list of EffectHandles.
Added support for effect modules and effect chains creation in PlaybackThread.
modified mixer thread loop to allow track volume control by effect modules and call effect processing.
-AudioMixer
Each track now specifies its output buffer used by mixer for accumulation
Modified mixer process functions to process tracks by groups of tracks with same buffer
Modified track process functions to support accumulation to auxiliary channel
Change-Id: I26d5f7c9e070a89bdd383e1a659f8b7ca150379c
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); }; // ---------------------------------------------------------------------------- |