diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index a9a3239a..05186875 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -11,13 +11,12 @@ pub struct Arranger { /// Currently selected element. pub selected: ArrangerFocus, /// Collection of tracks. - pub tracks: Vec, + pub tracks: Vec, /// Collection of scenes. pub scenes: Vec, pub focused: bool, pub entered: bool, pub fixed_height: bool, - pub sequencer: Sequencer, pub transport: Option>>, } @@ -32,7 +31,6 @@ impl Arranger { entered: true, focused: true, fixed_height: false, - sequencer: Sequencer::new(), transport: None } } diff --git a/crates/tek_sequencer/src/arranger_handle.rs b/crates/tek_sequencer/src/arranger_handle.rs index fab477a6..b96bbab2 100644 --- a/crates/tek_sequencer/src/arranger_handle.rs +++ b/crates/tek_sequencer/src/arranger_handle.rs @@ -42,12 +42,10 @@ pub const KEYMAP_ARRANGER: &'static [KeyBinding] = keymap!(Arranger { }], [Char('.'), NONE, "arranger_increment", "set next clip at cursor", |arranger: &mut Arranger| { arranger.phrase_next(); - arranger.sequencer.phrase = arranger.phrase().map(Clone::clone); Ok(true) }], [Char(','), NONE, "arranger_decrement", "set previous clip at cursor", |arranger: &mut Arranger| { arranger.phrase_prev(); - arranger.sequencer.phrase = arranger.phrase().map(Clone::clone); Ok(true) }], [Enter, NONE, "arranger_activate", "activate item at cursor", |arranger: &mut Arranger| { diff --git a/crates/tek_sequencer/src/arranger_track.rs b/crates/tek_sequencer/src/arranger_track.rs index a7c78fc5..f9309da5 100644 --- a/crates/tek_sequencer/src/arranger_track.rs +++ b/crates/tek_sequencer/src/arranger_track.rs @@ -4,10 +4,10 @@ use super::Arranger; /// Track management methods impl Arranger { - pub fn track (&self) -> Option<&SequencerTrack> { + pub fn track (&self) -> Option<&Sequencer> { self.selected.track().map(|t|self.tracks.get(t)).flatten() } - pub fn track_mut (&mut self) -> Option<&mut SequencerTrack> { + pub fn track_mut (&mut self) -> Option<&mut Sequencer> { self.selected.track().map(|t|self.tracks.get_mut(t)).flatten() } pub fn track_next (&mut self) { @@ -16,11 +16,11 @@ impl Arranger { pub fn track_prev (&mut self) { self.selected.track_prev() } - pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut SequencerTrack> { + pub fn track_add (&mut self, name: Option<&str>) -> Usually<&mut Sequencer> { self.tracks.push(name.map_or_else( - || SequencerTrack::new(&self.track_default_name()), - |name| SequencerTrack::new(name), - )?); + || Sequencer::new(&self.track_default_name()), + |name| Sequencer::new(name), + )); let index = self.tracks.len() - 1; Ok(&mut self.tracks[index]) } @@ -32,13 +32,13 @@ impl Arranger { } } -pub fn track_name_max_len (tracks: &[SequencerTrack]) -> usize { +pub fn track_name_max_len (tracks: &[Sequencer]) -> usize { tracks.iter() .map(|s|s.name.len()) .fold(0, usize::max) } -pub fn track_clip_name_lengths (tracks: &[SequencerTrack]) -> Vec<(usize, usize)> { +pub fn track_clip_name_lengths (tracks: &[Sequencer]) -> Vec<(usize, usize)> { let mut total = 0; let mut lengths: Vec<(usize, usize)> = tracks.iter().map(|track|{ let len = 2 + track.phrases diff --git a/crates/tek_sequencer/src/lib.rs b/crates/tek_sequencer/src/lib.rs index 8b61cf57..f8ab8162 100644 --- a/crates/tek_sequencer/src/lib.rs +++ b/crates/tek_sequencer/src/lib.rs @@ -15,7 +15,6 @@ submod! { sequencer_cli sequencer_handle sequencer_render - sequencer_track arranger arranger_cli arranger_focus diff --git a/crates/tek_sequencer/src/scene.rs b/crates/tek_sequencer/src/scene.rs index 94e3e9ba..48c07216 100644 --- a/crates/tek_sequencer/src/scene.rs +++ b/crates/tek_sequencer/src/scene.rs @@ -1,6 +1,7 @@ use crate::*; /// A collection of phrases to play on each track. +#[derive(Default)] pub struct Scene { pub name: String, pub clips: Vec>, @@ -13,14 +14,14 @@ impl Scene { Self { name, clips, } } /// Returns the pulse length of the longest phrase in the scene - pub fn pulses (&self, tracks: &[SequencerTrack]) -> usize { + pub fn pulses (&self, tracks: &[Sequencer]) -> usize { self.clips.iter().enumerate() .filter_map(|(i, c)|c.map(|c|tracks[i].phrases.get(c))) .filter_map(|p|p) .fold(0, |a, p|a.max(p.read().unwrap().length)) } /// Returns true if all phrases in the scene are currently playing - pub fn is_playing (&self, tracks: &[SequencerTrack]) -> bool { + pub fn is_playing (&self, tracks: &[Sequencer]) -> bool { self.clips.iter().enumerate() .all(|(track_index, phrase_index)|match phrase_index { Some(i) => tracks[track_index].sequence == Some(*i), @@ -35,7 +36,7 @@ pub fn scene_name_max_len (scenes: &[Scene]) -> usize { .fold(0, usize::max) } -pub fn scene_ppqs (tracks: &[SequencerTrack], scenes: &[Scene]) -> Vec<(usize, usize)> { +pub fn scene_ppqs (tracks: &[Sequencer], scenes: &[Scene]) -> Vec<(usize, usize)> { let mut total = 0; let mut scenes: Vec<(usize, usize)> = scenes.iter().map(|scene|{ let pulses = scene.pulses(tracks); diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index 77813de4..baa7c636 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -1,4 +1,5 @@ use crate::*; +use tek_core::Direction; /// Phrase editor. pub struct Sequencer { @@ -20,12 +21,42 @@ pub struct Sequencer { pub ppq: usize, pub note_axis: FixedAxis, pub time_axis: ScaledAxis, + /// Play input through output. + pub monitoring: bool, + /// Write input to sequence. + pub recording: bool, + /// Overdub input to sequence. + pub overdub: bool, + /// Map: tick -> MIDI events at tick + pub phrases: Vec>>, + /// Phrase selector + pub sequence: Option, + /// Output from current sequence. + pub midi_out: Option>, + /// MIDI output buffer + midi_out_buf: Vec>>, + /// Send all notes off + pub reset: bool, // TODO?: after Some(nframes) + /// Highlight keys on piano roll. + pub notes_in: [bool;128], + /// Highlight keys on piano roll. + pub notes_out: [bool;128], } impl Sequencer { - pub fn new () -> Self { + pub fn new (name: &str) -> Self { Self { - name: "".into(), + name: name.into(), + monitoring: false, + recording: false, + overdub: true, + phrases: vec![], + sequence: None, + midi_out: None, + midi_out_buf: vec![Vec::with_capacity(16);16384], + reset: true, + notes_in: [false;128], + notes_out: [false;128], buffer: Default::default(), keys: keys_vert(), entered: false, @@ -48,4 +79,128 @@ impl Sequencer { }, } } + pub fn toggle_monitor (&mut self) { + self.monitoring = !self.monitoring; + } + pub fn toggle_record (&mut self) { + self.recording = !self.recording; + } + pub fn toggle_overdub (&mut self) { + self.overdub = !self.overdub; + } + pub fn process ( + &mut self, + input: Option, + timebase: &Arc, + playing: Option, + started: Option<(usize, usize)>, + quant: usize, + reset: bool, + scope: &ProcessScope, + (frame0, frames): (usize, usize), + (_usec0, _usecs): (usize, usize), + period: f64, + ) { + if self.midi_out.is_some() { + // Clear the section of the output buffer that we will be using + for frame in &mut self.midi_out_buf[0..frames] { + frame.clear(); + } + // Emit "all notes off" at start of buffer if requested + if self.reset { + all_notes_off(&mut self.midi_out_buf); + self.reset = false; + } else if reset { + all_notes_off(&mut self.midi_out_buf); + } + } + if let ( + Some(TransportState::Rolling), Some((start_frame, _)), Some(phrase) + ) = ( + playing, started, self.sequence.and_then(|id|self.phrases.get_mut(id)) + ) { + phrase.read().map(|phrase|{ + if self.midi_out.is_some() { + phrase.process_out( + &mut self.midi_out_buf, + &mut self.notes_out, + timebase, + (frame0.saturating_sub(start_frame), frames, period) + ); + } + }).unwrap(); + let mut phrase = phrase.write().unwrap(); + let length = phrase.length; + // Monitor and record input + if input.is_some() && (self.recording || self.monitoring) { + // For highlighting keys and note repeat + for (frame, event, bytes) in parse_midi_input(input.unwrap()) { + match event { + LiveEvent::Midi { message, .. } => { + if self.monitoring { + self.midi_out_buf[frame].push(bytes.to_vec()) + } + if self.recording { + phrase.record_event({ + let pulse = timebase.frame_to_pulse( + (frame0 + frame - start_frame) as f64 + ); + let quantized = ( + pulse / quant as f64 + ).round() as usize * quant; + let looped = quantized % length; + looped + }, message); + } + match message { + MidiMessage::NoteOn { key, .. } => { + self.notes_in[key.as_int() as usize] = true; + } + MidiMessage::NoteOff { key, .. } => { + self.notes_in[key.as_int() as usize] = false; + }, + _ => {} + } + }, + _ => {} + } + } + } + } else if input.is_some() && self.midi_out.is_some() && self.monitoring { + for (frame, event, bytes) in parse_midi_input(input.unwrap()) { + self.process_monitor_event(frame, &event, bytes) + } + } + if let Some(out) = &mut self.midi_out { + write_midi_output(&mut out.writer(scope), &self.midi_out_buf, frames); + } + } + + #[inline] + fn process_monitor_event (&mut self, frame: usize, event: &LiveEvent, bytes: &[u8]) { + match event { + LiveEvent::Midi { message, .. } => { + self.write_to_output_buffer(frame, bytes); + self.process_monitor_message(&message); + }, + _ => {} + } + } + + #[inline] fn write_to_output_buffer (&mut self, frame: usize, bytes: &[u8]) { + self.midi_out_buf[frame].push(bytes.to_vec()); + } + + #[inline] + fn process_monitor_message (&mut self, message: &MidiMessage) { + match message { + MidiMessage::NoteOn { key, .. } => { + self.notes_in[key.as_int() as usize] = true; + } + MidiMessage::NoteOff { key, .. } => { + self.notes_in[key.as_int() as usize] = false; + }, + _ => {} + } + } } diff --git a/crates/tek_sequencer/src/sequencer_cli.rs b/crates/tek_sequencer/src/sequencer_cli.rs index 404b064b..fe256521 100644 --- a/crates/tek_sequencer/src/sequencer_cli.rs +++ b/crates/tek_sequencer/src/sequencer_cli.rs @@ -18,7 +18,7 @@ pub struct SequencerCli { impl Sequencer { pub fn from_args () -> Self { let args = SequencerCli::parse(); - let mut seq = Self::new(); + let mut seq = Self::new(""); if let Some(name) = args.name { seq.name = name.clone(); } diff --git a/crates/tek_sequencer/src/sequencer_track.rs b/crates/tek_sequencer/src/sequencer_track.rs deleted file mode 100644 index 7cb3806e..00000000 --- a/crates/tek_sequencer/src/sequencer_track.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::*; -use tek_core::Direction; - -/// A sequencer track. -#[derive(Debug)] -pub struct SequencerTrack { - pub name: String, - /// Play input through output. - pub monitoring: bool, - /// Write input to sequence. - pub recording: bool, - /// Overdub input to sequence. - pub overdub: bool, - /// Map: tick -> MIDI events at tick - pub phrases: Vec>>, - /// Phrase selector - pub sequence: Option, - /// Output from current sequence. - pub midi_out: Option>, - /// MIDI output buffer - midi_out_buf: Vec>>, - /// Send all notes off - pub reset: bool, // TODO?: after Some(nframes) - /// Highlight keys on piano roll. - pub notes_in: [bool;128], - /// Highlight keys on piano roll. - pub notes_out: [bool;128], -} - -impl SequencerTrack { - pub fn new (name: &str) -> Usually { - Ok(Self { - name: name.to_string(), - monitoring: false, - recording: false, - overdub: true, - phrases: vec![], - sequence: None, - midi_out: None, - midi_out_buf: vec![Vec::with_capacity(16);16384], - reset: true, - notes_in: [false;128], - notes_out: [false;128], - }) - } - pub fn toggle_monitor (&mut self) { - self.monitoring = !self.monitoring; - } - pub fn toggle_record (&mut self) { - self.recording = !self.recording; - } - pub fn toggle_overdub (&mut self) { - self.overdub = !self.overdub; - } - pub fn process ( - &mut self, - input: Option, - timebase: &Arc, - playing: Option, - started: Option<(usize, usize)>, - quant: usize, - reset: bool, - scope: &ProcessScope, - (frame0, frames): (usize, usize), - (_usec0, _usecs): (usize, usize), - period: f64, - ) { - if self.midi_out.is_some() { - // Clear the section of the output buffer that we will be using - for frame in &mut self.midi_out_buf[0..frames] { - frame.clear(); - } - // Emit "all notes off" at start of buffer if requested - if self.reset { - all_notes_off(&mut self.midi_out_buf); - self.reset = false; - } else if reset { - all_notes_off(&mut self.midi_out_buf); - } - } - if let ( - Some(TransportState::Rolling), Some((start_frame, _)), Some(phrase) - ) = ( - playing, started, self.sequence.and_then(|id|self.phrases.get_mut(id)) - ) { - phrase.read().map(|phrase|{ - if self.midi_out.is_some() { - phrase.process_out( - &mut self.midi_out_buf, - &mut self.notes_out, - timebase, - (frame0.saturating_sub(start_frame), frames, period) - ); - } - }).unwrap(); - let mut phrase = phrase.write().unwrap(); - let length = phrase.length; - // Monitor and record input - if input.is_some() && (self.recording || self.monitoring) { - // For highlighting keys and note repeat - for (frame, event, bytes) in parse_midi_input(input.unwrap()) { - match event { - LiveEvent::Midi { message, .. } => { - if self.monitoring { - self.midi_out_buf[frame].push(bytes.to_vec()) - } - if self.recording { - phrase.record_event({ - let pulse = timebase.frame_to_pulse( - (frame0 + frame - start_frame) as f64 - ); - let quantized = ( - pulse / quant as f64 - ).round() as usize * quant; - let looped = quantized % length; - looped - }, message); - } - match message { - MidiMessage::NoteOn { key, .. } => { - self.notes_in[key.as_int() as usize] = true; - } - MidiMessage::NoteOff { key, .. } => { - self.notes_in[key.as_int() as usize] = false; - }, - _ => {} - } - }, - _ => {} - } - } - } - } else if input.is_some() && self.midi_out.is_some() && self.monitoring { - for (frame, event, bytes) in parse_midi_input(input.unwrap()) { - self.process_monitor_event(frame, &event, bytes) - } - } - if let Some(out) = &mut self.midi_out { - write_midi_output(&mut out.writer(scope), &self.midi_out_buf, frames); - } - } - - #[inline] - fn process_monitor_event (&mut self, frame: usize, event: &LiveEvent, bytes: &[u8]) { - match event { - LiveEvent::Midi { message, .. } => { - self.write_to_output_buffer(frame, bytes); - self.process_monitor_message(&message); - }, - _ => {} - } - } - - #[inline] fn write_to_output_buffer (&mut self, frame: usize, bytes: &[u8]) { - self.midi_out_buf[frame].push(bytes.to_vec()); - } - - #[inline] - fn process_monitor_message (&mut self, message: &MidiMessage) { - match message { - MidiMessage::NoteOn { key, .. } => { - self.notes_in[key.as_int() as usize] = true; - } - MidiMessage::NoteOff { key, .. } => { - self.notes_in[key.as_int() as usize] = false; - }, - _ => {} - } - } -}