lv2_atom/
sequence.rs

1//! An atom containing a sequence of time-stamped events.
2//!
3//! These events are atoms again. Atoms passed in a sequence can be handled with frame-perfect timing and therefore is the prefered way to transmit events like MIDI messages. However, MIDI messages are implemented in separate crate.
4//!
5//! # Example
6//!
7//! ```
8//! use lv2_core::prelude::*;
9//! use lv2_units::prelude::*;
10//! use lv2_atom::prelude::*;
11//! use lv2_atom::sequence::*;
12//! use urid::*;
13//!
14//! #[derive(PortCollection)]
15//! struct MyPorts {
16//!     input: InputPort<AtomPort>,
17//!     output: OutputPort<AtomPort>,
18//! }
19//!
20//! #[derive(URIDCollection)]
21//! struct MyURIDs {
22//!     atom: AtomURIDCollection,
23//!     units: UnitURIDCollection,
24//! }
25//!
26//! /// Something like a plugin's run method.
27//! fn run(ports: &mut MyPorts, urids: &MyURIDs) {
28//!     // Get the read handle to the sequence.
29//!     // The reading method needs the URID of the BPM unit to tell if the time stamp
30//!     // is measured in beats or in frames. If the atom doesn't says that it's measured
31//!     // in beats, it is assumed that it is measured in frames.
32//!     let input_sequence: SequenceIterator = ports.input.read(
33//!         urids.atom.sequence,
34//!         urids.units.beat
35//!     ).unwrap();
36//!
37//!     // Get the write handle to the sequence.
38//!     // You have to provide the unit of the time stamps.
39//!     let mut output_sequence: SequenceWriter = ports.output.init(
40//!         urids.atom.sequence,
41//!         TimeStampURID::Frames(urids.units.frame)
42//!     ).unwrap();
43//!
44//!     // Iterate through all events in the input sequence.
45//!     //
46//!     // The specifications don't require the time stamps to be monotonic, your algorithms should
47//!     // be able to handle older events written after younger events.
48//!     //
49//!     // The sequence writer, however, assures that the written time stamps are monotonic.
50//!     for event in input_sequence {
51//!         // An event contains a timestamp and an atom.
52//!         let (timestamp, atom): (TimeStamp, UnidentifiedAtom) = event;
53//!         // If the read atom is a 32-bit integer...
54//!         if let Some(integer) = atom.read(urids.atom.int, ()) {
55//!             // Multiply it by two and write it to the sequence.
56//!             output_sequence.init(timestamp, urids.atom.int, integer * 2).unwrap();
57//!         } else {
58//!             // Forward the atom to the sequence without a change.
59//!             output_sequence.forward(timestamp, atom).unwrap();
60//!         }
61//!     }
62//! }
63//! ```
64//!
65//! # Specification
66//!
67//! [http://lv2plug.in/ns/ext/atom/atom.html#Sequence](http://lv2plug.in/ns/ext/atom/atom.html#Sequence)
68use crate::space::*;
69use crate::*;
70use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp;
71use units::prelude::*;
72use urid::*;
73
74/// An atom containing a sequence of time-stamped events.
75///
76/// [See also the module documentation.](index.html)
77pub struct Sequence;
78
79unsafe impl UriBound for Sequence {
80    const URI: &'static [u8] = sys::LV2_ATOM__Sequence;
81}
82
83impl<'a, 'b> Atom<'a, 'b> for Sequence
84where
85    'a: 'b,
86{
87    type ReadParameter = URID<Beat>;
88    type ReadHandle = SequenceIterator<'a>;
89    type WriteParameter = TimeStampURID;
90    type WriteHandle = SequenceWriter<'a, 'b>;
91
92    fn read(body: Space, bpm_urid: URID<Beat>) -> Option<SequenceIterator> {
93        let (header, body) = body.split_type::<sys::LV2_Atom_Sequence_Body>()?;
94        let unit = if header.unit == bpm_urid {
95            TimeStampUnit::BeatsPerMinute
96        } else {
97            TimeStampUnit::Frames
98        };
99        Some(SequenceIterator { space: body, unit })
100    }
101
102    fn init(
103        mut frame: FramedMutSpace<'a, 'b>,
104        unit: TimeStampURID,
105    ) -> Option<SequenceWriter<'a, 'b>> {
106        {
107            let frame = &mut frame as &mut dyn MutSpace;
108            let header = sys::LV2_Atom_Sequence_Body {
109                unit: match unit {
110                    TimeStampURID::BeatsPerMinute(urid) => urid.get(),
111                    TimeStampURID::Frames(urid) => urid.get(),
112                },
113                pad: 0,
114            };
115            frame.write(&header, true)?;
116        }
117        Some(SequenceWriter {
118            frame,
119            unit: unit.into(),
120            last_stamp: None,
121        })
122    }
123}
124
125/// The measuring units of time stamps.
126#[derive(Clone, Copy, PartialEq, Eq, Debug)]
127pub enum TimeStampUnit {
128    Frames,
129    BeatsPerMinute,
130}
131
132/// An event time stamp.
133#[derive(Clone, Copy, Debug)]
134pub enum TimeStamp {
135    Frames(i64),
136    BeatsPerMinute(f64),
137}
138
139/// The measuring units of time stamps, with their URIDs.
140#[derive(Clone, Copy)]
141pub enum TimeStampURID {
142    Frames(URID<Frame>),
143    BeatsPerMinute(URID<Beat>),
144}
145
146impl From<TimeStampURID> for TimeStampUnit {
147    fn from(urid: TimeStampURID) -> TimeStampUnit {
148        match urid {
149            TimeStampURID::Frames(_) => TimeStampUnit::Frames,
150            TimeStampURID::BeatsPerMinute(_) => TimeStampUnit::BeatsPerMinute,
151        }
152    }
153}
154
155impl TimeStamp {
156    pub fn as_frames(self) -> Option<i64> {
157        match self {
158            Self::Frames(frame) => Some(frame),
159            _ => None,
160        }
161    }
162
163    pub fn as_bpm(self) -> Option<f64> {
164        match self {
165            Self::BeatsPerMinute(bpm) => Some(bpm),
166            _ => None,
167        }
168    }
169}
170
171/// An iterator over all events in a sequence.
172pub struct SequenceIterator<'a> {
173    space: Space<'a>,
174    unit: TimeStampUnit,
175}
176
177impl<'a> SequenceIterator<'a> {
178    pub fn unit(&self) -> TimeStampUnit {
179        self.unit
180    }
181}
182
183impl<'a> Iterator for SequenceIterator<'a> {
184    type Item = (TimeStamp, UnidentifiedAtom<'a>);
185
186    fn next(&mut self) -> Option<(TimeStamp, UnidentifiedAtom<'a>)> {
187        let (raw_stamp, space) = self.space.split_type::<RawTimeStamp>()?;
188        let stamp = match self.unit {
189            TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) },
190            TimeStampUnit::BeatsPerMinute => unsafe { TimeStamp::BeatsPerMinute(raw_stamp.beats) },
191        };
192        let (atom, space) = space.split_atom()?;
193        self.space = space;
194        Some((stamp, UnidentifiedAtom::new(atom)))
195    }
196}
197
198/// The writing handle for sequences.
199pub struct SequenceWriter<'a, 'b> {
200    frame: FramedMutSpace<'a, 'b>,
201    unit: TimeStampUnit,
202    last_stamp: Option<TimeStamp>,
203}
204
205impl<'a, 'b> SequenceWriter<'a, 'b> {
206    /// Write out the time stamp and update `last_stamp`.
207    ///
208    /// This method returns `Ǹone` if:
209    /// * The time stamp is not measured in our unit.
210    /// * The last time stamp is younger than the time stamp.
211    /// * Space is insufficient.
212    fn write_time_stamp(&mut self, stamp: TimeStamp) -> Option<()> {
213        let raw_stamp = match self.unit {
214            TimeStampUnit::Frames => {
215                let frames = stamp.as_frames()?;
216                if let Some(last_stamp) = self.last_stamp {
217                    if last_stamp.as_frames().unwrap() > frames {
218                        return None;
219                    }
220                }
221                RawTimeStamp { frames }
222            }
223            TimeStampUnit::BeatsPerMinute => {
224                let beats = stamp.as_bpm()?;
225                if let Some(last_stamp) = self.last_stamp {
226                    if last_stamp.as_bpm().unwrap() > beats {
227                        return None;
228                    }
229                }
230                RawTimeStamp { beats }
231            }
232        };
233        self.last_stamp = Some(stamp);
234        (&mut self.frame as &mut dyn MutSpace)
235            .write(&raw_stamp, true)
236            .map(|_| ())
237    }
238
239    /// Initialize an event.
240    ///
241    /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`.
242    pub fn init<'c, A: Atom<'a, 'c>>(
243        &'c mut self,
244        stamp: TimeStamp,
245        urid: URID<A>,
246        parameter: A::WriteParameter,
247    ) -> Option<A::WriteHandle> {
248        self.write_time_stamp(stamp)?;
249        (&mut self.frame as &mut dyn MutSpace).init(urid, parameter)
250    }
251
252    /// Forward an unidentified atom to the sequence.
253    ///
254    /// If your cannot identify the type of the atom but have to write it, you can simply forward it.
255    ///
256    /// The time stamp has to be measured in the unit of the sequence. If the time stamp is measured in the wrong unit, is younger than the last written time stamp or space is insufficient, this method returns `None`.
257    pub fn forward(&mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> {
258        let data = atom.space.data()?;
259        self.write_time_stamp(stamp)?;
260        self.frame.write_raw(data, true).map(|_| ())
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use crate::prelude::*;
267    use crate::sequence::*;
268    use std::mem::size_of;
269    use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp;
270
271    #[derive(URIDCollection)]
272    struct TestURIDCollection {
273        atom: AtomURIDCollection,
274        units: UnitURIDCollection,
275    }
276
277    #[test]
278    fn test_sequence() {
279        let map = HashURIDMapper::new();
280        let urids = TestURIDCollection::from_map(&map).unwrap();
281
282        let mut raw_space: Box<[u8]> = Box::new([0; 256]);
283
284        // writing
285        {
286            let mut space = RootMutSpace::new(raw_space.as_mut());
287            let mut writer = (&mut space as &mut dyn MutSpace)
288                .init(
289                    urids.atom.sequence,
290                    TimeStampURID::Frames(urids.units.frame),
291                )
292                .unwrap();
293            writer
294                .init::<Int>(TimeStamp::Frames(0), urids.atom.int, 42)
295                .unwrap();
296            writer
297                .init::<Long>(TimeStamp::Frames(1), urids.atom.long, 17)
298                .unwrap();
299        }
300
301        // verifying
302        {
303            let (sequence, space) = raw_space.split_at(size_of::<sys::LV2_Atom_Sequence>());
304            let sequence = unsafe { &*(sequence.as_ptr() as *const sys::LV2_Atom_Sequence) };
305            assert_eq!(sequence.atom.type_, urids.atom.sequence);
306            assert_eq!(
307                sequence.atom.size as usize,
308                size_of::<sys::LV2_Atom_Sequence_Body>()
309                    + size_of::<RawTimeStamp>()
310                    + size_of::<sys::LV2_Atom_Int>()
311                    + 4
312                    + size_of::<RawTimeStamp>()
313                    + size_of::<sys::LV2_Atom_Long>()
314            );
315            assert_eq!(sequence.body.unit, urids.units.frame);
316
317            let (stamp, space) = space.split_at(size_of::<RawTimeStamp>());
318            let stamp = unsafe { *(stamp.as_ptr() as *const RawTimeStamp) };
319            assert_eq!(unsafe { stamp.frames }, 0);
320
321            let (int, space) = space.split_at(size_of::<sys::LV2_Atom_Int>());
322            let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Int) };
323            assert_eq!(int.atom.type_, urids.atom.int);
324            assert_eq!(int.atom.size as usize, size_of::<i32>());
325            assert_eq!(int.body, 42);
326            let (_, space) = space.split_at(4);
327
328            let (stamp, space) = space.split_at(size_of::<RawTimeStamp>());
329            let stamp = unsafe { *(stamp.as_ptr() as *const RawTimeStamp) };
330            assert_eq!(unsafe { stamp.frames }, 1);
331
332            let (int, _) = space.split_at(size_of::<sys::LV2_Atom_Long>());
333            let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Long) };
334            assert_eq!(int.atom.type_, urids.atom.long);
335            assert_eq!(int.atom.size as usize, size_of::<i64>());
336            assert_eq!(int.body, 17);
337        }
338
339        // reading
340        {
341            let space = Space::from_slice(raw_space.as_ref());
342            let (body, _) = space.split_atom_body(urids.atom.sequence).unwrap();
343            let mut reader = Sequence::read(body, urids.units.beat).unwrap();
344
345            assert_eq!(reader.unit(), TimeStampUnit::Frames);
346
347            let (stamp, atom) = reader.next().unwrap();
348            match stamp {
349                TimeStamp::Frames(frames) => assert_eq!(frames, 0),
350                _ => panic!("Invalid time stamp!"),
351            }
352            assert_eq!(atom.read::<Int>(urids.atom.int, ()).unwrap(), 42);
353
354            let (stamp, atom) = reader.next().unwrap();
355            match stamp {
356                TimeStamp::Frames(frames) => assert_eq!(frames, 1),
357                _ => panic!("Invalid time stamp!"),
358            }
359            assert_eq!(atom.read::<Long>(urids.atom.long, ()).unwrap(), 17);
360
361            assert!(reader.next().is_none());
362        }
363    }
364}