qualia_core_db/net/
sonic_token.rs1use bytemuck::{Pod, Zeroable};
6
7pub const SONIC_MAGIC: u8 = 0x53; #[repr(C)]
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
12pub struct SonicToken {
13 pub raw: u64,
14}
15
16#[repr(u8)]
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum SonicEventType {
19 NoteOn = 0,
20 NoteOff = 1,
21 ControlChange = 2,
22 Parametric = 3,
23}
24
25impl SonicToken {
26 #[inline]
27 pub const fn empty() -> Self {
28 Self { raw: 0 }
29 }
30
31 #[inline]
32 pub fn pack(
33 delta_time: u8,
34 event_type: SonicEventType,
35 channel: u8,
36 note: u8,
37 velocity: u8,
38 tensor_index: u32,
39 flags: u16,
40 ) -> Self {
41 let et = event_type as u64;
42 let raw = (delta_time as u64)
43 | ((et & 0x0f) << 8)
44 | ((channel as u64 & 0x0f) << 12)
45 | ((note as u64) << 16)
46 | ((velocity as u64) << 24)
47 | ((tensor_index as u64 & 0xffff) << 32)
48 | ((flags as u64 & 0xffff) << 48);
49 Self { raw }
50 }
51
52 #[inline]
53 pub fn delta_time(self) -> u8 {
54 self.raw as u8
55 }
56
57 #[inline]
58 pub fn event_type(self) -> SonicEventType {
59 match (self.raw >> 8) & 0x0f {
60 1 => SonicEventType::NoteOff,
61 2 => SonicEventType::ControlChange,
62 3 => SonicEventType::Parametric,
63 _ => SonicEventType::NoteOn,
64 }
65 }
66
67 #[inline]
68 pub fn channel(self) -> u8 {
69 ((self.raw >> 12) & 0x0f) as u8
70 }
71
72 #[inline]
73 pub fn note(self) -> u8 {
74 ((self.raw >> 16) & 0xff) as u8
75 }
76
77 #[inline]
78 pub fn velocity(self) -> u8 {
79 ((self.raw >> 24) & 0xff) as u8
80 }
81
82 #[inline]
83 pub fn tensor_index(self) -> u32 {
84 ((self.raw >> 32) & 0xffff) as u32
85 }
86
87 #[inline]
88 pub fn flags(self) -> u16 {
89 (self.raw >> 48) as u16
90 }
91
92 #[inline]
94 pub fn pitch_from_tensor(w: f32, q: f32, sigma: f32) -> u8 {
95 let base = 48.0 + w * 12.0 + sigma.fract() * 24.0;
96 let jitter = q * 6.0;
97 (base + jitter).clamp(0.0, 127.0) as u8
98 }
99
100 #[inline]
102 pub fn note_on(tensor_index: u32, note: u8, velocity: u8, channel: u8) -> Self {
103 Self::pack(
104 0,
105 SonicEventType::NoteOn,
106 channel,
107 note,
108 velocity,
109 tensor_index,
110 SONIC_MAGIC as u16,
111 )
112 }
113
114 #[inline]
115 pub fn parametric_pulse(tensor_index: u32, velocity: u8) -> Self {
116 Self::pack(
117 0,
118 SonicEventType::Parametric,
119 0,
120 0,
121 velocity,
122 tensor_index,
123 SONIC_MAGIC as u16,
124 )
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn pack_roundtrip() {
134 let t = SonicToken::pack(3, SonicEventType::NoteOn, 2, 60, 100, 42, 0x0053);
135 assert_eq!(t.delta_time(), 3);
136 assert_eq!(t.event_type(), SonicEventType::NoteOn);
137 assert_eq!(t.channel(), 2);
138 assert_eq!(t.note(), 60);
139 assert_eq!(t.velocity(), 100);
140 assert_eq!(t.tensor_index(), 42);
141 assert_eq!(t.flags(), 0x0053);
142 assert_eq!(std::mem::size_of::<SonicToken>(), 8);
143 }
144
145 #[test]
146 fn pitch_from_tensor_in_range() {
147 let n = SonicToken::pitch_from_tensor(2.0, 0.5, 3.7);
148 assert!(n <= 127);
149 }
150}