use crate::{JackPorts, Result as LooperResult}; use jack::{Frames, ProcessScope, RawMidi}; /// A thin abstraction over the data provided by the audio backend for one process cycle. /// This allows the `ProcessHandler`'s core logic to be tested without a live JACK server. pub trait AudioBackend<'a> { fn n_frames(&self) -> Frames; fn last_frame_time(&self) -> u32; fn audio_input(&self) -> &[f32]; fn audio_output(&mut self) -> &mut [f32]; fn click_output(&mut self) -> &mut [f32]; fn midi_input(&self) -> impl Iterator>; fn midi_output(&mut self, time: u32, bytes: &[u8]) -> LooperResult<()>; } pub struct JackAudioBackend<'a> { scope: &'a ProcessScope, ports: &'a mut JackPorts, } impl<'a> JackAudioBackend<'a> { pub fn new(scope: &'a ProcessScope, ports: &'a mut JackPorts) -> Self { Self { scope, ports } } } impl<'a> AudioBackend<'a> for JackAudioBackend<'a> { fn n_frames(&self) -> Frames { self.scope.n_frames() } fn last_frame_time(&self) -> u32 { self.scope.last_frame_time() } fn audio_input(&self) -> &[f32] { self.ports.audio_in.as_slice(self.scope) } fn audio_output(&mut self) -> &mut [f32] { self.ports.audio_out.as_mut_slice(self.scope) } fn click_output(&mut self) -> &mut [f32] { self.ports.click_track_out.as_mut_slice(self.scope) } fn midi_input(&self) -> impl Iterator> { self.ports.midi_in.iter(self.scope) } fn midi_output(&mut self, time: u32, bytes: &[u8]) -> LooperResult<()> { let mut writer = self.ports.midi_out.writer(self.scope); writer .write(&RawMidi { time, bytes }) .map_err(|_| crate::LooperError::Midi(std::panic::Location::caller())) } } pub struct MockAudioBackend<'a> { pub n_frames_val: Frames, pub last_frame_time_val: u32, pub audio_input_buffer: &'a [f32], pub audio_output_buffer: &'a mut [f32], pub click_output_buffer: &'a mut [f32], pub midi_input_events: Vec>, pub midi_output_events: Vec<(u32, Vec)>, // Store time and bytes } impl<'a> AudioBackend<'a> for MockAudioBackend<'a> { fn n_frames(&self) -> Frames { self.n_frames_val } fn last_frame_time(&self) -> u32 { self.last_frame_time_val } fn audio_input(&self) -> &[f32] { self.audio_input_buffer } fn audio_output(&mut self) -> &mut [f32] { self.audio_output_buffer } fn click_output(&mut self) -> &mut [f32] { self.click_output_buffer } fn midi_input(&self) -> impl Iterator> { self.midi_input_events.clone().into_iter() } fn midi_output(&mut self, time: u32, bytes: &[u8]) -> LooperResult<()> { self.midi_output_events.push((time, bytes.to_vec())); Ok(()) } }