wip: samples table

This commit is contained in:
🪞👃🪞 2024-12-27 16:54:32 +01:00
parent 71f4194cdf
commit a9fb6fc17c
9 changed files with 103 additions and 74 deletions

View file

@ -41,3 +41,8 @@ impl GrooveboxCli {
Ok(())
}
}
#[test] fn verify_groovebox_cli () {
use clap::CommandFactory;
GrooveboxCli::command().debug_assert();
}

View file

@ -42,3 +42,8 @@ impl SequencerCli {
Ok(())
}
}
#[test] fn verify_sequencer_cli () {
use clap::CommandFactory;
SequencerCli::command().debug_assert();
}

View file

@ -2,6 +2,7 @@
#[allow(unused_imports)] use clap::{self, Parser};
#[allow(unused_imports)] use tek::{*, jack::*};
#[allow(unused)]
fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> Usually<()> {
for port in ports.iter() {
if let Some(port) = jack.port_by_name(port).as_ref() {
@ -13,6 +14,7 @@ fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> U
Ok(())
}
#[allow(unused)]
fn connect_to (jack: &JackClient, output: &Port<MidiOut>, ports: &[String]) -> Usually<()> {
for port in ports.iter() {
if let Some(port) = jack.port_by_name(port).as_ref() {
@ -23,8 +25,3 @@ fn connect_to (jack: &JackClient, output: &Port<MidiOut>, ports: &[String]) -> U
}
Ok(())
}
#[test] fn verify_sequencer_cli () {
use clap::CommandFactory;
SequencerCli::command().debug_assert();
}

View file

@ -7,6 +7,15 @@ pub trait HasColor {
fn color_mut (&mut self) -> &mut ItemColor;
}
#[macro_export] macro_rules! has_color {
(|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? HasColor for $Struct $(<$($L),*$($T),*>)? {
fn color (&$self) -> ItemColor { &$cb }
fn color_mut (&mut $self) -> &mut ItemColor { &mut $cb }
}
}
}
/// A color in OKHSL and RGB representations.
#[derive(Debug, Default, Copy, Clone, PartialEq)]
pub struct ItemColor {

View file

@ -11,8 +11,17 @@ use crate::*;
}
}
};
(<$E:ty>|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
impl $(<$($L),*$($T $(: $U)?),*>)? Render<$E> for $Struct $(<$($L),*$($T),*>)? {
(<$E:ty>|$self:ident:$Struct:ident$(<
$($($L:lifetime),+)?
$($($T:ident$(:$U:path)?),+)?
>)?|$cb:expr) => {
impl $(<
$($($L),+)?
$($($T$(:$U)?),+)?
>)? Render<$E> for $Struct $(<
$($($L),+)?
$($($T),+)?
>)? {
fn min_size (&$self, to: <$E as Engine>::Size) -> Perhaps<<$E as Engine>::Size> {
$cb.min_size(to)
}

View file

@ -1,11 +1,14 @@
use crate::*;
use super::*;
use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right};
pub struct GrooveboxTui {
pub sequencer: SequencerTui,
pub sampler: SamplerTui,
pub split: u16,
pub focus: GrooveboxFocus
}
from_jack!(|jack|GrooveboxTui {
let mut sequencer = SequencerTui::try_from(jack)?;
sequencer.status = false;
@ -20,29 +23,42 @@ from_jack!(|jack|GrooveboxTui {
sequencer,
sampler: SamplerTui::try_from(jack)?,
split: 16,
focus: GrooveboxFocus::Sampler,
focus: GrooveboxFocus::Sequencer,
}
});
pub enum GrooveboxFocus {
Sequencer,
Sampler
}
audio!(|self:GrooveboxTui,_client,_process|Control::Continue);
render!(<Tui>|self:GrooveboxTui|Bsp::n(
Fixed::h(2, SequencerStatus::from(&self.sequencer)),
Fill::h(Bsp::s(&self.sequencer, &self.sampler)),
Fill::h(Bsp::s(
Tui::min_y(30, &self.sequencer),
Fill::h(&self.sampler),
)),
));
pub enum GrooveboxCommand {
Sequencer(SequencerCommand),
Sampler(SamplerCommand),
}
handle!(<Tui>|self:GrooveboxTui,input|GrooveboxCommand::execute_with_state(self, input));
input_to_command!(GrooveboxCommand: <Tui>|state:GrooveboxTui,input|match state.focus {
GrooveboxFocus::Sequencer => GrooveboxCommand::Sequencer(
SequencerCommand::input_to_command(&state.sequencer, input)?),
GrooveboxFocus::Sampler => GrooveboxCommand::Sampler(
SamplerCommand::input_to_command(&state.sampler, input)?),
handle!(<Tui>|self: GrooveboxTui, input|GrooveboxCommand::execute_with_state(self, input));
input_to_command!(GrooveboxCommand: <Tui>|state: GrooveboxTui,input|match input.event() {
key_pat!(Up) |
key_pat!(Down) |
key_pat!(Left) |
key_pat!(Right) =>
GrooveboxCommand::Sampler(SamplerCommand::input_to_command(&state.sampler, input)?),
_ =>
GrooveboxCommand::Sequencer(SequencerCommand::input_to_command(&state.sequencer, input)?),
});
command!(|self:GrooveboxCommand,state:GrooveboxTui|match self {
GrooveboxCommand::Sequencer(command) =>
command.execute(&mut state.sequencer)?.map(GrooveboxCommand::Sequencer),

View file

@ -1,5 +1,6 @@
use crate::*;
use super::*;
use super::{*, piano_h::PianoHorizontalKeys};
use KeyCode::Char;
use std::fs::File;
use symphonia::{
@ -13,12 +14,36 @@ use symphonia::{
},
default::get_codecs,
};
pub struct SamplerTui {
pub state: Sampler,
pub cursor: (usize, usize),
pub editing: Option<Arc<RwLock<Sample>>>,
pub mode: Option<SamplerMode>,
/// Size of actual notes area
pub size: Measure<Tui>,
}
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, &x)]);
with_border(Fill::wh(Bsp::s(
"kyp",
Bsp::e(
Fixed::w(keys_width, keys()),
Fill::wh(lay!([&self.size, Fill::wh("nymka")])),
),
)))
});
struct SamplerKeys<'a>(&'a SamplerTui);
render!(<Tui>|self: SamplerKeys<'a>|render(|to|Ok(render_keys_v(to, self))));
has_color!(|self: SamplerKeys<'a>|ItemColor::default());
pub enum SamplerMode {
// Load sample from path
Import(usize, FileBrowser),
@ -33,6 +58,7 @@ from_jack!(|jack|SamplerTui{
cursor: (0, 0),
editing: None,
mode: None,
size: Measure::new(),
state: Sampler {
jack: jack.clone(),
name: "Sampler".into(),
@ -64,16 +90,10 @@ input_to_command!(SamplerCommand:<Tui>|state:SamplerTui,input|match state.mode {
_ => match input.event() {
// load sample
key_pat!(Char('l')) => Self::Import(FileBrowserCommand::Begin),
key_pat!(KeyCode::Up) => { todo!() },
key_pat!(KeyCode::Down) => { todo!() },
_ => return None
}
//key_pat!(KeyCode::Up) => state.cursor.0 = if state.cursor.0 == 0 {
//mapped.len() + unmapped.len() - 1
//} else {
//state.cursor.0 - 1
//},
//key_pat!(KeyCode::Down) => {
//state.cursor.0 = (state.cursor.0 + 1) % (mapped.len() + unmapped.len());
//},
//key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() {
//voices.write().unwrap().push(Sample::play(sample, 0, &100.into()));
//},
@ -133,37 +153,6 @@ audio!(|self: SamplerTui, _client, scope|{
Control::Continue
});
render!(<Tui>|self: SamplerTui|Tui::min_y(10, Fill::wh(lay!([
Fill::wh(render(|to|{ // border
let [x, y, w, h] = to.area();
let green = Some(Style::default().fg(Color::Green));
to.blit(&"🭚", x, y, green);
to.blit(&"🭥", x + w.saturating_sub(1), y, green);
to.blit(&"🬿", x, y + h.saturating_sub(1), green);
to.blit(&"🭊", x + w.saturating_sub(1), y + h.saturating_sub(1), green);
Ok(())
})),
col!(|add|{
add(&Tui::push_x(2, row!([
Tui::bold(true, "Sampler"), "|Voices: ",
&format!("{}", self.state.voices.read().unwrap().len()),
])))?;
if let Some(SamplerMode::Import(_, browser)) = self.mode.as_ref() {
add(&browser)
} else {
add(&row!([
" ",
col!(note in 35..=42 => {
&format!("{note:>3} ---------------- +0.0dB")
}),
]))
}
}),
]))));
pub struct AddSampleModal {
exited: bool,
dir: PathBuf,

View file

@ -1,9 +1,15 @@
use crate::*;
use super::*;
mod piano_h_cursor; use self::piano_h_cursor::*;
mod piano_h_keys; use self::piano_h_keys::*;
mod piano_h_cursor;
use self::piano_h_cursor::*;
mod piano_h_keys;
pub(crate) use self::piano_h_keys::*;
pub use self::piano_h_keys::render_keys_v;
mod piano_h_notes; use self::piano_h_notes::*;
mod piano_h_time; use self::piano_h_time::*;
pub(crate) fn note_y_iter (note_lo: usize, note_hi: usize, y0: u16) -> impl Iterator<Item=(usize, u16, usize)> {
@ -46,11 +52,11 @@ impl PianoHorizontal {
}
render!(<Tui>|self: PianoHorizontal|{
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 keys_width = 5;
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(

View file

@ -2,11 +2,14 @@ use crate::*;
use super::note_y_iter;
pub struct PianoHorizontalKeys<'a>(pub(crate) &'a PianoHorizontal);
render!(<Tui>|self: PianoHorizontalKeys<'a>|render(|to|Ok({
let color = self.0.color;
let note_lo = self.0.note_lo().get();
let note_hi = self.0.note_hi();
let note_point = self.0.note_point();
render!(<Tui>|self: PianoHorizontalKeys<'a>|render(|to|Ok(render_keys_v(to, self))));
has_color!(|self: PianoHorizontalKeys<'a>|self.0.color);
pub fn render_keys_v <E: Engine, T: HasColor + MidiRange> (to: &mut E::Output, state: &T) {
let color = state.color();
let note_lo = state.note_lo().get();
let note_hi = state.note_hi();
let note_point = state.note_point();
let [x, y0, w, h] = to.area().xywh();
let key_style = Some(Style::default().fg(Color::Rgb(192, 192, 192)).bg(Color::Rgb(0, 0, 0)));
let off_style = Some(Style::default().fg(TuiTheme::g(160)));
@ -22,17 +25,7 @@ render!(<Tui>|self: PianoHorizontalKeys<'a>|render(|to|Ok({
to.blit(&to_note_name(note), x, screen_y, off_style)
};
}
//let debug = false;
//if debug {
//to.blit(&format!("XYU"), x, y0, None);
//to.blit(&format!("x={x}"), x, y0+1, None);
//to.blit(&format!("y0={y0}"), x, y0+2, None);
//to.blit(&format!("w={w}"), x, y0+3, None);
//to.blit(&format!("h={h}"), x, y0+4, None);
//to.blit(&format!("note_lo={note_lo}"), x, y0+5, None);
//to.blit(&format!("note_hi={note_hi}"), x, y0+6, None);
//}
})));
}
fn to_key (note: usize) -> &'static str {
match note % 12 {