Skip to content

Fix deref+port redo #108

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

Draft
wants to merge 18 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions atom/src/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,23 @@ impl PortType for AtomPort {
type OutputPortType = PortWriter<'static>;

#[inline]
unsafe fn input_from_raw(pointer: NonNull<c_void>, _sample_count: u32) -> PortReader<'static> {
let header = AtomHeader::from_raw(pointer.cast().as_ref());
PortReader::new(UnidentifiedAtom::from_header(header))
unsafe fn input_from_raw(
pointer: NonNull<c_void>,
_sample_count: u32,
) -> *const PortReader<'static> {
todo!();
//let header = AtomHeader::from_raw(pointer.cast().as_ref());
//PortReader::new(UnidentifiedAtom::from_header(header))
}

#[inline]
unsafe fn output_from_raw(pointer: NonNull<c_void>, _sample_count: u32) -> PortWriter<'static> {
let header = AtomHeader::from_raw_mut(pointer.cast().as_mut());
PortWriter::new(UnidentifiedAtom::from_header_mut(header).body_mut())
unsafe fn output_from_raw(
pointer: NonNull<c_void>,
_sample_count: u32,
) -> *mut PortWriter<'static> {
todo!();
//let header = AtomHeader::from_raw_mut(pointer.cast().as_mut());
//PortWriter::new(UnidentifiedAtom::from_header_mut(header).body_mut())
}
}

Expand Down
3 changes: 3 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,8 @@ urid = { version = "0.1.0", default-features = false }
lv2-sys = "2.0.0"
lv2-core-derive = { version = "2.1.0", optional = true }

[dev-dependencies]
urid = { version = "0.1.0", default-features = true }

[features]
default = ["lv2-core-derive"]
3 changes: 3 additions & 0 deletions core/src/feature/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ pub use cache::FeatureCache;
pub use core_features::*;
pub use descriptor::FeatureDescriptor;

#[cfg(feature = "lv2-core-derive")]
pub use lv2_core_derive::FeatureCollection;

use std::ffi::c_void;

/// All threading contexts of LV2 interface methods.
Expand Down
16 changes: 9 additions & 7 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@
//! ```
//! // Import everything we need.
//! use lv2_core::prelude::*;
//! use port::inplace::*; //imports ports supporting inplace processing
//! use urid::*;
//!
//! // The input and output ports are defined by a struct which implements the `PortCollection` trait.
//! // In this case, there is an input control port for the gain of the amplification, an input audio
//! // port and an output audio port.
//! #[derive(PortCollection)]
//! struct Ports {
//! gain: InputPort<Control>,
//! input: InputPort<Audio>,
//! output: OutputPort<Audio>,
//! gain: ControlInput,
//! input: AudioInput,
//! output: AudioOutput,
//! }
//!
//! // The plugin struct. In this case, we don't need any data and therefore, this struct is empty.
Expand Down Expand Up @@ -44,14 +45,15 @@
//! // Process a chunk of audio. The audio ports are dereferenced to slices, which the plugin
//! // iterates over.
//! fn run(&mut self, ports: &mut Ports, _features: &mut (), _: u32) {
//! let coef = if *(ports.gain) > -90.0 {
//! 10.0_f32.powf(*(ports.gain) * 0.05)
//! let gain = ports.gain.get();
//! let coef = if gain > -90.0 {
//! 10.0_f32.powf(gain * 0.05)
//! } else {
//! 0.0
//! };
//!
//! for (in_frame, out_frame) in Iterator::zip(ports.input.iter(), ports.output.iter_mut()) {
//! *out_frame = in_frame * coef;
//! for (in_frame, out_frame) in Iterator::zip(ports.input.iter(), ports.output.iter()) {
//! out_frame.set(in_frame.get() *coef);
//! }
//! }
//! }
Expand Down
195 changes: 93 additions & 102 deletions core/src/port.rs
Original file line number Diff line number Diff line change
@@ -1,118 +1,92 @@
//! 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::*;

//! 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.
pub mod inplace;
pub mod not_inplace;

use std::cell::Cell;
use std::ffi::c_void;
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;

#[cfg(feature = "lv2-core-derive")]
pub use lv2_core_derive::*;

