use crate::space::*;
use crate::*;
use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp;
use units::prelude::*;
use urid::*;
pub struct Sequence;
unsafe impl UriBound for Sequence {
const URI: &'static [u8] = sys::LV2_ATOM__Sequence;
}
impl<'a, 'b> Atom<'a, 'b> for Sequence
where
'a: 'b,
{
type ReadParameter = URID<Beat>;
type ReadHandle = SequenceIterator<'a>;
type WriteParameter = TimeStampURID;
type WriteHandle = SequenceWriter<'a, 'b>;
fn read(body: Space, bpm_urid: URID<Beat>) -> Option<SequenceIterator> {
let (header, body) = body.split_type::<sys::LV2_Atom_Sequence_Body>()?;
let unit = if header.unit == bpm_urid {
TimeStampUnit::BeatsPerMinute
} else {
TimeStampUnit::Frames
};
Some(SequenceIterator { space: body, unit })
}
fn init(
mut frame: FramedMutSpace<'a, 'b>,
unit: TimeStampURID,
) -> Option<SequenceWriter<'a, 'b>> {
{
let frame = &mut frame as &mut dyn MutSpace;
let header = sys::LV2_Atom_Sequence_Body {
unit: match unit {
TimeStampURID::BeatsPerMinute(urid) => urid.get(),
TimeStampURID::Frames(urid) => urid.get(),
},
pad: 0,
};
frame.write(&header, true)?;
}
Some(SequenceWriter {
frame,
unit: unit.into(),
last_stamp: None,
})
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum TimeStampUnit {
Frames,
BeatsPerMinute,
}
#[derive(Clone, Copy, Debug)]
pub enum TimeStamp {
Frames(i64),
BeatsPerMinute(f64),
}
#[derive(Clone, Copy)]
pub enum TimeStampURID {
Frames(URID<Frame>),
BeatsPerMinute(URID<Beat>),
}
impl From<TimeStampURID> for TimeStampUnit {
fn from(urid: TimeStampURID) -> TimeStampUnit {
match urid {
TimeStampURID::Frames(_) => TimeStampUnit::Frames,
TimeStampURID::BeatsPerMinute(_) => TimeStampUnit::BeatsPerMinute,
}
}
}
impl TimeStamp {
pub fn as_frames(self) -> Option<i64> {
match self {
Self::Frames(frame) => Some(frame),
_ => None,
}
}
pub fn as_bpm(self) -> Option<f64> {
match self {
Self::BeatsPerMinute(bpm) => Some(bpm),
_ => None,
}
}
}
pub struct SequenceIterator<'a> {
space: Space<'a>,
unit: TimeStampUnit,
}
impl<'a> SequenceIterator<'a> {
pub fn unit(&self) -> TimeStampUnit {
self.unit
}
}
impl<'a> Iterator for SequenceIterator<'a> {
type Item = (TimeStamp, UnidentifiedAtom<'a>);
fn next(&mut self) -> Option<(TimeStamp, UnidentifiedAtom<'a>)> {
let (raw_stamp, space) = self.space.split_type::<RawTimeStamp>()?;
let stamp = match self.unit {
TimeStampUnit::Frames => unsafe { TimeStamp::Frames(raw_stamp.frames) },
TimeStampUnit::BeatsPerMinute => unsafe { TimeStamp::BeatsPerMinute(raw_stamp.beats) },
};
let (atom, space) = space.split_atom()?;
self.space = space;
Some((stamp, UnidentifiedAtom::new(atom)))
}
}
pub struct SequenceWriter<'a, 'b> {
frame: FramedMutSpace<'a, 'b>,
unit: TimeStampUnit,
last_stamp: Option<TimeStamp>,
}
impl<'a, 'b> SequenceWriter<'a, 'b> {
fn write_time_stamp(&mut self, stamp: TimeStamp) -> Option<()> {
let raw_stamp = match self.unit {
TimeStampUnit::Frames => {
let frames = stamp.as_frames()?;
if let Some(last_stamp) = self.last_stamp {
if last_stamp.as_frames().unwrap() > frames {
return None;
}
}
RawTimeStamp { frames }
}
TimeStampUnit::BeatsPerMinute => {
let beats = stamp.as_bpm()?;
if let Some(last_stamp) = self.last_stamp {
if last_stamp.as_bpm().unwrap() > beats {
return None;
}
}
RawTimeStamp { beats }
}
};
self.last_stamp = Some(stamp);
(&mut self.frame as &mut dyn MutSpace)
.write(&raw_stamp, true)
.map(|_| ())
}
pub fn init<'c, A: Atom<'a, 'c>>(
&'c mut self,
stamp: TimeStamp,
urid: URID<A>,
parameter: A::WriteParameter,
) -> Option<A::WriteHandle> {
self.write_time_stamp(stamp)?;
(&mut self.frame as &mut dyn MutSpace).init(urid, parameter)
}
pub fn forward(&mut self, stamp: TimeStamp, atom: UnidentifiedAtom) -> Option<()> {
let data = atom.space.data()?;
self.write_time_stamp(stamp)?;
self.frame.write_raw(data, true).map(|_| ())
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::sequence::*;
use std::mem::size_of;
use sys::LV2_Atom_Event__bindgen_ty_1 as RawTimeStamp;
#[derive(URIDCollection)]
struct TestURIDCollection {
atom: AtomURIDCollection,
units: UnitURIDCollection,
}
#[test]
fn test_sequence() {
let map = HashURIDMapper::new();
let urids = TestURIDCollection::from_map(&map).unwrap();
let mut raw_space: Box<[u8]> = Box::new([0; 256]);
{
let mut space = RootMutSpace::new(raw_space.as_mut());
let mut writer = (&mut space as &mut dyn MutSpace)
.init(
urids.atom.sequence,
TimeStampURID::Frames(urids.units.frame),
)
.unwrap();
writer
.init::<Int>(TimeStamp::Frames(0), urids.atom.int, 42)
.unwrap();
writer
.init::<Long>(TimeStamp::Frames(1), urids.atom.long, 17)
.unwrap();
}
{
let (sequence, space) = raw_space.split_at(size_of::<sys::LV2_Atom_Sequence>());
let sequence = unsafe { &*(sequence.as_ptr() as *const sys::LV2_Atom_Sequence) };
assert_eq!(sequence.atom.type_, urids.atom.sequence);
assert_eq!(
sequence.atom.size as usize,
size_of::<sys::LV2_Atom_Sequence_Body>()
+ size_of::<RawTimeStamp>()
+ size_of::<sys::LV2_Atom_Int>()
+ 4
+ size_of::<RawTimeStamp>()
+ size_of::<sys::LV2_Atom_Long>()
);
assert_eq!(sequence.body.unit, urids.units.frame);
let (stamp, space) = space.split_at(size_of::<RawTimeStamp>());
let stamp = unsafe { *(stamp.as_ptr() as *const RawTimeStamp) };
assert_eq!(unsafe { stamp.frames }, 0);
let (int, space) = space.split_at(size_of::<sys::LV2_Atom_Int>());
let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Int) };
assert_eq!(int.atom.type_, urids.atom.int);
assert_eq!(int.atom.size as usize, size_of::<i32>());
assert_eq!(int.body, 42);
let (_, space) = space.split_at(4);
let (stamp, space) = space.split_at(size_of::<RawTimeStamp>());
let stamp = unsafe { *(stamp.as_ptr() as *const RawTimeStamp) };
assert_eq!(unsafe { stamp.frames }, 1);
let (int, _) = space.split_at(size_of::<sys::LV2_Atom_Long>());
let int = unsafe { &*(int.as_ptr() as *const sys::LV2_Atom_Long) };
assert_eq!(int.atom.type_, urids.atom.long);
assert_eq!(int.atom.size as usize, size_of::<i64>());
assert_eq!(int.body, 17);
}
{
let space = Space::from_slice(raw_space.as_ref());
let (body, _) = space.split_atom_body(urids.atom.sequence).unwrap();
let mut reader = Sequence::read(body, urids.units.beat).unwrap();
assert_eq!(reader.unit(), TimeStampUnit::Frames);
let (stamp, atom) = reader.next().unwrap();
match stamp {
TimeStamp::Frames(frames) => assert_eq!(frames, 0),
_ => panic!("Invalid time stamp!"),
}
assert_eq!(atom.read::<Int>(urids.atom.int, ()).unwrap(), 42);
let (stamp, atom) = reader.next().unwrap();
match stamp {
TimeStamp::Frames(frames) => assert_eq!(frames, 1),
_ => panic!("Invalid time stamp!"),
}
assert_eq!(atom.read::<Long>(urids.atom.long, ()).unwrap(), 17);
assert!(reader.next().is_none());
}
}
}