diff --git a/src/device/sequencer/mod.rs b/src/device/sequencer/mod.rs index da2b1e4a..7d4f0e4f 100644 --- a/src/device/sequencer/mod.rs +++ b/src/device/sequencer/mod.rs @@ -1,30 +1,13 @@ use crate::prelude::*; -mod keys; -use self::keys::*; -mod handle; -pub use self::handle::*; -mod process; -pub use self::process::*; +mod keys; use self::keys::*; +mod handle; pub use self::handle::*; +mod process; pub use self::process::*; +mod phrase; pub use self::phrase::*; pub mod horizontal; - pub mod vertical; -pub type PhraseData = BTreeMap>; - -pub struct Phrase { - pub name: String, - pub length: u32, - pub notes: PhraseData, -} - -impl Phrase { - pub fn new (name: &str, length: u32, notes: Option) -> Self { - Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) } - } -} - pub struct Sequencer { pub name: String, /// JACK transport handle. diff --git a/src/device/sequencer/phrase.rs b/src/device/sequencer/phrase.rs new file mode 100644 index 00000000..b53bd833 --- /dev/null +++ b/src/device/sequencer/phrase.rs @@ -0,0 +1,109 @@ +use crate::prelude::*; +use super::*; + +pub type PhraseData = BTreeMap>; + +pub type MIDIChunk = [Option>>]; + +pub struct Phrase { + pub name: String, + pub length: u32, + pub notes: PhraseData, +} + +impl Phrase { + pub fn new (name: &str, length: u32, notes: Option) -> Self { + Self { name: name.to_string(), length, notes: notes.unwrap_or(BTreeMap::new()) } + } + /** Write a chunk of MIDI events to an output port. */ + pub fn chunk_out ( + &self, + output: &mut MIDIChunk, + start: usize, + length: usize, + timebase: &Arc, + steps: usize, + resolution: usize, + ) { + let quant = timebase.frames_per_beat() as usize * steps / resolution; + let ticks = timebase.frames_to_ticks(start, start + length, quant); + for (time, tick) in ticks.iter() { + if let Some(events) = self.notes.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); + } + } + } + } + } + /** React a chunk of MIDI events from an input port. */ + pub fn chunk_in ( + &mut self, + port: &Port, + scope: &ProcessScope, + mut monitor: Option<&mut MIDIChunk>, + record: bool, + notes_on: &mut Vec, + tick: u32, + ) { + for event in port.iter(scope) { + let msg = LiveEvent::parse(event.bytes).unwrap(); + match msg { + LiveEvent::Midi { message, .. } => match message { + MidiMessage::NoteOn { key, vel: _ } => { + notes_on[key.as_int() as usize] = true; + if let Some(ref mut monitor) = monitor { + let t = event.time as usize; + if monitor[t].is_none() { + monitor[t] = Some(vec![]); + } + if let Some(Some(frame)) = monitor.get_mut(t) { + frame.push(event.bytes.into()) + } + } + if record { + let contains = self.notes.contains_key(&tick); + if contains { + self.notes.get_mut(&tick).unwrap().push(message.clone()); + } else { + self.notes.insert(tick, vec![message.clone()]); + } + } + }, + midly::MidiMessage::NoteOff { key, vel: _ } => { + notes_on[key.as_int() as usize] = false; + if let Some(ref mut monitor) = monitor { + let t = event.time as usize; + if monitor[t].is_none() { + monitor[t] = Some(vec![]); + } + if let Some(Some(frame)) = monitor.get_mut(t) { + frame.push(event.bytes.into()) + } + } + if record { + let contains = self.notes.contains_key(&tick); + if contains { + self.notes.get_mut(&tick).unwrap().push(message.clone()); + } else { + self.notes.insert(tick, vec![message.clone()]); + } + } + }, + _ => {} + }, + _ => {} + } + } + } + +} diff --git a/src/device/sequencer/process.rs b/src/device/sequencer/process.rs index d8f12f04..a07b3bac 100644 --- a/src/device/sequencer/process.rs +++ b/src/device/sequencer/process.rs @@ -2,58 +2,46 @@ use crate::prelude::*; use super::*; pub fn process (state: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Control { - // Get currently playing sequence let sequence = state.phrases.get_mut(state.sequence); if sequence.is_none() { return Control::Continue } let sequence = sequence.unwrap(); - // Prepare output buffer and transport + let frame = scope.last_frame_time() as usize;//transport.pos.frame() as usize; let frames = scope.n_frames() as usize; let mut output: Vec>>> = vec![None;frames]; + // Check transport state. If starting or stopping, send "all notes off": 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( + sequence.chunk_out( &mut output, - &sequence.notes, + frame, + frames, &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, + 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) as u32; + sequence.chunk_in( &state.midi_in, + &scope, + Some(&mut output), + state.recording && state.playing == TransportState::Rolling, &mut state.notes_on, - state.playing, - state.monitoring, - state.recording, - tick as u32 + tick ); - // Write to port from output buffer // (containing notes from sequence and/or monitor) let mut writer = state.midi_out.writer(scope); @@ -65,11 +53,9 @@ pub fn process (state: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Cont } } } - Control::Continue } -type MIDIChunk = [Option>>]; /// Add "all notes off" to the start of a buffer. pub fn all_notes_off (output: &mut MIDIChunk) { @@ -82,95 +68,3 @@ pub fn all_notes_off (output: &mut MIDIChunk) { frame.push(buf); } } - -pub 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); - } - } - } - } -} - -pub 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 = LiveEvent::parse(event.bytes).unwrap(); - match msg { - LiveEvent::Midi { message, .. } => match message { - 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()]); - } - } - }, - _ => {} - }, - _ => {} - } - } -}