Skip to main content

sim_lib_audio_graph_core/
bridge.rs

1use sim_lib_stream_core::{ClockDomain, DomainBridgeDescriptor};
2
3use crate::{PortDecl, PortDir, PortMedia, ProcessBlock, Processor};
4
5/// A [`Processor`] that bridges between clock domains or rate contracts.
6///
7/// Wraps a [`DomainBridgeDescriptor`] and carries audio through unchanged or
8/// forwards events, depending on its [`PortMedia`]. Its declared latency and
9/// clock domain come from the descriptor's output rate.
10#[derive(Clone, Debug)]
11pub struct DomainBridgeProcessor {
12    descriptor: DomainBridgeDescriptor,
13    media: PortMedia,
14}
15
16impl DomainBridgeProcessor {
17    /// Creates a bridge processor from a descriptor and port media kind.
18    pub fn new(descriptor: DomainBridgeDescriptor, media: PortMedia) -> Self {
19        Self { descriptor, media }
20    }
21
22    /// Creates an audio resampling bridge from `input_hz` to `output_hz`.
23    pub fn resampler(input_hz: u32, output_hz: u32) -> sim_kernel::Result<Self> {
24        Ok(Self::new(
25            DomainBridgeDescriptor::resampler(input_hz, output_hz)?,
26            PortMedia::Audio,
27        ))
28    }
29
30    /// Creates an event jitter-buffer bridge tolerating `max_late_packets`.
31    pub fn jitter_buffer(max_late_packets: u32) -> Self {
32        Self::new(
33            DomainBridgeDescriptor::jitter_buffer(max_late_packets),
34            PortMedia::Event,
35        )
36    }
37
38    /// Creates an audio latency-compensation delay of `frames` frames.
39    pub fn latency_comp_delay(frames: u64) -> Self {
40        Self::new(
41            DomainBridgeDescriptor::latency_comp_delay(frames),
42            PortMedia::Audio,
43        )
44    }
45
46    /// Creates an event-media rate gate bridging from `input_domain`.
47    pub fn event_rate_gate(input_domain: ClockDomain) -> sim_kernel::Result<Self> {
48        Ok(Self::new(
49            DomainBridgeDescriptor::event_rate_gate(input_domain)?,
50            PortMedia::Event,
51        ))
52    }
53
54    /// Creates a control-media rate gate bridging from `input_domain`.
55    pub fn control_rate_gate(input_domain: ClockDomain) -> sim_kernel::Result<Self> {
56        Ok(Self::new(
57            DomainBridgeDescriptor::event_rate_gate(input_domain)?,
58            PortMedia::Control,
59        ))
60    }
61
62    /// Returns the underlying domain-bridge descriptor.
63    pub fn bridge_descriptor(&self) -> &DomainBridgeDescriptor {
64        &self.descriptor
65    }
66
67    /// Returns the port media kind this bridge carries.
68    pub fn media(&self) -> PortMedia {
69        self.media
70    }
71}
72
73impl Processor for DomainBridgeProcessor {
74    fn prepare(&mut self, _cfg: crate::PrepareConfig) {}
75
76    fn reset(&mut self) {}
77
78    fn process(&mut self, block: &mut ProcessBlock<'_>) {
79        match self.media {
80            PortMedia::Audio => copy_audio(block),
81            PortMedia::Control | PortMedia::Event => {
82                for event in block.in_events {
83                    let _ = block.out_events.push(*event);
84                }
85            }
86        }
87    }
88
89    fn clock_domain(&self) -> ClockDomain {
90        self.descriptor.output_rate().clock_domain()
91    }
92
93    fn latency_class(&self) -> sim_lib_stream_core::LatencyClass {
94        self.descriptor.output_rate().latency_class()
95    }
96
97    fn realtime_pin(&self) -> bool {
98        false
99    }
100
101    fn ports(&self, in_channels: u16, out_channels: u16) -> Vec<PortDecl> {
102        let mut ports = Vec::new();
103        if in_channels > 0 {
104            ports.push(
105                PortDecl::new("in", self.media, PortDir::In, in_channels)
106                    .with_rate_contract(self.descriptor.input_rate()),
107            );
108        }
109        if out_channels > 0 {
110            ports.push(
111                PortDecl::new("out", self.media, PortDir::Out, out_channels)
112                    .with_rate_contract(self.descriptor.output_rate()),
113            );
114        }
115        ports
116    }
117
118    fn tail_frames(&self) -> u64 {
119        self.descriptor.latency().frame_count()
120    }
121}
122
123fn copy_audio(block: &mut ProcessBlock<'_>) {
124    let frames = block.frames as usize;
125    for (input, output) in block.in_audio.iter().zip(block.out_audio.iter_mut()) {
126        output[..frames].copy_from_slice(&input[..frames]);
127    }
128}