diff --git a/crates/tek_sequencer/src/arranger.rs b/crates/tek_sequencer/src/arranger.rs index 22eac590..ccd022ed 100644 --- a/crates/tek_sequencer/src/arranger.rs +++ b/crates/tek_sequencer/src/arranger.rs @@ -22,6 +22,8 @@ pub struct Arranger { pub arrangement_split: u16, /// Width of phrase pool pub phrases_split: u16, + /// Width and height of app at last render + pub size: Measure, } /// Sections in the arranger app that may be focused #[derive(Copy, Clone, PartialEq, Eq)] @@ -121,9 +123,9 @@ impl Arranger { phrases: Arc>>, ) -> Self { let mut app = Self { - jack: None, - focus_cursor: (0, 1), - phrases_split: 20, + jack: None, + focus_cursor: (0, 1), + phrases_split: 20, arrangement_split: 21, editor: PhraseEditor::new(), status: ArrangerStatusBar::ArrangementClip, @@ -135,6 +137,7 @@ impl Arranger { transport, arrangement, phrases, + size: Measure::new(), }; app.update_focus(); app diff --git a/crates/tek_sequencer/src/arranger_tui.rs b/crates/tek_sequencer/src/arranger_tui.rs index 0430dc93..c81237eb 100644 --- a/crates/tek_sequencer/src/arranger_tui.rs +++ b/crates/tek_sequencer/src/arranger_tui.rs @@ -118,7 +118,7 @@ impl Content for Arrangement { ArrangementViewMode::Vertical(factor) => add(&VerticalArranger(&self, factor)), }?; let color = if self.focused{Color::Rgb(150, 160, 90)}else{Color::Rgb(120, 130, 100)}; - add(&TuiStyle::fg("Session", color).push_x(1))?; + add(&TuiStyle::fg("Session", color).push_x(2))?; add(&self.size) }) } @@ -137,7 +137,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> { let border_hi = Color::Rgb(100, 110, 40); let border_lo = Color::Rgb(70, 80, 50); let border_fg = if self.0.focused { border_hi } else { border_lo }; - let border = Lozenge(Style::default().bg(border_bg).fg(border_fg)); + //let border = Lozenge(Style::default().bg(border_bg).fg(border_fg)); let track_title_h = 2u16; let tracks_footer = 3u16; let scene_title_w = 3 + Scene::longest_name(scenes) as u16; // x of 1st track diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index 52af81db..a8654cd2 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -403,15 +403,6 @@ impl PhrasePlayer { .map(|(started,_)|started.sample().get()) .map(|started|(started - self.clock.instant.sample().get()) as usize) } - pub fn playing_phrase (&self) -> Option<(usize, Arc>)> { - if let ( - Some(TransportState::Rolling), Some((started, Some(ref phrase))) - ) = (*self.clock.playing.read().unwrap(), &self.phrase) { - Some((started.sample().get() as usize, phrase.clone())) - } else { - None - } - } } /// Displays and edits phrase length pub struct PhraseLength { diff --git a/crates/tek_sequencer/src/sequencer_snd.rs b/crates/tek_sequencer/src/sequencer_snd.rs index 8e0831b2..2fd7e381 100644 --- a/crates/tek_sequencer/src/sequencer_snd.rs +++ b/crates/tek_sequencer/src/sequencer_snd.rs @@ -10,100 +10,137 @@ impl Audio for Sequencer { } impl Audio for PhrasePlayer { fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control { - let frame0 = scope.last_frame_time() as usize; - let frames = scope.n_frames() as usize; let has_midi_outputs = self.has_midi_outputs(); + let has_midi_inputs = self.has_midi_inputs(); if has_midi_outputs { - self.clear_midi_out_buf(frames); - self.reset_midi_out_buf(false /* FIXME where did force-reset come from? */); - } - let has_midi_inputs = self.has_midi_inputs(); - let quant = self.clock.quant().get(); - if let Some((start_frame, phrase)) = self.playing_phrase() { + // Clear output buffer(s) + self.clear(scope, false); // Write chunk of phrase to output - phrase.read().map(|phrase|{ - if has_midi_outputs { - let output = &mut self.midi_out_buf; - let notes_on = &mut self.notes_out.write().unwrap(); - let frame0 = frame0.saturating_sub(start_frame); - let frame_n = frame0 + frames; - let ticks = self.clock.timebase().pulses_between_samples(frame0, frame_n); - let mut buf = Vec::with_capacity(8); - for (sample, tick) in ticks { - let tick = tick % phrase.length; - for message in phrase.notes[tick].iter() { - buf.clear(); - let channel = 0.into(); - let message = *message; - LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); - output[sample].push(buf.clone()); - update_keys(notes_on, &message); - } - } - } - }).unwrap(); - let mut phrase = phrase.write().unwrap(); - let length = phrase.length; - // Monitor and record input - if has_midi_inputs && (self.recording || self.monitoring) { - // For highlighting keys and note repeat - let mut notes_in = self.notes_in.write().unwrap(); - // Record from each input - for input in self.midi_inputs.iter() { - for (frame, event, bytes) in parse_midi_input(input.iter(scope)) { - if let LiveEvent::Midi { message, .. } = event { - if self.monitoring { self.midi_out_buf[frame].push(bytes.to_vec()) } - if self.recording { - phrase.record_event({ - let pulse = self.clock.timebase().samples_to_pulse( - (frame0 + frame - start_frame) as f64 - ); - let quantized = (pulse / quant).round() * quant; - let looped = quantized as usize % length; - looped - }, message); - } - update_keys(&mut notes_in, &message); - } - } - } + self.play(scope); + } + if has_midi_inputs { + if self.recording || self.monitoring { + // Record and/or monitor input + self.record(scope) + } else if has_midi_outputs && self.monitoring { + // Monitor input to output + self.monitor(scope) } - } else if has_midi_inputs && has_midi_outputs && self.monitoring { + } + // Write to output port(s) + self.write(scope); + Control::Continue + } +} +impl PhrasePlayer { + fn has_midi_inputs (&self) -> bool { self.midi_inputs.len() > 0 } + fn has_midi_outputs (&self) -> bool { self.midi_outputs.len() > 0 } + /// 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, force_reset: bool) { + for frame in &mut self.midi_out_buf[0..scope.n_frames() as usize] { + frame.clear(); + } + if self.reset || force_reset { + all_notes_off(&mut self.midi_out_buf); self.reset = false; + } + } + /// Return playing phrase with starting point + fn playing (&self) -> Option<(usize, Arc>)> { + if let ( + Some(TransportState::Rolling), Some((started, Some(ref phrase))) + ) = (*self.clock.playing.read().unwrap(), &self.phrase) { + Some((started.sample().get() as usize, phrase.clone())) + } else { + None + } + } + /// Return next phrase with starting point + fn enqueued (&self) -> Option<(usize, Arc>)> { + if let ( + Some(TransportState::Rolling), Some((start_at, Some(ref phrase))) + ) = (*self.clock.playing.read().unwrap(), &self.next_phrase) { + Some((start_at.sample().get() as usize, phrase.clone())) + } else { + None + } + } + fn play (&mut self, scope: &ProcessScope) { + let sample0 = scope.last_frame_time() as usize; + let samples = scope.n_frames() as usize; + if let Some((start, ref phrase)) = self.playing() { + let sample = sample0.saturating_sub(start); + let ticks = self.clock.timebase().pulses_between_samples(sample, sample + samples); + phrase.read().map(|phrase|{ + let output = &mut self.midi_out_buf; + let notes_on = &mut self.notes_out.write().unwrap(); + let mut buf = Vec::with_capacity(8); + for (sample, tick) in ticks { + let tick = tick % phrase.length; + for message in phrase.notes[tick].iter() { + buf.clear(); + let channel = 0.into(); + let message = *message; + LiveEvent::Midi { channel, message }.write(&mut buf).unwrap(); + output[sample].push(buf.clone()); + update_keys(notes_on, &message); + } + } + }).unwrap() + } + } + fn record (&mut self, scope: &ProcessScope) { + let sample0 = scope.last_frame_time() as usize; + if let Some((start, ref phrase)) = self.playing() { + let quant = self.clock.quant().get(); + let mut phrase = phrase.write().unwrap(); + let length = phrase.length; + // For highlighting keys and note repeat let mut notes_in = self.notes_in.write().unwrap(); - // Monitor each input + // Record from each input for input in self.midi_inputs.iter() { - for (frame, event, bytes) in parse_midi_input(input.iter(scope)) { + for (sample, event, bytes) in parse_midi_input(input.iter(scope)) { if let LiveEvent::Midi { message, .. } = event { - self.midi_out_buf[frame].push(bytes.to_vec()); + if self.monitoring { self.midi_out_buf[sample].push(bytes.to_vec()) } + if self.recording { + phrase.record_event({ + let pulse = self.clock.timebase().samples_to_pulse( + (sample0 + sample - start) as f64 + ); + let quantized = (pulse / quant).round() * quant; + let looped = quantized as usize % length; + looped + }, message); + } update_keys(&mut notes_in, &message); } } } } - // Write to midi output + } + fn monitor (&mut self, scope: &ProcessScope) { + let mut notes_in = self.notes_in.write().unwrap(); + for input in self.midi_inputs.iter() { + for (sample, event, bytes) in parse_midi_input(input.iter(scope)) { + if let LiveEvent::Midi { message, .. } = event { + self.midi_out_buf[sample].push(bytes.to_vec()); + update_keys(&mut notes_in, &message); + } + } + } + } + fn write (&mut self, scope: &ProcessScope) { + let samples = scope.n_frames() as usize; for port in self.midi_outputs.iter_mut() { let writer = &mut port.writer(scope); let output = &self.midi_out_buf; - for time in 0..frames { + for time in 0..samples { for event in output[time].iter() { writer.write(&RawMidi { time: time as u32, bytes: &event }) .expect(&format!("{event:?}")); } } } - Control::Continue - } -} -impl PhrasePlayer { - pub fn has_midi_inputs (&self) -> bool { self.midi_inputs.len() > 0 } - pub fn has_midi_outputs (&self) -> bool { self.midi_outputs.len() > 0 } - /// Clear the section of the output buffer that we will be using - pub fn clear_midi_out_buf (&mut self, frames: usize) { - for frame in &mut self.midi_out_buf[0..frames] { frame.clear(); } - } - /// Emit "all notes off" at start of buffer if requested - pub fn reset_midi_out_buf (&mut self, force_reset: bool) { - if self.reset || force_reset { all_notes_off(&mut self.midi_out_buf); self.reset = false; } } } /// Add "all notes off" to the start of a buffer.