diff --git a/atom/src/port.rs b/atom/src/port.rs index 633fbcb9..d8f3dd67 100644 --- a/atom/src/port.rs +++ b/atom/src/port.rs @@ -105,11 +105,13 @@ impl PortType for AtomPort { type InputPortType = PortReader<'static>; type OutputPortType = PortWriter<'static>; + #[inline] unsafe fn input_from_raw(pointer: NonNull, _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, _sample_count: u32) -> PortWriter<'static> { let space = RootMutSpace::from_atom(pointer.cast().as_mut()); PortWriter::new(space) diff --git a/core/src/port.rs b/core/src/port.rs index 52eccbc3..76c8a3f1 100644 --- a/core/src/port.rs +++ b/core/src/port.rs @@ -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::*; @@ -36,79 +43,6 @@ pub trait PortType { unsafe fn output_from_raw(pointer: NonNull, 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, 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, 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, _sample_count: u32) -> f32 { - *(pointer.cast().as_ref()) - } - - unsafe fn output_from_raw(pointer: NonNull, _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, 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, 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. @@ -140,13 +74,9 @@ impl Deref for InputPort { impl PortHandle for InputPort { #[inline] unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option { - 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), + }) } } @@ -176,13 +106,9 @@ impl DerefMut for OutputPort { impl PortHandle for OutputPort { #[inline] unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option { - 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), + }) } } @@ -211,7 +137,7 @@ impl PortHandle for Option { /// optional_control_input: Option>, /// } /// -/// 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. /// diff --git a/core/src/port/audio.rs b/core/src/port/audio.rs new file mode 100644 index 00000000..ac755e90 --- /dev/null +++ b/core/src/port/audio.rs @@ -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