mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
show active clip in sequencer; highlight full clip
This commit is contained in:
parent
7d6a43d87e
commit
4e033da2b4
3 changed files with 33 additions and 28 deletions
|
|
@ -10,11 +10,11 @@ use tek_core::clap::{self, Parser};
|
|||
#[command(version, about, long_about = None)]
|
||||
pub struct SequencerCli {
|
||||
/// Name of JACK client
|
||||
#[arg(short, long)] name: Option<String>,
|
||||
#[arg(short, long)] name: Option<String>,
|
||||
/// 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)
|
||||
#[arg(short, long)] length: Option<usize>,
|
||||
#[arg(short, long)] length: Option<usize>,
|
||||
/// Whether to include a transport toolbar (default: true)
|
||||
#[arg(short, long)] transport: Option<bool>
|
||||
}
|
||||
|
|
@ -30,9 +30,10 @@ impl Sequencer<Tui> {
|
|||
seq.ppq = ppq;
|
||||
}
|
||||
if let Some(length) = args.length {
|
||||
if let Some(phrase) = seq.phrase.as_mut() {
|
||||
phrase.write().unwrap().length = length;
|
||||
}
|
||||
// TODO FIXME WTF
|
||||
//if let Some(phrase) = seq.phrase.as_mut() {
|
||||
//phrase.write().unwrap().length = length;
|
||||
//}
|
||||
}
|
||||
if args.transport == Some(true) {
|
||||
seq.transport = Some(Arc::new(RwLock::new(TransportToolbar::new(None))));
|
||||
|
|
|
|||
|
|
@ -452,7 +452,6 @@ pub struct Sequencer<E: Engine> {
|
|||
pub mode: bool,
|
||||
pub focused: bool,
|
||||
pub entered: bool,
|
||||
pub phrase: Option<Arc<RwLock<Phrase>>>,
|
||||
pub transport: Option<Arc<RwLock<TransportToolbar<E>>>>,
|
||||
/// The full piano roll is rendered to this buffer
|
||||
pub buffer: BigBuffer,
|
||||
|
|
@ -493,7 +492,6 @@ pub struct Sequencer<E: Engine> {
|
|||
/// Highlight keys on piano roll.
|
||||
pub notes_out: [bool;128],
|
||||
}
|
||||
|
||||
impl<E: Engine> Sequencer<E> {
|
||||
pub fn new (name: &str) -> Self {
|
||||
Self {
|
||||
|
|
@ -516,7 +514,6 @@ impl<E: Engine> Sequencer<E> {
|
|||
mode: false,
|
||||
keys_in: [false;128],
|
||||
keys_out: [false;128],
|
||||
phrase: None,
|
||||
now: 0,
|
||||
ppq: 96,
|
||||
transport: None,
|
||||
|
|
@ -620,7 +617,6 @@ impl<E: Engine> Sequencer<E> {
|
|||
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 {
|
||||
|
|
@ -631,11 +627,9 @@ impl<E: Engine> Sequencer<E> {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
|
|
|
|||
|
|
@ -93,10 +93,12 @@ impl Handle<Tui> for Arranger<Tui> {
|
|||
// increment: use next clip here
|
||||
key!(KeyCode::Char('.')) => {
|
||||
self.phrase_next();
|
||||
self.show_phrase();
|
||||
},
|
||||
// decrement: use previous next clip here
|
||||
key!(KeyCode::Char(',')) => {
|
||||
self.phrase_prev();
|
||||
self.show_phrase();
|
||||
},
|
||||
// decrement: use previous clip here
|
||||
key!(KeyCode::Enter) => {
|
||||
|
|
@ -164,13 +166,16 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
|||
Layers::new(move |add|{
|
||||
let rows: &[(usize, usize)] = rows.as_ref();
|
||||
let cols: &[(usize, usize)] = cols.as_ref();
|
||||
|
||||
let track_titles = row!((track, (w, _)) in tracks.iter().zip(cols) =>
|
||||
(&track.name.read().unwrap().as_str() as &dyn Widget<Engine = Tui>)
|
||||
.min_xy(*w as u16, 2).push_x(offset));
|
||||
|
||||
let scene_name = |scene, playing: bool, height|row!(
|
||||
if playing { "▶ " } else { " " },
|
||||
(scene as &Scene).name.read().unwrap().as_str(),
|
||||
).fixed_xy(offset.saturating_sub(1), height);
|
||||
|
||||
let scene_clip = |scene, track: usize, w: u16, h: u16|Layers::new(move |add|{
|
||||
let mut color = Color::Rgb(40, 50, 30);
|
||||
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 = name.read().unwrap();
|
||||
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) {
|
||||
color = COLOR_PLAYING
|
||||
} else {
|
||||
|
|
@ -192,6 +197,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
|||
};
|
||||
add(&Background(color))
|
||||
}).fixed_xy(w, h);
|
||||
|
||||
let tracks_clips = col!((scene, (pulses, _)) in scenes.iter().zip(rows) => {
|
||||
let height = 1.max((pulses / 96) as u16);
|
||||
let playing = scene.is_playing(tracks);
|
||||
|
|
@ -203,6 +209,7 @@ impl<'a> Content for VerticalArranger<'a, Tui> {
|
|||
Ok(())
|
||||
}).fixed_y(height)
|
||||
});
|
||||
|
||||
add(&VerticalArrangerGrid(offset, &rows, &cols))?;
|
||||
add(&VerticalArrangerCursor(state.focused, state.selected, offset, &cols, &rows))?;
|
||||
add(&col!(track_titles, tracks_clips))?;
|
||||
|
|
@ -644,21 +651,21 @@ impl Handle<Tui> for ArrangerRenameModal<Tui> {
|
|||
impl Sequencer<Tui> {
|
||||
const H_KEYS_OFFSET: usize = 5;
|
||||
/// 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<()> {
|
||||
self.phrase = phrase.map(Clone::clone);
|
||||
if let Some(ref phrase) = self.phrase {
|
||||
pub fn show (&mut self, index: Option<usize>) {
|
||||
self.viewing_phrase = index;
|
||||
if let Some(phrase) = index.map(|index|self.phrases.get(index)).flatten() {
|
||||
let width = usize::MAX.min(phrase.read().unwrap().length);
|
||||
let mut buffer = BigBuffer::new(width, 64);
|
||||
let phrase = phrase.read().unwrap();
|
||||
Self::fill_seq_bg(&mut buffer, phrase.length, self.ppq)?;
|
||||
Self::fill_seq_fg(&mut buffer, &phrase)?;
|
||||
Self::fill_seq_bg(&mut buffer, phrase.length, self.ppq);
|
||||
Self::fill_seq_fg(&mut buffer, &phrase);
|
||||
self.buffer = buffer;
|
||||
} else {
|
||||
self.viewing_phrase = None;
|
||||
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 {
|
||||
if x as usize >= length {
|
||||
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];
|
||||
for x in 0..buf.width {
|
||||
if x as usize >= phrase.length {
|
||||
|
|
@ -731,7 +737,6 @@ impl Sequencer<Tui> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub(crate) fn style_focus (&self) -> Option<Style> {
|
||||
Some(if self.focused {
|
||||
|
|
@ -765,14 +770,16 @@ impl Content for Sequencer<Tui> {
|
|||
type Engine = Tui;
|
||||
fn content (&self) -> impl Widget<Engine = Tui> {
|
||||
let toolbar = Stack::down(move|add|{
|
||||
add(&col! {"Name", self.name.read().unwrap().as_str(),})?;
|
||||
if let Some(phrase) = self.phrase.as_ref() {
|
||||
panic!();
|
||||
add(&col!("Name:", self.name.read().unwrap().as_str()))?;
|
||||
if let Some(phrase) = self.viewing_phrase
|
||||
.map(|index|self.phrases.get(index))
|
||||
.flatten()
|
||||
{
|
||||
let phrase = phrase.read().unwrap();
|
||||
let length = phrase.length;
|
||||
let looped = phrase.looped;
|
||||
add(&"")?;
|
||||
add(&col!("Length: ", format!("{length}").as_str()))?;
|
||||
add(&col!("Length: ", format!("{}q{}p", length / 96, length % 96).as_str()))?;
|
||||
add(&"")?;
|
||||
add(&col!("Loop [ ]", "From: ", " 1.1.1", "Length: ", " 1.0.0"))?;
|
||||
add(&"")?;
|
||||
|
|
@ -794,7 +801,10 @@ impl Content for Sequencer<Tui> {
|
|||
}).fill_y(),
|
||||
// playhead
|
||||
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_z = self.time_axis.scale;
|
||||
let now = self.now % phrase.read().unwrap().length;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue