AudioFlinger normal mixer uses FastMixer
Change-Id: I3131bb22d2d057e9197a2ebfa6aa1cfaab9e5321
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 7202b8b..4fbc67d 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -68,6 +68,6 @@
LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
-LOCAL_CFLAGS += -UHAVE_REQUEST_PRIORITY
+LOCAL_CFLAGS += -UHAVE_REQUEST_PRIORITY -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE -USOAKER
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 865051f..de60942 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -72,6 +72,18 @@
#include <common_time/cc_helper.h>
#include <common_time/local_clock.h>
+#include "FastMixer.h"
+
+// NBAIO implementations
+#include "AudioStreamOutSink.h"
+#include "MonoPipe.h"
+#include "MonoPipeReader.h"
+#include "SourceAudioBufferProvider.h"
+
+#ifdef SOAKER
+#include "Soaker.h"
+#endif
+
// ----------------------------------------------------------------------------
// Note: the following macro is used for extremely verbose logging message. In
@@ -121,6 +133,9 @@
// maximum divider applied to the active sleep time in the mixer thread loop
static const uint32_t kMaxThreadSleepTimeShift = 2;
+// minimum normal mix buffer size, expressed in milliseconds rather than frames
+static const uint32_t kMinNormalMixBufferSizeMs = 20;
+
nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
// ----------------------------------------------------------------------------
@@ -538,6 +553,8 @@
ALOGW("frameCount() unknown thread %d", output);
return 0;
}
+ // FIXME currently returns the normal mixer's frame count to avoid confusing legacy callers;
+ // should examine all callers and fix them to handle smaller counts
return thread->frameCount();
}
@@ -1060,7 +1077,7 @@
uint32_t device, type_t type)
: Thread(false),
mType(type),
- mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0),
+ mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mNormalFrameCount(0),
// mChannelMask
mChannelCount(0),
mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
@@ -1179,7 +1196,9 @@
result.append(buffer);
snprintf(buffer, SIZE, "Sample rate: %d\n", mSampleRate);
result.append(buffer);
- snprintf(buffer, SIZE, "Frame count: %d\n", mFrameCount);
+ snprintf(buffer, SIZE, "HAL frame count: %d\n", mFrameCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Normal frame count: %d\n", mNormalFrameCount);
result.append(buffer);
snprintf(buffer, SIZE, "Channel Count: %d\n", mChannelCount);
result.append(buffer);
@@ -1453,8 +1472,13 @@
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
mMixerStatus(MIXER_IDLE),
mPrevMixerStatus(MIXER_IDLE),
- standbyDelay(AudioFlinger::mStandbyTimeInNsecs)
+ standbyDelay(AudioFlinger::mStandbyTimeInNsecs),
+ mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
+ mFastTrackNewMask(0)
{
+#if !LOG_NDEBUG
+ memset(mFastTrackNewArray, 0, sizeof(mFastTrackNewArray));
+#endif
snprintf(mName, kNameLength, "AudioOut_%X", id);
readOutputParameters();
@@ -1489,9 +1513,25 @@
char buffer[SIZE];
String8 result;
+ result.appendFormat("Output thread %p stream volumes in dB:\n ", this);
+ for (int i = 0; i < AUDIO_STREAM_CNT; ++i) {
+ const stream_type_t *st = &mStreamTypes[i];
+ if (i > 0) {
+ result.appendFormat(", ");
+ }
+ result.appendFormat("%d:%.2g", i, 20.0 * log10(st->volume));
+ if (st->mute) {
+ result.append("M");
+ }
+ }
+ result.append("\n");
+ write(fd, result.string(), result.length());
+ result.clear();
+
snprintf(buffer, SIZE, "Output thread %p tracks\n", this);
result.append(buffer);
- result.append(" Name Clien Typ Fmt Chn mask Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n");
+ result.append(" Name Client Type Fmt Chn mask Session Frames S M F SRate L dB R dB "
+ "Server User Main buf Aux Buf\n");
for (size_t i = 0; i < mTracks.size(); ++i) {
sp<Track> track = mTracks[i];
if (track != 0) {
@@ -1502,7 +1542,8 @@
snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
result.append(buffer);
- result.append(" Name Clien Typ Fmt Chn mask Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n");
+ result.append(" Name Client Type Fmt Chn mask Session Frames S M F SRate L dB R dB "
+ "Server User Main buf Aux Buf\n");
for (size_t i = 0; i < mActiveTracks.size(); ++i) {
sp<Track> track = mActiveTracks[i].promote();
if (track != 0) {
@@ -1588,15 +1629,11 @@
(
(sharedBuffer != 0)
) ||
- // use case 2: callback handler and small power-of-2 frame count
+ // use case 2: callback handler and frame count at least as large as HAL
(
(tid != -1) &&
// FIXME supported frame counts should not be hard-coded
- (
- (frameCount == 128) ||
- (frameCount == 256) ||
- (frameCount == 512)
- )
+ frameCount >= (int) mFrameCount // FIXME int cast is due to wrong parameter type
)
) &&
// PCM data
@@ -1604,13 +1641,27 @@
// mono or stereo
( (channelMask == AUDIO_CHANNEL_OUT_MONO) ||
(channelMask == AUDIO_CHANNEL_OUT_STEREO) ) &&
+#ifndef FAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
// hardware sample rate
- (sampleRate == mSampleRate)
+ (sampleRate == mSampleRate) &&
+#endif
+ // normal mixer has an associated fast mixer
+ hasFastMixer() &&
+ // there are sufficient fast track slots available
+ (mFastTrackAvailMask != 0)
// FIXME test that MixerThread for this fast track has a capable output HAL
// FIXME add a permission test also?
) ) {
- ALOGW("AUDIO_OUTPUT_FLAG_FAST denied");
+ ALOGW("AUDIO_POLICY_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d "
+ "mFrameCount=%d format=%d isLinear=%d channelMask=%d sampleRate=%d mSampleRate=%d "
+ "hasFastMixer=%d tid=%d fastTrackAvailMask=%#x",
+ isTimed, sharedBuffer.get(), frameCount, mFrameCount, format,
+ audio_is_linear_pcm(format),
+ channelMask, sampleRate, mSampleRate, hasFastMixer(), tid, mFastTrackAvailMask);
flags &= ~IAudioFlinger::TRACK_FAST;
+ if (0 < frameCount && frameCount < (int) mNormalFrameCount) {
+ frameCount = mNormalFrameCount;
+ }
}
if (mType == DIRECT) {
@@ -1821,7 +1872,7 @@
desc.channels = mChannelMask;
desc.samplingRate = mSampleRate;
desc.format = mFormat;
- desc.frameCount = mFrameCount;
+ desc.frameCount = mNormalFrameCount; // FIXME see AudioFlinger::frameCount(audio_io_handle_t)
desc.latency = latency();
param2 = &desc;
break;
@@ -1843,12 +1894,29 @@
mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
mFrameSize = audio_stream_frame_size(&mOutput->stream->common);
mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize;
+ if (mFrameCount & 15) {
+ ALOGW("HAL output buffer size is %u frames but AudioMixer requires multiples of 16 frames",
+ mFrameCount);
+ }
+
+ // Calculate size of normal mix buffer
+ if (mType == MIXER) {
+ size_t minNormalFrameCount = (kMinNormalMixBufferSizeMs * mSampleRate) / 1000;
+ mNormalFrameCount = ((minNormalFrameCount + mFrameCount - 1) / mFrameCount) * mFrameCount;
+ if (mNormalFrameCount & 15) {
+ ALOGW("Normal mix buffer size is %u frames but AudioMixer requires multiples of 16 "
+ "frames", mNormalFrameCount);
+ }
+ } else {
+ mNormalFrameCount = mFrameCount;
+ }
+ ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount, mNormalFrameCount);
// FIXME - Current mixer implementation only supports stereo output: Always
// Allocate a stereo buffer even if HW output is mono.
delete[] mMixBuffer;
- mMixBuffer = new int16_t[mFrameCount * 2];
- memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t));
+ mMixBuffer = new int16_t[mNormalFrameCount * 2];
+ memset(mMixBuffer, 0, mNormalFrameCount * 2 * sizeof(int16_t));
// force reconfiguration of effect chains and engines to take new buffer size and audio
// parameters into account
@@ -1925,6 +1993,11 @@
Mutex::Autolock _l(mLock);
AudioStreamOut *output = mOutput;
mOutput = NULL;
+ // FIXME FastMixer might also have a raw ptr to mOutputSink;
+ // must push a NULL and wait for ack
+ mOutputSink.clear();
+ mPipeSink.clear();
+ mNormalSink.clear();
return output;
}
@@ -1943,7 +2016,7 @@
// decoding and transfer time. So sleeping for half of the latency would likely cause
// underruns
if (audio_is_a2dp_device((audio_devices_t)mDevice)) {
- return (uint32_t)((uint32_t)((mFrameCount * 1000) / mSampleRate) * 1000);
+ return (uint32_t)((uint32_t)((mNormalFrameCount * 1000) / mSampleRate) * 1000);
} else {
return (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2;
}
@@ -1983,17 +2056,130 @@
AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
audio_io_handle_t id, uint32_t device, type_t type)
- : PlaybackThread(audioFlinger, output, id, device, type)
+ : PlaybackThread(audioFlinger, output, id, device, type),
+ // mAudioMixer below
+#ifdef SOAKER
+ mSoaker(NULL),
+#endif
+ // mFastMixer below
+ mFastMixerFutex(0)
+ // mOutputSink below
+ // mPipeSink below
+ // mNormalSink below
{
- mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
+ ALOGV("MixerThread() id=%d device=%d type=%d", id, device, type);
+ ALOGV("mSampleRate=%d, mChannelMask=%d, mChannelCount=%d, mFormat=%d, mFrameSize=%d, "
+ "mFrameCount=%d, mNormalFrameCount=%d",
+ mSampleRate, mChannelMask, mChannelCount, mFormat, mFrameSize, mFrameCount,
+ mNormalFrameCount);
+ mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
+
// FIXME - Current mixer implementation only supports stereo output
if (mChannelCount == 1) {
ALOGE("Invalid audio hardware channel count");
}
+
+ // create an NBAIO sink for the HAL output stream, and negotiate
+ mOutputSink = new AudioStreamOutSink(output->stream);
+ size_t numCounterOffers = 0;
+ const NBAIO_Format offers[1] = {Format_from_SR_C(mSampleRate, mChannelCount)};
+ ssize_t index = mOutputSink->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+
+ // initialize fast mixer if needed
+ if (mFrameCount < mNormalFrameCount) {
+
+ // create a MonoPipe to connect our submix to FastMixer
+ NBAIO_Format format = mOutputSink->format();
+ // frame count will be rounded up to a power of 2, so this formula should work well
+ MonoPipe *monoPipe = new MonoPipe((mNormalFrameCount * 3) / 2, format,
+ true /*writeCanBlock*/);
+ const NBAIO_Format offers[1] = {format};
+ size_t numCounterOffers = 0;
+ ssize_t index = monoPipe->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mPipeSink = monoPipe;
+
+#ifdef SOAKER
+ // create a soaker as workaround for governor issues
+ mSoaker = new Soaker();
+ // FIXME Soaker should only run when needed, i.e. when FastMixer is not in COLD_IDLE
+ mSoaker->run("Soaker", PRIORITY_LOWEST);
+#endif
+
+ // create fast mixer and configure it initially with just one fast track for our submix
+ mFastMixer = new FastMixer();
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ FastTrack *fastTrack = &state->mFastTracks[0];
+ // wrap the source side of the MonoPipe to make it an AudioBufferProvider
+ fastTrack->mBufferProvider = new SourceAudioBufferProvider(new MonoPipeReader(monoPipe));
+ fastTrack->mVolumeProvider = NULL;
+ fastTrack->mGeneration++;
+ state->mFastTracksGen++;
+ state->mTrackMask = 1;
+ // fast mixer will use the HAL output sink
+ state->mOutputSink = mOutputSink.get();
+ state->mOutputSinkGen++;
+ state->mFrameCount = mFrameCount;
+ state->mCommand = FastMixerState::COLD_IDLE;
+ // already done in constructor initialization list
+ //mFastMixerFutex = 0;
+ state->mColdFutexAddr = &mFastMixerFutex;
+ state->mColdGen++;
+ state->mDumpState = &mFastMixerDumpState;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+
+ // start the fast mixer
+ mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO);
+#ifdef HAVE_REQUEST_PRIORITY
+ pid_t tid = mFastMixer->getTid();
+ int err = requestPriority(getpid_cached, tid, 2);
+ if (err != 0) {
+ ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
+ 2, getpid_cached, tid, err);
+ }
+#endif
+
+ } else {
+ mFastMixer = NULL;
+ }
+ mNormalSink = mOutputSink;
}
AudioFlinger::MixerThread::~MixerThread()
{
+ if (mFastMixer != NULL) {
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ if (state->mCommand == FastMixerState::COLD_IDLE) {
+ int32_t old = android_atomic_inc(&mFastMixerFutex);
+ if (old == -1) {
+ __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
+ }
+ }
+ state->mCommand = FastMixerState::EXIT;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+ mFastMixer->join();
+ // Though the fast mixer thread has exited, it's state queue is still valid.
+ // We'll use that extract the final state which contains one remaining fast track
+ // corresponding to our sub-mix.
+ state = sq->begin();
+ ALOG_ASSERT(state->mTrackMask == 1);
+ FastTrack *fastTrack = &state->mFastTracks[0];
+ ALOG_ASSERT(fastTrack->mBufferProvider != NULL);
+ delete fastTrack->mBufferProvider;
+ sq->end(false /*didModify*/);
+ delete mFastMixer;
+#ifdef SOAKER
+ if (mSoaker != NULL) {
+ mSoaker->requestExitAndWait();
+ }
+ delete mSoaker;
+#endif
+ }
delete mAudioMixer;
}
@@ -2259,9 +2445,10 @@
usleep(sleepTime);
}
- // finally let go of removed track(s), without the lock held
+ // Finally let go of removed track(s), without the lock held
// since we can't guarantee the destructors won't acquire that
- // same lock.
+ // same lock. This will also mutate and push a new fast mixer state.
+ threadLoop_removeTracks(tracksToRemove);
tracksToRemove.clear();
// FIXME I don't understand the need for this here;
@@ -2293,19 +2480,178 @@
return false;
}
+// FIXME This method needs a better name.
+// It pushes a new fast mixer state and returns (via tracksToRemove) a set of tracks to remove.
+void AudioFlinger::MixerThread::threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove)
+{
+ // were any of the removed tracks also fast tracks?
+ unsigned removedMask = 0;
+ for (size_t i = 0; i < tracksToRemove.size(); ++i) {
+ if (tracksToRemove[i]->isFastTrack()) {
+ int j = tracksToRemove[i]->mFastIndex;
+ ALOG_ASSERT(0 < j && j < FastMixerState::kMaxFastTracks);
+ removedMask |= 1 << j;
+ }
+ }
+ Track* newArray[FastMixerState::kMaxFastTracks];
+ unsigned newMask;
+ {
+ AutoMutex _l(mLock);
+ mFastTrackAvailMask |= removedMask;
+ newMask = mFastTrackNewMask;
+ if (newMask) {
+ mFastTrackNewMask = 0;
+ memcpy(newArray, mFastTrackNewArray, sizeof(mFastTrackNewArray));
+#if !LOG_NDEBUG
+ memset(mFastTrackNewArray, 0, sizeof(mFastTrackNewArray));
+#endif
+ }
+ }
+ unsigned changedMask = newMask | removedMask;
+ // are there any newly added or removed fast tracks?
+ if (changedMask) {
+
+ // This assert would be incorrect because it's theoretically possible (though unlikely)
+ // for a track to be created and then removed within the same normal mix cycle:
+ // ALOG_ASSERT(!(newMask & removedMask));
+ // The converse, of removing a track and then creating a new track at the identical slot
+ // within the same normal mix cycle, is impossible because the slot isn't marked available.
+
+ // prepare a new state to push
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ FastMixerStateQueue::block_t block = FastMixerStateQueue::BLOCK_UNTIL_PUSHED;
+ while (changedMask) {
+ int j = __builtin_ctz(changedMask);
+ ALOG_ASSERT(0 < j && j < FastMixerState::kMaxFastTracks);
+ changedMask &= ~(1 << j);
+ FastTrack *fastTrack = &state->mFastTracks[j];
+ // must first do new tracks, then removed tracks, in case same track in both
+ if (newMask & (1 << j)) {
+ ALOG_ASSERT(!(state->mTrackMask & (1 << j)));
+ ALOG_ASSERT(fastTrack->mBufferProvider == NULL &&
+ fastTrack->mVolumeProvider == NULL);
+ Track *track = newArray[j];
+ AudioBufferProvider *abp = track;
+ VolumeProvider *vp = track;
+ fastTrack->mBufferProvider = abp;
+ fastTrack->mVolumeProvider = vp;
+ fastTrack->mSampleRate = track->mSampleRate;
+ fastTrack->mChannelMask = track->mChannelMask;
+ state->mTrackMask |= 1 << j;
+ }
+ if (removedMask & (1 << j)) {
+ ALOG_ASSERT(state->mTrackMask & (1 << j));
+ ALOG_ASSERT(fastTrack->mBufferProvider != NULL &&
+ fastTrack->mVolumeProvider != NULL);
+ fastTrack->mBufferProvider = NULL;
+ fastTrack->mVolumeProvider = NULL;
+ fastTrack->mSampleRate = mSampleRate;
+ fastTrack->mChannelMask = AUDIO_CHANNEL_OUT_STEREO;
+ state->mTrackMask &= ~(1 << j);
+ }
+ fastTrack->mGeneration++;
+ }
+ state->mFastTracksGen++;
+ // if the fast mixer was active, but now there are no fast tracks, then put it in cold idle
+ if (state->mCommand == FastMixerState::MIX_WRITE && state->mTrackMask <= 1) {
+ state->mCommand = FastMixerState::COLD_IDLE;
+ state->mColdFutexAddr = &mFastMixerFutex;
+ state->mColdGen++;
+ mFastMixerFutex = 0;
+ mNormalSink = mOutputSink;
+ block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
+ }
+ sq->end();
+ // If any fast tracks were removed, we must wait for acknowledgement
+ // because we're about to decrement the last sp<> on those tracks.
+ // Similarly if we put it into cold idle, need to wait for acknowledgement
+ // so that it stops doing I/O.
+ if (removedMask) {
+ block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
+ }
+ sq->push(block);
+ }
+ PlaybackThread::threadLoop_removeTracks(tracksToRemove);
+}
+
+void AudioFlinger::MixerThread::threadLoop_write()
+{
+ // FIXME we should only do one push per cycle; confirm this is true
+ // Start the fast mixer if it's not already running
+ if (mFastMixer != NULL) {
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ if (state->mCommand != FastMixerState::MIX_WRITE && state->mTrackMask > 1) {
+ if (state->mCommand == FastMixerState::COLD_IDLE) {
+ int32_t old = android_atomic_inc(&mFastMixerFutex);
+ if (old == -1) {
+ __futex_syscall3(&mFastMixerFutex, FUTEX_WAKE_PRIVATE, 1);
+ }
+ }
+ state->mCommand = FastMixerState::MIX_WRITE;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+ mNormalSink = mPipeSink;
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
+ PlaybackThread::threadLoop_write();
+}
+
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_write()
{
// FIXME rewrite to reduce number of system calls
mLastWriteTime = systemTime();
mInWrite = true;
- mBytesWritten += mixBufferSize;
- int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
- if (bytesWritten < 0) mBytesWritten -= mixBufferSize;
+ int bytesWritten;
+
+ // If an NBAIO sink is present, use it to write the normal mixer's submix
+ if (mNormalSink != 0) {
+#define mBitShift 2 // FIXME
+ size_t count = mixBufferSize >> mBitShift;
+ ssize_t framesWritten = mNormalSink->write(mMixBuffer, count);
+ if (framesWritten > 0) {
+ bytesWritten = framesWritten << mBitShift;
+ } else {
+ bytesWritten = framesWritten;
+ }
+
+ // otherwise use the HAL / AudioStreamOut directly
+ } else {
+ // FIXME legacy, remove
+ bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize);
+ }
+
+ if (bytesWritten > 0) mBytesWritten += mixBufferSize;
mNumWrites++;
mInWrite = false;
}
+void AudioFlinger::MixerThread::threadLoop_standby()
+{
+ // Idle the fast mixer if it's currently running
+ if (mFastMixer != NULL) {
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ if (!(state->mCommand & FastMixerState::IDLE)) {
+ state->mCommand = FastMixerState::COLD_IDLE;
+ state->mColdFutexAddr = &mFastMixerFutex;
+ state->mColdGen++;
+ mFastMixerFutex = 0;
+ sq->end();
+ // BLOCK_UNTIL_PUSHED would be insufficient, as we need it to stop doing I/O now
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED);
+ mNormalSink = mOutputSink;
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
+ PlaybackThread::threadLoop_standby();
+}
+
// shared by MIXER and DIRECT, overridden by DUPLICATING
void AudioFlinger::PlaybackThread::threadLoop_standby()
{
@@ -2381,6 +2727,7 @@
size_t count = mActiveTracks.size();
size_t mixedTracks = 0;
size_t tracksWithEffect = 0;
+ size_t fastTracks = 0;
float masterVolume = mMasterVolume;
bool masterMute = mMasterMute;
@@ -2403,6 +2750,20 @@
// this const just means the local variable doesn't change
Track* const track = t.get();
+
+ if (track->isFastTrack()) {
+ // cache the combined master volume and stream type volume for fast mixer;
+ // this lacks any synchronization or barrier so VolumeProvider may read a stale value
+ track->mCachedVolume = masterVolume * mStreamTypes[track->streamType()].volume;
+ ++fastTracks;
+ if (track->isTerminated()) {
+ tracksToRemove->add(track);
+ }
+ continue;
+ }
+
+ { // local variable scope to avoid goto warning
+
audio_track_cblk_t* cblk = track->cblk();
// The first time a track is added we wait
@@ -2417,10 +2778,10 @@
if (!track->isStopped() && !track->isPausing() &&
(mPrevMixerStatus == MIXER_TRACKS_READY)) {
if (t->sampleRate() == (int)mSampleRate) {
- minFrames = mFrameCount;
+ minFrames = mNormalFrameCount;
} else {
// +1 for rounding and +1 for additional sample needed for interpolation
- minFrames = (mFrameCount * t->sampleRate()) / mSampleRate + 1 + 1;
+ minFrames = (mNormalFrameCount * t->sampleRate()) / mSampleRate + 1 + 1;
// add frames already consumed but not yet released by the resampler
// because cblk->framesReady() will include these frames
minFrames += mAudioMixer->getUnreleasedFrames(track->name());
@@ -2603,8 +2964,14 @@
}
mAudioMixer->disable(name);
}
+
+ } // local variable scope to avoid goto warning
+track_is_ready: ;
+
}
+ // FIXME Here is where we would push the new FastMixer state if necessary
+
// remove all the tracks that need to be...
count = tracksToRemove->size();
if (CC_UNLIKELY(count)) {
@@ -2627,10 +2994,15 @@
// 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));
+ if ((mixedTracks != 0 && mixedTracks == tracksWithEffect) || (mixedTracks == 0 && fastTracks > 0)) {
+ // FIXME as a performance optimization, should remember previous zero status
+ memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
}
+ // if any fast tracks, then status is ready
+ if (fastTracks > 0) {
+ mixerStatus = MIXER_TRACKS_READY;
+ }
return mixerStatus;
}
@@ -2655,7 +3027,7 @@
void AudioFlinger::PlaybackThread::cacheParameters_l()
{
- mixBufferSize = mFrameCount * mFrameSize;
+ mixBufferSize = mNormalFrameCount * mFrameSize;
activeSleepTime = activeSleepTimeUs();
idleSleepTime = idleSleepTimeUs();
}
@@ -2692,9 +3064,25 @@
// checkForNewParameters_l() must be called with ThreadBase::mLock held
bool AudioFlinger::MixerThread::checkForNewParameters_l()
{
+ // if !&IDLE, holds the FastMixer state to restore after new parameters processed
+ FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE;
bool reconfig = false;
while (!mNewParameters.isEmpty()) {
+
+ if (mFastMixer != NULL) {
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ if (!(state->mCommand & FastMixerState::IDLE)) {
+ previousCommand = state->mCommand;
+ state->mCommand = FastMixerState::HOT_IDLE;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED);
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
+
status_t status = NO_ERROR;
String8 keyValuePair = mNewParameters[0];
AudioParameter param = AudioParameter(keyValuePair);
@@ -2774,7 +3162,7 @@
// for safety in case readOutputParameters() accesses mAudioMixer (it doesn't)
mAudioMixer = NULL;
readOutputParameters();
- mAudioMixer = new AudioMixer(mFrameCount, mSampleRate);
+ mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
for (size_t i = 0; i < mTracks.size() ; i++) {
int name = getTrackName_l((audio_channel_mask_t)mTracks[i]->mChannelMask);
if (name < 0) break;
@@ -2796,6 +3184,17 @@
// already timed out waiting for the status and will never signal the condition.
mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
}
+
+ if (!(previousCommand & FastMixerState::IDLE)) {
+ ALOG_ASSERT(mFastMixer != NULL);
+ FastMixerStateQueue *sq = mFastMixer->sq();
+ FastMixerState *state = sq->begin();
+ ALOG_ASSERT(state->mCommand == FastMixerState::HOT_IDLE);
+ state->mCommand = previousCommand;
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+ }
+
return reconfig;
}
@@ -2810,17 +3209,22 @@
snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames());
result.append(buffer);
write(fd, result.string(), result.size());
+
+ // Make a non-atomic copy of fast mixer dump state so it won't change underneath us
+ FastMixerDumpState copy = mFastMixerDumpState;
+ copy.dump(fd);
+
return NO_ERROR;
}
uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const
{
- return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
+ return (uint32_t)(((mNormalFrameCount * 1000) / mSampleRate) * 1000) / 2;
}
uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs() const
{
- return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+ return (uint32_t)(((mNormalFrameCount * 1000) / mSampleRate) * 1000);
}
void AudioFlinger::MixerThread::cacheParameters_l()
@@ -2831,7 +3235,7 @@
// Should be reduced to 2x after the vendor fixes the driver issue
// increase threshold again due to low power audio mode. The way this warning
// threshold is calculated and its usefulness should be reconsidered anyway.
- maxPeriod = seconds(mFrameCount) / mSampleRate * 15;
+ maxPeriod = seconds(mNormalFrameCount) / mSampleRate * 15;
}
// ----------------------------------------------------------------------------
@@ -3098,7 +3502,7 @@
sleepTime = idleSleepTime;
}
} else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
- memset (mMixBuffer, 0, mFrameCount * mFrameSize);
+ memset(mMixBuffer, 0, mFrameCount * mFrameSize);
sleepTime = 0;
}
}
@@ -3230,7 +3634,7 @@
memset(mMixBuffer, 0, mixBufferSize);
}
sleepTime = 0;
- writeFrames = mFrameCount;
+ writeFrames = mNormalFrameCount;
}
void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
@@ -3285,7 +3689,7 @@
{
Mutex::Autolock _l(mLock);
// FIXME explain this formula
- int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate();
+ int frameCount = (3 * mNormalFrameCount * mSampleRate) / thread->sampleRate();
OutputTrack *outputTrack = new OutputTrack(thread,
this,
mSampleRate,
@@ -3380,6 +3784,7 @@
// mBufferEnd
mFrameCount(0),
mState(IDLE),
+ mSampleRate(sampleRate),
mFormat(format),
mStepServerFailed(false),
mSessionId(sessionId)
@@ -3548,7 +3953,7 @@
IAudioFlinger::track_flags_t flags)
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId),
mMute(false),
- // mFillingUpStatus ?
+ mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
mStreamType(streamType),
@@ -3557,12 +3962,27 @@
mAuxBuffer(NULL),
mAuxEffectId(0), mHasVolumeController(false),
mPresentationCompleteFrames(0),
- mFlags(flags)
+ mFlags(flags),
+ mFastIndex(-1),
+ mCachedVolume(1.0)
{
if (mCblk != NULL) {
// NOTE: audio_track_cblk_t::frameSize for 8 bit PCM data is based on a sample size of
// 16 bit because data is converted to 16 bit before being stored in buffer by AudioTrack
mCblk->frameSize = audio_is_linear_pcm(format) ? mChannelCount * sizeof(int16_t) : sizeof(uint8_t);
+ if (flags & IAudioFlinger::TRACK_FAST) {
+ mCblk->flags |= CBLK_FAST; // atomic op not needed yet
+ ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
+ int i = __builtin_ctz(thread->mFastTrackAvailMask);
+ ALOG_ASSERT(0 < i && i < FastMixerState::kMaxFastTracks);
+ mFastIndex = i;
+ thread->mFastTrackAvailMask &= ~(1 << i);
+ // Although we've allocated an index, we can't mutate or push a new fast track state
+ // here, because that data structure can only be changed within the normal mixer
+ // threadLoop(). So instead, make a note to mutate and push later.
+ thread->mFastTrackNewArray[i] = this;
+ thread->mFastTrackNewMask |= 1 << i;
+ }
// to avoid leaking a track name, do not allocate one unless there is an mCblk
mName = thread->getTrackName_l((audio_channel_mask_t)channelMask);
if (mName < 0) {
@@ -3617,8 +4037,12 @@
void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
{
uint32_t vlr = mCblk->getVolumeLR();
- snprintf(buffer, size, " %05d %05d %03u %03u 0x%08x %05u %04u %1d %1d %1d %05u %05u %05u 0x%08x 0x%08x 0x%08x 0x%08x\n",
- mName - AudioMixer::TRACK0,
+ if (isFastTrack()) {
+ strcpy(buffer, " fast");
+ } else {
+ sprintf(buffer, " %4d", mName - AudioMixer::TRACK0);
+ }
+ snprintf(&buffer[7], size-7, " %6d %4u %3u 0x%08x %7u %6u %1d %1d %1d %5u %5.2g %5.2g 0x%08x 0x%08x 0x%08x 0x%08x\n",
(mClient == 0) ? getpid_cached : mClient->pid(),
mStreamType,
mFormat,
@@ -3629,8 +4053,8 @@
mMute,
mFillingUpStatus,
mCblk->sampleRate,
- vlr & 0xFFFF,
- vlr >> 16,
+ 20.0 * log10((vlr & 0xFFFF) / 4096.0),
+ 20.0 * log10((vlr >> 16) / 4096.0),
mCblk->server,
mCblk->user,
(int)mMainBuffer,
@@ -3700,6 +4124,8 @@
int triggerSession)
{
status_t status = NO_ERROR;
+ ALOGV("start(%d), calling pid %d session %d",
+ mName, IPCThreadState::self()->getCallingPid(), mSessionId);
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
@@ -3883,6 +4309,32 @@
}
}
+// implement VolumeBufferProvider interface
+
+uint32_t AudioFlinger::PlaybackThread::Track::getVolumeLR()
+{
+ // called by FastMixer, so not allowed to take any locks, block, or do I/O including logs
+ ALOG_ASSERT(isFastTrack() && (mCblk != NULL));
+ uint32_t vlr = mCblk->getVolumeLR();
+ uint32_t vl = vlr & 0xFFFF;
+ uint32_t vr = vlr >> 16;
+ // track volumes come from shared memory, so can't be trusted and must be clamped
+ if (vl > MAX_GAIN_INT) {
+ vl = MAX_GAIN_INT;
+ }
+ if (vr > MAX_GAIN_INT) {
+ vr = MAX_GAIN_INT;
+ }
+ // now apply the cached master volume and stream type volume;
+ // this is trusted but lacks any synchronization or barrier so may be stale
+ float v = mCachedVolume;
+ vl *= v;
+ vr *= v;
+ // re-combine into U4.16
+ vlr = (vr << 16) | (vl & 0xFFFF);
+ // FIXME look at mute, pause, and stop flags
+ return vlr;
+}
// timed audio tracks
@@ -5298,8 +5750,7 @@
AudioSystem::sync_event_t event,
int triggerSession)
{
- // FIXME use tid here
- ALOGV("RecordThread::start tid=%d, event %d, triggerSession %d", tid, event, triggerSession);
+ ALOGV("RecordThread::start event %d, triggerSession %d", event, triggerSession);
sp<ThreadBase> strongMe = this;
status_t status = NO_ERROR;
@@ -5674,6 +6125,7 @@
mFrameSize = audio_stream_frame_size(&mInput->stream->common);
mInputBytes = mInput->stream->common.get_buffer_size(&mInput->stream->common);
mFrameCount = mInputBytes / mFrameSize;
+ mNormalFrameCount = mFrameCount; // not used by record, but used by input effects
mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2)
@@ -6891,7 +7343,7 @@
// 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;
+ size_t numSamples = mNormalFrameCount * mChannelCount;
buffer = new int16_t[numSamples];
memset(buffer, 0, numSamples * sizeof(int16_t));
ALOGV("addEffectChain_l() creating new input buffer %p session %d", buffer, session);
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 6b18945..23fc74d 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -46,6 +46,8 @@
#include <hardware/audio_policy.h>
#include "AudioBufferProvider.h"
+#include "FastMixer.h"
+#include "NBAIO.h"
#include <powermanager/IPowerManager.h>
@@ -56,6 +58,7 @@
class AudioMixer;
class AudioBuffer;
class AudioResampler;
+class FastMixer;
// ----------------------------------------------------------------------------
@@ -425,6 +428,8 @@
uint32_t mFrameCount;
// we don't really need a lock for these
track_state mState;
+ const uint32_t mSampleRate; // initial sample rate only; for tracks which
+ // support dynamic rates, the current value is in control block
const audio_format_t mFormat;
bool mStepServerFailed;
const int mSessionId;
@@ -461,7 +466,9 @@
uint32_t sampleRate() const { return mSampleRate; }
int channelCount() const { return mChannelCount; }
audio_format_t format() const { return mFormat; }
- size_t frameCount() const { return mFrameCount; }
+ // Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
+ // and returns the normal mix buffer's frame count. No API for HAL frame count.
+ size_t frameCount() const { return mNormalFrameCount; }
void wakeUp() { mWaitWorkCV.broadcast(); }
// Should be "virtual status_t requestExitAndWait()" and override same
// method in Thread, but Thread::requestExitAndWait() is not yet virtual.
@@ -586,7 +593,8 @@
const sp<AudioFlinger> mAudioFlinger;
uint32_t mSampleRate;
- size_t mFrameCount;
+ size_t mFrameCount; // output HAL, direct output, record
+ size_t mNormalFrameCount; // normal mixer and effects
uint32_t mChannelMask;
uint16_t mChannelCount;
size_t mFrameSize;
@@ -654,7 +662,7 @@
};
// playback track
- class Track : public TrackBase {
+ class Track : public TrackBase, public VolumeProvider {
public:
Track( PlaybackThread *thread,
const sp<Client>& client,
@@ -691,8 +699,13 @@
int16_t *mainBuffer() const { return mMainBuffer; }
int auxEffectId() const { return mAuxEffectId; }
+#if 0
bool isFastTrack() const
{ return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
+#endif
+
+ // implement FastMixerState::VolumeProvider interface
+ virtual uint32_t getVolumeLR();
protected:
// for numerous
@@ -729,18 +742,19 @@
public:
virtual bool isTimedTrack() const { return false; }
+ bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
protected:
// we don't really need a lock for these
volatile bool mMute;
// FILLED state is used for suppressing volume ramp at begin of playing
- enum {FS_FILLING, FS_FILLED, FS_ACTIVE};
+ enum {FS_INVALID, FS_FILLING, FS_FILLED, FS_ACTIVE};
mutable uint8_t mFillingUpStatus;
int8_t mRetryCount;
const sp<IMemory> mSharedBuffer;
bool mResetDone;
const audio_stream_type_t mStreamType;
- int mName;
+ int mName; // track name on the normal mixer
int16_t *mMainBuffer;
int32_t *mAuxBuffer;
int mAuxEffectId;
@@ -749,6 +763,10 @@
// when this track will be fully rendered
private:
IAudioFlinger::track_flags_t mFlags;
+ int mFastIndex; // index within FastMixerState::mFastTracks[] or -1
+ volatile float mCachedVolume; // combined master volume and stream type volume;
+ // 'volatile' means accessed without lock or
+ // barrier, but is read/written atomically
}; // end of Track
class TimedTrack : public Track {
@@ -899,6 +917,7 @@
virtual void threadLoop_sleepTime() = 0;
virtual void threadLoop_write();
virtual void threadLoop_standby();
+ virtual void threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove) { }
// prepareTracks_l reads and writes mActiveTracks, and also returns the
// pending set of tracks to remove via Vector 'tracksToRemove'. The caller is
@@ -1049,6 +1068,23 @@
// DUPLICATING only
uint32_t writeFrames;
+
+ private:
+ // The HAL output sink is treated as non-blocking, but current implementation is blocking
+ sp<NBAIO_Sink> mOutputSink;
+ // If a fast mixer is present, the blocking pipe sink, otherwise clear
+ sp<NBAIO_Sink> mPipeSink;
+ // The current sink for the normal mixer to write it's (sub)mix, mOutputSink or mPipeSink
+ sp<NBAIO_Sink> mNormalSink;
+ public:
+ virtual bool hasFastMixer() const = 0;
+
+ protected:
+ // accessed by both binder threads and within threadLoop(), lock on mutex needed
+ unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available
+ unsigned mFastTrackNewMask; // bit i set if fast track [i] just created
+ Track* mFastTrackNewArray[FastMixerState::kMaxFastTracks];
+
};
class MixerThread : public PlaybackThread {
@@ -1075,10 +1111,29 @@
virtual void cacheParameters_l();
// threadLoop snippets
+ virtual void threadLoop_write();
+ virtual void threadLoop_standby();
virtual void threadLoop_mix();
virtual void threadLoop_sleepTime();
+ virtual void threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove);
- AudioMixer* mAudioMixer;
+ AudioMixer* mAudioMixer; // normal mixer
+ private:
+#ifdef SOAKER
+ Thread* mSoaker;
+#endif
+ // one-time initialization, no locks required
+ FastMixer* mFastMixer; // non-NULL if there is also a fast mixer
+
+ // contents are not guaranteed to be consistent, no locks required
+ FastMixerDumpState mFastMixerDumpState;
+
+ // accessible only within the threadLoop(), no locks required
+ // mFastMixer->sq() // for mutating and pushing state
+ int32_t mFastMixerFutex; // for cold idle
+
+ public:
+ virtual bool hasFastMixer() const { return mFastMixer != NULL; }
};
class DirectOutputThread : public PlaybackThread {
@@ -1121,6 +1176,8 @@
private:
// prepareTracks_l() tells threadLoop_mix() the name of the single active track
sp<Track> mActiveTrack;
+ public:
+ virtual bool hasFastMixer() const { return false; }
};
class DuplicatingThread : public MixerThread {
@@ -1157,6 +1214,8 @@
uint32_t mWaitTimeMs;
SortedVector < sp<OutputTrack> > outputTracks;
SortedVector < sp<OutputTrack> > mOutputTracks;
+ public:
+ virtual bool hasFastMixer() const { return false; }
};
PlaybackThread *checkPlaybackThread_l(audio_io_handle_t output) const;
diff --git a/services/audioflinger/Soaker.h b/services/audioflinger/Soaker.h
new file mode 100644
index 0000000..43d9d2f
--- /dev/null
+++ b/services/audioflinger/Soaker.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_AUDIO_SOAKER_H
+#define _ANDROID_AUDIO_SOAKER_H
+
+#include <utils/Thread.h>
+
+namespace android {
+
+class Soaker : public Thread {
+public:
+ Soaker() : Thread() { }
+ virtual ~Soaker() { }
+protected:
+ virtual bool threadLoop() {
+ int j = 0;
+ for (;;) {
+ for (int i = 0; i < 10000; ++i) {
+ j += i * i;
+ }
+ if (exitPending()) {
+ return false;
+ }
+ }
+ return j < 555555;
+ }
+};
+
+} // namespace android
+
+#endif // _ANDROID_AUDIO_SOAKER_H