diff --git a/media/libaudioclient/include/media/AudioMixer.h b/media/libaudioclient/include/media/AudioMixer.h index 3ae7104459..fbbbd11135 100644 --- a/media/libaudioclient/include/media/AudioMixer.h +++ b/media/libaudioclient/include/media/AudioMixer.h @@ -80,6 +80,7 @@ public: 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. + HAPTIC_INTENSITY = 0x4008, // Set the intensity to play haptic data. // for target RESAMPLE SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name; // parameter 'value' is the new sample rate in Hz. @@ -102,6 +103,31 @@ public: // parameter 'value' is a pointer to the new playback rate. }; + enum { // Haptic intensity, should keep consistent with VibratorService + HAPTIC_SCALE_VERY_LOW = -2, + HAPTIC_SCALE_LOW = -1, + HAPTIC_SCALE_NONE = 0, + HAPTIC_SCALE_HIGH = 1, + HAPTIC_SCALE_VERY_HIGH = 2, + }; + typedef int32_t haptic_intensity_t; + static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2 / 3; + static constexpr float HAPTIC_SCALE_LOW_RATIO = 3 / 4; + static const CONSTEXPR float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f; + + static inline bool isValidHapticIntensity(haptic_intensity_t hapticIntensity) { + switch (hapticIntensity) { + case HAPTIC_SCALE_VERY_LOW: + case HAPTIC_SCALE_LOW: + case HAPTIC_SCALE_NONE: + case HAPTIC_SCALE_HIGH: + case HAPTIC_SCALE_VERY_HIGH: + return true; + default: + return false; + } + } + AudioMixer(size_t frameCount, uint32_t sampleRate) : mSampleRate(sampleRate) , mFrameCount(frameCount) { @@ -147,6 +173,7 @@ public: } } (this->*mHook)(); + processHapticData(); } size_t getUnreleasedFrames(int name) const; @@ -364,6 +391,7 @@ private: // Haptic bool mHapticPlaybackEnabled; + haptic_intensity_t mHapticIntensity; audio_channel_mask_t mHapticChannelMask; uint32_t mHapticChannelCount; audio_channel_mask_t mMixerHapticChannelMask; @@ -374,6 +402,37 @@ private: uint32_t mAdjustNonDestructiveOutChannelCount; bool mKeepContractedChannels; + float getHapticScaleGamma() const { + // Need to keep consistent with the value in VibratorService. + switch (mHapticIntensity) { + case HAPTIC_SCALE_VERY_LOW: + return 2.0f; + case HAPTIC_SCALE_LOW: + return 1.5f; + case HAPTIC_SCALE_HIGH: + return 0.5f; + case HAPTIC_SCALE_VERY_HIGH: + return 0.25f; + default: + return 1.0f; + } + } + + float getHapticMaxAmplitudeRatio() const { + // Need to keep consistent with the value in VibratorService. + switch (mHapticIntensity) { + case HAPTIC_SCALE_VERY_LOW: + return HAPTIC_SCALE_VERY_LOW_RATIO; + case HAPTIC_SCALE_LOW: + return HAPTIC_SCALE_LOW_RATIO; + case HAPTIC_SCALE_NONE: + case HAPTIC_SCALE_HIGH: + case HAPTIC_SCALE_VERY_HIGH: + default: + return 1.0f; + } + } + private: // hooks void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); @@ -410,6 +469,8 @@ private: template void process__noResampleOneTrack(); + void processHapticData(); + static process_hook_t getProcessHook(int processType, uint32_t channelCount, audio_format_t mixerInFormat, audio_format_t mixerOutFormat); diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp index 86711de647..86777d6ca7 100644 --- a/media/libaudioprocessing/AudioMixer.cpp +++ b/media/libaudioprocessing/AudioMixer.cpp @@ -167,6 +167,7 @@ status_t AudioMixer::create( t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT; // haptic t->mHapticPlaybackEnabled = false; + t->mHapticIntensity = HAPTIC_SCALE_NONE; t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE; t->mMixerHapticChannelCount = 0; t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount; @@ -717,6 +718,12 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) track->prepareForAdjustChannels(); } } break; + case HAPTIC_INTENSITY: { + const haptic_intensity_t hapticIntensity = static_cast(valueInt); + if (track->mHapticIntensity != hapticIntensity) { + track->mHapticIntensity = hapticIntensity; + } + } break; default: LOG_ALWAYS_FATAL("setParameter track: bad param %d", param); } @@ -1846,6 +1853,40 @@ void AudioMixer::process__noResampleOneTrack() } } +void AudioMixer::processHapticData() +{ + // Need to keep consistent with VibrationEffect.scale(int, float, int) + for (const auto &pair : mGroups) { + // process by group of tracks with same output main buffer. + const auto &group = pair.second; + for (const int name : group) { + const std::shared_ptr &t = mTracks[name]; + if (t->mHapticPlaybackEnabled) { + size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount; + float gamma = t->getHapticScaleGamma(); + float maxAmplitudeRatio = t->getHapticMaxAmplitudeRatio(); + uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame( + t->mMixerChannelCount, t->mMixerFormat); + switch (t->mMixerFormat) { + // Mixer format should be AUDIO_FORMAT_PCM_FLOAT. + case AUDIO_FORMAT_PCM_FLOAT: { + float* fout = (float*) buffer; + for (size_t i = 0; i < sampleCount; i++) { + float mul = fout[i] >= 0 ? 1.0 : -1.0; + fout[i] = powf(fabsf(fout[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma) + * maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * mul; + } + } break; + default: + LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat); + break; + } + break; + } + } + } +} + /* This track hook is called to do resampling then mixing, * pulling from the track's upstream AudioBufferProvider. * diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp index f328577456..ca0d74980d 100644 --- a/services/audioflinger/FastMixer.cpp +++ b/services/audioflinger/FastMixer.cpp @@ -295,6 +295,8 @@ void FastMixer::onStateChange() (void *)(uintptr_t)mSinkChannelMask); mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY, + (void *)(uintptr_t)fastTrack->mHapticIntensity); mMixer->enable(name); } mGenerations[i] = fastTrack->mGeneration; @@ -333,6 +335,8 @@ void FastMixer::onStateChange() (void *)(uintptr_t)mSinkChannelMask); mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled); + mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY, + (void *)(uintptr_t)fastTrack->mHapticIntensity); // already enabled } mGenerations[i] = fastTrack->mGeneration; diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h index 9d2a733670..c27f2b7a9c 100644 --- a/services/audioflinger/FastMixerState.h +++ b/services/audioflinger/FastMixerState.h @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,8 @@ struct FastTrack { audio_format_t mFormat; // track format int mGeneration; // increment when any field is assigned bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not + AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; // intensity of + // haptic data }; // Represents a single state of the fast mixer diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index d9f570dfe3..8aeae7d186 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -119,6 +119,14 @@ public: void setHapticPlaybackEnabled(bool hapticPlaybackEnabled) { mHapticPlaybackEnabled = hapticPlaybackEnabled; } + /** Return at what intensity to play haptics, used in mixer. */ + AudioMixer::haptic_intensity_t getHapticIntensity() const { return mHapticIntensity; } + /** Set intensity of haptic playback, should be set after querying vibrator service. */ + void setHapticIntensity(AudioMixer::haptic_intensity_t hapticIntensity) { + if (AudioMixer::isValidHapticIntensity(hapticIntensity)) { + mHapticIntensity = hapticIntensity; + } + } protected: // for numerous @@ -197,6 +205,8 @@ protected: sp mVolumeHandler; // handles multiple VolumeShaper configs and operations bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not + // intensity to play haptic data + AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; private: // The following fields are only for fast tracks, and should be in a subclass diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 9f838a3db8..c8480c3541 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -4615,6 +4615,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac fastTrack->mChannelMask = track->mChannelMask; fastTrack->mFormat = track->mFormat; fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled(); + fastTrack->mHapticIntensity = track->getHapticIntensity(); fastTrack->mGeneration++; state->mTrackMask |= 1 << j; didModify = true; @@ -4937,6 +4938,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac trackId, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)track->getHapticPlaybackEnabled()); + mAudioMixer->setParameter( + trackId, + AudioMixer::TRACK, + AudioMixer::HAPTIC_INTENSITY, (void *)(uintptr_t)track->getHapticIntensity()); // reset retry count track->mRetryCount = kMaxTrackRetries;