sequencer: merge Sequencer and SequencerTrack

This commit is contained in:
🪞👃🪞 2024-08-10 20:20:20 +03:00
parent 1d3d3875fe
commit 6206a43b4a
8 changed files with 171 additions and 190 deletions

View file

@ -11,13 +11,12 @@ pub struct Arranger {
/// Currently selected element.
pub selected: ArrangerFocus,
/// Collection of tracks.
pub tracks: Vec<SequencerTrack>,
pub tracks: Vec<Sequencer>,
/// Collection of scenes.
pub scenes: Vec<Scene>,
pub focused: bool,
pub entered: bool,
pub fixed_height: bool,
pub sequencer: Sequencer,
pub transport: Option<Arc<RwLock<TransportToolbar>>>,
}
@ -32,7 +31,6 @@ impl Arranger {
entered: true,
focused: true,
fixed_height: false,
sequencer: Sequencer::new(),
transport: None
}
}

View file

@ -42,12 +42,10 @@ pub const KEYMAP_ARRANGER: &'static [KeyBinding<Arranger>] = 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| {

View file

@ -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

View file

@ -15,7 +15,6 @@ submod! {
sequencer_cli
sequencer_handle
sequencer_render
sequencer_track
arranger
arranger_cli
arranger_focus

View file

@ -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<Option<usize>>,
@ -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);

View file

@ -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<usize>,
pub time_axis: ScaledAxis<usize>,
/// 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<Arc<RwLock<Phrase>>>,
/// Phrase selector
pub sequence: Option<usize>,
/// Output from current sequence.
pub midi_out: Option<Port<MidiOut>>,
/// MIDI output buffer
midi_out_buf: Vec<Vec<Vec<u8>>>,
/// 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<MidiIter>,
timebase: &Arc<Timebase>,
playing: Option<TransportState>,
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;
},
_ => {}
}
}
}

View file

@ -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();
}

View file

@ -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<Arc<RwLock<Phrase>>>,
/// Phrase selector
pub sequence: Option<usize>,
/// Output from current sequence.
pub midi_out: Option<Port<MidiOut>>,
/// MIDI output buffer
midi_out_buf: Vec<Vec<Vec<u8>>>,
/// 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<Self> {
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<MidiIter>,
timebase: &Arc<Timebase>,
playing: Option<TransportState>,
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;
},
_ => {}
}
}
}