summaryrefslogtreecommitdiffstats
path: root/services/audioflinger
diff options
context:
space:
mode:
Diffstat (limited to 'services/audioflinger')
-rw-r--r--services/audioflinger/Android.mk57
-rw-r--r--services/audioflinger/AudioFlinger.cpp8220
-rw-r--r--services/audioflinger/AudioFlinger.h1660
-rw-r--r--services/audioflinger/AudioMixer.cpp126
-rw-r--r--services/audioflinger/AudioMixer.h41
-rw-r--r--services/audioflinger/AudioPolicyService.cpp204
-rw-r--r--services/audioflinger/AudioPolicyService.h50
-rw-r--r--services/audioflinger/AudioResampler.cpp8
-rw-r--r--services/audioflinger/AudioResampler.h11
-rw-r--r--services/audioflinger/AudioResamplerSinc.cpp661
-rw-r--r--services/audioflinger/AudioResamplerSinc.h17
-rw-r--r--services/audioflinger/AudioWatchdog.cpp5
-rw-r--r--services/audioflinger/Configuration.h44
-rw-r--r--services/audioflinger/Effects.cpp1795
-rw-r--r--services/audioflinger/Effects.h373
-rw-r--r--services/audioflinger/FastMixer.cpp316
-rw-r--r--services/audioflinger/FastMixer.h21
-rw-r--r--services/audioflinger/FastMixerState.cpp5
-rw-r--r--services/audioflinger/FastMixerState.h3
-rw-r--r--services/audioflinger/ISchedulingPolicyService.cpp19
-rw-r--r--services/audioflinger/ISchedulingPolicyService.h2
-rw-r--r--services/audioflinger/PlaybackTracks.h288
-rw-r--r--services/audioflinger/RecordTracks.h63
-rw-r--r--services/audioflinger/SchedulingPolicyService.cpp30
-rw-r--r--services/audioflinger/SchedulingPolicyService.h5
-rw-r--r--services/audioflinger/ServiceUtilities.cpp21
-rw-r--r--services/audioflinger/ServiceUtilities.h4
-rw-r--r--services/audioflinger/StateQueue.cpp9
-rw-r--r--services/audioflinger/StateQueue.h74
-rw-r--r--services/audioflinger/StateQueueInstantiations.cpp1
-rw-r--r--services/audioflinger/Threads.cpp5337
-rw-r--r--services/audioflinger/Threads.h964
-rw-r--r--services/audioflinger/TrackBase.h145
-rw-r--r--services/audioflinger/Tracks.cpp1863
-rw-r--r--services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp31
-rw-r--r--services/audioflinger/audio-resampler/dnsampler_filter_coefficients_x128_10112011.h2585
-rw-r--r--services/audioflinger/audio-resampler/filter_coefficients.h285
-rw-r--r--services/audioflinger/audio-resampler/resampler_filter_coefficients_10042011.h2071
-rw-r--r--services/audioflinger/test-resample.cpp274
39 files changed, 13317 insertions, 14371 deletions
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index bd9421c..54377f1 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -15,33 +15,30 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
AudioFlinger.cpp \
+ Threads.cpp \
+ Tracks.cpp \
+ Effects.cpp \
AudioMixer.cpp.arm \
AudioResampler.cpp.arm \
AudioPolicyService.cpp \
ServiceUtilities.cpp \
+ AudioResamplerCubic.cpp.arm \
AudioResamplerSinc.cpp.arm
-# uncomment to enable AudioResampler::MED_QUALITY
-# LOCAL_SRC_FILES += AudioResamplerCubic.cpp.arm
-
LOCAL_SRC_FILES += StateQueue.cpp
-# uncomment for debugging timing problems related to StateQueue::push()
-LOCAL_CFLAGS += -DSTATE_QUEUE_DUMP
-
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects) \
$(call include-path-for, audio-utils)
-# FIXME keep libmedia_native but remove libmedia after split
LOCAL_SHARED_LIBRARIES := \
libaudioutils \
libcommon_time_client \
libcutils \
libutils \
+ liblog \
libbinder \
libmedia \
- libmedia_native \
libnbaio \
libhardware \
libhardware_legacy \
@@ -56,28 +53,42 @@ LOCAL_STATIC_LIBRARIES := \
LOCAL_MODULE:= libaudioflinger
-LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp
+LOCAL_SRC_FILES += FastMixer.cpp FastMixerState.cpp AudioWatchdog.cpp
-LOCAL_CFLAGS += -DFAST_MIXER_STATISTICS
+LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
-# uncomment to display CPU load adjusted for CPU frequency
-# LOCAL_CFLAGS += -DCPU_FREQUENCY_STATISTICS
+# Define ANDROID_SMP appropriately. Used to get inline tracing fast-path.
+ifeq ($(TARGET_CPU_SMP),true)
+ LOCAL_CFLAGS += -DANDROID_SMP=1
+else
+ LOCAL_CFLAGS += -DANDROID_SMP=0
+endif
-LOCAL_CFLAGS += -DSTATE_QUEUE_INSTANTIATIONS='"StateQueueInstantiations.cpp"'
+LOCAL_CFLAGS += -fvisibility=hidden
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
+# build audio resampler test tool
+#
+include $(CLEAR_VARS)
-LOCAL_CFLAGS += -UFAST_TRACKS_AT_NON_NATIVE_SAMPLE_RATE
+LOCAL_SRC_FILES:= \
+ test-resample.cpp \
+ AudioResampler.cpp.arm \
+ AudioResamplerCubic.cpp.arm \
+ AudioResamplerSinc.cpp.arm
-# uncomment for systrace
-# LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_AUDIO
+LOCAL_SHARED_LIBRARIES := \
+ libdl \
+ libcutils \
+ libutils \
+ liblog
-# uncomment for dumpsys to write most recent audio output to .wav file
-# 47.5 seconds at 44.1 kHz, 8 megabytes
-# LOCAL_CFLAGS += -DTEE_SINK_FRAMES=0x200000
+LOCAL_MODULE:= test-resample
-# uncomment to enable the audio watchdog
-# LOCAL_SRC_FILES += AudioWatchdog.cpp
-# LOCAL_CFLAGS += -DAUDIO_WATCHDOG
+LOCAL_MODULE_TAGS := optional
-include $(BUILD_SHARED_LIBRARY)
+include $(BUILD_EXECUTABLE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 76d6447..e9c38e3 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -19,6 +19,8 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
+#include <dirent.h>
#include <math.h>
#include <signal.h>
#include <sys/time.h>
@@ -29,24 +31,12 @@
#include <utils/Log.h>
#include <utils/Trace.h>
#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
#include <utils/String16.h>
#include <utils/threads.h>
#include <utils/Atomic.h>
#include <cutils/bitops.h>
#include <cutils/properties.h>
-#include <cutils/compiler.h>
-
-#undef ADD_BATTERY_DATA
-
-#ifdef ADD_BATTERY_DATA
-#include <media/IMediaPlayerService.h>
-#include <media/IMediaDeathNotifier.h>
-#endif
-
-#include <private/media/AudioTrackShared.h>
-#include <private/media/AudioEffectShared.h>
#include <system/audio.h>
#include <hardware/audio.h>
@@ -64,26 +54,14 @@
#include <powermanager/PowerManager.h>
-// #define DEBUG_CPU_USAGE 10 // log statistics every n wall clock seconds
-#ifdef DEBUG_CPU_USAGE
-#include <cpustats/CentralTendencyStatistics.h>
-#include <cpustats/ThreadCpuUsage.h>
-#endif
-
#include <common_time/cc_helper.h>
-#include <common_time/local_clock.h>
-#include "FastMixer.h"
+#include <media/IMediaLogService.h>
-// NBAIO implementations
-#include <media/nbaio/AudioStreamOutSink.h>
-#include <media/nbaio/MonoPipe.h>
-#include <media/nbaio/MonoPipeReader.h>
#include <media/nbaio/Pipe.h>
#include <media/nbaio/PipeReader.h>
-#include <media/nbaio/SourceAudioBufferProvider.h>
-
-#include "SchedulingPolicyService.h"
+#include <media/AudioParameter.h>
+#include <private/android_filesystem_config.h>
// ----------------------------------------------------------------------------
@@ -105,90 +83,27 @@ namespace android {
static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n";
static const char kHardwareLockedString[] = "Hardware lock is taken\n";
-static const float MAX_GAIN = 4096.0f;
-static const uint32_t MAX_GAIN_INT = 0x1000;
-
-// retry counts for buffer fill timeout
-// 50 * ~20msecs = 1 second
-static const int8_t kMaxTrackRetries = 50;
-static const int8_t kMaxTrackStartupRetries = 50;
-// allow less retry attempts on direct output thread.
-// direct outputs can be a scarce resource in audio hardware and should
-// be released as quickly as possible.
-static const int8_t kMaxTrackRetriesDirect = 2;
-
-static const int kDumpLockRetries = 50;
-static const int kDumpLockSleepUs = 20000;
-
-// don't warn about blocked writes or record buffer overflows more often than this
-static const nsecs_t kWarningThrottleNs = seconds(5);
-// RecordThread loop sleep time upon application overrun or audio HAL read error
-static const int kRecordThreadSleepUs = 5000;
-
-// maximum time to wait for setParameters to complete
-static const nsecs_t kSetParametersTimeoutNs = seconds(2);
+nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
-// minimum sleep time for the mixer thread loop when tracks are active but in underrun
-static const uint32_t kMinThreadSleepTimeUs = 5000;
-// maximum divider applied to the active sleep time in the mixer thread loop
-static const uint32_t kMaxThreadSleepTimeShift = 2;
+uint32_t AudioFlinger::mScreenState;
-// minimum normal mix buffer size, expressed in milliseconds rather than frames
-static const uint32_t kMinNormalMixBufferSizeMs = 20;
-// maximum normal mix buffer size
-static const uint32_t kMaxNormalMixBufferSizeMs = 24;
+#ifdef TEE_SINK
+bool AudioFlinger::mTeeSinkInputEnabled = false;
+bool AudioFlinger::mTeeSinkOutputEnabled = false;
+bool AudioFlinger::mTeeSinkTrackEnabled = false;
-nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs;
+size_t AudioFlinger::mTeeSinkInputFrames = kTeeSinkInputFramesDefault;
+size_t AudioFlinger::mTeeSinkOutputFrames = kTeeSinkOutputFramesDefault;
+size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
+#endif
-// Whether to use fast mixer
-static const enum {
- FastMixer_Never, // never initialize or use: for debugging only
- FastMixer_Always, // always initialize and use, even if not needed: for debugging only
- // normal mixer multiplier is 1
- FastMixer_Static, // initialize if needed, then use all the time if initialized,
- // multiplier is calculated based on min & max normal mixer buffer size
- FastMixer_Dynamic, // initialize if needed, then use dynamically depending on track load,
- // multiplier is calculated based on min & max normal mixer buffer size
- // FIXME for FastMixer_Dynamic:
- // Supporting this option will require fixing HALs that can't handle large writes.
- // For example, one HAL implementation returns an error from a large write,
- // and another HAL implementation corrupts memory, possibly in the sample rate converter.
- // We could either fix the HAL implementations, or provide a wrapper that breaks
- // up large writes into smaller ones, and the wrapper would need to deal with scheduler.
-} kUseFastMixer = FastMixer_Static;
-
-static uint32_t gScreenState; // incremented by 2 when screen state changes, bit 0 == 1 means "off"
- // AudioFlinger::setParameters() updates, other threads read w/o lock
-
-// Priorities for requestPriority
-static const int kPriorityAudioApp = 2;
-static const int kPriorityFastMixer = 3;
-
-// IAudioFlinger::createTrack() reports back to client the total size of shared memory area
-// for the track. The client then sub-divides this into smaller buffers for its use.
-// Currently the client uses double-buffering by default, but doesn't tell us about that.
-// So for now we just assume that client is double-buffered.
-// FIXME It would be better for client to tell AudioFlinger whether it wants double-buffering or
-// N-buffering, so AudioFlinger could allocate the right amount of memory.
-// See the client's minBufCount and mNotificationFramesAct calculations for details.
-static const int kFastTrackMultiplier = 2;
+// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
+// we define a minimum time during which a global effect is considered enabled.
+static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
// ----------------------------------------------------------------------------
-#ifdef ADD_BATTERY_DATA
-// To collect the amplifier usage
-static void addBatteryData(uint32_t params) {
- sp<IMediaPlayerService> service = IMediaDeathNotifier::getMediaPlayerService();
- if (service == NULL) {
- // it already logged
- return;
- }
-
- service->addBatteryData(params);
-}
-#endif
-
static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
{
const hw_module_t *mod;
@@ -228,8 +143,32 @@ AudioFlinger::AudioFlinger()
mMasterMute(false),
mNextUniqueId(1),
mMode(AUDIO_MODE_INVALID),
- mBtNrecIsOff(false)
-{
+ mBtNrecIsOff(false),
+ mIsLowRamDevice(true),
+ mIsDeviceTypeKnown(false),
+ mGlobalEffectEnableTime(0)
+{
+ getpid_cached = getpid();
+ char value[PROPERTY_VALUE_MAX];
+ bool doLog = (property_get("ro.test_harness", value, "0") > 0) && (atoi(value) == 1);
+ if (doLog) {
+ mLogMemoryDealer = new MemoryDealer(kLogMemorySize, "LogWriters");
+ }
+#ifdef TEE_SINK
+ (void) property_get("ro.debuggable", value, "0");
+ int debuggable = atoi(value);
+ int teeEnabled = 0;
+ if (debuggable) {
+ (void) property_get("af.tee", value, "0");
+ teeEnabled = atoi(value);
+ }
+ if (teeEnabled & 1)
+ mTeeSinkInputEnabled = true;
+ if (teeEnabled & 2)
+ mTeeSinkOutputEnabled = true;
+ if (teeEnabled & 4)
+ mTeeSinkTrackEnabled = true;
+#endif
}
void AudioFlinger::onFirstRef()
@@ -325,6 +264,12 @@ void AudioFlinger::dumpClients(int fd, const Vector<String16>& args)
}
}
+ result.append("Notification Clients:\n");
+ for (size_t i = 0; i < mNotificationClients.size(); ++i) {
+ snprintf(buffer, SIZE, " pid: %d\n", mNotificationClients.keyAt(i));
+ result.append(buffer);
+ }
+
result.append("Global session refs:\n");
result.append(" session pid count\n");
for (size_t i = 0; i < mAudioSessionRefs.size(); i++) {
@@ -364,7 +309,7 @@ void AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args)
write(fd, result.string(), result.size());
}
-static bool tryLock(Mutex& mutex)
+bool AudioFlinger::dumpTryLock(Mutex& mutex)
{
bool locked = false;
for (int i = 0; i < kDumpLockRetries; ++i) {
@@ -383,7 +328,7 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
dumpPermissionDenial(fd, args);
} else {
// get state of hardware lock
- bool hardwareLocked = tryLock(mHardwareLock);
+ bool hardwareLocked = dumpTryLock(mHardwareLock);
if (!hardwareLocked) {
String8 result(kHardwareLockedString);
write(fd, result.string(), result.size());
@@ -391,7 +336,7 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
mHardwareLock.unlock();
}
- bool locked = tryLock(mLock);
+ bool locked = dumpTryLock(mLock);
// failed to lock - AudioFlinger is probably deadlocked
if (!locked) {
@@ -417,7 +362,28 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args)
audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
dev->dump(dev, fd);
}
- if (locked) mLock.unlock();
+
+#ifdef TEE_SINK
+ // dump the serially shared record tee sink
+ if (mRecordTeeSource != 0) {
+ dumpTee(fd, mRecordTeeSource);
+ }
+#endif
+
+ if (locked) {
+ mLock.unlock();
+ }
+
+ // append a copy of media.log here by forwarding fd to it, but don't attempt
+ // to lookup the service if it's not running, as it will block for a second
+ if (mLogMemoryDealer != 0) {
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
+ if (binder != 0) {
+ fdprintf(fd, "\nmedia.log:\n");
+ Vector<String16> args;
+ binder->dump(fd, args);
+ }
+ }
}
return NO_ERROR;
}
@@ -435,21 +401,54 @@ sp<AudioFlinger::Client> AudioFlinger::registerPid_l(pid_t pid)
return client;
}
+sp<NBLog::Writer> AudioFlinger::newWriter_l(size_t size, const char *name)
+{
+ if (mLogMemoryDealer == 0) {
+ return new NBLog::Writer();
+ }
+ sp<IMemory> shared = mLogMemoryDealer->allocate(NBLog::Timeline::sharedSize(size));
+ sp<NBLog::Writer> writer = new NBLog::Writer(size, shared);
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
+ if (binder != 0) {
+ interface_cast<IMediaLogService>(binder)->registerWriter(shared, size, name);
+ }
+ return writer;
+}
+
+void AudioFlinger::unregisterWriter(const sp<NBLog::Writer>& writer)
+{
+ if (writer == 0) {
+ return;
+ }
+ sp<IMemory> iMemory(writer->getIMemory());
+ if (iMemory == 0) {
+ return;
+ }
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("media.log"));
+ if (binder != 0) {
+ interface_cast<IMediaLogService>(binder)->unregisterWriter(iMemory);
+ // Now the media.log remote reference to IMemory is gone.
+ // When our last local reference to IMemory also drops to zero,
+ // the IMemory destructor will deallocate the region from mMemoryDealer.
+ }
+}
+
// IAudioFlinger interface
sp<IAudioTrack> AudioFlinger::createTrack(
- pid_t pid,
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount,
- IAudioFlinger::track_flags_t flags,
+ size_t frameCount,
+ IAudioFlinger::track_flags_t *flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
pid_t tid,
int *sessionId,
+ String8& name,
+ int clientUid,
status_t *status)
{
sp<PlaybackThread::Track> track;
@@ -466,16 +465,26 @@ sp<IAudioTrack> AudioFlinger::createTrack(
goto Exit;
}
+ // client is responsible for conversion of 8-bit PCM to 16-bit PCM,
+ // and we don't yet support 8.24 or 32-bit PCM
+ if (audio_is_linear_pcm(format) && format != AUDIO_FORMAT_PCM_16_BIT) {
+ ALOGE("createTrack() invalid format %d", format);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
{
Mutex::Autolock _l(mLock);
PlaybackThread *thread = checkPlaybackThread_l(output);
PlaybackThread *effectThread = NULL;
if (thread == NULL) {
- ALOGE("unknown output thread");
+ ALOGE("no playback thread found for output handle %d", output);
lStatus = BAD_VALUE;
goto Exit;
}
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+
client = registerPid_l(pid);
ALOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
@@ -503,7 +512,7 @@ sp<IAudioTrack> AudioFlinger::createTrack(
ALOGV("createTrack() lSessionId: %d", lSessionId);
track = thread->createTrack_l(client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, &lStatus);
+ channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);
// move effect chain to this output thread if an effect on same session was waiting
// for a track to be created
@@ -529,6 +538,9 @@ sp<IAudioTrack> AudioFlinger::createTrack(
}
}
if (lStatus == NO_ERROR) {
+ // s for server's pid, n for normal mixer name, f for fast index
+ name = String8::format("s:%d;n:%d;f:%d", getpid_cached, track->name() - AudioMixer::TRACK0,
+ track->fastIndex());
trackHandle = new TrackHandle(track);
} else {
// remove local strong reference to Client before deleting the Track so that the Client
@@ -595,7 +607,7 @@ uint32_t AudioFlinger::latency(audio_io_handle_t output) const
Mutex::Autolock _l(mLock);
PlaybackThread *thread = checkPlaybackThread_l(output);
if (thread == NULL) {
- ALOGW("latency() unknown thread %d", output);
+ ALOGW("latency(): no playback thread found for output handle %d", output);
return 0;
}
return thread->latency();
@@ -856,8 +868,9 @@ bool AudioFlinger::streamMute(audio_stream_type_t stream) const
status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs)
{
- ALOGV("setParameters(): io %d, keyvalue %s, tid %d, calling pid %d",
- ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid());
+ ALOGV("setParameters(): io %d, keyvalue %s, calling pid %d",
+ ioHandle, keyValuePairs.string(), IPCThreadState::self()->getCallingPid());
+
// check calling permissions
if (!settingsAllowed()) {
return PERMISSION_DENIED;
@@ -906,8 +919,8 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8&
String8 screenState;
if (param.get(String8(AudioParameter::keyScreenState), screenState) == NO_ERROR) {
bool isOff = screenState == "off";
- if (isOff != (gScreenState & 1)) {
- gScreenState = ((gScreenState & ~1) + 2) | isOff;
+ if (isOff != (AudioFlinger::mScreenState & 1)) {
+ AudioFlinger::mScreenState = ((AudioFlinger::mScreenState & ~1) + 2) | isOff;
}
}
return final_result;
@@ -941,8 +954,8 @@ status_t AudioFlinger::setParameters(audio_io_handle_t ioHandle, const String8&
String8 AudioFlinger::getParameters(audio_io_handle_t ioHandle, const String8& keys) const
{
-// ALOGV("getParameters() io %d, keys %s, tid %d, calling pid %d",
-// ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid());
+ ALOGVV("getParameters() io %d, keys %s, calling pid %d",
+ ioHandle, keys.string(), IPCThreadState::self()->getCallingPid());
Mutex::Autolock _l(mLock);
@@ -985,18 +998,19 @@ size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t form
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;
- struct audio_config config = {
- sample_rate: sampleRate,
- channel_mask: channelMask,
- format: format,
- };
+ struct audio_config config;
+ memset(&config, 0, sizeof(config));
+ config.sample_rate = sampleRate;
+ config.channel_mask = channelMask;
+ config.format = format;
+
audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
size_t size = dev->get_input_buffer_size(dev, &config);
mHardwareStatus = AUDIO_HW_IDLE;
return size;
}
-unsigned int AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
+uint32_t AudioFlinger::getInputFramesLost(audio_io_handle_t ioHandle) const
{
Mutex::Autolock _l(mLock);
@@ -1112,7 +1126,8 @@ void AudioFlinger::audioConfigChanged_l(int event, audio_io_handle_t ioHandle, c
// removeClient_l() must be called with AudioFlinger::mLock held
void AudioFlinger::removeClient_l(pid_t pid)
{
- ALOGV("removeClient_l() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid());
+ ALOGV("removeClient_l() pid %d, calling pid %d", pid,
+ IPCThreadState::self()->getCallingPid());
mClients.removeItem(pid);
}
@@ -1131,4596 +1146,7 @@ sp<AudioFlinger::PlaybackThread> AudioFlinger::getEffectThread_l(int sessionId,
return thread;
}
-// ----------------------------------------------------------------------------
-
-AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
- audio_devices_t outDevice, audio_devices_t inDevice, type_t type)
- : Thread(false /*canCallJava*/),
- mType(type),
- mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mNormalFrameCount(0),
- // mChannelMask
- mChannelCount(0),
- mFrameSize(1), mFormat(AUDIO_FORMAT_INVALID),
- mParamStatus(NO_ERROR),
- mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
- mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
- // mName will be set by concrete (non-virtual) subclass
- mDeathRecipient(new PMDeathRecipient(this))
-{
-}
-
-AudioFlinger::ThreadBase::~ThreadBase()
-{
- mParamCond.broadcast();
- // do not lock the mutex in destructor
- releaseWakeLock_l();
- if (mPowerManager != 0) {
- sp<IBinder> binder = mPowerManager->asBinder();
- binder->unlinkToDeath(mDeathRecipient);
- }
-}
-
-void AudioFlinger::ThreadBase::exit()
-{
- ALOGV("ThreadBase::exit");
- // do any cleanup required for exit to succeed
- preExit();
- {
- // This lock prevents the following race in thread (uniprocessor for illustration):
- // if (!exitPending()) {
- // // context switch from here to exit()
- // // exit() calls requestExit(), what exitPending() observes
- // // exit() calls signal(), which is dropped since no waiters
- // // context switch back from exit() to here
- // mWaitWorkCV.wait(...);
- // // now thread is hung
- // }
- AutoMutex lock(mLock);
- requestExit();
- mWaitWorkCV.broadcast();
- }
- // When Thread::requestExitAndWait is made virtual and this method is renamed to
- // "virtual status_t requestExitAndWait()", replace by "return Thread::requestExitAndWait();"
- requestExitAndWait();
-}
-
-status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
-{
- status_t status;
-
- ALOGV("ThreadBase::setParameters() %s", keyValuePairs.string());
- Mutex::Autolock _l(mLock);
-
- mNewParameters.add(keyValuePairs);
- mWaitWorkCV.signal();
- // wait condition with timeout in case the thread loop has exited
- // before the request could be processed
- if (mParamCond.waitRelative(mLock, kSetParametersTimeoutNs) == NO_ERROR) {
- status = mParamStatus;
- mWaitWorkCV.signal();
- } else {
- status = TIMED_OUT;
- }
- return status;
-}
-
-void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param)
-{
- Mutex::Autolock _l(mLock);
- sendIoConfigEvent_l(event, param);
-}
-
-// sendIoConfigEvent_l() must be called with ThreadBase::mLock held
-void AudioFlinger::ThreadBase::sendIoConfigEvent_l(int event, int param)
-{
- IoConfigEvent *ioEvent = new IoConfigEvent(event, param);
- mConfigEvents.add(static_cast<ConfigEvent *>(ioEvent));
- ALOGV("sendIoConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param);
- mWaitWorkCV.signal();
-}
-
-// sendPrioConfigEvent_l() must be called with ThreadBase::mLock held
-void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio)
-{
- PrioConfigEvent *prioEvent = new PrioConfigEvent(pid, tid, prio);
- mConfigEvents.add(static_cast<ConfigEvent *>(prioEvent));
- ALOGV("sendPrioConfigEvent_l() num events %d pid %d, tid %d prio %d",
- mConfigEvents.size(), pid, tid, prio);
- mWaitWorkCV.signal();
-}
-
-void AudioFlinger::ThreadBase::processConfigEvents()
-{
- mLock.lock();
- while (!mConfigEvents.isEmpty()) {
- ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
- ConfigEvent *event = mConfigEvents[0];
- mConfigEvents.removeAt(0);
- // release mLock before locking AudioFlinger mLock: lock order is always
- // AudioFlinger then ThreadBase to avoid cross deadlock
- mLock.unlock();
- switch(event->type()) {
- case CFG_EVENT_PRIO: {
- PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event);
- int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio());
- if (err != 0) {
- ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err);
- }
- } break;
- case CFG_EVENT_IO: {
- IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event);
- mAudioFlinger->mLock.lock();
- audioConfigChanged_l(ioEvent->event(), ioEvent->param());
- mAudioFlinger->mLock.unlock();
- } break;
- default:
- ALOGE("processConfigEvents() unknown event type %d", event->type());
- break;
- }
- delete event;
- mLock.lock();
- }
- mLock.unlock();
-}
-
-void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- bool locked = tryLock(mLock);
- if (!locked) {
- snprintf(buffer, SIZE, "thread %p maybe dead locked\n", this);
- write(fd, buffer, strlen(buffer));
- }
-
- snprintf(buffer, SIZE, "io handle: %d\n", mId);
- result.append(buffer);
- snprintf(buffer, SIZE, "TID: %d\n", getTid());
- result.append(buffer);
- snprintf(buffer, SIZE, "standby: %d\n", mStandby);
- result.append(buffer);
- snprintf(buffer, SIZE, "Sample rate: %d\n", mSampleRate);
- result.append(buffer);
- 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);
- snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask);
- result.append(buffer);
- snprintf(buffer, SIZE, "Format: %d\n", mFormat);
- result.append(buffer);
- snprintf(buffer, SIZE, "Frame size: %u\n", mFrameSize);
- result.append(buffer);
-
- snprintf(buffer, SIZE, "\nPending setParameters commands: \n");
- result.append(buffer);
- result.append(" Index Command");
- for (size_t i = 0; i < mNewParameters.size(); ++i) {
- snprintf(buffer, SIZE, "\n %02d ", i);
- result.append(buffer);
- result.append(mNewParameters[i]);
- }
-
- snprintf(buffer, SIZE, "\n\nPending config events: \n");
- result.append(buffer);
- for (size_t i = 0; i < mConfigEvents.size(); i++) {
- mConfigEvents[i]->dump(buffer, SIZE);
- result.append(buffer);
- }
- result.append("\n");
-
- write(fd, result.string(), result.size());
-
- if (locked) {
- mLock.unlock();
- }
-}
-
-void AudioFlinger::ThreadBase::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);
- }
- }
-}
-
-void AudioFlinger::ThreadBase::acquireWakeLock()
-{
- Mutex::Autolock _l(mLock);
- acquireWakeLock_l();
-}
-
-void AudioFlinger::ThreadBase::acquireWakeLock_l()
-{
- if (mPowerManager == 0) {
- // use checkService() to avoid blocking if power service is not up yet
- sp<IBinder> binder =
- defaultServiceManager()->checkService(String16("power"));
- if (binder == 0) {
- ALOGW("Thread %s cannot connect to the power manager service", mName);
- } else {
- mPowerManager = interface_cast<IPowerManager>(binder);
- binder->linkToDeath(mDeathRecipient);
- }
- }
- if (mPowerManager != 0) {
- sp<IBinder> binder = new BBinder();
- status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
- binder,
- String16(mName));
- if (status == NO_ERROR) {
- mWakeLockToken = binder;
- }
- ALOGV("acquireWakeLock_l() %s status %d", mName, status);
- }
-}
-
-void AudioFlinger::ThreadBase::releaseWakeLock()
-{
- Mutex::Autolock _l(mLock);
- releaseWakeLock_l();
-}
-
-void AudioFlinger::ThreadBase::releaseWakeLock_l()
-{
- if (mWakeLockToken != 0) {
- ALOGV("releaseWakeLock_l() %s", mName);
- if (mPowerManager != 0) {
- mPowerManager->releaseWakeLock(mWakeLockToken, 0);
- }
- mWakeLockToken.clear();
- }
-}
-
-void AudioFlinger::ThreadBase::clearPowerManager()
-{
- Mutex::Autolock _l(mLock);
- releaseWakeLock_l();
- mPowerManager.clear();
-}
-
-void AudioFlinger::ThreadBase::PMDeathRecipient::binderDied(const wp<IBinder>& who)
-{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- thread->clearPowerManager();
- }
- ALOGW("power manager service died !!!");
-}
-
-void AudioFlinger::ThreadBase::setEffectSuspended(
- const effect_uuid_t *type, bool suspend, int sessionId)
-{
- Mutex::Autolock _l(mLock);
- setEffectSuspended_l(type, suspend, sessionId);
-}
-
-void AudioFlinger::ThreadBase::setEffectSuspended_l(
- const effect_uuid_t *type, bool suspend, int sessionId)
-{
- sp<EffectChain> chain = getEffectChain_l(sessionId);
- if (chain != 0) {
- if (type != NULL) {
- chain->setEffectSuspended_l(type, suspend);
- } else {
- chain->setEffectSuspendedAll_l(suspend);
- }
- }
-
- updateSuspendedSessions_l(type, suspend, sessionId);
-}
-
-void AudioFlinger::ThreadBase::checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain)
-{
- ssize_t index = mSuspendedSessions.indexOfKey(chain->sessionId());
- if (index < 0) {
- return;
- }
-
- const KeyedVector <int, sp<SuspendedSessionDesc> >& sessionEffects =
- mSuspendedSessions.valueAt(index);
-
- for (size_t i = 0; i < sessionEffects.size(); i++) {
- sp<SuspendedSessionDesc> desc = sessionEffects.valueAt(i);
- for (int j = 0; j < desc->mRefCount; j++) {
- if (sessionEffects.keyAt(i) == EffectChain::kKeyForSuspendAll) {
- chain->setEffectSuspendedAll_l(true);
- } else {
- ALOGV("checkSuspendOnAddEffectChain_l() suspending effects %08x",
- desc->mType.timeLow);
- chain->setEffectSuspended_l(&desc->mType, true);
- }
- }
- }
-}
-
-void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *type,
- bool suspend,
- int sessionId)
-{
- ssize_t index = mSuspendedSessions.indexOfKey(sessionId);
-
- KeyedVector <int, sp<SuspendedSessionDesc> > sessionEffects;
-
- if (suspend) {
- if (index >= 0) {
- sessionEffects = mSuspendedSessions.valueAt(index);
- } else {
- mSuspendedSessions.add(sessionId, sessionEffects);
- }
- } else {
- if (index < 0) {
- return;
- }
- sessionEffects = mSuspendedSessions.valueAt(index);
- }
-
-
- int key = EffectChain::kKeyForSuspendAll;
- if (type != NULL) {
- key = type->timeLow;
- }
- index = sessionEffects.indexOfKey(key);
-
- sp<SuspendedSessionDesc> desc;
- if (suspend) {
- if (index >= 0) {
- desc = sessionEffects.valueAt(index);
- } else {
- desc = new SuspendedSessionDesc();
- if (type != NULL) {
- desc->mType = *type;
- }
- sessionEffects.add(key, desc);
- ALOGV("updateSuspendedSessions_l() suspend adding effect %08x", key);
- }
- desc->mRefCount++;
- } else {
- if (index < 0) {
- return;
- }
- desc = sessionEffects.valueAt(index);
- if (--desc->mRefCount == 0) {
- ALOGV("updateSuspendedSessions_l() restore removing effect %08x", key);
- sessionEffects.removeItemsAt(index);
- if (sessionEffects.isEmpty()) {
- ALOGV("updateSuspendedSessions_l() restore removing session %d",
- sessionId);
- mSuspendedSessions.removeItem(sessionId);
- }
- }
- }
- if (!sessionEffects.isEmpty()) {
- mSuspendedSessions.replaceValueFor(sessionId, sessionEffects);
- }
-}
-
-void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled,
- int sessionId)
-{
- Mutex::Autolock _l(mLock);
- checkSuspendOnEffectEnabled_l(effect, enabled, sessionId);
-}
-
-void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
- bool enabled,
- int sessionId)
-{
- if (mType != RECORD) {
- // suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
- // another session. This gives the priority to well behaved effect control panels
- // and applications not using global effects.
- // Enabling post processing in AUDIO_SESSION_OUTPUT_STAGE session does not affect
- // global effects
- if ((sessionId != AUDIO_SESSION_OUTPUT_MIX) && (sessionId != AUDIO_SESSION_OUTPUT_STAGE)) {
- setEffectSuspended_l(NULL, enabled, AUDIO_SESSION_OUTPUT_MIX);
- }
- }
-
- sp<EffectChain> chain = getEffectChain_l(sessionId);
- if (chain != 0) {
- chain->checkSuspendOnEffectEnabled(effect, enabled);
- }
-}
-
-// ----------------------------------------------------------------------------
-
-AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamOut* output,
- audio_io_handle_t id,
- audio_devices_t device,
- type_t type)
- : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
- mMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
- // mStreamTypes[] initialized in constructor body
- mOutput(output),
- mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
- mMixerStatus(MIXER_IDLE),
- mMixerStatusIgnoringFastTracks(MIXER_IDLE),
- standbyDelay(AudioFlinger::mStandbyTimeInNsecs),
- mScreenState(gScreenState),
- // index 0 is reserved for normal mixer's submix
- mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1)
-{
- snprintf(mName, kNameLength, "AudioOut_%X", id);
-
- // Assumes constructor is called by AudioFlinger with it's mLock held, but
- // it would be safer to explicitly pass initial masterVolume/masterMute as
- // parameter.
- //
- // If the HAL we are using has support for master volume or master mute,
- // then do not attenuate or mute during mixing (just leave the volume at 1.0
- // and the mute set to false).
- mMasterVolume = audioFlinger->masterVolume_l();
- mMasterMute = audioFlinger->masterMute_l();
- if (mOutput && mOutput->audioHwDev) {
- if (mOutput->audioHwDev->canSetMasterVolume()) {
- mMasterVolume = 1.0;
- }
-
- if (mOutput->audioHwDev->canSetMasterMute()) {
- mMasterMute = false;
- }
- }
-
- readOutputParameters();
-
- // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
- // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
- for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT;
- stream = (audio_stream_type_t) (stream + 1)) {
- mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
- mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
- }
- // mStreamTypes[AUDIO_STREAM_CNT] exists but isn't explicitly initialized here,
- // because mAudioFlinger doesn't have one to copy from
-}
-
-AudioFlinger::PlaybackThread::~PlaybackThread()
-{
- delete [] mMixBuffer;
-}
-
-void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
-{
- dumpInternals(fd, args);
- dumpTracks(fd, args);
- dumpEffectChains(fd, args);
-}
-
-void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- 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);
- Track::appendDumpHeader(result);
- for (size_t i = 0; i < mTracks.size(); ++i) {
- sp<Track> track = mTracks[i];
- if (track != 0) {
- track->dump(buffer, SIZE);
- result.append(buffer);
- }
- }
-
- snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
- result.append(buffer);
- Track::appendDumpHeader(result);
- for (size_t i = 0; i < mActiveTracks.size(); ++i) {
- sp<Track> track = mActiveTracks[i].promote();
- if (track != 0) {
- track->dump(buffer, SIZE);
- result.append(buffer);
- }
- }
- write(fd, result.string(), result.size());
-
- // These values are "raw"; they will wrap around. See prepareTracks_l() for a better way.
- FastTrackUnderruns underruns = getFastTrackUnderruns(0);
- fdprintf(fd, "Normal mixer raw underrun counters: partial=%u empty=%u\n",
- underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty);
-}
-
-void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this);
- result.append(buffer);
- snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime));
- result.append(buffer);
- snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites);
- result.append(buffer);
- snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites);
- result.append(buffer);
- snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite);
- 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());
- fdprintf(fd, "Fast track availMask=%#x\n", mFastTrackAvailMask);
-
- dumpBase(fd, args);
-}
-
-// Thread virtuals
-status_t AudioFlinger::PlaybackThread::readyToRun()
-{
- status_t status = initCheck();
- if (status == NO_ERROR) {
- ALOGI("AudioFlinger's thread %p ready to run", this);
- } else {
- ALOGE("No working audio driver found.");
- }
- return status;
-}
-
-void AudioFlinger::PlaybackThread::onFirstRef()
-{
- run(mName, ANDROID_PRIORITY_URGENT_AUDIO);
-}
-
-// ThreadBase virtuals
-void AudioFlinger::PlaybackThread::preExit()
-{
- ALOGV(" preExit()");
- // FIXME this is using hard-coded strings but in the future, this functionality will be
- // converted to use audio HAL extensions required to support tunneling
- mOutput->stream->common.set_parameters(&mOutput->stream->common, "exiting=1");
-}
-
-// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
-sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
- const sp<AudioFlinger::Client>& client,
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId,
- IAudioFlinger::track_flags_t flags,
- pid_t tid,
- status_t *status)
-{
- sp<Track> track;
- status_t lStatus;
-
- bool isTimed = (flags & IAudioFlinger::TRACK_TIMED) != 0;
-
- // client expresses a preference for FAST, but we get the final say
- if (flags & IAudioFlinger::TRACK_FAST) {
- if (
- // not timed
- (!isTimed) &&
- // either of these use cases:
- (
- // use case 1: shared buffer with any frame count
- (
- (sharedBuffer != 0)
- ) ||
- // use case 2: callback handler and frame count is default or at least as large as HAL
- (
- (tid != -1) &&
- ((frameCount == 0) ||
- (frameCount >= (int) (mFrameCount * kFastTrackMultiplier)))
- )
- ) &&
- // PCM data
- audio_is_linear_pcm(format) &&
- // 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) &&
-#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?
- ) {
- // if frameCount not specified, then it defaults to fast mixer (HAL) frame count
- if (frameCount == 0) {
- frameCount = mFrameCount * kFastTrackMultiplier;
- }
- ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
- frameCount, mFrameCount);
- } else {
- ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d "
- "mFrameCount=%d format=%d isLinear=%d channelMask=%#x 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;
- // For compatibility with AudioTrack calculation, buffer depth is forced
- // to be at least 2 x the normal mixer frame count and cover audio hardware latency.
- // This is probably too conservative, but legacy application code may depend on it.
- // If you change this calculation, also review the start threshold which is related.
- uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
- uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
- if (minBufCount < 2) {
- minBufCount = 2;
- }
- int minFrameCount = mNormalFrameCount * minBufCount;
- if (frameCount < minFrameCount) {
- frameCount = minFrameCount;
- }
- }
- }
-
- if (mType == DIRECT) {
- if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) {
- if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
- ALOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \""
- "for output %p with format %d",
- sampleRate, format, channelMask, mOutput, mFormat);
- lStatus = BAD_VALUE;
- goto Exit;
- }
- }
- } else {
- // Resampler implementation limits input sampling rate to 2 x output sampling rate.
- if (sampleRate > mSampleRate*2) {
- ALOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate);
- lStatus = BAD_VALUE;
- goto Exit;
- }
- }
-
- lStatus = initCheck();
- if (lStatus != NO_ERROR) {
- ALOGE("Audio driver not initialized.");
- goto Exit;
- }
-
- { // scope for mLock
- Mutex::Autolock _l(mLock);
-
- // all tracks in same audio session must share the same routing strategy otherwise
- // conflicts will happen when tracks are moved from one output to another by audio policy
- // manager
- uint32_t strategy = AudioSystem::getStrategyForStream(streamType);
- for (size_t i = 0; i < mTracks.size(); ++i) {
- sp<Track> t = mTracks[i];
- if (t != 0 && !t->isOutputTrack()) {
- uint32_t actual = AudioSystem::getStrategyForStream(t->streamType());
- if (sessionId == t->sessionId() && strategy != actual) {
- ALOGE("createTrack_l() mismatched strategy; expected %u but found %u",
- strategy, actual);
- lStatus = BAD_VALUE;
- goto Exit;
- }
- }
- }
-
- if (!isTimed) {
- track = new Track(this, client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, sessionId, flags);
- } else {
- track = TimedTrack::create(this, client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, sessionId);
- }
- if (track == 0 || track->getCblk() == NULL || track->name() < 0) {
- lStatus = NO_MEMORY;
- goto Exit;
- }
- mTracks.add(track);
-
- sp<EffectChain> chain = getEffectChain_l(sessionId);
- if (chain != 0) {
- ALOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
- track->setMainBuffer(chain->inBuffer());
- chain->setStrategy(AudioSystem::getStrategyForStream(track->streamType()));
- chain->incTrackCnt();
- }
-
- if ((flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
- pid_t callingPid = IPCThreadState::self()->getCallingPid();
- // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
- // so ask activity manager to do this on our behalf
- sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp);
- }
- }
-
- lStatus = NO_ERROR;
-
-Exit:
- if (status) {
- *status = lStatus;
- }
- return track;
-}
-
-uint32_t AudioFlinger::MixerThread::correctLatency(uint32_t latency) const
-{
- if (mFastMixer != NULL) {
- MonoPipe *pipe = (MonoPipe *)mPipeSink.get();
- latency += (pipe->getAvgFrames() * 1000) / mSampleRate;
- }
- return latency;
-}
-
-uint32_t AudioFlinger::PlaybackThread::correctLatency(uint32_t latency) const
-{
- return latency;
-}
-
-uint32_t AudioFlinger::PlaybackThread::latency() const
-{
- Mutex::Autolock _l(mLock);
- return latency_l();
-}
-uint32_t AudioFlinger::PlaybackThread::latency_l() const
-{
- if (initCheck() == NO_ERROR) {
- return correctLatency(mOutput->stream->get_latency(mOutput->stream));
- } else {
- return 0;
- }
-}
-
-void AudioFlinger::PlaybackThread::setMasterVolume(float value)
-{
- Mutex::Autolock _l(mLock);
- // Don't apply master volume in SW if our HAL can do it for us.
- if (mOutput && mOutput->audioHwDev &&
- mOutput->audioHwDev->canSetMasterVolume()) {
- mMasterVolume = 1.0;
- } else {
- mMasterVolume = value;
- }
-}
-
-void AudioFlinger::PlaybackThread::setMasterMute(bool muted)
-{
- Mutex::Autolock _l(mLock);
- // Don't apply master mute in SW if our HAL can do it for us.
- if (mOutput && mOutput->audioHwDev &&
- mOutput->audioHwDev->canSetMasterMute()) {
- mMasterMute = false;
- } else {
- mMasterMute = muted;
- }
-}
-
-void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
-{
- Mutex::Autolock _l(mLock);
- mStreamTypes[stream].volume = value;
-}
-
-void AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
-{
- Mutex::Autolock _l(mLock);
- mStreamTypes[stream].mute = muted;
-}
-
-float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) const
-{
- Mutex::Autolock _l(mLock);
- return mStreamTypes[stream].volume;
-}
-
-// addTrack_l() must be called with ThreadBase::mLock held
-status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
-{
- status_t status = ALREADY_EXISTS;
-
- // set retry count for buffer fill
- track->mRetryCount = kMaxTrackStartupRetries;
- if (mActiveTracks.indexOf(track) < 0) {
- // the track is newly added, make sure it fills up all its
- // buffers before playing. This is to ensure the client will
- // effectively get the latency it requested.
- track->mFillingUpStatus = Track::FS_FILLING;
- track->mResetDone = false;
- track->mPresentationCompleteFrames = 0;
- mActiveTracks.add(track);
- if (track->mainBuffer() != mMixBuffer) {
- sp<EffectChain> chain = getEffectChain_l(track->sessionId());
- if (chain != 0) {
- ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId());
- chain->incActiveTrackCnt();
- }
- }
-
- status = NO_ERROR;
- }
-
- ALOGV("mWaitWorkCV.broadcast");
- mWaitWorkCV.broadcast();
-
- return status;
-}
-
-// destroyTrack_l() must be called with ThreadBase::mLock held
-void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
-{
- track->mState = TrackBase::TERMINATED;
- // active tracks are removed by threadLoop()
- if (mActiveTracks.indexOf(track) < 0) {
- removeTrack_l(track);
- }
-}
-
-void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
-{
- track->triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
- mTracks.remove(track);
- deleteTrackName_l(track->name());
- // redundant as track is about to be destroyed, for dumpsys only
- track->mName = -1;
- if (track->isFastTrack()) {
- int index = track->mFastIndex;
- ALOG_ASSERT(0 < index && index < (int)FastMixerState::kMaxFastTracks);
- ALOG_ASSERT(!(mFastTrackAvailMask & (1 << index)));
- mFastTrackAvailMask |= 1 << index;
- // redundant as track is about to be destroyed, for dumpsys only
- track->mFastIndex = -1;
- }
- sp<EffectChain> chain = getEffectChain_l(track->sessionId());
- if (chain != 0) {
- chain->decTrackCnt();
- }
-}
-
-String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
-{
- String8 out_s8 = String8("");
- char *s;
-
- Mutex::Autolock _l(mLock);
- if (initCheck() != NO_ERROR) {
- return out_s8;
- }
-
- s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string());
- out_s8 = String8(s);
- free(s);
- return out_s8;
-}
-
-// audioConfigChanged_l() must be called with AudioFlinger::mLock held
-void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
- AudioSystem::OutputDescriptor desc;
- void *param2 = NULL;
-
- ALOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param);
-
- switch (event) {
- case AudioSystem::OUTPUT_OPENED:
- case AudioSystem::OUTPUT_CONFIG_CHANGED:
- desc.channels = mChannelMask;
- desc.samplingRate = mSampleRate;
- desc.format = mFormat;
- desc.frameCount = mNormalFrameCount; // FIXME see AudioFlinger::frameCount(audio_io_handle_t)
- desc.latency = latency();
- param2 = &desc;
- break;
-
- case AudioSystem::STREAM_CONFIG_CHANGED:
- param2 = &param;
- case AudioSystem::OUTPUT_CLOSED:
- default:
- break;
- }
- mAudioFlinger->audioConfigChanged_l(event, mId, param2);
-}
-
-void AudioFlinger::PlaybackThread::readOutputParameters()
-{
- mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
- mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common);
- mChannelCount = (uint16_t)popcount(mChannelMask);
- 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 relative to the HAL output buffer size
- double multiplier = 1.0;
- if (mType == MIXER && (kUseFastMixer == FastMixer_Static || kUseFastMixer == FastMixer_Dynamic)) {
- size_t minNormalFrameCount = (kMinNormalMixBufferSizeMs * mSampleRate) / 1000;
- size_t maxNormalFrameCount = (kMaxNormalMixBufferSizeMs * mSampleRate) / 1000;
- // round up minimum and round down maximum to nearest 16 frames to satisfy AudioMixer
- minNormalFrameCount = (minNormalFrameCount + 15) & ~15;
- maxNormalFrameCount = maxNormalFrameCount & ~15;
- if (maxNormalFrameCount < minNormalFrameCount) {
- maxNormalFrameCount = minNormalFrameCount;
- }
- multiplier = (double) minNormalFrameCount / (double) mFrameCount;
- if (multiplier <= 1.0) {
- multiplier = 1.0;
- } else if (multiplier <= 2.0) {
- if (2 * mFrameCount <= maxNormalFrameCount) {
- multiplier = 2.0;
- } else {
- multiplier = (double) maxNormalFrameCount / (double) mFrameCount;
- }
- } else {
- // prefer an even multiplier, for compatibility with doubling of fast tracks due to HAL SRC
- // (it would be unusual for the normal mix buffer size to not be a multiple of fast
- // track, but we sometimes have to do this to satisfy the maximum frame count constraint)
- // FIXME this rounding up should not be done if no HAL SRC
- uint32_t truncMult = (uint32_t) multiplier;
- if ((truncMult & 1)) {
- if ((truncMult + 1) * mFrameCount <= maxNormalFrameCount) {
- ++truncMult;
- }
- }
- multiplier = (double) truncMult;
- }
- }
- mNormalFrameCount = multiplier * mFrameCount;
- // round up to nearest 16 frames to satisfy AudioMixer
- mNormalFrameCount = (mNormalFrameCount + 15) & ~15;
- ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount, mNormalFrameCount);
-
- delete[] mMixBuffer;
- mMixBuffer = new int16_t[mNormalFrameCount * mChannelCount];
- memset(mMixBuffer, 0, mNormalFrameCount * mChannelCount * sizeof(int16_t));
-
- // force reconfiguration of effect chains and engines to take new buffer size and audio
- // parameters into account
- // Note that mLock is not held when readOutputParameters() is called from the constructor
- // but in this case nothing is done below as no audio sessions have effect yet so it doesn't
- // matter.
- // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains
- Vector< sp<EffectChain> > effectChains = mEffectChains;
- for (size_t i = 0; i < effectChains.size(); i ++) {
- mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false);
- }
-}
-
-
-status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
-{
- if (halFrames == NULL || dspFrames == NULL) {
- return BAD_VALUE;
- }
- Mutex::Autolock _l(mLock);
- if (initCheck() != NO_ERROR) {
- return INVALID_OPERATION;
- }
- *halFrames = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
-
- if (isSuspended()) {
- // return an estimation of rendered frames when the output is suspended
- int32_t frames = mBytesWritten - latency_l();
- if (frames < 0) {
- frames = 0;
- }
- *dspFrames = (uint32_t)frames;
- return NO_ERROR;
- } else {
- return mOutput->stream->get_render_position(mOutput->stream, dspFrames);
- }
-}
-uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) const
-{
- Mutex::Autolock _l(mLock);
- uint32_t result = 0;
- if (getEffectChain_l(sessionId) != 0) {
- result = EFFECT_SESSION;
- }
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- sp<Track> track = mTracks[i];
- if (sessionId == track->sessionId() &&
- !(track->mCblk->flags & CBLK_INVALID_MSK)) {
- result |= TRACK_SESSION;
- break;
- }
- }
-
- return result;
-}
-
-uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
-{
- // session AUDIO_SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
- // it is moved to correct output by audio policy manager when A2DP is connected or disconnected
- if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
- return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
- }
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<Track> track = mTracks[i];
- if (sessionId == track->sessionId() &&
- !(track->mCblk->flags & CBLK_INVALID_MSK)) {
- return AudioSystem::getStrategyForStream(track->streamType());
- }
- }
- return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
-}
-
-
-AudioFlinger::AudioStreamOut* AudioFlinger::PlaybackThread::getOutput() const
-{
- Mutex::Autolock _l(mLock);
- return mOutput;
-}
-
-AudioFlinger::AudioStreamOut* AudioFlinger::PlaybackThread::clearOutput()
-{
- 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;
-}
-
-// this method must always be called either with ThreadBase mLock held or inside the thread loop
-audio_stream_t* AudioFlinger::PlaybackThread::stream() const
-{
- if (mOutput == NULL) {
- return NULL;
- }
- return &mOutput->stream->common;
-}
-
-uint32_t AudioFlinger::PlaybackThread::activeSleepTimeUs() const
-{
- return (uint32_t)((uint32_t)((mNormalFrameCount * 1000) / mSampleRate) * 1000);
-}
-
-status_t AudioFlinger::PlaybackThread::setSyncEvent(const sp<SyncEvent>& event)
-{
- if (!isValidSyncEvent(event)) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock _l(mLock);
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- sp<Track> track = mTracks[i];
- if (event->triggerSession() == track->sessionId()) {
- (void) track->setSyncEvent(event);
- return NO_ERROR;
- }
- }
-
- return NAME_NOT_FOUND;
-}
-
-bool AudioFlinger::PlaybackThread::isValidSyncEvent(const sp<SyncEvent>& event) const
-{
- return event->type() == AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE;
-}
-
-void AudioFlinger::PlaybackThread::threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove)
-{
- size_t count = tracksToRemove.size();
- if (CC_UNLIKELY(count)) {
- for (size_t i = 0 ; i < count ; i++) {
- const sp<Track>& track = tracksToRemove.itemAt(i);
- if ((track->sharedBuffer() != 0) &&
- (track->mState == TrackBase::ACTIVE || track->mState == TrackBase::RESUMING)) {
- AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
- }
- }
- }
-
-}
-
-// ----------------------------------------------------------------------------
-
-AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, audio_devices_t device, type_t type)
- : PlaybackThread(audioFlinger, output, id, device, type),
- // mAudioMixer below
- // mFastMixer below
- mFastMixerFutex(0)
- // mOutputSink below
- // mPipeSink below
- // mNormalSink below
-{
- ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type);
- ALOGV("mSampleRate=%d, mChannelMask=%#x, 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 != FCC_2) {
- ALOGE("Invalid audio hardware channel count %d", mChannelCount);
- }
-
- // 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 depending on configuration
- bool initFastMixer;
- switch (kUseFastMixer) {
- case FastMixer_Never:
- initFastMixer = false;
- break;
- case FastMixer_Always:
- initFastMixer = true;
- break;
- case FastMixer_Static:
- case FastMixer_Dynamic:
- initFastMixer = mFrameCount < mNormalFrameCount;
- break;
- }
- if (initFastMixer) {
-
- // create a MonoPipe to connect our submix to FastMixer
- NBAIO_Format format = mOutputSink->format();
- // This pipe depth compensates for scheduling latency of the normal mixer thread.
- // When it wakes up after a maximum latency, it runs a few cycles quickly before
- // finally blocking. Note the pipe implementation rounds up the request to a power of 2.
- MonoPipe *monoPipe = new MonoPipe(mNormalFrameCount * 4, 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);
- monoPipe->setAvgFrames((mScreenState & 1) ?
- (monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
- mPipeSink = monoPipe;
-
-#ifdef TEE_SINK_FRAMES
- // create a Pipe to archive a copy of FastMixer's output for dumpsys
- Pipe *teeSink = new Pipe(TEE_SINK_FRAMES, format);
- numCounterOffers = 0;
- index = teeSink->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mTeeSink = teeSink;
- PipeReader *teeSource = new PipeReader(*teeSink);
- numCounterOffers = 0;
- index = teeSource->negotiate(offers, 1, NULL, numCounterOffers);
- ALOG_ASSERT(index == 0);
- mTeeSource = teeSource;
-#endif
-
- // create fast mixer and configure it initially with just one fast track for our submix
- mFastMixer = new FastMixer();
- FastMixerStateQueue *sq = mFastMixer->sq();
-#ifdef STATE_QUEUE_DUMP
- sq->setObserverDump(&mStateQueueObserverDump);
- sq->setMutatorDump(&mStateQueueMutatorDump);
-#endif
- 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;
- state->mTeeSink = mTeeSink.get();
- sq->end();
- sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
-
- // start the fast mixer
- mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO);
- pid_t tid = mFastMixer->getTid();
- int err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
- if (err != 0) {
- ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- kPriorityFastMixer, getpid_cached, tid, err);
- }
-
-#ifdef AUDIO_WATCHDOG
- // create and start the watchdog
- mAudioWatchdog = new AudioWatchdog();
- mAudioWatchdog->setDump(&mAudioWatchdogDump);
- mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO);
- tid = mAudioWatchdog->getTid();
- err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
- if (err != 0) {
- ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
- kPriorityFastMixer, getpid_cached, tid, err);
- }
-#endif
-
- } else {
- mFastMixer = NULL;
- }
-
- switch (kUseFastMixer) {
- case FastMixer_Never:
- case FastMixer_Dynamic:
- mNormalSink = mOutputSink;
- break;
- case FastMixer_Always:
- mNormalSink = mPipeSink;
- break;
- case FastMixer_Static:
- mNormalSink = initFastMixer ? mPipeSink : mOutputSink;
- break;
- }
-}
-
-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 AUDIO_WATCHDOG
- if (mAudioWatchdog != 0) {
- mAudioWatchdog->requestExit();
- mAudioWatchdog->requestExitAndWait();
- mAudioWatchdog.clear();
- }
-#endif
- }
- delete mAudioMixer;
-}
-
-class CpuStats {
-public:
- CpuStats();
- void sample(const String8 &title);
-#ifdef DEBUG_CPU_USAGE
-private:
- ThreadCpuUsage mCpuUsage; // instantaneous thread CPU usage in wall clock ns
- CentralTendencyStatistics mWcStats; // statistics on thread CPU usage in wall clock ns
-
- CentralTendencyStatistics mHzStats; // statistics on thread CPU usage in cycles
-
- int mCpuNum; // thread's current CPU number
- int mCpukHz; // frequency of thread's current CPU in kHz
-#endif
-};
-
-CpuStats::CpuStats()
-#ifdef DEBUG_CPU_USAGE
- : mCpuNum(-1), mCpukHz(-1)
-#endif
-{
-}
-
-void CpuStats::sample(const String8 &title) {
-#ifdef DEBUG_CPU_USAGE
- // get current thread's delta CPU time in wall clock ns
- double wcNs;
- bool valid = mCpuUsage.sampleAndEnable(wcNs);
-
- // record sample for wall clock statistics
- if (valid) {
- mWcStats.sample(wcNs);
- }
-
- // get the current CPU number
- int cpuNum = sched_getcpu();
-
- // get the current CPU frequency in kHz
- int cpukHz = mCpuUsage.getCpukHz(cpuNum);
-
- // check if either CPU number or frequency changed
- if (cpuNum != mCpuNum || cpukHz != mCpukHz) {
- mCpuNum = cpuNum;
- mCpukHz = cpukHz;
- // ignore sample for purposes of cycles
- valid = false;
- }
-
- // if no change in CPU number or frequency, then record sample for cycle statistics
- if (valid && mCpukHz > 0) {
- double cycles = wcNs * cpukHz * 0.000001;
- mHzStats.sample(cycles);
- }
-
- unsigned n = mWcStats.n();
- // mCpuUsage.elapsed() is expensive, so don't call it every loop
- if ((n & 127) == 1) {
- long long elapsed = mCpuUsage.elapsed();
- if (elapsed >= DEBUG_CPU_USAGE * 1000000000LL) {
- double perLoop = elapsed / (double) n;
- double perLoop100 = perLoop * 0.01;
- double perLoop1k = perLoop * 0.001;
- double mean = mWcStats.mean();
- double stddev = mWcStats.stddev();
- double minimum = mWcStats.minimum();
- double maximum = mWcStats.maximum();
- double meanCycles = mHzStats.mean();
- double stddevCycles = mHzStats.stddev();
- double minCycles = mHzStats.minimum();
- double maxCycles = mHzStats.maximum();
- mCpuUsage.resetElapsed();
- mWcStats.reset();
- mHzStats.reset();
- ALOGD("CPU usage for %s over past %.1f secs\n"
- " (%u mixer loops at %.1f mean ms per loop):\n"
- " us per mix loop: mean=%.0f stddev=%.0f min=%.0f max=%.0f\n"
- " %% of wall: mean=%.1f stddev=%.1f min=%.1f max=%.1f\n"
- " MHz: mean=%.1f, stddev=%.1f, min=%.1f max=%.1f",
- title.string(),
- elapsed * .000000001, n, perLoop * .000001,
- mean * .001,
- stddev * .001,
- minimum * .001,
- maximum * .001,
- mean / perLoop100,
- stddev / perLoop100,
- minimum / perLoop100,
- maximum / perLoop100,
- meanCycles / perLoop1k,
- stddevCycles / perLoop1k,
- minCycles / perLoop1k,
- maxCycles / perLoop1k);
-
- }
- }
-#endif
-};
-
-void AudioFlinger::PlaybackThread::checkSilentMode_l()
-{
- if (!mMasterMute) {
- char value[PROPERTY_VALUE_MAX];
- if (property_get("ro.audio.silent", value, "0") > 0) {
- char *endptr;
- unsigned long ul = strtoul(value, &endptr, 0);
- if (*endptr == '\0' && ul != 0) {
- ALOGD("Silence is golden");
- // The setprop command will not allow a property to be changed after
- // the first time it is set, so we don't have to worry about un-muting.
- setMasterMute_l(true);
- }
- }
- }
-}
-
-bool AudioFlinger::PlaybackThread::threadLoop()
-{
- Vector< sp<Track> > tracksToRemove;
-
- standbyTime = systemTime();
-
- // MIXER
- nsecs_t lastWarning = 0;
-
- // DUPLICATING
- // FIXME could this be made local to while loop?
- writeFrames = 0;
-
- cacheParameters_l();
- sleepTime = idleSleepTime;
-
- if (mType == MIXER) {
- sleepTimeShift = 0;
- }
-
- CpuStats cpuStats;
- const String8 myName(String8::format("thread %p type %d TID %d", this, mType, gettid()));
-
- acquireWakeLock();
-
- while (!exitPending())
- {
- cpuStats.sample(myName);
-
- Vector< sp<EffectChain> > effectChains;
-
- processConfigEvents();
-
- { // scope for mLock
-
- Mutex::Autolock _l(mLock);
-
- if (checkForNewParameters_l()) {
- cacheParameters_l();
- }
-
- saveOutputTracks();
-
- // put audio hardware into standby after short delay
- if (CC_UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) ||
- isSuspended())) {
- if (!mStandby) {
-
- threadLoop_standby();
-
- mStandby = true;
- }
-
- if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
- // we're about to wait, flush the binder command buffer
- IPCThreadState::self()->flushCommands();
-
- clearOutputTracks();
-
- if (exitPending()) break;
-
- releaseWakeLock_l();
- // wait until we have something to do...
- ALOGV("%s going to sleep", myName.string());
- mWaitWorkCV.wait(mLock);
- ALOGV("%s waking up", myName.string());
- acquireWakeLock_l();
-
- mMixerStatus = MIXER_IDLE;
- mMixerStatusIgnoringFastTracks = MIXER_IDLE;
- mBytesWritten = 0;
-
- checkSilentMode_l();
-
- standbyTime = systemTime() + standbyDelay;
- sleepTime = idleSleepTime;
- if (mType == MIXER) {
- sleepTimeShift = 0;
- }
-
- continue;
- }
- }
-
- // mMixerStatusIgnoringFastTracks is also updated internally
- mMixerStatus = prepareTracks_l(&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
- lockEffectChains_l(effectChains);
- }
-
- if (CC_LIKELY(mMixerStatus == MIXER_TRACKS_READY)) {
- threadLoop_mix();
- } else {
- threadLoop_sleepTime();
- }
-
- if (isSuspended()) {
- sleepTime = suspendSleepTimeUs();
- mBytesWritten += mixBufferSize;
- }
-
- // only process effects if we're going to write
- if (sleepTime == 0) {
- for (size_t i = 0; i < effectChains.size(); i ++) {
- effectChains[i]->process_l();
- }
- }
-
- // enable changes in effect chain
- unlockEffectChains(effectChains);
-
- // sleepTime == 0 means we must write to audio hardware
- if (sleepTime == 0) {
-
- threadLoop_write();
-
-if (mType == MIXER) {
- // write blocked detection
- nsecs_t now = systemTime();
- nsecs_t delta = now - mLastWriteTime;
- if (!mStandby && delta > maxPeriod) {
- mNumDelayedWrites++;
- if ((now - lastWarning) > kWarningThrottleNs) {
-#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- ScopedTrace st(ATRACE_TAG, "underrun");
-#endif
- ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
- ns2ms(delta), mNumDelayedWrites, this);
- lastWarning = now;
- }
- }
-}
-
- mStandby = false;
- } else {
- usleep(sleepTime);
- }
-
- // Finally let go of removed track(s), without the lock held
- // since we can't guarantee the destructors won't acquire that
- // 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;
- // it was in the original code but maybe the
- // assignment in saveOutputTracks() makes this unnecessary?
- clearOutputTracks();
-
- // Effect chains will be actually deleted here if they were removed from
- // mEffectChains list during mixing or effects processing
- effectChains.clear();
-
- // FIXME Note that the above .clear() is no longer necessary since effectChains
- // is now local to this block, but will keep it for now (at least until merge done).
- }
-
- // for DuplicatingThread, standby mode is handled by the outputTracks, otherwise ...
- if (mType == MIXER || mType == DIRECT) {
- // put output stream into standby mode
- if (!mStandby) {
- mOutput->stream->common.standby(&mOutput->stream->common);
- }
- }
-
- releaseWakeLock();
-
- ALOGV("Thread %p type %d exiting", this, mType);
- return false;
-}
-
-void AudioFlinger::MixerThread::threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove)
-{
- 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 &&
- (kUseFastMixer != FastMixer_Dynamic || 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);
- }
-#ifdef AUDIO_WATCHDOG
- if (mAudioWatchdog != 0) {
- mAudioWatchdog->resume();
- }
-#endif
- }
- state->mCommand = FastMixerState::MIX_WRITE;
- sq->end();
- sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
- if (kUseFastMixer == FastMixer_Dynamic) {
- 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;
- 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;
-#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- Tracer::traceBegin(ATRACE_TAG, "write");
-#endif
- // update the setpoint when gScreenState changes
- uint32_t screenState = gScreenState;
- if (screenState != mScreenState) {
- mScreenState = screenState;
- MonoPipe *pipe = (MonoPipe *)mPipeSink.get();
- if (pipe != NULL) {
- pipe->setAvgFrames((mScreenState & 1) ?
- (pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
- }
- }
- ssize_t framesWritten = mNormalSink->write(mMixBuffer, count);
-#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- Tracer::traceEnd(ATRACE_TAG);
-#endif
- if (framesWritten > 0) {
- bytesWritten = framesWritten << mBitShift;
- } else {
- bytesWritten = framesWritten;
- }
- // otherwise use the HAL / AudioStreamOut directly
- } else {
- // Direct output thread.
- 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);
- if (kUseFastMixer == FastMixer_Dynamic) {
- mNormalSink = mOutputSink;
- }
-#ifdef AUDIO_WATCHDOG
- if (mAudioWatchdog != 0) {
- mAudioWatchdog->pause();
- }
-#endif
- } else {
- sq->end(false /*didModify*/);
- }
- }
- PlaybackThread::threadLoop_standby();
-}
-
-// shared by MIXER and DIRECT, overridden by DUPLICATING
-void AudioFlinger::PlaybackThread::threadLoop_standby()
-{
- ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended);
- mOutput->stream->common.standby(&mOutput->stream->common);
-}
-
-void AudioFlinger::MixerThread::threadLoop_mix()
-{
- // obtain the presentation timestamp of the next output buffer
- int64_t pts;
- status_t status = INVALID_OPERATION;
-
- if (mNormalSink != 0) {
- status = mNormalSink->getNextWriteTimestamp(&pts);
- } else {
- status = mOutputSink->getNextWriteTimestamp(&pts);
- }
-
- if (status != NO_ERROR) {
- pts = AudioBufferProvider::kInvalidPTS;
- }
-
- // mix buffers...
- mAudioMixer->process(pts);
- // increase sleep time progressively when application underrun condition clears.
- // Only increase sleep time if the mixer is ready for two consecutive times to avoid
- // that a steady state of alternating ready/not ready conditions keeps the sleep time
- // such that we would underrun the audio HAL.
- if ((sleepTime == 0) && (sleepTimeShift > 0)) {
- sleepTimeShift--;
- }
- sleepTime = 0;
- standbyTime = systemTime() + standbyDelay;
- //TODO: delay standby when effects have a tail
-}
-
-void AudioFlinger::MixerThread::threadLoop_sleepTime()
-{
- // If no tracks are ready, sleep once for the duration of an output
- // buffer size, then write 0s to the output
- if (sleepTime == 0) {
- if (mMixerStatus == MIXER_TRACKS_ENABLED) {
- sleepTime = activeSleepTime >> sleepTimeShift;
- if (sleepTime < kMinThreadSleepTimeUs) {
- sleepTime = kMinThreadSleepTimeUs;
- }
- // reduce sleep time in case of consecutive application underruns to avoid
- // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
- // duration we would end up writing less data than needed by the audio HAL if
- // the condition persists.
- if (sleepTimeShift < kMaxThreadSleepTimeShift) {
- sleepTimeShift++;
- }
- } else {
- sleepTime = idleSleepTime;
- }
- } else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) {
- memset (mMixBuffer, 0, mixBufferSize);
- sleepTime = 0;
- ALOGV_IF((mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED)), "anticipated start");
- }
- // TODO add standby time extension fct of effect tail
-}
-
-// prepareTracks_l() must be called with ThreadBase::mLock held
-AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
- Vector< sp<Track> > *tracksToRemove)
-{
-
- mixer_state mixerStatus = MIXER_IDLE;
- // find out which tracks need to be processed
- size_t count = mActiveTracks.size();
- size_t mixedTracks = 0;
- size_t tracksWithEffect = 0;
- // counts only _active_ fast tracks
- size_t fastTracks = 0;
- uint32_t resetMask = 0; // bit mask of fast tracks that need to be reset
-
- float masterVolume = mMasterVolume;
- bool masterMute = mMasterMute;
-
- if (masterMute) {
- masterVolume = 0;
- }
- // Delegate master volume control to effect in output mix effect chain if needed
- sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
- if (chain != 0) {
- uint32_t v = (uint32_t)(masterVolume * (1 << 24));
- chain->setVolume_l(&v, &v);
- masterVolume = (float)((v + (1 << 23)) >> 24);
- chain.clear();
- }
-
- // prepare a new state to push
- FastMixerStateQueue *sq = NULL;
- FastMixerState *state = NULL;
- bool didModify = false;
- FastMixerStateQueue::block_t block = FastMixerStateQueue::BLOCK_UNTIL_PUSHED;
- if (mFastMixer != NULL) {
- sq = mFastMixer->sq();
- state = sq->begin();
- }
-
- for (size_t i=0 ; i<count ; i++) {
- sp<Track> t = mActiveTracks[i].promote();
- if (t == 0) continue;
-
- // this const just means the local variable doesn't change
- Track* const track = t.get();
-
- // process fast tracks
- if (track->isFastTrack()) {
-
- // It's theoretically possible (though unlikely) for a fast track to be created
- // and then removed within the same normal mix cycle. This is not a problem, as
- // the track never becomes active so it's fast mixer slot is never touched.
- // The converse, of removing an (active) track and then creating a new track
- // at the identical fast mixer slot within the same normal mix cycle,
- // is impossible because the slot isn't marked available until the end of each cycle.
- int j = track->mFastIndex;
- ALOG_ASSERT(0 < j && j < (int)FastMixerState::kMaxFastTracks);
- ALOG_ASSERT(!(mFastTrackAvailMask & (1 << j)));
- FastTrack *fastTrack = &state->mFastTracks[j];
-
- // Determine whether the track is currently in underrun condition,
- // and whether it had a recent underrun.
- FastTrackDump *ftDump = &mFastMixerDumpState.mTracks[j];
- FastTrackUnderruns underruns = ftDump->mUnderruns;
- uint32_t recentFull = (underruns.mBitFields.mFull -
- track->mObservedUnderruns.mBitFields.mFull) & UNDERRUN_MASK;
- uint32_t recentPartial = (underruns.mBitFields.mPartial -
- track->mObservedUnderruns.mBitFields.mPartial) & UNDERRUN_MASK;
- uint32_t recentEmpty = (underruns.mBitFields.mEmpty -
- track->mObservedUnderruns.mBitFields.mEmpty) & UNDERRUN_MASK;
- uint32_t recentUnderruns = recentPartial + recentEmpty;
- track->mObservedUnderruns = underruns;
- // don't count underruns that occur while stopping or pausing
- // or stopped which can occur when flush() is called while active
- if (!(track->isStopping() || track->isPausing() || track->isStopped())) {
- track->mUnderrunCount += recentUnderruns;
- }
-
- // This is similar to the state machine for normal tracks,
- // with a few modifications for fast tracks.
- bool isActive = true;
- switch (track->mState) {
- case TrackBase::STOPPING_1:
- // track stays active in STOPPING_1 state until first underrun
- if (recentUnderruns > 0) {
- track->mState = TrackBase::STOPPING_2;
- }
- break;
- case TrackBase::PAUSING:
- // ramp down is not yet implemented
- track->setPaused();
- break;
- case TrackBase::RESUMING:
- // ramp up is not yet implemented
- track->mState = TrackBase::ACTIVE;
- break;
- case TrackBase::ACTIVE:
- if (recentFull > 0 || recentPartial > 0) {
- // track has provided at least some frames recently: reset retry count
- track->mRetryCount = kMaxTrackRetries;
- }
- if (recentUnderruns == 0) {
- // no recent underruns: stay active
- break;
- }
- // there has recently been an underrun of some kind
- if (track->sharedBuffer() == 0) {
- // were any of the recent underruns "empty" (no frames available)?
- if (recentEmpty == 0) {
- // no, then ignore the partial underruns as they are allowed indefinitely
- break;
- }
- // there has recently been an "empty" underrun: decrement the retry counter
- if (--(track->mRetryCount) > 0) {
- break;
- }
- // indicate to client process that the track was disabled because of underrun;
- // it will then automatically call start() when data is available
- android_atomic_or(CBLK_DISABLED_ON, &track->mCblk->flags);
- // remove from active list, but state remains ACTIVE [confusing but true]
- isActive = false;
- break;
- }
- // fall through
- case TrackBase::STOPPING_2:
- case TrackBase::PAUSED:
- case TrackBase::TERMINATED:
- case TrackBase::STOPPED:
- case TrackBase::FLUSHED: // flush() while active
- // Check for presentation complete if track is inactive
- // We have consumed all the buffers of this track.
- // This would be incomplete if we auto-paused on underrun
- {
- size_t audioHALFrames =
- (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
- size_t framesWritten =
- mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- if (!(mStandby || track->presentationComplete(framesWritten, audioHALFrames))) {
- // track stays in active list until presentation is complete
- break;
- }
- }
- if (track->isStopping_2()) {
- track->mState = TrackBase::STOPPED;
- }
- if (track->isStopped()) {
- // Can't reset directly, as fast mixer is still polling this track
- // track->reset();
- // So instead mark this track as needing to be reset after push with ack
- resetMask |= 1 << i;
- }
- isActive = false;
- break;
- case TrackBase::IDLE:
- default:
- LOG_FATAL("unexpected track state %d", track->mState);
- }
-
- if (isActive) {
- // was it previously inactive?
- if (!(state->mTrackMask & (1 << j))) {
- ExtendedAudioBufferProvider *eabp = track;
- VolumeProvider *vp = track;
- fastTrack->mBufferProvider = eabp;
- fastTrack->mVolumeProvider = vp;
- fastTrack->mSampleRate = track->mSampleRate;
- fastTrack->mChannelMask = track->mChannelMask;
- fastTrack->mGeneration++;
- state->mTrackMask |= 1 << j;
- didModify = true;
- // no acknowledgement required for newly active tracks
- }
- // 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 = track->isMuted() ?
- 0 : masterVolume * mStreamTypes[track->streamType()].volume;
- ++fastTracks;
- } else {
- // was it previously active?
- if (state->mTrackMask & (1 << j)) {
- fastTrack->mBufferProvider = NULL;
- fastTrack->mGeneration++;
- state->mTrackMask &= ~(1 << j);
- didModify = true;
- // If any fast tracks were removed, we must wait for acknowledgement
- // because we're about to decrement the last sp<> on those tracks.
- block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
- } else {
- LOG_FATAL("fast track %d should have been active", j);
- }
- tracksToRemove->add(track);
- // Avoids a misleading display in dumpsys
- track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL;
- }
- continue;
- }
-
- { // local variable scope to avoid goto warning
-
- audio_track_cblk_t* cblk = track->cblk();
-
- // The first time a track is added we wait
- // for all its buffers to be filled before processing it
- int name = track->name();
- // make sure that we have enough frames to mix one full buffer.
- // enforce this condition only once to enable draining the buffer in case the client
- // app does not call stop() and relies on underrun to stop:
- // hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
- // during last round
- uint32_t minFrames = 1;
- if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
- (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
- if (t->sampleRate() == (int)mSampleRate) {
- minFrames = mNormalFrameCount;
- } else {
- // +1 for rounding and +1 for additional sample needed for interpolation
- 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());
- // the minimum track buffer size is normally twice the number of frames necessary
- // to fill one buffer and the resampler should not leave more than one buffer worth
- // of unreleased frames after each pass, but just in case...
- ALOG_ASSERT(minFrames <= cblk->frameCount);
- }
- }
- if ((track->framesReady() >= minFrames) && track->isReady() &&
- !track->isPaused() && !track->isTerminated())
- {
- //ALOGV("track %d u=%08x, s=%08x [OK] on thread %p", 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 {
- ALOGW("prepareTracks_l(): track %d attached to effect but no chain found on session %d",
- 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;
- }
- mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
- } 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
- uint32_t vl, vr, va;
- if (track->isMuted() || track->isPausing() ||
- mStreamTypes[track->streamType()].mute) {
- vl = vr = va = 0;
- if (track->isPausing()) {
- track->setPaused();
- }
- } else {
-
- // read original volumes with volume control
- float typeVolume = mStreamTypes[track->streamType()].volume;
- float v = masterVolume * typeVolume;
- uint32_t vlr = cblk->getVolumeLR();
- vl = vlr & 0xFFFF;
- vr = vlr >> 16;
- // track volumes come from shared memory, so can't be trusted and must be clamped
- if (vl > MAX_GAIN_INT) {
- ALOGV("Track left volume out of range: %04X", vl);
- vl = MAX_GAIN_INT;
- }
- if (vr > MAX_GAIN_INT) {
- ALOGV("Track right volume out of range: %04X", vr);
- vr = MAX_GAIN_INT;
- }
- // now apply the master volume and stream type volume
- vl = (uint32_t)(v * vl) << 12;
- vr = (uint32_t)(v * vr) << 12;
- // assuming master volume and stream type volume each go up to 1.0,
- // vl and vr are now in 8.24 format
-
- uint16_t sendLevel = cblk->getSendLevel_U4_12();
- // send level comes from shared memory and so may be corrupt
- if (sendLevel > MAX_GAIN_INT) {
- ALOGV("Track send level out of range: %04X", sendLevel);
- sendLevel = MAX_GAIN_INT;
- }
- va = (uint32_t)(v * sendLevel);
- }
- // Delegate volume control to effect in track effect chain if needed
- if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
- // Do not ramp volume if volume is controlled by effect
- param = AudioMixer::VOLUME;
- track->mHasVolumeController = true;
- } else {
- // force no volume ramp when volume controller was just disabled or removed
- // from effect chain to avoid volume spike
- if (track->mHasVolumeController) {
- param = AudioMixer::VOLUME;
- }
- track->mHasVolumeController = false;
- }
-
- // Convert volumes from 8.24 to 4.12 format
- // This additional clamping is needed in case chain->setVolume_l() overshot
- vl = (vl + (1 << 11)) >> 12;
- if (vl > MAX_GAIN_INT) vl = MAX_GAIN_INT;
- vr = (vr + (1 << 11)) >> 12;
- if (vr > MAX_GAIN_INT) vr = MAX_GAIN_INT;
-
- if (va > MAX_GAIN_INT) va = MAX_GAIN_INT; // va is uint32_t, so no need to check for -
-
- // XXX: these things DON'T need to be done each time
- mAudioMixer->setBufferProvider(name, track);
- mAudioMixer->enable(name);
-
- mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)vl);
- mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)vr);
- mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)va);
- mAudioMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::FORMAT, (void *)track->format());
- mAudioMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
- mAudioMixer->setParameter(
- name,
- AudioMixer::RESAMPLE,
- AudioMixer::SAMPLE_RATE,
- (void *)(cblk->sampleRate));
- mAudioMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
- mAudioMixer->setParameter(
- name,
- AudioMixer::TRACK,
- AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
-
- // reset retry count
- track->mRetryCount = kMaxTrackRetries;
-
- // If one track is ready, set the mixer ready if:
- // - the mixer was not ready during previous round OR
- // - no other track is not ready
- if (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||
- mixerStatus != MIXER_TRACKS_ENABLED) {
- mixerStatus = MIXER_TRACKS_READY;
- }
- } else {
- // clear effect chain input buffer if an active track underruns to avoid sending
- // previous audio buffer again to effects
- chain = getEffectChain_l(track->sessionId());
- if (chain != 0) {
- chain->clearInputBuffer();
- }
-
- //ALOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", name, cblk->user, cblk->server, this);
- if ((track->sharedBuffer() != 0) || track->isTerminated() ||
- track->isStopped() || track->isPaused()) {
- // We have consumed all the buffers of this track.
- // Remove it from the list of active tracks.
- // TODO: use actual buffer filling status instead of latency when available from
- // audio HAL
- size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
- size_t framesWritten =
- mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
- if (track->isStopped()) {
- track->reset();
- }
- tracksToRemove->add(track);
- }
- } else {
- track->mUnderrunCount++;
- // No buffers for this track. Give it a few chances to
- // fill a buffer, then remove it from active list.
- if (--(track->mRetryCount) <= 0) {
- ALOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this);
- tracksToRemove->add(track);
- // indicate to client process that the track was disabled because of underrun;
- // it will then automatically call start() when data is available
- android_atomic_or(CBLK_DISABLED_ON, &cblk->flags);
- // If one track is not ready, mark the mixer also not ready if:
- // - the mixer was ready during previous round OR
- // - no other track is ready
- } else if (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY ||
- mixerStatus != MIXER_TRACKS_READY) {
- mixerStatus = MIXER_TRACKS_ENABLED;
- }
- }
- mAudioMixer->disable(name);
- }
-
- } // local variable scope to avoid goto warning
-track_is_ready: ;
-
- }
-
- // Push the new FastMixer state if necessary
- bool pauseAudioWatchdog = false;
- if (didModify) {
- state->mFastTracksGen++;
- // if the fast mixer was active, but now there are no fast tracks, then put it in cold idle
- if (kUseFastMixer == FastMixer_Dynamic &&
- state->mCommand == FastMixerState::MIX_WRITE && state->mTrackMask <= 1) {
- state->mCommand = FastMixerState::COLD_IDLE;
- state->mColdFutexAddr = &mFastMixerFutex;
- state->mColdGen++;
- mFastMixerFutex = 0;
- if (kUseFastMixer == FastMixer_Dynamic) {
- mNormalSink = mOutputSink;
- }
- // If we go into cold idle, need to wait for acknowledgement
- // so that fast mixer stops doing I/O.
- block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
- pauseAudioWatchdog = true;
- }
- sq->end();
- }
- if (sq != NULL) {
- sq->end(didModify);
- sq->push(block);
- }
-#ifdef AUDIO_WATCHDOG
- if (pauseAudioWatchdog && mAudioWatchdog != 0) {
- mAudioWatchdog->pause();
- }
-#endif
-
- // Now perform the deferred reset on fast tracks that have stopped
- while (resetMask != 0) {
- size_t i = __builtin_ctz(resetMask);
- ALOG_ASSERT(i < count);
- resetMask &= ~(1 << i);
- sp<Track> t = mActiveTracks[i].promote();
- if (t == 0) continue;
- Track* track = t.get();
- ALOG_ASSERT(track->isFastTrack() && track->isStopped());
- track->reset();
- }
-
- // remove all the tracks that need to be...
- count = tracksToRemove->size();
- if (CC_UNLIKELY(count)) {
- 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) {
- ALOGV("stopping track on chain %p for session Id: %d", chain.get(), track->sessionId());
- chain->decActiveTrackCnt();
- }
- }
- if (track->isTerminated()) {
- removeTrack_l(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) || (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
- mMixerStatusIgnoringFastTracks = mixerStatus;
- if (fastTracks > 0) {
- mixerStatus = MIXER_TRACKS_READY;
- }
- return mixerStatus;
-}
-
-/*
-The derived values that are cached:
- - mixBufferSize from frame count * frame size
- - activeSleepTime from activeSleepTimeUs()
- - idleSleepTime from idleSleepTimeUs()
- - standbyDelay from mActiveSleepTimeUs (DIRECT only)
- - maxPeriod from frame count and sample rate (MIXER only)
-
-The parameters that affect these derived values are:
- - frame count
- - frame size
- - sample rate
- - device type: A2DP or not
- - device latency
- - format: PCM or not
- - active sleep time
- - idle sleep time
-*/
-
-void AudioFlinger::PlaybackThread::cacheParameters_l()
-{
- mixBufferSize = mNormalFrameCount * mFrameSize;
- activeSleepTime = activeSleepTimeUs();
- idleSleepTime = idleSleepTimeUs();
-}
-
-void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamType)
-{
- ALOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
- this, streamType, mTracks.size());
- Mutex::Autolock _l(mLock);
-
- size_t size = mTracks.size();
- for (size_t i = 0; i < size; i++) {
- sp<Track> t = mTracks[i];
- if (t->streamType() == streamType) {
- android_atomic_or(CBLK_INVALID_ON, &t->mCblk->flags);
- t->mCblk->cv.signal();
- }
- }
-}
-
-// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId)
-{
- return mAudioMixer->getTrackName(channelMask, sessionId);
-}
-
-// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::MixerThread::deleteTrackName_l(int name)
-{
- ALOGV("remove track (%d) and delete from mixer", name);
- mAudioMixer->deleteTrackName(name);
-}
-
-// 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);
- int value;
-
- if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
- reconfig = true;
- }
- if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
- status = BAD_VALUE;
- } else {
- reconfig = true;
- }
- }
- if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- if (value != AUDIO_CHANNEL_OUT_STEREO) {
- status = BAD_VALUE;
- } else {
- reconfig = true;
- }
- }
- if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
- // do not accept frame count changes if tracks are open as the track buffer
- // size depends on frame count and correct behavior would not be guaranteed
- // if frame count is changed after track creation
- if (!mTracks.isEmpty()) {
- status = INVALID_OPERATION;
- } else {
- reconfig = true;
- }
- }
- if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
-#ifdef ADD_BATTERY_DATA
- // when changing the audio output device, call addBatteryData to notify
- // the change
- if (mOutDevice != value) {
- uint32_t params = 0;
- // check whether speaker is on
- if (value & AUDIO_DEVICE_OUT_SPEAKER) {
- params |= IMediaPlayerService::kBatteryDataSpeakerOn;
- }
-
- audio_devices_t deviceWithoutSpeaker
- = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
- // check if any other device (except speaker) is on
- if (value & deviceWithoutSpeaker ) {
- params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
- }
-
- if (params != 0) {
- addBatteryData(params);
- }
- }
-#endif
-
- // forward device change to effects that have requested to be
- // aware of attached audio device.
- mOutDevice = value;
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(mOutDevice);
- }
- }
-
- if (status == NO_ERROR) {
- status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
- keyValuePair.string());
- if (!mStandby && status == INVALID_OPERATION) {
- mOutput->stream->common.standby(&mOutput->stream->common);
- mStandby = true;
- mBytesWritten = 0;
- status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
- keyValuePair.string());
- }
- if (status == NO_ERROR && reconfig) {
- delete mAudioMixer;
- // for safety in case readOutputParameters() accesses mAudioMixer (it doesn't)
- mAudioMixer = NULL;
- readOutputParameters();
- mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
- for (size_t i = 0; i < mTracks.size() ; i++) {
- int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
- if (name < 0) break;
- mTracks[i]->mName = name;
- // limit track sample rate to 2 x new output sample rate
- if (mTracks[i]->mCblk->sampleRate > 2 * sampleRate()) {
- mTracks[i]->mCblk->sampleRate = 2 * sampleRate();
- }
- }
- sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
- }
- }
-
- mNewParameters.removeAt(0);
-
- mParamStatus = status;
- mParamCond.signal();
- // wait for condition with time out in case the thread calling ThreadBase::setParameters()
- // 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;
-}
-
-void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- PlaybackThread::dumpInternals(fd, args);
-
- 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);
-
-#ifdef STATE_QUEUE_DUMP
- // Similar for state queue
- StateQueueObserverDump observerCopy = mStateQueueObserverDump;
- observerCopy.dump(fd);
- StateQueueMutatorDump mutatorCopy = mStateQueueMutatorDump;
- mutatorCopy.dump(fd);
-#endif
-
- // Write the tee output to a .wav file
- NBAIO_Source *teeSource = mTeeSource.get();
- if (teeSource != NULL) {
- char teePath[64];
- struct timeval tv;
- gettimeofday(&tv, NULL);
- struct tm tm;
- localtime_r(&tv.tv_sec, &tm);
- strftime(teePath, sizeof(teePath), "/data/misc/media/%T.wav", &tm);
- int teeFd = open(teePath, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
- if (teeFd >= 0) {
- char wavHeader[44];
- memcpy(wavHeader,
- "RIFF\0\0\0\0WAVEfmt \20\0\0\0\1\0\2\0\104\254\0\0\0\0\0\0\4\0\20\0data\0\0\0\0",
- sizeof(wavHeader));
- NBAIO_Format format = teeSource->format();
- unsigned channelCount = Format_channelCount(format);
- ALOG_ASSERT(channelCount <= FCC_2);
- unsigned sampleRate = Format_sampleRate(format);
- wavHeader[22] = channelCount; // number of channels
- wavHeader[24] = sampleRate; // sample rate
- wavHeader[25] = sampleRate >> 8;
- wavHeader[32] = channelCount * 2; // block alignment
- write(teeFd, wavHeader, sizeof(wavHeader));
- size_t total = 0;
- bool firstRead = true;
- for (;;) {
-#define TEE_SINK_READ 1024
- short buffer[TEE_SINK_READ * FCC_2];
- size_t count = TEE_SINK_READ;
- ssize_t actual = teeSource->read(buffer, count,
- AudioBufferProvider::kInvalidPTS);
- bool wasFirstRead = firstRead;
- firstRead = false;
- if (actual <= 0) {
- if (actual == (ssize_t) OVERRUN && wasFirstRead) {
- continue;
- }
- break;
- }
- ALOG_ASSERT(actual <= (ssize_t)count);
- write(teeFd, buffer, actual * channelCount * sizeof(short));
- total += actual;
- }
- lseek(teeFd, (off_t) 4, SEEK_SET);
- uint32_t temp = 44 + total * channelCount * sizeof(short) - 8;
- write(teeFd, &temp, sizeof(temp));
- lseek(teeFd, (off_t) 40, SEEK_SET);
- temp = total * channelCount * sizeof(short);
- write(teeFd, &temp, sizeof(temp));
- close(teeFd);
- fdprintf(fd, "FastMixer tee copied to %s\n", teePath);
- } else {
- fdprintf(fd, "FastMixer unable to create tee %s: \n", strerror(errno));
- }
- }
-
-#ifdef AUDIO_WATCHDOG
- if (mAudioWatchdog != 0) {
- // Make a non-atomic copy of audio watchdog dump so it won't change underneath us
- AudioWatchdogDump wdCopy = mAudioWatchdogDump;
- wdCopy.dump(fd);
- }
-#endif
-}
-
-uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const
-{
- return (uint32_t)(((mNormalFrameCount * 1000) / mSampleRate) * 1000) / 2;
-}
-
-uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs() const
-{
- return (uint32_t)(((mNormalFrameCount * 1000) / mSampleRate) * 1000);
-}
-
-void AudioFlinger::MixerThread::cacheParameters_l()
-{
- PlaybackThread::cacheParameters_l();
-
- // FIXME: Relaxed timing because of a certain device that can't meet latency
- // 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(mNormalFrameCount) / mSampleRate * 15;
-}
-
-// ----------------------------------------------------------------------------
-AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device)
- : PlaybackThread(audioFlinger, output, id, device, DIRECT)
- // mLeftVolFloat, mRightVolFloat
-{
-}
-
-AudioFlinger::DirectOutputThread::~DirectOutputThread()
-{
-}
-
-AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prepareTracks_l(
- Vector< sp<Track> > *tracksToRemove
-)
-{
- sp<Track> trackToRemove;
-
- mixer_state mixerStatus = MIXER_IDLE;
-
- // find out which tracks need to be processed
- if (mActiveTracks.size() != 0) {
- sp<Track> t = mActiveTracks[0].promote();
- // The track died recently
- if (t == 0) return MIXER_IDLE;
-
- Track* const track = t.get();
- audio_track_cblk_t* cblk = track->cblk();
-
- // The first time a track is added we wait
- // for all its buffers to be filled before processing it
- uint32_t minFrames;
- if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing()) {
- minFrames = mNormalFrameCount;
- } else {
- minFrames = 1;
- }
- if ((track->framesReady() >= minFrames) && track->isReady() &&
- !track->isPaused() && !track->isTerminated())
- {
- //ALOGV("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;
- if (track->mState == TrackBase::RESUMING) {
- track->mState = TrackBase::ACTIVE;
- }
- }
-
- // compute volume for this track
- float left, right;
- if (track->isMuted() || mMasterMute || track->isPausing() ||
- mStreamTypes[track->streamType()].mute) {
- left = right = 0;
- if (track->isPausing()) {
- track->setPaused();
- }
- } else {
- float typeVolume = mStreamTypes[track->streamType()].volume;
- float v = mMasterVolume * typeVolume;
- uint32_t vlr = cblk->getVolumeLR();
- float v_clamped = v * (vlr & 0xFFFF);
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
- left = v_clamped/MAX_GAIN;
- v_clamped = v * (vlr >> 16);
- if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
- right = v_clamped/MAX_GAIN;
- }
-
- if (left != mLeftVolFloat || right != mRightVolFloat) {
- mLeftVolFloat = left;
- mRightVolFloat = right;
-
- // 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 (!mEffectChains.isEmpty()) {
- // Do not ramp volume if volume is controlled by effect
- mEffectChains[0]->setVolume_l(&vl, &vr);
- left = (float)vl / (1 << 24);
- right = (float)vr / (1 << 24);
- }
- mOutput->stream->set_volume(mOutput->stream, left, right);
- }
-
- // reset retry count
- track->mRetryCount = kMaxTrackRetriesDirect;
- mActiveTrack = t;
- mixerStatus = MIXER_TRACKS_READY;
- } else {
- // clear effect chain input buffer if an active track underruns to avoid sending
- // previous audio buffer again to effects
- if (!mEffectChains.isEmpty()) {
- mEffectChains[0]->clearInputBuffer();
- }
-
- //ALOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server);
- if ((track->sharedBuffer() != 0) || track->isTerminated() ||
- track->isStopped() || track->isPaused()) {
- // We have consumed all the buffers of this track.
- // Remove it from the list of active tracks.
- // TODO: implement behavior for compressed audio
- size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
- size_t framesWritten =
- mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
- if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
- if (track->isStopped()) {
- track->reset();
- }
- trackToRemove = track;
- }
- } else {
- // No buffers for this track. Give it a few chances to
- // fill a buffer, then remove it from active list.
- if (--(track->mRetryCount) <= 0) {
- ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
- trackToRemove = track;
- } else {
- mixerStatus = MIXER_TRACKS_ENABLED;
- }
- }
- }
- }
-
- // FIXME merge this with similar code for removing multiple tracks
- // remove all the tracks that need to be...
- if (CC_UNLIKELY(trackToRemove != 0)) {
- tracksToRemove->add(trackToRemove);
- mActiveTracks.remove(trackToRemove);
- if (!mEffectChains.isEmpty()) {
- ALOGV("stopping track on chain %p for session Id: %d", mEffectChains[0].get(),
- trackToRemove->sessionId());
- mEffectChains[0]->decActiveTrackCnt();
- }
- if (trackToRemove->isTerminated()) {
- removeTrack_l(trackToRemove);
- }
- }
-
- return mixerStatus;
-}
-
-void AudioFlinger::DirectOutputThread::threadLoop_mix()
-{
- AudioBufferProvider::Buffer buffer;
- size_t frameCount = mFrameCount;
- int8_t *curBuf = (int8_t *)mMixBuffer;
- // output audio to hardware
- while (frameCount) {
- buffer.frameCount = frameCount;
- mActiveTrack->getNextBuffer(&buffer);
- if (CC_UNLIKELY(buffer.raw == NULL)) {
- memset(curBuf, 0, frameCount * mFrameSize);
- break;
- }
- memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
- frameCount -= buffer.frameCount;
- curBuf += buffer.frameCount * mFrameSize;
- mActiveTrack->releaseBuffer(&buffer);
- }
- sleepTime = 0;
- standbyTime = systemTime() + standbyDelay;
- mActiveTrack.clear();
-
-}
-
-void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
-{
- if (sleepTime == 0) {
- if (mMixerStatus == MIXER_TRACKS_ENABLED) {
- sleepTime = activeSleepTime;
- } else {
- sleepTime = idleSleepTime;
- }
- } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
- memset(mMixBuffer, 0, mFrameCount * mFrameSize);
- sleepTime = 0;
- }
-}
-
-// getTrackName_l() must be called with ThreadBase::mLock held
-int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask,
- int sessionId)
-{
- return 0;
-}
-
-// deleteTrackName_l() must be called with ThreadBase::mLock held
-void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name)
-{
-}
-
-// checkForNewParameters_l() must be called with ThreadBase::mLock held
-bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
-{
- bool reconfig = false;
-
- while (!mNewParameters.isEmpty()) {
- status_t status = NO_ERROR;
- String8 keyValuePair = mNewParameters[0];
- AudioParameter param = AudioParameter(keyValuePair);
- int value;
-
- if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
- // do not accept frame count changes if tracks are open as the track buffer
- // size depends on frame count and correct behavior would not be garantied
- // if frame count is changed after track creation
- if (!mTracks.isEmpty()) {
- status = INVALID_OPERATION;
- } else {
- reconfig = true;
- }
- }
- if (status == NO_ERROR) {
- status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
- keyValuePair.string());
- if (!mStandby && status == INVALID_OPERATION) {
- mOutput->stream->common.standby(&mOutput->stream->common);
- mStandby = true;
- mBytesWritten = 0;
- status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
- keyValuePair.string());
- }
- if (status == NO_ERROR && reconfig) {
- readOutputParameters();
- sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
- }
- }
-
- mNewParameters.removeAt(0);
-
- mParamStatus = status;
- mParamCond.signal();
- // wait for condition with time out in case the thread calling ThreadBase::setParameters()
- // already timed out waiting for the status and will never signal the condition.
- mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
- }
- return reconfig;
-}
-
-uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs() const
-{
- uint32_t time;
- if (audio_is_linear_pcm(mFormat)) {
- time = PlaybackThread::activeSleepTimeUs();
- } else {
- time = 10000;
- }
- return time;
-}
-
-uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs() const
-{
- uint32_t time;
- if (audio_is_linear_pcm(mFormat)) {
- time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
- } else {
- time = 10000;
- }
- return time;
-}
-
-uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs() const
-{
- uint32_t time;
- if (audio_is_linear_pcm(mFormat)) {
- time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
- } else {
- time = 10000;
- }
- return time;
-}
-
-void AudioFlinger::DirectOutputThread::cacheParameters_l()
-{
- PlaybackThread::cacheParameters_l();
-
- // use shorter standby delay as on normal output to release
- // hardware resources as soon as possible
- standbyDelay = microseconds(activeSleepTime*2);
-}
-
-// ----------------------------------------------------------------------------
-
-AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
- AudioFlinger::MixerThread* mainThread, audio_io_handle_t id)
- : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->outDevice(), DUPLICATING),
- mWaitTimeMs(UINT_MAX)
-{
- addOutputTrack(mainThread);
-}
-
-AudioFlinger::DuplicatingThread::~DuplicatingThread()
-{
- for (size_t i = 0; i < mOutputTracks.size(); i++) {
- mOutputTracks[i]->destroy();
- }
-}
-
-void AudioFlinger::DuplicatingThread::threadLoop_mix()
-{
- // mix buffers...
- if (outputsReady(outputTracks)) {
- mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
- } else {
- memset(mMixBuffer, 0, mixBufferSize);
- }
- sleepTime = 0;
- writeFrames = mNormalFrameCount;
- standbyTime = systemTime() + standbyDelay;
-}
-
-void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
-{
- if (sleepTime == 0) {
- if (mMixerStatus == MIXER_TRACKS_ENABLED) {
- sleepTime = activeSleepTime;
- } else {
- sleepTime = idleSleepTime;
- }
- } else if (mBytesWritten != 0) {
- if (mMixerStatus == MIXER_TRACKS_ENABLED) {
- writeFrames = mNormalFrameCount;
- memset(mMixBuffer, 0, mixBufferSize);
- } else {
- // flush remaining overflow buffers in output tracks
- writeFrames = 0;
- }
- sleepTime = 0;
- }
-}
-
-void AudioFlinger::DuplicatingThread::threadLoop_write()
-{
- for (size_t i = 0; i < outputTracks.size(); i++) {
- outputTracks[i]->write(mMixBuffer, writeFrames);
- }
- mBytesWritten += mixBufferSize;
-}
-
-void AudioFlinger::DuplicatingThread::threadLoop_standby()
-{
- // DuplicatingThread implements standby by stopping all tracks
- for (size_t i = 0; i < outputTracks.size(); i++) {
- outputTracks[i]->stop();
- }
-}
-
-void AudioFlinger::DuplicatingThread::saveOutputTracks()
-{
- outputTracks = mOutputTracks;
-}
-
-void AudioFlinger::DuplicatingThread::clearOutputTracks()
-{
- outputTracks.clear();
-}
-
-void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
-{
- Mutex::Autolock _l(mLock);
- // FIXME explain this formula
- int frameCount = (3 * mNormalFrameCount * mSampleRate) / thread->sampleRate();
- OutputTrack *outputTrack = new OutputTrack(thread,
- this,
- mSampleRate,
- mFormat,
- mChannelMask,
- frameCount);
- if (outputTrack->cblk() != NULL) {
- thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
- mOutputTracks.add(outputTrack);
- ALOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
- updateWaitTime_l();
- }
-}
-
-void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread)
-{
- Mutex::Autolock _l(mLock);
- for (size_t i = 0; i < mOutputTracks.size(); i++) {
- if (mOutputTracks[i]->thread() == thread) {
- mOutputTracks[i]->destroy();
- mOutputTracks.removeAt(i);
- updateWaitTime_l();
- return;
- }
- }
- ALOGV("removeOutputTrack(): unkonwn thread: %p", thread);
-}
-
-// caller must hold mLock
-void AudioFlinger::DuplicatingThread::updateWaitTime_l()
-{
- mWaitTimeMs = UINT_MAX;
- for (size_t i = 0; i < mOutputTracks.size(); i++) {
- sp<ThreadBase> strong = mOutputTracks[i]->thread().promote();
- if (strong != 0) {
- uint32_t waitTimeMs = (strong->frameCount() * 2 * 1000) / strong->sampleRate();
- if (waitTimeMs < mWaitTimeMs) {
- mWaitTimeMs = waitTimeMs;
- }
- }
- }
-}
-
-
-bool AudioFlinger::DuplicatingThread::outputsReady(const SortedVector< sp<OutputTrack> > &outputTracks)
-{
- for (size_t i = 0; i < outputTracks.size(); i++) {
- sp<ThreadBase> thread = outputTracks[i]->thread().promote();
- if (thread == 0) {
- ALOGW("DuplicatingThread::outputsReady() could not promote thread on output track %p", outputTracks[i].get());
- return false;
- }
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- // see note at standby() declaration
- if (playbackThread->standby() && !playbackThread->isSuspended()) {
- ALOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(), thread.get());
- return false;
- }
- }
- return true;
-}
-
-uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs() const
-{
- return (mWaitTimeMs * 1000) / 2;
-}
-
-void AudioFlinger::DuplicatingThread::cacheParameters_l()
-{
- // updateWaitTime_l() sets mWaitTimeMs, which affects activeSleepTimeUs(), so call it first
- updateWaitTime_l();
-
- MixerThread::cacheParameters_l();
-}
-
-// ----------------------------------------------------------------------------
-
-// TrackBase constructor must be called with AudioFlinger::mLock held
-AudioFlinger::ThreadBase::TrackBase::TrackBase(
- ThreadBase *thread,
- const sp<Client>& client,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId)
- : RefBase(),
- mThread(thread),
- mClient(client),
- mCblk(NULL),
- // mBuffer
- // mBufferEnd
- mFrameCount(0),
- mState(IDLE),
- mSampleRate(sampleRate),
- mFormat(format),
- mStepServerFailed(false),
- mSessionId(sessionId)
- // mChannelCount
- // mChannelMask
-{
- ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
-
- // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
- size_t size = sizeof(audio_track_cblk_t);
- uint8_t channelCount = popcount(channelMask);
- size_t bufferSize = frameCount*channelCount*sizeof(int16_t);
- if (sharedBuffer == 0) {
- size += bufferSize;
- }
-
- if (client != NULL) {
- mCblkMemory = client->heap()->allocate(size);
- if (mCblkMemory != 0) {
- mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
- if (mCblk != NULL) { // construct the shared structure in-place.
- new(mCblk) audio_track_cblk_t();
- // clear all buffers
- mCblk->frameCount = frameCount;
- mCblk->sampleRate = sampleRate;
-// uncomment the following lines to quickly test 32-bit wraparound
-// mCblk->user = 0xffff0000;
-// mCblk->server = 0xffff0000;
-// mCblk->userBase = 0xffff0000;
-// mCblk->serverBase = 0xffff0000;
- mChannelCount = channelCount;
- mChannelMask = channelMask;
- if (sharedBuffer == 0) {
- mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
- memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
- // Force underrun condition to avoid false underrun callback until first data is
- // written to buffer (other flags are cleared)
- mCblk->flags = CBLK_UNDERRUN_ON;
- } else {
- mBuffer = sharedBuffer->pointer();
- }
- mBufferEnd = (uint8_t *)mBuffer + bufferSize;
- }
- } else {
- ALOGE("not enough memory for AudioTrack size=%u", size);
- client->heap()->dump("AudioTrack");
- return;
- }
- } else {
- mCblk = (audio_track_cblk_t *)(new uint8_t[size]);
- // construct the shared structure in-place.
- new(mCblk) audio_track_cblk_t();
- // clear all buffers
- mCblk->frameCount = frameCount;
- mCblk->sampleRate = sampleRate;
-// uncomment the following lines to quickly test 32-bit wraparound
-// mCblk->user = 0xffff0000;
-// mCblk->server = 0xffff0000;
-// mCblk->userBase = 0xffff0000;
-// mCblk->serverBase = 0xffff0000;
- mChannelCount = channelCount;
- mChannelMask = channelMask;
- mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
- memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t));
- // Force underrun condition to avoid false underrun callback until first data is
- // written to buffer (other flags are cleared)
- mCblk->flags = CBLK_UNDERRUN_ON;
- mBufferEnd = (uint8_t *)mBuffer + bufferSize;
- }
-}
-
-AudioFlinger::ThreadBase::TrackBase::~TrackBase()
-{
- if (mCblk != NULL) {
- if (mClient == 0) {
- delete mCblk;
- } else {
- mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
- }
- }
- mCblkMemory.clear(); // free the shared memory before releasing the heap it belongs to
- if (mClient != 0) {
- // Client destructor must run with AudioFlinger mutex locked
- Mutex::Autolock _l(mClient->audioFlinger()->mLock);
- // If the client's reference count drops to zero, the associated destructor
- // must run with AudioFlinger lock held. Thus the explicit clear() rather than
- // relying on the automatic clear() at end of scope.
- mClient.clear();
- }
-}
-
-// AudioBufferProvider interface
-// getNextBuffer() = 0;
-// This implementation of releaseBuffer() is used by Track and RecordTrack, but not TimedTrack
-void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
-{
- buffer->raw = NULL;
- mFrameCount = buffer->frameCount;
- // FIXME See note at getNextBuffer()
- (void) step(); // ignore return value of step()
- buffer->frameCount = 0;
-}
-
-bool AudioFlinger::ThreadBase::TrackBase::step() {
- bool result;
- audio_track_cblk_t* cblk = this->cblk();
-
- result = cblk->stepServer(mFrameCount);
- if (!result) {
- ALOGV("stepServer failed acquiring cblk mutex");
- mStepServerFailed = true;
- }
- return result;
-}
-
-void AudioFlinger::ThreadBase::TrackBase::reset() {
- audio_track_cblk_t* cblk = this->cblk();
-
- cblk->user = 0;
- cblk->server = 0;
- cblk->userBase = 0;
- cblk->serverBase = 0;
- mStepServerFailed = false;
- ALOGV("TrackBase::reset");
-}
-
-int AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
- return (int)mCblk->sampleRate;
-}
-
-void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
- audio_track_cblk_t* cblk = this->cblk();
- size_t frameSize = cblk->frameSize;
- int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase)*frameSize;
- int8_t *bufferEnd = bufferStart + frames * frameSize;
-
- // Check validity of returned pointer in case the track control block would have been corrupted.
- ALOG_ASSERT(!(bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd),
- "TrackBase::getBuffer buffer out of range:\n"
- " start: %p, end %p , mBuffer %p mBufferEnd %p\n"
- " server %u, serverBase %u, user %u, userBase %u, frameSize %d",
- bufferStart, bufferEnd, mBuffer, mBufferEnd,
- cblk->server, cblk->serverBase, cblk->user, cblk->userBase, frameSize);
-
- return bufferStart;
-}
-
-status_t AudioFlinger::ThreadBase::TrackBase::setSyncEvent(const sp<SyncEvent>& event)
-{
- mSyncEvents.add(event);
- return NO_ERROR;
-}
-
-// ----------------------------------------------------------------------------
-
-// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
-AudioFlinger::PlaybackThread::Track::Track(
- PlaybackThread *thread,
- const sp<Client>& client,
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId,
- IAudioFlinger::track_flags_t flags)
- : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer, sessionId),
- mMute(false),
- mFillingUpStatus(FS_INVALID),
- // mRetryCount initialized later when needed
- mSharedBuffer(sharedBuffer),
- mStreamType(streamType),
- mName(-1), // see note below
- mMainBuffer(thread->mixBuffer()),
- mAuxBuffer(NULL),
- mAuxEffectId(0), mHasVolumeController(false),
- mPresentationCompleteFrames(0),
- mFlags(flags),
- mFastIndex(-1),
- mUnderrunCount(0),
- 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);
- // to avoid leaking a track name, do not allocate one unless there is an mCblk
- mName = thread->getTrackName_l(channelMask, sessionId);
- mCblk->mName = mName;
- if (mName < 0) {
- ALOGE("no more track names available");
- return;
- }
- // only allocate a fast track index if we were able to allocate a normal track name
- 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 < (int)FastMixerState::kMaxFastTracks);
- // FIXME This is too eager. We allocate a fast track index before the
- // fast track becomes active. Since fast tracks are a scarce resource,
- // this means we are potentially denying other more important fast tracks from
- // being created. It would be better to allocate the index dynamically.
- mFastIndex = i;
- mCblk->mName = i;
- // Read the initial underruns because this field is never cleared by the fast mixer
- mObservedUnderruns = thread->getFastTrackUnderruns(i);
- thread->mFastTrackAvailMask &= ~(1 << i);
- }
- }
- ALOGV("Track constructor name %d, calling pid %d", mName, IPCThreadState::self()->getCallingPid());
-}
-
-AudioFlinger::PlaybackThread::Track::~Track()
-{
- ALOGV("PlaybackThread::Track destructor");
-}
-
-void AudioFlinger::PlaybackThread::Track::destroy()
-{
- // NOTE: destroyTrack_l() can remove a strong reference to this Track
- // by removing it from mTracks vector, so there is a risk that this Tracks's
- // destructor is called. As the destructor needs to lock mLock,
- // we must acquire a strong reference on this Track before locking mLock
- // here so that the destructor is called only when exiting this function.
- // On the other hand, as long as Track::destroy() is only called by
- // TrackHandle destructor, the TrackHandle still holds a strong ref on
- // this Track with its member mTrack.
- sp<Track> keep(this);
- { // scope for mLock
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- if (!isOutputTrack()) {
- if (mState == ACTIVE || mState == RESUMING) {
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
- AudioSystem::releaseOutput(thread->id());
- }
- Mutex::Autolock _l(thread->mLock);
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- playbackThread->destroyTrack_l(this);
- }
- }
-}
-
-/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
-{
- result.append(" Name Client Type Fmt Chn mask Session mFrCnt fCount S M F SRate L dB R dB "
- " Server User Main buf Aux Buf Flags Underruns\n");
-}
-
-void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
-{
- uint32_t vlr = mCblk->getVolumeLR();
- if (isFastTrack()) {
- sprintf(buffer, " F %2d", mFastIndex);
- } else {
- sprintf(buffer, " %4d", mName - AudioMixer::TRACK0);
- }
- track_state state = mState;
- char stateChar;
- switch (state) {
- case IDLE:
- stateChar = 'I';
- break;
- case TERMINATED:
- stateChar = 'T';
- break;
- case STOPPING_1:
- stateChar = 's';
- break;
- case STOPPING_2:
- stateChar = '5';
- break;
- case STOPPED:
- stateChar = 'S';
- break;
- case RESUMING:
- stateChar = 'R';
- break;
- case ACTIVE:
- stateChar = 'A';
- break;
- case PAUSING:
- stateChar = 'p';
- break;
- case PAUSED:
- stateChar = 'P';
- break;
- case FLUSHED:
- stateChar = 'F';
- break;
- default:
- stateChar = '?';
- break;
- }
- char nowInUnderrun;
- switch (mObservedUnderruns.mBitFields.mMostRecent) {
- case UNDERRUN_FULL:
- nowInUnderrun = ' ';
- break;
- case UNDERRUN_PARTIAL:
- nowInUnderrun = '<';
- break;
- case UNDERRUN_EMPTY:
- nowInUnderrun = '*';
- break;
- default:
- nowInUnderrun = '?';
- break;
- }
- snprintf(&buffer[7], size-7, " %6d %4u %3u 0x%08x %7u %6u %6u %1c %1d %1d %5u %5.2g %5.2g "
- "0x%08x 0x%08x 0x%08x 0x%08x %#5x %9u%c\n",
- (mClient == 0) ? getpid_cached : mClient->pid(),
- mStreamType,
- mFormat,
- mChannelMask,
- mSessionId,
- mFrameCount,
- mCblk->frameCount,
- stateChar,
- mMute,
- mFillingUpStatus,
- mCblk->sampleRate,
- 20.0 * log10((vlr & 0xFFFF) / 4096.0),
- 20.0 * log10((vlr >> 16) / 4096.0),
- mCblk->server,
- mCblk->user,
- (int)mMainBuffer,
- (int)mAuxBuffer,
- mCblk->flags,
- mUnderrunCount,
- nowInUnderrun);
-}
-
-// AudioBufferProvider interface
-status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
- AudioBufferProvider::Buffer* buffer, int64_t pts)
-{
- audio_track_cblk_t* cblk = this->cblk();
- uint32_t framesReady;
- uint32_t framesReq = buffer->frameCount;
-
- // Check if last stepServer failed, try to step now
- if (mStepServerFailed) {
- // FIXME When called by fast mixer, this takes a mutex with tryLock().
- // Since the fast mixer is higher priority than client callback thread,
- // it does not result in priority inversion for client.
- // But a non-blocking solution would be preferable to avoid
- // fast mixer being unable to tryLock(), and
- // to avoid the extra context switches if the client wakes up,
- // discovers the mutex is locked, then has to wait for fast mixer to unlock.
- if (!step()) goto getNextBuffer_exit;
- ALOGV("stepServer recovered");
- mStepServerFailed = false;
- }
-
- // FIXME Same as above
- framesReady = cblk->framesReady();
-
- if (CC_LIKELY(framesReady)) {
- uint32_t s = cblk->server;
- uint32_t bufferEnd = cblk->serverBase + cblk->frameCount;
-
- bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd;
- if (framesReq > framesReady) {
- framesReq = framesReady;
- }
- if (framesReq > bufferEnd - s) {
- framesReq = bufferEnd - s;
- }
-
- buffer->raw = getBuffer(s, framesReq);
- buffer->frameCount = framesReq;
- return NO_ERROR;
- }
-
-getNextBuffer_exit:
- buffer->raw = NULL;
- buffer->frameCount = 0;
- ALOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
- return NOT_ENOUGH_DATA;
-}
-
-// Note that framesReady() takes a mutex on the control block using tryLock().
-// This could result in priority inversion if framesReady() is called by the normal mixer,
-// as the normal mixer thread runs at lower
-// priority than the client's callback thread: there is a short window within framesReady()
-// during which the normal mixer could be preempted, and the client callback would block.
-// Another problem can occur if framesReady() is called by the fast mixer:
-// the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer.
-// FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue.
-size_t AudioFlinger::PlaybackThread::Track::framesReady() const {
- return mCblk->framesReady();
-}
-
-// Don't call for fast tracks; the framesReady() could result in priority inversion
-bool AudioFlinger::PlaybackThread::Track::isReady() const {
- if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing()) return true;
-
- if (framesReady() >= mCblk->frameCount ||
- (mCblk->flags & CBLK_FORCEREADY_MSK)) {
- mFillingUpStatus = FS_FILLED;
- android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
- return true;
- }
- return false;
-}
-
-status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event,
- 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) {
- Mutex::Autolock _l(thread->mLock);
- track_state state = mState;
- // here the track could be either new, or restarted
- // in both cases "unstop" the track
- if (mState == PAUSED) {
- mState = TrackBase::RESUMING;
- ALOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
- } else {
- mState = TrackBase::ACTIVE;
- ALOGV("? => ACTIVE (%d) on thread %p", mName, this);
- }
-
- if (!isOutputTrack() && state != ACTIVE && state != RESUMING) {
- thread->mLock.unlock();
- status = AudioSystem::startOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- if (status == NO_ERROR) {
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
- }
-#endif
- }
- if (status == NO_ERROR) {
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- playbackThread->addTrack_l(this);
- } else {
- mState = state;
- triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
- }
- } else {
- status = BAD_VALUE;
- }
- return status;
-}
-
-void AudioFlinger::PlaybackThread::Track::stop()
-{
- ALOGV("stop(%d), calling pid %d", mName, IPCThreadState::self()->getCallingPid());
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
- track_state state = mState;
- if (state == RESUMING || state == ACTIVE || state == PAUSING || state == PAUSED) {
- // If the track is not active (PAUSED and buffers full), flush buffers
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- if (playbackThread->mActiveTracks.indexOf(this) < 0) {
- reset();
- mState = STOPPED;
- } else if (!isFastTrack()) {
- mState = STOPPED;
- } else {
- // prepareTracks_l() will set state to STOPPING_2 after next underrun,
- // and then to STOPPED and reset() when presentation is complete
- mState = STOPPING_1;
- }
- ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName, playbackThread);
- }
- if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
- thread->mLock.unlock();
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
- }
-}
-
-void AudioFlinger::PlaybackThread::Track::pause()
-{
- ALOGV("pause(%d), calling pid %d", mName, IPCThreadState::self()->getCallingPid());
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
- if (mState == ACTIVE || mState == RESUMING) {
- mState = PAUSING;
- ALOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
- if (!isOutputTrack()) {
- thread->mLock.unlock();
- AudioSystem::stopOutput(thread->id(), mStreamType, mSessionId);
- thread->mLock.lock();
-
-#ifdef ADD_BATTERY_DATA
- // to track the speaker usage
- addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
-#endif
- }
- }
- }
-}
-
-void AudioFlinger::PlaybackThread::Track::flush()
-{
- ALOGV("flush(%d)", mName);
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
- if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED && mState != PAUSED &&
- mState != PAUSING) {
- return;
- }
- // No point remaining in PAUSED state after a flush => go to
- // FLUSHED state
- mState = FLUSHED;
- // do not reset the track if it is still in the process of being stopped or paused.
- // this will be done by prepareTracks_l() when the track is stopped.
- // prepareTracks_l() will see mState == FLUSHED, then
- // remove from active track list, reset(), and trigger presentation complete
- PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- if (playbackThread->mActiveTracks.indexOf(this) < 0) {
- reset();
- }
- }
-}
-
-void AudioFlinger::PlaybackThread::Track::reset()
-{
- // Do not reset twice to avoid discarding data written just after a flush and before
- // the audioflinger thread detects the track is stopped.
- if (!mResetDone) {
- TrackBase::reset();
- // Force underrun condition to avoid false underrun callback until first data is
- // written to buffer
- android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags);
- android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
- mFillingUpStatus = FS_FILLING;
- mResetDone = true;
- if (mState == FLUSHED) {
- mState = IDLE;
- }
- }
-}
-
-void AudioFlinger::PlaybackThread::Track::mute(bool muted)
-{
- mMute = muted;
-}
-
-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();
- sp<AudioFlinger> af = mClient->audioFlinger();
-
- Mutex::Autolock _l(af->mLock);
-
- sp<PlaybackThread> srcThread = af->getEffectThread_l(AUDIO_SESSION_OUTPUT_MIX, EffectId);
-
- if (EffectId != 0 && srcThread != 0 && playbackThread != srcThread.get()) {
- Mutex::Autolock _dl(playbackThread->mLock);
- Mutex::Autolock _sl(srcThread->mLock);
- sp<EffectChain> chain = srcThread->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
- if (chain == 0) {
- return INVALID_OPERATION;
- }
-
- sp<EffectModule> effect = chain->getEffectFromId_l(EffectId);
- if (effect == 0) {
- return INVALID_OPERATION;
- }
- srcThread->removeEffect_l(effect);
- playbackThread->addEffect_l(effect);
- // removeEffect_l() has stopped the effect if it was active so it must be restarted
- if (effect->state() == EffectModule::ACTIVE ||
- effect->state() == EffectModule::STOPPING) {
- effect->start();
- }
-
- sp<EffectChain> dstChain = effect->chain().promote();
- if (dstChain == 0) {
- srcThread->addEffect_l(effect);
- return INVALID_OPERATION;
- }
- AudioSystem::unregisterEffect(effect->id());
- AudioSystem::registerEffect(&effect->desc(),
- srcThread->id(),
- dstChain->strategy(),
- AUDIO_SESSION_OUTPUT_MIX,
- effect->id());
- }
- status = playbackThread->attachAuxEffect(this, EffectId);
- }
- return status;
-}
-
-void AudioFlinger::PlaybackThread::Track::setAuxBuffer(int EffectId, int32_t *buffer)
-{
- mAuxEffectId = EffectId;
- mAuxBuffer = buffer;
-}
-
-bool AudioFlinger::PlaybackThread::Track::presentationComplete(size_t framesWritten,
- size_t audioHalFrames)
-{
- // a track is considered presented when the total number of frames written to audio HAL
- // corresponds to the number of frames written when presentationComplete() is called for the
- // first time (mPresentationCompleteFrames == 0) plus the buffer filling status at that time.
- if (mPresentationCompleteFrames == 0) {
- mPresentationCompleteFrames = framesWritten + audioHalFrames;
- ALOGV("presentationComplete() reset: mPresentationCompleteFrames %d audioHalFrames %d",
- mPresentationCompleteFrames, audioHalFrames);
- }
- if (framesWritten >= mPresentationCompleteFrames) {
- ALOGV("presentationComplete() session %d complete: framesWritten %d",
- mSessionId, framesWritten);
- triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
- return true;
- }
- return false;
-}
-
-void AudioFlinger::PlaybackThread::Track::triggerEvents(AudioSystem::sync_event_t type)
-{
- for (int i = 0; i < (int)mSyncEvents.size(); i++) {
- if (mSyncEvents[i]->type() == type) {
- mSyncEvents[i]->trigger();
- mSyncEvents.removeAt(i);
- i--;
- }
- }
-}
-
-// 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;
-}
-
-status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>& event)
-{
- if (mState == TERMINATED || mState == PAUSED ||
- ((framesReady() == 0) && ((mSharedBuffer != 0) ||
- (mState == STOPPED)))) {
- ALOGW("Track::setSyncEvent() in invalid state %d on session %d %s mode, framesReady %d ",
- mState, mSessionId, (mSharedBuffer != 0) ? "static" : "stream", framesReady());
- event->cancel();
- return INVALID_OPERATION;
- }
- (void) TrackBase::setSyncEvent(event);
- return NO_ERROR;
-}
-
-// timed audio tracks
-
-sp<AudioFlinger::PlaybackThread::TimedTrack>
-AudioFlinger::PlaybackThread::TimedTrack::create(
- PlaybackThread *thread,
- const sp<Client>& client,
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId) {
- if (!client->reserveTimedTrack())
- return 0;
-
- return new TimedTrack(
- thread, client, streamType, sampleRate, format, channelMask, frameCount,
- sharedBuffer, sessionId);
-}
-
-AudioFlinger::PlaybackThread::TimedTrack::TimedTrack(
- PlaybackThread *thread,
- const sp<Client>& client,
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId)
- : Track(thread, client, streamType, sampleRate, format, channelMask,
- frameCount, sharedBuffer, sessionId, IAudioFlinger::TRACK_TIMED),
- mQueueHeadInFlight(false),
- mTrimQueueHeadOnRelease(false),
- mFramesPendingInQueue(0),
- mTimedSilenceBuffer(NULL),
- mTimedSilenceBufferSize(0),
- mTimedAudioOutputOnTime(false),
- mMediaTimeTransformValid(false)
-{
- LocalClock lc;
- mLocalTimeFreq = lc.getLocalFreq();
-
- mLocalTimeToSampleTransform.a_zero = 0;
- mLocalTimeToSampleTransform.b_zero = 0;
- mLocalTimeToSampleTransform.a_to_b_numer = sampleRate;
- mLocalTimeToSampleTransform.a_to_b_denom = mLocalTimeFreq;
- LinearTransform::reduce(&mLocalTimeToSampleTransform.a_to_b_numer,
- &mLocalTimeToSampleTransform.a_to_b_denom);
-
- mMediaTimeToSampleTransform.a_zero = 0;
- mMediaTimeToSampleTransform.b_zero = 0;
- mMediaTimeToSampleTransform.a_to_b_numer = sampleRate;
- mMediaTimeToSampleTransform.a_to_b_denom = 1000000;
- LinearTransform::reduce(&mMediaTimeToSampleTransform.a_to_b_numer,
- &mMediaTimeToSampleTransform.a_to_b_denom);
-}
-
-AudioFlinger::PlaybackThread::TimedTrack::~TimedTrack() {
- mClient->releaseTimedTrack();
- delete [] mTimedSilenceBuffer;
-}
-
-status_t AudioFlinger::PlaybackThread::TimedTrack::allocateTimedBuffer(
- size_t size, sp<IMemory>* buffer) {
-
- Mutex::Autolock _l(mTimedBufferQueueLock);
-
- trimTimedBufferQueue_l();
-
- // lazily initialize the shared memory heap for timed buffers
- if (mTimedMemoryDealer == NULL) {
- const int kTimedBufferHeapSize = 512 << 10;
-
- mTimedMemoryDealer = new MemoryDealer(kTimedBufferHeapSize,
- "AudioFlingerTimed");
- if (mTimedMemoryDealer == NULL)
- return NO_MEMORY;
- }
-
- sp<IMemory> newBuffer = mTimedMemoryDealer->allocate(size);
- if (newBuffer == NULL) {
- newBuffer = mTimedMemoryDealer->allocate(size);
- if (newBuffer == NULL)
- return NO_MEMORY;
- }
-
- *buffer = newBuffer;
- return NO_ERROR;
-}
-
-// caller must hold mTimedBufferQueueLock
-void AudioFlinger::PlaybackThread::TimedTrack::trimTimedBufferQueue_l() {
- int64_t mediaTimeNow;
- {
- Mutex::Autolock mttLock(mMediaTimeTransformLock);
- if (!mMediaTimeTransformValid)
- return;
-
- int64_t targetTimeNow;
- status_t res = (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME)
- ? mCCHelper.getCommonTime(&targetTimeNow)
- : mCCHelper.getLocalTime(&targetTimeNow);
-
- if (OK != res)
- return;
-
- if (!mMediaTimeTransform.doReverseTransform(targetTimeNow,
- &mediaTimeNow)) {
- return;
- }
- }
-
- size_t trimEnd;
- for (trimEnd = 0; trimEnd < mTimedBufferQueue.size(); trimEnd++) {
- int64_t bufEnd;
-
- if ((trimEnd + 1) < mTimedBufferQueue.size()) {
- // We have a next buffer. Just use its PTS as the PTS of the frame
- // following the last frame in this buffer. If the stream is sparse
- // (ie, there are deliberate gaps left in the stream which should be
- // filled with silence by the TimedAudioTrack), then this can result
- // in one extra buffer being left un-trimmed when it could have
- // been. In general, this is not typical, and we would rather
- // optimized away the TS calculation below for the more common case
- // where PTSes are contiguous.
- bufEnd = mTimedBufferQueue[trimEnd + 1].pts();
- } else {
- // We have no next buffer. Compute the PTS of the frame following
- // the last frame in this buffer by computing the duration of of
- // this frame in media time units and adding it to the PTS of the
- // buffer.
- int64_t frameCount = mTimedBufferQueue[trimEnd].buffer()->size()
- / mCblk->frameSize;
-
- if (!mMediaTimeToSampleTransform.doReverseTransform(frameCount,
- &bufEnd)) {
- ALOGE("Failed to convert frame count of %lld to media time"
- " duration" " (scale factor %d/%u) in %s",
- frameCount,
- mMediaTimeToSampleTransform.a_to_b_numer,
- mMediaTimeToSampleTransform.a_to_b_denom,
- __PRETTY_FUNCTION__);
- break;
- }
- bufEnd += mTimedBufferQueue[trimEnd].pts();
- }
-
- if (bufEnd > mediaTimeNow)
- break;
-
- // Is the buffer we want to use in the middle of a mix operation right
- // now? If so, don't actually trim it. Just wait for the releaseBuffer
- // from the mixer which should be coming back shortly.
- if (!trimEnd && mQueueHeadInFlight) {
- mTrimQueueHeadOnRelease = true;
- }
- }
-
- size_t trimStart = mTrimQueueHeadOnRelease ? 1 : 0;
- if (trimStart < trimEnd) {
- // Update the bookkeeping for framesReady()
- for (size_t i = trimStart; i < trimEnd; ++i) {
- updateFramesPendingAfterTrim_l(mTimedBufferQueue[i], "trim");
- }
-
- // Now actually remove the buffers from the queue.
- mTimedBufferQueue.removeItemsAt(trimStart, trimEnd);
- }
-}
-
-void AudioFlinger::PlaybackThread::TimedTrack::trimTimedBufferQueueHead_l(
- const char* logTag) {
- ALOG_ASSERT(mTimedBufferQueue.size() > 0,
- "%s called (reason \"%s\"), but timed buffer queue has no"
- " elements to trim.", __FUNCTION__, logTag);
-
- updateFramesPendingAfterTrim_l(mTimedBufferQueue[0], logTag);
- mTimedBufferQueue.removeAt(0);
-}
-
-void AudioFlinger::PlaybackThread::TimedTrack::updateFramesPendingAfterTrim_l(
- const TimedBuffer& buf,
- const char* logTag) {
- uint32_t bufBytes = buf.buffer()->size();
- uint32_t consumedAlready = buf.position();
-
- ALOG_ASSERT(consumedAlready <= bufBytes,
- "Bad bookkeeping while updating frames pending. Timed buffer is"
- " only %u bytes long, but claims to have consumed %u"
- " bytes. (update reason: \"%s\")",
- bufBytes, consumedAlready, logTag);
-
- uint32_t bufFrames = (bufBytes - consumedAlready) / mCblk->frameSize;
- ALOG_ASSERT(mFramesPendingInQueue >= bufFrames,
- "Bad bookkeeping while updating frames pending. Should have at"
- " least %u queued frames, but we think we have only %u. (update"
- " reason: \"%s\")",
- bufFrames, mFramesPendingInQueue, logTag);
-
- mFramesPendingInQueue -= bufFrames;
-}
-
-status_t AudioFlinger::PlaybackThread::TimedTrack::queueTimedBuffer(
- const sp<IMemory>& buffer, int64_t pts) {
-
- {
- Mutex::Autolock mttLock(mMediaTimeTransformLock);
- if (!mMediaTimeTransformValid)
- return INVALID_OPERATION;
- }
-
- Mutex::Autolock _l(mTimedBufferQueueLock);
-
- uint32_t bufFrames = buffer->size() / mCblk->frameSize;
- mFramesPendingInQueue += bufFrames;
- mTimedBufferQueue.add(TimedBuffer(buffer, pts));
-
- return NO_ERROR;
-}
-
-status_t AudioFlinger::PlaybackThread::TimedTrack::setMediaTimeTransform(
- const LinearTransform& xform, TimedAudioTrack::TargetTimeline target) {
-
- ALOGVV("setMediaTimeTransform az=%lld bz=%lld n=%d d=%u tgt=%d",
- xform.a_zero, xform.b_zero, xform.a_to_b_numer, xform.a_to_b_denom,
- target);
-
- if (!(target == TimedAudioTrack::LOCAL_TIME ||
- target == TimedAudioTrack::COMMON_TIME)) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mMediaTimeTransformLock);
- mMediaTimeTransform = xform;
- mMediaTimeTransformTarget = target;
- mMediaTimeTransformValid = true;
-
- return NO_ERROR;
-}
-
-#define min(a, b) ((a) < (b) ? (a) : (b))
-
-// implementation of getNextBuffer for tracks whose buffers have timestamps
-status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
- AudioBufferProvider::Buffer* buffer, int64_t pts)
-{
- if (pts == AudioBufferProvider::kInvalidPTS) {
- buffer->raw = NULL;
- buffer->frameCount = 0;
- mTimedAudioOutputOnTime = false;
- return INVALID_OPERATION;
- }
-
- Mutex::Autolock _l(mTimedBufferQueueLock);
-
- ALOG_ASSERT(!mQueueHeadInFlight,
- "getNextBuffer called without releaseBuffer!");
-
- while (true) {
-
- // if we have no timed buffers, then fail
- if (mTimedBufferQueue.isEmpty()) {
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
- }
-
- TimedBuffer& head = mTimedBufferQueue.editItemAt(0);
-
- // calculate the PTS of the head of the timed buffer queue expressed in
- // local time
- int64_t headLocalPTS;
- {
- Mutex::Autolock mttLock(mMediaTimeTransformLock);
-
- ALOG_ASSERT(mMediaTimeTransformValid, "media time transform invalid");
-
- if (mMediaTimeTransform.a_to_b_denom == 0) {
- // the transform represents a pause, so yield silence
- timedYieldSilence_l(buffer->frameCount, buffer);
- return NO_ERROR;
- }
-
- int64_t transformedPTS;
- if (!mMediaTimeTransform.doForwardTransform(head.pts(),
- &transformedPTS)) {
- // the transform failed. this shouldn't happen, but if it does
- // then just drop this buffer
- ALOGW("timedGetNextBuffer transform failed");
- buffer->raw = NULL;
- buffer->frameCount = 0;
- trimTimedBufferQueueHead_l("getNextBuffer; no transform");
- return NO_ERROR;
- }
-
- if (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME) {
- if (OK != mCCHelper.commonTimeToLocalTime(transformedPTS,
- &headLocalPTS)) {
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return INVALID_OPERATION;
- }
- } else {
- headLocalPTS = transformedPTS;
- }
- }
-
- // adjust the head buffer's PTS to reflect the portion of the head buffer
- // that has already been consumed
- int64_t effectivePTS = headLocalPTS +
- ((head.position() / mCblk->frameSize) * mLocalTimeFreq / sampleRate());
-
- // Calculate the delta in samples between the head of the input buffer
- // queue and the start of the next output buffer that will be written.
- // If the transformation fails because of over or underflow, it means
- // that the sample's position in the output stream is so far out of
- // whack that it should just be dropped.
- int64_t sampleDelta;
- if (llabs(effectivePTS - pts) >= (static_cast<int64_t>(1) << 31)) {
- ALOGV("*** head buffer is too far from PTS: dropped buffer");
- trimTimedBufferQueueHead_l("getNextBuffer, buf pts too far from"
- " mix");
- continue;
- }
- if (!mLocalTimeToSampleTransform.doForwardTransform(
- (effectivePTS - pts) << 32, &sampleDelta)) {
- ALOGV("*** too late during sample rate transform: dropped buffer");
- trimTimedBufferQueueHead_l("getNextBuffer, bad local to sample");
- continue;
- }
-
- ALOGVV("*** getNextBuffer head.pts=%lld head.pos=%d pts=%lld"
- " sampleDelta=[%d.%08x]",
- head.pts(), head.position(), pts,
- static_cast<int32_t>((sampleDelta >= 0 ? 0 : 1)
- + (sampleDelta >> 32)),
- static_cast<uint32_t>(sampleDelta & 0xFFFFFFFF));
-
- // if the delta between the ideal placement for the next input sample and
- // the current output position is within this threshold, then we will
- // concatenate the next input samples to the previous output
- const int64_t kSampleContinuityThreshold =
- (static_cast<int64_t>(sampleRate()) << 32) / 250;
-
- // if this is the first buffer of audio that we're emitting from this track
- // then it should be almost exactly on time.
- const int64_t kSampleStartupThreshold = 1LL << 32;
-
- if ((mTimedAudioOutputOnTime && llabs(sampleDelta) <= kSampleContinuityThreshold) ||
- (!mTimedAudioOutputOnTime && llabs(sampleDelta) <= kSampleStartupThreshold)) {
- // the next input is close enough to being on time, so concatenate it
- // with the last output
- timedYieldSamples_l(buffer);
-
- ALOGVV("*** on time: head.pos=%d frameCount=%u",
- head.position(), buffer->frameCount);
- return NO_ERROR;
- }
-
- // Looks like our output is not on time. Reset our on timed status.
- // Next time we mix samples from our input queue, then should be within
- // the StartupThreshold.
- mTimedAudioOutputOnTime = false;
- if (sampleDelta > 0) {
- // the gap between the current output position and the proper start of
- // the next input sample is too big, so fill it with silence
- uint32_t framesUntilNextInput = (sampleDelta + 0x80000000) >> 32;
-
- timedYieldSilence_l(framesUntilNextInput, buffer);
- ALOGV("*** silence: frameCount=%u", buffer->frameCount);
- return NO_ERROR;
- } else {
- // the next input sample is late
- uint32_t lateFrames = static_cast<uint32_t>(-((sampleDelta + 0x80000000) >> 32));
- size_t onTimeSamplePosition =
- head.position() + lateFrames * mCblk->frameSize;
-
- if (onTimeSamplePosition > head.buffer()->size()) {
- // all the remaining samples in the head are too late, so
- // drop it and move on
- ALOGV("*** too late: dropped buffer");
- trimTimedBufferQueueHead_l("getNextBuffer, dropped late buffer");
- continue;
- } else {
- // skip over the late samples
- head.setPosition(onTimeSamplePosition);
-
- // yield the available samples
- timedYieldSamples_l(buffer);
-
- ALOGV("*** late: head.pos=%d frameCount=%u", head.position(), buffer->frameCount);
- return NO_ERROR;
- }
- }
- }
-}
-
-// Yield samples from the timed buffer queue head up to the given output
-// buffer's capacity.
-//
-// Caller must hold mTimedBufferQueueLock
-void AudioFlinger::PlaybackThread::TimedTrack::timedYieldSamples_l(
- AudioBufferProvider::Buffer* buffer) {
-
- const TimedBuffer& head = mTimedBufferQueue[0];
-
- buffer->raw = (static_cast<uint8_t*>(head.buffer()->pointer()) +
- head.position());
-
- uint32_t framesLeftInHead = ((head.buffer()->size() - head.position()) /
- mCblk->frameSize);
- size_t framesRequested = buffer->frameCount;
- buffer->frameCount = min(framesLeftInHead, framesRequested);
-
- mQueueHeadInFlight = true;
- mTimedAudioOutputOnTime = true;
-}
-
-// Yield samples of silence up to the given output buffer's capacity
-//
-// Caller must hold mTimedBufferQueueLock
-void AudioFlinger::PlaybackThread::TimedTrack::timedYieldSilence_l(
- uint32_t numFrames, AudioBufferProvider::Buffer* buffer) {
-
- // lazily allocate a buffer filled with silence
- if (mTimedSilenceBufferSize < numFrames * mCblk->frameSize) {
- delete [] mTimedSilenceBuffer;
- mTimedSilenceBufferSize = numFrames * mCblk->frameSize;
- mTimedSilenceBuffer = new uint8_t[mTimedSilenceBufferSize];
- memset(mTimedSilenceBuffer, 0, mTimedSilenceBufferSize);
- }
-
- buffer->raw = mTimedSilenceBuffer;
- size_t framesRequested = buffer->frameCount;
- buffer->frameCount = min(numFrames, framesRequested);
-
- mTimedAudioOutputOnTime = false;
-}
-
-// AudioBufferProvider interface
-void AudioFlinger::PlaybackThread::TimedTrack::releaseBuffer(
- AudioBufferProvider::Buffer* buffer) {
-
- Mutex::Autolock _l(mTimedBufferQueueLock);
-
- // If the buffer which was just released is part of the buffer at the head
- // of the queue, be sure to update the amt of the buffer which has been
- // consumed. If the buffer being returned is not part of the head of the
- // queue, its either because the buffer is part of the silence buffer, or
- // because the head of the timed queue was trimmed after the mixer called
- // getNextBuffer but before the mixer called releaseBuffer.
- if (buffer->raw == mTimedSilenceBuffer) {
- ALOG_ASSERT(!mQueueHeadInFlight,
- "Queue head in flight during release of silence buffer!");
- goto done;
- }
-
- ALOG_ASSERT(mQueueHeadInFlight,
- "TimedTrack::releaseBuffer of non-silence buffer, but no queue"
- " head in flight.");
-
- if (mTimedBufferQueue.size()) {
- TimedBuffer& head = mTimedBufferQueue.editItemAt(0);
-
- void* start = head.buffer()->pointer();
- void* end = reinterpret_cast<void*>(
- reinterpret_cast<uint8_t*>(head.buffer()->pointer())
- + head.buffer()->size());
-
- ALOG_ASSERT((buffer->raw >= start) && (buffer->raw < end),
- "released buffer not within the head of the timed buffer"
- " queue; qHead = [%p, %p], released buffer = %p",
- start, end, buffer->raw);
-
- head.setPosition(head.position() +
- (buffer->frameCount * mCblk->frameSize));
- mQueueHeadInFlight = false;
-
- ALOG_ASSERT(mFramesPendingInQueue >= buffer->frameCount,
- "Bad bookkeeping during releaseBuffer! Should have at"
- " least %u queued frames, but we think we have only %u",
- buffer->frameCount, mFramesPendingInQueue);
-
- mFramesPendingInQueue -= buffer->frameCount;
-
- if ((static_cast<size_t>(head.position()) >= head.buffer()->size())
- || mTrimQueueHeadOnRelease) {
- trimTimedBufferQueueHead_l("releaseBuffer");
- mTrimQueueHeadOnRelease = false;
- }
- } else {
- LOG_FATAL("TimedTrack::releaseBuffer of non-silence buffer with no"
- " buffers in the timed buffer queue");
- }
-
-done:
- buffer->raw = 0;
- buffer->frameCount = 0;
-}
-
-size_t AudioFlinger::PlaybackThread::TimedTrack::framesReady() const {
- Mutex::Autolock _l(mTimedBufferQueueLock);
- return mFramesPendingInQueue;
-}
-
-AudioFlinger::PlaybackThread::TimedTrack::TimedBuffer::TimedBuffer()
- : mPTS(0), mPosition(0) {}
-
-AudioFlinger::PlaybackThread::TimedTrack::TimedBuffer::TimedBuffer(
- const sp<IMemory>& buffer, int64_t pts)
- : mBuffer(buffer), mPTS(pts), mPosition(0) {}
-
-// ----------------------------------------------------------------------------
-
-// RecordTrack constructor must be called with AudioFlinger::mLock held
-AudioFlinger::RecordThread::RecordTrack::RecordTrack(
- RecordThread *thread,
- const sp<Client>& client,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- int sessionId)
- : TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId),
- mOverflow(false)
-{
- if (mCblk != NULL) {
- ALOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
- if (format == AUDIO_FORMAT_PCM_16_BIT) {
- mCblk->frameSize = mChannelCount * sizeof(int16_t);
- } else if (format == AUDIO_FORMAT_PCM_8_BIT) {
- mCblk->frameSize = mChannelCount * sizeof(int8_t);
- } else {
- mCblk->frameSize = sizeof(int8_t);
- }
- }
-}
-
-AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
-{
- ALOGV("%s", __func__);
-}
-
-// AudioBufferProvider interface
-status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts)
-{
- audio_track_cblk_t* cblk = this->cblk();
- uint32_t framesAvail;
- uint32_t framesReq = buffer->frameCount;
-
- // Check if last stepServer failed, try to step now
- if (mStepServerFailed) {
- if (!step()) goto getNextBuffer_exit;
- ALOGV("stepServer recovered");
- mStepServerFailed = false;
- }
-
- framesAvail = cblk->framesAvailable_l();
-
- if (CC_LIKELY(framesAvail)) {
- uint32_t s = cblk->server;
- uint32_t bufferEnd = cblk->serverBase + cblk->frameCount;
-
- if (framesReq > framesAvail) {
- framesReq = framesAvail;
- }
- if (framesReq > bufferEnd - s) {
- framesReq = bufferEnd - s;
- }
-
- buffer->raw = getBuffer(s, framesReq);
- buffer->frameCount = framesReq;
- return NO_ERROR;
- }
-
-getNextBuffer_exit:
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
-}
-
-status_t AudioFlinger::RecordThread::RecordTrack::start(AudioSystem::sync_event_t event,
- int triggerSession)
-{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- RecordThread *recordThread = (RecordThread *)thread.get();
- return recordThread->start(this, event, triggerSession);
- } else {
- return BAD_VALUE;
- }
-}
-
-void AudioFlinger::RecordThread::RecordTrack::stop()
-{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- RecordThread *recordThread = (RecordThread *)thread.get();
- recordThread->mLock.lock();
- bool doStop = recordThread->stop_l(this);
- if (doStop) {
- TrackBase::reset();
- // Force overrun condition to avoid false overrun callback until first data is
- // read from buffer
- android_atomic_or(CBLK_UNDERRUN_ON, &mCblk->flags);
- }
- recordThread->mLock.unlock();
- if (doStop) {
- AudioSystem::stopInput(recordThread->id());
- }
- }
-}
-
-/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
-{
- result.append(" Clien Fmt Chn mask Session Buf S SRate Serv User FrameCount\n");
-}
-
-void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
-{
- snprintf(buffer, size, " %05d %03u 0x%08x %05d %04u %01d %05u %08x %08x %05d\n",
- (mClient == 0) ? getpid_cached : mClient->pid(),
- mFormat,
- mChannelMask,
- mSessionId,
- mFrameCount,
- mState,
- mCblk->sampleRate,
- mCblk->server,
- mCblk->user,
- mCblk->frameCount);
-}
-
-
-// ----------------------------------------------------------------------------
-
-AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
- PlaybackThread *playbackThread,
- DuplicatingThread *sourceThread,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount)
- : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
- NULL, 0, IAudioFlinger::TRACK_DEFAULT),
- mActive(false), mSourceThread(sourceThread)
-{
-
- if (mCblk != NULL) {
- mCblk->flags |= CBLK_DIRECTION_OUT;
- mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- mOutBuffer.frameCount = 0;
- playbackThread->mTracks.add(this);
- ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, mCblk->buffers %p, " \
- "mCblk->frameCount %d, mCblk->sampleRate %d, mChannelMask 0x%08x mBufferEnd %p",
- mCblk, mBuffer, mCblk->buffers,
- mCblk->frameCount, mCblk->sampleRate, mChannelMask, mBufferEnd);
- } else {
- ALOGW("Error creating output track on thread %p", playbackThread);
- }
-}
-
-AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack()
-{
- clearBufferQueue();
-}
-
-status_t AudioFlinger::PlaybackThread::OutputTrack::start(AudioSystem::sync_event_t event,
- int triggerSession)
-{
- status_t status = Track::start(event, triggerSession);
- if (status != NO_ERROR) {
- return status;
- }
-
- mActive = true;
- mRetryCount = 127;
- return status;
-}
-
-void AudioFlinger::PlaybackThread::OutputTrack::stop()
-{
- Track::stop();
- clearBufferQueue();
- mOutBuffer.frameCount = 0;
- mActive = false;
-}
-
-bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames)
-{
- Buffer *pInBuffer;
- Buffer inBuffer;
- uint32_t channelCount = mChannelCount;
- bool outputBufferFull = false;
- inBuffer.frameCount = frames;
- inBuffer.i16 = data;
-
- uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
-
- if (!mActive && frames != 0) {
- start();
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- MixerThread *mixerThread = (MixerThread *)thread.get();
- if (mCblk->frameCount > frames){
- if (mBufferQueue.size() < kMaxOverFlowBuffers) {
- uint32_t startFrames = (mCblk->frameCount - frames);
- pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[startFrames * channelCount];
- pInBuffer->frameCount = startFrames;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t));
- mBufferQueue.add(pInBuffer);
- } else {
- ALOGW ("OutputTrack::write() %p no more buffers in queue", this);
- }
- }
- }
- }
-
- while (waitTimeLeftMs) {
- // First write pending buffers, then new data
- if (mBufferQueue.size()) {
- pInBuffer = mBufferQueue.itemAt(0);
- } else {
- pInBuffer = &inBuffer;
- }
-
- if (pInBuffer->frameCount == 0) {
- break;
- }
-
- if (mOutBuffer.frameCount == 0) {
- mOutBuffer.frameCount = pInBuffer->frameCount;
- nsecs_t startTime = systemTime();
- if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)NO_MORE_BUFFERS) {
- ALOGV ("OutputTrack::write() %p thread %p no more output buffers", this, mThread.unsafe_get());
- outputBufferFull = true;
- break;
- }
- uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime);
- if (waitTimeLeftMs >= waitTimeMs) {
- waitTimeLeftMs -= waitTimeMs;
- } else {
- waitTimeLeftMs = 0;
- }
- }
-
- uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount : pInBuffer->frameCount;
- memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
- mCblk->stepUser(outFrames);
- pInBuffer->frameCount -= outFrames;
- pInBuffer->i16 += outFrames * channelCount;
- mOutBuffer.frameCount -= outFrames;
- mOutBuffer.i16 += outFrames * channelCount;
-
- if (pInBuffer->frameCount == 0) {
- if (mBufferQueue.size()) {
- mBufferQueue.removeAt(0);
- delete [] pInBuffer->mBuffer;
- delete pInBuffer;
- ALOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size());
- } else {
- break;
- }
- }
- }
-
- // If we could not write all frames, allocate a buffer and queue it for next time.
- if (inBuffer.frameCount) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0 && !thread->standby()) {
- if (mBufferQueue.size() < kMaxOverFlowBuffers) {
- pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channelCount];
- pInBuffer->frameCount = inBuffer.frameCount;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channelCount * sizeof(int16_t));
- mBufferQueue.add(pInBuffer);
- ALOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this, mThread.unsafe_get(), mBufferQueue.size());
- } else {
- ALOGW("OutputTrack::write() %p thread %p no more overflow buffers", mThread.unsafe_get(), this);
- }
- }
- }
-
- // Calling write() with a 0 length buffer, means that no more data will be written:
- // If no more buffers are pending, fill output track buffer to make sure it is started
- // by output mixer.
- if (frames == 0 && mBufferQueue.size() == 0) {
- if (mCblk->user < mCblk->frameCount) {
- frames = mCblk->frameCount - mCblk->user;
- pInBuffer = new Buffer;
- pInBuffer->mBuffer = new int16_t[frames * channelCount];
- pInBuffer->frameCount = frames;
- pInBuffer->i16 = pInBuffer->mBuffer;
- memset(pInBuffer->raw, 0, frames * channelCount * sizeof(int16_t));
- mBufferQueue.add(pInBuffer);
- } else if (mActive) {
- stop();
- }
- }
-
- return outputBufferFull;
-}
-
-status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
-{
- int active;
- status_t result;
- audio_track_cblk_t* cblk = mCblk;
- uint32_t framesReq = buffer->frameCount;
-
-// ALOGV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
- buffer->frameCount = 0;
-
- uint32_t framesAvail = cblk->framesAvailable();
-
-
- if (framesAvail == 0) {
- Mutex::Autolock _l(cblk->lock);
- goto start_loop_here;
- while (framesAvail == 0) {
- active = mActive;
- if (CC_UNLIKELY(!active)) {
- ALOGV("Not active and NO_MORE_BUFFERS");
- return NO_MORE_BUFFERS;
- }
- result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
- if (result != NO_ERROR) {
- return NO_MORE_BUFFERS;
- }
- // read the server count again
- start_loop_here:
- framesAvail = cblk->framesAvailable_l();
- }
- }
-
-// if (framesAvail < framesReq) {
-// return NO_MORE_BUFFERS;
-// }
-
- if (framesReq > framesAvail) {
- framesReq = framesAvail;
- }
-
- uint32_t u = cblk->user;
- uint32_t bufferEnd = cblk->userBase + cblk->frameCount;
-
- if (framesReq > bufferEnd - u) {
- framesReq = bufferEnd - u;
- }
-
- buffer->frameCount = framesReq;
- buffer->raw = (void *)cblk->buffer(u);
- return NO_ERROR;
-}
-
-
-void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
-{
- size_t size = mBufferQueue.size();
-
- for (size_t i = 0; i < size; i++) {
- Buffer *pBuffer = mBufferQueue.itemAt(i);
- delete [] pBuffer->mBuffer;
- delete pBuffer;
- }
- mBufferQueue.clear();
-}
// ----------------------------------------------------------------------------
@@ -5790,99 +1216,20 @@ void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who)
mAudioFlinger->removeNotificationClient(mPid);
}
-// ----------------------------------------------------------------------------
-
-AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track)
- : BnAudioTrack(),
- mTrack(track)
-{
-}
-
-AudioFlinger::TrackHandle::~TrackHandle() {
- // just stop the track on deletion, associated resources
- // will be freed from the main thread once all pending buffers have
- // been played. Unless it's not in the active track list, in which
- // case we free everything now...
- mTrack->destroy();
-}
-
-sp<IMemory> AudioFlinger::TrackHandle::getCblk() const {
- return mTrack->getCblk();
-}
-
-status_t AudioFlinger::TrackHandle::start() {
- return mTrack->start();
-}
-
-void AudioFlinger::TrackHandle::stop() {
- mTrack->stop();
-}
-
-void AudioFlinger::TrackHandle::flush() {
- mTrack->flush();
-}
-
-void AudioFlinger::TrackHandle::mute(bool e) {
- mTrack->mute(e);
-}
-
-void AudioFlinger::TrackHandle::pause() {
- mTrack->pause();
-}
-
-status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId)
-{
- return mTrack->attachAuxEffect(EffectId);
-}
-
-status_t AudioFlinger::TrackHandle::allocateTimedBuffer(size_t size,
- sp<IMemory>* buffer) {
- if (!mTrack->isTimedTrack())
- return INVALID_OPERATION;
-
- PlaybackThread::TimedTrack* tt =
- reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
- return tt->allocateTimedBuffer(size, buffer);
-}
-status_t AudioFlinger::TrackHandle::queueTimedBuffer(const sp<IMemory>& buffer,
- int64_t pts) {
- if (!mTrack->isTimedTrack())
- return INVALID_OPERATION;
-
- PlaybackThread::TimedTrack* tt =
- reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
- return tt->queueTimedBuffer(buffer, pts);
-}
-
-status_t AudioFlinger::TrackHandle::setMediaTimeTransform(
- const LinearTransform& xform, int target) {
-
- if (!mTrack->isTimedTrack())
- return INVALID_OPERATION;
-
- PlaybackThread::TimedTrack* tt =
- reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
- return tt->setMediaTimeTransform(
- xform, static_cast<TimedAudioTrack::TargetTimeline>(target));
-}
+// ----------------------------------------------------------------------------
-status_t AudioFlinger::TrackHandle::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- return BnAudioTrack::onTransact(code, data, reply, flags);
+static bool deviceRequiresCaptureAudioOutputPermission(audio_devices_t inDevice) {
+ return audio_is_remote_submix_device(inDevice);
}
-// ----------------------------------------------------------------------------
-
sp<IAudioRecord> AudioFlinger::openRecord(
- pid_t pid,
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount,
- IAudioFlinger::track_flags_t flags,
+ size_t frameCount,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
status_t *status)
@@ -5897,19 +1244,35 @@ sp<IAudioRecord> AudioFlinger::openRecord(
// check calling permissions
if (!recordingAllowed()) {
+ ALOGE("openRecord() permission denied: recording not allowed");
lStatus = PERMISSION_DENIED;
goto Exit;
}
+ if (format != AUDIO_FORMAT_PCM_16_BIT) {
+ ALOGE("openRecord() invalid format %d", format);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
// add client to list
{ // scope for mLock
Mutex::Autolock _l(mLock);
thread = checkRecordThread_l(input);
if (thread == NULL) {
+ ALOGE("openRecord() checkRecordThread_l failed");
lStatus = BAD_VALUE;
goto Exit;
}
+ if (deviceRequiresCaptureAudioOutputPermission(thread->inDevice())
+ && !captureAudioOutputAllowed()) {
+ ALOGE("openRecord() permission denied: capture not allowed");
+ lStatus = PERMISSION_DENIED;
+ goto Exit;
+ }
+
+ pid_t pid = IPCThreadState::self()->getCallingPid();
client = registerPid_l(pid);
// If no audio session id is provided, create one here
@@ -5921,13 +1284,18 @@ sp<IAudioRecord> AudioFlinger::openRecord(
*sessionId = lSessionId;
}
}
- // create new record track. The record track uses one track in mHardwareMixerThread by convention.
+ // create new record track.
+ // The record track uses one track in mHardwareMixerThread by convention.
+ // TODO: the uid should be passed in as a parameter to openRecord
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
- frameCount, lSessionId, flags, tid, &lStatus);
+ frameCount, lSessionId,
+ IPCThreadState::self()->getCallingUid(),
+ flags, tid, &lStatus);
+ LOG_ALWAYS_FATAL_IF((recordTrack != 0) != (lStatus == NO_ERROR));
}
if (lStatus != NO_ERROR) {
- // remove local strong reference to Client before deleting the RecordTrack so that the Client
- // destructor is called by the TrackBase destructor with mLock held
+ // remove local strong reference to Client before deleting the RecordTrack so that the
+ // Client destructor is called by the TrackBase destructor with mLock held
client.clear();
recordTrack.clear();
goto Exit;
@@ -5944,891 +1312,6 @@ Exit:
return recordHandle;
}
-// ----------------------------------------------------------------------------
-
-AudioFlinger::RecordHandle::RecordHandle(const sp<AudioFlinger::RecordThread::RecordTrack>& recordTrack)
- : BnAudioRecord(),
- mRecordTrack(recordTrack)
-{
-}
-
-AudioFlinger::RecordHandle::~RecordHandle() {
- stop_nonvirtual();
- mRecordTrack->destroy();
-}
-
-sp<IMemory> AudioFlinger::RecordHandle::getCblk() const {
- return mRecordTrack->getCblk();
-}
-
-status_t AudioFlinger::RecordHandle::start(int /*AudioSystem::sync_event_t*/ event, int triggerSession) {
- ALOGV("RecordHandle::start()");
- return mRecordTrack->start((AudioSystem::sync_event_t)event, triggerSession);
-}
-
-void AudioFlinger::RecordHandle::stop() {
- stop_nonvirtual();
-}
-
-void AudioFlinger::RecordHandle::stop_nonvirtual() {
- ALOGV("RecordHandle::stop()");
- mRecordTrack->stop();
-}
-
-status_t AudioFlinger::RecordHandle::onTransact(
- uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
-{
- return BnAudioRecord::onTransact(code, data, reply, flags);
-}
-
-// ----------------------------------------------------------------------------
-
-AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamIn *input,
- uint32_t sampleRate,
- audio_channel_mask_t channelMask,
- audio_io_handle_t id,
- audio_devices_t device) :
- ThreadBase(audioFlinger, id, AUDIO_DEVICE_NONE, device, RECORD),
- mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
- // mRsmpInIndex and mInputBytes set by readInputParameters()
- mReqChannelCount(popcount(channelMask)),
- mReqSampleRate(sampleRate)
- // mBytesRead is only meaningful while active, and so is cleared in start()
- // (but might be better to also clear here for dump?)
-{
- snprintf(mName, kNameLength, "AudioIn_%X", id);
-
- readInputParameters();
-}
-
-
-AudioFlinger::RecordThread::~RecordThread()
-{
- delete[] mRsmpInBuffer;
- delete mResampler;
- delete[] mRsmpOutBuffer;
-}
-
-void AudioFlinger::RecordThread::onFirstRef()
-{
- run(mName, PRIORITY_URGENT_AUDIO);
-}
-
-status_t AudioFlinger::RecordThread::readyToRun()
-{
- status_t status = initCheck();
- ALOGW_IF(status != NO_ERROR,"RecordThread %p could not initialize", this);
- return status;
-}
-
-bool AudioFlinger::RecordThread::threadLoop()
-{
- AudioBufferProvider::Buffer buffer;
- sp<RecordTrack> activeTrack;
- Vector< sp<EffectChain> > effectChains;
-
- nsecs_t lastWarning = 0;
-
- inputStandBy();
- acquireWakeLock();
-
- // used to verify we've read at least once before evaluating how many bytes were read
- bool readOnce = false;
-
- // start recording
- while (!exitPending()) {
-
- processConfigEvents();
-
- { // scope for mLock
- Mutex::Autolock _l(mLock);
- checkForNewParameters_l();
- if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
- standby();
-
- if (exitPending()) break;
-
- releaseWakeLock_l();
- ALOGV("RecordThread: loop stopping");
- // go to sleep
- mWaitWorkCV.wait(mLock);
- ALOGV("RecordThread: loop starting");
- acquireWakeLock_l();
- continue;
- }
- if (mActiveTrack != 0) {
- if (mActiveTrack->mState == TrackBase::PAUSING) {
- standby();
- mActiveTrack.clear();
- mStartStopCond.broadcast();
- } else if (mActiveTrack->mState == TrackBase::RESUMING) {
- if (mReqChannelCount != mActiveTrack->channelCount()) {
- mActiveTrack.clear();
- mStartStopCond.broadcast();
- } else if (readOnce) {
- // record start succeeds only if first read from audio input
- // succeeds
- if (mBytesRead >= 0) {
- mActiveTrack->mState = TrackBase::ACTIVE;
- } else {
- mActiveTrack.clear();
- }
- mStartStopCond.broadcast();
- }
- mStandby = false;
- } else if (mActiveTrack->mState == TrackBase::TERMINATED) {
- removeTrack_l(mActiveTrack);
- mActiveTrack.clear();
- }
- }
- lockEffectChains_l(effectChains);
- }
-
- if (mActiveTrack != 0) {
- if (mActiveTrack->mState != TrackBase::ACTIVE &&
- mActiveTrack->mState != TrackBase::RESUMING) {
- unlockEffectChains(effectChains);
- usleep(kRecordThreadSleepUs);
- continue;
- }
- for (size_t i = 0; i < effectChains.size(); i ++) {
- effectChains[i]->process_l();
- }
-
- buffer.frameCount = mFrameCount;
- if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
- readOnce = true;
- size_t framesOut = buffer.frameCount;
- if (mResampler == NULL) {
- // no resampling
- while (framesOut) {
- size_t framesIn = mFrameCount - mRsmpInIndex;
- if (framesIn) {
- int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize;
- int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) * mActiveTrack->mCblk->frameSize;
- if (framesIn > framesOut)
- framesIn = framesOut;
- mRsmpInIndex += framesIn;
- framesOut -= framesIn;
- if ((int)mChannelCount == mReqChannelCount ||
- mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- memcpy(dst, src, framesIn * mFrameSize);
- } else {
- if (mChannelCount == 1) {
- upmix_to_stereo_i16_from_mono_i16((int16_t *)dst,
- (int16_t *)src, framesIn);
- } else {
- downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
- (int16_t *)src, framesIn);
- }
- }
- }
- if (framesOut && mFrameCount == mRsmpInIndex) {
- if (framesOut == mFrameCount &&
- ((int)mChannelCount == mReqChannelCount || mFormat != AUDIO_FORMAT_PCM_16_BIT)) {
- mBytesRead = mInput->stream->read(mInput->stream, buffer.raw, mInputBytes);
- framesOut = 0;
- } else {
- mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
- mRsmpInIndex = 0;
- }
- if (mBytesRead <= 0) {
- if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE))
- {
- ALOGE("Error reading audio input");
- // Force input into standby so that it tries to
- // recover at next read attempt
- inputStandBy();
- usleep(kRecordThreadSleepUs);
- }
- mRsmpInIndex = mFrameCount;
- framesOut = 0;
- buffer.frameCount = 0;
- }
- }
- }
- } else {
- // resampling
-
- memset(mRsmpOutBuffer, 0, framesOut * 2 * sizeof(int32_t));
- // alter output frame count as if we were expecting stereo samples
- if (mChannelCount == 1 && mReqChannelCount == 1) {
- framesOut >>= 1;
- }
- mResampler->resample(mRsmpOutBuffer, framesOut, this /* AudioBufferProvider* */);
- // ditherAndClamp() works as long as all buffers returned by mActiveTrack->getNextBuffer()
- // are 32 bit aligned which should be always true.
- if (mChannelCount == 2 && mReqChannelCount == 1) {
- ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
- // the resampler always outputs stereo samples: do post stereo to mono conversion
- downmix_to_mono_i16_from_stereo_i16(buffer.i16, (int16_t *)mRsmpOutBuffer,
- framesOut);
- } else {
- ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
- }
-
- }
- if (mFramestoDrop == 0) {
- mActiveTrack->releaseBuffer(&buffer);
- } else {
- if (mFramestoDrop > 0) {
- mFramestoDrop -= buffer.frameCount;
- if (mFramestoDrop <= 0) {
- clearSyncStartEvent();
- }
- } else {
- mFramestoDrop += buffer.frameCount;
- if (mFramestoDrop >= 0 || mSyncStartEvent == 0 ||
- mSyncStartEvent->isCancelled()) {
- ALOGW("Synced record %s, session %d, trigger session %d",
- (mFramestoDrop >= 0) ? "timed out" : "cancelled",
- mActiveTrack->sessionId(),
- (mSyncStartEvent != 0) ? mSyncStartEvent->triggerSession() : 0);
- clearSyncStartEvent();
- }
- }
- }
- mActiveTrack->clearOverflow();
- }
- // client isn't retrieving buffers fast enough
- else {
- if (!mActiveTrack->setOverflow()) {
- nsecs_t now = systemTime();
- if ((now - lastWarning) > kWarningThrottleNs) {
- ALOGW("RecordThread: buffer overflow");
- lastWarning = now;
- }
- }
- // Release the processor for a while before asking for a new buffer.
- // This will give the application more chance to read from the buffer and
- // clear the overflow.
- usleep(kRecordThreadSleepUs);
- }
- }
- // enable changes in effect chain
- unlockEffectChains(effectChains);
- effectChains.clear();
- }
-
- standby();
-
- {
- Mutex::Autolock _l(mLock);
- mActiveTrack.clear();
- mStartStopCond.broadcast();
- }
-
- releaseWakeLock();
-
- ALOGV("RecordThread %p exiting", this);
- return false;
-}
-
-void AudioFlinger::RecordThread::standby()
-{
- if (!mStandby) {
- inputStandBy();
- mStandby = true;
- }
-}
-
-void AudioFlinger::RecordThread::inputStandBy()
-{
- mInput->stream->common.standby(&mInput->stream->common);
-}
-
-sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l(
- const sp<AudioFlinger::Client>& client,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- int sessionId,
- IAudioFlinger::track_flags_t flags,
- pid_t tid,
- status_t *status)
-{
- sp<RecordTrack> track;
- status_t lStatus;
-
- lStatus = initCheck();
- if (lStatus != NO_ERROR) {
- ALOGE("Audio driver not initialized.");
- goto Exit;
- }
-
- // FIXME use flags and tid similar to createTrack_l()
-
- { // scope for mLock
- Mutex::Autolock _l(mLock);
-
- track = new RecordTrack(this, client, sampleRate,
- format, channelMask, frameCount, sessionId);
-
- if (track->getCblk() == 0) {
- lStatus = NO_MEMORY;
- goto Exit;
- }
- mTracks.add(track);
-
- // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
- setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
- }
- lStatus = NO_ERROR;
-
-Exit:
- if (status) {
- *status = lStatus;
- }
- return track;
-}
-
-status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack,
- AudioSystem::sync_event_t event,
- int triggerSession)
-{
- ALOGV("RecordThread::start event %d, triggerSession %d", event, triggerSession);
- sp<ThreadBase> strongMe = this;
- status_t status = NO_ERROR;
-
- if (event == AudioSystem::SYNC_EVENT_NONE) {
- clearSyncStartEvent();
- } else if (event != AudioSystem::SYNC_EVENT_SAME) {
- mSyncStartEvent = mAudioFlinger->createSyncEvent(event,
- triggerSession,
- recordTrack->sessionId(),
- syncStartEventCallback,
- this);
- // Sync event can be cancelled by the trigger session if the track is not in a
- // compatible state in which case we start record immediately
- if (mSyncStartEvent->isCancelled()) {
- clearSyncStartEvent();
- } else {
- // do not wait for the event for more than AudioSystem::kSyncRecordStartTimeOutMs
- mFramestoDrop = - ((AudioSystem::kSyncRecordStartTimeOutMs * mReqSampleRate) / 1000);
- }
- }
-
- {
- AutoMutex lock(mLock);
- if (mActiveTrack != 0) {
- if (recordTrack != mActiveTrack.get()) {
- status = -EBUSY;
- } else if (mActiveTrack->mState == TrackBase::PAUSING) {
- mActiveTrack->mState = TrackBase::ACTIVE;
- }
- return status;
- }
-
- recordTrack->mState = TrackBase::IDLE;
- mActiveTrack = recordTrack;
- mLock.unlock();
- status_t status = AudioSystem::startInput(mId);
- mLock.lock();
- if (status != NO_ERROR) {
- mActiveTrack.clear();
- clearSyncStartEvent();
- return status;
- }
- mRsmpInIndex = mFrameCount;
- mBytesRead = 0;
- if (mResampler != NULL) {
- mResampler->reset();
- }
- mActiveTrack->mState = TrackBase::RESUMING;
- // signal thread to start
- ALOGV("Signal record thread");
- mWaitWorkCV.broadcast();
- // do not wait for mStartStopCond if exiting
- if (exitPending()) {
- mActiveTrack.clear();
- status = INVALID_OPERATION;
- goto startError;
- }
- mStartStopCond.wait(mLock);
- if (mActiveTrack == 0) {
- ALOGV("Record failed to start");
- status = BAD_VALUE;
- goto startError;
- }
- ALOGV("Record started OK");
- return status;
- }
-startError:
- AudioSystem::stopInput(mId);
- clearSyncStartEvent();
- return status;
-}
-
-void AudioFlinger::RecordThread::clearSyncStartEvent()
-{
- if (mSyncStartEvent != 0) {
- mSyncStartEvent->cancel();
- }
- mSyncStartEvent.clear();
- mFramestoDrop = 0;
-}
-
-void AudioFlinger::RecordThread::syncStartEventCallback(const wp<SyncEvent>& event)
-{
- sp<SyncEvent> strongEvent = event.promote();
-
- if (strongEvent != 0) {
- RecordThread *me = (RecordThread *)strongEvent->cookie();
- me->handleSyncStartEvent(strongEvent);
- }
-}
-
-void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event)
-{
- if (event == mSyncStartEvent) {
- // TODO: use actual buffer filling status instead of 2 buffers when info is available
- // from audio HAL
- mFramestoDrop = mFrameCount * 2;
- }
-}
-
-bool AudioFlinger::RecordThread::stop_l(RecordThread::RecordTrack* recordTrack) {
- ALOGV("RecordThread::stop");
- if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) {
- return false;
- }
- recordTrack->mState = TrackBase::PAUSING;
- // do not wait for mStartStopCond if exiting
- if (exitPending()) {
- return true;
- }
- mStartStopCond.wait(mLock);
- // if we have been restarted, recordTrack == mActiveTrack.get() here
- if (exitPending() || recordTrack != mActiveTrack.get()) {
- ALOGV("Record stopped OK");
- return true;
- }
- return false;
-}
-
-bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event) const
-{
- return false;
-}
-
-status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event)
-{
-#if 0 // This branch is currently dead code, but is preserved in case it will be needed in future
- if (!isValidSyncEvent(event)) {
- return BAD_VALUE;
- }
-
- int eventSession = event->triggerSession();
- status_t ret = NAME_NOT_FOUND;
-
- Mutex::Autolock _l(mLock);
-
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<RecordTrack> track = mTracks[i];
- if (eventSession == track->sessionId()) {
- (void) track->setSyncEvent(event);
- ret = NO_ERROR;
- }
- }
- return ret;
-#else
- return BAD_VALUE;
-#endif
-}
-
-void AudioFlinger::RecordThread::RecordTrack::destroy()
-{
- // see comments at AudioFlinger::PlaybackThread::Track::destroy()
- sp<RecordTrack> keep(this);
- {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- if (mState == ACTIVE || mState == RESUMING) {
- AudioSystem::stopInput(thread->id());
- }
- AudioSystem::releaseInput(thread->id());
- Mutex::Autolock _l(thread->mLock);
- RecordThread *recordThread = (RecordThread *) thread.get();
- recordThread->destroyTrack_l(this);
- }
- }
-}
-
-// destroyTrack_l() must be called with ThreadBase::mLock held
-void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
-{
- track->mState = TrackBase::TERMINATED;
- // active tracks are removed by threadLoop()
- if (mActiveTrack != track) {
- removeTrack_l(track);
- }
-}
-
-void AudioFlinger::RecordThread::removeTrack_l(const sp<RecordTrack>& track)
-{
- mTracks.remove(track);
- // need anything related to effects here?
-}
-
-void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
-{
- dumpInternals(fd, args);
- dumpTracks(fd, args);
- dumpEffectChains(fd, args);
-}
-
-void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, "\nInput thread %p internals\n", this);
- result.append(buffer);
-
- if (mActiveTrack != 0) {
- snprintf(buffer, SIZE, "In index: %d\n", mRsmpInIndex);
- result.append(buffer);
- snprintf(buffer, SIZE, "In size: %d\n", mInputBytes);
- result.append(buffer);
- snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != NULL));
- result.append(buffer);
- snprintf(buffer, SIZE, "Out channel count: %d\n", mReqChannelCount);
- result.append(buffer);
- snprintf(buffer, SIZE, "Out sample rate: %d\n", mReqSampleRate);
- result.append(buffer);
- } else {
- result.append("No active record client\n");
- }
-
- write(fd, result.string(), result.size());
-
- dumpBase(fd, args);
-}
-
-void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args)
-{
- const size_t SIZE = 256;
- char buffer[SIZE];
- String8 result;
-
- snprintf(buffer, SIZE, "Input thread %p tracks\n", this);
- result.append(buffer);
- RecordTrack::appendDumpHeader(result);
- for (size_t i = 0; i < mTracks.size(); ++i) {
- sp<RecordTrack> track = mTracks[i];
- if (track != 0) {
- track->dump(buffer, SIZE);
- result.append(buffer);
- }
- }
-
- if (mActiveTrack != 0) {
- snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this);
- result.append(buffer);
- RecordTrack::appendDumpHeader(result);
- mActiveTrack->dump(buffer, SIZE);
- result.append(buffer);
-
- }
- write(fd, result.string(), result.size());
-}
-
-// AudioBufferProvider interface
-status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts)
-{
- size_t framesReq = buffer->frameCount;
- size_t framesReady = mFrameCount - mRsmpInIndex;
- int channelCount;
-
- if (framesReady == 0) {
- mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mInputBytes);
- if (mBytesRead <= 0) {
- if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) {
- ALOGE("RecordThread::getNextBuffer() Error reading audio input");
- // Force input into standby so that it tries to
- // recover at next read attempt
- inputStandBy();
- usleep(kRecordThreadSleepUs);
- }
- buffer->raw = NULL;
- buffer->frameCount = 0;
- return NOT_ENOUGH_DATA;
- }
- mRsmpInIndex = 0;
- framesReady = mFrameCount;
- }
-
- if (framesReq > framesReady) {
- framesReq = framesReady;
- }
-
- if (mChannelCount == 1 && mReqChannelCount == 2) {
- channelCount = 1;
- } else {
- channelCount = 2;
- }
- buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount;
- buffer->frameCount = framesReq;
- return NO_ERROR;
-}
-
-// AudioBufferProvider interface
-void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer)
-{
- mRsmpInIndex += buffer->frameCount;
- buffer->frameCount = 0;
-}
-
-bool AudioFlinger::RecordThread::checkForNewParameters_l()
-{
- bool reconfig = false;
-
- while (!mNewParameters.isEmpty()) {
- status_t status = NO_ERROR;
- String8 keyValuePair = mNewParameters[0];
- AudioParameter param = AudioParameter(keyValuePair);
- int value;
- audio_format_t reqFormat = mFormat;
- int reqSamplingRate = mReqSampleRate;
- int reqChannelCount = mReqChannelCount;
-
- if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
- reqSamplingRate = value;
- reconfig = true;
- }
- if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
- reqFormat = (audio_format_t) value;
- reconfig = true;
- }
- if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- reqChannelCount = popcount(value);
- reconfig = true;
- }
- if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
- // do not accept frame count changes if tracks are open as the track buffer
- // size depends on frame count and correct behavior would not be guaranteed
- // if frame count is changed after track creation
- if (mActiveTrack != 0) {
- status = INVALID_OPERATION;
- } else {
- 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.
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setDevice_l(value);
- }
-
- // store input device and output device but do not forward output device to audio HAL.
- // Note that status is ignored by the caller for output device
- // (see AudioFlinger::setParameters()
- if (audio_is_output_devices(value)) {
- mOutDevice = value;
- status = BAD_VALUE;
- } else {
- mInDevice = value;
- // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
- if (mTracks.size() > 0) {
- bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
- mAudioFlinger->btNrecIsOff();
- for (size_t i = 0; i < mTracks.size(); i++) {
- sp<RecordTrack> track = mTracks[i];
- setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
- setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
- }
- }
- }
- }
- if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR &&
- mAudioSource != (audio_source_t)value) {
- // forward device change to effects that have requested to be
- // aware of attached audio device.
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->setAudioSource_l((audio_source_t)value);
- }
- mAudioSource = (audio_source_t)value;
- }
- if (status == NO_ERROR) {
- status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string());
- if (status == INVALID_OPERATION) {
- inputStandBy();
- status = mInput->stream->common.set_parameters(&mInput->stream->common,
- keyValuePair.string());
- }
- if (reconfig) {
- if (status == BAD_VALUE &&
- reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
- reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
- ((int)mInput->stream->common.get_sample_rate(&mInput->stream->common) <= (2 * reqSamplingRate)) &&
- popcount(mInput->stream->common.get_channels(&mInput->stream->common)) <= FCC_2 &&
- (reqChannelCount <= FCC_2)) {
- status = NO_ERROR;
- }
- if (status == NO_ERROR) {
- readInputParameters();
- sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
- }
- }
- }
-
- mNewParameters.removeAt(0);
-
- mParamStatus = status;
- mParamCond.signal();
- // wait for condition with time out in case the thread calling ThreadBase::setParameters()
- // already timed out waiting for the status and will never signal the condition.
- mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
- }
- return reconfig;
-}
-
-String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
-{
- char *s;
- String8 out_s8 = String8();
-
- Mutex::Autolock _l(mLock);
- if (initCheck() != NO_ERROR) {
- return out_s8;
- }
-
- s = mInput->stream->common.get_parameters(&mInput->stream->common, keys.string());
- out_s8 = String8(s);
- free(s);
- return out_s8;
-}
-
-void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
- AudioSystem::OutputDescriptor desc;
- void *param2 = NULL;
-
- switch (event) {
- case AudioSystem::INPUT_OPENED:
- case AudioSystem::INPUT_CONFIG_CHANGED:
- desc.channels = mChannelMask;
- desc.samplingRate = mSampleRate;
- desc.format = mFormat;
- desc.frameCount = mFrameCount;
- desc.latency = 0;
- param2 = &desc;
- break;
-
- case AudioSystem::INPUT_CLOSED:
- default:
- break;
- }
- mAudioFlinger->audioConfigChanged_l(event, mId, param2);
-}
-
-void AudioFlinger::RecordThread::readInputParameters()
-{
- delete mRsmpInBuffer;
- // mRsmpInBuffer is always assigned a new[] below
- delete mRsmpOutBuffer;
- mRsmpOutBuffer = NULL;
- delete mResampler;
- mResampler = NULL;
-
- mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
- mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
- mChannelCount = (uint16_t)popcount(mChannelMask);
- mFormat = mInput->stream->common.get_format(&mInput->stream->common);
- 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)
- {
- int channelCount;
- // optimization: if mono to mono, use the resampler in stereo to stereo mode to avoid
- // stereo to mono post process as the resampler always outputs stereo.
- if (mChannelCount == 1 && mReqChannelCount == 2) {
- channelCount = 1;
- } else {
- channelCount = 2;
- }
- mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
- mResampler->setSampleRate(mSampleRate);
- mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
- mRsmpOutBuffer = new int32_t[mFrameCount * 2];
-
- // optmization: if mono to mono, alter input frame count as if we were inputing stereo samples
- if (mChannelCount == 1 && mReqChannelCount == 1) {
- mFrameCount >>= 1;
- }
-
- }
- mRsmpInIndex = mFrameCount;
-}
-
-unsigned int AudioFlinger::RecordThread::getInputFramesLost()
-{
- Mutex::Autolock _l(mLock);
- if (initCheck() != NO_ERROR) {
- return 0;
- }
-
- return mInput->stream->get_input_frames_lost(mInput->stream);
-}
-
-uint32_t AudioFlinger::RecordThread::hasAudioSession(int sessionId) const
-{
- Mutex::Autolock _l(mLock);
- uint32_t result = 0;
- if (getEffectChain_l(sessionId) != 0) {
- result = EFFECT_SESSION;
- }
-
- for (size_t i = 0; i < mTracks.size(); ++i) {
- if (sessionId == mTracks[i]->sessionId()) {
- result |= TRACK_SESSION;
- break;
- }
- }
-
- return result;
-}
-
-KeyedVector<int, bool> AudioFlinger::RecordThread::sessionIds() const
-{
- KeyedVector<int, bool> ids;
- Mutex::Autolock _l(mLock);
- for (size_t j = 0; j < mTracks.size(); ++j) {
- sp<RecordThread::RecordTrack> track = mTracks[j];
- int sessionId = track->sessionId();
- if (ids.indexOfKey(sessionId) < 0) {
- ids.add(sessionId, true);
- }
- }
- return ids;
-}
-
-AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::clearInput()
-{
- Mutex::Autolock _l(mLock);
- AudioStreamIn *input = mInput;
- mInput = NULL;
- return input;
-}
-
-// this method must always be called either with ThreadBase mLock held or inside the thread loop
-audio_stream_t* AudioFlinger::RecordThread::stream() const
-{
- if (mInput == NULL) {
- return NULL;
- }
- return &mInput->stream->common;
-}
// ----------------------------------------------------------------------------
@@ -6924,14 +1407,14 @@ audio_module_handle_t AudioFlinger::loadHwModule_l(const char *name)
// ----------------------------------------------------------------------------
-int32_t AudioFlinger::getPrimaryOutputSamplingRate()
+uint32_t AudioFlinger::getPrimaryOutputSamplingRate()
{
Mutex::Autolock _l(mLock);
PlaybackThread *thread = primaryPlaybackThread_l();
return thread != NULL ? thread->sampleRate() : 0;
}
-int32_t AudioFlinger::getPrimaryOutputFrameCount()
+size_t AudioFlinger::getPrimaryOutputFrameCount()
{
Mutex::Autolock _l(mLock);
PlaybackThread *thread = primaryPlaybackThread_l();
@@ -6940,31 +1423,53 @@ int32_t AudioFlinger::getPrimaryOutputFrameCount()
// ----------------------------------------------------------------------------
+status_t AudioFlinger::setLowRamDevice(bool isLowRamDevice)
+{
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+ if (uid != AID_SYSTEM) {
+ return PERMISSION_DENIED;
+ }
+ Mutex::Autolock _l(mLock);
+ if (mIsDeviceTypeKnown) {
+ return INVALID_OPERATION;
+ }
+ mIsLowRamDevice = isLowRamDevice;
+ mIsDeviceTypeKnown = true;
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+
audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
audio_devices_t *pDevices,
uint32_t *pSamplingRate,
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
- status_t status;
PlaybackThread *thread = NULL;
- struct audio_config config = {
- sample_rate: pSamplingRate ? *pSamplingRate : 0,
- channel_mask: pChannelMask ? *pChannelMask : 0,
- format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
- };
+ struct audio_config config;
+ config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0;
+ config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0;
+ config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT;
+ if (offloadInfo) {
+ config.offload_info = *offloadInfo;
+ }
+
audio_stream_out_t *outStream = NULL;
AudioHwDevice *outHwDev;
- ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %d, Channels %x, flags %x",
+ ALOGV("openOutput(), module %d Device %x, SamplingRate %d, Format %#08x, Channels %x, flags %x",
module,
(pDevices != NULL) ? *pDevices : 0,
config.sample_rate,
config.format,
config.channel_mask,
flags);
+ ALOGV("openOutput(), offloadInfo %p version 0x%04x",
+ offloadInfo, offloadInfo == NULL ? -1 : offloadInfo->version );
if (pDevices == NULL || *pDevices == 0) {
return 0;
@@ -6981,7 +1486,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
mHardwareStatus = AUDIO_HW_OUTPUT_OPEN;
- status = hwDevHal->open_output_stream(hwDevHal,
+ status_t status = hwDevHal->open_output_stream(hwDevHal,
id,
*pDevices,
(audio_output_flags_t)flags,
@@ -6989,7 +1494,8 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
&outStream);
mHardwareStatus = AUDIO_HW_IDLE;
- ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %d, Channels %x, status %d",
+ ALOGV("openOutput() openOutputStream returned output %p, SamplingRate %d, Format %#08x, "
+ "Channels %x, status %d",
outStream,
config.sample_rate,
config.format,
@@ -6997,9 +1503,12 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
status);
if (status == NO_ERROR && outStream != NULL) {
- AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream);
+ AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags);
- if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
+ if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+ thread = new OffloadThread(this, output, id, *pDevices);
+ ALOGV("openOutput() created offload output: ID %d thread %p", id, thread);
+ } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
(config.format != AUDIO_FORMAT_PCM_16_BIT) ||
(config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
thread = new DirectOutputThread(this, output, id, *pDevices);
@@ -7010,10 +1519,18 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,
}
mPlaybackThreads.add(id, thread);
- if (pSamplingRate != NULL) *pSamplingRate = config.sample_rate;
- if (pFormat != NULL) *pFormat = config.format;
- if (pChannelMask != NULL) *pChannelMask = config.channel_mask;
- if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
+ if (pSamplingRate != NULL) {
+ *pSamplingRate = config.sample_rate;
+ }
+ if (pFormat != NULL) {
+ *pFormat = config.format;
+ }
+ if (pChannelMask != NULL) {
+ *pChannelMask = config.channel_mask;
+ }
+ if (pLatencyMs != NULL) {
+ *pLatencyMs = thread->latency();
+ }
// notify client processes of the new output creation
thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
@@ -7042,7 +1559,8 @@ audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1,
MixerThread *thread2 = checkMixerThread_l(output2);
if (thread1 == NULL || thread2 == NULL) {
- ALOGW("openDuplicateOutput() wrong output mixer type for output %d or %d", output1, output2);
+ ALOGW("openDuplicateOutput() wrong output mixer type for output %d or %d", output1,
+ output2);
return 0;
}
@@ -7077,13 +1595,31 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output)
if (thread->type() == ThreadBase::MIXER) {
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
if (mPlaybackThreads.valueAt(i)->type() == ThreadBase::DUPLICATING) {
- DuplicatingThread *dupThread = (DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
+ DuplicatingThread *dupThread =
+ (DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
dupThread->removeOutputTrack((MixerThread *)thread.get());
+
}
}
}
- audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
+
+
mPlaybackThreads.removeItem(output);
+ // save all effects to the default thread
+ if (mPlaybackThreads.size()) {
+ PlaybackThread *dstThread = checkPlaybackThread_l(mPlaybackThreads.keyAt(0));
+ if (dstThread != NULL) {
+ // audioflinger lock is held here so the acquisition order of thread locks does not
+ // matter
+ Mutex::Autolock _dl(dstThread->mLock);
+ Mutex::Autolock _sl(thread->mLock);
+ Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ moveEffectChain_l(effectChains[i]->sessionId(), thread.get(), dstThread, true);
+ }
+ }
+ }
+ audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
}
thread->exit();
// The thread entity (active unit of execution) is no longer running here,
@@ -7138,11 +1674,11 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
{
status_t status;
RecordThread *thread = NULL;
- struct audio_config config = {
- sample_rate: pSamplingRate ? *pSamplingRate : 0,
- channel_mask: pChannelMask ? *pChannelMask : 0,
- format: pFormat ? *pFormat : AUDIO_FORMAT_DEFAULT,
- };
+ struct audio_config config;
+ config.sample_rate = (pSamplingRate != NULL) ? *pSamplingRate : 0;
+ config.channel_mask = (pChannelMask != NULL) ? *pChannelMask : 0;
+ config.format = (pFormat != NULL) ? *pFormat : AUDIO_FORMAT_DEFAULT;
+
uint32_t reqSamplingRate = config.sample_rate;
audio_format_t reqFormat = config.format;
audio_channel_mask_t reqChannels = config.channel_mask;
@@ -7164,16 +1700,17 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
status = inHwHal->open_input_stream(inHwHal, id, *pDevices, &config,
&inStream);
- ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, status %d",
+ ALOGV("openInput() openInputStream returned input %p, SamplingRate %d, Format %d, Channels %x, "
+ "status %d",
inStream,
config.sample_rate,
config.format,
config.channel_mask,
status);
- // If the input could not be opened with the requested parameters and we can handle the conversion internally,
- // try to open again with the proposed parameters. The AudioFlinger can resample the input and do mono to stereo
- // or stereo to mono conversions on 16 bit PCM inputs.
+ // If the input could not be opened with the requested parameters and we can handle the
+ // conversion internally, try to open again with the proposed parameters. The AudioFlinger can
+ // resample the input and do mono to stereo or stereo to mono conversions on 16 bit PCM inputs.
if (status == BAD_VALUE &&
reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
(config.sample_rate <= 2 * reqSamplingRate) &&
@@ -7184,23 +1721,83 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module,
}
if (status == NO_ERROR && inStream != NULL) {
+
+#ifdef TEE_SINK
+ // Try to re-use most recently used Pipe to archive a copy of input for dumpsys,
+ // or (re-)create if current Pipe is idle and does not match the new format
+ sp<NBAIO_Sink> teeSink;
+ enum {
+ TEE_SINK_NO, // don't copy input
+ TEE_SINK_NEW, // copy input using a new pipe
+ TEE_SINK_OLD, // copy input using an existing pipe
+ } kind;
+ NBAIO_Format format = Format_from_SR_C(inStream->common.get_sample_rate(&inStream->common),
+ popcount(inStream->common.get_channels(&inStream->common)));
+ if (!mTeeSinkInputEnabled) {
+ kind = TEE_SINK_NO;
+ } else if (format == Format_Invalid) {
+ kind = TEE_SINK_NO;
+ } else if (mRecordTeeSink == 0) {
+ kind = TEE_SINK_NEW;
+ } else if (mRecordTeeSink->getStrongCount() != 1) {
+ kind = TEE_SINK_NO;
+ } else if (format == mRecordTeeSink->format()) {
+ kind = TEE_SINK_OLD;
+ } else {
+ kind = TEE_SINK_NEW;
+ }
+ switch (kind) {
+ case TEE_SINK_NEW: {
+ Pipe *pipe = new Pipe(mTeeSinkInputFrames, format);
+ size_t numCounterOffers = 0;
+ const NBAIO_Format offers[1] = {format};
+ ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ PipeReader *pipeReader = new PipeReader(*pipe);
+ numCounterOffers = 0;
+ index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mRecordTeeSink = pipe;
+ mRecordTeeSource = pipeReader;
+ teeSink = pipe;
+ }
+ break;
+ case TEE_SINK_OLD:
+ teeSink = mRecordTeeSink;
+ break;
+ case TEE_SINK_NO:
+ default:
+ break;
+ }
+#endif
+
AudioStreamIn *input = new AudioStreamIn(inHwDev, inStream);
// Start record thread
- // RecorThread require both input and output device indication to forward to audio
+ // RecordThread requires both input and output device indication to forward to audio
// pre processing modules
- audio_devices_t device = (*pDevices) | primaryOutputDevice_l();
thread = new RecordThread(this,
input,
reqSamplingRate,
reqChannels,
id,
- device);
+ primaryOutputDevice_l(),
+ *pDevices
+#ifdef TEE_SINK
+ , teeSink
+#endif
+ );
mRecordThreads.add(id, thread);
ALOGV("openInput() created record thread: ID %d thread %p", id, thread);
- if (pSamplingRate != NULL) *pSamplingRate = reqSamplingRate;
- if (pFormat != NULL) *pFormat = config.format;
- if (pChannelMask != NULL) *pChannelMask = reqChannels;
+ if (pSamplingRate != NULL) {
+ *pSamplingRate = reqSamplingRate;
+ }
+ if (pFormat != NULL) {
+ *pFormat = config.format;
+ }
+ if (pChannelMask != NULL) {
+ *pChannelMask = reqChannels;
+ }
// notify client processes of the new input creation
thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED);
@@ -7268,6 +1865,16 @@ void AudioFlinger::acquireAudioSessionId(int audioSession)
Mutex::Autolock _l(mLock);
pid_t caller = IPCThreadState::self()->getCallingPid();
ALOGV("acquiring %d from %d", audioSession, caller);
+
+ // Ignore requests received from processes not known as notification client. The request
+ // is likely proxied by mediaserver (e.g CameraService) and releaseAudioSessionId() can be
+ // called from a different pid leaving a stale session reference. Also we don't know how
+ // to clear this reference if the client process dies.
+ if (mNotificationClients.indexOfKey(caller) < 0) {
+ ALOGV("acquireAudioSessionId() unknown client %d for session %d", caller, audioSession);
+ return;
+ }
+
size_t num = mAudioSessionRefs.size();
for (size_t i = 0; i< num; i++) {
AudioSessionRef *ref = mAudioSessionRefs.editItemAt(i);
@@ -7300,7 +1907,9 @@ void AudioFlinger::releaseAudioSessionId(int audioSession)
return;
}
}
- ALOGW("session id %d not found for pid %d", audioSession, caller);
+ // If the caller is mediaserver it is likely that the session being released was acquired
+ // on behalf of a process not in notification clients and we ignore the warning.
+ ALOGW_IF(caller != getpid_cached, "session id %d not found for pid %d", audioSession, caller);
}
void AudioFlinger::purgeStaleEffects_l() {
@@ -7465,7 +2074,7 @@ status_t AudioFlinger::getEffectDescriptor(const effect_uuid_t *pUuid,
}
-sp<IEffect> AudioFlinger::createEffect(pid_t pid,
+sp<IEffect> AudioFlinger::createEffect(
effect_descriptor_t *pDesc,
const sp<IEffectClient>& effectClient,
int32_t priority,
@@ -7479,6 +2088,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
sp<EffectHandle> handle;
effect_descriptor_t desc;
+ pid_t pid = IPCThreadState::self()->getCallingPid();
ALOGV("createEffect pid %d, effectClient %p, priority %d, sessionId %d, io %d",
pid, effectClient.get(), priority, sessionId, io);
@@ -7500,24 +2110,7 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
goto Exit;
}
- if (io == 0) {
- if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
- // output must be specified by AudioPolicyManager when using session
- // AUDIO_SESSION_OUTPUT_STAGE
- lStatus = BAD_VALUE;
- goto Exit;
- } else if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
- // if the output returned by getOutputForEffect() is removed before we lock the
- // mutex below, the call to checkPlaybackThread_l(io) below will detect it
- // and we will exit safely
- io = AudioSystem::getOutputForEffect(&desc);
- }
- }
-
{
- Mutex::Autolock _l(mLock);
-
-
if (!EffectIsNullUuid(&pDesc->uuid)) {
// if uuid is specified, request effect descriptor
lStatus = EffectGetDescriptor(&pDesc->uuid, &desc);
@@ -7590,6 +2183,15 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
// return effect descriptor
*pDesc = desc;
+ if (io == 0 && sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ // if the output returned by getOutputForEffect() is removed before we lock the
+ // mutex below, the call to checkPlaybackThread_l(io) below will detect it
+ // and we will exit safely
+ io = AudioSystem::getOutputForEffect(&desc);
+ ALOGV("createEffect got output %d", io);
+ }
+
+ Mutex::Autolock _l(mLock);
// If output is not specified try to find a matching audio session ID in one of the
// output threads.
@@ -7597,6 +2199,12 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid,
// because of code checking output when entering the function.
// Note: io is never 0 when creating an effect on an input
if (io == 0) {
+ if (sessionId == AUDIO_SESSION_OUTPUT_STAGE) {
+ // output must be specified by AudioPolicyManager when using session
+ // AUDIO_SESSION_OUTPUT_STAGE
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
// 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) != 0) {
@@ -7670,9 +2278,7 @@ status_t AudioFlinger::moveEffects(int sessionId, audio_io_handle_t srcOutput,
Mutex::Autolock _dl(dstThread->mLock);
Mutex::Autolock _sl(srcThread->mLock);
- moveEffectChain_l(sessionId, srcThread, dstThread, false);
-
- return NO_ERROR;
+ return moveEffectChain_l(sessionId, srcThread, dstThread, false);
}
// moveEffectChain_l must be called with both srcThread and dstThread mLocks held
@@ -7699,13 +2305,18 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId,
// transfer all effects one by one so that new effect chain is created on new thread with
// correct buffer sizes and audio parameters and effect engines reconfigured accordingly
- audio_io_handle_t dstOutput = dstThread->id();
sp<EffectChain> dstChain;
uint32_t strategy = 0; // prevent compiler warning
sp<EffectModule> effect = chain->getEffectFromId_l(0);
+ Vector< sp<EffectModule> > removed;
+ status_t status = NO_ERROR;
while (effect != 0) {
srcThread->removeEffect_l(effect);
- dstThread->addEffect_l(effect);
+ removed.add(effect);
+ status = dstThread->addEffect_l(effect);
+ if (status != NO_ERROR) {
+ break;
+ }
// removeEffect_l() has stopped the effect if it was active so it must be restarted
if (effect->state() == EffectModule::ACTIVE ||
effect->state() == EffectModule::STOPPING) {
@@ -7717,2043 +2328,200 @@ status_t AudioFlinger::moveEffectChain_l(int sessionId,
dstChain = effect->chain().promote();
if (dstChain == 0) {
ALOGW("moveEffectChain_l() cannot get chain from effect %p", effect.get());
- srcThread->addEffect_l(effect);
- return NO_INIT;
+ status = NO_INIT;
+ break;
}
strategy = dstChain->strategy();
}
if (reRegister) {
AudioSystem::unregisterEffect(effect->id());
AudioSystem::registerEffect(&effect->desc(),
- dstOutput,
+ dstThread->id(),
strategy,
sessionId,
effect->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
effect = chain->getEffectFromId_l(0);
}
- return NO_ERROR;
-}
-
-
-// PlaybackThread::createEffect_l() must be called with AudioFlinger::mLock held
-sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::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<EffectChain> chain;
- bool chainCreated = false;
- bool effectCreated = false;
- bool effectRegistered = false;
-
- lStatus = initCheck();
- if (lStatus != NO_ERROR) {
- ALOGW("createEffect_l() Audio driver not initialized.");
- 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 == AUDIO_SESSION_OUTPUT_MIX && mType != MIXER) {
- ALOGW("createEffect_l() Cannot add auxiliary effect %s to session %d",
- desc->name, sessionId);
- lStatus = BAD_VALUE;
- goto Exit;
- }
- // Only Pre processor effects are allowed on input threads and only on input threads
- if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) {
- ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d",
- desc->name, desc->flags, mType);
- lStatus = BAD_VALUE;
- goto Exit;
- }
-
- ALOGV("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
- ALOGV("createEffect_l() new effect chain for session %d", sessionId);
- chain = new EffectChain(this, sessionId);
- addEffectChain_l(chain);
- chain->setStrategy(getStrategyForSession_l(sessionId));
- chainCreated = true;
- } else {
- effect = chain->getEffectFromDesc_l(desc);
- }
-
- ALOGV("createEffect_l() got effect %p on chain %p", effect.get(), chain.get());
-
- if (effect == 0) {
- int id = mAudioFlinger->nextUniqueId();
- // Check CPU and memory usage
- lStatus = AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);
- if (lStatus != NO_ERROR) {
- goto Exit;
- }
- effectRegistered = true;
- // create a new effect module if none present in the chain
- effect = new EffectModule(this, chain, desc, id, sessionId);
- lStatus = effect->status();
- if (lStatus != NO_ERROR) {
- goto Exit;
- }
- lStatus = chain->addEffect_l(effect);
- if (lStatus != NO_ERROR) {
- goto Exit;
- }
- effectCreated = true;
-
- effect->setDevice(mOutDevice);
- effect->setDevice(mInDevice);
- effect->setMode(mAudioFlinger->getMode());
- effect->setAudioSource(mAudioSource);
- }
- // create effect handle and connect it to effect module
- handle = new EffectHandle(effect, client, effectClient, priority);
- lStatus = effect->addHandle(handle.get());
- if (enabled != NULL) {
- *enabled = (int)effect->isEnabled();
- }
- }
-
-Exit:
- if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
- Mutex::Autolock _l(mLock);
- if (effectCreated) {
- chain->removeEffect_l(effect);
- }
- if (effectRegistered) {
- AudioSystem::unregisterEffect(effect->id());
- }
- if (chainCreated) {
- removeEffectChain_l(chain);
- }
- handle.clear();
- }
-
- if (status != NULL) {
- *status = lStatus;
- }
- return handle;
-}
-
-sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect(int sessionId, int effectId)
-{
- Mutex::Autolock _l(mLock);
- return getEffect_l(sessionId, effectId);
-}
-
-sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect_l(int sessionId, int effectId)
-{
- sp<EffectChain> chain = getEffectChain_l(sessionId);
- return chain != 0 ? chain->getEffectFromId_l(effectId) : 0;
-}
-
-// PlaybackThread::addEffect_l() must be called with AudioFlinger::mLock and
-// PlaybackThread::mLock held
-status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect)
-{
- // check for existing effect chain with the requested audio session
- int sessionId = effect->sessionId();
- sp<EffectChain> chain = getEffectChain_l(sessionId);
- bool chainCreated = false;
-
- if (chain == 0) {
- // create a new chain for this session
- ALOGV("addEffect_l() new effect chain for session %d", sessionId);
- chain = new EffectChain(this, sessionId);
- addEffectChain_l(chain);
- chain->setStrategy(getStrategyForSession_l(sessionId));
- chainCreated = true;
- }
- ALOGV("addEffect_l() %p chain %p effect %p", this, chain.get(), effect.get());
-
- if (chain->getEffectFromId_l(effect->id()) != 0) {
- ALOGW("addEffect_l() %p effect %s already present in chain %p",
- this, effect->desc().name, chain.get());
- return BAD_VALUE;
- }
-
- status_t status = chain->addEffect_l(effect);
if (status != NO_ERROR) {
- if (chainCreated) {
- removeEffectChain_l(chain);
- }
- return status;
- }
-
- effect->setDevice(mOutDevice);
- effect->setDevice(mInDevice);
- effect->setMode(mAudioFlinger->getMode());
- effect->setAudioSource(mAudioSource);
- return NO_ERROR;
-}
-
-void AudioFlinger::ThreadBase::removeEffect_l(const sp<EffectModule>& effect) {
-
- ALOGV("removeEffect_l() %p effect %p", this, effect.get());
- effect_descriptor_t desc = effect->desc();
- if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- detachAuxEffect_l(effect->id());
- }
-
- sp<EffectChain> chain = effect->chain().promote();
- if (chain != 0) {
- // remove effect chain if removing last effect
- if (chain->removeEffect_l(effect) == 0) {
- removeEffectChain_l(chain);
- }
- } else {
- ALOGW("removeEffect_l() %p cannot promote chain for effect %p", this, effect.get());
- }
-}
-
-void AudioFlinger::ThreadBase::lockEffectChains_l(
- Vector< sp<AudioFlinger::EffectChain> >& effectChains)
-{
- effectChains = mEffectChains;
- for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->lock();
- }
-}
-
-void AudioFlinger::ThreadBase::unlockEffectChains(
- const Vector< sp<AudioFlinger::EffectChain> >& effectChains)
-{
- for (size_t i = 0; i < effectChains.size(); i++) {
- effectChains[i]->unlock();
- }
-}
-
-sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain(int sessionId)
-{
- Mutex::Autolock _l(mLock);
- return getEffectChain_l(sessionId);
-}
-
-sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain_l(int sessionId) const
-{
- size_t size = mEffectChains.size();
- for (size_t i = 0; i < size; i++) {
- if (mEffectChains[i]->sessionId() == sessionId) {
- return mEffectChains[i];
- }
- }
- return 0;
-}
-
-void AudioFlinger::ThreadBase::setMode(audio_mode_t mode)
-{
- Mutex::Autolock _l(mLock);
- size_t size = mEffectChains.size();
- for (size_t i = 0; i < size; i++) {
- mEffectChains[i]->setMode_l(mode);
- }
-}
-
-void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect,
- EffectHandle *handle,
- bool unpinIfLast) {
-
- Mutex::Autolock _l(mLock);
- ALOGV("disconnectEffect() %p effect %p", this, effect.get());
- // delete the effect module if removing last handle on it
- if (effect->removeHandle(handle) == 0) {
- if (!effect->isPinned() || unpinIfLast) {
- removeEffect_l(effect);
- AudioSystem::unregisterEffect(effect->id());
- }
- }
-}
-
-status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
-{
- int session = chain->sessionId();
- int16_t *buffer = mMixBuffer;
- bool ownsBuffer = false;
-
- ALOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
- if (session > 0) {
- // 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 = 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);
- ownsBuffer = true;
- }
-
- // 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()) {
- ALOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(), buffer);
- track->setMainBuffer(buffer);
- chain->incTrackCnt();
- }
- }
-
- // 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()) {
- ALOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
- chain->incActiveTrackCnt();
- }
- }
- }
-
- chain->setInBuffer(buffer, ownsBuffer);
- chain->setOutBuffer(mMixBuffer);
- // Effect chain for session AUDIO_SESSION_OUTPUT_STAGE is inserted at end of effect
- // chains list in order to be processed last as it contains output stage effects
- // Effect chain for session AUDIO_SESSION_OUTPUT_MIX is inserted before
- // session AUDIO_SESSION_OUTPUT_STAGE to be processed
- // after track specific effects and before output stage
- // It is therefore mandatory that AUDIO_SESSION_OUTPUT_MIX == 0 and
- // that AUDIO_SESSION_OUTPUT_STAGE < AUDIO_SESSION_OUTPUT_MIX
- // Effect chain for other sessions are inserted at beginning of effect
- // chains list to be processed before output mix effects. Relative order between other
- // sessions is not important
- size_t size = mEffectChains.size();
- size_t i = 0;
- for (i = 0; i < size; i++) {
- if (mEffectChains[i]->sessionId() < session) break;
- }
- mEffectChains.insertAt(chain, i);
- checkSuspendOnAddEffectChain_l(chain);
-
- return NO_ERROR;
-}
-
-size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& chain)
-{
- int session = chain->sessionId();
-
- ALOGV("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 active tracks from 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()) {
- ALOGV("removeEffectChain_l(): stopping track on chain %p for session Id: %d",
- chain.get(), session);
- chain->decActiveTrackCnt();
- }
- }
-
- // 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);
- chain->decTrackCnt();
- }
- }
- break;
- }
- }
- return mEffectChains.size();
-}
-
-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 AUDIO_SESSION_OUTPUT_MIX
- sp<EffectModule> effect = getEffect_l(AUDIO_SESSION_OUTPUT_MIX, 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);
- }
- }
-}
-
-status_t AudioFlinger::RecordThread::addEffectChain_l(const sp<EffectChain>& chain)
-{
- // only one chain per input thread
- if (mEffectChains.size() != 0) {
- return INVALID_OPERATION;
- }
- ALOGV("addEffectChain_l() %p on thread %p", chain.get(), this);
-
- chain->setInBuffer(NULL);
- chain->setOutBuffer(NULL);
-
- checkSuspendOnAddEffectChain_l(chain);
-
- mEffectChains.add(chain);
-
- return NO_ERROR;
-}
-
-size_t AudioFlinger::RecordThread::removeEffectChain_l(const sp<EffectChain>& chain)
-{
- ALOGV("removeEffectChain_l() %p from thread %p", chain.get(), this);
- ALOGW_IF(mEffectChains.size() != 1,
- "removeEffectChain_l() %p invalid chain size %d on thread %p",
- chain.get(), mEffectChains.size(), this);
- if (mEffectChains.size() == 1) {
- mEffectChains.removeAt(0);
- }
- return 0;
-}
-
-// ----------------------------------------------------------------------------
-// EffectModule implementation
-// ----------------------------------------------------------------------------
-
-#undef LOG_TAG
-#define LOG_TAG "AudioFlinger::EffectModule"
-
-AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
- const wp<AudioFlinger::EffectChain>& chain,
- effect_descriptor_t *desc,
- int id,
- int sessionId)
- : mPinned(sessionId > AUDIO_SESSION_OUTPUT_MIX),
- mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
- mDescriptor(*desc),
- // mConfig is set by configure() and not used before then
- mEffectInterface(NULL),
- mStatus(NO_INIT), mState(IDLE),
- // mMaxDisableWaitCnt is set by configure() and not used before then
- // mDisableWaitCnt is set by process() and updateState() and not used before then
- mSuspended(false)
-{
- ALOGV("Constructor %p", this);
- int lStatus;
-
- // create effect engine from effect factory
- mStatus = EffectCreate(&desc->uuid, sessionId, thread->id(), &mEffectInterface);
-
- if (mStatus != NO_ERROR) {
- return;
- }
- lStatus = init();
- if (lStatus < 0) {
- mStatus = lStatus;
- goto Error;
- }
-
- ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
- return;
-Error:
- EffectRelease(mEffectInterface);
- mEffectInterface = NULL;
- ALOGV("Constructor Error %d", mStatus);
-}
-
-AudioFlinger::EffectModule::~EffectModule()
-{
- ALOGV("Destructor %p", this);
- if (mEffectInterface != NULL) {
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
- (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- audio_stream_t *stream = thread->stream();
- if (stream != NULL) {
- stream->remove_audio_effect(stream, mEffectInterface);
- }
- }
- }
- // release effect engine
- EffectRelease(mEffectInterface);
- }
-}
-
-status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle)
-{
- status_t status;
-
- Mutex::Autolock _l(mLock);
- int priority = handle->priority();
- size_t size = mHandles.size();
- EffectHandle *controlHandle = NULL;
- size_t i;
- for (i = 0; i < size; i++) {
- EffectHandle *h = mHandles[i];
- if (h == NULL || h->destroyed_l()) continue;
- // first non destroyed handle is considered in control
- if (controlHandle == NULL)
- controlHandle = h;
- if (h->priority() <= priority) break;
- }
- // if inserted in first place, move effect control from previous owner to this handle
- if (i == 0) {
- bool enabled = false;
- if (controlHandle != NULL) {
- enabled = controlHandle->enabled();
- controlHandle->setControl(false/*hasControl*/, true /*signal*/, enabled /*enabled*/);
- }
- handle->setControl(true /*hasControl*/, false /*signal*/, enabled /*enabled*/);
- status = NO_ERROR;
- } else {
- status = ALREADY_EXISTS;
- }
- ALOGV("addHandle() %p added handle %p in position %d", this, handle, i);
- mHandles.insertAt(handle, i);
- return status;
-}
-
-size_t AudioFlinger::EffectModule::removeHandle(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;
- }
- ALOGV("removeHandle() %p removed handle %p in position %d", this, handle, i);
-
- mHandles.removeAt(i);
- // if removed from first place, move effect control from this handle to next in line
- if (i == 0) {
- EffectHandle *h = controlHandle_l();
- if (h != NULL) {
- h->setControl(true /*hasControl*/, true /*signal*/ , handle->enabled() /*enabled*/);
- }
- }
-
- // Prevent calls to process() and other functions on effect interface from now on.
- // The effect engine will be released by the destructor when the last strong reference on
- // this object is released which can happen after next process is called.
- if (mHandles.size() == 0 && !mPinned) {
- mState = DESTROYED;
- }
-
- return mHandles.size();
-}
-
-// must be called with EffectModule::mLock held
-AudioFlinger::EffectHandle *AudioFlinger::EffectModule::controlHandle_l()
-{
- // the first valid handle in the list has control over the module
- for (size_t i = 0; i < mHandles.size(); i++) {
- EffectHandle *h = mHandles[i];
- if (h != NULL && !h->destroyed_l()) {
- return h;
- }
- }
-
- return NULL;
-}
-
-size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIfLast)
-{
- ALOGV("disconnect() %p handle %p", this, 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) {
- thread->disconnectEffect(keep, handle, unpinIfLast);
- }
- }
- return mHandles.size();
-}
-
-void AudioFlinger::EffectModule::updateState() {
- Mutex::Autolock _l(mLock);
-
- switch (mState) {
- case RESTART:
- reset_l();
- // FALL THROUGH
-
- case STARTING:
- // 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));
- }
- start_l();
- mState = ACTIVE;
- break;
- case STOPPING:
- stop_l();
- mDisableWaitCnt = mMaxDisableWaitCnt;
- mState = STOPPED;
- break;
- case STOPPED:
- // mDisableWaitCnt is forced to 1 by process() when the engine indicates the end of the
- // turn off sequence.
- if (--mDisableWaitCnt == 0) {
- reset_l();
- mState = IDLE;
- }
- break;
- default: //IDLE , ACTIVE, DESTROYED
- break;
- }
-}
-
-void AudioFlinger::EffectModule::process()
-{
- Mutex::Autolock _l(mLock);
-
- if (mState == DESTROYED || mEffectInterface == NULL ||
- mConfig.inputCfg.buffer.raw == NULL ||
- mConfig.outputCfg.buffer.raw == NULL) {
- return;
- }
-
- if (isProcessEnabled()) {
- // do 32 bit to 16 bit conversion for auxiliary effect input buffer
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- ditherAndClamp(mConfig.inputCfg.buffer.s32,
- mConfig.inputCfg.buffer.s32,
- mConfig.inputCfg.buffer.frameCount/2);
- }
-
- // do the actual processing in the effect engine
- int ret = (*mEffectInterface)->process(mEffectInterface,
- &mConfig.inputCfg.buffer,
- &mConfig.outputCfg.buffer);
-
- // force transition to IDLE state when engine is ready
- if (mState == STOPPED && ret == -ENODATA) {
- mDisableWaitCnt = 1;
- }
-
- // 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,
- // accumulate input onto output
- sp<EffectChain> chain = mChain.promote();
- if (chain != 0 && chain->activeTrackCnt() != 0) {
- size_t frameCnt = mConfig.inputCfg.buffer.frameCount * 2; //always stereo here
- int16_t *in = mConfig.inputCfg.buffer.s16;
- int16_t *out = mConfig.outputCfg.buffer.s16;
- for (size_t i = 0; i < frameCnt; i++) {
- out[i] = clamp16((int32_t)out[i] + (int32_t)in[i]);
- }
- }
- }
-}
-
-void AudioFlinger::EffectModule::reset_l()
-{
- if (mEffectInterface == NULL) {
- return;
- }
- (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);
-}
-
-status_t AudioFlinger::EffectModule::configure()
-{
- 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
- audio_channel_mask_t channelMask = thread->channelMask();
-
- if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
- mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
- } else {
- mConfig.inputCfg.channels = channelMask;
- }
- mConfig.outputCfg.channels = channelMask;
- mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
- mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
- 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 AUDIO_SESSION_OUTPUT_MIX or AUDIO_SESSION_OUTPUT_STAGE,
- // 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;
-
- ALOGV("configure() %p thread %p buffer %p framecount %d",
- this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
-
- status_t cmdStatus;
- uint32_t size = sizeof(int);
- status_t status = (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_SET_CONFIG,
- sizeof(effect_config_t),
- &mConfig,
- &size,
- &cmdStatus);
- if (status == 0) {
- status = cmdStatus;
- }
-
- if (status == 0 &&
- (memcmp(&mDescriptor.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0)) {
- uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
- effect_param_t *p = (effect_param_t *)buf32;
-
- p->psize = sizeof(uint32_t);
- p->vsize = sizeof(uint32_t);
- size = sizeof(int);
- *(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;
-
- uint32_t latency = 0;
- PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
- if (pbt != NULL) {
- latency = pbt->latency_l();
- }
-
- *((int32_t *)p->data + 1)= latency;
- (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_SET_PARAM,
- sizeof(effect_param_t) + 8,
- &buf32,
- &size,
- &cmdStatus);
- }
-
- mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
- (1000 * mConfig.outputCfg.buffer.frameCount);
-
- return status;
-}
-
-status_t AudioFlinger::EffectModule::init()
-{
- Mutex::Autolock _l(mLock);
- if (mEffectInterface == NULL) {
- return NO_INIT;
- }
- status_t cmdStatus;
- uint32_t 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()
-{
- Mutex::Autolock _l(mLock);
- return start_l();
-}
-
-status_t AudioFlinger::EffectModule::start_l()
-{
- if (mEffectInterface == NULL) {
- return NO_INIT;
- }
- status_t cmdStatus;
- uint32_t size = sizeof(status_t);
- status_t status = (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_ENABLE,
- 0,
- NULL,
- &size,
- &cmdStatus);
- if (status == 0) {
- status = cmdStatus;
- }
- if (status == 0 &&
- ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
- (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC)) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- audio_stream_t *stream = thread->stream();
- if (stream != NULL) {
- stream->add_audio_effect(stream, mEffectInterface);
+ for (size_t i = 0; i < removed.size(); i++) {
+ srcThread->addEffect_l(removed[i]);
+ if (dstChain != 0 && reRegister) {
+ AudioSystem::unregisterEffect(removed[i]->id());
+ AudioSystem::registerEffect(&removed[i]->desc(),
+ srcThread->id(),
+ strategy,
+ sessionId,
+ removed[i]->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
}
}
- return status;
-}
-status_t AudioFlinger::EffectModule::stop()
-{
- Mutex::Autolock _l(mLock);
- return stop_l();
-}
-
-status_t AudioFlinger::EffectModule::stop_l()
-{
- if (mEffectInterface == NULL) {
- return NO_INIT;
- }
- status_t cmdStatus;
- uint32_t size = sizeof(status_t);
- status_t status = (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_DISABLE,
- 0,
- NULL,
- &size,
- &cmdStatus);
- if (status == 0) {
- status = cmdStatus;
- }
- if (status == 0 &&
- ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
- (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC)) {
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- audio_stream_t *stream = thread->stream();
- if (stream != NULL) {
- stream->remove_audio_effect(stream, mEffectInterface);
- }
- }
- }
return status;
}
-status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData)
-{
- Mutex::Autolock _l(mLock);
-// ALOGV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
-
- if (mState == DESTROYED || 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) {
- uint32_t size = (replySize == NULL) ? 0 : *replySize;
- for (size_t i = 1; i < mHandles.size(); i++) {
- EffectHandle *h = mHandles[i];
- if (h != NULL && !h->destroyed_l()) {
- h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
- }
- }
- }
- return status;
-}
-
-status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
-{
- Mutex::Autolock _l(mLock);
- return setEnabled_l(enabled);
-}
-
-// must be called with EffectModule::mLock held
-status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled)
-{
-
- ALOGV("setEnabled %p enabled %d", this, enabled);
-
- if (enabled != isEnabled()) {
- status_t status = AudioSystem::setEffectEnabled(mId, enabled);
- if (enabled && status != NO_ERROR) {
- return status;
- }
-
- switch (mState) {
- // going from disabled to enabled
- case IDLE:
- mState = STARTING;
- break;
- case STOPPED:
- mState = RESTART;
- break;
- case STOPPING:
- mState = ACTIVE;
- break;
-
- // going from enabled to disabled
- case RESTART:
- mState = STOPPED;
- break;
- case STARTING:
- mState = IDLE;
- break;
- case ACTIVE:
- mState = STOPPING;
- break;
- case DESTROYED:
- return NO_ERROR; // simply ignore as we are being destroyed
- }
- for (size_t i = 1; i < mHandles.size(); i++) {
- EffectHandle *h = mHandles[i];
- if (h != NULL && !h->destroyed_l()) {
- h->setEnabled(enabled);
- }
- }
- }
- return NO_ERROR;
-}
-
-bool AudioFlinger::EffectModule::isEnabled() const
-{
- switch (mState) {
- case RESTART:
- case STARTING:
- case ACTIVE:
- return true;
- case IDLE:
- case STOPPING:
- case STOPPED:
- case DESTROYED:
- default:
- return false;
- }
-}
-
-bool AudioFlinger::EffectModule::isProcessEnabled() const
+bool AudioFlinger::isNonOffloadableGlobalEffectEnabled_l()
{
- switch (mState) {
- case RESTART:
- case ACTIVE:
- case STOPPING:
- case STOPPED:
+ if (mGlobalEffectEnableTime != 0 &&
+ ((systemTime() - mGlobalEffectEnableTime) < kMinGlobalEffectEnabletimeNs)) {
return true;
- case IDLE:
- case STARTING:
- case DESTROYED:
- default:
- return false;
- }
-}
-
-status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
-{
- Mutex::Autolock _l(mLock);
- 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 (isProcessEnabled() &&
- ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
- (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) {
- status_t cmdStatus;
- uint32_t volume[2];
- uint32_t *pVolume = NULL;
- uint32_t 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(audio_devices_t device)
-{
- if (device == AUDIO_DEVICE_NONE) {
- return NO_ERROR;
- }
-
- Mutex::Autolock _l(mLock);
- status_t status = NO_ERROR;
- if (device && (mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
- status_t cmdStatus;
- uint32_t size = sizeof(status_t);
- uint32_t cmd = audio_is_output_devices(device) ? EFFECT_CMD_SET_DEVICE :
- EFFECT_CMD_SET_INPUT_DEVICE;
- status = (*mEffectInterface)->command(mEffectInterface,
- cmd,
- sizeof(uint32_t),
- &device,
- &size,
- &cmdStatus);
- }
- return status;
-}
-
-status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode)
-{
- Mutex::Autolock _l(mLock);
- status_t status = NO_ERROR;
- if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
- status_t cmdStatus;
- uint32_t size = sizeof(status_t);
- status = (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_SET_AUDIO_MODE,
- sizeof(audio_mode_t),
- &mode,
- &size,
- &cmdStatus);
- if (status == NO_ERROR) {
- status = cmdStatus;
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<EffectChain> ec =
+ mPlaybackThreads.valueAt(i)->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+ if (ec != 0 && ec->isNonOffloadableEnabled()) {
+ return true;
}
}
- return status;
-}
-
-status_t AudioFlinger::EffectModule::setAudioSource(audio_source_t source)
-{
- Mutex::Autolock _l(mLock);
- status_t status = NO_ERROR;
- if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_SOURCE_MASK) == EFFECT_FLAG_AUDIO_SOURCE_IND) {
- uint32_t size = 0;
- status = (*mEffectInterface)->command(mEffectInterface,
- EFFECT_CMD_SET_AUDIO_SOURCE,
- sizeof(audio_source_t),
- &source,
- &size,
- NULL);
- }
- return status;
-}
-
-void AudioFlinger::EffectModule::setSuspended(bool suspended)
-{
- Mutex::Autolock _l(mLock);
- mSuspended = suspended;
-}
-
-bool AudioFlinger::EffectModule::suspended() const
-{
- Mutex::Autolock _l(mLock);
- return mSuspended;
+ return false;
}
-bool AudioFlinger::EffectModule::purgeHandles()
+void AudioFlinger::onNonOffloadableGlobalEffectEnable()
{
- bool enabled = false;
Mutex::Autolock _l(mLock);
- for (size_t i = 0; i < mHandles.size(); i++) {
- EffectHandle *handle = mHandles[i];
- if (handle != NULL && !handle->destroyed_l()) {
- handle->effect().clear();
- if (handle->hasControl()) {
- enabled = handle->enabled();
- }
- }
- }
- return enabled;
-}
-
-void 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: %08X\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) {
- EffectHandle *handle = mHandles[i];
- if (handle != NULL && !handle->destroyed_l()) {
- handle->dump(buffer, SIZE);
- result.append(buffer);
- }
- }
- result.append("\n");
-
- write(fd, result.string(), result.length());
-
- if (locked) {
- mLock.unlock();
- }
-}
-
-// ----------------------------------------------------------------------------
-// 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), mCblk(NULL),
- mPriority(priority), mHasControl(false), mEnabled(false), mDestroyed(false)
-{
- ALOGV("constructor %p", this);
+ mGlobalEffectEnableTime = systemTime();
- if (client == 0) {
- return;
- }
- 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 != NULL) {
- new(mCblk) effect_param_cblk_t();
- mBuffer = (uint8_t *)mCblk + bufOffset;
- }
- } else {
- ALOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE + sizeof(effect_param_cblk_t));
- return;
- }
-}
-
-AudioFlinger::EffectHandle::~EffectHandle()
-{
- ALOGV("Destructor %p", this);
-
- if (mEffect == 0) {
- mDestroyed = true;
- return;
- }
- mEffect->lock();
- mDestroyed = true;
- mEffect->unlock();
- disconnect(false);
-}
-
-status_t AudioFlinger::EffectHandle::enable()
-{
- ALOGV("enable %p", this);
- if (!mHasControl) return INVALID_OPERATION;
- if (mEffect == 0) return DEAD_OBJECT;
-
- if (mEnabled) {
- return NO_ERROR;
- }
-
- mEnabled = true;
-
- sp<ThreadBase> thread = mEffect->thread().promote();
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(mEffect, true, mEffect->sessionId());
- }
-
- // checkSuspendOnEffectEnabled() can suspend this same effect when enabled
- if (mEffect->suspended()) {
- return NO_ERROR;
- }
-
- status_t status = mEffect->setEnabled(true);
- if (status != NO_ERROR) {
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
- }
- mEnabled = false;
- }
- return status;
-}
-
-status_t AudioFlinger::EffectHandle::disable()
-{
- ALOGV("disable %p", this);
- if (!mHasControl) return INVALID_OPERATION;
- if (mEffect == 0) return DEAD_OBJECT;
-
- if (!mEnabled) {
- return NO_ERROR;
- }
- mEnabled = false;
-
- if (mEffect->suspended()) {
- return NO_ERROR;
- }
-
- status_t status = mEffect->setEnabled(false);
-
- sp<ThreadBase> thread = mEffect->thread().promote();
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
- }
-
- return status;
-}
-
-void AudioFlinger::EffectHandle::disconnect()
-{
- disconnect(true);
-}
-
-void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast)
-{
- ALOGV("disconnect(%s)", unpinIfLast ? "true" : "false");
- if (mEffect == 0) {
- return;
- }
- // restore suspended effects if the disconnected handle was enabled and the last one.
- if ((mEffect->disconnect(this, unpinIfLast) == 0) && mEnabled) {
- sp<ThreadBase> thread = mEffect->thread().promote();
- if (thread != 0) {
- thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
- }
- }
-
- // release sp on module => module destructor can be called now
- mEffect.clear();
- if (mClient != 0) {
- if (mCblk != NULL) {
- // unlike ~TrackBase(), mCblk is never a local new, so don't delete
- mCblk->~effect_param_cblk_t(); // destroy our shared-structure.
- }
- mCblkMemory.clear(); // free the shared memory before releasing the heap it belongs to
- // Client destructor must run with AudioFlinger mutex locked
- Mutex::Autolock _l(mClient->audioFlinger()->mLock);
- mClient.clear();
- }
-}
-
-status_t AudioFlinger::EffectHandle::command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData)
-{
-// ALOGV("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;
- if (mClient == 0) return INVALID_OPERATION;
-
- // 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;
- uint32_t rsize = sizeof(int);
- int *p = (int *)(mBuffer + mCblk->serverIndex);
- int size = *p++;
- if (((uint8_t *)p + size) > mBuffer + mCblk->clientIndex) {
- ALOGW("command(): invalid parameter block size");
- break;
- }
- effect_param_t *param = (effect_param_t *)p;
- if (param->psize == 0 || param->vsize == 0) {
- ALOGW("command(): null parameter or value size");
- mCblk->serverIndex += size;
- continue;
- }
- uint32_t 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);
- // stop at first error encountered
- if (ret != NO_ERROR) {
- status = ret;
- *(int *)pReplyData = reply;
- break;
- } else if (reply != NO_ERROR) {
- *(int *)pReplyData = reply;
- break;
- }
- mCblk->serverIndex += size;
- }
- mCblk->serverIndex = 0;
- mCblk->clientIndex = 0;
- return status;
- } else if (cmdCode == EFFECT_CMD_ENABLE) {
- *(int *)pReplyData = NO_ERROR;
- return enable();
- } else if (cmdCode == EFFECT_CMD_DISABLE) {
- *(int *)pReplyData = NO_ERROR;
- return disable();
- }
-
- return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
-}
-
-void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal, bool enabled)
-{
- ALOGV("setControl %p control %d", this, hasControl);
-
- mHasControl = hasControl;
- mEnabled = enabled;
-
- if (signal && mEffectClient != 0) {
- mEffectClient->controlStatusChanged(hasControl);
- }
-}
-
-void AudioFlinger::EffectHandle::commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t 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 = mCblk != NULL && tryLock(mCblk->lock);
-
- snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n",
- (mClient == 0) ? getpid_cached : mClient->pid(),
- mPriority,
- mHasControl,
- !locked,
- mCblk ? mCblk->clientIndex : 0,
- mCblk ? mCblk->serverIndex : 0
- );
-
- if (locked) {
- mCblk->lock.unlock();
- }
-}
-
-#undef LOG_TAG
-#define LOG_TAG "AudioFlinger::EffectChain"
-
-AudioFlinger::EffectChain::EffectChain(ThreadBase *thread,
- int sessionId)
- : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
- mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
- mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
-{
- mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
- if (thread == NULL) {
- return;
- }
- mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) /
- thread->frameCount();
-}
-
-AudioFlinger::EffectChain::~EffectChain()
-{
- if (mOwnInBuffer) {
- delete mInBuffer;
- }
-
-}
-
-// getEffectFromDesc_l() must be called with ThreadBase::mLock held
-sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc_l(effect_descriptor_t *descriptor)
-{
- 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) {
- return mEffects[i];
- }
- }
- return 0;
-}
-
-// getEffectFromId_l() must be called with ThreadBase::mLock held
-sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId_l(int id)
-{
- size_t size = mEffects.size();
-
- for (size_t i = 0; i < size; i++) {
- // by convention, return first effect if id provided is 0 (0 is never a valid id)
- if (id == 0 || mEffects[i]->id() == id) {
- return mEffects[i];
- }
- }
- return 0;
-}
-
-// getEffectFromType_l() must be called with ThreadBase::mLock held
-sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromType_l(
- const effect_uuid_t *type)
-{
- size_t size = mEffects.size();
-
- for (size_t i = 0; i < size; i++) {
- if (memcmp(&mEffects[i]->desc().type, type, sizeof(effect_uuid_t)) == 0) {
- return mEffects[i];
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ if (t->mType == ThreadBase::OFFLOAD) {
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
}
}
- return 0;
-}
-void AudioFlinger::EffectChain::clearInputBuffer()
-{
- Mutex::Autolock _l(mLock);
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- ALOGW("clearInputBuffer(): cannot promote mixer thread");
- return;
- }
- clearInputBuffer_l(thread);
}
-// Must be called with EffectChain::mLock locked
-void AudioFlinger::EffectChain::clearInputBuffer_l(sp<ThreadBase> thread)
-{
- size_t numSamples = thread->frameCount() * thread->channelCount();
- memset(mInBuffer, 0, numSamples * sizeof(int16_t));
-
-}
+struct Entry {
+#define MAX_NAME 32 // %Y%m%d%H%M%S_%d.wav
+ char mName[MAX_NAME];
+};
-// Must be called with EffectChain::mLock locked
-void AudioFlinger::EffectChain::process_l()
+int comparEntry(const void *p1, const void *p2)
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- ALOGW("process_l(): cannot promote mixer thread");
- return;
- }
- bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) ||
- (mSessionId == AUDIO_SESSION_OUTPUT_STAGE);
- // always process effects unless no more tracks are on the session and the effect tail
- // has been rendered
- bool doProcess = true;
- if (!isGlobalSession) {
- bool tracksOnSession = (trackCnt() != 0);
-
- if (!tracksOnSession && mTailBufferCount == 0) {
- doProcess = false;
- }
-
- if (activeTrackCnt() == 0) {
- // if no track is active and the effect tail has not been rendered,
- // the input buffer must be cleared here as the mixer process will not do it
- if (tracksOnSession || mTailBufferCount > 0) {
- clearInputBuffer_l(thread);
- if (mTailBufferCount > 0) {
- mTailBufferCount--;
- }
- }
- }
- }
-
- size_t size = mEffects.size();
- if (doProcess) {
- for (size_t i = 0; i < size; i++) {
- mEffects[i]->process();
- }
- }
- for (size_t i = 0; i < size; i++) {
- mEffects[i]->updateState();
- }
+ return strcmp(((const Entry *) p1)->mName, ((const Entry *) p2)->mName);
}
-// addEffect_l() must be called with PlaybackThread::mLock held
-status_t AudioFlinger::EffectChain::addEffect_l(const sp<EffectModule>& effect)
+#ifdef TEE_SINK
+void AudioFlinger::dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id)
{
- effect_descriptor_t desc = effect->desc();
- uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
-
- Mutex::Autolock _l(mLock);
- effect->setChain(this);
- sp<ThreadBase> thread = mThread.promote();
- if (thread == 0) {
- return NO_INIT;
- }
- effect->setThread(thread);
-
- 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);
-
- // 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 as a function of indicated preference:
- // if EFFECT_FLAG_INSERT_EXCLUSIVE, insert in first position or reject if
- // another effect is present
- // else if EFFECT_FLAG_INSERT_FIRST, insert in first position or after the
- // last effect claiming first position
- // else if EFFECT_FLAG_INSERT_LAST, insert in last position or before the
- // first effect claiming last position
- // else if EFFECT_FLAG_INSERT_ANY insert after first or before last
- // Reject insertion if an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is
- // already present
-
- size_t size = mEffects.size();
- size_t idx_insert = size;
- ssize_t idx_insert_first = -1;
- ssize_t idx_insert_last = -1;
-
- for (size_t 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) {
- ALOGW("addEffect_l() could not insert effect %s: exclusive conflict with %s", desc.name, d.name);
- return INVALID_OPERATION;
+ NBAIO_Source *teeSource = source.get();
+ if (teeSource != NULL) {
+ // .wav rotation
+ // There is a benign race condition if 2 threads call this simultaneously.
+ // They would both traverse the directory, but the result would simply be
+ // failures at unlink() which are ignored. It's also unlikely since
+ // normally dumpsys is only done by bugreport or from the command line.
+ char teePath[32+256];
+ strcpy(teePath, "/data/misc/media");
+ size_t teePathLen = strlen(teePath);
+ DIR *dir = opendir(teePath);
+ teePath[teePathLen++] = '/';
+ if (dir != NULL) {
+#define MAX_SORT 20 // number of entries to sort
+#define MAX_KEEP 10 // number of entries to keep
+ struct Entry entries[MAX_SORT];
+ size_t entryCount = 0;
+ while (entryCount < MAX_SORT) {
+ struct dirent de;
+ struct dirent *result = NULL;
+ int rc = readdir_r(dir, &de, &result);
+ if (rc != 0) {
+ ALOGW("readdir_r failed %d", rc);
+ break;
}
- // remember position of first insert effect and by default
- // select this as insert position for new effect
- if (idx_insert == size) {
- idx_insert = i;
+ if (result == NULL) {
+ break;
}
- // remember position of last insert effect claiming
- // first position
- if (iPref == EFFECT_FLAG_INSERT_FIRST) {
- idx_insert_first = i;
+ if (result != &de) {
+ ALOGW("readdir_r returned unexpected result %p != %p", result, &de);
+ break;
}
- // remember position of first insert effect claiming
- // last position
- if (iPref == EFFECT_FLAG_INSERT_LAST &&
- idx_insert_last == -1) {
- idx_insert_last = i;
+ // ignore non .wav file entries
+ size_t nameLen = strlen(de.d_name);
+ if (nameLen <= 4 || nameLen >= MAX_NAME ||
+ strcmp(&de.d_name[nameLen - 4], ".wav")) {
+ continue;
}
+ strcpy(entries[entryCount++].mName, de.d_name);
}
- }
-
- // modify idx_insert from first position if needed
- if (insertPref == EFFECT_FLAG_INSERT_LAST) {
- if (idx_insert_last != -1) {
- idx_insert = idx_insert_last;
- } else {
- idx_insert = size;
- }
- } else {
- if (idx_insert_first != -1) {
- idx_insert = idx_insert_first + 1;
- }
- }
-
- // 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);
- }
- mEffects.insertAt(effect, idx_insert);
-
- ALOGV("addEffect_l() effect %p, added in chain %p at rank %d", effect.get(), this, idx_insert);
- }
- effect->configure();
- return NO_ERROR;
-}
-
-// removeEffect_l() must be called with PlaybackThread::mLock held
-size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
-{
- Mutex::Autolock _l(mLock);
- size_t size = mEffects.size();
- uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;
-
- for (size_t i = 0; i < size; i++) {
- if (effect == mEffects[i]) {
- // calling stop here will remove pre-processing effect from the audio HAL.
- // This is safe as we hold the EffectChain mutex which guarantees that we are not in
- // the middle of a read from audio HAL
- if (mEffects[i]->state() == EffectModule::ACTIVE ||
- mEffects[i]->state() == EffectModule::STOPPING) {
- mEffects[i]->stop();
- }
- 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();
+ (void) closedir(dir);
+ if (entryCount > MAX_KEEP) {
+ qsort(entries, entryCount, sizeof(Entry), comparEntry);
+ for (size_t i = 0; i < entryCount - MAX_KEEP; ++i) {
+ strcpy(&teePath[teePathLen], entries[i].mName);
+ (void) unlink(teePath);
}
}
- mEffects.removeAt(i);
- ALOGV("removeEffect_l() effect %p, removed from chain %p at rank %d", effect.get(), this, i);
- break;
- }
- }
-
- return mEffects.size();
-}
-
-// setDevice_l() must be called with PlaybackThread::mLock held
-void AudioFlinger::EffectChain::setDevice_l(audio_devices_t device)
-{
- size_t size = mEffects.size();
- for (size_t i = 0; i < size; i++) {
- mEffects[i]->setDevice(device);
- }
-}
-
-// setMode_l() must be called with PlaybackThread::mLock held
-void AudioFlinger::EffectChain::setMode_l(audio_mode_t mode)
-{
- size_t size = mEffects.size();
- for (size_t i = 0; i < size; i++) {
- mEffects[i]->setMode(mode);
- }
-}
-
-// setAudioSource_l() must be called with PlaybackThread::mLock held
-void AudioFlinger::EffectChain::setAudioSource_l(audio_source_t source)
-{
- size_t size = mEffects.size();
- for (size_t i = 0; i < size; i++) {
- mEffects[i]->setAudioSource(source);
- }
-}
-
-// setVolume_l() must be called with PlaybackThread::mLock held
-bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right)
-{
- uint32_t newLeft = *left;
- uint32_t newRight = *right;
- bool hasControl = false;
- int ctrlIdx = -1;
- size_t size = mEffects.size();
-
- // first update volume controller
- for (size_t i = size; i > 0; i--) {
- if (mEffects[i - 1]->isProcessEnabled() &&
- (mEffects[i - 1]->desc().flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL) {
- ctrlIdx = i - 1;
- hasControl = true;
- break;
- }
- }
-
- if (ctrlIdx == mVolumeCtrlIdx && *left == mLeftVolume && *right == mRightVolume) {
- if (hasControl) {
- *left = mNewLeftVolume;
- *right = mNewRightVolume;
- }
- return hasControl;
- }
-
- mVolumeCtrlIdx = ctrlIdx;
- mLeftVolume = newLeft;
- mRightVolume = newRight;
-
- // second get volume update from volume controller
- if (ctrlIdx >= 0) {
- mEffects[ctrlIdx]->setVolume(&newLeft, &newRight, true);
- mNewLeftVolume = newLeft;
- mNewRightVolume = newRight;
- }
- // 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;
-
- for (size_t i = 0; i < size; i++) {
- if ((int)i == ctrlIdx) continue;
- // this also works for ctrlIdx == -1 when there is no volume controller
- if ((int)i > ctrlIdx) {
- lVol = *left;
- rVol = *right;
- }
- mEffects[i]->setVolume(&lVol, &rVol, false);
- }
- *left = newLeft;
- *right = newRight;
-
- return hasControl;
-}
-
-void 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 Active tracks:\n");
- snprintf(buffer, SIZE, "\t%02d 0x%08x 0x%08x %d\n",
- mEffects.size(),
- (uint32_t)mInBuffer,
- (uint32_t)mOutBuffer,
- 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();
- }
-}
-
-// must be called with ThreadBase::mLock held
-void AudioFlinger::EffectChain::setEffectSuspended_l(
- const effect_uuid_t *type, bool suspend)
-{
- sp<SuspendedEffectDesc> desc;
- // use effect type UUID timelow as key as there is no real risk of identical
- // timeLow fields among effect type UUIDs.
- ssize_t index = mSuspendedEffects.indexOfKey(type->timeLow);
- if (suspend) {
- if (index >= 0) {
- desc = mSuspendedEffects.valueAt(index);
} else {
- desc = new SuspendedEffectDesc();
- desc->mType = *type;
- mSuspendedEffects.add(type->timeLow, desc);
- ALOGV("setEffectSuspended_l() add entry for %08x", type->timeLow);
- }
- if (desc->mRefCount++ == 0) {
- sp<EffectModule> effect = getEffectIfEnabled(type);
- if (effect != 0) {
- desc->mEffect = effect;
- effect->setSuspended(true);
- effect->setEnabled(false);
+ if (fd >= 0) {
+ fdprintf(fd, "unable to rotate tees in %s: %s\n", teePath, strerror(errno));
}
}
- } else {
- if (index < 0) {
- return;
- }
- desc = mSuspendedEffects.valueAt(index);
- if (desc->mRefCount <= 0) {
- ALOGW("setEffectSuspended_l() restore refcount should not be 0 %d", desc->mRefCount);
- desc->mRefCount = 1;
- }
- if (--desc->mRefCount == 0) {
- ALOGV("setEffectSuspended_l() remove entry for %08x", mSuspendedEffects.keyAt(index));
- if (desc->mEffect != 0) {
- sp<EffectModule> effect = desc->mEffect.promote();
- if (effect != 0) {
- effect->setSuspended(false);
- effect->lock();
- EffectHandle *handle = effect->controlHandle_l();
- if (handle != NULL && !handle->destroyed_l()) {
- effect->setEnabled_l(handle->enabled());
+ char teeTime[16];
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ struct tm tm;
+ localtime_r(&tv.tv_sec, &tm);
+ strftime(teeTime, sizeof(teeTime), "%Y%m%d%H%M%S", &tm);
+ snprintf(&teePath[teePathLen], sizeof(teePath) - teePathLen, "%s_%d.wav", teeTime, id);
+ // if 2 dumpsys are done within 1 second, and rotation didn't work, then discard 2nd
+ int teeFd = open(teePath, O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW, S_IRUSR | S_IWUSR);
+ if (teeFd >= 0) {
+ char wavHeader[44];
+ memcpy(wavHeader,
+ "RIFF\0\0\0\0WAVEfmt \20\0\0\0\1\0\2\0\104\254\0\0\0\0\0\0\4\0\20\0data\0\0\0\0",
+ sizeof(wavHeader));
+ NBAIO_Format format = teeSource->format();
+ unsigned channelCount = Format_channelCount(format);
+ ALOG_ASSERT(channelCount <= FCC_2);
+ uint32_t sampleRate = Format_sampleRate(format);
+ wavHeader[22] = channelCount; // number of channels
+ wavHeader[24] = sampleRate; // sample rate
+ wavHeader[25] = sampleRate >> 8;
+ wavHeader[32] = channelCount * 2; // block alignment
+ write(teeFd, wavHeader, sizeof(wavHeader));
+ size_t total = 0;
+ bool firstRead = true;
+ for (;;) {
+#define TEE_SINK_READ 1024
+ short buffer[TEE_SINK_READ * FCC_2];
+ size_t count = TEE_SINK_READ;
+ ssize_t actual = teeSource->read(buffer, count,
+ AudioBufferProvider::kInvalidPTS);
+ bool wasFirstRead = firstRead;
+ firstRead = false;
+ if (actual <= 0) {
+ if (actual == (ssize_t) OVERRUN && wasFirstRead) {
+ continue;
}
- effect->unlock();
- }
- desc->mEffect.clear();
- }
- mSuspendedEffects.removeItemsAt(index);
- }
- }
-}
-
-// must be called with ThreadBase::mLock held
-void AudioFlinger::EffectChain::setEffectSuspendedAll_l(bool suspend)
-{
- sp<SuspendedEffectDesc> desc;
-
- ssize_t index = mSuspendedEffects.indexOfKey((int)kKeyForSuspendAll);
- if (suspend) {
- if (index >= 0) {
- desc = mSuspendedEffects.valueAt(index);
- } else {
- desc = new SuspendedEffectDesc();
- mSuspendedEffects.add((int)kKeyForSuspendAll, desc);
- ALOGV("setEffectSuspendedAll_l() add entry for 0");
- }
- if (desc->mRefCount++ == 0) {
- Vector< sp<EffectModule> > effects;
- getSuspendEligibleEffects(effects);
- for (size_t i = 0; i < effects.size(); i++) {
- setEffectSuspended_l(&effects[i]->desc().type, true);
- }
- }
- } else {
- if (index < 0) {
- return;
- }
- desc = mSuspendedEffects.valueAt(index);
- if (desc->mRefCount <= 0) {
- ALOGW("setEffectSuspendedAll_l() restore refcount should not be 0 %d", desc->mRefCount);
- desc->mRefCount = 1;
- }
- if (--desc->mRefCount == 0) {
- Vector<const effect_uuid_t *> types;
- for (size_t i = 0; i < mSuspendedEffects.size(); i++) {
- if (mSuspendedEffects.keyAt(i) == (int)kKeyForSuspendAll) {
- continue;
+ break;
}
- types.add(&mSuspendedEffects.valueAt(i)->mType);
- }
- for (size_t i = 0; i < types.size(); i++) {
- setEffectSuspended_l(types[i], false);
- }
- ALOGV("setEffectSuspendedAll_l() remove entry for %08x", mSuspendedEffects.keyAt(index));
- mSuspendedEffects.removeItem((int)kKeyForSuspendAll);
- }
- }
-}
-
-
-// The volume effect is used for automated tests only
-#ifndef OPENSL_ES_H_
-static const effect_uuid_t SL_IID_VOLUME_ = { 0x09e8ede0, 0xddde, 0x11db, 0xb4f6,
- { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
-const effect_uuid_t * const SL_IID_VOLUME = &SL_IID_VOLUME_;
-#endif //OPENSL_ES_H_
-
-bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descriptor_t& desc)
-{
- // auxiliary effects and visualizer are never suspended on output mix
- if ((mSessionId == AUDIO_SESSION_OUTPUT_MIX) &&
- (((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) ||
- (memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) ||
- (memcmp(&desc.type, SL_IID_VOLUME, sizeof(effect_uuid_t)) == 0))) {
- return false;
- }
- return true;
-}
-
-void AudioFlinger::EffectChain::getSuspendEligibleEffects(Vector< sp<AudioFlinger::EffectModule> > &effects)
-{
- effects.clear();
- for (size_t i = 0; i < mEffects.size(); i++) {
- if (isEffectEligibleForSuspend(mEffects[i]->desc())) {
- effects.add(mEffects[i]);
- }
- }
-}
-
-sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectIfEnabled(
- const effect_uuid_t *type)
-{
- sp<EffectModule> effect = getEffectFromType_l(type);
- return effect != 0 && effect->isEnabled() ? effect : 0;
-}
-
-void AudioFlinger::EffectChain::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled)
-{
- ssize_t index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
- if (enabled) {
- if (index < 0) {
- // if the effect is not suspend check if all effects are suspended
- index = mSuspendedEffects.indexOfKey((int)kKeyForSuspendAll);
- if (index < 0) {
- return;
+ ALOG_ASSERT(actual <= (ssize_t)count);
+ write(teeFd, buffer, actual * channelCount * sizeof(short));
+ total += actual;
}
- if (!isEffectEligibleForSuspend(effect->desc())) {
- return;
+ lseek(teeFd, (off_t) 4, SEEK_SET);
+ uint32_t temp = 44 + total * channelCount * sizeof(short) - 8;
+ write(teeFd, &temp, sizeof(temp));
+ lseek(teeFd, (off_t) 40, SEEK_SET);
+ temp = total * channelCount * sizeof(short);
+ write(teeFd, &temp, sizeof(temp));
+ close(teeFd);
+ if (fd >= 0) {
+ fdprintf(fd, "tee copied to %s\n", teePath);
}
- setEffectSuspended_l(&effect->desc().type, enabled);
- index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
- if (index < 0) {
- ALOGW("checkSuspendOnEffectEnabled() Fx should be suspended here!");
- return;
+ } else {
+ if (fd >= 0) {
+ fdprintf(fd, "unable to create tee %s: %s\n", teePath, strerror(errno));
}
}
- ALOGV("checkSuspendOnEffectEnabled() enable suspending fx %08x",
- effect->desc().type.timeLow);
- sp<SuspendedEffectDesc> desc = mSuspendedEffects.valueAt(index);
- // if effect is requested to suspended but was not yet enabled, supend it now.
- if (desc->mEffect == 0) {
- desc->mEffect = effect;
- effect->setEnabled(false);
- effect->setSuspended(true);
- }
- } else {
- if (index < 0) {
- return;
- }
- ALOGV("checkSuspendOnEffectEnabled() disable restoring fx %08x",
- effect->desc().type.timeLow);
- sp<SuspendedEffectDesc> desc = mSuspendedEffects.valueAt(index);
- desc->mEffect.clear();
- effect->setSuspended(false);
}
}
-
-#undef LOG_TAG
-#define LOG_TAG "AudioFlinger"
+#endif
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 49e2b2c..7320144 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -24,6 +24,8 @@
#include <common_time/cc_helper.h>
+#include <cutils/compiler.h>
+
#include <media/IAudioFlinger.h>
#include <media/IAudioFlingerClient.h>
#include <media/IAudioTrack.h>
@@ -53,6 +55,9 @@
#include <powermanager/IPowerManager.h>
+#include <media/nbaio/NBLog.h>
+#include <private/media/AudioTrackShared.h>
+
namespace android {
class audio_track_cblk_t;
@@ -61,6 +66,7 @@ class AudioMixer;
class AudioBuffer;
class AudioResampler;
class FastMixer;
+class ServerProxy;
// ----------------------------------------------------------------------------
@@ -75,39 +81,44 @@ class FastMixer;
static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3);
+#define MAX_GAIN 4096.0f
+#define MAX_GAIN_INT 0x1000
+
+#define INCLUDING_FROM_AUDIOFLINGER_H
+
class AudioFlinger :
public BinderService<AudioFlinger>,
public BnAudioFlinger
{
friend class BinderService<AudioFlinger>; // for AudioFlinger()
public:
- static const char* getServiceName() { return "media.audio_flinger"; }
+ static const char* getServiceName() ANDROID_API { return "media.audio_flinger"; }
virtual status_t dump(int fd, const Vector<String16>& args);
// IAudioFlinger interface, in binder opcode order
virtual sp<IAudioTrack> createTrack(
- pid_t pid,
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount,
- IAudioFlinger::track_flags_t flags,
+ size_t frameCount,
+ IAudioFlinger::track_flags_t *flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
pid_t tid,
int *sessionId,
+ String8& name,
+ int clientUid,
status_t *status);
virtual sp<IAudioRecord> openRecord(
- pid_t pid,
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- int frameCount,
- IAudioFlinger::track_flags_t flags,
+ size_t frameCount,
+ IAudioFlinger::track_flags_t *flags,
pid_t tid,
int *sessionId,
status_t *status);
@@ -151,7 +162,8 @@ public:
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_output_flags_t flags);
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo);
virtual audio_io_handle_t openDuplicateOutput(audio_io_handle_t output1,
audio_io_handle_t output2);
@@ -177,7 +189,7 @@ public:
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames,
audio_io_handle_t output) const;
- virtual unsigned int getInputFramesLost(audio_io_handle_t ioHandle) const;
+ virtual uint32_t getInputFramesLost(audio_io_handle_t ioHandle) const;
virtual int newAudioSessionId();
@@ -192,7 +204,7 @@ public:
virtual status_t getEffectDescriptor(const effect_uuid_t *pUuid,
effect_descriptor_t *descriptor) const;
- virtual sp<IEffect> createEffect(pid_t pid,
+ virtual sp<IEffect> createEffect(
effect_descriptor_t *pDesc,
const sp<IEffectClient>& effectClient,
int32_t priority,
@@ -207,8 +219,10 @@ public:
virtual audio_module_handle_t loadHwModule(const char *name);
- virtual int32_t getPrimaryOutputSamplingRate();
- virtual int32_t getPrimaryOutputFrameCount();
+ virtual uint32_t getPrimaryOutputSamplingRate();
+ virtual size_t getPrimaryOutputFrameCount();
+
+ virtual status_t setLowRamDevice(bool isLowRamDevice);
virtual status_t onTransact(
uint32_t code,
@@ -218,6 +232,13 @@ public:
// end of IAudioFlinger interface
+ sp<NBLog::Writer> newWriter_l(size_t size, const char *name);
+ void unregisterWriter(const sp<NBLog::Writer>& writer);
+private:
+ static const size_t kLogMemorySize = 10 * 1024;
+ sp<MemoryDealer> mLogMemoryDealer; // == 0 when NBLog is disabled
+public:
+
class SyncEvent;
typedef void (*sync_event_callback_t)(const wp<SyncEvent>& event) ;
@@ -265,23 +286,32 @@ private:
bool btNrecIsOff() const { return mBtNrecIsOff; }
- AudioFlinger();
+ AudioFlinger() ANDROID_API;
virtual ~AudioFlinger();
// call in any IAudioFlinger method that accesses mPrimaryHardwareDev
- status_t initCheck() const { return mPrimaryHardwareDev == NULL ? NO_INIT : NO_ERROR; }
+ status_t initCheck() const { return mPrimaryHardwareDev == NULL ?
+ NO_INIT : NO_ERROR; }
// RefBase
virtual void onFirstRef();
- AudioHwDevice* findSuitableHwDev_l(audio_module_handle_t module, audio_devices_t devices);
+ AudioHwDevice* findSuitableHwDev_l(audio_module_handle_t module,
+ audio_devices_t devices);
void purgeStaleEffects_l();
// standby delay for MIXER and DUPLICATING playback threads is read from property
// ro.audio.flinger_standbytime_ms or defaults to kDefaultStandbyTimeInNsecs
static nsecs_t mStandbyTimeInNsecs;
+ // incremented by 2 when screen state changes, bit 0 == 1 means "off"
+ // AudioFlinger::setParameters() updates, other threads read w/o lock
+ static uint32_t mScreenState;
+
// Internal dump utilities.
+ static const int kDumpLockRetries = 50;
+ static const int kDumpLockSleepUs = 20000;
+ static bool dumpTryLock(Mutex& mutex);
void dumpPermissionDenial(int fd, const Vector<String16>& args);
void dumpClients(int fd, const Vector<String16>& args);
void dumpInternals(int fd, const Vector<String16>& args);
@@ -337,7 +367,9 @@ private:
class PlaybackThread;
class MixerThread;
class DirectOutputThread;
+ class OffloadThread;
class DuplicatingThread;
+ class AsyncCallbackThread;
class Track;
class RecordTrack;
class EffectModule;
@@ -346,409 +378,6 @@ private:
struct AudioStreamOut;
struct AudioStreamIn;
- class ThreadBase : public Thread {
- public:
-
- enum type_t {
- MIXER, // Thread class is MixerThread
- DIRECT, // Thread class is DirectOutputThread
- DUPLICATING, // Thread class is DuplicatingThread
- RECORD // Thread class is RecordThread
- };
-
- ThreadBase (const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
- audio_devices_t outDevice, audio_devices_t inDevice, type_t type);
- virtual ~ThreadBase();
-
- void dumpBase(int fd, const Vector<String16>& args);
- void dumpEffectChains(int fd, const Vector<String16>& args);
-
- void clearPowerManager();
-
- // base for record and playback
- class TrackBase : public ExtendedAudioBufferProvider, public RefBase {
-
- public:
- enum track_state {
- IDLE,
- TERMINATED,
- FLUSHED,
- STOPPED,
- // next 2 states are currently used for fast tracks only
- STOPPING_1, // waiting for first underrun
- STOPPING_2, // waiting for presentation complete
- RESUMING,
- ACTIVE,
- PAUSING,
- PAUSED
- };
-
- TrackBase(ThreadBase *thread,
- const sp<Client>& client,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId);
- virtual ~TrackBase();
-
- virtual status_t start(AudioSystem::sync_event_t event,
- int triggerSession) = 0;
- virtual void stop() = 0;
- sp<IMemory> getCblk() const { return mCblkMemory; }
- audio_track_cblk_t* cblk() const { return mCblk; }
- int sessionId() const { return mSessionId; }
- virtual status_t setSyncEvent(const sp<SyncEvent>& event);
-
- protected:
- TrackBase(const TrackBase&);
- TrackBase& operator = (const TrackBase&);
-
- // AudioBufferProvider interface
- virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts) = 0;
- virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
-
- // ExtendedAudioBufferProvider interface is only needed for Track,
- // but putting it in TrackBase avoids the complexity of virtual inheritance
- virtual size_t framesReady() const { return SIZE_MAX; }
-
- audio_format_t format() const {
- return mFormat;
- }
-
- int channelCount() const { return mChannelCount; }
-
- audio_channel_mask_t channelMask() const { return mChannelMask; }
-
- int sampleRate() const; // FIXME inline after cblk sr moved
-
- // Return a pointer to the start of a contiguous slice of the track buffer.
- // Parameter 'offset' is the requested start position, expressed in
- // monotonically increasing frame units relative to the track epoch.
- // Parameter 'frames' is the requested length, also in frame units.
- // Always returns non-NULL. It is the caller's responsibility to
- // verify that this will be successful; the result of calling this
- // function with invalid 'offset' or 'frames' is undefined.
- void* getBuffer(uint32_t offset, uint32_t frames) const;
-
- bool isStopped() const {
- return (mState == STOPPED || mState == FLUSHED);
- }
-
- // for fast tracks only
- bool isStopping() const {
- return mState == STOPPING_1 || mState == STOPPING_2;
- }
- bool isStopping_1() const {
- return mState == STOPPING_1;
- }
- bool isStopping_2() const {
- return mState == STOPPING_2;
- }
-
- bool isTerminated() const {
- return mState == TERMINATED;
- }
-
- bool step();
- void reset();
-
- const wp<ThreadBase> mThread;
- /*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const
- sp<IMemory> mCblkMemory;
- audio_track_cblk_t* mCblk;
- void* mBuffer; // start of track buffer, typically in shared memory
- void* mBufferEnd; // &mBuffer[mFrameCount * frameSize], where frameSize
- // is based on mChannelCount and 16-bit samples
- 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;
- uint8_t mChannelCount;
- audio_channel_mask_t mChannelMask;
- Vector < sp<SyncEvent> >mSyncEvents;
- };
-
- enum {
- CFG_EVENT_IO,
- CFG_EVENT_PRIO
- };
-
- class ConfigEvent {
- public:
- ConfigEvent(int type) : mType(type) {}
- virtual ~ConfigEvent() {}
-
- int type() const { return mType; }
-
- virtual void dump(char *buffer, size_t size) = 0;
-
- private:
- const int mType;
- };
-
- class IoConfigEvent : public ConfigEvent {
- public:
- IoConfigEvent(int event, int param) :
- ConfigEvent(CFG_EVENT_IO), mEvent(event), mParam(event) {}
- virtual ~IoConfigEvent() {}
-
- int event() const { return mEvent; }
- int param() const { return mParam; }
-
- virtual void dump(char *buffer, size_t size) {
- snprintf(buffer, size, "IO event: event %d, param %d\n", mEvent, mParam);
- }
-
- private:
- const int mEvent;
- const int mParam;
- };
-
- class PrioConfigEvent : public ConfigEvent {
- public:
- PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) :
- ConfigEvent(CFG_EVENT_PRIO), mPid(pid), mTid(tid), mPrio(prio) {}
- virtual ~PrioConfigEvent() {}
-
- pid_t pid() const { return mPid; }
- pid_t tid() const { return mTid; }
- int32_t prio() const { return mPrio; }
-
- virtual void dump(char *buffer, size_t size) {
- snprintf(buffer, size, "Prio event: pid %d, tid %d, prio %d\n", mPid, mTid, mPrio);
- }
-
- private:
- const pid_t mPid;
- const pid_t mTid;
- const int32_t mPrio;
- };
-
-
- class PMDeathRecipient : public IBinder::DeathRecipient {
- public:
- PMDeathRecipient(const wp<ThreadBase>& thread) : mThread(thread) {}
- virtual ~PMDeathRecipient() {}
-
- // IBinder::DeathRecipient
- virtual void binderDied(const wp<IBinder>& who);
-
- private:
- PMDeathRecipient(const PMDeathRecipient&);
- PMDeathRecipient& operator = (const PMDeathRecipient&);
-
- wp<ThreadBase> mThread;
- };
-
- virtual status_t initCheck() const = 0;
-
- // static externally-visible
- type_t type() const { return mType; }
- audio_io_handle_t id() const { return mId;}
-
- // dynamic externally-visible
- uint32_t sampleRate() const { return mSampleRate; }
- int channelCount() const { return mChannelCount; }
- audio_channel_mask_t channelMask() const { return mChannelMask; }
- audio_format_t format() const { return mFormat; }
- // Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
- // and returns the normal mix buffer's frame count.
- size_t frameCount() const { return mNormalFrameCount; }
- // Return's the HAL's frame count i.e. fast mixer buffer size.
- size_t frameCountHAL() const { return mFrameCount; }
-
- // Should be "virtual status_t requestExitAndWait()" and override same
- // method in Thread, but Thread::requestExitAndWait() is not yet virtual.
- void exit();
- virtual bool checkForNewParameters_l() = 0;
- virtual status_t setParameters(const String8& keyValuePairs);
- virtual String8 getParameters(const String8& keys) = 0;
- virtual void audioConfigChanged_l(int event, int param = 0) = 0;
- void sendIoConfigEvent(int event, int param = 0);
- void sendIoConfigEvent_l(int event, int param = 0);
- void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
- void processConfigEvents();
-
- // see note at declaration of mStandby, mOutDevice and mInDevice
- bool standby() const { return mStandby; }
- audio_devices_t outDevice() const { return mOutDevice; }
- audio_devices_t inDevice() const { return mInDevice; }
-
- virtual audio_stream_t* stream() const = 0;
-
- 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);
- void disconnectEffect(const sp< EffectModule>& effect,
- EffectHandle *handle,
- bool unpinIfLast);
-
- // return values for hasAudioSession (bit field)
- enum effect_state {
- EFFECT_SESSION = 0x1, // the audio session corresponds to at least one
- // effect
- TRACK_SESSION = 0x2 // the audio session corresponds to at least one
- // track
- };
-
- // get effect chain corresponding to session Id.
- sp<EffectChain> getEffectChain(int sessionId);
- // same as getEffectChain() but must be called with ThreadBase mutex locked
- sp<EffectChain> getEffectChain_l(int sessionId) const;
- // add an effect chain to the chain list (mEffectChains)
- virtual status_t addEffectChain_l(const sp<EffectChain>& chain) = 0;
- // remove an effect chain from the chain list (mEffectChains)
- virtual size_t removeEffectChain_l(const sp<EffectChain>& chain) = 0;
- // lock all effect chains Mutexes. Must be called before releasing the
- // ThreadBase mutex before processing the mixer and effects. This guarantees the
- // integrity of the chains during the process.
- // Also sets the parameter 'effectChains' to current value of mEffectChains.
- void lockEffectChains_l(Vector< sp<EffectChain> >& effectChains);
- // unlock effect chains after process
- void unlockEffectChains(const Vector< sp<EffectChain> >& effectChains);
- // set audio mode to all effect chains
- void setMode(audio_mode_t mode);
- // get effect module with corresponding ID on specified audio session
- sp<AudioFlinger::EffectModule> getEffect(int sessionId, int effectId);
- sp<AudioFlinger::EffectModule> getEffect_l(int sessionId, int effectId);
- // add and effect module. Also creates the effect chain is none exists for
- // the effects audio session
- status_t addEffect_l(const sp< EffectModule>& effect);
- // remove and effect module. Also removes the effect chain is this was the last
- // effect
- void removeEffect_l(const sp< EffectModule>& effect);
- // detach all tracks connected to an auxiliary effect
- virtual void detachAuxEffect_l(int effectId) {}
- // returns either EFFECT_SESSION if effects on this audio session exist in one
- // chain, or TRACK_SESSION if tracks on this audio session exist, or both
- virtual uint32_t hasAudioSession(int sessionId) const = 0;
- // the value returned by default implementation is not important as the
- // strategy is only meaningful for PlaybackThread which implements this method
- virtual uint32_t getStrategyForSession_l(int sessionId) { return 0; }
-
- // suspend or restore effect according to the type of effect passed. a NULL
- // type pointer means suspend all effects in the session
- void setEffectSuspended(const effect_uuid_t *type,
- bool suspend,
- int sessionId = AUDIO_SESSION_OUTPUT_MIX);
- // check if some effects must be suspended/restored when an effect is enabled
- // or disabled
- void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled,
- int sessionId = AUDIO_SESSION_OUTPUT_MIX);
- void checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
- bool enabled,
- int sessionId = AUDIO_SESSION_OUTPUT_MIX);
-
- virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0;
- virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0;
-
-
- mutable Mutex mLock;
-
- protected:
-
- // entry describing an effect being suspended in mSuspendedSessions keyed vector
- class SuspendedSessionDesc : public RefBase {
- public:
- SuspendedSessionDesc() : mRefCount(0) {}
-
- int mRefCount; // number of active suspend requests
- effect_uuid_t mType; // effect type UUID
- };
-
- void acquireWakeLock();
- void acquireWakeLock_l();
- void releaseWakeLock();
- void releaseWakeLock_l();
- void setEffectSuspended_l(const effect_uuid_t *type,
- bool suspend,
- int sessionId);
- // updated mSuspendedSessions when an effect suspended or restored
- void updateSuspendedSessions_l(const effect_uuid_t *type,
- bool suspend,
- int sessionId);
- // check if some effects must be suspended when an effect chain is added
- void checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain);
-
- virtual void preExit() { }
-
- friend class AudioFlinger; // for mEffectChains
-
- const type_t mType;
-
- // Used by parameters, config events, addTrack_l, exit
- Condition mWaitWorkCV;
-
- const sp<AudioFlinger> mAudioFlinger;
- uint32_t mSampleRate;
- size_t mFrameCount; // output HAL, direct output, record
- size_t mNormalFrameCount; // normal mixer and effects
- audio_channel_mask_t mChannelMask;
- uint16_t mChannelCount;
- size_t mFrameSize;
- audio_format_t mFormat;
-
- // Parameter sequence by client: binder thread calling setParameters():
- // 1. Lock mLock
- // 2. Append to mNewParameters
- // 3. mWaitWorkCV.signal
- // 4. mParamCond.waitRelative with timeout
- // 5. read mParamStatus
- // 6. mWaitWorkCV.signal
- // 7. Unlock
- //
- // Parameter sequence by server: threadLoop calling checkForNewParameters_l():
- // 1. Lock mLock
- // 2. If there is an entry in mNewParameters proceed ...
- // 2. Read first entry in mNewParameters
- // 3. Process
- // 4. Remove first entry from mNewParameters
- // 5. Set mParamStatus
- // 6. mParamCond.signal
- // 7. mWaitWorkCV.wait with timeout (this is to avoid overwriting mParamStatus)
- // 8. Unlock
- Condition mParamCond;
- Vector<String8> mNewParameters;
- status_t mParamStatus;
-
- Vector<ConfigEvent *> mConfigEvents;
-
- // These fields are written and read by thread itself without lock or barrier,
- // and read by other threads without lock or barrier via standby() , outDevice()
- // and inDevice().
- // Because of the absence of a lock or barrier, any other thread that reads
- // these fields must use the information in isolation, or be prepared to deal
- // with possibility that it might be inconsistent with other information.
- bool mStandby; // Whether thread is currently in standby.
- audio_devices_t mOutDevice; // output device
- audio_devices_t mInDevice; // input device
- audio_source_t mAudioSource; // (see audio.h, audio_source_t)
-
- const audio_io_handle_t mId;
- Vector< sp<EffectChain> > mEffectChains;
-
- static const int kNameLength = 16; // prctl(PR_SET_NAME) limit
- char mName[kNameLength];
- sp<IPowerManager> mPowerManager;
- sp<IBinder> mWakeLockToken;
- const sp<PMDeathRecipient> mDeathRecipient;
- // list of suspended effects per session and per type. The first vector is
- // keyed by session ID, the second by type UUID timeLow field
- KeyedVector< int, KeyedVector< int, sp<SuspendedSessionDesc> > > mSuspendedSessions;
- };
-
struct stream_type_t {
stream_type_t()
: volume(1.0f),
@@ -760,644 +389,10 @@ private:
};
// --- PlaybackThread ---
- class PlaybackThread : public ThreadBase {
- public:
-
- enum mixer_state {
- MIXER_IDLE, // no active tracks
- MIXER_TRACKS_ENABLED, // at least one active track, but no track has any data ready
- MIXER_TRACKS_READY // at least one active track, and at least one track has data
- // standby mode does not have an enum value
- // suspend by audio policy manager is orthogonal to mixer state
- };
-
- // playback track
- class Track : public TrackBase, public VolumeProvider {
- public:
- Track( PlaybackThread *thread,
- const sp<Client>& client,
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId,
- IAudioFlinger::track_flags_t flags);
- virtual ~Track();
-
- static void appendDumpHeader(String8& result);
- void dump(char* buffer, size_t size);
- virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
- int triggerSession = 0);
- virtual void stop();
- void pause();
-
- void flush();
- void destroy();
- void mute(bool);
- int name() const { return mName; }
-
- audio_stream_type_t streamType() const {
- return mStreamType;
- }
- status_t attachAuxEffect(int EffectId);
- void setAuxBuffer(int EffectId, int32_t *buffer);
- int32_t *auxBuffer() const { return mAuxBuffer; }
- void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
- int16_t *mainBuffer() const { return mMainBuffer; }
- int auxEffectId() const { return mAuxEffectId; }
-
- // implement FastMixerState::VolumeProvider interface
- virtual uint32_t getVolumeLR();
- virtual status_t setSyncEvent(const sp<SyncEvent>& event);
-
- protected:
- // for numerous
- friend class PlaybackThread;
- friend class MixerThread;
- friend class DirectOutputThread;
-
- Track(const Track&);
- Track& operator = (const Track&);
-
- // AudioBufferProvider interface
- virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts = kInvalidPTS);
- // releaseBuffer() not overridden
-
- virtual size_t framesReady() const;
-
- bool isMuted() const { return mMute; }
- bool isPausing() const {
- return mState == PAUSING;
- }
- bool isPaused() const {
- return mState == PAUSED;
- }
- bool isResuming() const {
- return mState == RESUMING;
- }
- bool isReady() const;
- void setPaused() { mState = PAUSED; }
- void reset();
-
- bool isOutputTrack() const {
- return (mStreamType == AUDIO_STREAM_CNT);
- }
-
- sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
-
- bool presentationComplete(size_t framesWritten, size_t audioHalFrames);
-
- public:
- void triggerEvents(AudioSystem::sync_event_t type);
- virtual bool isTimedTrack() const { return false; }
- bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
-
- protected:
-
- // written by Track::mute() called by binder thread(s), without a mutex or barrier.
- // read by Track::isMuted() called by playback thread, also without a mutex or barrier.
- // The lack of mutex or barrier is safe because the mute status is only used by itself.
- bool mMute;
-
- // FILLED state is used for suppressing volume ramp at begin of playing
- 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; // track name on the normal mixer,
- // allocated statically at track creation time,
- // and is even allocated (though unused) for fast tracks
- // FIXME don't allocate track name for fast tracks
- int16_t *mMainBuffer;
- int32_t *mAuxBuffer;
- int mAuxEffectId;
- bool mHasVolumeController;
- size_t mPresentationCompleteFrames; // number of frames written to the audio HAL
- // when this track will be fully rendered
- private:
- IAudioFlinger::track_flags_t mFlags;
-
- // The following fields are only for fast tracks, and should be in a subclass
- int mFastIndex; // index within FastMixerState::mFastTracks[];
- // either mFastIndex == -1 if not isFastTrack()
- // or 0 < mFastIndex < FastMixerState::kMaxFast because
- // index 0 is reserved for normal mixer's submix;
- // index is allocated statically at track creation time
- // but the slot is only used if track is active
- FastTrackUnderruns mObservedUnderruns; // Most recently observed value of
- // mFastMixerDumpState.mTracks[mFastIndex].mUnderruns
- uint32_t mUnderrunCount; // Counter of total number of underruns, never reset
- 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 {
- public:
- static sp<TimedTrack> create(PlaybackThread *thread,
- const sp<Client>& client,
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId);
- virtual ~TimedTrack();
-
- class TimedBuffer {
- public:
- TimedBuffer();
- TimedBuffer(const sp<IMemory>& buffer, int64_t pts);
- const sp<IMemory>& buffer() const { return mBuffer; }
- int64_t pts() const { return mPTS; }
- uint32_t position() const { return mPosition; }
- void setPosition(uint32_t pos) { mPosition = pos; }
- private:
- sp<IMemory> mBuffer;
- int64_t mPTS;
- uint32_t mPosition;
- };
-
- // Mixer facing methods.
- virtual bool isTimedTrack() const { return true; }
- virtual size_t framesReady() const;
-
- // AudioBufferProvider interface
- virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer,
- int64_t pts);
- virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
-
- // Client/App facing methods.
- status_t allocateTimedBuffer(size_t size,
- sp<IMemory>* buffer);
- status_t queueTimedBuffer(const sp<IMemory>& buffer,
- int64_t pts);
- status_t setMediaTimeTransform(const LinearTransform& xform,
- TimedAudioTrack::TargetTimeline target);
-
- private:
- TimedTrack(PlaybackThread *thread,
- const sp<Client>& client,
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId);
-
- void timedYieldSamples_l(AudioBufferProvider::Buffer* buffer);
- void timedYieldSilence_l(uint32_t numFrames,
- AudioBufferProvider::Buffer* buffer);
- void trimTimedBufferQueue_l();
- void trimTimedBufferQueueHead_l(const char* logTag);
- void updateFramesPendingAfterTrim_l(const TimedBuffer& buf,
- const char* logTag);
-
- uint64_t mLocalTimeFreq;
- LinearTransform mLocalTimeToSampleTransform;
- LinearTransform mMediaTimeToSampleTransform;
- sp<MemoryDealer> mTimedMemoryDealer;
-
- Vector<TimedBuffer> mTimedBufferQueue;
- bool mQueueHeadInFlight;
- bool mTrimQueueHeadOnRelease;
- uint32_t mFramesPendingInQueue;
-
- uint8_t* mTimedSilenceBuffer;
- uint32_t mTimedSilenceBufferSize;
- mutable Mutex mTimedBufferQueueLock;
- bool mTimedAudioOutputOnTime;
- CCHelper mCCHelper;
-
- Mutex mMediaTimeTransformLock;
- LinearTransform mMediaTimeTransform;
- bool mMediaTimeTransformValid;
- TimedAudioTrack::TargetTimeline mMediaTimeTransformTarget;
- };
-
-
- // playback track
- class OutputTrack : public Track {
- public:
-
- class Buffer: public AudioBufferProvider::Buffer {
- public:
- int16_t *mBuffer;
- };
-
- OutputTrack(PlaybackThread *thread,
- DuplicatingThread *sourceThread,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount);
- virtual ~OutputTrack();
-
- virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
- int triggerSession = 0);
- virtual void stop();
- bool write(int16_t* data, uint32_t frames);
- bool bufferQueueEmpty() const { return mBufferQueue.size() == 0; }
- bool isActive() const { return mActive; }
- const wp<ThreadBase>& thread() const { return mThread; }
-
- private:
-
- enum {
- NO_MORE_BUFFERS = 0x80000001, // same in AudioTrack.h, ok to be different value
- };
-
- status_t obtainBuffer(AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs);
- void clearBufferQueue();
-
- // Maximum number of pending buffers allocated by OutputTrack::write()
- static const uint8_t kMaxOverFlowBuffers = 10;
-
- Vector < Buffer* > mBufferQueue;
- AudioBufferProvider::Buffer mOutBuffer;
- bool mActive;
- DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
- }; // end of OutputTrack
-
- PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, audio_devices_t device, type_t type);
- virtual ~PlaybackThread();
-
- void dump(int fd, const Vector<String16>& args);
-
- // Thread virtuals
- virtual status_t readyToRun();
- virtual bool threadLoop();
-
- // RefBase
- virtual void onFirstRef();
-
-protected:
- // Code snippets that were lifted up out of threadLoop()
- virtual void threadLoop_mix() = 0;
- 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 returns
- // the pending set of tracks to remove via Vector 'tracksToRemove'. The caller
- // is responsible for clearing or destroying this Vector later on, when it
- // is safe to do so. That will drop the final ref count and destroy the tracks.
- virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) = 0;
+#include "Threads.h"
- // ThreadBase virtuals
- virtual void preExit();
-
-public:
-
- virtual status_t initCheck() const { return (mOutput == NULL) ? NO_INIT : NO_ERROR; }
-
- // return estimated latency in milliseconds, as reported by HAL
- uint32_t latency() const;
- // same, but lock must already be held
- uint32_t latency_l() const;
-
- void setMasterVolume(float value);
- void setMasterMute(bool muted);
-
- void setStreamVolume(audio_stream_type_t stream, float value);
- void setStreamMute(audio_stream_type_t stream, bool muted);
-
- float streamVolume(audio_stream_type_t stream) const;
-
- sp<Track> createTrack_l(
- const sp<AudioFlinger::Client>& client,
- audio_stream_type_t streamType,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- const sp<IMemory>& sharedBuffer,
- int sessionId,
- IAudioFlinger::track_flags_t flags,
- pid_t tid,
- status_t *status);
-
- AudioStreamOut* getOutput() const;
- AudioStreamOut* clearOutput();
- virtual audio_stream_t* stream() const;
-
- // a very large number of suspend() will eventually wraparound, but unlikely
- void suspend() { (void) android_atomic_inc(&mSuspended); }
- void restore()
- {
- // if restore() is done without suspend(), get back into
- // range so that the next suspend() will operate correctly
- if (android_atomic_dec(&mSuspended) <= 0) {
- android_atomic_release_store(0, &mSuspended);
- }
- }
- bool isSuspended() const
- { return android_atomic_acquire_load(&mSuspended) > 0; }
-
- virtual String8 getParameters(const String8& keys);
- virtual void audioConfigChanged_l(int event, int param = 0);
- status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
- int16_t *mixBuffer() const { return mMixBuffer; };
-
- virtual 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);
-
- virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
- virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
- virtual uint32_t hasAudioSession(int sessionId) const;
- virtual uint32_t getStrategyForSession_l(int sessionId);
-
-
- virtual status_t setSyncEvent(const sp<SyncEvent>& event);
- virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
- void invalidateTracks(audio_stream_type_t streamType);
-
-
- protected:
- int16_t* mMixBuffer;
-
- // suspend count, > 0 means suspended. While suspended, the thread continues to pull from
- // tracks and mix, but doesn't write to HAL. A2DP and SCO HAL implementations can't handle
- // concurrent use of both of them, so Audio Policy Service suspends one of the threads to
- // workaround that restriction.
- // 'volatile' means accessed via atomic operations and no lock.
- volatile int32_t mSuspended;
-
- int mBytesWritten;
- private:
- // mMasterMute is in both PlaybackThread and in AudioFlinger. When a
- // PlaybackThread needs to find out if master-muted, it checks it's local
- // copy rather than the one in AudioFlinger. This optimization saves a lock.
- bool mMasterMute;
- void setMasterMute_l(bool muted) { mMasterMute = muted; }
- protected:
- SortedVector< wp<Track> > mActiveTracks; // FIXME check if this could be sp<>
-
- // Allocate a track name for a given channel mask.
- // Returns name >= 0 if successful, -1 on failure.
- virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0;
- virtual void deleteTrackName_l(int name) = 0;
-
- // Time to sleep between cycles when:
- virtual uint32_t activeSleepTimeUs() const; // mixer state MIXER_TRACKS_ENABLED
- virtual uint32_t idleSleepTimeUs() const = 0; // mixer state MIXER_IDLE
- virtual uint32_t suspendSleepTimeUs() const = 0; // audio policy manager suspended us
- // No sleep when mixer state == MIXER_TRACKS_READY; relies on audio HAL stream->write()
- // No sleep in standby mode; waits on a condition
-
- // Code snippets that are temporarily lifted up out of threadLoop() until the merge
- void checkSilentMode_l();
-
- // Non-trivial for DUPLICATING only
- virtual void saveOutputTracks() { }
- virtual void clearOutputTracks() { }
-
- // Cache various calculated values, at threadLoop() entry and after a parameter change
- virtual void cacheParameters_l();
-
- virtual uint32_t correctLatency(uint32_t latency) const;
-
- private:
-
- friend class AudioFlinger; // for numerous
-
- PlaybackThread(const Client&);
- PlaybackThread& operator = (const PlaybackThread&);
-
- status_t addTrack_l(const sp<Track>& track);
- void destroyTrack_l(const sp<Track>& track);
- void removeTrack_l(const sp<Track>& track);
-
- void readOutputParameters();
-
- virtual void dumpInternals(int fd, const Vector<String16>& args);
- void dumpTracks(int fd, const Vector<String16>& args);
-
- SortedVector< sp<Track> > mTracks;
- // mStreamTypes[] uses 1 additional stream type internally for the OutputTrack used by DuplicatingThread
- stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1];
- AudioStreamOut *mOutput;
-
- float mMasterVolume;
- nsecs_t mLastWriteTime;
- int mNumWrites;
- int mNumDelayedWrites;
- bool mInWrite;
-
- // FIXME rename these former local variables of threadLoop to standard "m" names
- nsecs_t standbyTime;
- size_t mixBufferSize;
-
- // cached copies of activeSleepTimeUs() and idleSleepTimeUs() made by cacheParameters_l()
- uint32_t activeSleepTime;
- uint32_t idleSleepTime;
-
- uint32_t sleepTime;
-
- // mixer status returned by prepareTracks_l()
- mixer_state mMixerStatus; // current cycle
- // previous cycle when in prepareTracks_l()
- mixer_state mMixerStatusIgnoringFastTracks;
- // FIXME or a separate ready state per track
-
- // FIXME move these declarations into the specific sub-class that needs them
- // MIXER only
- uint32_t sleepTimeShift;
-
- // same as AudioFlinger::mStandbyTimeInNsecs except for DIRECT which uses a shorter value
- nsecs_t standbyDelay;
-
- // MIXER only
- nsecs_t maxPeriod;
-
- // 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;
- // For dumpsys
- sp<NBAIO_Sink> mTeeSink;
- sp<NBAIO_Source> mTeeSource;
- uint32_t mScreenState; // cached copy of gScreenState
- public:
- virtual bool hasFastMixer() const = 0;
- virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const
- { FastTrackUnderruns dummy; return dummy; }
-
- protected:
- // accessed by both binder threads and within threadLoop(), lock on mutex needed
- unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available
-
- };
-
- class MixerThread : public PlaybackThread {
- public:
- MixerThread (const sp<AudioFlinger>& audioFlinger,
- AudioStreamOut* output,
- audio_io_handle_t id,
- audio_devices_t device,
- type_t type = MIXER);
- virtual ~MixerThread();
-
- // Thread virtuals
-
- virtual bool checkForNewParameters_l();
- virtual void dumpInternals(int fd, const Vector<String16>& args);
-
- protected:
- virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
- virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
- virtual void deleteTrackName_l(int name);
- virtual uint32_t idleSleepTimeUs() const;
- virtual uint32_t suspendSleepTimeUs() const;
- 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);
- virtual uint32_t correctLatency(uint32_t latency) const;
-
- AudioMixer* mAudioMixer; // normal mixer
- private:
- // one-time initialization, no locks required
- FastMixer* mFastMixer; // non-NULL if there is also a fast mixer
- sp<AudioWatchdog> mAudioWatchdog; // non-0 if there is an audio watchdog thread
-
- // contents are not guaranteed to be consistent, no locks required
- FastMixerDumpState mFastMixerDumpState;
-#ifdef STATE_QUEUE_DUMP
- StateQueueObserverDump mStateQueueObserverDump;
- StateQueueMutatorDump mStateQueueMutatorDump;
-#endif
- AudioWatchdogDump mAudioWatchdogDump;
-
- // 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; }
- virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const {
- ALOG_ASSERT(fastIndex < FastMixerState::kMaxFastTracks);
- return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
- }
- };
-
- class DirectOutputThread : public PlaybackThread {
- public:
-
- DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
- audio_io_handle_t id, audio_devices_t device);
- virtual ~DirectOutputThread();
-
- // Thread virtuals
-
- virtual bool checkForNewParameters_l();
-
- protected:
- virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
- virtual void deleteTrackName_l(int name);
- virtual uint32_t activeSleepTimeUs() const;
- virtual uint32_t idleSleepTimeUs() const;
- virtual uint32_t suspendSleepTimeUs() const;
- virtual void cacheParameters_l();
-
- // threadLoop snippets
- virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
- virtual void threadLoop_mix();
- virtual void threadLoop_sleepTime();
-
- // volumes last sent to audio HAL with stream->set_volume()
- float mLeftVolFloat;
- float mRightVolFloat;
-
-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 {
- public:
- DuplicatingThread (const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread,
- audio_io_handle_t id);
- virtual ~DuplicatingThread();
-
- // Thread virtuals
- void addOutputTrack(MixerThread* thread);
- void removeOutputTrack(MixerThread* thread);
- uint32_t waitTimeMs() const { return mWaitTimeMs; }
- protected:
- virtual uint32_t activeSleepTimeUs() const;
-
- private:
- bool outputsReady(const SortedVector< sp<OutputTrack> > &outputTracks);
- protected:
- // threadLoop snippets
- virtual void threadLoop_mix();
- virtual void threadLoop_sleepTime();
- virtual void threadLoop_write();
- virtual void threadLoop_standby();
- virtual void cacheParameters_l();
-
- private:
- // called from threadLoop, addOutputTrack, removeOutputTrack
- virtual void updateWaitTime_l();
- protected:
- virtual void saveOutputTracks();
- virtual void clearOutputTracks();
- private:
-
- 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;
- MixerThread *checkMixerThread_l(audio_io_handle_t output) const;
- RecordThread *checkRecordThread_l(audio_io_handle_t input) const;
- // no range check, AudioFlinger::mLock held
- bool streamMute_l(audio_stream_type_t stream) const
- { return mStreamTypes[stream].mute; }
- // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
- float streamVolume_l(audio_stream_type_t stream) const
- { return mStreamTypes[stream].volume; }
- void audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2);
-
- // allocate an audio_io_handle_t, session ID, or effect ID
- uint32_t nextUniqueId();
-
- status_t moveEffectChain_l(int sessionId,
- PlaybackThread *srcThread,
- PlaybackThread *dstThread,
- bool reRegister);
- // return thread associated with primary hardware device, or NULL
- PlaybackThread *primaryPlaybackThread_l() const;
- audio_devices_t primaryOutputDevice_l() const;
-
- sp<PlaybackThread> getEffectThread_l(int sessionId, int EffectId);
+#include "Effects.h"
// server side of the client's IAudioTrack
class TrackHandle : public android::BnAudioTrack {
@@ -1408,7 +403,6 @@ private:
virtual status_t start();
virtual void stop();
virtual void flush();
- virtual void mute(bool);
virtual void pause();
virtual status_t attachAuxEffect(int effectId);
virtual status_t allocateTimedBuffer(size_t size,
@@ -1417,161 +411,15 @@ private:
int64_t pts);
virtual status_t setMediaTimeTransform(const LinearTransform& xform,
int target);
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ virtual void signal(); // signal playback thread for a change in control block
+
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
- private:
- const sp<PlaybackThread::Track> mTrack;
- };
-
- void removeClient_l(pid_t pid);
- void removeNotificationClient(pid_t pid);
-
-
- // record thread
- class RecordThread : public ThreadBase, public AudioBufferProvider
- // derives from AudioBufferProvider interface for use by resampler
- {
- public:
-
- // record track
- class RecordTrack : public TrackBase {
- public:
- RecordTrack(RecordThread *thread,
- const sp<Client>& client,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- int sessionId);
- virtual ~RecordTrack();
-
- virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
- virtual void stop();
-
- void destroy();
-
- // clear the buffer overflow flag
- void clearOverflow() { mOverflow = false; }
- // set the buffer overflow flag and return previous value
- bool setOverflow() { bool tmp = mOverflow; mOverflow = true; return tmp; }
-
- static void appendDumpHeader(String8& result);
- void dump(char* buffer, size_t size);
-
- private:
- friend class AudioFlinger; // for mState
-
- RecordTrack(const RecordTrack&);
- RecordTrack& operator = (const RecordTrack&);
-
- // AudioBufferProvider interface
- virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts = kInvalidPTS);
- // releaseBuffer() not overridden
-
- bool mOverflow; // overflow on most recent attempt to fill client buffer
- };
-
- RecordThread(const sp<AudioFlinger>& audioFlinger,
- AudioStreamIn *input,
- uint32_t sampleRate,
- audio_channel_mask_t channelMask,
- audio_io_handle_t id,
- audio_devices_t device);
- virtual ~RecordThread();
-
- // no addTrack_l ?
- void destroyTrack_l(const sp<RecordTrack>& track);
- void removeTrack_l(const sp<RecordTrack>& track);
-
- void dumpInternals(int fd, const Vector<String16>& args);
- void dumpTracks(int fd, const Vector<String16>& args);
-
- // Thread virtuals
- virtual bool threadLoop();
- virtual status_t readyToRun();
-
- // RefBase
- virtual void onFirstRef();
-
- virtual status_t initCheck() const { return (mInput == NULL) ? NO_INIT : NO_ERROR; }
- sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l(
- const sp<AudioFlinger::Client>& client,
- uint32_t sampleRate,
- audio_format_t format,
- audio_channel_mask_t channelMask,
- int frameCount,
- int sessionId,
- IAudioFlinger::track_flags_t flags,
- pid_t tid,
- status_t *status);
-
- status_t start(RecordTrack* recordTrack,
- AudioSystem::sync_event_t event,
- int triggerSession);
-
- // ask the thread to stop the specified track, and
- // return true if the caller should then do it's part of the stopping process
- bool stop_l(RecordTrack* recordTrack);
-
- void dump(int fd, const Vector<String16>& args);
- AudioStreamIn* clearInput();
- virtual audio_stream_t* stream() const;
-
- // AudioBufferProvider interface
- virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts);
- virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
-
- virtual bool checkForNewParameters_l();
- virtual String8 getParameters(const String8& keys);
- virtual void audioConfigChanged_l(int event, int param = 0);
- void readInputParameters();
- virtual unsigned int getInputFramesLost();
-
- virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
- virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
- virtual uint32_t hasAudioSession(int sessionId) const;
-
- // Return the set of unique session IDs across all tracks.
- // The keys are the session IDs, and the associated values are meaningless.
- // FIXME replace by Set [and implement Bag/Multiset for other uses].
- KeyedVector<int, bool> sessionIds() const;
-
- virtual status_t setSyncEvent(const sp<SyncEvent>& event);
- virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
-
- static void syncStartEventCallback(const wp<SyncEvent>& event);
- void handleSyncStartEvent(const sp<SyncEvent>& event);
private:
- void clearSyncStartEvent();
-
- // Enter standby if not already in standby, and set mStandby flag
- void standby();
-
- // Call the HAL standby method unconditionally, and don't change mStandby flag
- void inputStandBy();
-
- AudioStreamIn *mInput;
- SortedVector < sp<RecordTrack> > mTracks;
- // mActiveTrack has dual roles: it indicates the current active track, and
- // is used together with mStartStopCond to indicate start()/stop() progress
- sp<RecordTrack> mActiveTrack;
- Condition mStartStopCond;
- AudioResampler *mResampler;
- int32_t *mRsmpOutBuffer;
- int16_t *mRsmpInBuffer;
- size_t mRsmpInIndex;
- size_t mInputBytes;
- const int mReqChannelCount;
- const uint32_t mReqSampleRate;
- ssize_t mBytesRead;
- // sync event triggering actual audio capture. Frames read before this event will
- // be dropped and therefore not read by the application.
- sp<SyncEvent> mSyncStartEvent;
- // number of captured frames to drop after the start sync event has been received.
- // when < 0, maximum frames to drop before starting capture even if sync event is
- // not received
- ssize_t mFramestoDrop;
+ const sp<PlaybackThread::Track> mTrack;
};
// server side of the client's IAudioRecord
@@ -1591,343 +439,37 @@ private:
void stop_nonvirtual();
};
- //--- 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(ThreadBase *thread,
- const wp<AudioFlinger::EffectChain>& chain,
- effect_descriptor_t *desc,
- int id,
- int sessionId);
- virtual ~EffectModule();
-
- enum effect_state {
- IDLE,
- RESTART,
- STARTING,
- ACTIVE,
- STOPPING,
- STOPPED,
- DESTROYED
- };
- int id() const { return mId; }
- void process();
- void updateState();
- status_t command(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData);
-
- void reset_l();
- status_t configure();
- status_t init();
- effect_state state() const {
- return mState;
- }
- uint32_t status() {
- return mStatus;
- }
- int sessionId() const {
- return mSessionId;
- }
- status_t setEnabled(bool enabled);
- status_t setEnabled_l(bool enabled);
- bool isEnabled() const;
- bool isProcessEnabled() const;
-
- 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; }
- void setChain(const wp<EffectChain>& chain) { mChain = chain; }
- void setThread(const wp<ThreadBase>& thread) { mThread = thread; }
- const wp<ThreadBase>& thread() { return mThread; }
-
- status_t addHandle(EffectHandle *handle);
- size_t disconnect(EffectHandle *handle, bool unpinIfLast);
- size_t removeHandle(EffectHandle *handle);
-
- const effect_descriptor_t& desc() const { return mDescriptor; }
- wp<EffectChain>& chain() { return mChain; }
-
- status_t setDevice(audio_devices_t device);
- status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
- status_t setMode(audio_mode_t mode);
- status_t setAudioSource(audio_source_t source);
- status_t start();
- status_t stop();
- void setSuspended(bool suspended);
- bool suspended() const;
-
- EffectHandle* controlHandle_l();
-
- bool isPinned() const { return mPinned; }
- void unPin() { mPinned = false; }
- bool purgeHandles();
- void lock() { mLock.lock(); }
- void unlock() { mLock.unlock(); }
-
- void dump(int fd, const Vector<String16>& args);
-
- protected:
- friend class AudioFlinger; // for mHandles
- bool mPinned;
-
- // Maximum time allocated to effect engines to complete the turn off sequence
- static const uint32_t MAX_DISABLE_TIME_MS = 10000;
-
- EffectModule(const EffectModule&);
- EffectModule& operator = (const EffectModule&);
-
- status_t start_l();
- status_t stop_l();
-
-mutable Mutex mLock; // mutex for process, commands and handles list protection
- wp<ThreadBase> mThread; // parent thread
- wp<EffectChain> mChain; // parent effect chain
- const int mId; // this instance unique ID
- const int mSessionId; // audio session ID
- const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
- effect_config_t mConfig; // input and output audio configuration
- effect_handle_t mEffectInterface; // Effect module C API
- status_t mStatus; // initialization status
- effect_state mState; // current activation state
- Vector<EffectHandle *> mHandles; // list of client handles
- // First handle in mHandles has highest priority and controls the effect module
- uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after
- // sending disable command.
- uint32_t mDisableWaitCnt; // current process() calls count during disable period.
- bool mSuspended; // effect is suspended: temporarily disabled by framework
- };
-
- // 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(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t *replySize,
- void *pReplyData);
- virtual void disconnect();
- private:
- void disconnect(bool unpinIfLast);
- public:
- virtual sp<IMemory> getCblk() const { return mCblkMemory; }
- virtual status_t onTransact(uint32_t code, const Parcel& data,
- Parcel* reply, uint32_t flags);
-
-
- // Give or take control of effect module
- // - hasControl: true if control is given, false if removed
- // - signal: true client app should be signaled of change, false otherwise
- // - enabled: state of the effect when control is passed
- void setControl(bool hasControl, bool signal, bool enabled);
- void commandExecuted(uint32_t cmdCode,
- uint32_t cmdSize,
- void *pCmdData,
- uint32_t replySize,
- void *pReplyData);
- void setEnabled(bool enabled);
- bool enabled() const { return mEnabled; }
-
- // Getters
- int id() const { return mEffect->id(); }
- int priority() const { return mPriority; }
- bool hasControl() const { return mHasControl; }
- sp<EffectModule> effect() const { return mEffect; }
- // destroyed_l() must be called with the associated EffectModule mLock held
- bool destroyed_l() const { return mDestroyed; }
-
- void dump(char* buffer, size_t size);
-
- protected:
- friend class AudioFlinger; // for mEffect, mHasControl, mEnabled
- EffectHandle(const EffectHandle&);
- EffectHandle& operator =(const EffectHandle&);
-
- sp<EffectModule> mEffect; // pointer to controlled EffectModule
- sp<IEffectClient> mEffectClient; // callback interface for client notifications
- /*const*/ sp<Client> mClient; // client for shared memory allocation, see disconnect()
- sp<IMemory> mCblkMemory; // shared memory for control block
- effect_param_cblk_t* mCblk; // control block for deferred parameter setting via shared memory
- uint8_t* mBuffer; // pointer to parameter area in shared memory
- int mPriority; // client application priority to control the effect
- bool mHasControl; // true if this handle is controlling the effect
- bool mEnabled; // cached enable state: needed when the effect is
- // restored after being suspended
- bool mDestroyed; // Set to true by destructor. Access with EffectModule
- // mLock held
- };
-
- // 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(ThreadBase *thread, int sessionId);
- virtual ~EffectChain();
-
- // special key used for an entry in mSuspendedEffects keyed vector
- // corresponding to a suspend all request.
- static const int kKeyForSuspendAll = 0;
-
- // minimum duration during which we force calling effect process when last track on
- // a session is stopped or removed to allow effect tail to be rendered
- static const int kProcessTailDurationMs = 1000;
-
- void process_l();
-
- void lock() {
- mLock.lock();
- }
- void unlock() {
- mLock.unlock();
- }
-
- status_t addEffect_l(const sp<EffectModule>& handle);
- size_t removeEffect_l(const sp<EffectModule>& handle);
-
- int sessionId() const { return mSessionId; }
- void setSessionId(int sessionId) { mSessionId = sessionId; }
-
- sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor);
- sp<EffectModule> getEffectFromId_l(int id);
- sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);
- bool setVolume_l(uint32_t *left, uint32_t *right);
- void setDevice_l(audio_devices_t device);
- void setMode_l(audio_mode_t mode);
- void setAudioSource_l(audio_source_t source);
-
- void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
- mInBuffer = buffer;
- mOwnInBuffer = ownsBuffer;
- }
- int16_t *inBuffer() const {
- return mInBuffer;
- }
- void setOutBuffer(int16_t *buffer) {
- mOutBuffer = buffer;
- }
- int16_t *outBuffer() const {
- return mOutBuffer;
- }
-
- void incTrackCnt() { android_atomic_inc(&mTrackCnt); }
- void decTrackCnt() { android_atomic_dec(&mTrackCnt); }
- int32_t trackCnt() const { return android_atomic_acquire_load(&mTrackCnt); }
-
- void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt);
- mTailBufferCount = mMaxTailBuffers; }
- void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); }
- int32_t activeTrackCnt() const { return android_atomic_acquire_load(&mActiveTrackCnt); }
-
- uint32_t strategy() const { return mStrategy; }
- void setStrategy(uint32_t strategy)
- { mStrategy = strategy; }
-
- // suspend effect of the given type
- void setEffectSuspended_l(const effect_uuid_t *type,
- bool suspend);
- // suspend all eligible effects
- void setEffectSuspendedAll_l(bool suspend);
- // check if effects should be suspend or restored when a given effect is enable or disabled
- void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
- bool enabled);
+ PlaybackThread *checkPlaybackThread_l(audio_io_handle_t output) const;
+ MixerThread *checkMixerThread_l(audio_io_handle_t output) const;
+ RecordThread *checkRecordThread_l(audio_io_handle_t input) const;
+ // no range check, AudioFlinger::mLock held
+ bool streamMute_l(audio_stream_type_t stream) const
+ { return mStreamTypes[stream].mute; }
+ // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
+ float streamVolume_l(audio_stream_type_t stream) const
+ { return mStreamTypes[stream].volume; }
+ void audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2);
- void clearInputBuffer();
+ // allocate an audio_io_handle_t, session ID, or effect ID
+ uint32_t nextUniqueId();
- void dump(int fd, const Vector<String16>& args);
+ status_t moveEffectChain_l(int sessionId,
+ PlaybackThread *srcThread,
+ PlaybackThread *dstThread,
+ bool reRegister);
+ // return thread associated with primary hardware device, or NULL
+ PlaybackThread *primaryPlaybackThread_l() const;
+ audio_devices_t primaryOutputDevice_l() const;
- protected:
- friend class AudioFlinger; // for mThread, mEffects
- EffectChain(const EffectChain&);
- EffectChain& operator =(const EffectChain&);
+ sp<PlaybackThread> getEffectThread_l(int sessionId, int EffectId);
- class SuspendedEffectDesc : public RefBase {
- public:
- SuspendedEffectDesc() : mRefCount(0) {}
- int mRefCount;
- effect_uuid_t mType;
- wp<EffectModule> mEffect;
- };
+ void removeClient_l(pid_t pid);
+ void removeNotificationClient(pid_t pid);
- // get a list of effect modules to suspend when an effect of the type
- // passed is enabled.
- void getSuspendEligibleEffects(Vector< sp<EffectModule> > &effects);
-
- // get an effect module if it is currently enable
- sp<EffectModule> getEffectIfEnabled(const effect_uuid_t *type);
- // true if the effect whose descriptor is passed can be suspended
- // OEMs can modify the rules implemented in this method to exclude specific effect
- // types or implementations from the suspend/restore mechanism.
- bool isEffectEligibleForSuspend(const effect_descriptor_t& desc);
-
- void clearInputBuffer_l(sp<ThreadBase> thread);
-
- 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
-
- // 'volatile' here means these are accessed with atomic operations instead of mutex
- volatile int32_t mActiveTrackCnt; // number of active tracks connected
- volatile int32_t mTrackCnt; // number of tracks connected
-
- int32_t mTailBufferCount; // current effect tail buffer count
- int32_t mMaxTailBuffers; // maximum effect tail buffers
- bool mOwnInBuffer; // true if the chain owns its input buffer
- int mVolumeCtrlIdx; // index of insert effect having control over volume
- uint32_t mLeftVolume; // previous volume on left channel
- uint32_t mRightVolume; // previous volume on right channel
- uint32_t mNewLeftVolume; // new volume on left channel
- uint32_t mNewRightVolume; // new volume on right channel
- uint32_t mStrategy; // strategy for this effect chain
- // mSuspendedEffects lists all effects currently suspended in the chain.
- // Use effect type UUID timelow field as key. There is no real risk of identical
- // timeLow fields among effect type UUIDs.
- // Updated by updateSuspendedSessions_l() only.
- KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
- };
+ bool isNonOffloadableGlobalEffectEnabled_l();
+ void onNonOffloadableGlobalEffectEnable();
class AudioHwDevice {
public:
@@ -1967,11 +509,12 @@ mutable Mutex mLock; // mutex for process, commands and handl
struct AudioStreamOut {
AudioHwDevice* const audioHwDev;
audio_stream_out_t* const stream;
+ audio_output_flags_t flags;
audio_hw_device_t* hwDev() const { return audioHwDev->hwDevice(); }
- AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out) :
- audioHwDev(dev), stream(out) {}
+ AudioStreamOut(AudioHwDevice *dev, audio_stream_out_t *out, audio_output_flags_t flags) :
+ audioHwDev(dev), stream(out), flags(flags) {}
};
struct AudioStreamIn {
@@ -2064,8 +607,49 @@ private:
// for use from destructor
status_t closeOutput_nonvirtual(audio_io_handle_t output);
status_t closeInput_nonvirtual(audio_io_handle_t input);
+
+#ifdef TEE_SINK
+ // all record threads serially share a common tee sink, which is re-created on format change
+ sp<NBAIO_Sink> mRecordTeeSink;
+ sp<NBAIO_Source> mRecordTeeSource;
+#endif
+
+public:
+
+#ifdef TEE_SINK
+ // tee sink, if enabled by property, allows dumpsys to write most recent audio to .wav file
+ static void dumpTee(int fd, const sp<NBAIO_Source>& source, audio_io_handle_t id = 0);
+
+ // whether tee sink is enabled by property
+ static bool mTeeSinkInputEnabled;
+ static bool mTeeSinkOutputEnabled;
+ static bool mTeeSinkTrackEnabled;
+
+ // runtime configured size of each tee sink pipe, in frames
+ static size_t mTeeSinkInputFrames;
+ static size_t mTeeSinkOutputFrames;
+ static size_t mTeeSinkTrackFrames;
+
+ // compile-time default size of tee sink pipes, in frames
+ // 0x200000 stereo 16-bit PCM frames = 47.5 seconds at 44.1 kHz, 8 megabytes
+ static const size_t kTeeSinkInputFramesDefault = 0x200000;
+ static const size_t kTeeSinkOutputFramesDefault = 0x200000;
+ static const size_t kTeeSinkTrackFramesDefault = 0x1000;
+#endif
+
+ // This method reads from a variable without mLock, but the variable is updated under mLock. So
+ // we might read a stale value, or a value that's inconsistent with respect to other variables.
+ // In this case, it's safe because the return value isn't used for making an important decision.
+ // The reason we don't want to take mLock is because it could block the caller for a long time.
+ bool isLowRamDevice() const { return mIsLowRamDevice; }
+
+private:
+ bool mIsLowRamDevice;
+ bool mIsDeviceTypeKnown;
+ nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled
};
+#undef INCLUDING_FROM_AUDIOFLINGER_H
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index af169d5..f92421e 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -18,6 +18,7 @@
#define LOG_TAG "AudioMixer"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
@@ -106,14 +107,23 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr
ALOG_ASSERT(maxNumTracks <= MAX_NUM_TRACKS, "maxNumTracks %u > MAX_NUM_TRACKS %u",
maxNumTracks, MAX_NUM_TRACKS);
+ // AudioMixer is not yet capable of more than 32 active track inputs
+ ALOG_ASSERT(32 >= MAX_NUM_TRACKS, "bad MAX_NUM_TRACKS %d", MAX_NUM_TRACKS);
+
+ // AudioMixer is not yet capable of multi-channel output beyond stereo
+ ALOG_ASSERT(2 == MAX_NUM_CHANNELS, "bad MAX_NUM_CHANNELS %d", MAX_NUM_CHANNELS);
+
LocalClock lc;
+ pthread_once(&sOnceControl, &sInitRoutine);
+
mState.enabledTracks= 0;
mState.needsChanged = 0;
mState.frameCount = frameCount;
mState.hook = process__nop;
mState.outputTemp = NULL;
mState.resampleTemp = NULL;
+ mState.mLog = &mDummyLog;
// mState.reserved
// FIXME Most of the following initialization is probably redundant since
@@ -121,8 +131,6 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate, uint32_t maxNumTr
// and mTrackNames is initially 0. However, leave it here until that's verified.
track_t* t = mState.tracks;
for (unsigned i=0 ; i < MAX_NUM_TRACKS ; i++) {
- // FIXME redundant per track
- t->localTimeFreq = lc.getLocalFreq();
t->resampler = NULL;
t->downmixerBufferProvider = NULL;
t++;
@@ -163,6 +171,11 @@ AudioMixer::~AudioMixer()
delete [] mState.resampleTemp;
}
+void AudioMixer::setLog(NBLog::Writer *log)
+{
+ mState.mLog = log;
+}
+
int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
{
uint32_t names = (~mTrackNames) & mConfiguredNames;
@@ -192,7 +205,6 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
t->sessionId = sessionId;
// setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
t->bufferProvider = NULL;
- t->downmixerBufferProvider = NULL;
t->buffer.raw = NULL;
// no initialization needed
// t->buffer.frameCount
@@ -203,7 +215,7 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, int sessionId)
// setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name)
t->mainBuffer = NULL;
t->auxBuffer = NULL;
- // see t->localTimeFreq in constructor above
+ t->downmixerBufferProvider = NULL;
status_t status = initTrackDownmix(&mState.tracks[n], n, channelMask);
if (status == OK) {
@@ -409,15 +421,16 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
ALOG_ASSERT(uint32_t(name) < MAX_NUM_TRACKS, "bad track name %d", name);
track_t& track = mState.tracks[name];
- int valueInt = (int)value;
- int32_t *valueBuf = (int32_t *)value;
+ int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
+ int32_t *valueBuf = reinterpret_cast<int32_t*>(value);
switch (target) {
case TRACK:
switch (param) {
case CHANNEL_MASK: {
- audio_channel_mask_t mask = (audio_channel_mask_t) value;
+ audio_channel_mask_t mask =
+ static_cast<audio_channel_mask_t>(reinterpret_cast<uintptr_t>(value));
if (track.channelMask != mask) {
uint32_t channelCount = popcount(mask);
ALOG_ASSERT((channelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && channelCount);
@@ -556,7 +569,7 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
// the resampler sees the number of channels after the downmixer, if any
downmixerBufferProvider != NULL ? MAX_NUM_CHANNELS : channelCount,
devSampleRate, quality);
- resampler->setLocalTimeFreq(localTimeFreq);
+ resampler->setLocalTimeFreq(sLocalTimeFreq);
}
return true;
}
@@ -615,7 +628,6 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider
}
-
void AudioMixer::process(int64_t pts)
{
mState.hook(&mState, pts);
@@ -760,7 +772,8 @@ void AudioMixer::process__validate(state_t* state, int64_t pts)
}
-void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
+void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount,
+ int32_t* temp, int32_t* aux)
{
t->resampler->setSampleRate(t->sampleRate);
@@ -793,11 +806,13 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
}
}
-void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
+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, int32_t* aux)
+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];
@@ -839,7 +854,8 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i
t->adjustVolumeRamp(aux != NULL);
}
-void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
+ int32_t* aux)
{
const int16_t vl = t->volume[0];
const int16_t vr = t->volume[1];
@@ -867,7 +883,8 @@ void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32
}
}
-void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
+ int32_t* aux)
{
const int16_t *in = static_cast<const int16_t *>(t->in);
@@ -957,7 +974,8 @@ void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount
t->in = in;
}
-void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
+void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp,
+ int32_t* aux)
{
const int16_t *in = static_cast<int16_t const *>(t->in);
@@ -1053,33 +1071,37 @@ void AudioMixer::process__nop(state_t* state, int64_t pts)
// 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);
+ {
+ track_t& t1 = state->tracks[i];
e2 &= ~(1<<i);
- track_t& t2 = state->tracks[i];
- if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) {
- e1 &= ~(1<<i);
+ while (e2) {
+ i = 31 - __builtin_clz(e2);
+ e2 &= ~(1<<i);
+ track_t& t2 = state->tracks[i];
+ if (CC_UNLIKELY(t2.mainBuffer != t1.mainBuffer)) {
+ e1 &= ~(1<<i);
+ }
}
- }
- e0 &= ~(e1);
+ e0 &= ~(e1);
- memset(t1.mainBuffer, 0, bufSize);
+ 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;
- int64_t outputPTS = calculateOutputPTS(
- t1, pts, state->frameCount - outFrames);
- t1.bufferProvider->getNextBuffer(&t1.buffer, outputPTS);
- if (t1.buffer.raw == NULL) break;
- outFrames -= t1.buffer.frameCount;
- t1.bufferProvider->releaseBuffer(&t1.buffer);
+ {
+ track_t& t3 = state->tracks[i];
+ size_t outFrames = state->frameCount;
+ while (outFrames) {
+ t3.buffer.frameCount = outFrames;
+ int64_t outputPTS = calculateOutputPTS(
+ t3, pts, state->frameCount - outFrames);
+ t3.bufferProvider->getNextBuffer(&t3.buffer, outputPTS);
+ if (t3.buffer.raw == NULL) break;
+ outFrames -= t3.buffer.frameCount;
+ t3.bufferProvider->releaseBuffer(&t3.buffer);
+ }
}
}
}
@@ -1101,10 +1123,6 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
t.bufferProvider->getNextBuffer(&t.buffer, pts);
t.frameCount = t.buffer.frameCount;
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)
- enabledTracks &= ~(1<<i);
}
e0 = enabledTracks;
@@ -1140,9 +1158,17 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
aux = t.auxBuffer + numFrames;
}
while (outFrames) {
+ // t.in == NULL can happen if the track was flushed just after having
+ // been enabled for mixing.
+ if (t.in == NULL) {
+ enabledTracks &= ~(1<<i);
+ e1 &= ~(1<<i);
+ break;
+ }
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.hook(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames,
+ state->resampleTemp, aux);
t.frameCount -= inFrames;
outFrames -= inFrames;
if (CC_UNLIKELY(aux != NULL)) {
@@ -1151,7 +1177,8 @@ void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts)
}
if (t.frameCount == 0 && outFrames) {
t.bufferProvider->releaseBuffer(&t.buffer);
- t.buffer.frameCount = (state->frameCount - numFrames) - (BLOCKSIZE - outFrames);
+ t.buffer.frameCount = (state->frameCount - numFrames) -
+ (BLOCKSIZE - outFrames);
int64_t outputPTS = calculateOutputPTS(
t, pts, numFrames + (BLOCKSIZE - outFrames));
t.bufferProvider->getNextBuffer(&t.buffer, outputPTS);
@@ -1241,7 +1268,8 @@ void AudioMixer::process__genericResampling(state_t* state, int64_t pts)
if (CC_UNLIKELY(aux != NULL)) {
aux += outFrames;
}
- t.hook(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux);
+ t.hook(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount,
+ state->resampleTemp, aux);
outFrames += t.buffer.frameCount;
t.bufferProvider->releaseBuffer(&t.buffer);
}
@@ -1281,7 +1309,8 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state,
// been enabled for mixing.
if (in == NULL || ((unsigned long)in & 3)) {
memset(out, 0, numFrames*MAX_NUM_CHANNELS*sizeof(int16_t));
- ALOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: buffer %p track %d, channels %d, needs %08x",
+ ALOGE_IF(((unsigned long)in & 3), "process stereo track: input buffer alignment pb: "
+ "buffer %p track %d, channels %d, needs %08x",
in, i, t.channelCount, t.needs);
return;
}
@@ -1423,7 +1452,16 @@ int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS,
if (AudioBufferProvider::kInvalidPTS == basePTS)
return AudioBufferProvider::kInvalidPTS;
- return basePTS + ((outputFrameIndex * t.localTimeFreq) / t.sampleRate);
+ return basePTS + ((outputFrameIndex * sLocalTimeFreq) / t.sampleRate);
+}
+
+/*static*/ uint64_t AudioMixer::sLocalTimeFreq;
+/*static*/ pthread_once_t AudioMixer::sOnceControl = PTHREAD_ONCE_INIT;
+
+/*static*/ void AudioMixer::sInitRoutine()
+{
+ LocalClock lc;
+ sLocalTimeFreq = lc.getLocalFreq();
}
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index 6333357..43aeb86 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -28,6 +28,7 @@
#include <audio_effects/effect_downmix.h>
#include <system/audio.h>
+#include <media/nbaio/NBLog.h>
namespace android {
@@ -41,8 +42,15 @@ public:
/*virtual*/ ~AudioMixer(); // non-virtual saves a v-table, restore if sub-classed
+
+ // This mixer has a hard-coded upper limit of 32 active track inputs.
+ // Adding support for > 32 tracks would require more than simply changing this value.
static const uint32_t MAX_NUM_TRACKS = 32;
// maximum number of channels supported by the mixer
+
+ // This mixer has a hard-coded upper limit of 2 channels for output.
+ // There is support for > 2 channel tracks down-mixed to 2 channel output via a down-mix effect.
+ // Adding support for > 2 channel output would require more than simply changing this value.
static const uint32_t MAX_NUM_CHANNELS = 2;
// maximum number of channels supported for the content
static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = 8;
@@ -139,7 +147,8 @@ private:
struct track_t;
class DownmixerBufferProvider;
- typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
+ 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 {
@@ -188,12 +197,12 @@ private:
// 16-byte boundary
- uint64_t localTimeFreq;
-
DownmixerBufferProvider* downmixerBufferProvider; // 4 bytes
int32_t sessionId;
+ int32_t padding[2];
+
// 16-byte boundary
bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
@@ -212,7 +221,8 @@ private:
void (*hook)(state_t* state, int64_t pts); // one of process__*, never NULL
int32_t *outputTemp;
int32_t *resampleTemp;
- int32_t reserved[2];
+ NBLog::Writer* mLog;
+ int32_t reserved[1];
// FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
track_t tracks[MAX_NUM_TRACKS]; __attribute__((aligned(32)));
};
@@ -239,6 +249,10 @@ private:
const uint32_t mSampleRate;
+ NBLog::Writer mDummyLog;
+public:
+ void setLog(NBLog::Writer* log);
+private:
state_t mState __attribute__((aligned(32)));
// effect descriptor for the downmixer used by the mixer
@@ -254,12 +268,17 @@ private:
static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum);
static void unprepareTrackForDownmix(track_t* pTrack, int trackName);
- static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
+ 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 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, int64_t pts);
static void process__nop(state_t* state, int64_t pts);
@@ -274,6 +293,10 @@ private:
static int64_t calculateOutputPTS(const track_t& t, int64_t basePTS,
int outputFrameIndex);
+
+ static uint64_t sLocalTimeFreq;
+ static pthread_once_t sOnceControl;
+ static void sInitRoutine();
};
// ----------------------------------------------------------------------------
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 8b99bd2..646a317 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "AudioPolicyService"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#undef __STRICT_ANSI__
#define __STDINT_LIMITS
#define __STDC_LIMIT_MACROS
@@ -40,6 +41,7 @@
#include <system/audio_policy.h>
#include <hardware/audio_policy.h>
#include <audio_effects/audio_effects_conf.h>
+#include <media/AudioParameter.h>
namespace android {
@@ -49,6 +51,8 @@ static const char kCmdDeadlockedString[] = "AudioPolicyService command thread ma
static const int kDumpLockRetries = 50;
static const int kDumpLockSleepUs = 20000;
+static const nsecs_t kAudioCommandTimeout = 3000000000LL; // 3 seconds
+
namespace {
extern struct audio_policy_service_ops aps_ops;
};
@@ -66,10 +70,11 @@ AudioPolicyService::AudioPolicyService()
Mutex::Autolock _l(mLock);
// start tone playback thread
- mTonePlaybackThread = new AudioCommandThread(String8(""));
+ mTonePlaybackThread = new AudioCommandThread(String8("ApmTone"), this);
// start audio commands thread
- mAudioCommandThread = new AudioCommandThread(String8("ApmCommand"));
-
+ mAudioCommandThread = new AudioCommandThread(String8("ApmAudio"), this);
+ // start output activity command thread
+ mOutputCommandThread = new AudioCommandThread(String8("ApmOutput"), this);
/* instantiate the audio policy manager */
rc = hw_get_module(AUDIO_POLICY_HARDWARE_MODULE_ID, &module);
if (rc)
@@ -145,7 +150,7 @@ status_t AudioPolicyService::setDeviceConnectionState(audio_devices_t device,
return BAD_VALUE;
}
- ALOGV("setDeviceConnectionState() tid %d", gettid());
+ ALOGV("setDeviceConnectionState()");
Mutex::Autolock _l(mLock);
return mpAudioPolicy->set_device_connection_state(mpAudioPolicy, device,
state, device_address);
@@ -174,7 +179,7 @@ status_t AudioPolicyService::setPhoneState(audio_mode_t state)
return BAD_VALUE;
}
- ALOGV("setPhoneState() tid %d", gettid());
+ ALOGV("setPhoneState()");
// TODO: check if it is more appropriate to do it in platform specific policy manager
AudioSystem::setMode(state);
@@ -199,7 +204,7 @@ status_t AudioPolicyService::setForceUse(audio_policy_force_use_t usage,
if (config < 0 || config >= AUDIO_POLICY_FORCE_CFG_CNT) {
return BAD_VALUE;
}
- ALOGV("setForceUse() tid %d", gettid());
+ ALOGV("setForceUse()");
Mutex::Autolock _l(mLock);
mpAudioPolicy->set_force_use(mpAudioPolicy, usage, config);
return NO_ERROR;
@@ -220,14 +225,16 @@ audio_io_handle_t AudioPolicyService::getOutput(audio_stream_type_t stream,
uint32_t samplingRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
if (mpAudioPolicy == NULL) {
return 0;
}
- ALOGV("getOutput() tid %d", gettid());
+ ALOGV("getOutput()");
Mutex::Autolock _l(mLock);
- return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate, format, channelMask, flags);
+ return mpAudioPolicy->get_output(mpAudioPolicy, stream, samplingRate,
+ format, channelMask, flags, offloadInfo);
}
status_t AudioPolicyService::startOutput(audio_io_handle_t output,
@@ -237,7 +244,7 @@ status_t AudioPolicyService::startOutput(audio_io_handle_t output,
if (mpAudioPolicy == NULL) {
return NO_INIT;
}
- ALOGV("startOutput() tid %d", gettid());
+ ALOGV("startOutput()");
Mutex::Autolock _l(mLock);
return mpAudioPolicy->start_output(mpAudioPolicy, output, stream, session);
}
@@ -249,7 +256,16 @@ status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
if (mpAudioPolicy == NULL) {
return NO_INIT;
}
- ALOGV("stopOutput() tid %d", gettid());
+ ALOGV("stopOutput()");
+ mOutputCommandThread->stopOutputCommand(output, stream, session);
+ return NO_ERROR;
+}
+
+status_t AudioPolicyService::doStopOutput(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session)
+{
+ ALOGV("doStopOutput from tid %d", gettid());
Mutex::Autolock _l(mLock);
return mpAudioPolicy->stop_output(mpAudioPolicy, output, stream, session);
}
@@ -259,7 +275,13 @@ void AudioPolicyService::releaseOutput(audio_io_handle_t output)
if (mpAudioPolicy == NULL) {
return;
}
- ALOGV("releaseOutput() tid %d", gettid());
+ ALOGV("releaseOutput()");
+ mOutputCommandThread->releaseOutputCommand(output);
+}
+
+void AudioPolicyService::doReleaseOutput(audio_io_handle_t output)
+{
+ ALOGV("doReleaseOutput from tid %d", gettid());
Mutex::Autolock _l(mLock);
mpAudioPolicy->release_output(mpAudioPolicy, output);
}
@@ -274,19 +296,27 @@ audio_io_handle_t AudioPolicyService::getInput(audio_source_t inputSource,
return 0;
}
// already checked by client, but double-check in case the client wrapper is bypassed
- if (uint32_t(inputSource) >= AUDIO_SOURCE_CNT) {
+ if (inputSource >= AUDIO_SOURCE_CNT && inputSource != AUDIO_SOURCE_HOTWORD) {
return 0;
}
+
+ if ((inputSource == AUDIO_SOURCE_HOTWORD) && !captureHotwordAllowed()) {
+ return 0;
+ }
+
Mutex::Autolock _l(mLock);
// the audio_in_acoustics_t parameter is ignored by get_input()
audio_io_handle_t input = mpAudioPolicy->get_input(mpAudioPolicy, inputSource, samplingRate,
- format, channelMask, (audio_in_acoustics_t) 0);
+ format, channelMask, (audio_in_acoustics_t) 0);
if (input == 0) {
return input;
}
// create audio pre processors according to input source
- ssize_t index = mInputSources.indexOfKey(inputSource);
+ audio_source_t aliasSource = (inputSource == AUDIO_SOURCE_HOTWORD) ?
+ AUDIO_SOURCE_VOICE_RECOGNITION : inputSource;
+
+ ssize_t index = mInputSources.indexOfKey(aliasSource);
if (index < 0) {
return input;
}
@@ -483,6 +513,15 @@ bool AudioPolicyService::isStreamActive(audio_stream_type_t stream, uint32_t inP
return mpAudioPolicy->is_stream_active(mpAudioPolicy, stream, inPastMs);
}
+bool AudioPolicyService::isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs) const
+{
+ if (mpAudioPolicy == NULL) {
+ return 0;
+ }
+ Mutex::Autolock _l(mLock);
+ return mpAudioPolicy->is_stream_active_remotely(mpAudioPolicy, stream, inPastMs);
+}
+
bool AudioPolicyService::isSourceActive(audio_source_t source) const
{
if (mpAudioPolicy == NULL) {
@@ -533,7 +572,7 @@ status_t AudioPolicyService::queryDefaultPreProcessing(int audioSession,
}
void AudioPolicyService::binderDied(const wp<IBinder>& who) {
- ALOGW("binderDied() %p, tid %d, calling pid %d", who.unsafe_get(), gettid(),
+ ALOGW("binderDied() %p, calling pid %d", who.unsafe_get(),
IPCThreadState::self()->getCallingPid());
}
@@ -626,8 +665,9 @@ status_t AudioPolicyService::onTransact(
// ----------- AudioPolicyService::AudioCommandThread implementation ----------
-AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
- : Thread(false), mName(name)
+AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name,
+ const wp<AudioPolicyService>& service)
+ : Thread(false), mName(name), mService(service)
{
mpToneGenerator = NULL;
}
@@ -635,7 +675,7 @@ AudioPolicyService::AudioCommandThread::AudioCommandThread(String8 name)
AudioPolicyService::AudioCommandThread::~AudioCommandThread()
{
- if (mName != "" && !mAudioCommands.isEmpty()) {
+ if (!mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
mAudioCommands.clear();
@@ -644,11 +684,7 @@ AudioPolicyService::AudioCommandThread::~AudioCommandThread()
void AudioPolicyService::AudioCommandThread::onFirstRef()
{
- if (mName != "") {
- run(mName.string(), ANDROID_PRIORITY_AUDIO);
- } else {
- run("AudioCommand", ANDROID_PRIORITY_AUDIO);
- }
+ run(mName.string(), ANDROID_PRIORITY_AUDIO);
}
bool AudioPolicyService::AudioCommandThread::threadLoop()
@@ -697,7 +733,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
data->mIO);
if (command->mWaitStatus) {
command->mCond.signal();
- mWaitWorkCV.wait(mLock);
+ command->mCond.waitRelative(mLock, kAudioCommandTimeout);
}
delete data;
}break;
@@ -708,7 +744,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
command->mStatus = AudioSystem::setParameters(data->mIO, data->mKeyValuePairs);
if (command->mWaitStatus) {
command->mCond.signal();
- mWaitWorkCV.wait(mLock);
+ command->mCond.waitRelative(mLock, kAudioCommandTimeout);
}
delete data;
}break;
@@ -719,8 +755,34 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
command->mStatus = AudioSystem::setVoiceVolume(data->mVolume);
if (command->mWaitStatus) {
command->mCond.signal();
- mWaitWorkCV.wait(mLock);
+ command->mCond.waitRelative(mLock, kAudioCommandTimeout);
+ }
+ delete data;
+ }break;
+ case STOP_OUTPUT: {
+ StopOutputData *data = (StopOutputData *)command->mParam;
+ ALOGV("AudioCommandThread() processing stop output %d",
+ data->mIO);
+ sp<AudioPolicyService> svc = mService.promote();
+ if (svc == 0) {
+ break;
}
+ mLock.unlock();
+ svc->doStopOutput(data->mIO, data->mStream, data->mSession);
+ mLock.lock();
+ delete data;
+ }break;
+ case RELEASE_OUTPUT: {
+ ReleaseOutputData *data = (ReleaseOutputData *)command->mParam;
+ ALOGV("AudioCommandThread() processing release output %d",
+ data->mIO);
+ sp<AudioPolicyService> svc = mService.promote();
+ if (svc == 0) {
+ break;
+ }
+ mLock.unlock();
+ svc->doReleaseOutput(data->mIO);
+ mLock.lock();
delete data;
}break;
default:
@@ -734,7 +796,7 @@ bool AudioPolicyService::AudioCommandThread::threadLoop()
}
}
// release delayed commands wake lock
- if (mName != "" && mAudioCommands.isEmpty()) {
+ if (mAudioCommands.isEmpty()) {
release_wake_lock(mName.string());
}
ALOGV("AudioCommandThread() going to sleep");
@@ -827,7 +889,7 @@ status_t AudioPolicyService::AudioCommandThread::volumeCommand(audio_stream_type
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
- mWaitWorkCV.signal();
+ command->mCond.signal();
}
return status;
}
@@ -852,7 +914,7 @@ status_t AudioPolicyService::AudioCommandThread::parametersCommand(audio_io_hand
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
- mWaitWorkCV.signal();
+ command->mCond.signal();
}
return status;
}
@@ -873,22 +935,50 @@ status_t AudioPolicyService::AudioCommandThread::voiceVolumeCommand(float volume
if (command->mWaitStatus) {
command->mCond.wait(mLock);
status = command->mStatus;
- mWaitWorkCV.signal();
+ command->mCond.signal();
}
return status;
}
+void AudioPolicyService::AudioCommandThread::stopOutputCommand(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session)
+{
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = STOP_OUTPUT;
+ StopOutputData *data = new StopOutputData();
+ data->mIO = output;
+ data->mStream = stream;
+ data->mSession = session;
+ command->mParam = (void *)data;
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command);
+ ALOGV("AudioCommandThread() adding stop output %d", output);
+ mWaitWorkCV.signal();
+}
+
+void AudioPolicyService::AudioCommandThread::releaseOutputCommand(audio_io_handle_t output)
+{
+ AudioCommand *command = new AudioCommand();
+ command->mCommand = RELEASE_OUTPUT;
+ ReleaseOutputData *data = new ReleaseOutputData();
+ data->mIO = output;
+ command->mParam = (void *)data;
+ Mutex::Autolock _l(mLock);
+ insertCommand_l(command);
+ ALOGV("AudioCommandThread() adding release output %d", output);
+ mWaitWorkCV.signal();
+}
+
// insertCommand_l() must be called with mLock held
void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *command, int delayMs)
{
ssize_t i; // not size_t because i will count down to -1
Vector <AudioCommand *> removedCommands;
-
- nsecs_t time = 0;
command->mTime = systemTime() + milliseconds(delayMs);
// acquire wake lock to make sure delayed commands are processed
- if (mName != "" && mAudioCommands.isEmpty()) {
+ if (mAudioCommands.isEmpty()) {
acquire_wake_lock(PARTIAL_WAKE_LOCK, mName.string());
}
@@ -930,7 +1020,10 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
} else {
data2->mKeyValuePairs = param2.toString();
}
- time = command2->mTime;
+ command->mTime = command2->mTime;
+ // force delayMs to non 0 so that code below does not request to wait for
+ // command status as the command is now delayed
+ delayMs = 1;
} break;
case SET_VOLUME: {
@@ -941,7 +1034,10 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
ALOGV("Filtering out volume command on output %d for stream %d",
data->mIO, data->mStream);
removedCommands.add(command2);
- time = command2->mTime;
+ command->mTime = command2->mTime;
+ // force delayMs to non 0 so that code below does not request to wait for
+ // command status as the command is now delayed
+ delayMs = 1;
} break;
case START_TONE:
case STOP_TONE:
@@ -963,16 +1059,12 @@ void AudioPolicyService::AudioCommandThread::insertCommand_l(AudioCommand *comma
}
removedCommands.clear();
- // wait for status only if delay is 0 and command time was not modified above
- if (delayMs == 0 && time == 0) {
+ // wait for status only if delay is 0
+ if (delayMs == 0) {
command->mWaitStatus = true;
} else {
command->mWaitStatus = false;
}
- // update command time if modified above
- if (time != 0) {
- command->mTime = time;
- }
// insert command at the right place according to its time stamp
ALOGV("inserting command: %d at index %d, num commands %d",
@@ -1043,6 +1135,21 @@ int AudioPolicyService::setVoiceVolume(float volume, int delayMs)
return (int)mAudioCommandThread->voiceVolumeCommand(volume, delayMs);
}
+bool AudioPolicyService::isOffloadSupported(const audio_offload_info_t& info)
+{
+ if (mpAudioPolicy == NULL) {
+ ALOGV("mpAudioPolicy == NULL");
+ return false;
+ }
+
+ if (mpAudioPolicy->is_offload_supported == NULL) {
+ ALOGV("HAL does not implement is_offload_supported");
+ return false;
+ }
+
+ return mpAudioPolicy->is_offload_supported(mpAudioPolicy, &info);
+}
+
// ----------------------------------------------------------------------------
// Audio pre-processing configuration
// ----------------------------------------------------------------------------
@@ -1326,6 +1433,14 @@ status_t AudioPolicyService::loadPreProcessorConfig(const char *path)
loadEffects(root, effects);
loadInputSources(root, effects);
+ // delete effects to fix memory leak.
+ // as effects is local var and valgrind would treat this as memory leak
+ // and although it only did in mediaserver init, but free it in case mediaserver reboot
+ size_t i;
+ for (i = 0; i < effects.size(); i++) {
+ delete effects[i];
+ }
+
config_free(root);
free(root);
free(data);
@@ -1375,7 +1490,8 @@ static audio_io_handle_t aps_open_output_on_module(void *service,
audio_format_t *pFormat,
audio_channel_mask_t *pChannelMask,
uint32_t *pLatencyMs,
- audio_output_flags_t flags)
+ audio_output_flags_t flags,
+ const audio_offload_info_t *offloadInfo)
{
sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
if (af == 0) {
@@ -1383,7 +1499,7 @@ static audio_io_handle_t aps_open_output_on_module(void *service,
return 0;
}
return af->openOutput(module, pDevices, pSamplingRate, pFormat, pChannelMask,
- pLatencyMs, flags);
+ pLatencyMs, flags, offloadInfo);
}
static audio_io_handle_t aps_open_dup_output(void *service,
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 63f9549..ae053a9 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -19,6 +19,7 @@
#include <cutils/misc.h>
#include <cutils/config_utils.h>
+#include <cutils/compiler.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/SortedVector.h>
@@ -44,7 +45,7 @@ class AudioPolicyService :
public:
// for BinderService
- static const char *getServiceName() { return "media.audio_policy"; }
+ static const char *getServiceName() ANDROID_API { return "media.audio_policy"; }
virtual status_t dump(int fd, const Vector<String16>& args);
@@ -66,7 +67,8 @@ public:
audio_format_t format = AUDIO_FORMAT_DEFAULT,
audio_channel_mask_t channelMask = 0,
audio_output_flags_t flags =
- AUDIO_OUTPUT_FLAG_NONE);
+ AUDIO_OUTPUT_FLAG_NONE,
+ const audio_offload_info_t *offloadInfo = NULL);
virtual status_t startOutput(audio_io_handle_t output,
audio_stream_type_t stream,
int session = 0);
@@ -104,6 +106,7 @@ public:
virtual status_t unregisterEffect(int id);
virtual status_t setEffectEnabled(int id, bool enabled);
virtual bool isStreamActive(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
+ virtual bool isStreamActiveRemotely(audio_stream_type_t stream, uint32_t inPastMs = 0) const;
virtual bool isSourceActive(audio_source_t source) const;
virtual status_t queryDefaultPreProcessing(int audioSession,
@@ -134,19 +137,25 @@ public:
virtual status_t startTone(audio_policy_tone_t tone, audio_stream_type_t stream);
virtual status_t stopTone();
virtual status_t setVoiceVolume(float volume, int delayMs = 0);
+ virtual bool isOffloadSupported(const audio_offload_info_t &config);
+
+ status_t doStopOutput(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session = 0);
+ void doReleaseOutput(audio_io_handle_t output);
private:
- AudioPolicyService();
+ AudioPolicyService() ANDROID_API;
virtual ~AudioPolicyService();
status_t dumpInternals(int fd);
// Thread used for tone playback and to send audio config commands to audio flinger
- // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because startTone()
- // and stopTone() are normally called with mLock locked and requesting a tone start or stop will cause
- // calls to AudioPolicyService and an attempt to lock mLock.
- // For audio config commands, it is necessary because audio flinger requires that the calling process (user)
- // has permission to modify audio settings.
+ // For tone playback, using a separate thread is necessary to avoid deadlock with mLock because
+ // startTone() and stopTone() are normally called with mLock locked and requesting a tone start
+ // or stop will cause calls to AudioPolicyService and an attempt to lock mLock.
+ // For audio config commands, it is necessary because audio flinger requires that the calling
+ // process (user) has permission to modify audio settings.
class AudioCommandThread : public Thread {
class AudioCommand;
public:
@@ -157,10 +166,12 @@ private:
STOP_TONE,
SET_VOLUME,
SET_PARAMETERS,
- SET_VOICE_VOLUME
+ SET_VOICE_VOLUME,
+ STOP_OUTPUT,
+ RELEASE_OUTPUT
};
- AudioCommandThread (String8 name);
+ AudioCommandThread (String8 name, const wp<AudioPolicyService>& service);
virtual ~AudioCommandThread();
status_t dump(int fd);
@@ -178,6 +189,11 @@ private:
status_t parametersCommand(audio_io_handle_t ioHandle,
const char *keyValuePairs, int delayMs = 0);
status_t voiceVolumeCommand(float volume, int delayMs = 0);
+ void stopOutputCommand(audio_io_handle_t output,
+ audio_stream_type_t stream,
+ int session);
+ void releaseOutputCommand(audio_io_handle_t output);
+
void insertCommand_l(AudioCommand *command, int delayMs = 0);
private:
@@ -222,12 +238,25 @@ private:
float mVolume;
};
+ class StopOutputData {
+ public:
+ audio_io_handle_t mIO;
+ audio_stream_type_t mStream;
+ int mSession;
+ };
+
+ class ReleaseOutputData {
+ public:
+ audio_io_handle_t mIO;
+ };
+
Mutex mLock;
Condition mWaitWorkCV;
Vector <AudioCommand *> mAudioCommands; // list of pending commands
ToneGenerator *mpToneGenerator; // the tone generator
AudioCommand mLastCommand; // last processed command (used by dump)
String8 mName; // string used by wake lock fo delayed commands
+ wp<AudioPolicyService> mService;
};
class EffectDesc {
@@ -312,6 +341,7 @@ private:
// device connection state or routing
sp<AudioCommandThread> mAudioCommandThread; // audio commands thread
sp<AudioCommandThread> mTonePlaybackThread; // tone playback thread
+ sp<AudioCommandThread> mOutputCommandThread; // process stop and release output
struct audio_policy_device *mpAudioPolicyDev;
struct audio_policy *mpAudioPolicy;
KeyedVector< audio_source_t, InputSourceDesc* > mInputSources;
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index ffea9b9..e5cceb1 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -82,10 +82,8 @@ bool AudioResampler::qualityIsSupported(src_quality quality)
switch (quality) {
case DEFAULT_QUALITY:
case LOW_QUALITY:
-#if 0 // these have not been qualified recently so are not supported unless explicitly requested
case MED_QUALITY:
case HIGH_QUALITY:
-#endif
case VERY_HIGH_QUALITY:
return true;
default:
@@ -190,12 +188,10 @@ AudioResampler* AudioResampler::create(int bitDepth, int inChannelCount,
ALOGV("Create linear Resampler");
resampler = new AudioResamplerOrder1(bitDepth, inChannelCount, sampleRate);
break;
-#if 0 // disabled because it has not been qualified recently, if requested will use default:
case MED_QUALITY:
ALOGV("Create cubic Resampler");
resampler = new AudioResamplerCubic(bitDepth, inChannelCount, sampleRate);
break;
-#endif
case HIGH_QUALITY:
ALOGV("Create HIGH_QUALITY sinc Resampler");
resampler = new AudioResamplerSinc(bitDepth, inChannelCount, sampleRate);
@@ -530,7 +526,7 @@ void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t
" ldr r8, [sp, #" MO_PARAM5 " + 4]\n" // out
" ldr r0, [sp, #" MO_PARAM5 " + 0]\n" // &outputIndex
" ldr r0, [r0]\n" // outputIndex
- " add r8, r0, asl #2\n" // curOut
+ " add r8, r8, r0, asl #2\n" // curOut
" ldr r9, [sp, #" MO_PARAM5 " + 24]\n" // phaseIncrement
" ldr r10, [sp, #" MO_PARAM5 " + 12]\n" // vl
" ldr r11, [sp, #" MO_PARAM5 " + 16]\n" // vr
@@ -640,7 +636,7 @@ void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32
" ldr r8, [sp, #" ST_PARAM5 " + 4]\n" // out
" ldr r0, [sp, #" ST_PARAM5 " + 0]\n" // &outputIndex
" ldr r0, [r0]\n" // outputIndex
- " add r8, r0, asl #2\n" // curOut
+ " add r8, r8, r0, asl #2\n" // curOut
" ldr r9, [sp, #" ST_PARAM5 " + 24]\n" // phaseIncrement
" ldr r10, [sp, #" ST_PARAM5 " + 12]\n" // vl
" ldr r11, [sp, #" ST_PARAM5 " + 16]\n" // vr
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index 2b8694f..33e64ce 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -19,13 +19,14 @@
#include <stdint.h>
#include <sys/types.h>
+#include <cutils/compiler.h>
#include <media/AudioBufferProvider.h>
namespace android {
// ----------------------------------------------------------------------------
-class AudioResampler {
+class ANDROID_API AudioResampler {
public:
// Determines quality of SRC.
// LOW_QUALITY: linear interpolator (1st order)
@@ -55,6 +56,14 @@ public:
// set the PTS of the next buffer output by the resampler
virtual void setPTS(int64_t pts);
+ // Resample int16_t samples from provider and accumulate into 'out'.
+ // A mono provider delivers a sequence of samples.
+ // A stereo provider delivers a sequence of interleaved pairs of samples.
+ // Multi-channel providers are not supported.
+ // In either case, 'out' holds interleaved pairs of fixed-point signed Q19.12.
+ // That is, for a mono provider, there is an implicit up-channeling.
+ // Since this method accumulates, the caller is responsible for clearing 'out' initially.
+ // FIXME assumes provider is always successful; it should return the actual frame count.
virtual void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider) = 0;
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index 9e8447a..207f26b 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -17,13 +17,33 @@
#define LOG_TAG "AudioResamplerSinc"
//#define LOG_NDEBUG 0
+#include <malloc.h>
#include <string.h>
-#include "AudioResamplerSinc.h"
+#include <stdlib.h>
#include <dlfcn.h>
+
+#include <cutils/compiler.h>
#include <cutils/properties.h>
-#include <stdlib.h>
+
#include <utils/Log.h>
+#include "AudioResamplerSinc.h"
+
+
+
+#if defined(__arm__) && !defined(__thumb__)
+#define USE_INLINE_ASSEMBLY (true)
+#else
+#define USE_INLINE_ASSEMBLY (false)
+#endif
+
+#if USE_INLINE_ASSEMBLY && defined(__ARM_NEON__)
+#define USE_NEON (true)
+#else
+#define USE_NEON (false)
+#endif
+
+
namespace android {
// ----------------------------------------------------------------------------
@@ -31,37 +51,274 @@ namespace android {
/*
* These coeficients are computed with the "fir" utility found in
* tools/resampler_tools
- * TODO: A good optimization would be to transpose this matrix, to take
- * better advantage of the data-cache.
+ * cmd-line: fir -l 7 -s 48000 -c 20478
*/
-const int32_t AudioResamplerSinc::mFirCoefsUp[] = {
- 0x7fffffff, 0x7f15d078, 0x7c5e0da6, 0x77ecd867, 0x71e2e251, 0x6a6c304a, 0x61be7269, 0x58170412, 0x4db8ab05, 0x42e92ea6, 0x37eee214, 0x2d0e3bb1, 0x22879366, 0x18951e95, 0x0f693d0d, 0x072d2621,
- 0x00000000, 0xf9f66655, 0xf51a5fd7, 0xf16bbd84, 0xeee0d9ac, 0xed67a922, 0xece70de6, 0xed405897, 0xee50e505, 0xeff3be30, 0xf203370f, 0xf45a6741, 0xf6d67d53, 0xf957db66, 0xfbc2f647, 0xfe00f2b9,
- 0x00000000, 0x01b37218, 0x0313a0c6, 0x041d930d, 0x04d28057, 0x053731b0, 0x05534dff, 0x05309bfd, 0x04da440d, 0x045c1aee, 0x03c1fcdd, 0x03173ef5, 0x02663ae8, 0x01b7f736, 0x0113ec79, 0x007fe6a9,
- 0x00000000, 0xff96b229, 0xff44f99f, 0xff0a86be, 0xfee5f803, 0xfed518fd, 0xfed521fd, 0xfee2f4fd, 0xfefb54f8, 0xff1b159b, 0xff3f4203, 0xff6539e0, 0xff8ac502, 0xffae1ddd, 0xffcdf3f9, 0xffe96798,
- 0x00000000, 0x00119de6, 0x001e6b7e, 0x0026cb7a, 0x002b4830, 0x002c83d6, 0x002b2a82, 0x0027e67a, 0x002356f9, 0x001e098e, 0x001875e4, 0x0012fbbe, 0x000de2d1, 0x00095c10, 0x00058414, 0x00026636,
- 0x00000000, 0xfffe44a9, 0xfffd206d, 0xfffc7b7f, 0xfffc3c8f, 0xfffc4ac2, 0xfffc8f2b, 0xfffcf5c4, 0xfffd6df3, 0xfffdeab2, 0xfffe6275, 0xfffececf, 0xffff2c07, 0xffff788c, 0xffffb471, 0xffffe0f2,
- 0x00000000, 0x000013e6, 0x00001f03, 0x00002396, 0x00002399, 0x000020b6, 0x00001c3c, 0x00001722, 0x00001216, 0x00000d81, 0x0000099c, 0x0000067c, 0x00000419, 0x0000025f, 0x00000131, 0x00000070,
- 0x00000000, 0xffffffc7, 0xffffffb3, 0xffffffb3, 0xffffffbe, 0xffffffcd, 0xffffffdb, 0xffffffe7, 0xfffffff0, 0xfffffff7, 0xfffffffb, 0xfffffffe, 0xffffffff, 0x00000000, 0x00000000, 0x00000000,
- 0x00000000 // this one is needed for lerping the last coefficient
+const uint32_t AudioResamplerSinc::mFirCoefsUp[] __attribute__ ((aligned (32))) = {
+ 0x6d374bc7, 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300,
+ 0x6d35278a, 0x103e8192, 0xf36b9dfd, 0x07bdfaa5, 0xfc5102d0, 0x013d618d, 0xffc663b9, 0xfffd9592,
+ 0x6d2ebafe, 0x0f62811a, 0xf3b3d8ac, 0x07a9f399, 0xfc51d9a6, 0x0140bea5, 0xffc41212, 0xfffe631e,
+ 0x6d24069d, 0x0e8875ad, 0xf3fcb43e, 0x07953976, 0xfc53216f, 0x0143e67c, 0xffc1d373, 0xffff2b9f,
+ 0x6d150b35, 0x0db06a89, 0xf4462690, 0x077fd0ac, 0xfc54d8ae, 0x0146d965, 0xffbfa7d9, 0xffffef10,
+ 0x6d01c9e3, 0x0cda6ab5, 0xf4902587, 0x0769bdaf, 0xfc56fdda, 0x014997bb, 0xffbd8f40, 0x0000ad6e,
+ 0x6cea4418, 0x0c0680fe, 0xf4daa718, 0x07530501, 0xfc598f60, 0x014c21db, 0xffbb89a1, 0x000166b6,
+ 0x6cce7b97, 0x0b34b7f5, 0xf525a143, 0x073bab28, 0xfc5c8ba5, 0x014e782a, 0xffb996f3, 0x00021ae5,
+ 0x6cae7272, 0x0a6519f4, 0xf5710a17, 0x0723b4b4, 0xfc5ff105, 0x01509b14, 0xffb7b728, 0x0002c9fd,
+ 0x6c8a2b0f, 0x0997b116, 0xf5bcd7b1, 0x070b2639, 0xfc63bdd3, 0x01528b08, 0xffb5ea31, 0x000373fb,
+ 0x6c61a823, 0x08cc873c, 0xf609003f, 0x06f20453, 0xfc67f05a, 0x0154487b, 0xffb42ffc, 0x000418e2,
+ 0x6c34ecb5, 0x0803a60a, 0xf6557a00, 0x06d853a2, 0xfc6c86dd, 0x0155d3e8, 0xffb28876, 0x0004b8b3,
+ 0x6c03fc1c, 0x073d16e7, 0xf6a23b44, 0x06be18cd, 0xfc717f97, 0x01572dcf, 0xffb0f388, 0x00055371,
+ 0x6bced9ff, 0x0678e2fc, 0xf6ef3a6e, 0x06a3587e, 0xfc76d8bc, 0x015856b6, 0xffaf7118, 0x0005e921,
+ 0x6b958a54, 0x05b71332, 0xf73c6df4, 0x06881761, 0xfc7c9079, 0x01594f25, 0xffae010b, 0x000679c5,
+ 0x6b581163, 0x04f7b037, 0xf789cc61, 0x066c5a27, 0xfc82a4f4, 0x015a17ab, 0xffaca344, 0x00070564,
+ 0x6b1673c1, 0x043ac276, 0xf7d74c53, 0x06502583, 0xfc89144d, 0x015ab0db, 0xffab57a1, 0x00078c04,
+ 0x6ad0b652, 0x0380521c, 0xf824e480, 0x06337e2a, 0xfc8fdc9f, 0x015b1b4e, 0xffaa1e02, 0x00080dab,
+ 0x6a86de48, 0x02c86715, 0xf8728bb3, 0x061668d2, 0xfc96fbfc, 0x015b579e, 0xffa8f641, 0x00088a62,
+ 0x6a38f123, 0x0213090c, 0xf8c038d0, 0x05f8ea30, 0xfc9e7074, 0x015b666c, 0xffa7e039, 0x00090230,
+ 0x69e6f4b1, 0x01603f6e, 0xf90de2d1, 0x05db06fc, 0xfca63810, 0x015b485b, 0xffa6dbc0, 0x0009751e,
+ 0x6990ef0b, 0x00b01162, 0xf95b80cb, 0x05bcc3ed, 0xfcae50d6, 0x015afe14, 0xffa5e8ad, 0x0009e337,
+ 0x6936e697, 0x000285d0, 0xf9a909ea, 0x059e25b5, 0xfcb6b8c4, 0x015a8843, 0xffa506d2, 0x000a4c85,
+ 0x68d8e206, 0xff57a35e, 0xf9f67577, 0x057f310a, 0xfcbf6dd8, 0x0159e796, 0xffa43603, 0x000ab112,
+ 0x6876e855, 0xfeaf706f, 0xfa43bad2, 0x055fea9d, 0xfcc86e09, 0x01591cc0, 0xffa3760e, 0x000b10ec,
+ 0x681100c9, 0xfe09f323, 0xfa90d17b, 0x0540571a, 0xfcd1b74c, 0x01582878, 0xffa2c6c2, 0x000b6c1d,
+ 0x67a732f4, 0xfd673159, 0xfaddb10c, 0x05207b2f, 0xfcdb4793, 0x01570b77, 0xffa227ec, 0x000bc2b3,
+ 0x673986ac, 0xfcc730aa, 0xfb2a513b, 0x05005b82, 0xfce51ccb, 0x0155c678, 0xffa19957, 0x000c14bb,
+ 0x66c80413, 0xfc29f670, 0xfb76a9dd, 0x04dffcb6, 0xfcef34e1, 0x01545a3c, 0xffa11acb, 0x000c6244,
+ 0x6652b392, 0xfb8f87bd, 0xfbc2b2e4, 0x04bf6369, 0xfcf98dbe, 0x0152c783, 0xffa0ac11, 0x000cab5c,
+ 0x65d99dd5, 0xfaf7e963, 0xfc0e6461, 0x049e9433, 0xfd04254a, 0x01510f13, 0xffa04cf0, 0x000cf012,
+ 0x655ccbd3, 0xfa631fef, 0xfc59b685, 0x047d93a8, 0xfd0ef969, 0x014f31b2, 0xff9ffd2c, 0x000d3075,
+ 0x64dc46c3, 0xf9d12fab, 0xfca4a19f, 0x045c6654, 0xfd1a0801, 0x014d3029, 0xff9fbc89, 0x000d6c97,
+ 0x64581823, 0xf9421c9d, 0xfcef1e20, 0x043b10bd, 0xfd254ef4, 0x014b0b45, 0xff9f8ac9, 0x000da486,
+ 0x63d049b4, 0xf8b5ea87, 0xfd392498, 0x04199760, 0xfd30cc24, 0x0148c3d2, 0xff9f67ae, 0x000dd854,
+ 0x6344e578, 0xf82c9ce7, 0xfd82adba, 0x03f7feb4, 0xfd3c7d73, 0x01465a9f, 0xff9f52f7, 0x000e0812,
+ 0x62b5f5b2, 0xf7a636fa, 0xfdcbb25a, 0x03d64b27, 0xfd4860c2, 0x0143d07f, 0xff9f4c65, 0x000e33d3,
+ 0x622384e8, 0xf722bbb5, 0xfe142b6e, 0x03b4811d, 0xfd5473f3, 0x01412643, 0xff9f53b4, 0x000e5ba7,
+ 0x618d9ddc, 0xf6a22dcf, 0xfe5c120f, 0x0392a4f4, 0xfd60b4e7, 0x013e5cc0, 0xff9f68a1, 0x000e7fa1,
+ 0x60f44b91, 0xf6248fb6, 0xfea35f79, 0x0370bafc, 0xfd6d2180, 0x013b74ca, 0xff9f8ae9, 0x000e9fd5,
+ 0x60579947, 0xf5a9e398, 0xfeea0d0c, 0x034ec77f, 0xfd79b7a1, 0x01386f3a, 0xff9fba47, 0x000ebc54,
+ 0x5fb79278, 0xf5322b61, 0xff30144a, 0x032ccebb, 0xfd86752e, 0x01354ce7, 0xff9ff674, 0x000ed533,
+ 0x5f1442dc, 0xf4bd68b6, 0xff756edc, 0x030ad4e1, 0xfd93580d, 0x01320ea9, 0xffa03f2b, 0x000eea84,
+ 0x5e6db665, 0xf44b9cfe, 0xffba168d, 0x02e8de19, 0xfda05e23, 0x012eb55a, 0xffa09425, 0x000efc5c,
+ 0x5dc3f93c, 0xf3dcc959, 0xfffe054e, 0x02c6ee7f, 0xfdad855b, 0x012b41d3, 0xffa0f519, 0x000f0ace,
+ 0x5d1717c4, 0xf370eea9, 0x00413536, 0x02a50a22, 0xfdbacb9e, 0x0127b4f1, 0xffa161bf, 0x000f15ef,
+ 0x5c671e96, 0xf3080d8c, 0x0083a081, 0x02833506, 0xfdc82edb, 0x01240f8e, 0xffa1d9cf, 0x000f1dd2,
+ 0x5bb41a80, 0xf2a2265e, 0x00c54190, 0x02617321, 0xfdd5ad01, 0x01205285, 0xffa25cfe, 0x000f228d,
+ 0x5afe1886, 0xf23f393b, 0x010612eb, 0x023fc85c, 0xfde34403, 0x011c7eb2, 0xffa2eb04, 0x000f2434,
+ 0x5a4525df, 0xf1df45fd, 0x01460f41, 0x021e3891, 0xfdf0f1d6, 0x011894f0, 0xffa38395, 0x000f22dc,
+ 0x59894ff3, 0xf1824c3e, 0x01853165, 0x01fcc78f, 0xfdfeb475, 0x0114961b, 0xffa42668, 0x000f1e99,
+ 0x58caa45b, 0xf1284b58, 0x01c37452, 0x01db7914, 0xfe0c89db, 0x0110830f, 0xffa4d332, 0x000f1781,
+ 0x580930e1, 0xf0d14267, 0x0200d32c, 0x01ba50d2, 0xfe1a7009, 0x010c5ca6, 0xffa589a6, 0x000f0da8,
+ 0x5745037c, 0xf07d3043, 0x023d493c, 0x0199526b, 0xfe286505, 0x010823ba, 0xffa6497c, 0x000f0125,
+ 0x567e2a51, 0xf02c138a, 0x0278d1f2, 0x01788170, 0xfe3666d5, 0x0103d927, 0xffa71266, 0x000ef20b,
+ 0x55b4b3af, 0xefddea9a, 0x02b368e6, 0x0157e166, 0xfe447389, 0x00ff7dc4, 0xffa7e41a, 0x000ee070,
+ 0x54e8ae13, 0xef92b393, 0x02ed09d7, 0x013775bf, 0xfe528931, 0x00fb126b, 0xffa8be4c, 0x000ecc69,
+ 0x541a281e, 0xef4a6c58, 0x0325b0ad, 0x011741df, 0xfe60a5e5, 0x00f697f3, 0xffa9a0b1, 0x000eb60b,
+ 0x5349309e, 0xef051290, 0x035d5977, 0x00f7491a, 0xfe6ec7c0, 0x00f20f32, 0xffaa8afe, 0x000e9d6b,
+ 0x5275d684, 0xeec2a3a3, 0x0394006a, 0x00d78eb3, 0xfe7cece2, 0x00ed78ff, 0xffab7ce7, 0x000e829e,
+ 0x51a028e8, 0xee831cc3, 0x03c9a1e5, 0x00b815da, 0xfe8b1373, 0x00e8d62d, 0xffac7621, 0x000e65ba,
+ 0x50c83704, 0xee467ae1, 0x03fe3a6f, 0x0098e1b3, 0xfe99399f, 0x00e4278f, 0xffad7662, 0x000e46d3,
+ 0x4fee1037, 0xee0cbab9, 0x0431c6b5, 0x0079f54c, 0xfea75d97, 0x00df6df7, 0xffae7d5f, 0x000e25fd,
+ 0x4f11c3fe, 0xedd5d8ca, 0x0464438c, 0x005b53a4, 0xfeb57d92, 0x00daaa34, 0xffaf8acd, 0x000e034f,
+ 0x4e3361f7, 0xeda1d15c, 0x0495adf2, 0x003cffa9, 0xfec397cf, 0x00d5dd16, 0xffb09e63, 0x000ddedb,
+ 0x4d52f9df, 0xed70a07d, 0x04c6030d, 0x001efc35, 0xfed1aa92, 0x00d10769, 0xffb1b7d8, 0x000db8b7,
+ 0x4c709b8e, 0xed424205, 0x04f54029, 0x00014c12, 0xfedfb425, 0x00cc29f7, 0xffb2d6e1, 0x000d90f6,
+ 0x4b8c56f8, 0xed16b196, 0x052362ba, 0xffe3f1f7, 0xfeedb2da, 0x00c7458a, 0xffb3fb37, 0x000d67ae,
+ 0x4aa63c2c, 0xecedea99, 0x0550685d, 0xffc6f08a, 0xfefba508, 0x00c25ae8, 0xffb52490, 0x000d3cf1,
+ 0x49be5b50, 0xecc7e845, 0x057c4ed4, 0xffaa4a5d, 0xff09890f, 0x00bd6ad7, 0xffb652a7, 0x000d10d5,
+ 0x48d4c4a2, 0xeca4a59b, 0x05a7140b, 0xff8e01f1, 0xff175d53, 0x00b87619, 0xffb78533, 0x000ce36b,
+ 0x47e98874, 0xec841d68, 0x05d0b612, 0xff7219b3, 0xff252042, 0x00b37d70, 0xffb8bbed, 0x000cb4c8,
+ 0x46fcb72d, 0xec664a48, 0x05f93324, 0xff5693fe, 0xff32d04f, 0x00ae8198, 0xffb9f691, 0x000c84ff,
+ 0x460e6148, 0xec4b26a2, 0x0620899e, 0xff3b731b, 0xff406bf8, 0x00a9834e, 0xffbb34d8, 0x000c5422,
+ 0x451e9750, 0xec32acb0, 0x0646b808, 0xff20b93e, 0xff4df1be, 0x00a4834c, 0xffbc767f, 0x000c2245,
+ 0x442d69de, 0xec1cd677, 0x066bbd0d, 0xff066889, 0xff5b602c, 0x009f8249, 0xffbdbb42, 0x000bef79,
+ 0x433ae99c, 0xec099dcf, 0x068f9781, 0xfeec830d, 0xff68b5d5, 0x009a80f8, 0xffbf02dd, 0x000bbbd2,
+ 0x4247273f, 0xebf8fc64, 0x06b2465b, 0xfed30ac5, 0xff75f153, 0x0095800c, 0xffc04d0f, 0x000b8760,
+ 0x41523389, 0xebeaebaf, 0x06d3c8bb, 0xfeba0199, 0xff831148, 0x00908034, 0xffc19996, 0x000b5235,
+ 0x405c1f43, 0xebdf6500, 0x06f41de3, 0xfea16960, 0xff90145e, 0x008b821b, 0xffc2e832, 0x000b1c64,
+ 0x3f64fb40, 0xebd6617b, 0x0713453d, 0xfe8943dc, 0xff9cf947, 0x0086866b, 0xffc438a3, 0x000ae5fc,
+ 0x3e6cd85b, 0xebcfda19, 0x07313e56, 0xfe7192bd, 0xffa9bebe, 0x00818dcb, 0xffc58aaa, 0x000aaf0f,
+ 0x3d73c772, 0xebcbc7a7, 0x074e08e0, 0xfe5a579d, 0xffb66386, 0x007c98de, 0xffc6de09, 0x000a77ac,
+ 0x3c79d968, 0xebca22cc, 0x0769a4b2, 0xfe439407, 0xffc2e669, 0x0077a845, 0xffc83285, 0x000a3fe5,
+ 0x3b7f1f23, 0xebcae405, 0x078411c7, 0xfe2d496f, 0xffcf463a, 0x0072bc9d, 0xffc987e0, 0x000a07c9,
+ 0x3a83a989, 0xebce03aa, 0x079d503b, 0xfe177937, 0xffdb81d6, 0x006dd680, 0xffcadde1, 0x0009cf67,
+ 0x3987897f, 0xebd379eb, 0x07b56051, 0xfe0224b0, 0xffe79820, 0x0068f687, 0xffcc344c, 0x000996ce,
+ 0x388acfe9, 0xebdb3ed5, 0x07cc426c, 0xfded4d13, 0xfff38806, 0x00641d44, 0xffcd8aeb, 0x00095e0e,
+ 0x378d8da8, 0xebe54a4f, 0x07e1f712, 0xfdd8f38b, 0xffff507b, 0x005f4b4a, 0xffcee183, 0x00092535,
+ 0x368fd397, 0xebf1941f, 0x07f67eec, 0xfdc5192d, 0x000af07f, 0x005a8125, 0xffd037e0, 0x0008ec50,
+ 0x3591b28b, 0xec0013e8, 0x0809dac3, 0xfdb1befc, 0x00166718, 0x0055bf60, 0xffd18dcc, 0x0008b36e,
+ 0x34933b50, 0xec10c12c, 0x081c0b84, 0xfd9ee5e7, 0x0021b355, 0x00510682, 0xffd2e311, 0x00087a9c,
+ 0x33947eab, 0xec23934f, 0x082d1239, 0xfd8c8ecc, 0x002cd44d, 0x004c570f, 0xffd4377d, 0x000841e8,
+ 0x32958d55, 0xec388194, 0x083cf010, 0xfd7aba74, 0x0037c922, 0x0047b186, 0xffd58ade, 0x0008095d,
+ 0x319677fa, 0xec4f8322, 0x084ba654, 0xfd696998, 0x004290fc, 0x00431666, 0xffd6dd02, 0x0007d108,
+ 0x30974f3b, 0xec688f02, 0x08593671, 0xfd589cdc, 0x004d2b0e, 0x003e8628, 0xffd82dba, 0x000798f5,
+ 0x2f9823a8, 0xec839c22, 0x0865a1f1, 0xfd4854d3, 0x00579691, 0x003a0141, 0xffd97cd6, 0x00076130,
+ 0x2e9905c1, 0xeca0a156, 0x0870ea7e, 0xfd3891fd, 0x0061d2ca, 0x00358824, 0xffdaca2a, 0x000729c4,
+ 0x2d9a05f4, 0xecbf9558, 0x087b11de, 0xfd2954c8, 0x006bdf05, 0x00311b41, 0xffdc1588, 0x0006f2bb,
+ 0x2c9b349e, 0xece06ecb, 0x088419f6, 0xfd1a9d91, 0x0075ba95, 0x002cbb03, 0xffdd5ec6, 0x0006bc21,
+ 0x2b9ca203, 0xed032439, 0x088c04c8, 0xfd0c6ca2, 0x007f64da, 0x002867d2, 0xffdea5bb, 0x000685ff,
+ 0x2a9e5e57, 0xed27ac16, 0x0892d470, 0xfcfec233, 0x0088dd38, 0x00242213, 0xffdfea3c, 0x0006505f,
+ 0x29a079b2, 0xed4dfcc2, 0x08988b2a, 0xfcf19e6b, 0x0092231e, 0x001fea27, 0xffe12c22, 0x00061b4b,
+ 0x28a30416, 0xed760c88, 0x089d2b4a, 0xfce50161, 0x009b3605, 0x001bc06b, 0xffe26b48, 0x0005e6cb,
+ 0x27a60d6a, 0xed9fd1a2, 0x08a0b740, 0xfcd8eb17, 0x00a4156b, 0x0017a53b, 0xffe3a788, 0x0005b2e8,
+ 0x26a9a57b, 0xedcb4237, 0x08a33196, 0xfccd5b82, 0x00acc0da, 0x001398ec, 0xffe4e0bf, 0x00057faa,
+ 0x25addbf9, 0xedf8545b, 0x08a49cf0, 0xfcc25285, 0x00b537e1, 0x000f9bd2, 0xffe616c8, 0x00054d1a,
+ 0x24b2c075, 0xee26fe17, 0x08a4fc0d, 0xfcb7cff0, 0x00bd7a1c, 0x000bae3c, 0xffe74984, 0x00051b3e,
+ 0x23b86263, 0xee573562, 0x08a451c0, 0xfcadd386, 0x00c5872a, 0x0007d075, 0xffe878d3, 0x0004ea1d,
+ 0x22bed116, 0xee88f026, 0x08a2a0f8, 0xfca45cf7, 0x00cd5eb7, 0x000402c8, 0xffe9a494, 0x0004b9c0,
+ 0x21c61bc0, 0xeebc2444, 0x089fecbb, 0xfc9b6be5, 0x00d50075, 0x00004579, 0xffeaccaa, 0x00048a2b,
+ 0x20ce516f, 0xeef0c78d, 0x089c3824, 0xfc92ffe1, 0x00dc6c1e, 0xfffc98c9, 0xffebf0fa, 0x00045b65,
+ 0x1fd7810f, 0xef26cfca, 0x08978666, 0xfc8b186d, 0x00e3a175, 0xfff8fcf7, 0xffed1166, 0x00042d74,
+ 0x1ee1b965, 0xef5e32bd, 0x0891dac8, 0xfc83b4fc, 0x00eaa045, 0xfff5723d, 0xffee2dd7, 0x0004005e,
+ 0x1ded0911, 0xef96e61c, 0x088b38a9, 0xfc7cd4f0, 0x00f16861, 0xfff1f8d2, 0xffef4632, 0x0003d426,
+ 0x1cf97e8b, 0xefd0df9a, 0x0883a378, 0xfc76779e, 0x00f7f9a3, 0xffee90eb, 0xfff05a60, 0x0003a8d2,
+ 0x1c072823, 0xf00c14e1, 0x087b1ebc, 0xfc709c4d, 0x00fe53ef, 0xffeb3ab8, 0xfff16a4a, 0x00037e65,
+ 0x1b1613ff, 0xf0487b98, 0x0871ae0d, 0xfc6b4233, 0x0104772e, 0xffe7f666, 0xfff275db, 0x000354e5,
+ 0x1a26501b, 0xf0860962, 0x08675516, 0xfc66687a, 0x010a6353, 0xffe4c41e, 0xfff37d00, 0x00032c54,
+ 0x1937ea47, 0xf0c4b3e0, 0x085c1794, 0xfc620e3d, 0x01101858, 0xffe1a408, 0xfff47fa5, 0x000304b7,
+ 0x184af025, 0xf10470b0, 0x084ff957, 0xfc5e328c, 0x0115963d, 0xffde9646, 0xfff57db8, 0x0002de0e,
+ 0x175f6f2b, 0xf1453571, 0x0842fe3d, 0xfc5ad465, 0x011add0b, 0xffdb9af8, 0xfff67729, 0x0002b85f,
+ 0x1675749e, 0xf186f7c0, 0x08352a35, 0xfc57f2be, 0x011fecd3, 0xffd8b23b, 0xfff76be9, 0x000293aa,
+ 0x158d0d95, 0xf1c9ad40, 0x0826813e, 0xfc558c7c, 0x0124c5ab, 0xffd5dc28, 0xfff85be8, 0x00026ff2,
+ 0x14a646f6, 0xf20d4b92, 0x08170767, 0xfc53a07b, 0x012967b1, 0xffd318d6, 0xfff9471b, 0x00024d39,
+ 0x13c12d73, 0xf251c85d, 0x0806c0cb, 0xfc522d88, 0x012dd30a, 0xffd06858, 0xfffa2d74, 0x00022b7f,
+ 0x12ddcd8f, 0xf297194d, 0x07f5b193, 0xfc513266, 0x013207e4, 0xffcdcabe, 0xfffb0ee9, 0x00020ac7,
+ 0x11fc3395, 0xf2dd3411, 0x07e3ddf7, 0xfc50adcc, 0x01360670, 0xffcb4014, 0xfffbeb70, 0x0001eb10,
+ 0x111c6ba0, 0xf3240e61, 0x07d14a38, 0xfc509e64, 0x0139cee9, 0xffc8c866, 0xfffcc300, 0x0001cc5c,
};
/*
- * These coefficients are optimized for 48KHz -> 44.1KHz (stop-band at 22.050KHz)
- * It's possible to use the above coefficient for any down-sampling
- * at the expense of a slower processing loop (we can interpolate
- * these coefficient from the above by "Stretching" them in time).
+ * These coefficients are optimized for 48KHz -> 44.1KHz
+ * cmd-line: fir -l 7 -s 48000 -c 17189
*/
-const int32_t AudioResamplerSinc::mFirCoefsDown[] = {
- 0x7fffffff, 0x7f55e46d, 0x7d5b4c60, 0x7a1b4b98, 0x75a7fb14, 0x7019f0bd, 0x698f875a, 0x622bfd59, 0x5a167256, 0x5178cc54, 0x487e8e6c, 0x3f53aae8, 0x36235ad4, 0x2d17047b, 0x245539ab, 0x1c00d540,
- 0x14383e57, 0x0d14d5ca, 0x06aa910b, 0x0107c38b, 0xfc351654, 0xf835abae, 0xf5076b45, 0xf2a37202, 0xf0fe9faa, 0xf00a3bbd, 0xefb4aa81, 0xefea2b05, 0xf0959716, 0xf1a11e83, 0xf2f6f7a0, 0xf481fff4,
- 0xf62e48ce, 0xf7e98ca5, 0xf9a38b4c, 0xfb4e4bfa, 0xfcde456f, 0xfe4a6d30, 0xff8c2fdf, 0x009f5555, 0x0181d393, 0x0233940f, 0x02b62f06, 0x030ca07d, 0x033afa62, 0x03461725, 0x03334f83, 0x030835fa,
- 0x02ca59cc, 0x027f12d1, 0x022b570d, 0x01d39a49, 0x017bb78f, 0x0126e414, 0x00d7aaaf, 0x008feec7, 0x0050f584, 0x001b73e3, 0xffefa063, 0xffcd46ed, 0xffb3ddcd, 0xffa29aaa, 0xff988691, 0xff949066,
- 0xff959d24, 0xff9a959e, 0xffa27195, 0xffac4011, 0xffb72d2b, 0xffc28569, 0xffcdb706, 0xffd85171, 0xffe20364, 0xffea97e9, 0xfff1f2b2, 0xfff80c06, 0xfffcec92, 0x0000a955, 0x00035fd8, 0x000532cf,
- 0x00064735, 0x0006c1f9, 0x0006c62d, 0x000673ba, 0x0005e68f, 0x00053630, 0x000475a3, 0x0003b397, 0x0002fac1, 0x00025257, 0x0001be9e, 0x0001417a, 0x0000dafd, 0x000089eb, 0x00004c28, 0x00001f1d,
- 0x00000000, 0xffffec10, 0xffffe0be, 0xffffdbc5, 0xffffdb39, 0xffffdd8b, 0xffffe182, 0xffffe638, 0xffffeb0a, 0xffffef8f, 0xfffff38b, 0xfffff6e3, 0xfffff993, 0xfffffba6, 0xfffffd30, 0xfffffe4a,
- 0xffffff09, 0xffffff85, 0xffffffd1, 0xfffffffb, 0x0000000f, 0x00000016, 0x00000015, 0x00000012, 0x0000000d, 0x00000009, 0x00000006, 0x00000003, 0x00000002, 0x00000001, 0x00000000, 0x00000000,
- 0x00000000 // this one is needed for lerping the last coefficient
+const uint32_t AudioResamplerSinc::mFirCoefsDown[] __attribute__ ((aligned (32))) = {
+ 0x5bacb6f4, 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631,
+ 0x5bab6c81, 0x1d3ddccd, 0xf0421d2c, 0x03af9995, 0x01818dc9, 0xfe6bb63e, 0x0079812a, 0xfffdc37d,
+ 0x5ba78d37, 0x1c8f2cf9, 0xf04beb1d, 0x03c9a04a, 0x016f8aca, 0xfe70a511, 0x0079e34d, 0xfffd2545,
+ 0x5ba1194f, 0x1be11231, 0xf056f2c7, 0x03e309fe, 0x015d9e64, 0xfe75a79f, 0x007a36e2, 0xfffc8b86,
+ 0x5b981122, 0x1b3393f8, 0xf0632fb7, 0x03fbd625, 0x014bc9fa, 0xfe7abd23, 0x007a7c20, 0xfffbf639,
+ 0x5b8c7530, 0x1a86b9bf, 0xf0709d74, 0x04140449, 0x013a0ee9, 0xfe7fe4db, 0x007ab33d, 0xfffb655b,
+ 0x5b7e461a, 0x19da8ae5, 0xf07f3776, 0x042b93fd, 0x01286e86, 0xfe851e05, 0x007adc72, 0xfffad8e4,
+ 0x5b6d84a8, 0x192f0eb7, 0xf08ef92d, 0x044284e6, 0x0116ea22, 0xfe8a67dd, 0x007af7f6, 0xfffa50ce,
+ 0x5b5a31c6, 0x18844c70, 0xf09fddfe, 0x0458d6b7, 0x01058306, 0xfe8fc1a5, 0x007b0603, 0xfff9cd12,
+ 0x5b444e81, 0x17da4b37, 0xf0b1e143, 0x046e8933, 0x00f43a74, 0xfe952a9b, 0x007b06d4, 0xfff94da9,
+ 0x5b2bdc0e, 0x17311222, 0xf0c4fe50, 0x04839c29, 0x00e311a9, 0xfe9aa201, 0x007afaa1, 0xfff8d28c,
+ 0x5b10dbc2, 0x1688a832, 0xf0d9306d, 0x04980f79, 0x00d209db, 0xfea02719, 0x007ae1a7, 0xfff85bb1,
+ 0x5af34f18, 0x15e11453, 0xf0ee72db, 0x04abe310, 0x00c12439, 0xfea5b926, 0x007abc20, 0xfff7e910,
+ 0x5ad337af, 0x153a5d5e, 0xf104c0d2, 0x04bf16e9, 0x00b061eb, 0xfeab576d, 0x007a8a49, 0xfff77a9f,
+ 0x5ab09748, 0x14948a16, 0xf11c1583, 0x04d1ab0d, 0x009fc413, 0xfeb10134, 0x007a4c5d, 0xfff71057,
+ 0x5a8b6fc7, 0x13efa12c, 0xf1346c17, 0x04e39f93, 0x008f4bcb, 0xfeb6b5c0, 0x007a029a, 0xfff6aa2b,
+ 0x5a63c336, 0x134ba937, 0xf14dbfb1, 0x04f4f4a2, 0x007efa29, 0xfebc745c, 0x0079ad3d, 0xfff64812,
+ 0x5a3993c0, 0x12a8a8bb, 0xf1680b6e, 0x0505aa6a, 0x006ed038, 0xfec23c50, 0x00794c82, 0xfff5ea02,
+ 0x5a0ce3b2, 0x1206a625, 0xf1834a63, 0x0515c12d, 0x005ecf01, 0xfec80ce8, 0x0078e0a9, 0xfff58ff0,
+ 0x59ddb57f, 0x1165a7cc, 0xf19f77a0, 0x05253938, 0x004ef782, 0xfecde571, 0x007869ee, 0xfff539cf,
+ 0x59ac0bba, 0x10c5b3ef, 0xf1bc8e31, 0x053412e4, 0x003f4ab4, 0xfed3c538, 0x0077e891, 0xfff4e794,
+ 0x5977e919, 0x1026d0b8, 0xf1da891b, 0x05424e9b, 0x002fc98a, 0xfed9ab8f, 0x00775ccf, 0xfff49934,
+ 0x59415075, 0x0f890437, 0xf1f96360, 0x054feccf, 0x002074ed, 0xfedf97c6, 0x0076c6e8, 0xfff44ea3,
+ 0x590844c9, 0x0eec5465, 0xf21917ff, 0x055cee03, 0x00114dc3, 0xfee58932, 0x00762719, 0xfff407d2,
+ 0x58ccc930, 0x0e50c723, 0xf239a1ef, 0x056952c3, 0x000254e8, 0xfeeb7f27, 0x00757da3, 0xfff3c4b7,
+ 0x588ee0ea, 0x0db6623b, 0xf25afc29, 0x05751baa, 0xfff38b32, 0xfef178fc, 0x0074cac4, 0xfff38542,
+ 0x584e8f56, 0x0d1d2b5d, 0xf27d219f, 0x0580495c, 0xffe4f171, 0xfef7760c, 0x00740ebb, 0xfff34968,
+ 0x580bd7f4, 0x0c85281f, 0xf2a00d43, 0x058adc8d, 0xffd6886d, 0xfefd75af, 0x007349c7, 0xfff3111b,
+ 0x57c6be67, 0x0bee5dff, 0xf2c3ba04, 0x0594d5fa, 0xffc850e6, 0xff037744, 0x00727c27, 0xfff2dc4c,
+ 0x577f4670, 0x0b58d262, 0xf2e822ce, 0x059e366c, 0xffba4b98, 0xff097a29, 0x0071a61b, 0xfff2aaef,
+ 0x573573f2, 0x0ac48a92, 0xf30d428e, 0x05a6feb9, 0xffac7936, 0xff0f7dbf, 0x0070c7e1, 0xfff27cf3,
+ 0x56e94af1, 0x0a318bc1, 0xf333142f, 0x05af2fbf, 0xff9eda6d, 0xff15816a, 0x006fe1b8, 0xfff2524c,
+ 0x569acf90, 0x099fdb04, 0xf359929a, 0x05b6ca6b, 0xff916fe1, 0xff1b848e, 0x006ef3df, 0xfff22aea,
+ 0x564a0610, 0x090f7d57, 0xf380b8ba, 0x05bdcfb2, 0xff843a32, 0xff218692, 0x006dfe94, 0xfff206bf,
+ 0x55f6f2d3, 0x0880779d, 0xf3a88179, 0x05c44095, 0xff7739f7, 0xff2786e1, 0x006d0217, 0xfff1e5bb,
+ 0x55a19a5c, 0x07f2ce9b, 0xf3d0e7c2, 0x05ca1e1f, 0xff6a6fc1, 0xff2d84e5, 0x006bfea4, 0xfff1c7d0,
+ 0x554a0148, 0x076686fc, 0xf3f9e680, 0x05cf6965, 0xff5ddc1a, 0xff33800e, 0x006af47b, 0xfff1acef,
+ 0x54f02c56, 0x06dba551, 0xf42378a0, 0x05d42387, 0xff517f86, 0xff3977cb, 0x0069e3d9, 0xfff19508,
+ 0x54942061, 0x06522e0f, 0xf44d9912, 0x05d84daf, 0xff455a80, 0xff3f6b8f, 0x0068ccfa, 0xfff1800b,
+ 0x5435e263, 0x05ca258f, 0xf47842c5, 0x05dbe90f, 0xff396d7f, 0xff455acf, 0x0067b01e, 0xfff16de9,
+ 0x53d57774, 0x0543900d, 0xf4a370ad, 0x05def6e4, 0xff2db8f2, 0xff4b4503, 0x00668d80, 0xfff15e93,
+ 0x5372e4c6, 0x04be71ab, 0xf4cf1dbf, 0x05e17873, 0xff223d40, 0xff5129a3, 0x0065655d, 0xfff151f9,
+ 0x530e2fac, 0x043ace6e, 0xf4fb44f4, 0x05e36f0d, 0xff16faca, 0xff57082e, 0x006437f1, 0xfff1480b,
+ 0x52a75d90, 0x03b8aa40, 0xf527e149, 0x05e4dc08, 0xff0bf1ed, 0xff5ce021, 0x00630577, 0xfff140b9,
+ 0x523e73fd, 0x033808eb, 0xf554edbd, 0x05e5c0c6, 0xff0122fc, 0xff62b0fd, 0x0061ce2c, 0xfff13bf3,
+ 0x51d37897, 0x02b8ee22, 0xf5826555, 0x05e61eae, 0xfef68e45, 0xff687a47, 0x00609249, 0xfff139aa,
+ 0x5166711c, 0x023b5d76, 0xf5b0431a, 0x05e5f733, 0xfeec340f, 0xff6e3b84, 0x005f520a, 0xfff139cd,
+ 0x50f76368, 0x01bf5a5e, 0xf5de8218, 0x05e54bcd, 0xfee2149b, 0xff73f43d, 0x005e0da8, 0xfff13c4c,
+ 0x5086556f, 0x0144e834, 0xf60d1d63, 0x05e41dfe, 0xfed83023, 0xff79a3fe, 0x005cc55c, 0xfff14119,
+ 0x50134d3e, 0x00cc0a36, 0xf63c1012, 0x05e26f4e, 0xfece86db, 0xff7f4a54, 0x005b7961, 0xfff14821,
+ 0x4f9e50ff, 0x0054c382, 0xf66b5544, 0x05e0414d, 0xfec518f1, 0xff84e6d0, 0x005a29ed, 0xfff15156,
+ 0x4f2766f2, 0xffdf171b, 0xf69ae81d, 0x05dd9593, 0xfebbe68c, 0xff8a7905, 0x0058d738, 0xfff15ca8,
+ 0x4eae9571, 0xff6b07e7, 0xf6cac3c7, 0x05da6dbe, 0xfeb2efcd, 0xff900089, 0x0057817b, 0xfff16a07,
+ 0x4e33e2ee, 0xfef898ae, 0xf6fae373, 0x05d6cb72, 0xfeaa34d0, 0xff957cf4, 0x005628ec, 0xfff17962,
+ 0x4db755f3, 0xfe87cc1b, 0xf72b425b, 0x05d2b05c, 0xfea1b5a9, 0xff9aede0, 0x0054cdc0, 0xfff18aab,
+ 0x4d38f520, 0xfe18a4bc, 0xf75bdbbd, 0x05ce1e2d, 0xfe997268, 0xffa052ec, 0x0053702d, 0xfff19dd1,
+ 0x4cb8c72e, 0xfdab2501, 0xf78caae0, 0x05c9169d, 0xfe916b15, 0xffa5abb8, 0x00521068, 0xfff1b2c5,
+ 0x4c36d2eb, 0xfd3f4f3d, 0xf7bdab16, 0x05c39b6a, 0xfe899fb2, 0xffaaf7e6, 0x0050aea5, 0xfff1c976,
+ 0x4bb31f3c, 0xfcd525a5, 0xf7eed7b4, 0x05bdae57, 0xfe82103f, 0xffb0371c, 0x004f4b17, 0xfff1e1d6,
+ 0x4b2db31a, 0xfc6caa53, 0xf8202c1c, 0x05b7512e, 0xfe7abcb1, 0xffb56902, 0x004de5f1, 0xfff1fbd5,
+ 0x4aa69594, 0xfc05df40, 0xf851a3b6, 0x05b085bc, 0xfe73a4fb, 0xffba8d44, 0x004c7f66, 0xfff21764,
+ 0x4a1dcdce, 0xfba0c64b, 0xf88339f5, 0x05a94dd5, 0xfe6cc909, 0xffbfa38d, 0x004b17a6, 0xfff23473,
+ 0x499362ff, 0xfb3d6133, 0xf8b4ea55, 0x05a1ab52, 0xfe6628c1, 0xffc4ab8f, 0x0049aee3, 0xfff252f3,
+ 0x49075c72, 0xfadbb19a, 0xf8e6b059, 0x0599a00e, 0xfe5fc405, 0xffc9a4fc, 0x0048454b, 0xfff272d6,
+ 0x4879c185, 0xfa7bb908, 0xf9188793, 0x05912dea, 0xfe599aaf, 0xffce8f8a, 0x0046db0f, 0xfff2940b,
+ 0x47ea99a9, 0xfa1d78e3, 0xf94a6b9b, 0x058856cd, 0xfe53ac97, 0xffd36af1, 0x0045705c, 0xfff2b686,
+ 0x4759ec60, 0xf9c0f276, 0xf97c5815, 0x057f1c9e, 0xfe4df98e, 0xffd836eb, 0x00440561, 0xfff2da36,
+ 0x46c7c140, 0xf96626f0, 0xf9ae48af, 0x0575814c, 0xfe48815e, 0xffdcf336, 0x00429a4a, 0xfff2ff0d,
+ 0x46341fed, 0xf90d1761, 0xf9e03924, 0x056b86c6, 0xfe4343d0, 0xffe19f91, 0x00412f43, 0xfff324fd,
+ 0x459f101d, 0xf8b5c4be, 0xfa122537, 0x05612f00, 0xfe3e40a6, 0xffe63bc0, 0x003fc478, 0xfff34bf9,
+ 0x45089996, 0xf8602fdc, 0xfa4408ba, 0x05567bf1, 0xfe39779a, 0xffeac787, 0x003e5a12, 0xfff373f0,
+ 0x4470c42d, 0xf80c5977, 0xfa75df87, 0x054b6f92, 0xfe34e867, 0xffef42af, 0x003cf03d, 0xfff39cd7,
+ 0x43d797c7, 0xf7ba422b, 0xfaa7a586, 0x05400be1, 0xfe3092bf, 0xfff3ad01, 0x003b871f, 0xfff3c69f,
+ 0x433d1c56, 0xf769ea78, 0xfad956ab, 0x053452dc, 0xfe2c7650, 0xfff8064b, 0x003a1ee3, 0xfff3f13a,
+ 0x42a159dc, 0xf71b52c4, 0xfb0aeef6, 0x05284685, 0xfe2892c5, 0xfffc4e5c, 0x0038b7ae, 0xfff41c9c,
+ 0x42045865, 0xf6ce7b57, 0xfb3c6a73, 0x051be8dd, 0xfe24e7c3, 0x00008507, 0x003751a7, 0xfff448b7,
+ 0x4166200e, 0xf683645a, 0xfb6dc53c, 0x050f3bec, 0xfe2174ec, 0x0004aa1f, 0x0035ecf4, 0xfff4757e,
+ 0x40c6b8fd, 0xf63a0ddf, 0xfb9efb77, 0x050241b6, 0xfe1e39da, 0x0008bd7c, 0x003489b9, 0xfff4a2e5,
+ 0x40262b65, 0xf5f277d9, 0xfbd00956, 0x04f4fc46, 0xfe1b3628, 0x000cbef7, 0x0033281a, 0xfff4d0de,
+ 0x3f847f83, 0xf5aca21f, 0xfc00eb1b, 0x04e76da3, 0xfe18696a, 0x0010ae6e, 0x0031c83a, 0xfff4ff5d,
+ 0x3ee1bda2, 0xf5688c6d, 0xfc319d13, 0x04d997d8, 0xfe15d32f, 0x00148bbd, 0x00306a3b, 0xfff52e57,
+ 0x3e3dee13, 0xf5263665, 0xfc621b9a, 0x04cb7cf2, 0xfe137304, 0x001856c7, 0x002f0e3f, 0xfff55dbf,
+ 0x3d991932, 0xf4e59f8a, 0xfc926319, 0x04bd1efb, 0xfe114872, 0x001c0f6e, 0x002db466, 0xfff58d89,
+ 0x3cf34766, 0xf4a6c748, 0xfcc27008, 0x04ae8000, 0xfe0f52fc, 0x001fb599, 0x002c5cd0, 0xfff5bdaa,
+ 0x3c4c811c, 0xf469aced, 0xfcf23eec, 0x049fa20f, 0xfe0d9224, 0x0023492f, 0x002b079a, 0xfff5ee17,
+ 0x3ba4cec9, 0xf42e4faf, 0xfd21cc59, 0x04908733, 0xfe0c0567, 0x0026ca1c, 0x0029b4e4, 0xfff61ec5,
+ 0x3afc38eb, 0xf3f4aea6, 0xfd5114f0, 0x0481317a, 0xfe0aac3f, 0x002a384c, 0x002864c9, 0xfff64fa8,
+ 0x3a52c805, 0xf3bcc8d3, 0xfd801564, 0x0471a2ef, 0xfe098622, 0x002d93ae, 0x00271766, 0xfff680b5,
+ 0x39a884a1, 0xf3869d1a, 0xfdaeca73, 0x0461dda0, 0xfe089283, 0x0030dc34, 0x0025ccd7, 0xfff6b1e4,
+ 0x38fd774e, 0xf3522a49, 0xfddd30eb, 0x0451e396, 0xfe07d0d3, 0x003411d2, 0x00248535, 0xfff6e329,
+ 0x3851a8a2, 0xf31f6f0f, 0xfe0b45aa, 0x0441b6dd, 0xfe07407d, 0x0037347d, 0x0023409a, 0xfff7147a,
+ 0x37a52135, 0xf2ee6a07, 0xfe39059b, 0x0431597d, 0xfe06e0eb, 0x003a442e, 0x0021ff1f, 0xfff745cd,
+ 0x36f7e9a4, 0xf2bf19ae, 0xfe666dbc, 0x0420cd80, 0xfe06b184, 0x003d40e0, 0x0020c0dc, 0xfff7771a,
+ 0x364a0a90, 0xf2917c6d, 0xfe937b15, 0x041014eb, 0xfe06b1ac, 0x00402a8e, 0x001f85e6, 0xfff7a857,
+ 0x359b8c9d, 0xf265908f, 0xfec02ac2, 0x03ff31c3, 0xfe06e0c4, 0x00430137, 0x001e4e56, 0xfff7d97a,
+ 0x34ec786f, 0xf23b544b, 0xfeec79ec, 0x03ee260d, 0xfe073e2a, 0x0045c4dd, 0x001d1a3f, 0xfff80a7c,
+ 0x343cd6af, 0xf212c5be, 0xff1865cd, 0x03dcf3ca, 0xfe07c93a, 0x00487582, 0x001be9b7, 0xfff83b52,
+ 0x338cb004, 0xf1ebe2ec, 0xff43ebac, 0x03cb9cf9, 0xfe08814e, 0x004b132b, 0x001abcd0, 0xfff86bf6,
+ 0x32dc0d17, 0xf1c6a9c3, 0xff6f08e4, 0x03ba2398, 0xfe0965bc, 0x004d9dde, 0x0019939d, 0xfff89c60,
+ 0x322af693, 0xf1a3181a, 0xff99badb, 0x03a889a1, 0xfe0a75da, 0x005015a5, 0x00186e31, 0xfff8cc86,
+ 0x3179751f, 0xf1812bb0, 0xffc3ff0c, 0x0396d10c, 0xfe0bb0f9, 0x00527a8a, 0x00174c9c, 0xfff8fc62,
+ 0x30c79163, 0xf160e22d, 0xffedd2fd, 0x0384fbd1, 0xfe0d166b, 0x0054cc9a, 0x00162eef, 0xfff92bec,
+ 0x30155404, 0xf1423924, 0x00173447, 0x03730be0, 0xfe0ea57e, 0x00570be4, 0x00151538, 0xfff95b1e,
+ 0x2f62c5a7, 0xf1252e0f, 0x00402092, 0x0361032a, 0xfe105d7e, 0x00593877, 0x0013ff88, 0xfff989ef,
+ 0x2eafeeed, 0xf109be56, 0x00689598, 0x034ee39b, 0xfe123db6, 0x005b5267, 0x0012edea, 0xfff9b85b,
+ 0x2dfcd873, 0xf0efe748, 0x0090911f, 0x033caf1d, 0xfe144570, 0x005d59c6, 0x0011e06d, 0xfff9e65a,
+ 0x2d498ad3, 0xf0d7a622, 0x00b81102, 0x032a6796, 0xfe1673f2, 0x005f4eac, 0x0010d71d, 0xfffa13e5,
+ 0x2c960ea3, 0xf0c0f808, 0x00df1328, 0x03180ee7, 0xfe18c884, 0x0061312e, 0x000fd205, 0xfffa40f8,
+ 0x2be26c73, 0xf0abda0e, 0x0105958c, 0x0305a6f0, 0xfe1b4268, 0x00630167, 0x000ed130, 0xfffa6d8d,
+ 0x2b2eaccf, 0xf0984931, 0x012b9635, 0x02f3318a, 0xfe1de0e2, 0x0064bf71, 0x000dd4a7, 0xfffa999d,
+ 0x2a7ad83c, 0xf086425a, 0x0151133e, 0x02e0b08d, 0xfe20a335, 0x00666b68, 0x000cdc74, 0xfffac525,
+ 0x29c6f738, 0xf075c260, 0x01760ad1, 0x02ce25ca, 0xfe2388a1, 0x0068056b, 0x000be89f, 0xfffaf01e,
+ 0x2913123c, 0xf066c606, 0x019a7b27, 0x02bb9310, 0xfe269065, 0x00698d98, 0x000af931, 0xfffb1a84,
+ 0x285f31b7, 0xf05949fb, 0x01be628c, 0x02a8fa2a, 0xfe29b9c1, 0x006b0411, 0x000a0e2f, 0xfffb4453,
+ 0x27ab5e12, 0xf04d4ade, 0x01e1bf58, 0x02965cdb, 0xfe2d03f2, 0x006c68f8, 0x000927a0, 0xfffb6d86,
+ 0x26f79fab, 0xf042c539, 0x02048ff8, 0x0283bce6, 0xfe306e35, 0x006dbc71, 0x00084589, 0xfffb961a,
+ 0x2643feda, 0xf039b587, 0x0226d2e6, 0x02711c05, 0xfe33f7c7, 0x006efea0, 0x000767f0, 0xfffbbe09,
+ 0x259083eb, 0xf032182f, 0x024886ad, 0x025e7bf0, 0xfe379fe3, 0x00702fae, 0x00068ed8, 0xfffbe552,
+ 0x24dd3721, 0xf02be98a, 0x0269a9e9, 0x024bde5a, 0xfe3b65c4, 0x00714fc0, 0x0005ba46, 0xfffc0bef,
+ 0x242a20b3, 0xf02725dc, 0x028a3b44, 0x023944ee, 0xfe3f48a5, 0x00725f02, 0x0004ea3a, 0xfffc31df,
+ 0x237748cf, 0xf023c95d, 0x02aa397b, 0x0226b156, 0xfe4347c0, 0x00735d9c, 0x00041eb9, 0xfffc571e,
+ 0x22c4b795, 0xf021d031, 0x02c9a359, 0x02142533, 0xfe476250, 0x00744bba, 0x000357c2, 0xfffc7ba9,
+ 0x2212751a, 0xf0213671, 0x02e877b9, 0x0201a223, 0xfe4b978e, 0x0075298a, 0x00029558, 0xfffc9f7e,
+ 0x21608968, 0xf021f823, 0x0306b586, 0x01ef29be, 0xfe4fe6b3, 0x0075f739, 0x0001d779, 0xfffcc29a,
+ 0x20aefc79, 0xf0241140, 0x03245bbc, 0x01dcbd96, 0xfe544efb, 0x0076b4f5, 0x00011e26, 0xfffce4fc,
+ 0x1ffdd63b, 0xf0277db1, 0x03416966, 0x01ca5f37, 0xfe58cf9d, 0x007762f0, 0x0000695e, 0xfffd06a1,
+ 0x1f4d1e8e, 0xf02c3953, 0x035ddd9e, 0x01b81028, 0xfe5d67d4, 0x0078015a, 0xffffb91f, 0xfffd2787,
+ 0x1e9cdd43, 0xf0323ff5, 0x0379b790, 0x01a5d1ea, 0xfe6216db, 0x00789065, 0xffff0d66, 0xfffd47ae,
+ 0x1ded1a1d, 0xf0398d56, 0x0394f674, 0x0193a5f9, 0xfe66dbeb, 0x00791043, 0xfffe6631, 0xfffd6713,
};
// we use 15 bits to interpolate between these samples
@@ -96,12 +353,16 @@ void AudioResamplerSinc::init_routine()
return;
}
- readResampleCoefficients = (readCoefficientsFn) dlsym(resampleCoeffLib,
- "readResamplerCoefficients");
- readResampleFirNumCoeffFn readResampleFirNumCoeff = (readResampleFirNumCoeffFn)
+ readResampleFirNumCoeffFn readResampleFirNumCoeff;
+ readResampleFirLerpIntBitsFn readResampleFirLerpIntBits;
+
+ readResampleCoefficients = (readCoefficientsFn)
+ dlsym(resampleCoeffLib, "readResamplerCoefficients");
+ readResampleFirNumCoeff = (readResampleFirNumCoeffFn)
dlsym(resampleCoeffLib, "readResampleFirNumCoeff");
- readResampleFirLerpIntBitsFn readResampleFirLerpIntBits = (readResampleFirLerpIntBitsFn)
+ readResampleFirLerpIntBits = (readResampleFirLerpIntBitsFn)
dlsym(resampleCoeffLib, "readResampleFirLerpIntBits");
+
if (!readResampleCoefficients || !readResampleFirNumCoeff || !readResampleFirLerpIntBits) {
readResampleCoefficients = NULL;
dlclose(resampleCoeffLib);
@@ -111,15 +372,14 @@ void AudioResamplerSinc::init_routine()
}
c = &veryHighQualityConstants;
- // we have 16 coefs samples per zero-crossing
c->coefsBits = readResampleFirLerpIntBits();
- ALOGV("coefsBits = %d", c->coefsBits);
c->cShift = kNumPhaseBits - c->coefsBits;
c->cMask = ((1<<c->coefsBits)-1) << c->cShift;
c->pShift = kNumPhaseBits - c->coefsBits - pLerpBits;
c->pMask = ((1<<pLerpBits)-1) << c->pShift;
// number of zero-crossing on each side
c->halfNumCoefs = readResampleFirNumCoeff();
+ ALOGV("coefsBits = %d", c->coefsBits);
ALOGV("halfNumCoefs = %d", c->halfNumCoefs);
// note that we "leak" resampleCoeffLib until the process exits
}
@@ -129,7 +389,7 @@ void AudioResamplerSinc::init_routine()
static inline
int32_t mulRL(int left, int32_t in, uint32_t vRL)
{
-#if defined(__arm__) && !defined(__thumb__)
+#if USE_INLINE_ASSEMBLY
int32_t out;
if (left) {
asm( "smultb %[out], %[in], %[vRL] \n"
@@ -144,18 +404,15 @@ int32_t mulRL(int left, int32_t in, uint32_t vRL)
}
return out;
#else
- if (left) {
- return int16_t(in>>16) * int16_t(vRL&0xFFFF);
- } else {
- return int16_t(in>>16) * int16_t(vRL>>16);
- }
+ int16_t v = left ? int16_t(vRL) : int16_t(vRL>>16);
+ return int32_t((int64_t(in) * v) >> 16);
#endif
}
static inline
int32_t mulAdd(int16_t in, int32_t v, int32_t a)
{
-#if defined(__arm__) && !defined(__thumb__)
+#if USE_INLINE_ASSEMBLY
int32_t out;
asm( "smlawb %[out], %[v], %[in], %[a] \n"
: [out]"=r"(out)
@@ -163,16 +420,14 @@ int32_t mulAdd(int16_t in, int32_t v, int32_t a)
: );
return out;
#else
- return a + in * (v>>16);
- // improved precision
- // return a + in * (v>>16) + ((in * (v & 0xffff)) >> 16);
+ return a + int32_t((int64_t(v) * in) >> 16);
#endif
}
static inline
int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
{
-#if defined(__arm__) && !defined(__thumb__)
+#if USE_INLINE_ASSEMBLY
int32_t out;
if (left) {
asm( "smlawb %[out], %[v], %[inRL], %[a] \n"
@@ -187,13 +442,8 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
}
return out;
#else
- if (left) {
- return a + (int16_t(inRL&0xFFFF) * (v>>16));
- //improved precision
- // return a + (int16_t(inRL&0xFFFF) * (v>>16)) + ((int16_t(inRL&0xFFFF) * (v & 0xffff)) >> 16);
- } else {
- return a + (int16_t(inRL>>16) * (v>>16));
- }
+ int16_t s = left ? int16_t(inRL) : int16_t(inRL>>16);
+ return a + int32_t((int64_t(v) * s) >> 16);
#endif
}
@@ -202,7 +452,7 @@ int32_t mulAddRL(int left, uint32_t inRL, int32_t v, int32_t a)
AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
int inChannelCount, int32_t sampleRate, src_quality quality)
: AudioResampler(bitDepth, inChannelCount, sampleRate, quality),
- mState(0)
+ mState(0), mImpulse(0), mRingFull(0), mFirCoefs(0)
{
/*
* Layout of the state buffer for 32 tap:
@@ -220,45 +470,49 @@ AudioResamplerSinc::AudioResamplerSinc(int bitDepth,
*
*/
+ mVolumeSIMD[0] = 0;
+ mVolumeSIMD[1] = 0;
+
// Load the constants for coefficients
int ok = pthread_once(&once_control, init_routine);
if (ok != 0) {
ALOGE("%s pthread_once failed: %d", __func__, ok);
}
- mConstants = (quality == VERY_HIGH_QUALITY) ? &veryHighQualityConstants : &highQualityConstants;
+ mConstants = (quality == VERY_HIGH_QUALITY) ?
+ &veryHighQualityConstants : &highQualityConstants;
}
-AudioResamplerSinc::~AudioResamplerSinc()
-{
- delete[] mState;
+AudioResamplerSinc::~AudioResamplerSinc() {
+ free(mState);
}
void AudioResamplerSinc::init() {
- const Constants *c = mConstants;
-
- const size_t numCoefs = 2*c->halfNumCoefs;
+ const Constants& c(*mConstants);
+ const size_t numCoefs = 2 * c.halfNumCoefs;
const size_t stateSize = numCoefs * mChannelCount * 2;
- mState = new int16_t[stateSize];
+ mState = (int16_t*)memalign(32, stateSize*sizeof(int16_t));
memset(mState, 0, sizeof(int16_t)*stateSize);
- mImpulse = mState + (c->halfNumCoefs-1)*mChannelCount;
+ mImpulse = mState + (c.halfNumCoefs-1)*mChannelCount;
mRingFull = mImpulse + (numCoefs+1)*mChannelCount;
}
+void AudioResamplerSinc::setVolume(int16_t left, int16_t right) {
+ AudioResampler::setVolume(left, right);
+ mVolumeSIMD[0] = int32_t(left)<<16;
+ mVolumeSIMD[1] = int32_t(right)<<16;
+}
+
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
-
// FIXME store current state (up or down sample) and only load the coefs when the state
// changes. Or load two pointers one for up and one for down in the init function.
// Not critical now since the read functions are fast, but would be important if read was slow.
if (mConstants == &veryHighQualityConstants && readResampleCoefficients) {
- ALOGV("get coefficient from libmm-audio resampler library");
- mFirCoefs = (mInSampleRate <= mSampleRate) ? readResampleCoefficients(true) :
- readResampleCoefficients(false);
+ mFirCoefs = readResampleCoefficients( mInSampleRate <= mSampleRate );
} else {
- ALOGV("Use default coefficients");
- mFirCoefs = (mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown;
+ mFirCoefs = (const int32_t *) ((mInSampleRate <= mSampleRate) ? mFirCoefsUp : mFirCoefsDown);
}
// select the appropriate resampler
@@ -270,7 +524,6 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
resample<2>(out, outFrameCount, provider);
break;
}
-
}
@@ -278,7 +531,8 @@ template<int CHANNELS>
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider)
{
- const Constants *c = mConstants;
+ const Constants& c(*mConstants);
+ const size_t headOffset = c.halfNumCoefs*CHANNELS;
int16_t* impulse = mImpulse;
uint32_t vRL = mVolumeRL;
size_t inputIndex = mInputIndex;
@@ -313,43 +567,31 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
}
}
}
- int16_t *in = mBuffer.i16;
+ int16_t const * const in = mBuffer.i16;
const size_t frameCount = mBuffer.frameCount;
// Always read-in the first samples from the input buffer
- int16_t* head = impulse + c->halfNumCoefs*CHANNELS;
- head[0] = in[inputIndex*CHANNELS + 0];
- if (CHANNELS == 2)
- head[1] = in[inputIndex*CHANNELS + 1];
+ int16_t* head = impulse + headOffset;
+ for (size_t i=0 ; i<CHANNELS ; i++) {
+ head[i] = in[inputIndex*CHANNELS + i];
+ }
// handle boundary case
- int32_t l, r;
- while (outputIndex < outputSampleCount) {
- filterCoefficient<CHANNELS>(l, r, phaseFraction, impulse);
- out[outputIndex++] += 2 * mulRL(1, l, vRL);
- out[outputIndex++] += 2 * mulRL(0, r, vRL);
+ while (CC_LIKELY(outputIndex < outputSampleCount)) {
+ filterCoefficient<CHANNELS>(&out[outputIndex], phaseFraction, impulse, vRL);
+ outputIndex += 2;
phaseFraction += phaseIncrement;
- const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
- if (phaseIndex == 1) {
- inputIndex++;
- if (inputIndex >= frameCount)
- break; // need a new buffer
- read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
- } else if (phaseIndex == 2) { // maximum value
+ const size_t phaseIndex = phaseFraction >> kNumPhaseBits;
+ for (size_t i=0 ; i<phaseIndex ; i++) {
inputIndex++;
- if (inputIndex >= frameCount)
- break; // 0 frame available, 2 frames needed
- // read first frame
- read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
- inputIndex++;
- if (inputIndex >= frameCount)
- break; // 0 frame available, 1 frame needed
- // read second frame
+ if (inputIndex >= frameCount) {
+ goto done; // need a new buffer
+ }
read<CHANNELS>(impulse, phaseFraction, in, inputIndex);
}
}
-
+done:
// if done with buffer, save samples
if (inputIndex >= frameCount) {
inputIndex -= frameCount;
@@ -375,66 +617,215 @@ void AudioResamplerSinc::read(
int16_t*& impulse, uint32_t& phaseFraction,
const int16_t* in, size_t inputIndex)
{
- const Constants *c = mConstants;
- const uint32_t phaseIndex = phaseFraction >> kNumPhaseBits;
impulse += CHANNELS;
phaseFraction -= 1LU<<kNumPhaseBits;
- if (impulse >= mRingFull) {
- const size_t stateSize = (c->halfNumCoefs*2)*CHANNELS;
+
+ const Constants& c(*mConstants);
+ if (CC_UNLIKELY(impulse >= mRingFull)) {
+ const size_t stateSize = (c.halfNumCoefs*2)*CHANNELS;
memcpy(mState, mState+stateSize, sizeof(int16_t)*stateSize);
impulse -= stateSize;
}
- int16_t* head = impulse + c->halfNumCoefs*CHANNELS;
- head[0] = in[inputIndex*CHANNELS + 0];
- if (CHANNELS == 2)
- head[1] = in[inputIndex*CHANNELS + 1];
+
+ int16_t* head = impulse + c.halfNumCoefs*CHANNELS;
+ for (size_t i=0 ; i<CHANNELS ; i++) {
+ head[i] = in[inputIndex*CHANNELS + i];
+ }
}
template<int CHANNELS>
void AudioResamplerSinc::filterCoefficient(
- int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples)
+ int32_t* out, uint32_t phase, const int16_t *samples, uint32_t vRL)
{
- const Constants *c = mConstants;
+ // NOTE: be very careful when modifying the code here. register
+ // pressure is very high and a small change might cause the compiler
+ // to generate far less efficient code.
+ // Always sanity check the result with objdump or test-resample.
// compute the index of the coefficient on the positive side and
// negative side
- uint32_t indexP = (phase & c->cMask) >> c->cShift;
- uint16_t lerpP = (phase & c->pMask) >> c->pShift;
- uint32_t indexN = (-phase & c->cMask) >> c->cShift;
- uint16_t lerpN = (-phase & c->pMask) >> c->pShift;
- if ((indexP == 0) && (lerpP == 0)) {
- indexN = c->cMask >> c->cShift;
- lerpN = c->pMask >> c->pShift;
- }
-
- l = 0;
- r = 0;
- const int32_t* coefs = mFirCoefs;
- const int16_t *sP = samples;
- const int16_t *sN = samples+CHANNELS;
- for (unsigned int i=0 ; i < c->halfNumCoefs/4 ; i++) {
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits;
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits;
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits;
- interpolate<CHANNELS>(l, r, coefs+indexP, lerpP, sP);
- interpolate<CHANNELS>(l, r, coefs+indexN, lerpN, sN);
- sP -= CHANNELS; sN += CHANNELS; coefs += 1 << c->coefsBits;
+ const Constants& c(*mConstants);
+ const int32_t ONE = c.cMask | c.pMask;
+ uint32_t indexP = ( phase & c.cMask) >> c.cShift;
+ uint32_t lerpP = ( phase & c.pMask) >> c.pShift;
+ uint32_t indexN = ((ONE-phase) & c.cMask) >> c.cShift;
+ uint32_t lerpN = ((ONE-phase) & c.pMask) >> c.pShift;
+
+ const size_t offset = c.halfNumCoefs;
+ indexP *= offset;
+ indexN *= offset;
+
+ int32_t const* coefsP = mFirCoefs + indexP;
+ int32_t const* coefsN = mFirCoefs + indexN;
+ int16_t const* sP = samples;
+ int16_t const* sN = samples + CHANNELS;
+
+ size_t count = offset;
+
+ if (!USE_NEON) {
+ int32_t l = 0;
+ int32_t r = 0;
+ for (size_t i=0 ; i<count ; i++) {
+ interpolate<CHANNELS>(l, r, coefsP++, offset, lerpP, sP);
+ sP -= CHANNELS;
+ interpolate<CHANNELS>(l, r, coefsN++, offset, lerpN, sN);
+ sN += CHANNELS;
+ }
+ out[0] += 2 * mulRL(1, l, vRL);
+ out[1] += 2 * mulRL(0, r, vRL);
+ } else if (CHANNELS == 1) {
+ int32_t const* coefsP1 = coefsP + offset;
+ int32_t const* coefsN1 = coefsN + offset;
+ sP -= CHANNELS*3;
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n" // load the positive phase
+ "vmov.32 d2[1], %[lerpN] \n" // load the negative phase
+ "veor q0, q0, q0 \n" // result, initialize to 0
+ "vshl.s32 d2, d2, #16 \n" // convert to 32 bits
+
+ "1: \n"
+ "vld1.16 { d4}, [%[sP]] \n" // load 4 16-bits stereo samples
+ "vld1.32 { q8}, [%[coefsP0]:128]! \n" // load 4 32-bits coefs
+ "vld1.32 { q9}, [%[coefsP1]:128]! \n" // load 4 32-bits coefs for interpolation
+ "vld1.16 { d6}, [%[sN]]! \n" // load 4 16-bits stereo samples
+ "vld1.32 {q10}, [%[coefsN0]:128]! \n" // load 4 32-bits coefs
+ "vld1.32 {q11}, [%[coefsN1]:128]! \n" // load 4 32-bits coefs for interpolation
+
+ "vrev64.16 d4, d4 \n" // reverse 2 frames of the positive side
+
+ "vsub.s32 q9, q9, q8 \n" // interpolate (step1) 1st set of coefs
+ "vsub.s32 q11, q11, q10 \n" // interpolate (step1) 2nd set of coets
+ "vshll.s16 q12, d4, #15 \n" // extend samples to 31 bits
+
+ "vqrdmulh.s32 q9, q9, d2[0] \n" // interpolate (step2) 1st set of coefs
+ "vqrdmulh.s32 q11, q11, d2[1] \n" // interpolate (step3) 2nd set of coefs
+ "vshll.s16 q14, d6, #15 \n" // extend samples to 31 bits
+
+ "vadd.s32 q8, q8, q9 \n" // interpolate (step3) 1st set
+ "vadd.s32 q10, q10, q11 \n" // interpolate (step4) 2nd set
+ "subs %[count], %[count], #4 \n" // update loop counter
+
+ "vqrdmulh.s32 q12, q12, q8 \n" // multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n" // multiply samples by interpolated coef
+ "sub %[sP], %[sP], #8 \n" // move pointer to next set of samples
+
+ "vadd.s32 q0, q0, q12 \n" // accumulate result
+ "vadd.s32 q0, q0, q14 \n" // accumulate result
+
+ "bne 1b \n" // loop
+
+ "vld1.s32 {d2}, [%[vLR]] \n" // load volumes
+ "vld1.s32 {d3}, %[out] \n" // load the output
+ "vpadd.s32 d0, d0, d1 \n" // add all 4 partial sums
+ "vpadd.s32 d0, d0, d0 \n" // together
+ "vdup.i32 d0, d0[0] \n" // interleave L,R channels
+ "vqrdmulh.s32 d0, d0, d2 \n" // apply volume
+ "vadd.s32 d3, d3, d0 \n" // accumulate result
+ "vst1.s32 {d3}, %[out] \n" // store result
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN0] "+r" (coefsN),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [lerpN] "r" (lerpN),
+ [vLR] "r" (mVolumeSIMD)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3",
+ "q8", "q9", "q10", "q11",
+ "q12", "q14"
+ );
+ } else if (CHANNELS == 2) {
+ int32_t const* coefsP1 = coefsP + offset;
+ int32_t const* coefsN1 = coefsN + offset;
+ sP -= CHANNELS*3;
+ asm (
+ "vmov.32 d2[0], %[lerpP] \n" // load the positive phase
+ "vmov.32 d2[1], %[lerpN] \n" // load the negative phase
+ "veor q0, q0, q0 \n" // result, initialize to 0
+ "veor q4, q4, q4 \n" // result, initialize to 0
+ "vshl.s32 d2, d2, #16 \n" // convert to 32 bits
+
+ "1: \n"
+ "vld2.16 {d4,d5}, [%[sP]] \n" // load 4 16-bits stereo samples
+ "vld1.32 { q8}, [%[coefsP0]:128]! \n" // load 4 32-bits coefs
+ "vld1.32 { q9}, [%[coefsP1]:128]! \n" // load 4 32-bits coefs for interpolation
+ "vld2.16 {d6,d7}, [%[sN]]! \n" // load 4 16-bits stereo samples
+ "vld1.32 {q10}, [%[coefsN0]:128]! \n" // load 4 32-bits coefs
+ "vld1.32 {q11}, [%[coefsN1]:128]! \n" // load 4 32-bits coefs for interpolation
+
+ "vrev64.16 d4, d4 \n" // reverse 2 frames of the positive side
+ "vrev64.16 d5, d5 \n" // reverse 2 frames of the positive side
+
+ "vsub.s32 q9, q9, q8 \n" // interpolate (step1) 1st set of coefs
+ "vsub.s32 q11, q11, q10 \n" // interpolate (step1) 2nd set of coets
+ "vshll.s16 q12, d4, #15 \n" // extend samples to 31 bits
+ "vshll.s16 q13, d5, #15 \n" // extend samples to 31 bits
+
+ "vqrdmulh.s32 q9, q9, d2[0] \n" // interpolate (step2) 1st set of coefs
+ "vqrdmulh.s32 q11, q11, d2[1] \n" // interpolate (step3) 2nd set of coefs
+ "vshll.s16 q14, d6, #15 \n" // extend samples to 31 bits
+ "vshll.s16 q15, d7, #15 \n" // extend samples to 31 bits
+
+ "vadd.s32 q8, q8, q9 \n" // interpolate (step3) 1st set
+ "vadd.s32 q10, q10, q11 \n" // interpolate (step4) 2nd set
+ "subs %[count], %[count], #4 \n" // update loop counter
+
+ "vqrdmulh.s32 q12, q12, q8 \n" // multiply samples by interpolated coef
+ "vqrdmulh.s32 q13, q13, q8 \n" // multiply samples by interpolated coef
+ "vqrdmulh.s32 q14, q14, q10 \n" // multiply samples by interpolated coef
+ "vqrdmulh.s32 q15, q15, q10 \n" // multiply samples by interpolated coef
+ "sub %[sP], %[sP], #16 \n" // move pointer to next set of samples
+
+ "vadd.s32 q0, q0, q12 \n" // accumulate result
+ "vadd.s32 q4, q4, q13 \n" // accumulate result
+ "vadd.s32 q0, q0, q14 \n" // accumulate result
+ "vadd.s32 q4, q4, q15 \n" // accumulate result
+
+ "bne 1b \n" // loop
+
+ "vld1.s32 {d2}, [%[vLR]] \n" // load volumes
+ "vld1.s32 {d3}, %[out] \n" // load the output
+ "vpadd.s32 d0, d0, d1 \n" // add all 4 partial sums from q0
+ "vpadd.s32 d8, d8, d9 \n" // add all 4 partial sums from q4
+ "vpadd.s32 d0, d0, d0 \n" // together
+ "vpadd.s32 d8, d8, d8 \n" // together
+ "vtrn.s32 d0, d8 \n" // interlace L,R channels
+ "vqrdmulh.s32 d0, d0, d2 \n" // apply volume
+ "vadd.s32 d3, d3, d0 \n" // accumulate result
+ "vst1.s32 {d3}, %[out] \n" // store result
+
+ : [out] "=Uv" (out[0]),
+ [count] "+r" (count),
+ [coefsP0] "+r" (coefsP),
+ [coefsP1] "+r" (coefsP1),
+ [coefsN0] "+r" (coefsN),
+ [coefsN1] "+r" (coefsN1),
+ [sP] "+r" (sP),
+ [sN] "+r" (sN)
+ : [lerpP] "r" (lerpP),
+ [lerpN] "r" (lerpN),
+ [vLR] "r" (mVolumeSIMD)
+ : "cc", "memory",
+ "q0", "q1", "q2", "q3", "q4",
+ "q8", "q9", "q10", "q11",
+ "q12", "q13", "q14", "q15"
+ );
}
}
template<int CHANNELS>
void AudioResamplerSinc::interpolate(
int32_t& l, int32_t& r,
- const int32_t* coefs, int16_t lerp, const int16_t* samples)
+ const int32_t* coefs, size_t offset,
+ int32_t lerp, const int16_t* samples)
{
int32_t c0 = coefs[0];
- int32_t c1 = coefs[1];
+ int32_t c1 = coefs[offset];
int32_t sinc = mulAdd(lerp, (c1-c0)<<1, c0);
if (CHANNELS == 2) {
uint32_t rl = *reinterpret_cast<const uint32_t*>(samples);
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index 25fc025..1ea4474 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -44,18 +44,21 @@ public:
private:
void init();
+ virtual void setVolume(int16_t left, int16_t right);
+
template<int CHANNELS>
void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
template<int CHANNELS>
inline void filterCoefficient(
- int32_t& l, int32_t& r, uint32_t phase, const int16_t *samples);
+ int32_t* out, uint32_t phase, const int16_t *samples, uint32_t vRL);
template<int CHANNELS>
inline void interpolate(
int32_t& l, int32_t& r,
- const int32_t* coefs, int16_t lerp, const int16_t* samples);
+ const int32_t* coefs, size_t offset,
+ int32_t lerp, const int16_t* samples);
template<int CHANNELS>
inline void read(int16_t*& impulse, uint32_t& phaseFraction,
@@ -64,24 +67,22 @@ private:
int16_t *mState;
int16_t *mImpulse;
int16_t *mRingFull;
+ int32_t mVolumeSIMD[2];
const int32_t * mFirCoefs;
- static const int32_t mFirCoefsDown[];
- static const int32_t mFirCoefsUp[];
+ static const uint32_t mFirCoefsDown[];
+ static const uint32_t mFirCoefsUp[];
// ----------------------------------------------------------------------------
static const int32_t RESAMPLE_FIR_NUM_COEF = 8;
- static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 4;
+ static const int32_t RESAMPLE_FIR_LERP_INT_BITS = 7;
struct Constants {
- // we have 16 coefs samples per zero-crossing
int coefsBits;
int cShift;
uint32_t cMask;
-
int pShift;
uint32_t pMask;
-
// number of zero-crossing on each side
unsigned int halfNumCoefs;
};
diff --git a/services/audioflinger/AudioWatchdog.cpp b/services/audioflinger/AudioWatchdog.cpp
index 8f328ee..93d185e 100644
--- a/services/audioflinger/AudioWatchdog.cpp
+++ b/services/audioflinger/AudioWatchdog.cpp
@@ -17,9 +17,12 @@
#define LOG_TAG "AudioWatchdog"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <utils/Log.h>
#include "AudioWatchdog.h"
+#ifdef AUDIO_WATCHDOG
+
namespace android {
void AudioWatchdogDump::dump(int fd)
@@ -132,3 +135,5 @@ void AudioWatchdog::setDump(AudioWatchdogDump *dump)
}
} // namespace android
+
+#endif // AUDIO_WATCHDOG
diff --git a/services/audioflinger/Configuration.h b/services/audioflinger/Configuration.h
new file mode 100644
index 0000000..0754d9d
--- /dev/null
+++ b/services/audioflinger/Configuration.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+// Put build-time configuration options here rather than Android.mk,
+// so that the instantiate for AudioFlinger service will pick up the same options.
+
+#ifndef ANDROID_AUDIOFLINGER_CONFIGURATION_H
+#define ANDROID_AUDIOFLINGER_CONFIGURATION_H
+
+// uncomment to enable detailed battery usage reporting (not debugged)
+//#define ADD_BATTERY_DATA
+
+// uncomment to enable the audio watchdog
+//#define AUDIO_WATCHDOG
+
+// uncomment to display CPU load adjusted for CPU frequency
+//#define CPU_FREQUENCY_STATISTICS
+
+// uncomment to enable fast mixer to take performance samples for later statistical analysis
+#define FAST_MIXER_STATISTICS
+
+// uncomment for debugging timing problems related to StateQueue::push()
+//#define STATE_QUEUE_DUMP
+
+// uncomment to allow tee sink debugging to be enabled by property
+//#define TEE_SINK
+
+// uncomment to log CPU statistics every n wall clock seconds
+//#define DEBUG_CPU_USAGE 10
+
+#endif // ANDROID_AUDIOFLINGER_CONFIGURATION_H
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
new file mode 100644
index 0000000..010e233
--- /dev/null
+++ b/services/audioflinger/Effects.cpp
@@ -0,0 +1,1795 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+
+#include "Configuration.h"
+#include <utils/Log.h>
+#include <audio_effects/effect_visualizer.h>
+#include <audio_utils/primitives.h>
+#include <private/media/AudioEffectShared.h>
+#include <media/EffectsFactoryApi.h>
+
+#include "AudioFlinger.h"
+#include "ServiceUtilities.h"
+
+// ----------------------------------------------------------------------------
+
+// Note: the following macro is used for extremely verbose logging message. In
+// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to
+// 0; but one side effect of this is to turn all LOGV's as well. Some messages
+// are so verbose that we want to suppress them even when we have ALOG_ASSERT
+// turned on. Do not uncomment the #def below unless you really know what you
+// are doing and want to see all of the extremely verbose messages.
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// EffectModule implementation
+// ----------------------------------------------------------------------------
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectModule"
+
+AudioFlinger::EffectModule::EffectModule(ThreadBase *thread,
+ const wp<AudioFlinger::EffectChain>& chain,
+ effect_descriptor_t *desc,
+ int id,
+ int sessionId)
+ : mPinned(sessionId > AUDIO_SESSION_OUTPUT_MIX),
+ mThread(thread), mChain(chain), mId(id), mSessionId(sessionId),
+ mDescriptor(*desc),
+ // mConfig is set by configure() and not used before then
+ mEffectInterface(NULL),
+ mStatus(NO_INIT), mState(IDLE),
+ // mMaxDisableWaitCnt is set by configure() and not used before then
+ // mDisableWaitCnt is set by process() and updateState() and not used before then
+ mSuspended(false)
+{
+ ALOGV("Constructor %p", this);
+ int lStatus;
+
+ // create effect engine from effect factory
+ mStatus = EffectCreate(&desc->uuid, sessionId, thread->id(), &mEffectInterface);
+
+ if (mStatus != NO_ERROR) {
+ return;
+ }
+ lStatus = init();
+ if (lStatus < 0) {
+ mStatus = lStatus;
+ goto Error;
+ }
+
+ ALOGV("Constructor success name %s, Interface %p", mDescriptor.name, mEffectInterface);
+ return;
+Error:
+ EffectRelease(mEffectInterface);
+ mEffectInterface = NULL;
+ ALOGV("Constructor Error %d", mStatus);
+}
+
+AudioFlinger::EffectModule::~EffectModule()
+{
+ ALOGV("Destructor %p", this);
+ if (mEffectInterface != NULL) {
+ remove_effect_from_hal_l();
+ // release effect engine
+ EffectRelease(mEffectInterface);
+ }
+}
+
+status_t AudioFlinger::EffectModule::addHandle(EffectHandle *handle)
+{
+ status_t status;
+
+ Mutex::Autolock _l(mLock);
+ int priority = handle->priority();
+ size_t size = mHandles.size();
+ EffectHandle *controlHandle = NULL;
+ size_t i;
+ for (i = 0; i < size; i++) {
+ EffectHandle *h = mHandles[i];
+ if (h == NULL || h->destroyed_l()) {
+ continue;
+ }
+ // first non destroyed handle is considered in control
+ if (controlHandle == NULL)
+ controlHandle = h;
+ if (h->priority() <= priority) {
+ break;
+ }
+ }
+ // if inserted in first place, move effect control from previous owner to this handle
+ if (i == 0) {
+ bool enabled = false;
+ if (controlHandle != NULL) {
+ enabled = controlHandle->enabled();
+ controlHandle->setControl(false/*hasControl*/, true /*signal*/, enabled /*enabled*/);
+ }
+ handle->setControl(true /*hasControl*/, false /*signal*/, enabled /*enabled*/);
+ status = NO_ERROR;
+ } else {
+ status = ALREADY_EXISTS;
+ }
+ ALOGV("addHandle() %p added handle %p in position %d", this, handle, i);
+ mHandles.insertAt(handle, i);
+ return status;
+}
+
+size_t AudioFlinger::EffectModule::removeHandle(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;
+ }
+ ALOGV("removeHandle() %p removed handle %p in position %d", this, handle, i);
+
+ mHandles.removeAt(i);
+ // if removed from first place, move effect control from this handle to next in line
+ if (i == 0) {
+ EffectHandle *h = controlHandle_l();
+ if (h != NULL) {
+ h->setControl(true /*hasControl*/, true /*signal*/ , handle->enabled() /*enabled*/);
+ }
+ }
+
+ // Prevent calls to process() and other functions on effect interface from now on.
+ // The effect engine will be released by the destructor when the last strong reference on
+ // this object is released which can happen after next process is called.
+ if (mHandles.size() == 0 && !mPinned) {
+ mState = DESTROYED;
+ }
+
+ return mHandles.size();
+}
+
+// must be called with EffectModule::mLock held
+AudioFlinger::EffectHandle *AudioFlinger::EffectModule::controlHandle_l()
+{
+ // the first valid handle in the list has control over the module
+ for (size_t i = 0; i < mHandles.size(); i++) {
+ EffectHandle *h = mHandles[i];
+ if (h != NULL && !h->destroyed_l()) {
+ return h;
+ }
+ }
+
+ return NULL;
+}
+
+size_t AudioFlinger::EffectModule::disconnect(EffectHandle *handle, bool unpinIfLast)
+{
+ ALOGV("disconnect() %p handle %p", this, 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) {
+ thread->disconnectEffect(keep, handle, unpinIfLast);
+ }
+ }
+ return mHandles.size();
+}
+
+void AudioFlinger::EffectModule::updateState() {
+ Mutex::Autolock _l(mLock);
+
+ switch (mState) {
+ case RESTART:
+ reset_l();
+ // FALL THROUGH
+
+ case STARTING:
+ // 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));
+ }
+ if (start_l() == NO_ERROR) {
+ mState = ACTIVE;
+ } else {
+ mState = IDLE;
+ }
+ break;
+ case STOPPING:
+ if (stop_l() == NO_ERROR) {
+ mDisableWaitCnt = mMaxDisableWaitCnt;
+ } else {
+ mDisableWaitCnt = 1; // will cause immediate transition to IDLE
+ }
+ mState = STOPPED;
+ break;
+ case STOPPED:
+ // mDisableWaitCnt is forced to 1 by process() when the engine indicates the end of the
+ // turn off sequence.
+ if (--mDisableWaitCnt == 0) {
+ reset_l();
+ mState = IDLE;
+ }
+ break;
+ default: //IDLE , ACTIVE, DESTROYED
+ break;
+ }
+}
+
+void AudioFlinger::EffectModule::process()
+{
+ Mutex::Autolock _l(mLock);
+
+ if (mState == DESTROYED || mEffectInterface == NULL ||
+ mConfig.inputCfg.buffer.raw == NULL ||
+ mConfig.outputCfg.buffer.raw == NULL) {
+ return;
+ }
+
+ if (isProcessEnabled()) {
+ // do 32 bit to 16 bit conversion for auxiliary effect input buffer
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ ditherAndClamp(mConfig.inputCfg.buffer.s32,
+ mConfig.inputCfg.buffer.s32,
+ mConfig.inputCfg.buffer.frameCount/2);
+ }
+
+ // do the actual processing in the effect engine
+ int ret = (*mEffectInterface)->process(mEffectInterface,
+ &mConfig.inputCfg.buffer,
+ &mConfig.outputCfg.buffer);
+
+ // force transition to IDLE state when engine is ready
+ if (mState == STOPPED && ret == -ENODATA) {
+ mDisableWaitCnt = 1;
+ }
+
+ // 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,
+ // accumulate input onto output
+ sp<EffectChain> chain = mChain.promote();
+ if (chain != 0 && chain->activeTrackCnt() != 0) {
+ size_t frameCnt = mConfig.inputCfg.buffer.frameCount * 2; //always stereo here
+ int16_t *in = mConfig.inputCfg.buffer.s16;
+ int16_t *out = mConfig.outputCfg.buffer.s16;
+ for (size_t i = 0; i < frameCnt; i++) {
+ out[i] = clamp16((int32_t)out[i] + (int32_t)in[i]);
+ }
+ }
+ }
+}
+
+void AudioFlinger::EffectModule::reset_l()
+{
+ if (mStatus != NO_ERROR || mEffectInterface == NULL) {
+ return;
+ }
+ (*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);
+}
+
+status_t AudioFlinger::EffectModule::configure()
+{
+ status_t status;
+ sp<ThreadBase> thread;
+ uint32_t size;
+ audio_channel_mask_t channelMask;
+
+ if (mEffectInterface == NULL) {
+ status = NO_INIT;
+ goto exit;
+ }
+
+ thread = mThread.promote();
+ if (thread == 0) {
+ status = DEAD_OBJECT;
+ goto exit;
+ }
+
+ // TODO: handle configuration of effects replacing track process
+ channelMask = thread->channelMask();
+
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
+ } else {
+ mConfig.inputCfg.channels = channelMask;
+ }
+ mConfig.outputCfg.channels = channelMask;
+ mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ 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 AUDIO_SESSION_OUTPUT_MIX or AUDIO_SESSION_OUTPUT_STAGE,
+ // 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;
+
+ ALOGV("configure() %p thread %p buffer %p framecount %d",
+ this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
+
+ status_t cmdStatus;
+ size = sizeof(int);
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_CONFIG,
+ sizeof(effect_config_t),
+ &mConfig,
+ &size,
+ &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+
+ if (status == 0 &&
+ (memcmp(&mDescriptor.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0)) {
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
+ effect_param_t *p = (effect_param_t *)buf32;
+
+ p->psize = sizeof(uint32_t);
+ p->vsize = sizeof(uint32_t);
+ size = sizeof(int);
+ *(int32_t *)p->data = VISUALIZER_PARAM_LATENCY;
+
+ uint32_t latency = 0;
+ PlaybackThread *pbt = thread->mAudioFlinger->checkPlaybackThread_l(thread->mId);
+ if (pbt != NULL) {
+ latency = pbt->latency_l();
+ }
+
+ *((int32_t *)p->data + 1)= latency;
+ (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_PARAM,
+ sizeof(effect_param_t) + 8,
+ &buf32,
+ &size,
+ &cmdStatus);
+ }
+
+ mMaxDisableWaitCnt = (MAX_DISABLE_TIME_MS * mConfig.outputCfg.samplingRate) /
+ (1000 * mConfig.outputCfg.buffer.frameCount);
+
+exit:
+ mStatus = status;
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::init()
+{
+ Mutex::Autolock _l(mLock);
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ status_t cmdStatus;
+ uint32_t 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()
+{
+ Mutex::Autolock _l(mLock);
+ return start_l();
+}
+
+status_t AudioFlinger::EffectModule::start_l()
+{
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_ENABLE,
+ 0,
+ NULL,
+ &size,
+ &cmdStatus);
+ if (status == 0) {
+ status = cmdStatus;
+ }
+ if (status == 0 &&
+ ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
+ (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC)) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ audio_stream_t *stream = thread->stream();
+ if (stream != NULL) {
+ stream->add_audio_effect(stream, mEffectInterface);
+ }
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::stop()
+{
+ Mutex::Autolock _l(mLock);
+ return stop_l();
+}
+
+status_t AudioFlinger::EffectModule::stop_l()
+{
+ if (mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t cmdStatus = NO_ERROR;
+ uint32_t size = sizeof(status_t);
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_DISABLE,
+ 0,
+ NULL,
+ &size,
+ &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ if (status == NO_ERROR) {
+ status = remove_effect_from_hal_l();
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::remove_effect_from_hal_l()
+{
+ if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC ||
+ (mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_POST_PROC) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ audio_stream_t *stream = thread->stream();
+ if (stream != NULL) {
+ stream->remove_audio_effect(stream, mEffectInterface);
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::EffectModule::command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData)
+{
+ Mutex::Autolock _l(mLock);
+ ALOGVV("command(), cmdCode: %d, mEffectInterface: %p", cmdCode, mEffectInterface);
+
+ if (mState == DESTROYED || mEffectInterface == NULL) {
+ return NO_INIT;
+ }
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t status = (*mEffectInterface)->command(mEffectInterface,
+ cmdCode,
+ cmdSize,
+ pCmdData,
+ replySize,
+ pReplyData);
+ if (cmdCode != EFFECT_CMD_GET_PARAM && status == NO_ERROR) {
+ uint32_t size = (replySize == NULL) ? 0 : *replySize;
+ for (size_t i = 1; i < mHandles.size(); i++) {
+ EffectHandle *h = mHandles[i];
+ if (h != NULL && !h->destroyed_l()) {
+ h->commandExecuted(cmdCode, cmdSize, pCmdData, size, pReplyData);
+ }
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setEnabled(bool enabled)
+{
+ Mutex::Autolock _l(mLock);
+ return setEnabled_l(enabled);
+}
+
+// must be called with EffectModule::mLock held
+status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled)
+{
+
+ ALOGV("setEnabled %p enabled %d", this, enabled);
+
+ if (enabled != isEnabled()) {
+ status_t status = AudioSystem::setEffectEnabled(mId, enabled);
+ if (enabled && status != NO_ERROR) {
+ return status;
+ }
+
+ switch (mState) {
+ // going from disabled to enabled
+ case IDLE:
+ mState = STARTING;
+ break;
+ case STOPPED:
+ mState = RESTART;
+ break;
+ case STOPPING:
+ mState = ACTIVE;
+ break;
+
+ // going from enabled to disabled
+ case RESTART:
+ mState = STOPPED;
+ break;
+ case STARTING:
+ mState = IDLE;
+ break;
+ case ACTIVE:
+ mState = STOPPING;
+ break;
+ case DESTROYED:
+ return NO_ERROR; // simply ignore as we are being destroyed
+ }
+ for (size_t i = 1; i < mHandles.size(); i++) {
+ EffectHandle *h = mHandles[i];
+ if (h != NULL && !h->destroyed_l()) {
+ h->setEnabled(enabled);
+ }
+ }
+ }
+ return NO_ERROR;
+}
+
+bool AudioFlinger::EffectModule::isEnabled() const
+{
+ switch (mState) {
+ case RESTART:
+ case STARTING:
+ case ACTIVE:
+ return true;
+ case IDLE:
+ case STOPPING:
+ case STOPPED:
+ case DESTROYED:
+ default:
+ return false;
+ }
+}
+
+bool AudioFlinger::EffectModule::isProcessEnabled() const
+{
+ if (mStatus != NO_ERROR) {
+ return false;
+ }
+
+ switch (mState) {
+ case RESTART:
+ case ACTIVE:
+ case STOPPING:
+ case STOPPED:
+ return true;
+ case IDLE:
+ case STARTING:
+ case DESTROYED:
+ default:
+ return false;
+ }
+}
+
+status_t AudioFlinger::EffectModule::setVolume(uint32_t *left, uint32_t *right, bool controller)
+{
+ Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ 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 (isProcessEnabled() &&
+ ((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
+ (mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) {
+ status_t cmdStatus;
+ uint32_t volume[2];
+ uint32_t *pVolume = NULL;
+ uint32_t 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(audio_devices_t device)
+{
+ if (device == AUDIO_DEVICE_NONE) {
+ return NO_ERROR;
+ }
+
+ Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ uint32_t cmd = audio_is_output_devices(device) ? EFFECT_CMD_SET_DEVICE :
+ EFFECT_CMD_SET_INPUT_DEVICE;
+ status = (*mEffectInterface)->command(mEffectInterface,
+ cmd,
+ sizeof(uint32_t),
+ &device,
+ &size,
+ &cmdStatus);
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setMode(audio_mode_t mode)
+{
+ Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_AUDIO_MODE,
+ sizeof(audio_mode_t),
+ &mode,
+ &size,
+ &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectModule::setAudioSource(audio_source_t source)
+{
+ Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_SOURCE_MASK) == EFFECT_FLAG_AUDIO_SOURCE_IND) {
+ uint32_t size = 0;
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_SET_AUDIO_SOURCE,
+ sizeof(audio_source_t),
+ &source,
+ &size,
+ NULL);
+ }
+ return status;
+}
+
+void AudioFlinger::EffectModule::setSuspended(bool suspended)
+{
+ Mutex::Autolock _l(mLock);
+ mSuspended = suspended;
+}
+
+bool AudioFlinger::EffectModule::suspended() const
+{
+ Mutex::Autolock _l(mLock);
+ return mSuspended;
+}
+
+bool AudioFlinger::EffectModule::purgeHandles()
+{
+ bool enabled = false;
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mHandles.size(); i++) {
+ EffectHandle *handle = mHandles[i];
+ if (handle != NULL && !handle->destroyed_l()) {
+ handle->effect().clear();
+ if (handle->hasControl()) {
+ enabled = handle->enabled();
+ }
+ }
+ }
+ return enabled;
+}
+
+status_t AudioFlinger::EffectModule::setOffloaded(bool offloaded, audio_io_handle_t io)
+{
+ Mutex::Autolock _l(mLock);
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ status_t status = NO_ERROR;
+ if ((mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0) {
+ status_t cmdStatus;
+ uint32_t size = sizeof(status_t);
+ effect_offload_param_t cmd;
+
+ cmd.isOffload = offloaded;
+ cmd.ioHandle = io;
+ status = (*mEffectInterface)->command(mEffectInterface,
+ EFFECT_CMD_OFFLOAD,
+ sizeof(effect_offload_param_t),
+ &cmd,
+ &size,
+ &cmdStatus);
+ if (status == NO_ERROR) {
+ status = cmdStatus;
+ }
+ mOffloaded = (status == NO_ERROR) ? offloaded : false;
+ } else {
+ if (offloaded) {
+ status = INVALID_OPERATION;
+ }
+ mOffloaded = false;
+ }
+ ALOGV("setOffloaded() offloaded %d io %d status %d", offloaded, io, status);
+ return status;
+}
+
+bool AudioFlinger::EffectModule::isOffloaded() const
+{
+ Mutex::Autolock _l(mLock);
+ return mOffloaded;
+}
+
+void 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 = AudioFlinger::dumpTryLock(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 %p\n",
+ mSessionId, mStatus, mState, 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: %08X\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\tFrames Smp rate Channels Format Buffer\n");
+ snprintf(buffer, SIZE, "\t\t\t%05zu %05d %08x %6d %p\n",
+ mConfig.inputCfg.buffer.frameCount,
+ mConfig.inputCfg.samplingRate,
+ mConfig.inputCfg.channels,
+ mConfig.inputCfg.format,
+ mConfig.inputCfg.buffer.raw);
+ 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\t%p %05zu %05d %08x %d\n",
+ 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%zu 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) {
+ EffectHandle *handle = mHandles[i];
+ if (handle != NULL && !handle->destroyed_l()) {
+ handle->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+
+ result.append("\n");
+
+ write(fd, result.string(), result.length());
+
+ if (locked) {
+ mLock.unlock();
+ }
+}
+
+// ----------------------------------------------------------------------------
+// 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), mCblk(NULL),
+ mPriority(priority), mHasControl(false), mEnabled(false), mDestroyed(false)
+{
+ ALOGV("constructor %p", this);
+
+ if (client == 0) {
+ return;
+ }
+ 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 != NULL) {
+ new(mCblk) effect_param_cblk_t();
+ mBuffer = (uint8_t *)mCblk + bufOffset;
+ }
+ } else {
+ ALOGE("not enough memory for Effect size=%u", EFFECT_PARAM_BUFFER_SIZE +
+ sizeof(effect_param_cblk_t));
+ return;
+ }
+}
+
+AudioFlinger::EffectHandle::~EffectHandle()
+{
+ ALOGV("Destructor %p", this);
+
+ if (mEffect == 0) {
+ mDestroyed = true;
+ return;
+ }
+ mEffect->lock();
+ mDestroyed = true;
+ mEffect->unlock();
+ disconnect(false);
+}
+
+status_t AudioFlinger::EffectHandle::enable()
+{
+ ALOGV("enable %p", this);
+ if (!mHasControl) {
+ return INVALID_OPERATION;
+ }
+ if (mEffect == 0) {
+ return DEAD_OBJECT;
+ }
+
+ if (mEnabled) {
+ return NO_ERROR;
+ }
+
+ mEnabled = true;
+
+ sp<ThreadBase> thread = mEffect->thread().promote();
+ if (thread != 0) {
+ thread->checkSuspendOnEffectEnabled(mEffect, true, mEffect->sessionId());
+ }
+
+ // checkSuspendOnEffectEnabled() can suspend this same effect when enabled
+ if (mEffect->suspended()) {
+ return NO_ERROR;
+ }
+
+ status_t status = mEffect->setEnabled(true);
+ if (status != NO_ERROR) {
+ if (thread != 0) {
+ thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ }
+ mEnabled = false;
+ } else {
+ if (thread != 0) {
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
+ if (!mEffect->isOffloadable()) {
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ thread->mAudioFlinger->onNonOffloadableGlobalEffectEnable();
+ }
+ }
+ }
+ }
+ return status;
+}
+
+status_t AudioFlinger::EffectHandle::disable()
+{
+ ALOGV("disable %p", this);
+ if (!mHasControl) {
+ return INVALID_OPERATION;
+ }
+ if (mEffect == 0) {
+ return DEAD_OBJECT;
+ }
+
+ if (!mEnabled) {
+ return NO_ERROR;
+ }
+ mEnabled = false;
+
+ if (mEffect->suspended()) {
+ return NO_ERROR;
+ }
+
+ status_t status = mEffect->setEnabled(false);
+
+ sp<ThreadBase> thread = mEffect->thread().promote();
+ if (thread != 0) {
+ thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ if (thread->type() == ThreadBase::OFFLOAD) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
+ }
+
+ return status;
+}
+
+void AudioFlinger::EffectHandle::disconnect()
+{
+ disconnect(true);
+}
+
+void AudioFlinger::EffectHandle::disconnect(bool unpinIfLast)
+{
+ ALOGV("disconnect(%s)", unpinIfLast ? "true" : "false");
+ if (mEffect == 0) {
+ return;
+ }
+ // restore suspended effects if the disconnected handle was enabled and the last one.
+ if ((mEffect->disconnect(this, unpinIfLast) == 0) && mEnabled) {
+ sp<ThreadBase> thread = mEffect->thread().promote();
+ if (thread != 0) {
+ thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
+ }
+ }
+
+ // release sp on module => module destructor can be called now
+ mEffect.clear();
+ if (mClient != 0) {
+ if (mCblk != NULL) {
+ // unlike ~TrackBase(), mCblk is never a local new, so don't delete
+ mCblk->~effect_param_cblk_t(); // destroy our shared-structure.
+ }
+ mCblkMemory.clear(); // free the shared memory before releasing the heap it belongs to
+ // Client destructor must run with AudioFlinger mutex locked
+ Mutex::Autolock _l(mClient->audioFlinger()->mLock);
+ mClient.clear();
+ }
+}
+
+status_t AudioFlinger::EffectHandle::command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData)
+{
+ ALOGVV("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;
+ }
+ if (mClient == 0) {
+ return INVALID_OPERATION;
+ }
+
+ // 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 if 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;
+ uint32_t rsize = sizeof(int);
+ int *p = (int *)(mBuffer + mCblk->serverIndex);
+ int size = *p++;
+ if (((uint8_t *)p + size) > mBuffer + mCblk->clientIndex) {
+ ALOGW("command(): invalid parameter block size");
+ break;
+ }
+ effect_param_t *param = (effect_param_t *)p;
+ if (param->psize == 0 || param->vsize == 0) {
+ ALOGW("command(): null parameter or value size");
+ mCblk->serverIndex += size;
+ continue;
+ }
+ uint32_t 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);
+ // stop at first error encountered
+ if (ret != NO_ERROR) {
+ status = ret;
+ *(int *)pReplyData = reply;
+ break;
+ } else if (reply != NO_ERROR) {
+ *(int *)pReplyData = reply;
+ break;
+ }
+ mCblk->serverIndex += size;
+ }
+ mCblk->serverIndex = 0;
+ mCblk->clientIndex = 0;
+ return status;
+ } else if (cmdCode == EFFECT_CMD_ENABLE) {
+ *(int *)pReplyData = NO_ERROR;
+ return enable();
+ } else if (cmdCode == EFFECT_CMD_DISABLE) {
+ *(int *)pReplyData = NO_ERROR;
+ return disable();
+ }
+
+ return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
+}
+
+void AudioFlinger::EffectHandle::setControl(bool hasControl, bool signal, bool enabled)
+{
+ ALOGV("setControl %p control %d", this, hasControl);
+
+ mHasControl = hasControl;
+ mEnabled = enabled;
+
+ if (signal && mEffectClient != 0) {
+ mEffectClient->controlStatusChanged(hasControl);
+ }
+}
+
+void AudioFlinger::EffectHandle::commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t 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 = mCblk != NULL && AudioFlinger::dumpTryLock(mCblk->lock);
+
+ snprintf(buffer, size, "\t\t\t%05d %05d %01u %01u %05u %05u\n",
+ (mClient == 0) ? getpid_cached : mClient->pid(),
+ mPriority,
+ mHasControl,
+ !locked,
+ mCblk ? mCblk->clientIndex : 0,
+ mCblk ? mCblk->serverIndex : 0
+ );
+
+ if (locked) {
+ mCblk->lock.unlock();
+ }
+}
+
+#undef LOG_TAG
+#define LOG_TAG "AudioFlinger::EffectChain"
+
+AudioFlinger::EffectChain::EffectChain(ThreadBase *thread,
+ int sessionId)
+ : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
+ mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
+{
+ mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
+ if (thread == NULL) {
+ return;
+ }
+ mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) /
+ thread->frameCount();
+}
+
+AudioFlinger::EffectChain::~EffectChain()
+{
+ if (mOwnInBuffer) {
+ delete mInBuffer;
+ }
+
+}
+
+// getEffectFromDesc_l() must be called with ThreadBase::mLock held
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromDesc_l(
+ effect_descriptor_t *descriptor)
+{
+ 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) {
+ return mEffects[i];
+ }
+ }
+ return 0;
+}
+
+// getEffectFromId_l() must be called with ThreadBase::mLock held
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromId_l(int id)
+{
+ size_t size = mEffects.size();
+
+ for (size_t i = 0; i < size; i++) {
+ // by convention, return first effect if id provided is 0 (0 is never a valid id)
+ if (id == 0 || mEffects[i]->id() == id) {
+ return mEffects[i];
+ }
+ }
+ return 0;
+}
+
+// getEffectFromType_l() must be called with ThreadBase::mLock held
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromType_l(
+ const effect_uuid_t *type)
+{
+ size_t size = mEffects.size();
+
+ for (size_t i = 0; i < size; i++) {
+ if (memcmp(&mEffects[i]->desc().type, type, sizeof(effect_uuid_t)) == 0) {
+ return mEffects[i];
+ }
+ }
+ return 0;
+}
+
+void AudioFlinger::EffectChain::clearInputBuffer()
+{
+ Mutex::Autolock _l(mLock);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ ALOGW("clearInputBuffer(): cannot promote mixer thread");
+ return;
+ }
+ clearInputBuffer_l(thread);
+}
+
+// Must be called with EffectChain::mLock locked
+void AudioFlinger::EffectChain::clearInputBuffer_l(sp<ThreadBase> thread)
+{
+ memset(mInBuffer, 0, thread->frameCount() * thread->frameSize());
+}
+
+// Must be called with EffectChain::mLock locked
+void AudioFlinger::EffectChain::process_l()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ ALOGW("process_l(): cannot promote mixer thread");
+ return;
+ }
+ bool isGlobalSession = (mSessionId == AUDIO_SESSION_OUTPUT_MIX) ||
+ (mSessionId == AUDIO_SESSION_OUTPUT_STAGE);
+ // never process effects when:
+ // - on an OFFLOAD thread
+ // - no more tracks are on the session and the effect tail has been rendered
+ bool doProcess = (thread->type() != ThreadBase::OFFLOAD);
+ if (!isGlobalSession) {
+ bool tracksOnSession = (trackCnt() != 0);
+
+ if (!tracksOnSession && mTailBufferCount == 0) {
+ doProcess = false;
+ }
+
+ if (activeTrackCnt() == 0) {
+ // if no track is active and the effect tail has not been rendered,
+ // the input buffer must be cleared here as the mixer process will not do it
+ if (tracksOnSession || mTailBufferCount > 0) {
+ clearInputBuffer_l(thread);
+ if (mTailBufferCount > 0) {
+ mTailBufferCount--;
+ }
+ }
+ }
+ }
+
+ size_t size = mEffects.size();
+ if (doProcess) {
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->process();
+ }
+ }
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->updateState();
+ }
+}
+
+// addEffect_l() must be called with PlaybackThread::mLock held
+status_t AudioFlinger::EffectChain::addEffect_l(const sp<EffectModule>& effect)
+{
+ effect_descriptor_t desc = effect->desc();
+ uint32_t insertPref = desc.flags & EFFECT_FLAG_INSERT_MASK;
+
+ Mutex::Autolock _l(mLock);
+ effect->setChain(this);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return NO_INIT;
+ }
+ effect->setThread(thread);
+
+ 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);
+
+ // 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 as a function of indicated preference:
+ // if EFFECT_FLAG_INSERT_EXCLUSIVE, insert in first position or reject if
+ // another effect is present
+ // else if EFFECT_FLAG_INSERT_FIRST, insert in first position or after the
+ // last effect claiming first position
+ // else if EFFECT_FLAG_INSERT_LAST, insert in last position or before the
+ // first effect claiming last position
+ // else if EFFECT_FLAG_INSERT_ANY insert after first or before last
+ // Reject insertion if an effect with EFFECT_FLAG_INSERT_EXCLUSIVE is
+ // already present
+
+ size_t size = mEffects.size();
+ size_t idx_insert = size;
+ ssize_t idx_insert_first = -1;
+ ssize_t idx_insert_last = -1;
+
+ for (size_t 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) {
+ ALOGW("addEffect_l() could not insert effect %s: exclusive conflict with %s",
+ desc.name, d.name);
+ return INVALID_OPERATION;
+ }
+ // remember position of first insert effect and by default
+ // select this as insert position for new effect
+ if (idx_insert == size) {
+ idx_insert = i;
+ }
+ // remember position of last insert effect claiming
+ // first position
+ if (iPref == EFFECT_FLAG_INSERT_FIRST) {
+ idx_insert_first = i;
+ }
+ // remember position of first insert effect claiming
+ // last position
+ if (iPref == EFFECT_FLAG_INSERT_LAST &&
+ idx_insert_last == -1) {
+ idx_insert_last = i;
+ }
+ }
+ }
+
+ // modify idx_insert from first position if needed
+ if (insertPref == EFFECT_FLAG_INSERT_LAST) {
+ if (idx_insert_last != -1) {
+ idx_insert = idx_insert_last;
+ } else {
+ idx_insert = size;
+ }
+ } else {
+ if (idx_insert_first != -1) {
+ idx_insert = idx_insert_first + 1;
+ }
+ }
+
+ // 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);
+ }
+ mEffects.insertAt(effect, idx_insert);
+
+ ALOGV("addEffect_l() effect %p, added in chain %p at rank %d", effect.get(), this,
+ idx_insert);
+ }
+ effect->configure();
+ return NO_ERROR;
+}
+
+// removeEffect_l() must be called with PlaybackThread::mLock held
+size_t AudioFlinger::EffectChain::removeEffect_l(const sp<EffectModule>& effect)
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mEffects.size();
+ uint32_t type = effect->desc().flags & EFFECT_FLAG_TYPE_MASK;
+
+ for (size_t i = 0; i < size; i++) {
+ if (effect == mEffects[i]) {
+ // calling stop here will remove pre-processing effect from the audio HAL.
+ // This is safe as we hold the EffectChain mutex which guarantees that we are not in
+ // the middle of a read from audio HAL
+ if (mEffects[i]->state() == EffectModule::ACTIVE ||
+ mEffects[i]->state() == EffectModule::STOPPING) {
+ mEffects[i]->stop();
+ }
+ 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);
+ ALOGV("removeEffect_l() effect %p, removed from chain %p at rank %d", effect.get(),
+ this, i);
+ break;
+ }
+ }
+
+ return mEffects.size();
+}
+
+// setDevice_l() must be called with PlaybackThread::mLock held
+void AudioFlinger::EffectChain::setDevice_l(audio_devices_t device)
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->setDevice(device);
+ }
+}
+
+// setMode_l() must be called with PlaybackThread::mLock held
+void AudioFlinger::EffectChain::setMode_l(audio_mode_t mode)
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->setMode(mode);
+ }
+}
+
+// setAudioSource_l() must be called with PlaybackThread::mLock held
+void AudioFlinger::EffectChain::setAudioSource_l(audio_source_t source)
+{
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffects[i]->setAudioSource(source);
+ }
+}
+
+// setVolume_l() must be called with PlaybackThread::mLock held
+bool AudioFlinger::EffectChain::setVolume_l(uint32_t *left, uint32_t *right)
+{
+ uint32_t newLeft = *left;
+ uint32_t newRight = *right;
+ bool hasControl = false;
+ int ctrlIdx = -1;
+ size_t size = mEffects.size();
+
+ // first update volume controller
+ for (size_t i = size; i > 0; i--) {
+ if (mEffects[i - 1]->isProcessEnabled() &&
+ (mEffects[i - 1]->desc().flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL) {
+ ctrlIdx = i - 1;
+ hasControl = true;
+ break;
+ }
+ }
+
+ if (ctrlIdx == mVolumeCtrlIdx && *left == mLeftVolume && *right == mRightVolume) {
+ if (hasControl) {
+ *left = mNewLeftVolume;
+ *right = mNewRightVolume;
+ }
+ return hasControl;
+ }
+
+ mVolumeCtrlIdx = ctrlIdx;
+ mLeftVolume = newLeft;
+ mRightVolume = newRight;
+
+ // second get volume update from volume controller
+ if (ctrlIdx >= 0) {
+ mEffects[ctrlIdx]->setVolume(&newLeft, &newRight, true);
+ mNewLeftVolume = newLeft;
+ mNewRightVolume = newRight;
+ }
+ // 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;
+
+ for (size_t i = 0; i < size; i++) {
+ if ((int)i == ctrlIdx) {
+ continue;
+ }
+ // this also works for ctrlIdx == -1 when there is no volume controller
+ if ((int)i > ctrlIdx) {
+ lVol = *left;
+ rVol = *right;
+ }
+ mEffects[i]->setVolume(&lVol, &rVol, false);
+ }
+ *left = newLeft;
+ *right = newRight;
+
+ return hasControl;
+}
+
+void 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 = AudioFlinger::dumpTryLock(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 Active tracks:\n");
+ snprintf(buffer, SIZE, "\t%02zu %p %p %d\n",
+ mEffects.size(),
+ mInBuffer,
+ mOutBuffer,
+ 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();
+ }
+}
+
+// must be called with ThreadBase::mLock held
+void AudioFlinger::EffectChain::setEffectSuspended_l(
+ const effect_uuid_t *type, bool suspend)
+{
+ sp<SuspendedEffectDesc> desc;
+ // use effect type UUID timelow as key as there is no real risk of identical
+ // timeLow fields among effect type UUIDs.
+ ssize_t index = mSuspendedEffects.indexOfKey(type->timeLow);
+ if (suspend) {
+ if (index >= 0) {
+ desc = mSuspendedEffects.valueAt(index);
+ } else {
+ desc = new SuspendedEffectDesc();
+ desc->mType = *type;
+ mSuspendedEffects.add(type->timeLow, desc);
+ ALOGV("setEffectSuspended_l() add entry for %08x", type->timeLow);
+ }
+ if (desc->mRefCount++ == 0) {
+ sp<EffectModule> effect = getEffectIfEnabled(type);
+ if (effect != 0) {
+ desc->mEffect = effect;
+ effect->setSuspended(true);
+ effect->setEnabled(false);
+ }
+ }
+ } else {
+ if (index < 0) {
+ return;
+ }
+ desc = mSuspendedEffects.valueAt(index);
+ if (desc->mRefCount <= 0) {
+ ALOGW("setEffectSuspended_l() restore refcount should not be 0 %d", desc->mRefCount);
+ desc->mRefCount = 1;
+ }
+ if (--desc->mRefCount == 0) {
+ ALOGV("setEffectSuspended_l() remove entry for %08x", mSuspendedEffects.keyAt(index));
+ if (desc->mEffect != 0) {
+ sp<EffectModule> effect = desc->mEffect.promote();
+ if (effect != 0) {
+ effect->setSuspended(false);
+ effect->lock();
+ EffectHandle *handle = effect->controlHandle_l();
+ if (handle != NULL && !handle->destroyed_l()) {
+ effect->setEnabled_l(handle->enabled());
+ }
+ effect->unlock();
+ }
+ desc->mEffect.clear();
+ }
+ mSuspendedEffects.removeItemsAt(index);
+ }
+ }
+}
+
+// must be called with ThreadBase::mLock held
+void AudioFlinger::EffectChain::setEffectSuspendedAll_l(bool suspend)
+{
+ sp<SuspendedEffectDesc> desc;
+
+ ssize_t index = mSuspendedEffects.indexOfKey((int)kKeyForSuspendAll);
+ if (suspend) {
+ if (index >= 0) {
+ desc = mSuspendedEffects.valueAt(index);
+ } else {
+ desc = new SuspendedEffectDesc();
+ mSuspendedEffects.add((int)kKeyForSuspendAll, desc);
+ ALOGV("setEffectSuspendedAll_l() add entry for 0");
+ }
+ if (desc->mRefCount++ == 0) {
+ Vector< sp<EffectModule> > effects;
+ getSuspendEligibleEffects(effects);
+ for (size_t i = 0; i < effects.size(); i++) {
+ setEffectSuspended_l(&effects[i]->desc().type, true);
+ }
+ }
+ } else {
+ if (index < 0) {
+ return;
+ }
+ desc = mSuspendedEffects.valueAt(index);
+ if (desc->mRefCount <= 0) {
+ ALOGW("setEffectSuspendedAll_l() restore refcount should not be 0 %d", desc->mRefCount);
+ desc->mRefCount = 1;
+ }
+ if (--desc->mRefCount == 0) {
+ Vector<const effect_uuid_t *> types;
+ for (size_t i = 0; i < mSuspendedEffects.size(); i++) {
+ if (mSuspendedEffects.keyAt(i) == (int)kKeyForSuspendAll) {
+ continue;
+ }
+ types.add(&mSuspendedEffects.valueAt(i)->mType);
+ }
+ for (size_t i = 0; i < types.size(); i++) {
+ setEffectSuspended_l(types[i], false);
+ }
+ ALOGV("setEffectSuspendedAll_l() remove entry for %08x",
+ mSuspendedEffects.keyAt(index));
+ mSuspendedEffects.removeItem((int)kKeyForSuspendAll);
+ }
+ }
+}
+
+
+// The volume effect is used for automated tests only
+#ifndef OPENSL_ES_H_
+static const effect_uuid_t SL_IID_VOLUME_ = { 0x09e8ede0, 0xddde, 0x11db, 0xb4f6,
+ { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } };
+const effect_uuid_t * const SL_IID_VOLUME = &SL_IID_VOLUME_;
+#endif //OPENSL_ES_H_
+
+bool AudioFlinger::EffectChain::isEffectEligibleForSuspend(const effect_descriptor_t& desc)
+{
+ // auxiliary effects and visualizer are never suspended on output mix
+ if ((mSessionId == AUDIO_SESSION_OUTPUT_MIX) &&
+ (((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) ||
+ (memcmp(&desc.type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) ||
+ (memcmp(&desc.type, SL_IID_VOLUME, sizeof(effect_uuid_t)) == 0))) {
+ return false;
+ }
+ return true;
+}
+
+void AudioFlinger::EffectChain::getSuspendEligibleEffects(
+ Vector< sp<AudioFlinger::EffectModule> > &effects)
+{
+ effects.clear();
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ if (isEffectEligibleForSuspend(mEffects[i]->desc())) {
+ effects.add(mEffects[i]);
+ }
+ }
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectIfEnabled(
+ const effect_uuid_t *type)
+{
+ sp<EffectModule> effect = getEffectFromType_l(type);
+ return effect != 0 && effect->isEnabled() ? effect : 0;
+}
+
+void AudioFlinger::EffectChain::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
+ bool enabled)
+{
+ ssize_t index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
+ if (enabled) {
+ if (index < 0) {
+ // if the effect is not suspend check if all effects are suspended
+ index = mSuspendedEffects.indexOfKey((int)kKeyForSuspendAll);
+ if (index < 0) {
+ return;
+ }
+ if (!isEffectEligibleForSuspend(effect->desc())) {
+ return;
+ }
+ setEffectSuspended_l(&effect->desc().type, enabled);
+ index = mSuspendedEffects.indexOfKey(effect->desc().type.timeLow);
+ if (index < 0) {
+ ALOGW("checkSuspendOnEffectEnabled() Fx should be suspended here!");
+ return;
+ }
+ }
+ ALOGV("checkSuspendOnEffectEnabled() enable suspending fx %08x",
+ effect->desc().type.timeLow);
+ sp<SuspendedEffectDesc> desc = mSuspendedEffects.valueAt(index);
+ // if effect is requested to suspended but was not yet enabled, supend it now.
+ if (desc->mEffect == 0) {
+ desc->mEffect = effect;
+ effect->setEnabled(false);
+ effect->setSuspended(true);
+ }
+ } else {
+ if (index < 0) {
+ return;
+ }
+ ALOGV("checkSuspendOnEffectEnabled() disable restoring fx %08x",
+ effect->desc().type.timeLow);
+ sp<SuspendedEffectDesc> desc = mSuspendedEffects.valueAt(index);
+ desc->mEffect.clear();
+ effect->setSuspended(false);
+ }
+}
+
+bool AudioFlinger::EffectChain::isNonOffloadableEnabled()
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ if (mEffects[i]->isEnabled() && !mEffects[i]->isOffloadable()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+}; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
new file mode 100644
index 0000000..b717857
--- /dev/null
+++ b/services/audioflinger/Effects.h
@@ -0,0 +1,373 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+//--- Audio Effect Management
+
+// EffectModule and EffectChain classes both have their own mutex to protect
+// state changes or resource modifications. Always respect the following order
+// if multiple mutexes must be acquired to avoid cross deadlock:
+// AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
+// In addition, methods that lock the AudioPolicyService mutex (getOutputForEffect(),
+// startOutput()...) should never be called with AudioFlinger or Threadbase mutex locked
+// to avoid cross deadlock with other clients calling AudioPolicyService methods that in turn
+// call AudioFlinger thus locking the same mutexes in the reverse order.
+
+// The EffectModule class is a wrapper object controlling the effect engine implementation
+// in the effect library. It prevents concurrent calls to process() and command() functions
+// from different client threads. It keeps a list of EffectHandle objects corresponding
+// to all client applications using this effect and notifies applications of effect state,
+// control or parameter changes. It manages the activation state machine to send appropriate
+// reset, enable, disable commands to effect engine and provide volume
+// ramping when effects are activated/deactivated.
+// When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
+// the attached track(s) to accumulate their auxiliary channel.
+class EffectModule : public RefBase {
+public:
+ EffectModule(ThreadBase *thread,
+ const wp<AudioFlinger::EffectChain>& chain,
+ effect_descriptor_t *desc,
+ int id,
+ int sessionId);
+ virtual ~EffectModule();
+
+ enum effect_state {
+ IDLE,
+ RESTART,
+ STARTING,
+ ACTIVE,
+ STOPPING,
+ STOPPED,
+ DESTROYED
+ };
+
+ int id() const { return mId; }
+ void process();
+ void updateState();
+ status_t command(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
+
+ void reset_l();
+ status_t configure();
+ status_t init();
+ effect_state state() const {
+ return mState;
+ }
+ uint32_t status() {
+ return mStatus;
+ }
+ int sessionId() const {
+ return mSessionId;
+ }
+ status_t setEnabled(bool enabled);
+ status_t setEnabled_l(bool enabled);
+ bool isEnabled() const;
+ bool isProcessEnabled() const;
+
+ 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; }
+ void setChain(const wp<EffectChain>& chain) { mChain = chain; }
+ void setThread(const wp<ThreadBase>& thread) { mThread = thread; }
+ const wp<ThreadBase>& thread() { return mThread; }
+
+ status_t addHandle(EffectHandle *handle);
+ size_t disconnect(EffectHandle *handle, bool unpinIfLast);
+ size_t removeHandle(EffectHandle *handle);
+
+ const effect_descriptor_t& desc() const { return mDescriptor; }
+ wp<EffectChain>& chain() { return mChain; }
+
+ status_t setDevice(audio_devices_t device);
+ status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
+ status_t setMode(audio_mode_t mode);
+ status_t setAudioSource(audio_source_t source);
+ status_t start();
+ status_t stop();
+ void setSuspended(bool suspended);
+ bool suspended() const;
+
+ EffectHandle* controlHandle_l();
+
+ bool isPinned() const { return mPinned; }
+ void unPin() { mPinned = false; }
+ bool purgeHandles();
+ void lock() { mLock.lock(); }
+ void unlock() { mLock.unlock(); }
+ bool isOffloadable() const
+ { return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
+ status_t setOffloaded(bool offloaded, audio_io_handle_t io);
+ bool isOffloaded() const;
+
+ void dump(int fd, const Vector<String16>& args);
+
+protected:
+ friend class AudioFlinger; // for mHandles
+ bool mPinned;
+
+ // Maximum time allocated to effect engines to complete the turn off sequence
+ static const uint32_t MAX_DISABLE_TIME_MS = 10000;
+
+ EffectModule(const EffectModule&);
+ EffectModule& operator = (const EffectModule&);
+
+ status_t start_l();
+ status_t stop_l();
+ status_t remove_effect_from_hal_l();
+
+mutable Mutex mLock; // mutex for process, commands and handles list protection
+ wp<ThreadBase> mThread; // parent thread
+ wp<EffectChain> mChain; // parent effect chain
+ const int mId; // this instance unique ID
+ const int mSessionId; // audio session ID
+ const effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
+ effect_config_t mConfig; // input and output audio configuration
+ effect_handle_t mEffectInterface; // Effect module C API
+ status_t mStatus; // initialization status
+ effect_state mState; // current activation state
+ Vector<EffectHandle *> mHandles; // list of client handles
+ // First handle in mHandles has highest priority and controls the effect module
+ uint32_t mMaxDisableWaitCnt; // maximum grace period before forcing an effect off after
+ // sending disable command.
+ uint32_t mDisableWaitCnt; // current process() calls count during disable period.
+ bool mSuspended; // effect is suspended: temporarily disabled by framework
+ bool mOffloaded; // effect is currently offloaded to the audio DSP
+};
+
+// 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(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
+ virtual void disconnect();
+private:
+ void disconnect(bool unpinIfLast);
+public:
+ virtual sp<IMemory> getCblk() const { return mCblkMemory; }
+ virtual status_t onTransact(uint32_t code, const Parcel& data,
+ Parcel* reply, uint32_t flags);
+
+
+ // Give or take control of effect module
+ // - hasControl: true if control is given, false if removed
+ // - signal: true client app should be signaled of change, false otherwise
+ // - enabled: state of the effect when control is passed
+ void setControl(bool hasControl, bool signal, bool enabled);
+ void commandExecuted(uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t replySize,
+ void *pReplyData);
+ void setEnabled(bool enabled);
+ bool enabled() const { return mEnabled; }
+
+ // Getters
+ int id() const { return mEffect->id(); }
+ int priority() const { return mPriority; }
+ bool hasControl() const { return mHasControl; }
+ sp<EffectModule> effect() const { return mEffect; }
+ // destroyed_l() must be called with the associated EffectModule mLock held
+ bool destroyed_l() const { return mDestroyed; }
+
+ void dump(char* buffer, size_t size);
+
+protected:
+ friend class AudioFlinger; // for mEffect, mHasControl, mEnabled
+ EffectHandle(const EffectHandle&);
+ EffectHandle& operator =(const EffectHandle&);
+
+ sp<EffectModule> mEffect; // pointer to controlled EffectModule
+ sp<IEffectClient> mEffectClient; // callback interface for client notifications
+ /*const*/ sp<Client> mClient; // client for shared memory allocation, see disconnect()
+ sp<IMemory> mCblkMemory; // shared memory for control block
+ effect_param_cblk_t* mCblk; // control block for deferred parameter setting via
+ // shared memory
+ uint8_t* mBuffer; // pointer to parameter area in shared memory
+ int mPriority; // client application priority to control the effect
+ bool mHasControl; // true if this handle is controlling the effect
+ bool mEnabled; // cached enable state: needed when the effect is
+ // restored after being suspended
+ bool mDestroyed; // Set to true by destructor. Access with EffectModule
+ // mLock held
+};
+
+// 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(ThreadBase *thread, int sessionId);
+ virtual ~EffectChain();
+
+ // special key used for an entry in mSuspendedEffects keyed vector
+ // corresponding to a suspend all request.
+ static const int kKeyForSuspendAll = 0;
+
+ // minimum duration during which we force calling effect process when last track on
+ // a session is stopped or removed to allow effect tail to be rendered
+ static const int kProcessTailDurationMs = 1000;
+
+ void process_l();
+
+ void lock() {
+ mLock.lock();
+ }
+ void unlock() {
+ mLock.unlock();
+ }
+
+ status_t addEffect_l(const sp<EffectModule>& handle);
+ size_t removeEffect_l(const sp<EffectModule>& handle);
+
+ int sessionId() const { return mSessionId; }
+ void setSessionId(int sessionId) { mSessionId = sessionId; }
+
+ sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor);
+ sp<EffectModule> getEffectFromId_l(int id);
+ sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);
+ bool setVolume_l(uint32_t *left, uint32_t *right);
+ void setDevice_l(audio_devices_t device);
+ void setMode_l(audio_mode_t mode);
+ void setAudioSource_l(audio_source_t source);
+
+ void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
+ mInBuffer = buffer;
+ mOwnInBuffer = ownsBuffer;
+ }
+ int16_t *inBuffer() const {
+ return mInBuffer;
+ }
+ void setOutBuffer(int16_t *buffer) {
+ mOutBuffer = buffer;
+ }
+ int16_t *outBuffer() const {
+ return mOutBuffer;
+ }
+
+ void incTrackCnt() { android_atomic_inc(&mTrackCnt); }
+ void decTrackCnt() { android_atomic_dec(&mTrackCnt); }
+ int32_t trackCnt() const { return android_atomic_acquire_load(&mTrackCnt); }
+
+ void incActiveTrackCnt() { android_atomic_inc(&mActiveTrackCnt);
+ mTailBufferCount = mMaxTailBuffers; }
+ void decActiveTrackCnt() { android_atomic_dec(&mActiveTrackCnt); }
+ int32_t activeTrackCnt() const { return android_atomic_acquire_load(&mActiveTrackCnt); }
+
+ uint32_t strategy() const { return mStrategy; }
+ void setStrategy(uint32_t strategy)
+ { mStrategy = strategy; }
+
+ // suspend effect of the given type
+ void setEffectSuspended_l(const effect_uuid_t *type,
+ bool suspend);
+ // suspend all eligible effects
+ void setEffectSuspendedAll_l(bool suspend);
+ // check if effects should be suspend or restored when a given effect is enable or disabled
+ void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
+ bool enabled);
+
+ void clearInputBuffer();
+
+ // At least one non offloadable effect in the chain is enabled
+ bool isNonOffloadableEnabled();
+
+
+ void dump(int fd, const Vector<String16>& args);
+
+protected:
+ friend class AudioFlinger; // for mThread, mEffects
+ EffectChain(const EffectChain&);
+ EffectChain& operator =(const EffectChain&);
+
+ class SuspendedEffectDesc : public RefBase {
+ public:
+ SuspendedEffectDesc() : mRefCount(0) {}
+
+ int mRefCount;
+ effect_uuid_t mType;
+ wp<EffectModule> mEffect;
+ };
+
+ // get a list of effect modules to suspend when an effect of the type
+ // passed is enabled.
+ void getSuspendEligibleEffects(Vector< sp<EffectModule> > &effects);
+
+ // get an effect module if it is currently enable
+ sp<EffectModule> getEffectIfEnabled(const effect_uuid_t *type);
+ // true if the effect whose descriptor is passed can be suspended
+ // OEMs can modify the rules implemented in this method to exclude specific effect
+ // types or implementations from the suspend/restore mechanism.
+ bool isEffectEligibleForSuspend(const effect_descriptor_t& desc);
+
+ void clearInputBuffer_l(sp<ThreadBase> thread);
+
+ 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
+
+ // 'volatile' here means these are accessed with atomic operations instead of mutex
+ volatile int32_t mActiveTrackCnt; // number of active tracks connected
+ volatile int32_t mTrackCnt; // number of tracks connected
+
+ int32_t mTailBufferCount; // current effect tail buffer count
+ int32_t mMaxTailBuffers; // maximum effect tail buffers
+ bool mOwnInBuffer; // true if the chain owns its input buffer
+ int mVolumeCtrlIdx; // index of insert effect having control over volume
+ uint32_t mLeftVolume; // previous volume on left channel
+ uint32_t mRightVolume; // previous volume on right channel
+ uint32_t mNewLeftVolume; // new volume on left channel
+ uint32_t mNewRightVolume; // new volume on right channel
+ uint32_t mStrategy; // strategy for this effect chain
+ // mSuspendedEffects lists all effects currently suspended in the chain.
+ // Use effect type UUID timelow field as key. There is no real risk of identical
+ // timeLow fields among effect type UUIDs.
+ // Updated by updateSuspendedSessions_l() only.
+ KeyedVector< int, sp<SuspendedEffectDesc> > mSuspendedEffects;
+};
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 3c8a256..85d637e 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -14,9 +14,18 @@
* limitations under the License.
*/
+// <IMPORTANT_WARNING>
+// Design rules for threadLoop() are given in the comments at section "Fast mixer thread" of
+// StateQueue.h. In particular, avoid library and system calls except at well-known points.
+// The design rules are only for threadLoop(), and don't apply to FastMixerDumpState methods.
+// </IMPORTANT_WARNING>
+
#define LOG_TAG "FastMixer"
//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include "Configuration.h"
#include <sys/atomics.h>
#include <time.h>
#include <utils/Log.h>
@@ -36,6 +45,8 @@
#define MIN_WARMUP_CYCLES 2 // minimum number of loop cycles to wait for warmup
#define MAX_WARMUP_CYCLES 10 // maximum number of loop cycles to wait for warmup
+#define FCC_2 2 // fixed channel count assumption
+
namespace android {
// Fast mixer thread
@@ -74,7 +85,7 @@ bool FastMixer::threadLoop()
struct timespec oldLoad = {0, 0}; // previous value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
bool oldLoadValid = false; // whether oldLoad is valid
uint32_t bounds = 0;
- bool full = false; // whether we have collected at least kSamplingN samples
+ bool full = false; // whether we have collected at least mSamplingN samples
#ifdef CPU_FREQUENCY_STATISTICS
ThreadCpuUsage tcu; // for reading the current CPU clock frequency in kHz
#endif
@@ -84,6 +95,13 @@ bool FastMixer::threadLoop()
struct timespec measuredWarmupTs = {0, 0}; // how long did it take for warmup to complete
uint32_t warmupCycles = 0; // counter of number of loop cycles required to warmup
NBAIO_Sink* teeSink = NULL; // if non-NULL, then duplicate write() to this non-blocking sink
+ NBLog::Writer dummyLogWriter, *logWriter = &dummyLogWriter;
+ uint32_t totalNativeFramesWritten = 0; // copied to dumpState->mFramesWritten
+
+ // next 2 fields are valid only when timestampStatus == NO_ERROR
+ AudioTimestamp timestamp;
+ uint32_t nativeFramesWrittenButNotPresented = 0; // the = 0 is to silence the compiler
+ status_t timestampStatus = INVALID_OPERATION;
for (;;) {
@@ -114,6 +132,10 @@ bool FastMixer::threadLoop()
// As soon as possible of learning of a new dump area, start using it
dumpState = next->mDumpState != NULL ? next->mDumpState : &dummyDumpState;
teeSink = next->mTeeSink;
+ logWriter = next->mNBLogWriter != NULL ? next->mNBLogWriter : &dummyLogWriter;
+ if (mixer != NULL) {
+ mixer->setLog(logWriter);
+ }
// 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.
@@ -129,7 +151,9 @@ bool FastMixer::threadLoop()
preIdle = *current;
current = &preIdle;
oldTsValid = false;
+#ifdef FAST_MIXER_STATISTICS
oldLoadValid = false;
+#endif
ignoreNextOverrun = true;
}
previous = current;
@@ -157,6 +181,10 @@ bool FastMixer::threadLoop()
if (old <= 0) {
__futex_syscall4(coldFutexAddr, FUTEX_WAIT_PRIVATE, old - 1, NULL);
}
+ int policy = sched_getscheduler(0);
+ if (!(policy == SCHED_FIFO || policy == SCHED_RR)) {
+ ALOGE("did not receive expected priority boost");
+ }
// This may be overly conservative; there could be times that the normal mixer
// requests such a brief cold idle that it doesn't require resetting this flag.
isWarm = false;
@@ -165,9 +193,12 @@ bool FastMixer::threadLoop()
warmupCycles = 0;
sleepNs = -1;
coldGen = current->mColdGen;
+#ifdef FAST_MIXER_STATISTICS
bounds = 0;
full = false;
+#endif
oldTsValid = !clock_gettime(CLOCK_MONOTONIC, &oldTs);
+ timestampStatus = INVALID_OPERATION;
} else {
sleepNs = FAST_HOT_IDLE_NS;
}
@@ -203,9 +234,8 @@ bool FastMixer::threadLoop()
} else {
format = outputSink->format();
sampleRate = Format_sampleRate(format);
- ALOG_ASSERT(Format_channelCount(format) == 2);
+ ALOG_ASSERT(Format_channelCount(format) == FCC_2);
}
- dumpState->mSampleRate = sampleRate;
}
if ((format != previousFormat) || (frameCount != previous->mFrameCount)) {
@@ -219,7 +249,7 @@ bool FastMixer::threadLoop()
// 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];
+ mixBuffer = new short[frameCount * FCC_2];
periodNs = (frameCount * 1000000000LL) / sampleRate; // 1.00
underrunNs = (frameCount * 1750000000LL) / sampleRate; // 1.75
overrunNs = (frameCount * 500000000LL) / sampleRate; // 0.50
@@ -290,18 +320,14 @@ bool FastMixer::threadLoop()
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER,
(void *) mixBuffer);
// newly allocated track names default to full scale volume
- if (fastTrack->mSampleRate != 0 && fastTrack->mSampleRate != sampleRate) {
- mixer->setParameter(name, AudioMixer::RESAMPLE,
- AudioMixer::SAMPLE_RATE, (void*) fastTrack->mSampleRate);
- }
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
- (void *) fastTrack->mChannelMask);
+ (void *)(uintptr_t)fastTrack->mChannelMask);
mixer->enable(name);
}
generations[i] = fastTrack->mGeneration;
}
- // finally process modified tracks; these use the same slot
+ // finally process (potentially) modified tracks; these use the same slot
// but may have a different buffer provider or volume provider
unsigned modifiedTracks = currentTrackMask & previousTrackMask;
while (modifiedTracks != 0) {
@@ -309,6 +335,7 @@ bool FastMixer::threadLoop()
modifiedTracks &= ~(1 << i);
const FastTrack* fastTrack = &current->mFastTracks[i];
if (fastTrack->mGeneration != generations[i]) {
+ // this track was actually modified
AudioBufferProvider *bufferProvider = fastTrack->mBufferProvider;
ALOG_ASSERT(bufferProvider != NULL);
if (mixer != NULL) {
@@ -321,16 +348,10 @@ bool FastMixer::threadLoop()
mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
(void *)0x1000);
}
- if (fastTrack->mSampleRate != 0 &&
- fastTrack->mSampleRate != sampleRate) {
- mixer->setParameter(name, AudioMixer::RESAMPLE,
- AudioMixer::SAMPLE_RATE, (void*) fastTrack->mSampleRate);
- } else {
- mixer->setParameter(name, AudioMixer::RESAMPLE,
- AudioMixer::REMOVE, NULL);
- }
+ mixer->setParameter(name, AudioMixer::RESAMPLE,
+ AudioMixer::REMOVE, NULL);
mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::CHANNEL_MASK,
- (void *) fastTrack->mChannelMask);
+ (void *)(uintptr_t) fastTrack->mChannelMask);
// already enabled
}
generations[i] = fastTrack->mGeneration;
@@ -357,28 +378,45 @@ bool FastMixer::threadLoop()
i = __builtin_ctz(currentTrackMask);
currentTrackMask &= ~(1 << i);
const FastTrack* fastTrack = &current->mFastTracks[i];
+
+ // Refresh the per-track timestamp
+ if (timestampStatus == NO_ERROR) {
+ uint32_t trackFramesWrittenButNotPresented =
+ nativeFramesWrittenButNotPresented;
+ uint32_t trackFramesWritten = fastTrack->mBufferProvider->framesReleased();
+ // Can't provide an AudioTimestamp before first frame presented,
+ // or during the brief 32-bit wraparound window
+ if (trackFramesWritten >= trackFramesWrittenButNotPresented) {
+ AudioTimestamp perTrackTimestamp;
+ perTrackTimestamp.mPosition =
+ trackFramesWritten - trackFramesWrittenButNotPresented;
+ perTrackTimestamp.mTime = timestamp.mTime;
+ fastTrack->mBufferProvider->onTimestamp(perTrackTimestamp);
+ }
+ }
+
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));
+ (void *)(uintptr_t)(vlr & 0xFFFF));
mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1,
- (void *)(vlr >> 16));
+ (void *)(uintptr_t)(vlr >> 16));
}
// FIXME The current implementation of framesReady() for fast tracks
// takes a tryLock, which can block
// up to 1 ms. If enough active tracks all blocked in sequence, this would result
// in the overall fast mix cycle being delayed. Should use a non-blocking FIFO.
size_t framesReady = fastTrack->mBufferProvider->framesReady();
-#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- // I wish we had formatted trace names
- char traceName[16];
- strcpy(traceName, "framesReady");
- traceName[11] = i + (i < 10 ? '0' : 'A' - 10);
- traceName[12] = '\0';
- ATRACE_INT(traceName, framesReady);
-#endif
+ if (ATRACE_ENABLED()) {
+ // I wish we had formatted trace names
+ char traceName[16];
+ strcpy(traceName, "fRdy");
+ traceName[4] = i + (i < 10 ? '0' : 'A' - 10);
+ traceName[5] = '\0';
+ ATRACE_INT(traceName, framesReady);
+ }
FastTrackDump *ftDump = &dumpState->mTracks[i];
FastTrackUnderruns underruns = ftDump->mUnderruns;
if (framesReady < frameCount) {
@@ -415,7 +453,7 @@ bool FastMixer::threadLoop()
//bool didFullWrite = false; // dumpsys could display a count of partial writes
if ((command & FastMixerState::WRITE) && (outputSink != NULL) && (mixBuffer != NULL)) {
if (mixBufferState == UNDEFINED) {
- memset(mixBuffer, 0, frameCount * 2 * sizeof(short));
+ memset(mixBuffer, 0, frameCount * FCC_2 * sizeof(short));
mixBufferState = ZEROED;
}
if (teeSink != NULL) {
@@ -424,17 +462,14 @@ bool FastMixer::threadLoop()
// 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++;
-#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- Tracer::traceBegin(ATRACE_TAG, "write");
-#endif
+ ATRACE_BEGIN("write");
ssize_t framesWritten = outputSink->write(mixBuffer, frameCount);
-#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- Tracer::traceEnd(ATRACE_TAG);
-#endif
+ ATRACE_END();
dumpState->mWriteSequence++;
if (framesWritten >= 0) {
- ALOG_ASSERT(framesWritten <= frameCount);
- dumpState->mFramesWritten += framesWritten;
+ ALOG_ASSERT((size_t) framesWritten <= frameCount);
+ totalNativeFramesWritten += framesWritten;
+ dumpState->mFramesWritten = totalNativeFramesWritten;
//if ((size_t) framesWritten == frameCount) {
// didFullWrite = true;
//}
@@ -443,6 +478,18 @@ bool FastMixer::threadLoop()
}
attemptedWrite = true;
// FIXME count # of writes blocked excessively, CPU usage, etc. for dump
+
+ timestampStatus = outputSink->getTimestamp(timestamp);
+ if (timestampStatus == NO_ERROR) {
+ uint32_t totalNativeFramesPresented = timestamp.mPosition;
+ if (totalNativeFramesPresented <= totalNativeFramesWritten) {
+ nativeFramesWrittenButNotPresented =
+ totalNativeFramesWritten - totalNativeFramesPresented;
+ } else {
+ // HAL reported that more frames were presented than were written
+ timestampStatus = INVALID_OPERATION;
+ }
+ }
}
// To be exactly periodic, compute the next sleep time based on current time.
@@ -451,6 +498,7 @@ bool FastMixer::threadLoop()
struct timespec newTs;
int rc = clock_gettime(CLOCK_MONOTONIC, &newTs);
if (rc == 0) {
+ //logWriter->logTimestamp(newTs);
if (oldTsValid) {
time_t sec = newTs.tv_sec - oldTs.tv_sec;
long nsec = newTs.tv_nsec - oldTs.tv_nsec;
@@ -483,95 +531,91 @@ bool FastMixer::threadLoop()
}
}
sleepNs = -1;
- if (isWarm) {
- if (sec > 0 || nsec > underrunNs) {
-#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- ScopedTrace st(ATRACE_TAG, "underrun");
-#endif
- // FIXME only log occasionally
- ALOGV("underrun: time since last cycle %d.%03ld sec",
- (int) sec, nsec / 1000000L);
- dumpState->mUnderruns++;
- ignoreNextOverrun = true;
- } else if (nsec < overrunNs) {
- if (ignoreNextOverrun) {
- ignoreNextOverrun = false;
- } else {
+ if (isWarm) {
+ if (sec > 0 || nsec > underrunNs) {
+ ATRACE_NAME("underrun");
// FIXME only log occasionally
- ALOGV("overrun: time since last cycle %d.%03ld sec",
+ ALOGV("underrun: time since last cycle %d.%03ld sec",
(int) sec, nsec / 1000000L);
- dumpState->mOverruns++;
+ dumpState->mUnderruns++;
+ 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++;
+ }
+ // This forces a minimum cycle time. It:
+ // - compensates for an audio HAL with jitter due to sample rate conversion
+ // - works with a variable buffer depth audio HAL that never pulls at a
+ // rate < than overrunNs per buffer.
+ // - recovers from overrun immediately after underrun
+ // It doesn't work with a non-blocking audio HAL.
+ sleepNs = forceNs - nsec;
+ } else {
+ ignoreNextOverrun = false;
}
- // This forces a minimum cycle time. It:
- // - compensates for an audio HAL with jitter due to sample rate conversion
- // - works with a variable buffer depth audio HAL that never pulls at a rate
- // < than overrunNs per buffer.
- // - recovers from overrun immediately after underrun
- // It doesn't work with a non-blocking audio HAL.
- sleepNs = forceNs - nsec;
- } else {
- ignoreNextOverrun = false;
}
- }
#ifdef FAST_MIXER_STATISTICS
- if (isWarm) {
- // advance the FIFO queue bounds
- size_t i = bounds & (FastMixerDumpState::kSamplingN - 1);
- bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF);
- if (full) {
- bounds += 0x10000;
- } else if (!(bounds & (FastMixerDumpState::kSamplingN - 1))) {
- full = true;
- }
- // compute the delta value of clock_gettime(CLOCK_MONOTONIC)
- uint32_t monotonicNs = nsec;
- if (sec > 0 && sec < 4) {
- monotonicNs += sec * 1000000000;
- }
- // compute the raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
- uint32_t loadNs = 0;
- struct timespec newLoad;
- rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad);
- if (rc == 0) {
- if (oldLoadValid) {
- sec = newLoad.tv_sec - oldLoad.tv_sec;
- nsec = newLoad.tv_nsec - oldLoad.tv_nsec;
- if (nsec < 0) {
- --sec;
- nsec += 1000000000;
- }
- loadNs = nsec;
- if (sec > 0 && sec < 4) {
- loadNs += sec * 1000000000;
+ if (isWarm) {
+ // advance the FIFO queue bounds
+ size_t i = bounds & (dumpState->mSamplingN - 1);
+ bounds = (bounds & 0xFFFF0000) | ((bounds + 1) & 0xFFFF);
+ if (full) {
+ bounds += 0x10000;
+ } else if (!(bounds & (dumpState->mSamplingN - 1))) {
+ full = true;
+ }
+ // compute the delta value of clock_gettime(CLOCK_MONOTONIC)
+ uint32_t monotonicNs = nsec;
+ if (sec > 0 && sec < 4) {
+ monotonicNs += sec * 1000000000;
+ }
+ // compute raw CPU load = delta value of clock_gettime(CLOCK_THREAD_CPUTIME_ID)
+ uint32_t loadNs = 0;
+ struct timespec newLoad;
+ rc = clock_gettime(CLOCK_THREAD_CPUTIME_ID, &newLoad);
+ if (rc == 0) {
+ if (oldLoadValid) {
+ sec = newLoad.tv_sec - oldLoad.tv_sec;
+ nsec = newLoad.tv_nsec - oldLoad.tv_nsec;
+ if (nsec < 0) {
+ --sec;
+ nsec += 1000000000;
+ }
+ loadNs = nsec;
+ if (sec > 0 && sec < 4) {
+ loadNs += sec * 1000000000;
+ }
+ } else {
+ // first time through the loop
+ oldLoadValid = true;
}
- } else {
- // first time through the loop
- oldLoadValid = true;
+ oldLoad = newLoad;
}
- oldLoad = newLoad;
- }
#ifdef CPU_FREQUENCY_STATISTICS
- // get the absolute value of CPU clock frequency in kHz
- int cpuNum = sched_getcpu();
- uint32_t kHz = tcu.getCpukHz(cpuNum);
- kHz = (kHz << 4) | (cpuNum & 0xF);
+ // get the absolute value of CPU clock frequency in kHz
+ int cpuNum = sched_getcpu();
+ uint32_t kHz = tcu.getCpukHz(cpuNum);
+ kHz = (kHz << 4) | (cpuNum & 0xF);
#endif
- // save values in FIFO queues for dumpsys
- // these stores #1, #2, #3 are not atomic with respect to each other,
- // or with respect to store #4 below
- dumpState->mMonotonicNs[i] = monotonicNs;
- dumpState->mLoadNs[i] = loadNs;
+ // save values in FIFO queues for dumpsys
+ // these stores #1, #2, #3 are not atomic with respect to each other,
+ // or with respect to store #4 below
+ dumpState->mMonotonicNs[i] = monotonicNs;
+ dumpState->mLoadNs[i] = loadNs;
#ifdef CPU_FREQUENCY_STATISTICS
- dumpState->mCpukHz[i] = kHz;
-#endif
- // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
- // the newest open and oldest closed halves are atomic with respect to each other
- dumpState->mBounds = bounds;
-#if defined(ATRACE_TAG) && (ATRACE_TAG != ATRACE_TAG_NEVER)
- ATRACE_INT("cycle_ms", monotonicNs / 1000000);
- ATRACE_INT("load_us", loadNs / 1000);
+ dumpState->mCpukHz[i] = kHz;
#endif
- }
+ // this store #4 is not atomic with respect to stores #1, #2, #3 above, but
+ // the newest open & oldest closed halves are atomic with respect to each other
+ dumpState->mBounds = bounds;
+ ATRACE_INT("cycle_ms", monotonicNs / 1000000);
+ ATRACE_INT("load_us", loadNs / 1000);
+ }
#endif
} else {
// first time through the loop
@@ -592,25 +636,43 @@ bool FastMixer::threadLoop()
// never return 'true'; Thread::_threadLoop() locks mutex which can result in priority inversion
}
-FastMixerDumpState::FastMixerDumpState() :
+FastMixerDumpState::FastMixerDumpState(
+#ifdef FAST_MIXER_STATISTICS
+ uint32_t samplingN
+#endif
+ ) :
mCommand(FastMixerState::INITIAL), mWriteSequence(0), mFramesWritten(0),
mNumTracks(0), mWriteErrors(0), mUnderruns(0), mOverruns(0),
mSampleRate(0), mFrameCount(0), /* mMeasuredWarmupTs({0, 0}), */ mWarmupCycles(0),
mTrackMask(0)
#ifdef FAST_MIXER_STATISTICS
- , mBounds(0)
+ , mSamplingN(0), mBounds(0)
#endif
{
mMeasuredWarmupTs.tv_sec = 0;
mMeasuredWarmupTs.tv_nsec = 0;
+#ifdef FAST_MIXER_STATISTICS
+ increaseSamplingN(samplingN);
+#endif
+}
+
+#ifdef FAST_MIXER_STATISTICS
+void FastMixerDumpState::increaseSamplingN(uint32_t samplingN)
+{
+ if (samplingN <= mSamplingN || samplingN > kSamplingN || roundup(samplingN) != samplingN) {
+ return;
+ }
+ uint32_t additional = samplingN - mSamplingN;
// sample arrays aren't accessed atomically with respect to the bounds,
// so clearing reduces chance for dumpsys to read random uninitialized samples
- memset(&mMonotonicNs, 0, sizeof(mMonotonicNs));
- memset(&mLoadNs, 0, sizeof(mLoadNs));
+ memset(&mMonotonicNs[mSamplingN], 0, sizeof(mMonotonicNs[0]) * additional);
+ memset(&mLoadNs[mSamplingN], 0, sizeof(mLoadNs[0]) * additional);
#ifdef CPU_FREQUENCY_STATISTICS
- memset(&mCpukHz, 0, sizeof(mCpukHz));
+ memset(&mCpukHz[mSamplingN], 0, sizeof(mCpukHz[0]) * additional);
#endif
+ mSamplingN = samplingN;
}
+#endif
FastMixerDumpState::~FastMixerDumpState()
{
@@ -630,7 +692,7 @@ static int compare_uint32_t(const void *pa, const void *pb)
}
}
-void FastMixerDumpState::dump(int fd)
+void FastMixerDumpState::dump(int fd) const
{
if (mCommand == FastMixerState::INITIAL) {
fdprintf(fd, "FastMixer not initialized\n");
@@ -669,7 +731,7 @@ void FastMixerDumpState::dump(int fd)
double mixPeriodSec = (double) mFrameCount / (double) mSampleRate;
fdprintf(fd, "FastMixer command=%s writeSequence=%u framesWritten=%u\n"
" numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
- " sampleRate=%u frameCount=%u measuredWarmup=%.3g ms, warmupCycles=%u\n"
+ " sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
" mixPeriod=%.2f ms\n",
string, mWriteSequence, mFramesWritten,
mNumTracks, mWriteErrors, mUnderruns, mOverruns,
@@ -681,9 +743,9 @@ void FastMixerDumpState::dump(int fd)
uint32_t newestOpen = bounds & 0xFFFF;
uint32_t oldestClosed = bounds >> 16;
uint32_t n = (newestOpen - oldestClosed) & 0xFFFF;
- if (n > kSamplingN) {
+ if (n > mSamplingN) {
ALOGE("too many samples %u", n);
- n = kSamplingN;
+ n = mSamplingN;
}
// statistics for monotonic (wall clock) time, thread raw CPU load in time, CPU clock frequency,
// and adjusted CPU load in MHz normalized for CPU clock frequency
@@ -699,7 +761,7 @@ void FastMixerDumpState::dump(int fd)
uint32_t *tail = n >= kTailDenominator ? new uint32_t[n] : NULL;
// loop over all the samples
for (uint32_t j = 0; j < n; ++j) {
- size_t i = oldestClosed++ & (kSamplingN - 1);
+ size_t i = oldestClosed++ & (mSamplingN - 1);
uint32_t wallNs = mMonotonicNs[i];
if (tail != NULL) {
tail[j] = wallNs;
@@ -783,7 +845,7 @@ void FastMixerDumpState::dump(int fd)
mostRecent = "?";
break;
}
- fdprintf(fd, "%5u %6s %4u %7u %5u %7s %5u\n", i, isActive ? "yes" : "no",
+ fdprintf(fd, "%5u %6s %4u %7u %5u %7s %5zu\n", i, isActive ? "yes" : "no",
(underruns.mBitFields.mFull) & UNDERRUN_MASK,
(underruns.mBitFields.mPartial) & UNDERRUN_MASK,
(underruns.mBitFields.mEmpty) & UNDERRUN_MASK,
diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h
index 462739b..6158925 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -85,10 +85,14 @@ struct FastTrackDump {
// Only POD types are permitted, and the contents shouldn't be trusted (i.e. do range checks).
// It has a different lifetime than the FastMixer, and so it can't be a member of FastMixer.
struct FastMixerDumpState {
- FastMixerDumpState();
+ FastMixerDumpState(
+#ifdef FAST_MIXER_STATISTICS
+ uint32_t samplingN = kSamplingNforLowRamDevice
+#endif
+ );
/*virtual*/ ~FastMixerDumpState();
- void dump(int fd); // should only be called on a stable copy, not the original
+ void dump(int fd) const; // should only be called on a stable copy, not the original
FastMixerState::Command mCommand; // current command
uint32_t mWriteSequence; // incremented before and after each write()
@@ -106,8 +110,15 @@ struct FastMixerDumpState {
#ifdef FAST_MIXER_STATISTICS
// Recently collected samples of per-cycle monotonic time, thread CPU time, and CPU frequency.
- // kSamplingN is the size of the sampling frame, and must be a power of 2 <= 0x8000.
- static const uint32_t kSamplingN = 0x1000;
+ // kSamplingN is max size of sampling frame (statistics), and must be a power of 2 <= 0x8000.
+ // The sample arrays are virtually allocated based on this compile-time constant,
+ // but are only initialized and used based on the runtime parameter mSamplingN.
+ static const uint32_t kSamplingN = 0x8000;
+ // Compile-time constant for a "low RAM device", must be a power of 2 <= kSamplingN.
+ // This value was chosen such that each array uses 1 small page (4 Kbytes).
+ static const uint32_t kSamplingNforLowRamDevice = 0x400;
+ // Corresponding runtime maximum size of sample arrays, must be a power of 2 <= kSamplingN.
+ uint32_t mSamplingN;
// The bounds define the interval of valid samples, and are represented as follows:
// newest open (excluded) endpoint = lower 16 bits of bounds, modulo N
// oldest closed (included) endpoint = upper 16 bits of bounds, modulo N
@@ -119,6 +130,8 @@ struct FastMixerDumpState {
#ifdef CPU_FREQUENCY_STATISTICS
uint32_t mCpukHz[kSamplingN]; // absolute CPU clock frequency in kHz, bits 0-3 are CPU#
#endif
+ // Increase sampling window after construction, must be a power of 2 <= kSamplingN
+ void increaseSamplingN(uint32_t samplingN);
#endif
};
diff --git a/services/audioflinger/FastMixerState.cpp b/services/audioflinger/FastMixerState.cpp
index 6305a83..43ff233 100644
--- a/services/audioflinger/FastMixerState.cpp
+++ b/services/audioflinger/FastMixerState.cpp
@@ -14,12 +14,13 @@
* limitations under the License.
*/
+#include "Configuration.h"
#include "FastMixerState.h"
namespace android {
FastTrack::FastTrack() :
- mBufferProvider(NULL), mVolumeProvider(NULL), mSampleRate(0),
+ mBufferProvider(NULL), mVolumeProvider(NULL),
mChannelMask(AUDIO_CHANNEL_OUT_STEREO), mGeneration(0)
{
}
@@ -31,7 +32,7 @@ FastTrack::~FastTrack()
FastMixerState::FastMixerState() :
mFastTracksGen(0), mTrackMask(0), mOutputSink(NULL), mOutputSinkGen(0),
mFrameCount(0), mCommand(INITIAL), mColdFutexAddr(NULL), mColdGen(0),
- mDumpState(NULL), mTeeSink(NULL)
+ mDumpState(NULL), mTeeSink(NULL), mNBLogWriter(NULL)
{
}
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 6e53f21..9739fe9 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -20,6 +20,7 @@
#include <system/audio.h>
#include <media/ExtendedAudioBufferProvider.h>
#include <media/nbaio/NBAIO.h>
+#include <media/nbaio/NBLog.h>
namespace android {
@@ -42,7 +43,6 @@ struct FastTrack {
ExtendedAudioBufferProvider* mBufferProvider; // must be NULL if inactive, or non-NULL if active
VolumeProvider* mVolumeProvider; // optional; if NULL then full-scale
- unsigned mSampleRate; // optional; if zero then use mixer sample rate
audio_channel_mask_t mChannelMask; // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO
int mGeneration; // increment when any field is assigned
};
@@ -77,6 +77,7 @@ struct FastMixerState {
// This might be a one-time configuration rather than per-state
FastMixerDumpState* mDumpState; // if non-NULL, then update dump state periodically
NBAIO_Sink* mTeeSink; // if non-NULL, then duplicate write()s to this non-blocking sink
+ NBLog::Writer* mNBLogWriter; // non-blocking logger
}; // struct FastMixerState
} // namespace android
diff --git a/services/audioflinger/ISchedulingPolicyService.cpp b/services/audioflinger/ISchedulingPolicyService.cpp
index 909b77e..f55bc02 100644
--- a/services/audioflinger/ISchedulingPolicyService.cpp
+++ b/services/audioflinger/ISchedulingPolicyService.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "SchedulingPolicyService"
+#define LOG_TAG "ISchedulingPolicyService"
//#define LOG_NDEBUG 0
#include <binder/Parcel.h>
@@ -37,16 +37,25 @@ public:
{
}
- virtual int requestPriority(int32_t pid, int32_t tid, int32_t prio)
+ virtual int requestPriority(int32_t pid, int32_t tid, int32_t prio, bool asynchronous)
{
Parcel data, reply;
data.writeInterfaceToken(ISchedulingPolicyService::getInterfaceDescriptor());
data.writeInt32(pid);
data.writeInt32(tid);
data.writeInt32(prio);
- remote()->transact(REQUEST_PRIORITY_TRANSACTION, data, &reply);
- // fail on exception
- if (reply.readExceptionCode() != 0) return -1;
+ uint32_t flags = asynchronous ? IBinder::FLAG_ONEWAY : 0;
+ status_t status = remote()->transact(REQUEST_PRIORITY_TRANSACTION, data, &reply, flags);
+ if (status != NO_ERROR) {
+ return status;
+ }
+ if (asynchronous) {
+ return NO_ERROR;
+ }
+ // fail on exception: force binder reconnection
+ if (reply.readExceptionCode() != 0) {
+ return DEAD_OBJECT;
+ }
return reply.readInt32();
}
};
diff --git a/services/audioflinger/ISchedulingPolicyService.h b/services/audioflinger/ISchedulingPolicyService.h
index a38e67e..b94b191 100644
--- a/services/audioflinger/ISchedulingPolicyService.h
+++ b/services/audioflinger/ISchedulingPolicyService.h
@@ -27,7 +27,7 @@ public:
DECLARE_META_INTERFACE(SchedulingPolicyService);
virtual int requestPriority(/*pid_t*/int32_t pid, /*pid_t*/int32_t tid,
- int32_t prio) = 0;
+ int32_t prio, bool asynchronous) = 0;
};
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
new file mode 100644
index 0000000..43b77f3
--- /dev/null
+++ b/services/audioflinger/PlaybackTracks.h
@@ -0,0 +1,288 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+// playback track
+class Track : public TrackBase, public VolumeProvider {
+public:
+ Track( PlaybackThread *thread,
+ const sp<Client>& client,
+ audio_stream_type_t streamType,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ int uid,
+ IAudioFlinger::track_flags_t flags);
+ virtual ~Track();
+
+ static void appendDumpHeader(String8& result);
+ void dump(char* buffer, size_t size);
+ virtual status_t start(AudioSystem::sync_event_t event =
+ AudioSystem::SYNC_EVENT_NONE,
+ int triggerSession = 0);
+ virtual void stop();
+ void pause();
+
+ void flush();
+ void destroy();
+ int name() const { return mName; }
+
+ virtual uint32_t sampleRate() const;
+
+ audio_stream_type_t streamType() const {
+ return mStreamType;
+ }
+ bool isOffloaded() const { return (mFlags & IAudioFlinger::TRACK_OFFLOAD) != 0; }
+ status_t setParameters(const String8& keyValuePairs);
+ status_t attachAuxEffect(int EffectId);
+ void setAuxBuffer(int EffectId, int32_t *buffer);
+ int32_t *auxBuffer() const { return mAuxBuffer; }
+ void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
+ int16_t *mainBuffer() const { return mMainBuffer; }
+ int auxEffectId() const { return mAuxEffectId; }
+ virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ void signal();
+
+// implement FastMixerState::VolumeProvider interface
+ virtual uint32_t getVolumeLR();
+
+ virtual status_t setSyncEvent(const sp<SyncEvent>& event);
+
+protected:
+ // for numerous
+ friend class PlaybackThread;
+ friend class MixerThread;
+ friend class DirectOutputThread;
+ friend class OffloadThread;
+
+ Track(const Track&);
+ Track& operator = (const Track&);
+
+ // AudioBufferProvider interface
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer,
+ int64_t pts = kInvalidPTS);
+ // releaseBuffer() not overridden
+
+ // ExtendedAudioBufferProvider interface
+ virtual size_t framesReady() const;
+ virtual size_t framesReleased() const;
+
+ bool isPausing() const { return mState == PAUSING; }
+ bool isPaused() const { return mState == PAUSED; }
+ bool isResuming() const { return mState == RESUMING; }
+ bool isReady() const;
+ void setPaused() { mState = PAUSED; }
+ void reset();
+
+ bool isOutputTrack() const {
+ return (mStreamType == AUDIO_STREAM_CNT);
+ }
+
+ sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
+
+ // framesWritten is cumulative, never reset, and is shared all tracks
+ // audioHalFrames is derived from output latency
+ // FIXME parameters not needed, could get them from the thread
+ bool presentationComplete(size_t framesWritten, size_t audioHalFrames);
+
+public:
+ void triggerEvents(AudioSystem::sync_event_t type);
+ void invalidate();
+ bool isInvalid() const { return mIsInvalid; }
+ virtual bool isTimedTrack() const { return false; }
+ bool isFastTrack() const { return (mFlags & IAudioFlinger::TRACK_FAST) != 0; }
+ int fastIndex() const { return mFastIndex; }
+
+protected:
+
+ // FILLED state is used for suppressing volume ramp at begin of playing
+ enum {FS_INVALID, FS_FILLING, FS_FILLED, FS_ACTIVE};
+ mutable uint8_t mFillingUpStatus;
+ int8_t mRetryCount;
+
+ // see comment at AudioFlinger::PlaybackThread::Track::~Track for why this can't be const
+ sp<IMemory> mSharedBuffer;
+
+ bool mResetDone;
+ const audio_stream_type_t mStreamType;
+ int mName; // track name on the normal mixer,
+ // allocated statically at track creation time,
+ // and is even allocated (though unused) for fast tracks
+ // FIXME don't allocate track name for fast tracks
+ int16_t *mMainBuffer;
+ int32_t *mAuxBuffer;
+ int mAuxEffectId;
+ bool mHasVolumeController;
+ size_t mPresentationCompleteFrames; // number of frames written to the
+ // audio HAL when this track will be fully rendered
+ // zero means not monitoring
+private:
+ IAudioFlinger::track_flags_t mFlags;
+
+ // The following fields are only for fast tracks, and should be in a subclass
+ int mFastIndex; // index within FastMixerState::mFastTracks[];
+ // either mFastIndex == -1 if not isFastTrack()
+ // or 0 < mFastIndex < FastMixerState::kMaxFast because
+ // index 0 is reserved for normal mixer's submix;
+ // index is allocated statically at track creation time
+ // but the slot is only used if track is active
+ FastTrackUnderruns mObservedUnderruns; // Most recently observed value of
+ // mFastMixerDumpState.mTracks[mFastIndex].mUnderruns
+ volatile float mCachedVolume; // combined master volume and stream type volume;
+ // 'volatile' means accessed without lock or
+ // barrier, but is read/written atomically
+ bool mIsInvalid; // non-resettable latch, set by invalidate()
+ AudioTrackServerProxy* mAudioTrackServerProxy;
+ bool mResumeToStopping; // track was paused in stopping state.
+}; // end of Track
+
+class TimedTrack : public Track {
+ public:
+ static sp<TimedTrack> create(PlaybackThread *thread,
+ const sp<Client>& client,
+ audio_stream_type_t streamType,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ int uid);
+ virtual ~TimedTrack();
+
+ class TimedBuffer {
+ public:
+ TimedBuffer();
+ TimedBuffer(const sp<IMemory>& buffer, int64_t pts);
+ const sp<IMemory>& buffer() const { return mBuffer; }
+ int64_t pts() const { return mPTS; }
+ uint32_t position() const { return mPosition; }
+ void setPosition(uint32_t pos) { mPosition = pos; }
+ private:
+ sp<IMemory> mBuffer;
+ int64_t mPTS;
+ uint32_t mPosition;
+ };
+
+ // Mixer facing methods.
+ virtual bool isTimedTrack() const { return true; }
+ virtual size_t framesReady() const;
+
+ // AudioBufferProvider interface
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer,
+ int64_t pts);
+ virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+
+ // Client/App facing methods.
+ status_t allocateTimedBuffer(size_t size,
+ sp<IMemory>* buffer);
+ status_t queueTimedBuffer(const sp<IMemory>& buffer,
+ int64_t pts);
+ status_t setMediaTimeTransform(const LinearTransform& xform,
+ TimedAudioTrack::TargetTimeline target);
+
+ private:
+ TimedTrack(PlaybackThread *thread,
+ const sp<Client>& client,
+ audio_stream_type_t streamType,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ int uid);
+
+ void timedYieldSamples_l(AudioBufferProvider::Buffer* buffer);
+ void timedYieldSilence_l(uint32_t numFrames,
+ AudioBufferProvider::Buffer* buffer);
+ void trimTimedBufferQueue_l();
+ void trimTimedBufferQueueHead_l(const char* logTag);
+ void updateFramesPendingAfterTrim_l(const TimedBuffer& buf,
+ const char* logTag);
+
+ uint64_t mLocalTimeFreq;
+ LinearTransform mLocalTimeToSampleTransform;
+ LinearTransform mMediaTimeToSampleTransform;
+ sp<MemoryDealer> mTimedMemoryDealer;
+
+ Vector<TimedBuffer> mTimedBufferQueue;
+ bool mQueueHeadInFlight;
+ bool mTrimQueueHeadOnRelease;
+ uint32_t mFramesPendingInQueue;
+
+ uint8_t* mTimedSilenceBuffer;
+ uint32_t mTimedSilenceBufferSize;
+ mutable Mutex mTimedBufferQueueLock;
+ bool mTimedAudioOutputOnTime;
+ CCHelper mCCHelper;
+
+ Mutex mMediaTimeTransformLock;
+ LinearTransform mMediaTimeTransform;
+ bool mMediaTimeTransformValid;
+ TimedAudioTrack::TargetTimeline mMediaTimeTransformTarget;
+};
+
+
+// playback track, used by DuplicatingThread
+class OutputTrack : public Track {
+public:
+
+ class Buffer : public AudioBufferProvider::Buffer {
+ public:
+ int16_t *mBuffer;
+ };
+
+ OutputTrack(PlaybackThread *thread,
+ DuplicatingThread *sourceThread,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ int uid);
+ virtual ~OutputTrack();
+
+ virtual status_t start(AudioSystem::sync_event_t event =
+ AudioSystem::SYNC_EVENT_NONE,
+ int triggerSession = 0);
+ virtual void stop();
+ bool write(int16_t* data, uint32_t frames);
+ bool bufferQueueEmpty() const { return mBufferQueue.size() == 0; }
+ bool isActive() const { return mActive; }
+ const wp<ThreadBase>& thread() const { return mThread; }
+
+private:
+
+ status_t obtainBuffer(AudioBufferProvider::Buffer* buffer,
+ uint32_t waitTimeMs);
+ void clearBufferQueue();
+
+ // Maximum number of pending buffers allocated by OutputTrack::write()
+ static const uint8_t kMaxOverFlowBuffers = 10;
+
+ Vector < Buffer* > mBufferQueue;
+ AudioBufferProvider::Buffer mOutBuffer;
+ bool mActive;
+ DuplicatingThread* const mSourceThread; // for waitTimeMs() in write()
+ AudioTrackClientProxy* mClientProxy;
+}; // end of OutputTrack
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
new file mode 100644
index 0000000..57de568
--- /dev/null
+++ b/services/audioflinger/RecordTracks.h
@@ -0,0 +1,63 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+// record track
+class RecordTrack : public TrackBase {
+public:
+ RecordTrack(RecordThread *thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ int sessionId,
+ int uid);
+ virtual ~RecordTrack();
+
+ virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
+ virtual void stop();
+
+ void destroy();
+
+ void invalidate();
+ // clear the buffer overflow flag
+ void clearOverflow() { mOverflow = false; }
+ // set the buffer overflow flag and return previous value
+ bool setOverflow() { bool tmp = mOverflow; mOverflow = true;
+ return tmp; }
+
+ static void appendDumpHeader(String8& result);
+ void dump(char* buffer, size_t size);
+
+private:
+ friend class AudioFlinger; // for mState
+
+ RecordTrack(const RecordTrack&);
+ RecordTrack& operator = (const RecordTrack&);
+
+ // AudioBufferProvider interface
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer,
+ int64_t pts = kInvalidPTS);
+ // releaseBuffer() not overridden
+
+ bool mOverflow; // overflow on most recent attempt to fill client buffer
+ AudioRecordServerProxy* mAudioRecordServerProxy;
+};
diff --git a/services/audioflinger/SchedulingPolicyService.cpp b/services/audioflinger/SchedulingPolicyService.cpp
index 59cc99a..70a3f1a 100644
--- a/services/audioflinger/SchedulingPolicyService.cpp
+++ b/services/audioflinger/SchedulingPolicyService.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#define LOG_TAG "SchedulingPolicyService"
+//#define LOG_NDEBUG 0
+
#include <binder/IServiceManager.h>
#include <utils/Mutex.h>
#include "ISchedulingPolicyService.h"
@@ -25,28 +28,35 @@ static sp<ISchedulingPolicyService> sSchedulingPolicyService;
static const String16 _scheduling_policy("scheduling_policy");
static Mutex sMutex;
-int requestPriority(pid_t pid, pid_t tid, int32_t prio)
+int requestPriority(pid_t pid, pid_t tid, int32_t prio, bool asynchronous)
{
// FIXME merge duplicated code related to service lookup, caching, and error recovery
- sp<ISchedulingPolicyService> sps;
+ int ret;
for (;;) {
sMutex.lock();
- sps = sSchedulingPolicyService;
+ sp<ISchedulingPolicyService> sps = sSchedulingPolicyService;
sMutex.unlock();
- if (sps != 0) {
- break;
- }
- sp<IBinder> binder = defaultServiceManager()->checkService(_scheduling_policy);
- if (binder != 0) {
+ if (sps == 0) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(_scheduling_policy);
+ if (binder == 0) {
+ sleep(1);
+ continue;
+ }
sps = interface_cast<ISchedulingPolicyService>(binder);
sMutex.lock();
sSchedulingPolicyService = sps;
sMutex.unlock();
+ }
+ ret = sps->requestPriority(pid, tid, prio, asynchronous);
+ if (ret != DEAD_OBJECT) {
break;
}
- sleep(1);
+ ALOGW("SchedulingPolicyService died");
+ sMutex.lock();
+ sSchedulingPolicyService.clear();
+ sMutex.unlock();
}
- return sps->requestPriority(pid, tid, prio);
+ return ret;
}
} // namespace android
diff --git a/services/audioflinger/SchedulingPolicyService.h b/services/audioflinger/SchedulingPolicyService.h
index 7ac8454..a9870d4 100644
--- a/services/audioflinger/SchedulingPolicyService.h
+++ b/services/audioflinger/SchedulingPolicyService.h
@@ -21,7 +21,10 @@ namespace android {
// Request elevated priority for thread tid, whose thread group leader must be pid.
// The priority parameter is currently restricted to either 1 or 2.
-int requestPriority(pid_t pid, pid_t tid, int32_t prio);
+// The asynchronous parameter should be 'true' to return immediately,
+// after the request is enqueued but not necessarily executed.
+// The default value 'false' means to return after request has been enqueued and executed.
+int requestPriority(pid_t pid, pid_t tid, int32_t prio, bool asynchronous = false);
} // namespace android
diff --git a/services/audioflinger/ServiceUtilities.cpp b/services/audioflinger/ServiceUtilities.cpp
index 6a58852..152455d 100644
--- a/services/audioflinger/ServiceUtilities.cpp
+++ b/services/audioflinger/ServiceUtilities.cpp
@@ -21,8 +21,9 @@
namespace android {
-// This optimization assumes mediaserver process doesn't fork, which it doesn't
-const pid_t getpid_cached = getpid();
+// Not valid until initialized by AudioFlinger constructor. It would have to be
+// re-initialized if the process containing AudioFlinger service forks (which it doesn't).
+pid_t getpid_cached;
bool recordingAllowed() {
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
@@ -33,6 +34,22 @@ bool recordingAllowed() {
return ok;
}
+bool captureAudioOutputAllowed() {
+ if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
+ static const String16 sCaptureAudioOutput("android.permission.CAPTURE_AUDIO_OUTPUT");
+ // don't use PermissionCache; this is not a system permission
+ bool ok = checkCallingPermission(sCaptureAudioOutput);
+ if (!ok) ALOGE("Request requires android.permission.CAPTURE_AUDIO_OUTPUT");
+ return ok;
+}
+
+bool captureHotwordAllowed() {
+ static const String16 sCaptureHotwordAllowed("android.permission.CAPTURE_AUDIO_HOTWORD");
+ bool ok = checkCallingPermission(sCaptureHotwordAllowed);
+ if (!ok) ALOGE("android.permission.CAPTURE_AUDIO_HOTWORD");
+ return ok;
+}
+
bool settingsAllowed() {
if (getpid_cached == IPCThreadState::self()->getCallingPid()) return true;
static const String16 sAudioSettings("android.permission.MODIFY_AUDIO_SETTINGS");
diff --git a/services/audioflinger/ServiceUtilities.h b/services/audioflinger/ServiceUtilities.h
index f77ec5b..531bc56 100644
--- a/services/audioflinger/ServiceUtilities.h
+++ b/services/audioflinger/ServiceUtilities.h
@@ -18,9 +18,11 @@
namespace android {
-extern const pid_t getpid_cached;
+extern pid_t getpid_cached;
bool recordingAllowed();
+bool captureAudioOutputAllowed();
+bool captureHotwordAllowed();
bool settingsAllowed();
bool dumpAllowed();
diff --git a/services/audioflinger/StateQueue.cpp b/services/audioflinger/StateQueue.cpp
index 3e891a5..48399c0 100644
--- a/services/audioflinger/StateQueue.cpp
+++ b/services/audioflinger/StateQueue.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "StateQueue"
//#define LOG_NDEBUG 0
+#include "Configuration.h"
#include <time.h>
#include <cutils/atomic.h>
#include <utils/Log.h>
@@ -57,7 +58,11 @@ template<typename T> StateQueue<T>::~StateQueue()
template<typename T> const T* StateQueue<T>::poll()
{
+#ifdef __LP64__
+ const T *next = (const T *) android_atomic_acquire_load64((volatile int64_t *) &mNext);
+#else
const T *next = (const T *) android_atomic_acquire_load((volatile int32_t *) &mNext);
+#endif
if (next != mCurrent) {
mAck = next; // no additional barrier needed
mCurrent = next;
@@ -139,7 +144,11 @@ template<typename T> bool StateQueue<T>::push(StateQueue<T>::block_t block)
}
// publish
+#ifdef __LP64__
+ android_atomic_release_store64((int64_t) mMutating, (volatile int64_t *) &mNext);
+#else
android_atomic_release_store((int32_t) mMutating, (volatile int32_t *) &mNext);
+#endif
mExpecting = mMutating;
// copy with circular wraparound
diff --git a/services/audioflinger/StateQueue.h b/services/audioflinger/StateQueue.h
index eba190c..9cde642 100644
--- a/services/audioflinger/StateQueue.h
+++ b/services/audioflinger/StateQueue.h
@@ -17,6 +17,78 @@
#ifndef ANDROID_AUDIO_STATE_QUEUE_H
#define ANDROID_AUDIO_STATE_QUEUE_H
+// The state queue template class was originally driven by this use case / requirements:
+// There are two threads: a fast mixer, and a normal mixer, and they share state.
+// The interesting part of the shared state is a set of active fast tracks,
+// and the output HAL configuration (buffer size in frames, sample rate, etc.).
+// Fast mixer thread:
+// periodic with typical period < 10 ms
+// FIFO/RR scheduling policy and a low fixed priority
+// ok to block for bounded time using nanosleep() to achieve desired period
+// must not block on condition wait, mutex lock, atomic operation spin, I/O, etc.
+// under typical operations of mixing, writing, or adding/removing tracks
+// ok to block for unbounded time when the output HAL configuration changes,
+// and this may result in an audible artifact
+// needs read-only access to a recent stable state,
+// but not necessarily the most current one
+// only allocate and free memory when configuration changes
+// avoid conventional logging, as this is a form of I/O and could block
+// defer computation to other threads when feasible; for example
+// cycle times are collected by fast mixer thread but the floating-point
+// statistical calculations on these cycle times are computed by normal mixer
+// these requirements also apply to callouts such as AudioBufferProvider and VolumeProvider
+// Normal mixer thread:
+// periodic with typical period ~20 ms
+// SCHED_OTHER scheduling policy and nice priority == urgent audio
+// ok to block, but prefer to avoid as much as possible
+// needs read/write access to state
+// The normal mixer may need to temporarily suspend the fast mixer thread during mode changes.
+// It will do this using the state -- one of the fields tells the fast mixer to idle.
+
+// Additional requirements:
+// - observer must always be able to poll for and view the latest pushed state; it must never be
+// blocked from seeing that state
+// - observer does not need to see every state in sequence; it is OK for it to skip states
+// [see below for more on this]
+// - mutator must always be able to read/modify a state, it must never be blocked from reading or
+// modifying state
+// - reduce memcpy where possible
+// - work well if the observer runs more frequently than the mutator,
+// as is the case with fast mixer/normal mixer.
+// It is not a requirement to work well if the roles were reversed,
+// and the mutator were to run more frequently than the observer.
+// In this case, the mutator could get blocked waiting for a slot to fill up for
+// it to work with. This could be solved somewhat by increasing the depth of the queue, but it would
+// still limit the mutator to a finite number of changes before it would block. A future
+// possibility, not implemented here, would be to allow the mutator to safely overwrite an already
+// pushed state. This could be done by the mutator overwriting mNext, but then being prepared to
+// read an mAck which is actually for the earlier mNext (since there is a race).
+
+// Solution:
+// Let's call the fast mixer thread the "observer" and normal mixer thread the "mutator".
+// We assume there is only a single observer and a single mutator; this is critical.
+// Each state is of type <T>, and should contain only POD (Plain Old Data) and raw pointers, as
+// memcpy() may be used to copy state, and the destructors are run in unpredictable order.
+// The states in chronological order are: previous, current, next, and mutating:
+// previous read-only, observer can compare vs. current to see the subset that changed
+// current read-only, this is the primary state for observer
+// next read-only, when observer is ready to accept a new state it will shift it in:
+// previous = current
+// current = next
+// and the slot formerly used by previous is now available to the mutator.
+// mutating invisible to observer, read/write to mutator
+// Initialization is tricky, especially for the observer. If the observer starts execution
+// before the mutator, there are no previous, current, or next states. And even if the observer
+// starts execution after the mutator, there is a next state but no previous or current states.
+// To solve this, we'll have the observer idle until there is a next state,
+// and it will have to deal with the case where there is no previous state.
+// The states are stored in a shared FIFO queue represented using a circular array.
+// The observer polls for mutations, and receives a new state pointer after a
+// a mutation is pushed onto the queue. To the observer, the state pointers are
+// effectively in random order, that is the observer should not do address
+// arithmetic on the state pointers. However to the mutator, the state pointers
+// are in a definite circular order.
+
namespace android {
#ifdef STATE_QUEUE_DUMP
@@ -108,7 +180,7 @@ public:
#endif
private:
- static const unsigned kN = 4; // values != 4 are not supported by this code
+ static const unsigned kN = 4; // values < 4 are not supported by this code
T mStates[kN]; // written by mutator, read by observer
// "volatile" is meaningless with SMP, but here it indicates that we're using atomic ops
diff --git a/services/audioflinger/StateQueueInstantiations.cpp b/services/audioflinger/StateQueueInstantiations.cpp
index 077582f..0d5cd0c 100644
--- a/services/audioflinger/StateQueueInstantiations.cpp
+++ b/services/audioflinger/StateQueueInstantiations.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "Configuration.h"
#include "FastMixerState.h"
#include "StateQueue.h"
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
new file mode 100644
index 0000000..498ddb6
--- /dev/null
+++ b/services/audioflinger/Threads.cpp
@@ -0,0 +1,5337 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+
+#include "Configuration.h"
+#include <math.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <cutils/properties.h>
+#include <media/AudioParameter.h>
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <private/media/AudioTrackShared.h>
+#include <hardware/audio.h>
+#include <audio_effects/effect_ns.h>
+#include <audio_effects/effect_aec.h>
+#include <audio_utils/primitives.h>
+
+// NBAIO implementations
+#include <media/nbaio/AudioStreamOutSink.h>
+#include <media/nbaio/MonoPipe.h>
+#include <media/nbaio/MonoPipeReader.h>
+#include <media/nbaio/Pipe.h>
+#include <media/nbaio/PipeReader.h>
+#include <media/nbaio/SourceAudioBufferProvider.h>
+
+#include <powermanager/PowerManager.h>
+
+#include <common_time/cc_helper.h>
+#include <common_time/local_clock.h>
+
+#include "AudioFlinger.h"
+#include "AudioMixer.h"
+#include "FastMixer.h"
+#include "ServiceUtilities.h"
+#include "SchedulingPolicyService.h"
+
+#ifdef ADD_BATTERY_DATA
+#include <media/IMediaPlayerService.h>
+#include <media/IMediaDeathNotifier.h>
+#endif
+
+#ifdef DEBUG_CPU_USAGE
+#include <cpustats/CentralTendencyStatistics.h>
+#include <cpustats/ThreadCpuUsage.h>
+#endif
+
+// ----------------------------------------------------------------------------
+
+// Note: the following macro is used for extremely verbose logging message. In
+// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to
+// 0; but one side effect of this is to turn all LOGV's as well. Some messages
+// are so verbose that we want to suppress them even when we have ALOG_ASSERT
+// turned on. Do not uncomment the #def below unless you really know what you
+// are doing and want to see all of the extremely verbose messages.
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
+
+namespace android {
+
+// retry counts for buffer fill timeout
+// 50 * ~20msecs = 1 second
+static const int8_t kMaxTrackRetries = 50;
+static const int8_t kMaxTrackStartupRetries = 50;
+// allow less retry attempts on direct output thread.
+// direct outputs can be a scarce resource in audio hardware and should
+// be released as quickly as possible.
+static const int8_t kMaxTrackRetriesDirect = 2;
+
+// don't warn about blocked writes or record buffer overflows more often than this
+static const nsecs_t kWarningThrottleNs = seconds(5);
+
+// RecordThread loop sleep time upon application overrun or audio HAL read error
+static const int kRecordThreadSleepUs = 5000;
+
+// maximum time to wait for setParameters to complete
+static const nsecs_t kSetParametersTimeoutNs = seconds(2);
+
+// minimum sleep time for the mixer thread loop when tracks are active but in underrun
+static const uint32_t kMinThreadSleepTimeUs = 5000;
+// 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;
+// maximum normal mix buffer size
+static const uint32_t kMaxNormalMixBufferSizeMs = 24;
+
+// Offloaded output thread standby delay: allows track transition without going to standby
+static const nsecs_t kOffloadStandbyDelayNs = seconds(1);
+
+// Whether to use fast mixer
+static const enum {
+ FastMixer_Never, // never initialize or use: for debugging only
+ FastMixer_Always, // always initialize and use, even if not needed: for debugging only
+ // normal mixer multiplier is 1
+ FastMixer_Static, // initialize if needed, then use all the time if initialized,
+ // multiplier is calculated based on min & max normal mixer buffer size
+ FastMixer_Dynamic, // initialize if needed, then use dynamically depending on track load,
+ // multiplier is calculated based on min & max normal mixer buffer size
+ // FIXME for FastMixer_Dynamic:
+ // Supporting this option will require fixing HALs that can't handle large writes.
+ // For example, one HAL implementation returns an error from a large write,
+ // and another HAL implementation corrupts memory, possibly in the sample rate converter.
+ // We could either fix the HAL implementations, or provide a wrapper that breaks
+ // up large writes into smaller ones, and the wrapper would need to deal with scheduler.
+} kUseFastMixer = FastMixer_Static;
+
+// Priorities for requestPriority
+static const int kPriorityAudioApp = 2;
+static const int kPriorityFastMixer = 3;
+
+// IAudioFlinger::createTrack() reports back to client the total size of shared memory area
+// for the track. The client then sub-divides this into smaller buffers for its use.
+// Currently the client uses N-buffering by default, but doesn't tell us about the value of N.
+// So for now we just assume that client is double-buffered for fast tracks.
+// FIXME It would be better for client to tell AudioFlinger the value of N,
+// so AudioFlinger could allocate the right amount of memory.
+// See the client's minBufCount and mNotificationFramesAct calculations for details.
+static const int kFastTrackMultiplier = 2;
+
+// ----------------------------------------------------------------------------
+
+#ifdef ADD_BATTERY_DATA
+// To collect the amplifier usage
+static void addBatteryData(uint32_t params) {
+ sp<IMediaPlayerService> service = IMediaDeathNotifier::getMediaPlayerService();
+ if (service == NULL) {
+ // it already logged
+ return;
+ }
+
+ service->addBatteryData(params);
+}
+#endif
+
+
+// ----------------------------------------------------------------------------
+// CPU Stats
+// ----------------------------------------------------------------------------
+
+class CpuStats {
+public:
+ CpuStats();
+ void sample(const String8 &title);
+#ifdef DEBUG_CPU_USAGE
+private:
+ ThreadCpuUsage mCpuUsage; // instantaneous thread CPU usage in wall clock ns
+ CentralTendencyStatistics mWcStats; // statistics on thread CPU usage in wall clock ns
+
+ CentralTendencyStatistics mHzStats; // statistics on thread CPU usage in cycles
+
+ int mCpuNum; // thread's current CPU number
+ int mCpukHz; // frequency of thread's current CPU in kHz
+#endif
+};
+
+CpuStats::CpuStats()
+#ifdef DEBUG_CPU_USAGE
+ : mCpuNum(-1), mCpukHz(-1)
+#endif
+{
+}
+
+void CpuStats::sample(const String8 &title) {
+#ifdef DEBUG_CPU_USAGE
+ // get current thread's delta CPU time in wall clock ns
+ double wcNs;
+ bool valid = mCpuUsage.sampleAndEnable(wcNs);
+
+ // record sample for wall clock statistics
+ if (valid) {
+ mWcStats.sample(wcNs);
+ }
+
+ // get the current CPU number
+ int cpuNum = sched_getcpu();
+
+ // get the current CPU frequency in kHz
+ int cpukHz = mCpuUsage.getCpukHz(cpuNum);
+
+ // check if either CPU number or frequency changed
+ if (cpuNum != mCpuNum || cpukHz != mCpukHz) {
+ mCpuNum = cpuNum;
+ mCpukHz = cpukHz;
+ // ignore sample for purposes of cycles
+ valid = false;
+ }
+
+ // if no change in CPU number or frequency, then record sample for cycle statistics
+ if (valid && mCpukHz > 0) {
+ double cycles = wcNs * cpukHz * 0.000001;
+ mHzStats.sample(cycles);
+ }
+
+ unsigned n = mWcStats.n();
+ // mCpuUsage.elapsed() is expensive, so don't call it every loop
+ if ((n & 127) == 1) {
+ long long elapsed = mCpuUsage.elapsed();
+ if (elapsed >= DEBUG_CPU_USAGE * 1000000000LL) {
+ double perLoop = elapsed / (double) n;
+ double perLoop100 = perLoop * 0.01;
+ double perLoop1k = perLoop * 0.001;
+ double mean = mWcStats.mean();
+ double stddev = mWcStats.stddev();
+ double minimum = mWcStats.minimum();
+ double maximum = mWcStats.maximum();
+ double meanCycles = mHzStats.mean();
+ double stddevCycles = mHzStats.stddev();
+ double minCycles = mHzStats.minimum();
+ double maxCycles = mHzStats.maximum();
+ mCpuUsage.resetElapsed();
+ mWcStats.reset();
+ mHzStats.reset();
+ ALOGD("CPU usage for %s over past %.1f secs\n"
+ " (%u mixer loops at %.1f mean ms per loop):\n"
+ " us per mix loop: mean=%.0f stddev=%.0f min=%.0f max=%.0f\n"
+ " %% of wall: mean=%.1f stddev=%.1f min=%.1f max=%.1f\n"
+ " MHz: mean=%.1f, stddev=%.1f, min=%.1f max=%.1f",
+ title.string(),
+ elapsed * .000000001, n, perLoop * .000001,
+ mean * .001,
+ stddev * .001,
+ minimum * .001,
+ maximum * .001,
+ mean / perLoop100,
+ stddev / perLoop100,
+ minimum / perLoop100,
+ maximum / perLoop100,
+ meanCycles / perLoop1k,
+ stddevCycles / perLoop1k,
+ minCycles / perLoop1k,
+ maxCycles / perLoop1k);
+
+ }
+ }
+#endif
+};
+
+// ----------------------------------------------------------------------------
+// ThreadBase
+// ----------------------------------------------------------------------------
+
+AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
+ audio_devices_t outDevice, audio_devices_t inDevice, type_t type)
+ : Thread(false /*canCallJava*/),
+ mType(type),
+ mAudioFlinger(audioFlinger),
+ // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, and mFormat are
+ // set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters()
+ mParamStatus(NO_ERROR),
+ //FIXME: mStandby should be true here. Is this some kind of hack?
+ mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
+ mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
+ // mName will be set by concrete (non-virtual) subclass
+ mDeathRecipient(new PMDeathRecipient(this))
+{
+}
+
+AudioFlinger::ThreadBase::~ThreadBase()
+{
+ // mConfigEvents should be empty, but just in case it isn't, free the memory it owns
+ for (size_t i = 0; i < mConfigEvents.size(); i++) {
+ delete mConfigEvents[i];
+ }
+ mConfigEvents.clear();
+
+ mParamCond.broadcast();
+ // do not lock the mutex in destructor
+ releaseWakeLock_l();
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = mPowerManager->asBinder();
+ binder->unlinkToDeath(mDeathRecipient);
+ }
+}
+
+void AudioFlinger::ThreadBase::exit()
+{
+ ALOGV("ThreadBase::exit");
+ // do any cleanup required for exit to succeed
+ preExit();
+ {
+ // This lock prevents the following race in thread (uniprocessor for illustration):
+ // if (!exitPending()) {
+ // // context switch from here to exit()
+ // // exit() calls requestExit(), what exitPending() observes
+ // // exit() calls signal(), which is dropped since no waiters
+ // // context switch back from exit() to here
+ // mWaitWorkCV.wait(...);
+ // // now thread is hung
+ // }
+ AutoMutex lock(mLock);
+ requestExit();
+ mWaitWorkCV.broadcast();
+ }
+ // When Thread::requestExitAndWait is made virtual and this method is renamed to
+ // "virtual status_t requestExitAndWait()", replace by "return Thread::requestExitAndWait();"
+ requestExitAndWait();
+}
+
+status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs)
+{
+ status_t status;
+
+ ALOGV("ThreadBase::setParameters() %s", keyValuePairs.string());
+ Mutex::Autolock _l(mLock);
+
+ mNewParameters.add(keyValuePairs);
+ mWaitWorkCV.signal();
+ // wait condition with timeout in case the thread loop has exited
+ // before the request could be processed
+ if (mParamCond.waitRelative(mLock, kSetParametersTimeoutNs) == NO_ERROR) {
+ status = mParamStatus;
+ mWaitWorkCV.signal();
+ } else {
+ status = TIMED_OUT;
+ }
+ return status;
+}
+
+void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param)
+{
+ Mutex::Autolock _l(mLock);
+ sendIoConfigEvent_l(event, param);
+}
+
+// sendIoConfigEvent_l() must be called with ThreadBase::mLock held
+void AudioFlinger::ThreadBase::sendIoConfigEvent_l(int event, int param)
+{
+ IoConfigEvent *ioEvent = new IoConfigEvent(event, param);
+ mConfigEvents.add(static_cast<ConfigEvent *>(ioEvent));
+ ALOGV("sendIoConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event,
+ param);
+ mWaitWorkCV.signal();
+}
+
+// sendPrioConfigEvent_l() must be called with ThreadBase::mLock held
+void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio)
+{
+ PrioConfigEvent *prioEvent = new PrioConfigEvent(pid, tid, prio);
+ mConfigEvents.add(static_cast<ConfigEvent *>(prioEvent));
+ ALOGV("sendPrioConfigEvent_l() num events %d pid %d, tid %d prio %d",
+ mConfigEvents.size(), pid, tid, prio);
+ mWaitWorkCV.signal();
+}
+
+void AudioFlinger::ThreadBase::processConfigEvents()
+{
+ mLock.lock();
+ while (!mConfigEvents.isEmpty()) {
+ ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size());
+ ConfigEvent *event = mConfigEvents[0];
+ mConfigEvents.removeAt(0);
+ // release mLock before locking AudioFlinger mLock: lock order is always
+ // AudioFlinger then ThreadBase to avoid cross deadlock
+ mLock.unlock();
+ switch(event->type()) {
+ case CFG_EVENT_PRIO: {
+ PrioConfigEvent *prioEvent = static_cast<PrioConfigEvent *>(event);
+ // FIXME Need to understand why this has be done asynchronously
+ int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio(),
+ true /*asynchronous*/);
+ if (err != 0) {
+ ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; "
+ "error %d",
+ prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err);
+ }
+ } break;
+ case CFG_EVENT_IO: {
+ IoConfigEvent *ioEvent = static_cast<IoConfigEvent *>(event);
+ mAudioFlinger->mLock.lock();
+ audioConfigChanged_l(ioEvent->event(), ioEvent->param());
+ mAudioFlinger->mLock.unlock();
+ } break;
+ default:
+ ALOGE("processConfigEvents() unknown event type %d", event->type());
+ break;
+ }
+ delete event;
+ mLock.lock();
+ }
+ mLock.unlock();
+}
+
+void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ bool locked = AudioFlinger::dumpTryLock(mLock);
+ if (!locked) {
+ snprintf(buffer, SIZE, "thread %p maybe dead locked\n", this);
+ write(fd, buffer, strlen(buffer));
+ }
+
+ snprintf(buffer, SIZE, "io handle: %d\n", mId);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "TID: %d\n", getTid());
+ result.append(buffer);
+ snprintf(buffer, SIZE, "standby: %d\n", mStandby);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Sample rate: %u\n", mSampleRate);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "HAL frame count: %zu\n", mFrameCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Channel Count: %u\n", mChannelCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Format: %d\n", mFormat);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Frame size: %zu\n", mFrameSize);
+ result.append(buffer);
+
+ snprintf(buffer, SIZE, "\nPending setParameters commands: \n");
+ result.append(buffer);
+ result.append(" Index Command");
+ for (size_t i = 0; i < mNewParameters.size(); ++i) {
+ snprintf(buffer, SIZE, "\n %02zu ", i);
+ result.append(buffer);
+ result.append(mNewParameters[i]);
+ }
+
+ snprintf(buffer, SIZE, "\n\nPending config events: \n");
+ result.append(buffer);
+ for (size_t i = 0; i < mConfigEvents.size(); i++) {
+ mConfigEvents[i]->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ result.append("\n");
+
+ write(fd, result.string(), result.size());
+
+ if (locked) {
+ mLock.unlock();
+ }
+}
+
+void AudioFlinger::ThreadBase::dumpEffectChains(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\n- %zu 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);
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::acquireWakeLock(int uid)
+{
+ Mutex::Autolock _l(mLock);
+ acquireWakeLock_l(uid);
+}
+
+String16 AudioFlinger::ThreadBase::getWakeLockTag()
+{
+ switch (mType) {
+ case MIXER:
+ return String16("AudioMix");
+ case DIRECT:
+ return String16("AudioDirectOut");
+ case DUPLICATING:
+ return String16("AudioDup");
+ case RECORD:
+ return String16("AudioIn");
+ case OFFLOAD:
+ return String16("AudioOffload");
+ default:
+ ALOG_ASSERT(false);
+ return String16("AudioUnknown");
+ }
+}
+
+void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid)
+{
+ getPowerManager_l();
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = new BBinder();
+ status_t status;
+ if (uid >= 0) {
+ status = mPowerManager->acquireWakeLockWithUid(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ getWakeLockTag(),
+ String16("media"),
+ uid);
+ } else {
+ status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ getWakeLockTag(),
+ String16("media"));
+ }
+ if (status == NO_ERROR) {
+ mWakeLockToken = binder;
+ }
+ ALOGV("acquireWakeLock_l() %s status %d", mName, status);
+ }
+}
+
+void AudioFlinger::ThreadBase::releaseWakeLock()
+{
+ Mutex::Autolock _l(mLock);
+ releaseWakeLock_l();
+}
+
+void AudioFlinger::ThreadBase::releaseWakeLock_l()
+{
+ if (mWakeLockToken != 0) {
+ ALOGV("releaseWakeLock_l() %s", mName);
+ if (mPowerManager != 0) {
+ mPowerManager->releaseWakeLock(mWakeLockToken, 0);
+ }
+ mWakeLockToken.clear();
+ }
+}
+
+void AudioFlinger::ThreadBase::updateWakeLockUids(const SortedVector<int> &uids) {
+ Mutex::Autolock _l(mLock);
+ updateWakeLockUids_l(uids);
+}
+
+void AudioFlinger::ThreadBase::getPowerManager_l() {
+
+ if (mPowerManager == 0) {
+ // use checkService() to avoid blocking if power service is not up yet
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16("power"));
+ if (binder == 0) {
+ ALOGW("Thread %s cannot connect to the power manager service", mName);
+ } else {
+ mPowerManager = interface_cast<IPowerManager>(binder);
+ binder->linkToDeath(mDeathRecipient);
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uids) {
+
+ getPowerManager_l();
+ if (mWakeLockToken == NULL) {
+ ALOGE("no wake lock to update!");
+ return;
+ }
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = new BBinder();
+ status_t status;
+ status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array());
+ ALOGV("acquireWakeLock_l() %s status %d", mName, status);
+ }
+}
+
+void AudioFlinger::ThreadBase::clearPowerManager()
+{
+ Mutex::Autolock _l(mLock);
+ releaseWakeLock_l();
+ mPowerManager.clear();
+}
+
+void AudioFlinger::ThreadBase::PMDeathRecipient::binderDied(const wp<IBinder>& who)
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ thread->clearPowerManager();
+ }
+ ALOGW("power manager service died !!!");
+}
+
+void AudioFlinger::ThreadBase::setEffectSuspended(
+ const effect_uuid_t *type, bool suspend, int sessionId)
+{
+ Mutex::Autolock _l(mLock);
+ setEffectSuspended_l(type, suspend, sessionId);
+}
+
+void AudioFlinger::ThreadBase::setEffectSuspended_l(
+ const effect_uuid_t *type, bool suspend, int sessionId)
+{
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain != 0) {
+ if (type != NULL) {
+ chain->setEffectSuspended_l(type, suspend);
+ } else {
+ chain->setEffectSuspendedAll_l(suspend);
+ }
+ }
+
+ updateSuspendedSessions_l(type, suspend, sessionId);
+}
+
+void AudioFlinger::ThreadBase::checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain)
+{
+ ssize_t index = mSuspendedSessions.indexOfKey(chain->sessionId());
+ if (index < 0) {
+ return;
+ }
+
+ const KeyedVector <int, sp<SuspendedSessionDesc> >& sessionEffects =
+ mSuspendedSessions.valueAt(index);
+
+ for (size_t i = 0; i < sessionEffects.size(); i++) {
+ sp<SuspendedSessionDesc> desc = sessionEffects.valueAt(i);
+ for (int j = 0; j < desc->mRefCount; j++) {
+ if (sessionEffects.keyAt(i) == EffectChain::kKeyForSuspendAll) {
+ chain->setEffectSuspendedAll_l(true);
+ } else {
+ ALOGV("checkSuspendOnAddEffectChain_l() suspending effects %08x",
+ desc->mType.timeLow);
+ chain->setEffectSuspended_l(&desc->mType, true);
+ }
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::updateSuspendedSessions_l(const effect_uuid_t *type,
+ bool suspend,
+ int sessionId)
+{
+ ssize_t index = mSuspendedSessions.indexOfKey(sessionId);
+
+ KeyedVector <int, sp<SuspendedSessionDesc> > sessionEffects;
+
+ if (suspend) {
+ if (index >= 0) {
+ sessionEffects = mSuspendedSessions.valueAt(index);
+ } else {
+ mSuspendedSessions.add(sessionId, sessionEffects);
+ }
+ } else {
+ if (index < 0) {
+ return;
+ }
+ sessionEffects = mSuspendedSessions.valueAt(index);
+ }
+
+
+ int key = EffectChain::kKeyForSuspendAll;
+ if (type != NULL) {
+ key = type->timeLow;
+ }
+ index = sessionEffects.indexOfKey(key);
+
+ sp<SuspendedSessionDesc> desc;
+ if (suspend) {
+ if (index >= 0) {
+ desc = sessionEffects.valueAt(index);
+ } else {
+ desc = new SuspendedSessionDesc();
+ if (type != NULL) {
+ desc->mType = *type;
+ }
+ sessionEffects.add(key, desc);
+ ALOGV("updateSuspendedSessions_l() suspend adding effect %08x", key);
+ }
+ desc->mRefCount++;
+ } else {
+ if (index < 0) {
+ return;
+ }
+ desc = sessionEffects.valueAt(index);
+ if (--desc->mRefCount == 0) {
+ ALOGV("updateSuspendedSessions_l() restore removing effect %08x", key);
+ sessionEffects.removeItemsAt(index);
+ if (sessionEffects.isEmpty()) {
+ ALOGV("updateSuspendedSessions_l() restore removing session %d",
+ sessionId);
+ mSuspendedSessions.removeItem(sessionId);
+ }
+ }
+ }
+ if (!sessionEffects.isEmpty()) {
+ mSuspendedSessions.replaceValueFor(sessionId, sessionEffects);
+ }
+}
+
+void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
+ bool enabled,
+ int sessionId)
+{
+ Mutex::Autolock _l(mLock);
+ checkSuspendOnEffectEnabled_l(effect, enabled, sessionId);
+}
+
+void AudioFlinger::ThreadBase::checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
+ bool enabled,
+ int sessionId)
+{
+ if (mType != RECORD) {
+ // suspend all effects in AUDIO_SESSION_OUTPUT_MIX when enabling any effect on
+ // another session. This gives the priority to well behaved effect control panels
+ // and applications not using global effects.
+ // Enabling post processing in AUDIO_SESSION_OUTPUT_STAGE session does not affect
+ // global effects
+ if ((sessionId != AUDIO_SESSION_OUTPUT_MIX) && (sessionId != AUDIO_SESSION_OUTPUT_STAGE)) {
+ setEffectSuspended_l(NULL, enabled, AUDIO_SESSION_OUTPUT_MIX);
+ }
+ }
+
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain != 0) {
+ chain->checkSuspendOnEffectEnabled(effect, enabled);
+ }
+}
+
+// ThreadBase::createEffect_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::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<EffectChain> chain;
+ bool chainCreated = false;
+ bool effectCreated = false;
+ bool effectRegistered = false;
+
+ lStatus = initCheck();
+ if (lStatus != NO_ERROR) {
+ ALOGW("createEffect_l() Audio driver not initialized.");
+ goto Exit;
+ }
+
+ // Allow global effects only on offloaded and mixer threads
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ switch (mType) {
+ case MIXER:
+ case OFFLOAD:
+ break;
+ case DIRECT:
+ case DUPLICATING:
+ case RECORD:
+ default:
+ ALOGW("createEffect_l() Cannot add global effect %s on thread %s", desc->name, mName);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
+
+ // Only Pre processor effects are allowed on input threads and only on input threads
+ if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) {
+ ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d",
+ desc->name, desc->flags, mType);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+
+ ALOGV("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
+ ALOGV("createEffect_l() new effect chain for session %d", sessionId);
+ chain = new EffectChain(this, sessionId);
+ addEffectChain_l(chain);
+ chain->setStrategy(getStrategyForSession_l(sessionId));
+ chainCreated = true;
+ } else {
+ effect = chain->getEffectFromDesc_l(desc);
+ }
+
+ ALOGV("createEffect_l() got effect %p on chain %p", effect.get(), chain.get());
+
+ if (effect == 0) {
+ int id = mAudioFlinger->nextUniqueId();
+ // Check CPU and memory usage
+ lStatus = AudioSystem::registerEffect(desc, mId, chain->strategy(), sessionId, id);
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+ effectRegistered = true;
+ // create a new effect module if none present in the chain
+ effect = new EffectModule(this, chain, desc, id, sessionId);
+ lStatus = effect->status();
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+ effect->setOffloaded(mType == OFFLOAD, mId);
+
+ lStatus = chain->addEffect_l(effect);
+ if (lStatus != NO_ERROR) {
+ goto Exit;
+ }
+ effectCreated = true;
+
+ effect->setDevice(mOutDevice);
+ effect->setDevice(mInDevice);
+ effect->setMode(mAudioFlinger->getMode());
+ effect->setAudioSource(mAudioSource);
+ }
+ // create effect handle and connect it to effect module
+ handle = new EffectHandle(effect, client, effectClient, priority);
+ lStatus = effect->addHandle(handle.get());
+ if (enabled != NULL) {
+ *enabled = (int)effect->isEnabled();
+ }
+ }
+
+Exit:
+ if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
+ Mutex::Autolock _l(mLock);
+ if (effectCreated) {
+ chain->removeEffect_l(effect);
+ }
+ if (effectRegistered) {
+ AudioSystem::unregisterEffect(effect->id());
+ }
+ if (chainCreated) {
+ removeEffectChain_l(chain);
+ }
+ handle.clear();
+ }
+
+ if (status != NULL) {
+ *status = lStatus;
+ }
+ return handle;
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect(int sessionId, int effectId)
+{
+ Mutex::Autolock _l(mLock);
+ return getEffect_l(sessionId, effectId);
+}
+
+sp<AudioFlinger::EffectModule> AudioFlinger::ThreadBase::getEffect_l(int sessionId, int effectId)
+{
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ return chain != 0 ? chain->getEffectFromId_l(effectId) : 0;
+}
+
+// PlaybackThread::addEffect_l() must be called with AudioFlinger::mLock and
+// PlaybackThread::mLock held
+status_t AudioFlinger::ThreadBase::addEffect_l(const sp<EffectModule>& effect)
+{
+ // check for existing effect chain with the requested audio session
+ int sessionId = effect->sessionId();
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ bool chainCreated = false;
+
+ ALOGD_IF((mType == OFFLOAD) && !effect->isOffloadable(),
+ "addEffect_l() on offloaded thread %p: effect %s does not support offload flags %x",
+ this, effect->desc().name, effect->desc().flags);
+
+ if (chain == 0) {
+ // create a new chain for this session
+ ALOGV("addEffect_l() new effect chain for session %d", sessionId);
+ chain = new EffectChain(this, sessionId);
+ addEffectChain_l(chain);
+ chain->setStrategy(getStrategyForSession_l(sessionId));
+ chainCreated = true;
+ }
+ ALOGV("addEffect_l() %p chain %p effect %p", this, chain.get(), effect.get());
+
+ if (chain->getEffectFromId_l(effect->id()) != 0) {
+ ALOGW("addEffect_l() %p effect %s already present in chain %p",
+ this, effect->desc().name, chain.get());
+ return BAD_VALUE;
+ }
+
+ effect->setOffloaded(mType == OFFLOAD, mId);
+
+ status_t status = chain->addEffect_l(effect);
+ if (status != NO_ERROR) {
+ if (chainCreated) {
+ removeEffectChain_l(chain);
+ }
+ return status;
+ }
+
+ effect->setDevice(mOutDevice);
+ effect->setDevice(mInDevice);
+ effect->setMode(mAudioFlinger->getMode());
+ effect->setAudioSource(mAudioSource);
+ return NO_ERROR;
+}
+
+void AudioFlinger::ThreadBase::removeEffect_l(const sp<EffectModule>& effect) {
+
+ ALOGV("removeEffect_l() %p effect %p", this, effect.get());
+ effect_descriptor_t desc = effect->desc();
+ if ((desc.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
+ detachAuxEffect_l(effect->id());
+ }
+
+ sp<EffectChain> chain = effect->chain().promote();
+ if (chain != 0) {
+ // remove effect chain if removing last effect
+ if (chain->removeEffect_l(effect) == 0) {
+ removeEffectChain_l(chain);
+ }
+ } else {
+ ALOGW("removeEffect_l() %p cannot promote chain for effect %p", this, effect.get());
+ }
+}
+
+void AudioFlinger::ThreadBase::lockEffectChains_l(
+ Vector< sp<AudioFlinger::EffectChain> >& effectChains)
+{
+ effectChains = mEffectChains;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->lock();
+ }
+}
+
+void AudioFlinger::ThreadBase::unlockEffectChains(
+ const Vector< sp<AudioFlinger::EffectChain> >& effectChains)
+{
+ for (size_t i = 0; i < effectChains.size(); i++) {
+ effectChains[i]->unlock();
+ }
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain(int sessionId)
+{
+ Mutex::Autolock _l(mLock);
+ return getEffectChain_l(sessionId);
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::ThreadBase::getEffectChain_l(int sessionId) const
+{
+ size_t size = mEffectChains.size();
+ for (size_t i = 0; i < size; i++) {
+ if (mEffectChains[i]->sessionId() == sessionId) {
+ return mEffectChains[i];
+ }
+ }
+ return 0;
+}
+
+void AudioFlinger::ThreadBase::setMode(audio_mode_t mode)
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mEffectChains.size();
+ for (size_t i = 0; i < size; i++) {
+ mEffectChains[i]->setMode_l(mode);
+ }
+}
+
+void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect,
+ EffectHandle *handle,
+ bool unpinIfLast) {
+
+ Mutex::Autolock _l(mLock);
+ ALOGV("disconnectEffect() %p effect %p", this, effect.get());
+ // delete the effect module if removing last handle on it
+ if (effect->removeHandle(handle) == 0) {
+ if (!effect->isPinned() || unpinIfLast) {
+ removeEffect_l(effect);
+ AudioSystem::unregisterEffect(effect->id());
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Playback
+// ----------------------------------------------------------------------------
+
+AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output,
+ audio_io_handle_t id,
+ audio_devices_t device,
+ type_t type)
+ : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
+ mNormalFrameCount(0), mMixBuffer(NULL),
+ mAllocMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+ mActiveTracksGeneration(0),
+ // mStreamTypes[] initialized in constructor body
+ mOutput(output),
+ mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
+ mMixerStatus(MIXER_IDLE),
+ mMixerStatusIgnoringFastTracks(MIXER_IDLE),
+ standbyDelay(AudioFlinger::mStandbyTimeInNsecs),
+ mBytesRemaining(0),
+ mCurrentWriteLength(0),
+ mUseAsyncWrite(false),
+ mWriteAckSequence(0),
+ mDrainSequence(0),
+ mSignalPending(false),
+ mScreenState(AudioFlinger::mScreenState),
+ // index 0 is reserved for normal mixer's submix
+ mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
+ // mLatchD, mLatchQ,
+ mLatchDValid(false), mLatchQValid(false)
+{
+ snprintf(mName, kNameLength, "AudioOut_%X", id);
+ mNBLogWriter = audioFlinger->newWriter_l(kLogSize, mName);
+
+ // Assumes constructor is called by AudioFlinger with it's mLock held, but
+ // it would be safer to explicitly pass initial masterVolume/masterMute as
+ // parameter.
+ //
+ // If the HAL we are using has support for master volume or master mute,
+ // then do not attenuate or mute during mixing (just leave the volume at 1.0
+ // and the mute set to false).
+ mMasterVolume = audioFlinger->masterVolume_l();
+ mMasterMute = audioFlinger->masterMute_l();
+ if (mOutput && mOutput->audioHwDev) {
+ if (mOutput->audioHwDev->canSetMasterVolume()) {
+ mMasterVolume = 1.0;
+ }
+
+ if (mOutput->audioHwDev->canSetMasterMute()) {
+ mMasterMute = false;
+ }
+ }
+
+ readOutputParameters();
+
+ // mStreamTypes[AUDIO_STREAM_CNT] is initialized by stream_type_t default constructor
+ // There is no AUDIO_STREAM_MIN, and ++ operator does not compile
+ for (audio_stream_type_t stream = (audio_stream_type_t) 0; stream < AUDIO_STREAM_CNT;
+ stream = (audio_stream_type_t) (stream + 1)) {
+ mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
+ mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
+ }
+ // mStreamTypes[AUDIO_STREAM_CNT] exists but isn't explicitly initialized here,
+ // because mAudioFlinger doesn't have one to copy from
+}
+
+AudioFlinger::PlaybackThread::~PlaybackThread()
+{
+ mAudioFlinger->unregisterWriter(mNBLogWriter);
+ delete [] mAllocMixBuffer;
+}
+
+void AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args)
+{
+ dumpInternals(fd, args);
+ dumpTracks(fd, args);
+ dumpEffectChains(fd, args);
+}
+
+void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ 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);
+ Track::appendDumpHeader(result);
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+
+ snprintf(buffer, SIZE, "Output thread %p active tracks\n", this);
+ result.append(buffer);
+ Track::appendDumpHeader(result);
+ for (size_t i = 0; i < mActiveTracks.size(); ++i) {
+ sp<Track> track = mActiveTracks[i].promote();
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+ write(fd, result.string(), result.size());
+
+ // These values are "raw"; they will wrap around. See prepareTracks_l() for a better way.
+ FastTrackUnderruns underruns = getFastTrackUnderruns(0);
+ fdprintf(fd, "Normal mixer raw underrun counters: partial=%u empty=%u\n",
+ underruns.mBitFields.mPartial, underruns.mBitFields.mEmpty);
+}
+
+void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Normal frame count: %zu\n", mNormalFrameCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n",
+ ns2ms(systemTime() - mLastWriteTime));
+ result.append(buffer);
+ snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite);
+ 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());
+ fdprintf(fd, "Fast track availMask=%#x\n", mFastTrackAvailMask);
+
+ dumpBase(fd, args);
+}
+
+// Thread virtuals
+status_t AudioFlinger::PlaybackThread::readyToRun()
+{
+ status_t status = initCheck();
+ if (status == NO_ERROR) {
+ ALOGI("AudioFlinger's thread %p ready to run", this);
+ } else {
+ ALOGE("No working audio driver found.");
+ }
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::onFirstRef()
+{
+ run(mName, ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+// ThreadBase virtuals
+void AudioFlinger::PlaybackThread::preExit()
+{
+ ALOGV(" preExit()");
+ // FIXME this is using hard-coded strings but in the future, this functionality will be
+ // converted to use audio HAL extensions required to support tunneling
+ mOutput->stream->common.set_parameters(&mOutput->stream->common, "exiting=1");
+}
+
+// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
+sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
+ const sp<AudioFlinger::Client>& client,
+ audio_stream_type_t streamType,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ IAudioFlinger::track_flags_t *flags,
+ pid_t tid,
+ int uid,
+ status_t *status)
+{
+ sp<Track> track;
+ status_t lStatus;
+
+ bool isTimed = (*flags & IAudioFlinger::TRACK_TIMED) != 0;
+
+ // client expresses a preference for FAST, but we get the final say
+ if (*flags & IAudioFlinger::TRACK_FAST) {
+ if (
+ // not timed
+ (!isTimed) &&
+ // either of these use cases:
+ (
+ // use case 1: shared buffer with any frame count
+ (
+ (sharedBuffer != 0)
+ ) ||
+ // use case 2: callback handler and frame count is default or at least as large as HAL
+ (
+ (tid != -1) &&
+ ((frameCount == 0) ||
+ (frameCount >= mFrameCount))
+ )
+ ) &&
+ // PCM data
+ audio_is_linear_pcm(format) &&
+ // mono or stereo
+ ( (channelMask == AUDIO_CHANNEL_OUT_MONO) ||
+ (channelMask == AUDIO_CHANNEL_OUT_STEREO) ) &&
+ // hardware sample rate
+ (sampleRate == mSampleRate) &&
+ // 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?
+ ) {
+ // if frameCount not specified, then it defaults to fast mixer (HAL) frame count
+ if (frameCount == 0) {
+ frameCount = mFrameCount * kFastTrackMultiplier;
+ }
+ ALOGV("AUDIO_OUTPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
+ frameCount, mFrameCount);
+ } else {
+ ALOGV("AUDIO_OUTPUT_FLAG_FAST denied: isTimed=%d sharedBuffer=%p frameCount=%d "
+ "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
+ "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;
+ // For compatibility with AudioTrack calculation, buffer depth is forced
+ // to be at least 2 x the normal mixer frame count and cover audio hardware latency.
+ // This is probably too conservative, but legacy application code may depend on it.
+ // If you change this calculation, also review the start threshold which is related.
+ uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
+ uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+ if (minBufCount < 2) {
+ minBufCount = 2;
+ }
+ size_t minFrameCount = mNormalFrameCount * minBufCount;
+ if (frameCount < minFrameCount) {
+ frameCount = minFrameCount;
+ }
+ }
+ }
+
+ if (mType == DIRECT) {
+ if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) {
+ if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
+ ALOGE("createTrack_l() Bad parameter: sampleRate %u format %d, channelMask 0x%08x "
+ "for output %p with format %d",
+ sampleRate, format, channelMask, mOutput, mFormat);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
+ } else if (mType == OFFLOAD) {
+ if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
+ ALOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \""
+ "for output %p with format %d",
+ sampleRate, format, channelMask, mOutput, mFormat);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ } else {
+ if ((format & AUDIO_FORMAT_MAIN_MASK) != AUDIO_FORMAT_PCM) {
+ ALOGE("createTrack_l() Bad parameter: format %d \""
+ "for output %p with format %d",
+ format, mOutput, mFormat);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ // Resampler implementation limits input sampling rate to 2 x output sampling rate.
+ if (sampleRate > mSampleRate*2) {
+ ALOGE("Sample rate out of range: %u mSampleRate %u", sampleRate, mSampleRate);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
+
+ lStatus = initCheck();
+ if (lStatus != NO_ERROR) {
+ ALOGE("Audio driver not initialized.");
+ goto Exit;
+ }
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+
+ // all tracks in same audio session must share the same routing strategy otherwise
+ // conflicts will happen when tracks are moved from one output to another by audio policy
+ // manager
+ uint32_t strategy = AudioSystem::getStrategyForStream(streamType);
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> t = mTracks[i];
+ if (t != 0 && !t->isOutputTrack()) {
+ uint32_t actual = AudioSystem::getStrategyForStream(t->streamType());
+ if (sessionId == t->sessionId() && strategy != actual) {
+ ALOGE("createTrack_l() mismatched strategy; expected %u but found %u",
+ strategy, actual);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ }
+ }
+
+ if (!isTimed) {
+ track = new Track(this, client, streamType, sampleRate, format,
+ channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);
+ } else {
+ track = TimedTrack::create(this, client, streamType, sampleRate, format,
+ channelMask, frameCount, sharedBuffer, sessionId, uid);
+ }
+ if (track == 0 || track->getCblk() == NULL || track->name() < 0) {
+ lStatus = NO_MEMORY;
+ goto Exit;
+ }
+
+ mTracks.add(track);
+
+ sp<EffectChain> chain = getEffectChain_l(sessionId);
+ if (chain != 0) {
+ ALOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
+ track->setMainBuffer(chain->inBuffer());
+ chain->setStrategy(AudioSystem::getStrategyForStream(track->streamType()));
+ chain->incTrackCnt();
+ }
+
+ if ((*flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
+ // so ask activity manager to do this on our behalf
+ sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp);
+ }
+ }
+
+ lStatus = NO_ERROR;
+
+Exit:
+ if (status) {
+ *status = lStatus;
+ }
+ return track;
+}
+
+uint32_t AudioFlinger::PlaybackThread::correctLatency_l(uint32_t latency) const
+{
+ return latency;
+}
+
+uint32_t AudioFlinger::PlaybackThread::latency() const
+{
+ Mutex::Autolock _l(mLock);
+ return latency_l();
+}
+uint32_t AudioFlinger::PlaybackThread::latency_l() const
+{
+ if (initCheck() == NO_ERROR) {
+ return correctLatency_l(mOutput->stream->get_latency(mOutput->stream));
+ } else {
+ return 0;
+ }
+}
+
+void AudioFlinger::PlaybackThread::setMasterVolume(float value)
+{
+ Mutex::Autolock _l(mLock);
+ // Don't apply master volume in SW if our HAL can do it for us.
+ if (mOutput && mOutput->audioHwDev &&
+ mOutput->audioHwDev->canSetMasterVolume()) {
+ mMasterVolume = 1.0;
+ } else {
+ mMasterVolume = value;
+ }
+}
+
+void AudioFlinger::PlaybackThread::setMasterMute(bool muted)
+{
+ Mutex::Autolock _l(mLock);
+ // Don't apply master mute in SW if our HAL can do it for us.
+ if (mOutput && mOutput->audioHwDev &&
+ mOutput->audioHwDev->canSetMasterMute()) {
+ mMasterMute = false;
+ } else {
+ mMasterMute = muted;
+ }
+}
+
+void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, float value)
+{
+ Mutex::Autolock _l(mLock);
+ mStreamTypes[stream].volume = value;
+ broadcast_l();
+}
+
+void AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
+{
+ Mutex::Autolock _l(mLock);
+ mStreamTypes[stream].mute = muted;
+ broadcast_l();
+}
+
+float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) const
+{
+ Mutex::Autolock _l(mLock);
+ return mStreamTypes[stream].volume;
+}
+
+// addTrack_l() must be called with ThreadBase::mLock held
+status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
+{
+ status_t status = ALREADY_EXISTS;
+
+ // set retry count for buffer fill
+ track->mRetryCount = kMaxTrackStartupRetries;
+ if (mActiveTracks.indexOf(track) < 0) {
+ // the track is newly added, make sure it fills up all its
+ // buffers before playing. This is to ensure the client will
+ // effectively get the latency it requested.
+ if (!track->isOutputTrack()) {
+ TrackBase::track_state state = track->mState;
+ mLock.unlock();
+ status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId());
+ mLock.lock();
+ // abort track was stopped/paused while we released the lock
+ if (state != track->mState) {
+ if (status == NO_ERROR) {
+ mLock.unlock();
+ AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+ mLock.lock();
+ }
+ return INVALID_OPERATION;
+ }
+ // abort if start is rejected by audio policy manager
+ if (status != NO_ERROR) {
+ return PERMISSION_DENIED;
+ }
+#ifdef ADD_BATTERY_DATA
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
+#endif
+ }
+
+ track->mFillingUpStatus = track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
+ track->mResetDone = false;
+ track->mPresentationCompleteFrames = 0;
+ mActiveTracks.add(track);
+ mWakeLockUids.add(track->uid());
+ mActiveTracksGeneration++;
+ mLatestActiveTrack = track;
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
+ track->sessionId());
+ chain->incActiveTrackCnt();
+ }
+
+ status = NO_ERROR;
+ }
+
+ ALOGV("signal playback thread");
+ broadcast_l();
+
+ return status;
+}
+
+bool AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track)
+{
+ track->terminate();
+ // active tracks are removed by threadLoop()
+ bool trackActive = (mActiveTracks.indexOf(track) >= 0);
+ track->mState = TrackBase::STOPPED;
+ if (!trackActive) {
+ removeTrack_l(track);
+ } else if (track->isFastTrack() || track->isOffloaded()) {
+ track->mState = TrackBase::STOPPING_1;
+ }
+
+ return trackActive;
+}
+
+void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
+{
+ track->triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ mTracks.remove(track);
+ deleteTrackName_l(track->name());
+ // redundant as track is about to be destroyed, for dumpsys only
+ track->mName = -1;
+ if (track->isFastTrack()) {
+ int index = track->mFastIndex;
+ ALOG_ASSERT(0 < index && index < (int)FastMixerState::kMaxFastTracks);
+ ALOG_ASSERT(!(mFastTrackAvailMask & (1 << index)));
+ mFastTrackAvailMask |= 1 << index;
+ // redundant as track is about to be destroyed, for dumpsys only
+ track->mFastIndex = -1;
+ }
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ chain->decTrackCnt();
+ }
+}
+
+void AudioFlinger::PlaybackThread::broadcast_l()
+{
+ // Thread could be blocked waiting for async
+ // so signal it to handle state changes immediately
+ // If threadLoop is currently unlocked a signal of mWaitWorkCV will
+ // be lost so we also flag to prevent it blocking on mWaitWorkCV
+ mSignalPending = true;
+ mWaitWorkCV.broadcast();
+}
+
+String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
+{
+ Mutex::Autolock _l(mLock);
+ if (initCheck() != NO_ERROR) {
+ return String8();
+ }
+
+ char *s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string());
+ const String8 out_s8(s);
+ free(s);
+ return out_s8;
+}
+
+// audioConfigChanged_l() must be called with AudioFlinger::mLock held
+void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) {
+ AudioSystem::OutputDescriptor desc;
+ void *param2 = NULL;
+
+ ALOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event,
+ param);
+
+ switch (event) {
+ case AudioSystem::OUTPUT_OPENED:
+ case AudioSystem::OUTPUT_CONFIG_CHANGED:
+ desc.channelMask = mChannelMask;
+ desc.samplingRate = mSampleRate;
+ desc.format = mFormat;
+ desc.frameCount = mNormalFrameCount; // FIXME see
+ // AudioFlinger::frameCount(audio_io_handle_t)
+ desc.latency = latency();
+ param2 = &desc;
+ break;
+
+ case AudioSystem::STREAM_CONFIG_CHANGED:
+ param2 = &param;
+ case AudioSystem::OUTPUT_CLOSED:
+ default:
+ break;
+ }
+ mAudioFlinger->audioConfigChanged_l(event, mId, param2);
+}
+
+void AudioFlinger::PlaybackThread::writeCallback()
+{
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->resetWriteBlocked();
+}
+
+void AudioFlinger::PlaybackThread::drainCallback()
+{
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->resetDraining();
+}
+
+void AudioFlinger::PlaybackThread::resetWriteBlocked(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // reject out of sequence requests
+ if ((mWriteAckSequence & 1) && (sequence == mWriteAckSequence)) {
+ mWriteAckSequence &= ~1;
+ mWaitWorkCV.signal();
+ }
+}
+
+void AudioFlinger::PlaybackThread::resetDraining(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // reject out of sequence requests
+ if ((mDrainSequence & 1) && (sequence == mDrainSequence)) {
+ mDrainSequence &= ~1;
+ mWaitWorkCV.signal();
+ }
+}
+
+// static
+int AudioFlinger::PlaybackThread::asyncCallback(stream_callback_event_t event,
+ void *param,
+ void *cookie)
+{
+ AudioFlinger::PlaybackThread *me = (AudioFlinger::PlaybackThread *)cookie;
+ ALOGV("asyncCallback() event %d", event);
+ switch (event) {
+ case STREAM_CBK_EVENT_WRITE_READY:
+ me->writeCallback();
+ break;
+ case STREAM_CBK_EVENT_DRAIN_READY:
+ me->drainCallback();
+ break;
+ default:
+ ALOGW("asyncCallback() unknown event %d", event);
+ break;
+ }
+ return 0;
+}
+
+void AudioFlinger::PlaybackThread::readOutputParameters()
+{
+ // unfortunately we have no way of recovering from errors here, hence the LOG_FATAL
+ mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common);
+ mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common);
+ if (!audio_is_output_channel(mChannelMask)) {
+ LOG_FATAL("HAL channel mask %#x not valid for output", mChannelMask);
+ }
+ if ((mType == MIXER || mType == DUPLICATING) && mChannelMask != AUDIO_CHANNEL_OUT_STEREO) {
+ LOG_FATAL("HAL channel mask %#x not supported for mixed output; "
+ "must be AUDIO_CHANNEL_OUT_STEREO", mChannelMask);
+ }
+ mChannelCount = popcount(mChannelMask);
+ mFormat = mOutput->stream->common.get_format(&mOutput->stream->common);
+ if (!audio_is_valid_format(mFormat)) {
+ LOG_FATAL("HAL format %d not valid for output", mFormat);
+ }
+ if ((mType == MIXER || mType == DUPLICATING) && mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ LOG_FATAL("HAL format %d not supported for mixed output; must be AUDIO_FORMAT_PCM_16_BIT",
+ mFormat);
+ }
+ 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);
+ }
+
+ if ((mOutput->flags & AUDIO_OUTPUT_FLAG_NON_BLOCKING) &&
+ (mOutput->stream->set_callback != NULL)) {
+ if (mOutput->stream->set_callback(mOutput->stream,
+ AudioFlinger::PlaybackThread::asyncCallback, this) == 0) {
+ mUseAsyncWrite = true;
+ mCallbackThread = new AudioFlinger::AsyncCallbackThread(this);
+ }
+ }
+
+ // Calculate size of normal mix buffer relative to the HAL output buffer size
+ double multiplier = 1.0;
+ if (mType == MIXER && (kUseFastMixer == FastMixer_Static ||
+ kUseFastMixer == FastMixer_Dynamic)) {
+ size_t minNormalFrameCount = (kMinNormalMixBufferSizeMs * mSampleRate) / 1000;
+ size_t maxNormalFrameCount = (kMaxNormalMixBufferSizeMs * mSampleRate) / 1000;
+ // round up minimum and round down maximum to nearest 16 frames to satisfy AudioMixer
+ minNormalFrameCount = (minNormalFrameCount + 15) & ~15;
+ maxNormalFrameCount = maxNormalFrameCount & ~15;
+ if (maxNormalFrameCount < minNormalFrameCount) {
+ maxNormalFrameCount = minNormalFrameCount;
+ }
+ multiplier = (double) minNormalFrameCount / (double) mFrameCount;
+ if (multiplier <= 1.0) {
+ multiplier = 1.0;
+ } else if (multiplier <= 2.0) {
+ if (2 * mFrameCount <= maxNormalFrameCount) {
+ multiplier = 2.0;
+ } else {
+ multiplier = (double) maxNormalFrameCount / (double) mFrameCount;
+ }
+ } else {
+ // prefer an even multiplier, for compatibility with doubling of fast tracks due to HAL
+ // SRC (it would be unusual for the normal mix buffer size to not be a multiple of fast
+ // track, but we sometimes have to do this to satisfy the maximum frame count
+ // constraint)
+ // FIXME this rounding up should not be done if no HAL SRC
+ uint32_t truncMult = (uint32_t) multiplier;
+ if ((truncMult & 1)) {
+ if ((truncMult + 1) * mFrameCount <= maxNormalFrameCount) {
+ ++truncMult;
+ }
+ }
+ multiplier = (double) truncMult;
+ }
+ }
+ mNormalFrameCount = multiplier * mFrameCount;
+ // round up to nearest 16 frames to satisfy AudioMixer
+ mNormalFrameCount = (mNormalFrameCount + 15) & ~15;
+ ALOGI("HAL output buffer size %u frames, normal mix buffer size %u frames", mFrameCount,
+ mNormalFrameCount);
+
+ delete[] mAllocMixBuffer;
+ size_t align = (mFrameSize < sizeof(int16_t)) ? sizeof(int16_t) : mFrameSize;
+ mAllocMixBuffer = new int8_t[mNormalFrameCount * mFrameSize + align - 1];
+ mMixBuffer = (int16_t *) ((((size_t)mAllocMixBuffer + align - 1) / align) * align);
+ memset(mMixBuffer, 0, mNormalFrameCount * mFrameSize);
+
+ // force reconfiguration of effect chains and engines to take new buffer size and audio
+ // parameters into account
+ // Note that mLock is not held when readOutputParameters() is called from the constructor
+ // but in this case nothing is done below as no audio sessions have effect yet so it doesn't
+ // matter.
+ // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains
+ Vector< sp<EffectChain> > effectChains = mEffectChains;
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false);
+ }
+}
+
+
+status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames)
+{
+ if (halFrames == NULL || dspFrames == NULL) {
+ return BAD_VALUE;
+ }
+ Mutex::Autolock _l(mLock);
+ if (initCheck() != NO_ERROR) {
+ return INVALID_OPERATION;
+ }
+ size_t framesWritten = mBytesWritten / mFrameSize;
+ *halFrames = framesWritten;
+
+ if (isSuspended()) {
+ // return an estimation of rendered frames when the output is suspended
+ size_t latencyFrames = (latency_l() * mSampleRate) / 1000;
+ *dspFrames = framesWritten >= latencyFrames ? framesWritten - latencyFrames : 0;
+ return NO_ERROR;
+ } else {
+ status_t status;
+ uint32_t frames;
+ status = mOutput->stream->get_render_position(mOutput->stream, &frames);
+ *dspFrames = (size_t)frames;
+ return status;
+ }
+}
+
+uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) const
+{
+ Mutex::Autolock _l(mLock);
+ uint32_t result = 0;
+ if (getEffectChain_l(sessionId) != 0) {
+ result = EFFECT_SESSION;
+ }
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (sessionId == track->sessionId() && !track->isInvalid()) {
+ result |= TRACK_SESSION;
+ break;
+ }
+ }
+
+ return result;
+}
+
+uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId)
+{
+ // session AUDIO_SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
+ // it is moved to correct output by audio policy manager when A2DP is connected or disconnected
+ if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
+ return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
+ }
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<Track> track = mTracks[i];
+ if (sessionId == track->sessionId() && !track->isInvalid()) {
+ return AudioSystem::getStrategyForStream(track->streamType());
+ }
+ }
+ return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
+}
+
+
+AudioFlinger::AudioStreamOut* AudioFlinger::PlaybackThread::getOutput() const
+{
+ Mutex::Autolock _l(mLock);
+ return mOutput;
+}
+
+AudioFlinger::AudioStreamOut* AudioFlinger::PlaybackThread::clearOutput()
+{
+ 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;
+}
+
+// this method must always be called either with ThreadBase mLock held or inside the thread loop
+audio_stream_t* AudioFlinger::PlaybackThread::stream() const
+{
+ if (mOutput == NULL) {
+ return NULL;
+ }
+ return &mOutput->stream->common;
+}
+
+uint32_t AudioFlinger::PlaybackThread::activeSleepTimeUs() const
+{
+ return (uint32_t)((uint32_t)((mNormalFrameCount * 1000) / mSampleRate) * 1000);
+}
+
+status_t AudioFlinger::PlaybackThread::setSyncEvent(const sp<SyncEvent>& event)
+{
+ if (!isValidSyncEvent(event)) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock _l(mLock);
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<Track> track = mTracks[i];
+ if (event->triggerSession() == track->sessionId()) {
+ (void) track->setSyncEvent(event);
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+bool AudioFlinger::PlaybackThread::isValidSyncEvent(const sp<SyncEvent>& event) const
+{
+ return event->type() == AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE;
+}
+
+void AudioFlinger::PlaybackThread::threadLoop_removeTracks(
+ const Vector< sp<Track> >& tracksToRemove)
+{
+ size_t count = tracksToRemove.size();
+ if (count) {
+ for (size_t i = 0 ; i < count ; i++) {
+ const sp<Track>& track = tracksToRemove.itemAt(i);
+ if (!track->isOutputTrack()) {
+ AudioSystem::stopOutput(mId, track->streamType(), track->sessionId());
+#ifdef ADD_BATTERY_DATA
+ // to track the speaker usage
+ addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
+#endif
+ if (track->isTerminated()) {
+ AudioSystem::releaseOutput(mId);
+ }
+ }
+ }
+ }
+}
+
+void AudioFlinger::PlaybackThread::checkSilentMode_l()
+{
+ if (!mMasterMute) {
+ char value[PROPERTY_VALUE_MAX];
+ if (property_get("ro.audio.silent", value, "0") > 0) {
+ char *endptr;
+ unsigned long ul = strtoul(value, &endptr, 0);
+ if (*endptr == '\0' && ul != 0) {
+ ALOGD("Silence is golden");
+ // The setprop command will not allow a property to be changed after
+ // the first time it is set, so we don't have to worry about un-muting.
+ setMasterMute_l(true);
+ }
+ }
+ }
+}
+
+// shared by MIXER and DIRECT, overridden by DUPLICATING
+ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
+{
+ // FIXME rewrite to reduce number of system calls
+ mLastWriteTime = systemTime();
+ mInWrite = true;
+ ssize_t 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 = mBytesRemaining >> mBitShift;
+ size_t offset = (mCurrentWriteLength - mBytesRemaining) >> 1;
+ ATRACE_BEGIN("write");
+ // update the setpoint when AudioFlinger::mScreenState changes
+ uint32_t screenState = AudioFlinger::mScreenState;
+ if (screenState != mScreenState) {
+ mScreenState = screenState;
+ MonoPipe *pipe = (MonoPipe *)mPipeSink.get();
+ if (pipe != NULL) {
+ pipe->setAvgFrames((mScreenState & 1) ?
+ (pipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
+ }
+ }
+ ssize_t framesWritten = mNormalSink->write(mMixBuffer + offset, count);
+ ATRACE_END();
+ if (framesWritten > 0) {
+ bytesWritten = framesWritten << mBitShift;
+ } else {
+ bytesWritten = framesWritten;
+ }
+ status_t status = mNormalSink->getTimestamp(mLatchD.mTimestamp);
+ if (status == NO_ERROR) {
+ size_t totalFramesWritten = mNormalSink->framesWritten();
+ if (totalFramesWritten >= mLatchD.mTimestamp.mPosition) {
+ mLatchD.mUnpresentedFrames = totalFramesWritten - mLatchD.mTimestamp.mPosition;
+ mLatchDValid = true;
+ }
+ }
+ // otherwise use the HAL / AudioStreamOut directly
+ } else {
+ // Direct output and offload threads
+ size_t offset = (mCurrentWriteLength - mBytesRemaining) / sizeof(int16_t);
+ if (mUseAsyncWrite) {
+ ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
+ mWriteAckSequence += 2;
+ mWriteAckSequence |= 1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ }
+ // FIXME We should have an implementation of timestamps for direct output threads.
+ // They are used e.g for multichannel PCM playback over HDMI.
+ bytesWritten = mOutput->stream->write(mOutput->stream,
+ mMixBuffer + offset, mBytesRemaining);
+ if (mUseAsyncWrite &&
+ ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
+ // do not wait for async callback in case of error of full write
+ mWriteAckSequence &= ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ }
+ }
+
+ mNumWrites++;
+ mInWrite = false;
+ mStandby = false;
+ return bytesWritten;
+}
+
+void AudioFlinger::PlaybackThread::threadLoop_drain()
+{
+ if (mOutput->stream->drain) {
+ ALOGV("draining %s", (mMixerStatus == MIXER_DRAIN_TRACK) ? "early" : "full");
+ if (mUseAsyncWrite) {
+ ALOGW_IF(mDrainSequence & 1, "threadLoop_drain(): out of sequence drain request");
+ mDrainSequence |= 1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
+ mOutput->stream->drain(mOutput->stream,
+ (mMixerStatus == MIXER_DRAIN_TRACK) ? AUDIO_DRAIN_EARLY_NOTIFY
+ : AUDIO_DRAIN_ALL);
+ }
+}
+
+void AudioFlinger::PlaybackThread::threadLoop_exit()
+{
+ // Default implementation has nothing to do
+}
+
+/*
+The derived values that are cached:
+ - mixBufferSize from frame count * frame size
+ - activeSleepTime from activeSleepTimeUs()
+ - idleSleepTime from idleSleepTimeUs()
+ - standbyDelay from mActiveSleepTimeUs (DIRECT only)
+ - maxPeriod from frame count and sample rate (MIXER only)
+
+The parameters that affect these derived values are:
+ - frame count
+ - frame size
+ - sample rate
+ - device type: A2DP or not
+ - device latency
+ - format: PCM or not
+ - active sleep time
+ - idle sleep time
+*/
+
+void AudioFlinger::PlaybackThread::cacheParameters_l()
+{
+ mixBufferSize = mNormalFrameCount * mFrameSize;
+ activeSleepTime = activeSleepTimeUs();
+ idleSleepTime = idleSleepTimeUs();
+}
+
+void AudioFlinger::PlaybackThread::invalidateTracks(audio_stream_type_t streamType)
+{
+ ALOGV("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d",
+ this, streamType, mTracks.size());
+ Mutex::Autolock _l(mLock);
+
+ size_t size = mTracks.size();
+ for (size_t i = 0; i < size; i++) {
+ sp<Track> t = mTracks[i];
+ if (t->streamType() == streamType) {
+ t->invalidate();
+ }
+ }
+}
+
+status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp<EffectChain>& chain)
+{
+ int session = chain->sessionId();
+ int16_t *buffer = mMixBuffer;
+ bool ownsBuffer = false;
+
+ ALOGV("addEffectChain_l() %p on thread %p for session %d", chain.get(), this, session);
+ if (session > 0) {
+ // 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 = 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);
+ ownsBuffer = true;
+ }
+
+ // 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()) {
+ ALOGV("addEffectChain_l() track->setMainBuffer track %p buffer %p", track.get(),
+ buffer);
+ track->setMainBuffer(buffer);
+ chain->incTrackCnt();
+ }
+ }
+
+ // 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()) {
+ ALOGV("addEffectChain_l() activating track %p on session %d", track.get(), session);
+ chain->incActiveTrackCnt();
+ }
+ }
+ }
+
+ chain->setInBuffer(buffer, ownsBuffer);
+ chain->setOutBuffer(mMixBuffer);
+ // Effect chain for session AUDIO_SESSION_OUTPUT_STAGE is inserted at end of effect
+ // chains list in order to be processed last as it contains output stage effects
+ // Effect chain for session AUDIO_SESSION_OUTPUT_MIX is inserted before
+ // session AUDIO_SESSION_OUTPUT_STAGE to be processed
+ // after track specific effects and before output stage
+ // It is therefore mandatory that AUDIO_SESSION_OUTPUT_MIX == 0 and
+ // that AUDIO_SESSION_OUTPUT_STAGE < AUDIO_SESSION_OUTPUT_MIX
+ // Effect chain for other sessions are inserted at beginning of effect
+ // chains list to be processed before output mix effects. Relative order between other
+ // sessions is not important
+ size_t size = mEffectChains.size();
+ size_t i = 0;
+ for (i = 0; i < size; i++) {
+ if (mEffectChains[i]->sessionId() < session) {
+ break;
+ }
+ }
+ mEffectChains.insertAt(chain, i);
+ checkSuspendOnAddEffectChain_l(chain);
+
+ return NO_ERROR;
+}
+
+size_t AudioFlinger::PlaybackThread::removeEffectChain_l(const sp<EffectChain>& chain)
+{
+ int session = chain->sessionId();
+
+ ALOGV("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 active tracks from 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()) {
+ ALOGV("removeEffectChain_l(): stopping track on chain %p for session Id: %d",
+ chain.get(), session);
+ chain->decActiveTrackCnt();
+ }
+ }
+
+ // 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);
+ chain->decTrackCnt();
+ }
+ }
+ break;
+ }
+ }
+ return mEffectChains.size();
+}
+
+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 AUDIO_SESSION_OUTPUT_MIX
+ sp<EffectModule> effect = getEffect_l(AUDIO_SESSION_OUTPUT_MIX, 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);
+ }
+ }
+}
+
+bool AudioFlinger::PlaybackThread::threadLoop()
+{
+ Vector< sp<Track> > tracksToRemove;
+
+ standbyTime = systemTime();
+
+ // MIXER
+ nsecs_t lastWarning = 0;
+
+ // DUPLICATING
+ // FIXME could this be made local to while loop?
+ writeFrames = 0;
+
+ int lastGeneration = 0;
+
+ cacheParameters_l();
+ sleepTime = idleSleepTime;
+
+ if (mType == MIXER) {
+ sleepTimeShift = 0;
+ }
+
+ CpuStats cpuStats;
+ const String8 myName(String8::format("thread %p type %d TID %d", this, mType, gettid()));
+
+ acquireWakeLock();
+
+ // mNBLogWriter->log can only be called while thread mutex mLock is held.
+ // So if you need to log when mutex is unlocked, set logString to a non-NULL string,
+ // and then that string will be logged at the next convenient opportunity.
+ const char *logString = NULL;
+
+ checkSilentMode_l();
+
+ while (!exitPending())
+ {
+ cpuStats.sample(myName);
+
+ Vector< sp<EffectChain> > effectChains;
+
+ processConfigEvents();
+
+ { // scope for mLock
+
+ Mutex::Autolock _l(mLock);
+
+ if (logString != NULL) {
+ mNBLogWriter->logTimestamp();
+ mNBLogWriter->log(logString);
+ logString = NULL;
+ }
+
+ if (mLatchDValid) {
+ mLatchQ = mLatchD;
+ mLatchDValid = false;
+ mLatchQValid = true;
+ }
+
+ if (checkForNewParameters_l()) {
+ cacheParameters_l();
+ }
+
+ saveOutputTracks();
+ if (mSignalPending) {
+ // A signal was raised while we were unlocked
+ mSignalPending = false;
+ } else if (waitingAsyncCallback_l()) {
+ if (exitPending()) {
+ break;
+ }
+ releaseWakeLock_l();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
+ ALOGV("wait async completion");
+ mWaitWorkCV.wait(mLock);
+ ALOGV("async completion/wake");
+ acquireWakeLock_l();
+ standbyTime = systemTime() + standbyDelay;
+ sleepTime = 0;
+
+ continue;
+ }
+ if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
+ isSuspended()) {
+ // put audio hardware into standby after short delay
+ if (shouldStandby_l()) {
+
+ threadLoop_standby();
+
+ mStandby = true;
+ }
+
+ if (!mActiveTracks.size() && mConfigEvents.isEmpty()) {
+ // we're about to wait, flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+
+ clearOutputTracks();
+
+ if (exitPending()) {
+ break;
+ }
+
+ releaseWakeLock_l();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
+ // wait until we have something to do...
+ ALOGV("%s going to sleep", myName.string());
+ mWaitWorkCV.wait(mLock);
+ ALOGV("%s waking up", myName.string());
+ acquireWakeLock_l();
+
+ mMixerStatus = MIXER_IDLE;
+ mMixerStatusIgnoringFastTracks = MIXER_IDLE;
+ mBytesWritten = 0;
+ mBytesRemaining = 0;
+ checkSilentMode_l();
+
+ standbyTime = systemTime() + standbyDelay;
+ sleepTime = idleSleepTime;
+ if (mType == MIXER) {
+ sleepTimeShift = 0;
+ }
+
+ continue;
+ }
+ }
+ // mMixerStatusIgnoringFastTracks is also updated internally
+ mMixerStatus = prepareTracks_l(&tracksToRemove);
+
+ // compare with previously applied list
+ if (lastGeneration != mActiveTracksGeneration) {
+ // update wakelock
+ updateWakeLockUids_l(mWakeLockUids);
+ lastGeneration = mActiveTracksGeneration;
+ }
+
+ // 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
+ lockEffectChains_l(effectChains);
+ } // mLock scope ends
+
+ if (mBytesRemaining == 0) {
+ mCurrentWriteLength = 0;
+ if (mMixerStatus == MIXER_TRACKS_READY) {
+ // threadLoop_mix() sets mCurrentWriteLength
+ threadLoop_mix();
+ } else if ((mMixerStatus != MIXER_DRAIN_TRACK)
+ && (mMixerStatus != MIXER_DRAIN_ALL)) {
+ // threadLoop_sleepTime sets sleepTime to 0 if data
+ // must be written to HAL
+ threadLoop_sleepTime();
+ if (sleepTime == 0) {
+ mCurrentWriteLength = mixBufferSize;
+ }
+ }
+ mBytesRemaining = mCurrentWriteLength;
+ if (isSuspended()) {
+ sleepTime = suspendSleepTimeUs();
+ // simulate write to HAL when suspended
+ mBytesWritten += mixBufferSize;
+ mBytesRemaining = 0;
+ }
+
+ // only process effects if we're going to write
+ if (sleepTime == 0 && mType != OFFLOAD) {
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ }
+ }
+ // Process effect chains for offloaded thread even if no audio
+ // was read from audio track: process only updates effect state
+ // and thus does have to be synchronized with audio writes but may have
+ // to be called while waiting for async write callback
+ if (mType == OFFLOAD) {
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+ }
+
+ // enable changes in effect chain
+ unlockEffectChains(effectChains);
+
+ if (!waitingAsyncCallback()) {
+ // sleepTime == 0 means we must write to audio hardware
+ if (sleepTime == 0) {
+ if (mBytesRemaining) {
+ ssize_t ret = threadLoop_write();
+ if (ret < 0) {
+ mBytesRemaining = 0;
+ } else {
+ mBytesWritten += ret;
+ mBytesRemaining -= ret;
+ }
+ } else if ((mMixerStatus == MIXER_DRAIN_TRACK) ||
+ (mMixerStatus == MIXER_DRAIN_ALL)) {
+ threadLoop_drain();
+ }
+if (mType == MIXER) {
+ // write blocked detection
+ nsecs_t now = systemTime();
+ nsecs_t delta = now - mLastWriteTime;
+ if (!mStandby && delta > maxPeriod) {
+ mNumDelayedWrites++;
+ if ((now - lastWarning) > kWarningThrottleNs) {
+ ATRACE_NAME("underrun");
+ ALOGW("write blocked for %llu msecs, %d delayed writes, thread %p",
+ ns2ms(delta), mNumDelayedWrites, this);
+ lastWarning = now;
+ }
+ }
+}
+
+ } else {
+ usleep(sleepTime);
+ }
+ }
+
+ // Finally let go of removed track(s), without the lock held
+ // since we can't guarantee the destructors won't acquire that
+ // 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;
+ // it was in the original code but maybe the
+ // assignment in saveOutputTracks() makes this unnecessary?
+ clearOutputTracks();
+
+ // Effect chains will be actually deleted here if they were removed from
+ // mEffectChains list during mixing or effects processing
+ effectChains.clear();
+
+ // FIXME Note that the above .clear() is no longer necessary since effectChains
+ // is now local to this block, but will keep it for now (at least until merge done).
+ }
+
+ threadLoop_exit();
+
+ // for DuplicatingThread, standby mode is handled by the outputTracks, otherwise ...
+ if (mType == MIXER || mType == DIRECT || mType == OFFLOAD) {
+ // put output stream into standby mode
+ if (!mStandby) {
+ mOutput->stream->common.standby(&mOutput->stream->common);
+ }
+ }
+
+ releaseWakeLock();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
+
+ ALOGV("Thread %p type %d exiting", this, mType);
+ return false;
+}
+
+// removeTracks_l() must be called with ThreadBase::mLock held
+void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove)
+{
+ size_t count = tracksToRemove.size();
+ if (count) {
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<Track>& track = tracksToRemove.itemAt(i);
+ mActiveTracks.remove(track);
+ mWakeLockUids.remove(track->uid());
+ mActiveTracksGeneration++;
+ ALOGV("removeTracks_l removing track on session %d", track->sessionId());
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ ALOGV("stopping track on chain %p for session Id: %d", chain.get(),
+ track->sessionId());
+ chain->decActiveTrackCnt();
+ }
+ if (track->isTerminated()) {
+ removeTrack_l(track);
+ }
+ }
+ }
+
+}
+
+status_t AudioFlinger::PlaybackThread::getTimestamp_l(AudioTimestamp& timestamp)
+{
+ if (mNormalSink != 0) {
+ return mNormalSink->getTimestamp(timestamp);
+ }
+ if (mType == OFFLOAD && mOutput->stream->get_presentation_position) {
+ uint64_t position64;
+ int ret = mOutput->stream->get_presentation_position(
+ mOutput->stream, &position64, &timestamp.mTime);
+ if (ret == 0) {
+ timestamp.mPosition = (uint32_t)position64;
+ return NO_ERROR;
+ }
+ }
+ return INVALID_OPERATION;
+}
+// ----------------------------------------------------------------------------
+
+AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, audio_devices_t device, type_t type)
+ : PlaybackThread(audioFlinger, output, id, device, type),
+ // mAudioMixer below
+ // mFastMixer below
+ mFastMixerFutex(0)
+ // mOutputSink below
+ // mPipeSink below
+ // mNormalSink below
+{
+ ALOGV("MixerThread() id=%d device=%#x type=%d", id, device, type);
+ ALOGV("mSampleRate=%u, mChannelMask=%#x, mChannelCount=%u, mFormat=%d, mFrameSize=%u, "
+ "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 != FCC_2) {
+ ALOGE("Invalid audio hardware channel count %d", mChannelCount);
+ }
+
+ // 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 depending on configuration
+ bool initFastMixer;
+ switch (kUseFastMixer) {
+ case FastMixer_Never:
+ initFastMixer = false;
+ break;
+ case FastMixer_Always:
+ initFastMixer = true;
+ break;
+ case FastMixer_Static:
+ case FastMixer_Dynamic:
+ initFastMixer = mFrameCount < mNormalFrameCount;
+ break;
+ }
+ if (initFastMixer) {
+
+ // create a MonoPipe to connect our submix to FastMixer
+ NBAIO_Format format = mOutputSink->format();
+ // This pipe depth compensates for scheduling latency of the normal mixer thread.
+ // When it wakes up after a maximum latency, it runs a few cycles quickly before
+ // finally blocking. Note the pipe implementation rounds up the request to a power of 2.
+ MonoPipe *monoPipe = new MonoPipe(mNormalFrameCount * 4, 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);
+ monoPipe->setAvgFrames((mScreenState & 1) ?
+ (monoPipe->maxFrames() * 7) / 8 : mNormalFrameCount * 2);
+ mPipeSink = monoPipe;
+
+#ifdef TEE_SINK
+ if (mTeeSinkOutputEnabled) {
+ // create a Pipe to archive a copy of FastMixer's output for dumpsys
+ Pipe *teeSink = new Pipe(mTeeSinkOutputFrames, format);
+ numCounterOffers = 0;
+ index = teeSink->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mTeeSink = teeSink;
+ PipeReader *teeSource = new PipeReader(*teeSink);
+ numCounterOffers = 0;
+ index = teeSource->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mTeeSource = teeSource;
+ }
+#endif
+
+ // create fast mixer and configure it initially with just one fast track for our submix
+ mFastMixer = new FastMixer();
+ FastMixerStateQueue *sq = mFastMixer->sq();
+#ifdef STATE_QUEUE_DUMP
+ sq->setObserverDump(&mStateQueueObserverDump);
+ sq->setMutatorDump(&mStateQueueMutatorDump);
+#endif
+ 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;
+#ifdef TEE_SINK
+ state->mTeeSink = mTeeSink.get();
+#endif
+ mFastMixerNBLogWriter = audioFlinger->newWriter_l(kFastMixerLogSize, "FastMixer");
+ state->mNBLogWriter = mFastMixerNBLogWriter.get();
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+
+ // start the fast mixer
+ mFastMixer->run("FastMixer", PRIORITY_URGENT_AUDIO);
+ pid_t tid = mFastMixer->getTid();
+ int err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
+ if (err != 0) {
+ ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
+ kPriorityFastMixer, getpid_cached, tid, err);
+ }
+
+#ifdef AUDIO_WATCHDOG
+ // create and start the watchdog
+ mAudioWatchdog = new AudioWatchdog();
+ mAudioWatchdog->setDump(&mAudioWatchdogDump);
+ mAudioWatchdog->run("AudioWatchdog", PRIORITY_URGENT_AUDIO);
+ tid = mAudioWatchdog->getTid();
+ err = requestPriority(getpid_cached, tid, kPriorityFastMixer);
+ if (err != 0) {
+ ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d",
+ kPriorityFastMixer, getpid_cached, tid, err);
+ }
+#endif
+
+ } else {
+ mFastMixer = NULL;
+ }
+
+ switch (kUseFastMixer) {
+ case FastMixer_Never:
+ case FastMixer_Dynamic:
+ mNormalSink = mOutputSink;
+ break;
+ case FastMixer_Always:
+ mNormalSink = mPipeSink;
+ break;
+ case FastMixer_Static:
+ mNormalSink = initFastMixer ? mPipeSink : mOutputSink;
+ break;
+ }
+}
+
+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 AUDIO_WATCHDOG
+ if (mAudioWatchdog != 0) {
+ mAudioWatchdog->requestExit();
+ mAudioWatchdog->requestExitAndWait();
+ mAudioWatchdog.clear();
+ }
+#endif
+ }
+ mAudioFlinger->unregisterWriter(mFastMixerNBLogWriter);
+ delete mAudioMixer;
+}
+
+
+uint32_t AudioFlinger::MixerThread::correctLatency_l(uint32_t latency) const
+{
+ if (mFastMixer != NULL) {
+ MonoPipe *pipe = (MonoPipe *)mPipeSink.get();
+ latency += (pipe->getAvgFrames() * 1000) / mSampleRate;
+ }
+ return latency;
+}
+
+
+void AudioFlinger::MixerThread::threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove)
+{
+ PlaybackThread::threadLoop_removeTracks(tracksToRemove);
+}
+
+ssize_t 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 &&
+ (kUseFastMixer != FastMixer_Dynamic || 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);
+ }
+#ifdef AUDIO_WATCHDOG
+ if (mAudioWatchdog != 0) {
+ mAudioWatchdog->resume();
+ }
+#endif
+ }
+ state->mCommand = FastMixerState::MIX_WRITE;
+ mFastMixerDumpState.increaseSamplingN(mAudioFlinger->isLowRamDevice() ?
+ FastMixerDumpState::kSamplingNforLowRamDevice : FastMixerDumpState::kSamplingN);
+ sq->end();
+ sq->push(FastMixerStateQueue::BLOCK_UNTIL_PUSHED);
+ if (kUseFastMixer == FastMixer_Dynamic) {
+ mNormalSink = mPipeSink;
+ }
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
+ return PlaybackThread::threadLoop_write();
+}
+
+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);
+ if (kUseFastMixer == FastMixer_Dynamic) {
+ mNormalSink = mOutputSink;
+ }
+#ifdef AUDIO_WATCHDOG
+ if (mAudioWatchdog != 0) {
+ mAudioWatchdog->pause();
+ }
+#endif
+ } else {
+ sq->end(false /*didModify*/);
+ }
+ }
+ PlaybackThread::threadLoop_standby();
+}
+
+// Empty implementation for standard mixer
+// Overridden for offloaded playback
+void AudioFlinger::PlaybackThread::flushOutput_l()
+{
+}
+
+bool AudioFlinger::PlaybackThread::waitingAsyncCallback_l()
+{
+ return false;
+}
+
+bool AudioFlinger::PlaybackThread::shouldStandby_l()
+{
+ return !mStandby;
+}
+
+bool AudioFlinger::PlaybackThread::waitingAsyncCallback()
+{
+ Mutex::Autolock _l(mLock);
+ return waitingAsyncCallback_l();
+}
+
+// shared by MIXER and DIRECT, overridden by DUPLICATING
+void AudioFlinger::PlaybackThread::threadLoop_standby()
+{
+ ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended);
+ mOutput->stream->common.standby(&mOutput->stream->common);
+ if (mUseAsyncWrite != 0) {
+ // discard any pending drain or write ack by incrementing sequence
+ mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+ mDrainSequence = (mDrainSequence + 2) & ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
+}
+
+void AudioFlinger::MixerThread::threadLoop_mix()
+{
+ // obtain the presentation timestamp of the next output buffer
+ int64_t pts;
+ status_t status = INVALID_OPERATION;
+
+ if (mNormalSink != 0) {
+ status = mNormalSink->getNextWriteTimestamp(&pts);
+ } else {
+ status = mOutputSink->getNextWriteTimestamp(&pts);
+ }
+
+ if (status != NO_ERROR) {
+ pts = AudioBufferProvider::kInvalidPTS;
+ }
+
+ // mix buffers...
+ mAudioMixer->process(pts);
+ mCurrentWriteLength = mixBufferSize;
+ // increase sleep time progressively when application underrun condition clears.
+ // Only increase sleep time if the mixer is ready for two consecutive times to avoid
+ // that a steady state of alternating ready/not ready conditions keeps the sleep time
+ // such that we would underrun the audio HAL.
+ if ((sleepTime == 0) && (sleepTimeShift > 0)) {
+ sleepTimeShift--;
+ }
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
+ //TODO: delay standby when effects have a tail
+}
+
+void AudioFlinger::MixerThread::threadLoop_sleepTime()
+{
+ // If no tracks are ready, sleep once for the duration of an output
+ // buffer size, then write 0s to the output
+ if (sleepTime == 0) {
+ if (mMixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime >> sleepTimeShift;
+ if (sleepTime < kMinThreadSleepTimeUs) {
+ sleepTime = kMinThreadSleepTimeUs;
+ }
+ // reduce sleep time in case of consecutive application underruns to avoid
+ // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer
+ // duration we would end up writing less data than needed by the audio HAL if
+ // the condition persists.
+ if (sleepTimeShift < kMaxThreadSleepTimeShift) {
+ sleepTimeShift++;
+ }
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0 || (mMixerStatus == MIXER_TRACKS_ENABLED)) {
+ memset (mMixBuffer, 0, mixBufferSize);
+ sleepTime = 0;
+ ALOGV_IF(mBytesWritten == 0 && (mMixerStatus == MIXER_TRACKS_ENABLED),
+ "anticipated start");
+ }
+ // TODO add standby time extension fct of effect tail
+}
+
+// prepareTracks_l() must be called with ThreadBase::mLock held
+AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTracks_l(
+ Vector< sp<Track> > *tracksToRemove)
+{
+
+ mixer_state mixerStatus = MIXER_IDLE;
+ // find out which tracks need to be processed
+ size_t count = mActiveTracks.size();
+ size_t mixedTracks = 0;
+ size_t tracksWithEffect = 0;
+ // counts only _active_ fast tracks
+ size_t fastTracks = 0;
+ uint32_t resetMask = 0; // bit mask of fast tracks that need to be reset
+
+ float masterVolume = mMasterVolume;
+ bool masterMute = mMasterMute;
+
+ if (masterMute) {
+ masterVolume = 0;
+ }
+ // Delegate master volume control to effect in output mix effect chain if needed
+ sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+ if (chain != 0) {
+ uint32_t v = (uint32_t)(masterVolume * (1 << 24));
+ chain->setVolume_l(&v, &v);
+ masterVolume = (float)((v + (1 << 23)) >> 24);
+ chain.clear();
+ }
+
+ // prepare a new state to push
+ FastMixerStateQueue *sq = NULL;
+ FastMixerState *state = NULL;
+ bool didModify = false;
+ FastMixerStateQueue::block_t block = FastMixerStateQueue::BLOCK_UNTIL_PUSHED;
+ if (mFastMixer != NULL) {
+ sq = mFastMixer->sq();
+ state = sq->begin();
+ }
+
+ for (size_t i=0 ; i<count ; i++) {
+ const sp<Track> t = mActiveTracks[i].promote();
+ if (t == 0) {
+ continue;
+ }
+
+ // this const just means the local variable doesn't change
+ Track* const track = t.get();
+
+ // process fast tracks
+ if (track->isFastTrack()) {
+
+ // It's theoretically possible (though unlikely) for a fast track to be created
+ // and then removed within the same normal mix cycle. This is not a problem, as
+ // the track never becomes active so it's fast mixer slot is never touched.
+ // The converse, of removing an (active) track and then creating a new track
+ // at the identical fast mixer slot within the same normal mix cycle,
+ // is impossible because the slot isn't marked available until the end of each cycle.
+ int j = track->mFastIndex;
+ ALOG_ASSERT(0 < j && j < (int)FastMixerState::kMaxFastTracks);
+ ALOG_ASSERT(!(mFastTrackAvailMask & (1 << j)));
+ FastTrack *fastTrack = &state->mFastTracks[j];
+
+ // Determine whether the track is currently in underrun condition,
+ // and whether it had a recent underrun.
+ FastTrackDump *ftDump = &mFastMixerDumpState.mTracks[j];
+ FastTrackUnderruns underruns = ftDump->mUnderruns;
+ uint32_t recentFull = (underruns.mBitFields.mFull -
+ track->mObservedUnderruns.mBitFields.mFull) & UNDERRUN_MASK;
+ uint32_t recentPartial = (underruns.mBitFields.mPartial -
+ track->mObservedUnderruns.mBitFields.mPartial) & UNDERRUN_MASK;
+ uint32_t recentEmpty = (underruns.mBitFields.mEmpty -
+ track->mObservedUnderruns.mBitFields.mEmpty) & UNDERRUN_MASK;
+ uint32_t recentUnderruns = recentPartial + recentEmpty;
+ track->mObservedUnderruns = underruns;
+ // don't count underruns that occur while stopping or pausing
+ // or stopped which can occur when flush() is called while active
+ if (!(track->isStopping() || track->isPausing() || track->isStopped()) &&
+ recentUnderruns > 0) {
+ // FIXME fast mixer will pull & mix partial buffers, but we count as a full underrun
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(recentUnderruns * mFrameCount);
+ }
+
+ // This is similar to the state machine for normal tracks,
+ // with a few modifications for fast tracks.
+ bool isActive = true;
+ switch (track->mState) {
+ case TrackBase::STOPPING_1:
+ // track stays active in STOPPING_1 state until first underrun
+ if (recentUnderruns > 0 || track->isTerminated()) {
+ track->mState = TrackBase::STOPPING_2;
+ }
+ break;
+ case TrackBase::PAUSING:
+ // ramp down is not yet implemented
+ track->setPaused();
+ break;
+ case TrackBase::RESUMING:
+ // ramp up is not yet implemented
+ track->mState = TrackBase::ACTIVE;
+ break;
+ case TrackBase::ACTIVE:
+ if (recentFull > 0 || recentPartial > 0) {
+ // track has provided at least some frames recently: reset retry count
+ track->mRetryCount = kMaxTrackRetries;
+ }
+ if (recentUnderruns == 0) {
+ // no recent underruns: stay active
+ break;
+ }
+ // there has recently been an underrun of some kind
+ if (track->sharedBuffer() == 0) {
+ // were any of the recent underruns "empty" (no frames available)?
+ if (recentEmpty == 0) {
+ // no, then ignore the partial underruns as they are allowed indefinitely
+ break;
+ }
+ // there has recently been an "empty" underrun: decrement the retry counter
+ if (--(track->mRetryCount) > 0) {
+ break;
+ }
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &track->mCblk->mFlags);
+ // remove from active list, but state remains ACTIVE [confusing but true]
+ isActive = false;
+ break;
+ }
+ // fall through
+ case TrackBase::STOPPING_2:
+ case TrackBase::PAUSED:
+ case TrackBase::STOPPED:
+ case TrackBase::FLUSHED: // flush() while active
+ // Check for presentation complete if track is inactive
+ // We have consumed all the buffers of this track.
+ // This would be incomplete if we auto-paused on underrun
+ {
+ size_t audioHALFrames =
+ (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+ size_t framesWritten = mBytesWritten / mFrameSize;
+ if (!(mStandby || track->presentationComplete(framesWritten, audioHALFrames))) {
+ // track stays in active list until presentation is complete
+ break;
+ }
+ }
+ if (track->isStopping_2()) {
+ track->mState = TrackBase::STOPPED;
+ }
+ if (track->isStopped()) {
+ // Can't reset directly, as fast mixer is still polling this track
+ // track->reset();
+ // So instead mark this track as needing to be reset after push with ack
+ resetMask |= 1 << i;
+ }
+ isActive = false;
+ break;
+ case TrackBase::IDLE:
+ default:
+ LOG_FATAL("unexpected track state %d", track->mState);
+ }
+
+ if (isActive) {
+ // was it previously inactive?
+ if (!(state->mTrackMask & (1 << j))) {
+ ExtendedAudioBufferProvider *eabp = track;
+ VolumeProvider *vp = track;
+ fastTrack->mBufferProvider = eabp;
+ fastTrack->mVolumeProvider = vp;
+ fastTrack->mChannelMask = track->mChannelMask;
+ fastTrack->mGeneration++;
+ state->mTrackMask |= 1 << j;
+ didModify = true;
+ // no acknowledgement required for newly active tracks
+ }
+ // 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;
+ } else {
+ // was it previously active?
+ if (state->mTrackMask & (1 << j)) {
+ fastTrack->mBufferProvider = NULL;
+ fastTrack->mGeneration++;
+ state->mTrackMask &= ~(1 << j);
+ didModify = true;
+ // If any fast tracks were removed, we must wait for acknowledgement
+ // because we're about to decrement the last sp<> on those tracks.
+ block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
+ } else {
+ LOG_FATAL("fast track %d should have been active", j);
+ }
+ tracksToRemove->add(track);
+ // Avoids a misleading display in dumpsys
+ track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL;
+ }
+ continue;
+ }
+
+ { // local variable scope to avoid goto warning
+
+ audio_track_cblk_t* cblk = track->cblk();
+
+ // The first time a track is added we wait
+ // for all its buffers to be filled before processing it
+ int name = track->name();
+ // make sure that we have enough frames to mix one full buffer.
+ // enforce this condition only once to enable draining the buffer in case the client
+ // app does not call stop() and relies on underrun to stop:
+ // hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
+ // during last round
+ size_t desiredFrames;
+ uint32_t sr = track->sampleRate();
+ if (sr == mSampleRate) {
+ desiredFrames = mNormalFrameCount;
+ } else {
+ // +1 for rounding and +1 for additional sample needed for interpolation
+ desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
+ // add frames already consumed but not yet released by the resampler
+ // because cblk->framesReady() will include these frames
+ desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+ // the minimum track buffer size is normally twice the number of frames necessary
+ // to fill one buffer and the resampler should not leave more than one buffer worth
+ // of unreleased frames after each pass, but just in case...
+ ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
+ }
+ uint32_t minFrames = 1;
+ if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
+ (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
+ minFrames = desiredFrames;
+ }
+
+ size_t framesReady = track->framesReady();
+ if ((framesReady >= minFrames) && track->isReady() &&
+ !track->isPaused() && !track->isTerminated())
+ {
+ ALOGVV("track %d s=%08x [OK] on thread %p", name, cblk->mServer, 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 {
+ ALOGW("prepareTracks_l(): track %d attached to effect but no chain found on "
+ "session %d",
+ 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;
+ }
+ mAudioMixer->setParameter(name, AudioMixer::RESAMPLE, AudioMixer::RESET, NULL);
+ // FIXME should not make a decision based on mServer
+ } else if (cblk->mServer != 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
+ uint32_t vl, vr, va;
+ if (track->isPausing() || mStreamTypes[track->streamType()].mute) {
+ vl = vr = va = 0;
+ if (track->isPausing()) {
+ track->setPaused();
+ }
+ } else {
+
+ // read original volumes with volume control
+ float typeVolume = mStreamTypes[track->streamType()].volume;
+ float v = masterVolume * typeVolume;
+ AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
+ uint32_t vlr = proxy->getVolumeLR();
+ vl = vlr & 0xFFFF;
+ vr = vlr >> 16;
+ // track volumes come from shared memory, so can't be trusted and must be clamped
+ if (vl > MAX_GAIN_INT) {
+ ALOGV("Track left volume out of range: %04X", vl);
+ vl = MAX_GAIN_INT;
+ }
+ if (vr > MAX_GAIN_INT) {
+ ALOGV("Track right volume out of range: %04X", vr);
+ vr = MAX_GAIN_INT;
+ }
+ // now apply the master volume and stream type volume
+ vl = (uint32_t)(v * vl) << 12;
+ vr = (uint32_t)(v * vr) << 12;
+ // assuming master volume and stream type volume each go up to 1.0,
+ // vl and vr are now in 8.24 format
+
+ uint16_t sendLevel = proxy->getSendLevel_U4_12();
+ // send level comes from shared memory and so may be corrupt
+ if (sendLevel > MAX_GAIN_INT) {
+ ALOGV("Track send level out of range: %04X", sendLevel);
+ sendLevel = MAX_GAIN_INT;
+ }
+ va = (uint32_t)(v * sendLevel);
+ }
+
+ // Delegate volume control to effect in track effect chain if needed
+ if (chain != 0 && chain->setVolume_l(&vl, &vr)) {
+ // Do not ramp volume if volume is controlled by effect
+ param = AudioMixer::VOLUME;
+ track->mHasVolumeController = true;
+ } else {
+ // force no volume ramp when volume controller was just disabled or removed
+ // from effect chain to avoid volume spike
+ if (track->mHasVolumeController) {
+ param = AudioMixer::VOLUME;
+ }
+ track->mHasVolumeController = false;
+ }
+
+ // Convert volumes from 8.24 to 4.12 format
+ // This additional clamping is needed in case chain->setVolume_l() overshot
+ vl = (vl + (1 << 11)) >> 12;
+ if (vl > MAX_GAIN_INT) {
+ vl = MAX_GAIN_INT;
+ }
+ vr = (vr + (1 << 11)) >> 12;
+ if (vr > MAX_GAIN_INT) {
+ vr = MAX_GAIN_INT;
+ }
+
+ if (va > MAX_GAIN_INT) {
+ va = MAX_GAIN_INT; // va is uint32_t, so no need to check for -
+ }
+
+ // XXX: these things DON'T need to be done each time
+ mAudioMixer->setBufferProvider(name, track);
+ mAudioMixer->enable(name);
+
+ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)(uintptr_t)vl);
+ mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)(uintptr_t)vr);
+ mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)(uintptr_t)va);
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::FORMAT, (void *)track->format());
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::CHANNEL_MASK, (void *)(uintptr_t)track->channelMask());
+ // limit track sample rate to 2 x output sample rate, which changes at re-configuration
+ uint32_t maxSampleRate = mSampleRate * 2;
+ uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate();
+ if (reqSampleRate == 0) {
+ reqSampleRate = mSampleRate;
+ } else if (reqSampleRate > maxSampleRate) {
+ reqSampleRate = maxSampleRate;
+ }
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::RESAMPLE,
+ AudioMixer::SAMPLE_RATE,
+ (void *)(uintptr_t)reqSampleRate);
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer());
+ mAudioMixer->setParameter(
+ name,
+ AudioMixer::TRACK,
+ AudioMixer::AUX_BUFFER, (void *)track->auxBuffer());
+
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetries;
+
+ // If one track is ready, set the mixer ready if:
+ // - the mixer was not ready during previous round OR
+ // - no other track is not ready
+ if (mMixerStatusIgnoringFastTracks != MIXER_TRACKS_READY ||
+ mixerStatus != MIXER_TRACKS_ENABLED) {
+ mixerStatus = MIXER_TRACKS_READY;
+ }
+ } else {
+ if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
+ track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+ }
+ // clear effect chain input buffer if an active track underruns to avoid sending
+ // previous audio buffer again to effects
+ chain = getEffectChain_l(track->sessionId());
+ if (chain != 0) {
+ chain->clearInputBuffer();
+ }
+
+ ALOGVV("track %d s=%08x [NOT READY] on thread %p", name, cblk->mServer, this);
+ if ((track->sharedBuffer() != 0) || track->isTerminated() ||
+ track->isStopped() || track->isPaused()) {
+ // We have consumed all the buffers of this track.
+ // Remove it from the list of active tracks.
+ // TODO: use actual buffer filling status instead of latency when available from
+ // audio HAL
+ size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
+ size_t framesWritten = mBytesWritten / mFrameSize;
+ if (mStandby || track->presentationComplete(framesWritten, audioHALFrames)) {
+ if (track->isStopped()) {
+ track->reset();
+ }
+ tracksToRemove->add(track);
+ }
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ ALOGI("BUFFER TIMEOUT: remove(%d) from active list on thread %p", name, this);
+ tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
+ // If one track is not ready, mark the mixer also not ready if:
+ // - the mixer was ready during previous round OR
+ // - no other track is ready
+ } else if (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY ||
+ mixerStatus != MIXER_TRACKS_READY) {
+ mixerStatus = MIXER_TRACKS_ENABLED;
+ }
+ }
+ mAudioMixer->disable(name);
+ }
+
+ } // local variable scope to avoid goto warning
+track_is_ready: ;
+
+ }
+
+ // Push the new FastMixer state if necessary
+ bool pauseAudioWatchdog = false;
+ if (didModify) {
+ state->mFastTracksGen++;
+ // if the fast mixer was active, but now there are no fast tracks, then put it in cold idle
+ if (kUseFastMixer == FastMixer_Dynamic &&
+ state->mCommand == FastMixerState::MIX_WRITE && state->mTrackMask <= 1) {
+ state->mCommand = FastMixerState::COLD_IDLE;
+ state->mColdFutexAddr = &mFastMixerFutex;
+ state->mColdGen++;
+ mFastMixerFutex = 0;
+ if (kUseFastMixer == FastMixer_Dynamic) {
+ mNormalSink = mOutputSink;
+ }
+ // If we go into cold idle, need to wait for acknowledgement
+ // so that fast mixer stops doing I/O.
+ block = FastMixerStateQueue::BLOCK_UNTIL_ACKED;
+ pauseAudioWatchdog = true;
+ }
+ }
+ if (sq != NULL) {
+ sq->end(didModify);
+ sq->push(block);
+ }
+#ifdef AUDIO_WATCHDOG
+ if (pauseAudioWatchdog && mAudioWatchdog != 0) {
+ mAudioWatchdog->pause();
+ }
+#endif
+
+ // Now perform the deferred reset on fast tracks that have stopped
+ while (resetMask != 0) {
+ size_t i = __builtin_ctz(resetMask);
+ ALOG_ASSERT(i < count);
+ resetMask &= ~(1 << i);
+ sp<Track> t = mActiveTracks[i].promote();
+ if (t == 0) {
+ continue;
+ }
+ Track* track = t.get();
+ ALOG_ASSERT(track->isFastTrack() && track->isStopped());
+ track->reset();
+ }
+
+ // remove all the tracks that need to be...
+ removeTracks_l(*tracksToRemove);
+
+ // 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 ((mBytesRemaining == 0) && ((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
+ mMixerStatusIgnoringFastTracks = mixerStatus;
+ if (fastTracks > 0) {
+ mixerStatus = MIXER_TRACKS_READY;
+ }
+ return mixerStatus;
+}
+
+// getTrackName_l() must be called with ThreadBase::mLock held
+int AudioFlinger::MixerThread::getTrackName_l(audio_channel_mask_t channelMask, int sessionId)
+{
+ return mAudioMixer->getTrackName(channelMask, sessionId);
+}
+
+// deleteTrackName_l() must be called with ThreadBase::mLock held
+void AudioFlinger::MixerThread::deleteTrackName_l(int name)
+{
+ ALOGV("remove track (%d) and delete from mixer", name);
+ mAudioMixer->deleteTrackName(name);
+}
+
+// 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);
+ int value;
+
+ if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
+ if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+ status = BAD_VALUE;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
+ if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) {
+ status = BAD_VALUE;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be guaranteed
+ // if frame count is changed after track creation
+ if (!mTracks.isEmpty()) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+#ifdef ADD_BATTERY_DATA
+ // when changing the audio output device, call addBatteryData to notify
+ // the change
+ if (mOutDevice != value) {
+ uint32_t params = 0;
+ // check whether speaker is on
+ if (value & AUDIO_DEVICE_OUT_SPEAKER) {
+ params |= IMediaPlayerService::kBatteryDataSpeakerOn;
+ }
+
+ audio_devices_t deviceWithoutSpeaker
+ = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER;
+ // check if any other device (except speaker) is on
+ if (value & deviceWithoutSpeaker ) {
+ params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
+ }
+
+ if (params != 0) {
+ addBatteryData(params);
+ }
+ }
+#endif
+
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ if (value != AUDIO_DEVICE_NONE) {
+ mOutDevice = value;
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(mOutDevice);
+ }
+ }
+ }
+
+ if (status == NO_ERROR) {
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
+ if (!mStandby && status == INVALID_OPERATION) {
+ mOutput->stream->common.standby(&mOutput->stream->common);
+ mStandby = true;
+ mBytesWritten = 0;
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
+ }
+ if (status == NO_ERROR && reconfig) {
+ readOutputParameters();
+ delete mAudioMixer;
+ mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate);
+ for (size_t i = 0; i < mTracks.size() ; i++) {
+ int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId);
+ if (name < 0) {
+ break;
+ }
+ mTracks[i]->mName = name;
+ }
+ sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ }
+ }
+
+ mNewParameters.removeAt(0);
+
+ mParamStatus = status;
+ mParamCond.signal();
+ // wait for condition with time out in case the thread calling ThreadBase::setParameters()
+ // 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;
+}
+
+
+void AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ PlaybackThread::dumpInternals(fd, args);
+
+ 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
+ const FastMixerDumpState copy(mFastMixerDumpState);
+ copy.dump(fd);
+
+#ifdef STATE_QUEUE_DUMP
+ // Similar for state queue
+ StateQueueObserverDump observerCopy = mStateQueueObserverDump;
+ observerCopy.dump(fd);
+ StateQueueMutatorDump mutatorCopy = mStateQueueMutatorDump;
+ mutatorCopy.dump(fd);
+#endif
+
+#ifdef TEE_SINK
+ // Write the tee output to a .wav file
+ dumpTee(fd, mTeeSource, mId);
+#endif
+
+#ifdef AUDIO_WATCHDOG
+ if (mAudioWatchdog != 0) {
+ // Make a non-atomic copy of audio watchdog dump so it won't change underneath us
+ AudioWatchdogDump wdCopy = mAudioWatchdogDump;
+ wdCopy.dump(fd);
+ }
+#endif
+}
+
+uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() const
+{
+ return (uint32_t)(((mNormalFrameCount * 1000) / mSampleRate) * 1000) / 2;
+}
+
+uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs() const
+{
+ return (uint32_t)(((mNormalFrameCount * 1000) / mSampleRate) * 1000);
+}
+
+void AudioFlinger::MixerThread::cacheParameters_l()
+{
+ PlaybackThread::cacheParameters_l();
+
+ // FIXME: Relaxed timing because of a certain device that can't meet latency
+ // 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(mNormalFrameCount) / mSampleRate * 15;
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device)
+ : PlaybackThread(audioFlinger, output, id, device, DIRECT)
+ // mLeftVolFloat, mRightVolFloat
+{
+}
+
+AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output, audio_io_handle_t id, uint32_t device,
+ ThreadBase::type_t type)
+ : PlaybackThread(audioFlinger, output, id, device, type)
+ // mLeftVolFloat, mRightVolFloat
+{
+}
+
+AudioFlinger::DirectOutputThread::~DirectOutputThread()
+{
+}
+
+void AudioFlinger::DirectOutputThread::processVolume_l(Track *track, bool lastTrack)
+{
+ audio_track_cblk_t* cblk = track->cblk();
+ float left, right;
+
+ if (mMasterMute || mStreamTypes[track->streamType()].mute) {
+ left = right = 0;
+ } else {
+ float typeVolume = mStreamTypes[track->streamType()].volume;
+ float v = mMasterVolume * typeVolume;
+ AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
+ uint32_t vlr = proxy->getVolumeLR();
+ float v_clamped = v * (vlr & 0xFFFF);
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ left = v_clamped/MAX_GAIN;
+ v_clamped = v * (vlr >> 16);
+ if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN;
+ right = v_clamped/MAX_GAIN;
+ }
+
+ if (lastTrack) {
+ if (left != mLeftVolFloat || right != mRightVolFloat) {
+ mLeftVolFloat = left;
+ mRightVolFloat = right;
+
+ // 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 (!mEffectChains.isEmpty()) {
+ mEffectChains[0]->setVolume_l(&vl, &vr);
+ left = (float)vl / (1 << 24);
+ right = (float)vr / (1 << 24);
+ }
+ if (mOutput->stream->set_volume) {
+ mOutput->stream->set_volume(mOutput->stream, left, right);
+ }
+ }
+ }
+}
+
+
+AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prepareTracks_l(
+ Vector< sp<Track> > *tracksToRemove
+)
+{
+ size_t count = mActiveTracks.size();
+ mixer_state mixerStatus = MIXER_IDLE;
+
+ // find out which tracks need to be processed
+ for (size_t i = 0; i < count; i++) {
+ sp<Track> t = mActiveTracks[i].promote();
+ // The track died recently
+ if (t == 0) {
+ continue;
+ }
+
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
+ // Only consider last track started for volume and mixer state control.
+ // In theory an older track could underrun and restart after the new one starts
+ // but as we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ sp<Track> l = mLatestActiveTrack.promote();
+ bool last = l.get() == track;
+
+ // The first time a track is added we wait
+ // for all its buffers to be filled before processing it
+ uint32_t minFrames;
+ if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing()) {
+ minFrames = mNormalFrameCount;
+ } else {
+ minFrames = 1;
+ }
+
+ if ((track->framesReady() >= minFrames) && track->isReady() &&
+ !track->isPaused() && !track->isTerminated())
+ {
+ ALOGVV("track %d s=%08x [OK]", track->name(), cblk->mServer);
+
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ }
+ }
+
+ // compute volume for this track
+ processVolume_l(track, last);
+ if (last) {
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetriesDirect;
+ mActiveTrack = t;
+ mixerStatus = MIXER_TRACKS_READY;
+ }
+ } else {
+ // clear effect chain input buffer if the last active track started underruns
+ // to avoid sending previous audio buffer again to effects
+ if (!mEffectChains.isEmpty() && last) {
+ mEffectChains[0]->clearInputBuffer();
+ }
+
+ ALOGVV("track %d s=%08x [NOT READY]", track->name(), cblk->mServer);
+ if ((track->sharedBuffer() != 0) || track->isTerminated() ||
+ track->isStopped() || track->isPaused()) {
+ // We have consumed all the buffers of this track.
+ // Remove it from the list of active tracks.
+ // TODO: implement behavior for compressed audio
+ size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
+ size_t framesWritten = mBytesWritten / mFrameSize;
+ if (mStandby || !last ||
+ track->presentationComplete(framesWritten, audioHALFrames)) {
+ if (track->isStopped()) {
+ track->reset();
+ }
+ tracksToRemove->add(track);
+ }
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ // Only consider last track started for mixer state control
+ if (--(track->mRetryCount) <= 0) {
+ ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
+ tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
+ } else if (last) {
+ mixerStatus = MIXER_TRACKS_ENABLED;
+ }
+ }
+ }
+ }
+
+ // remove all the tracks that need to be...
+ removeTracks_l(*tracksToRemove);
+
+ return mixerStatus;
+}
+
+void AudioFlinger::DirectOutputThread::threadLoop_mix()
+{
+ size_t frameCount = mFrameCount;
+ int8_t *curBuf = (int8_t *)mMixBuffer;
+ // output audio to hardware
+ while (frameCount) {
+ AudioBufferProvider::Buffer buffer;
+ buffer.frameCount = frameCount;
+ mActiveTrack->getNextBuffer(&buffer);
+ if (buffer.raw == NULL) {
+ memset(curBuf, 0, frameCount * mFrameSize);
+ break;
+ }
+ memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
+ frameCount -= buffer.frameCount;
+ curBuf += buffer.frameCount * mFrameSize;
+ mActiveTrack->releaseBuffer(&buffer);
+ }
+ mCurrentWriteLength = curBuf - (int8_t *)mMixBuffer;
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
+ mActiveTrack.clear();
+}
+
+void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
+{
+ if (sleepTime == 0) {
+ if (mMixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) {
+ memset(mMixBuffer, 0, mFrameCount * mFrameSize);
+ sleepTime = 0;
+ }
+}
+
+// getTrackName_l() must be called with ThreadBase::mLock held
+int AudioFlinger::DirectOutputThread::getTrackName_l(audio_channel_mask_t channelMask,
+ int sessionId)
+{
+ return 0;
+}
+
+// deleteTrackName_l() must be called with ThreadBase::mLock held
+void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name)
+{
+}
+
+// checkForNewParameters_l() must be called with ThreadBase::mLock held
+bool AudioFlinger::DirectOutputThread::checkForNewParameters_l()
+{
+ bool reconfig = false;
+
+ while (!mNewParameters.isEmpty()) {
+ status_t status = NO_ERROR;
+ String8 keyValuePair = mNewParameters[0];
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be garantied
+ // if frame count is changed after track creation
+ if (!mTracks.isEmpty()) {
+ status = INVALID_OPERATION;
+ } else {
+ reconfig = true;
+ }
+ }
+ if (status == NO_ERROR) {
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
+ if (!mStandby && status == INVALID_OPERATION) {
+ mOutput->stream->common.standby(&mOutput->stream->common);
+ mStandby = true;
+ mBytesWritten = 0;
+ status = mOutput->stream->common.set_parameters(&mOutput->stream->common,
+ keyValuePair.string());
+ }
+ if (status == NO_ERROR && reconfig) {
+ readOutputParameters();
+ sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED);
+ }
+ }
+
+ mNewParameters.removeAt(0);
+
+ mParamStatus = status;
+ mParamCond.signal();
+ // wait for condition with time out in case the thread calling ThreadBase::setParameters()
+ // already timed out waiting for the status and will never signal the condition.
+ mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
+ }
+ return reconfig;
+}
+
+uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs() const
+{
+ uint32_t time;
+ if (audio_is_linear_pcm(mFormat)) {
+ time = PlaybackThread::activeSleepTimeUs();
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs() const
+{
+ uint32_t time;
+ if (audio_is_linear_pcm(mFormat)) {
+ time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs() const
+{
+ uint32_t time;
+ if (audio_is_linear_pcm(mFormat)) {
+ time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+ } else {
+ time = 10000;
+ }
+ return time;
+}
+
+void AudioFlinger::DirectOutputThread::cacheParameters_l()
+{
+ PlaybackThread::cacheParameters_l();
+
+ // use shorter standby delay as on normal output to release
+ // hardware resources as soon as possible
+ if (audio_is_linear_pcm(mFormat)) {
+ standbyDelay = microseconds(activeSleepTime*2);
+ } else {
+ standbyDelay = kOffloadStandbyDelayNs;
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::AsyncCallbackThread::AsyncCallbackThread(
+ const wp<AudioFlinger::PlaybackThread>& playbackThread)
+ : Thread(false /*canCallJava*/),
+ mPlaybackThread(playbackThread),
+ mWriteAckSequence(0),
+ mDrainSequence(0)
+{
+}
+
+AudioFlinger::AsyncCallbackThread::~AsyncCallbackThread()
+{
+}
+
+void AudioFlinger::AsyncCallbackThread::onFirstRef()
+{
+ run("Offload Cbk", ANDROID_PRIORITY_URGENT_AUDIO);
+}
+
+bool AudioFlinger::AsyncCallbackThread::threadLoop()
+{
+ while (!exitPending()) {
+ uint32_t writeAckSequence;
+ uint32_t drainSequence;
+
+ {
+ Mutex::Autolock _l(mLock);
+ while (!((mWriteAckSequence & 1) ||
+ (mDrainSequence & 1) ||
+ exitPending())) {
+ mWaitWorkCV.wait(mLock);
+ }
+
+ if (exitPending()) {
+ break;
+ }
+ ALOGV("AsyncCallbackThread mWriteAckSequence %d mDrainSequence %d",
+ mWriteAckSequence, mDrainSequence);
+ writeAckSequence = mWriteAckSequence;
+ mWriteAckSequence &= ~1;
+ drainSequence = mDrainSequence;
+ mDrainSequence &= ~1;
+ }
+ {
+ sp<AudioFlinger::PlaybackThread> playbackThread = mPlaybackThread.promote();
+ if (playbackThread != 0) {
+ if (writeAckSequence & 1) {
+ playbackThread->resetWriteBlocked(writeAckSequence >> 1);
+ }
+ if (drainSequence & 1) {
+ playbackThread->resetDraining(drainSequence >> 1);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::AsyncCallbackThread::exit()
+{
+ ALOGV("AsyncCallbackThread::exit");
+ Mutex::Autolock _l(mLock);
+ requestExit();
+ mWaitWorkCV.broadcast();
+}
+
+void AudioFlinger::AsyncCallbackThread::setWriteBlocked(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // bit 0 is cleared
+ mWriteAckSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetWriteBlocked()
+{
+ Mutex::Autolock _l(mLock);
+ // ignore unexpected callbacks
+ if (mWriteAckSequence & 2) {
+ mWriteAckSequence |= 1;
+ mWaitWorkCV.signal();
+ }
+}
+
+void AudioFlinger::AsyncCallbackThread::setDraining(uint32_t sequence)
+{
+ Mutex::Autolock _l(mLock);
+ // bit 0 is cleared
+ mDrainSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetDraining()
+{
+ Mutex::Autolock _l(mLock);
+ // ignore unexpected callbacks
+ if (mDrainSequence & 2) {
+ mDrainSequence |= 1;
+ mWaitWorkCV.signal();
+ }
+}
+
+
+// ----------------------------------------------------------------------------
+AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output, audio_io_handle_t id, uint32_t device)
+ : DirectOutputThread(audioFlinger, output, id, device, OFFLOAD),
+ mHwPaused(false),
+ mFlushPending(false),
+ mPausedBytesRemaining(0)
+{
+ //FIXME: mStandby should be set to true by ThreadBase constructor
+ mStandby = true;
+}
+
+void AudioFlinger::OffloadThread::threadLoop_exit()
+{
+ if (mFlushPending || mHwPaused) {
+ // If a flush is pending or track was paused, just discard buffered data
+ flushHw_l();
+ } else {
+ mMixerStatus = MIXER_DRAIN_ALL;
+ threadLoop_drain();
+ }
+ mCallbackThread->exit();
+ PlaybackThread::threadLoop_exit();
+}
+
+AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTracks_l(
+ Vector< sp<Track> > *tracksToRemove
+)
+{
+ size_t count = mActiveTracks.size();
+
+ mixer_state mixerStatus = MIXER_IDLE;
+ bool doHwPause = false;
+ bool doHwResume = false;
+
+ ALOGV("OffloadThread::prepareTracks_l active tracks %d", count);
+
+ // find out which tracks need to be processed
+ for (size_t i = 0; i < count; i++) {
+ sp<Track> t = mActiveTracks[i].promote();
+ // The track died recently
+ if (t == 0) {
+ continue;
+ }
+ Track* const track = t.get();
+ audio_track_cblk_t* cblk = track->cblk();
+ // Only consider last track started for volume and mixer state control.
+ // In theory an older track could underrun and restart after the new one starts
+ // but as we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ sp<Track> l = mLatestActiveTrack.promote();
+ bool last = l.get() == track;
+
+ if (track->isPausing()) {
+ track->setPaused();
+ if (last) {
+ if (!mHwPaused) {
+ doHwPause = true;
+ mHwPaused = true;
+ }
+ // If we were part way through writing the mixbuffer to
+ // the HAL we must save this until we resume
+ // BUG - this will be wrong if a different track is made active,
+ // in that case we want to discard the pending data in the
+ // mixbuffer and tell the client to present it again when the
+ // track is resumed
+ mPausedWriteLength = mCurrentWriteLength;
+ mPausedBytesRemaining = mBytesRemaining;
+ mBytesRemaining = 0; // stop writing
+ }
+ tracksToRemove->add(track);
+ } else if (track->framesReady() && track->isReady() &&
+ !track->isPaused() && !track->isTerminated() && !track->isStopping_2()) {
+ ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
+ if (track->mFillingUpStatus == Track::FS_FILLED) {
+ track->mFillingUpStatus = Track::FS_ACTIVE;
+ // make sure processVolume_l() will apply new volume even if 0
+ mLeftVolFloat = mRightVolFloat = -1.0;
+ if (track->mState == TrackBase::RESUMING) {
+ track->mState = TrackBase::ACTIVE;
+ if (last) {
+ if (mPausedBytesRemaining) {
+ // Need to continue write that was interrupted
+ mCurrentWriteLength = mPausedWriteLength;
+ mBytesRemaining = mPausedBytesRemaining;
+ mPausedBytesRemaining = 0;
+ }
+ if (mHwPaused) {
+ doHwResume = true;
+ mHwPaused = false;
+ // threadLoop_mix() will handle the case that we need to
+ // resume an interrupted write
+ }
+ // enable write to audio HAL
+ sleepTime = 0;
+ }
+ }
+ }
+
+ if (last) {
+ sp<Track> previousTrack = mPreviousTrack.promote();
+ if (previousTrack != 0) {
+ if (track != previousTrack.get()) {
+ // Flush any data still being written from last track
+ mBytesRemaining = 0;
+ if (mPausedBytesRemaining) {
+ // Last track was paused so we also need to flush saved
+ // mixbuffer state and invalidate track so that it will
+ // re-submit that unwritten data when it is next resumed
+ mPausedBytesRemaining = 0;
+ // Invalidate is a bit drastic - would be more efficient
+ // to have a flag to tell client that some of the
+ // previously written data was lost
+ previousTrack->invalidate();
+ }
+ // flush data already sent to the DSP if changing audio session as audio
+ // comes from a different source. Also invalidate previous track to force a
+ // seek when resuming.
+ if (previousTrack->sessionId() != track->sessionId()) {
+ previousTrack->invalidate();
+ mFlushPending = true;
+ }
+ }
+ }
+ mPreviousTrack = track;
+ // reset retry count
+ track->mRetryCount = kMaxTrackRetriesOffload;
+ mActiveTrack = t;
+ mixerStatus = MIXER_TRACKS_READY;
+ }
+ } else {
+ ALOGVV("OffloadThread: track %d s=%08x [NOT READY]", track->name(), cblk->mServer);
+ if (track->isStopping_1()) {
+ // Hardware buffer can hold a large amount of audio so we must
+ // wait for all current track's data to drain before we say
+ // that the track is stopped.
+ if (mBytesRemaining == 0) {
+ // Only start draining when all data in mixbuffer
+ // has been written
+ ALOGV("OffloadThread: underrun and STOPPING_1 -> draining, STOPPING_2");
+ track->mState = TrackBase::STOPPING_2; // so presentation completes after drain
+ // do not drain if no data was ever sent to HAL (mStandby == true)
+ if (last && !mStandby) {
+ // do not modify drain sequence if we are already draining. This happens
+ // when resuming from pause after drain.
+ if ((mDrainSequence & 1) == 0) {
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
+ mixerStatus = MIXER_DRAIN_TRACK;
+ mDrainSequence += 2;
+ }
+ if (mHwPaused) {
+ // It is possible to move from PAUSED to STOPPING_1 without
+ // a resume so we must ensure hardware is running
+ doHwResume = true;
+ mHwPaused = false;
+ }
+ }
+ }
+ } else if (track->isStopping_2()) {
+ // Drain has completed or we are in standby, signal presentation complete
+ if (!(mDrainSequence & 1) || !last || mStandby) {
+ track->mState = TrackBase::STOPPED;
+ size_t audioHALFrames =
+ (mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
+ size_t framesWritten =
+ mBytesWritten / audio_stream_frame_size(&mOutput->stream->common);
+ track->presentationComplete(framesWritten, audioHALFrames);
+ track->reset();
+ tracksToRemove->add(track);
+ }
+ } else {
+ // No buffers for this track. Give it a few chances to
+ // fill a buffer, then remove it from active list.
+ if (--(track->mRetryCount) <= 0) {
+ ALOGV("OffloadThread: BUFFER TIMEOUT: remove(%d) from active list",
+ track->name());
+ tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
+ } else if (last){
+ mixerStatus = MIXER_TRACKS_ENABLED;
+ }
+ }
+ }
+ // compute volume for this track
+ processVolume_l(track, last);
+ }
+
+ // make sure the pause/flush/resume sequence is executed in the right order.
+ // If a flush is pending and a track is active but the HW is not paused, force a HW pause
+ // before flush and then resume HW. This can happen in case of pause/flush/resume
+ // if resume is received before pause is executed.
+ if (!mStandby && (doHwPause || (mFlushPending && !mHwPaused && (count != 0)))) {
+ mOutput->stream->pause(mOutput->stream);
+ if (!doHwPause) {
+ doHwResume = true;
+ }
+ }
+ if (mFlushPending) {
+ flushHw_l();
+ mFlushPending = false;
+ }
+ if (!mStandby && doHwResume) {
+ mOutput->stream->resume(mOutput->stream);
+ }
+
+ // remove all the tracks that need to be...
+ removeTracks_l(*tracksToRemove);
+
+ return mixerStatus;
+}
+
+void AudioFlinger::OffloadThread::flushOutput_l()
+{
+ mFlushPending = true;
+}
+
+// must be called with thread mutex locked
+bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
+{
+ ALOGVV("waitingAsyncCallback_l mWriteAckSequence %d mDrainSequence %d",
+ mWriteAckSequence, mDrainSequence);
+ if (mUseAsyncWrite && ((mWriteAckSequence & 1) || (mDrainSequence & 1))) {
+ return true;
+ }
+ return false;
+}
+
+// must be called with thread mutex locked
+bool AudioFlinger::OffloadThread::shouldStandby_l()
+{
+ bool TrackPaused = false;
+
+ // do not put the HAL in standby when paused. AwesomePlayer clear the offloaded AudioTrack
+ // after a timeout and we will enter standby then.
+ if (mTracks.size() > 0) {
+ TrackPaused = mTracks[mTracks.size() - 1]->isPaused();
+ }
+
+ return !mStandby && !TrackPaused;
+}
+
+
+bool AudioFlinger::OffloadThread::waitingAsyncCallback()
+{
+ Mutex::Autolock _l(mLock);
+ return waitingAsyncCallback_l();
+}
+
+void AudioFlinger::OffloadThread::flushHw_l()
+{
+ mOutput->stream->flush(mOutput->stream);
+ // Flush anything still waiting in the mixbuffer
+ mCurrentWriteLength = 0;
+ mBytesRemaining = 0;
+ mPausedWriteLength = 0;
+ mPausedBytesRemaining = 0;
+ if (mUseAsyncWrite) {
+ // discard any pending drain or write ack by incrementing sequence
+ mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+ mDrainSequence = (mDrainSequence + 2) & ~1;
+ ALOG_ASSERT(mCallbackThread != 0);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ mCallbackThread->setDraining(mDrainSequence);
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
+ AudioFlinger::MixerThread* mainThread, audio_io_handle_t id)
+ : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->outDevice(),
+ DUPLICATING),
+ mWaitTimeMs(UINT_MAX)
+{
+ addOutputTrack(mainThread);
+}
+
+AudioFlinger::DuplicatingThread::~DuplicatingThread()
+{
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ mOutputTracks[i]->destroy();
+ }
+}
+
+void AudioFlinger::DuplicatingThread::threadLoop_mix()
+{
+ // mix buffers...
+ if (outputsReady(outputTracks)) {
+ mAudioMixer->process(AudioBufferProvider::kInvalidPTS);
+ } else {
+ memset(mMixBuffer, 0, mixBufferSize);
+ }
+ sleepTime = 0;
+ writeFrames = mNormalFrameCount;
+ mCurrentWriteLength = mixBufferSize;
+ standbyTime = systemTime() + standbyDelay;
+}
+
+void AudioFlinger::DuplicatingThread::threadLoop_sleepTime()
+{
+ if (sleepTime == 0) {
+ if (mMixerStatus == MIXER_TRACKS_ENABLED) {
+ sleepTime = activeSleepTime;
+ } else {
+ sleepTime = idleSleepTime;
+ }
+ } else if (mBytesWritten != 0) {
+ if (mMixerStatus == MIXER_TRACKS_ENABLED) {
+ writeFrames = mNormalFrameCount;
+ memset(mMixBuffer, 0, mixBufferSize);
+ } else {
+ // flush remaining overflow buffers in output tracks
+ writeFrames = 0;
+ }
+ sleepTime = 0;
+ }
+}
+
+ssize_t AudioFlinger::DuplicatingThread::threadLoop_write()
+{
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ outputTracks[i]->write(mMixBuffer, writeFrames);
+ }
+ mStandby = false;
+ return (ssize_t)mixBufferSize;
+}
+
+void AudioFlinger::DuplicatingThread::threadLoop_standby()
+{
+ // DuplicatingThread implements standby by stopping all tracks
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ outputTracks[i]->stop();
+ }
+}
+
+void AudioFlinger::DuplicatingThread::saveOutputTracks()
+{
+ outputTracks = mOutputTracks;
+}
+
+void AudioFlinger::DuplicatingThread::clearOutputTracks()
+{
+ outputTracks.clear();
+}
+
+void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread)
+{
+ Mutex::Autolock _l(mLock);
+ // FIXME explain this formula
+ size_t frameCount = (3 * mNormalFrameCount * mSampleRate) / thread->sampleRate();
+ OutputTrack *outputTrack = new OutputTrack(thread,
+ this,
+ mSampleRate,
+ mFormat,
+ mChannelMask,
+ frameCount,
+ IPCThreadState::self()->getCallingUid());
+ if (outputTrack->cblk() != NULL) {
+ thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
+ mOutputTracks.add(outputTrack);
+ ALOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread);
+ updateWaitTime_l();
+ }
+}
+
+void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread)
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ if (mOutputTracks[i]->thread() == thread) {
+ mOutputTracks[i]->destroy();
+ mOutputTracks.removeAt(i);
+ updateWaitTime_l();
+ return;
+ }
+ }
+ ALOGV("removeOutputTrack(): unkonwn thread: %p", thread);
+}
+
+// caller must hold mLock
+void AudioFlinger::DuplicatingThread::updateWaitTime_l()
+{
+ mWaitTimeMs = UINT_MAX;
+ for (size_t i = 0; i < mOutputTracks.size(); i++) {
+ sp<ThreadBase> strong = mOutputTracks[i]->thread().promote();
+ if (strong != 0) {
+ uint32_t waitTimeMs = (strong->frameCount() * 2 * 1000) / strong->sampleRate();
+ if (waitTimeMs < mWaitTimeMs) {
+ mWaitTimeMs = waitTimeMs;
+ }
+ }
+ }
+}
+
+
+bool AudioFlinger::DuplicatingThread::outputsReady(
+ const SortedVector< sp<OutputTrack> > &outputTracks)
+{
+ for (size_t i = 0; i < outputTracks.size(); i++) {
+ sp<ThreadBase> thread = outputTracks[i]->thread().promote();
+ if (thread == 0) {
+ ALOGW("DuplicatingThread::outputsReady() could not promote thread on output track %p",
+ outputTracks[i].get());
+ return false;
+ }
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ // see note at standby() declaration
+ if (playbackThread->standby() && !playbackThread->isSuspended()) {
+ ALOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(),
+ thread.get());
+ return false;
+ }
+ }
+ return true;
+}
+
+uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs() const
+{
+ return (mWaitTimeMs * 1000) / 2;
+}
+
+void AudioFlinger::DuplicatingThread::cacheParameters_l()
+{
+ // updateWaitTime_l() sets mWaitTimeMs, which affects activeSleepTimeUs(), so call it first
+ updateWaitTime_l();
+
+ MixerThread::cacheParameters_l();
+}
+
+// ----------------------------------------------------------------------------
+// Record
+// ----------------------------------------------------------------------------
+
+AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamIn *input,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_io_handle_t id,
+ audio_devices_t outDevice,
+ audio_devices_t inDevice
+#ifdef TEE_SINK
+ , const sp<NBAIO_Sink>& teeSink
+#endif
+ ) :
+ ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
+ mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
+ // mRsmpInIndex and mBufferSize set by readInputParameters()
+ mReqChannelCount(popcount(channelMask)),
+ mReqSampleRate(sampleRate)
+ // mBytesRead is only meaningful while active, and so is cleared in start()
+ // (but might be better to also clear here for dump?)
+#ifdef TEE_SINK
+ , mTeeSink(teeSink)
+#endif
+{
+ snprintf(mName, kNameLength, "AudioIn_%X", id);
+
+ readInputParameters();
+}
+
+
+AudioFlinger::RecordThread::~RecordThread()
+{
+ delete[] mRsmpInBuffer;
+ delete mResampler;
+ delete[] mRsmpOutBuffer;
+}
+
+void AudioFlinger::RecordThread::onFirstRef()
+{
+ run(mName, PRIORITY_URGENT_AUDIO);
+}
+
+status_t AudioFlinger::RecordThread::readyToRun()
+{
+ status_t status = initCheck();
+ ALOGW_IF(status != NO_ERROR,"RecordThread %p could not initialize", this);
+ return status;
+}
+
+bool AudioFlinger::RecordThread::threadLoop()
+{
+ AudioBufferProvider::Buffer buffer;
+ sp<RecordTrack> activeTrack;
+ Vector< sp<EffectChain> > effectChains;
+
+ nsecs_t lastWarning = 0;
+
+ inputStandBy();
+ {
+ Mutex::Autolock _l(mLock);
+ activeTrack = mActiveTrack;
+ acquireWakeLock_l(activeTrack != 0 ? activeTrack->uid() : -1);
+ }
+
+ // used to verify we've read at least once before evaluating how many bytes were read
+ bool readOnce = false;
+
+ // start recording
+ while (!exitPending()) {
+
+ processConfigEvents();
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+ checkForNewParameters_l();
+ if (mActiveTrack != 0 && activeTrack != mActiveTrack) {
+ SortedVector<int> tmp;
+ tmp.add(mActiveTrack->uid());
+ updateWakeLockUids_l(tmp);
+ }
+ activeTrack = mActiveTrack;
+ if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
+ standby();
+
+ if (exitPending()) {
+ break;
+ }
+
+ releaseWakeLock_l();
+ ALOGV("RecordThread: loop stopping");
+ // go to sleep
+ mWaitWorkCV.wait(mLock);
+ ALOGV("RecordThread: loop starting");
+ acquireWakeLock_l(mActiveTrack != 0 ? mActiveTrack->uid() : -1);
+ continue;
+ }
+ if (mActiveTrack != 0) {
+ if (mActiveTrack->isTerminated()) {
+ removeTrack_l(mActiveTrack);
+ mActiveTrack.clear();
+ } else if (mActiveTrack->mState == TrackBase::PAUSING) {
+ standby();
+ mActiveTrack.clear();
+ mStartStopCond.broadcast();
+ } else if (mActiveTrack->mState == TrackBase::RESUMING) {
+ if (mReqChannelCount != mActiveTrack->channelCount()) {
+ mActiveTrack.clear();
+ mStartStopCond.broadcast();
+ } else if (readOnce) {
+ // record start succeeds only if first read from audio input
+ // succeeds
+ if (mBytesRead >= 0) {
+ mActiveTrack->mState = TrackBase::ACTIVE;
+ } else {
+ mActiveTrack.clear();
+ }
+ mStartStopCond.broadcast();
+ }
+ mStandby = false;
+ }
+ }
+
+ lockEffectChains_l(effectChains);
+ }
+
+ if (mActiveTrack != 0) {
+ if (mActiveTrack->mState != TrackBase::ACTIVE &&
+ mActiveTrack->mState != TrackBase::RESUMING) {
+ unlockEffectChains(effectChains);
+ usleep(kRecordThreadSleepUs);
+ continue;
+ }
+ for (size_t i = 0; i < effectChains.size(); i ++) {
+ effectChains[i]->process_l();
+ }
+
+ buffer.frameCount = mFrameCount;
+ status_t status = mActiveTrack->getNextBuffer(&buffer);
+ if (status == NO_ERROR) {
+ readOnce = true;
+ size_t framesOut = buffer.frameCount;
+ if (mResampler == NULL) {
+ // no resampling
+ while (framesOut) {
+ size_t framesIn = mFrameCount - mRsmpInIndex;
+ if (framesIn) {
+ int8_t *src = (int8_t *)mRsmpInBuffer + mRsmpInIndex * mFrameSize;
+ int8_t *dst = buffer.i8 + (buffer.frameCount - framesOut) *
+ mActiveTrack->mFrameSize;
+ if (framesIn > framesOut)
+ framesIn = framesOut;
+ mRsmpInIndex += framesIn;
+ framesOut -= framesIn;
+ if (mChannelCount == mReqChannelCount) {
+ memcpy(dst, src, framesIn * mFrameSize);
+ } else {
+ if (mChannelCount == 1) {
+ upmix_to_stereo_i16_from_mono_i16((int16_t *)dst,
+ (int16_t *)src, framesIn);
+ } else {
+ downmix_to_mono_i16_from_stereo_i16((int16_t *)dst,
+ (int16_t *)src, framesIn);
+ }
+ }
+ }
+ if (framesOut && mFrameCount == mRsmpInIndex) {
+ void *readInto;
+ if (framesOut == mFrameCount && mChannelCount == mReqChannelCount) {
+ readInto = buffer.raw;
+ framesOut = 0;
+ } else {
+ readInto = mRsmpInBuffer;
+ mRsmpInIndex = 0;
+ }
+ mBytesRead = mInput->stream->read(mInput->stream, readInto,
+ mBufferSize);
+ if (mBytesRead <= 0) {
+ if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE))
+ {
+ ALOGE("Error reading audio input");
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ inputStandBy();
+ usleep(kRecordThreadSleepUs);
+ }
+ mRsmpInIndex = mFrameCount;
+ framesOut = 0;
+ buffer.frameCount = 0;
+ }
+#ifdef TEE_SINK
+ else if (mTeeSink != 0) {
+ (void) mTeeSink->write(readInto,
+ mBytesRead >> Format_frameBitShift(mTeeSink->format()));
+ }
+#endif
+ }
+ }
+ } else {
+ // resampling
+
+ // resampler accumulates, but we only have one source track
+ memset(mRsmpOutBuffer, 0, framesOut * FCC_2 * sizeof(int32_t));
+ // alter output frame count as if we were expecting stereo samples
+ if (mChannelCount == 1 && mReqChannelCount == 1) {
+ framesOut >>= 1;
+ }
+ mResampler->resample(mRsmpOutBuffer, framesOut,
+ this /* AudioBufferProvider* */);
+ // ditherAndClamp() works as long as all buffers returned by
+ // mActiveTrack->getNextBuffer() are 32 bit aligned which should be always true.
+ if (mChannelCount == 2 && mReqChannelCount == 1) {
+ // temporarily type pun mRsmpOutBuffer from Q19.12 to int16_t
+ ditherAndClamp(mRsmpOutBuffer, mRsmpOutBuffer, framesOut);
+ // the resampler always outputs stereo samples:
+ // do post stereo to mono conversion
+ downmix_to_mono_i16_from_stereo_i16(buffer.i16, (int16_t *)mRsmpOutBuffer,
+ framesOut);
+ } else {
+ ditherAndClamp((int32_t *)buffer.raw, mRsmpOutBuffer, framesOut);
+ }
+ // now done with mRsmpOutBuffer
+
+ }
+ if (mFramestoDrop == 0) {
+ mActiveTrack->releaseBuffer(&buffer);
+ } else {
+ if (mFramestoDrop > 0) {
+ mFramestoDrop -= buffer.frameCount;
+ if (mFramestoDrop <= 0) {
+ clearSyncStartEvent();
+ }
+ } else {
+ mFramestoDrop += buffer.frameCount;
+ if (mFramestoDrop >= 0 || mSyncStartEvent == 0 ||
+ mSyncStartEvent->isCancelled()) {
+ ALOGW("Synced record %s, session %d, trigger session %d",
+ (mFramestoDrop >= 0) ? "timed out" : "cancelled",
+ mActiveTrack->sessionId(),
+ (mSyncStartEvent != 0) ? mSyncStartEvent->triggerSession() : 0);
+ clearSyncStartEvent();
+ }
+ }
+ }
+ mActiveTrack->clearOverflow();
+ }
+ // client isn't retrieving buffers fast enough
+ else {
+ if (!mActiveTrack->setOverflow()) {
+ nsecs_t now = systemTime();
+ if ((now - lastWarning) > kWarningThrottleNs) {
+ ALOGW("RecordThread: buffer overflow");
+ lastWarning = now;
+ }
+ }
+ // Release the processor for a while before asking for a new buffer.
+ // This will give the application more chance to read from the buffer and
+ // clear the overflow.
+ usleep(kRecordThreadSleepUs);
+ }
+ }
+ // enable changes in effect chain
+ unlockEffectChains(effectChains);
+ effectChains.clear();
+ }
+
+ standby();
+
+ {
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ track->invalidate();
+ }
+ mActiveTrack.clear();
+ mStartStopCond.broadcast();
+ }
+
+ releaseWakeLock();
+
+ ALOGV("RecordThread %p exiting", this);
+ return false;
+}
+
+void AudioFlinger::RecordThread::standby()
+{
+ if (!mStandby) {
+ inputStandBy();
+ mStandby = true;
+ }
+}
+
+void AudioFlinger::RecordThread::inputStandBy()
+{
+ mInput->stream->common.standby(&mInput->stream->common);
+}
+
+sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l(
+ const sp<AudioFlinger::Client>& client,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ int sessionId,
+ int uid,
+ IAudioFlinger::track_flags_t *flags,
+ pid_t tid,
+ status_t *status)
+{
+ sp<RecordTrack> track;
+ status_t lStatus;
+
+ lStatus = initCheck();
+ if (lStatus != NO_ERROR) {
+ ALOGE("createRecordTrack_l() audio driver not initialized");
+ goto Exit;
+ }
+ // client expresses a preference for FAST, but we get the final say
+ if (*flags & IAudioFlinger::TRACK_FAST) {
+ if (
+ // use case: callback handler and frame count is default or at least as large as HAL
+ (
+ (tid != -1) &&
+ ((frameCount == 0) ||
+ (frameCount >= mFrameCount))
+ ) &&
+ // FIXME when record supports non-PCM data, also check for audio_is_linear_pcm(format)
+ // mono or stereo
+ ( (channelMask == AUDIO_CHANNEL_OUT_MONO) ||
+ (channelMask == AUDIO_CHANNEL_OUT_STEREO) ) &&
+ // hardware sample rate
+ (sampleRate == mSampleRate) &&
+ // record thread has an associated fast recorder
+ hasFastRecorder()
+ // FIXME test that RecordThread for this fast track has a capable output HAL
+ // FIXME add a permission test also?
+ ) {
+ // if frameCount not specified, then it defaults to fast recorder (HAL) frame count
+ if (frameCount == 0) {
+ frameCount = mFrameCount * kFastTrackMultiplier;
+ }
+ ALOGV("AUDIO_INPUT_FLAG_FAST accepted: frameCount=%d mFrameCount=%d",
+ frameCount, mFrameCount);
+ } else {
+ ALOGV("AUDIO_INPUT_FLAG_FAST denied: frameCount=%d "
+ "mFrameCount=%d format=%d isLinear=%d channelMask=%#x sampleRate=%u mSampleRate=%u "
+ "hasFastRecorder=%d tid=%d",
+ frameCount, mFrameCount, format,
+ audio_is_linear_pcm(format),
+ channelMask, sampleRate, mSampleRate, hasFastRecorder(), tid);
+ *flags &= ~IAudioFlinger::TRACK_FAST;
+ // For compatibility with AudioRecord calculation, buffer depth is forced
+ // to be at least 2 x the record thread frame count and cover audio hardware latency.
+ // This is probably too conservative, but legacy application code may depend on it.
+ // If you change this calculation, also review the start threshold which is related.
+ uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream);
+ size_t mNormalFrameCount = 2048; // FIXME
+ uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+ if (minBufCount < 2) {
+ minBufCount = 2;
+ }
+ size_t minFrameCount = mNormalFrameCount * minBufCount;
+ if (frameCount < minFrameCount) {
+ frameCount = minFrameCount;
+ }
+ }
+ }
+
+ // FIXME use flags and tid similar to createTrack_l()
+
+ { // scope for mLock
+ Mutex::Autolock _l(mLock);
+
+ track = new RecordTrack(this, client, sampleRate,
+ format, channelMask, frameCount, sessionId, uid);
+
+ if (track->getCblk() == 0) {
+ ALOGE("createRecordTrack_l() no control block");
+ lStatus = NO_MEMORY;
+ track.clear();
+ goto Exit;
+ }
+ mTracks.add(track);
+
+ // disable AEC and NS if the device is a BT SCO headset supporting those pre processings
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
+ setEffectSuspended_l(FX_IID_AEC, suspend, sessionId);
+ setEffectSuspended_l(FX_IID_NS, suspend, sessionId);
+
+ if ((*flags & IAudioFlinger::TRACK_FAST) && (tid != -1)) {
+ pid_t callingPid = IPCThreadState::self()->getCallingPid();
+ // we don't have CAP_SYS_NICE, nor do we want to have it as it's too powerful,
+ // so ask activity manager to do this on our behalf
+ sendPrioConfigEvent_l(callingPid, tid, kPriorityAudioApp);
+ }
+ }
+ lStatus = NO_ERROR;
+
+Exit:
+ if (status) {
+ *status = lStatus;
+ }
+ return track;
+}
+
+status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrack,
+ AudioSystem::sync_event_t event,
+ int triggerSession)
+{
+ ALOGV("RecordThread::start event %d, triggerSession %d", event, triggerSession);
+ sp<ThreadBase> strongMe = this;
+ status_t status = NO_ERROR;
+
+ if (event == AudioSystem::SYNC_EVENT_NONE) {
+ clearSyncStartEvent();
+ } else if (event != AudioSystem::SYNC_EVENT_SAME) {
+ mSyncStartEvent = mAudioFlinger->createSyncEvent(event,
+ triggerSession,
+ recordTrack->sessionId(),
+ syncStartEventCallback,
+ this);
+ // Sync event can be cancelled by the trigger session if the track is not in a
+ // compatible state in which case we start record immediately
+ if (mSyncStartEvent->isCancelled()) {
+ clearSyncStartEvent();
+ } else {
+ // do not wait for the event for more than AudioSystem::kSyncRecordStartTimeOutMs
+ mFramestoDrop = - ((AudioSystem::kSyncRecordStartTimeOutMs * mReqSampleRate) / 1000);
+ }
+ }
+
+ {
+ AutoMutex lock(mLock);
+ if (mActiveTrack != 0) {
+ if (recordTrack != mActiveTrack.get()) {
+ status = -EBUSY;
+ } else if (mActiveTrack->mState == TrackBase::PAUSING) {
+ mActiveTrack->mState = TrackBase::ACTIVE;
+ }
+ return status;
+ }
+
+ recordTrack->mState = TrackBase::IDLE;
+ mActiveTrack = recordTrack;
+ mLock.unlock();
+ status_t status = AudioSystem::startInput(mId);
+ mLock.lock();
+ if (status != NO_ERROR) {
+ mActiveTrack.clear();
+ clearSyncStartEvent();
+ return status;
+ }
+ mRsmpInIndex = mFrameCount;
+ mBytesRead = 0;
+ if (mResampler != NULL) {
+ mResampler->reset();
+ }
+ mActiveTrack->mState = TrackBase::RESUMING;
+ // signal thread to start
+ ALOGV("Signal record thread");
+ mWaitWorkCV.broadcast();
+ // do not wait for mStartStopCond if exiting
+ if (exitPending()) {
+ mActiveTrack.clear();
+ status = INVALID_OPERATION;
+ goto startError;
+ }
+ mStartStopCond.wait(mLock);
+ if (mActiveTrack == 0) {
+ ALOGV("Record failed to start");
+ status = BAD_VALUE;
+ goto startError;
+ }
+ ALOGV("Record started OK");
+ return status;
+ }
+
+startError:
+ AudioSystem::stopInput(mId);
+ clearSyncStartEvent();
+ return status;
+}
+
+void AudioFlinger::RecordThread::clearSyncStartEvent()
+{
+ if (mSyncStartEvent != 0) {
+ mSyncStartEvent->cancel();
+ }
+ mSyncStartEvent.clear();
+ mFramestoDrop = 0;
+}
+
+void AudioFlinger::RecordThread::syncStartEventCallback(const wp<SyncEvent>& event)
+{
+ sp<SyncEvent> strongEvent = event.promote();
+
+ if (strongEvent != 0) {
+ RecordThread *me = (RecordThread *)strongEvent->cookie();
+ me->handleSyncStartEvent(strongEvent);
+ }
+}
+
+void AudioFlinger::RecordThread::handleSyncStartEvent(const sp<SyncEvent>& event)
+{
+ if (event == mSyncStartEvent) {
+ // TODO: use actual buffer filling status instead of 2 buffers when info is available
+ // from audio HAL
+ mFramestoDrop = mFrameCount * 2;
+ }
+}
+
+bool AudioFlinger::RecordThread::stop(RecordThread::RecordTrack* recordTrack) {
+ ALOGV("RecordThread::stop");
+ AutoMutex _l(mLock);
+ if (recordTrack != mActiveTrack.get() || recordTrack->mState == TrackBase::PAUSING) {
+ return false;
+ }
+ recordTrack->mState = TrackBase::PAUSING;
+ // do not wait for mStartStopCond if exiting
+ if (exitPending()) {
+ return true;
+ }
+ mStartStopCond.wait(mLock);
+ // if we have been restarted, recordTrack == mActiveTrack.get() here
+ if (exitPending() || recordTrack != mActiveTrack.get()) {
+ ALOGV("Record stopped OK");
+ return true;
+ }
+ return false;
+}
+
+bool AudioFlinger::RecordThread::isValidSyncEvent(const sp<SyncEvent>& event) const
+{
+ return false;
+}
+
+status_t AudioFlinger::RecordThread::setSyncEvent(const sp<SyncEvent>& event)
+{
+#if 0 // This branch is currently dead code, but is preserved in case it will be needed in future
+ if (!isValidSyncEvent(event)) {
+ return BAD_VALUE;
+ }
+
+ int eventSession = event->triggerSession();
+ status_t ret = NAME_NOT_FOUND;
+
+ Mutex::Autolock _l(mLock);
+
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ if (eventSession == track->sessionId()) {
+ (void) track->setSyncEvent(event);
+ ret = NO_ERROR;
+ }
+ }
+ return ret;
+#else
+ return BAD_VALUE;
+#endif
+}
+
+// destroyTrack_l() must be called with ThreadBase::mLock held
+void AudioFlinger::RecordThread::destroyTrack_l(const sp<RecordTrack>& track)
+{
+ track->terminate();
+ track->mState = TrackBase::STOPPED;
+ // active tracks are removed by threadLoop()
+ if (mActiveTrack != track) {
+ removeTrack_l(track);
+ }
+}
+
+void AudioFlinger::RecordThread::removeTrack_l(const sp<RecordTrack>& track)
+{
+ mTracks.remove(track);
+ // need anything related to effects here?
+}
+
+void AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args)
+{
+ dumpInternals(fd, args);
+ dumpTracks(fd, args);
+ dumpEffectChains(fd, args);
+}
+
+void AudioFlinger::RecordThread::dumpInternals(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "\nInput thread %p internals\n", this);
+ result.append(buffer);
+
+ if (mActiveTrack != 0) {
+ snprintf(buffer, SIZE, "In index: %zu\n", mRsmpInIndex);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Buffer size: %zu bytes\n", mBufferSize);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Resampling: %d\n", (mResampler != NULL));
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Out channel count: %u\n", mReqChannelCount);
+ result.append(buffer);
+ snprintf(buffer, SIZE, "Out sample rate: %u\n", mReqSampleRate);
+ result.append(buffer);
+ } else {
+ result.append("No active record client\n");
+ }
+
+ write(fd, result.string(), result.size());
+
+ dumpBase(fd, args);
+}
+
+void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args)
+{
+ const size_t SIZE = 256;
+ char buffer[SIZE];
+ String8 result;
+
+ snprintf(buffer, SIZE, "Input thread %p tracks\n", this);
+ result.append(buffer);
+ RecordTrack::appendDumpHeader(result);
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ sp<RecordTrack> track = mTracks[i];
+ if (track != 0) {
+ track->dump(buffer, SIZE);
+ result.append(buffer);
+ }
+ }
+
+ if (mActiveTrack != 0) {
+ snprintf(buffer, SIZE, "\nInput thread %p active tracks\n", this);
+ result.append(buffer);
+ RecordTrack::appendDumpHeader(result);
+ mActiveTrack->dump(buffer, SIZE);
+ result.append(buffer);
+
+ }
+ write(fd, result.string(), result.size());
+}
+
+// AudioBufferProvider interface
+status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts)
+{
+ size_t framesReq = buffer->frameCount;
+ size_t framesReady = mFrameCount - mRsmpInIndex;
+ int channelCount;
+
+ if (framesReady == 0) {
+ mBytesRead = mInput->stream->read(mInput->stream, mRsmpInBuffer, mBufferSize);
+ if (mBytesRead <= 0) {
+ if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE)) {
+ ALOGE("RecordThread::getNextBuffer() Error reading audio input");
+ // Force input into standby so that it tries to
+ // recover at next read attempt
+ inputStandBy();
+ usleep(kRecordThreadSleepUs);
+ }
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ return NOT_ENOUGH_DATA;
+ }
+ mRsmpInIndex = 0;
+ framesReady = mFrameCount;
+ }
+
+ if (framesReq > framesReady) {
+ framesReq = framesReady;
+ }
+
+ if (mChannelCount == 1 && mReqChannelCount == 2) {
+ channelCount = 1;
+ } else {
+ channelCount = 2;
+ }
+ buffer->raw = mRsmpInBuffer + mRsmpInIndex * channelCount;
+ buffer->frameCount = framesReq;
+ return NO_ERROR;
+}
+
+// AudioBufferProvider interface
+void AudioFlinger::RecordThread::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+{
+ mRsmpInIndex += buffer->frameCount;
+ buffer->frameCount = 0;
+}
+
+bool AudioFlinger::RecordThread::checkForNewParameters_l()
+{
+ bool reconfig = false;
+
+ while (!mNewParameters.isEmpty()) {
+ status_t status = NO_ERROR;
+ String8 keyValuePair = mNewParameters[0];
+ AudioParameter param = AudioParameter(keyValuePair);
+ int value;
+ audio_format_t reqFormat = mFormat;
+ uint32_t reqSamplingRate = mReqSampleRate;
+ uint32_t reqChannelCount = mReqChannelCount;
+
+ if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
+ reqSamplingRate = value;
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) {
+ if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) {
+ status = BAD_VALUE;
+ } else {
+ reqFormat = (audio_format_t) value;
+ reconfig = true;
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
+ reqChannelCount = popcount(value);
+ reconfig = true;
+ }
+ if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
+ // do not accept frame count changes if tracks are open as the track buffer
+ // size depends on frame count and correct behavior would not be guaranteed
+ // if frame count is changed after track creation
+ if (mActiveTrack != 0) {
+ status = INVALID_OPERATION;
+ } else {
+ 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.
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setDevice_l(value);
+ }
+
+ // store input device and output device but do not forward output device to audio HAL.
+ // Note that status is ignored by the caller for output device
+ // (see AudioFlinger::setParameters()
+ if (audio_is_output_devices(value)) {
+ mOutDevice = value;
+ status = BAD_VALUE;
+ } else {
+ mInDevice = value;
+ // disable AEC and NS if the device is a BT SCO headset supporting those
+ // pre processings
+ if (mTracks.size() > 0) {
+ bool suspend = audio_is_bluetooth_sco_device(mInDevice) &&
+ mAudioFlinger->btNrecIsOff();
+ for (size_t i = 0; i < mTracks.size(); i++) {
+ sp<RecordTrack> track = mTracks[i];
+ setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId());
+ setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId());
+ }
+ }
+ }
+ }
+ if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR &&
+ mAudioSource != (audio_source_t)value) {
+ // forward device change to effects that have requested to be
+ // aware of attached audio device.
+ for (size_t i = 0; i < mEffectChains.size(); i++) {
+ mEffectChains[i]->setAudioSource_l((audio_source_t)value);
+ }
+ mAudioSource = (audio_source_t)value;
+ }
+ if (status == NO_ERROR) {
+ status = mInput->stream->common.set_parameters(&mInput->stream->common,
+ keyValuePair.string());
+ if (status == INVALID_OPERATION) {
+ inputStandBy();
+ status = mInput->stream->common.set_parameters(&mInput->stream->common,
+ keyValuePair.string());
+ }
+ if (reconfig) {
+ if (status == BAD_VALUE &&
+ reqFormat == mInput->stream->common.get_format(&mInput->stream->common) &&
+ reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
+ (mInput->stream->common.get_sample_rate(&mInput->stream->common)
+ <= (2 * reqSamplingRate)) &&
+ popcount(mInput->stream->common.get_channels(&mInput->stream->common))
+ <= FCC_2 &&
+ (reqChannelCount <= FCC_2)) {
+ status = NO_ERROR;
+ }
+ if (status == NO_ERROR) {
+ readInputParameters();
+ sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED);
+ }
+ }
+ }
+
+ mNewParameters.removeAt(0);
+
+ mParamStatus = status;
+ mParamCond.signal();
+ // wait for condition with time out in case the thread calling ThreadBase::setParameters()
+ // already timed out waiting for the status and will never signal the condition.
+ mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs);
+ }
+ return reconfig;
+}
+
+String8 AudioFlinger::RecordThread::getParameters(const String8& keys)
+{
+ Mutex::Autolock _l(mLock);
+ if (initCheck() != NO_ERROR) {
+ return String8();
+ }
+
+ char *s = mInput->stream->common.get_parameters(&mInput->stream->common, keys.string());
+ const String8 out_s8(s);
+ free(s);
+ return out_s8;
+}
+
+void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param) {
+ AudioSystem::OutputDescriptor desc;
+ void *param2 = NULL;
+
+ switch (event) {
+ case AudioSystem::INPUT_OPENED:
+ case AudioSystem::INPUT_CONFIG_CHANGED:
+ desc.channelMask = mChannelMask;
+ desc.samplingRate = mSampleRate;
+ desc.format = mFormat;
+ desc.frameCount = mFrameCount;
+ desc.latency = 0;
+ param2 = &desc;
+ break;
+
+ case AudioSystem::INPUT_CLOSED:
+ default:
+ break;
+ }
+ mAudioFlinger->audioConfigChanged_l(event, mId, param2);
+}
+
+void AudioFlinger::RecordThread::readInputParameters()
+{
+ delete[] mRsmpInBuffer;
+ // mRsmpInBuffer is always assigned a new[] below
+ delete[] mRsmpOutBuffer;
+ mRsmpOutBuffer = NULL;
+ delete mResampler;
+ mResampler = NULL;
+
+ mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
+ mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
+ mChannelCount = popcount(mChannelMask);
+ mFormat = mInput->stream->common.get_format(&mInput->stream->common);
+ if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
+ ALOGE("HAL format %d not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
+ }
+ mFrameSize = audio_stream_frame_size(&mInput->stream->common);
+ mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
+ mFrameCount = mBufferSize / mFrameSize;
+ mRsmpInBuffer = new int16_t[mFrameCount * mChannelCount];
+
+ if (mSampleRate != mReqSampleRate && mChannelCount <= FCC_2 && mReqChannelCount <= FCC_2)
+ {
+ int channelCount;
+ // optimization: if mono to mono, use the resampler in stereo to stereo mode to avoid
+ // stereo to mono post process as the resampler always outputs stereo.
+ if (mChannelCount == 1 && mReqChannelCount == 2) {
+ channelCount = 1;
+ } else {
+ channelCount = 2;
+ }
+ mResampler = AudioResampler::create(16, channelCount, mReqSampleRate);
+ mResampler->setSampleRate(mSampleRate);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN, AudioMixer::UNITY_GAIN);
+ mRsmpOutBuffer = new int32_t[mFrameCount * FCC_2];
+
+ // optmization: if mono to mono, alter input frame count as if we were inputing
+ // stereo samples
+ if (mChannelCount == 1 && mReqChannelCount == 1) {
+ mFrameCount >>= 1;
+ }
+
+ }
+ mRsmpInIndex = mFrameCount;
+}
+
+unsigned int AudioFlinger::RecordThread::getInputFramesLost()
+{
+ Mutex::Autolock _l(mLock);
+ if (initCheck() != NO_ERROR) {
+ return 0;
+ }
+
+ return mInput->stream->get_input_frames_lost(mInput->stream);
+}
+
+uint32_t AudioFlinger::RecordThread::hasAudioSession(int sessionId) const
+{
+ Mutex::Autolock _l(mLock);
+ uint32_t result = 0;
+ if (getEffectChain_l(sessionId) != 0) {
+ result = EFFECT_SESSION;
+ }
+
+ for (size_t i = 0; i < mTracks.size(); ++i) {
+ if (sessionId == mTracks[i]->sessionId()) {
+ result |= TRACK_SESSION;
+ break;
+ }
+ }
+
+ return result;
+}
+
+KeyedVector<int, bool> AudioFlinger::RecordThread::sessionIds() const
+{
+ KeyedVector<int, bool> ids;
+ Mutex::Autolock _l(mLock);
+ for (size_t j = 0; j < mTracks.size(); ++j) {
+ sp<RecordThread::RecordTrack> track = mTracks[j];
+ int sessionId = track->sessionId();
+ if (ids.indexOfKey(sessionId) < 0) {
+ ids.add(sessionId, true);
+ }
+ }
+ return ids;
+}
+
+AudioFlinger::AudioStreamIn* AudioFlinger::RecordThread::clearInput()
+{
+ Mutex::Autolock _l(mLock);
+ AudioStreamIn *input = mInput;
+ mInput = NULL;
+ return input;
+}
+
+// this method must always be called either with ThreadBase mLock held or inside the thread loop
+audio_stream_t* AudioFlinger::RecordThread::stream() const
+{
+ if (mInput == NULL) {
+ return NULL;
+ }
+ return &mInput->stream->common;
+}
+
+status_t AudioFlinger::RecordThread::addEffectChain_l(const sp<EffectChain>& chain)
+{
+ // only one chain per input thread
+ if (mEffectChains.size() != 0) {
+ return INVALID_OPERATION;
+ }
+ ALOGV("addEffectChain_l() %p on thread %p", chain.get(), this);
+
+ chain->setInBuffer(NULL);
+ chain->setOutBuffer(NULL);
+
+ checkSuspendOnAddEffectChain_l(chain);
+
+ mEffectChains.add(chain);
+
+ return NO_ERROR;
+}
+
+size_t AudioFlinger::RecordThread::removeEffectChain_l(const sp<EffectChain>& chain)
+{
+ ALOGV("removeEffectChain_l() %p from thread %p", chain.get(), this);
+ ALOGW_IF(mEffectChains.size() != 1,
+ "removeEffectChain_l() %p invalid chain size %d on thread %p",
+ chain.get(), mEffectChains.size(), this);
+ if (mEffectChains.size() == 1) {
+ mEffectChains.removeAt(0);
+ }
+ return 0;
+}
+
+}; // namespace android
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
new file mode 100644
index 0000000..a2fb874
--- /dev/null
+++ b/services/audioflinger/Threads.h
@@ -0,0 +1,964 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+class ThreadBase : public Thread {
+public:
+
+#include "TrackBase.h"
+
+ enum type_t {
+ MIXER, // Thread class is MixerThread
+ DIRECT, // Thread class is DirectOutputThread
+ DUPLICATING, // Thread class is DuplicatingThread
+ RECORD, // Thread class is RecordThread
+ OFFLOAD // Thread class is OffloadThread
+ };
+
+ ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id,
+ audio_devices_t outDevice, audio_devices_t inDevice, type_t type);
+ virtual ~ThreadBase();
+
+ void dumpBase(int fd, const Vector<String16>& args);
+ void dumpEffectChains(int fd, const Vector<String16>& args);
+
+ void clearPowerManager();
+
+ // base for record and playback
+ enum {
+ CFG_EVENT_IO,
+ CFG_EVENT_PRIO
+ };
+
+ class ConfigEvent {
+ public:
+ ConfigEvent(int type) : mType(type) {}
+ virtual ~ConfigEvent() {}
+
+ int type() const { return mType; }
+
+ virtual void dump(char *buffer, size_t size) = 0;
+
+ private:
+ const int mType;
+ };
+
+ class IoConfigEvent : public ConfigEvent {
+ public:
+ IoConfigEvent(int event, int param) :
+ ConfigEvent(CFG_EVENT_IO), mEvent(event), mParam(event) {}
+ virtual ~IoConfigEvent() {}
+
+ int event() const { return mEvent; }
+ int param() const { return mParam; }
+
+ virtual void dump(char *buffer, size_t size) {
+ snprintf(buffer, size, "IO event: event %d, param %d\n", mEvent, mParam);
+ }
+
+ private:
+ const int mEvent;
+ const int mParam;
+ };
+
+ class PrioConfigEvent : public ConfigEvent {
+ public:
+ PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) :
+ ConfigEvent(CFG_EVENT_PRIO), mPid(pid), mTid(tid), mPrio(prio) {}
+ virtual ~PrioConfigEvent() {}
+
+ pid_t pid() const { return mPid; }
+ pid_t tid() const { return mTid; }
+ int32_t prio() const { return mPrio; }
+
+ virtual void dump(char *buffer, size_t size) {
+ snprintf(buffer, size, "Prio event: pid %d, tid %d, prio %d\n", mPid, mTid, mPrio);
+ }
+
+ private:
+ const pid_t mPid;
+ const pid_t mTid;
+ const int32_t mPrio;
+ };
+
+
+ class PMDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ PMDeathRecipient(const wp<ThreadBase>& thread) : mThread(thread) {}
+ virtual ~PMDeathRecipient() {}
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ PMDeathRecipient(const PMDeathRecipient&);
+ PMDeathRecipient& operator = (const PMDeathRecipient&);
+
+ wp<ThreadBase> mThread;
+ };
+
+ virtual status_t initCheck() const = 0;
+
+ // static externally-visible
+ type_t type() const { return mType; }
+ audio_io_handle_t id() const { return mId;}
+
+ // dynamic externally-visible
+ uint32_t sampleRate() const { return mSampleRate; }
+ uint32_t channelCount() const { return mChannelCount; }
+ audio_channel_mask_t channelMask() const { return mChannelMask; }
+ audio_format_t format() const { return mFormat; }
+ // Called by AudioFlinger::frameCount(audio_io_handle_t output) and effects,
+ // and returns the [normal mix] buffer's frame count.
+ virtual size_t frameCount() const = 0;
+ size_t frameSize() const { return mFrameSize; }
+
+ // Should be "virtual status_t requestExitAndWait()" and override same
+ // method in Thread, but Thread::requestExitAndWait() is not yet virtual.
+ void exit();
+ virtual bool checkForNewParameters_l() = 0;
+ virtual status_t setParameters(const String8& keyValuePairs);
+ virtual String8 getParameters(const String8& keys) = 0;
+ virtual void audioConfigChanged_l(int event, int param = 0) = 0;
+ void sendIoConfigEvent(int event, int param = 0);
+ void sendIoConfigEvent_l(int event, int param = 0);
+ void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
+ void processConfigEvents();
+
+ // see note at declaration of mStandby, mOutDevice and mInDevice
+ bool standby() const { return mStandby; }
+ audio_devices_t outDevice() const { return mOutDevice; }
+ audio_devices_t inDevice() const { return mInDevice; }
+
+ virtual audio_stream_t* stream() const = 0;
+
+ 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);
+ void disconnectEffect(const sp< EffectModule>& effect,
+ EffectHandle *handle,
+ bool unpinIfLast);
+
+ // return values for hasAudioSession (bit field)
+ enum effect_state {
+ EFFECT_SESSION = 0x1, // the audio session corresponds to at least one
+ // effect
+ TRACK_SESSION = 0x2 // the audio session corresponds to at least one
+ // track
+ };
+
+ // get effect chain corresponding to session Id.
+ sp<EffectChain> getEffectChain(int sessionId);
+ // same as getEffectChain() but must be called with ThreadBase mutex locked
+ sp<EffectChain> getEffectChain_l(int sessionId) const;
+ // add an effect chain to the chain list (mEffectChains)
+ virtual status_t addEffectChain_l(const sp<EffectChain>& chain) = 0;
+ // remove an effect chain from the chain list (mEffectChains)
+ virtual size_t removeEffectChain_l(const sp<EffectChain>& chain) = 0;
+ // lock all effect chains Mutexes. Must be called before releasing the
+ // ThreadBase mutex before processing the mixer and effects. This guarantees the
+ // integrity of the chains during the process.
+ // Also sets the parameter 'effectChains' to current value of mEffectChains.
+ void lockEffectChains_l(Vector< sp<EffectChain> >& effectChains);
+ // unlock effect chains after process
+ void unlockEffectChains(const Vector< sp<EffectChain> >& effectChains);
+ // get a copy of mEffectChains vector
+ Vector< sp<EffectChain> > getEffectChains_l() const { return mEffectChains; };
+ // set audio mode to all effect chains
+ void setMode(audio_mode_t mode);
+ // get effect module with corresponding ID on specified audio session
+ sp<AudioFlinger::EffectModule> getEffect(int sessionId, int effectId);
+ sp<AudioFlinger::EffectModule> getEffect_l(int sessionId, int effectId);
+ // add and effect module. Also creates the effect chain is none exists for
+ // the effects audio session
+ status_t addEffect_l(const sp< EffectModule>& effect);
+ // remove and effect module. Also removes the effect chain is this was the last
+ // effect
+ void removeEffect_l(const sp< EffectModule>& effect);
+ // detach all tracks connected to an auxiliary effect
+ virtual void detachAuxEffect_l(int effectId) {}
+ // returns either EFFECT_SESSION if effects on this audio session exist in one
+ // chain, or TRACK_SESSION if tracks on this audio session exist, or both
+ virtual uint32_t hasAudioSession(int sessionId) const = 0;
+ // the value returned by default implementation is not important as the
+ // strategy is only meaningful for PlaybackThread which implements this method
+ virtual uint32_t getStrategyForSession_l(int sessionId) { return 0; }
+
+ // suspend or restore effect according to the type of effect passed. a NULL
+ // type pointer means suspend all effects in the session
+ void setEffectSuspended(const effect_uuid_t *type,
+ bool suspend,
+ int sessionId = AUDIO_SESSION_OUTPUT_MIX);
+ // check if some effects must be suspended/restored when an effect is enabled
+ // or disabled
+ void checkSuspendOnEffectEnabled(const sp<EffectModule>& effect,
+ bool enabled,
+ int sessionId = AUDIO_SESSION_OUTPUT_MIX);
+ void checkSuspendOnEffectEnabled_l(const sp<EffectModule>& effect,
+ bool enabled,
+ int sessionId = AUDIO_SESSION_OUTPUT_MIX);
+
+ virtual status_t setSyncEvent(const sp<SyncEvent>& event) = 0;
+ virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const = 0;
+
+
+ mutable Mutex mLock;
+
+protected:
+
+ // entry describing an effect being suspended in mSuspendedSessions keyed vector
+ class SuspendedSessionDesc : public RefBase {
+ public:
+ SuspendedSessionDesc() : mRefCount(0) {}
+
+ int mRefCount; // number of active suspend requests
+ effect_uuid_t mType; // effect type UUID
+ };
+
+ void acquireWakeLock(int uid = -1);
+ void acquireWakeLock_l(int uid = -1);
+ void releaseWakeLock();
+ void releaseWakeLock_l();
+ void updateWakeLockUids(const SortedVector<int> &uids);
+ void updateWakeLockUids_l(const SortedVector<int> &uids);
+ void getPowerManager_l();
+ void setEffectSuspended_l(const effect_uuid_t *type,
+ bool suspend,
+ int sessionId);
+ // updated mSuspendedSessions when an effect suspended or restored
+ void updateSuspendedSessions_l(const effect_uuid_t *type,
+ bool suspend,
+ int sessionId);
+ // check if some effects must be suspended when an effect chain is added
+ void checkSuspendOnAddEffectChain_l(const sp<EffectChain>& chain);
+
+ String16 getWakeLockTag();
+
+ virtual void preExit() { }
+
+ friend class AudioFlinger; // for mEffectChains
+
+ const type_t mType;
+
+ // Used by parameters, config events, addTrack_l, exit
+ Condition mWaitWorkCV;
+
+ const sp<AudioFlinger> mAudioFlinger;
+
+ // updated by PlaybackThread::readOutputParameters() or
+ // RecordThread::readInputParameters()
+ uint32_t mSampleRate;
+ size_t mFrameCount; // output HAL, direct output, record
+ audio_channel_mask_t mChannelMask;
+ uint32_t mChannelCount;
+ size_t mFrameSize;
+ audio_format_t mFormat;
+
+ // Parameter sequence by client: binder thread calling setParameters():
+ // 1. Lock mLock
+ // 2. Append to mNewParameters
+ // 3. mWaitWorkCV.signal
+ // 4. mParamCond.waitRelative with timeout
+ // 5. read mParamStatus
+ // 6. mWaitWorkCV.signal
+ // 7. Unlock
+ //
+ // Parameter sequence by server: threadLoop calling checkForNewParameters_l():
+ // 1. Lock mLock
+ // 2. If there is an entry in mNewParameters proceed ...
+ // 2. Read first entry in mNewParameters
+ // 3. Process
+ // 4. Remove first entry from mNewParameters
+ // 5. Set mParamStatus
+ // 6. mParamCond.signal
+ // 7. mWaitWorkCV.wait with timeout (this is to avoid overwriting mParamStatus)
+ // 8. Unlock
+ Condition mParamCond;
+ Vector<String8> mNewParameters;
+ status_t mParamStatus;
+
+ // vector owns each ConfigEvent *, so must delete after removing
+ Vector<ConfigEvent *> mConfigEvents;
+
+ // These fields are written and read by thread itself without lock or barrier,
+ // and read by other threads without lock or barrier via standby() , outDevice()
+ // and inDevice().
+ // Because of the absence of a lock or barrier, any other thread that reads
+ // these fields must use the information in isolation, or be prepared to deal
+ // with possibility that it might be inconsistent with other information.
+ bool mStandby; // Whether thread is currently in standby.
+ audio_devices_t mOutDevice; // output device
+ audio_devices_t mInDevice; // input device
+ audio_source_t mAudioSource; // (see audio.h, audio_source_t)
+
+ const audio_io_handle_t mId;
+ Vector< sp<EffectChain> > mEffectChains;
+
+ static const int kNameLength = 16; // prctl(PR_SET_NAME) limit
+ char mName[kNameLength];
+ sp<IPowerManager> mPowerManager;
+ sp<IBinder> mWakeLockToken;
+ const sp<PMDeathRecipient> mDeathRecipient;
+ // list of suspended effects per session and per type. The first vector is
+ // keyed by session ID, the second by type UUID timeLow field
+ KeyedVector< int, KeyedVector< int, sp<SuspendedSessionDesc> > >
+ mSuspendedSessions;
+ static const size_t kLogSize = 4 * 1024;
+ sp<NBLog::Writer> mNBLogWriter;
+};
+
+// --- PlaybackThread ---
+class PlaybackThread : public ThreadBase {
+public:
+
+#include "PlaybackTracks.h"
+
+ enum mixer_state {
+ MIXER_IDLE, // no active tracks
+ MIXER_TRACKS_ENABLED, // at least one active track, but no track has any data ready
+ MIXER_TRACKS_READY, // at least one active track, and at least one track has data
+ MIXER_DRAIN_TRACK, // drain currently playing track
+ MIXER_DRAIN_ALL, // fully drain the hardware
+ // standby mode does not have an enum value
+ // suspend by audio policy manager is orthogonal to mixer state
+ };
+
+ // retry count before removing active track in case of underrun on offloaded thread:
+ // we need to make sure that AudioTrack client has enough time to send large buffers
+//FIXME may be more appropriate if expressed in time units. Need to revise how underrun is handled
+ // for offloaded tracks
+ static const int8_t kMaxTrackRetriesOffload = 20;
+
+ PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, audio_devices_t device, type_t type);
+ virtual ~PlaybackThread();
+
+ void dump(int fd, const Vector<String16>& args);
+
+ // Thread virtuals
+ virtual status_t readyToRun();
+ virtual bool threadLoop();
+
+ // RefBase
+ virtual void onFirstRef();
+
+protected:
+ // Code snippets that were lifted up out of threadLoop()
+ virtual void threadLoop_mix() = 0;
+ virtual void threadLoop_sleepTime() = 0;
+ virtual ssize_t threadLoop_write();
+ virtual void threadLoop_drain();
+ virtual void threadLoop_standby();
+ virtual void threadLoop_exit();
+ virtual void threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove);
+
+ // prepareTracks_l reads and writes mActiveTracks, and returns
+ // the pending set of tracks to remove via Vector 'tracksToRemove'. The caller
+ // is responsible for clearing or destroying this Vector later on, when it
+ // is safe to do so. That will drop the final ref count and destroy the tracks.
+ virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove) = 0;
+ void removeTracks_l(const Vector< sp<Track> >& tracksToRemove);
+
+ void writeCallback();
+ void resetWriteBlocked(uint32_t sequence);
+ void drainCallback();
+ void resetDraining(uint32_t sequence);
+
+ static int asyncCallback(stream_callback_event_t event, void *param, void *cookie);
+
+ virtual bool waitingAsyncCallback();
+ virtual bool waitingAsyncCallback_l();
+ virtual bool shouldStandby_l();
+
+
+ // ThreadBase virtuals
+ virtual void preExit();
+
+public:
+
+ virtual status_t initCheck() const { return (mOutput == NULL) ? NO_INIT : NO_ERROR; }
+
+ // return estimated latency in milliseconds, as reported by HAL
+ uint32_t latency() const;
+ // same, but lock must already be held
+ uint32_t latency_l() const;
+
+ void setMasterVolume(float value);
+ void setMasterMute(bool muted);
+
+ void setStreamVolume(audio_stream_type_t stream, float value);
+ void setStreamMute(audio_stream_type_t stream, bool muted);
+
+ float streamVolume(audio_stream_type_t stream) const;
+
+ sp<Track> createTrack_l(
+ const sp<AudioFlinger::Client>& client,
+ audio_stream_type_t streamType,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ IAudioFlinger::track_flags_t *flags,
+ pid_t tid,
+ int uid,
+ status_t *status);
+
+ AudioStreamOut* getOutput() const;
+ AudioStreamOut* clearOutput();
+ virtual audio_stream_t* stream() const;
+
+ // a very large number of suspend() will eventually wraparound, but unlikely
+ void suspend() { (void) android_atomic_inc(&mSuspended); }
+ void restore()
+ {
+ // if restore() is done without suspend(), get back into
+ // range so that the next suspend() will operate correctly
+ if (android_atomic_dec(&mSuspended) <= 0) {
+ android_atomic_release_store(0, &mSuspended);
+ }
+ }
+ bool isSuspended() const
+ { return android_atomic_acquire_load(&mSuspended) > 0; }
+
+ virtual String8 getParameters(const String8& keys);
+ virtual void audioConfigChanged_l(int event, int param = 0);
+ status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
+ int16_t *mixBuffer() const { return mMixBuffer; };
+
+ virtual 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);
+
+ virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
+ virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
+ virtual uint32_t hasAudioSession(int sessionId) const;
+ virtual uint32_t getStrategyForSession_l(int sessionId);
+
+
+ virtual status_t setSyncEvent(const sp<SyncEvent>& event);
+ virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
+
+ // called with AudioFlinger lock held
+ void invalidateTracks(audio_stream_type_t streamType);
+
+ virtual size_t frameCount() const { return mNormalFrameCount; }
+
+ // Return's the HAL's frame count i.e. fast mixer buffer size.
+ size_t frameCountHAL() const { return mFrameCount; }
+
+ status_t getTimestamp_l(AudioTimestamp& timestamp);
+
+protected:
+ // updated by readOutputParameters()
+ size_t mNormalFrameCount; // normal mixer and effects
+
+ int16_t* mMixBuffer; // frame size aligned mix buffer
+ int8_t* mAllocMixBuffer; // mixer buffer allocation address
+
+ // suspend count, > 0 means suspended. While suspended, the thread continues to pull from
+ // tracks and mix, but doesn't write to HAL. A2DP and SCO HAL implementations can't handle
+ // concurrent use of both of them, so Audio Policy Service suspends one of the threads to
+ // workaround that restriction.
+ // 'volatile' means accessed via atomic operations and no lock.
+ volatile int32_t mSuspended;
+
+ // FIXME overflows every 6+ hours at 44.1 kHz stereo 16-bit samples
+ // mFramesWritten would be better, or 64-bit even better
+ size_t mBytesWritten;
+private:
+ // mMasterMute is in both PlaybackThread and in AudioFlinger. When a
+ // PlaybackThread needs to find out if master-muted, it checks it's local
+ // copy rather than the one in AudioFlinger. This optimization saves a lock.
+ bool mMasterMute;
+ void setMasterMute_l(bool muted) { mMasterMute = muted; }
+protected:
+ SortedVector< wp<Track> > mActiveTracks; // FIXME check if this could be sp<>
+ SortedVector<int> mWakeLockUids;
+ int mActiveTracksGeneration;
+ wp<Track> mLatestActiveTrack; // latest track added to mActiveTracks
+
+ // Allocate a track name for a given channel mask.
+ // Returns name >= 0 if successful, -1 on failure.
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId) = 0;
+ virtual void deleteTrackName_l(int name) = 0;
+
+ // Time to sleep between cycles when:
+ virtual uint32_t activeSleepTimeUs() const; // mixer state MIXER_TRACKS_ENABLED
+ virtual uint32_t idleSleepTimeUs() const = 0; // mixer state MIXER_IDLE
+ virtual uint32_t suspendSleepTimeUs() const = 0; // audio policy manager suspended us
+ // No sleep when mixer state == MIXER_TRACKS_READY; relies on audio HAL stream->write()
+ // No sleep in standby mode; waits on a condition
+
+ // Code snippets that are temporarily lifted up out of threadLoop() until the merge
+ void checkSilentMode_l();
+
+ // Non-trivial for DUPLICATING only
+ virtual void saveOutputTracks() { }
+ virtual void clearOutputTracks() { }
+
+ // Cache various calculated values, at threadLoop() entry and after a parameter change
+ virtual void cacheParameters_l();
+
+ virtual uint32_t correctLatency_l(uint32_t latency) const;
+
+private:
+
+ friend class AudioFlinger; // for numerous
+
+ PlaybackThread(const Client&);
+ PlaybackThread& operator = (const PlaybackThread&);
+
+ status_t addTrack_l(const sp<Track>& track);
+ bool destroyTrack_l(const sp<Track>& track);
+ void removeTrack_l(const sp<Track>& track);
+ void broadcast_l();
+
+ void readOutputParameters();
+
+ virtual void dumpInternals(int fd, const Vector<String16>& args);
+ void dumpTracks(int fd, const Vector<String16>& args);
+
+ SortedVector< sp<Track> > mTracks;
+ // mStreamTypes[] uses 1 additional stream type internally for the OutputTrack used by
+ // DuplicatingThread
+ stream_type_t mStreamTypes[AUDIO_STREAM_CNT + 1];
+ AudioStreamOut *mOutput;
+
+ float mMasterVolume;
+ nsecs_t mLastWriteTime;
+ int mNumWrites;
+ int mNumDelayedWrites;
+ bool mInWrite;
+
+ // FIXME rename these former local variables of threadLoop to standard "m" names
+ nsecs_t standbyTime;
+ size_t mixBufferSize;
+
+ // cached copies of activeSleepTimeUs() and idleSleepTimeUs() made by cacheParameters_l()
+ uint32_t activeSleepTime;
+ uint32_t idleSleepTime;
+
+ uint32_t sleepTime;
+
+ // mixer status returned by prepareTracks_l()
+ mixer_state mMixerStatus; // current cycle
+ // previous cycle when in prepareTracks_l()
+ mixer_state mMixerStatusIgnoringFastTracks;
+ // FIXME or a separate ready state per track
+
+ // FIXME move these declarations into the specific sub-class that needs them
+ // MIXER only
+ uint32_t sleepTimeShift;
+
+ // same as AudioFlinger::mStandbyTimeInNsecs except for DIRECT which uses a shorter value
+ nsecs_t standbyDelay;
+
+ // MIXER only
+ nsecs_t maxPeriod;
+
+ // DUPLICATING only
+ uint32_t writeFrames;
+
+ size_t mBytesRemaining;
+ size_t mCurrentWriteLength;
+ bool mUseAsyncWrite;
+ // mWriteAckSequence contains current write sequence on bits 31-1. The write sequence is
+ // incremented each time a write(), a flush() or a standby() occurs.
+ // Bit 0 is set when a write blocks and indicates a callback is expected.
+ // Bit 0 is reset by the async callback thread calling resetWriteBlocked(). Out of sequence
+ // callbacks are ignored.
+ uint32_t mWriteAckSequence;
+ // mDrainSequence contains current drain sequence on bits 31-1. The drain sequence is
+ // incremented each time a drain is requested or a flush() or standby() occurs.
+ // Bit 0 is set when the drain() command is called at the HAL and indicates a callback is
+ // expected.
+ // Bit 0 is reset by the async callback thread calling resetDraining(). Out of sequence
+ // callbacks are ignored.
+ uint32_t mDrainSequence;
+ // A condition that must be evaluated by prepareTrack_l() has changed and we must not wait
+ // for async write callback in the thread loop before evaluating it
+ bool mSignalPending;
+ sp<AsyncCallbackThread> mCallbackThread;
+
+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;
+#ifdef TEE_SINK
+ // For dumpsys
+ sp<NBAIO_Sink> mTeeSink;
+ sp<NBAIO_Source> mTeeSource;
+#endif
+ uint32_t mScreenState; // cached copy of gScreenState
+ static const size_t kFastMixerLogSize = 4 * 1024;
+ sp<NBLog::Writer> mFastMixerNBLogWriter;
+public:
+ virtual bool hasFastMixer() const = 0;
+ virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const
+ { FastTrackUnderruns dummy; return dummy; }
+
+protected:
+ // accessed by both binder threads and within threadLoop(), lock on mutex needed
+ unsigned mFastTrackAvailMask; // bit i set if fast track [i] is available
+ virtual void flushOutput_l();
+
+private:
+ // timestamp latch:
+ // D input is written by threadLoop_write while mutex is unlocked, and read while locked
+ // Q output is written while locked, and read while locked
+ struct {
+ AudioTimestamp mTimestamp;
+ uint32_t mUnpresentedFrames;
+ } mLatchD, mLatchQ;
+ bool mLatchDValid; // true means mLatchD is valid, and clock it into latch at next opportunity
+ bool mLatchQValid; // true means mLatchQ is valid
+};
+
+class MixerThread : public PlaybackThread {
+public:
+ MixerThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamOut* output,
+ audio_io_handle_t id,
+ audio_devices_t device,
+ type_t type = MIXER);
+ virtual ~MixerThread();
+
+ // Thread virtuals
+
+ virtual bool checkForNewParameters_l();
+ virtual void dumpInternals(int fd, const Vector<String16>& args);
+
+protected:
+ virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
+ virtual void deleteTrackName_l(int name);
+ virtual uint32_t idleSleepTimeUs() const;
+ virtual uint32_t suspendSleepTimeUs() const;
+ virtual void cacheParameters_l();
+
+ // threadLoop snippets
+ virtual ssize_t threadLoop_write();
+ virtual void threadLoop_standby();
+ virtual void threadLoop_mix();
+ virtual void threadLoop_sleepTime();
+ virtual void threadLoop_removeTracks(const Vector< sp<Track> >& tracksToRemove);
+ virtual uint32_t correctLatency_l(uint32_t latency) const;
+
+ AudioMixer* mAudioMixer; // normal mixer
+private:
+ // one-time initialization, no locks required
+ FastMixer* mFastMixer; // non-NULL if there is also a fast mixer
+ sp<AudioWatchdog> mAudioWatchdog; // non-0 if there is an audio watchdog thread
+
+ // contents are not guaranteed to be consistent, no locks required
+ FastMixerDumpState mFastMixerDumpState;
+#ifdef STATE_QUEUE_DUMP
+ StateQueueObserverDump mStateQueueObserverDump;
+ StateQueueMutatorDump mStateQueueMutatorDump;
+#endif
+ AudioWatchdogDump mAudioWatchdogDump;
+
+ // 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; }
+ virtual FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const {
+ ALOG_ASSERT(fastIndex < FastMixerState::kMaxFastTracks);
+ return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
+ }
+};
+
+class DirectOutputThread : public PlaybackThread {
+public:
+
+ DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, audio_devices_t device);
+ virtual ~DirectOutputThread();
+
+ // Thread virtuals
+
+ virtual bool checkForNewParameters_l();
+
+protected:
+ virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId);
+ virtual void deleteTrackName_l(int name);
+ virtual uint32_t activeSleepTimeUs() const;
+ virtual uint32_t idleSleepTimeUs() const;
+ virtual uint32_t suspendSleepTimeUs() const;
+ virtual void cacheParameters_l();
+
+ // threadLoop snippets
+ virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
+ virtual void threadLoop_mix();
+ virtual void threadLoop_sleepTime();
+
+ // volumes last sent to audio HAL with stream->set_volume()
+ float mLeftVolFloat;
+ float mRightVolFloat;
+
+ DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, uint32_t device, ThreadBase::type_t type);
+ void processVolume_l(Track *track, bool lastTrack);
+
+ // prepareTracks_l() tells threadLoop_mix() the name of the single active track
+ sp<Track> mActiveTrack;
+public:
+ virtual bool hasFastMixer() const { return false; }
+};
+
+class OffloadThread : public DirectOutputThread {
+public:
+
+ OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
+ audio_io_handle_t id, uint32_t device);
+ virtual ~OffloadThread() {};
+
+protected:
+ // threadLoop snippets
+ virtual mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
+ virtual void threadLoop_exit();
+ virtual void flushOutput_l();
+
+ virtual bool waitingAsyncCallback();
+ virtual bool waitingAsyncCallback_l();
+ virtual bool shouldStandby_l();
+
+private:
+ void flushHw_l();
+
+private:
+ bool mHwPaused;
+ bool mFlushPending;
+ size_t mPausedWriteLength; // length in bytes of write interrupted by pause
+ size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume
+ wp<Track> mPreviousTrack; // used to detect track switch
+};
+
+class AsyncCallbackThread : public Thread {
+public:
+
+ AsyncCallbackThread(const wp<PlaybackThread>& playbackThread);
+
+ virtual ~AsyncCallbackThread();
+
+ // Thread virtuals
+ virtual bool threadLoop();
+
+ // RefBase
+ virtual void onFirstRef();
+
+ void exit();
+ void setWriteBlocked(uint32_t sequence);
+ void resetWriteBlocked();
+ void setDraining(uint32_t sequence);
+ void resetDraining();
+
+private:
+ const wp<PlaybackThread> mPlaybackThread;
+ // mWriteAckSequence corresponds to the last write sequence passed by the offload thread via
+ // setWriteBlocked(). The sequence is shifted one bit to the left and the lsb is used
+ // to indicate that the callback has been received via resetWriteBlocked()
+ uint32_t mWriteAckSequence;
+ // mDrainSequence corresponds to the last drain sequence passed by the offload thread via
+ // setDraining(). The sequence is shifted one bit to the left and the lsb is used
+ // to indicate that the callback has been received via resetDraining()
+ uint32_t mDrainSequence;
+ Condition mWaitWorkCV;
+ Mutex mLock;
+};
+
+class DuplicatingThread : public MixerThread {
+public:
+ DuplicatingThread(const sp<AudioFlinger>& audioFlinger, MixerThread* mainThread,
+ audio_io_handle_t id);
+ virtual ~DuplicatingThread();
+
+ // Thread virtuals
+ void addOutputTrack(MixerThread* thread);
+ void removeOutputTrack(MixerThread* thread);
+ uint32_t waitTimeMs() const { return mWaitTimeMs; }
+protected:
+ virtual uint32_t activeSleepTimeUs() const;
+
+private:
+ bool outputsReady(const SortedVector< sp<OutputTrack> > &outputTracks);
+protected:
+ // threadLoop snippets
+ virtual void threadLoop_mix();
+ virtual void threadLoop_sleepTime();
+ virtual ssize_t threadLoop_write();
+ virtual void threadLoop_standby();
+ virtual void cacheParameters_l();
+
+private:
+ // called from threadLoop, addOutputTrack, removeOutputTrack
+ virtual void updateWaitTime_l();
+protected:
+ virtual void saveOutputTracks();
+ virtual void clearOutputTracks();
+private:
+
+ uint32_t mWaitTimeMs;
+ SortedVector < sp<OutputTrack> > outputTracks;
+ SortedVector < sp<OutputTrack> > mOutputTracks;
+public:
+ virtual bool hasFastMixer() const { return false; }
+};
+
+
+// record thread
+class RecordThread : public ThreadBase, public AudioBufferProvider
+ // derives from AudioBufferProvider interface for use by resampler
+{
+public:
+
+#include "RecordTracks.h"
+
+ RecordThread(const sp<AudioFlinger>& audioFlinger,
+ AudioStreamIn *input,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_io_handle_t id,
+ audio_devices_t outDevice,
+ audio_devices_t inDevice
+#ifdef TEE_SINK
+ , const sp<NBAIO_Sink>& teeSink
+#endif
+ );
+ virtual ~RecordThread();
+
+ // no addTrack_l ?
+ void destroyTrack_l(const sp<RecordTrack>& track);
+ void removeTrack_l(const sp<RecordTrack>& track);
+
+ void dumpInternals(int fd, const Vector<String16>& args);
+ void dumpTracks(int fd, const Vector<String16>& args);
+
+ // Thread virtuals
+ virtual bool threadLoop();
+ virtual status_t readyToRun();
+
+ // RefBase
+ virtual void onFirstRef();
+
+ virtual status_t initCheck() const { return (mInput == NULL) ? NO_INIT : NO_ERROR; }
+ sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l(
+ const sp<AudioFlinger::Client>& client,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ int sessionId,
+ int uid,
+ IAudioFlinger::track_flags_t *flags,
+ pid_t tid,
+ status_t *status);
+
+ status_t start(RecordTrack* recordTrack,
+ AudioSystem::sync_event_t event,
+ int triggerSession);
+
+ // ask the thread to stop the specified track, and
+ // return true if the caller should then do it's part of the stopping process
+ bool stop(RecordTrack* recordTrack);
+
+ void dump(int fd, const Vector<String16>& args);
+ AudioStreamIn* clearInput();
+ virtual audio_stream_t* stream() const;
+
+ // AudioBufferProvider interface
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts);
+ virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+
+ virtual bool checkForNewParameters_l();
+ virtual String8 getParameters(const String8& keys);
+ virtual void audioConfigChanged_l(int event, int param = 0);
+ void readInputParameters();
+ virtual unsigned int getInputFramesLost();
+
+ virtual status_t addEffectChain_l(const sp<EffectChain>& chain);
+ virtual size_t removeEffectChain_l(const sp<EffectChain>& chain);
+ virtual uint32_t hasAudioSession(int sessionId) const;
+
+ // Return the set of unique session IDs across all tracks.
+ // The keys are the session IDs, and the associated values are meaningless.
+ // FIXME replace by Set [and implement Bag/Multiset for other uses].
+ KeyedVector<int, bool> sessionIds() const;
+
+ virtual status_t setSyncEvent(const sp<SyncEvent>& event);
+ virtual bool isValidSyncEvent(const sp<SyncEvent>& event) const;
+
+ static void syncStartEventCallback(const wp<SyncEvent>& event);
+ void handleSyncStartEvent(const sp<SyncEvent>& event);
+
+ virtual size_t frameCount() const { return mFrameCount; }
+ bool hasFastRecorder() const { return false; }
+
+private:
+ void clearSyncStartEvent();
+
+ // Enter standby if not already in standby, and set mStandby flag
+ void standby();
+
+ // Call the HAL standby method unconditionally, and don't change mStandby flag
+ void inputStandBy();
+
+ AudioStreamIn *mInput;
+ SortedVector < sp<RecordTrack> > mTracks;
+ // mActiveTrack has dual roles: it indicates the current active track, and
+ // is used together with mStartStopCond to indicate start()/stop() progress
+ sp<RecordTrack> mActiveTrack;
+ Condition mStartStopCond;
+
+ // updated by RecordThread::readInputParameters()
+ AudioResampler *mResampler;
+ // interleaved stereo pairs of fixed-point signed Q19.12
+ int32_t *mRsmpOutBuffer;
+ int16_t *mRsmpInBuffer; // [mFrameCount * mChannelCount]
+ size_t mRsmpInIndex;
+ size_t mBufferSize; // stream buffer size for read()
+ const uint32_t mReqChannelCount;
+ const uint32_t mReqSampleRate;
+ ssize_t mBytesRead;
+ // sync event triggering actual audio capture. Frames read before this event will
+ // be dropped and therefore not read by the application.
+ sp<SyncEvent> mSyncStartEvent;
+ // number of captured frames to drop after the start sync event has been received.
+ // when < 0, maximum frames to drop before starting capture even if sync event is
+ // not received
+ ssize_t mFramestoDrop;
+
+ // For dumpsys
+ const sp<NBAIO_Sink> mTeeSink;
+};
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
new file mode 100644
index 0000000..cd201d9
--- /dev/null
+++ b/services/audioflinger/TrackBase.h
@@ -0,0 +1,145 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef INCLUDING_FROM_AUDIOFLINGER_H
+ #error This header file should only be included from AudioFlinger.h
+#endif
+
+// base for record and playback
+class TrackBase : public ExtendedAudioBufferProvider, public RefBase {
+
+public:
+ enum track_state {
+ IDLE,
+ FLUSHED,
+ STOPPED,
+ // next 2 states are currently used for fast tracks
+ // and offloaded tracks only
+ STOPPING_1, // waiting for first underrun
+ STOPPING_2, // waiting for presentation complete
+ RESUMING,
+ ACTIVE,
+ PAUSING,
+ PAUSED
+ };
+
+ TrackBase(ThreadBase *thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ int uid,
+ bool isOut);
+ virtual ~TrackBase();
+
+ virtual status_t start(AudioSystem::sync_event_t event,
+ int triggerSession) = 0;
+ virtual void stop() = 0;
+ sp<IMemory> getCblk() const { return mCblkMemory; }
+ audio_track_cblk_t* cblk() const { return mCblk; }
+ int sessionId() const { return mSessionId; }
+ int uid() const { return mUid; }
+ virtual status_t setSyncEvent(const sp<SyncEvent>& event);
+
+protected:
+ TrackBase(const TrackBase&);
+ TrackBase& operator = (const TrackBase&);
+
+ // AudioBufferProvider interface
+ virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts) = 0;
+ virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
+
+ // ExtendedAudioBufferProvider interface is only needed for Track,
+ // but putting it in TrackBase avoids the complexity of virtual inheritance
+ virtual size_t framesReady() const { return SIZE_MAX; }
+
+ audio_format_t format() const { return mFormat; }
+
+ uint32_t channelCount() const { return mChannelCount; }
+
+ audio_channel_mask_t channelMask() const { return mChannelMask; }
+
+ virtual uint32_t sampleRate() const { return mSampleRate; }
+
+ // Return a pointer to the start of a contiguous slice of the track buffer.
+ // Parameter 'offset' is the requested start position, expressed in
+ // monotonically increasing frame units relative to the track epoch.
+ // Parameter 'frames' is the requested length, also in frame units.
+ // Always returns non-NULL. It is the caller's responsibility to
+ // verify that this will be successful; the result of calling this
+ // function with invalid 'offset' or 'frames' is undefined.
+ void* getBuffer(uint32_t offset, uint32_t frames) const;
+
+ bool isStopped() const {
+ return (mState == STOPPED || mState == FLUSHED);
+ }
+
+ // for fast tracks and offloaded tracks only
+ bool isStopping() const {
+ return mState == STOPPING_1 || mState == STOPPING_2;
+ }
+ bool isStopping_1() const {
+ return mState == STOPPING_1;
+ }
+ bool isStopping_2() const {
+ return mState == STOPPING_2;
+ }
+
+ bool isTerminated() const {
+ return mTerminated;
+ }
+
+ void terminate() {
+ mTerminated = true;
+ }
+
+ bool isOut() const { return mIsOut; }
+ // true for Track and TimedTrack, false for RecordTrack,
+ // this could be a track type if needed later
+
+ const wp<ThreadBase> mThread;
+ /*const*/ sp<Client> mClient; // see explanation at ~TrackBase() why not const
+ sp<IMemory> mCblkMemory;
+ audio_track_cblk_t* mCblk;
+ void* mBuffer; // start of track buffer, typically in shared memory
+ // except for OutputTrack when it is in local memory
+ // 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;
+ const audio_channel_mask_t mChannelMask;
+ const uint32_t mChannelCount;
+ const size_t mFrameSize; // AudioFlinger's view of frame size in shared memory,
+ // where for AudioTrack (but not AudioRecord),
+ // 8-bit PCM samples are stored as 16-bit
+ const size_t mFrameCount;// size of track buffer given at createTrack() or
+ // openRecord(), and then adjusted as needed
+
+ const int mSessionId;
+ int mUid;
+ Vector < sp<SyncEvent> >mSyncEvents;
+ const bool mIsOut;
+ ServerProxy* mServerProxy;
+ const int mId;
+ sp<NBAIO_Sink> mTeeSink;
+ sp<NBAIO_Source> mTeeSource;
+ bool mTerminated;
+};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
new file mode 100644
index 0000000..fccc7b8
--- /dev/null
+++ b/services/audioflinger/Tracks.cpp
@@ -0,0 +1,1863 @@
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+
+#include "Configuration.h"
+#include <math.h>
+#include <utils/Log.h>
+
+#include <private/media/AudioTrackShared.h>
+
+#include <common_time/cc_helper.h>
+#include <common_time/local_clock.h>
+
+#include "AudioMixer.h"
+#include "AudioFlinger.h"
+#include "ServiceUtilities.h"
+
+#include <media/nbaio/Pipe.h>
+#include <media/nbaio/PipeReader.h>
+
+// ----------------------------------------------------------------------------
+
+// Note: the following macro is used for extremely verbose logging message. In
+// order to run with ALOG_ASSERT turned on, we need to have LOG_NDEBUG set to
+// 0; but one side effect of this is to turn all LOGV's as well. Some messages
+// are so verbose that we want to suppress them even when we have ALOG_ASSERT
+// turned on. Do not uncomment the #def below unless you really know what you
+// are doing and want to see all of the extremely verbose messages.
+//#define VERY_VERY_VERBOSE_LOGGING
+#ifdef VERY_VERY_VERBOSE_LOGGING
+#define ALOGVV ALOGV
+#else
+#define ALOGVV(a...) do { } while(0)
+#endif
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+// TrackBase
+// ----------------------------------------------------------------------------
+
+static volatile int32_t nextTrackId = 55;
+
+// TrackBase constructor must be called with AudioFlinger::mLock held
+AudioFlinger::ThreadBase::TrackBase::TrackBase(
+ ThreadBase *thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ int clientUid,
+ bool isOut)
+ : RefBase(),
+ mThread(thread),
+ mClient(client),
+ mCblk(NULL),
+ // mBuffer
+ mState(IDLE),
+ mSampleRate(sampleRate),
+ mFormat(format),
+ mChannelMask(channelMask),
+ mChannelCount(popcount(channelMask)),
+ mFrameSize(audio_is_linear_pcm(format) ?
+ mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
+ mFrameCount(frameCount),
+ mSessionId(sessionId),
+ mIsOut(isOut),
+ mServerProxy(NULL),
+ mId(android_atomic_inc(&nextTrackId)),
+ mTerminated(false)
+{
+ // if the caller is us, trust the specified uid
+ if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {
+ int newclientUid = IPCThreadState::self()->getCallingUid();
+ if (clientUid != -1 && clientUid != newclientUid) {
+ ALOGW("uid %d tried to pass itself off as %d", newclientUid, clientUid);
+ }
+ clientUid = newclientUid;
+ }
+ // clientUid contains the uid of the app that is responsible for this track, so we can blame
+ // battery usage on it.
+ mUid = clientUid;
+
+ // client == 0 implies sharedBuffer == 0
+ ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
+
+ ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(),
+ sharedBuffer->size());
+
+ // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
+ size_t size = sizeof(audio_track_cblk_t);
+ size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
+ if (sharedBuffer == 0) {
+ size += bufferSize;
+ }
+
+ if (client != 0) {
+ mCblkMemory = client->heap()->allocate(size);
+ if (mCblkMemory != 0) {
+ mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer());
+ // can't assume mCblk != NULL
+ } else {
+ ALOGE("not enough memory for AudioTrack size=%u", size);
+ client->heap()->dump("AudioTrack");
+ return;
+ }
+ } else {
+ // this syntax avoids calling the audio_track_cblk_t constructor twice
+ mCblk = (audio_track_cblk_t *) new uint8_t[size];
+ // assume mCblk != NULL
+ }
+
+ // construct the shared structure in-place.
+ if (mCblk != NULL) {
+ new(mCblk) audio_track_cblk_t();
+ // clear all buffers
+ mCblk->frameCount_ = frameCount;
+ if (sharedBuffer == 0) {
+ mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
+ memset(mBuffer, 0, bufferSize);
+ } else {
+ mBuffer = sharedBuffer->pointer();
+#if 0
+ mCblk->mFlags = CBLK_FORCEREADY; // FIXME hack, need to fix the track ready logic
+#endif
+ }
+
+#ifdef TEE_SINK
+ if (mTeeSinkTrackEnabled) {
+ NBAIO_Format pipeFormat = Format_from_SR_C(mSampleRate, mChannelCount);
+ if (pipeFormat != Format_Invalid) {
+ Pipe *pipe = new Pipe(mTeeSinkTrackFrames, pipeFormat);
+ size_t numCounterOffers = 0;
+ const NBAIO_Format offers[1] = {pipeFormat};
+ ssize_t index = pipe->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ PipeReader *pipeReader = new PipeReader(*pipe);
+ numCounterOffers = 0;
+ index = pipeReader->negotiate(offers, 1, NULL, numCounterOffers);
+ ALOG_ASSERT(index == 0);
+ mTeeSink = pipe;
+ mTeeSource = pipeReader;
+ }
+ }
+#endif
+
+ }
+}
+
+AudioFlinger::ThreadBase::TrackBase::~TrackBase()
+{
+#ifdef TEE_SINK
+ dumpTee(-1, mTeeSource, mId);
+#endif
+ // delete the proxy before deleting the shared memory it refers to, to avoid dangling reference
+ delete mServerProxy;
+ if (mCblk != NULL) {
+ if (mClient == 0) {
+ delete mCblk;
+ } else {
+ mCblk->~audio_track_cblk_t(); // destroy our shared-structure.
+ }
+ }
+ mCblkMemory.clear(); // free the shared memory before releasing the heap it belongs to
+ if (mClient != 0) {
+ // Client destructor must run with AudioFlinger mutex locked
+ Mutex::Autolock _l(mClient->audioFlinger()->mLock);
+ // If the client's reference count drops to zero, the associated destructor
+ // must run with AudioFlinger lock held. Thus the explicit clear() rather than
+ // relying on the automatic clear() at end of scope.
+ mClient.clear();
+ }
+}
+
+// AudioBufferProvider interface
+// getNextBuffer() = 0;
+// This implementation of releaseBuffer() is used by Track and RecordTrack, but not TimedTrack
+void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
+{
+#ifdef TEE_SINK
+ if (mTeeSink != 0) {
+ (void) mTeeSink->write(buffer->raw, buffer->frameCount);
+ }
+#endif
+
+ ServerProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ buf.mRaw = buffer->raw;
+ buffer->frameCount = 0;
+ buffer->raw = NULL;
+ mServerProxy->releaseBuffer(&buf);
+}
+
+status_t AudioFlinger::ThreadBase::TrackBase::setSyncEvent(const sp<SyncEvent>& event)
+{
+ mSyncEvents.add(event);
+ return NO_ERROR;
+}
+
+// ----------------------------------------------------------------------------
+// Playback
+// ----------------------------------------------------------------------------
+
+AudioFlinger::TrackHandle::TrackHandle(const sp<AudioFlinger::PlaybackThread::Track>& track)
+ : BnAudioTrack(),
+ mTrack(track)
+{
+}
+
+AudioFlinger::TrackHandle::~TrackHandle() {
+ // just stop the track on deletion, associated resources
+ // will be freed from the main thread once all pending buffers have
+ // been played. Unless it's not in the active track list, in which
+ // case we free everything now...
+ mTrack->destroy();
+}
+
+sp<IMemory> AudioFlinger::TrackHandle::getCblk() const {
+ return mTrack->getCblk();
+}
+
+status_t AudioFlinger::TrackHandle::start() {
+ return mTrack->start();
+}
+
+void AudioFlinger::TrackHandle::stop() {
+ mTrack->stop();
+}
+
+void AudioFlinger::TrackHandle::flush() {
+ mTrack->flush();
+}
+
+void AudioFlinger::TrackHandle::pause() {
+ mTrack->pause();
+}
+
+status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId)
+{
+ return mTrack->attachAuxEffect(EffectId);
+}
+
+status_t AudioFlinger::TrackHandle::allocateTimedBuffer(size_t size,
+ sp<IMemory>* buffer) {
+ if (!mTrack->isTimedTrack())
+ return INVALID_OPERATION;
+
+ PlaybackThread::TimedTrack* tt =
+ reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
+ return tt->allocateTimedBuffer(size, buffer);
+}
+
+status_t AudioFlinger::TrackHandle::queueTimedBuffer(const sp<IMemory>& buffer,
+ int64_t pts) {
+ if (!mTrack->isTimedTrack())
+ return INVALID_OPERATION;
+
+ PlaybackThread::TimedTrack* tt =
+ reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
+ return tt->queueTimedBuffer(buffer, pts);
+}
+
+status_t AudioFlinger::TrackHandle::setMediaTimeTransform(
+ const LinearTransform& xform, int target) {
+
+ if (!mTrack->isTimedTrack())
+ return INVALID_OPERATION;
+
+ PlaybackThread::TimedTrack* tt =
+ reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get());
+ return tt->setMediaTimeTransform(
+ xform, static_cast<TimedAudioTrack::TargetTimeline>(target));
+}
+
+status_t AudioFlinger::TrackHandle::setParameters(const String8& keyValuePairs) {
+ return mTrack->setParameters(keyValuePairs);
+}
+
+status_t AudioFlinger::TrackHandle::getTimestamp(AudioTimestamp& timestamp)
+{
+ return mTrack->getTimestamp(timestamp);
+}
+
+
+void AudioFlinger::TrackHandle::signal()
+{
+ return mTrack->signal();
+}
+
+status_t AudioFlinger::TrackHandle::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnAudioTrack::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+
+// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
+AudioFlinger::PlaybackThread::Track::Track(
+ PlaybackThread *thread,
+ const sp<Client>& client,
+ audio_stream_type_t streamType,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ int uid,
+ IAudioFlinger::track_flags_t flags)
+ : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
+ sessionId, uid, true /*isOut*/),
+ mFillingUpStatus(FS_INVALID),
+ // mRetryCount initialized later when needed
+ mSharedBuffer(sharedBuffer),
+ mStreamType(streamType),
+ mName(-1), // see note below
+ mMainBuffer(thread->mixBuffer()),
+ mAuxBuffer(NULL),
+ mAuxEffectId(0), mHasVolumeController(false),
+ mPresentationCompleteFrames(0),
+ mFlags(flags),
+ mFastIndex(-1),
+ mCachedVolume(1.0),
+ mIsInvalid(false),
+ mAudioTrackServerProxy(NULL),
+ mResumeToStopping(false)
+{
+ if (mCblk != NULL) {
+ if (sharedBuffer == 0) {
+ mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ } else {
+ mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ }
+ mServerProxy = mAudioTrackServerProxy;
+ // to avoid leaking a track name, do not allocate one unless there is an mCblk
+ mName = thread->getTrackName_l(channelMask, sessionId);
+ if (mName < 0) {
+ ALOGE("no more track names available");
+ return;
+ }
+ // only allocate a fast track index if we were able to allocate a normal track name
+ if (flags & IAudioFlinger::TRACK_FAST) {
+ mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();
+ ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
+ int i = __builtin_ctz(thread->mFastTrackAvailMask);
+ ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks);
+ // FIXME This is too eager. We allocate a fast track index before the
+ // fast track becomes active. Since fast tracks are a scarce resource,
+ // this means we are potentially denying other more important fast tracks from
+ // being created. It would be better to allocate the index dynamically.
+ mFastIndex = i;
+ // Read the initial underruns because this field is never cleared by the fast mixer
+ mObservedUnderruns = thread->getFastTrackUnderruns(i);
+ thread->mFastTrackAvailMask &= ~(1 << i);
+ }
+ }
+ ALOGV("Track constructor name %d, calling pid %d", mName,
+ IPCThreadState::self()->getCallingPid());
+}
+
+AudioFlinger::PlaybackThread::Track::~Track()
+{
+ ALOGV("PlaybackThread::Track destructor");
+
+ // The destructor would clear mSharedBuffer,
+ // but it will not push the decremented reference count,
+ // leaving the client's IMemory dangling indefinitely.
+ // This prevents that leak.
+ if (mSharedBuffer != 0) {
+ mSharedBuffer.clear();
+ // flush the binder command buffer
+ IPCThreadState::self()->flushCommands();
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::destroy()
+{
+ // NOTE: destroyTrack_l() can remove a strong reference to this Track
+ // by removing it from mTracks vector, so there is a risk that this Tracks's
+ // destructor is called. As the destructor needs to lock mLock,
+ // we must acquire a strong reference on this Track before locking mLock
+ // here so that the destructor is called only when exiting this function.
+ // On the other hand, as long as Track::destroy() is only called by
+ // TrackHandle destructor, the TrackHandle still holds a strong ref on
+ // this Track with its member mTrack.
+ sp<Track> keep(this);
+ { // scope for mLock
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ bool wasActive = playbackThread->destroyTrack_l(this);
+ if (!isOutputTrack() && !wasActive) {
+ AudioSystem::releaseOutput(thread->id());
+ }
+ }
+ }
+}
+
+/*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
+{
+ result.append(" Name Client Type Fmt Chn mask Session fCount S F SRate "
+ "L dB R dB Server Main buf Aux Buf Flags UndFrmCnt\n");
+}
+
+void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
+{
+ uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
+ if (isFastTrack()) {
+ sprintf(buffer, " F %2d", mFastIndex);
+ } else {
+ sprintf(buffer, " %4d", mName - AudioMixer::TRACK0);
+ }
+ track_state state = mState;
+ char stateChar;
+ if (isTerminated()) {
+ stateChar = 'T';
+ } else {
+ switch (state) {
+ case IDLE:
+ stateChar = 'I';
+ break;
+ case STOPPING_1:
+ stateChar = 's';
+ break;
+ case STOPPING_2:
+ stateChar = '5';
+ break;
+ case STOPPED:
+ stateChar = 'S';
+ break;
+ case RESUMING:
+ stateChar = 'R';
+ break;
+ case ACTIVE:
+ stateChar = 'A';
+ break;
+ case PAUSING:
+ stateChar = 'p';
+ break;
+ case PAUSED:
+ stateChar = 'P';
+ break;
+ case FLUSHED:
+ stateChar = 'F';
+ break;
+ default:
+ stateChar = '?';
+ break;
+ }
+ }
+ char nowInUnderrun;
+ switch (mObservedUnderruns.mBitFields.mMostRecent) {
+ case UNDERRUN_FULL:
+ nowInUnderrun = ' ';
+ break;
+ case UNDERRUN_PARTIAL:
+ nowInUnderrun = '<';
+ break;
+ case UNDERRUN_EMPTY:
+ nowInUnderrun = '*';
+ break;
+ default:
+ nowInUnderrun = '?';
+ break;
+ }
+ snprintf(&buffer[7], size-7, " %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g "
+ "%08X %p %p 0x%03X %9u%c\n",
+ (mClient == 0) ? getpid_cached : mClient->pid(),
+ mStreamType,
+ mFormat,
+ mChannelMask,
+ mSessionId,
+ mFrameCount,
+ stateChar,
+ mFillingUpStatus,
+ mAudioTrackServerProxy->getSampleRate(),
+ 20.0 * log10((vlr & 0xFFFF) / 4096.0),
+ 20.0 * log10((vlr >> 16) / 4096.0),
+ mCblk->mServer,
+ mMainBuffer,
+ mAuxBuffer,
+ mCblk->mFlags,
+ mAudioTrackServerProxy->getUnderrunFrames(),
+ nowInUnderrun);
+}
+
+uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const {
+ return mAudioTrackServerProxy->getSampleRate();
+}
+
+// AudioBufferProvider interface
+status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
+ AudioBufferProvider::Buffer* buffer, int64_t pts)
+{
+ ServerProxy::Buffer buf;
+ size_t desiredFrames = buffer->frameCount;
+ buf.mFrameCount = desiredFrames;
+ status_t status = mServerProxy->obtainBuffer(&buf);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ if (buf.mFrameCount == 0) {
+ mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
+ }
+ return status;
+}
+
+// releaseBuffer() is not overridden
+
+// ExtendedAudioBufferProvider interface
+
+// Note that framesReady() takes a mutex on the control block using tryLock().
+// This could result in priority inversion if framesReady() is called by the normal mixer,
+// as the normal mixer thread runs at lower
+// priority than the client's callback thread: there is a short window within framesReady()
+// during which the normal mixer could be preempted, and the client callback would block.
+// Another problem can occur if framesReady() is called by the fast mixer:
+// the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer.
+// FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue.
+size_t AudioFlinger::PlaybackThread::Track::framesReady() const {
+ return mAudioTrackServerProxy->framesReady();
+}
+
+size_t AudioFlinger::PlaybackThread::Track::framesReleased() const
+{
+ return mAudioTrackServerProxy->framesReleased();
+}
+
+// Don't call for fast tracks; the framesReady() could result in priority inversion
+bool AudioFlinger::PlaybackThread::Track::isReady() const {
+ if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing()) {
+ return true;
+ }
+
+ if (framesReady() >= mFrameCount ||
+ (mCblk->mFlags & CBLK_FORCEREADY)) {
+ mFillingUpStatus = FS_FILLED;
+ android_atomic_and(~CBLK_FORCEREADY, &mCblk->mFlags);
+ return true;
+ }
+ return false;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event,
+ 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) {
+ if (isOffloaded()) {
+ Mutex::Autolock _laf(thread->mAudioFlinger->mLock);
+ Mutex::Autolock _lth(thread->mLock);
+ sp<EffectChain> ec = thread->getEffectChain_l(mSessionId);
+ if (thread->mAudioFlinger->isNonOffloadableGlobalEffectEnabled_l() ||
+ (ec != 0 && ec->isNonOffloadableEnabled())) {
+ invalidate();
+ return PERMISSION_DENIED;
+ }
+ }
+ Mutex::Autolock _lth(thread->mLock);
+ track_state state = mState;
+ // here the track could be either new, or restarted
+ // in both cases "unstop" the track
+
+ if (state == PAUSED) {
+ if (mResumeToStopping) {
+ // happened we need to resume to STOPPING_1
+ mState = TrackBase::STOPPING_1;
+ ALOGV("PAUSED => STOPPING_1 (%d) on thread %p", mName, this);
+ } else {
+ mState = TrackBase::RESUMING;
+ ALOGV("PAUSED => RESUMING (%d) on thread %p", mName, this);
+ }
+ } else {
+ mState = TrackBase::ACTIVE;
+ ALOGV("? => ACTIVE (%d) on thread %p", mName, this);
+ }
+
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ status = playbackThread->addTrack_l(this);
+ if (status == INVALID_OPERATION || status == PERMISSION_DENIED) {
+ triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ // restore previous state if start was rejected by policy manager
+ if (status == PERMISSION_DENIED) {
+ mState = state;
+ }
+ }
+ // track was already in the active list, not a problem
+ if (status == ALREADY_EXISTS) {
+ status = NO_ERROR;
+ } else {
+ // Acknowledge any pending flush(), so that subsequent new data isn't discarded.
+ // It is usually unsafe to access the server proxy from a binder thread.
+ // But in this case we know the mixer thread (whether normal mixer or fast mixer)
+ // isn't looking at this track yet: we still hold the normal mixer thread lock,
+ // and for fast tracks the track is not yet in the fast mixer thread's active set.
+ ServerProxy::Buffer buffer;
+ buffer.mFrameCount = 1;
+ (void) mAudioTrackServerProxy->obtainBuffer(&buffer, true /*ackFlush*/);
+ }
+ } else {
+ status = BAD_VALUE;
+ }
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::Track::stop()
+{
+ ALOGV("stop(%d), calling pid %d", mName, IPCThreadState::self()->getCallingPid());
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ track_state state = mState;
+ if (state == RESUMING || state == ACTIVE || state == PAUSING || state == PAUSED) {
+ // If the track is not active (PAUSED and buffers full), flush buffers
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ reset();
+ mState = STOPPED;
+ } else if (!isFastTrack() && !isOffloaded()) {
+ mState = STOPPED;
+ } else {
+ // For fast tracks prepareTracks_l() will set state to STOPPING_2
+ // presentation is complete
+ // For an offloaded track this starts a drain and state will
+ // move to STOPPING_2 when drain completes and then STOPPED
+ mState = STOPPING_1;
+ }
+ ALOGV("not stopping/stopped => stopping/stopped (%d) on thread %p", mName,
+ playbackThread);
+ }
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::pause()
+{
+ ALOGV("pause(%d), calling pid %d", mName, IPCThreadState::self()->getCallingPid());
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ switch (mState) {
+ case STOPPING_1:
+ case STOPPING_2:
+ if (!isOffloaded()) {
+ /* nothing to do if track is not offloaded */
+ break;
+ }
+
+ // Offloaded track was draining, we need to carry on draining when resumed
+ mResumeToStopping = true;
+ // fall through...
+ case ACTIVE:
+ case RESUMING:
+ mState = PAUSING;
+ ALOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
+ playbackThread->broadcast_l();
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::flush()
+{
+ ALOGV("flush(%d)", mName);
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+
+ if (isOffloaded()) {
+ // If offloaded we allow flush during any state except terminated
+ // and keep the track active to avoid problems if user is seeking
+ // rapidly and underlying hardware has a significant delay handling
+ // a pause
+ if (isTerminated()) {
+ return;
+ }
+
+ ALOGV("flush: offload flush");
+ reset();
+
+ if (mState == STOPPING_1 || mState == STOPPING_2) {
+ ALOGV("flushed in STOPPING_1 or 2 state, change state to ACTIVE");
+ mState = ACTIVE;
+ }
+
+ if (mState == ACTIVE) {
+ ALOGV("flush called in active state, resetting buffer time out retry count");
+ mRetryCount = PlaybackThread::kMaxTrackRetriesOffload;
+ }
+
+ mResumeToStopping = false;
+ } else {
+ if (mState != STOPPING_1 && mState != STOPPING_2 && mState != STOPPED &&
+ mState != PAUSED && mState != PAUSING && mState != IDLE && mState != FLUSHED) {
+ return;
+ }
+ // No point remaining in PAUSED state after a flush => go to
+ // FLUSHED state
+ mState = FLUSHED;
+ // do not reset the track if it is still in the process of being stopped or paused.
+ // this will be done by prepareTracks_l() when the track is stopped.
+ // prepareTracks_l() will see mState == FLUSHED, then
+ // remove from active track list, reset(), and trigger presentation complete
+ if (playbackThread->mActiveTracks.indexOf(this) < 0) {
+ reset();
+ }
+ }
+ // Prevent flush being lost if the track is flushed and then resumed
+ // before mixer thread can run. This is important when offloading
+ // because the hardware buffer could hold a large amount of audio
+ playbackThread->flushOutput_l();
+ playbackThread->broadcast_l();
+ }
+}
+
+void AudioFlinger::PlaybackThread::Track::reset()
+{
+ // Do not reset twice to avoid discarding data written just after a flush and before
+ // the audioflinger thread detects the track is stopped.
+ if (!mResetDone) {
+ // Force underrun condition to avoid false underrun callback until first data is
+ // written to buffer
+ android_atomic_and(~CBLK_FORCEREADY, &mCblk->mFlags);
+ mFillingUpStatus = FS_FILLING;
+ mResetDone = true;
+ if (mState == FLUSHED) {
+ mState = IDLE;
+ }
+ }
+}
+
+status_t AudioFlinger::PlaybackThread::Track::setParameters(const String8& keyValuePairs)
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ ALOGE("thread is dead");
+ return FAILED_TRANSACTION;
+ } else if ((thread->type() == ThreadBase::DIRECT) ||
+ (thread->type() == ThreadBase::OFFLOAD)) {
+ return thread->setParameters(keyValuePairs);
+ } else {
+ return PERMISSION_DENIED;
+ }
+}
+
+status_t AudioFlinger::PlaybackThread::Track::getTimestamp(AudioTimestamp& timestamp)
+{
+ // Client should implement this using SSQ; the unpresented frame count in latch is irrelevant
+ if (isFastTrack()) {
+ return INVALID_OPERATION;
+ }
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread == 0) {
+ return INVALID_OPERATION;
+ }
+ Mutex::Autolock _l(thread->mLock);
+ PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
+ if (!isOffloaded()) {
+ if (!playbackThread->mLatchQValid) {
+ return INVALID_OPERATION;
+ }
+ uint32_t unpresentedFrames =
+ ((int64_t) playbackThread->mLatchQ.mUnpresentedFrames * mSampleRate) /
+ playbackThread->mSampleRate;
+ uint32_t framesWritten = mAudioTrackServerProxy->framesReleased();
+ if (framesWritten < unpresentedFrames) {
+ return INVALID_OPERATION;
+ }
+ timestamp.mPosition = framesWritten - unpresentedFrames;
+ timestamp.mTime = playbackThread->mLatchQ.mTimestamp.mTime;
+ return NO_ERROR;
+ }
+
+ return playbackThread->getTimestamp_l(timestamp);
+}
+
+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();
+ sp<AudioFlinger> af = mClient->audioFlinger();
+
+ Mutex::Autolock _l(af->mLock);
+
+ sp<PlaybackThread> srcThread = af->getEffectThread_l(AUDIO_SESSION_OUTPUT_MIX, EffectId);
+
+ if (EffectId != 0 && srcThread != 0 && playbackThread != srcThread.get()) {
+ Mutex::Autolock _dl(playbackThread->mLock);
+ Mutex::Autolock _sl(srcThread->mLock);
+ sp<EffectChain> chain = srcThread->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+ if (chain == 0) {
+ return INVALID_OPERATION;
+ }
+
+ sp<EffectModule> effect = chain->getEffectFromId_l(EffectId);
+ if (effect == 0) {
+ return INVALID_OPERATION;
+ }
+ srcThread->removeEffect_l(effect);
+ status = playbackThread->addEffect_l(effect);
+ if (status != NO_ERROR) {
+ srcThread->addEffect_l(effect);
+ return INVALID_OPERATION;
+ }
+ // removeEffect_l() has stopped the effect if it was active so it must be restarted
+ if (effect->state() == EffectModule::ACTIVE ||
+ effect->state() == EffectModule::STOPPING) {
+ effect->start();
+ }
+
+ sp<EffectChain> dstChain = effect->chain().promote();
+ if (dstChain == 0) {
+ srcThread->addEffect_l(effect);
+ return INVALID_OPERATION;
+ }
+ AudioSystem::unregisterEffect(effect->id());
+ AudioSystem::registerEffect(&effect->desc(),
+ srcThread->id(),
+ dstChain->strategy(),
+ AUDIO_SESSION_OUTPUT_MIX,
+ effect->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
+ }
+ status = playbackThread->attachAuxEffect(this, EffectId);
+ }
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::Track::setAuxBuffer(int EffectId, int32_t *buffer)
+{
+ mAuxEffectId = EffectId;
+ mAuxBuffer = buffer;
+}
+
+bool AudioFlinger::PlaybackThread::Track::presentationComplete(size_t framesWritten,
+ size_t audioHalFrames)
+{
+ // a track is considered presented when the total number of frames written to audio HAL
+ // corresponds to the number of frames written when presentationComplete() is called for the
+ // first time (mPresentationCompleteFrames == 0) plus the buffer filling status at that time.
+ // For an offloaded track the HAL+h/w delay is variable so a HAL drain() is used
+ // to detect when all frames have been played. In this case framesWritten isn't
+ // useful because it doesn't always reflect whether there is data in the h/w
+ // buffers, particularly if a track has been paused and resumed during draining
+ ALOGV("presentationComplete() mPresentationCompleteFrames %d framesWritten %d",
+ mPresentationCompleteFrames, framesWritten);
+ if (mPresentationCompleteFrames == 0) {
+ mPresentationCompleteFrames = framesWritten + audioHalFrames;
+ ALOGV("presentationComplete() reset: mPresentationCompleteFrames %d audioHalFrames %d",
+ mPresentationCompleteFrames, audioHalFrames);
+ }
+
+ if (framesWritten >= mPresentationCompleteFrames || isOffloaded()) {
+ ALOGV("presentationComplete() session %d complete: framesWritten %d",
+ mSessionId, framesWritten);
+ triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
+ mAudioTrackServerProxy->setStreamEndDone();
+ return true;
+ }
+ return false;
+}
+
+void AudioFlinger::PlaybackThread::Track::triggerEvents(AudioSystem::sync_event_t type)
+{
+ for (int i = 0; i < (int)mSyncEvents.size(); i++) {
+ if (mSyncEvents[i]->type() == type) {
+ mSyncEvents[i]->trigger();
+ mSyncEvents.removeAt(i);
+ i--;
+ }
+ }
+}
+
+// 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 = mAudioTrackServerProxy->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;
+}
+
+status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>& event)
+{
+ if (isTerminated() || mState == PAUSED ||
+ ((framesReady() == 0) && ((mSharedBuffer != 0) ||
+ (mState == STOPPED)))) {
+ ALOGW("Track::setSyncEvent() in invalid state %d on session %d %s mode, framesReady %d ",
+ mState, mSessionId, (mSharedBuffer != 0) ? "static" : "stream", framesReady());
+ event->cancel();
+ return INVALID_OPERATION;
+ }
+ (void) TrackBase::setSyncEvent(event);
+ return NO_ERROR;
+}
+
+void AudioFlinger::PlaybackThread::Track::invalidate()
+{
+ // FIXME should use proxy, and needs work
+ audio_track_cblk_t* cblk = mCblk;
+ android_atomic_or(CBLK_INVALID, &cblk->mFlags);
+ android_atomic_release_store(0x40000000, &cblk->mFutex);
+ // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
+ (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
+ mIsInvalid = true;
+}
+
+void AudioFlinger::PlaybackThread::Track::signal()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ Mutex::Autolock _l(t->mLock);
+ t->broadcast_l();
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+sp<AudioFlinger::PlaybackThread::TimedTrack>
+AudioFlinger::PlaybackThread::TimedTrack::create(
+ PlaybackThread *thread,
+ const sp<Client>& client,
+ audio_stream_type_t streamType,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ int uid) {
+ if (!client->reserveTimedTrack())
+ return 0;
+
+ return new TimedTrack(
+ thread, client, streamType, sampleRate, format, channelMask, frameCount,
+ sharedBuffer, sessionId, uid);
+}
+
+AudioFlinger::PlaybackThread::TimedTrack::TimedTrack(
+ PlaybackThread *thread,
+ const sp<Client>& client,
+ audio_stream_type_t streamType,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ const sp<IMemory>& sharedBuffer,
+ int sessionId,
+ int uid)
+ : Track(thread, client, streamType, sampleRate, format, channelMask,
+ frameCount, sharedBuffer, sessionId, uid, IAudioFlinger::TRACK_TIMED),
+ mQueueHeadInFlight(false),
+ mTrimQueueHeadOnRelease(false),
+ mFramesPendingInQueue(0),
+ mTimedSilenceBuffer(NULL),
+ mTimedSilenceBufferSize(0),
+ mTimedAudioOutputOnTime(false),
+ mMediaTimeTransformValid(false)
+{
+ LocalClock lc;
+ mLocalTimeFreq = lc.getLocalFreq();
+
+ mLocalTimeToSampleTransform.a_zero = 0;
+ mLocalTimeToSampleTransform.b_zero = 0;
+ mLocalTimeToSampleTransform.a_to_b_numer = sampleRate;
+ mLocalTimeToSampleTransform.a_to_b_denom = mLocalTimeFreq;
+ LinearTransform::reduce(&mLocalTimeToSampleTransform.a_to_b_numer,
+ &mLocalTimeToSampleTransform.a_to_b_denom);
+
+ mMediaTimeToSampleTransform.a_zero = 0;
+ mMediaTimeToSampleTransform.b_zero = 0;
+ mMediaTimeToSampleTransform.a_to_b_numer = sampleRate;
+ mMediaTimeToSampleTransform.a_to_b_denom = 1000000;
+ LinearTransform::reduce(&mMediaTimeToSampleTransform.a_to_b_numer,
+ &mMediaTimeToSampleTransform.a_to_b_denom);
+}
+
+AudioFlinger::PlaybackThread::TimedTrack::~TimedTrack() {
+ mClient->releaseTimedTrack();
+ delete [] mTimedSilenceBuffer;
+}
+
+status_t AudioFlinger::PlaybackThread::TimedTrack::allocateTimedBuffer(
+ size_t size, sp<IMemory>* buffer) {
+
+ Mutex::Autolock _l(mTimedBufferQueueLock);
+
+ trimTimedBufferQueue_l();
+
+ // lazily initialize the shared memory heap for timed buffers
+ if (mTimedMemoryDealer == NULL) {
+ const int kTimedBufferHeapSize = 512 << 10;
+
+ mTimedMemoryDealer = new MemoryDealer(kTimedBufferHeapSize,
+ "AudioFlingerTimed");
+ if (mTimedMemoryDealer == NULL)
+ return NO_MEMORY;
+ }
+
+ sp<IMemory> newBuffer = mTimedMemoryDealer->allocate(size);
+ if (newBuffer == NULL) {
+ newBuffer = mTimedMemoryDealer->allocate(size);
+ if (newBuffer == NULL)
+ return NO_MEMORY;
+ }
+
+ *buffer = newBuffer;
+ return NO_ERROR;
+}
+
+// caller must hold mTimedBufferQueueLock
+void AudioFlinger::PlaybackThread::TimedTrack::trimTimedBufferQueue_l() {
+ int64_t mediaTimeNow;
+ {
+ Mutex::Autolock mttLock(mMediaTimeTransformLock);
+ if (!mMediaTimeTransformValid)
+ return;
+
+ int64_t targetTimeNow;
+ status_t res = (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME)
+ ? mCCHelper.getCommonTime(&targetTimeNow)
+ : mCCHelper.getLocalTime(&targetTimeNow);
+
+ if (OK != res)
+ return;
+
+ if (!mMediaTimeTransform.doReverseTransform(targetTimeNow,
+ &mediaTimeNow)) {
+ return;
+ }
+ }
+
+ size_t trimEnd;
+ for (trimEnd = 0; trimEnd < mTimedBufferQueue.size(); trimEnd++) {
+ int64_t bufEnd;
+
+ if ((trimEnd + 1) < mTimedBufferQueue.size()) {
+ // We have a next buffer. Just use its PTS as the PTS of the frame
+ // following the last frame in this buffer. If the stream is sparse
+ // (ie, there are deliberate gaps left in the stream which should be
+ // filled with silence by the TimedAudioTrack), then this can result
+ // in one extra buffer being left un-trimmed when it could have
+ // been. In general, this is not typical, and we would rather
+ // optimized away the TS calculation below for the more common case
+ // where PTSes are contiguous.
+ bufEnd = mTimedBufferQueue[trimEnd + 1].pts();
+ } else {
+ // We have no next buffer. Compute the PTS of the frame following
+ // the last frame in this buffer by computing the duration of of
+ // this frame in media time units and adding it to the PTS of the
+ // buffer.
+ int64_t frameCount = mTimedBufferQueue[trimEnd].buffer()->size()
+ / mFrameSize;
+
+ if (!mMediaTimeToSampleTransform.doReverseTransform(frameCount,
+ &bufEnd)) {
+ ALOGE("Failed to convert frame count of %lld to media time"
+ " duration" " (scale factor %d/%u) in %s",
+ frameCount,
+ mMediaTimeToSampleTransform.a_to_b_numer,
+ mMediaTimeToSampleTransform.a_to_b_denom,
+ __PRETTY_FUNCTION__);
+ break;
+ }
+ bufEnd += mTimedBufferQueue[trimEnd].pts();
+ }
+
+ if (bufEnd > mediaTimeNow)
+ break;
+
+ // Is the buffer we want to use in the middle of a mix operation right
+ // now? If so, don't actually trim it. Just wait for the releaseBuffer
+ // from the mixer which should be coming back shortly.
+ if (!trimEnd && mQueueHeadInFlight) {
+ mTrimQueueHeadOnRelease = true;
+ }
+ }
+
+ size_t trimStart = mTrimQueueHeadOnRelease ? 1 : 0;
+ if (trimStart < trimEnd) {
+ // Update the bookkeeping for framesReady()
+ for (size_t i = trimStart; i < trimEnd; ++i) {
+ updateFramesPendingAfterTrim_l(mTimedBufferQueue[i], "trim");
+ }
+
+ // Now actually remove the buffers from the queue.
+ mTimedBufferQueue.removeItemsAt(trimStart, trimEnd);
+ }
+}
+
+void AudioFlinger::PlaybackThread::TimedTrack::trimTimedBufferQueueHead_l(
+ const char* logTag) {
+ ALOG_ASSERT(mTimedBufferQueue.size() > 0,
+ "%s called (reason \"%s\"), but timed buffer queue has no"
+ " elements to trim.", __FUNCTION__, logTag);
+
+ updateFramesPendingAfterTrim_l(mTimedBufferQueue[0], logTag);
+ mTimedBufferQueue.removeAt(0);
+}
+
+void AudioFlinger::PlaybackThread::TimedTrack::updateFramesPendingAfterTrim_l(
+ const TimedBuffer& buf,
+ const char* logTag) {
+ uint32_t bufBytes = buf.buffer()->size();
+ uint32_t consumedAlready = buf.position();
+
+ ALOG_ASSERT(consumedAlready <= bufBytes,
+ "Bad bookkeeping while updating frames pending. Timed buffer is"
+ " only %u bytes long, but claims to have consumed %u"
+ " bytes. (update reason: \"%s\")",
+ bufBytes, consumedAlready, logTag);
+
+ uint32_t bufFrames = (bufBytes - consumedAlready) / mFrameSize;
+ ALOG_ASSERT(mFramesPendingInQueue >= bufFrames,
+ "Bad bookkeeping while updating frames pending. Should have at"
+ " least %u queued frames, but we think we have only %u. (update"
+ " reason: \"%s\")",
+ bufFrames, mFramesPendingInQueue, logTag);
+
+ mFramesPendingInQueue -= bufFrames;
+}
+
+status_t AudioFlinger::PlaybackThread::TimedTrack::queueTimedBuffer(
+ const sp<IMemory>& buffer, int64_t pts) {
+
+ {
+ Mutex::Autolock mttLock(mMediaTimeTransformLock);
+ if (!mMediaTimeTransformValid)
+ return INVALID_OPERATION;
+ }
+
+ Mutex::Autolock _l(mTimedBufferQueueLock);
+
+ uint32_t bufFrames = buffer->size() / mFrameSize;
+ mFramesPendingInQueue += bufFrames;
+ mTimedBufferQueue.add(TimedBuffer(buffer, pts));
+
+ return NO_ERROR;
+}
+
+status_t AudioFlinger::PlaybackThread::TimedTrack::setMediaTimeTransform(
+ const LinearTransform& xform, TimedAudioTrack::TargetTimeline target) {
+
+ ALOGVV("setMediaTimeTransform az=%lld bz=%lld n=%d d=%u tgt=%d",
+ xform.a_zero, xform.b_zero, xform.a_to_b_numer, xform.a_to_b_denom,
+ target);
+
+ if (!(target == TimedAudioTrack::LOCAL_TIME ||
+ target == TimedAudioTrack::COMMON_TIME)) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mMediaTimeTransformLock);
+ mMediaTimeTransform = xform;
+ mMediaTimeTransformTarget = target;
+ mMediaTimeTransformValid = true;
+
+ return NO_ERROR;
+}
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+// implementation of getNextBuffer for tracks whose buffers have timestamps
+status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer(
+ AudioBufferProvider::Buffer* buffer, int64_t pts)
+{
+ if (pts == AudioBufferProvider::kInvalidPTS) {
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ mTimedAudioOutputOnTime = false;
+ return INVALID_OPERATION;
+ }
+
+ Mutex::Autolock _l(mTimedBufferQueueLock);
+
+ ALOG_ASSERT(!mQueueHeadInFlight,
+ "getNextBuffer called without releaseBuffer!");
+
+ while (true) {
+
+ // if we have no timed buffers, then fail
+ if (mTimedBufferQueue.isEmpty()) {
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ return NOT_ENOUGH_DATA;
+ }
+
+ TimedBuffer& head = mTimedBufferQueue.editItemAt(0);
+
+ // calculate the PTS of the head of the timed buffer queue expressed in
+ // local time
+ int64_t headLocalPTS;
+ {
+ Mutex::Autolock mttLock(mMediaTimeTransformLock);
+
+ ALOG_ASSERT(mMediaTimeTransformValid, "media time transform invalid");
+
+ if (mMediaTimeTransform.a_to_b_denom == 0) {
+ // the transform represents a pause, so yield silence
+ timedYieldSilence_l(buffer->frameCount, buffer);
+ return NO_ERROR;
+ }
+
+ int64_t transformedPTS;
+ if (!mMediaTimeTransform.doForwardTransform(head.pts(),
+ &transformedPTS)) {
+ // the transform failed. this shouldn't happen, but if it does
+ // then just drop this buffer
+ ALOGW("timedGetNextBuffer transform failed");
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ trimTimedBufferQueueHead_l("getNextBuffer; no transform");
+ return NO_ERROR;
+ }
+
+ if (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME) {
+ if (OK != mCCHelper.commonTimeToLocalTime(transformedPTS,
+ &headLocalPTS)) {
+ buffer->raw = NULL;
+ buffer->frameCount = 0;
+ return INVALID_OPERATION;
+ }
+ } else {
+ headLocalPTS = transformedPTS;
+ }
+ }
+
+ uint32_t sr = sampleRate();
+
+ // adjust the head buffer's PTS to reflect the portion of the head buffer
+ // that has already been consumed
+ int64_t effectivePTS = headLocalPTS +
+ ((head.position() / mFrameSize) * mLocalTimeFreq / sr);
+
+ // Calculate the delta in samples between the head of the input buffer
+ // queue and the start of the next output buffer that will be written.
+ // If the transformation fails because of over or underflow, it means
+ // that the sample's position in the output stream is so far out of
+ // whack that it should just be dropped.
+ int64_t sampleDelta;
+ if (llabs(effectivePTS - pts) >= (static_cast<int64_t>(1) << 31)) {
+ ALOGV("*** head buffer is too far from PTS: dropped buffer");
+ trimTimedBufferQueueHead_l("getNextBuffer, buf pts too far from"
+ " mix");
+ continue;
+ }
+ if (!mLocalTimeToSampleTransform.doForwardTransform(
+ (effectivePTS - pts) << 32, &sampleDelta)) {
+ ALOGV("*** too late during sample rate transform: dropped buffer");
+ trimTimedBufferQueueHead_l("getNextBuffer, bad local to sample");
+ continue;
+ }
+
+ ALOGVV("*** getNextBuffer head.pts=%lld head.pos=%d pts=%lld"
+ " sampleDelta=[%d.%08x]",
+ head.pts(), head.position(), pts,
+ static_cast<int32_t>((sampleDelta >= 0 ? 0 : 1)
+ + (sampleDelta >> 32)),
+ static_cast<uint32_t>(sampleDelta & 0xFFFFFFFF));
+
+ // if the delta between the ideal placement for the next input sample and
+ // the current output position is within this threshold, then we will
+ // concatenate the next input samples to the previous output
+ const int64_t kSampleContinuityThreshold =
+ (static_cast<int64_t>(sr) << 32) / 250;
+
+ // if this is the first buffer of audio that we're emitting from this track
+ // then it should be almost exactly on time.
+ const int64_t kSampleStartupThreshold = 1LL << 32;
+
+ if ((mTimedAudioOutputOnTime && llabs(sampleDelta) <= kSampleContinuityThreshold) ||
+ (!mTimedAudioOutputOnTime && llabs(sampleDelta) <= kSampleStartupThreshold)) {
+ // the next input is close enough to being on time, so concatenate it
+ // with the last output
+ timedYieldSamples_l(buffer);
+
+ ALOGVV("*** on time: head.pos=%d frameCount=%u",
+ head.position(), buffer->frameCount);
+ return NO_ERROR;
+ }
+
+ // Looks like our output is not on time. Reset our on timed status.
+ // Next time we mix samples from our input queue, then should be within
+ // the StartupThreshold.
+ mTimedAudioOutputOnTime = false;
+ if (sampleDelta > 0) {
+ // the gap between the current output position and the proper start of
+ // the next input sample is too big, so fill it with silence
+ uint32_t framesUntilNextInput = (sampleDelta + 0x80000000) >> 32;
+
+ timedYieldSilence_l(framesUntilNextInput, buffer);
+ ALOGV("*** silence: frameCount=%u", buffer->frameCount);
+ return NO_ERROR;
+ } else {
+ // the next input sample is late
+ uint32_t lateFrames = static_cast<uint32_t>(-((sampleDelta + 0x80000000) >> 32));
+ size_t onTimeSamplePosition =
+ head.position() + lateFrames * mFrameSize;
+
+ if (onTimeSamplePosition > head.buffer()->size()) {
+ // all the remaining samples in the head are too late, so
+ // drop it and move on
+ ALOGV("*** too late: dropped buffer");
+ trimTimedBufferQueueHead_l("getNextBuffer, dropped late buffer");
+ continue;
+ } else {
+ // skip over the late samples
+ head.setPosition(onTimeSamplePosition);
+
+ // yield the available samples
+ timedYieldSamples_l(buffer);
+
+ ALOGV("*** late: head.pos=%d frameCount=%u", head.position(), buffer->frameCount);
+ return NO_ERROR;
+ }
+ }
+ }
+}
+
+// Yield samples from the timed buffer queue head up to the given output
+// buffer's capacity.
+//
+// Caller must hold mTimedBufferQueueLock
+void AudioFlinger::PlaybackThread::TimedTrack::timedYieldSamples_l(
+ AudioBufferProvider::Buffer* buffer) {
+
+ const TimedBuffer& head = mTimedBufferQueue[0];
+
+ buffer->raw = (static_cast<uint8_t*>(head.buffer()->pointer()) +
+ head.position());
+
+ uint32_t framesLeftInHead = ((head.buffer()->size() - head.position()) /
+ mFrameSize);
+ size_t framesRequested = buffer->frameCount;
+ buffer->frameCount = min(framesLeftInHead, framesRequested);
+
+ mQueueHeadInFlight = true;
+ mTimedAudioOutputOnTime = true;
+}
+
+// Yield samples of silence up to the given output buffer's capacity
+//
+// Caller must hold mTimedBufferQueueLock
+void AudioFlinger::PlaybackThread::TimedTrack::timedYieldSilence_l(
+ uint32_t numFrames, AudioBufferProvider::Buffer* buffer) {
+
+ // lazily allocate a buffer filled with silence
+ if (mTimedSilenceBufferSize < numFrames * mFrameSize) {
+ delete [] mTimedSilenceBuffer;
+ mTimedSilenceBufferSize = numFrames * mFrameSize;
+ mTimedSilenceBuffer = new uint8_t[mTimedSilenceBufferSize];
+ memset(mTimedSilenceBuffer, 0, mTimedSilenceBufferSize);
+ }
+
+ buffer->raw = mTimedSilenceBuffer;
+ size_t framesRequested = buffer->frameCount;
+ buffer->frameCount = min(numFrames, framesRequested);
+
+ mTimedAudioOutputOnTime = false;
+}
+
+// AudioBufferProvider interface
+void AudioFlinger::PlaybackThread::TimedTrack::releaseBuffer(
+ AudioBufferProvider::Buffer* buffer) {
+
+ Mutex::Autolock _l(mTimedBufferQueueLock);
+
+ // If the buffer which was just released is part of the buffer at the head
+ // of the queue, be sure to update the amt of the buffer which has been
+ // consumed. If the buffer being returned is not part of the head of the
+ // queue, its either because the buffer is part of the silence buffer, or
+ // because the head of the timed queue was trimmed after the mixer called
+ // getNextBuffer but before the mixer called releaseBuffer.
+ if (buffer->raw == mTimedSilenceBuffer) {
+ ALOG_ASSERT(!mQueueHeadInFlight,
+ "Queue head in flight during release of silence buffer!");
+ goto done;
+ }
+
+ ALOG_ASSERT(mQueueHeadInFlight,
+ "TimedTrack::releaseBuffer of non-silence buffer, but no queue"
+ " head in flight.");
+
+ if (mTimedBufferQueue.size()) {
+ TimedBuffer& head = mTimedBufferQueue.editItemAt(0);
+
+ void* start = head.buffer()->pointer();
+ void* end = reinterpret_cast<void*>(
+ reinterpret_cast<uint8_t*>(head.buffer()->pointer())
+ + head.buffer()->size());
+
+ ALOG_ASSERT((buffer->raw >= start) && (buffer->raw < end),
+ "released buffer not within the head of the timed buffer"
+ " queue; qHead = [%p, %p], released buffer = %p",
+ start, end, buffer->raw);
+
+ head.setPosition(head.position() +
+ (buffer->frameCount * mFrameSize));
+ mQueueHeadInFlight = false;
+
+ ALOG_ASSERT(mFramesPendingInQueue >= buffer->frameCount,
+ "Bad bookkeeping during releaseBuffer! Should have at"
+ " least %u queued frames, but we think we have only %u",
+ buffer->frameCount, mFramesPendingInQueue);
+
+ mFramesPendingInQueue -= buffer->frameCount;
+
+ if ((static_cast<size_t>(head.position()) >= head.buffer()->size())
+ || mTrimQueueHeadOnRelease) {
+ trimTimedBufferQueueHead_l("releaseBuffer");
+ mTrimQueueHeadOnRelease = false;
+ }
+ } else {
+ LOG_FATAL("TimedTrack::releaseBuffer of non-silence buffer with no"
+ " buffers in the timed buffer queue");
+ }
+
+done:
+ buffer->raw = 0;
+ buffer->frameCount = 0;
+}
+
+size_t AudioFlinger::PlaybackThread::TimedTrack::framesReady() const {
+ Mutex::Autolock _l(mTimedBufferQueueLock);
+ return mFramesPendingInQueue;
+}
+
+AudioFlinger::PlaybackThread::TimedTrack::TimedBuffer::TimedBuffer()
+ : mPTS(0), mPosition(0) {}
+
+AudioFlinger::PlaybackThread::TimedTrack::TimedBuffer::TimedBuffer(
+ const sp<IMemory>& buffer, int64_t pts)
+ : mBuffer(buffer), mPTS(pts), mPosition(0) {}
+
+
+// ----------------------------------------------------------------------------
+
+AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
+ PlaybackThread *playbackThread,
+ DuplicatingThread *sourceThread,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ int uid)
+ : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
+ NULL, 0, uid, IAudioFlinger::TRACK_DEFAULT),
+ mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
+{
+
+ if (mCblk != NULL) {
+ mOutBuffer.frameCount = 0;
+ playbackThread->mTracks.add(this);
+ ALOGV("OutputTrack constructor mCblk %p, mBuffer %p, "
+ "mCblk->frameCount_ %u, mChannelMask 0x%08x",
+ mCblk, mBuffer,
+ mCblk->frameCount_, mChannelMask);
+ // since client and server are in the same process,
+ // the buffer has the same virtual address on both sides
+ mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize);
+ mClientProxy->setVolumeLR((uint32_t(uint16_t(0x1000)) << 16) | uint16_t(0x1000));
+ mClientProxy->setSendLevel(0.0);
+ mClientProxy->setSampleRate(sampleRate);
+ mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
+ true /*clientInServer*/);
+ } else {
+ ALOGW("Error creating output track on thread %p", playbackThread);
+ }
+}
+
+AudioFlinger::PlaybackThread::OutputTrack::~OutputTrack()
+{
+ clearBufferQueue();
+ delete mClientProxy;
+ // superclass destructor will now delete the server proxy and shared memory both refer to
+}
+
+status_t AudioFlinger::PlaybackThread::OutputTrack::start(AudioSystem::sync_event_t event,
+ int triggerSession)
+{
+ status_t status = Track::start(event, triggerSession);
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ mActive = true;
+ mRetryCount = 127;
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::OutputTrack::stop()
+{
+ Track::stop();
+ clearBufferQueue();
+ mOutBuffer.frameCount = 0;
+ mActive = false;
+}
+
+bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t frames)
+{
+ Buffer *pInBuffer;
+ Buffer inBuffer;
+ uint32_t channelCount = mChannelCount;
+ bool outputBufferFull = false;
+ inBuffer.frameCount = frames;
+ inBuffer.i16 = data;
+
+ uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
+
+ if (!mActive && frames != 0) {
+ start();
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ MixerThread *mixerThread = (MixerThread *)thread.get();
+ if (mFrameCount > frames) {
+ if (mBufferQueue.size() < kMaxOverFlowBuffers) {
+ uint32_t startFrames = (mFrameCount - frames);
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[startFrames * channelCount];
+ pInBuffer->frameCount = startFrames;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memset(pInBuffer->raw, 0, startFrames * channelCount * sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ } else {
+ ALOGW("OutputTrack::write() %p no more buffers in queue", this);
+ }
+ }
+ }
+ }
+
+ while (waitTimeLeftMs) {
+ // First write pending buffers, then new data
+ if (mBufferQueue.size()) {
+ pInBuffer = mBufferQueue.itemAt(0);
+ } else {
+ pInBuffer = &inBuffer;
+ }
+
+ if (pInBuffer->frameCount == 0) {
+ break;
+ }
+
+ if (mOutBuffer.frameCount == 0) {
+ mOutBuffer.frameCount = pInBuffer->frameCount;
+ nsecs_t startTime = systemTime();
+ status_t status = obtainBuffer(&mOutBuffer, waitTimeLeftMs);
+ if (status != NO_ERROR) {
+ ALOGV("OutputTrack::write() %p thread %p no more output buffers; status %d", this,
+ mThread.unsafe_get(), status);
+ outputBufferFull = true;
+ break;
+ }
+ uint32_t waitTimeMs = (uint32_t)ns2ms(systemTime() - startTime);
+ if (waitTimeLeftMs >= waitTimeMs) {
+ waitTimeLeftMs -= waitTimeMs;
+ } else {
+ waitTimeLeftMs = 0;
+ }
+ }
+
+ uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount :
+ pInBuffer->frameCount;
+ memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
+ Proxy::Buffer buf;
+ buf.mFrameCount = outFrames;
+ buf.mRaw = NULL;
+ mClientProxy->releaseBuffer(&buf);
+ pInBuffer->frameCount -= outFrames;
+ pInBuffer->i16 += outFrames * channelCount;
+ mOutBuffer.frameCount -= outFrames;
+ mOutBuffer.i16 += outFrames * channelCount;
+
+ if (pInBuffer->frameCount == 0) {
+ if (mBufferQueue.size()) {
+ mBufferQueue.removeAt(0);
+ delete [] pInBuffer->mBuffer;
+ delete pInBuffer;
+ ALOGV("OutputTrack::write() %p thread %p released overflow buffer %d", this,
+ mThread.unsafe_get(), mBufferQueue.size());
+ } else {
+ break;
+ }
+ }
+ }
+
+ // If we could not write all frames, allocate a buffer and queue it for next time.
+ if (inBuffer.frameCount) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0 && !thread->standby()) {
+ if (mBufferQueue.size() < kMaxOverFlowBuffers) {
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[inBuffer.frameCount * channelCount];
+ pInBuffer->frameCount = inBuffer.frameCount;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memcpy(pInBuffer->raw, inBuffer.raw, inBuffer.frameCount * channelCount *
+ sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ ALOGV("OutputTrack::write() %p thread %p adding overflow buffer %d", this,
+ mThread.unsafe_get(), mBufferQueue.size());
+ } else {
+ ALOGW("OutputTrack::write() %p thread %p no more overflow buffers",
+ mThread.unsafe_get(), this);
+ }
+ }
+ }
+
+ // Calling write() with a 0 length buffer, means that no more data will be written:
+ // If no more buffers are pending, fill output track buffer to make sure it is started
+ // by output mixer.
+ if (frames == 0 && mBufferQueue.size() == 0) {
+ // FIXME borken, replace by getting framesReady() from proxy
+ size_t user = 0; // was mCblk->user
+ if (user < mFrameCount) {
+ frames = mFrameCount - user;
+ pInBuffer = new Buffer;
+ pInBuffer->mBuffer = new int16_t[frames * channelCount];
+ pInBuffer->frameCount = frames;
+ pInBuffer->i16 = pInBuffer->mBuffer;
+ memset(pInBuffer->raw, 0, frames * channelCount * sizeof(int16_t));
+ mBufferQueue.add(pInBuffer);
+ } else if (mActive) {
+ stop();
+ }
+ }
+
+ return outputBufferFull;
+}
+
+status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(
+ AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
+{
+ ClientProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ struct timespec timeout;
+ timeout.tv_sec = waitTimeMs / 1000;
+ timeout.tv_nsec = (int) (waitTimeMs % 1000) * 1000000;
+ status_t status = mClientProxy->obtainBuffer(&buf, &timeout);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ return status;
+}
+
+void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
+{
+ size_t size = mBufferQueue.size();
+
+ for (size_t i = 0; i < size; i++) {
+ Buffer *pBuffer = mBufferQueue.itemAt(i);
+ delete [] pBuffer->mBuffer;
+ delete pBuffer;
+ }
+ mBufferQueue.clear();
+}
+
+
+// ----------------------------------------------------------------------------
+// Record
+// ----------------------------------------------------------------------------
+
+AudioFlinger::RecordHandle::RecordHandle(
+ const sp<AudioFlinger::RecordThread::RecordTrack>& recordTrack)
+ : BnAudioRecord(),
+ mRecordTrack(recordTrack)
+{
+}
+
+AudioFlinger::RecordHandle::~RecordHandle() {
+ stop_nonvirtual();
+ mRecordTrack->destroy();
+}
+
+sp<IMemory> AudioFlinger::RecordHandle::getCblk() const {
+ return mRecordTrack->getCblk();
+}
+
+status_t AudioFlinger::RecordHandle::start(int /*AudioSystem::sync_event_t*/ event,
+ int triggerSession) {
+ ALOGV("RecordHandle::start()");
+ return mRecordTrack->start((AudioSystem::sync_event_t)event, triggerSession);
+}
+
+void AudioFlinger::RecordHandle::stop() {
+ stop_nonvirtual();
+}
+
+void AudioFlinger::RecordHandle::stop_nonvirtual() {
+ ALOGV("RecordHandle::stop()");
+ mRecordTrack->stop();
+}
+
+status_t AudioFlinger::RecordHandle::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnAudioRecord::onTransact(code, data, reply, flags);
+}
+
+// ----------------------------------------------------------------------------
+
+// RecordTrack constructor must be called with AudioFlinger::mLock held
+AudioFlinger::RecordThread::RecordTrack::RecordTrack(
+ RecordThread *thread,
+ const sp<Client>& client,
+ uint32_t sampleRate,
+ audio_format_t format,
+ audio_channel_mask_t channelMask,
+ size_t frameCount,
+ int sessionId,
+ int uid)
+ : TrackBase(thread, client, sampleRate, format,
+ channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/),
+ mOverflow(false)
+{
+ ALOGV("RecordTrack constructor");
+ if (mCblk != NULL) {
+ mAudioRecordServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
+ mFrameSize);
+ mServerProxy = mAudioRecordServerProxy;
+ }
+}
+
+AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
+{
+ ALOGV("%s", __func__);
+}
+
+// AudioBufferProvider interface
+status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer,
+ int64_t pts)
+{
+ ServerProxy::Buffer buf;
+ buf.mFrameCount = buffer->frameCount;
+ status_t status = mServerProxy->obtainBuffer(&buf);
+ buffer->frameCount = buf.mFrameCount;
+ buffer->raw = buf.mRaw;
+ if (buf.mFrameCount == 0) {
+ // FIXME also wake futex so that overrun is noticed more quickly
+ (void) android_atomic_or(CBLK_OVERRUN, &mCblk->mFlags);
+ }
+ return status;
+}
+
+status_t AudioFlinger::RecordThread::RecordTrack::start(AudioSystem::sync_event_t event,
+ int triggerSession)
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ RecordThread *recordThread = (RecordThread *)thread.get();
+ return recordThread->start(this, event, triggerSession);
+ } else {
+ return BAD_VALUE;
+ }
+}
+
+void AudioFlinger::RecordThread::RecordTrack::stop()
+{
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ RecordThread *recordThread = (RecordThread *)thread.get();
+ if (recordThread->stop(this)) {
+ AudioSystem::stopInput(recordThread->id());
+ }
+ }
+}
+
+void AudioFlinger::RecordThread::RecordTrack::destroy()
+{
+ // see comments at AudioFlinger::PlaybackThread::Track::destroy()
+ sp<RecordTrack> keep(this);
+ {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ if (mState == ACTIVE || mState == RESUMING) {
+ AudioSystem::stopInput(thread->id());
+ }
+ AudioSystem::releaseInput(thread->id());
+ Mutex::Autolock _l(thread->mLock);
+ RecordThread *recordThread = (RecordThread *) thread.get();
+ recordThread->destroyTrack_l(this);
+ }
+ }
+}
+
+void AudioFlinger::RecordThread::RecordTrack::invalidate()
+{
+ // FIXME should use proxy, and needs work
+ audio_track_cblk_t* cblk = mCblk;
+ android_atomic_or(CBLK_INVALID, &cblk->mFlags);
+ android_atomic_release_store(0x40000000, &cblk->mFutex);
+ // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
+ (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
+}
+
+
+/*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
+{
+ result.append("Client Fmt Chn mask Session S Server fCount\n");
+}
+
+void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
+{
+ snprintf(buffer, size, "%6u %3u %08X %7u %1d %08X %6zu\n",
+ (mClient == 0) ? getpid_cached : mClient->pid(),
+ mFormat,
+ mChannelMask,
+ mSessionId,
+ mState,
+ mCblk->mServer,
+ mFrameCount);
+}
+
+}; // namespace android
diff --git a/services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp b/services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp
index ade58a7..7fc03a6 100644
--- a/services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp
+++ b/services/audioflinger/audio-resampler/AudioResamplerCoefficients.cpp
@@ -14,42 +14,41 @@
* limitations under the License.
*/
-#include <dnsampler_filter_coefficients_x128_10112011.h>
-#include <resampler_filter_coefficients_10042011.h>
-#undef LOG_TAG
-#include <utils/Log.h>
-//#include "common_log.h"
#define LOG_TAG "ResamplerCoefficients"
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
-const int32_t RESAMPLE_FIR_NUM_COEF = 16;
-const int32_t RESAMPLE_FIR_LERP_INT_BITS = 7;
+#include "filter_coefficients.h"
+
+const int32_t RESAMPLE_FIR_NUM_COEF = 16;
+const int32_t RESAMPLE_FIR_LERP_INT_BITS = 7;
using namespace android;
+
#ifdef __cplusplus
extern "C" {
#endif
+
const int32_t* readResamplerCoefficients(bool upSample) {
ALOGV("readResamplerCoefficients");
- if(upSample) {
- return resampler_filter_coefficients_10042011;
+ if (upSample) {
+ return (const int32_t *) up_sampler_filter_coefficients;
+ } else {
+ return (const int32_t *) dn_sampler_filter_coefficients;
}
- else {
- return dnsampler_filter_coefficients_x128_10112011;
- }
}
int32_t readResampleFirNumCoeff() {
-
return RESAMPLE_FIR_NUM_COEF;
}
int32_t readResampleFirLerpIntBits() {
-
- return RESAMPLE_FIR_LERP_INT_BITS;
+ return RESAMPLE_FIR_LERP_INT_BITS;
}
+
#ifdef __cplusplus
}
#endif
diff --git a/services/audioflinger/audio-resampler/dnsampler_filter_coefficients_x128_10112011.h b/services/audioflinger/audio-resampler/dnsampler_filter_coefficients_x128_10112011.h
deleted file mode 100644
index eb2944c..0000000
--- a/services/audioflinger/audio-resampler/dnsampler_filter_coefficients_x128_10112011.h
+++ /dev/null
@@ -1,2585 +0,0 @@
-
-/*
- * 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 <stdlib.h>
-
-namespace android {
-
-const int32_t dnsampler_filter_coefficients_x128_10112011[] = {
-1849391518,
-1849249650,
-1848824221,
-1848115177,
-1847122891,
-1845847499,
-1844289491,
-1842449129,
-1840327103,
-1837923861,
-1835240203,
-1832276710,
-1829034388,
-1825513999,
-1821716652,
-1817643240,
-1813295074,
-1808673214,
-1803779065,
-1798613825,
-1793179109,
-1787476282,
-1781507048,
-1775272909,
-1768775774,
-1762017295,
-1754999460,
-1747724062,
-1740193297,
-1732409109,
-1724373764,
-1716089338,
-1707558293,
-1698782831,
-1689765473,
-1680508558,
-1671014814,
-1661286715,
-1651327042,
-1641138399,
-1630723762,
-1620085842,
-1609227651,
-1598152036,
-1586862206,
-1575361116,
-1563652006,
-1551737947,
-1539622358,
-1527308388,
-1514799466,
-1502098869,
-1489210218,
-1476136877,
-1462882477,
-1449450498,
-1435844739,
-1422068735,
-1408126274,
-1394021008,
-1379756900,
-1365337657,
-1350767225,
-1336049408,
-1321188297,
-1306187721,
-1291051731,
-1275784259,
-1260389523,
-1244871491,
-1229234343,
-1213482137,
-1197619187,
-1181649550,
-1165577481,
-1149407125,
-1133142876,
-1116788884,
-1100349476,
-1083828868,
-1067231493,
-1050561533,
-1033823337,
-1017021160,
-1000159475,
-983242522,
-966274693,
-949260282,
-932203771,
-915109401,
-897981548,
-880824504,
-863642743,
-846440509,
-829222164,
-811991981,
-794754382,
-777513558,
-760273800,
-743039333,
-725814532,
-708603558,
-691410662,
-674240023,
-657095935,
-639982476,
-622903794,
-605863979,
-588867231,
-571917545,
-555018972,
-538175499,
-521391190,
-504669905,
-488015541,
-471431958,
-454923090,
-438492688,
-422144531,
-405882352,
-389709927,
-373630848,
-357648715,
-341767096,
-325989596,
-310319649,
-294760687,
-279316098,
-263989284,
-248783473,
-233701878,
-218747691,
-203924116,
-189234208,
-174680993,
-160267473,
-145996632,
-131871305,
-117894282,
-104068338,
-90396230,
-76880575,
-63523937,
-50328860,
-37297842,
-24433248,
-11737378,
--787473,
--13139049,
--25315210,
--37313887,
--49133021,
--60770615,
--72224791,
--83493750,
--94575698,
--105468905,
--116171744,
--126682678,
--137000178,
--147122799,
--157049194,
--166778112,
--176308299,
--185638576,
--194767849,
--203695121,
--212419395,
--220939772,
--229255432,
--237365662,
--245269744,
--252967058,
--260457050,
--267739278,
--274813300,
--281678782,
--288335451,
--294783151,
--301021713,
--307051072,
--312871207,
--318482217,
--323884189,
--329077326,
--334061875,
--338838202,
--343406662,
--347767725,
--351921891,
--355869785,
--359612019,
--363149327,
--366482470,
--369612329,
--372539768,
--375265766,
--377791316,
--380117528,
--382245500,
--384176449,
--385911601,
--387452302,
--388799883,
--389955791,
--390921477,
--391698507,
--392288434,
--392692926,
--392913649,
--392952381,
--392810881,
--392491020,
--391994654,
--391323748,
--390480249,
--389466214,
--388283683,
--386934801,
--385421695,
--383746599,
--381911725,
--379919384,
--377771869,
--375471577,
--373020872,
--370422215,
--367678046,
--364790904,
--361763287,
--358597785,
--355296968,
--351863496,
--348299986,
--344609141,
--340793645,
--336856270,
--332799742,
--328626863,
--324340418,
--319943269,
--315438225,
--310828168,
--306115960,
--301304533,
--296396768,
--291395607,
--286303973,
--281124849,
--275861163,
--270515897,
--265092015,
--259592541,
--254020439,
--248378720,
--242670378,
--236898450,
--231065914,
--225175783,
--219231057,
--213234773,
--207189910,
--201099474,
--194966455,
--188793871,
--182584674,
--176341840,
--170068330,
--163767128,
--157441156,
--151093349,
--144726627,
--138343919,
--131948090,
--125542009,
--119128533,
--112710525,
--106290787,
--99872116,
--93457296,
--87049105,
--80650255,
--74263449,
--67891377,
--61536721,
--55202101,
--48890119,
--42603362,
--36344397,
--30115726,
--23919828,
--17759168,
--11636191,
--5553280,
-487212,
-6482947,
-12431620,
-18330990,
-24178851,
-29973011,
-35711312,
-41391655,
-47011985,
-52570260,
-58064483,
-63492718,
-68853076,
-74143678,
-79362692,
-84508338,
-89578892,
-94572639,
-99487923,
-104323138,
-109076739,
-113747190,
-118333012,
-122832775,
-127245111,
-131568663,
-135802141,
-139944301,
-143993966,
-147949965,
-151811195,
-155576593,
-159245166,
-162815931,
-166287976,
-169660430,
-172932498,
-176103387,
-179172383,
-182138803,
-185002039,
-187761495,
-190416648,
-192967011,
-195412171,
-197751720,
-199985330,
-202112693,
-204133584,
-206047780,
-207855140,
-209555545,
-211148956,
-212635338,
-214014737,
-215287214,
-216452911,
-217511975,
-218464633,
-219311127,
-220051778,
-220686909,
-221216923,
-221642232,
-221963326,
-222180699,
-222294922,
-222306577,
-222216320,
-222024810,
-221732784,
-221340984,
-220850224,
-220261321,
-219575167,
-218792653,
-217914741,
-216942395,
-215876649,
-214718535,
-213469149,
-212129590,
-210701024,
-209184611,
-207581573,
-205893134,
-204120584,
-202265202,
-200328328,
-198311300,
-196215518,
-194042369,
-191793295,
-189469738,
-187073198,
-184605160,
-182067159,
-179460730,
-176787462,
-174048923,
-171246730,
-168382500,
-165457899,
-162474572,
-159434208,
-156338493,
-153189156,
-149987902,
-146736470,
-143436603,
-140090079,
-136698650,
-133264101,
-129788215,
-126272805,
-122719654,
-119130572,
-115507371,
-111851884,
-108165922,
-104451311,
-100709877,
-96943466,
-93153889,
-89342976,
-85512554,
-81664466,
-77800525,
-73922553,
-70032372,
-66131809,
-62222659,
-58306723,
-54385799,
-50461691,
-46536172,
-42611011,
-38687978,
-34768834,
-30855311,
-26949132,
-23052017,
-19165682,
-15291809,
-11432068,
-7588126,
-3761633,
--45792,
--3832550,
--7597044,
--11337694,
--15052951,
--18741290,
--22401190,
--26031153,
--29629717,
--33195444,
--36726898,
--40222670,
--43681381,
--47101682,
--50482228,
--53821707,
--57118836,
--60372370,
--63581062,
--66743703,
--69859111,
--72926141,
--75943654,
--78910550,
--81825759,
--84688251,
--87496999,
--90251018,
--92949348,
--95591074,
--98175284,
--100701113,
--103167725,
--105574326,
--107920130,
--110204396,
--112426408,
--114585497,
--116681001,
--118712307,
--120678826,
--122580018,
--124415349,
--126184333,
--127886505,
--129521449,
--131088758,
--132588075,
--134019062,
--135381436,
--136674915,
--137899273,
--139054300,
--140139836,
--141155730,
--142101884,
--142978213,
--143784688,
--144521281,
--145188022,
--145784950,
--146312156,
--146769738,
--147157848,
--147476649,
--147726356,
--147907187,
--148019419,
--148063331,
--148039258,
--147947536,
--147788557,
--147562717,
--147270463,
--146912244,
--146488563,
--145999923,
--145446877,
--144829983,
--144149847,
--143407076,
--142602324,
--141736248,
--140809557,
--139822954,
--138777189,
--137673015,
--136511230,
--135292629,
--134018047,
--132688326,
--131304347,
--129866988,
--128377166,
--126835801,
--125243852,
--123602271,
--121912047,
--120174172,
--118389676,
--116559578,
--114684934,
--112766800,
--110806267,
--108804411,
--106762342,
--104681172,
--102562041,
--100406081,
--98214447,
--95988299,
--93728821,
--91437183,
--89114578,
--86762204,
--84381279,
--81973009,
--79538619,
--77079333,
--74596397,
--72091035,
--69564491,
--67018008,
--64452847,
--61870249,
--59271470,
--56657765,
--54030397,
--51390615,
--48739670,
--46078819,
--43409324,
--40732431,
--38049385,
--35361437,
--32669835,
--29975809,
--27280589,
--24585404,
--21891486,
--19200045,
--16512287,
--13829420,
--11152644,
--8483140,
--5822079,
--3170634,
--529970,
-2098768,
-4714446,
-7315931,
-9902105,
-12471869,
-15024139,
-17557833,
-20071880,
-22565234,
-25036861,
-27485734,
-29910841,
-32311195,
-34685828,
-37033773,
-39354083,
-41645834,
-43908123,
-46140049,
-48340738,
-50509337,
-52645015,
-54746948,
-56814333,
-58846388,
-60842359,
-62801494,
-64723072,
-66606390,
-68450776,
-70255559,
-72020101,
-73743779,
-75426002,
-77066186,
-78663776,
-80218238,
-81729070,
-83195771,
-84617877,
-85994939,
-87326540,
-88612269,
-89851753,
-91044631,
-92190579,
-93289279,
-94340447,
-95343813,
-96299144,
-97206213,
-98064827,
-98874809,
-99636019,
-100348318,
-101011607,
-101625796,
-102190834,
-102706673,
-103173303,
-103590724,
-103958975,
-104278097,
-104548170,
-104769282,
-104941559,
-105065128,
-105140157,
-105166820,
-105145327,
-105075891,
-104958764,
-104794201,
-104582494,
-104323937,
-104018863,
-103667606,
-103270537,
-102828031,
-102340497,
-101808346,
-101232024,
-100611979,
-99948695,
-99242655,
-98494373,
-97704368,
-96873190,
-96001391,
-95089549,
-94138248,
-93148103,
-92119724,
-91053751,
-89950827,
-88811624,
-87636812,
-86427086,
-85183142,
-83905705,
-82595493,
-81253249,
-79879717,
-78475668,
-77041865,
-75579093,
-74088140,
-72569815,
-71024919,
-69454270,
-67858693,
-66239031,
-64596117,
-62930801,
-61243936,
-59536392,
-57809025,
-56062708,
-54298314,
-52516733,
-50718843,
-48905532,
-47077692,
-45236223,
-43382015,
-41515965,
-39638972,
-37751945,
-35855780,
-33951378,
-32039643,
-30121481,
-28197786,
-26269454,
-24337383,
-22402475,
-20465617,
-18527696,
-16589601,
-14652220,
-12716424,
-10783083,
-8853070,
-6927251,
-5006483,
-3091612,
-1183489,
--717043,
--2609159,
--4492039,
--6364862,
--8226816,
--10077103,
--11914934,
--13739523,
--15550093,
--17345884,
--19126146,
--20890132,
--22637106,
--24366346,
--26077146,
--27768800,
--29440618,
--31091927,
--32722068,
--34330384,
--35916235,
--37478994,
--39018053,
--40532806,
--42022667,
--43487063,
--44925444,
--46337259,
--47721979,
--49079087,
--50408090,
--51708495,
--52979834,
--54221651,
--55433514,
--56614992,
--57765679,
--58885178,
--59973119,
--61029133,
--62052877,
--63044020,
--64002256,
--64927281,
--65818818,
--66676601,
--67500387,
--68289938,
--69045045,
--69765506,
--70451147,
--71101796,
--71717309,
--72297549,
--72842408,
--73351780,
--73825587,
--74263759,
--74666254,
--75033033,
--75364085,
--75659405,
--75919016,
--76142943,
--76331242,
--76483972,
--76601219,
--76683074,
--76729655,
--76741083,
--76717507,
--76659078,
--76565976,
--76438382,
--76276507,
--76080561,
--75850784,
--75587416,
--75290725,
--74960980,
--74598477,
--74203511,
--73776405,
--73317481,
--72827089,
--72305578,
--71753320,
--71170691,
--70558089,
--69915913,
--69244582,
--68544519,
--67816171,
--67059982,
--66276416,
--65465942,
--64629046,
--63766215,
--62877953,
--61964766,
--61027182,
--60065722,
--59080928,
--58073342,
--57043524,
--55992029,
--54919428,
--53826296,
--52713221,
--51580788,
--50429596,
--49260245,
--48073350,
--46869516,
--45649365,
--44413516,
--43162604,
--41897255,
--40618108,
--39325799,
--38020978,
--36704285,
--35376367,
--34037875,
--32689467,
--31331794,
--29965512,
--28591278,
--27209755,
--25821597,
--24427461,
--23028006,
--21623896,
--20215785,
--18804329,
--17390186,
--15974013,
--14556459,
--13138172,
--11719802,
--10301999,
--8885401,
--7470647,
--6058375,
--4649221,
--3243807,
--1842755,
--446687,
-943782,
-2328043,
-3705495,
-5075537,
-6437575,
-7791026,
-9135312,
-10469860,
-11794099,
-13107474,
-14409434,
-15699433,
-16976932,
-18241406,
-19492338,
-20729214,
-21951528,
-23158786,
-24350507,
-25526208,
-26685422,
-27827692,
-28952575,
-30059626,
-31148419,
-32218533,
-33269565,
-34301113,
-35312788,
-36304216,
-37275034,
-38224885,
-39153423,
-40060316,
-40945246,
-41807897,
-42647972,
-43465182,
-44259258,
-45029930,
-45776947,
-46500068,
-47199069,
-47873729,
-48523844,
-49149222,
-49749687,
-50325066,
-50875204,
-51399956,
-51899193,
-52372790,
-52820642,
-53242651,
-53638739,
-54008828,
-54352864,
-54670795,
-54962591,
-55228223,
-55467686,
-55680976,
-55868110,
-56029109,
-56164013,
-56272864,
-56355729,
-56412671,
-56443780,
-56449143,
-56428871,
-56383077,
-56311893,
-56215452,
-56093910,
-55947423,
-55776167,
-55580318,
-55360074,
-55115633,
-54847211,
-54555028,
-54239319,
-53900322,
-53538294,
-53153491,
-52746187,
-52316657,
-51865197,
-51392097,
-50897668,
-50382220,
-49846081,
-49289577,
-48713047,
-48116836,
-47501302,
-46866801,
-46213703,
-45542381,
-44853220,
-44146603,
-43422927,
-42682590,
-41926002,
-41153572,
-40365719,
-39562864,
-38745438,
-37913870,
-37068598,
-36210060,
-35338709,
-34454988,
-33559353,
-32652259,
-31734171,
-30805546,
-29866852,
-28918556,
-27961133,
-26995053,
-26020792,
-25038826,
-24049638,
-23053702,
-22051499,
-21043509,
-20030219,
-19012108,
-17989658,
-16963352,
-15933674,
-14901104,
-13866119,
-12829202,
-11790833,
-10751487,
-9711638,
-8671763,
-7632335,
-6593820,
-5556683,
-4521391,
-3488407,
-2458187,
-1431187,
-407859,
--611345,
--1625983,
--2635618,
--3639811,
--4638127,
--5630141,
--6615431,
--7593576,
--8564163,
--9526785,
--10481043,
--11426537,
--12362876,
--13289673,
--14206552,
--15113136,
--16009059,
--16893959,
--17767487,
--18629291,
--19479032,
--20316375,
--21140997,
--21952575,
--22750798,
--23535360,
--24305970,
--25062334,
--25804171,
--26531205,
--27243175,
--27939817,
--28620882,
--29286128,
--29935325,
--30568244,
--31184668,
--31784388,
--32367206,
--32932927,
--33481369,
--34012357,
--34525729,
--35021324,
--35498994,
--35958600,
--36400012,
--36823105,
--37227767,
--37613893,
--37981391,
--38330171,
--38660157,
--38971278,
--39263479,
--39536704,
--39790914,
--40026074,
--40242163,
--40439161,
--40617065,
--40775874,
--40915600,
--41036259,
--41137883,
--41220502,
--41284167,
--41328926,
--41354844,
--41361987,
--41350437,
--41320276,
--41271601,
--41204513,
--41119123,
--41015546,
--40893911,
--40754348,
--40597001,
--40422014,
--40229546,
--40019757,
--39792819,
--39548906,
--39288206,
--39010907,
--38717208,
--38407310,
--38081429,
--37739776,
--37382578,
--37010062,
--36622464,
--36220024,
--35802990,
--35371611,
--34926149,
--34466863,
--33994021,
--33507895,
--33008767,
--32496915,
--31972628,
--31436196,
--30887917,
--30328089,
--29757014,
--29175000,
--28582360,
--27979405,
--27366455,
--26743828,
--26111852,
--25470849,
--24821150,
--24163084,
--23496990,
--22823200,
--22142053,
--21453889,
--20759052,
--20057882,
--19350724,
--18637922,
--17919826,
--17196782,
--16469137,
--15737241,
--15001445,
--14262095,
--13519542,
--12774133,
--12026222,
--11276155,
--10524279,
--9770942,
--9016494,
--8261277,
--7505635,
--6749910,
--5994447,
--5239585,
--4485659,
--3733007,
--2981966,
--2232865,
--1486032,
--741794,
--479,
-737595,
-1472108,
-2202745,
-2929192,
-3651141,
-4368289,
-5080331,
-5786969,
-6487909,
-7182860,
-7871535,
-8553649,
-9228926,
-9897092,
-10557878,
-11211015,
-11856245,
-12493314,
-13121967,
-13741959,
-14353050,
-14955005,
-15547593,
-16130588,
-16703768,
-17266923,
-17819839,
-18362313,
-18894148,
-19415155,
-19925144,
-20423935,
-20911353,
-21387231,
-21851406,
-22303720,
-22744023,
-23172174,
-23588031,
-23991464,
-24382346,
-24760559,
-25125987,
-25478524,
-25818069,
-26144531,
-26457817,
-26757849,
-27044549,
-27317851,
-27577690,
-27824011,
-28056763,
-28275905,
-28481398,
-28673213,
-28851323,
-29015713,
-29166367,
-29303283,
-29426459,
-29535904,
-29631629,
-29713655,
-29782006,
-29836715,
-29877818,
-29905359,
-29919387,
-29919958,
-29907132,
-29880978,
-29841566,
-29788975,
-29723288,
-29644596,
-29552991,
-29448575,
-29331452,
-29201735,
-29059537,
-28904981,
-28738192,
-28559303,
-28368447,
-28165766,
-27951404,
-27725513,
-27488244,
-27239759,
-26980219,
-26709793,
-26428650,
-26136967,
-25834922,
-25522700,
-25200487,
-24868475,
-24526855,
-24175830,
-23815596,
-23446360,
-23068326,
-22681709,
-22286719,
-21883572,
-21472486,
-21053686,
-20627392,
-20193830,
-19753228,
-19305819,
-18851833,
-18391506,
-17925071,
-17452770,
-16974838,
-16491518,
-16003049,
-15509677,
-15011645,
-14509198,
-14002580,
-13492042,
-12977828,
-12460185,
-11939362,
-11415608,
-10889171,
-10360299,
-9829240,
-9296245,
-8761559,
-8225429,
-7688103,
-7149828,
-6610849,
-6071411,
-5531756,
-4992131,
-4452774,
-3913926,
-3375826,
-2838712,
-2302821,
-1768384,
-1235637,
-704811,
-176132,
--350173,
--873879,
--1394763,
--1912606,
--2427192,
--2938303,
--3445727,
--3949255,
--4448681,
--4943802,
--5434414,
--5920319,
--6401326,
--6877239,
--7347870,
--7813034,
--8272552,
--8726243,
--9173932,
--9615448,
--10050623,
--10479293,
--10901295,
--11316474,
--11724679,
--12125759,
--12519569,
--12905967,
--13284818,
--13655986,
--14019344,
--14374766,
--14722134,
--15061330,
--15392240,
--15714758,
--16028781,
--16334207,
--16630942,
--16918895,
--17197981,
--17468118,
--17729228,
--17981237,
--18224079,
--18457687,
--18682004,
--18896973,
--19102546,
--19298674,
--19485318,
--19662437,
--19830002,
--19987982,
--20136353,
--20275096,
--20404197,
--20523643,
--20633430,
--20733554,
--20824018,
--20904829,
--20975997,
--21037538,
--21089473,
--21131822,
--21164617,
--21187886,
--21201668,
--21206001,
--21200931,
--21186504,
--21162774,
--21129796,
--21087631,
--21036341,
--20975996,
--20906666,
--20828426,
--20741355,
--20645535,
--20541051,
--20427994,
--20306454,
--20176529,
--20038316,
--19891920,
--19737444,
--19574998,
--19404692,
--19226642,
--19040965,
--18847782,
--18647215,
--18439392,
--18224439,
--18002487,
--17773669,
--17538123,
--17295986,
--17047397,
--16792499,
--16531439,
--16264360,
--15991413,
--15712746,
--15428514,
--15138869,
--14843968,
--14543967,
--14239027,
--13929305,
--13614963,
--13296164,
--12973072,
--12645852,
--12314669,
--11979690,
--11641084,
--11299018,
--10953660,
--10605181,
--10253752,
--9899543,
--9542724,
--9183468,
--8821947,
--8458330,
--8092791,
--7725499,
--7356629,
--6986351,
--6614835,
--6242252,
--5868775,
--5494571,
--5119810,
--4744660,
--4369291,
--3993868,
--3618559,
--3243529,
--2868942,
--2494962,
--2121750,
--1749467,
--1378274,
--1008329,
--639789,
--272809,
-92455,
-455851,
-817231,
-1176442,
-1533338,
-1887773,
-2239604,
-2588688,
-2934884,
-3278056,
-3618068,
-3954787,
-4288079,
-4617816,
-4943871,
-5266119,
-5584437,
-5898705,
-6208806,
-6514625,
-6816049,
-7112968,
-7405275,
-7692864,
-7975633,
-8253483,
-8526319,
-8794045,
-9056570,
-9313805,
-9565666,
-9812069,
-10052933,
-10288182,
-10517742,
-10741541,
-10959512,
-11171587,
-11377707,
-11577809,
-11771839,
-11959741,
-12141468,
-12316971,
-12486205,
-12649128,
-12805703,
-12955894,
-13099669,
-13236996,
-13367853,
-13492214,
-13610059,
-13721371,
-13826136,
-13924343,
-14015982,
-14101050,
-14179544,
-14251463,
-14316813,
-14375598,
-14427829,
-14473516,
-14512676,
-14545324,
-14571484,
-14591176,
-14604428,
-14611268,
-14611727,
-14605840,
-14593643,
-14575174,
-14550478,
-14519596,
-14482577,
-14439469,
-14390325,
-14335196,
-14274142,
-14207219,
-14134490,
-14056016,
-13971864,
-13882101,
-13786797,
-13686023,
-13579854,
-13468364,
-13351632,
-13229736,
-13102759,
-12970783,
-12833894,
-12692177,
-12545721,
-12394615,
-12238951,
-12078822,
-11914322,
-11745546,
-11572592,
-11395558,
-11214544,
-11029649,
-10840977,
-10648630,
-10452712,
-10253328,
-10050584,
-9844586,
-9635444,
-9423263,
-9208154,
-8990226,
-8769590,
-8546356,
-8320637,
-8092543,
-7862188,
-7629684,
-7395143,
-7158678,
-6920405,
-6680434,
-6438881,
-6195859,
-5951482,
-5705863,
-5459115,
-5211352,
-4962688,
-4713234,
-4463103,
-4212408,
-3961262,
-3709774,
-3458055,
-3206216,
-2954367,
-2702617,
-2451073,
-2199845,
-1949039,
-1698760,
-1449115,
-1200206,
-952139,
-705015,
-458936,
-214002,
--29687,
--272033,
--512941,
--752313,
--990055,
--1226074,
--1460278,
--1692576,
--1922878,
--2151097,
--2377147,
--2600942,
--2822399,
--3041435,
--3257970,
--3471925,
--3683223,
--3891787,
--4097545,
--4300424,
--4500352,
--4697261,
--4891083,
--5081754,
--5269208,
--5453384,
--5634222,
--5811664,
--5985653,
--6156134,
--6323055,
--6486364,
--6646013,
--6801954,
--6954144,
--7102537,
--7247093,
--7387773,
--7524538,
--7657353,
--7786184,
--7910999,
--8031770,
--8148467,
--8261064,
--8369539,
--8473868,
--8574031,
--8670011,
--8761790,
--8849356,
--8932694,
--9011796,
--9086651,
--9157254,
--9223598,
--9285682,
--9343504,
--9397065,
--9446367,
--9491415,
--9532214,
--9568774,
--9601103,
--9629214,
--9653119,
--9672835,
--9688376,
--9699763,
--9707015,
--9710154,
--9709202,
--9704187,
--9695132,
--9682068,
--9665024,
--9644031,
--9619121,
--9590330,
--9557692,
--9521245,
--9481028,
--9437080,
--9389443,
--9338159,
--9283272,
--9224828,
--9162872,
--9097452,
--9028617,
--8956417,
--8880903,
--8802127,
--8720142,
--8635003,
--8546764,
--8455484,
--8361217,
--8264022,
--8163959,
--8061088,
--7955468,
--7847161,
--7736229,
--7622737,
--7506745,
--7388320,
--7267525,
--7144428,
--7019092,
--6891585,
--6761974,
--6630327,
--6496711,
--6361194,
--6223845,
--6084734,
--5943929,
--5801500,
--5657517,
--5512050,
--5365168,
--5216942,
--5067443,
--4916741,
--4764906,
--4612009,
--4458120,
--4303311,
--4147650,
--3991209,
--3834057,
--3676265,
--3517901,
--3359037,
--3199740,
--3040080,
--2880126,
--2719945,
--2559606,
--2399177,
--2238724,
--2078315,
--1918015,
--1757891,
--1598007,
--1438428,
--1279218,
--1120441,
--962159,
--804435,
--647330,
--490906,
--335223,
--180338,
--26312,
-126797,
-278933,
-430040,
-580062,
-728943,
-876632,
-1023074,
-1168219,
-1312015,
-1454411,
-1595360,
-1734812,
-1872720,
-2009038,
-2143721,
-2276725,
-2408006,
-2537522,
-2665233,
-2791097,
-2915076,
-3037131,
-3157228,
-3275328,
-3391399,
-3505405,
-3617315,
-3727097,
-3834720,
-3940156,
-4043377,
-4144356,
-4243067,
-4339485,
-4433587,
-4525351,
-4614755,
-4701780,
-4786406,
-4868616,
-4948393,
-5025721,
-5100587,
-5172977,
-5242878,
-5310279,
-5375172,
-5437547,
-5497396,
-5554712,
-5609491,
-5661728,
-5711419,
-5758562,
-5803157,
-5845203,
-5884701,
-5921652,
-5956061,
-5987931,
-6017267,
-6044075,
-6068363,
-6090138,
-6109409,
-6126186,
-6140482,
-6152306,
-6161673,
-6168595,
-6173088,
-6175167,
-6174849,
-6172151,
-6167091,
-6159687,
-6149960,
-6137930,
-6123618,
-6107046,
-6088238,
-6067217,
-6044007,
-6018632,
-5991120,
-5961496,
-5929787,
-5896020,
-5860225,
-5822429,
-5782663,
-5740956,
-5697338,
-5651841,
-5604497,
-5555336,
-5504393,
-5451699,
-5397289,
-5341196,
-5283454,
-5224099,
-5163164,
-5100686,
-5036700,
-4971242,
-4904348,
-4836055,
-4766400,
-4695420,
-4623152,
-4549633,
-4474901,
-4398995,
-4321952,
-4243810,
-4164608,
-4084383,
-4003175,
-3921021,
-3837960,
-3754032,
-3669273,
-3583724,
-3497422,
-3410406,
-3322714,
-3234384,
-3145456,
-3055967,
-2965954,
-2875457,
-2784513,
-2693159,
-2601433,
-2509371,
-2417012,
-2324391,
-2231545,
-2138511,
-2045325,
-1952022,
-1858638,
-1765208,
-1671768,
-1578350,
-1484991,
-1391723,
-1298581,
-1205597,
-1112804,
-1020235,
-927921,
-835894,
-744186,
-652826,
-561846,
-471276,
-381143,
-291479,
-202310,
-113666,
-25572,
--61942,
--148851,
--235129,
--320751,
--405691,
--489926,
--573430,
--656181,
--738156,
--819332,
--899688,
--979202,
--1057854,
--1135623,
--1212489,
--1288435,
--1363441,
--1437489,
--1510563,
--1582644,
--1653718,
--1723769,
--1792781,
--1860740,
--1927631,
--1993443,
--2058161,
--2121774,
--2184270,
--2245638,
--2305868,
--2364949,
--2422873,
--2479630,
--2535212,
--2589613,
--2642824,
--2694840,
--2745654,
--2795261,
--2843656,
--2890834,
--2936793,
--2981528,
--3025036,
--3067316,
--3108366,
--3148185,
--3186772,
--3224126,
--3260249,
--3295140,
--3328802,
--3361236,
--3392444,
--3422430,
--3451195,
--3478744,
--3505081,
--3530210,
--3554136,
--3576865,
--3598402,
--3618754,
--3637927,
--3655929,
--3672765,
--3688446,
--3702978,
--3716370,
--3728631,
--3739770,
--3749797,
--3758721,
--3766553,
--3773304,
--3778983,
--3783603,
--3787174,
--3789709,
--3791219,
--3791717,
--3791215,
--3789725,
--3787262,
--3783838,
--3779466,
--3774160,
--3767934,
--3760802,
--3752778,
--3743877,
--3734113,
--3723501,
--3712056,
--3699792,
--3686725,
--3672871,
--3658244,
--3642860,
--3626735,
--3609884,
--3592324,
--3574069,
--3555137,
--3535542,
--3515302,
--3494432,
--3472947,
--3450866,
--3428203,
--3404974,
--3381197,
--3356887,
--3332060,
--3306732,
--3280921,
--3254641,
--3227909,
--3200741,
--3173153,
--3145162,
--3116781,
--3088029,
--3058920,
--3029469,
--2999694,
--2969608,
--2939228,
--2908568,
--2877644,
--2846472,
--2815064,
--2783438,
--2751607,
--2719586,
--2687389,
--2655030,
--2622524,
--2589885,
--2557126,
--2524261,
--2491304,
--2458268,
--2425166,
--2392011,
--2358815,
--2325592,
--2292354,
--2259114,
--2225882,
--2192671,
--2159493,
--2126359,
--2093281,
--2060269,
--2027334,
--1994487,
--1961738,
--1929098,
--1896576,
--1864183,
--1831927,
--1799818,
--1767865,
--1736078,
--1704464,
--1673032,
--1641791,
--1610748,
--1579911,
--1549288,
--1518886,
--1488713,
--1458774,
--1429078,
--1399630,
--1370436,
--1341503,
--1312836,
--1284441,
--1256324,
--1228489,
--1200941,
--1173686,
--1146727,
--1120069,
--1093717,
--1067673,
--1041942,
--1016527,
--991432,
--966659,
--942211,
--918092,
--894303,
--870846,
--847724,
--824940,
--802493,
--780386,
--758621,
--737199,
--716120,
--695385,
--674996,
--654951,
--635253,
--615901,
--596895,
--578234,
--559919,
--541949,
--524324,
--507042,
--490103,
--473505,
--457248,
--441330,
--425750,
--410506,
--395596,
--381019,
--366773,
--352855,
--339263,
--325995,
--313049,
--300421,
--288110,
--276112,
--264425,
--253046,
--241972,
--231199,
--220725,
--210546,
--200659,
--191060,
--181746,
--172713,
--163958,
--155477,
--147266,
--139321,
--131639,
--124215,
--117045,
--110126,
--103453,
--97022,
--90829,
--84870,
--79140,
--73636,
--68352,
--63285,
--58431,
--53784,
--49341,
--45097,
--41048,
--37188,
--33515,
--30023,
--26708,
--23566,
--20592,
--17782,
--15130,
--12634,
--10289,
--8089,
--6031,
--4111,
--2324,
--666,
-868,
-2281,
-3578,
-4763,
-5839,
-6812,
-7685,
-8462,
-9146,
-9743,
-10255,
-10686,
-11041,
-11322,
-11533,
-11679,
-11762,
-11785,
-11753,
-11668,
-11533,
-11352,
-11128,
-10864,
-10563,
-10227,
-9860,
-9464,
-9042,
-8596,
-8129,
-7644,
-7142,
-6626,
-6099,
-5562,
-5017,
-4467,
-3913,
-3357,
-2800,
-2245,
-1694,
-1146,
-605,
-71,
--454,
--970,
--1474,
--1966,
--2446,
--2911,
--3362,
--3797,
--4215,
--4617,
--5001,
--5367,
--5714,
--6043,
--6352,
--6641,
--6910,
--7160,
--7389,
--7598,
--7786,
--7954,
--8102,
--8230,
--8338,
--8426,
--8495,
--8545,
--8575,
--8587,
--8582,
--8558,
--8517,
--8460,
--8386,
--8297,
--8192,
--8073,
--7940,
--7794,
--7635,
--7464,
--7281,
--7088,
--6885,
--6672,
--6450,
--6221,
--5984,
--5740,
--5490,
--5235,
--4975,
--4711,
--4443,
--4173,
--3901,
--3627,
--3352,
--3077,
--2803,
--2529,
--2257,
--1986,
--1719,
--1454,
--1193,
--935,
--683,
--435,
--192,
-45,
-276,
-501,
-719,
-930,
-1134,
-1331,
-1519,
-1700,
-1873,
-2038,
-2194,
-2342,
-2481,
-2611,
-2733,
-2846,
-2950,
-3046,
-3133,
-3211,
-3281,
-3343,
-3396,
-3441,
-3477,
-3506,
-3527,
-3541,
-3547,
-3546,
-3538,
-3523,
-3502,
-3474,
-3441,
-3401,
-3357,
-3307,
-3252,
-3192,
-3128,
-3060,
-2989,
-2913,
-2835,
-2753,
-2669,
-2583,
-2494,
-2403,
-2311,
-2218,
-2124,
-2028,
-1933,
-1837,
-1741,
-1645,
-1550,
-1455,
-1361,
-1268,
-1176,
-1086,
-997,
-910,
-825,
-741,
-660,
-581,
-504,
-429,
-357,
-287,
-220,
-156,
-94,
-35,
--22,
--75,
--126,
--175,
--220,
--263,
--303,
--341,
--375,
--408,
--437,
--464,
--489,
--511,
--531,
--548,
--564,
--577,
--588,
--597,
--604,
--610,
--613,
--615,
--616,
--614,
--612,
--608,
--603,
--597,
--590,
--582,
--573,
--563,
--553,
--542,
--530,
--518,
--506,
--493,
--480,
--466,
--453,
--439,
--425,
--411,
--398,
--384,
--370,
--357,
--344,
--331,
--318,
--305,
--293,
--281,
--269,
--258,
--247,
--237,
--227,
--217,
--208,
--199,
--190,
--182,
--174,
--167,
--160,
--154,
--147,
--142,
--136,
--131,
--126,
--121,
--117,
--113,
--109,
--106,
--102,
--99,
--96,
--93,
--90,
--87,
--85,
--82,
--80,
--78,
--76,
--74,
--72,
--70,
--68,
--66,
--64,
--62,
--60,
--58,
--57,
--55,
--53,
--51,
--50,
--48,
--46,
--45,
--43,
--41,
--40,
--38,
--36,
--35,
--33,
--31,
--30,
--28,
--27,
--25,
--24,
--22,
--21,
--20,
--18,
--17,
--16,
--15,
--13,
--12,
--11,
--10,
--9,
--9,
--8,
--7,
--6,
-};
-}
diff --git a/services/audioflinger/audio-resampler/filter_coefficients.h b/services/audioflinger/audio-resampler/filter_coefficients.h
new file mode 100644
index 0000000..8b082b3
--- /dev/null
+++ b/services/audioflinger/audio-resampler/filter_coefficients.h
@@ -0,0 +1,285 @@
+/*
+ * 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 <stdlib.h>
+
+namespace android {
+
+// cmd-line: fir -l 7 -s 48000 -c 23400 -n 16 -b 9.62
+const uint32_t up_sampler_filter_coefficients[] __attribute__ ((aligned (32))) = {
+ 0x7ccccccd, 0x0323eb7f, 0xfd086246, 0x02b2aa5c, 0xfda45e2c, 0x01fa5183, 0xfe694e12, 0x0137e672, 0xff1c87d3, 0x009ce6d8, 0xff9a68b0, 0x003d150d, 0xffde727a, 0x00106595, 0xfff93679, 0x00021fc5,
+ 0x7cc9b757, 0x022ac835, 0xfd7e3a71, 0x026b7da1, 0xfdd2b905, 0x01db7c90, 0xfe7db77c, 0x012aa7bf, 0xff24dc32, 0x0097dfc9, 0xff9d4ae9, 0x003b8742, 0xffdf38e5, 0x00100be5, 0xfff959f5, 0x0002144b,
+ 0x7cc0773c, 0x01354bc1, 0xfdf365e8, 0x0224726d, 0xfe011d2e, 0x01bc908b, 0xfe923a2b, 0x011d528d, 0xff2d426f, 0x0092cbc0, 0xffa035cc, 0x0039f42e, 0xffe00236, 0x000fb0d2, 0xfff97dfa, 0x000208b0,
+ 0x7cb10d52, 0x0043843f, 0xfe67d5a8, 0x01dd92df, 0xfe2f83c1, 0x019d9230, 0xfea6d2e5, 0x010fe901, 0xff35b924, 0x008dab9d, 0xffa328d4, 0x00385c1d, 0xffe0ce46, 0x000f5471, 0xfff9a27f, 0x0001fcf5,
+ 0x7c9b7afd, 0xff557f58, 0xfedb7ae9, 0x0196e8fe, 0xfe5de5e3, 0x017e8635, 0xfebb7e75, 0x01026d40, 0xff3e3eed, 0x0088803e, 0xffa6237a, 0x0036bf58, 0xffe19cec, 0x000ef6d4, 0xfff9c77d, 0x0001f11e,
+ 0x7c7fc22f, 0xfe6b4a44, 0xff4e471d, 0x01507eb8, 0xfe8c3cc3, 0x015f714d, 0xfed039a8, 0x00f4e16f, 0xff46d266, 0x00834a83, 0xffa9253b, 0x00351e2d, 0xffe26e01, 0x000e980f, 0xfff9eceb, 0x0001e52e,
+ 0x7c5de56a, 0xfd84f1c8, 0xffc02bf2, 0x010a5de2, 0xfeba819d, 0x01405821, 0xfee5014c, 0x00e747b0, 0xff4f722b, 0x007e0b4b, 0xffac2d8f, 0x003378e7, 0xffe3415d, 0x000e3834, 0xfffa12c0, 0x0001d927,
+ 0x7c35e7bb, 0xfca28234, 0x00311b54, 0x00c49034, 0xfee8adba, 0x01213f58, 0xfef9d232, 0x00d9a226, 0xff581cd8, 0x0078c375, 0xffaf3bf2, 0x0031cfd1, 0xffe416d8, 0x000dd758, 0xfffa38f5, 0x0001cd0d,
+ 0x7c07ccbe, 0xfbc40766, 0x00a1076e, 0x007f1f4b, 0xff16ba71, 0x01022b90, 0xff0ea931, 0x00cbf2f0, 0xff60d10b, 0x007373de, 0xffb24fde, 0x00302337, 0xffe4ee4b, 0x000d758d, 0xfffa5f81, 0x0001c0e1,
+ 0x7bd3989d, 0xfae98cc5, 0x010fe2ab, 0x003a14a6, 0xff44a128, 0x00e3215e, 0xff238322, 0x00be3c2d, 0xff698d62, 0x006e1d66, 0xffb568ce, 0x002e7363, 0xffe5c78d, 0x000d12e6, 0xfffa865d, 0x0001b4a8,
+ 0x7b99500c, 0xfa131d41, 0x017d9fb8, 0xfff579a3, 0xff725b54, 0x00c42551, 0xff385ce3, 0x00b07ff8, 0xff72507e, 0x0068c0e9, 0xffb8863e, 0x002cc0a2, 0xffe6a277, 0x000caf76, 0xfffaad81, 0x0001a863,
+ 0x7b58f84d, 0xf940c355, 0x01ea3184, 0xffb15783, 0xff9fe27d, 0x00a53bed, 0xff4d3358, 0x00a2c06b, 0xff7b18fe, 0x00635f45, 0xffbba7aa, 0x002b0b3d, 0xffe77ee2, 0x000c4b50, 0xfffad4e4, 0x00019c15,
+ 0x7b12972d, 0xf8728902, 0x02558b43, 0xff6db764, 0xffcd303b, 0x008669ae, 0xff620368, 0x0094ff9b, 0xff83e586, 0x005df954, 0xffbecc8d, 0x00295380, 0xffe85ca7, 0x000be687, 0xfffafc7f, 0x00018fc1,
+ 0x7ac63304, 0xf7a877d4, 0x02bfa06d, 0xff2aa243, 0xfffa3e37, 0x0067b303, 0xff76ca02, 0x00873f9b, 0xff8cb4bb, 0x00588ff1, 0xffc1f465, 0x002799b3, 0xffe93b9e, 0x000b812d, 0xfffb244a, 0x0001836a,
+ 0x7a73d2b5, 0xf6e298db, 0x032864c1, 0xfee820f8, 0x00270631, 0x00491c54, 0xff8b841a, 0x0079827a, 0xff958542, 0x005323f7, 0xffc51eaf, 0x0025de22, 0xffea1ba2, 0x000b1b55, 0xfffb4c3e, 0x00017712,
+ 0x7a1b7daa, 0xf620f4b2, 0x038fcc44, 0xfea63c38, 0x005381fa, 0x002aa9fa, 0xffa02eac, 0x006bca44, 0xff9e55c6, 0x004db63c, 0xffc84ae9, 0x00242115, 0xffeafc8b, 0x000ab510, 0xfffb7452, 0x00016abb,
+ 0x79bd3bd8, 0xf5639376, 0x03f5cb46, 0xfe64fc93, 0x007fab77, 0x000c6043, 0xffb4c6b9, 0x005e1900, 0xffa724f0, 0x00484799, 0xffcb7893, 0x002262d6, 0xffebde33, 0x000a4e72, 0xfffb9c80, 0x00015e68,
+ 0x795915bc, 0xf4aa7cce, 0x045a565c, 0xfe246a72, 0x00ab7ca6, 0xffee4372, 0xffc9494b, 0x005070b0, 0xffaff16f, 0x0042d8e1, 0xffcea72c, 0x0020a3ad, 0xffecc075, 0x0009e78c, 0xfffbc4bf, 0x0001521b,
+ 0x78ef1457, 0xf3f5b7e4, 0x04bd6269, 0xfde48e17, 0x00d6ef99, 0xffd057bb, 0xffddb374, 0x0042d353, 0xffb8b9f3, 0x003d6aea, 0xffd1d635, 0x001ee3e1, 0xffeda32a, 0x00098070, 0xfffbed0a, 0x000145d7,
+ 0x787f4134, 0xf3454b6a, 0x051ee498, 0xfda56f9c, 0x0101fe7a, 0xffb2a145, 0xfff2024e, 0x003542e2, 0xffc17d30, 0x0037fe85, 0xffd50530, 0x001d23b9, 0xffee862e, 0x0009192f, 0xfffc1558, 0x0001399e,
+ 0x7809a65e, 0xf2993d95, 0x057ed264, 0xfd6716f2, 0x012ca389, 0xff952429, 0x000632fa, 0x0027c151, 0xffca39dd, 0x00329483, 0xffd833a0, 0x001b637e, 0xffef695c, 0x0008b1db, 0xfffc3da2, 0x00012d72,
+ 0x778e4e68, 0xf1f19421, 0x05dd218f, 0xfd298be0, 0x0156d920, 0xff77e470, 0x001a42a4, 0x001a508e, 0xffd2eeb3, 0x002d2db0, 0xffdb6109, 0x0019a373, 0xfff04c8f, 0x00084a86, 0xfffc65e2, 0x00012155,
+ 0x770d4466, 0xf14e544f, 0x0639c82d, 0xfcecd602, 0x018099b2, 0xff5ae614, 0x002e2e82, 0x000cf281, 0xffdb9a70, 0x0027cada, 0xffde8cf1, 0x0017e3df, 0xfff12fa3, 0x0007e33f, 0xfffc8e11, 0x0001154a,
+ 0x768693ec, 0xf0af82e4, 0x0694bca0, 0xfcb0fcca, 0x01a9dfcc, 0xff3e2d01, 0x0041f3d2, 0xffffa90e, 0xffe43bd5, 0x00226ccb, 0xffe1b6dd, 0x00162507, 0xfff21275, 0x00077c17, 0xfffcb628, 0x00010952,
+ 0x75fa4911, 0xf015242b, 0x06edf595, 0xfc76077b, 0x01d2a615, 0xff21bd11, 0x00558fdc, 0xfff27611, 0xffecd1a6, 0x001d144a, 0xffe4de56, 0x0014672d, 0xfff2f4e0, 0x00071520, 0xfffcde20, 0x0000fd6f,
+ 0x75687068, 0xef7f3bf5, 0x07456a0e, 0xfc3bfd2e, 0x01fae74e, 0xff059a0e, 0x0068fff3, 0xffe55b60, 0xfff55aae, 0x0017c21c, 0xffe802e6, 0x0012aa95, 0xfff3d6c3, 0x0006ae6a, 0xfffd05f3, 0x0000f1a4,
+ 0x74d11703, 0xeeedcd98, 0x079b1158, 0xfc02e4cc, 0x02229e57, 0xfee9c7af, 0x007c4177, 0xffd85ac9, 0xfffdd5b8, 0x00127704, 0xffeb2416, 0x0010ef82, 0xfff4b7fb, 0x00064804, 0xfffd2d9b, 0x0000e5f3,
+ 0x74344a70, 0xee60dbee, 0x07eee314, 0xfbcac510, 0x0249c629, 0xfece499d, 0x008f51cf, 0xffcb7615, 0x00064197, 0x000d33c3, 0xffee4174, 0x000f3633, 0xfff59866, 0x0005e1fe, 0xfffd5511, 0x0000da5c,
+ 0x739218b8, 0xedd86958, 0x0840d732, 0xfb93a486, 0x027059da, 0xfeb3236b, 0x00a22e71, 0xffbeaf06, 0x000e9d1f, 0x0007f915, 0xfff15a8d, 0x000d7eea, 0xfff677e2, 0x00057c68, 0xfffd7c4f, 0x0000cee3,
+ 0x72ea905a, 0xed5477be, 0x0890e5f7, 0xfb5d898c, 0x029654a0, 0xfe98589b, 0x00b4d4dd, 0xffb20754, 0x0016e72c, 0x0002c7b6, 0xfff46ef1, 0x000bc9e6, 0xfff75650, 0x00051750, 0xfffda350, 0x0000c388,
+ 0x723dc051, 0xecd5088e, 0x08df07f6, 0xfb287a4d, 0x02bbb1cc, 0xfe7dec9c, 0x00c7429f, 0xffa580b1, 0x001f1e9b, 0xfffda05c, 0xfff77e31, 0x000a1765, 0xfff8338e, 0x0004b2c7, 0xfffdca0d, 0x0000b84d,
+ 0x718bb80b, 0xec5a1cbc, 0x092b3617, 0xfaf47cc4, 0x02e06ccf, 0xfe63e2cc, 0x00d97550, 0xff991cc9, 0x00274253, 0xfff883be, 0xfffa87df, 0x000867a5, 0xfff90f7c, 0x00044eda, 0xfffdf080, 0x0000ad34,
+ 0x70d4876b, 0xebe3b4c5, 0x09756994, 0xfac196bb, 0x03048139, 0xfe4a3e70, 0x00eb6a95, 0xff8cdd3c, 0x002f513a, 0xfff3728d, 0xfffd8b92, 0x0006bae1, 0xfff9e9fd, 0x0003eb98, 0xfffe16a6, 0x0000a23f,
+ 0x70183ec5, 0xeb71d0ab, 0x09bd9bfb, 0xfa8fcdca, 0x0327eab8, 0xfe3102bd, 0x00fd2022, 0xff80c3a4, 0x00374a40, 0xffee6d78, 0x000088df, 0x00051157, 0xfffac2f0, 0x0003890e, 0xfffe3c76, 0x0000976e,
+ 0x6f56eee1, 0xeb046ffc, 0x0a03c72b, 0xfa5f2755, 0x034aa51b, 0xfe1832d4, 0x010e93b5, 0xff74d194, 0x003f2c57, 0xffe97529, 0x00037f60, 0x00036b3f, 0xfffb9a38, 0x0003274c, 0xfffe61ee, 0x00008cc4,
+ 0x6e90a8f2, 0xea9b91cc, 0x0a47e559, 0xfa2fa890, 0x036cac52, 0xfdffd1bd, 0x011fc31c, 0xff690894, 0x0046f679, 0xffe48a4a, 0x00066eae, 0x0001c8d2, 0xfffc6fb8, 0x0002c65d, 0xfffe8707, 0x00008241,
+ 0x6dc57e9b, 0xea3734bb, 0x0a89f10c, 0xfa015679, 0x038dfc6c, 0xfde7e26f, 0x0130ac31, 0xff5d6a24, 0x004ea7a3, 0xffdfad7f, 0x00095666, 0x00002a4a, 0xfffd4352, 0x00026650, 0xfffeabbd, 0x000077e8,
+ 0x6cf581e8, 0xe9d756f3, 0x0ac9e521, 0xf9d435dc, 0x03ae919a, 0xfdd067ca, 0x01414cdd, 0xff51f7bb, 0x00563edb, 0xffdadf69, 0x000c3627, 0xfffe8fdc, 0xfffe14eb, 0x00020730, 0xfffed00a, 0x00006db9,
+ 0x6c20c550, 0xe97bf627, 0x0b07bcc6, 0xf9a84b50, 0x03ce682d, 0xfdb96498, 0x0151a317, 0xff46b2c7, 0x005dbb29, 0xffd620a6, 0x000f0d91, 0xfffcf9be, 0xfffee466, 0x0001a90b, 0xfffef3ea, 0x000063b5,
+ 0x6b475bb0, 0xe9250f99, 0x0b437380, 0xf97d9b37, 0x03ed7c9a, 0xfda2db8c, 0x0161ace5, 0xff3b9cad, 0x00651b9c, 0xffd171d1, 0x0011dc47, 0xfffb6825, 0xffffb1aa, 0x00014bed, 0xffff1759, 0x000059dd,
+ 0x6a69584a, 0xe8d2a017, 0x0b7d0525, 0xf95429c0, 0x040bcb77, 0xfd8ccf46, 0x01716859, 0xff30b6c8, 0x006c5f4b, 0xffccd380, 0x0014a1ee, 0xfff9db44, 0x00007c9c, 0x0000efe1, 0xffff3a53, 0x00005033,
+ 0x6986cec4, 0xe884a3fb, 0x0bb46de2, 0xf92bfae4, 0x0429517b, 0xfd77424c, 0x0180d397, 0xff260269, 0x00738551, 0xffc84645, 0x00175e2d, 0xfff8534d, 0x00014521, 0x000094f3, 0xffff5cd2, 0x000046b8,
+ 0x689fd324, 0xe83b1731, 0x0be9aa34, 0xf9051266, 0x04460b81, 0xfd62370e, 0x018fecd1, 0xff1b80da, 0x007a8cd0, 0xffc3cab1, 0x001a10ad, 0xfff6d070, 0x00020b23, 0x00003b2e, 0xffff7ed3, 0x00003d6c,
+ 0x67b479cf, 0xe7f5f531, 0x0c1cb6ef, 0xf8df73d6, 0x0461f688, 0xfd4dafe6, 0x019eb246, 0xff113358, 0x008174ef, 0xffbf614e, 0x001cb91a, 0xfff552de, 0x0002ce87, 0xffffe29d, 0xffffa052, 0x00003450,
+ 0x66c4d787, 0xe7b53908, 0x0c4d913a, 0xf8bb228c, 0x047d0fb1, 0xfd39af17, 0x01ad2249, 0xff071b16, 0x00883cdc, 0xffbb0aa3, 0x001f5723, 0xfff3dac3, 0x00038f37, 0xffff8b4b, 0xffffc14b, 0x00002b66,
+ 0x65d10168, 0xe778dd50, 0x0c7c368d, 0xf89821ac, 0x0497543f, 0xfd2636ca, 0x01bb3b37, 0xfefd3941, 0x008ee3cd, 0xffb6c735, 0x0021ea76, 0xfff2684e, 0x00044d1b, 0xffff3540, 0xffffe1bc, 0x000022ad,
+ 0x64d90ce7, 0xe740dc3c, 0x0ca8a4b7, 0xf8767422, 0x04b0c19a, 0xfd134913, 0x01c8fb81, 0xfef38ef6, 0x009568fc, 0xffb29782, 0x002472c8, 0xfff0fba9, 0x0005081f, 0xfffee088, 0x0000019f, 0x00001a28,
+ 0x63dd0fcd, 0xe70d2f8d, 0x0cd2d9d5, 0xf8561ca7, 0x04c9554e, 0xfd00e7ec, 0x01d661a6, 0xfeea1d4c, 0x009bcbab, 0xffae7c06, 0x0026efcc, 0xffef94fe, 0x0005c02c, 0xfffe8d2c, 0x000020f3, 0x000011d5,
+ 0x62dd2039, 0xe6ddd09f, 0x0cfad45a, 0xf8371dbb, 0x04e10d0a, 0xfcef153a, 0x01e36c34, 0xfee0e54e, 0x00a20b23, 0xffaa7538, 0x0029613a, 0xffee3477, 0x0006752d, 0xfffe3b35, 0x00003fb3, 0x000009b6,
+ 0x61d95497, 0xe6b2b862, 0x0d209309, 0xf81979ab, 0x04f7e6a2, 0xfcddd2c7, 0x01f019cb, 0xfed7e7fd, 0x00a826b2, 0xffa6838c, 0x002bc6cd, 0xffecda3b, 0x0007270f, 0xfffdeaaa, 0x00005ddd, 0x000001cc,
+ 0x60d1c3a6, 0xe68bdf5e, 0x0d4414f9, 0xf7fd328c, 0x050de00d, 0xfccd2246, 0x01fc691b, 0xfecf2650, 0x00ae1dae, 0xffa2a770, 0x002e2040, 0xffeb866f, 0x0007d5bf, 0xfffd9b96, 0x00007b6f, 0xfffffa17,
+ 0x5fc68470, 0xe6693db5, 0x0d65598f, 0xf7e24a3c, 0x0522f766, 0xfcbd0551, 0x020858e2, 0xfec6a130, 0x00b3ef73, 0xff9ee150, 0x00306d52, 0xffea3939, 0x0008812a, 0xfffd4dff, 0x00009865, 0xfffff297,
+ 0x5eb7ae46, 0xe64acb24, 0x0d846084, 0xf7c8c267, 0x05372aee, 0xfcad7d6b, 0x0213e7f0, 0xfebe5980, 0x00b99b65, 0xff9b3192, 0x0032adc4, 0xffe8f2bb, 0x0009293e, 0xfffd01ee, 0x0000b4bd, 0xffffeb4c,
+ 0x5da558c5, 0xe6307f05, 0x0da129df, 0xf7b09c7f, 0x054a7909, 0xfc9e8bfd, 0x021f1526, 0xfeb65015, 0x00bf20ee, 0xff979898, 0x0034e15b, 0xffe7b317, 0x0009cdeb, 0xfffcb769, 0x0000d074, 0xffffe438,
+ 0x5c8f9bcb, 0xe61a504f, 0x0dbbb5f6, 0xf799d9c4, 0x055ce03f, 0xfc903258, 0x0229df75, 0xfeae85bb, 0x00c47f7f, 0xff9416c1, 0x003707dc, 0xffe67a6f, 0x000a6f20, 0xfffc6e78, 0x0000eb89, 0xffffdd5a,
+ 0x5b768f7a, 0xe6083599, 0x0dd40571, 0xf7847b3d, 0x056e5f3d, 0xfc8271b4, 0x023445dd, 0xfea6fb32, 0x00c9b691, 0xff90ac66, 0x00392111, 0xffe548e0, 0x000b0cce, 0xfffc2720, 0x000105f9, 0xffffd6b2,
+ 0x5a5a4c32, 0xe5fa2519, 0x0dea1943, 0xf77081be, 0x057ef4d3, 0xfc754b32, 0x023e4772, 0xfe9fb12e, 0x00cec5a1, 0xff8d59dd, 0x003b2cc5, 0xffe41e88, 0x000ba6e5, 0xfffbe169, 0x00011fc3, 0xffffd041,
+ 0x593aea93, 0xe5f014aa, 0x0dfdf2ae, 0xf75dede5, 0x058e9ff8, 0xfc68bfd7, 0x0247e354, 0xfe98a85b, 0x00d3ac38, 0xff8a1f77, 0x003d2ac6, 0xffe2fb83, 0x000c3d59, 0xfffb9d59, 0x000138e4, 0xffffca06,
+ 0x58188376, 0xe5e9f9ca, 0x0e0f9342, 0xf74cc01c, 0x059d5fc5, 0xfc5cd092, 0x025118b8, 0xfe91e159, 0x00d869e1, 0xff86fd81, 0x003f1ae4, 0xffe1dfec, 0x000cd01b, 0xfffb5af3, 0x0001515c, 0xffffc402,
+ 0x56f32fea, 0xe5e7c99e, 0x0e1efcdb, 0xf73cf898, 0x05ab3377, 0xfc517e38, 0x0259e6e1, 0xfe8b5cba, 0x00dcfe32, 0xff83f443, 0x0040fcf3, 0xffe0cbdc, 0x000d5f1f, 0xfffb1a3f, 0x00016928, 0xffffbe35,
+ 0x55cb0935, 0xe5e978f0, 0x0e2c319d, 0xf72e9758, 0x05b81a70, 0xfc46c987, 0x02624d23, 0xfe851b09, 0x00e168c5, 0xff810401, 0x0042d0c9, 0xffdfbf6b, 0x000dea5a, 0xfffadb40, 0x00018048, 0xffffb89f,
+ 0x54a028d0, 0xe5eefc35, 0x0e3733fc, 0xf7219c2a, 0x05c41435, 0xfc3cb323, 0x026a4ae5, 0xfe7f1cc4, 0x00e5a93c, 0xff7e2cfb, 0x0044963d, 0xffdebaaf, 0x000e71c1, 0xfffa9dfa, 0x000196ba, 0xffffb340,
+ 0x5372a862, 0xe5f8478d, 0x0e4006b2, 0xf71606a6, 0x05cf2070, 0xfc333b97, 0x0271df9c, 0xfe79625e, 0x00e9bf43, 0xff7b6f6c, 0x00464d2b, 0xffddbdbd, 0x000ef549, 0xfffa6273, 0x0001ac7d, 0xffffae17,
+ 0x5242a1c1, 0xe6054ec6, 0x0e46acc4, 0xf70bd632, 0x05d93eee, 0xfc2a6356, 0x02790ace, 0xfe73ec40, 0x00edaa88, 0xff78cb8c, 0x0047f571, 0xffdcc8a9, 0x000f74e9, 0xfffa28ad, 0x0001c191, 0xffffa924,
+ 0x51102eec, 0xe616055a, 0x0e4b297c, 0xf7030a01, 0x05e26f9f, 0xfc222abb, 0x027fcc12, 0xfe6ebac6, 0x00f16ac4, 0xff76418b, 0x00498eed, 0xffdbdb84, 0x000ff098, 0xfff9f0ac, 0x0001d5f4, 0xffffa467,
+ 0x4fdb6a09, 0xe62a5e76, 0x0e4d806f, 0xf6fba113, 0x05eab296, 0xfc1a9208, 0x02862311, 0xfe69ce43, 0x00f4ffb6, 0xff73d199, 0x004b1984, 0xffdaf65e, 0x0010684e, 0xfff9ba73, 0x0001e9a7, 0xffff9fe0,
+ 0x4ea46d66, 0xe6424cf8, 0x0e4db575, 0xf6f59a36, 0x05f20809, 0xfc139968, 0x028c0f83, 0xfe6526fe, 0x00f86924, 0xff717bdf, 0x004c951b, 0xffda1948, 0x0010dc05, 0xfff98604, 0x0001fca8, 0xffff9b8f,
+ 0x4d6b536f, 0xe65dc373, 0x0e4bccac, 0xf6f0f407, 0x05f87053, 0xfc0d40ec, 0x0291912f, 0xfe60c533, 0x00fba6da, 0xff6f4083, 0x004e0199, 0xffd9444e, 0x00114bb4, 0xfff95363, 0x00020ef7, 0xffff9773,
+ 0x4c3036b2, 0xe67cb42f, 0x0e47ca78, 0xf6edacf2, 0x05fdebee, 0xfc07888e, 0x0296a7f0, 0xfe5ca913, 0x00feb8ad, 0xff6d1fa5, 0x004f5ee9, 0xffd8777d, 0x0011b757, 0xfff92290, 0x00022095, 0xffff938c,
+ 0x4af331d9, 0xe69f112f, 0x0e41b37c, 0xf6ebc332, 0x06027b78, 0xfc027031, 0x029b53af, 0xfe58d2c5, 0x01019e78, 0xff6b1961, 0x0050acf7, 0xffd7b2e0, 0x00121ee9, 0xfff8f38e, 0x00023181, 0xffff8fd9,
+ 0x49b45fa8, 0xe6c4cc2e, 0x0e398c9f, 0xf6eb34d4, 0x06061fb2, 0xfbfdf79e, 0x029f9466, 0xfe554265, 0x0104581c, 0xff692dd2, 0x0051ebb4, 0xffd6f67f, 0x00128265, 0xfff8c65d, 0x000241bb, 0xffff8c5a,
+ 0x4873daf7, 0xe6edd6a4, 0x0e2f5b0b, 0xf6ebffb2, 0x0608d97c, 0xfbfa1e88, 0x02a36a1e, 0xfe51f802, 0x0106e583, 0xff675d09, 0x00531b12, 0xffd64264, 0x0012e1c8, 0xfff89b00, 0x00025143, 0xffff890e,
+ 0x4731beb7, 0xe71a21c7, 0x0e232425, 0xf6ee217b, 0x060aa9da, 0xfbf6e48c, 0x02a6d4f0, 0xfe4ef3a4, 0x0109469d, 0xff65a718, 0x00543b04, 0xffd59695, 0x00133d0e, 0xfff87176, 0x0002601b, 0xffff85f5,
+ 0x45ee25e7, 0xe7499e8f, 0x0e14ed93, 0xf6f197ad, 0x060b91ee, 0xfbf4492d, 0x02a9d508, 0xfe4c3546, 0x010b7b61, 0xff640c08, 0x00554b83, 0xffd4f316, 0x00139436, 0xfff849c0, 0x00026e41, 0xffff830e,
+ 0x44a92b96, 0xe77c3db4, 0x0e04bd39, 0xf6f65f9b, 0x060b92ff, 0xfbf24bd9, 0x02ac6a9e, 0xfe49bcd9, 0x010d83cb, 0xff628be3, 0x00564c88, 0xffd457ec, 0x0013e73e, 0xfff823dd, 0x00027bb8, 0xffff805a,
+ 0x4362eadc, 0xe7b1efb4, 0x0df29936, 0xf6fc766a, 0x060aae6e, 0xfbf0ebe7, 0x02ae95fb, 0xfe478a42, 0x010f5fe2, 0xff6126a9, 0x00573e0f, 0xffd3c519, 0x00143626, 0xfff7ffce, 0x0002887f, 0xffff7dd6,
+ 0x421b7edf, 0xe7eaa4d4, 0x0dde87e2, 0xf703d912, 0x0608e5c2, 0xfbf02896, 0x02b05779, 0xfe459d5e, 0x01110faf, 0xff5fdc5b, 0x00582016, 0xffd33a9e, 0x001480ec, 0xfff7dd92, 0x00029497, 0xffff7b82,
+ 0x40d302c5, 0xe8264d21, 0x0dc88fd2, 0xf70c8461, 0x06063a9d, 0xfbf00112, 0x02b1af7f, 0xfe43f5ff, 0x01129344, 0xff5eacf3, 0x0058f29f, 0xffd2b87c, 0x0014c792, 0xfff7bd28, 0x0002a002, 0xffff795f,
+ 0x3f8991bd, 0xe864d874, 0x0db0b7d1, 0xf71674fa, 0x0602aec3, 0xfbf0746e, 0x02b29e84, 0xfe4293ec, 0x0113eabb, 0xff5d9867, 0x0059b5ad, 0xffd23eaf, 0x00150a19, 0xfff79e8f, 0x0002aac0, 0xffff776a,
+ 0x3e3f46f2, 0xe8a63671, 0x0d9706e1, 0xf721a756, 0x05fe4414, 0xfbf181a9, 0x02b3250f, 0xfe4176e2, 0x01151632, 0xff5c9eaa, 0x005a6946, 0xffd1cd37, 0x00154883, 0xfff781c5, 0x0002b4d2, 0xffff75a3,
+ 0x3cf43d8f, 0xe8ea568f, 0x0d7b843b, 0xf72e17c4, 0x05f8fc8f, 0xfbf327ab, 0x02b343b5, 0xfe409e95, 0x011615ce, 0xff5bbfaa, 0x005b0d72, 0xffd1640e, 0x001582d3, 0xfff766c8, 0x0002be3b, 0xffff740a,
+ 0x3ba890b9, 0xe9312813, 0x0d5e3749, 0xf73bc26b, 0x05f2da52, 0xfbf56549, 0x02b2fb1a, 0xfe400aae, 0x0116e9bc, 0xff5afb53, 0x005ba23b, 0xffd1032f, 0x0015b90b, 0xfff74d97, 0x0002c6fa, 0xffff729e,
+ 0x3a5c5b8e, 0xe97a9a17, 0x0d3f27ab, 0xf74aa34c, 0x05ebdf97, 0xfbf83941, 0x02b24bf1, 0xfe3fbacd, 0x0117922f, 0xff5a5189, 0x005c27af, 0xffd0aa93, 0x0015eb2f, 0xfff7362f, 0x0002cf12, 0xffff715d,
+ 0x390fb920, 0xe9c69b8c, 0x0d1e5d32, 0xf75ab63f, 0x05e40eb3, 0xfbfba23f, 0x02b136f9, 0xfe3fae87, 0x01180f5d, 0xff59c230, 0x005c9ddc, 0xffd05a33, 0x00161944, 0xfff7208d, 0x0002d684, 0xffff7047,
+ 0x37c2c474, 0xea151b3a, 0x0cfbdfdd, 0xf76bf6f7, 0x05db6a19, 0xfbff9ed7, 0x02afbd02, 0xfe3fe569, 0x01186187, 0xff594d27, 0x005d04d4, 0xffd01205, 0x0016434f, 0xfff70caf, 0x0002dd53, 0xffff6f5c,
+ 0x36759880, 0xea6607c4, 0x0cd7b7dd, 0xf77e6103, 0x05d1f459, 0xfc042d8e, 0x02addee8, 0xfe405ef6, 0x011888f2, 0xff58f249, 0x005d5cab, 0xffcfd1ff, 0x00166956, 0xfff6fa92, 0x0002e37e, 0xffff6e99,
+ 0x35285026, 0xeab94fa9, 0x0cb1ed8c, 0xf791efcb, 0x05c7b01a, 0xfc094cd2, 0x02ab9d96, 0xfe411aa8, 0x011885e7, 0xff58b16c, 0x005da575, 0xffcf9a15, 0x00168b5e, 0xfff6ea31, 0x0002e90a, 0xffff6dff,
+ 0x33db0631, 0xeb0ee148, 0x0c8a8973, 0xf7a69e96, 0x05bca021, 0xfc0efafe, 0x02a8fa03, 0xfe4217ef, 0x011858b9, 0xff588a65, 0x005ddf4c, 0xffcf6a3b, 0x0016a96f, 0xfff6db89, 0x0002edf6, 0xffff6d8d,
+ 0x328dd556, 0xeb66aae0, 0x0c619444, 0xf7bc6889, 0x05b0c74b, 0xfc15365c, 0x02a5f535, 0xfe435633, 0x011801be, 0xff587d03, 0x005e0a48, 0xffcf4262, 0x0016c390, 0xfff6ce97, 0x0002f246, 0xffff6d40,
+ 0x3140d82e, 0xebc09a94, 0x0c3716da, 0xf7d348a4, 0x05a42890, 0xfc1bfd22, 0x02a2903e, 0xfe44d4d3, 0x01178152, 0xff588913, 0x005e2687, 0xffcf227b, 0x0016d9c9, 0xfff6c356, 0x0002f5fc, 0xffff6d1a,
+ 0x2ff42933, 0xec1c9e6d, 0x0c0b1a37, 0xf7eb39cc, 0x0596c6ff, 0xfc234d75, 0x029ecc3c, 0xfe469325, 0x0116d7d7, 0xff58ae5d, 0x005e3427, 0xffcf0a77, 0x0016ec22, 0xfff6b9c1, 0x0002f919, 0xffff6d17,
+ 0x2ea7e2c0, 0xec7aa45b, 0x0bdda783, 0xf80436c0, 0x0588a5bf, 0xfc2b2567, 0x029aaa5a, 0xfe489077, 0x011605b5, 0xff58eca8, 0x005e3347, 0xffcefa44, 0x0016faa5, 0xfff6b1d5, 0x0002fba0, 0xffff6d38,
+ 0x2d5c1f0e, 0xecda9a39, 0x0baec80a, 0xf81e3a25, 0x0579c812, 0xfc3382fb, 0x02962bd1, 0xfe4acc0e, 0x01150b5a, 0xff5943b4, 0x005e240a, 0xffcef1cf, 0x0017055b, 0xfff6ab8c, 0x0002fd94, 0xffff6d7c,
+ 0x2c10f82d, 0xed3c6dce, 0x0b7e853c, 0xf8393e81, 0x056a314b, 0xfc3c6420, 0x029151e3, 0xfe4d4526, 0x0113e937, 0xff59b340, 0x005e0694, 0xffcef106, 0x00170c4f, 0xfff6a6e2, 0x0002fef6, 0xffff6de2,
+ 0x2ac68807, 0xeda00cd1, 0x0b4ce8a8, 0xf8553e3c, 0x0559e4da, 0xfc45c6b6, 0x028c1de0, 0xfe4ffaf6, 0x01129fc5, 0xff5a3b09, 0x005ddb0b, 0xffcef7d4, 0x00170f8a, 0xfff6a3d0, 0x0002ffc9, 0xffff6e67,
+ 0x297ce85a, 0xee0564e8, 0x0b19fbfe, 0xf87233a4, 0x0548e63f, 0xfc4fa88f, 0x02869122, 0xfe52ecab, 0x01112f81, 0xff5adac6, 0x005da198, 0xffcf0623, 0x00170f18, 0xfff6a252, 0x00030010, 0xffff6f0d,
+ 0x283432b9, 0xee6c63ad, 0x0ae5c90b, 0xf89018eb, 0x05373912, 0xfc5a076a, 0x0280ad0f, 0xfe561969, 0x010f98eb, 0xff5b922d, 0x005d5a62, 0xffcf1bde, 0x00170b04, 0xfff6a262, 0x0002ffcd, 0xffff6fd1,
+ 0x26ec8083, 0xeed4f6b0, 0x0ab059bc, 0xf8aee828, 0x0524e100, 0xfc64e0f9, 0x027a7318, 0xfe598050, 0x010ddc8c, 0xff5c60ee, 0x005d0597, 0xffcf38ec, 0x0017035a, 0xfff6a3f9, 0x0002ff03, 0xffff70b2,
+ 0x25a5eae8, 0xef3f0b78, 0x0a79b814, 0xf8ce9b5d, 0x0511e1c6, 0xfc7032de, 0x0273e4b8, 0xfe5d2075, 0x010bfaee, 0xff5d46bb, 0x005ca363, 0xffcf5d36, 0x0016f828, 0xfff6a713, 0x0002fdb4, 0xffff71b0,
+ 0x24608ae2, 0xefaa8f87, 0x0a41ee32, 0xf8ef2c71, 0x04fe3f39, 0xfc7bfaad, 0x026d0374, 0xfe60f8ea, 0x0109f4a2, 0xff5e433e, 0x005c33f6, 0xffcf88a2, 0x0016e979, 0xfff6aba9, 0x0002fbe4, 0xffff72c9,
+ 0x231c7932, 0xf017705a, 0x0a09064e, 0xf9109535, 0x04e9fd3c, 0xfc8835ed, 0x0265d0dd, 0xfe6508b6, 0x0107ca3c, 0xff5f5621, 0x005bb77f, 0xffcfbb17, 0x0016d75b, 0xfff6b1b4, 0x0002f995, 0xffff73fc,
+ 0x21d9ce63, 0xf0859b6e, 0x09cf0ab4, 0xf932cf65, 0x04d51fc6, 0xfc94e216, 0x025e4e8b, 0xfe694edd, 0x01057c57, 0xff607f0b, 0x005b2e31, 0xffcff478, 0x0016c1dc, 0xfff6b92d, 0x0002f6c9, 0xffff7549,
+ 0x2098a2bf, 0xf0f4fe3d, 0x099405c6, 0xf955d4a7, 0x04bfaadf, 0xfca1fc96, 0x02567e22, 0xfe6dca58, 0x01030b8e, 0xff61bd9f, 0x005a9840, 0xffd034ac, 0x0016a90a, 0xfff6c20f, 0x0002f385, 0xffff76ae,
+ 0x1f590e55, 0xf1658649, 0x095801f8, 0xf9799e8f, 0x04a9a29e, 0xfcaf82ca, 0x024e614c, 0xfe727a1f, 0x01007885, 0xff631180, 0x0059f5e1, 0xffd07b95, 0x00168cf2, 0xfff6cc52, 0x0002efca, 0xffff782a,
+ 0x1e1b28f2, 0xf1d72114, 0x091b09d1, 0xf99e269e, 0x04930b2b, 0xfcbd7206, 0x0245f9bf, 0xfe775d1f, 0x00fdc3e0, 0xff647a4b, 0x0059474a, 0xffd0c915, 0x00166da5, 0xfff6d7f0, 0x0002eb9c, 0xffff79bc,
+ 0x1cdf0a20, 0xf249bc2c, 0x08dd27e6, 0xf9c36642, 0x047be8bc, 0xfccbc793, 0x023d4937, 0xfe7c7243, 0x00faee49, 0xff65f79e, 0x00588cb4, 0xffd11d0f, 0x00164b32, 0xfff6e4e1, 0x0002e6fe, 0xffff7b63,
+ 0x1ba4c923, 0xf2bd4523, 0x089e66dd, 0xf9e956da, 0x04643f95, 0xfcda80ad, 0x0234517a, 0xfe81b86d, 0x00f7f86e, 0xff678912, 0x0057c658, 0xffd17764, 0x001625a7, 0xfff6f31d, 0x0002e1f3, 0xffff7d1f,
+ 0x1a6c7cf9, 0xf331a99b, 0x085ed167, 0xfa0ff1b6, 0x044c1409, 0xfce99a86, 0x022b1455, 0xfe872e7c, 0x00f4e2ff, 0xff692e3f, 0x0056f471, 0xffd1d7f5, 0x0015fd15, 0xfff7029f, 0x0002dc7d, 0xffff7eed,
+ 0x19363c54, 0xf3a6d741, 0x081e7241, 0xfa373017, 0x04336a75, 0xfcf91246, 0x0221939d, 0xfe8cd349, 0x00f1aeb2, 0xff6ae6ba, 0x0056173b, 0xffd23ea1, 0x0015d18b, 0xfff7135d, 0x0002d6a0, 0xffff80cd,
+ 0x18021d9d, 0xf41cbbd3, 0x07dd5430, 0xfa5f0b30, 0x041a4744, 0xfd08e50c, 0x0217d12d, 0xfe92a5a7, 0x00ee5c3e, 0xff6cb218, 0x00552ef3, 0xffd2ab47, 0x0015a31b, 0xfff72551, 0x0002d060, 0xffff82bf,
+ 0x16d036eb, 0xf493451f, 0x079b8203, 0xfa877c29, 0x0400aeec, 0xfd190fed, 0x020dcee8, 0xfe98a466, 0x00eaec5e, 0xff6e8fe9, 0x00543bd8, 0xffd31dc7, 0x001571d5, 0xfff73873, 0x0002c9be, 0xffff84c0,
+ 0x15a09e09, 0xf50a610a, 0x0759068f, 0xfab07c1d, 0x03e6a5ee, 0xfd298ff6, 0x02038eb7, 0xfe9ece4f, 0x00e75fd1, 0xff707fbd, 0x00533e29, 0xffd395fd, 0x00153dca, 0xfff74cba, 0x0002c2be, 0xffff86d0,
+ 0x1473686d, 0xf581fd8b, 0x0715ecae, 0xfada0420, 0x03cc30d4, 0xfd3a622b, 0x01f9128a, 0xfea52227, 0x00e3b758, 0xff728121, 0x00523626, 0xffd413c9, 0x0015070b, 0xfff76220, 0x0002bb64, 0xffff88ee,
+ 0x1348ab3a, 0xf5fa08b5, 0x06d23f3d, 0xfb040d3b, 0x03b15431, 0xfd4b8389, 0x01ee5c55, 0xfeab9eb2, 0x00dff3b7, 0xff7493a2, 0x00512412, 0xffd49705, 0x0014cdab, 0xfff7789c, 0x0002b3b3, 0xffff8b19,
+ 0x12207b3e, 0xf67270b1, 0x068e091c, 0xfb2e906f, 0x039614a1, 0xfd5cf105, 0x01e36e14, 0xfeb242ac, 0x00dc15b4, 0xff76b6ca, 0x0050082f, 0xffd51f90, 0x001491b9, 0xfff79026, 0x0002abad, 0xffff8d50,
+ 0x10faecee, 0xf6eb23c6, 0x0649552a, 0xfb5986b6, 0x037a76c7, 0xfd6ea790, 0x01d849c7, 0xfeb90cce, 0x00d81e1a, 0xff78ea20, 0x004ee2c1, 0xffd5ad44, 0x00145349, 0xfff7a8b6, 0x0002a357, 0xffff8f92,
+ 0x0fd81464, 0xf7641059, 0x06042e45, 0xfb84e906, 0x035e7f4e, 0xfd80a411, 0x01ccf173, 0xfebffbd0, 0x00d40db3, 0xff7b2d2d, 0x004db40c, 0xffd63ffe, 0x0014126c, 0xfff7c245, 0x00029ab2, 0xffff91de,
+ 0x0eb80562, 0xf7dd24ef, 0x05be9f49, 0xfbb0b04e, 0x034232e6, 0xfd92e36c, 0x01c16720, 0xfec70e64, 0x00cfe54f, 0xff7d7f76, 0x004c7c55, 0xffd6d798, 0x0013cf36, 0xfff7dcc8, 0x000291c3, 0xffff9434,
+ 0x0d9ad348, 0xf856502d, 0x0578b30e, 0xfbdcd57a, 0x03259644, 0xfda5627e, 0x01b5acdd, 0xfece433a, 0x00cba5bc, 0xff7fe07f, 0x004b3be3, 0xffd773ed, 0x001389b7, 0xfff7f83a, 0x0002888c, 0xffff9691,
+ 0x0c80911b, 0xf8cf80de, 0x05327467, 0xfc095174, 0x0308ae24, 0xfdb81e22, 0x01a9c4bc, 0xfed598fe, 0x00c74fce, 0xff824fca, 0x0049f2fc, 0xffd814d7, 0x00134204, 0xfff81490, 0x00027f11, 0xffff98f5,
+ 0x0b69517e, 0xf948a5f0, 0x04ebee1c, 0xfc361d25, 0x02eb7f44, 0xfdcb132d, 0x019db0d0, 0xfedd0e5c, 0x00c2e457, 0xff84ccdb, 0x0048a1e7, 0xffd8ba31, 0x0012f82e, 0xfff831c3, 0x00027555, 0xffff9b60,
+ 0x0a5526b0, 0xf9c1ae7b, 0x04a52af2, 0xfc633173, 0x02ce0e67, 0xfdde3e6f, 0x01917334, 0xfee4a1fa, 0x00be642f, 0xff875731, 0x004748ed, 0xffd963d4, 0x0012ac48, 0xfff84fcb, 0x00026b5b, 0xffff9dd0,
+ 0x0944228e, 0xfa3a89be, 0x045e359f, 0xfc908746, 0x02b0604f, 0xfdf19cb9, 0x01850e00, 0xfeec527e, 0x00b9d02b, 0xff89ee4d, 0x0045e856, 0xffda1199, 0x00125e66, 0xfff86e9e, 0x00026126, 0xffffa045,
+ 0x08365690, 0xfab32723, 0x041718d2, 0xfcbe1789, 0x029279c4, 0xfe052ad4, 0x01788354, 0xfef41e8c, 0x00b52925, 0xff8c91ad, 0x0044806c, 0xffdac35a, 0x00120e9b, 0xfff88e35, 0x000256b9, 0xffffa2be,
+ 0x072bd3c5, 0xfb2b7641, 0x03cfdf29, 0xfcebdb26, 0x02745f8c, 0xfe18e58c, 0x016bd54f, 0xfefc04c6, 0x00b06ff7, 0xff8f40d0, 0x00431177, 0xffdb78ef, 0x0011bcf9, 0xfff8ae88, 0x00024c18, 0xffffa539,
+ 0x0624aad6, 0xfba366df, 0x03889336, 0xfd19cb0e, 0x02561670, 0xfe2cc9a7, 0x015f0612, 0xff0403cc, 0x00aba57c, 0xff91fb31, 0x00419bc2, 0xffdc3231, 0x00116994, 0xfff8cf8d, 0x00024146, 0xffffa7b7,
+ 0x0520ec00, 0xfc1ae8f2, 0x03413f7b, 0xfd47e035, 0x0237a337, 0xfe40d3ed, 0x015217c0, 0xff0c1a3c, 0x00a6ca90, 0xff94c04f, 0x00401f98, 0xffdceef9, 0x00111480, 0xfff8f13c, 0x00023645, 0xffffaa35,
+ 0x0420a716, 0xfc91eca1, 0x02f9ee68, 0xfd761395, 0x02190aa6, 0xfe550124, 0x01450c7f, 0xff1446b5, 0x00a1e00f, 0xff978fa6, 0x003e9d42, 0xffddaf1e, 0x0010bdcf, 0xfff9138e, 0x00022b19, 0xffffacb4,
+ 0x0323eb7f, 0xfd086246, 0x02b2aa5c, 0xfda45e2c, 0x01fa5183, 0xfe694e12, 0x0137e672, 0xff1c87d3, 0x009ce6d8, 0xff9a68b0, 0x003d150d, 0xffde727a, 0x00106595, 0xfff93679, 0x00021fc5, 0xffffaf33,
+};
+
+// cmd-line: fir -l 7 -s 44100 -c 19876 -n 16 -b 9.62
+const uint32_t dn_sampler_filter_coefficients[] __attribute__ ((aligned (32))) = {
+ 0x736144b5, 0x0c333a22, 0xf4fca390, 0x09424904, 0xf8c92a41, 0x052ac04c, 0xfca4fc64, 0x01ed8cc7, 0xff119cc0, 0x0053ba6e, 0xfff9a80d, 0xffeaeaab, 0x001690d9, 0xfff11dcd, 0x000715d9, 0xfffdb4b9,
+ 0x735ed3aa, 0x0b433de8, 0xf560f0f3, 0x091282c4, 0xf8dd5ccf, 0x0525cb66, 0xfca23e3d, 0x01f33960, 0xff0bc9c2, 0x00586127, 0xfff68603, 0xffecbad5, 0x0015ab8b, 0xfff17c10, 0x0006f71a, 0xfffdbc2f,
+ 0x735780bb, 0x0a55a98f, 0xf5c5b2a1, 0x08e1ea27, 0xf8f25767, 0x0520366d, 0xfc9ff262, 0x01f89c98, 0xff0620a4, 0x005cf349, 0xfff36c0d, 0xffee8913, 0x0014c5dc, 0xfff1db1a, 0x0006d7d7, 0xfffdc3db,
+ 0x734b4c77, 0x096a8a51, 0xf62adb7c, 0x08b086aa, 0xf9081629, 0x051a030f, 0xfc9e186a, 0x01fdb637, 0xff00a1d8, 0x00617065, 0xfff05a84, 0xfff0552d, 0x0013dfed, 0xfff23ada, 0x0006b817, 0xfffdcbba,
+ 0x733a37d2, 0x0881ed1f, 0xf6905e79, 0x087e5fd7, 0xf91e9521, 0x05133308, 0xfc9cafe0, 0x0202860e, 0xfefb4dc7, 0x0065d80c, 0xffed51bc, 0xfff21ee8, 0x0012f9de, 0xfff29b40, 0x000697e0, 0xfffdd3ca,
+ 0x7324441e, 0x079bdea7, 0xf6f62e9d, 0x084b7d43, 0xf935d048, 0x050bc828, 0xfc9bb83e, 0x02070bf9, 0xfef624d8, 0x006a29d6, 0xffea520a, 0xfff3e60f, 0x001213d0, 0xfff2fc3d, 0x00067739, 0xfffddc07,
+ 0x7309730f, 0x06b86b52, 0xf75c3eff, 0x0817e68c, 0xf94dc388, 0x0503c44d, 0xfc9b30f3, 0x020b47dd, 0xfef12766, 0x006e655c, 0xffe75bbe, 0xfff5aa69, 0x00112de1, 0xfff35dc1, 0x00065629, 0xfffde470,
+ 0x72e9c6b8, 0x05d79f40, 0xf7c282cb, 0x07e3a35a, 0xf9666ab7, 0x04fb2969, 0xfc9b195f, 0x020f39ab, 0xfeec55cc, 0x00728a3d, 0xffe46f2a, 0xfff76bc2, 0x00104831, 0xfff3bfbc, 0x000634b6, 0xfffded03,
+ 0x72c5418e, 0x04f98649, 0xf828ed43, 0x07aebb5d, 0xf97fc19e, 0x04f1f97c, 0xfc9b70d6, 0x0212e15c, 0xfee7b059, 0x0076981a, 0xffe18c9a, 0xfff929e3, 0x000f62de, 0xfff4221f, 0x000612e8, 0xfffdf5bc,
+ 0x729be665, 0x041e2bfe, 0xf88f71bf, 0x0779364a, 0xf999c3f4, 0x04e83697, 0xfc9c369c, 0x02163ef1, 0xfee33759, 0x007a8e98, 0xffdeb45b, 0xfffae49b, 0x000e7e08, 0xfff484db, 0x0005f0c4, 0xfffdfe9b,
+ 0x726db871, 0x03459ba4, 0xf8f603ae, 0x07431bdf, 0xf9b46d64, 0x04dde2da, 0xfc9d69eb, 0x02195278, 0xfedeeb11, 0x007e6d61, 0xffdbe6b6, 0xfffc9bb4, 0x000d99cc, 0xfff4e7e1, 0x0005ce51, 0xfffe079b,
+ 0x723abb44, 0x026fe039, 0xf95c9699, 0x070c73dd, 0xf9cfb988, 0x04d30074, 0xfc9f09ee, 0x021c1c06, 0xfedacbbf, 0x00823422, 0xffd923f4, 0xfffe4efd, 0x000cb647, 0xfff54b20, 0x0005ab95, 0xfffe10bc,
+ 0x7202f2d3, 0x019d046d, 0xf9c31e22, 0x06d5460b, 0xf9eba3ef, 0x04c791a4, 0xfca115c5, 0x021e9bbb, 0xfed6d99c, 0x0085e28b, 0xffd66c59, 0xfffffe46, 0x000bd397, 0xfff5ae8c, 0x00058898, 0xfffe19fa,
+ 0x71c6636d, 0x00cd12a4, 0xfa298e07, 0x069d9a31, 0xfa082817, 0x04bb98b5, 0xfca38c83, 0x0220d1bf, 0xfed314da, 0x00897851, 0xffd3c02a, 0x0001a95d, 0x000af1d9, 0xfff61214, 0x0005655e, 0xfffe2354,
+ 0x718511c2, 0x000014f8, 0xfa8fda21, 0x0665781b, 0xfa254176, 0x04af1804, 0xfca66d2e, 0x0222be45, 0xfecf7da3, 0x008cf52d, 0xffd11fa9, 0x00035015, 0x000a1129, 0xfff675ab, 0x000541f0, 0xfffe2cc8,
+ 0x713f02e0, 0xff361534, 0xfaf5f669, 0x062ce795, 0xfa42eb75, 0x04a211f8, 0xfca9b6bf, 0x02246187, 0xfecc141d, 0x009058da, 0xffce8b13, 0x0004f23e, 0x000931a3, 0xfff6d942, 0x00051e52, 0xfffe3652,
+ 0x70f43c32, 0xfe6f1cd7, 0xfb5bd6f4, 0x05f3f06b, 0xfa61216f, 0x04948906, 0xfcad6827, 0x0225bbca, 0xfec8d867, 0x0093a31a, 0xffcc02a8, 0x00068fad, 0x00085362, 0xfff73ccb, 0x0004fa8b, 0xfffe3ff2,
+ 0x70a4c37f, 0xfdab350f, 0xfbc16ff6, 0x05ba9a6b, 0xfa7fdeba, 0x04867fb3, 0xfcb18047, 0x0226cd5b, 0xfec5ca9a, 0x0096d3af, 0xffc986a1, 0x00082835, 0x00077681, 0xfff7a037, 0x0004d6a1, 0xfffe49a4,
+ 0x70509eec, 0xfcea66be, 0xfc26b5c5, 0x0580ed5f, 0xfa9f1e9e, 0x0477f88d, 0xfcb5fdf7, 0x02279691, 0xfec2eaca, 0x0099ea62, 0xffc71738, 0x0009bbab, 0x00069b1b, 0xfff8037a, 0x0004b29a, 0xfffe5367,
+ 0x6ff7d4f8, 0xfc2cba75, 0xfc8b9cda, 0x0546f10f, 0xfabedc5a, 0x0468f62e, 0xfcbae002, 0x022817ca, 0xfec03901, 0x009ce6fe, 0xffc4b4a4, 0x000b49e6, 0x0005c149, 0xfff86686, 0x00048e7c, 0xfffe5d38,
+ 0x6f9a6c7f, 0xfb723876, 0xfcf019cd, 0x050cad3f, 0xfadf1328, 0x04597b40, 0xfcc0252b, 0x0228516f, 0xfebdb547, 0x009fc954, 0xffc25f1a, 0x000cd2bd, 0x0004e926, 0xfff8c94c, 0x00046a4c, 0xfffe6716,
+ 0x6f386cb6, 0xfabae8b2, 0xfd54215c, 0x04d229b1, 0xfaffbe36, 0x04498a72, 0xfcc5cc26, 0x022843f0, 0xfebb5f9b, 0x00a29136, 0xffc016cb, 0x000e5609, 0x000412c9, 0xfff92bc0, 0x00044612, 0xfffe70ff,
+ 0x6ed1dd2e, 0xfa06d2ca, 0xfdb7a869, 0x04976e20, 0xfb20d8ad, 0x04392684, 0xfccbd3a0, 0x0227efc6, 0xfeb937f9, 0x00a53e7b, 0xffbddbe8, 0x000fd3a3, 0x00033e4c, 0xfff98dd6, 0x000421d2, 0xfffe7aef,
+ 0x6e66c5ce, 0xf955fe0c, 0xfe1aa3fc, 0x045c8240, 0xfb425db0, 0x0428523d, 0xfcd23a3a, 0x02275572, 0xfeb73e54, 0x00a7d0ff, 0xffbbae9f, 0x00114b67, 0x00026bc6, 0xfff9ef80, 0x0003fd92, 0xfffe84e7,
+ 0x6df72ed9, 0xf8a87178, 0xfe7d0942, 0x04216dc0, 0xfb64485b, 0x0417106e, 0xfcd8fe8b, 0x0226757e, 0xfeb5729b, 0x00aa48a0, 0xffb98f1c, 0x0012bd30, 0x00019b4e, 0xfffa50b1, 0x0003d957, 0xfffe8ee3,
+ 0x6d8320e6, 0xf7fe33ba, 0xfedecd90, 0x03e63846, 0xfb8693c6, 0x040563f4, 0xfce01f21, 0x0225507c, 0xfeb3d4b7, 0x00aca542, 0xffb77d88, 0x001428db, 0x0000ccfc, 0xfffab15e, 0x0003b527, 0xfffe98e2,
+ 0x6d0aa4e6, 0xf7574b2b, 0xff3fe663, 0x03aae970, 0xfba93b01, 0x03f34fb2, 0xfce79a7f, 0x0223e706, 0xfeb26489, 0x00aee6ca, 0xffb57a0b, 0x00158e47, 0x000000e6, 0xfffb117a, 0x00039108, 0xfffea2e1,
+ 0x6c8dc41f, 0xf6b3bdd3, 0xffa04963, 0x036f88d2, 0xfbcc391d, 0x03e0d697, 0xfcef6f20, 0x022239bc, 0xfeb121ee, 0x00b10d23, 0xffb384ca, 0x0016ed53, 0xffff3721, 0xfffb70fa, 0x00036cfe, 0xfffeacdf,
+ 0x6c0c882a, 0xf6139169, 0xffffec5f, 0x03341df4, 0xfbef8924, 0x03cdfb99, 0xfcf79b75, 0x02204949, 0xfeb00cbf, 0x00b3183c, 0xffb19de7, 0x001845e0, 0xfffe6fc3, 0xfffbcfd2, 0x00034910, 0xfffeb6db,
+ 0x6b86faf8, 0xf576cb4e, 0x005ec552, 0x02f8b055, 0xfc13261f, 0x03bac1b4, 0xfd001de8, 0x021e165d, 0xfeaf24cc, 0x00b50805, 0xffafc584, 0x001997d0, 0xfffdaadf, 0xfffc2df6, 0x00032541, 0xfffec0d2,
+ 0x6afd26cb, 0xf4dd7092, 0x00bcca63, 0x02bd4768, 0xfc370b14, 0x03a72bf0, 0xfd08f4d6, 0x021ba1b2, 0xfeae69e1, 0x00b6dc75, 0xffadfbbe, 0x001ae306, 0xfffce88b, 0xfffc8b5c, 0x00030196, 0xfffecac3,
+ 0x6a6f1638, 0xf44785f1, 0x0119f1e4, 0x0281ea90, 0xfc5b3309, 0x03933d58, 0xfd121e99, 0x0218ec06, 0xfeaddbc4, 0x00b89584, 0xffac40b3, 0x001c2765, 0xfffc28d9, 0xfffce7f8, 0x0002de16, 0xfffed4ab,
+ 0x69dcd425, 0xf3b50fd6, 0x01763256, 0x0246a125, 0xfc7f9902, 0x037ef900, 0xfd1b9980, 0x0215f621, 0xfead7a37, 0x00ba3330, 0xffaa947c, 0x001d64d5, 0xfffb6bdd, 0xfffd43c1, 0x0002bac4, 0xfffede8a,
+ 0x69466bc8, 0xf3261255, 0x01d18265, 0x020b726f, 0xfca43803, 0x036a6201, 0xfd2563d3, 0x0212c0d2, 0xfead44f4, 0x00bbb579, 0xffa8f730, 0x001e9b3a, 0xfffab1a8, 0xfffd9eab, 0x000297a5, 0xfffee85e,
+ 0x68abe8a8, 0xf29a9133, 0x022bd8ee, 0x01d065a8, 0xfcc90b12, 0x03557b7a, 0xfd2f7bd1, 0x020f4cec, 0xfead3bb2, 0x00bd1c63, 0xffa768e6, 0x001fca7d, 0xfff9fa4d, 0xfffdf8ae, 0x000274be, 0xfffef225,
+ 0x680d5698, 0xf2128fde, 0x02852cfc, 0x019581f9, 0xfcee0d33, 0x03404890, 0xfd39dfb4, 0x020b9b4c, 0xfead5e22, 0x00be67f6, 0xffa5e9b1, 0x0020f288, 0xfff945dc, 0xfffe51be, 0x00025214, 0xfffefbde,
+ 0x676ac1bb, 0xf18e1174, 0x02dd75ca, 0x015ace79, 0xfd133970, 0x032acc6d, 0xfd448dae, 0x0207acd4, 0xfeadabef, 0x00bf983d, 0xffa479a2, 0x00221344, 0xfff89465, 0xfffea9d2, 0x00022fa9, 0xffff0587,
+ 0x66c4367d, 0xf10d18bd, 0x0334aac4, 0x0120522f, 0xfd388ad1, 0x03150a3f, 0xfd4f83eb, 0x0203826c, 0xfeae24c1, 0x00c0ad48, 0xffa318c7, 0x00232c9d, 0xfff7e5f9, 0xffff00e1, 0x00020d84, 0xffff0f1f,
+ 0x6619c197, 0xf08fa82f, 0x038ac385, 0x00e6140f, 0xfd5dfc63, 0x02ff0538, 0xfd5ac08e, 0x01ff1d04, 0xfeaec838, 0x00c1a728, 0xffa1c72f, 0x00243e7f, 0xfff73aa7, 0xffff56e3, 0x0001eba8, 0xffff18a4,
+ 0x656b700a, 0xf015c1ee, 0x03dfb7dd, 0x00ac1af9, 0xfd838938, 0x02e8c08e, 0xfd6641b8, 0x01fa7d91, 0xfeaf95f2, 0x00c285f4, 0xffa084e3, 0x002548d9, 0xfff6927e, 0xffffabcd, 0x0001ca18, 0xffff2215,
+ 0x64b94f22, 0xef9f67cb, 0x04337fcb, 0x00726dbb, 0xfda92c63, 0x02d23f7a, 0xfd720581, 0x01f5a50d, 0xfeb08d86, 0x00c349c4, 0xff9f51eb, 0x00264b9a, 0xfff5ed8b, 0xffffff99, 0x0001a8da, 0xffff2b70,
+ 0x64036c6f, 0xef2c9b43, 0x04861383, 0x0039130c, 0xfdcee0ff, 0x02bb8537, 0xfd7e09fc, 0x01f0947a, 0xfeb1ae87, 0x00c3f2b6, 0xff9e2e50, 0x002746b2, 0xfff54bdc, 0x0000523d, 0x000187f0, 0xffff34b6,
+ 0x6349d5c9, 0xeebd5d81, 0x04d76b6b, 0x00001191, 0xfdf4a22a, 0x02a49505, 0xfd8a4d37, 0x01eb4cde, 0xfeb2f884, 0x00c480e9, 0xff9d1a14, 0x00283a12, 0xfff4ad7e, 0x0000a3b3, 0x0001675f, 0xffff3de3,
+ 0x628c994c, 0xee51af5f, 0x0527801d, 0xffc76fd5, 0xfe1a6b08, 0x028d7223, 0xfd96cd3d, 0x01e5cf44, 0xfeb46b07, 0x00c4f480, 0xff9c1539, 0x002925ae, 0xfff4127d, 0x0000f3f1, 0x00014729, 0xffff46f7,
+ 0x61cbc559, 0xede99165, 0x05764a68, 0xff8f344f, 0xfe4036c5, 0x02761fd3, 0xfda3880f, 0x01e01cbe, 0xfeb60596, 0x00c54da2, 0xff9b1fc1, 0x002a0979, 0xfff37ae4, 0x000142f1, 0x00012754, 0xffff4ff1,
+ 0x61076890, 0xed8503c7, 0x05c3c34e, 0xff576560, 0xfe660094, 0x025ea157, 0xfdb07bb0, 0x01da3661, 0xfeb7c7b0, 0x00c58c79, 0xff9a39a9, 0x002ae568, 0xfff2e6bf, 0x000190ac, 0x000107e1, 0xffff58d0,
+ 0x603f91d5, 0xed24066b, 0x060fe408, 0xff20094d, 0xfe8bc3ad, 0x0246f9f3, 0xfdbda61a, 0x01d41d4a, 0xfeb9b0d3, 0x00c5b132, 0xff9962ec, 0x002bb971, 0xfff25619, 0x0001dd1b, 0x0000e8d4, 0xffff6192,
+ 0x5f745049, 0xecc698e6, 0x065aa604, 0xfee92646, 0xfeb17b53, 0x022f2cea, 0xfdcb0546, 0x01cdd297, 0xfebbc078, 0x00c5bbfc, 0xff989b85, 0x002c858d, 0xfff1c8fa, 0x00022837, 0x0000ca30, 0xffff6a38,
+ 0x5ea5b34c, 0xec6cba79, 0x06a402e4, 0xfeb2c261, 0xfed722d0, 0x02173d81, 0xfdd89727, 0x01c7576d, 0xfebdf613, 0x00c5ad0a, 0xff97e36c, 0x002d49b4, 0xfff13f6c, 0x000271fa, 0x0000abf8, 0xffff72be,
+ 0x5dd3ca7a, 0xec166a19, 0x06ebf483, 0xfe7ce399, 0xfefcb57a, 0x01ff2ef9, 0xfde659af, 0x01c0acf5, 0xfec05114, 0x00c58494, 0xff973a96, 0x002e05df, 0xfff0b977, 0x0002ba5f, 0x00008e30, 0xffff7b26,
+ 0x5cfea5aa, 0xebc3a669, 0x073274f1, 0xfe478fd2, 0xff222eac, 0x01e70494, 0xfdf44acc, 0x01b9d45b, 0xfec2d0e8, 0x00c542d1, 0xff96a0f8, 0x002eba0a, 0xfff03724, 0x0003015f, 0x000070d9, 0xffff836d,
+ 0x5c2654ed, 0xeb746dbe, 0x07777e74, 0xfe12ccd1, 0xff4789d1, 0x01cec194, 0xfe026869, 0x01b2ced1, 0xfec574f9, 0x00c4e7fe, 0xff961684, 0x002f6630, 0xffefb87a, 0x000346f6, 0x000053f7, 0xffff8b93,
+ 0x5b4ae88d, 0xeb28be1f, 0x07bb0b8b, 0xfddea042, 0xff6cc25a, 0x01b66936, 0xfe10b06f, 0x01ab9d8b, 0xfec83caa, 0x00c47459, 0xff959b29, 0x00300a4f, 0xffef3d7f, 0x00038b1d, 0x0000378c, 0xffff9398,
+ 0x5a6c7108, 0xeae09544, 0x07fd16eb, 0xfdab0fb6, 0xff91d3c6, 0x019dfeb6, 0xfe1f20c5, 0x01a441c2, 0xfecb275e, 0x00c3e824, 0xff952ed7, 0x0030a665, 0xffeec63a, 0x0003cdd1, 0x00001b9a, 0xffff9b7a,
+ 0x598aff13, 0xea9bf097, 0x083d9b81, 0xfd7820a0, 0xffb6b99f, 0x0185854f, 0xfe2db74f, 0x019cbcb1, 0xfece3472, 0x00c343a4, 0xff94d178, 0x00313a72, 0xffee52b1, 0x00040f0d, 0x00000024, 0xffffa339,
+ 0x58a6a397, 0xea5acd38, 0x087c9471, 0xfd45d856, 0xffdb6f7c, 0x016d0037, 0xfe3c71f1, 0x01950f98, 0xfed16342, 0x00c2871f, 0xff9482f8, 0x0031c677, 0xffede2e7, 0x00044ecb, 0xffffe52d, 0xffffaad3,
+ 0x57bf6fae, 0xea1d27f7, 0x08b9fd18, 0xfd143c12, 0xfffff100, 0x015472a1, 0xfe4b4e8c, 0x018d3bb8, 0xfed4b325, 0x00c1b2e0, 0xff944340, 0x00324a74, 0xffed76e3, 0x00048d0a, 0xffffcab5, 0xffffb249,
+ 0x56d574a2, 0xe9e2fd5b, 0x08f5d10a, 0xfce350f0, 0x002439db, 0x013bdfbc, 0xfe5a4b03, 0x01854258, 0xfed82370, 0x00c0c731, 0xff941236, 0x0032c66e, 0xffed0ea7, 0x0004c9c4, 0xffffb0bf, 0xffffb99a,
+ 0x55e8c3ee, 0xe9ac49a0, 0x09300c14, 0xfcb31bec, 0x004845cc, 0x01234ab4, 0xfe696534, 0x017d24bf, 0xfedbb373, 0x00bfc463, 0xff93efbf, 0x00333a67, 0xffecaa36, 0x000504f6, 0xffff974d, 0xffffc0c5,
+ 0x54f96f37, 0xe97908b8, 0x0968aa3b, 0xfc83a1e5, 0x006c10a0, 0x010ab6b0, 0xfe789b01, 0x0174e437, 0xfedf627d, 0x00beaac6, 0xff93dbc0, 0x0033a665, 0xffec4994, 0x00053e9e, 0xffff7e61, 0xffffc7ca,
+ 0x54078851, 0xe9493649, 0x099fa7bb, 0xfc54e79a, 0x008f9631, 0x00f226d0, 0xfe87ea47, 0x016c820d, 0xfee32fdb, 0x00bd7aae, 0xff93d618, 0x00340a6d, 0xffebecc2, 0x000576b8, 0xffff65fc, 0xffffcea8,
+ 0x53132138, 0xe91ccdb5, 0x09d5010b, 0xfc26f1ad, 0x00b2d26b, 0x00d99e31, 0xfe9750e8, 0x0163ff90, 0xfee71ad4, 0x00bc3470, 0xff93deaa, 0x00346687, 0xffeb93c3, 0x0005ad41, 0xffff4e20, 0xffffd55f,
+ 0x521c4c10, 0xe8f3ca12, 0x0a08b2d9, 0xfbf9c49d, 0x00d5c147, 0x00c11feb, 0xfea6ccc3, 0x015b5e11, 0xfeeb22af, 0x00bad866, 0xff93f552, 0x0034babb, 0xffeb3e96, 0x0005e238, 0xffff36ce, 0xffffdbee,
+ 0x51231b26, 0xe8ce2631, 0x0a3aba09, 0xfbcd64ca, 0x00f85ecf, 0x00a8af0c, 0xfeb65bb9, 0x01529ee3, 0xfeef46b0, 0x00b966e9, 0xff9419ef, 0x00350711, 0xffeaed3c, 0x00061599, 0xffff2007, 0xffffe255,
+ 0x5027a0e9, 0xe8abdc9d, 0x0a6b13bc, 0xfba1d673, 0x011aa71d, 0x00904ea0, 0xfec5fbac, 0x0149c35a, 0xfef3861a, 0x00b7e055, 0xff944c5a, 0x00354b94, 0xffea9fb6, 0x00064764, 0xffff09ce, 0xffffe894,
+ 0x4f29efed, 0xe88ce79a, 0x0a99bd47, 0xfb771db9, 0x013c965b, 0x007801aa, 0xfed5aa7e, 0x0140cccb, 0xfef7e02a, 0x00b6450a, 0xff948c6e, 0x0035884f, 0xffea5602, 0x00067797, 0xfffef421, 0xffffeeaa,
+ 0x4e2a1ae8, 0xe871412a, 0x0ac6b43a, 0xfb4d3e97, 0x015e28c7, 0x005fcb26, 0xfee56614, 0x0137bc8f, 0xfefc541e, 0x00b49568, 0xff94da03, 0x0035bd4e, 0xffea1020, 0x0006a630, 0xfffedf04, 0xfffff498,
+ 0x4d2834b0, 0xe858e30a, 0x0af1f65d, 0xfb243cea, 0x017f5aad, 0x0047ae09, 0xfef52c54, 0x012e93fc, 0xff00e133, 0x00b2d1d1, 0xff9534f0, 0x0035ea9d, 0xffe9ce0d, 0x0006d32f, 0xfffeca76, 0xfffffa5d,
+ 0x4c245038, 0xe843c6b5, 0x0b1b81ad, 0xfafc1c6e, 0x01a0286c, 0x002fad3f, 0xff04fb25, 0x0125546c, 0xff0586a0, 0x00b0faaa, 0xff959d0a, 0x0036104b, 0xffe98fc8, 0x0006fe92, 0xfffeb678, 0xfffffff8,
+ 0x4b1e8091, 0xe831e563, 0x0b435462, 0xfad4e0b9, 0x01c08e78, 0x0017cbae, 0xff14d073, 0x011bff38, 0xff0a439e, 0x00af1059, 0xff961224, 0x00362e66, 0xffe9554c, 0x00072859, 0xfffea30b, 0x0000056a,
+ 0x4a16d8e5, 0xe823380d, 0x0b696ceb, 0xfaae8d43, 0x01e08952, 0x00000c33, 0xff24aa2a, 0x011295bb, 0xff0f1762, 0x00ad1346, 0xff969412, 0x003644fd, 0xffe91e99, 0x00075084, 0xfffe9030, 0x00000ab3,
+ 0x490d6c79, 0xe817b76c, 0x0b8dc9ed, 0xfa89255f, 0x02001593, 0xffe871a0, 0xff348639, 0x0109194f, 0xff140121, 0x00ab03da, 0xff9722a5, 0x00365422, 0xffe8eba8, 0x00077712, 0xfffe7de7, 0x00000fd2,
+ 0x48024ea7, 0xe80f5bfb, 0x0bb06a47, 0xfa64ac3f, 0x021f2fe5, 0xffd0fec1, 0xff446293, 0x00ff8b4f, 0xff19000e, 0x00a8e282, 0xff97bdac, 0x00365be6, 0xffe8bc77, 0x00079c04, 0xfffe6c2f, 0x000014c8,
+ 0x46f592e2, 0xe80a1df5, 0x0bd14d0b, 0xfa4124f2, 0x023dd505, 0xffb9b656, 0xff543d2e, 0x00f5ed15, 0xff1e135b, 0x00a6afa8, 0xff9864f6, 0x00365c5b, 0xffe89101, 0x0007bf5b, 0xfffe5b0b, 0x00001994,
+ 0x45e74cad, 0xe807f55b, 0x0bf07186, 0xfa1e9262, 0x025c01c5, 0xffa29b18, 0xff641402, 0x00ec3ffc, 0xff233a39, 0x00a46bbc, 0xff991851, 0x00365594, 0xffe8693f, 0x0007e116, 0xfffe4a79, 0x00001e37,
+ 0x44d78fa0, 0xe808d9f1, 0x0c0dd738, 0xf9fcf758, 0x0279b30b, 0xff8bafb3, 0xff73e50e, 0x00e2855d, 0xff2873d6, 0x00a2172d, 0xff99d789, 0x003647a5, 0xffe8452d, 0x00080137, 0xfffe3a79, 0x000022b1,
+ 0x43c66f62, 0xe80cc342, 0x0c297dd9, 0xf9dc567b, 0x0296e5d0, 0xff74f6cc, 0xff83ae52, 0x00d8be92, 0xff2dbf61, 0x009fb26c, 0xff9aa268, 0x003632a2, 0xffe824c5, 0x00081fbf, 0xfffe2b0d, 0x00002701,
+ 0x42b3ffa9, 0xe813a89f, 0x0c436557, 0xf9bcb24a, 0x02b39724, 0xff5e72fb, 0xff936dd2, 0x00ceecf5, 0xff331c08, 0x009d3deb, 0xff9b78ba, 0x003616a2, 0xffe807ff, 0x00083cb0, 0xfffe1c32, 0x00002b28,
+ 0x41a05437, 0xe81d8122, 0x0c5b8dd4, 0xf99e0d26, 0x02cfc429, 0xff4826cf, 0xffa3219a, 0x00c511dc, 0xff3888f8, 0x009aba1d, 0xff9c5a47, 0x0035f3b9, 0xffe7eed5, 0x0008580a, 0xfffe0dea, 0x00002f26,
+ 0x408b80d9, 0xe82a43ac, 0x0c71f7a9, 0xf980694a, 0x02eb6a18, 0xff3214c9, 0xffb2c7b6, 0x00bb2e9f, 0xff3e055d, 0x00982778, 0xff9d46d6, 0x0035ca00, 0xffe7d93f, 0x000871cf, 0xfffe0034, 0x000032fb,
+ 0x3f759967, 0xe839e6e9, 0x0c86a361, 0xf963c8cc, 0x03068640, 0xff1c3f63, 0xffc25e3b, 0x00b14493, 0xff439064, 0x0095866f, 0xff9e3e30, 0x0035998d, 0xffe7c735, 0x00088a02, 0xfffdf310, 0x000036a8,
+ 0x3e5eb1bd, 0xe84c6152, 0x0c9991be, 0xf9482da0, 0x03211603, 0xff06a907, 0xffd1e340, 0x00a7550c, 0xff492937, 0x0092d77b, 0xff9f4019, 0x00356279, 0xffe7b8af, 0x0008a0a5, 0xfffde67c, 0x00003a2d,
+ 0x3d46ddc1, 0xe861a92b, 0x0caac3b5, 0xf92d9997, 0x033b16dc, 0xfef15417, 0xffe154e3, 0x009d615d, 0xff4ecf02, 0x00901b11, 0xffa04c57, 0x003524dd, 0xffe7ada5, 0x0008b5ba, 0xfffdda79, 0x00003d89,
+ 0x3c2e315a, 0xe879b487, 0x0cba3a6d, 0xf9140e5e, 0x03548659, 0xfedc42e7, 0xfff0b148, 0x00936ad6, 0xff5480f0, 0x008d51ab, 0xffa162ae, 0x0034e0d3, 0xffe7a60d, 0x0008c944, 0xfffdcf05, 0x000040be,
+ 0x3b14c072, 0xe8947947, 0x0cc7f742, 0xf8fb8d7d, 0x036d621f, 0xfec777be, 0xfffff697, 0x008972c7, 0xff5a3e2c, 0x008a7bc1, 0xffa282e1, 0x00349674, 0xffe7a1de, 0x0008db46, 0xfffdc421, 0x000043cc,
+ 0x39fa9ef3, 0xe8b1ed1c, 0x0cd3fbc0, 0xf8e4185a, 0x0385a7eb, 0xfeb2f4d9, 0x000f22fe, 0x007f7a7c, 0xff6005e1, 0x008799cd, 0xffa3acb4, 0x003445dc, 0xffe7a10d, 0x0008ebc1, 0xfffdb9cb, 0x000046b2,
+ 0x38dfe0c6, 0xe8d2058b, 0x0cde49a8, 0xf8cdb036, 0x039d558e, 0xfe9ebc66, 0x001e34b4, 0x00758341, 0xff65d73a, 0x0084ac48, 0xffa4dfe8, 0x0033ef25, 0xffe7a391, 0x0008fabb, 0xfffdb002, 0x00004972,
+ 0x37c499d0, 0xe8f4b7e9, 0x0ce6e2ea, 0xf8b85631, 0x03b468f1, 0xfe8ad087, 0x002d29f3, 0x006b8e5c, 0xff6bb163, 0x0081b3af, 0xffa61c3e, 0x0033926d, 0xffe7a95f, 0x00090836, 0xfffda6c5, 0x00004c0b,
+ 0x36a8ddf3, 0xe919f961, 0x0cedc9a7, 0xf8a40b44, 0x03cae014, 0xfe773351, 0x003c00fd, 0x00619d15, 0xff719388, 0x007eb07b, 0xffa76176, 0x00332fcf, 0xffe7b26c, 0x00091435, 0xfffd9e13, 0x00004e7f,
+ 0x358cc109, 0xe941bef3, 0x0cf30031, 0xf890d048, 0x03e0b90d, 0xfe63e6cb, 0x004ab81b, 0x0057b0ae, 0xff777cd6, 0x007ba32a, 0xffa8af51, 0x0032c769, 0xffe7bead, 0x00091ebd, 0xfffd95eb, 0x000050cd,
+ 0x347056e3, 0xe96bfd76, 0x0cf6890a, 0xf87ea5f1, 0x03f5f20a, 0xfe50ecf0, 0x00594d9d, 0x004dca68, 0xff7d6c79, 0x00788c36, 0xffaa058d, 0x00325958, 0xffe7ce16, 0x000927d1, 0xfffd8e4d, 0x000052f7,
+ 0x3353b349, 0xe998a999, 0x0cf866e1, 0xf86d8cd1, 0x040a894e, 0xfe3e47ac, 0x0067bfd8, 0x0043eb7f, 0xff83619f, 0x00756c1d, 0xffab63ea, 0x0031e5ba, 0xffe7e09c, 0x00092f75, 0xfffd8735, 0x000054fc,
+ 0x3236e9f7, 0xe9c7b7e3, 0x0cf89c96, 0xf85d8555, 0x041e7d34, 0xfe2bf8de, 0x00760d2a, 0x003a152f, 0xff895b77, 0x0072435b, 0xffacca25, 0x00316cae, 0xffe7f631, 0x000935ad, 0xfffd80a4, 0x000056dd,
+ 0x311a0e9b, 0xe9f91cb9, 0x0cf72d34, 0xf84e8fc9, 0x0431cc31, 0xfe1a0256, 0x008433f9, 0x003048ae, 0xff8f5930, 0x006f126b, 0xffae37fd, 0x0030ee53, 0xffe80eca, 0x00093a7f, 0xfffd7a98, 0x0000589b,
+ 0x2ffd34d4, 0xea2ccc59, 0x0cf41bf7, 0xf840ac57, 0x044474ce, 0xfe0865d7, 0x009232b2, 0x0026872f, 0xff9559fb, 0x006bd9cd, 0xffafad2e, 0x00306ac8, 0xffe82a59, 0x00093ded, 0xfffd750f, 0x00005a36,
+ 0x2ee07030, 0xea62bae0, 0x0cef6c43, 0xf833db04, 0x045675ab, 0xfdf72515, 0x00a007c9, 0x001cd1e4, 0xff9b5d0a, 0x006899fb, 0xffb12976, 0x002fe22c, 0xffe848d3, 0x00093ffe, 0xfffd7008, 0x00005baf,
+ 0x2dc3d429, 0xea9adc49, 0x0ce921ab, 0xf8281bb6, 0x0467cd83, 0xfde641b7, 0x00adb1bb, 0x001329f7, 0xffa16190, 0x00655372, 0xffb2ac90, 0x002f54a1, 0xffe86a29, 0x000940b6, 0xfffd6b81, 0x00005d06,
+ 0x2ca77428, 0xead52471, 0x0ce13feb, 0xf81d6e2e, 0x04787b24, 0xfdd5bd53, 0x00bb2f0b, 0x00099093, 0xffa766c0, 0x006206b1, 0xffb4363a, 0x002ec246, 0xffe88e4d, 0x00094019, 0xfffd6779, 0x00005e3d,
+ 0x2b8b637b, 0xeb118714, 0x0cd7caec, 0xf813d20d, 0x04887d76, 0xfdc59972, 0x00c87e47, 0x000006db, 0xffad6bd0, 0x005eb431, 0xffb5c630, 0x002e2b3c, 0xffe8b532, 0x00093e2e, 0xfffd63ed, 0x00005f52,
+ 0x2a6fb55e, 0xeb4ff7d4, 0x0cccc6bc, 0xf80b46d3, 0x0497d378, 0xfdb5d78f, 0x00d59e03, 0xfff68df1, 0xffb36ff9, 0x005b5c71, 0xffb75c2c, 0x002d8fa4, 0xffe8decb, 0x00093af8, 0xfffd60dd, 0x00006048,
+ 0x29547ced, 0xeb906a35, 0x0cc03797, 0xf803cbdc, 0x04a67c41, 0xfda67913, 0x00e28cdd, 0xffed26f0, 0xffb97271, 0x0057ffec, 0xffb8f7ea, 0x002cefa1, 0xffe90b08, 0x0009367e, 0xfffd5e46, 0x0000611f,
+ 0x2839cd30, 0xebd2d1a1, 0x0cb221de, 0xf7fd6065, 0x04b476fe, 0xfd977f5d, 0x00ef497a, 0xffe3d2f2, 0xffbf7274, 0x00549f1c, 0xffba9927, 0x002c4b53, 0xffe939db, 0x000930c4, 0xfffd5c26, 0x000061d8,
+ 0x271fb90d, 0xec17216b, 0x0ca28a1a, 0xf7f8038c, 0x04c1c2f3, 0xfd88ebb9, 0x00fbd28a, 0xffda930a, 0xffc56f3e, 0x00513a7e, 0xffbc3f9d, 0x002ba2dc, 0xffe96b35, 0x000929d1, 0xfffd5a7c, 0x00006272,
+ 0x2606534e, 0xec5d4ccd, 0x0c9174fa, 0xf7f3b44b, 0x04ce5f7d, 0xfd7abf64, 0x010826c4, 0xffd16848, 0xffcb680e, 0x004dd28c, 0xffbdeb07, 0x002af65f, 0xffe99f08, 0x000921aa, 0xfffd5945, 0x000062f0,
+ 0x24edae9c, 0xeca546eb, 0x0c7ee754, 0xf7f0717e, 0x04da4c10, 0xfd6cfb8e, 0x011444e7, 0xffc853b6, 0xffd15c22, 0x004a67c0, 0xffbf9b21, 0x002a45fe, 0xffe9d545, 0x00091854, 0xfffd5880, 0x00006351,
+ 0x23d5dd81, 0xecef02d5, 0x0c6ae622, 0xf7ee39e2, 0x04e58836, 0xfd5fa157, 0x01202bbe, 0xffbf565a, 0xffd74abe, 0x0046fa93, 0xffc14fa5, 0x002991db, 0xffea0ddc, 0x00090dd6, 0xfffd582a, 0x00006396,
+ 0x22bef262, 0xed3a7388, 0x0c557681, 0xf7ed0c12, 0x04f01392, 0xfd52b1cf, 0x012bda1b, 0xffb67137, 0xffdd3325, 0x00438b7e, 0xffc3084f, 0x0028da1a, 0xffea48be, 0x00090236, 0xfffd5842, 0x000063c0,
+ 0x21a8ff7e, 0xed878bf0, 0x0c3e9db5, 0xf7ece68c, 0x04f9edda, 0xfd462df6, 0x01374eda, 0xffada547, 0xffe3149e, 0x00401af9, 0xffc4c4da, 0x00281edd, 0xffea85dc, 0x0008f57a, 0xfffd58c5, 0x000063d0,
+ 0x209416f2, 0xedd63ee5, 0x0c26611f, 0xf7edc7af, 0x050316e0, 0xfd3a16c0, 0x014288e0, 0xffa4f383, 0xffe8ee72, 0x003ca97b, 0xffc68502, 0x00276046, 0xffeac525, 0x0008e7a7, 0xfffd59b2, 0x000063c6,
+ 0x1f804ab0, 0xee267f35, 0x0c0cc646, 0xf7efadbd, 0x050b8e8a, 0xfd2e6d0d, 0x014d871b, 0xff9c5cdc, 0xffeebfec, 0x0039377a, 0xffc84881, 0x00269e7a, 0xffeb068a, 0x0008d8c4, 0xfffd5b05, 0x000063a3,
+ 0x1e6dac83, 0xee783f9e, 0x0bf1d2d0, 0xf7f296d7, 0x051354d5, 0xfd2331b0, 0x01584883, 0xff93e241, 0xfff48859, 0x0035c56c, 0xffca0f14, 0x0025d99b, 0xffeb49fc, 0x0008c8d7, 0xfffd5cbe, 0x00006368,
+ 0x1d5c4e09, 0xeecb72d1, 0x0bd58c81, 0xf7f68103, 0x051a69d4, 0xfd18656f, 0x0162cc19, 0xff8b8498, 0xfffa470a, 0x003253c6, 0xffcbd876, 0x002511cd, 0xffeb8f6a, 0x0008b7e7, 0xfffd5ed8, 0x00006316,
+ 0x1c4c40b6, 0xef200b76, 0x0bb7f940, 0xf7fb6a29, 0x0520cdb1, 0xfd0e08fb, 0x016d10e9, 0xff8344c4, 0xfffffb51, 0x002ee2fa, 0xffcda463, 0x00244733, 0xffebd6c4, 0x0008a5fa, 0xfffd6154, 0x000062ad,
+ 0x1b3d95d1, 0xef75fc2b, 0x0b991f0f, 0xf8015015, 0x052680ae, 0xfd041cfa, 0x01771608, 0xff7b23a1, 0x0005a483, 0x002b737b, 0xffcf7299, 0x002379ef, 0xffec1ffa, 0x00089316, 0xfffd642d, 0x0000622e,
+ 0x1a305e70, 0xefcd3787, 0x0b79040c, 0xf8083077, 0x052b8320, 0xfcfaa200, 0x0180da94, 0xff732209, 0x000b41fa, 0x002805ba, 0xffd142d3, 0x0022aa26, 0xffec6afc, 0x00087f43, 0xfffd6762, 0x0000619a,
+ 0x1924ab7b, 0xf025b01a, 0x0b57ae75, 0xf81008e2, 0x052fd573, 0xfcf19894, 0x018a5db5, 0xff6b40cb, 0x0010d30e, 0x00249a28, 0xffd314cf, 0x0021d7fa, 0xffecb7b9, 0x00086a86, 0xfffd6af1, 0x000060f1,
+ 0x181a8da5, 0xf07f586e, 0x0b3524a0, 0xf818d6cf, 0x0533782a, 0xfce9012c, 0x01939e9e, 0xff6380b5, 0x00165720, 0x00213134, 0xffd4e84a, 0x00210390, 0xffed0621, 0x000854e6, 0xfffd6ed6, 0x00006035,
+ 0x17121573, 0xf0da230b, 0x0b116cff, 0xf822979b, 0x05366bdc, 0xfce0dc2f, 0x019c9c8b, 0xff5be28d, 0x001bcd8e, 0x001dcb4a, 0xffd6bd01, 0x00202d09, 0xffed5624, 0x00083e6a, 0xfffd7310, 0x00005f66,
+ 0x160b5331, 0xf1360276, 0x0aec8e1c, 0xf82d488c, 0x0538b136, 0xfcd929f4, 0x01a556c1, 0xff546713, 0x002135bd, 0x001a68d8, 0xffd892b4, 0x001f5489, 0xffeda7b1, 0x00082718, 0xfffd779d, 0x00005e84,
+ 0x150656f8, 0xf192e932, 0x0ac68e9b, 0xf838e6c9, 0x053a48fa, 0xfcd1eac3, 0x01adcc91, 0xff4d0f02, 0x00268f13, 0x00170a47, 0xffda6921, 0x001e7a33, 0xffedfab8, 0x00080ef7, 0xfffd7c7a, 0x00005d92,
+ 0x140330a9, 0xf1f0c9c5, 0x0a9f7537, 0xf8456f65, 0x053b3400, 0xfccb1ed7, 0x01b5fd54, 0xff45db10, 0x002bd8fa, 0x0013b003, 0xffdc4007, 0x001d9e2a, 0xffee4f29, 0x0007f60f, 0xfffd81a4, 0x00005c8e,
+ 0x1301efed, 0xf24f96b5, 0x0a7748c0, 0xf852df56, 0x053b7332, 0xfcc4c658, 0x01bde86f, 0xff3ecbea, 0x003112e0, 0x00105a72, 0xffde1726, 0x001cc091, 0xffeea4f2, 0x0007dc65, 0xfffd8719, 0x00005b7b,
+ 0x1202a434, 0xf2af428c, 0x0a4e101f, 0xf861337c, 0x053b0791, 0xfcbee162, 0x01c58d50, 0xff37e23b, 0x00363c35, 0x000d09fc, 0xffdfee3f, 0x001be18a, 0xffeefc04, 0x0007c201, 0xfffd8cd7, 0x00005a58,
+ 0x11055cb4, 0xf30fbfd7, 0x0a23d24e, 0xf870689f, 0x0539f231, 0xfcb97001, 0x01cceb6e, 0xff311ea4, 0x003b546b, 0x0009bf05, 0xffe1c511, 0x001b0138, 0xffef544e, 0x0007a6e9, 0xfffd92db, 0x00005927,
+ 0x100a2864, 0xf371012c, 0x09f8965d, 0xf8807b70, 0x0538343a, 0xfcb47232, 0x01d4024c, 0xff2a81c4, 0x00405afa, 0x000679f2, 0xffe39b60, 0x001a1fbc, 0xffefadc0, 0x00078b24, 0xfffd9923, 0x000057e9,
+ 0x0f111603, 0xf3d2f926, 0x09cc636e, 0xf8916889, 0x0535cee9, 0xfcafe7e2, 0x01dad175, 0xff240c2f, 0x00454f5d, 0x00033b23, 0xffe570ed, 0x00193d3a, 0xfff00849, 0x00076eba, 0xfffd9fac, 0x0000569d,
+ 0x0e1a340d, 0xf4359a6a, 0x099f40b5, 0xf8a32c6e, 0x0532c38c, 0xfcabd0f2, 0x01e15880, 0xff1dbe77, 0x004a310f, 0x000002f9, 0xffe7457c, 0x001859d2, 0xfff063d9, 0x000751b0, 0xfffda675, 0x00005545,
+ 0x0d2590c3, 0xf498d7a5, 0x09713575, 0xf8b5c38d, 0x052f1386, 0xfca82d32, 0x01e7970e, 0xff179926, 0x004eff94, 0xfffcd1d3, 0xffe918ce, 0x001775a7, 0xfff0c060, 0x0007340d, 0xfffdad79, 0x000053e2,
+ 0x0c333a22, 0xf4fca390, 0x09424904, 0xf8c92a41, 0x052ac04c, 0xfca4fc64, 0x01ed8cc7, 0xff119cc0, 0x0053ba6e, 0xfff9a80d, 0xffeaeaab, 0x001690d9, 0xfff11dcd, 0x000715d9, 0xfffdb4b9, 0x00005274,
+};
+}
diff --git a/services/audioflinger/audio-resampler/resampler_filter_coefficients_10042011.h b/services/audioflinger/audio-resampler/resampler_filter_coefficients_10042011.h
deleted file mode 100644
index 8c6a899..0000000
--- a/services/audioflinger/audio-resampler/resampler_filter_coefficients_10042011.h
+++ /dev/null
@@ -1,2071 +0,0 @@
-/*
- * 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 <stdlib.h>
-
-namespace android {
-
-const int32_t resampler_filter_coefficients_10042011[] = {
-2075076504,
-2074870219,
-2074269557,
-2073262841,
-2071862786,
-2070051926,
-2067849110,
-2065243563,
-2062248465,
-2058846262,
-2055055548,
-2050866069,
-2046291635,
-2041315273,
-2035955897,
-2030204167,
-2024074532,
-2017550518,
-2010651175,
-2003368165,
-1995716923,
-1987682243,
-1979283938,
-1970514752,
-1961390076,
-1951894181,
-1942045775,
-1931837943,
-1921286728,
-1910377736,
-1899130344,
-1887538902,
-1875619770,
-1863358864,
-1850775104,
-1837863721,
-1824641940,
-1811097380,
-1797249776,
-1783095528,
-1768651291,
-1753903742,
-1738870726,
-1723548746,
-1707955054,
-1692078274,
-1675937190,
-1659529795,
-1642873313,
-1625956474,
-1608797063,
-1591393772,
-1573764495,
-1555899971,
-1537818785,
-1519520784,
-1501022796,
-1482314240,
-1463411160,
-1444313227,
-1425037741,
-1405576492,
-1385946622,
-1366149382,
-1346201706,
-1326095287,
-1305845828,
-1285455045,
-1264940308,
-1244295464,
-1223536951,
-1202667530,
-1181703085,
-1160635857,
-1139479271,
-1118235636,
-1096921134,
-1075530585,
-1054078577,
-1032568949,
-1011017224,
-989417949,
-967783970,
-946119336,
-924439752,
-902741900,
-881039249,
-859336703,
-837648175,
-815968557,
-794308053,
-772670861,
-751070988,
-729505896,
-707986911,
-686519594,
-665117063,
-643776375,
-622506966,
-601314359,
-580211607,
-559197735,
-538282662,
-517472516,
-496778402,
-476197491,
-455736426,
-435400430,
-415200497,
-395136166,
-375215078,
-355443561,
-335831583,
-316378199,
-297089183,
-277970603,
-259032151,
-240274557,
-221703918,
-203326638,
-185150446,
-167174280,
-149401179,
-131836509,
-114487708,
-97355714,
-80444376,
-63759844,
-47308498,
-31090767,
-15108809,
--631654,
--16124677,
--31368536,
--46360908,
--61095999,
--75569692,
--89781876,
--103732869,
--117417959,
--130833461,
--143977734,
--156850513,
--169446641,
--181763420,
--193799687,
--205556559,
--217029409,
--228216068,
--239114500,
--249725794,
--260045485,
--270072966,
--279807538,
--289252364,
--298404017,
--307262396,
--315825804,
--324097052,
--332072584,
--339753137,
--347137398,
--354229167,
--361025450,
--367527527,
--373733643,
--379647676,
--385266970,
--390594013,
--395628043,
--400374357,
--404831217,
--409001628,
--412884357,
--416484569,
--419800667,
--422836272,
--425590412,
--428068799,
--430270358,
--432199204,
--433854305,
--435241518,
--436360214,
--437215297,
--437806349,
--438139967,
--438216244,
--438040549,
--437612462,
--436938620,
--436019455,
--434860689,
--433462013,
--431830172,
--429966019,
--427875663,
--425559045,
--423023081,
--420269108,
--417303590,
--414126712,
--410745497,
--407161766,
--403382348,
--399407797,
--395245305,
--390897144,
--386370224,
--381665062,
--376788564,
--371743279,
--366536360,
--361168782,
--355647611,
--349975843,
--344160581,
--338202705,
--332108851,
--325882239,
--319530194,
--313054213,
--306461164,
--299754744,
--292942106,
--286024571,
--279008412,
--271897436,
--264698872,
--257414606,
--250051032,
--242612307,
--235105276,
--227531464,
--219896432,
--212204319,
--204462036,
--196671840,
--188839540,
--180969700,
--173068854,
--165138974,
--157185076,
--149211664,
--141225186,
--133228184,
--125225726,
--117222535,
--109224429,
--101233397,
--93253442,
--85289050,
--77345947,
--69426846,
--61535955,
--53678071,
--45858404,
--38079310,
--30344121,
--22657427,
--15024209,
--7447312,
-69952,
-7522929,
-14907403,
-22221174,
-29462063,
-36625813,
-43708432,
-50707112,
-57619541,
-64441301,
-71168979,
-77800149,
-84333341,
-90764454,
-97090405,
-103308422,
-109417126,
-115412500,
-121292248,
-127054242,
-132698149,
-138220442,
-143619140,
-148891701,
-154037743,
-159053739,
-163938275,
-168689170,
-173306749,
-177787867,
-182131495,
-186335286,
-190399716,
-194321853,
-198101380,
-201736519,
-205228586,
-208575166,
-211776310,
-214830056,
-217737773,
-220497194,
-223108861,
-225571114,
-227885824,
-230051117,
-232067922,
-233934597,
-235653195,
-237222146,
-238642925,
-239914316,
-241038916,
-242015623,
-242846277,
-243529706,
-244068632,
-244462216,
-244712654,
-244818983,
-244784181,
-244607765,
-244292268,
-243836913,
-243244874,
-242516010,
-241653201,
-240655918,
-239527557,
-238268354,
-236881508,
-235366743,
-233727638,
-231964766,
-230081521,
-228077731,
-225956985,
-223720128,
-221370808,
-218909157,
-216338938,
-213661333,
-210880096,
-207995414,
-205010946,
-201928111,
-198750891,
-195479864,
-192118892,
-188669741,
-185136417,
-181519490,
-177822602,
-174047687,
-170198894,
-166277172,
-162286302,
-158228508,
-154107834,
-149925117,
-145683777,
-141386129,
-137036351,
-132635752,
-128187958,
-123695608,
-119162747,
-114590586,
-109982363,
-105340765,
-100669861,
-95971242,
-91248228,
-86503703,
-81741443,
-76962776,
-72170457,
-67367307,
-62557108,
-57741653,
-52923856,
-48106777,
-43293939,
-38486954,
-33688240,
-28900789,
-24128036,
-19371931,
-14634917,
-9920080,
-5230446,
-567618,
--4066612,
--8669336,
--13237622,
--17769457,
--22262939,
--26715017,
--31123091,
--35485370,
--39800471,
--44065504,
--48278050,
--52436071,
--56538223,
--60581634,
--64564356,
--68484740,
--72342096,
--76133828,
--79858170,
--83513191,
--87098163,
--90610469,
--94048704,
--97411174,
--100697628,
--103905676,
--107034151,
--110081227,
--113046738,
--115928406,
--118725517,
--121436605,
--124062060,
--126599916,
--129049689,
--131409767,
--133680567,
--135860199,
--137948517,
--139944122,
--141847799,
--143657917,
--145374591,
--146996418,
--148524310,
--149956828,
--151294475,
--152536152,
--153683178,
--154734431,
--155690674,
--156550825,
--157316298,
--157986149,
--158561410,
--159041176,
--159427086,
--159718451,
--159916548,
--160020582,
--160032332,
--159951337,
--159779143,
--159515147,
--159161329,
--158717499,
--158185432,
--157564678,
--156857347,
--156063474,
--155185008,
--154221601,
--153175426,
--152046727,
--150837654,
--149548057,
--148180246,
--146734704,
--145213706,
--143617181,
--141947434,
--140205146,
--138392776,
--136510514,
--134560819,
--132544622,
--130464445,
--128320507,
--126115178,
--123849532,
--121526221,
--119145720,
--116710510,
--114221879,
--111682456,
--109092675,
--106454825,
--103770291,
--101041818,
--98270160,
--95457755,
--92606223,
--89718259,
--86794577,
--83837395,
--80848397,
--77830331,
--74784179,
--71712237,
--68616354,
--65499123,
--62361390,
--59205117,
--56032152,
--52845129,
--49645224,
--46434530,
--43215084,
--39989380,
--36758496,
--33524217,
--30288565,
--27054001,
--23821849,
--20593926,
--17372347,
--14159320,
--10955954,
--7763647,
--4584416,
--1420431,
-1726908,
-4856115,
-7965053,
-11051765,
-14114989,
-17153587,
-20165512,
-23148908,
-26102328,
-29024647,
-31913805,
-34768253,
-37586799,
-40368755,
-43112232,
-45815788,
-48478013,
-51098183,
-53674369,
-56205373,
-58689945,
-61127693,
-63516834,
-65856322,
-68144797,
-70381920,
-72565965,
-74696201,
-76771518,
-78791971,
-80756044,
-82663155,
-84512076,
-86302872,
-88034067,
-89705316,
-91315544,
-92865092,
-94352657,
-95778072,
-97140241,
-98439585,
-99674922,
-100846363,
-101953029,
-102995643,
-103973242,
-104886114,
-105733366,
-106515783,
-107232507,
-107884029,
-108469586,
-108990146,
-109445031,
-109834907,
-110159075,
-110418598,
-110612956,
-110743021,
-110808247,
-110809873,
-110747573,
-110622392,
-110433872,
-110183341,
-109870630,
-109496925,
-109061853,
-108566821,
-108011814,
-107398172,
-106725651,
-105995758,
-105208649,
-104365776,
-103466973,
-102513780,
-101506507,
-100446746,
-99334505,
-98171433,
-96958015,
-95695913,
-94385172,
-93027406,
-91623219,
-90174378,
-88681103,
-87145094,
-85567114,
-83948941,
-82290788,
-80594255,
-78860190,
-77090470,
-75285527,
-73447070,
-71576122,
-69674546,
-67742761,
-65782346,
-63794384,
-61780793,
-59742181,
-57680191,
-55596034,
-53491542,
-51367245,
-49224577,
-47064766,
-44889688,
-42700109,
-40497558,
-38283410,
-36059459,
-33826411,
-31585598,
-29338397,
-27086596,
-24831081,
-22573215,
-20314456,
-18056430,
-15799888,
-13545910,
-11295904,
-9051483,
-6813612,
-4583444,
-2362482,
-152205,
--2046514,
--4232760,
--6405078,
--8562051,
--10702661,
--12825994,
--14930573,
--17015184,
--19078982,
--21121361,
--23140947,
--25136591,
--27107282,
--29052375,
--30970456,
--32860543,
--34721731,
--36553615,
--38354874,
--40124623,
--41861868,
--43566233,
--45236425,
--46871781,
--48471484,
--50035443,
--51562506,
--53052109,
--54503334,
--55916090,
--57289239,
--58622387,
--59914726,
--61166369,
--62376298,
--63544241,
--64669360,
--65751822,
--66790682,
--67785875,
--68736719,
--69643614,
--70505769,
--71323237,
--72095315,
--72822435,
--73503872,
--74139832,
--74729705,
--75274073,
--75772337,
--76224828,
--76630970,
--76991410,
--77305656,
--77574197,
--77796578,
--77973591,
--78104888,
--78191082,
--78231765,
--78227787,
--78178906,
--78085845,
--77948266,
--77767093,
--77542200,
--77274423,
--76963506,
--76610446,
--76215234,
--75778805,
--75300971,
--74782775,
--74224327,
--73626669,
--72989720,
--72314602,
--71601546,
--70851655,
--70064886,
--69242354,
--68384382,
--67492158,
--66565755,
--65606354,
--64614392,
--63591082,
--62536507,
--61451798,
--60337466,
--59194800,
--58024034,
--56826376,
--55602460,
--54353584,
--53079977,
--51782773,
--50462662,
--49120984,
--47758105,
--46375208,
--44973078,
--43553013,
--42115335,
--40661095,
--39191102,
--37706696,
--36208364,
--34697230,
--33174211,
--31640600,
--30096851,
--28543956,
--26982847,
--25414822,
--23840464,
--22260795,
--20676812,
--19089709,
--17499984,
--15908468,
--14316132,
--12724175,
--11133249,
--9544240,
--7958198,
--6376234,
--4798939,
--3227038,
--1661555,
--103565,
-1446230,
-2987101,
-4517998,
-6037984,
-7546475,
-9042956,
-10526443,
-11996036,
-13451023,
-14890857,
-16314514,
-17721207,
-19110300,
-20481414,
-21833586,
-23166092,
-24478220,
-25769611,
-27039309,
-28286749,
-29511342,
-30712939,
-31890678,
-33044057,
-34172405,
-35275563,
-36352672,
-37403349,
-38427003,
-39423626,
-40392440,
-41333144,
-42245116,
-43128383,
-43982211,
-44806449,
-45600591,
-46364840,
-47098569,
-47801711,
-48473727,
-49114838,
-49724455,
-50302623,
-50848874,
-51363545,
-51846135,
-52296779,
-52715020,
-53101240,
-53455010,
-53776585,
-54065600,
-54322557,
-54547131,
-54739663,
-54899810,
-55028111,
-55124307,
-55188827,
-55221380,
-55222572,
-55192228,
-55130859,
-55038225,
-54914980,
-54761033,
-54576974,
-54362622,
-54118682,
-53845150,
-53542695,
-53211202,
-52851427,
-52463450,
-52047991,
-51604968,
-51135146,
-50638673,
-50116337,
-49568129,
-48994861,
-48396765,
-47774658,
-47128548,
-46459228,
-45766990,
-45052710,
-44316493,
-43559189,
-42781174,
-41983339,
-41165794,
-40329347,
-39474419,
-38601939,
-37712110,
-36805777,
-35883432,
-34945984,
-33993615,
-33027089,
-32046926,
-31054070,
-30048815,
-29031979,
-28004160,
-26966277,
-25918608,
-24861885,
-23796723,
-22724052,
-21644243,
-20558050,
-19466141,
-18369382,
-17268088,
-16162890,
-15054445,
-13943628,
-12830865,
-11716830,
-10602238,
-9487911,
-8374236,
-7261776,
-6151235,
-5043417,
-3938790,
-2837924,
-1741550,
-650377,
--435204,
--1514773,
--2587638,
--3653109,
--4710700,
--5759963,
--6800171,
--7830712,
--8851149,
--9861153,
--10860035,
--11847221,
--12822216,
--13784698,
--14733981,
--15669597,
--16591140,
--17498438,
--18390866,
--19267997,
--20129356,
--20974762,
--21803581,
--22615471,
--23410012,
--24187134,
--24946258,
--25687095,
--26409195,
--27112509,
--27796483,
--28460937,
--29105505,
--29730269,
--30334749,
--30918819,
--31482081,
--32024624,
--32545988,
--33046126,
--33524693,
--33981867,
--34417247,
--34830850,
--35222329,
--35591891,
--35939181,
--36264307,
--36566990,
--36847536,
--37105661,
--37341533,
--37554878,
--37746021,
--37914721,
--38061211,
--38185256,
--38287239,
--38366977,
--38424762,
--38460388,
--38474270,
--38466280,
--38436775,
--38385595,
--38313205,
--38219543,
--38105021,
--37969515,
--37813522,
--37637035,
--37440507,
--37223843,
--36987554,
--36731686,
--36456739,
--36162666,
--35850010,
--35518873,
--35169788,
--34802724,
--34418227,
--34016443,
--33597949,
--33162777,
--32711507,
--32244345,
--31761885,
--31264164,
--30751745,
--30224868,
--29684158,
--29129713,
--28562123,
--27981679,
--27389002,
--26784181,
--26167762,
--25540059,
--24901722,
--24252917,
--23594223,
--22926012,
--22248923,
--21563113,
--20869110,
--20167303,
--19458342,
--18742448,
--18020168,
--17291929,
--16558346,
--15819604,
--15076174,
--14328481,
--13577150,
--12822445,
--12064865,
--11304882,
--10543086,
--9779720,
--9015209,
--8250021,
--7484741,
--6719667,
--5955234,
--5191932,
--4430285,
--3670541,
--2913036,
--2158236,
--1406657,
--658617,
-85527,
-825280,
-1560174,
-2289926,
-3014260,
-3732701,
-4444806,
-5150245,
-5848747,
-6539832,
-7223129,
-7898368,
-8565383,
-9223733,
-9873071,
-10513075,
-11143565,
-11764092,
-12374365,
-12974099,
-13563194,
-14141234,
-14707964,
-15263070,
-15806466,
-16337748,
-16856735,
-17363175,
-17857079,
-18338088,
-18806059,
-19260708,
-19702046,
-20129723,
-20543652,
-20943584,
-21329597,
-21701383,
-22058894,
-22401875,
-22730421,
-23044252,
-23343385,
-23627618,
-23897120,
-24151661,
-24391299,
-24615824,
-24825417,
-25019868,
-25199285,
-25363488,
-25512702,
-25646760,
-25765809,
-25869680,
-25958622,
-26032502,
-26091517,
-26135536,
-26164850,
-26179375,
-26179344,
-26164644,
-26135585,
-26092113,
-26034498,
-25962646,
-25876887,
-25777203,
-25663899,
-25536906,
-25396578,
-25242934,
-25076305,
-24896644,
-24704311,
-24499364,
-24282163,
-24052697,
-23811348,
-23558211,
-23293664,
-23017702,
-22730703,
-22432787,
-22124357,
-21805445,
-21476448,
-21137523,
-20789074,
-20431134,
-20064079,
-19688086,
-19303582,
-18910646,
-18509679,
-18100894,
-17684716,
-17261223,
-16830785,
-16393630,
-15950197,
-15500602,
-15045231,
-14584340,
-14118348,
-13647355,
-13171699,
-12691641,
-12207609,
-11719754,
-11228437,
-10733948,
-10236700,
-9736830,
-9234652,
-8730459,
-8224662,
-7717439,
-7209110,
-6699986,
-6190443,
-5680626,
-5170792,
-4661242,
-4152348,
-3644304,
-3137385,
-2631912,
-2128229,
-1626510,
-1126973,
-629931,
-135715,
--355469,
--843402,
--1327763,
--1808270,
--2284755,
--2757069,
--3224916,
--3688026,
--4146190,
--4599250,
--5046898,
--5488901,
--5925074,
--6355314,
--6779333,
--7196918,
--7607862,
--8012069,
--8409254,
--8799255,
--9181907,
--9557181,
--9924822,
--10284691,
--10636595,
--10980501,
--11316157,
--11643459,
--11962240,
--12272514,
--12574056,
--12866786,
--13150528,
--13425307,
--13690910,
--13947306,
--14194356,
--14432139,
--14660477,
--14879363,
--15088645,
--15288408,
--15478483,
--15658898,
--15829522,
--15990474,
--16141613,
--16282992,
--16414484,
--16536222,
--16648083,
--16750160,
--16842353,
--16924831,
--16997506,
--17060493,
--17113697,
--17157297,
--17191222,
--17215613,
--17230389,
--17235750,
--17231648,
--17218249,
--17195484,
--17163567,
--17122473,
--17072392,
--17013271,
--16945340,
--16868599,
--16783258,
--16689284,
--16586915,
--16476178,
--16357294,
--16230239,
--16095254,
--15952382,
--15801866,
--15643700,
--15478137,
--15305245,
--15125274,
--14938221,
--14744336,
--14543701,
--14336583,
--14123005,
--13903232,
--13677369,
--13445685,
--13208205,
--12965179,
--12716724,
--12463120,
--12204418,
--11940874,
--11672628,
--11399952,
--11122887,
--10841668,
--10556438,
--10267479,
--9974863,
--9678839,
--9379572,
--9077334,
--8772193,
--8464370,
--8154033,
--7841459,
--7526740,
--7210102,
--6891729,
--6571875,
--6250617,
--5928143,
--5604631,
--5280339,
--4955374,
--4629936,
--4304221,
--3978468,
--3652773,
--3327303,
--3002250,
--2677848,
--2354215,
--2031520,
--1709962,
--1389747,
--1070968,
--753751,
--438281,
--124760,
-186691,
-495939,
-802786,
-1107056,
-1408639,
-1707439,
-2003270,
-2295964,
-2585396,
-2871472,
-3154006,
-3432861,
-3707940,
-3979193,
-4246453,
-4509594,
-4768498,
-5023110,
-5273258,
-5518843,
-5759760,
-5995988,
-6227371,
-6453823,
-6675229,
-6891575,
-7102708,
-7308575,
-7509086,
-7704267,
-7893987,
-8078205,
-8256820,
-8429855,
-8597183,
-8758786,
-8914577,
-9064605,
-9208760,
-9347039,
-9479353,
-9605758,
-9726154,
-9840566,
-9948924,
-10051316,
-10147660,
-10237999,
-10322257,
-10400525,
-10472731,
-10538934,
-10599070,
-10653248,
-10701410,
-10743630,
-10779849,
-10810183,
-10834587,
-10853155,
-10865840,
-10872775,
-10873933,
-10869421,
-10859197,
-10843400,
-10822013,
-10795156,
-10762793,
-10725068,
-10681980,
-10633658,
-10580078,
-10521391,
-10457608,
-10388869,
-10315156,
-10236621,
-10153290,
-10065313,
-9972683,
-9875561,
-9773986,
-9668111,
-9557933,
-9443609,
-9325184,
-9202823,
-9076535,
-8946481,
-8812721,
-8675419,
-8534581,
-8390359,
-8242821,
-8092134,
-7938325,
-7781551,
-7621894,
-7459519,
-7294449,
-7126832,
-6956750,
-6784374,
-6609741,
-6433003,
-6254252,
-6073651,
-5891227,
-5707114,
-5521403,
-5334259,
-5145731,
-4955957,
-4765041,
-4573140,
-4380296,
-4186630,
-3992245,
-3797298,
-3601843,
-3406004,
-3209891,
-3013645,
-2817308,
-2620977,
-2424757,
-2228787,
-2033128,
-1837884,
-1643168,
-1449107,
-1255755,
-1063192,
-871528,
-680885,
-491329,
-302942,
-115833,
--69891,
--254183,
--436990,
--618208,
--797740,
--975518,
--1151487,
--1325539,
--1497588,
--1667578,
--1835474,
--2001175,
--2164603,
--2325693,
--2484411,
--2640658,
--2794375,
--2945514,
--3094068,
--3239949,
--3383105,
--3523475,
--3661049,
--3795739,
--3927507,
--4056301,
--4182128,
--4304909,
--4424616,
--4541189,
--4654642,
--4764898,
--4871946,
--4975742,
--5076322,
--5173620,
--5267633,
--5358312,
--5445689,
--5529702,
--5610360,
--5687620,
--5761528,
--5832032,
--5899149,
--5962833,
--6023137,
--6080011,
--6133489,
--6183535,
--6230216,
--6273495,
--6313411,
--6349927,
--6383111,
--6412929,
--6439430,
--6462582,
--6482459,
--6499037,
--6512371,
--6522432,
--6529297,
--6532949,
--6533453,
--6530785,
--6525030,
--6516181,
--6504306,
--6489387,
--6471510,
--6450672,
--6426949,
--6400323,
--6370884,
--6338633,
--6303653,
--6265930,
--6225556,
--6182542,
--6136972,
--6088835,
--6038222,
--5985148,
--5929704,
--5871883,
--5811781,
--5749418,
--5684887,
--5618180,
--5549390,
--5478541,
--5405727,
--5330951,
--5254303,
--5175817,
--5095584,
--5013604,
--4929960,
--4844689,
--4757882,
--4669550,
--4579780,
--4488613,
--4396142,
--4302370,
--4207380,
--4111213,
--4013962,
--3915640,
--3816329,
--3716076,
--3614967,
--3513009,
--3410272,
--3306801,
--3202684,
--3097938,
--2992636,
--2886829,
--2780600,
--2673962,
--2566977,
--2459696,
--2352199,
--2244507,
--2136684,
--2028782,
--1920874,
--1812971,
--1705121,
--1597373,
--1489799,
--1382421,
--1275290,
--1168459,
--1061994,
--955913,
--850253,
--745067,
--640414,
--536322,
--432828,
--329984,
--227841,
--126414,
--25726,
-74177,
-173245,
-271455,
-368780,
-465170,
-560585,
-655003,
-748412,
-840765,
-932024,
-1022164,
-1111173,
-1199003,
-1285630,
-1371035,
-1455222,
-1538151,
-1619799,
-1700140,
-1779174,
-1856860,
-1933182,
-2008118,
-2081679,
-2153827,
-2224550,
-2293824,
-2361659,
-2428020,
-2492903,
-2556292,
-2618211,
-2678631,
-2737549,
-2794945,
-2850839,
-2905201,
-2958036,
-3009325,
-3059097,
-3107325,
-3154018,
-3199156,
-3242768,
-3284829,
-3325357,
-3364336,
-3401806,
-3437747,
-3472179,
-3505082,
-3536494,
-3566396,
-3594812,
-3621723,
-3647174,
-3671149,
-3693673,
-3714729,
-3734362,
-3752557,
-3769346,
-3784715,
-3798715,
-3811334,
-3822606,
-3832516,
-3841112,
-3848384,
-3854368,
-3859049,
-3862479,
-3864649,
-3865599,
-3865314,
-3863845,
-3861188,
-3857383,
-3852418,
-3846346,
-3839164,
-3830915,
-3821584,
-3811225,
-3799836,
-3787460,
-3774084,
-3759760,
-3744487,
-3728310,
-3711218,
-3693263,
-3674447,
-3654814,
-3634352,
-3613113,
-3591098,
-3568353,
-3544866,
-3520689,
-3495824,
-3470316,
-3444153,
-3417383,
-3390012,
-3362082,
-3333586,
-3304570,
-3275042,
-3245044,
-3214563,
-3183645,
-3152295,
-3120556,
-3088416,
-3055921,
-3023078,
-2989927,
-2956457,
-2922708,
-2888688,
-2854437,
-2819945,
-2785255,
-2750373,
-2715338,
-2680135,
-2644802,
-2609344,
-2573798,
-2538153,
-2502446,
-2466684,
-2430901,
-2395085,
-2359269,
-2323458,
-2287688,
-2251947,
-2216270,
-2180661,
-2145152,
-2109728,
-2074417,
-2039222,
-2004173,
-1969258,
-1934504,
-1899918,
-1865524,
-1831311,
-1797303,
-1763503,
-1729936,
-1696594,
-1663499,
-1630656,
-1598085,
-1565774,
-1533740,
-1501984,
-1470527,
-1439358,
-1408495,
-1377941,
-1347715,
-1317803,
-1288221,
-1258970,
-1230067,
-1201504,
-1173294,
-1145440,
-1117954,
-1090824,
-1064057,
-1037655,
-1011629,
-985969,
-960683,
-935774,
-911250,
-887101,
-863334,
-839947,
-816951,
-794337,
-772110,
-750271,
-728827,
-707764,
-687085,
-666787,
-646876,
-627343,
-608189,
-589415,
-571023,
-553003,
-535356,
-518081,
-501181,
-484646,
-468479,
-452677,
-437242,
-422161,
-407434,
-393055,
-379026,
-365337,
-351987,
-338974,
-326296,
-313944,
-301916,
-290208,
-278820,
-267744,
-256978,
-246518,
-236362,
-226500,
-216928,
-207640,
-198636,
-189906,
-181447,
-173255,
-165327,
-157655,
-150236,
-143064,
-136137,
-129449,
-122995,
-116772,
-110776,
-104999,
-99436,
-94081,
-88932,
-83981,
-79224,
-74658,
-70277,
-66077,
-62052,
-58198,
-54512,
-50989,
-47624,
-44413,
-41353,
-38438,
-35663,
-33023,
-30515,
-28134,
-25876,
-23736,
-21712,
-19799,
-17992,
-16290,
-14687,
-13182,
-11769,
-10446,
-9210,
-8057,
-6982,
-5983,
-5056,
-4198,
-3407,
-2678,
-2010,
-1400,
-844,
-341,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-0,
-};
-}
diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp
new file mode 100644
index 0000000..7a314cf
--- /dev/null
+++ b/services/audioflinger/test-resample.cpp
@@ -0,0 +1,274 @@
+/*
+ * 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 "AudioResampler.h"
+#include <media/AudioBufferProvider.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <time.h>
+#include <math.h>
+
+using namespace android;
+
+struct HeaderWav {
+ HeaderWav(size_t size, int nc, int sr, int bits) {
+ strncpy(RIFF, "RIFF", 4);
+ chunkSize = size + sizeof(HeaderWav);
+ strncpy(WAVE, "WAVE", 4);
+ strncpy(fmt, "fmt ", 4);
+ fmtSize = 16;
+ audioFormat = 1;
+ numChannels = nc;
+ samplesRate = sr;
+ byteRate = sr * numChannels * (bits/8);
+ align = nc*(bits/8);
+ bitsPerSample = bits;
+ strncpy(data, "data", 4);
+ dataSize = size;
+ }
+
+ char RIFF[4]; // RIFF
+ uint32_t chunkSize; // File size
+ char WAVE[4]; // WAVE
+ char fmt[4]; // fmt\0
+ uint32_t fmtSize; // fmt size
+ uint16_t audioFormat; // 1=PCM
+ uint16_t numChannels; // num channels
+ uint32_t samplesRate; // sample rate in hz
+ uint32_t byteRate; // Bps
+ uint16_t align; // 2=16-bit mono, 4=16-bit stereo
+ uint16_t bitsPerSample; // bits per sample
+ char data[4]; // "data"
+ uint32_t dataSize; // size
+};
+
+static int usage(const char* name) {
+ fprintf(stderr,"Usage: %s [-p] [-h] [-s] [-q {dq|lq|mq|hq|vhq}] [-i input-sample-rate] "
+ "[-o output-sample-rate] [<input-file>] <output-file>\n", name);
+ fprintf(stderr," -p enable profiling\n");
+ fprintf(stderr," -h create wav file\n");
+ fprintf(stderr," -s stereo\n");
+ fprintf(stderr," -q resampler quality\n");
+ fprintf(stderr," dq : default quality\n");
+ fprintf(stderr," lq : low quality\n");
+ fprintf(stderr," mq : medium quality\n");
+ fprintf(stderr," hq : high quality\n");
+ fprintf(stderr," vhq : very high quality\n");
+ fprintf(stderr," -i input file sample rate\n");
+ fprintf(stderr," -o output file sample rate\n");
+ return -1;
+}
+
+int main(int argc, char* argv[]) {
+
+ const char* const progname = argv[0];
+ bool profiling = false;
+ bool writeHeader = false;
+ int channels = 1;
+ int input_freq = 0;
+ int output_freq = 0;
+ AudioResampler::src_quality quality = AudioResampler::DEFAULT_QUALITY;
+
+ int ch;
+ while ((ch = getopt(argc, argv, "phsq:i:o:")) != -1) {
+ switch (ch) {
+ case 'p':
+ profiling = true;
+ break;
+ case 'h':
+ writeHeader = true;
+ break;
+ case 's':
+ channels = 2;
+ break;
+ case 'q':
+ if (!strcmp(optarg, "dq"))
+ quality = AudioResampler::DEFAULT_QUALITY;
+ else if (!strcmp(optarg, "lq"))
+ quality = AudioResampler::LOW_QUALITY;
+ else if (!strcmp(optarg, "mq"))
+ quality = AudioResampler::MED_QUALITY;
+ else if (!strcmp(optarg, "hq"))
+ quality = AudioResampler::HIGH_QUALITY;
+ else if (!strcmp(optarg, "vhq"))
+ quality = AudioResampler::VERY_HIGH_QUALITY;
+ else {
+ usage(progname);
+ return -1;
+ }
+ break;
+ case 'i':
+ input_freq = atoi(optarg);
+ break;
+ case 'o':
+ output_freq = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage(progname);
+ return -1;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ const char* file_in = NULL;
+ const char* file_out = NULL;
+ if (argc == 1) {
+ file_out = argv[0];
+ } else if (argc == 2) {
+ file_in = argv[0];
+ file_out = argv[1];
+ } else {
+ usage(progname);
+ return -1;
+ }
+
+ // ----------------------------------------------------------
+
+ size_t input_size;
+ void* input_vaddr;
+ if (argc == 2) {
+ struct stat st;
+ if (stat(file_in, &st) < 0) {
+ fprintf(stderr, "stat: %s\n", strerror(errno));
+ return -1;
+ }
+
+ int input_fd = open(file_in, O_RDONLY);
+ if (input_fd < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ return -1;
+ }
+
+ input_size = st.st_size;
+ input_vaddr = mmap(0, input_size, PROT_READ, MAP_PRIVATE, input_fd, 0);
+ if (input_vaddr == MAP_FAILED ) {
+ fprintf(stderr, "mmap: %s\n", strerror(errno));
+ return -1;
+ }
+ } else {
+ double k = 1000; // Hz / s
+ double time = (input_freq / 2) / k;
+ size_t input_frames = size_t(input_freq * time);
+ input_size = channels * sizeof(int16_t) * input_frames;
+ input_vaddr = malloc(input_size);
+ int16_t* in = (int16_t*)input_vaddr;
+ for (size_t i=0 ; i<input_frames ; i++) {
+ double t = double(i) / input_freq;
+ double y = sin(M_PI * k * t * t);
+ int16_t yi = floor(y * 32767.0 + 0.5);
+ for (size_t j=0 ; j<(size_t)channels ; j++) {
+ in[i*channels + j] = yi / (1+j);
+ }
+ }
+ }
+
+ // ----------------------------------------------------------
+
+ class Provider: public AudioBufferProvider {
+ int16_t* mAddr;
+ size_t mNumFrames;
+ public:
+ Provider(const void* addr, size_t size, int channels) {
+ mAddr = (int16_t*) addr;
+ mNumFrames = size / (channels*sizeof(int16_t));
+ }
+ virtual status_t getNextBuffer(Buffer* buffer,
+ int64_t pts = kInvalidPTS) {
+ buffer->frameCount = mNumFrames;
+ buffer->i16 = mAddr;
+ return NO_ERROR;
+ }
+ virtual void releaseBuffer(Buffer* buffer) {
+ }
+ } provider(input_vaddr, input_size, channels);
+
+ size_t input_frames = input_size / (channels * sizeof(int16_t));
+ size_t output_size = 2 * 4 * ((int64_t) input_frames * output_freq) / input_freq;
+ output_size &= ~7; // always stereo, 32-bits
+
+ void* output_vaddr = malloc(output_size);
+
+ if (profiling) {
+ AudioResampler* resampler = AudioResampler::create(16, channels,
+ output_freq, quality);
+
+ size_t out_frames = output_size/8;
+ resampler->setSampleRate(input_freq);
+ resampler->setVolume(0x1000, 0x1000);
+
+ memset(output_vaddr, 0, output_size);
+ timespec start, end;
+ clock_gettime(CLOCK_MONOTONIC, &start);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ int64_t start_ns = start.tv_sec * 1000000000LL + start.tv_nsec;
+ int64_t end_ns = end.tv_sec * 1000000000LL + end.tv_nsec;
+ int64_t time = (end_ns - start_ns)/4;
+ printf("%f Mspl/s\n", out_frames/(time/1e9)/1e6);
+
+ delete resampler;
+ }
+
+ AudioResampler* resampler = AudioResampler::create(16, channels,
+ output_freq, quality);
+ size_t out_frames = output_size/8;
+ resampler->setSampleRate(input_freq);
+ resampler->setVolume(0x1000, 0x1000);
+
+ memset(output_vaddr, 0, output_size);
+ resampler->resample((int*) output_vaddr, out_frames, &provider);
+
+ // down-mix (we just truncate and keep the left channel)
+ int32_t* out = (int32_t*) output_vaddr;
+ int16_t* convert = (int16_t*) malloc(out_frames * channels * sizeof(int16_t));
+ for (size_t i = 0; i < out_frames; i++) {
+ for (int j=0 ; j<channels ; j++) {
+ int32_t s = out[i * 2 + j] >> 12;
+ if (s > 32767) s = 32767;
+ else if (s < -32768) s = -32768;
+ convert[i * channels + j] = int16_t(s);
+ }
+ }
+
+ // write output to disk
+ int output_fd = open(file_out, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (output_fd < 0) {
+ fprintf(stderr, "open: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (writeHeader) {
+ HeaderWav wav(out_frames * channels * sizeof(int16_t), channels, output_freq, 16);
+ write(output_fd, &wav, sizeof(wav));
+ }
+
+ write(output_fd, convert, out_frames * channels * sizeof(int16_t));
+ close(output_fd);
+
+ return 0;
+}