euphony 0.1.1

music composition environment for programmers
Documentation
use core::{
    pin::Pin,
    task::{Context, Poll},
};
use euphony_core::time::beat::Instant;
use euphony_pattern::{Pattern, ResultExt, Status};
use euphony_runtime::time::Timer;
use futures::stream::Stream;
use pin_project::pin_project;

pub trait PatternStreamExt: Pattern {
    fn as_stream(self) -> PatternStream<Self> {
        PatternStream {
            pattern: self,
            start_time: Instant(0, 1),
            expansion: 0,
            is_finished: false,
            timer: None,
        }
    }

    fn read(&self) -> Option<Self::Output> {
        let now = crate::runtime::time::scheduler().beats();
        let pattern_cx = euphony_pattern::Context::new(now, 0);
        let result = self.poll(&pattern_cx);
        let (value, status) = result.split_status();
        value
    }
}

impl<P: Pattern> PatternStreamExt for P {}

#[pin_project]
pub struct PatternStream<P: Pattern> {
    pattern: P,
    start_time: Instant,
    expansion: usize,
    is_finished: bool,
    timer: Option<Timer>,
}

impl<P: Pattern> Stream for PatternStream<P> {
    type Item = P::Output;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let this = &mut *self;

        if this.is_finished {
            return None.into();
        }

        if let Some(timer) = this.timer.as_mut() {
            if timer.poll_unpin(cx).is_pending() {
                return Poll::Pending;
            }
            this.timer = None;
        }

        let now = crate::runtime::time::scheduler().beats() - this.start_time;
        let pattern_cx = euphony_pattern::Context::new(now, this.expansion);
        let result = this.pattern.poll(&pattern_cx);
        let (value, status) = result.split_status();

        match status {
            Status::Pending(time) => {
                let delay = (time - now).as_ratio().into();
                this.timer = Some(crate::runtime::time::scheduler().delay(delay));
            }
            Status::Continuous => {
                this.is_finished = true;
            }
        }

        if let Some(value) = value {
            Some(value).into()
        } else {
            Poll::Pending
        }
    }
}