1use core::cell::UnsafeCell;
6use std::sync::atomic::{AtomicU32, Ordering};
7
8use bytemuck::{Pod, Zeroable};
9
10use crate::audio::audio_spectral_sheet::{preview_bins_from_tensor, SPECTRAL_PREVIEW_BINS};
11use crate::audio::dsp_kernel::configure_voice_from_tensor;
12use crate::audio::hrtf::{binaural_from_position, room_damp_from_manifold};
13use crate::gpu_context::{ComputeUniverse, OperationalMode};
14use crate::render::acoustic::{
15 phenomenal_acoustic_params, phenomenal_fm_index, phenomenal_voice_frequency_hz,
16};
17use crate::sonic_token::SonicToken;
18use crate::tensor::Tensor10D;
19
20pub const SONIC_RING_CAP: usize = 128;
21
22#[repr(C)]
24#[derive(Debug, Clone, Copy, PartialEq, Pod, Zeroable)]
25pub struct AcousticUniform {
26 pub alpha: f32,
27 pub mu: f32,
28 pub position: [f32; 3],
29 pub track_v: f32,
30 pub manifold_w: f32,
31 pub epistemic_q: f32,
32 pub fm_index: f32,
33 pub frequency_hz: f32,
34 pub enabled: u32,
35 pub gain_l: f32,
36 pub gain_r: f32,
37 pub itd_seconds: f32,
38 pub azimuth_rad: f32,
39 pub elevation_rad: f32,
40 pub room_damp: f32,
41 pub stft_frame: f32,
42 pub preview_bins: [f32; SPECTRAL_PREVIEW_BINS],
43}
44
45impl Default for AcousticUniform {
46 fn default() -> Self {
47 Self {
48 alpha: 0.0,
49 mu: 0.0,
50 position: [0.0; 3],
51 track_v: 0.0,
52 manifold_w: 0.0,
53 epistemic_q: 0.0,
54 fm_index: 0.0,
55 frequency_hz: 440.0,
56 enabled: 0,
57 gain_l: 0.707,
58 gain_r: 0.707,
59 itd_seconds: 0.0,
60 azimuth_rad: 0.0,
61 elevation_rad: 0.0,
62 room_damp: 1.0,
63 stft_frame: 0.0,
64 preview_bins: [0.0; SPECTRAL_PREVIEW_BINS],
65 }
66 }
67}
68
69#[inline]
70pub fn apply_binaural_to_uniform(u: &mut AcousticUniform, listener_yaw: f32) {
71 let g = binaural_from_position(u.position, listener_yaw);
72 u.gain_l = g.gain_l;
73 u.gain_r = g.gain_r;
74 u.itd_seconds = g.itd_seconds;
75 u.azimuth_rad = g.azimuth_rad;
76 u.elevation_rad = g.elevation_rad;
77 u.room_damp = room_damp_from_manifold(u.manifold_w);
78}
79
80#[derive(Debug, Clone, Copy, PartialEq)]
82pub struct AcousticParams {
83 pub alpha: f32,
84 pub mu: f32,
85 pub position: [f32; 3],
86 pub track_v: f32,
87 pub manifold_w: f32,
88 pub epistemic_q: f32,
89 pub preview_bins: [f32; SPECTRAL_PREVIEW_BINS],
90}
91
92impl AcousticParams {
93 #[inline]
94 pub fn from_tensor(t: &Tensor10D) -> Self {
95 Self {
96 alpha: t.alpha,
97 mu: t.mu,
98 position: [t.x, t.y, t.z],
99 track_v: t.v,
100 manifold_w: t.w,
101 epistemic_q: t.q,
102 preview_bins: preview_bins_from_tensor(t),
103 }
104 }
105
106 #[inline]
107 pub fn to_uniform(self, enabled: bool) -> AcousticUniform {
108 let mut voice = crate::audio::dsp_kernel::ParametricVoiceState::default();
109 configure_voice_from_tensor(
110 &mut voice,
111 self.epistemic_q,
112 self.mu,
113 self.alpha,
114 &self.preview_bins,
115 );
116 let mut u = AcousticUniform {
117 alpha: self.alpha,
118 mu: self.mu,
119 position: self.position,
120 track_v: self.track_v,
121 manifold_w: self.manifold_w,
122 epistemic_q: self.epistemic_q,
123 fm_index: voice.fm_index,
124 frequency_hz: voice.frequency_hz,
125 enabled: u32::from(enabled),
126 preview_bins: self.preview_bins,
127 ..AcousticUniform::default()
128 };
129 u.enabled = u32::from(enabled);
130 u
131 }
132
133 #[inline]
135 pub fn to_phenomenal_uniform(
136 self,
137 enabled: bool,
138 t: &Tensor10D,
139 listener_yaw: f32,
140 ) -> AcousticUniform {
141 let mut u = self.to_uniform(enabled);
142 u.frequency_hz = phenomenal_voice_frequency_hz(t);
143 u.fm_index = phenomenal_fm_index(t);
144 u.stft_frame = (t.t * 32.0).fract() * 32.0;
145 apply_binaural_to_uniform(&mut u, listener_yaw);
146 u
147 }
148}
149
150#[inline]
151pub fn acoustic_params_from_tensor(t: &Tensor10D) -> AcousticParams {
152 phenomenal_acoustic_params(t)
153}
154
155#[inline]
157pub fn acoustic_enabled_for_mode(mode: OperationalMode) -> bool {
158 !matches!(mode, OperationalMode::Reserve)
159}
160
161#[inline]
163pub fn acoustic_effective_mode(global: OperationalMode) -> OperationalMode {
164 crate::gpu_context::universe_orchestrator()
165 .effective_mode(ComputeUniverse::AcousticPlane, global)
166}
167
168pub struct SonicTokenRing {
170 slots: UnsafeCell<[u64; SONIC_RING_CAP]>,
171 write_seq: AtomicU32,
172 read_seq: AtomicU32,
173}
174
175unsafe impl Sync for SonicTokenRing {}
177
178impl SonicTokenRing {
179 pub const fn new() -> Self {
180 Self {
181 slots: UnsafeCell::new([0u64; SONIC_RING_CAP]),
182 write_seq: AtomicU32::new(0),
183 read_seq: AtomicU32::new(0),
184 }
185 }
186
187 #[inline]
188 pub fn len(&self) -> usize {
189 let w = self.write_seq.load(Ordering::Acquire);
190 let r = self.read_seq.load(Ordering::Acquire);
191 (w.wrapping_sub(r)) as usize
192 }
193
194 #[inline]
195 pub fn is_empty(&self) -> bool {
196 self.len() == 0
197 }
198
199 pub fn try_push(&self, token: SonicToken) -> bool {
200 let w = self.write_seq.load(Ordering::Relaxed);
201 let r = self.read_seq.load(Ordering::Acquire);
202 if w.wrapping_sub(r) >= SONIC_RING_CAP as u32 {
203 return false;
204 }
205 let slot = (w % SONIC_RING_CAP as u32) as usize;
206 unsafe {
207 (*self.slots.get())[slot] = token.raw;
208 }
209 self.write_seq.store(w.wrapping_add(1), Ordering::Release);
210 true
211 }
212
213 pub fn try_pop(&self) -> Option<SonicToken> {
214 let r = self.read_seq.load(Ordering::Relaxed);
215 let w = self.write_seq.load(Ordering::Acquire);
216 if r == w {
217 return None;
218 }
219 let slot = (r % SONIC_RING_CAP as u32) as usize;
220 let raw = unsafe { (*self.slots.get())[slot] };
221 self.read_seq.store(r.wrapping_add(1), Ordering::Release);
222 Some(SonicToken { raw })
223 }
224}
225
226static SONIC_TOKEN_RING: SonicTokenRing = SonicTokenRing::new();
227
228#[inline]
229pub fn sonic_token_ring() -> &'static SonicTokenRing {
230 &SONIC_TOKEN_RING
231}
232
233#[inline]
234pub fn push_sonic_token(token: SonicToken) -> bool {
235 sonic_token_ring().try_push(token)
236}
237
238#[inline]
239pub fn pop_sonic_token() -> Option<SonicToken> {
240 sonic_token_ring().try_pop()
241}
242
243pub fn drain_sonic_tokens(out: &mut [u64]) -> usize {
245 let mut n = 0usize;
246 while n < out.len() {
247 match pop_sonic_token() {
248 Some(t) => {
249 out[n] = t.raw;
250 n += 1;
251 }
252 None => break,
253 }
254 }
255 n
256}
257
258pub fn sonify_tensor_node(tensor_index: u32, t: &Tensor10D, note_on: bool) -> usize {
260 let mut pushed = 0usize;
261 let pitch = SonicToken::pitch_from_tensor(t.w, t.q, t.sigma);
262 let velocity = (t.alpha.clamp(0.0, 1.0) * 127.0) as u8;
263 if push_sonic_token(SonicToken::parametric_pulse(tensor_index, velocity)) {
264 pushed += 1;
265 }
266 if note_on {
267 if push_sonic_token(SonicToken::note_on(tensor_index, pitch, velocity, 0)) {
268 pushed += 1;
269 }
270 }
271 pushed
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277 use crate::sonic_token::{SonicEventType, SONIC_MAGIC};
278
279 fn fresh_ring() -> SonicTokenRing {
280 SonicTokenRing::new()
281 }
282
283 #[test]
284 fn sonic_ring_push_pop() {
285 let ring = fresh_ring();
286 let t = SonicToken::note_on(1, 60, 100, 0);
287 assert!(ring.try_push(t));
288 let popped = ring.try_pop().expect("token");
289 assert_eq!(popped.tensor_index(), 1);
290 assert_eq!(popped.note(), 60);
291 }
292
293 #[test]
294 fn sonic_ring_full_returns_false() {
295 let ring = fresh_ring();
296 for i in 0..SONIC_RING_CAP {
297 assert!(
298 ring.try_push(SonicToken::note_on(i as u32, 60, 100, 0)),
299 "push {i}"
300 );
301 }
302 assert!(!ring.try_push(SonicToken::note_on(999, 60, 100, 0)));
303 }
304
305 #[test]
306 fn drain_respects_capacity() {
307 let ring = fresh_ring();
308 for i in 0..4 {
309 ring.try_push(SonicToken::note_on(i, 60, 100, 0));
310 }
311 let mut local = [0u64; 2];
312 let mut n = 0usize;
313 while n < local.len() {
314 if let Some(t) = ring.try_pop() {
315 local[n] = t.raw;
316 n += 1;
317 } else {
318 break;
319 }
320 }
321 assert_eq!(n, 2);
322 assert_eq!(ring.len(), 2);
323 }
324
325 #[test]
326 fn acoustic_uniform_size_stable() {
327 assert_eq!(std::mem::size_of::<AcousticUniform>() % 4, 0);
328 let t = Tensor10D::default();
329 let u = AcousticParams::from_tensor(&t).to_uniform(true);
330 assert_eq!(u.enabled, 1);
331 }
332
333 #[test]
334 fn acoustic_disabled_in_reserve() {
335 assert!(!acoustic_enabled_for_mode(OperationalMode::Reserve));
336 assert!(acoustic_enabled_for_mode(OperationalMode::Full));
337 }
338
339 #[test]
340 fn sonify_pushes_tokens() {
341 let ring = fresh_ring();
342 let t = Tensor10D::new(0.5, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.8, 0.1, 2.5);
343 let pitch = SonicToken::pitch_from_tensor(t.w, t.q, t.sigma);
345 assert!(ring.try_push(SonicToken::parametric_pulse(7, 100)));
346 assert!(ring.try_push(SonicToken::note_on(7, pitch, 100, 0)));
347 assert_eq!(ring.len(), 2);
348 let first = ring.try_pop().unwrap();
349 assert_eq!(first.event_type(), SonicEventType::Parametric);
350 let second = ring.try_pop().unwrap();
351 assert_eq!(second.event_type(), SonicEventType::NoteOn);
352 assert_eq!(second.flags() & 0xff, SONIC_MAGIC as u16);
353 }
354}