From 245cdd91559873aeacf21b7c780655ef43c01682 Mon Sep 17 00:00:00 2001 From: jiabin Date: Fri, 7 Dec 2018 17:55:15 -0800 Subject: [PATCH] Support audio-haptic coupled playback. When trying to play with haptic channel mask, use adjust channels buffer provider to make the haptic channel the same as the output one. If haptic playback is supported, use adjust channel non destructive buffer provider to output haptic data to the end of the sink buffer. Otherwise, haptic data will be ignored. Test: Manually Bug: 111454766 Change-Id: Ic5f780de48c1e71de6ba5c4774d1ed2e9c8c51a0 --- .../libaudioclient/include/media/AudioMixer.h | 8 ++ media/libaudioprocessing/AudioMixer.cpp | 62 +++++++++-- services/audioflinger/FastMixer.cpp | 24 +++- services/audioflinger/FastMixer.h | 2 + services/audioflinger/FastMixerState.h | 4 + services/audioflinger/PlaybackTracks.h | 10 ++ services/audioflinger/Threads.cpp | 103 ++++++++++++++++-- services/audioflinger/Threads.h | 5 + 8 files changed, 196 insertions(+), 22 deletions(-) diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index 0532232073..3ae7104459 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -78,6 +78,8 @@ public: DOWNMIX_TYPE = 0X4004, MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT) MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output + // for haptic + HAPTIC_ENABLED = 0x4007, // Set haptic data from this track should be played or not. // for target RESAMPLE SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name; // parameter 'value' is the new sample rate in Hz. @@ -329,6 +331,7 @@ private: * 7) mTimestretchBufferProvider: Adds timestretching for playback rate */ AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. + // TODO: combine AdjustChannelsBufferProvider and AdjustChannelsNonDestructiveBufferProvider std::unique_ptr mAdjustChannelsBufferProvider; std::unique_ptr mAdjustChannelsNonDestructiveBufferProvider; std::unique_ptr mReformatBufferProvider; @@ -360,6 +363,11 @@ private: AudioPlaybackRate mPlaybackRate; // Haptic + bool mHapticPlaybackEnabled; + audio_channel_mask_t mHapticChannelMask; + uint32_t mHapticChannelCount; + audio_channel_mask_t mMixerHapticChannelMask; + uint32_t mMixerHapticChannelCount; uint32_t mAdjustInChannelCount; uint32_t mAdjustOutChannelCount; uint32_t mAdjustNonDestructiveInChannelCount; diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index 365af75c1c..2567b3b238 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -136,6 +136,9 @@ status_t AudioMixer::create( // no initialization needed // t->frameCount + t->mHapticChannelMask = channelMask & AUDIO_CHANNEL_HAPTIC_ALL; + t->mHapticChannelCount = audio_channel_count_from_out_mask(t->mHapticChannelMask); + channelMask &= ~AUDIO_CHANNEL_HAPTIC_ALL; t->channelCount = audio_channel_count_from_out_mask(channelMask); t->enabled = false; ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO, @@ -163,10 +166,13 @@ status_t AudioMixer::create( t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask); t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; // haptic - t->mAdjustInChannelCount = 0; - t->mAdjustOutChannelCount = 0; - t->mAdjustNonDestructiveInChannelCount = 0; - t->mAdjustNonDestructiveOutChannelCount = 0; + t->mHapticPlaybackEnabled = false; + t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE; + t->mMixerHapticChannelCount = 0; + t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount; + t->mAdjustOutChannelCount = t->channelCount + t->mMixerHapticChannelCount; + t->mAdjustNonDestructiveInChannelCount = t->mAdjustOutChannelCount; + t->mAdjustNonDestructiveOutChannelCount = t->channelCount; t->mKeepContractedChannels = false; // Check the downmixing (or upmixing) requirements. status_t status = t->prepareForDownmix(); @@ -193,13 +199,20 @@ bool AudioMixer::setChannelMasks(int name, LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name); const std::shared_ptr &track = mTracks[name]; - if (trackChannelMask == track->channelMask - && mixerChannelMask == track->mMixerChannelMask) { + if (trackChannelMask == (track->channelMask | track->mHapticChannelMask) + && mixerChannelMask == (track->mMixerChannelMask | track->mMixerHapticChannelMask)) { return false; // no need to change } + const audio_channel_mask_t hapticChannelMask = trackChannelMask & AUDIO_CHANNEL_HAPTIC_ALL; + trackChannelMask &= ~AUDIO_CHANNEL_HAPTIC_ALL; + const audio_channel_mask_t mixerHapticChannelMask = mixerChannelMask & AUDIO_CHANNEL_HAPTIC_ALL; + mixerChannelMask &= ~AUDIO_CHANNEL_HAPTIC_ALL; // always recompute for both channel masks even if only one has changed. const uint32_t trackChannelCount = audio_channel_count_from_out_mask(trackChannelMask); const uint32_t mixerChannelCount = audio_channel_count_from_out_mask(mixerChannelMask); + const uint32_t hapticChannelCount = audio_channel_count_from_out_mask(hapticChannelMask); + const uint32_t mixerHapticChannelCount = + audio_channel_count_from_out_mask(mixerHapticChannelMask); ALOG_ASSERT((trackChannelCount <= MAX_NUM_CHANNELS_TO_DOWNMIX) && trackChannelCount @@ -208,6 +221,24 @@ bool AudioMixer::setChannelMasks(int name, track->channelCount = trackChannelCount; track->mMixerChannelMask = mixerChannelMask; track->mMixerChannelCount = mixerChannelCount; + track->mHapticChannelMask = hapticChannelMask; + track->mHapticChannelCount = hapticChannelCount; + track->mMixerHapticChannelMask = mixerHapticChannelMask; + track->mMixerHapticChannelCount = mixerHapticChannelCount; + + if (track->mHapticChannelCount > 0) { + track->mAdjustInChannelCount = track->channelCount + track->mHapticChannelCount; + track->mAdjustOutChannelCount = track->channelCount + track->mMixerHapticChannelCount; + track->mAdjustNonDestructiveInChannelCount = track->mAdjustOutChannelCount; + track->mAdjustNonDestructiveOutChannelCount = track->channelCount; + track->mKeepContractedChannels = track->mHapticPlaybackEnabled; + } else { + track->mAdjustInChannelCount = 0; + track->mAdjustOutChannelCount = 0; + track->mAdjustNonDestructiveInChannelCount = 0; + track->mAdjustNonDestructiveOutChannelCount = 0; + track->mKeepContractedChannels = false; + } // channel masks have changed, does this track need a downmixer? // update to try using our desired format (if we aren't already using it) @@ -616,7 +647,8 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) case CHANNEL_MASK: { const audio_channel_mask_t trackChannelMask = static_cast(valueInt); - if (setChannelMasks(name, trackChannelMask, track->mMixerChannelMask)) { + if (setChannelMasks(name, trackChannelMask, + (track->mMixerChannelMask | track->mMixerHapticChannelMask))) { ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask); invalidate(); } @@ -665,11 +697,21 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) case MIXER_CHANNEL_MASK: { const audio_channel_mask_t mixerChannelMask = static_cast(valueInt); - if (setChannelMasks(name, track->channelMask, mixerChannelMask)) { + if (setChannelMasks(name, track->channelMask | track->mHapticChannelMask, + mixerChannelMask)) { ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask); invalidate(); } } break; + case HAPTIC_ENABLED: { + const bool hapticPlaybackEnabled = static_cast(valueInt); + if (track->mHapticPlaybackEnabled != hapticPlaybackEnabled) { + track->mHapticPlaybackEnabled = hapticPlaybackEnabled; + track->mKeepContractedChannels = hapticPlaybackEnabled; + track->prepareForAdjustChannelsNonDestructive(mFrameCount); + track->prepareForAdjustChannels(); + } + } break; default: LOG_ALWAYS_FATAL("setParameter track: bad param %d", param); } @@ -1359,8 +1401,8 @@ void AudioMixer::process__nop() const std::shared_ptr &t = mTracks[group[0]]; memset(t->mainBuffer, 0, - mFrameCount * t->mMixerChannelCount - * audio_bytes_per_sample(t->mMixerFormat)); + mFrameCount * audio_bytes_per_frame( + t->mMixerChannelCount + t->mMixerHapticChannelCount, t->mMixerFormat)); // now consume data for (const int name : group) { diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index d15841fcf2..f328577456 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -37,8 +37,9 @@ #include #endif #endif -#include +#include #include +#include #include #include "FastMixer.h" #include "TypedLogger.h" @@ -159,20 +160,24 @@ void FastMixer::onStateChange() if (current->mOutputSinkGen != mOutputSinkGen) { mOutputSink = current->mOutputSink; mOutputSinkGen = current->mOutputSinkGen; + mSinkChannelMask = current->mSinkChannelMask; if (mOutputSink == NULL) { mFormat = Format_Invalid; mSampleRate = 0; mSinkChannelCount = 0; mSinkChannelMask = AUDIO_CHANNEL_NONE; + mAudioChannelCount = 0; } else { mFormat = mOutputSink->format(); mSampleRate = Format_sampleRate(mFormat); mSinkChannelCount = Format_channelCount(mFormat); LOG_ALWAYS_FATAL_IF(mSinkChannelCount > AudioMixer::MAX_NUM_CHANNELS); - // TODO: Add channel mask to NBAIO_Format - // We assume that the channel mask must be a valid positional channel mask. - mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); + if (mSinkChannelMask == AUDIO_CHANNEL_NONE) { + mSinkChannelMask = audio_channel_out_mask_from_count(mSinkChannelCount); + } + mAudioChannelCount = mSinkChannelCount - audio_channel_count_from_out_mask( + mSinkChannelMask & AUDIO_CHANNEL_HAPTIC_ALL); } dumpState->mSampleRate = mSampleRate; } @@ -288,6 +293,8 @@ void FastMixer::onStateChange() (void *)(uintptr_t)fastTrack->mChannelMask); mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, + (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); mMixer->enable(name); } mGenerations[i] = fastTrack->mGeneration; @@ -324,6 +331,8 @@ void FastMixer::onStateChange() (void *)(uintptr_t)fastTrack->mChannelMask); mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mSinkChannelMask); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, + (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); // already enabled } mGenerations[i] = fastTrack->mGeneration; @@ -468,6 +477,13 @@ void FastMixer::onWork() memcpy_by_audio_format(buffer, mFormat.mFormat, mMixerBuffer, mMixerBufferFormat, frameCount * Format_channelCount(mFormat)); } + if (mSinkChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) { + // When there are haptic channels, the sample data is partially interleaved. + // Make the sample data fully interleaved here. + adjust_channels_non_destructive(buffer, mAudioChannelCount, buffer, mSinkChannelCount, + audio_bytes_per_sample(mFormat.mFormat), + frameCount * audio_bytes_per_frame(mAudioChannelCount, mFormat.mFormat)); + } // if non-NULL, then duplicate write() to this non-blocking sink #ifdef TEE_SINK mTee.write(buffer, frameCount); diff --git a/services/audioflinger/FastMixer.h b/services/audioflinger/FastMixer.h index 1c86d9ae7d..1d332e01af 100644 --- a/services/audioflinger/FastMixer.h +++ b/services/audioflinger/FastMixer.h @@ -76,6 +76,8 @@ private: size_t mMixerBufferSize; audio_format_t mMixerBufferFormat; // mixer output format: AUDIO_FORMAT_PCM_(16_BIT|FLOAT). + uint32_t mAudioChannelCount; // audio channel count, excludes haptic channels. + enum {UNDEFINED, MIXED, ZEROED} mMixerBufferState; NBAIO_Format mFormat; unsigned mSampleRate; diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index c7fcbd83ce..9d2a733670 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -47,6 +47,7 @@ struct FastTrack { audio_channel_mask_t mChannelMask; // AUDIO_CHANNEL_OUT_MONO or AUDIO_CHANNEL_OUT_STEREO audio_format_t mFormat; // track format int mGeneration; // increment when any field is assigned + bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not }; // Represents a single state of the fast mixer @@ -69,6 +70,9 @@ struct FastMixerState : FastThreadState { NBAIO_Sink* mOutputSink; // HAL output device, must already be negotiated int mOutputSinkGen; // increment when mOutputSink is assigned size_t mFrameCount; // number of frames per fast mix buffer + audio_channel_mask_t mSinkChannelMask; // If not AUDIO_CHANNEL_NONE, specifies sink channel + // mask when it cannot be directly calculated from + // channel count // Extends FastThreadState::Command static const Command diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index 971f6a5077..d9f570dfe3 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -112,6 +112,14 @@ public: /** Copy the track metadata in the provided iterator. Thread safe. */ virtual void copyMetadataTo(MetadataInserter& backInserter) const; + /** Return haptic playback of the track is enabled or not, used in mixer. */ + bool getHapticPlaybackEnabled() const { return mHapticPlaybackEnabled; } + /** Set haptic playback of the track is enabled or not, should be + * set after query or get callback from vibrator service */ + void setHapticPlaybackEnabled(bool hapticPlaybackEnabled) { + mHapticPlaybackEnabled = hapticPlaybackEnabled; + } + protected: // for numerous friend class PlaybackThread; @@ -188,6 +196,8 @@ protected: sp mVolumeHandler; // handles multiple VolumeShaper configs and operations + bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not + private: // The following fields are only for fast tracks, and should be in a subclass int mFastIndex; // index within FastMixerState::mFastTracks[]; diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 46e40c75ef..1fdb27e88e 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -38,6 +38,7 @@ #include #include +#include #include #include #include @@ -751,6 +752,7 @@ String8 channelMaskToString(audio_channel_mask_t mask, bool output) { audio_channel_mask_get_representation(mask); switch (representation) { + // Travel all single bit channel mask to convert channel mask to string. case AUDIO_CHANNEL_REPRESENTATION_POSITION: { if (output) { if (mask & AUDIO_CHANNEL_OUT_FRONT_LEFT) s.append("front-left, "); @@ -773,6 +775,8 @@ String8 channelMaskToString(audio_channel_mask_t mask, bool output) { if (mask & AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT) s.append("top-back-right, " ); if (mask & AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT) s.append("top-side-left, " ); if (mask & AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT) s.append("top-side-right, " ); + if (mask & AUDIO_CHANNEL_OUT_HAPTIC_B) s.append("haptic-B, " ); + if (mask & AUDIO_CHANNEL_OUT_HAPTIC_A) s.append("haptic-A, " ); if (mask & ~AUDIO_CHANNEL_OUT_ALL) s.append("unknown, "); } else { if (mask & AUDIO_CHANNEL_IN_LEFT) s.append("left, "); @@ -1845,6 +1849,10 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector& dumpBase(fd, args); dprintf(fd, " Master mute: %s\n", mMasterMute ? "on" : "off"); + if (mHapticChannelMask != AUDIO_CHANNEL_NONE) { + dprintf(fd, " Haptic channel mask: %#x (%s)\n", mHapticChannelMask, + channelMaskToString(mHapticChannelMask, true /* output */).c_str()); + } dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); dprintf(fd, " Last write occurred (msecs): %llu\n", (unsigned long long) ns2ms(systemTime() - mLastWriteTime)); @@ -1946,7 +1954,7 @@ sp AudioFlinger::PlaybackThread::createTrac audio_is_linear_pcm(format) && // TODO: extract as a data library function that checks that a computationally // expensive downmixer is not required: isFastOutputChannelConversion() - (channelMask == mChannelMask || + (channelMask == (mChannelMask | mHapticChannelMask) || mChannelMask != AUDIO_CHANNEL_OUT_STEREO || (channelMask == AUDIO_CHANNEL_OUT_MONO /* && mChannelMask == AUDIO_CHANNEL_OUT_STEREO */)) && @@ -2348,6 +2356,17 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp& track) track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING; } + // Disable all haptic playback for all other active tracks when haptic playback is supported + // and the track contains haptic channels. Enable haptic playback for current track. + // TODO: Request actual haptic playback status from vibrator service + if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE + && mHapticChannelMask != AUDIO_CHANNEL_NONE) { + for (auto &t : mActiveTracks) { + t->setHapticPlaybackEnabled(false); + } + track->setHapticPlaybackEnabled(true); + } + track->mResetDone = false; track->mPresentationCompleteFrames = 0; mActiveTracks.add(track); @@ -2635,6 +2654,11 @@ void AudioFlinger::PlaybackThread::readOutputParameters_l() (void)posix_memalign(&mEffectBuffer, 32, mEffectBufferSize); } + mHapticChannelMask = mChannelMask & AUDIO_CHANNEL_HAPTIC_ALL; + mChannelMask &= ~mHapticChannelMask; + mHapticChannelCount = audio_channel_count_from_out_mask(mHapticChannelMask); + mChannelCount -= mHapticChannelCount; + // 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_l() is called from the constructor @@ -3007,7 +3031,7 @@ status_t AudioFlinger::PlaybackThread::addEffectChain_l(const sp& c // Only one effect chain can be present in direct output thread and it uses // the sink buffer as input if (mType != DIRECT) { - size_t numSamples = mNormalFrameCount * mChannelCount; + size_t numSamples = mNormalFrameCount * (mChannelCount + mHapticChannelCount); status_t result = mAudioFlinger->mEffectsFactoryHal->allocateBuffer( numSamples * sizeof(effect_buffer_t), &halInBuffer); @@ -3506,7 +3530,17 @@ bool AudioFlinger::PlaybackThread::threadLoop() } memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat, - mNormalFrameCount * mChannelCount); + mNormalFrameCount * (mChannelCount + mHapticChannelCount)); + + // If we're going directly to the sink and there are haptic channels, + // we should adjust channels as the sample data is partially interleaved + // in this case. + if (!mEffectBufferValid && mHapticChannelCount > 0) { + adjust_channels_non_destructive(buffer, mChannelCount, buffer, + mChannelCount + mHapticChannelCount, + audio_bytes_per_sample(format), + audio_bytes_per_frame(mChannelCount, format) * mNormalFrameCount); + } } mBytesRemaining = mCurrentWriteLength; @@ -3550,7 +3584,15 @@ bool AudioFlinger::PlaybackThread::threadLoop() } memcpy_by_audio_format(mSinkBuffer, mFormat, mEffectBuffer, mEffectBufferFormat, - mNormalFrameCount * mChannelCount); + mNormalFrameCount * (mChannelCount + mHapticChannelCount)); + // The sample data is partially interleaved when haptic channels exist, + // we need to adjust channels here. + if (mHapticChannelCount > 0) { + adjust_channels_non_destructive(mSinkBuffer, mChannelCount, mSinkBuffer, + mChannelCount + mHapticChannelCount, + audio_bytes_per_sample(mFormat), + audio_bytes_per_frame(mChannelCount, mFormat) * mNormalFrameCount); + } } // enable changes in effect chain @@ -3716,6 +3758,7 @@ bool AudioFlinger::PlaybackThread::threadLoop() // removeTracks_l() must be called with ThreadBase::mLock held void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp >& tracksToRemove) { + bool enabledHapticTracksRemoved = false; for (const auto& track : tracksToRemove) { mActiveTracks.remove(track); ALOGV("%s(%d): removing track on session %d", __func__, track->id(), track->sessionId()); @@ -3737,6 +3780,18 @@ void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp >& tra // remove from our tracks vector removeTrack_l(track); } + enabledHapticTracksRemoved |= track->getHapticPlaybackEnabled(); + } + // If the thread supports haptic playback and the track playing haptic data was removed, + // enable haptic playback on the first active track that contains haptic channels. + // TODO: Query vibrator service to know which track should enable haptic playback. + if (enabledHapticTracksRemoved && mHapticChannelMask != AUDIO_CHANNEL_NONE) { + for (auto &t : mActiveTracks) { + if (t->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) { + t->setHapticPlaybackEnabled(true); + break; + } + } } } @@ -3942,7 +3997,8 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // 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, mFormat)}; + const NBAIO_Format offers[1] = {Format_from_SR_C( + mSampleRate, mChannelCount + mHapticChannelCount, mFormat)}; #if !LOG_NDEBUG ssize_t index = #else @@ -3984,7 +4040,7 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // change our Sink format to accept our intermediate precision mFormat = fastMixerFormat; free(mSinkBuffer); - mFrameSize = mChannelCount * audio_bytes_per_sample(mFormat); + mFrameSize = audio_bytes_per_frame(mChannelCount + mHapticChannelCount, mFormat); const size_t sinkBufferSize = mNormalFrameCount * mFrameSize; (void)posix_memalign(&mSinkBuffer, 32, sinkBufferSize); } @@ -4026,8 +4082,10 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud // wrap the source side of the MonoPipe to make it an AudioBufferProvider fastTrack->mBufferProvider = new SourceAudioBufferProvider(new MonoPipeReader(monoPipe)); fastTrack->mVolumeProvider = NULL; - fastTrack->mChannelMask = mChannelMask; // mPipeSink channel mask for audio to FastMixer + fastTrack->mChannelMask = mChannelMask | mHapticChannelMask; // mPipeSink channel mask for + // audio to FastMixer fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer + fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE; fastTrack->mGeneration++; state->mFastTracksGen++; state->mTrackMask = 1; @@ -4035,6 +4093,10 @@ AudioFlinger::MixerThread::MixerThread(const sp& audioFlinger, Aud state->mOutputSink = mOutputSink.get(); state->mOutputSinkGen++; state->mFrameCount = mFrameCount; + // specify sink channel mask when haptic channel mask present as it can not + // be calculated directly from channel count + state->mSinkChannelMask = mHapticChannelMask == AUDIO_CHANNEL_NONE + ? AUDIO_CHANNEL_NONE : mChannelMask | mHapticChannelMask; state->mCommand = FastMixerState::COLD_IDLE; // already done in constructor initialization list //mFastMixerFutex = 0; @@ -4411,6 +4473,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac std::vector, size_t>> mUnderrunFrames; } deferredOperations(&mixerStatus); // implicit nested scope for variable capture + bool noFastHapticTrack = true; for (size_t i=0 ; i t = mActiveTracks[i]; @@ -4419,6 +4482,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // process fast tracks if (track->isFastTrack()) { + if (track->getHapticPlaybackEnabled()) { + noFastHapticTrack = false; + } // 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 @@ -4544,6 +4610,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac fastTrack->mVolumeProvider = vp; fastTrack->mChannelMask = track->mChannelMask; fastTrack->mFormat = track->mFormat; + fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled(); fastTrack->mGeneration++; state->mTrackMask |= 1 << j; didModify = true; @@ -4589,6 +4656,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac // Avoids a misleading display in dumpsys track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL; } + if (fastTrack->mHapticPlaybackEnabled != track->getHapticPlaybackEnabled()) { + fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled(); + didModify = true; + } continue; } @@ -4796,7 +4867,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac mAudioMixer->setParameter( trackId, AudioMixer::TRACK, - AudioMixer::MIXER_CHANNEL_MASK, (void *)(uintptr_t)mChannelMask); + AudioMixer::MIXER_CHANNEL_MASK, + (void *)(uintptr_t)(mChannelMask | mHapticChannelMask)); // limit track sample rate to 2 x output sample rate, which changes at re-configuration uint32_t maxSampleRate = mSampleRate * AUDIO_RESAMPLER_DOWN_RATIO_MAX; uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate(); @@ -4857,6 +4929,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac trackId, AudioMixer::TRACK, AudioMixer::AUX_BUFFER, (void *)track->auxBuffer()); + mAudioMixer->setParameter( + trackId, + AudioMixer::TRACK, + AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)track->getHapticPlaybackEnabled()); // reset retry count track->mRetryCount = kMaxTrackRetries; @@ -4924,6 +5000,17 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } + if (mHapticChannelMask != AUDIO_CHANNEL_NONE && sq != NULL) { + // When there is no fast track playing haptic and FastMixer exists, + // enabling the first FastTrack, which provides mixed data from normal + // tracks, to play haptic data. + FastTrack *fastTrack = &state->mFastTracks[0]; + if (fastTrack->mHapticPlaybackEnabled != noFastHapticTrack) { + fastTrack->mHapticPlaybackEnabled = noFastHapticTrack; + didModify = true; + } + } + // Push the new FastMixer state if necessary bool pauseAudioWatchdog = false; if (didModify) { diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 7f3ea0f4e7..e8b2158e71 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -907,6 +907,11 @@ protected: int64_t mBytesWritten; int64_t mFramesWritten; // not reset on standby int64_t mSuspendedFrames; // not reset on standby + + // mHapticChannelMask and mHapticChannelCount will only be valid when the thread support + // haptic playback. + audio_channel_mask_t mHapticChannelMask = AUDIO_CHANNEL_NONE; + uint32_t mHapticChannelCount = 0; private: // mMasterMute is in both PlaybackThread and in AudioFlinger. When a // PlaybackThread needs to find out if master-muted, it checks it's local