fcb_looper/audio_engine/src/audio_backend.rs

100 lines
2.8 KiB
Rust

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<Item = RawMidi<'_>>;
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<Item = RawMidi<'_>> {
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<RawMidi<'a>>,
pub midi_output_events: Vec<(u32, Vec<u8>)>, // 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<Item = jack::RawMidi<'_>> {
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(())
}
}