Skip to main content

qualia_core_db/render/
acoustic.rs

1//! Phenomenal U2/U3 spectral parity — shared σ truth for vision and AcousticPlane (P-F2).
2//!
3//! Visual: `portal_spectral::sigma_to_cie_xyz` / `spectral.wgsl` (λ ≈ 400–700 nm).
4//! Audio: `sigma_to_center_frequency_hz` folds the same λ band into audible Hz for parametric synth.
5
6use crate::audio::acoustic_plane::AcousticParams;
7use crate::audio::audio_spectral_sheet::{preview_bins_from_tensor, SPECTRAL_PREVIEW_BINS};
8use crate::audio::dsp_kernel::epistemic_fm_index;
9use crate::tensor::Tensor10D;
10
11/// Flat uniform scalar count pushed to AudioWorklet (`acoustic_uniform_floats`).
12pub const ACOUSTIC_UNIFORM_SCALAR_COUNT: usize = 18;
13pub const ACOUSTIC_UNIFORM_FLOAT_COUNT: usize =
14    ACOUSTIC_UNIFORM_SCALAR_COUNT + SPECTRAL_PREVIEW_BINS;
15
16#[inline]
17fn fract_sigma(sigma: f32) -> f32 {
18    sigma - sigma.floor()
19}
20
21/// Wavelength (nm) — must stay aligned with `portal_spectral::sigma_to_cie_xyz`.
22#[inline]
23pub fn sigma_to_wavelength_nm(sigma: f32) -> f32 {
24    400.0 + fract_sigma(sigma) * 300.0
25}
26
27/// Phenomenal audio twin: map σ → center frequency (Hz) for parametric voice.
28///
29/// Linear fold of the same 400–700 nm band used by `portal_spectral` into 1760–110 Hz
30/// (short λ / blue → higher pitch; long λ / red → lower pitch).
31#[inline]
32pub fn sigma_to_center_frequency_hz(sigma: f32) -> f32 {
33    let lambda = sigma_to_wavelength_nm(sigma);
34    let t = ((lambda - 400.0) / 300.0).clamp(0.0, 1.0);
35    (1760.0 * (1.0 - t) + 110.0 * t).clamp(55.0, 8_000.0)
36}
37
38/// Build phenomenal acoustic params — σ oracle drives frequency; preview bins carry tensor lerp.
39#[inline]
40pub fn phenomenal_acoustic_params(t: &Tensor10D) -> AcousticParams {
41    let mut bins = preview_bins_from_tensor(t);
42    let peak = ((fract_sigma(t.sigma) * (SPECTRAL_PREVIEW_BINS - 1) as f32).round() as usize)
43        .min(SPECTRAL_PREVIEW_BINS - 1);
44    bins[peak] = t.alpha.max(bins[peak]);
45    AcousticParams {
46        alpha: t.alpha,
47        mu: t.mu,
48        position: [t.x, t.y, t.z],
49        track_v: t.v,
50        manifold_w: t.w,
51        epistemic_q: t.q,
52        preview_bins: bins,
53    }
54}
55
56/// Frequency + FM for worklet — uses phenomenal σ mapping (not bin peak alone).
57#[inline]
58pub fn phenomenal_voice_frequency_hz(t: &Tensor10D) -> f32 {
59    let sigma_hz = sigma_to_center_frequency_hz(t.sigma);
60    let bin_hz =
61        crate::audio::dsp_kernel::sigma_dominant_frequency(&preview_bins_from_tensor(t), 220.0);
62    sigma_hz * 0.72 + bin_hz * 0.28
63}
64
65#[inline]
66pub fn phenomenal_fm_index(t: &Tensor10D) -> f32 {
67    epistemic_fm_index(t.q, t.mu)
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn sigma_wavelength_matches_spectral_fract() {
76        let s = 0.42;
77        let lambda = sigma_to_wavelength_nm(s);
78        assert!((lambda - 526.0).abs() < 1.0);
79        assert!((sigma_to_wavelength_nm(s + 1.0) - lambda).abs() < 1e-3);
80    }
81
82    #[test]
83    fn sigma_frequency_monotonic_in_fract_band() {
84        let blue = sigma_to_center_frequency_hz(0.1);
85        let red = sigma_to_center_frequency_hz(0.9);
86        assert!(blue > red, "blue {blue} should exceed red {red}");
87        assert!(blue >= 55.0 && red <= 8_000.0);
88        assert!((sigma_to_center_frequency_hz(0.0) - 1760.0).abs() < 1.0);
89        assert!((sigma_to_center_frequency_hz(0.99) - 110.0).abs() < 50.0);
90    }
91
92    #[test]
93    fn phenomenal_params_preserves_alpha_mu() {
94        let t = Tensor10D::new(0.3, 1.0, 2.0, 0.1, 0.2, 0.3, 0.5, 0.8, 0.15, 0.6);
95        let p = phenomenal_acoustic_params(&t);
96        assert_eq!(p.alpha, 0.8);
97        assert_eq!(p.mu, 0.15);
98        assert_eq!(p.preview_bins.len(), SPECTRAL_PREVIEW_BINS);
99    }
100
101    #[test]
102    fn uniform_float_count_matches_portal() {
103        assert_eq!(ACOUSTIC_UNIFORM_FLOAT_COUNT, 82);
104        assert_eq!(ACOUSTIC_UNIFORM_SCALAR_COUNT, 18);
105    }
106}