show active clip in sequencer; highlight full clip

This commit is contained in:
🪞👃🪞 2024-10-04 10:31:41 +03:00
parent 7d6a43d87e
commit 4e033da2b4
3 changed files with 33 additions and 28 deletions

View file

@ -10,11 +10,11 @@ use tek_core::clap::{self, Parser};
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
pub struct SequencerCli { pub struct SequencerCli {
/// Name of JACK client /// Name of JACK client
#[arg(short, long)] name: Option<String>, #[arg(short, long)] name: Option<String>,
/// Pulses per quarter note (sequencer resolution; default: 96) /// Pulses per quarter note (sequencer resolution; default: 96)
#[arg(short, long)] ppq: Option<usize>, #[arg(short, long)] ppq: Option<usize>,
/// Default phrase duration (in pulses; default: 4 * PPQ = 1 bar) /// Default phrase duration (in pulses; default: 4 * PPQ = 1 bar)
#[arg(short, long)] length: Option<usize>, #[arg(short, long)] length: Option<usize>,
/// Whether to include a transport toolbar (default: true) /// Whether to include a transport toolbar (default: true)
#[arg(short, long)] transport: Option<bool> #[arg(short, long)] transport: Option<bool>
} }
@ -30,9 +30,10 @@ impl Sequencer<Tui> {
seq.ppq = ppq; seq.ppq = ppq;
} }
if let Some(length) = args.length { if let Some(length) = args.length {
if let Some(phrase) = seq.phrase.as_mut() { // TODO FIXME WTF
phrase.write().unwrap().length = length; //if let Some(phrase) = seq.phrase.as_mut() {
} //phrase.write().unwrap().length = length;
//}
} }
if args.transport == Some(true) { if args.transport == Some(true) {
seq.transport = Some(Arc::new(RwLock::new(TransportToolbar::new(None)))); seq.transport = Some(Arc::new(RwLock::new(TransportToolbar::new(None))));

View file

@ -452,7 +452,6 @@ pub struct Sequencer<E: Engine> {
pub mode: bool, pub mode: bool,
pub focused: bool, pub focused: bool,
pub entered: bool, pub entered: bool,
pub phrase: Option<Arc<RwLock<Phrase>>>,
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>, pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
/// The full piano roll is rendered to this buffer /// The full piano roll is rendered to this buffer
pub buffer: BigBuffer, pub buffer: BigBuffer,
@ -493,7 +492,6 @@ pub struct Sequencer<E: Engine> {
/// Highlight keys on piano roll. /// Highlight keys on piano roll.
pub notes_out: [bool;128], pub notes_out: [bool;128],
} }
impl<E: Engine> Sequencer<E> { impl<E: Engine> Sequencer<E> {
pub fn new (name: &str) -> Self { pub fn new (name: &str) -> Self {
Self { Self {
@ -516,7 +514,6 @@ impl<E: Engine> Sequencer<E> {
mode: false, mode: false,
keys_in: [false;128], keys_in: [false;128],
keys_out: [false;128], keys_out: [false;128],
phrase: None,
now: 0, now: 0,
ppq: 96, ppq: 96,
transport: None, transport: None,
@ -620,7 +617,6 @@ impl<E: Engine> Sequencer<E> {
write_midi_output(&mut out.writer(scope), &self.midi_out_buf, frames); write_midi_output(&mut out.writer(scope), &self.midi_out_buf, frames);
} }
} }
#[inline] #[inline]
fn process_monitor_event (&mut self, frame: usize, event: &LiveEvent, bytes: &[u8]) { fn process_monitor_event (&mut self, frame: usize, event: &LiveEvent, bytes: &[u8]) {
match event { match event {
@ -631,11 +627,9 @@ impl<E: Engine> Sequencer<E> {
_ => {} _ => {}
} }
} }
#[inline] fn write_to_output_buffer (&mut self, frame: usize, bytes: &[u8]) { #[inline] fn write_to_output_buffer (&mut self, frame: usize, bytes: &[u8]) {
self.midi_out_buf[frame].push(bytes.to_vec()); self.midi_out_buf[frame].push(bytes.to_vec());
} }
#[inline] #[inline]
fn process_monitor_message (&mut self, message: &MidiMessage) { fn process_monitor_message (&mut self, message: &MidiMessage) {
match message { match message {

View file

@ -93,10 +93,12 @@ impl Handle<Tui> for Arranger<Tui> {
// increment: use next clip here // increment: use next clip here
key!(KeyCode::Char('.')) => { key!(KeyCode::Char('.')) => {
self.phrase_next(); self.phrase_next();
self.show_phrase();
}, },
// decrement: use previous next clip here // decrement: use previous next clip here
key!(KeyCode::Char(',')) => { key!(KeyCode::Char(',')) => {
self.phrase_prev(); self.phrase_prev();
self.show_phrase();
}, },
// decrement: use previous clip here // decrement: use previous clip here
key!(KeyCode::Enter) => { key!(KeyCode::Enter) => {
@ -164,13 +166,16 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
Layers::new(move |add|{ Layers::new(move |add|{
let rows: &[(usize, usize)] = rows.as_ref(); let rows: &[(usize, usize)] = rows.as_ref();
let cols: &[(usize, usize)] = cols.as_ref(); let cols: &[(usize, usize)] = cols.as_ref();
let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) => let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) =>
(&track.name.read().unwrap().as_str() as &dyn Widget<Engine = Tui>) (&track.name.read().unwrap().as_str() as &dyn Widget<Engine = Tui>)
.min_xy(*w as u16, 2).push_x(offset)); .min_xy(*w as u16, 2).push_x(offset));
let scene_name = |scene, playing: bool, height|row!( let scene_name = |scene, playing: bool, height|row!(
if playing { "" } else { " " }, if playing { "" } else { " " },
(scene as &Scene).name.read().unwrap().as_str(), (scene as &Scene).name.read().unwrap().as_str(),
).fixed_xy(offset.saturating_sub(1), height); ).fixed_xy(offset.saturating_sub(1), height);
let scene_clip = |scene, track: usize, w: u16, h: u16|Layers::new(move |add|{ let scene_clip = |scene, track: usize, w: u16, h: u16|Layers::new(move |add|{
let mut color = Color::Rgb(40, 50, 30); let mut color = Color::Rgb(40, 50, 30);
match (tracks.get(track), (scene as &Scene).clips.get(track)) { match (tracks.get(track), (scene as &Scene).clips.get(track)) {
@ -179,7 +184,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name; let name = &(phrase as &Arc<RwLock<Phrase>>).read().unwrap().name;
let name = name.read().unwrap(); let name = name.read().unwrap();
let name = format!("{clip:02} {}", name); let name = format!("{clip:02} {}", name);
add(&name.as_str().push_x(1))?; add(&name.as_str().push_x(1).fixed_x(w))?;
if (track as &Sequencer<_>).playing_phrase == Some(*clip) { if (track as &Sequencer<_>).playing_phrase == Some(*clip) {
color = COLOR_PLAYING color = COLOR_PLAYING
} else { } else {
@ -192,6 +197,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
}; };
add(&Background(color)) add(&Background(color))
}).fixed_xy(w, h); }).fixed_xy(w, h);
let tracks_clips = col!((scene, (pulses, _)) in scenes.iter().zip(rows) => { let tracks_clips = col!((scene, (pulses, _)) in scenes.iter().zip(rows) => {
let height = 1.max((pulses / 96) as u16); let height = 1.max((pulses / 96) as u16);
let playing = scene.is_playing(tracks); let playing = scene.is_playing(tracks);
@ -203,6 +209,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
Ok(()) Ok(())
}).fixed_y(height) }).fixed_y(height)
}); });
add(&VerticalArrangerGrid(offset, &rows, &cols))?; add(&VerticalArrangerGrid(offset, &rows, &cols))?;
add(&VerticalArrangerCursor(state.focused, state.selected, offset, &cols, &rows))?; add(&VerticalArrangerCursor(state.focused, state.selected, offset, &cols, &rows))?;
add(&col!(track_titles, tracks_clips))?; add(&col!(track_titles, tracks_clips))?;
@ -644,21 +651,21 @@ impl Handle<Tui> for ArrangerRenameModal<Tui> {
impl Sequencer<Tui> { impl Sequencer<Tui> {
const H_KEYS_OFFSET: usize = 5; const H_KEYS_OFFSET: usize = 5;
/// Select which pattern to display. This pre-renders it to the buffer at full resolution. /// Select which pattern to display. This pre-renders it to the buffer at full resolution.
pub fn show (&mut self, phrase: Option<&Arc<RwLock<Phrase>>>) -> Usually<()> { pub fn show (&mut self, index: Option<usize>) {
self.phrase = phrase.map(Clone::clone); self.viewing_phrase = index;
if let Some(ref phrase) = self.phrase { if let Some(phrase) = index.map(|index|self.phrases.get(index)).flatten() {
let width = usize::MAX.min(phrase.read().unwrap().length); let width = usize::MAX.min(phrase.read().unwrap().length);
let mut buffer = BigBuffer::new(width, 64); let mut buffer = BigBuffer::new(width, 64);
let phrase = phrase.read().unwrap(); let phrase = phrase.read().unwrap();
Self::fill_seq_bg(&mut buffer, phrase.length, self.ppq)?; Self::fill_seq_bg(&mut buffer, phrase.length, self.ppq);
Self::fill_seq_fg(&mut buffer, &phrase)?; Self::fill_seq_fg(&mut buffer, &phrase);
self.buffer = buffer; self.buffer = buffer;
} else { } else {
self.viewing_phrase = None;
self.buffer = Default::default(); self.buffer = Default::default();
} }
Ok(())
} }
fn fill_seq_bg (buf: &mut BigBuffer, length: usize, ppq: usize) -> Usually<()> { fn fill_seq_bg (buf: &mut BigBuffer, length: usize, ppq: usize) {
for x in 0..buf.width { for x in 0..buf.width {
if x as usize >= length { if x as usize >= length {
break break
@ -684,9 +691,8 @@ impl Sequencer<Tui> {
}); });
} }
} }
Ok(())
} }
fn fill_seq_fg (buf: &mut BigBuffer, phrase: &Phrase) -> Usually<()> { fn fill_seq_fg (buf: &mut BigBuffer, phrase: &Phrase) {
let mut notes_on = [false;128]; let mut notes_on = [false;128];
for x in 0..buf.width { for x in 0..buf.width {
if x as usize >= phrase.length { if x as usize >= phrase.length {
@ -731,7 +737,6 @@ impl Sequencer<Tui> {
} }
} }
} }
Ok(())
} }
pub(crate) fn style_focus (&self) -> Option<Style> { pub(crate) fn style_focus (&self) -> Option<Style> {
Some(if self.focused { Some(if self.focused {
@ -765,14 +770,16 @@ impl Content for Sequencer<Tui> {
type Engine = Tui; type Engine = Tui;
fn content (&self) -> impl Widget<Engine = Tui> { fn content (&self) -> impl Widget<Engine = Tui> {
let toolbar = Stack::down(move|add|{ let toolbar = Stack::down(move|add|{
add(&col! {"Name", self.name.read().unwrap().as_str(),})?; add(&col!("Name:", self.name.read().unwrap().as_str()))?;
if let Some(phrase) = self.phrase.as_ref() { if let Some(phrase) = self.viewing_phrase
panic!(); .map(|index|self.phrases.get(index))
.flatten()
{
let phrase = phrase.read().unwrap(); let phrase = phrase.read().unwrap();
let length = phrase.length; let length = phrase.length;
let looped = phrase.looped; let looped = phrase.looped;
add(&"")?; add(&"")?;
add(&col!("Length: ", format!("{length}").as_str()))?; add(&col!("Length: ", format!("{}q{}p", length / 96, length % 96).as_str()))?;
add(&"")?; add(&"")?;
add(&col!("Loop [ ]", "From: ", " 1.1.1", "Length: ", " 1.0.0"))?; add(&col!("Loop [ ]", "From: ", " 1.1.1", "Length: ", " 1.0.0"))?;
add(&"")?; add(&"")?;
@ -794,7 +801,10 @@ impl Content for Sequencer<Tui> {
}).fill_y(), }).fill_y(),
// playhead // playhead
CustomWidget::new(|_|Ok(Some([32,2])), |to: &mut TuiOutput|{ CustomWidget::new(|_|Ok(Some([32,2])), |to: &mut TuiOutput|{
if let Some(phrase) = self.phrase.as_ref() { if let Some(phrase) = self.viewing_phrase
.map(|index|self.phrases.get(index))
.flatten()
{
let time_0 = self.time_axis.start; let time_0 = self.time_axis.start;
let time_z = self.time_axis.scale; let time_z = self.time_axis.scale;
let now = self.now % phrase.read().unwrap().length; let now = self.now % phrase.read().unwrap().length;