layer midi status; navigate sample list

This commit is contained in:
🪞👃🪞 2024-12-27 20:55:34 +01:00
parent fc0a398702
commit e69cf6d9cb
5 changed files with 127 additions and 54 deletions

View file

@ -97,7 +97,7 @@ render!(<Tui>|self: ArrangerTui|{
let pool_size = if self.phrases.visible { self.splits[1] } else { 0 };
let with_pool = |x|Split::left(false, pool_size, PoolView(&self.phrases), x);
let status = ArrangerStatus::from(self);
let with_editbar = |x|Tui::split_n(false, 3, MidiEditStatus(&self.editor), x);
let with_editbar = |x|Tui::split_n(false, 1, MidiEditStatus(&self.editor), x);
let with_status = |x|Tui::split_n(false, 2, status, x);
let with_size = |x|lay!([&self.size, x]);
let arranger = ||lay!(|add|{

View file

@ -24,27 +24,10 @@ pub struct SamplerTui {
pub size: Measure<Tui>,
/// Lowest note displayed
pub note_lo: AtomicUsize,
color: ItemColor
pub note_pt: AtomicUsize,
color: ItemPalette
}
render!(<Tui>|self: SamplerTui|{
let keys_width = 5;
let keys = move||SamplerKeys(self);
let fg = TuiTheme::g(200);
let bg = TuiTheme::g(50);
let border = Fill::wh(Outer(Style::default().fg(fg).bg(bg)));
let with_border = |x|lay!([
border,
Tui::inset_xy(1, 1, Fill::wh(&x))
]);
Tui::bg(bg, Fill::wh(with_border(Bsp::s(
"Sampler",
Bsp::e(
Fixed::w(keys_width, keys()),
Fill::wh(lay!([&self.size, Fill::wh("Sample")])),
),
))))
});
from_jack!(|jack|SamplerTui{
let midi_in = jack.read().unwrap().client().register_port("in", MidiIn::default())?;
let audio_outs = vec![
@ -57,7 +40,8 @@ from_jack!(|jack|SamplerTui{
mode: None,
size: Measure::new(),
note_lo: 36.into(),
color: ItemColor::default(),
note_pt: 36.into(),
color: ItemPalette::from(Color::Rgb(64, 128, 32)),
state: Sampler {
jack: jack.clone(),
name: "Sampler".into(),
@ -72,8 +56,52 @@ from_jack!(|jack|SamplerTui{
}
});
render!(<Tui>|self: SamplerTui|{
let keys_width = 5;
let keys = move||SamplerKeys(self);
let fg = self.color.base.rgb;
let bg = self.color.darkest.rgb;
let border = Fill::wh(Outer(Style::default().fg(fg).bg(bg)));
let inset = 0;
let with_border = |x|lay!([border, Tui::inset_xy(inset, inset, Fill::wh(&x))]);
let with_size = |x|lay!([self.size, x]);
Tui::bg(bg, Fill::wh(with_border(Bsp::s(
Tui::push_x(1, Tui::fg(self.color.light.rgb, Tui::bold(true, "Sampler"))),
with_size(Tui::shrink_y(1, Bsp::e(
Fixed::w(keys_width, keys()),
Fill::wh(render(|to: &mut TuiOutput|Ok({
let x = to.area.x() + 1;
let rows = self.size.h() as u16;
let bg_base = self.color.darkest.rgb;
let bg_selected = self.color.darker.rgb;
let style_empty = Style::default().fg(self.color.base.rgb);
let style_full = Style::default().fg(self.color.lighter.rgb);
let note_hi = self.note_hi();
let note_pt = self.note_point();
for y in 0..rows {
let note = note_hi - y as usize;
let bg = if note == note_pt { bg_selected } else { bg_base };
let style = Some(style_empty.bg(bg));
to.blit(&" (no sample) ", x, to.area.y() + y, style)
}
})))
))),
))))
});
impl NoteRange for SamplerTui {
fn note_lo (&self) -> &AtomicUsize { &self.note_lo }
fn note_axis (&self) -> &AtomicUsize { &self.size.y }
}
impl NotePoint for SamplerTui {
fn note_len (&self) -> usize {0/*TODO*/}
fn set_note_len (&self, x: usize) {}
fn note_point (&self) -> usize { self.note_pt.load(Relaxed) }
fn set_note_point (&self, x: usize) { self.note_pt.store(x, Relaxed); }
}
struct SamplerKeys<'a>(&'a SamplerTui);
has_color!(|self: SamplerKeys<'a>|self.0.color);
has_color!(|self: SamplerKeys<'a>|self.0.color.base);
render!(<Tui>|self: SamplerKeys<'a>|render(|to|Ok(render_keys_v(to, self))));
impl<'a> NoteRange for SamplerKeys<'a> {
fn note_lo (&self) -> &AtomicUsize { &self.0.note_lo }
@ -82,8 +110,8 @@ impl<'a> NoteRange for SamplerKeys<'a> {
impl<'a> NotePoint for SamplerKeys<'a> {
fn note_len (&self) -> usize {0/*TODO*/}
fn set_note_len (&self, x: usize) {}
fn note_point (&self) -> usize {0/*TODO*/}
fn set_note_point (&self, x: usize) {}
fn note_point (&self) -> usize { self.0.note_point() }
fn set_note_point (&self, x: usize) { self.0.set_note_point(x); }
}
pub enum SamplerMode {
@ -101,15 +129,19 @@ pub enum SamplerCommand {
NoteOn(u7, u7),
NoteOff(u7)
}
input_to_command!(SamplerCommand:<Tui>|state:SamplerTui,input|match state.mode {
input_to_command!(SamplerCommand: <Tui>|state: SamplerTui, input|match state.mode {
Some(SamplerMode::Import(..)) => Self::Import(
FileBrowserCommand::input_to_command(state, input)?
),
_ => match input.event() {
// load sample
key_pat!(Char('l')) => Self::Import(FileBrowserCommand::Begin),
key_pat!(KeyCode::Up) => { todo!() },
key_pat!(KeyCode::Down) => { todo!() },
key_pat!(KeyCode::Up) => {
Self::SelectNote(state.note_point().overflowing_add(1).0.min(127))
},
key_pat!(KeyCode::Down) => {
Self::SelectNote(state.note_point().overflowing_sub(1).0.min(127))
},
_ => return None
}
//key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() {
@ -135,12 +167,17 @@ input_to_command!(FileBrowserCommand:<Tui>|state:SamplerTui,input|match input {
_ => return None
});
command!(|self:SamplerCommand,state:SamplerTui|match self {
SamplerCommand::Import(FileBrowserCommand::Begin) => {
Self::Import(FileBrowserCommand::Begin) => {
let voices = &state.state.voices;
let sample = Arc::new(RwLock::new(Sample::new("", 0, 0, vec![])));
state.mode = Some(SamplerMode::Import(0, FileBrowser::new(None)?));
None
},
Self::SelectNote(index) => {
let old = state.note_point();
state.set_note_point(index);
Some(Self::SelectNote(old))
},
_ => todo!()
});
command!(|self:FileBrowserCommand,state:SamplerTui|match self {

View file

@ -44,7 +44,7 @@ render!(<Tui>|self: SequencerTui|{
let with_pool = move|x|Tui::split_w(false, pool_w, pool, x);
let status = SequencerStatus::from(self);
let with_status = |x|Tui::split_n(false, if self.status { 2 } else { 0 }, status, x);
let with_editbar = |x|Tui::split_n(false, 3, MidiEditStatus(&self.editor), x);
let with_editbar = |x|Tui::split_n(false, 1, MidiEditStatus(&self.editor), x);
let with_size = |x|lay!([self.size, x]);
let editor = with_editbar(with_pool(Fill::wh(&self.editor)));
let color = self.player.play_phrase().as_ref().map(|(_,p)|

View file

@ -52,20 +52,55 @@ impl PianoHorizontal {
}
render!(<Tui>|self: PianoHorizontal|{
let (color, name, length, looped) = if let Some(phrase) = self.phrase().as_ref().map(|p|p.read().unwrap()) {
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
} else {
(ItemPalette::from(TuiTheme::g(64)), String::new(), 0, false)
};
let field = move|x, y|row!([
Tui::fg_bg(color.lighter.rgb, color.darker.rgb, Tui::bold(true, x)),
Tui::fg_bg(color.light.rgb, color.darker.rgb, Tui::bold(true, "")),
Tui::fg_bg(color.lighter.rgb, color.dark.rgb, &y),
]);
let keys_width = 5;
let keys = move||PianoHorizontalKeys(self);
let timeline = move||PianoHorizontalTimeline(self);
let notes = move||PianoHorizontalNotes(self);
let cursor = move||PianoHorizontalCursor(self);
let border = Fill::wh(Outer(Style::default().fg(self.color.dark.rgb).bg(self.color.darkest.rgb)));
let with_border = |x|lay!([border, Tui::inset_xy(1, 1, &x)]);
with_border(Fill::wh(Bsp::s(
Fixed::h(1, Bsp::e(Fixed::w(keys_width, ""), Fill::w(timeline()),)),
Bsp::e(
Fixed::w(keys_width, keys()),
Fill::wh(lay!([&self.size, Fill::wh(lay!([Fill::wh(notes()), Fill::wh(cursor()),]))])),
),
)))
let with_border = |x|lay!([border, Tui::inset_xy(0, 0, &x)]);
with_border(lay!([
Tui::push_x(0, row!(![
" ",
field(" Edit", name.to_string()),
field(" Length", length.to_string()),
field(" Loop", looped.to_string())
])),
Tui::inset_xy(0, 1, Fill::wh(Bsp::s(
Fixed::h(1, Bsp::e(
Fixed::w(keys_width, ""),
Fill::w(timeline()),
)),
Bsp::e(
Fixed::w(keys_width, keys()),
Fill::wh(lay!([
&self.size,
Fill::wh(lay!([
Fill::wh(notes()),
Fill::wh(cursor()),
]))
])),
),
)))
]))
});
impl PianoHorizontal {

View file

@ -2,29 +2,30 @@ use crate::*;
pub struct MidiEditStatus<'a>(pub &'a MidiEditorModel);
render!(<Tui>|self:MidiEditStatus<'a>|{
let (color, name, length, looped) = if let Some(phrase) = self.0.phrase().as_ref().map(|p|p.read().unwrap()) {
(phrase.color, phrase.name.clone(), phrase.length, phrase.looped)
} else {
(ItemPalette::from(TuiTheme::g(64)), String::new(), 0, false)
};
let bg = color.darkest.rgb;
let fg = color.lightest.rgb;
let field = move|x, y|row!([
Tui::fg_bg(color.lighter.rgb, color.darker.rgb, Tui::bold(true, x)),
Tui::fg_bg(color.light.rgb, color.darker.rgb, Tui::bold(true, "")),
Fill::w(Tui::fg_bg(color.lightest.rgb, color.dark.rgb, &y)),
Tui::fg_bg(color.lightest.rgb, color.dark.rgb, &y),
]);
Fill::w(Tui::fg_bg(fg, bg, row!([
Fixed::wh(26, 3, col!(![
field(" Edit", name.to_string()),
field(" Length", length.to_string()),
field(" Loop", looped.to_string())])),
Fixed::wh(30, 3, col!(![
field(" Time", format!("{}/{}-{} ({}*{}) {}",
self.0.time_point(), self.0.time_start().get(), self.0.time_end(),
self.0.time_axis().get(), self.0.time_zoom().get(),
if self.0.time_lock().get() { "[lock]" } else { " " })),
field(" Note", format!("{} ({}) {} | {}-{} ({})",
self.0.note_point(), to_note_name(self.0.note_point()), self.0.note_len(),
to_note_name(self.0.note_lo().get()), to_note_name(self.0.note_hi()),
self.0.note_axis().get()))]))])))});
let bg = color.darkest.rgb;
let fg = color.lightest.rgb;
Tui::bg(bg, Fill::w(Tui::fg(fg, row!([
field(" Time", format!("{}/{}-{} ({}*{}) {}",
self.0.time_point(), self.0.time_start().get(), self.0.time_end(),
self.0.time_axis().get(), self.0.time_zoom().get(),
if self.0.time_lock().get() { "[lock]" } else { " " })),
" ",
field(" Note", format!("{} ({}) {} | {}-{} ({})",
self.0.note_point(), to_note_name(self.0.note_point()), self.0.note_len(),
to_note_name(self.0.note_lo().get()), to_note_name(self.0.note_hi()),
self.0.note_axis().get()))
]))))
});