1#![forbid(unsafe_code)]
2
3use std::array;
43use std::fmt;
44use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
45use std::sync::{Arc, Condvar, Mutex};
46use std::time::{Duration, Instant};
47
48mod block;
49pub use block::{BlockError, BlockLane, Exchange};
50pub mod events;
51#[cfg(test)]
52mod serial_tests;
53use events::{CONTEXT_WORDS, EVENT_WORDS, METADATA_WORDS, PACKET_WORDS};
54pub use events::{Event, EventBuffer, EventData, MAX_EVENTS, MAX_SYSEX_BYTES, Transport};
55#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
56#[repr(u32)]
57pub enum SampleFormat {
58 #[default]
59 F32 = 0,
60 F64 = 1,
61}
62impl SampleFormat {
63 pub fn words(self) -> usize {
64 if self == Self::F64 { 2 } else { 1 }
65 }
66}
67
68pub const MAGIC: u32 = u32::from_le_bytes(*b"SSRR");
69pub const LAYOUT_VERSION: u32 = 4;
70pub const HEADER_BYTES: usize = 512;
71pub const CACHE_LINE: usize = 64;
72pub const MAX_CHANNELS: u32 = 64;
73pub const MAX_FRAMES: u32 = 65_536;
74pub const MAX_SLOTS: u32 = 16;
75pub const MAX_REGION_BYTES: usize = 128 * 1024 * 1024;
76pub const COUNTER_BYTES: usize = 2 * CACHE_LINE;
79pub const COUNTER_WORDS: usize = COUNTER_BYTES / 8;
81pub const MIN_REGION_BYTES: usize = HEADER_BYTES + COUNTER_BYTES;
83const HEADER_WORDS: usize = HEADER_BYTES / 4;
84
85const MAGIC_I: usize = 0;
86const VERSION_I: usize = 1;
87const TOTAL_I: usize = 2;
88const LANES_I: usize = 3;
89const INPUTS_I: usize = 4;
90const OUTPUTS_I: usize = 5;
91const FRAMES_I: usize = 6;
92const RATE_I: usize = 7;
93const LATENCY_I: usize = 8;
94pub const LATENCY_BOUND_OFFSET: usize = LATENCY_I * 4;
97const STATE_I: usize = 9;
98const FAULT_I: usize = 10;
99const PRODUCER_PID_I: usize = 11;
100const CONSUMER_PID_I: usize = 12;
101const FORMAT_I: usize = 13;
102const SLOTS_I: usize = 14;
103const LANE_GEOMETRY_I: usize = 16;
104const SEQUENCE_I: usize = 32;
105const COMPLETED_I: usize = 48;
106
107#[derive(Clone, Copy, Debug, Eq, PartialEq)]
108#[repr(u32)]
109pub enum State {
110 Empty = 0,
111 Announced = 1,
112 Ready = 2,
113 Closing = 3,
114 Fault = 4,
115}
116
117impl State {
118 fn from_wire(value: u32) -> Option<Self> {
119 Some(match value {
120 0 => Self::Empty,
121 1 => Self::Announced,
122 2 => Self::Ready,
123 3 => Self::Closing,
124 4 => Self::Fault,
125 _ => return None,
126 })
127 }
128}
129
130#[derive(Clone, Copy, Debug, Eq, PartialEq)]
131#[repr(u32)]
132pub enum Fault {
133 None = 0,
134 LayoutMismatch = 1,
135 EngineFailed = 2,
136 HostShuttingDown = 3,
137}
138
139#[derive(Clone, Copy, Debug, Eq, PartialEq)]
140pub struct Geometry {
141 pub sample_format: SampleFormat,
142 pub lanes: u32,
143 pub slots: u32,
144 pub input_channels: u32,
145 pub output_channels: u32,
146 pub max_frames: u32,
147 pub sample_rate: u32,
148 pub latency_bound_frames: u32,
149}
150
151impl Geometry {
152 pub fn validate(self) -> Result<Self, GeometryError> {
153 if !(1..=2).contains(&self.lanes) {
154 return Err(GeometryError::LaneCount);
155 }
156 if !matches!(self.slots, 2 | 4 | 8 | 16) {
157 return Err(GeometryError::Slots);
158 }
159 if self.input_channels > MAX_CHANNELS || self.output_channels > MAX_CHANNELS {
160 return Err(GeometryError::Channels);
161 }
162 if !(1..=MAX_FRAMES).contains(&self.max_frames) {
163 return Err(GeometryError::Frames);
164 }
165 if !(1_000..=1_000_000).contains(&self.sample_rate) {
166 return Err(GeometryError::SampleRate);
167 }
168 if layout(self).is_none() {
169 return Err(GeometryError::RegionTooLarge);
170 }
171 Ok(self)
172 }
173
174 pub fn planes(self) -> usize {
175 self.input_channels.max(self.output_channels) as usize
176 }
177
178 pub fn samples_per_slot(self) -> usize {
179 self.planes() * self.max_frames as usize
180 }
181
182 pub fn total_bytes(self) -> Result<usize, GeometryError> {
183 self.validate()?;
184 Ok(layout(self)
185 .expect("validated geometry has a layout")
186 .total_bytes)
187 }
188
189 pub fn mapping_split(self, mapping_bytes: usize) -> Result<usize, GeometryError> {
194 let total = self.total_bytes()?;
195 if total > mapping_bytes {
196 return Err(GeometryError::RegionTooLarge);
197 }
198 Ok(total - COUNTER_BYTES)
199 }
200}
201
202#[derive(Clone, Copy, Debug, Eq, PartialEq)]
203pub enum GeometryError {
204 LaneCount,
205 Slots,
206 Channels,
207 Frames,
208 SampleRate,
209 RegionTooLarge,
210}
211
212#[derive(Clone, Copy, Debug)]
213struct Layout {
214 slot_bytes: usize,
215 first_slot_offsets: [usize; 2],
216 slots: u32,
217 prefix_bytes: usize,
218 total_bytes: usize,
219}
220
221impl Layout {
222 fn slot_offset(self, lane: usize, sequence: u32) -> usize {
223 self.first_slot_offsets[lane] + (sequence & (self.slots - 1)) as usize * self.slot_bytes
224 }
225}
226
227fn round_up(value: usize, alignment: usize) -> Option<usize> {
228 value
229 .checked_add(alignment - 1)
230 .map(|v| v / alignment * alignment)
231}
232
233fn layout(geometry: Geometry) -> Option<Layout> {
234 let audio_words = geometry
235 .samples_per_slot()
236 .checked_mul(geometry.sample_format.words())?;
237 let slot_bytes = round_up(
238 audio_words.checked_add(METADATA_WORDS)?.checked_mul(4)?,
239 CACHE_LINE,
240 )?;
241 let mut offset = HEADER_BYTES;
242 let mut first_slot_offsets = [0; 2];
243 for first_slot_offset in first_slot_offsets.iter_mut().take(geometry.lanes as usize) {
244 *first_slot_offset = offset;
245 offset = offset.checked_add(slot_bytes.checked_mul(geometry.slots as usize)?)?;
246 }
247 let prefix_bytes = round_up(offset, CACHE_LINE)?;
248 let total_bytes = prefix_bytes.checked_add(COUNTER_BYTES)?;
249 (total_bytes < MAX_REGION_BYTES).then_some(Layout {
250 slot_bytes,
251 first_slot_offsets,
252 slots: geometry.slots,
253 prefix_bytes,
254 total_bytes,
255 })
256}
257
258pub trait Signal: Send + Sync {
259 fn signal(&self);
260}
261
262impl<T: Signal + ?Sized> Signal for Arc<T> {
263 fn signal(&self) {
264 T::signal(self);
265 }
266}
267
268impl<T: Signal + ?Sized> Signal for Box<T> {
269 fn signal(&self) {
270 T::signal(self);
271 }
272}
273
274pub trait Wait: Send + Sync {
275 fn wait(&self, timeout: Duration) -> Waited;
276}
277
278#[derive(Clone, Copy, Debug, Eq, PartialEq)]
279pub enum Waited {
280 Signalled,
281 TimedOut,
282 PeerGone,
283}
284
285#[derive(Debug)]
286struct EventInner {
287 generation: AtomicU64,
288 gone: AtomicBool,
289 observed: Mutex<u64>,
290 changed: Condvar,
291}
292
293#[derive(Clone, Debug)]
297pub struct InProcessEvent(Arc<EventInner>);
298
299impl InProcessEvent {
300 pub fn new() -> Self {
301 Self(Arc::new(EventInner {
302 generation: AtomicU64::new(0),
303 gone: AtomicBool::new(false),
304 observed: Mutex::new(0),
305 changed: Condvar::new(),
306 }))
307 }
308
309 pub fn peer_gone(&self) {
310 self.0.gone.store(true, Ordering::Release);
311 self.0.changed.notify_all();
312 }
313}
314
315impl Default for InProcessEvent {
316 fn default() -> Self {
317 Self::new()
318 }
319}
320
321impl Signal for InProcessEvent {
322 fn signal(&self) {
323 self.0.generation.fetch_add(1, Ordering::Release);
324 self.0.changed.notify_one();
325 }
326}
327
328impl Wait for InProcessEvent {
329 fn wait(&self, timeout: Duration) -> Waited {
330 if self.0.gone.load(Ordering::Acquire) {
331 return Waited::PeerGone;
332 }
333 let mut observed = self.0.observed.lock().unwrap_or_else(|e| e.into_inner());
334 let current = self.0.generation.load(Ordering::Acquire);
335 if current != *observed {
336 *observed = current;
337 return Waited::Signalled;
338 }
339 let (mut observed, result) = self
340 .0
341 .changed
342 .wait_timeout_while(observed, timeout, |seen| {
343 !self.0.gone.load(Ordering::Acquire)
344 && self.0.generation.load(Ordering::Acquire) == *seen
345 })
346 .unwrap_or_else(|e| e.into_inner());
347 if self.0.gone.load(Ordering::Acquire) {
348 Waited::PeerGone
349 } else {
350 let current = self.0.generation.load(Ordering::Acquire);
351 if current != *observed {
352 *observed = current;
353 Waited::Signalled
354 } else if result.timed_out() {
355 Waited::TimedOut
356 } else {
357 Waited::Signalled
358 }
359 }
360 }
361}
362
363#[derive(Clone, Copy, Debug)]
366pub struct Region<'a> {
367 words32: &'a [AtomicU32],
368 words64: &'a [AtomicU64],
369}
370
371impl<'a> Region<'a> {
372 pub fn new(words32: &'a [AtomicU32], words64: &'a [AtomicU64]) -> Self {
373 Self { words32, words64 }
374 }
375
376 pub fn state(self) -> Option<State> {
377 State::from_wire(self.load32(STATE_I, Ordering::Acquire))
378 }
379
380 pub fn fault_code(self) -> u32 {
381 self.load32(FAULT_I, Ordering::Acquire)
382 }
383
384 pub fn heartbeat(self) -> u64 {
385 self.words64
386 .first()
387 .map_or(0, |v| v.load(Ordering::Acquire))
388 }
389
390 pub fn last_error(self) -> u64 {
391 self.words64
392 .get(CACHE_LINE / 8)
393 .map_or(0, |v| v.load(Ordering::Acquire))
394 }
395
396 fn load32(self, index: usize, ordering: Ordering) -> u32 {
397 self.words32.get(index).map_or(0, |v| v.load(ordering))
398 }
399
400 fn store32(self, index: usize, value: u32, ordering: Ordering) {
401 if let Some(word) = self.words32.get(index) {
402 word.store(value, ordering);
403 }
404 }
405}
406
407#[derive(Debug)]
408pub struct RegionBuf {
409 words32: Box<[AtomicU32]>,
410 words64: Box<[AtomicU64]>,
411}
412
413impl RegionBuf {
414 pub fn new(geometry: Geometry) -> Result<Self, GeometryError> {
415 let geometry = geometry.validate()?;
416 let layout = layout(geometry).expect("validated geometry has a layout");
417 Ok(Self {
418 words32: (0..layout.prefix_bytes / 4)
419 .map(|_| AtomicU32::new(0))
420 .collect(),
421 words64: (0..COUNTER_WORDS).map(|_| AtomicU64::new(0)).collect(),
422 })
423 }
424
425 pub fn region(&self) -> Region<'_> {
426 Region::new(&self.words32, &self.words64)
427 }
428}
429
430pub trait RegionMemory: Send + Sync {
435 fn region(&self) -> Region<'_>;
436}
437
438impl RegionMemory for RegionBuf {
439 fn region(&self) -> Region<'_> {
440 self.region()
441 }
442}
443
444impl<T: RegionMemory + ?Sized> RegionMemory for Arc<T> {
445 fn region(&self) -> Region<'_> {
446 T::region(self)
447 }
448}
449
450impl<T: RegionMemory + ?Sized> RegionMemory for Box<T> {
451 fn region(&self) -> Region<'_> {
452 T::region(self)
453 }
454}
455
456#[derive(Clone, Copy, Debug, Eq, PartialEq)]
457pub enum HeaderError {
458 TooShort,
459 Magic,
460 Version,
461 State,
462 Geometry(GeometryError),
463 TotalBytes,
464 LaneGeometry,
465}
466
467#[derive(Clone, Copy, Debug, Eq, PartialEq)]
468pub enum Publish {
469 Published,
470 Dropped,
471}
472
473#[derive(Clone, Copy, Debug)]
478pub struct Publisher {
479 geometry: Geometry,
480 layout: Layout,
481}
482
483impl Publisher {
484 pub fn geometry(&self) -> Geometry {
485 self.geometry
486 }
487
488 pub fn state(&self, region: Region<'_>) -> Option<State> {
489 region.state()
490 }
491
492 pub fn published(&self, region: Region<'_>, lane: usize) -> u32 {
495 if lane >= self.geometry.lanes as usize {
496 return 0;
497 }
498 u32::from_le(region.load32(SEQUENCE_I + lane, Ordering::Acquire))
499 }
500
501 pub fn completed(&self, region: Region<'_>, lane: usize) -> u32 {
504 if lane >= self.geometry.lanes as usize {
505 return 0;
506 }
507 u32::from_le(region.load32(COMPLETED_I + lane, Ordering::Acquire))
508 }
509
510 pub fn try_publish<F>(
515 &self,
516 region: Region<'_>,
517 lane: usize,
518 sequence: u32,
519 work: Option<&dyn Signal>,
520 write: F,
521 ) -> Publish
522 where
523 F: FnOnce(&mut SlotWriter<'_>),
524 {
525 if region.state() != Some(State::Ready) || lane >= self.geometry.lanes as usize {
526 return Publish::Dropped;
527 }
528 let completed = self.completed(region, lane);
529 if !sequence_at_least(completed, sequence.wrapping_sub(self.geometry.slots)) {
530 return Publish::Dropped;
531 }
532 let offset = self.layout.slot_offset(lane, sequence) / 4;
533 let Some(samples) = region.words32.get(
534 offset..offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words(),
535 ) else {
536 return Publish::Dropped;
537 };
538 let meta_offset =
539 offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words();
540 let metadata = ®ion.words32[meta_offset..meta_offset + METADATA_WORDS];
541 let mut writer = SlotWriter {
542 samples,
543 metadata,
544 format: self.geometry.sample_format,
545 frames: self.geometry.max_frames as usize,
546 planes: self.geometry.planes(),
547 };
548 writer.set_transport(Transport::default());
549 writer.set_events(&[]);
550 metadata[CONTEXT_WORDS + PACKET_WORDS].store(0, Ordering::Relaxed);
551 metadata[CONTEXT_WORDS + PACKET_WORDS + 1].store(0, Ordering::Relaxed);
552 write(&mut writer);
553 region.store32(SEQUENCE_I + lane, sequence.to_le(), Ordering::Release);
554 if let Some(signal) = work {
555 signal.signal();
556 }
557 Publish::Published
558 }
559
560 pub fn try_read<F>(&self, region: Region<'_>, lane: usize, sequence: u32, read: F) -> bool
562 where
563 F: FnOnce(SlotReader<'_>),
564 {
565 if lane >= self.geometry.lanes as usize
566 || !sequence_at_least(self.completed(region, lane), sequence)
567 {
568 return false;
569 }
570 let published = self.published(region, lane);
571 if published.wrapping_sub(sequence) >= self.geometry.slots {
572 return false;
573 }
574 let offset = self.layout.slot_offset(lane, sequence) / 4;
575 let Some(samples) = region.words32.get(
576 offset..offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words(),
577 ) else {
578 return false;
579 };
580 let meta_offset =
581 offset + self.geometry.samples_per_slot() * self.geometry.sample_format.words();
582 read(SlotReader {
583 samples,
584 metadata: ®ion.words32[meta_offset..meta_offset + METADATA_WORDS],
585 format: self.geometry.sample_format,
586 frames: self.geometry.max_frames as usize,
587 planes: self.geometry.planes(),
588 });
589 true
590 }
591}
592
593pub struct Producer<'a> {
594 region: Region<'a>,
595 geometry: Option<Geometry>,
596 layout: Option<Layout>,
597 work: [Option<&'a dyn Signal>; 2],
598 ready: &'a dyn Wait,
599}
600
601impl fmt::Debug for Producer<'_> {
602 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
603 formatter
604 .debug_struct("Producer")
605 .field("region", &self.region)
606 .field("geometry", &self.geometry)
607 .field("layout", &self.layout)
608 .finish_non_exhaustive()
609 }
610}
611
612impl<'a> Producer<'a> {
613 pub fn new(region: Region<'a>, work: [Option<&'a dyn Signal>; 2], ready: &'a dyn Wait) -> Self {
614 Self {
615 region,
616 geometry: None,
617 layout: None,
618 work,
619 ready,
620 }
621 }
622
623 pub fn announce(&mut self, geometry: Geometry, producer_pid: u32) -> Result<(), GeometryError> {
624 self.announce_at(geometry, producer_pid, 0)
625 }
626
627 pub fn announce_at(
629 &mut self,
630 geometry: Geometry,
631 producer_pid: u32,
632 origin: u32,
633 ) -> Result<(), GeometryError> {
634 let geometry = geometry.validate()?;
635 let layout = layout(geometry).expect("validated geometry has a layout");
636 if self.region.words32.len() * 4 < layout.prefix_bytes
637 || self.region.words64.len() < COUNTER_WORDS
638 {
639 return Err(GeometryError::RegionTooLarge);
640 }
641 for word in self.region.words32.iter().take(HEADER_WORDS) {
642 word.store(0, Ordering::Relaxed);
643 }
644 for word in self.region.words64 {
645 word.store(0, Ordering::Relaxed);
646 }
647 let fields = [
648 (MAGIC_I, MAGIC),
649 (VERSION_I, LAYOUT_VERSION),
650 (TOTAL_I, layout.total_bytes as u32),
651 (LANES_I, geometry.lanes),
652 (INPUTS_I, geometry.input_channels),
653 (OUTPUTS_I, geometry.output_channels),
654 (FRAMES_I, geometry.max_frames),
655 (RATE_I, geometry.sample_rate),
656 (FORMAT_I, geometry.sample_format as u32),
657 (SLOTS_I, geometry.slots),
658 (LATENCY_I, geometry.latency_bound_frames),
659 (PRODUCER_PID_I, producer_pid),
660 ];
661 for (index, value) in fields {
662 self.region.store32(index, value.to_le(), Ordering::Relaxed);
663 }
664 for lane in 0..geometry.lanes as usize {
665 self.region
666 .store32(SEQUENCE_I + lane, origin.to_le(), Ordering::Relaxed);
667 self.region
668 .store32(COMPLETED_I + lane, origin.to_le(), Ordering::Relaxed);
669 let base = LANE_GEOMETRY_I + lane * 3;
670 self.region.store32(
671 base,
672 (layout.first_slot_offsets[lane] as u32).to_le(),
673 Ordering::Relaxed,
674 );
675 self.region.store32(
676 base + 1,
677 (layout.slot_bytes as u32).to_le(),
678 Ordering::Relaxed,
679 );
680 self.region
681 .store32(base + 2, geometry.slots.to_le(), Ordering::Relaxed);
682 }
683 self.region
684 .store32(FAULT_I, Fault::None as u32, Ordering::Release);
685 self.region
686 .store32(STATE_I, State::Announced as u32, Ordering::Release);
687 self.geometry = Some(geometry);
688 self.layout = Some(layout);
689 Ok(())
690 }
691
692 pub fn wait_ready(&self, timeout: Duration) -> Waited {
693 let deadline = Instant::now() + timeout;
694 loop {
695 match self.region.state() {
696 Some(State::Ready) => return Waited::Signalled,
697 Some(State::Fault | State::Closing) | None => return Waited::PeerGone,
698 _ => {}
699 }
700 let now = Instant::now();
701 if now >= deadline {
702 return Waited::TimedOut;
703 }
704 match self.ready.wait(deadline.saturating_duration_since(now)) {
705 Waited::Signalled => {}
706 other => return other,
707 }
708 }
709 }
710
711 pub fn publisher(&self) -> Option<Publisher> {
713 Some(Publisher {
714 geometry: self.geometry?,
715 layout: self.layout?,
716 })
717 }
718
719 pub fn completed(&self, lane: usize) -> u32 {
720 self.publisher()
721 .map_or(0, |publisher| publisher.completed(self.region, lane))
722 }
723
724 pub fn try_publish<F>(&self, lane: usize, sequence: u32, write: F) -> Publish
725 where
726 F: FnOnce(&mut SlotWriter<'_>),
727 {
728 let Some(publisher) = self.publisher() else {
729 return Publish::Dropped;
730 };
731 publisher.try_publish(self.region, lane, sequence, self.work[lane], write)
732 }
733
734 pub fn try_read<F>(&self, lane: usize, sequence: u32, read: F) -> bool
735 where
736 F: FnOnce(SlotReader<'_>),
737 {
738 self.publisher()
739 .is_some_and(|publisher| publisher.try_read(self.region, lane, sequence, read))
740 }
741
742 pub fn close(&self) {
743 self.region
744 .store32(STATE_I, State::Closing as u32, Ordering::Release);
745 for signal in self.work.iter().flatten() {
746 signal.signal();
747 }
748 }
749}
750
751#[derive(Debug)]
752pub struct SlotWriter<'a> {
753 metadata: &'a [AtomicU32],
754 format: SampleFormat,
755 samples: &'a [AtomicU32],
756 frames: usize,
757 planes: usize,
758}
759
760impl SlotWriter<'_> {
761 pub fn frames(&self) -> usize {
762 self.frames
763 }
764
765 pub fn planes(&self) -> usize {
766 self.planes
767 }
768
769 pub fn write(&mut self, plane: usize, frame: usize, sample: f32) {
770 self.write_f64(plane, frame, f64::from(sample));
771 }
772
773 pub fn write_f64(&mut self, plane: usize, frame: usize, sample: f64) {
774 let index = (plane * self.frames + frame) * self.format.words();
775 if self.format == SampleFormat::F64 {
776 self.samples[index].store((sample.to_bits() as u32).to_le(), Ordering::Relaxed);
777 self.samples[index + 1]
778 .store(((sample.to_bits() >> 32) as u32).to_le(), Ordering::Relaxed);
779 } else {
780 self.samples[index].store((sample as f32).to_bits().to_le(), Ordering::Relaxed);
781 }
782 }
783
784 pub fn write_plane(&mut self, plane: usize, samples: &[f32]) -> bool {
785 if plane >= self.planes || samples.len() != self.frames {
786 return false;
787 }
788 let words = self.format.words();
789 let destination =
790 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
791 if self.format == SampleFormat::F64 {
792 for (to, from) in destination.as_chunks::<2>().0.iter().zip(samples) {
793 let bits = f64::from(*from).to_bits();
794 to[0].store((bits as u32).to_le(), Ordering::Relaxed);
795 to[1].store(((bits >> 32) as u32).to_le(), Ordering::Relaxed);
796 }
797 } else {
798 for (to, from) in destination.iter().zip(samples) {
799 to.store(from.to_bits().to_le(), Ordering::Relaxed);
800 }
801 }
802 true
803 }
804
805 pub fn write_plane_f64(&mut self, plane: usize, samples: &[f64]) -> bool {
806 if plane >= self.planes || samples.len() != self.frames {
807 return false;
808 }
809 let words = self.format.words();
810 let destination =
811 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
812 if self.format == SampleFormat::F64 {
813 for (to, from) in destination.as_chunks::<2>().0.iter().zip(samples) {
814 let bits = from.to_bits();
815 to[0].store((bits as u32).to_le(), Ordering::Relaxed);
816 to[1].store(((bits >> 32) as u32).to_le(), Ordering::Relaxed);
817 }
818 } else {
819 for (to, from) in destination.iter().zip(samples) {
820 to.store((*from as f32).to_bits().to_le(), Ordering::Relaxed);
821 }
822 }
823 true
824 }
825
826 pub fn clear_plane(&mut self, plane: usize) -> bool {
827 if plane >= self.planes {
828 return false;
829 }
830 let words = self.format.words();
831 let destination =
832 &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
833 for word in destination {
834 word.store(0, Ordering::Relaxed);
835 }
836 true
837 }
838
839 pub fn set_transport(&mut self, transport: Transport) {
840 for (to, from) in self.metadata.iter().zip(transport.words()) {
841 to.store(from.to_le(), Ordering::Relaxed);
842 }
843 }
844 pub fn set_events(&mut self, events: &[Event]) -> bool {
845 if events.len() > MAX_EVENTS || events.iter().any(|e| !e.valid(self.frames as u32)) {
846 return false;
847 }
848 store_packet(
849 &self.metadata[CONTEXT_WORDS..CONTEXT_WORDS + PACKET_WORDS],
850 events,
851 0,
852 );
853 true
854 }
855 pub fn copy_from(&mut self, samples: &[f32]) -> bool {
856 if self.format != SampleFormat::F32 || samples.len() != self.samples.len() {
857 return false;
858 }
859 for (destination, sample) in self.samples.iter().zip(samples) {
860 destination.store(sample.to_bits(), Ordering::Relaxed);
861 }
862 true
863 }
864}
865
866#[derive(Clone, Copy, Debug)]
867pub struct SlotReader<'a> {
868 metadata: &'a [AtomicU32],
869 format: SampleFormat,
870 samples: &'a [AtomicU32],
871 frames: usize,
872 planes: usize,
873}
874
875impl SlotReader<'_> {
876 pub fn frames(self) -> usize {
877 self.frames
878 }
879
880 pub fn planes(self) -> usize {
881 self.planes
882 }
883
884 pub fn read(self, plane: usize, frame: usize) -> f32 {
885 self.read_f64(plane, frame) as f32
886 }
887
888 pub fn read_f64(self, plane: usize, frame: usize) -> f64 {
889 let index = (plane * self.frames + frame) * self.format.words();
890 let lo = u32::from_le(self.samples[index].load(Ordering::Relaxed));
891 if self.format == SampleFormat::F64 {
892 let hi = u32::from_le(self.samples[index + 1].load(Ordering::Relaxed));
893 f64::from_bits(u64::from(lo) | (u64::from(hi) << 32))
894 } else {
895 f64::from(f32::from_bits(lo))
896 }
897 }
898
899 pub fn read_plane(self, plane: usize, samples: &mut [f32]) -> bool {
900 if plane >= self.planes || samples.len() != self.frames {
901 return false;
902 }
903 let words = self.format.words();
904 let source = &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
905 if self.format == SampleFormat::F64 {
906 for (to, from) in samples.iter_mut().zip(source.as_chunks::<2>().0.iter()) {
907 let lo = u32::from_le(from[0].load(Ordering::Relaxed));
908 let hi = u32::from_le(from[1].load(Ordering::Relaxed));
909 *to = f64::from_bits(u64::from(lo) | (u64::from(hi) << 32)) as f32;
910 }
911 } else {
912 for (to, from) in samples.iter_mut().zip(source) {
913 *to = f32::from_bits(u32::from_le(from.load(Ordering::Relaxed)));
914 }
915 }
916 true
917 }
918
919 pub fn read_plane_f64(self, plane: usize, samples: &mut [f64]) -> bool {
920 if plane >= self.planes || samples.len() != self.frames {
921 return false;
922 }
923 let words = self.format.words();
924 let source = &self.samples[plane * self.frames * words..(plane + 1) * self.frames * words];
925 if self.format == SampleFormat::F64 {
926 for (to, from) in samples.iter_mut().zip(source.as_chunks::<2>().0.iter()) {
927 let lo = u32::from_le(from[0].load(Ordering::Relaxed));
928 let hi = u32::from_le(from[1].load(Ordering::Relaxed));
929 *to = f64::from_bits(u64::from(lo) | (u64::from(hi) << 32));
930 }
931 } else {
932 for (to, from) in samples.iter_mut().zip(source) {
933 *to = f64::from(f32::from_bits(u32::from_le(from.load(Ordering::Relaxed))));
934 }
935 }
936 true
937 }
938
939 pub fn output_events(self, events: &mut EventBuffer) -> bool {
940 load_packet(
941 &self.metadata[CONTEXT_WORDS + PACKET_WORDS..],
942 self.frames as u32,
943 events,
944 )
945 }
946 pub fn copy_to(self, samples: &mut [f32]) -> bool {
947 if self.format != SampleFormat::F32 || samples.len() != self.samples.len() {
948 return false;
949 }
950 for (destination, sample) in samples.iter_mut().zip(self.samples) {
951 *destination = f32::from_bits(sample.load(Ordering::Relaxed));
952 }
953 true
954 }
955}
956
957fn store_packet(words: &[AtomicU32], events: &[Event], dropped: u32) {
958 words[0].store((events.len() as u32).to_le(), Ordering::Relaxed);
959 words[1].store(dropped.to_le(), Ordering::Relaxed);
960 for (target, event) in words[2..].as_chunks::<EVENT_WORDS>().0.iter().zip(events) {
961 for (to, from) in target.iter().zip(event.words()) {
962 to.store(from.to_le(), Ordering::Relaxed);
963 }
964 }
965}
966fn load_packet(words: &[AtomicU32], frames: u32, events: &mut EventBuffer) -> bool {
967 events.clear();
968 let count = u32::from_le(words[0].load(Ordering::Relaxed)) as usize;
969 if count > MAX_EVENTS {
970 return false;
971 }
972 events.dropped = u32::from_le(words[1].load(Ordering::Relaxed));
973 for source in words[2..].as_chunks::<EVENT_WORDS>().0.iter().take(count) {
974 let mut values = [0; EVENT_WORDS];
975 for (to, from) in values.iter_mut().zip(source) {
976 *to = u32::from_le(from.load(Ordering::Relaxed));
977 }
978 let Some(event) = Event::from_words(&values, frames) else {
979 events.clear();
980 return false;
981 };
982 events.push(event);
983 }
984 true
985}
986
987pub struct Consumer<'a> {
988 region: Region<'a>,
989 geometry: Option<Geometry>,
990 layout: Option<Layout>,
991 ready: &'a dyn Signal,
992 done: [Option<&'a dyn Signal>; 2],
993}
994
995impl fmt::Debug for Consumer<'_> {
996 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
997 formatter
998 .debug_struct("Consumer")
999 .field("region", &self.region)
1000 .field("geometry", &self.geometry)
1001 .field("layout", &self.layout)
1002 .finish_non_exhaustive()
1003 }
1004}
1005
1006impl<'a> Consumer<'a> {
1007 pub fn new(
1008 region: Region<'a>,
1009 ready: &'a dyn Signal,
1010 done: [Option<&'a dyn Signal>; 2],
1011 ) -> Self {
1012 Self {
1013 region,
1014 geometry: None,
1015 layout: None,
1016 ready,
1017 done,
1018 }
1019 }
1020
1021 pub fn validate_and_ready(&mut self, consumer_pid: u32) -> Result<Geometry, HeaderError> {
1022 let geometry = validate_header(self.region)?;
1023 let layout = layout(geometry).expect("validated geometry has a layout");
1024 self.geometry = Some(geometry);
1025 self.layout = Some(layout);
1026 self.region
1027 .store32(CONSUMER_PID_I, consumer_pid.to_le(), Ordering::Relaxed);
1028 self.region
1029 .store32(FAULT_I, Fault::None as u32, Ordering::Relaxed);
1030 self.region
1031 .store32(STATE_I, State::Ready as u32, Ordering::Release);
1032 self.ready.signal();
1033 Ok(geometry)
1034 }
1035
1036 pub fn fault(&self, code: u32) {
1037 self.region.store32(FAULT_I, code, Ordering::Relaxed);
1038 self.region
1039 .store32(STATE_I, State::Fault as u32, Ordering::Release);
1040 self.ready.signal();
1041 for signal in self.done.iter().flatten() {
1042 signal.signal();
1043 }
1044 }
1045
1046 pub fn next_published(&self, lane: usize) -> Option<u32> {
1047 let geometry = self.geometry?;
1048 if lane >= geometry.lanes as usize || self.region.state() != Some(State::Ready) {
1049 return None;
1050 }
1051 let published = u32::from_le(self.region.load32(SEQUENCE_I + lane, Ordering::Acquire));
1052 let completed = u32::from_le(self.region.load32(COMPLETED_I + lane, Ordering::Acquire));
1053 let next = completed.wrapping_add(1);
1054 (published != completed && sequence_at_least(published, next)).then_some(next)
1055 }
1056
1057 pub fn slot_mut<F>(&self, lane: usize, sequence: u32, buffer: &mut [f32], process: F) -> bool
1060 where
1061 F: FnOnce(&mut [f32]),
1062 {
1063 let Some((geometry, layout)) = self.geometry.zip(self.layout) else {
1064 return false;
1065 };
1066 if geometry.sample_format != SampleFormat::F32
1067 || lane >= geometry.lanes as usize
1068 || buffer.len() != geometry.samples_per_slot()
1069 {
1070 return false;
1071 }
1072 let offset = layout.slot_offset(lane, sequence) / 4;
1073 let samples = &self.region.words32[offset..offset + buffer.len()];
1074 for (destination, sample) in buffer.iter_mut().zip(samples) {
1075 *destination = f32::from_bits(sample.load(Ordering::Relaxed));
1076 }
1077 process(buffer);
1078 for (destination, sample) in samples.iter().zip(buffer) {
1079 destination.store(sample.to_bits(), Ordering::Relaxed);
1080 }
1081 true
1082 }
1083
1084 pub fn slot_mut_f64<F>(
1085 &self,
1086 lane: usize,
1087 sequence: u32,
1088 buffer: &mut [f64],
1089 process: F,
1090 ) -> bool
1091 where
1092 F: FnOnce(&mut [f64]),
1093 {
1094 let Some((geometry, layout)) = self.geometry.zip(self.layout) else {
1095 return false;
1096 };
1097 if geometry.sample_format != SampleFormat::F64
1098 || lane >= geometry.lanes as usize
1099 || buffer.len() != geometry.samples_per_slot()
1100 {
1101 return false;
1102 }
1103 let offset = layout.slot_offset(lane, sequence) / 4;
1104 let samples = &self.region.words32[offset..offset + buffer.len() * 2];
1105 for (to, from) in buffer.iter_mut().zip(samples.as_chunks::<2>().0.iter()) {
1106 *to = f64::from_bits(
1107 u64::from(u32::from_le(from[0].load(Ordering::Relaxed)))
1108 | (u64::from(u32::from_le(from[1].load(Ordering::Relaxed))) << 32),
1109 );
1110 }
1111 process(buffer);
1112 for (to, from) in samples.as_chunks::<2>().0.iter().zip(buffer) {
1113 to[0].store((from.to_bits() as u32).to_le(), Ordering::Relaxed);
1114 to[1].store(((from.to_bits() >> 32) as u32).to_le(), Ordering::Relaxed);
1115 }
1116 true
1117 }
1118 fn metadata(&self, lane: usize, sequence: u32) -> Option<&[AtomicU32]> {
1119 let geometry = self.geometry?;
1120 let layout = self.layout?;
1121 if lane >= geometry.lanes as usize {
1122 return None;
1123 }
1124 let offset = layout.slot_offset(lane, sequence) / 4
1125 + geometry.samples_per_slot() * geometry.sample_format.words();
1126 self.region.words32.get(offset..offset + METADATA_WORDS)
1127 }
1128 pub fn input_packet(
1129 &self,
1130 lane: usize,
1131 sequence: u32,
1132 context: &mut Transport,
1133 events: &mut EventBuffer,
1134 ) -> bool {
1135 let Some(words) = self.metadata(lane, sequence) else {
1136 return false;
1137 };
1138 let mut raw = [0; CONTEXT_WORDS];
1139 for (to, from) in raw.iter_mut().zip(words) {
1140 *to = u32::from_le(from.load(Ordering::Relaxed));
1141 }
1142 let Some(value) = Transport::from_words(&raw) else {
1143 return false;
1144 };
1145 *context = value;
1146 load_packet(
1147 &words[CONTEXT_WORDS..CONTEXT_WORDS + PACKET_WORDS],
1148 self.geometry.expect("validated").max_frames,
1149 events,
1150 )
1151 }
1152 pub fn output_packet(&self, lane: usize, sequence: u32, events: &EventBuffer) -> bool {
1153 let Some(words) = self.metadata(lane, sequence) else {
1154 return false;
1155 };
1156 if events
1157 .as_slice()
1158 .iter()
1159 .any(|e| !e.valid(self.geometry.expect("validated").max_frames))
1160 {
1161 return false;
1162 }
1163 store_packet(
1164 &words[CONTEXT_WORDS + PACKET_WORDS..],
1165 events.as_slice(),
1166 events.dropped,
1167 );
1168 true
1169 }
1170
1171 pub fn complete(&self, lane: usize, sequence: u32) {
1172 self.region
1173 .store32(COMPLETED_I + lane, sequence.to_le(), Ordering::Release);
1174 if let Some(signal) = self.done[lane] {
1175 signal.signal();
1176 }
1177 }
1178
1179 pub fn beat(&self) -> u64 {
1180 self.region
1181 .words64
1182 .first()
1183 .map_or(0, |v| v.fetch_add(1, Ordering::Release).wrapping_add(1))
1184 }
1185
1186 pub fn set_last_error(&self, error: u64) {
1187 if let Some(word) = self.region.words64.get(CACHE_LINE / 8) {
1188 word.store(error, Ordering::Release);
1189 }
1190 }
1191}
1192
1193fn validate_header(region: Region<'_>) -> Result<Geometry, HeaderError> {
1194 if region.words32.len() < HEADER_WORDS || region.words64.len() < COUNTER_WORDS {
1195 return Err(HeaderError::TooShort);
1196 }
1197 if u32::from_le(region.load32(MAGIC_I, Ordering::Relaxed)) != MAGIC {
1198 return Err(HeaderError::Magic);
1199 }
1200 if u32::from_le(region.load32(VERSION_I, Ordering::Relaxed)) != LAYOUT_VERSION {
1201 return Err(HeaderError::Version);
1202 }
1203 if region.state() != Some(State::Announced) {
1204 return Err(HeaderError::State);
1205 }
1206 let geometry = Geometry {
1207 sample_format: match u32::from_le(region.load32(FORMAT_I, Ordering::Relaxed)) {
1208 0 => SampleFormat::F32,
1209 1 => SampleFormat::F64,
1210 _ => return Err(HeaderError::Version),
1211 },
1212 lanes: u32::from_le(region.load32(LANES_I, Ordering::Relaxed)),
1213 slots: u32::from_le(region.load32(SLOTS_I, Ordering::Relaxed)),
1214 input_channels: u32::from_le(region.load32(INPUTS_I, Ordering::Relaxed)),
1215 output_channels: u32::from_le(region.load32(OUTPUTS_I, Ordering::Relaxed)),
1216 max_frames: u32::from_le(region.load32(FRAMES_I, Ordering::Relaxed)),
1217 sample_rate: u32::from_le(region.load32(RATE_I, Ordering::Relaxed)),
1218 latency_bound_frames: u32::from_le(region.load32(LATENCY_I, Ordering::Relaxed)),
1219 }
1220 .validate()
1221 .map_err(HeaderError::Geometry)?;
1222 let expected = layout(geometry).expect("validated geometry has a layout");
1223 if u32::from_le(region.load32(TOTAL_I, Ordering::Relaxed)) as usize != expected.total_bytes
1224 || region.words32.len() * 4 < expected.prefix_bytes
1225 {
1226 return Err(HeaderError::TotalBytes);
1227 }
1228 for lane in 0..geometry.lanes as usize {
1229 let base = LANE_GEOMETRY_I + lane * 3;
1230 let actual = [
1231 u32::from_le(region.load32(base, Ordering::Relaxed)) as usize,
1232 u32::from_le(region.load32(base + 1, Ordering::Relaxed)) as usize,
1233 u32::from_le(region.load32(base + 2, Ordering::Relaxed)) as usize,
1234 ];
1235 if actual
1236 != [
1237 expected.first_slot_offsets[lane],
1238 expected.slot_bytes,
1239 expected.slots as usize,
1240 ]
1241 {
1242 return Err(HeaderError::LaneGeometry);
1243 }
1244 }
1245 Ok(geometry)
1246}
1247
1248pub fn sequence_at_least(value: u32, reference: u32) -> bool {
1253 value.wrapping_sub(reference) as i32 >= 0
1254}
1255
1256#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
1257struct PipelineEntry {
1258 sequence: u32,
1259 app_block: u64,
1260 generation: u64,
1261 valid: bool,
1262}
1263
1264#[derive(Debug)]
1269pub struct Pipeline {
1270 entries: [PipelineEntry; MAX_SLOTS as usize],
1271 depth: u32,
1272}
1273
1274impl Default for Pipeline {
1275 fn default() -> Self {
1276 Self::with_depth(1)
1277 }
1278}
1279
1280impl Pipeline {
1281 pub fn new() -> Self {
1282 Self::default()
1283 }
1284
1285 pub fn with_depth(depth: u32) -> Self {
1286 assert!((1..=MAX_SLOTS).contains(&depth));
1287 Self {
1288 entries: array::from_fn(|_| PipelineEntry::default()),
1289 depth,
1290 }
1291 }
1292
1293 pub fn depth(&self) -> u32 {
1294 self.depth
1295 }
1296
1297 pub fn set_depth(&mut self, depth: u32) {
1298 assert!((1..=MAX_SLOTS).contains(&depth));
1299 self.depth = depth;
1300 self.clear();
1301 }
1302
1303 pub fn published(&mut self, sequence: u32, app_block: u64, generation: u64) {
1304 self.entries[sequence as usize % MAX_SLOTS as usize] = PipelineEntry {
1305 sequence,
1306 app_block,
1307 generation,
1308 valid: true,
1309 };
1310 }
1311
1312 pub fn output_sequence(
1315 &mut self,
1316 app_block: u64,
1317 generation: u64,
1318 completed: u32,
1319 ) -> Option<u32> {
1320 let wanted = app_block.checked_sub(u64::from(self.depth))?;
1321 let found = self.entries.iter_mut().find(|entry| {
1322 entry.valid && entry.app_block == wanted && entry.generation == generation
1323 })?;
1324 if sequence_at_least(completed, found.sequence) {
1325 found.valid = false;
1326 Some(found.sequence)
1327 } else {
1328 found.valid = false;
1330 None
1331 }
1332 }
1333
1334 pub fn clear(&mut self) {
1335 self.entries = array::from_fn(|_| PipelineEntry::default());
1336 }
1337}