diff options
author | Vince Leung <leungv@google.com> | 2021-02-11 02:21:57 +0000 |
---|---|---|
committer | Vince Leung <leungv@google.com> | 2021-03-24 06:20:21 +0000 |
commit | 823cf5f4551721aa41403d0c873940294984ece4 (patch) | |
tree | affcfbc7baf549503c9568ed38c2e5beddd0c870 /vibrator/aidl/default/Vibrator.cpp | |
parent | aa32e6903901574780c9aeb6617729bb0fcbe1d5 (diff) |
Add Chirp APIs for PWLE sequence creation
Add the necessary Chirp APIs required to allow for piecewise-linear
equation sequencing of haptic waveforms. These APIs will allow
developers to create a fully customizable sequence of haptics waveforms
for playback.
Bug: 162859057
Test: verify pwle sequences can be created and played using idlcli
command. Also verify using atest.
Change-Id: I7fec224b7090e482bbcd1c94a3799ec232cc547f
Diffstat (limited to 'vibrator/aidl/default/Vibrator.cpp')
-rw-r--r-- | vibrator/aidl/default/Vibrator.cpp | 173 |
1 files changed, 172 insertions, 1 deletions
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp index bf61bfec91..c6682b318f 100644 --- a/vibrator/aidl/default/Vibrator.cpp +++ b/vibrator/aidl/default/Vibrator.cpp @@ -26,9 +26,16 @@ namespace vibrator { static constexpr int32_t kComposeDelayMaxMs = 1000; static constexpr int32_t kComposeSizeMax = 256; +static constexpr int32_t kComposePwleSizeMax = 127; static constexpr float kResonantFrequency = 150.0; static constexpr float kQFactor = 11.0; +static constexpr int32_t COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS = 16383; +static constexpr float PWLE_LEVEL_MIN = 0.0; +static constexpr float PWLE_LEVEL_MAX = 0.98256; +static constexpr float PWLE_FREQUENCY_RESOLUTION_HZ = 1.0; +static constexpr float PWLE_FREQUENCY_MIN_HZ = 140.0; +static constexpr float PWLE_FREQUENCY_MAX_HZ = 160.0; ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) { LOG(INFO) << "Vibrator reporting capabilities"; @@ -36,7 +43,8 @@ ndk::ScopedAStatus Vibrator::getCapabilities(int32_t* _aidl_return) { IVibrator::CAP_AMPLITUDE_CONTROL | IVibrator::CAP_EXTERNAL_CONTROL | IVibrator::CAP_EXTERNAL_AMPLITUDE_CONTROL | IVibrator::CAP_COMPOSE_EFFECTS | IVibrator::CAP_ALWAYS_ON_CONTROL | IVibrator::CAP_GET_RESONANT_FREQUENCY | - IVibrator::CAP_GET_Q_FACTOR; + IVibrator::CAP_GET_Q_FACTOR | IVibrator::CAP_FREQUENCY_CONTROL | + IVibrator::CAP_COMPOSE_PWLE_EFFECTS; return ndk::ScopedAStatus::ok(); } @@ -215,6 +223,169 @@ ndk::ScopedAStatus Vibrator::getQFactor(float *qFactor) { return ndk::ScopedAStatus::ok(); } +ndk::ScopedAStatus Vibrator::getFrequencyResolution(float *freqResolutionHz) { + *freqResolutionHz = PWLE_FREQUENCY_RESOLUTION_HZ; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getFrequencyMinimum(float *freqMinimumHz) { + *freqMinimumHz = PWLE_FREQUENCY_MIN_HZ; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getBandwidthAmplitudeMap(std::vector<float> *_aidl_return) { + // A valid array should be of size: + // (PWLE_FREQUENCY_MAX_HZ - PWLE_FREQUENCY_MIN_HZ) / PWLE_FREQUENCY_RESOLUTION_HZ + *_aidl_return = {0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.10, + 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.20}; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getPwlePrimitiveDurationMax(int32_t *durationMs) { + *durationMs = COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getPwleCompositionSizeMax(int32_t *maxSize) { + *maxSize = kComposePwleSizeMax; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Vibrator::getSupportedBraking(std::vector<Braking> *supported) { + *supported = { + Braking::NONE, + Braking::CLAB, + }; + return ndk::ScopedAStatus::ok(); +} + +void resetPreviousEndAmplitudeEndFrequency(float &prevEndAmplitude, float &prevEndFrequency) { + const float reset = -1.0; + prevEndAmplitude = reset; + prevEndFrequency = reset; +} + +void incrementIndex(int &index) { + index += 1; +} + +void constructActiveDefaults(std::ostringstream &pwleBuilder, const int &segmentIdx) { + pwleBuilder << ",C" << segmentIdx << ":1"; + pwleBuilder << ",B" << segmentIdx << ":0"; + pwleBuilder << ",AR" << segmentIdx << ":0"; + pwleBuilder << ",V" << segmentIdx << ":0"; +} + +void constructActiveSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration, + float amplitude, float frequency) { + pwleBuilder << ",T" << segmentIdx << ":" << duration; + pwleBuilder << ",L" << segmentIdx << ":" << amplitude; + pwleBuilder << ",F" << segmentIdx << ":" << frequency; + constructActiveDefaults(pwleBuilder, segmentIdx); +} + +void constructBrakingSegment(std::ostringstream &pwleBuilder, const int &segmentIdx, int duration, + Braking brakingType) { + pwleBuilder << ",T" << segmentIdx << ":" << duration; + pwleBuilder << ",L" << segmentIdx << ":" << 0; + pwleBuilder << ",F" << segmentIdx << ":" << 0; + pwleBuilder << ",C" << segmentIdx << ":0"; + pwleBuilder << ",B" << segmentIdx << ":" + << static_cast<std::underlying_type<Braking>::type>(brakingType); + pwleBuilder << ",AR" << segmentIdx << ":0"; + pwleBuilder << ",V" << segmentIdx << ":0"; +} + +ndk::ScopedAStatus Vibrator::composePwle(const std::vector<PrimitivePwle> &composite, + const std::shared_ptr<IVibratorCallback> &callback) { + std::ostringstream pwleBuilder; + std::string pwleQueue; + + int compositionSizeMax; + getPwleCompositionSizeMax(&compositionSizeMax); + if (composite.size() <= 0 || composite.size() > compositionSizeMax) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + float prevEndAmplitude; + float prevEndFrequency; + resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency); + + int segmentIdx = 0; + uint32_t totalDuration = 0; + + pwleBuilder << "S:0,WF:4,RP:0,WT:0"; + + for (auto &e : composite) { + switch (e.getTag()) { + case PrimitivePwle::active: { + auto active = e.get<PrimitivePwle::active>(); + if (active.duration < 0 || + active.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (active.startAmplitude < PWLE_LEVEL_MIN || + active.startAmplitude > PWLE_LEVEL_MAX || + active.endAmplitude < PWLE_LEVEL_MIN || active.endAmplitude > PWLE_LEVEL_MAX) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (active.startFrequency < PWLE_FREQUENCY_MIN_HZ || + active.startFrequency > PWLE_FREQUENCY_MAX_HZ || + active.endFrequency < PWLE_FREQUENCY_MIN_HZ || + active.endFrequency > PWLE_FREQUENCY_MAX_HZ) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + if (!((active.startAmplitude == prevEndAmplitude) && + (active.startFrequency == prevEndFrequency))) { + constructActiveSegment(pwleBuilder, segmentIdx, 0, active.startAmplitude, + active.startFrequency); + incrementIndex(segmentIdx); + } + + constructActiveSegment(pwleBuilder, segmentIdx, active.duration, + active.endAmplitude, active.endFrequency); + incrementIndex(segmentIdx); + + prevEndAmplitude = active.endAmplitude; + prevEndFrequency = active.endFrequency; + totalDuration += active.duration; + break; + } + case PrimitivePwle::braking: { + auto braking = e.get<PrimitivePwle::braking>(); + if (braking.braking > Braking::CLAB) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + if (braking.duration > COMPOSE_PWLE_PRIMITIVE_DURATION_MAX_MS) { + return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT); + } + + constructBrakingSegment(pwleBuilder, segmentIdx, 0, braking.braking); + incrementIndex(segmentIdx); + + constructBrakingSegment(pwleBuilder, segmentIdx, braking.duration, braking.braking); + incrementIndex(segmentIdx); + + resetPreviousEndAmplitudeEndFrequency(prevEndAmplitude, prevEndFrequency); + totalDuration += braking.duration; + break; + } + } + } + + std::thread([=] { + LOG(INFO) << "Starting composePwle on another thread"; + usleep(totalDuration * 1000); + if (callback != nullptr) { + LOG(INFO) << "Notifying compose PWLE complete"; + callback->onComplete(); + } + }).detach(); + + return ndk::ScopedAStatus::ok(); +} + } // namespace vibrator } // namespace hardware } // namespace android |