use crate::prelude::*; use super::*; pub fn process (state: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Control { // Get currently playing sequence let sequence = state.sequences.get_mut(state.sequence); if sequence.is_none() { return Control::Continue } let sequence = sequence.unwrap(); // Prepare output buffer and transport let frames = scope.n_frames() as usize; let mut output: Vec>>> = vec![None;frames]; let transport = state.transport.query().unwrap(); // If starting or stopping, send "all notes off": if transport.state != state.playing { all_notes_off(&mut output); } // Update time state.playing = transport.state; let frame = transport.pos.frame() as usize; let usecs = state.timebase.frame_to_usec(frame); let steps = usecs / state.timebase.usec_per_step(state.resolution as usize); let step = steps % state.steps; let tick = step * state.timebase.ppq() / state.resolution; // Play from sequence into output buffer if state.playing == TransportState::Rolling { play_from_sequence( &mut output, &sequence.notes, &state.timebase, state.steps, state.resolution, frame, frames, ); } // Play from input to monitor, and record into sequence. monitor_and_record( &scope, &mut output, &mut sequence.notes, &state.midi_in, &mut state.notes_on, state.playing, state.monitoring, state.recording, tick as u32 ); // Write to port from output buffer // (containing notes from sequence and/or monitor) let mut writer = state.midi_out.writer(scope); for time in 0..scope.n_frames() { if let Some(Some(frame)) = output.get_mut(time as usize) { for event in frame.iter() { writer.write(&::jack::RawMidi { time, bytes: &event }) .expect(&format!("{event:?}")); } } } Control::Continue } type MIDIChunk = [Option>>]; /// Add "all notes off" to the start of a buffer. fn all_notes_off (output: &mut MIDIChunk) { output[0] = Some(vec![]); if let Some(Some(frame)) = output.get_mut(0) { 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(); frame.push(buf); } } fn play_from_sequence ( output: &mut MIDIChunk, sequence: &PhraseData, timebase: &Arc, steps: usize, resolution: usize, frame: usize, frames: usize, ) { let quant = timebase.frames_per_beat() as usize * steps / resolution; let ticks = timebase.frames_to_ticks(frame, frame + frames, quant); for (time, tick) in ticks.iter() { if let Some(events) = sequence.get(&(*tick as u32)) { for message in events.iter() { let mut buf = vec![]; let channel = 0.into(); let message = *message; LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); let t = *time as usize; if output[t].is_none() { output[t] = Some(vec![]); } if let Some(Some(frame)) = output.get_mut(t) { frame.push(buf); } } } } } fn monitor_and_record ( scope: &ProcessScope, output: &mut MIDIChunk, sequence: &mut PhraseData, midi_in: &Port, notes_on: &mut Vec, playing: TransportState, monitoring: bool, recording: bool, tick: u32, ) { for event in midi_in.iter(scope) { let msg = midly::live::LiveEvent::parse(event.bytes).unwrap(); match msg { midly::live::LiveEvent::Midi { channel: _, message } => match message { midly::MidiMessage::NoteOn { key, vel: _ } => { notes_on[key.as_int() as usize] = true; if monitoring { let t = event.time as usize; if output[t].is_none() { output[t] = Some(vec![]); } if let Some(Some(frame)) = output.get_mut(t) { frame.push(event.bytes.into()) } } if recording && playing == TransportState::Rolling { let contains = sequence.contains_key(&tick); if contains { sequence.get_mut(&tick).unwrap().push(message.clone()); } else { sequence.insert(tick, vec![message.clone()]); } } }, midly::MidiMessage::NoteOff { key, vel: _ } => { notes_on[key.as_int() as usize] = false; if monitoring { let t = event.time as usize; if output[t].is_none() { output[t] = Some(vec![]); } if let Some(Some(frame)) = output.get_mut(t) { frame.push(event.bytes.into()) } } if recording && playing == TransportState::Rolling { let contains = sequence.contains_key(&tick); if contains { sequence.get_mut(&tick).unwrap().push(message.clone()); } else { sequence.insert(tick, vec![message.clone()]); } } }, _ => {} }, _ => {} } } }