mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16: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
|
|
@ -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))));
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue