summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/audioflinger/Android.mk6
-rw-r--r--services/audioflinger/FastMixer.cpp454
-rw-r--r--services/audioflinger/FastMixer.h72
-rw-r--r--services/audioflinger/FastMixerState.cpp40
-rw-r--r--services/audioflinger/FastMixerState.h80
-rw-r--r--services/audioflinger/StateQueueInstantiations.cpp26
6 files changed, 678 insertions, 0 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 2f78a7e..d52ed42 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -62,4 +62,10 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_MODULE:= libaudioflinger
+LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp
+
+#LOCAL_CFLAGS += -DFAST_MIXER_STATISTICS
+
+LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
+
include $(BUILD_SHARED_LIBRARY)
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
new file mode 100644
index 0000000..1543152
--- /dev/null
+++ b/services/audioflinger/FastMixer.cpp
@@ -0,0 +1,454 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "FastMixer"
+//#define LOG_NDEBUG 0
+
+#include <sys/atomics.h>
+#include <time.h>
+#include <utils/Log.h>
+#include <system/audio.h>
+#ifdef FAST_MIXER_STATISTICS
+#include <cpustats/CentralTendencyStatistics.h>
+#endif
+#include "AudioMixer.h"
+#include "FastMixer.h"
+
+#define FAST_HOT_IDLE_NS 1000000L // 1 ms: time to sleep while hot idling
+#define FAST_DEFAULT_NS 999999999L // ~1 sec: default time to sleep
+
+namespace android {
+
+// Fast mixer thread
+bool FastMixer::threadLoop()
+{
+ static const FastMixerState initial;
+ const FastMixerState *previous = &initial, *current = &initial;
+ FastMixerState preIdle; // copy of state before we went into idle
+ struct timespec oldTs = {0, 0};
+ bool oldTsValid = false;
+ long slopNs = 0; // accumulated time we've woken up too early (> 0) or too late (< 0)
+ long sleepNs = -1; // -1: busy wait, 0: sched_yield, > 0: nanosleep
+ int fastTrackNames[FastMixerState::kMaxFastTracks]; // handles used by mixer to identify tracks
+ int generations[FastMixerState::kMaxFastTracks]; // last observed mFastTracks[i].mGeneration
+ unsigned i;
+ for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
+ fastTrackNames[i] = -1;
+ generations[i] = 0;
+ }
+ NBAIO_Sink *outputSink = NULL;
+ int outputSinkGen = 0;
+ AudioMixer* mixer = NULL;
+ short *mixBuffer = NULL;
+ enum {UNDEFINED, MIXED, ZEROED} mixBufferState = UNDEFINED;
+ NBAIO_Format format = Format_Invalid;
+ unsigned sampleRate = 0;
+ int fastTracksGen = 0;
+ long periodNs = 0; // expected period; the time required to render one mix buffer
+ long underrunNs = 0; // an underrun is likely if an actual cycle is greater than this value
+ long overrunNs = 0; // an overrun is likely if an actual cycle if less than this value
+ FastMixerDumpState dummyDumpState, *dumpState = &dummyDumpState;
+ bool ignoreNextOverrun = true; // used to ignore initial overrun and first after an underrun
+#ifdef FAST_MIXER_STATISTICS
+ CentralTendencyStatistics cts; // cycle times in seconds
+ static const unsigned kMaxSamples = 1000;
+#endif
+ unsigned coldGen = 0; // last observed mColdGen
+
+ for (;;) {
+
+ // either nanosleep, sched_yield, or busy wait
+ if (sleepNs >= 0) {
+ if (sleepNs > 0) {
+ ALOG_ASSERT(sleepNs < 1000000000);
+ const struct timespec req = {0, sleepNs};
+ nanosleep(&req, NULL);
+ } else {
+ sched_yield();
+ }
+ }
+ // default to long sleep for next cycle
+ sleepNs = FAST_DEFAULT_NS;
+
+ // poll for state change
+ const FastMixerState *next = mSQ.poll();
+ if (next == NULL) {
+ // continue to use the default initial state until a real state is available
+ ALOG_ASSERT(current == &initial && previous == &initial);
+ next = current;
+ }
+
+ FastMixerState::Command command = next->mCommand;
+ if (next != current) {
+
+ // As soon as possible of learning of a new dump area, start using it
+ dumpState = next->mDumpState != NULL ? next->mDumpState : &dummyDumpState;
+
+ // We want to always have a valid reference to the previous (non-idle) state.
+ // However, the state queue only guarantees access to current and previous states.
+ // So when there is a transition from a non-idle state into an idle state, we make a
+ // copy of the last known non-idle state so it is still available on return from idle.
+ // The possible transitions are:
+ // non-idle -> non-idle update previous from current in-place
+ // non-idle -> idle update previous from copy of current
+ // idle -> idle don't update previous
+ // idle -> non-idle don't update previous
+ if (!(current->mCommand & FastMixerState::IDLE)) {
+ if (command & FastMixerState::IDLE) {
+ preIdle = *current;
+ current = &preIdle;
+ oldTsValid = false;
+ ignoreNextOverrun = true;
+ }
+ previous = current;
+ }
+ current = next;
+ }
+#if !LOG_NDEBUG
+ next = NULL; // not referenced again
+#endif
+
+ dumpState->mCommand = command;
+
+ switch (command) {
+ case FastMixerState::INITIAL:
+ case FastMixerState::HOT_IDLE:
+ sleepNs = FAST_HOT_IDLE_NS;
+ continue;
+ case FastMixerState::COLD_IDLE:
+ // only perform a cold idle command once
+ if (current->mColdGen != coldGen) {
+ int32_t *coldFutexAddr = current->mColdFutexAddr;
+ ALOG_ASSERT(coldFutexAddr != NULL);
+ int32_t old = android_atomic_dec(coldFutexAddr);
+ if (old <= 0) {
+ __futex_syscall4(coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
+ }
+ sleepNs = -1;
+ coldGen = current->mColdGen;
+ } else {
+ sleepNs = FAST_HOT_IDLE_NS;
+ }
+ continue;
+ case FastMixerState::EXIT:
+ delete mixer;
+ delete[] mixBuffer;
+ return false;
+ case FastMixerState::MIX:
+ case FastMixerState::WRITE:
+ case FastMixerState::MIX_WRITE:
+ break;
+ default:
+ LOG_FATAL("bad command %d", command);
+ }
+
+ // there is a non-idle state available to us; did the state change?
+ size_t frameCount = current->mFrameCount;
+ if (current != previous) {
+
+ // handle state change here, but since we want to diff the state,
+ // we're prepared for previous == &initial the first time through
+ unsigned previousTrackMask;
+
+ // check for change in output HAL configuration
+ NBAIO_Format previousFormat = format;
+ if (current->mOutputSinkGen != outputSinkGen) {
+ outputSink = current->mOutputSink;
+ outputSinkGen = current->mOutputSinkGen;
+ if (outputSink == NULL) {
+ format = Format_Invalid;
+ sampleRate = 0;
+ } else {
+ format = outputSink->format();
+ sampleRate = Format_sampleRate(format);
+ ALOG_ASSERT(Format_channelCount(format) == 2);
+ }
+ }
+
+ if ((format != previousFormat) || (frameCount != previous->mFrameCount)) {
+ // FIXME to avoid priority inversion, don't delete here
+ delete mixer;
+ mixer = NULL;
+ delete[] mixBuffer;
+ mixBuffer = NULL;
+ if (frameCount > 0 && sampleRate > 0) {
+ // FIXME new may block for unbounded time at internal mutex of the heap
+ // implementation; it would be better to have normal mixer allocate for us
+ // to avoid blocking here and to prevent possible priority inversion
+ mixer = new AudioMixer(frameCount, sampleRate, FastMixerState::kMaxFastTracks);
+ mixBuffer = new short[frameCount * 2];
+ periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
+ underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
+ overrunNs = (frameCount * 250000000LL) / sampleRate; // 0.25
+ } else {
+ periodNs = 0;
+ underrunNs = 0;
+ overrunNs = 0;
+ }
+ mixBufferState = UNDEFINED;
+#if !LOG_NDEBUG
+ for (i = 0; i < FastMixerState::kMaxFastTracks; ++i) {
+ fastTrackNames[i] = -1;
+ }
+#endif
+ // we need to reconfigure all active tracks
+ previousTrackMask = 0;
+ fastTracksGen = current->mFastTracksGen - 1;
+ } else {
+ previousTrackMask = previous->mTrackMask;
+ }
+
+ // check for change in active track set
+ unsigned currentTrackMask = current->mTrackMask;
+ if (current->mFastTracksGen != fastTracksGen) {
+ ALOG_ASSERT(mixBuffer != NULL);
+ int name;
+
+ // process removed tracks first to avoid running out of track names
+ unsigned removedTracks = previousTrackMask & ~currentTrackMask;
+ while (removedTracks != 0) {
+ i = __builtin_ctz(removedTracks);
+ removedTracks &= ~(1 << i);
+ const FastTrack* fastTrack = &current->mFastTracks[i];
+ if (mixer != NULL) {
+ name = fastTrackNames[i];
+ ALOG_ASSERT(name >= 0);
+ mixer->deleteTrackName(name);
+ }
+#if !LOG_NDEBUG
+ fastTrackNames[i] = -1;
+#endif
+ generations[i] = fastTrack->mGeneration;
+ }
+
+ // now process added tracks
+ unsigned addedTracks = currentTrackMask & ~previousTrackMask;
+ while (addedTracks != 0) {
+ i = __builtin_ctz(addedTracks);
+ addedTracks &= ~(1 << i);
+ const FastTrack* fastTrack = &current->mFastTracks[i];
+ AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
+ ALOG_ASSERT(bufferProvider != NULL && fastTrackNames[i] == -1);
+ if (mixer != NULL) {
+ name = mixer->getTrackName();
+ ALOG_ASSERT(name >= 0);
+ fastTrackNames[i] = name;
+ mixer->setBufferProvider(name, bufferProvider);
+ mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
+ (void *) mixBuffer);
+ // newly allocated track names default to full scale volume
+ mixer->enable(name);
+ }
+ generations[i] = fastTrack->mGeneration;
+ }
+
+ // finally process modified tracks; these use the same slot
+ // but may have a different buffer provider or volume provider
+ unsigned modifiedTracks = currentTrackMask & previousTrackMask;
+ while (modifiedTracks != 0) {
+ i = __builtin_ctz(modifiedTracks);
+ modifiedTracks &= ~(1 << i);
+ const FastTrack* fastTrack = &current->mFastTracks[i];
+ if (fastTrack->mGeneration != generations[i]) {
+ AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
+ ALOG_ASSERT(bufferProvider != NULL);
+ if (mixer != NULL) {
+ name = fastTrackNames[i];
+ ALOG_ASSERT(name >= 0);
+ mixer->setBufferProvider(name, bufferProvider);
+ if (fastTrack->mVolumeProvider == NULL) {
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
+ (void *)0x1000);
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
+ (void *)0x1000);
+ }
+ // already enabled
+ }
+ generations[i] = fastTrack->mGeneration;
+ }
+ }
+
+ fastTracksGen = current->mFastTracksGen;
+
+ dumpState->mNumTracks = popcount(currentTrackMask);
+ }
+
+#if 1 // FIXME shouldn't need this
+ // only process state change once
+ previous = current;
+#endif
+ }
+
+ // do work using current state here
+ if ((command & FastMixerState::MIX) && (mixer != NULL)) {
+ ALOG_ASSERT(mixBuffer != NULL);
+ // update volumes
+ unsigned volumeTracks = current->mTrackMask;
+ while (volumeTracks != 0) {
+ i = __builtin_ctz(volumeTracks);
+ volumeTracks &= ~(1 << i);
+ const FastTrack* fastTrack = &current->mFastTracks[i];
+ int name = fastTrackNames[i];
+ ALOG_ASSERT(name >= 0);
+ if (fastTrack->mVolumeProvider != NULL) {
+ uint32_t vlr = fastTrack->mVolumeProvider->getVolumeLR();
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0,
+ (void *)(vlr & 0xFFFF));
+ mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
+ (void *)(vlr >> 16));
+ }
+ }
+ // process() is CPU-bound
+ mixer->process(AudioBufferProvider::kInvalidPTS);
+ mixBufferState = MIXED;
+ } else if (mixBufferState == MIXED) {
+ mixBufferState = UNDEFINED;
+ }
+ if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) {
+ if (mixBufferState == UNDEFINED) {
+ memset(mixBuffer, 0, frameCount * 2 * sizeof(short));
+ mixBufferState = ZEROED;
+ }
+ // FIXME write() is non-blocking and lock-free for a properly implemented NBAIO sink,
+ // but this code should be modified to handle both non-blocking and blocking sinks
+ dumpState->mWriteSequence++;
+ ssize_t framesWritten = outputSink->write(mixBuffer, frameCount);
+ dumpState->mWriteSequence++;
+ if (framesWritten >= 0) {
+ dumpState->mFramesWritten += framesWritten;
+ } else {
+ dumpState->mWriteErrors++;
+ }
+ // FIXME count # of writes blocked excessively, CPU usage, etc. for dump
+ }
+
+ // To be exactly periodic, compute the next sleep time based on current time.
+ // This code doesn't have long-term stability when the sink is non-blocking.
+ // FIXME To avoid drift, use the local audio clock or watch the sink's fill status.
+ struct timespec newTs;
+ int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
+ if (rc == 0) {
+ if (oldTsValid) {
+ time_t sec = newTs.tv_sec - oldTs.tv_sec;
+ long nsec = newTs.tv_nsec - oldTs.tv_nsec;
+ if (nsec < 0) {
+ --sec;
+ nsec += 1000000000;
+ }
+ if (sec > 0 || nsec > underrunNs) {
+ // FIXME only log occasionally
+ ALOGV("underrun: time since last cycle %d.%03ld sec",
+ (int) sec, nsec / 1000000L);
+ dumpState->mUnderruns++;
+ sleepNs = -1;
+ ignoreNextOverrun = true;
+ } else if (nsec < overrunNs) {
+ if (ignoreNextOverrun) {
+ ignoreNextOverrun = false;
+ } else {
+ // FIXME only log occasionally
+ ALOGV("overrun: time since last cycle %d.%03ld sec",
+ (int) sec, nsec / 1000000L);
+ dumpState->mOverruns++;
+ }
+ sleepNs = periodNs - overrunNs;
+ } else {
+ sleepNs = -1;
+ ignoreNextOverrun = false;
+ }
+#ifdef FAST_MIXER_STATISTICS
+ // long-term statistics
+ cts.sample(sec + nsec * 1e-9);
+ if (cts.n() >= kMaxSamples) {
+ dumpState->mMean = cts.mean();
+ dumpState->mMinimum = cts.minimum();
+ dumpState->mMaximum = cts.maximum();
+ dumpState->mStddev = cts.stddev();
+ cts.reset();
+ }
+#endif
+ } else {
+ // first time through the loop
+ oldTsValid = true;
+ sleepNs = periodNs;
+ ignoreNextOverrun = true;
+ }
+ oldTs = newTs;
+ } else {
+ // monotonic clock is broken
+ oldTsValid = false;
+ sleepNs = periodNs;
+ }
+
+ } // for (;;)
+
+ // never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion
+}
+
+FastMixerDumpState::FastMixerDumpState() :
+ mCommand(FastMixerState::INITIAL), mWriteSequence(0), mFramesWritten(0),
+ mNumTracks(0), mWriteErrors(0), mUnderruns(0), mOverruns(0)
+#ifdef FAST_MIXER_STATISTICS
+ , mMean(0.0), mMinimum(0.0), mMaximum(0.0), mStddev(0.0)
+#endif
+{
+}
+
+FastMixerDumpState::~FastMixerDumpState()
+{
+}
+
+void FastMixerDumpState::dump(int fd)
+{
+#define COMMAND_MAX 32
+ char string[COMMAND_MAX];
+ switch (mCommand) {
+ case FastMixerState::INITIAL:
+ strcpy(string, "INITIAL");
+ break;
+ case FastMixerState::HOT_IDLE:
+ strcpy(string, "HOT_IDLE");
+ break;
+ case FastMixerState::COLD_IDLE:
+ strcpy(string, "COLD_IDLE");
+ break;
+ case FastMixerState::EXIT:
+ strcpy(string, "EXIT");
+ break;
+ case FastMixerState::MIX:
+ strcpy(string, "MIX");
+ break;
+ case FastMixerState::WRITE:
+ strcpy(string, "WRITE");
+ break;
+ case FastMixerState::MIX_WRITE:
+ strcpy(string, "MIX_WRITE");
+ break;
+ default:
+ snprintf(string, COMMAND_MAX, "%d", mCommand);
+ break;
+ }
+ fdprintf(fd, "FastMixer command=%s writeSequence=%u framesWritten=%u\n"
+ " numTracks=%u writeErrors=%u underruns=%u overruns=%u\n",
+ string, mWriteSequence, mFramesWritten,
+ mNumTracks, mWriteErrors, mUnderruns, mOverruns);
+#ifdef FAST_MIXER_STATISTICS
+ fdprintf(fd, " cycle time in ms: mean=%.1f min=%.1f max=%.1f stddev=%.1f\n",
+ mMean*1e3, mMinimum*1e3, mMaximum*1e3, mStddev*1e3);
+#endif
+}
+
+} // namespace android
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
new file mode 100644
index 0000000..b24f2eb
--- /dev/null
+++ b/services/audioflinger/FastMixer.h
@@ -0,0 +1,72 @@
+/*
+ * 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_FAST_MIXER_H
+#define ANDROID_AUDIO_FAST_MIXER_H
+
+#include <utils/Thread.h>
+extern "C" {
+#include "../private/bionic_futex.h"
+}
+#include "StateQueue.h"
+#include "FastMixerState.h"
+
+namespace android {
+
+typedef StateQueue<FastMixerState> FastMixerStateQueue;
+
+class FastMixer : public Thread {
+
+public:
+ FastMixer() : Thread(false /*canCallJava*/) { }
+ virtual ~FastMixer() { }
+
+ FastMixerStateQueue* sq() { return &mSQ; }
+
+private:
+ virtual bool threadLoop();
+ FastMixerStateQueue mSQ;
+
+}; // class FastMixer
+
+// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys.
+// Since used non-atomically, only POD types are permitted, and the contents can't be trusted.
+// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer.
+struct FastMixerDumpState {
+ FastMixerDumpState();
+ /*virtual*/ ~FastMixerDumpState();
+
+ void dump(int fd);
+
+ FastMixerState::Command mCommand; // current command
+ uint32_t mWriteSequence; // incremented before and after each write()
+ uint32_t mFramesWritten; // total number of frames written successfully
+ uint32_t mNumTracks; // total number of active fast tracks
+ uint32_t mWriteErrors; // total number of write() errors
+ uint32_t mUnderruns; // total number of underruns
+ uint32_t mOverruns; // total number of overruns
+#ifdef FAST_MIXER_STATISTICS
+ // cycle times in seconds
+ float mMean;
+ float mMinimum;
+ float mMaximum;
+ float mStddev;
+#endif
+};
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_FAST_MIXER_H
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
new file mode 100644
index 0000000..4eacacf
--- /dev/null
+++ b/services/audioflinger/FastMixerState.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#include "FastMixerState.h"
+
+namespace android {
+
+FastTrack::FastTrack() :
+ mBufferProvider(NULL), mVolumeProvider(NULL), mGeneration(0)
+{
+}
+
+FastTrack::~FastTrack()
+{
+}
+
+FastMixerState::FastMixerState() :
+ mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0),
+ mFrameCount(0), mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0), mDumpState(NULL)
+{
+}
+
+FastMixerState::~FastMixerState()
+{
+}
+
+} // namespace android
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
new file mode 100644
index 0000000..64171ac
--- /dev/null
+++ b/services/audioflinger/FastMixerState.h
@@ -0,0 +1,80 @@
+/*
+ * 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_FAST_MIXER_STATE_H
+#define ANDROID_AUDIO_FAST_MIXER_STATE_H
+
+#include "AudioBufferProvider.h"
+#include "NBAIO.h"
+
+namespace android {
+
+struct FastMixerDumpState;
+
+class VolumeProvider {
+public:
+ // Return the track volume in U4_12 format: left in lower half, right in upper half. The
+ // provider implementation is responsible for validating that the return value is in range.
+ virtual uint32_t getVolumeLR() = 0;
+protected:
+ VolumeProvider() { }
+ virtual ~VolumeProvider() { }
+};
+
+// Represents the state of a fast track
+struct FastTrack {
+ FastTrack();
+ /*virtual*/ ~FastTrack();
+
+ AudioBufferProvider* mBufferProvider; // must not be NULL
+ VolumeProvider* mVolumeProvider; // optional; if NULL then full-scale
+ int mGeneration; // increment when any field is assigned
+};
+
+// Represents a single state of the fast mixer
+struct FastMixerState {
+ FastMixerState();
+ /*virtual*/ ~FastMixerState();
+
+ static const unsigned kMaxFastTracks = 8; // must be between 2 and 32 inclusive
+
+ // all pointer fields use raw pointers; objects are owned and ref-counted by the normal mixer
+ FastTrack mFastTracks[kMaxFastTracks];
+ int mFastTracksGen; // increment when any mFastTracks[i].mGeneration is incremented
+ unsigned mTrackMask; // bit i is set if and only if mFastTracks[i] != NULL
+ NBAIO_Sink* mOutputSink; // HAL output device, must already be negotiated
+ int mOutputSinkGen; // increment when mOutputSink is assigned
+ size_t mFrameCount; // number of frames per fast mix buffer
+ enum Command {
+ INITIAL = 0, // used only for the initial state
+ HOT_IDLE = 1, // do nothing
+ COLD_IDLE = 2, // wait for the futex
+ IDLE = 3, // either HOT_IDLE or COLD_IDLE
+ EXIT = 4, // exit from thread
+ // The following commands also process configuration changes, and can be "or"ed:
+ MIX = 0x8, // mix tracks
+ WRITE = 0x10, // write to output sink
+ MIX_WRITE = 0x18, // mix tracks and write to output sink
+ } mCommand;
+ int32_t* mColdFutexAddr; // for COLD_IDLE only, pointer to the associated futex
+ unsigned mColdGen; // increment when COLD_IDLE is requested so it's only performed once
+ // This might be a one-time configuration rather than per-state
+ FastMixerDumpState* mDumpState; // if non-NULL, then update dump state periodically
+}; // struct FastMixerState
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_FAST_MIXER_STATE_H
diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
new file mode 100644
index 0000000..077582f
--- /dev/null
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#include "FastMixerState.h"
+#include "StateQueue.h"
+
+// FIXME hack for gcc
+
+namespace android {
+
+template class StateQueue<FastMixerState>; // typedef FastMixerStateQueue
+
+}