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