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
diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h
index 0532232..3ae7104 100644
--- a/media/libaudioclient/include/media/AudioMixer.h
+++ b/media/libaudioclient/include/media/AudioMixer.h
@@ -78,6 +78,8 @@
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 @@
* 7) mTimestretchBufferProvider: Adds timestretching for playback rate
*/
AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
+ // TODO: combine AdjustChannelsBufferProvider and AdjustChannelsNonDestructiveBufferProvider
std::unique_ptr<PassthruBufferProvider> mAdjustChannelsBufferProvider;
std::unique_ptr<PassthruBufferProvider> mAdjustChannelsNonDestructiveBufferProvider;
std::unique_ptr<PassthruBufferProvider> mReformatBufferProvider;
@@ -360,6 +363,11 @@
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 365af75..2567b3b 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -136,6 +136,9 @@
// 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 @@
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 @@
LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
const std::shared_ptr<Track> &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 @@
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 @@
case CHANNEL_MASK: {
const audio_channel_mask_t trackChannelMask =
static_cast<audio_channel_mask_t>(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 @@
case MIXER_CHANNEL_MASK: {
const audio_channel_mask_t mixerChannelMask =
static_cast<audio_channel_mask_t>(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<bool>(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 @@
const std::shared_ptr<Track> &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 d15841f..f328577 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -37,8 +37,9 @@
#include <cpustats/ThreadCpuUsage.h>
#endif
#endif
-#include <audio_utils/mono_blend.h>
+#include <audio_utils/channels.h>
#include <audio_utils/format.h>
+#include <audio_utils/mono_blend.h>
#include <media/AudioMixer.h>
#include "FastMixer.h"
#include "TypedLogger.h"
@@ -159,20 +160,24 @@
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 *)(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 *)(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 @@
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 1c86d9a..1d332e0 100644
--- a/services/audioflinger/FastMixer.h
+++ b/services/audioflinger/FastMixer.h
@@ -76,6 +76,8 @@
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 c7fcbd8..9d2a733 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -47,6 +47,7 @@
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 @@
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 971f6a5..d9f570d 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -112,6 +112,14 @@
/** 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 @@
sp<media::VolumeHandler> 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 46e40c7..1fdb27e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -38,6 +38,7 @@
#include <private/media/AudioTrackShared.h>
#include <private/android_filesystem_config.h>
+#include <audio_utils/channels.h>
#include <audio_utils/mono_blend.h>
#include <audio_utils/primitives.h>
#include <audio_utils/format.h>
@@ -751,6 +752,7 @@
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 @@
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 @@
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 @@
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 @@
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)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 @@
// 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 @@
}
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 @@
}
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 @@
// removeTracks_l() must be called with ThreadBase::mLock held
void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& 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 @@
// 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 @@
// 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 @@
// 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 @@
// 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 @@
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 @@
std::vector<std::pair<sp<Track>, size_t>> mUnderrunFrames;
} deferredOperations(&mixerStatus); // implicit nested scope for variable capture
+ bool noFastHapticTrack = true;
for (size_t i=0 ; i<count ; i++) {
const sp<Track> t = mActiveTracks[i];
@@ -4419,6 +4482,9 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
}
+ 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 7f3ea0f..e8b2158 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -907,6 +907,11 @@
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