From f44597df7c073d9cffdb57d5ff978fbd51c77ef5 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Wed, 2 Feb 2022 15:01:41 +0100 Subject: [PATCH] Move the buffer adapter to its own module --- plugins/gain/src/lib.rs | 3 +- plugins/sine/src/lib.rs | 3 +- src/buffer.rs | 133 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/plugin.rs | 119 +---------------------------------- src/wrapper/vst3.rs | 3 +- 6 files changed, 141 insertions(+), 121 deletions(-) create mode 100644 src/buffer.rs diff --git a/plugins/gain/src/lib.rs b/plugins/gain/src/lib.rs index b040b93e..558f59cf 100644 --- a/plugins/gain/src/lib.rs +++ b/plugins/gain/src/lib.rs @@ -18,10 +18,11 @@ extern crate nih_plug; use nih_plug::{ + buffer::Buffer, context::ProcessContext, formatters, param::{BoolParam, FloatParam, Param, Params, Range}, - plugin::{Buffer, BufferConfig, BusConfig, Plugin, ProcessStatus, Vst3Plugin}, + plugin::{BufferConfig, BusConfig, Plugin, ProcessStatus, Vst3Plugin}, util, }; use parking_lot::RwLock; diff --git a/plugins/sine/src/lib.rs b/plugins/sine/src/lib.rs index 2f09ebc5..dab5bf1d 100644 --- a/plugins/sine/src/lib.rs +++ b/plugins/sine/src/lib.rs @@ -18,10 +18,11 @@ extern crate nih_plug; use nih_plug::{ + buffer::Buffer, context::ProcessContext, formatters, param::{FloatParam, Param, Params, Range}, - plugin::{Buffer, BufferConfig, BusConfig, Plugin, ProcessStatus, Vst3Plugin}, + plugin::{BufferConfig, BusConfig, Plugin, ProcessStatus, Vst3Plugin}, util, }; use std::f32::consts; diff --git a/src/buffer.rs b/src/buffer.rs new file mode 100644 index 00000000..37bf9884 --- /dev/null +++ b/src/buffer.rs @@ -0,0 +1,133 @@ +// nih-plug: plugins, but rewritten in Rust +// Copyright (C) 2022 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/// The audio buffers used during processing. This contains the output audio output buffers with the +/// inputs already copied to the outputs. You can either use the iterator adapters to conveniently +/// and efficiently iterate over the samples, or you can do your own thing using the raw audio +/// buffers. +pub struct Buffer<'a> { + /// Contains slices for the plugin's outputs. You can't directly create a nested slice form + /// apointer to pointers, so this needs to be preallocated in the setup call and kept around + /// between process calls. And because storing a reference to this means a) that you need a lot + /// of lifetime annotations everywhere and b) that at some point you need unsound lifetime casts + /// because this `Buffers` either cannot have the same lifetime as the separately stored output + /// buffers, and it also cannot be stored in a field next to it because that would mean + /// containing mutable references to data stored in a mutex. + output_slices: Vec<&'a mut [f32]>, +} + +impl<'a> Buffer<'a> { + /// Construct a new buffer adapter based on a set of audio buffers. + pub fn new() -> Self { + Self { + output_slices: Vec::new(), + } + } + + /// Returns true if this buffer does not contain any samples. + pub fn is_empty(&self) -> bool { + self.output_slices.is_empty() || self.output_slices[0].is_empty() + } + + /// Obtain the raw audio buffers. + pub fn as_raw(&mut self) -> &mut [&'a mut [f32]] { + &mut self.output_slices + } + + /// Iterate over the samples, returning a channel iterator for each sample. + pub fn iter_mut(&mut self) -> Samples<'_, 'a> { + Samples { + buffers: &mut self.output_slices, + current_sample: 0, + } + } + + /// Access the raw output slice vector. This neds to be resized to match the number of output + /// channels during the plugin's initialization. Then during audio processing, these slices + /// should be updated to point to the plugin's audio buffers. + /// + /// # Safety + /// + /// The stored slices must point to live data when this object is passed to the plugins' process + /// function. The rest of this object also assumes all channel lengths are equal. Panics will + /// likely occur if this is not the case. + pub unsafe fn as_raw_vec(&mut self) -> &mut Vec<&'a mut [f32]> { + &mut self.output_slices + } +} + +/// An iterator over all samples in the buffer, yielding iterators over each channel for every +/// sample. This iteration order offers good cache locality for per-sample access. +pub struct Samples<'outer, 'inner> { + /// The raw output buffers. + pub(self) buffers: &'outer mut [&'inner mut [f32]], + pub(self) current_sample: usize, +} + +impl<'outer, 'inner> Iterator for Samples<'outer, 'inner> { + type Item = Channels<'outer, 'inner>; + + fn next(&mut self) -> Option { + if self.current_sample < self.buffers[0].len() { + // SAFETY: We guarantee that each sample is only mutably borrowed once in the channels + // iterator + let buffers: &'outer mut _ = unsafe { &mut *(self.buffers as *mut _) }; + let channels = Channels { + buffers, + current_sample: self.current_sample, + current_channel: 0, + }; + + self.current_sample += 1; + + Some(channels) + } else { + None + } + } +} + +/// An iterator over the channel data for a sample, yielded by [Samples]. +pub struct Channels<'outer, 'inner> { + /// The raw output buffers. + pub(self) buffers: &'outer mut [&'inner mut [f32]], + pub(self) current_sample: usize, + pub(self) current_channel: usize, +} + +impl<'outer, 'inner> Iterator for Channels<'outer, 'inner> { + type Item = &'inner mut f32; + + fn next(&mut self) -> Option { + if self.current_channel < self.buffers.len() { + // SAFETY: These bounds have already been checked + let sample = unsafe { + self.buffers + .get_unchecked_mut(self.current_channel) + .get_unchecked_mut(self.current_sample) + }; + // SAFETY: It is not possible to have multiple mutable references to the same sample at + // the same time + let sample: &'inner mut f32 = unsafe { &mut *(sample as *mut f32) }; + + self.current_channel += 1; + + Some(sample) + } else { + None + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 04bb6ab2..1534d424 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,7 @@ // TODO: Once everything is more fleshed out, document the basic usage of this library +pub mod buffer; pub mod context; #[macro_use] pub mod debug; diff --git a/src/plugin.rs b/src/plugin.rs index b21e21db..aed4d227 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -16,6 +16,7 @@ use std::pin::Pin; +use crate::buffer::Buffer; use crate::context::ProcessContext; use crate::param::Params; @@ -140,121 +141,3 @@ pub enum ProcessStatus { /// infite tail. KeepAlive, } - -/// The audio buffers used during processing. This contains the output audio output buffers with the -/// inputs already copied to the outputs. You can either use the iterator adapters to conveniently -/// and efficiently iterate over the samples, or you can do your own thing using the raw audio -/// buffers. -pub struct Buffer<'a> { - /// Contains slices for the plugin's outputs. You can't directly create a nested slice form - /// apointer to pointers, so this needs to be preallocated in the setup call and kept around - /// between process calls. And because storing a reference to this means a) that you need a lot - /// of lifetime annotations everywhere and b) that at some point you need unsound lifetime casts - /// because this `Buffers` either cannot have the same lifetime as the separately stored output - /// buffers, and it also cannot be stored in a field next to it because that would mean - /// containing mutable references to data stored in a mutex. - output_slices: Vec<&'a mut [f32]>, -} - -impl<'a> Buffer<'a> { - /// Construct a new buffer adapter based on a set of audio buffers. - pub fn new() -> Self { - Self { - output_slices: Vec::new(), - } - } - - /// Returns true if this buffer does not contain any samples. - pub fn is_empty(&self) -> bool { - self.output_slices.is_empty() || self.output_slices[0].is_empty() - } - - /// Obtain the raw audio buffers. - pub fn as_raw(&mut self) -> &mut [&'a mut [f32]] { - &mut self.output_slices - } - - /// Iterate over the samples, returning a channel iterator for each sample. - pub fn iter_mut(&mut self) -> Samples<'_, 'a> { - Samples { - buffers: &mut self.output_slices, - current_sample: 0, - } - } - - /// Access the raw output slice vector. This neds to be resized to match the number of output - /// channels during the plugin's initialization. Then during audio processing, these slices - /// should be updated to point to the plugin's audio buffers. - /// - /// # Safety - /// - /// The stored slices must point to live data when this object is passed to the plugins' process - /// function. The rest of this object also assumes all channel lengths are equal. Panics will - /// likely occur if this is not the case. - pub unsafe fn as_raw_vec(&mut self) -> &mut Vec<&'a mut [f32]> { - &mut self.output_slices - } -} - -/// An iterator over all samples in the buffer, yielding iterators over each channel for every -/// sample. This iteration order offers good cache locality for per-sample access. -pub struct Samples<'outer, 'inner> { - /// The raw output buffers. - pub(self) buffers: &'outer mut [&'inner mut [f32]], - pub(self) current_sample: usize, -} - -impl<'outer, 'inner> Iterator for Samples<'outer, 'inner> { - type Item = Channels<'outer, 'inner>; - - fn next(&mut self) -> Option { - if self.current_sample < self.buffers[0].len() { - // SAFETY: We guarantee that each sample is only mutably borrowed once in the channels - // iterator - let buffers: &'outer mut _ = unsafe { &mut *(self.buffers as *mut _) }; - let channels = Channels { - buffers, - current_sample: self.current_sample, - current_channel: 0, - }; - - self.current_sample += 1; - - Some(channels) - } else { - None - } - } -} - -/// An iterator over the channel data for a sample, yielded by [Samples]. -pub struct Channels<'outer, 'inner> { - /// The raw output buffers. - pub(self) buffers: &'outer mut [&'inner mut [f32]], - pub(self) current_sample: usize, - pub(self) current_channel: usize, -} - -impl<'outer, 'inner> Iterator for Channels<'outer, 'inner> { - type Item = &'inner mut f32; - - fn next(&mut self) -> Option { - if self.current_channel < self.buffers.len() { - // SAFETY: These bounds have already been checked - let sample = unsafe { - self.buffers - .get_unchecked_mut(self.current_channel) - .get_unchecked_mut(self.current_sample) - }; - // SAFETY: It is not possible to have multiple mutable references to the same sample at - // the same time - let sample: &'inner mut f32 = unsafe { &mut *(sample as *mut f32) }; - - self.current_channel += 1; - - Some(sample) - } else { - None - } - } -} diff --git a/src/wrapper/vst3.rs b/src/wrapper/vst3.rs index 33f2b050..41f49994 100644 --- a/src/wrapper/vst3.rs +++ b/src/wrapper/vst3.rs @@ -40,10 +40,11 @@ use vst3_sys::vst::{ use vst3_sys::VST3; use widestring::U16CStr; +use crate::buffer::Buffer; use crate::context::{EventLoop, MainThreadExecutor, OsEventLoop, ProcessContext}; use crate::param::internals::ParamPtr; use crate::param::Param; -use crate::plugin::{Buffer, BufferConfig, BusConfig, Plugin, ProcessStatus, Vst3Plugin}; +use crate::plugin::{BufferConfig, BusConfig, Plugin, ProcessStatus, Vst3Plugin}; use crate::wrapper::state::{ParamValue, State}; use crate::wrapper::util::{hash_param_id, strlcpy, u16strlcpy};