1use std::sync::atomic::{AtomicU32, Ordering};
2
3pub const MAGIC: u32 = 0x4D41_4F4C;
5
6pub const VERSION: u32 = 1;
8
9pub const MAX_CHANNELS: usize = 32;
11
12pub const NUM_BUSES: usize = 2;
14
15pub const MAX_BLOCK_SIZE: usize = 4096;
17
18pub const RING_CAPACITY: usize = 4096;
20
21pub const HEADER_SIZE: usize = 256;
23pub const CONTROL_SIZE: usize = 256;
24pub const AUDIO_BUFFER_SIZE: usize = MAX_CHANNELS * NUM_BUSES * MAX_BLOCK_SIZE * 4; pub const PARAM_RING_SIZE: usize = RING_CAPACITY * std::mem::size_of::<ParameterEvent>();
26pub const MIDI_RING_SIZE: usize = RING_CAPACITY * std::mem::size_of::<MidiEvent>();
27pub const TRANSPORT_SIZE: usize = 256;
28pub const SCRATCH_SIZE: usize = 65536;
29
30pub const CONTROL_OFFSET: usize = HEADER_SIZE;
33pub const AUDIO_OFFSET: usize = HEADER_SIZE + CONTROL_SIZE;
35pub const PARAM_RING_OFFSET: usize = AUDIO_OFFSET + AUDIO_BUFFER_SIZE;
37pub const MIDI_RING_OFFSET: usize = PARAM_RING_OFFSET + PARAM_RING_SIZE;
39pub const ECHO_RING_OFFSET: usize = MIDI_RING_OFFSET + MIDI_RING_SIZE;
40pub const ECHO_RING_SIZE: usize = RING_CAPACITY * std::mem::size_of::<ParameterEvent>();
41pub const TRANSPORT_OFFSET: usize = {
43 let end = ECHO_RING_OFFSET + ECHO_RING_SIZE;
44 (end + 255) & !255
46};
47pub const SCRATCH_OFFSET: usize = TRANSPORT_OFFSET + TRANSPORT_SIZE;
49
50pub const LAYOUT_SIZE: usize = SCRATCH_OFFSET + SCRATCH_SIZE;
52
53pub const SHM_SIZE: usize = 4 * 1024 * 1024;
55
56pub const PARAM_WRITE_IDX_OFFSET: usize = CONTROL_OFFSET;
58pub const PARAM_READ_IDX_OFFSET: usize = CONTROL_OFFSET + 4;
59pub const MIDI_WRITE_IDX_OFFSET: usize = CONTROL_OFFSET + 8;
60pub const MIDI_READ_IDX_OFFSET: usize = CONTROL_OFFSET + 12;
61pub const ECHO_WRITE_IDX_OFFSET: usize = CONTROL_OFFSET + 16;
62pub const ECHO_READ_IDX_OFFSET: usize = CONTROL_OFFSET + 20;
63
64pub const PARAM_EVENT_VALUE: u32 = 0;
67pub const PARAM_EVENT_MOD: u32 = 1;
68pub const PARAM_EVENT_GESTURE_BEGIN: u32 = 2;
69pub const PARAM_EVENT_GESTURE_END: u32 = 3;
70
71#[repr(C, align(16))]
73#[derive(Clone, Copy, Debug, Default)]
74pub struct ParameterEvent {
75 pub param_index: u32,
76 pub value: f32,
77 pub sample_offset: u32,
78 pub event_kind: u32,
79}
80
81#[repr(C, align(16))]
83#[derive(Clone, Copy, Debug, Default)]
84pub struct MidiEvent {
85 pub sample_offset: u32,
86 pub data: [u8; 3],
87 pub channel: u8,
88 pub flags: u16,
89 pub _pad: u16,
90}
91
92#[repr(C, align(256))]
94#[derive(Clone, Copy, Debug)]
95pub struct TransportState {
96 pub playhead_sample: u64,
97 pub tempo: f64,
98 pub numerator: u32,
99 pub denominator: u32,
100 pub flags: u32,
101 pub sample_rate_hz: f64,
102 _pad: [u8; 256 - 40],
103}
104
105impl Default for TransportState {
106 fn default() -> Self {
107 Self {
108 playhead_sample: 0,
109 tempo: 120.0,
110 numerator: 4,
111 denominator: 4,
112 flags: 0,
113 sample_rate_hz: 0.0,
114 _pad: [0; 256 - 40],
115 }
116 }
117}
118
119#[repr(C, align(256))]
121pub struct ShmHeader {
122 pub magic: u32,
123 pub version: u32,
124 pub flags: u32,
125 pub ready: AtomicU32,
126 pub heartbeat: AtomicU32,
127 pub error_code: u32,
128 pub shutdown_request: AtomicU32,
129 pub tasks_issued: AtomicU32,
130 pub tasks_completed: AtomicU32,
131 pub block_size: AtomicU32,
132 pub num_input_channels: AtomicU32,
133 pub num_output_channels: AtomicU32,
134 pub request_type: AtomicU32,
136 pub request_status: AtomicU32,
138 pub scratch_size: AtomicU32,
140 pub parent_window: AtomicU32,
142 _pad: [u8; 256 - 64],
143}
144
145impl Default for ShmHeader {
146 fn default() -> Self {
147 Self {
148 magic: MAGIC,
149 version: VERSION,
150 flags: 0,
151 ready: AtomicU32::new(0),
152 heartbeat: AtomicU32::new(0),
153 error_code: 0,
154 shutdown_request: AtomicU32::new(0),
155 tasks_issued: AtomicU32::new(0),
156 tasks_completed: AtomicU32::new(0),
157 block_size: AtomicU32::new(0),
158 num_input_channels: AtomicU32::new(0),
159 num_output_channels: AtomicU32::new(0),
160 request_type: AtomicU32::new(0),
161 request_status: AtomicU32::new(0),
162 scratch_size: AtomicU32::new(0),
163 parent_window: AtomicU32::new(0),
164 _pad: [0; 256 - 64],
165 }
166 }
167}
168
169pub unsafe fn init_shm_layout(ptr: *mut u8, size: usize) {
176 unsafe {
177 std::ptr::write_bytes(ptr, 0, size);
178 let header = ptr as *mut ShmHeader;
179 std::ptr::write(header, ShmHeader::default());
180 }
181}
182
183pub unsafe fn header_ref(ptr: *mut u8) -> &'static ShmHeader {
188 unsafe { &*(ptr as *mut ShmHeader) }
189}
190
191pub unsafe fn header_mut(ptr: *mut u8) -> &'static mut ShmHeader {
196 unsafe { &mut *(ptr as *mut ShmHeader) }
197}
198
199pub unsafe fn audio_ptr(ptr: *mut u8) -> *mut f32 {
204 unsafe { ptr.add(AUDIO_OFFSET) as *mut f32 }
205}
206
207pub unsafe fn audio_channel_ptr(ptr: *mut u8, channel: usize, bus: usize) -> *mut f32 {
215 let plane_size = MAX_BLOCK_SIZE * std::mem::size_of::<f32>();
216 let offset = AUDIO_OFFSET + (channel * NUM_BUSES + bus) * plane_size;
217 unsafe { ptr.add(offset) as *mut f32 }
218}
219
220pub unsafe fn param_ring_ptr(ptr: *mut u8) -> *mut ParameterEvent {
225 unsafe { ptr.add(PARAM_RING_OFFSET) as *mut ParameterEvent }
226}
227
228pub unsafe fn param_indices(ptr: *mut u8) -> (*mut AtomicU32, *mut AtomicU32) {
233 unsafe {
234 (
235 ptr.add(PARAM_WRITE_IDX_OFFSET) as *mut AtomicU32,
236 ptr.add(PARAM_READ_IDX_OFFSET) as *mut AtomicU32,
237 )
238 }
239}
240
241pub unsafe fn midi_ring_ptr(ptr: *mut u8) -> *mut MidiEvent {
246 unsafe { ptr.add(MIDI_RING_OFFSET) as *mut MidiEvent }
247}
248
249pub unsafe fn midi_indices(ptr: *mut u8) -> (*mut AtomicU32, *mut AtomicU32) {
254 unsafe {
255 (
256 ptr.add(MIDI_WRITE_IDX_OFFSET) as *mut AtomicU32,
257 ptr.add(MIDI_READ_IDX_OFFSET) as *mut AtomicU32,
258 )
259 }
260}
261
262pub unsafe fn echo_ring_ptr(ptr: *mut u8) -> *mut ParameterEvent {
267 unsafe { ptr.add(ECHO_RING_OFFSET) as *mut ParameterEvent }
268}
269
270pub unsafe fn echo_indices(ptr: *mut u8) -> (*mut AtomicU32, *mut AtomicU32) {
275 unsafe {
276 (
277 ptr.add(ECHO_WRITE_IDX_OFFSET) as *mut AtomicU32,
278 ptr.add(ECHO_READ_IDX_OFFSET) as *mut AtomicU32,
279 )
280 }
281}
282
283pub unsafe fn transport_ref(ptr: *mut u8) -> &'static TransportState {
288 unsafe { &*(ptr.add(TRANSPORT_OFFSET) as *mut TransportState) }
289}
290
291pub unsafe fn transport_mut(ptr: *mut u8) -> &'static mut TransportState {
296 unsafe { &mut *(ptr.add(TRANSPORT_OFFSET) as *mut TransportState) }
297}
298
299pub unsafe fn scratch_ptr(ptr: *mut u8) -> *mut u8 {
304 unsafe { ptr.add(SCRATCH_OFFSET) }
305}
306
307const _: () = assert!(std::mem::size_of::<ShmHeader>() == 256);
310const _: () = assert!(std::mem::align_of::<ShmHeader>() == 256);
311const _: () = assert!(std::mem::size_of::<ParameterEvent>() == 16);
312const _: () = assert!(std::mem::align_of::<ParameterEvent>() == 16);
313const _: () = assert!(std::mem::size_of::<MidiEvent>() == 16);
314const _: () = assert!(std::mem::align_of::<MidiEvent>() == 16);
315const _: () = assert!(std::mem::size_of::<TransportState>() == 256);
316const _: () = assert!(std::mem::align_of::<TransportState>() == 256);
317const _: () = assert!(LAYOUT_SIZE <= SHM_SIZE);
318
319pub fn wait_for_ready(header: &ShmHeader, timeout: std::time::Duration) -> bool {
321 let start = std::time::Instant::now();
322 while header.ready.load(Ordering::Acquire) == 0 {
323 if start.elapsed() >= timeout {
324 return false;
325 }
326 std::thread::yield_now();
327 }
328 true
329}