lv2rs_atom/
sequence.rs

1//! Atom tuple with time stamps for every contained atom.
2//!
3//! A sequence is basically a tuple, but every contained atom is marked with the time stamp. This
4//! way, these atoms, now called "events", can be matched to frames in audio slices and processed
5//! with accurate timing. However, these time stamps can be given either in frames, which
6//! correspond to elements in an audio data slice, or in beats, a more abstract, tempo-independent
7//! way of describing timing.
8//!
9//! When writing a sequence, you have to pass over the time unit the sequence should. However, after
10//! it was initialized, a sequence does not contain any atoms. These have to be pushed to the sequence
11//! using the [`SequenceWritingFrame`](trait.SequenceWritingFrame.html) trait. Every
12//! writing frame implements this trait via a blanket implementation and the trait is included in
13//! the crate's prelude. You can, therefore, act as if the extended methods were normal methods of a
14//! writing frame.
15//!
16//! Reading atoms is done by iterating through all atoms one by one. Iterators are produced by the
17//! [`iter`](type.Tuple.html#method.iter) method.
18//!
19//! An example:
20//!
21//!     extern crate lv2rs_atom as atom;
22//!     extern crate lv2rs_urid as urid;
23//!
24//!     use atom::prelude::*;
25//!     use atom::ports::*;
26//!     use urid::{CachedMap, debug::DebugMap};
27//!     use std::ffi::CStr;
28//!
29//!     pub struct Plugin {
30//!         in_port: AtomInputPort<Tuple>,
31//!         out_port: AtomOutputPort<Tuple>,
32//!         urids: CachedMap,
33//!     }
34//!
35//!     impl Plugin {
36//!         /// Simulated `run` method.
37//!         fn run(&mut self) {
38//!             // Writing
39//!             {
40//!                 let mut frame =
41//!                     unsafe { self.out_port.write_atom_body(&(), &mut self.urids) }.unwrap();
42//!                 frame.push_atom::<i32>(&42, &mut self.urids).unwrap();
43//!                 frame.push_atom::<f32>(&17.0, &mut self.urids).unwrap();
44//!             }
45//!
46//!             let i32_urid = self.urids.map(<i32 as AtomBody>::get_uri());
47//!             let f32_urid = self.urids.map(<f32 as AtomBody>::get_uri());
48//!
49//!             // Reading.
50//!             let tuple = unsafe { self.in_port.get_atom_body(&mut self.urids) }.unwrap();
51//!             for sub_atom in tuple.iter() {
52//!                 match unsafe { sub_atom.get_body::<i32>(&mut self.urids) } {
53//!                     Ok(integer) => {
54//!                         assert_eq!(42, *integer);
55//!                         continue
56//!                     }
57//!                     Err(_) => (),
58//!                 }
59//!                 match unsafe { sub_atom.get_body::<f32>(&mut self.urids) } {
60//!                     Ok(float) => {
61//!                         assert_eq!(17.0, *float);
62//!                         continue
63//!                     }
64//!                     Err(_) => (),
65//!                 }
66//!                 panic!("Unknown property in object!");
67//!             }
68//!         }
69//!     }
70//!
71//!     // Getting a debug URID map.
72//!     let mut debug_map = DebugMap::new();
73//!     let mut urids = unsafe {debug_map.create_cached_map()};
74//!
75//!     // Creating the plugin.
76//!     let mut plugin = Plugin {
77//!         in_port: AtomInputPort::new(),
78//!         out_port: AtomOutputPort::new(),
79//!         urids: urids,
80//!     };
81//!
82//!     // Creating the atom space.
83//!     let mut atom_space = vec![0u8; 256];
84//!     let atom = unsafe { (atom_space.as_mut_ptr() as *mut Atom).as_mut() }.unwrap();
85//!     *(atom.mut_size()) = 256 - 8;
86//!
87//!     // Connecting the ports.
88//!     plugin.in_port.connect_port(atom as &Atom);
89//!     plugin.out_port.connect_port(atom);
90//!
91//!     // Calling `run`.
92//!     plugin.run();
93use crate::atom::{array::*, *};
94use crate::frame::{NestedFrame, WritingFrame, WritingFrameExt};
95use crate::uris;
96use std::ffi::CStr;
97use urid::URID;
98
99/// Nice handle for the time unit.
100///
101/// It is primarily used as a parameter for the
102/// [`Sequence`](type.Sequence.html) initialization method.
103///
104/// This type is not `repr(C)` and can not be directly used to interpret raw data.
105#[derive(Clone, PartialEq, Debug)]
106pub enum TimeUnit {
107    Frames,
108    Beats,
109}
110
111impl TimeUnit {
112    /// Try to get a `TimeUnit` value from a urid.
113    ///
114    /// If the given uri is the same as the URID of
115    /// [`uris::BEAT_TIME_URI`](../uris/constant.BEAT_TIME_URI.html), this method will return
116    /// `TimeUnit::Beats`. Otherwise, it will return `Time::Frames`.
117    pub fn from_urid(urid: URID, urids: &mut urid::CachedMap) -> TimeUnit {
118        if urid == urids.map(unsafe { CStr::from_bytes_with_nul_unchecked(uris::BEAT_TIME_URI) }) {
119            TimeUnit::Beats
120        } else {
121            TimeUnit::Frames
122        }
123    }
124
125    /// Return the corresponding URID of the time unit.
126    pub fn into_urid(&self, urids: &mut urid::CachedMap) -> URID {
127        match self {
128            TimeUnit::Frames => {
129                urids.map(unsafe { CStr::from_bytes_with_nul_unchecked(uris::FRAME_TIME_URI) })
130            }
131            TimeUnit::Beats => {
132                urids.map(unsafe { CStr::from_bytes_with_nul_unchecked(uris::BEAT_TIME_URI) })
133            }
134        }
135    }
136}
137
138/// Nice handle for time stamps.
139///
140/// Time stamps can be given in frames since startup or in beats since startup.
141///
142/// This type is not `repr(C)` and can not be directly used to interpret raw data.
143#[derive(Clone, PartialEq, Debug)]
144pub enum TimeStamp {
145    Frames(i64),
146    Beats(f64),
147}
148
149impl TimeStamp {
150    /// Get the time unit of the stamp.
151    pub fn get_unit(&self) -> TimeUnit {
152        match self {
153            TimeStamp::Frames(_) => TimeUnit::Frames,
154            TimeStamp::Beats(_) => TimeUnit::Beats,
155        }
156    }
157}
158
159#[repr(C)]
160#[derive(Clone, Copy)]
161/// Raw representation of the time stamp.
162///
163/// The type of this union is depending on the context, in our case the time unit given in the
164/// body header of the sequence.
165///
166/// This type is `repr(C)` and is used to interpret raw data.
167union RawTimeStamp {
168    frames: i64,
169    beats: f64,
170}
171
172impl From<TimeStamp> for RawTimeStamp {
173    fn from(other: TimeStamp) -> RawTimeStamp {
174        match other {
175            TimeStamp::Frames(frames) => RawTimeStamp { frames: frames },
176            TimeStamp::Beats(beats) => RawTimeStamp { beats: beats },
177        }
178    }
179}
180
181#[repr(C)]
182/// The header of a sequence.
183///
184/// It contains the time unit used by the time stamp of every event.
185///
186/// This type is `repr(C)` and is used to interpret raw data.
187pub struct SequenceHeader {
188    pub unit: URID,
189    pub pad: u32,
190}
191
192impl ArrayAtomHeader for SequenceHeader {
193    type InitializationParameter = TimeUnit;
194
195    unsafe fn initialize<'a, W, T>(
196        writer: &mut W,
197        unit: &TimeUnit,
198        urids: &mut urid::CachedMap,
199    ) -> Result<(), ()>
200    where
201        T: 'static + Sized + Copy,
202        ArrayAtomBody<Self, T>: AtomBody,
203        W: WritingFrame<'a> + WritingFrameExt<'a, ArrayAtomBody<Self, T>>,
204    {
205        let header = SequenceHeader {
206            unit: unit.into_urid(urids),
207            pad: 0,
208        };
209        writer.write_sized(&header).map(|_| ())
210    }
211}
212
213/// Atom tuple with time stamps for every contained atom.
214///
215/// Sequences are used to express real-time events that should be handled with frame- or
216/// beat-perfect timing, for example midi events.
217///
218/// See the [module documentation](index.html) for more information.
219pub type Sequence = ArrayAtomBody<SequenceHeader, u8>;
220
221impl AtomBody for Sequence {
222    type InitializationParameter = TimeUnit;
223
224    fn get_uri() -> &'static CStr {
225        unsafe { CStr::from_bytes_with_nul_unchecked(uris::SEQUENCE_TYPE_URI) }
226    }
227
228    unsafe fn initialize_body<'a, W>(
229        writer: &mut W,
230        parameter: &TimeUnit,
231        urids: &mut urid::CachedMap,
232    ) -> Result<(), ()>
233    where
234        W: WritingFrame<'a> + WritingFrameExt<'a, Self>,
235    {
236        Self::__initialize_body(writer, parameter, urids)
237    }
238
239    fn create_ref<'a>(raw_data: &'a [u8]) -> Result<&'a Self, ()> {
240        Self::__create_ref(raw_data)
241    }
242}
243
244impl Sequence {
245    pub fn time_unit(&self, urids: &mut urid::CachedMap) -> TimeUnit {
246        TimeUnit::from_urid(self.header.unit, urids)
247    }
248
249    pub fn iter<'a>(
250        &'a self,
251        urids: &mut urid::CachedMap,
252    ) -> impl Iterator<Item = (TimeStamp, &'a Atom)> {
253        let time_unit = TimeUnit::from_urid(self.header.unit, urids);
254        AtomIterator::new(&self.data).map(
255            move |(raw_stamp, chunk): (&'a RawTimeStamp, &'a Atom)| -> (TimeStamp, &'a Atom) {
256                let stamp = match time_unit {
257                    TimeUnit::Frames => TimeStamp::Frames(unsafe { raw_stamp.frames }),
258                    TimeUnit::Beats => TimeStamp::Beats(unsafe { raw_stamp.beats }),
259                };
260                (stamp, chunk)
261            },
262        )
263    }
264}
265
266/// Extension for [`WritingFrame`](../frame/trait.WritingFrame.html) and
267/// [`WritingFrameExt`](../frame/trait.WritingFrameExt.html) for vectors.
268///
269/// See the [module documentation](index.html) for more information.
270pub trait SequenceWritingFrame<'a>: WritingFrame<'a> + WritingFrameExt<'a, Sequence> {
271    fn push_event<'b, A: AtomBody + ?Sized>(
272        &'b mut self,
273        time: TimeStamp,
274        parameter: &A::InitializationParameter,
275        urids: &mut urid::CachedMap,
276    ) -> Result<NestedFrame<'b, 'a, A>, ()> {
277        // Retrieving the time unit of the sequence.
278        let header_unit: TimeUnit = {
279            let atom_body = unsafe { self.get_atom_body(urids) }.unwrap();
280            TimeUnit::from_urid(atom_body.header.unit, urids)
281        };
282
283        if header_unit != time.get_unit() {
284            return Err(());
285        }
286
287        unsafe {
288            self.write_sized(&RawTimeStamp::from(time.clone()))?;
289            let mut frame = self.create_nested_frame::<A>(urids)?;
290            A::initialize_body(&mut frame, parameter, urids)?;
291            Ok(frame)
292        }
293    }
294}
295
296impl<'a, W> SequenceWritingFrame<'a> for W where W: WritingFrame<'a> + WritingFrameExt<'a, Sequence> {}