use crate::*; pub trait HasPlayer: HasJack + HasClock { fn player (&self) -> &MIDIPlayer; fn player_mut (&mut self) -> &mut MIDIPlayer; } pub trait HasMidiBuffer { fn midi_buffer (&self) -> &mut Vec>>; fn reset (&self) -> bool; fn reset_mut (&mut self) -> &mut bool; /// Clear the section of the output buffer that we will be using, /// emitting "all notes off" at start of buffer if requested. fn clear (&mut self, scope: &ProcessScope, force_reset: bool) { for frame in &mut self.midi_buffer()[0..scope.n_frames() as usize] { frame.clear(); } if self.reset() || force_reset { all_notes_off(&mut self.midi_buffer()); *self.reset_mut() = false; } } } pub trait HasPhrase { fn phrase (&self) -> &Option<(Instant, Arc>)>; fn next_phrase (&self) -> &Option<(Instant, Arc>)>; fn switchover (&mut self, scope: &ProcessScope) { if self.is_rolling() { let sample0 = scope.last_frame_time() as usize; //let samples = scope.n_frames() as usize; if let Some((start_at, phrase)) = &self.next_phrase { let start = start_at.sample.get() as usize; let sample = self.clock.started.read().unwrap().unwrap().0; // If it's time to switch to the next phrase: if start <= sample0.saturating_sub(sample) { // Samples elapsed since phrase was supposed to start let skipped = sample0 - start; // Switch over to enqueued phrase let started = Instant::from_sample(&self.clock.timebase(), start as f64); self.phrase = Some((started, phrase.clone())); // Unset enqueuement (TODO: where to implement looping?) self.next_phrase = None } // TODO fill in remaining ticks of chunk from next phrase. // ?? just call self.play(scope) again, since enqueuement is off ??? self.play(scope); // ?? or must it be with modified scope ?? // likely not because start time etc } } } fn enqueue_next (&mut self, phrase: Option<&Arc>>) { let start = self.clock.next_launch_pulse(); self.next_phrase = Some(( Instant::from_pulse(&self.clock.timebase(), start as f64), phrase.map(|p|p.clone()) )); self.reset = true; } fn pulses_since_start (&self) -> Option { if let Some((started, Some(_))) = self.phrase.as_ref() { Some(self.clock.current.pulse.get() - started.pulse.get()) } else { None } } } pub trait MidiInputApi: PlayheadApi + HasMidiBuffer + HasPhrase { fn midi_ins (&self) -> &Vec>; fn midi_ins_mut (&self) -> &mut Vec>; fn has_midi_ins (&self) -> bool { self.midi_ins().len() > 0 } fn recording (&self) -> bool; fn recording_mut (&mut self) -> &mut bool; fn toggle_record (&mut self) { *self.recording_mut() = !self.recording(); } fn monitoring (&self) -> bool; fn monitoring_mut (&mut self) -> &mut bool; fn toggle_monitor (&mut self) { *self.monitoring_mut() = !self.monitoring(); } fn overdub (&self) -> bool; fn overdub_mut (&mut self) -> &mut bool; fn toggle_overdub (&mut self) { *self.overdub_mut() = !self.overdub(); } fn notes_in (&self) -> &Arc>; fn record (&mut self, scope: &ProcessScope) { let sample0 = scope.last_frame_time() as usize; if let (true, Some((started, phrase))) = (self.is_rolling(), self.phrase()) { let start = started.sample.get() as usize; let quant = self.quant().get(); // For highlighting keys and note repeat let mut notes_in = self.notes_in().write().unwrap(); // Record from each input for input in self.midi_ins_mut().iter() { for (sample, event, bytes) in parse_midi_input(input.iter(scope)) { if let LiveEvent::Midi { message, .. } = event { if self.monitoring() { self.midi_buffer()[sample].push(bytes.to_vec()) } if self.recording() { if let Some(phrase) = phrase { let mut phrase = phrase.write().unwrap(); let length = phrase.length; phrase.record_event({ let sample = (sample0 + sample - start) as f64; let pulse = self.timebase().samples_to_pulse(sample); let quantized = (pulse / quant).round() * quant; let looped = quantized as usize % length; looped }, message); } } update_keys(&mut notes_in, &message); } } } } if let (true, Some((start_at, phrase))) = (self.is_rolling(), &self.next_phrase()) { // TODO switch to next phrase and record into it } } fn monitor (&mut self, scope: &ProcessScope) { let mut notes_in = self.notes_in().write().unwrap(); for input in self.midi_ins_mut().iter() { for (sample, event, bytes) in parse_midi_input(input.iter(scope)) { if let LiveEvent::Midi { message, .. } = event { self.midi_buffer()[sample].push(bytes.to_vec()); update_keys(&mut notes_in, &message); } } } } } pub trait MidiOutputApi: PlayheadApi + HasMidiBuffer + HasPhrase + HasClock { fn midi_outs (&self) -> &Vec>; fn midi_outs_mut (&self) -> &mut Vec>; fn midi_note (&mut self) -> &mut Vec; fn notes_out (&mut self) -> &Arc>>; fn has_midi_outs (&self) -> bool { self.midi_outs().len() > 0 } fn play (&mut self, scope: &ProcessScope) -> bool { let mut next = false; // Write MIDI events from currently playing phrase (if any) to MIDI output buffer if self.is_rolling() { let sample0 = scope.last_frame_time() as usize; let samples = scope.n_frames() as usize; // If no phrase is playing, prepare for switchover immediately next = self.phrase().is_none(); if let Some((started, phrase)) = &self.phrase() { // First sample to populate. Greater than 0 means that the first // pulse of the phrase falls somewhere in the middle of the chunk. let sample = started.sample.get() as usize; let sample = sample + self.clock().started.read().unwrap().unwrap().0; let sample = sample0.saturating_sub(sample); // Iterator that emits sample (index into output buffer at which to write MIDI event) // paired with pulse (index into phrase from which to take the MIDI event) for each // sample of the output buffer that corresponds to a MIDI pulse. let pulses = self.clock().timebase().pulses_between_samples(sample, sample + samples); // Notes active during current chunk. let notes = &mut self.notes_out().write().unwrap(); for (sample, pulse) in pulses { // If a next phrase is enqueued, and we're past the end of the current one, // break the loop here (FIXME count pulse correctly) next = self.next_phrase().is_some() && if let Some(ref phrase) = phrase { pulse >= phrase.read().unwrap().length } else { true }; if next { break } // If there's a currently playing phrase, output notes from it to buffer: if let Some(ref phrase) = phrase { // Source phrase from which the MIDI events will be taken. let phrase = phrase.read().unwrap(); // Current pulse index in source phrase let pulse = pulse % phrase.length; // Output each MIDI event from phrase at appropriate frames of output buffer: for message in phrase.notes[pulse].iter() { // Clear output buffer for this MIDI event. self.midi_note().clear(); // TODO: support MIDI channels other than CH1. let channel = 0.into(); // Serialize MIDI event into message buffer. LiveEvent::Midi { channel, message: *message } .write(&mut self.midi_note()) .unwrap(); // Append serialized message to output buffer. self.midi_buffer()[sample].push(self.midi_note().clone()); // Update the list of currently held notes. update_keys(&mut*notes, &message); } } } } } next } fn write (&mut self, scope: &ProcessScope) { let samples = scope.n_frames() as usize; for port in self.midi_outs_mut().iter_mut() { let writer = &mut port.writer(scope); let output = &self.midi_buffer(); for time in 0..samples { for event in output[time].iter() { writer.write(&RawMidi { time: time as u32, bytes: &event }) .expect(&format!("{event:?}")); } } } } } /// Add "all notes off" to the start of a buffer. pub fn all_notes_off (output: &mut [Vec>]) { let mut buf = vec![]; let msg = MidiMessage::Controller { controller: 123.into(), value: 0.into() }; let evt = LiveEvent::Midi { channel: 0.into(), message: msg }; evt.write(&mut buf).unwrap(); output[0].push(buf); } /// Return boxed iterator of MIDI events pub fn parse_midi_input (input: MidiIter) -> Box + '_> { Box::new(input.map(|RawMidi { time, bytes }|( time as usize, LiveEvent::parse(bytes).unwrap(), bytes ))) } /// Update notes_in array pub fn update_keys (keys: &mut[bool;128], message: &MidiMessage) { match message { MidiMessage::NoteOn { key, .. } => { keys[key.as_int() as usize] = true; } MidiMessage::NoteOff { key, .. } => { keys[key.as_int() as usize] = false; }, _ => {} } }