feat(android): simulate intensity & frequency via PWM on no-amplitude devices#95
Open
piaskowyk wants to merge 2 commits into
Open
feat(android): simulate intensity & frequency via PWM on no-amplitude devices#95piaskowyk wants to merge 2 commits into
piaskowyk wants to merge 2 commits into
Conversation
… devices Older Android phones without amplitude control collapsed every continuous segment into one solid buzz: convertToTimingWaveform only checked `intensity > 0` and discarded both the intensity level and the frequency, so distinct presets all felt identical. Port the web engine's PWM / duty-cycle approach (PatternComposer + RealtimeComposer) to the on/off-only Android path: - Add PwmTimingSimulator: intensity -> ON pulse length (duty cycle), frequency -> OFF gap (inverted). ERM-tuned bounds with the min pulse floored near the actuator engagement threshold. - Rewrite convertToTimingWaveform to tile [shot, pause, ...] across the timeline, sampling intensity/frequency per pulse; genuine silent regions stay off. - Fix RealtimePrimitiveComposer LIMITED/<API33 path: encode intensity as pulse length instead of the ignored createOneShot amplitude, and set the loop interval to shot+pause. API 33+ keeps scalable primitives. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On older Android phones without amplitude control (
CompatibilityMode.LIMITED_SUPPORT), continuous presets all felt like one undifferentiated buzz.convertToTimingWaveformonly checkedintensity > 0and collapsed each segment into a solid ON block — discarding both the intensity level and the frequency entirely.Approach
A no-amplitude LRA/ERM driven via
VibrationEffect.createWaveform(timings, -1)is the exact on/off-only equivalent of the webnavigator.vibrate(array). So this ports the web engine's PWM / duty-cycle simulation (web/Pulsar/src/PatternComposer.ts+RealtimeComposer.ts):Changes
PwmTimingSimulator— shared duty-cycle math. ERM-tuned bounds with the minimum pulse floored near the actuator engagement threshold (~40 ms) so low intensities stay perceptible instead of falling into the weak-buzz dead zone (same reasoning the webRealtimeComposerdocuments).convertToTimingWaveform(static patterns/presets) — rewritten to walk the timeline and tile[shot, pause, …], sampling intensity/frequency at each pulse start. Genuine silent regions (intensity 0) stay off; pulses are clamped to the remaining duration.RealtimePrimitiveComposer(realtime gestures, LIMITED + API < 33) — encodes intensity as the ON pulse length instead of the ignoredcreateOneShotamplitude, and sets the loop interval toshot + pause. API 33+ keeps scalable composition primitives, so no regression on capable devices.Verification
./gradlew :Pulsar:compileDebugKotlin/:Pulsar:assembleDebug— ✅ build succeeds.forceHapticsSupportLevel(LIMITED_SUPPORT)still routes to the amplitude waveform on a device that has amplitude (branch isisAmplitudeSupported() && forced >= LIMITED); forceNO_SUPPORTto exerciseconvertToTimingWaveform.usesPwmFallbackrequires API < 33, so it only runs on actual old phones.🤖 Generated with Claude Code