Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement in-place processing (fix Ardour support) #93

Merged
merged 8 commits into from
Aug 7, 2021
2 changes: 2 additions & 0 deletions atom/src/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,13 @@ impl PortType for AtomPort {
type InputPortType = PortReader<'static>;
type OutputPortType = PortWriter<'static>;

#[inline]
unsafe fn input_from_raw(pointer: NonNull<c_void>, _sample_count: u32) -> PortReader<'static> {
let space = Space::from_atom(pointer.cast().as_ref());
PortReader::new(space)
}

#[inline]
unsafe fn output_from_raw(pointer: NonNull<c_void>, _sample_count: u32) -> PortWriter<'static> {
let space = RootMutSpace::from_atom(pointer.cast().as_mut());
PortWriter::new(space)
Expand Down
104 changes: 15 additions & 89 deletions core/src/port.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
//! Types to declare derivable port collections.
//!
//! Every plugin has a type of [`PortCollection`](trait.PortCollection.html) which is used to handle input/output ports. In order to make the creation of these port collection types easier, `PortCollection` can simply be derived. However, the macro that implements `PortCollection` requires the fields of the struct to have specific types. These types are provided in this module.
mod audio;
mod control;
mod cv;

pub use audio::*;
pub use control::*;
pub use cv::*;

use std::ffi::c_void;
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
use urid::UriBound;

pub use lv2_core_derive::*;

Expand Down Expand Up @@ -36,79 +43,6 @@ pub trait PortType {
unsafe fn output_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::OutputPortType;
}

/// Audio port type.
///
/// Audio ports are the most common type of input/output ports: Their input is a slice of audio samples, as well as their output.
pub struct Audio;

unsafe impl UriBound for Audio {
const URI: &'static [u8] = ::lv2_sys::LV2_CORE__AudioPort;
}

impl PortType for Audio {
type InputPortType = &'static [f32];
type OutputPortType = &'static mut [f32];

#[inline]
unsafe fn input_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::InputPortType {
std::slice::from_raw_parts(pointer.as_ptr() as *const f32, sample_count as usize)
}

#[inline]
unsafe fn output_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::OutputPortType {
std::slice::from_raw_parts_mut(pointer.as_ptr() as *mut f32, sample_count as usize)
}
}

/// Control value port type.
///
/// Control ports in general are used to control the behaviour of the plugin. These control value ports only have one value per `run` call and therefore don't have a fixed sampling rate.
///
/// Therefore, their input is a floating-point number and their output is a mutable reference to a floating-point number.
pub struct Control;

unsafe impl UriBound for Control {
const URI: &'static [u8] = ::lv2_sys::LV2_CORE__ControlPort;
}

impl PortType for Control {
type InputPortType = f32;
type OutputPortType = &'static mut f32;

#[inline]
unsafe fn input_from_raw(pointer: NonNull<c_void>, _sample_count: u32) -> f32 {
*(pointer.cast().as_ref())
}

unsafe fn output_from_raw(pointer: NonNull<c_void>, _sample_count: u32) -> &'static mut f32 {
(pointer.as_ptr() as *mut f32).as_mut().unwrap()
}
}

/// CV port type.
///
/// Control ports in general are used to control the behaviour of the plugin. CV ports are sampled just like [audio data](struct.Audio.html). This means that audio data is often valid CV data, but CV data generally is not audio data, because it may not be within the audio bounds of -1.0 to 1.0.
pub struct CV;

unsafe impl UriBound for CV {
const URI: &'static [u8] = ::lv2_sys::LV2_CORE__CVPort;
}

impl PortType for CV {
type InputPortType = &'static [f32];
type OutputPortType = &'static mut [f32];

#[inline]
unsafe fn input_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::InputPortType {
std::slice::from_raw_parts(pointer.as_ptr() as *const f32, sample_count as usize)
}

#[inline]
unsafe fn output_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::OutputPortType {
std::slice::from_raw_parts_mut(pointer.as_ptr() as *mut f32, sample_count as usize)
}
}

