100 lines
2.8 KiB
Rust
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(())
|
|
}
|
|
}
|