1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
//! Stream of notes

use crate::messages::Status;
use crate::midi::MidiSend;
use crate::music::duration::Duration;
use crate::music::note::Note;
use crate::music::time::Time;
use itertools::Itertools;
use midir::MidiOutputConnection;
use tokio::time::{self, Duration as TDuration};

/// Temporal arrangement of events
#[derive(Debug, Default, Clone)]
pub struct Stream {
    pub events: Vec<Event>,
}

/// An event is a timed command
#[derive(Clone, Debug)]
pub struct Event {
    time: Time,
    status: Status,
    note: Note,
}

impl Event {
    pub fn new(time: Time, status: Status, note: Note) -> Self {
        Event { time, status, note }
    }
}

impl Stream {
    /// Creates a new empty `Stream` with no events.
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// use mumuse::music::stream::Stream;
    /// let s = Stream::new(); 
    /// ```
    pub fn new() -> Self {
        Self { events: vec![] }
    }

    /// Adds event to stream
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// use mumuse::music::{time::Time, note::Note, stream::{Stream, Event}};
    /// use mumuse::messages::Status;
    /// let mut stream: Stream = Stream::new();
    /// let time: Time = Time::new(1, 16, 1);
    /// let note: Note = Note::try_from("A3").unwrap();
    /// stream.add_event(Event::new(time, Status::NoteOn, note));
    /// ```
    pub fn add_event(&mut self, event: Event) {
        self.events.push(event);
    }

    /// Adds note to stream
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// use mumuse::music::{time::Time, note::Note, stream::Stream, duration::Duration};
    /// let mut stream: Stream = Stream::new();
    /// let note: Note = Note::try_from("A3").unwrap();
    /// let time: Time = Time::new(1, 16, 1);
    /// let duration: Duration = Duration::new(16, 1);
    /// stream.add_note(note, time, duration);
    /// ```
    pub fn add_note(&mut self, note: Note, time: Time, duration: Duration) {
        println!("{:?} + {:?} = {:?}", time, duration, time + duration);
        self.events.push(Event::new(time, Status::NoteOn, note));
        self.events
            .push(Event::new(time + duration, Status::NoteOff, note))
    }

    /// Converts Events to seconds timeline
    ///
    /// In order to convert the stream with `Time` events, one need to declare a `bpm`
    /// (beats per minutes) and a `bpb` (beats per bar).
    ///
    /// # Examples
    ///
    /// Basic usage:
    ///
    /// ```
    /// use mumuse::music::{time::Time, note::Note, stream::Stream, duration::Duration};
    /// let mut stream: Stream = Stream::new();
    /// let note: Note = Note::try_from("A3").unwrap();
    /// let time: Time = Time::new(1, 16, 1);
    /// let duration: Duration = Duration::new(16, 1);
    /// stream.add_note(note, time, duration);
    /// let stream_seconds = stream.to_seconds(120.0, 4);
    /// ```
    pub fn to_seconds(&self, bpm: f64, bpb: u32) -> Vec<(f64, Status, Note)> {
        let mut events_seconds: Vec<(f64, Status, Note)> = vec![];
        for event in self.events.iter() {
            events_seconds.push((event.time.to_seconds(bpm, bpb), event.status, event.note));
        }
        // Sort by time
        events_seconds.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
        events_seconds
    }

    /// Plays stream of events in real time
    ///
    /// Use ticking for playing the stream of events at regular intervals.
    /// For each tick, the events with Time to seconds lying in the tick window are
    /// sent as MIDI. 
    /// The `bpm` (beats per minutes) and `bpb` (beats per bar) are needed for the conversion
    /// to seconds.
    #[tokio::main]
    pub async fn play(&self, conn_out: &mut MidiOutputConnection, bpm: f64, bpb: u32) {
        let events_seconds = self.to_seconds(bpm, bpb); // Vector of events with seconds unit
        let interval_time = 10.0; // in ms
        let mut played_events = 0; // Count of sent event
        let mut n_tick = 0; // Tick number counter
        let total_events = events_seconds.len(); // Number of total events to send
        let mut interval = time::interval(TDuration::from_millis(interval_time as u64)); 

        // Async function to send midi events with constant tick time
        // Constant tick time is managed by Tokio interval ticking with Burst missed tick strategy
        // Tick time is set to 10ms here, adjust for more / less precision in event timing
        async fn play_events(
            conn_out: &mut MidiOutputConnection,
            n_tick: usize,
            interval_time: f64,
            events_seconds: &Vec<(f64, Status, Note)>,
        ) -> usize {
            let last_tick_time = n_tick as f64 * interval_time / 1000.0; // in sec
            let next_tick_time = (n_tick + 1) as f64 * interval_time / 1000.0; // in sec

            // Filter out events that to not belong to current tick window
            // Could be smarter by removing past events
            let current_events = events_seconds
                .iter()
                .filter(|event| (event.0 >= last_tick_time) && (event.0 < next_tick_time))
                .collect_vec();

            
            // Send events, currently only NoteOn and Note Off are handled.
            // Add match arms for additional case handling
            for current_event in &current_events {
                match current_event.1 {
                    Status::NoteOn => current_event.2.send_midi(Status::NoteOn, conn_out),
                    Status::NoteOff => current_event.2.send_midi(Status::NoteOff, conn_out),
                    _ => (),
                };
                println!("sent {:?}", current_event);
            }
            current_events.len()
        }

        loop {
            // We tick until all events are sent
            interval.tick().await;
            played_events += play_events(conn_out, n_tick, interval_time, &events_seconds).await;
            if total_events == played_events {
                break;
            }
            n_tick += 1;
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn add_event() {
        let mut stream: Stream = Stream::new();
        let time: Time = Time::new(1, 16, 1);
        let note: Note = Note::try_from("A3").unwrap();
        stream.add_event(Event::new(time, Status::NoteOn, note));
        assert_eq!(stream.events.get(0).unwrap().time.bar, 1);
        assert_eq!(stream.events.get(0).unwrap().time.divisions, 16);
        assert_eq!(stream.events.get(0).unwrap().time.position, 1);
    }

    #[test]
    fn sort_stream_time() {
        let mut stream: Stream = Stream::new();
        let note: Note = Note::try_from("A3").unwrap();

        // Unordered declaration of events
        stream.add_event(Event::new(Time::new(1, 4, 4), Status::NoteOn, note));
        stream.add_event(Event::new(Time::new(1, 4, 3), Status::NoteOn, note));
        stream.add_event(Event::new(Time::new(1, 4, 2), Status::NoteOn, note));
        stream.add_event(Event::new(Time::new(1, 4, 1), Status::NoteOn, note));

        // time_stream time should be ordered
        let time_stream = stream.to_seconds(120.0, 4);
        assert!(time_stream[0].0 < time_stream[1].0);
        assert!(time_stream[1].0 < time_stream[2].0);
        assert!(time_stream[2].0 < time_stream[3].0);
    }
}