use crate::*; pub trait HasPlayer: JackApi { fn player (&self) -> &impl MidiPlayerApi; fn player_mut (&mut self) -> &mut impl MidiPlayerApi; } pub trait MidiPlayerApi: MidiInputApi + MidiOutputApi + Send + Sync {} pub trait HasPhrase: HasClock { fn reset (&self) -> bool; fn reset_mut (&mut self) -> &mut bool; fn play_phrase (&self) -> &Option<(Instant, Option>>)>; fn play_phrase_mut (&mut self) -> &mut Option<(Instant, Option>>)>; fn next_phrase (&self) -> &Option<(Instant, Option>>)>; fn next_phrase_mut (&mut self) -> &mut Option<(Instant, Option>>)>; fn enqueue_next (&mut self, phrase: Option<&Arc>>) { let start = self.clock().next_launch_pulse() as f64; let instant = Instant::from_pulse(&self.clock().timebase(), start); let phrase = phrase.map(|p|p.clone()); *self.next_phrase_mut() = Some((instant, phrase)); *self.reset_mut() = true; } fn pulses_since_start (&self) -> Option { if let Some((started, Some(_))) = self.play_phrase().as_ref() { Some(self.clock().current.pulse.get() - started.pulse.get()) } else { None } } } pub trait MidiInputApi: HasPhrase { fn midi_ins (&self) -> &Vec>; fn midi_ins_mut (&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, midi_buf: &mut Vec>>, ) { let sample0 = scope.last_frame_time() as usize; // For highlighting keys and note repeat let notes_in = self.notes_in().clone(); if self.clock().is_rolling() { if let Some((started, ref phrase)) = self.play_phrase().clone() { let start = started.sample.get() as usize; let quant = self.clock().quant.get(); let timebase = self.clock().timebase().clone(); let monitoring = self.monitoring(); let recording = self.recording(); 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 monitoring { midi_buf[sample].push(bytes.to_vec()) } if 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 = timebase.samples_to_pulse(sample); let quantized = (pulse / quant).round() * quant; let looped = quantized as usize % length; looped }, message); } } update_keys(&mut*notes_in.write().unwrap(), &message); } } } } if let Some((start_at, phrase)) = &self.next_phrase() { // TODO switch to next phrase and record into it } } } fn monitor ( &mut self, scope: &ProcessScope, midi_buf: &mut Vec>>, ) { // For highlighting keys and note repeat let notes_in = self.notes_in().clone(); 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 { midi_buf[sample].push(bytes.to_vec()); update_keys(&mut*notes_in.write().unwrap(), &message); } } } } } pub trait MidiOutputApi: HasPhrase { fn midi_outs (&self) -> &Vec>; fn midi_outs_mut (&mut self) -> &mut Vec>; fn midi_note (&mut self) -> &mut Vec; fn notes_out (&self) -> &Arc>; fn has_midi_outs (&self) -> bool { self.midi_outs().len() > 0 } /// 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, midi_buf: &mut Vec>>, reset: bool ) { for frame in &mut midi_buf[0..scope.n_frames() as usize] { frame.clear(); } if reset { all_notes_off(midi_buf); } } fn play ( &mut self, scope: &ProcessScope, note_buffer: &mut Vec, output_buffer: &mut Vec>> ) -> bool { let mut next = false; // Write MIDI events from currently playing phrase (if any) to MIDI output buffer if self.clock().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.play_phrase().is_none(); let phrase = self.play_phrase(); let started0 = &self.clock().started; let timebase = self.clock().timebase(); let notes_out = self.notes_out(); let next_phrase = self.next_phrase(); if let Some((started, phrase)) = 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 + started0.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 = timebase.pulses_between_samples(sample, sample + samples); // Notes active during current chunk. let notes = &mut 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 = 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(); // Phrase with zero length is not processed if phrase.length > 0 { // 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. note_buffer.clear(); // TODO: support MIDI channels other than CH1. let channel = 0.into(); // Serialize MIDI event into message buffer. LiveEvent::Midi { channel, message: *message } .write(note_buffer) .unwrap(); // Append serialized message to output buffer. output_buffer[sample].push(note_buffer.clone()); // Update the list of currently held notes. update_keys(&mut*notes, &message); } } } } } } next } fn switchover ( &mut self, scope: &ProcessScope, note_buffer: &mut Vec, output_buffer: &mut Vec>> ) { if self.clock().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.play_phrase_mut() = Some((started, phrase.clone())); // Unset enqueuement (TODO: where to implement looping?) *self.next_phrase_mut() = None } // TODO fill in remaining ticks of chunk from next phrase. // ?? just call self.play(scope) again, since enqueuement is off ??? self.play(scope, note_buffer, output_buffer); // ?? or must it be with modified scope ?? // likely not because start time etc } } } fn write (&mut self, scope: &ProcessScope, output_buffer: &Vec>>) { let samples = scope.n_frames() as usize; for port in self.midi_outs_mut().iter_mut() { let writer = &mut port.writer(scope); for time in 0..samples { for event in output_buffer[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; }, _ => {} } } /// Hosts the JACK callback for a single MIDI player pub struct PlayerAudio<'a, T: MidiPlayerApi>( /// Player pub &'a mut T, /// Note buffer pub &'a mut Vec, /// Note chunk buffer pub &'a mut Vec>>, ); /// JACK process callback for a sequencer's phrase player/recorder. impl<'a, T: MidiPlayerApi> Audio for PlayerAudio<'a, T> { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { let model = &mut self.0; let note_buf = &mut self.1; let midi_buf = &mut self.2; // Clear output buffer(s) model.clear(scope, midi_buf, false); // Write chunk of phrase to output, handle switchover if model.play(scope, note_buf, midi_buf) { model.switchover(scope, note_buf, midi_buf); } if model.has_midi_ins() { if model.recording() || model.monitoring() { // Record and/or monitor input model.record(scope, midi_buf) } else if model.has_midi_outs() && model.monitoring() { // Monitor input to output model.monitor(scope, midi_buf) } } // Write to output port(s) model.write(scope, midi_buf); Control::Continue } } //#[derive(Debug)] //pub struct MIDIPlayer { ///// Global timebase //pub clock: Arc, ///// Start time and phrase being played //pub play_phrase: Option<(Instant, Option>>)>, ///// Start time and next phrase //pub next_phrase: Option<(Instant, Option>>)>, ///// Play input through output. //pub monitoring: bool, ///// Write input to sequence. //pub recording: bool, ///// Overdub input to sequence. //pub overdub: bool, ///// Send all notes off //pub reset: bool, // TODO?: after Some(nframes) ///// Record from MIDI ports to current sequence. //pub midi_inputs: Vec>, ///// Play from current sequence to MIDI ports //pub midi_outputs: Vec>, ///// MIDI output buffer //pub midi_note: Vec, ///// MIDI output buffer //pub midi_chunk: Vec>>, ///// Notes currently held at input //pub notes_in: Arc>, ///// Notes currently held at output //pub notes_out: Arc>, //} ///// Methods used primarily by the process callback //impl MIDIPlayer { //pub fn new ( //jack: &Arc>, //clock: &Arc, //name: &str //) -> Usually { //let jack = jack.read().unwrap(); //Ok(Self { //clock: clock.clone(), //phrase: None, //next_phrase: None, //notes_in: Arc::new(RwLock::new([false;128])), //notes_out: Arc::new(RwLock::new([false;128])), //monitoring: false, //recording: false, //overdub: true, //reset: true, //midi_note: Vec::with_capacity(8), //midi_chunk: vec![Vec::with_capacity(16);16384], //midi_outputs: vec![ //jack.client().register_port(format!("{name}_out0").as_str(), MidiOut::default())? //], //midi_inputs: vec![ //jack.client().register_port(format!("{name}_in0").as_str(), MidiIn::default())? //], //}) //} //}