lv2_atom/
lib.rs

1//! General data IO for LV2 plugins.
2//!
3//! This crate empowers LV2 plugins to communicate using a common type system, which is defined in the [LV2 Atom Specification](http://lv2plug.in/ns/ext/atom/atom.html). Many plugin standards only provide audio and MIDI IO, but LV2 plugins can read and write anything from simple integers over vectors and strings to event sequences using this specification.
4//!
5//! # How to use atoms
6//!
7//! The foundation of this crate is the [`Atom`](trait.Atom.html) trait. This trait provides type definitions and methods to read and write atoms. However, you will never handle these types directly, only via handles and generics.
8//!
9//! Your entry point to the atom system are the [`PortReader`](port/struct.PortReader.html) and [`PortWriter`](port/struct.PortWriter.html) structs provided by your plugin's ports. If you provide them the URID of the desired atom type as a atom-specific parameter, they will try to retrieve a handle that either lets you access the contents of an atom or write additional data to it. This is a general pattern in this crate; you will encounter it several times. From there, you use the handles as documented.
10//!
11//! # Example
12//!
13//! ```
14//! use lv2_atom::prelude::*;
15//! use lv2_core::prelude::*;
16//! use lv2_units::prelude::*;
17//! use urid::*;
18//!
19//! #[derive(PortCollection)]
20//! struct MyPorts {
21//!     input: InputPort<AtomPort>,
22//!     output: OutputPort<AtomPort>,
23//! }
24//!
25//! #[derive(URIDCollection)]
26//! struct MyURIDs {
27//!     atom: AtomURIDCollection,
28//!     units: UnitURIDCollection,
29//! }
30//!
31//! /// Something like a plugin's run method.
32//! fn run(ports: &mut MyPorts, urids: &MyURIDs) {
33//!     // Get the read handle to the sequence.
34//!     let input_sequence = ports.input.read(
35//!         urids.atom.sequence,
36//!         urids.units.beat
37//!     ).unwrap();
38//!
39//!     // Get the write handle to the sequence.
40//!     let mut output_sequence = ports.output.init(
41//!         urids.atom.sequence,
42//!         TimeStampURID::Frames(urids.units.frame)
43//!     ).unwrap();
44//!
45//!     // Iterate through all events in the input sequence.
46//!     for event in input_sequence {
47//!         // An event contains a timestamp and an atom.
48//!         let (timestamp, atom) = event;
49//!         // If the read atom is a 32-bit integer...
50//!         if let Some(integer) = atom.read(urids.atom.int, ()) {
51//!             // Multiply it by two and write it to the sequence.
52//!             output_sequence.init(timestamp, urids.atom.int, integer * 2).unwrap();
53//!         } else {
54//!             // Forward the atom to the sequence without a change.
55//!             output_sequence.forward(timestamp, atom).unwrap();
56//!         }
57//!     }
58//! }
59//! ```
60//!
61//! # Internals
62//!
63//! Internally, all atoms are powered by the structs in the [`space`](space/index.html) module. They safely abstract the reading and writing process and assure that no memory is improperly accessed or leaked and that alignments are upheld. If you simply want to use the atoms in this crate, you don't need to deal with. They are only interesting if you want to create your own atom types.
64extern crate lv2_sys as sys;
65extern crate lv2_units as units;
66
67pub mod chunk;
68pub mod object;
69pub mod scalar;
70pub mod sequence;
71pub mod space;
72pub mod string;
73pub mod tuple;
74pub mod vector;
75
76#[cfg(feature = "lv2-core")]
77pub mod port;
78
79/// Prelude of `lv2_atom` for wildcard usage.
80pub mod prelude {
81    use crate::*;
82
83    pub use crate::{Atom, AtomURIDCollection, UnidentifiedAtom};
84    pub use chunk::Chunk;
85    pub use object::{Object, ObjectHeader, PropertyHeader};
86    pub use port::AtomPort;
87    pub use scalar::{AtomURID, Bool, Double, Float, Int, Long};
88    pub use sequence::{Sequence, TimeStamp, TimeStampURID};
89    pub use space::{FramedMutSpace, MutSpace, Space};
90    pub use string::{Literal, LiteralInfo, String};
91    pub use tuple::Tuple;
92    pub use vector::Vector;
93}
94
95use space::*;
96use urid::*;
97
98#[derive(Clone, URIDCollection)]
99/// Collection with the URIDs of all `UriBound`s in this crate.
100pub struct AtomURIDCollection {
101    pub blank: URID<object::Blank>,
102    pub double: URID<scalar::Double>,
103    pub float: URID<scalar::Float>,
104    pub int: URID<scalar::Int>,
105    pub long: URID<scalar::Long>,
106    pub urid: URID<scalar::AtomURID>,
107    pub bool: URID<scalar::Bool>,
108    vector: URID<vector::Vector<scalar::Int>>,
109    pub chunk: URID<chunk::Chunk>,
110    pub literal: URID<string::Literal>,
111    pub object: URID<object::Object>,
112    pub property: URID<object::Property>,
113    pub string: URID<string::String>,
114    pub tuple: URID<tuple::Tuple>,
115    pub sequence: URID<sequence::Sequence>,
116}
117
118impl AtomURIDCollection {
119    pub fn vector<S: scalar::ScalarAtom>(&self) -> URID<vector::Vector<S>> {
120        unsafe { URID::new_unchecked(self.vector.get()) }
121    }
122}
123
124/// Atom type.
125///
126/// This is the foundation of this crate: Types that implement `Atom` define the reading and writing functions for an atom type. However, these types will never be constructed; They are only names to be used for generic type arguments.
127///
128/// This trait has two lifetime parameters: The first one is the lifetime of the atom in memory. In practice, this will often be `'static`, but it's good to keep it generic for testing purposes. The second parameter is the lifetime of the `MutSpace` borrowed by the `FramedMutSpace` parameter in the `write` method. Since the `WriteParameter` may contain this `FramedMutSpace`, it has to be assured that it lives long enough. Since the referenced `MutSpace` also has to borrow the atom, it may not live longer than the atom.
129pub trait Atom<'a, 'b>: UriBound
130where
131    'a: 'b,
132{
133    /// The atom-specific parameter of the `read` function.
134    ///
135    /// If your atom does not need a reading parameter, you may set it to `()`.
136    type ReadParameter;
137
138    /// The return value of the `read` function.
139    ///
140    /// It may contain a reference to the atom and therefore may not outlive it.
141    type ReadHandle: 'a;
142
143    /// The atom-specific parameter of the `write` function.
144    ///
145    /// If your atom does not need a writing parameter, you may set it to `()`.
146    type WriteParameter;
147
148    /// The return value of the `write` function.
149    ///
150    /// It may contain a reference to a `MutSpace` and therefore may not outlive it.
151    type WriteHandle: 'b;
152
153    /// Read the body of the atom.
154    ///
155    /// The passed space exactly covers the body of the atom, excluding the header. You may assume that the body is actually of your atom type, since the URID of the atom was checked beforehand.
156    ///
157    /// If the atom is malformed, you may not panic and return `None` instead.
158    fn read(body: Space<'a>, parameter: Self::ReadParameter) -> Option<Self::ReadHandle>;
159
160    /// Initialize the body of the atom.
161    ///
162    /// In this method, the atom is prepared for the writing handle. Usually, the atom will not be
163    /// valid when initializied; Users have to use the write handle to make it valid.
164    ///
165    /// The frame of the atom was already initialized, containing the URID.
166    ///
167    /// If space is insufficient, you may not panic and return `None` instead. The written results are assumed to be malformed.
168    fn init(
169        frame: FramedMutSpace<'a, 'b>,
170        parameter: Self::WriteParameter,
171    ) -> Option<Self::WriteHandle>;
172}
173
174/// An atom of yet unknown type.
175///
176/// This is used by reading handles that have to return a reference to an atom, but can not check it's type. This struct contains a `Space` containing the header and the body of the atom and can identify/read the atom from it.
177#[derive(Clone, Copy)]
178pub struct UnidentifiedAtom<'a> {
179    space: Space<'a>,
180}
181
182impl<'a> UnidentifiedAtom<'a> {
183    /// Construct a new unidentified atom.
184    ///
185    /// The space actually has to contain an atom. If it doesn't, crazy (but not undefined) things can happen.
186    pub fn new(space: Space<'a>) -> Self {
187        Self { space }
188    }
189
190    /// Try to read the atom.
191    ///
192    /// To identify the atom, it's URID and an atom-specific parameter is needed. If the atom was identified, a reading handle is returned.
193    pub fn read<'b, A: Atom<'a, 'b>>(
194        self,
195        urid: URID<A>,
196        parameter: A::ReadParameter,
197    ) -> Option<A::ReadHandle> {
198        self.space
199            .split_atom_body(urid)
200            .map(|(body, _)| body)
201            .and_then(|body| A::read(body, parameter))
202    }
203
204    /// Retrieve the type URID of the atom.
205    ///
206    /// This can be used to identify atoms without actually reading them.
207    pub fn type_urid(self) -> Option<URID> {
208        self.space
209            .split_type::<sys::LV2_Atom>()
210            .and_then(|(header, _)| URID::new(header.type_))
211    }
212}