import PhysarumEngine
/// The 10 audio modulation factors (6 agent + 4 post) as a compact `Codable` wire payload (T2b). The
/// phone computes these from its mic + routes via `evaluateAudioFactors` and streams them; the ATV
/// applies them directly to `sim.audioFactors` + `renderer.audioPostFactors`. A dedicated type (not
/// `UniformInput.Audio`, a GPU uniform struct) keeps the wire format explicit + `Equatable` for tests.
struct AudioFactorFrame: Codable, Equatable {
var moveSpeed, turnSpeed, deposit, sensorDistance, rhythmAmplitude, chaosStrength: Float
var exposure, bloomIntensity, saturation, contrast: Float
}
extension AudioFactorFrame {
/// All factors at 1.0 — the visuals run un-modulated. Sent on a clean stop so the ATV relaxes.
static let neutral = AudioFactorFrame(
moveSpeed: 1, turnSpeed: 1, deposit: 1, sensorDistance: 1, rhythmAmplitude: 1, chaosStrength: 1,
exposure: 1, bloomIntensity: 1, saturation: 1, contrast: 1)
/// Flatten the per-`ModTarget` factor map into the 10 wire fields (6 agent + 4 post).
init(_ f: AudioFactors) {
let a = f.agentUniform
self.init(moveSpeed: a.moveSpeedFactor, turnSpeed: a.turnSpeedFactor, deposit: a.depositFactor,
sensorDistance: a.sensorDistanceFactor, rhythmAmplitude: a.rhythmAmplitudeFactor,
chaosStrength: a.chaosStrengthFactor,
exposure: f.postExposure, bloomIntensity: f.postBloomIntensity,
saturation: f.postSaturation, contrast: f.postContrast)
}
/// The 6 agent-side factors as the engine's GPU uniform struct.
var agentUniform: UniformInput.Audio {
var a = UniformInput.Audio()
a.moveSpeedFactor = moveSpeed; a.turnSpeedFactor = turnSpeed; a.depositFactor = deposit
a.sensorDistanceFactor = sensorDistance; a.rhythmAmplitudeFactor = rhythmAmplitude
a.chaosStrengthFactor = chaosStrength
return a
}
var postExposure: Float { exposure }
var postBloomIntensity: Float { bloomIntensity }
var postSaturation: Float { saturation }
var postContrast: Float { contrast }
}