dualsense-ts
Version:
The natural interface for your DualSense and DualSense Access controllers, with Typescript
132 lines • 5.63 kB
TypeScript
/**
* Shake detector with frequency-band energy analysis.
*
* Maintains circular buffers of per-axis acceleration samples. Shake
* intensity is computed via RMS of the acceleration magnitude deviation
* from its mean (high-pass to remove gravity). The dominant shake
* frequency is identified using the Goertzel algorithm — a single-bin
* DFT that's far cheaper than a full FFT — applied to the first-
* difference of individual axis signals:
*
* - Per-axis preserves the fundamental frequency (magnitude would
* full-wave-rectify the signal, doubling the apparent frequency).
* - First-difference acts as a high-pass filter, removing DC (gravity)
* and low-frequency drift from arm movement. Its gain is proportional
* to frequency, so the Goertzel output naturally favors oscillatory
* shake signals over slow drift — no ad-hoc weighting needed.
*
* A 1/freq compensation is applied so the net effective weighting is
* proportional to freq (diff-filter gain² ∝ freq², divided by freq),
* giving moderate preference to actual shake frequencies without
* completely suppressing sub-1 Hz rocking.
*
* `.frequency` reports the **reversal rate** (2× fundamental) — how many
* direction changes per second. This matches the intuitive "how fast am
* I shaking it?" mental model. `.fundamental` reports the true oscillation
* frequency (one full back-and-forth cycle).
*
* The active flag uses a sustain counter to require several consecutive
* frames above threshold before triggering, preventing transient jolts
* from registering as shakes.
*/
/** A single frequency bin with its associated power level. */
export interface FrequencyBin {
/** Frequency in Hz. */
freq: number;
/** Weighted power (arbitrary units, relative within the array). */
power: number;
}
/** Configuration for the shake detector. */
export interface ShakeDetectorParams {
/** Intensity (0–1) above which `.active` becomes true. Default 0.15. */
threshold?: number;
/** Number of samples in the analysis window. Default 256 (~1s at 250 Hz, ~3s at 84 Hz in browser). */
windowSize?: number;
/**
* Number of consecutive above-threshold frames required before `.active`
* becomes true. Also the number of below-threshold frames required to
* deactivate. Default 15 (~60 ms at 250 Hz).
*/
sustain?: number;
/** Frequency bin resolution in Hz. Default 0.25. Smaller = finer but more bins to compute. */
freqStep?: number;
/** Minimum detectable frequency in Hz. Default 0.25. */
freqMin?: number;
/** Maximum detectable frequency in Hz. Default 15. */
freqMax?: number;
}
export declare class ShakeDetector {
/** Whether the controller is currently being shaken (sustained). */
active: boolean;
/** Shake intensity from 0 (still) to 1 (violent shake). */
intensity: number;
/**
* Dominant shake frequency in Hz as a **reversal rate** — how many
* direction changes per second (2× the fundamental oscillation
* frequency). 0 when not shaking.
*/
frequency: number;
/**
* Fundamental oscillation frequency in Hz — one complete back-and-forth
* cycle. Equal to `frequency / 2`. 0 when not shaking.
*/
fundamental: number;
/** Intensity threshold for `active`. */
threshold: number;
/** Estimated sample rate in Hz (derived from dt). Useful for diagnostics. */
get inputRate(): number;
/** Number of samples in the analysis window. Can be changed at runtime (resets state). */
get windowSize(): number;
set windowSize(n: number);
/**
* Current frequency spectrum — weighted power at each probed bin.
* Updated every frame when intensity is above half the threshold.
* Useful for visualization/diagnostics.
*/
get spectrum(): readonly FrequencyBin[];
private bufX;
private bufY;
private bufZ;
private head;
private filled;
private _sampleRate;
/** Sustain counter for debounce — counts up when above threshold, down when below. */
private sustainCounter;
private readonly sustainRequired;
/** EMA-smoothed frequency for stable readout. */
private smoothedFreq;
/** Frequency bins to probe, built from constructor params. */
private readonly freqBins;
/** Bin step size, used for snapping the EMA output. */
private readonly freqStep;
/** Latest computed bin powers. */
private _bins;
/**
* Scale factor: maps RMS of our [-1, 1] calibrated accel deviation
* to a 0–1 intensity. A vigorous shake produces magnitude-RMS of
* ~0.3–0.5 in our units; this maps that to ~0.6–1.0 intensity.
*/
private static readonly INTENSITY_SCALE;
constructor(params?: ShakeDetectorParams);
/** Reset all state (call on disconnect). */
reset(): void;
/**
* Feed one accelerometer sample.
*
* @param ax Calibrated accel X ([-1, 1])
* @param ay Calibrated accel Y ([-1, 1])
* @param az Calibrated accel Z ([-1, 1])
* @param dt Time delta in seconds since last sample
*/
update(ax: number, ay: number, az: number, dt: number): void;
/**
* Goertzel algorithm: compute power at a single target frequency.
*
* Operates on the first-difference of the filled portion of a circular
* buffer. The first-difference (x[n] - x[n-1]) acts as a high-pass
* filter that removes DC (gravity) and low-frequency arm drift, with
* gain proportional to frequency.
*/
private goertzel;
}
//# sourceMappingURL=shake.d.ts.map