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