Zum Inhalt

apple/PhysarumApp/Sources/Remote/AudioFactorFrame.swift

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 }
}