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 carries domain-bridge metadata through a graph.
6///
7/// This node is metadata-only: it advertises a [`DomainBridgeDescriptor`] for
8/// graph planning, copies audio, and forwards current-block events unchanged.
9/// It does not resample audio, insert delay, or buffer events.
10#[derive(Clone, Debug)]
11pub struct DomainBridgeMetadataProcessor {
12    descriptor: DomainBridgeDescriptor,
13    media: PortMedia,
14}
15
16impl DomainBridgeMetadataProcessor {
17    /// Creates a metadata bridge node from a descriptor and port media kind.
18    pub fn new(descriptor: DomainBridgeDescriptor, media: PortMedia) -> Self {
19        Self { descriptor, media }
20    }
21
22    /// Creates metadata for an audio sample-rate bridge from `input_hz` to `output_hz`.
23    pub fn sample_rate_bridge_metadata(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 metadata for an event jitter bridge tolerating `max_late_packets`.
31    pub fn event_jitter_bridge_metadata(max_late_packets: u32) -> Self {
32        Self::new(
33            DomainBridgeDescriptor::jitter_buffer(max_late_packets),
34            PortMedia::Event,
35        )
36    }
37
38    /// Creates metadata for an audio latency-compensation bridge of `frames` frames.
39    pub fn audio_latency_bridge_metadata(frames: u64) -> Self {
40        Self::new(
41            DomainBridgeDescriptor::latency_comp_delay(frames),
42            PortMedia::Audio,
43        )
44    }
45
46    /// Creates metadata for an event-media block gate from `input_domain`.
47    pub fn event_block_gate_metadata(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 metadata for a control-media block gate from `input_domain`.
55    pub fn control_block_gate_metadata(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 DomainBridgeMetadataProcessor {
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        0
120    }
121}
122
123fn copy_audio(block: &mut ProcessBlock<'_>) {
124    let frames = block.frames as usize;
125    for (channel, output) in block.out_audio.iter_mut().enumerate() {
126        let output = &mut output[..frames];
127        if let Some(input) = block.in_audio.get(channel) {
128            output.copy_from_slice(&input[..frames]);
129        } else {
130            output.fill(0.0);
131        }
132    }
133}