sim_lib_stream_alsa/
bridge.rs1use sim_kernel::{Error, Result};
2use sim_lib_audio_graph_core::{
3 BlockArena, NullEventSink, PrepareConfig, ProcessBlock, Processor, Transport,
4};
5use sim_lib_stream_audio::{
6 PcmBuffer, PcmSampleFormat, PcmSpec, f32_interleaved_to_planar, f32_planar_to_interleaved,
7 f32_samples_to_i16, i16_samples_to_f32,
8};
9use sim_lib_stream_core::{PcmPacket, PushResult, StreamPacket};
10use sim_lib_stream_host::HostCallbackQueue;
11
12#[derive(Debug)]
14pub struct AlsaPlaybackBridge<P> {
15 processor: P,
16 spec: PcmSpec,
17 max_block_frames: u32,
18 scratch: BlockArena,
19 sample_pos: u64,
20}
21
22#[derive(Clone)]
24pub struct AlsaCaptureBridge {
25 queue: HostCallbackQueue,
26 spec: PcmSpec,
27}
28
29impl<P: Processor> AlsaPlaybackBridge<P> {
30 pub fn new(mut processor: P, spec: PcmSpec, max_block_frames: u32) -> Result<Self> {
36 if max_block_frames == 0 {
37 return Err(Error::Eval(
38 "ALSA playback max block frames must be greater than zero".to_owned(),
39 ));
40 }
41 processor.prepare(PrepareConfig::new(
42 spec.sample_rate_hz(),
43 max_block_frames,
44 0,
45 checked_channels(spec.channels(), "playback output")?,
46 ));
47 Ok(Self {
48 processor,
49 spec,
50 max_block_frames,
51 scratch: BlockArena::with_f32_capacity(max_block_frames as usize * spec.channels()),
52 sample_pos: 0,
53 })
54 }
55
56 pub fn render_buffer(&mut self, frames: usize) -> Result<PcmBuffer> {
61 let interleaved = self.render_interleaved_f32(frames)?;
62 match self.spec.sample_format() {
63 PcmSampleFormat::F32 => PcmBuffer::f32(self.spec, frames, interleaved),
64 PcmSampleFormat::I16 => {
65 PcmBuffer::i16(self.spec, frames, f32_samples_to_i16(&interleaved)?)
66 }
67 }
68 }
69
70 pub fn render_interleaved_f32(&mut self, frames: usize) -> Result<Vec<f32>> {
76 if frames > self.max_block_frames as usize {
77 return Err(Error::Eval(format!(
78 "ALSA playback callback received {frames} frames, max is {}",
79 self.max_block_frames
80 )));
81 }
82 let mut output_planar = vec![vec![0.0; frames]; self.spec.channels()];
83 {
84 let input_refs: [&[f32]; 0] = [];
85 let mut output_refs = output_planar
86 .iter_mut()
87 .map(Vec::as_mut_slice)
88 .collect::<Vec<_>>();
89 self.scratch.reset();
90 let mut event_sink = NullEventSink;
91 let mut block = ProcessBlock {
92 frames: frames as u32,
93 in_audio: &input_refs,
94 out_audio: &mut output_refs,
95 in_events: &[],
96 out_events: &mut event_sink,
97 transport: Transport {
98 playing: true,
99 sample_pos: self.sample_pos,
100 tempo_bpm: 120.0,
101 ppq_pos: 0.0,
102 },
103 scratch: &mut self.scratch,
104 };
105 block.validate_audio_lanes()?;
106 self.processor.process(&mut block);
107 block.validate_audio_lanes()?;
108 }
109 self.sample_pos = self.sample_pos.saturating_add(frames as u64);
110 f32_planar_to_interleaved(&output_planar)
111 }
112
113 pub fn sample_pos(&self) -> u64 {
115 self.sample_pos
116 }
117
118 pub fn reset(&mut self) {
120 self.processor.reset();
121 self.sample_pos = 0;
122 self.scratch.reset();
123 }
124}
125
126impl AlsaCaptureBridge {
127 pub fn new(queue: HostCallbackQueue, spec: PcmSpec) -> Self {
129 Self { queue, spec }
130 }
131
132 pub fn capture_interleaved_f32(&self, samples: &[f32]) -> Result<PushResult> {
138 let frames = samples_to_frames(samples.len(), self.spec.channels())?;
139 let planar = f32_interleaved_to_planar(samples, self.spec.channels())?;
140 let packet_samples = f32_planar_to_interleaved(&planar)?;
141 let packet = match self.spec.sample_format() {
142 PcmSampleFormat::F32 => PcmPacket::f32(self.spec.channels(), frames, packet_samples)?,
143 PcmSampleFormat::I16 => PcmPacket::i16(
144 self.spec.channels(),
145 frames,
146 f32_samples_to_i16(&packet_samples)?,
147 )?,
148 };
149 self.queue.callback_packet(StreamPacket::Pcm(packet))
150 }
151
152 pub fn capture_interleaved_i16(&self, samples: &[i16]) -> Result<PushResult> {
158 let frames = samples_to_frames(samples.len(), self.spec.channels())?;
159 let f32_samples = i16_samples_to_f32(samples);
160 let packet = match self.spec.sample_format() {
161 PcmSampleFormat::F32 => PcmPacket::f32(self.spec.channels(), frames, f32_samples)?,
162 PcmSampleFormat::I16 => PcmPacket::i16(self.spec.channels(), frames, samples.to_vec())?,
163 };
164 self.queue.callback_packet(StreamPacket::Pcm(packet))
165 }
166
167 pub fn spec(&self) -> PcmSpec {
169 self.spec
170 }
171}
172
173fn samples_to_frames(samples: usize, channels: usize) -> Result<usize> {
174 if channels == 0 {
175 return Err(Error::Eval(
176 "ALSA capture channel count must be greater than zero".to_owned(),
177 ));
178 }
179 if !samples.is_multiple_of(channels) {
180 return Err(Error::Eval(format!(
181 "ALSA capture sample length {samples} is not divisible by channels {channels}"
182 )));
183 }
184 Ok(samples / channels)
185}
186
187fn checked_channels(channels: usize, role: &str) -> Result<u16> {
188 u16::try_from(channels)
189 .map_err(|_| Error::Eval(format!("ALSA {role} channel count exceeds u16")))
190}