Some checks failed
Deploy to Pages / build (pull_request) Failing after 3m15s
83 lines
2.2 KiB
TypeScript
83 lines
2.2 KiB
TypeScript
import type { GardenAudioEngineConfig } from '../config';
|
|
import { clamp01 } from '../utils/clamp';
|
|
|
|
const STROKE_IMMEDIATE_ACTIVITY_SCALE = 0.85;
|
|
|
|
export class GardenAudioEnergy {
|
|
private isGestureActive = false;
|
|
private energy = 0;
|
|
private targetEnergy = 0;
|
|
private lastEnergyUpdateAt = 0;
|
|
|
|
public constructor(private readonly engineConfig: GardenAudioEngineConfig) {}
|
|
|
|
public beginGesture(now: number): void {
|
|
this.isGestureActive = true;
|
|
this.lastEnergyUpdateAt = now;
|
|
}
|
|
|
|
public endGesture(): void {
|
|
this.isGestureActive = false;
|
|
this.targetEnergy = 0;
|
|
}
|
|
|
|
public recordStroke(strokeEnergy: number, now: number): void {
|
|
const energy = clamp01(strokeEnergy);
|
|
this.targetEnergy = Math.max(this.targetEnergy, energy);
|
|
if (this.isGestureActive) {
|
|
this.energy = Math.max(this.energy, energy * STROKE_IMMEDIATE_ACTIVITY_SCALE);
|
|
}
|
|
this.lastEnergyUpdateAt ||= now;
|
|
}
|
|
|
|
public recordEraserStroke(): void {
|
|
this.targetEnergy = 0;
|
|
}
|
|
|
|
public silence(): void {
|
|
this.targetEnergy = 0;
|
|
this.energy = 0;
|
|
}
|
|
|
|
public update(now: number): void {
|
|
if (this.lastEnergyUpdateAt <= 0) {
|
|
this.lastEnergyUpdateAt = now;
|
|
return;
|
|
}
|
|
|
|
const elapsedSeconds = Math.max(0, now - this.lastEnergyUpdateAt);
|
|
this.lastEnergyUpdateAt = now;
|
|
this.targetEnergy *= Math.exp(
|
|
-elapsedSeconds / this.engineConfig.energy.strokeDecaySeconds
|
|
);
|
|
|
|
const target = this.isGestureActive ? this.targetEnergy : 0;
|
|
let timeConstant = this.engineConfig.energy.decaySeconds;
|
|
if (!this.isGestureActive) {
|
|
timeConstant = this.engineConfig.energy.releaseSeconds;
|
|
} else if (target > this.energy) {
|
|
timeConstant = this.engineConfig.energy.attackSeconds;
|
|
}
|
|
const amount = 1 - Math.exp(-elapsedSeconds / timeConstant);
|
|
this.energy += (target - this.energy) * amount;
|
|
}
|
|
|
|
public getActivity(): number {
|
|
if (!this.isGestureActive) {
|
|
return 0;
|
|
}
|
|
|
|
return this.getLevel();
|
|
}
|
|
|
|
public getLevel(): number {
|
|
return clamp01(this.energy);
|
|
}
|
|
|
|
public reset(): void {
|
|
this.isGestureActive = false;
|
|
this.energy = 0;
|
|
this.targetEnergy = 0;
|
|
this.lastEnergyUpdateAt = 0;
|
|
}
|
|
}
|