/// Abstraction of safe port handles.
pub trait PortHandle: Sized {
/// Try to create a port handle from a port connection pointer and the sample count.
Expand Down Expand Up @@ -140,13 +74,9 @@ impl<T: PortType> Deref for InputPort<T> {
impl<T: PortType> PortHandle for InputPort<T> {
#[inline]
unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option<Self> {
if let Some(pointer) = NonNull::new(pointer) {
Some(Self {
port: T::input_from_raw(pointer, sample_count),
})
} else {
None
}
Some(Self {
port: T::input_from_raw(NonNull::new(pointer)?, sample_count),
})
}
}

Expand Down Expand Up @@ -176,13 +106,9 @@ impl<T: PortType> DerefMut for OutputPort<T> {
impl<T: PortType> PortHandle for OutputPort<T> {
#[inline]
unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option<Self> {
if let Some(pointer) = NonNull::new(pointer) {
Some(Self {
port: T::output_from_raw(pointer, sample_count),
})
} else {
None
}
Some(Self {
port: T::output_from_raw(NonNull::new(pointer)?, sample_count),
})
}
}

Expand Down Expand Up @@ -211,7 +137,7 @@ impl<T: PortHandle> PortHandle for Option<T> {
/// optional_control_input: Option<InputPort<Control>>,
/// }
///
/// Please note that port indices are mapped in the order of occurence; In our example, the implementation will treat `audio_input` as port `0`, `audio_output` as port `1` and so on. Therefore, your plugin definition and your port collection have to match. Otherwise, undefined behaviour will occur.
/// Please note that port indices are mapped in the order of occurrence; In our example, the implementation will treat `audio_input` as port `0`, `audio_output` as port `1` and so on. Therefore, your plugin definition and your port collection have to match. Otherwise, undefined behaviour will occur.
pub trait PortCollection: Sized {
/// The type of the port pointer cache.
///
Expand Down
155 changes: 155 additions & 0 deletions core/src/port/audio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
use crate::port::PortType;
use std::cell::Cell;
use std::ffi::c_void;
use std::ptr::NonNull;
use urid::UriBound;

/// A port connected to an array of float audio samples. Using this port **requires** the `inPlaceBroken` feature.
///
/// Ports of this type are connected to a buffer of float audio samples, represented as a slice.
///
/// Audio samples are normalized between -1.0 and 1.0, though there is no requirement for samples to be strictly within this range.
///
/// See the [LV2 reference](https://lv2plug.in/ns/lv2core#AudioPort) for more information.
///
/// # Example
///
/// This very simple amplifier plugin multiplies the input sample by 2 and outputs the result.
///
/// ```
/// # use lv2_core::prelude::*;
/// # use urid::*;
/// # #[uri("http://lv2plug.in/plugins.rs/simple_amp")]
/// # struct SimpleAmp;
/// #[derive(PortCollection)]
/// struct SimpleAmpPorts {
/// input: InputPort<Audio>,
/// output: OutputPort<Audio>,
/// }
///
/// impl Plugin for SimpleAmp {
/// type Ports = SimpleAmpPorts;
/// # type InitFeatures = ();
/// # type AudioFeatures = ();
/// # fn new(plugin_info: &PluginInfo,features: &mut Self::InitFeatures) -> Option<Self> {
/// # unimplemented!()
/// # }
/// // some implementation details elided…
///
/// fn run(&mut self, ports: &mut SimpleAmpPorts, _: &mut (), _: u32) {
/// // Input and Output dereference to `&[f32]` and `&mut [f32]`, respectively.
/// let input = ports.input.iter();
/// let output = ports.output.iter_mut();
///
/// for (input_sample, output_sample) in input.zip(output) {
/// *output_sample = *input_sample * 2.0;
/// }
/// }
/// }
///
///
/// ```
///
/// # Safety
///
/// Using this port type requires the `inPlaceBroken` LV2 feature in your plugin. Because this port
/// type uses shared (`&[f32]`) and exclusive (`&mut [f32]`) references to its data, LV2 hosts
/// MUST NOT use the same buffer for both the input and the output.
/// However, do note that some hosts (Ardour, Zrythm, etc.) do not support `inPlaceBroken` plugins.
///
/// Use [`InPlaceAudio`] instead if you do not want to enforce this restriction on hosts,
/// and do not need references pointing into the buffer's contents.
pub struct Audio;

unsafe impl UriBound for Audio {
const URI: &'static [u8] = ::lv2_sys::LV2_CORE__AudioPort;
}

impl PortType for Audio {
type InputPortType = &'static [f32];
type OutputPortType = &'static mut [f32];

#[inline]
unsafe fn input_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::InputPortType {
std::slice::from_raw_parts(pointer.as_ptr() as *const f32, sample_count as usize)
}

#[inline]
unsafe fn output_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::OutputPortType {
std::slice::from_raw_parts_mut(pointer.as_ptr() as *mut f32, sample_count as usize)
}
}

/// A port connected to an array of float audio samples. This port type can safely operate on shared input and output buffers.
///
/// Ports of this type are connected to a buffer of float audio samples, represented as a slice of [`Cell`s](std::cell::Cell).
///
/// Audio samples are normalized between -1.0 and 1.0, though there is no requirement for samples to be strictly within this range.
///
/// See the [LV2 reference](https://lv2plug.in/ns/lv2core#AudioPort) for more information.
///
/// # Example
///
/// This very simple amplifier plugin multiplies the input sample by 2 and outputs the result.
///
/// ```
/// # use lv2_core::prelude::*;
/// # use urid::*;
/// # #[uri("http://lv2plug.in/plugins.rs/simple_amp")]
/// # struct SimpleAmp;
/// #[derive(PortCollection)]
/// struct SimpleAmpPorts {
/// input: InputPort<InPlaceAudio>,
/// output: OutputPort<InPlaceAudio>,
/// }
///
/// impl Plugin for SimpleAmp {
/// type Ports = SimpleAmpPorts;
/// # type InitFeatures = ();
/// # type AudioFeatures = ();
/// # fn new(plugin_info: &PluginInfo,features: &mut Self::InitFeatures) -> Option<Self> {
/// # unimplemented!()
/// # }
/// // some implementation details elided…
///
/// fn run(&mut self, ports: &mut SimpleAmpPorts, _: &mut (), _: u32) {
/// // Input and Output both dereference to `&[Cell<f32>]`.
/// let input = ports.input.iter();
/// let output = ports.output.iter();
///
/// for (input_sample, output_sample) in input.zip(output) {
/// output_sample.set(input_sample.get() * 2.0);
/// }
/// }
/// }
///
///
/// ```
pub struct InPlaceAudio;

unsafe impl UriBound for InPlaceAudio {
const URI: &'static [u8] = ::lv2_sys::LV2_CORE__AudioPort;
}

impl PortType for InPlaceAudio {
type InputPortType = &'static [Cell<f32>];
type OutputPortType = &'static [Cell<f32>];

#[inline]
unsafe fn input_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::InputPortType {
Cell::from_mut(std::slice::from_raw_parts_mut(
pointer.as_ptr() as *mut f32,
sample_count as usize,
))
.as_slice_of_cells()
}

#[inline]
unsafe fn output_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::OutputPortType {
Cell::from_mut(std::slice::from_raw_parts_mut(
pointer.as_ptr() as *mut f32,
sample_count as usize,
))
.as_slice_of_cells()
}
}
Loading