midi-toolkit-rs 0.3.1

A library for ultra high performance MIDI operations, designed for black MIDI. The library isn't perfect
Documentation
use crate::gen_iter::GenIter;

use crate::{
    events::{BatchTempo, MIDIDelta, MIDIEvent, MIDIEventEnum},
    num::MIDINum,
    unwrap,
};

use super::{Delta, Track};

#[derive(Debug)]
pub struct EventBatch<T> {
    events: Vec<T>,
}

impl<T> EventBatch<T> {
    fn new(events: Vec<T>) -> Self {
        Self { events }
    }

    pub fn into_iter_inner(self) -> impl Iterator<Item = T> {
        self.events.into_iter()
    }

    pub fn iter_inner(&self) -> impl Iterator<Item = &T> {
        self.events.iter()
    }

    pub fn count(&self) -> usize {
        self.events.len()
    }
}

impl<D: MIDINum, T> Delta<D, EventBatch<T>> {
    pub fn into_iter_events(self) -> impl Iterator<Item = Delta<D, T>> {
        let mut delta = self.delta;
        self.event.into_iter_inner().map(move |event| {
            let event = Delta::new(delta, event);
            delta = D::zero();
            event
        })
    }

    pub fn iter_events(&self) -> impl Iterator<Item = Delta<D, &T>> {
        let mut delta = self.delta;
        self.event.iter_inner().map(move |event| {
            let event = Delta::new(delta, event);
            delta = D::zero();
            event
        })
    }
}

impl<D: MIDINum, T> Delta<D, Track<EventBatch<T>>> {
    pub fn iter_events(&self) -> impl Iterator<Item = Delta<D, Track<&T>>> {
        let mut delta = self.delta;
        let track = self.event.track;
        self.event.event.iter_inner().map(move |event| {
            let event = Delta::new(delta, Track::new(event, track));
            delta = D::zero();
            event
        })
    }
}

impl<D: MIDINum, T> IntoIterator for Delta<D, Track<EventBatch<T>>> {
    type Item = Delta<D, Track<T>>;
    type IntoIter = impl Iterator<Item = Delta<D, Track<T>>>;

    fn into_iter(self) -> Self::IntoIter {
        let mut delta = self.delta;
        let track = self.event.track;
        self.event
            .inner_event()
            .into_iter_inner()
            .map(move |event| {
                let event = Delta::new(delta, Track::new(event, track));
                delta = D::zero();
                event
            })
    }
}

impl<E: MIDIEventEnum> BatchTempo for EventBatch<E> {
    fn inner_tempo(&self) -> Option<u32> {
        for e in self.events.iter().rev() {
            if let Some(t) = e.as_event().inner_tempo() {
                return Some(t);
            }
        }
        None
    }

    fn without_tempo(self) -> Option<Self> {
        let new = self
            .events
            .into_iter()
            .filter(|e| e.as_event().inner_tempo().is_none())
            .collect::<Vec<_>>();

        if new.is_empty() {
            None
        } else {
            Some(Self::new(new))
        }
    }
}

pub fn convert_events_into_batches<D: MIDINum, E, Err>(
    iter: impl Iterator<Item = Result<Delta<D, E>, Err>>,
) -> impl Iterator<Item = Result<Delta<D, EventBatch<E>>, Err>> {
    GenIter(
        #[coroutine]
        move || {
            let mut next_batch = Delta::new(D::zero(), EventBatch::new(Vec::new()));
            for e in iter {
                let e = unwrap!(e);
                if e.delta() > D::zero() {
                    if !next_batch.events.is_empty() {
                        yield Ok(next_batch);
                    }
                    next_batch = Delta::new(e.delta(), EventBatch::new(Vec::new()));
                }
                next_batch.events.push(e.event);
            }
            if !next_batch.events.is_empty() {
                yield Ok(next_batch);
            }
        },
    )
}

pub fn flatten_batches_to_events<D: MIDINum, E: MIDIEvent, Err>(
    iter: impl Iterator<Item = Result<Delta<D, EventBatch<E>>, Err>>,
) -> impl Iterator<Item = Result<Delta<D, E>, Err>> {
    GenIter(
        #[coroutine]
        move || {
            for batch in iter {
                let batch = unwrap!(batch);
                let mut delta = batch.delta;
                for event in batch.event.into_iter_inner() {
                    yield Ok(Delta::new(delta, event));
                    delta = D::zero();
                }
            }
        },
    )
}

pub fn flatten_track_batches_to_events<D: MIDINum, E: MIDIEvent, Err>(
    iter: impl Iterator<Item = Result<Delta<D, Track<EventBatch<E>>>, Err>>,
) -> impl Iterator<Item = Result<Delta<D, Track<E>>, Err>> {
    GenIter(
        #[coroutine]
        move || {
            for batch in iter {
                let batch = unwrap!(batch);
                let track = batch.event.track;
                let mut delta = batch.delta;
                for event in batch.event.inner_event().into_iter_inner() {
                    yield Ok(Delta::new(delta, Track::new(event, track)));
                    delta = D::zero();
                }
            }
        },
    )
}

#[cfg(test)]
mod tests {
    use super::EventBatch;
    use crate::events::{BatchTempo, Event, NoteOnEvent, TempoEvent};

    #[test]
    fn without_tempo_keeps_non_tempo_events() {
        let batch = EventBatch::new(vec![
            Event::Tempo(Box::new(TempoEvent { tempo: 500000 })),
            Event::NoteOn(NoteOnEvent {
                channel: 0,
                key: 64,
                velocity: 127,
            }),
        ]);

        let filtered = batch.without_tempo().unwrap();
        assert_eq!(filtered.count(), 1);
        assert!(matches!(
            filtered.into_iter_inner().next(),
            Some(Event::NoteOn(NoteOnEvent {
                channel: 0,
                key: 64,
                velocity: 127,
            }))
        ));
    }
}