sim_lib_stream_jack/
bridge.rs1use sim_kernel::{Error, Result};
2use sim_lib_audio_graph_core::{
3 BlockArena, BlockEvent, NullEventSink, PrepareConfig, ProcessBlock, Processor,
4};
5use sim_lib_stream_audio::{PcmSpec, f32_interleaved_to_planar, f32_planar_to_interleaved};
6
7use crate::JackTransportState;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq)]
11pub struct JackMidiEvent {
12 offset: u32,
13 bytes: [u8; 3],
14 len: u8,
15}
16
17#[derive(Debug)]
19pub struct JackGraphBridge<P> {
20 processor: P,
21 spec: PcmSpec,
22 input_channels: usize,
23 max_block_frames: u32,
24 scratch: BlockArena,
25 last_transport: JackTransportState,
26}
27
28impl JackMidiEvent {
29 pub fn short(offset: u32, bytes: &[u8]) -> Result<Self> {
52 if bytes.is_empty() || bytes.len() > 3 {
53 return Err(Error::Eval(
54 "JACK short MIDI event must contain one to three bytes".to_owned(),
55 ));
56 }
57 let mut padded = [0; 3];
58 padded[..bytes.len()].copy_from_slice(bytes);
59 Ok(Self {
60 offset,
61 bytes: padded,
62 len: bytes.len() as u8,
63 })
64 }
65
66 pub fn offset(self) -> u32 {
68 self.offset
69 }
70
71 pub fn bytes(self) -> [u8; 3] {
73 self.bytes
74 }
75
76 pub fn byte_len(self) -> u8 {
78 self.len
79 }
80
81 fn block_event(self) -> BlockEvent<'static> {
82 BlockEvent::Midi {
83 offset: self.offset,
84 bytes: self.bytes,
85 len: self.len,
86 }
87 }
88}
89
90impl<P: Processor> JackGraphBridge<P> {
91 pub fn new(
103 mut processor: P,
104 spec: PcmSpec,
105 input_channels: usize,
106 max_block_frames: u32,
107 ) -> Result<Self> {
108 if max_block_frames == 0 {
109 return Err(Error::Eval(
110 "JACK block size must be greater than zero".to_owned(),
111 ));
112 }
113 processor.prepare(PrepareConfig::new(
114 spec.sample_rate_hz(),
115 max_block_frames,
116 checked_channels(input_channels, "input")?,
117 checked_channels(spec.channels(), "output")?,
118 ));
119 Ok(Self {
120 processor,
121 spec,
122 input_channels,
123 max_block_frames,
124 scratch: BlockArena::with_f32_capacity(
125 max_block_frames as usize * spec.channels().max(input_channels).max(1),
126 ),
127 last_transport: JackTransportState::stopped(0),
128 })
129 }
130
131 pub fn process_interleaved_f32(
146 &mut self,
147 input: Option<&[f32]>,
148 frames: usize,
149 transport: JackTransportState,
150 midi_events: &[JackMidiEvent],
151 ) -> Result<Vec<f32>> {
152 if frames > self.max_block_frames as usize {
153 return Err(Error::Eval(format!(
154 "JACK callback received {frames} frames, max block size is {}",
155 self.max_block_frames
156 )));
157 }
158 let input_planar = match input {
159 Some(samples) => f32_interleaved_to_planar(samples, self.input_channels)?,
160 None => vec![vec![0.0; frames]; self.input_channels],
161 };
162 let mut output_planar = vec![vec![0.0; frames]; self.spec.channels()];
163 let input_events = midi_events
164 .iter()
165 .copied()
166 .map(JackMidiEvent::block_event)
167 .collect::<Vec<_>>();
168 {
169 let input_refs = input_planar.iter().map(Vec::as_slice).collect::<Vec<_>>();
170 let mut output_refs = output_planar
171 .iter_mut()
172 .map(Vec::as_mut_slice)
173 .collect::<Vec<_>>();
174 self.scratch.reset();
175 let mut event_sink = NullEventSink;
176 let mut block = ProcessBlock {
177 frames: frames as u32,
178 in_audio: &input_refs,
179 out_audio: &mut output_refs,
180 in_events: &input_events,
181 out_events: &mut event_sink,
182 transport: transport.to_graph_transport(),
183 scratch: &mut self.scratch,
184 };
185 block.validate_audio_lanes()?;
186 self.processor.process(&mut block);
187 block.validate_audio_lanes()?;
188 }
189 self.last_transport = transport;
190 f32_planar_to_interleaved(&output_planar)
191 }
192
193 pub fn last_transport(&self) -> JackTransportState {
195 self.last_transport
196 }
197
198 pub fn reset(&mut self) {
200 self.processor.reset();
201 self.last_transport = JackTransportState::stopped(0);
202 self.scratch.reset();
203 }
204}
205
206fn checked_channels(channels: usize, role: &str) -> Result<u16> {
207 u16::try_from(channels)
208 .map_err(|_| Error::Eval(format!("JACK {role} channel count exceeds u16")))
209}