use crate::bus_config::CachedBusConfig;
use crate::sample::Sample;
use std::slice;
#[derive(Clone)]
pub struct ProcessBufferStorage<S: Sample> {
pub main_inputs: Vec<*const S>,
pub main_outputs: Vec<*mut S>,
pub aux_inputs: Vec<Vec<*const S>>,
pub aux_outputs: Vec<Vec<*mut S>>,
pub internal_output_buffers: Option<Vec<Vec<S>>>,
pub max_frames: usize,
}
impl<S: Sample> ProcessBufferStorage<S> {
pub fn new() -> Self {
Self {
main_inputs: Vec::new(),
main_outputs: Vec::new(),
aux_inputs: Vec::new(),
aux_outputs: Vec::new(),
internal_output_buffers: None,
max_frames: 0,
}
}
pub fn allocate_from_config(bus_config: &CachedBusConfig, max_frames: usize) -> Self {
let main_in_channels = bus_config
.input_bus_info(0)
.map(|b| b.channel_count)
.unwrap_or(0);
let main_out_channels = bus_config
.output_bus_info(0)
.map(|b| b.channel_count)
.unwrap_or(0);
let aux_in_buses = bus_config.input_bus_count.saturating_sub(1);
let aux_out_buses = bus_config.output_bus_count.saturating_sub(1);
let aux_inputs = if aux_in_buses > 0 {
let mut vec = Vec::with_capacity(aux_in_buses);
for i in 1..=aux_in_buses {
let channels = bus_config
.input_bus_info(i)
.map(|b| b.channel_count)
.unwrap_or(0);
vec.push(Vec::with_capacity(channels));
}
vec
} else {
Vec::new() };
let aux_outputs = if aux_out_buses > 0 {
let mut vec = Vec::with_capacity(aux_out_buses);
for i in 1..=aux_out_buses {
let channels = bus_config
.output_bus_info(i)
.map(|b| b.channel_count)
.unwrap_or(0);
vec.push(Vec::with_capacity(channels));
}
vec
} else {
Vec::new() };
let internal_output_buffers = if main_in_channels == 0 && main_out_channels > 0 {
let mut buffers = Vec::with_capacity(main_out_channels);
for _ in 0..main_out_channels {
buffers.push(vec![S::ZERO; max_frames]);
}
Some(buffers)
} else {
None
};
Self {
main_inputs: Vec::with_capacity(main_in_channels),
main_outputs: Vec::with_capacity(main_out_channels),
aux_inputs,
aux_outputs,
internal_output_buffers,
max_frames,
}
}
pub fn allocate(
main_in_channels: usize,
main_out_channels: usize,
aux_in_buses: usize,
aux_out_buses: usize,
aux_channels: usize,
) -> Self {
let mut aux_inputs = Vec::with_capacity(aux_in_buses);
for _ in 0..aux_in_buses {
aux_inputs.push(Vec::with_capacity(aux_channels));
}
let mut aux_outputs = Vec::with_capacity(aux_out_buses);
for _ in 0..aux_out_buses {
aux_outputs.push(Vec::with_capacity(aux_channels));
}
Self {
main_inputs: Vec::with_capacity(main_in_channels),
main_outputs: Vec::with_capacity(main_out_channels),
aux_inputs,
aux_outputs,
internal_output_buffers: None, max_frames: 0,
}
}
#[inline]
pub fn clear(&mut self) {
self.main_inputs.clear();
self.main_outputs.clear();
for bus in &mut self.aux_inputs {
bus.clear();
}
for bus in &mut self.aux_outputs {
bus.clear();
}
}
#[inline]
pub fn input_channel_count(&self) -> usize {
self.main_inputs.len()
}
#[inline]
pub fn output_channel_count(&self) -> usize {
self.main_outputs.len()
}
#[inline]
pub fn aux_input_bus_count(&self) -> usize {
self.aux_inputs.len()
}
#[inline]
pub fn aux_output_bus_count(&self) -> usize {
self.aux_outputs.len()
}
#[inline]
pub fn max_frames(&self) -> usize {
self.max_frames
}
#[inline]
pub fn has_internal_output_buffers(&self) -> bool {
self.internal_output_buffers.is_some()
}
#[inline]
pub unsafe fn push_main_input(&mut self, ptr: *const S) {
self.main_inputs.push(ptr);
}
#[inline]
pub unsafe fn push_main_output(&mut self, ptr: *mut S) {
self.main_outputs.push(ptr);
}
#[inline]
pub unsafe fn push_aux_input(&mut self, bus_index: usize, ptr: *const S) {
if bus_index < self.aux_inputs.len() {
self.aux_inputs[bus_index].push(ptr);
}
}
#[inline]
pub unsafe fn push_aux_output(&mut self, bus_index: usize, ptr: *mut S) {
if bus_index < self.aux_outputs.len() {
self.aux_outputs[bus_index].push(ptr);
}
}
#[inline]
pub fn main_input_capacity(&self) -> usize {
self.main_inputs.capacity()
}
#[inline]
pub fn main_output_capacity(&self) -> usize {
self.main_outputs.capacity()
}
#[inline]
pub fn aux_input_capacity(&self, bus_index: usize) -> usize {
self.aux_inputs.get(bus_index).map(|v| v.capacity()).unwrap_or(0)
}
#[inline]
pub fn aux_output_capacity(&self, bus_index: usize) -> usize {
self.aux_outputs.get(bus_index).map(|v| v.capacity()).unwrap_or(0)
}
#[inline]
pub unsafe fn input_slices(&self, num_samples: usize) -> Vec<&[S]> {
self.main_inputs
.iter()
.map(|&ptr| {
unsafe { slice::from_raw_parts(ptr, num_samples) }
})
.collect()
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn output_slices(&self, num_samples: usize) -> Vec<&mut [S]> {
self.main_outputs
.iter()
.map(|&ptr| {
unsafe { slice::from_raw_parts_mut(ptr, num_samples) }
})
.collect()
}
#[inline]
pub unsafe fn aux_input_slices(&self, num_samples: usize) -> Vec<Vec<&[S]>> {
self.aux_inputs
.iter()
.map(|bus| {
bus.iter()
.map(|&ptr| {
unsafe { slice::from_raw_parts(ptr, num_samples) }
})
.collect()
})
.collect()
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub unsafe fn aux_output_slices(&self, num_samples: usize) -> Vec<Vec<&mut [S]>> {
self.aux_outputs
.iter()
.map(|bus| {
bus.iter()
.map(|&ptr| {
unsafe { slice::from_raw_parts_mut(ptr, num_samples) }
})
.collect()
})
.collect()
}
#[inline]
pub fn internal_output_buffer_mut(&mut self, channel: usize) -> Option<&mut Vec<S>> {
self.internal_output_buffers
.as_mut()
.and_then(|buffers| buffers.get_mut(channel))
}
#[inline]
pub fn internal_output_buffer(&self, channel: usize) -> Option<&Vec<S>> {
self.internal_output_buffers
.as_ref()
.and_then(|buffers| buffers.get(channel))
}
#[inline]
pub fn internal_output_buffer_count(&self) -> usize {
self.internal_output_buffers
.as_ref()
.map(|b| b.len())
.unwrap_or(0)
}
}
impl<S: Sample> Default for ProcessBufferStorage<S> {
fn default() -> Self {
Self::new()
}
}
unsafe impl<S: Sample> Send for ProcessBufferStorage<S> {}
unsafe impl<S: Sample> Sync for ProcessBufferStorage<S> {}
#[cfg(test)]
#[allow(clippy::undocumented_unsafe_blocks)]
mod tests {
use super::*;
use crate::bus_config::CachedBusInfo;
use crate::plugin::BusType;
use crate::types::MAX_CHANNELS;
#[test]
fn test_validate_bus_limits_success() {
let config = CachedBusConfig::new(
vec![CachedBusInfo::new(2, BusType::Main)],
vec![CachedBusInfo::new(2, BusType::Main)],
);
assert!(config.validate().is_ok());
}
#[test]
fn test_validate_bus_limits_too_many_channels() {
let config = CachedBusConfig::new(
vec![CachedBusInfo::new(MAX_CHANNELS + 1, BusType::Main)],
vec![CachedBusInfo::new(2, BusType::Main)],
);
assert!(config.validate().is_err());
}
#[test]
fn test_allocate_from_config_stereo() {
let config = CachedBusConfig::default(); let storage: ProcessBufferStorage<f32> =
ProcessBufferStorage::allocate_from_config(&config, 4096);
assert_eq!(storage.main_inputs.capacity(), 2);
assert_eq!(storage.main_outputs.capacity(), 2);
assert_eq!(storage.aux_inputs.len(), 0);
assert_eq!(storage.aux_outputs.len(), 0);
}
#[test]
fn test_allocate_from_config_with_aux() {
let config = CachedBusConfig::new(
vec![
CachedBusInfo::new(2, BusType::Main),
CachedBusInfo::new(2, BusType::Aux),
],
vec![CachedBusInfo::new(6, BusType::Main)],
);
let storage: ProcessBufferStorage<f32> =
ProcessBufferStorage::allocate_from_config(&config, 4096);
assert_eq!(storage.main_inputs.capacity(), 2);
assert_eq!(storage.main_outputs.capacity(), 6);
assert_eq!(storage.aux_inputs.len(), 1);
assert_eq!(storage.aux_inputs[0].capacity(), 2);
assert_eq!(storage.aux_outputs.len(), 0);
}
#[test]
fn test_allocate_and_clear() {
let mut storage: ProcessBufferStorage<f32> = ProcessBufferStorage::allocate(2, 2, 1, 0, 2);
assert_eq!(storage.main_inputs.capacity(), 2);
assert_eq!(storage.main_outputs.capacity(), 2);
assert_eq!(storage.aux_inputs.len(), 1);
assert_eq!(storage.aux_inputs[0].capacity(), 2);
let dummy: f32 = 0.0;
unsafe {
storage.push_main_input(&dummy as *const f32);
storage.push_main_input(&dummy as *const f32);
}
assert_eq!(storage.main_inputs.len(), 2);
storage.clear();
assert_eq!(storage.main_inputs.len(), 0);
assert_eq!(storage.main_inputs.capacity(), 2);
}
#[test]
fn test_allocate_from_config_mono() {
let config = CachedBusConfig::new(
vec![CachedBusInfo::new(1, BusType::Main)],
vec![CachedBusInfo::new(1, BusType::Main)],
);
let storage: ProcessBufferStorage<f32> =
ProcessBufferStorage::allocate_from_config(&config, 4096);
assert_eq!(storage.main_inputs.capacity(), 1);
assert_eq!(storage.main_outputs.capacity(), 1);
assert_eq!(storage.aux_inputs.len(), 0);
assert_eq!(storage.aux_outputs.len(), 0);
}
#[test]
fn test_instrument_internal_buffers_allocated() {
let config = CachedBusConfig::new(
vec![], vec![CachedBusInfo::new(2, BusType::Main)],
);
let storage: ProcessBufferStorage<f32> =
ProcessBufferStorage::allocate_from_config(&config, 512);
assert!(storage.internal_output_buffers.is_some());
let internal = storage.internal_output_buffers.as_ref().unwrap();
assert_eq!(internal.len(), 2); assert_eq!(internal[0].len(), 512); assert_eq!(internal[1].len(), 512);
assert_eq!(storage.max_frames, 512);
}
#[test]
fn test_effect_no_internal_buffers() {
let config = CachedBusConfig::default();
let storage: ProcessBufferStorage<f32> =
ProcessBufferStorage::allocate_from_config(&config, 512);
assert!(storage.internal_output_buffers.is_none());
}
#[test]
fn test_clear_maintains_capacity() {
let config = CachedBusConfig::new(
vec![
CachedBusInfo::new(2, BusType::Main),
CachedBusInfo::new(2, BusType::Aux),
],
vec![CachedBusInfo::new(2, BusType::Main)],
);
let mut storage: ProcessBufferStorage<f32> =
ProcessBufferStorage::allocate_from_config(&config, 4096);
let main_in_cap = storage.main_inputs.capacity();
let main_out_cap = storage.main_outputs.capacity();
let aux_in_count = storage.aux_inputs.len();
let aux_in_cap = if aux_in_count > 0 {
storage.aux_inputs[0].capacity()
} else {
0
};
let dummy: f32 = 0.0;
unsafe {
storage.push_main_input(&dummy as *const f32);
storage.push_main_input(&dummy as *const f32);
if aux_in_count > 0 {
storage.push_aux_input(0, &dummy as *const f32);
}
}
storage.clear();
assert_eq!(storage.main_inputs.capacity(), main_in_cap);
assert_eq!(storage.main_outputs.capacity(), main_out_cap);
assert_eq!(storage.aux_inputs.len(), aux_in_count);
if aux_in_count > 0 {
assert_eq!(storage.aux_inputs[0].capacity(), aux_in_cap);
}
assert_eq!(storage.main_inputs.len(), 0);
assert_eq!(storage.main_outputs.len(), 0);
if aux_in_count > 0 {
assert_eq!(storage.aux_inputs[0].len(), 0);
}
}
}