qualia_core_db/audio/
acoustic_sab.rs1use bytemuck::{Pod, Zeroable};
6
7use super::acoustic_plane::AcousticUniform;
8use crate::sonic_token::SonicToken;
9
10pub const ACOUSTIC_SAB_MAGIC: u32 = 0x5133_4153; pub const ACOUSTIC_SAB_VERSION: u16 = 1;
12pub const ACOUSTIC_SAB_HEADER_BYTES: usize = 32;
13pub const ACOUSTIC_SAB_UNIFORM_OFFSET: usize = 32;
14pub const ACOUSTIC_SAB_UNIFORM_PADDED: usize = 352;
15pub const ACOUSTIC_SAB_TOKEN_OFFSET: usize =
16 ACOUSTIC_SAB_UNIFORM_OFFSET + ACOUSTIC_SAB_UNIFORM_PADDED;
17pub const ACOUSTIC_SAB_TOKEN_BYTES: usize = 128;
18pub const ACOUSTIC_SAB_FLOAT_MIRROR_OFFSET: usize =
19 ACOUSTIC_SAB_TOKEN_OFFSET + ACOUSTIC_SAB_TOKEN_BYTES;
20pub const ACOUSTIC_SAB_FLOAT_MIRROR_BYTES: usize = 328;
21pub const ACOUSTIC_SAB_BYTES: usize = 1024;
22
23#[repr(C)]
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
26pub struct AcousticSabHeader {
27 pub magic: u32,
28 pub version: u16,
29 pub uniform_seq: u16,
30 pub token_write: u32,
31 pub token_read: u32,
32 pub stft_frame: u32,
33 pub sample_rate: u32,
34 pub _pad: u32,
35}
36
37impl AcousticSabHeader {
38 #[inline]
39 pub const fn new() -> Self {
40 Self {
41 magic: ACOUSTIC_SAB_MAGIC,
42 version: ACOUSTIC_SAB_VERSION,
43 uniform_seq: 0,
44 token_write: 0,
45 token_read: 0,
46 stft_frame: 0,
47 sample_rate: 48_000,
48 _pad: 0,
49 }
50 }
51
52 #[inline]
53 pub fn parse(bytes: &[u8]) -> Option<Self> {
54 let hlen = std::mem::size_of::<Self>();
55 if bytes.len() < hlen {
56 return None;
57 }
58 let h = bytemuck::pod_read_unaligned::<AcousticSabHeader>(&bytes[..hlen]);
59 (h.magic == ACOUSTIC_SAB_MAGIC && h.version == ACOUSTIC_SAB_VERSION).then_some(h)
60 }
61}
62
63#[inline]
64pub fn init_acoustic_sab(out: &mut [u8]) -> bool {
65 if out.len() < ACOUSTIC_SAB_BYTES {
66 return false;
67 }
68 let header = AcousticSabHeader::new();
69 out[..ACOUSTIC_SAB_HEADER_BYTES].fill(0);
70 let hlen = std::mem::size_of::<AcousticSabHeader>();
71 out[..hlen].copy_from_slice(bytemuck::bytes_of(&header));
72 out[ACOUSTIC_SAB_UNIFORM_OFFSET..].fill(0);
73 true
74}
75
76#[inline]
78pub fn write_uniform_to_sab(out: &mut [u8], uniform: &AcousticUniform) -> bool {
79 write_uniform_to_sab_with_mirror(out, uniform, None)
80}
81
82#[inline]
83pub fn write_uniform_to_sab_with_mirror(
84 out: &mut [u8],
85 uniform: &AcousticUniform,
86 float_mirror: Option<&[f32]>,
87) -> bool {
88 if out.len() < ACOUSTIC_SAB_FLOAT_MIRROR_OFFSET + ACOUSTIC_SAB_FLOAT_MIRROR_BYTES {
89 return false;
90 }
91 let bytes = bytemuck::bytes_of(uniform);
92 let end = ACOUSTIC_SAB_UNIFORM_OFFSET + bytes.len();
93 if end > ACOUSTIC_SAB_TOKEN_OFFSET {
94 return false;
95 }
96 out[ACOUSTIC_SAB_UNIFORM_OFFSET..end].copy_from_slice(bytes);
97 if let Some(mirror) = float_mirror {
98 let n = mirror.len().min(ACOUSTIC_SAB_FLOAT_MIRROR_BYTES / 4);
99 out[ACOUSTIC_SAB_FLOAT_MIRROR_OFFSET..ACOUSTIC_SAB_FLOAT_MIRROR_OFFSET + n * 4]
100 .copy_from_slice(bytemuck::cast_slice(&mirror[..n]));
101 }
102 let header = AcousticSabHeader::parse(out).unwrap_or(AcousticSabHeader::new());
103 let mut h = header;
104 h.uniform_seq = h.uniform_seq.wrapping_add(1);
105 let hlen = std::mem::size_of::<AcousticSabHeader>();
106 out[..hlen].copy_from_slice(bytemuck::bytes_of(&h));
107 true
108}
109
110#[inline]
111fn write_sab_header(out: &mut [u8], h: &AcousticSabHeader) {
112 let hlen = std::mem::size_of::<AcousticSabHeader>();
113 out[..hlen].copy_from_slice(bytemuck::bytes_of(h));
114}
115
116#[inline]
117pub fn read_uniform_from_sab(bytes: &[u8]) -> Option<AcousticUniform> {
118 if bytes.len() < ACOUSTIC_SAB_UNIFORM_OFFSET + std::mem::size_of::<AcousticUniform>() {
119 return None;
120 }
121 let u_size = std::mem::size_of::<AcousticUniform>();
122 Some(bytemuck::pod_read_unaligned::<AcousticUniform>(
123 &bytes[ACOUSTIC_SAB_UNIFORM_OFFSET..ACOUSTIC_SAB_UNIFORM_OFFSET + u_size],
124 ))
125}
126
127#[inline]
129pub fn push_token_to_sab(out: &mut [u8], token: SonicToken) -> bool {
130 if out.len() < ACOUSTIC_SAB_BYTES {
131 return false;
132 }
133 let mut h = AcousticSabHeader::parse(out).unwrap_or(AcousticSabHeader::new());
134 let w = h.token_write;
135 let r = h.token_read;
136 if w.wrapping_sub(r) >= 16 {
137 return false;
138 }
139 let slot = (w % 16) as usize;
140 let off = ACOUSTIC_SAB_TOKEN_OFFSET + slot * 8;
141 out[off..off + 8].copy_from_slice(bytemuck::bytes_of(&token.raw));
142 h.token_write = w.wrapping_add(1);
143 write_sab_header(out, &h);
144 true
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn sab_header_pod_fits_slot() {
153 let hlen = std::mem::size_of::<AcousticSabHeader>();
154 assert!(hlen <= ACOUSTIC_SAB_HEADER_BYTES);
155 assert_eq!(hlen, 28);
156 }
157
158 #[test]
159 fn sab_roundtrip_uniform() {
160 let mut sab = [0u8; ACOUSTIC_SAB_BYTES];
161 assert!(init_acoustic_sab(&mut sab));
162 let u = AcousticUniform::default();
163 assert!(write_uniform_to_sab(&mut sab, &u));
164 let h = AcousticSabHeader::parse(&sab).unwrap();
165 assert_eq!(h.uniform_seq, 1);
166 let read = read_uniform_from_sab(&sab).unwrap();
167 assert_eq!(read.frequency_hz, u.frequency_hz);
168 }
169
170 #[test]
171 fn sab_token_push() {
172 let mut sab = [0u8; ACOUSTIC_SAB_BYTES];
173 init_acoustic_sab(&mut sab);
174 let t = SonicToken::note_on(1, 60, 100, 0);
175 assert!(push_token_to_sab(&mut sab, t));
176 let h = AcousticSabHeader::parse(&sab).unwrap();
177 assert_eq!(h.token_write, 1);
178 }
179}