diff options
Diffstat (limited to 'services/audioflinger')
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 = ¶m; - 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 = ¤t->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 = ¤t->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 = ¶m; + 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, ×tamp.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; +} |