Merge changes from topic "ScaleHapticDataForHG"
* changes:
Support scaling haptic data in HapticGenerator
Force selecting haptic supported thread when creating HapticGenerator.
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index 1a31420..2ea215f 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -423,7 +423,7 @@
}
} break;
case HAPTIC_INTENSITY: {
- const haptic_intensity_t hapticIntensity = static_cast<haptic_intensity_t>(valueInt);
+ const os::HapticScale hapticIntensity = static_cast<os::HapticScale>(valueInt);
if (track->mHapticIntensity != hapticIntensity) {
track->mHapticIntensity = hapticIntensity;
}
@@ -545,7 +545,7 @@
t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// haptic
t->mHapticPlaybackEnabled = false;
- t->mHapticIntensity = HAPTIC_SCALE_NONE;
+ t->mHapticIntensity = os::HapticScale::NONE;
t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
t->mMixerHapticChannelCount = 0;
t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
@@ -590,19 +590,12 @@
const std::shared_ptr<Track> &t = getTrack(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;
- }
+ os::scaleHapticData((float*) buffer, sampleCount, t->mHapticIntensity);
} break;
default:
LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat);
diff --git a/media/libaudioprocessing/include/media/AudioMixer.h b/media/libaudioprocessing/include/media/AudioMixer.h
index 3f7cd48..70eafe3 100644
--- a/media/libaudioprocessing/include/media/AudioMixer.h
+++ b/media/libaudioprocessing/include/media/AudioMixer.h
@@ -22,10 +22,10 @@
#include <stdint.h>
#include <sys/types.h>
-#include <android/os/IExternalVibratorService.h>
#include <media/AudioMixerBase.h>
#include <media/BufferProviders.h>
#include <utils/threads.h>
+#include <vibrator/ExternalVibrationUtils.h>
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
#define MAX_GAIN_INT AudioMixerBase::UNITY_GAIN_INT
@@ -55,32 +55,6 @@
// parameter 'value' is a pointer to the new playback rate.
};
- typedef enum { // Haptic intensity, should keep consistent with VibratorService
- HAPTIC_SCALE_MUTE = os::IExternalVibratorService::SCALE_MUTE,
- HAPTIC_SCALE_VERY_LOW = os::IExternalVibratorService::SCALE_VERY_LOW,
- HAPTIC_SCALE_LOW = os::IExternalVibratorService::SCALE_LOW,
- HAPTIC_SCALE_NONE = os::IExternalVibratorService::SCALE_NONE,
- HAPTIC_SCALE_HIGH = os::IExternalVibratorService::SCALE_HIGH,
- HAPTIC_SCALE_VERY_HIGH = os::IExternalVibratorService::SCALE_VERY_HIGH,
- } haptic_intensity_t;
- static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2.0f / 3.0f;
- static constexpr float HAPTIC_SCALE_LOW_RATIO = 3.0f / 4.0f;
- static const constexpr float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
-
- static inline bool isValidHapticIntensity(haptic_intensity_t hapticIntensity) {
- switch (hapticIntensity) {
- case HAPTIC_SCALE_MUTE:
- 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)
: AudioMixerBase(frameCount, sampleRate) {
pthread_once(&sOnceControl, &sInitRoutine);
@@ -170,7 +144,7 @@
// Haptic
bool mHapticPlaybackEnabled;
- haptic_intensity_t mHapticIntensity;
+ os::HapticScale mHapticIntensity;
audio_channel_mask_t mHapticChannelMask;
uint32_t mHapticChannelCount;
audio_channel_mask_t mMixerHapticChannelMask;
@@ -180,38 +154,6 @@
uint32_t mAdjustNonDestructiveInChannelCount;
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:
- return 1.0f;
- default:
- return 0.0f;
- }
- }
};
inline std::shared_ptr<Track> getTrack(int name) {
diff --git a/media/libeffects/hapticgenerator/Android.bp b/media/libeffects/hapticgenerator/Android.bp
index ac40e33..f947339 100644
--- a/media/libeffects/hapticgenerator/Android.bp
+++ b/media/libeffects/hapticgenerator/Android.bp
@@ -36,8 +36,10 @@
shared_libs: [
"libaudioutils",
+ "libbinder",
"liblog",
"libutils",
+ "libvibrator",
],
relative_install_path: "soundfx",
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
index 2a41ed5..311e5ba 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
@@ -96,7 +96,10 @@
context->config.outputCfg.bufferProvider.cookie = nullptr;
context->config.outputCfg.mask = EFFECT_CONFIG_ALL;
- memset(&context->param, 0, sizeof(struct HapticGeneratorParam));
+ memset(context->param.hapticChannelSource, 0, sizeof(context->param.hapticChannelSource));
+ context->param.hapticChannelCount = 0;
+ context->param.audioChannelCount = 0;
+ context->param.maxHapticIntensity = os::HapticScale::MUTE;
context->state = HAPTICGENERATOR_STATE_INITIALIZED;
return 0;
@@ -236,12 +239,36 @@
return 0;
}
-int HapticGenerator_SetParameter(struct HapticGeneratorContext *context __unused,
- int32_t param __unused,
- uint32_t size __unused,
- void *value __unused) {
- ALOGW("Setparameter is not implemented in HapticGenerator");
- return -ENOSYS;
+int HapticGenerator_SetParameter(struct HapticGeneratorContext *context,
+ int32_t param,
+ uint32_t size,
+ void *value) {
+ switch (param) {
+ case HG_PARAM_HAPTIC_INTENSITY: {
+ if (value == nullptr || size != (uint32_t) (2 * sizeof(int))) {
+ return -EINVAL;
+ }
+ int id = *(int *) value;
+ os::HapticScale hapticIntensity = static_cast<os::HapticScale>(*((int *) value + 1));
+ if (hapticIntensity == os::HapticScale::MUTE) {
+ context->param.id2Intensity.erase(id);
+ } else {
+ context->param.id2Intensity.emplace(id, hapticIntensity);
+ }
+ context->param.maxHapticIntensity = hapticIntensity;
+ for (const auto&[id, intensity] : context->param.id2Intensity) {
+ context->param.maxHapticIntensity = std::max(
+ context->param.maxHapticIntensity, intensity);
+ }
+ break;
+ }
+
+ default:
+ ALOGW("Unknown param: %d", param);
+ return -EINVAL;
+ }
+
+ return 0;
}
/**
@@ -346,6 +373,11 @@
return -ENODATA;
}
+ if (context->param.maxHapticIntensity == os::HapticScale::MUTE) {
+ // Haptic channels are muted, not need to generate haptic data.
+ return 0;
+ }
+
// Resize buffer if the haptic sample count is greater than buffer size.
size_t hapticSampleCount = inBuffer->frameCount * context->param.hapticChannelCount;
if (hapticSampleCount > context->inputBuffer.size()) {
@@ -367,6 +399,7 @@
float* hapticOutBuffer = HapticGenerator_runProcessingChain(
context->processingChain, context->inputBuffer.data(),
context->outputBuffer.data(), inBuffer->frameCount);
+ os::scaleHapticData(hapticOutBuffer, hapticSampleCount, context->param.maxHapticIntensity);
// For haptic data, the haptic playback thread will copy the data from effect input buffer,
// which contains haptic data at the end of the buffer, directly to sink buffer.
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.h b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
index 4a2308b..a5688de 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.h
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
@@ -19,9 +19,11 @@
#include <functional>
#include <vector>
+#include <map>
#include <hardware/audio_effect.h>
#include <system/audio_effect.h>
+#include <vibrator/ExternalVibrationUtils.h>
#include "Processors.h"
@@ -45,6 +47,10 @@
// The value will be offset of audio channel
uint32_t audioChannelCount;
uint32_t hapticChannelCount;
+
+ // A map from track id to haptic intensity.
+ std::map<int, os::HapticScale> id2Intensity;
+ os::HapticScale maxHapticIntensity; // max intensity will be used to scale haptic data.
};
// A structure to keep all shared pointers for all processors in HapticGenerator.
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f014209..136ae98 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -61,6 +61,7 @@
#include <system/audio_effects/effect_visualizer.h>
#include <system/audio_effects/effect_ns.h>
#include <system/audio_effects/effect_aec.h>
+#include <system/audio_effects/effect_hapticgenerator.h>
#include <audio_utils/primitives.h>
@@ -398,7 +399,7 @@
return ret;
}
}
- return AudioMixer::HAPTIC_SCALE_MUTE;
+ return static_cast<int>(os::HapticScale::MUTE);
}
/* static */
@@ -3297,6 +3298,16 @@
return minThread;
}
+AudioFlinger::ThreadBase *AudioFlinger::hapticPlaybackThread_l() const {
+ for (size_t i = 0; i < mPlaybackThreads.size(); ++i) {
+ PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
+ if (thread->hapticChannelMask() != AUDIO_CHANNEL_NONE) {
+ return thread;
+ }
+ }
+ return nullptr;
+}
+
sp<AudioFlinger::SyncEvent> AudioFlinger::createSyncEvent(AudioSystem::sync_event_t type,
audio_session_t triggerSession,
audio_session_t listenerSession,
@@ -3538,6 +3549,16 @@
goto Exit;
}
+ const bool hapticPlaybackRequired = EffectModule::isHapticGenerator(&desc.type);
+ if (hapticPlaybackRequired
+ && (sessionId == AUDIO_SESSION_DEVICE
+ || sessionId == AUDIO_SESSION_OUTPUT_MIX
+ || sessionId == AUDIO_SESSION_OUTPUT_STAGE)) {
+ // haptic-generating effect is only valid when the session id is a general session id
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+
// return effect descriptor
*pDesc = desc;
if (io == AUDIO_IO_HANDLE_NONE && sessionId == AUDIO_SESSION_OUTPUT_MIX) {
@@ -3612,7 +3633,17 @@
// allow only one effect chain per sessionId on mPlaybackThreads.
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
const audio_io_handle_t checkIo = mPlaybackThreads.keyAt(i);
- if (io == checkIo) continue;
+ if (io == checkIo) {
+ if (hapticPlaybackRequired
+ && mPlaybackThreads.valueAt(i)
+ ->hapticChannelMask() == AUDIO_CHANNEL_NONE) {
+ ALOGE("%s: haptic playback thread is required while the required playback "
+ "thread(io=%d) doesn't support", __func__, (int)io);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ continue;
+ }
const uint32_t sessionType =
mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId);
if ((sessionType & ThreadBase::EFFECT_SESSION) != 0) {
@@ -3649,6 +3680,20 @@
// create effect on selected output thread
bool pinned = !audio_is_global_session(sessionId) && isSessionAcquired_l(sessionId);
+ ThreadBase *oriThread = nullptr;
+ if (hapticPlaybackRequired && thread->hapticChannelMask() == AUDIO_CHANNEL_NONE) {
+ ThreadBase *hapticThread = hapticPlaybackThread_l();
+ if (hapticThread == nullptr) {
+ ALOGE("%s haptic thread not found while it is required", __func__);
+ lStatus = INVALID_OPERATION;
+ goto Exit;
+ }
+ if (hapticThread != thread) {
+ // Force to use haptic thread for haptic-generating effect.
+ oriThread = thread;
+ thread = hapticThread;
+ }
+ }
handle = thread->createEffect_l(client, effectClient, priority, sessionId,
&desc, enabled, &lStatus, pinned, probe);
if (lStatus != NO_ERROR && lStatus != ALREADY_EXISTS) {
@@ -3658,6 +3703,11 @@
} else {
// handle must be valid here, but check again to be safe.
if (handle.get() != nullptr && id != nullptr) *id = handle->id();
+ // Invalidate audio session when haptic playback is created.
+ if (hapticPlaybackRequired && oriThread != nullptr) {
+ // invalidateTracksForAudioSession will trigger locking the thread.
+ oriThread->invalidateTracksForAudioSession(sessionId);
+ }
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index ef7b1ab..d3ad908 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -98,6 +98,7 @@
#include <private/media/AudioTrackShared.h>
#include <vibrator/ExternalVibration.h>
+#include <vibrator/ExternalVibrationUtils.h>
#include "android/media/BnAudioRecord.h"
@@ -756,6 +757,8 @@
sp<ThreadBase> getEffectThread_l(audio_session_t sessionId, int effectId);
+ ThreadBase *hapticPlaybackThread_l() const;
+
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index 81e6065..c6d2110 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -165,6 +165,7 @@
uint32_t sampleRate() const override { return 0; }
audio_channel_mask_t channelMask() const override { return AUDIO_CHANNEL_NONE; }
uint32_t channelCount() const override { return 0; }
+ audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
size_t frameCount() const override { return 0; }
uint32_t latency() const override { return 0; }
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 3dfeb83..9ee47c9 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -25,6 +25,7 @@
#include <utils/Log.h>
#include <system/audio_effects/effect_aec.h>
#include <system/audio_effects/effect_dynamicsprocessing.h>
+#include <system/audio_effects/effect_hapticgenerator.h>
#include <system/audio_effects/effect_ns.h>
#include <system/audio_effects/effect_visualizer.h>
#include <audio_utils/channels.h>
@@ -879,6 +880,11 @@
}
#endif
}
+ if (isHapticGenerator()) {
+ audio_channel_mask_t hapticChannelMask = mCallback->hapticChannelMask();
+ mConfig.inputCfg.channels |= hapticChannelMask;
+ mConfig.outputCfg.channels |= hapticChannelMask;
+ }
mInChannelCountRequested =
audio_channel_count_from_out_mask(mConfig.inputCfg.channels);
mOutChannelCountRequested =
@@ -1522,6 +1528,42 @@
return mOffloaded;
}
+/*static*/
+bool AudioFlinger::EffectModule::isHapticGenerator(const effect_uuid_t *type) {
+ return memcmp(type, FX_IID_HAPTICGENERATOR, sizeof(effect_uuid_t)) == 0;
+}
+
+bool AudioFlinger::EffectModule::isHapticGenerator() const {
+ return isHapticGenerator(&mDescriptor.type);
+}
+
+status_t AudioFlinger::EffectModule::setHapticIntensity(int id, int intensity)
+{
+ if (mStatus != NO_ERROR) {
+ return mStatus;
+ }
+ if (!isHapticGenerator()) {
+ ALOGW("Should not set haptic intensity for effects that are not HapticGenerator");
+ return INVALID_OPERATION;
+ }
+
+ uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 3];
+ effect_param_t *param = (effect_param_t*) buf32;
+ param->psize = sizeof(int32_t);
+ param->vsize = sizeof(int32_t) * 2;
+ *(int32_t*)param->data = HG_PARAM_HAPTIC_INTENSITY;
+ *((int32_t*)param->data + 1) = id;
+ *((int32_t*)param->data + 2) = intensity;
+ uint32_t size = sizeof(int32_t);
+ status_t status = command(
+ EFFECT_CMD_SET_PARAM, sizeof(effect_param_t) + param->psize + param->vsize,
+ param, &size, ¶m->status);
+ if (status == NO_ERROR) {
+ status = param->status;
+ }
+ return status;
+}
+
static std::string dumpInOutBuffer(bool isInput, const sp<EffectBufferHalInterface> &buffer) {
std::stringstream ss;
@@ -2385,6 +2427,25 @@
}
}
+// containsHapticGeneratingEffect_l must be called with ThreadBase::mLock or EffectChain::mLock held
+bool AudioFlinger::EffectChain::containsHapticGeneratingEffect_l()
+{
+ for (size_t i = 0; i < mEffects.size(); ++i) {
+ if (mEffects[i]->isHapticGenerator()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::EffectChain::setHapticIntensity_l(int id, int intensity)
+{
+ Mutex::Autolock _l(mLock);
+ for (size_t i = 0; i < mEffects.size(); ++i) {
+ mEffects[i]->setHapticIntensity(id, intensity);
+ }
+}
+
void AudioFlinger::EffectChain::syncHalEffectsState()
{
Mutex::Autolock _l(mLock);
@@ -2839,6 +2900,14 @@
return t->channelCount();
}
+audio_channel_mask_t AudioFlinger::EffectChain::EffectCallback::hapticChannelMask() const {
+ sp<ThreadBase> t = mThread.promote();
+ if (t == nullptr) {
+ return AUDIO_CHANNEL_NONE;
+ }
+ return t->hapticChannelMask();
+}
+
size_t AudioFlinger::EffectChain::EffectCallback::frameCount() const {
sp<ThreadBase> t = mThread.promote();
if (t == nullptr) {
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 2826297..3cc5a44 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -36,6 +36,7 @@
virtual uint32_t sampleRate() const = 0;
virtual audio_channel_mask_t channelMask() const = 0;
virtual uint32_t channelCount() const = 0;
+ virtual audio_channel_mask_t hapticChannelMask() const = 0;
virtual size_t frameCount() const = 0;
// Non trivial methods usually implemented with help from ThreadBase:
@@ -257,6 +258,11 @@
sp<EffectModule> asEffectModule() override { return this; }
+ static bool isHapticGenerator(const effect_uuid_t* type);
+ bool isHapticGenerator() const;
+
+ status_t setHapticIntensity(int id, int intensity);
+
void dump(int fd, const Vector<String16>& args);
private:
@@ -503,6 +509,10 @@
// isCompatibleWithThread_l() must be called with thread->mLock held
bool isCompatibleWithThread_l(const sp<ThreadBase>& thread) const;
+ bool containsHapticGeneratingEffect_l();
+
+ void setHapticIntensity_l(int id, int intensity);
+
sp<EffectCallbackInterface> effectCallback() const { return mEffectCallback; }
wp<ThreadBase> thread() const { return mEffectCallback->thread(); }
@@ -534,6 +544,7 @@
uint32_t sampleRate() const override;
audio_channel_mask_t channelMask() const override;
uint32_t channelCount() const override;
+ audio_channel_mask_t hapticChannelMask() const override;
size_t frameCount() const override;
uint32_t latency() const override;
@@ -685,6 +696,7 @@
uint32_t sampleRate() const override;
audio_channel_mask_t channelMask() const override;
uint32_t channelCount() const override;
+ audio_channel_mask_t hapticChannelMask() const override { return AUDIO_CHANNEL_NONE; }
size_t frameCount() const override { return 0; }
uint32_t latency() const override { return 0; }
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 396c797..857d3de 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -23,6 +23,7 @@
#include <media/ExtendedAudioBufferProvider.h>
#include <media/nbaio/NBAIO.h>
#include <media/nblog/NBLog.h>
+#include <vibrator/ExternalVibrationUtils.h>
#include "FastThreadState.h"
namespace android {
@@ -49,8 +50,7 @@
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_MUTE; // intensity of
- // haptic data
+ os::HapticScale mHapticIntensity = os::HapticScale::MUTE; // 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 d8eebf3..d05c8b8 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -159,12 +159,12 @@
mHapticPlaybackEnabled = hapticPlaybackEnabled;
}
/** Return at what intensity to play haptics, used in mixer. */
- AudioMixer::haptic_intensity_t getHapticIntensity() const { return mHapticIntensity; }
+ os::HapticScale 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)) {
+ void setHapticIntensity(os::HapticScale hapticIntensity) {
+ if (os::isValidHapticScale(hapticIntensity)) {
mHapticIntensity = hapticIntensity;
- setHapticPlaybackEnabled(mHapticIntensity != AudioMixer::HAPTIC_SCALE_MUTE);
+ setHapticPlaybackEnabled(mHapticIntensity != os::HapticScale::MUTE);
}
}
sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
@@ -265,7 +265,7 @@
bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not
// intensity to play haptic data
- AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_MUTE;
+ os::HapticScale mHapticIntensity = os::HapticScale::MUTE;
class AudioVibrationController : public os::BnExternalVibrationController {
public:
explicit AudioVibrationController(Track* track) : mTrack(track) {}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index c252d77..92f18c1 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1243,6 +1243,11 @@
return BAD_VALUE;
}
}
+
+ if (EffectModule::isHapticGenerator(&desc->type)) {
+ ALOGE("%s(): HapticGenerator is not supported in RecordThread", __func__);
+ return BAD_VALUE;
+ }
return NO_ERROR;
}
@@ -1262,6 +1267,12 @@
return NO_ERROR;
}
+ if (EffectModule::isHapticGenerator(&desc->type) && mHapticChannelCount == 0) {
+ ALOGW("%s: thread doesn't support haptic playback while the effect is HapticGenerator",
+ __func__);
+ return BAD_VALUE;
+ }
+
switch (mType) {
case MIXER: {
#ifndef MULTICHANNEL_EFFECT_CHAIN
@@ -2527,15 +2538,17 @@
track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
}
- if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
- && mHapticChannelMask != AUDIO_CHANNEL_NONE) {
+ sp<EffectChain> chain = getEffectChain_l(track->sessionId());
+ if (mHapticChannelMask != AUDIO_CHANNEL_NONE
+ && ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ || (chain != nullptr && chain->containsHapticGeneratingEffect_l()))) {
// Unlock due to VibratorService will lock for this call and will
// call Tracks.mute/unmute which also require thread's lock.
mLock.unlock();
const int intensity = AudioFlinger::onExternalVibrationStart(
track->getExternalVibration());
mLock.lock();
- track->setHapticIntensity(static_cast<AudioMixer::haptic_intensity_t>(intensity));
+ track->setHapticIntensity(static_cast<os::HapticScale>(intensity));
// Haptic playback should be enabled by vibrator service.
if (track->getHapticPlaybackEnabled()) {
// Disable haptic playback of all active track to ensure only
@@ -2544,12 +2557,16 @@
t->setHapticPlaybackEnabled(false);
}
}
+
+ // Set haptic intensity for effect
+ if (chain != nullptr) {
+ chain->setHapticIntensity_l(track->id(), intensity);
+ }
}
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(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());
@@ -3732,9 +3749,15 @@
// Determine which session to pick up haptic data.
// This must be done under the same lock as prepareTracks_l().
+ // The haptic data from the effect is at a higher priority than the one from track.
// TODO: Write haptic data directly to sink buffer when mixing.
if (mHapticChannelCount > 0 && effectChains.size() > 0) {
for (const auto& track : mActiveTracks) {
+ sp<EffectChain> effectChain = getEffectChain_l(track->sessionId());
+ if (effectChain != nullptr && effectChain->containsHapticGeneratingEffect_l()) {
+ activeHapticSessionId = track->sessionId();
+ break;
+ }
if (track->getHapticPlaybackEnabled()) {
activeHapticSessionId = track->sessionId();
break;
@@ -4104,13 +4127,20 @@
// remove from our tracks vector
removeTrack_l(track);
}
- if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
- && mHapticChannelCount > 0) {
+ if (mHapticChannelCount > 0 &&
+ ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
+ || (chain != nullptr && chain->containsHapticGeneratingEffect_l()))) {
mLock.unlock();
// Unlock due to VibratorService will lock for this call and will
// call Tracks.mute/unmute which also require thread's lock.
AudioFlinger::onExternalVibrationStop(track->getExternalVibration());
mLock.lock();
+
+ // When the track is stop, set the haptic intensity as MUTE
+ // for the HapticGenerator effect.
+ if (chain != nullptr) {
+ chain->setHapticIntensity_l(track->id(), static_cast<int>(os::HapticScale::MUTE));
+ }
}
}
}
@@ -4453,7 +4483,7 @@
// audio to FastMixer
fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE;
- fastTrack->mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE;
+ fastTrack->mHapticIntensity = os::HapticScale::NONE;
fastTrack->mGeneration++;
state->mFastTracksGen++;
state->mTrackMask = 1;
@@ -9393,6 +9423,11 @@
return BAD_VALUE;
}
+ if (EffectModule::isHapticGenerator(&desc->type)) {
+ ALOGE("%s(): HapticGenerator is not supported for MmapThread", __func__);
+ return BAD_VALUE;
+ }
+
return NO_ERROR;
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index f81387e..2e81ae7 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -272,6 +272,7 @@
// 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;
+ virtual audio_channel_mask_t hapticChannelMask() const { return AUDIO_CHANNEL_NONE; }
virtual uint32_t latency_l() const { return 0; }
virtual void setVolumeForOutput_l(float left __unused, float right __unused) const {}
@@ -478,6 +479,25 @@
void onEffectEnable(const sp<EffectModule>& effect);
void onEffectDisable();
+ // invalidateTracksForAudioSession_l must be called with holding mLock.
+ virtual void invalidateTracksForAudioSession_l(audio_session_t sessionId __unused) const { }
+ // Invalidate all the tracks with the given audio session.
+ void invalidateTracksForAudioSession(audio_session_t sessionId) const {
+ Mutex::Autolock _l(mLock);
+ invalidateTracksForAudioSession_l(sessionId);
+ }
+
+ template <typename T>
+ void invalidateTracksForAudioSession_l(audio_session_t sessionId,
+ const T& tracks) const {
+ for (size_t i = 0; i < tracks.size(); ++i) {
+ const sp<TrackBase>& track = tracks[i];
+ if (sessionId == track->sessionId()) {
+ track->invalidate();
+ }
+ }
+ }
+
protected:
// entry describing an effect being suspended in mSuspendedSessions keyed vector
@@ -939,6 +959,13 @@
&& outDeviceTypes().count(mTimestampCorrectedDevice) != 0;
}
+ audio_channel_mask_t hapticChannelMask() const override {
+ return mHapticChannelMask;
+ }
+ bool supportsHapticPlayback() const {
+ return (mHapticChannelMask & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE;
+ }
+
protected:
// updated by readOutputParameters_l()
size_t mNormalFrameCount; // normal mixer and effects
@@ -1061,6 +1088,11 @@
uint32_t trackCountForUid_l(uid_t uid) const;
+ void invalidateTracksForAudioSession_l(
+ audio_session_t sessionId) const override {
+ ThreadBase::invalidateTracksForAudioSession_l(sessionId, mTracks);
+ }
+
private:
friend class AudioFlinger; // for numerous
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index d366bb7..c92bce5 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -595,7 +595,10 @@
+ "_" + std::to_string(mId) + "_T");
#endif
- if (channelMask & AUDIO_CHANNEL_HAPTIC_ALL) {
+ if (thread->supportsHapticPlayback()) {
+ // If the track is attached to haptic playback thread, it is potentially to have
+ // HapticGenerator effect, which will generate haptic data, on the track. In that case,
+ // external vibration is always created for all tracks attached to haptic playback thread.
mAudioVibrationController = new AudioVibrationController(this);
mExternalVibration = new os::ExternalVibration(
mUid, "" /* pkg */, mAttr, mAudioVibrationController);