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> {}