//! MIDI player use crate::*; pub trait HasPlayer { fn player (&self) -> &impl MidiPlayerApi; fn player_mut (&mut self) -> &mut impl MidiPlayerApi; } #[macro_export] macro_rules! has_player { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? HasPlayer for $Struct $(<$($L),*$($T),*>)? { fn player (&$self) -> &impl MidiPlayerApi { &$cb } fn player_mut (&mut $self) -> &mut impl MidiPlayerApi { &mut$cb } } } } pub trait MidiPlayerApi: MidiRecordApi + MidiPlaybackApi + Send + Sync {} impl MidiPlayerApi for MidiPlayer {} /// Contains state for playing a clip pub struct MidiPlayer { /// State of clock and playhead pub clock: Clock, /// Start time and clip being played pub play_clip: Option<(Moment, Option>>)>, /// Start time and next clip pub next_clip: Option<(Moment, 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_ins: Vec, /// Play from current sequence to MIDI ports pub midi_outs: Vec, /// Notes currently held at input pub notes_in: Arc>, /// Notes currently held at output pub notes_out: Arc>, /// MIDI output buffer pub note_buf: Vec, } impl Default for MidiPlayer { fn default () -> Self { Self { play_clip: None, next_clip: None, recording: false, monitoring: false, overdub: false, notes_in: RwLock::new([false;128]).into(), notes_out: RwLock::new([false;128]).into(), note_buf: vec![0;8], reset: true, midi_ins: vec![], midi_outs: vec![], clock: Clock::default(), } } } impl MidiPlayer { pub fn new ( name: impl AsRef, jack: &Jack, clock: Option<&Clock>, clip: Option<&Arc>>, midi_from: &[PortConnect], midi_to: &[PortConnect], ) -> Usually { let _name = name.as_ref(); let clock = clock.cloned().unwrap_or_default(); Ok(Self { midi_ins: vec![JackMidiIn::new(jack, format!("M/{}", name.as_ref()), midi_from)?,], midi_outs: vec![JackMidiOut::new(jack, format!("{}/M", name.as_ref()), midi_to)?, ], play_clip: clip.map(|clip|(Moment::zero(&clock.timebase), Some(clip.clone()))), clock, note_buf: vec![0;8], reset: true, recording: false, monitoring: false, overdub: false, next_clip: None, notes_in: RwLock::new([false;128]).into(), notes_out: RwLock::new([false;128]).into(), }) } } impl std::fmt::Debug for MidiPlayer { fn fmt (&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { f.debug_struct("MidiPlayer") .field("clock", &self.clock) .field("play_clip", &self.play_clip) .field("next_clip", &self.next_clip) .finish() } } has_clock!(|self: MidiPlayer|self.clock); impl HasMidiIns for MidiPlayer { fn midi_ins (&self) -> &Vec { &self.midi_ins } fn midi_ins_mut (&mut self) -> &mut Vec { &mut self.midi_ins } } impl HasMidiOuts for MidiPlayer { fn midi_outs (&self) -> &Vec { &self.midi_outs } fn midi_outs_mut (&mut self) -> &mut Vec { &mut self.midi_outs } fn midi_note (&mut self) -> &mut Vec { &mut self.note_buf } } /// 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 clip player/recorder. impl Audio for PlayerAudio<'_, 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 clip 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 } } impl MidiRecordApi for MidiPlayer { fn recording (&self) -> bool { self.recording } fn recording_mut (&mut self) -> &mut bool { &mut self.recording } fn monitoring (&self) -> bool { self.monitoring } fn monitoring_mut (&mut self) -> &mut bool { &mut self.monitoring } fn overdub (&self) -> bool { self.overdub } fn overdub_mut (&mut self) -> &mut bool { &mut self.overdub } fn notes_in (&self) -> &Arc> { &self.notes_in } } impl MidiPlaybackApi for MidiPlayer { fn notes_out (&self) -> &Arc> { &self.notes_out } } impl HasPlayClip for MidiPlayer { fn reset (&self) -> bool { self.reset } fn reset_mut (&mut self) -> &mut bool { &mut self.reset } fn play_clip (&self) -> &Option<(Moment, Option>>)> { &self.play_clip } fn play_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)> { &mut self.play_clip } fn next_clip (&self) -> &Option<(Moment, Option>>)> { &self.next_clip } fn next_clip_mut (&mut self) -> &mut Option<(Moment, Option>>)> { &mut self.next_clip } } pub trait MidiRecordApi: HasClock + HasPlayClip + HasMidiIns { fn notes_in (&self) -> &Arc>; 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 monitor (&mut self, scope: &ProcessScope, midi_buf: &mut Vec>>) { // For highlighting keys and note repeat let notes_in = self.notes_in().clone(); let monitoring = self.monitoring(); for input in self.midi_ins_mut().iter() { for (sample, event, bytes) in parse_midi_input(input.port().iter(scope)) { if let LiveEvent::Midi { message, .. } = event { if monitoring { midi_buf[sample].push(bytes.to_vec()); } // FIXME: don't lock on every event! update_keys(&mut notes_in.write().unwrap(), &message); } } } } fn record (&mut self, scope: &ProcessScope, midi_buf: &mut Vec>>) { if self.monitoring() { self.monitor(scope, midi_buf); } if !self.clock().is_rolling() { return } if let Some((started, ref clip)) = self.play_clip().clone() { self.record_clip(scope, started, clip, midi_buf); } if let Some((_start_at, _clip)) = &self.next_clip() { self.record_next(); } } fn record_clip ( &mut self, scope: &ProcessScope, started: Moment, clip: &Option>>, _midi_buf: &mut Vec>> ) { if let Some(clip) = clip { let sample0 = scope.last_frame_time() as usize; let start = started.sample.get() as usize; let _recording = self.recording(); let timebase = self.clock().timebase().clone(); let quant = self.clock().quant.get(); let mut clip = clip.write().unwrap(); let length = clip.length; for input in self.midi_ins_mut().iter() { for (sample, event, _bytes) in parse_midi_input(input.port().iter(scope)) { if let LiveEvent::Midi { message, .. } = event { clip.record_event({ let sample = (sample0 + sample - start) as f64; let pulse = timebase.samples_to_pulse(sample); let quantized = (pulse / quant).round() * quant; quantized as usize % length }, message); } } } } } fn record_next (&mut self) { // TODO switch to next clip and record into it } } pub trait MidiPlaybackApi: HasPlayClip + HasClock + HasMidiOuts { fn notes_out (&self) -> &Arc>; /// 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, out: &mut [Vec>], reset: bool ) { let n_frames = (scope.n_frames() as usize).min(out.len()); for frame in &mut out[0..n_frames] { frame.clear(); } if reset { all_notes_off(out); } } /// Output notes from clip to MIDI output ports. fn play ( &mut self, scope: &ProcessScope, note_buf: &mut Vec, out: &mut [Vec>] ) -> bool { if !self.clock().is_rolling() { return false } // If a clip is playing, write a chunk of MIDI events from it to the output buffer. // If no clip is playing, prepare for switchover immediately. self.play_clip().as_ref().map_or(true, |(started, clip)|{ self.play_chunk(scope, note_buf, out, started, clip) }) } /// Handle switchover from current to next playing clip. fn switchover ( &mut self, scope: &ProcessScope, note_buf: &mut Vec, out: &mut [Vec>] ) { if !self.clock().is_rolling() { return } let sample0 = scope.last_frame_time() as usize; //let samples = scope.n_frames() as usize; if let Some((start_at, clip)) = &self.next_clip() { let start = start_at.sample.get() as usize; let sample = self.clock().started.read().unwrap() .as_ref().unwrap().sample.get() as usize; // If it's time to switch to the next clip: if start <= sample0.saturating_sub(sample) { // Samples elapsed since clip was supposed to start let _skipped = sample0 - start; // Switch over to enqueued clip let started = Moment::from_sample(self.clock().timebase(), start as f64); // Launch enqueued clip *self.play_clip_mut() = Some((started, clip.clone())); // Unset enqueuement (TODO: where to implement looping?) *self.next_clip_mut() = None; // Fill in remaining ticks of chunk from next clip. self.play(scope, note_buf, out); } } } fn play_chunk ( &self, scope: &ProcessScope, note_buf: &mut Vec, out: &mut [Vec>], started: &Moment, clip: &Option>> ) -> bool { // First sample to populate. Greater than 0 means that the first // pulse of the clip falls somewhere in the middle of the chunk. let sample = (scope.last_frame_time() as usize).saturating_sub( started.sample.get() as usize + self.clock().started.read().unwrap().as_ref().unwrap().sample.get() as usize ); // Iterator that emits sample (index into output buffer at which to write MIDI event) // paired with pulse (index into clip 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 + scope.n_frames() as usize); // Notes active during current chunk. let notes = &mut self.notes_out().write().unwrap(); let length = clip.as_ref().map_or(0, |p|p.read().unwrap().length); for (sample, pulse) in pulses { // If a next clip is enqueued, and we're past the end of the current one, // break the loop here (FIXME count pulse correctly) let past_end = if clip.is_some() { pulse >= length } else { true }; if self.next_clip().is_some() && past_end { return true } // If there's a currently playing clip, output notes from it to buffer: if let Some(ref clip) = clip { Self::play_pulse(clip, pulse, sample, note_buf, out, notes) } } false } fn play_pulse ( clip: &RwLock, pulse: usize, sample: usize, note_buf: &mut Vec, out: &mut [Vec>], notes: &mut [bool;128] ) { // Source clip from which the MIDI events will be taken. let clip = clip.read().unwrap(); // Clip with zero length is not processed if clip.length > 0 { // Current pulse index in source clip let pulse = pulse % clip.length; // Output each MIDI event from clip at appropriate frames of output buffer: for message in clip.notes[pulse].iter() { // Clear output buffer for this MIDI event. note_buf.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_buf) .unwrap(); // Append serialized message to output buffer. out[sample].push(note_buf.clone()); // Update the list of currently held notes. update_keys(&mut*notes, message); } } } /// Write a chunk of MIDI data from the output buffer to all assigned output ports. fn write (&mut self, scope: &ProcessScope, out: &[Vec>]) { let samples = scope.n_frames() as usize; for port in self.midi_outs_mut().iter_mut() { Self::write_port(&mut port.port_mut().writer(scope), samples, out) } } /// Write a chunk of MIDI data from the output buffer to an output port. fn write_port (writer: &mut MidiWriter, samples: usize, out: &[Vec>]) { for (time, events) in out.iter().enumerate().take(samples) { for bytes in events.iter() { writer.write(&RawMidi { time: time as u32, bytes }).unwrap_or_else(|_|{ panic!("Failed to write MIDI data: {bytes:?}"); }); } } } }