fleeting-garden/src/audio/garden-audio-input.ts
2026-05-16 15:05:35 +01:00

70 lines
2 KiB
TypeScript

import type { GardenAudioEngineConfig } from '../config';
import { clamp01 } from '../utils/clamp';
import { GardenAudioStroke } from './garden-audio-types';
export interface GardenAudioStrokeMetrics {
distancePixels: number;
pressure: number;
speedPixelsPerSecond: number;
speedAmount: number;
effectiveEnergy: number;
}
export const getStrokeMetrics = (
stroke: GardenAudioStroke,
speedForFullEnergyPixelsPerSecond: number,
inputConfig: GardenAudioEngineConfig['input']
): GardenAudioStrokeMetrics => {
const dx = stroke.to[0] - stroke.from[0];
const dy = stroke.to[1] - stroke.from[1];
const distancePixels = Math.hypot(dx, dy);
const speedPixelsPerSecond = getStrokeVelocity(stroke, distancePixels, inputConfig);
const pressure = getPressureAmount(stroke);
const speedAmount = clamp01(speedPixelsPerSecond / speedForFullEnergyPixelsPerSecond);
const strokeEnergy = clamp01(
inputConfig.strokeEnergyBase +
speedAmount * inputConfig.strokeEnergySpeedWeight +
pressure * inputConfig.strokeEnergyPressureWeight
);
const effectiveEnergy =
strokeEnergy *
(inputConfig.distanceEnergyBase +
clamp01(distancePixels / inputConfig.distanceForFullEnergyPixels) *
inputConfig.distanceEnergyScale);
return {
distancePixels,
pressure,
speedPixelsPerSecond,
speedAmount,
effectiveEnergy,
};
};
const getStrokeVelocity = (
stroke: GardenAudioStroke,
distancePixels: number,
inputConfig: GardenAudioEngineConfig['input']
): number => {
if (
stroke.velocityPixelsPerSecond !== undefined &&
Number.isFinite(stroke.velocityPixelsPerSecond) &&
stroke.velocityPixelsPerSecond >= 0
) {
return stroke.velocityPixelsPerSecond;
}
return distancePixels / inputConfig.fallbackFrameSeconds;
};
const getPressureAmount = (stroke: GardenAudioStroke): number => {
if (
stroke.pressure !== undefined &&
Number.isFinite(stroke.pressure) &&
stroke.pressure > 0
) {
return clamp01(stroke.pressure);
}
return 0;
};