/// Generalization of port types.
///
/// A port can read input or create a pointer to the output, but the exact type of input/output (pointer) depends on the type of port. This trait generalizes these types and behaviour.
pub trait PortType {
/// The type of input read by the port.
type InputPortType: Sized;
/// The type of output reference created by the port.
type OutputPortType: Sized;

/// Read data from the pointer or create a reference to the input.
///
/// If the resulting data is a slice, `sample_count` is the length of the slice.
///
/// # Safety
///
/// This method is unsafe because one needs to de-reference a raw pointer to implement this method.
unsafe fn input_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::InputPortType;

/// Create a reference to the data where output should be written to.
///
/// If the data is a slice, `sample_count` is the length of the slice.
///
/// # Safety
///
/// This method is unsafe because one needs to de-reference a raw pointer to implement this method.
unsafe fn output_from_raw(pointer: NonNull<c_void>, sample_count: u32) -> Self::OutputPortType;
}

/// Abstraction of safe port handles.
pub trait PortHandle: Sized {
/// Try to create a port handle from a port connection pointer and the sample count.
///
/// If the pointer is null, this method will return `None`.
///
/// # Safety
///
/// Implementing this method requires a de-referentation of a raw pointer and therefore, it is unsafe.
unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option<Self>;
}

/// Handle for input ports.
/// A readonly cell. Used to give read only access for input port with inplace processing support.
///
/// Fields of this type can be dereferenced to the input type of the port type.
pub struct InputPort<T: PortType> {
port: T::InputPortType,
/// This cell is used to give read only access to data when a writable alias may exist for the
/// underlying memory location. This is used by inplace input port because it's allow inplace
/// processing while preventing to write data through the current input port.
#[repr(transparent)]
pub struct RCell<T: ?Sized> {
value: Cell<T>,
}

impl<T: PortType> Deref for InputPort<T> {
type Target = T::InputPortType;

impl<T: Copy> RCell<T> {
/// Returns a copy of the contained value.
#[inline]
fn deref(&self) -> &Self::Target {
&self.port
pub fn get(&self) -> T {
self.value.get()
}
}

impl<T: PortType> PortHandle for InputPort<T> {
#[inline]
unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option<Self> {
Some(Self {
port: T::input_from_raw(NonNull::new(pointer)?, sample_count),
})
impl<T> RCell<[T]> {
/// Returns a `&[RCell<T>]` from a `&RCell<[T]>`
pub fn as_slice_of_cells(&self) -> &[RCell<T>] {
// SAFETY: `RCell<T>` has the same memory layout as `T`.
unsafe { &*(self as *const RCell<[T]> as *const [RCell<T>]) }
}
}

/// Handle for output ports.
/// A read/write cell. Used to give read/write access for output port with inplace processing
/// support.
///
/// Fields of this type can be dereferenced to the output type of the port type.
pub struct OutputPort<T: PortType> {
port: T::OutputPortType,
/// This cell is used to give read and write access to data when an alias may exist for the
/// underlying memory location. It works by giving interior mutability, like [`std::cell::Cell`].
/// This is used by inplace output because it's allow inplace processing.
// Note: technically, a std::Cell could be used, but custom cell is better to express the specific
// usage.
#[repr(transparent)]
pub struct RwCell<T: ?Sized> {
value: Cell<T>,
}

impl<T: PortType> Deref for OutputPort<T> {
type Target = T::OutputPortType;

impl<T: Copy> RwCell<T> {
/// Returns a copy of the contained value.
#[inline]
fn deref(&self) -> &Self::Target {
&self.port
pub fn get(&self) -> T {
self.value.get()
}
}

impl<T: PortType> DerefMut for OutputPort<T> {
impl<T> RwCell<T> {
///Sets the contained value.
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.port
pub fn set(&self, val: T) {
self.value.set(val);
}
}

impl<T: PortType> PortHandle for OutputPort<T> {
#[inline]
unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option<Self> {
Some(Self {
port: T::output_from_raw(NonNull::new(pointer)?, sample_count),
})
impl<T> RwCell<[T]> {
/// Returns a `&[RwCell<T>]` from a `&RwCell<[T]>`
pub fn as_slice_of_cells(&self) -> &[RwCell<T>] {
// SAFETY: `RwCell<T>` has the same memory layout as `T`.
unsafe { &*(self as *const RwCell<[T]> as *const [RwCell<T>]) }
}
}

/// Abstraction of safe port handles.
pub trait PortHandle: Sized {
/// Try to create a port handle from a port connection pointer and the sample count.
///
/// If the pointer is null, this method will return `None`.
///
/// # Safety
///
/// Implementing this method requires a de-referentation of a raw pointer and therefore, it is
/// unsafe.
unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option<Self>;
}

impl<T: PortHandle> PortHandle for Option<T> {
unsafe fn from_raw(pointer: *mut c_void, sample_count: u32) -> Option<Self> {
Some(T::from_raw(pointer, sample_count))
Expand All @@ -121,37 +95,51 @@ impl<T: PortHandle> PortHandle for Option<T> {

/// Collection of IO ports.
///
/// Plugins do not handle port management on their own. Instead, they define a struct with all of the required ports. Then, the plugin instance will collect the port pointers from the host and create a `PortCollection` instance for every `run` call. Using this instance, plugins have access to all of their required ports.
/// Plugins do not handle port management on their own. Instead, they define a struct with all of
/// the required ports. Then, the plugin instance will collect the port pointers from the host and
/// create a `PortCollection` instance for every `run` call. Using this instance, plugins have
/// access to all of their required ports.
///
/// # Implementing
///
/// The most convenient way to create a port collections is to define a struct with port types from the [`port`](index.html) module and then simply derive `PortCollection` for it. An example:
///
/// use lv2_core::port::*;
/// The most convenient way to create a port collections is to define a struct with port types from
/// the [`port`](index.html) module and then simply derive `PortCollection` for it. An example:
/// ```
/// use lv2_core::prelude::*;
/// use lv2_core::port::inplace::*;
///
/// #[derive(PortCollection)]
/// struct MyPortCollection {
/// audio_input: InputPort<Audio>,
/// audio_output: OutputPort<Audio>,
/// control_input: InputPort<Control>,
/// control_output: OutputPort<Control>,
/// optional_control_input: Option<InputPort<Control>>,
/// audio_input: AudioInput,
/// audio_output: AudioOutput,
/// control_input: ControlInput,
/// control_output: ControlOutput,
/// optional_control_input: Option<ControlInput>,
/// }
/// ```
///
/// 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.
/// 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.
///
/// The host passes port pointers to the plugin one by one and in an undefined order. Therefore, the plugin instance can not collect these pointers in the port collection directly. Instead, the pointers are stored in a cache which is then used to create the proper port collection.
/// The host passes port pointers to the plugin one by one and in an undefined order.
/// Therefore, the plugin instance can not collect these pointers in the port collection
/// directly. Instead, the pointers are stored in a cache which is then used to create the
/// proper port collection.
type Cache: PortPointerCache;

/// Try to construct a port collection instance from a port pointer cache.
///
/// If one of the port connection pointers is null, this method will return `None`, because a `PortCollection` can not be constructed.
/// If one of the port connection pointers is null, this method will return `None`, because a
/// `PortCollection` can not be constructed.
///
/// # Safety
///
/// Since the pointer cache is only storing the pointers, implementing this method requires the de-referencation of raw pointers and therefore, this method is unsafe.
/// Since the pointer cache is only storing the pointers, implementing this method requires the
/// de-referencation of raw pointers and therefore, this method is unsafe.
unsafe fn from_connections(cache: &Self::Cache, sample_count: u32) -> Option<Self>;
}

Expand All @@ -165,11 +153,14 @@ impl PortCollection for () {

/// Cache for port connection pointers.
///
/// The host will pass the port connection pointers one by one and in an undefined order. Therefore, the `PortCollection` struct can not be created instantly. Instead, the pointers will be stored in a cache, which is then used to create a proper port collection for the plugin.
/// The host will pass the port connection pointers one by one and in an undefined order.
/// Therefore, the `PortCollection` struct can not be created instantly. Instead, the pointers will
/// be stored in a cache, which is then used to create a proper port collection for the plugin.
pub trait PortPointerCache: Sized + Default {
/// Store the connection pointer for the port with index `index`.
///
/// The passed pointer may not be valid yet and therefore, implementors should only store the pointer, not dereference it.
/// The passed pointer may not be valid yet and therefore, implementors should only store the
/// pointer, not dereference it.
fn connect(&mut self, index: u32, pointer: *mut c_void);
}

Expand Down
Loading