sim_lib_stream_combinators/
bridge.rs1use std::sync::{
2 Arc, Mutex,
3 atomic::{AtomicUsize, Ordering},
4};
5
6use sim_kernel::{Error, Ref, Result, Symbol};
7use sim_lib_stream_core::{
8 ClockDomain, DomainBridgeDescriptor, PcmPacket, StreamItem, StreamMetadata, StreamPacket,
9};
10
11use crate::{Stream, StreamNode};
12
13pub fn resample_pcm(source: Stream, input_hz: u32, output_hz: u32) -> Result<Stream> {
18 let descriptor = DomainBridgeDescriptor::resampler(input_hz, output_hz)?;
19 let metadata = source.metadata().clone();
20 Ok(Stream::new(ResamplePcmNode {
21 source,
22 metadata,
23 input_hz,
24 output_hz,
25 _descriptor: descriptor,
26 }))
27}
28
29pub fn jitter_buffer(source: Stream, clock: Symbol, max_late_packets: u32) -> Stream {
40 jitter_buffer_with_drops(source, clock, max_late_packets).0
41}
42
43pub(crate) fn jitter_buffer_with_drops(
48 source: Stream,
49 clock: Symbol,
50 max_late_packets: u32,
51) -> (Stream, Arc<AtomicUsize>) {
52 let descriptor = DomainBridgeDescriptor::jitter_buffer(max_late_packets);
53 let metadata = source.metadata().clone();
54 let late_dropped = Arc::new(AtomicUsize::new(0));
55 let stream = Stream::new(JitterBufferNode {
56 source,
57 metadata,
58 clock,
59 max_late_packets,
60 state: Mutex::new(JitterBufferState::default()),
61 late_dropped: Arc::clone(&late_dropped),
62 _descriptor: descriptor,
63 });
64 (stream, late_dropped)
65}
66
67pub fn latency_comp_delay(source: Stream, frames: u64) -> Stream {
72 let descriptor = DomainBridgeDescriptor::latency_comp_delay(frames);
73 let metadata = source.metadata().clone();
74 Stream::new(PassthroughBridgeNode {
75 source,
76 metadata,
77 _descriptor: descriptor,
78 })
79}
80
81pub fn event_rate_gate(source: Stream) -> Result<Stream> {
87 let input_domain =
88 ClockDomain::from_symbol(source.metadata().clock()).unwrap_or(ClockDomain::Control);
89 let descriptor = DomainBridgeDescriptor::event_rate_gate(input_domain)?;
90 let metadata = source.metadata().clone();
91 Ok(Stream::new(PassthroughBridgeNode {
92 source,
93 metadata,
94 _descriptor: descriptor,
95 }))
96}
97
98struct ResamplePcmNode {
99 source: Stream,
100 metadata: StreamMetadata,
101 input_hz: u32,
102 output_hz: u32,
103 _descriptor: DomainBridgeDescriptor,
104}
105
106impl StreamNode for ResamplePcmNode {
107 fn metadata(&self) -> &StreamMetadata {
108 &self.metadata
109 }
110
111 fn next_packet(&self) -> Result<Option<StreamItem>> {
112 let Some(item) = self.source.next_packet()? else {
113 return Ok(None);
114 };
115 let StreamPacket::Pcm(packet) = item.packet() else {
116 return Ok(Some(item));
117 };
118 let packet = resample_packet(packet, self.input_hz, self.output_hz)?;
119 StreamItem::with_ticks(StreamPacket::Pcm(packet), item.ticks().to_vec()).map(Some)
120 }
121
122 fn is_done(&self) -> Result<bool> {
123 self.source.is_done()
124 }
125}
126
127struct JitterBufferNode {
128 source: Stream,
129 metadata: StreamMetadata,
130 clock: Symbol,
131 max_late_packets: u32,
132 state: Mutex<JitterBufferState>,
133 late_dropped: Arc<AtomicUsize>,
134 _descriptor: DomainBridgeDescriptor,
135}
136
137#[derive(Default)]
139struct JitterBufferState {
140 window: Vec<(usize, StreamItem)>,
142 next_ordinal: usize,
144 last_emitted: Option<Ref>,
146 source_done: bool,
148}
149
150impl StreamNode for JitterBufferNode {
151 fn metadata(&self) -> &StreamMetadata {
152 &self.metadata
153 }
154
155 fn next_packet(&self) -> Result<Option<StreamItem>> {
156 let mut state = self
157 .state
158 .lock()
159 .map_err(|_| Error::PoisonedLock("jitter-buffer state"))?;
160 self.fill_window(&mut state)?;
161 let target = self.max_late_packets as usize + 1;
162 if state.window.len() < target && !state.source_done {
163 return Ok(None);
165 }
166 Ok(self.pop_next(&mut state))
167 }
168
169 fn is_done(&self) -> Result<bool> {
170 let state = self
171 .state
172 .lock()
173 .map_err(|_| Error::PoisonedLock("jitter-buffer state"))?;
174 Ok(state.window.is_empty() && (state.source_done || self.source.is_done()?))
175 }
176}
177
178impl JitterBufferNode {
179 fn fill_window(&self, state: &mut JitterBufferState) -> Result<()> {
182 let target = self.max_late_packets as usize + 1;
183 while !state.source_done && state.window.len() < target {
184 match self.source.next_packet()? {
185 Some(item) => self.accept_or_drop(state, item),
186 None => {
187 if self.source.is_done()? {
188 state.source_done = true;
189 }
190 break;
191 }
192 }
193 }
194 Ok(())
195 }
196
197 fn accept_or_drop(&self, state: &mut JitterBufferState, item: StreamItem) {
200 let key = tick_key(&item, &self.clock);
201 let late = match (&key, &state.last_emitted) {
202 (Some(key), Some(last)) => key < last,
203 _ => false,
204 };
205 if late {
206 self.late_dropped.fetch_add(1, Ordering::Relaxed);
207 return;
208 }
209 let ordinal = state.next_ordinal;
210 state.next_ordinal = state.next_ordinal.saturating_add(1);
211 state.window.push((ordinal, item));
212 }
213
214 fn pop_next(&self, state: &mut JitterBufferState) -> Option<StreamItem> {
217 if state.window.is_empty() {
218 return None;
219 }
220 let mut best = 0usize;
221 for index in 1..state.window.len() {
222 if self.precedes(&state.window[index], &state.window[best]) {
223 best = index;
224 }
225 }
226 let (_, item) = state.window.remove(best);
227 if let Some(key) = tick_key(&item, &self.clock) {
228 state.last_emitted = Some(key);
229 }
230 Some(item)
231 }
232
233 fn precedes(&self, left: &(usize, StreamItem), right: &(usize, StreamItem)) -> bool {
236 match (
237 tick_key(&left.1, &self.clock),
238 tick_key(&right.1, &self.clock),
239 ) {
240 (Some(left_key), Some(right_key)) => (left_key, left.0) < (right_key, right.0),
241 _ => left.0 < right.0,
242 }
243 }
244}
245
246struct PassthroughBridgeNode {
247 source: Stream,
248 metadata: StreamMetadata,
249 _descriptor: DomainBridgeDescriptor,
250}
251
252impl StreamNode for PassthroughBridgeNode {
253 fn metadata(&self) -> &StreamMetadata {
254 &self.metadata
255 }
256
257 fn next_packet(&self) -> Result<Option<StreamItem>> {
258 self.source.next_packet()
259 }
260
261 fn is_done(&self) -> Result<bool> {
262 self.source.is_done()
263 }
264}
265
266fn resample_packet(packet: &PcmPacket, input_hz: u32, output_hz: u32) -> Result<PcmPacket> {
267 if input_hz == 0 || output_hz == 0 {
268 return Err(Error::Eval("PCM resample rates must be nonzero".to_owned()));
269 }
270 let output_frames = resampled_frame_count(packet.frames(), input_hz, output_hz);
271 match packet.sample_format() {
272 sim_lib_stream_core::PcmSampleFormat::I16 => PcmPacket::i16(
273 packet.channels(),
274 output_frames,
275 resample_interleaved(
276 packet.samples_i16(),
277 packet.channels(),
278 output_frames,
279 |v| v,
280 ),
281 ),
282 sim_lib_stream_core::PcmSampleFormat::F32 => PcmPacket::f32(
283 packet.channels(),
284 output_frames,
285 resample_interleaved(
286 packet.samples_f32(),
287 packet.channels(),
288 output_frames,
289 |v| v,
290 ),
291 ),
292 }
293}
294
295fn resampled_frame_count(input_frames: usize, input_hz: u32, output_hz: u32) -> usize {
296 let frames = (input_frames as u64)
297 .saturating_mul(u64::from(output_hz))
298 .saturating_add(u64::from(input_hz / 2))
299 / u64::from(input_hz);
300 frames.max(1) as usize
301}
302
303fn resample_interleaved<T: Copy>(
304 samples: &[T],
305 channels: usize,
306 output_frames: usize,
307 copy: impl Fn(T) -> T,
308) -> Vec<T> {
309 let input_frames = samples.len() / channels;
310 let mut out = Vec::with_capacity(output_frames * channels);
311 for frame in 0..output_frames {
312 let source_frame = frame.saturating_mul(input_frames) / output_frames;
313 let source_frame = source_frame.min(input_frames.saturating_sub(1));
314 for channel in 0..channels {
315 out.push(copy(samples[source_frame * channels + channel]));
316 }
317 }
318 out
319}
320
321fn tick_key(item: &StreamItem, clock: &Symbol) -> Option<Ref> {
322 item.ticks()
323 .iter()
324 .find(|tick| &tick.clock == clock)
325 .map(|tick| tick.index.clone())
326}