mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: samples table
This commit is contained in:
parent
71f4194cdf
commit
a9fb6fc17c
9 changed files with 103 additions and 74 deletions
|
|
@ -41,3 +41,8 @@ impl GrooveboxCli {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test] fn verify_groovebox_cli () {
|
||||
use clap::CommandFactory;
|
||||
GrooveboxCli::command().debug_assert();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,3 +42,8 @@ impl SequencerCli {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test] fn verify_sequencer_cli () {
|
||||
use clap::CommandFactory;
|
||||
SequencerCli::command().debug_assert();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue