sim_lib_stream_asio/
bridge.rs1use sim_kernel::{Error, Result};
2use sim_lib_audio_graph_core::{
3 BlockArena, NullEventSink, PrepareConfig, ProcessBlock, Processor, Transport,
4};
5
6use crate::AsioTiming;
7
8#[derive(Debug)]
10pub struct AsioBufferSwitchBridge<P> {
11 processor: P,
12 timing: AsioTiming,
13 input_channels: usize,
14 output_channels: usize,
15 max_block_frames: u32,
16 scratch: BlockArena,
17 sample_pos: u64,
18}
19
20impl<P: Processor> AsioBufferSwitchBridge<P> {
21 pub fn new(
28 mut processor: P,
29 timing: AsioTiming,
30 input_channels: usize,
31 output_channels: usize,
32 ) -> Result<Self> {
33 if input_channels == 0 && output_channels == 0 {
34 return Err(Error::Eval(
35 "ASIO bridge needs at least one audio lane".to_owned(),
36 ));
37 }
38 let max_block_frames = checked_frames(timing.buffer_frames())?;
39 processor.prepare(PrepareConfig::new(
40 timing.sample_rate_hz(),
41 max_block_frames,
42 checked_channels(input_channels, "input")?,
43 checked_channels(output_channels, "output")?,
44 ));
45 Ok(Self {
46 processor,
47 timing,
48 input_channels,
49 output_channels,
50 max_block_frames,
51 scratch: BlockArena::with_f32_capacity(
52 timing.buffer_frames() * input_channels.max(output_channels).max(1),
53 ),
54 sample_pos: 0,
55 })
56 }
57
58 pub fn process_planar_f32(
67 &mut self,
68 input: Option<&[&[f32]]>,
69 frames: usize,
70 ) -> Result<Vec<Vec<f32>>> {
71 if frames > self.max_block_frames as usize {
72 return Err(Error::Eval(format!(
73 "ASIO callback received {frames} frames, max block size is {}",
74 self.max_block_frames
75 )));
76 }
77 let input_planar = input_planar(input, self.input_channels, frames)?;
78 let mut output_planar = vec![vec![0.0; frames]; self.output_channels];
79 {
80 let input_refs = input_planar.iter().map(Vec::as_slice).collect::<Vec<_>>();
81 let mut output_refs = output_planar
82 .iter_mut()
83 .map(Vec::as_mut_slice)
84 .collect::<Vec<_>>();
85 self.scratch.reset();
86 let mut event_sink = NullEventSink;
87 let mut block = ProcessBlock {
88 frames: frames as u32,
89 in_audio: &input_refs,
90 out_audio: &mut output_refs,
91 in_events: &[],
92 out_events: &mut event_sink,
93 transport: Transport {
94 sample_pos: self.sample_pos,
95 ..Transport::default()
96 },
97 scratch: &mut self.scratch,
98 };
99 block.validate_audio_lanes()?;
100 self.processor.process(&mut block);
101 block.validate_audio_lanes()?;
102 }
103 self.sample_pos += frames as u64;
104 Ok(output_planar)
105 }
106
107 pub fn sample_pos(&self) -> u64 {
110 self.sample_pos
111 }
112
113 pub fn timing(&self) -> AsioTiming {
115 self.timing
116 }
117
118 pub fn reset(&mut self) {
121 self.processor.reset();
122 self.sample_pos = 0;
123 self.scratch.reset();
124 }
125}
126
127fn input_planar(input: Option<&[&[f32]]>, channels: usize, frames: usize) -> Result<Vec<Vec<f32>>> {
128 let Some(input) = input else {
129 return Ok(vec![vec![0.0; frames]; channels]);
130 };
131 if input.len() != channels {
132 return Err(Error::Eval(format!(
133 "ASIO input has {} channels, expected {channels}",
134 input.len()
135 )));
136 }
137 input
138 .iter()
139 .map(|lane| {
140 if lane.len() < frames {
141 return Err(Error::Eval(format!(
142 "ASIO input lane has {} frames, expected {frames}",
143 lane.len()
144 )));
145 }
146 Ok(lane[..frames].to_vec())
147 })
148 .collect()
149}
150
151fn checked_frames(frames: usize) -> Result<u32> {
152 u32::try_from(frames).map_err(|_| Error::Eval("ASIO buffer size exceeds u32".to_owned()))
153}
154
155fn checked_channels(channels: usize, role: &str) -> Result<u16> {
156 u16::try_from(channels)
157 .map_err(|_| Error::Eval(format!("ASIO {role} channel count exceeds u16")))
158}