mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-08 20:56:43 +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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test] fn verify_groovebox_cli () {
|
||||||
|
use clap::CommandFactory;
|
||||||
|
GrooveboxCli::command().debug_assert();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,3 +42,8 @@ impl SequencerCli {
|
||||||
Ok(())
|
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 clap::{self, Parser};
|
||||||
#[allow(unused_imports)] use tek::{*, jack::*};
|
#[allow(unused_imports)] use tek::{*, jack::*};
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> Usually<()> {
|
fn connect_from (jack: &JackClient, input: &Port<MidiIn>, ports: &[String]) -> Usually<()> {
|
||||||
for port in ports.iter() {
|
for port in ports.iter() {
|
||||||
if let Some(port) = jack.port_by_name(port).as_ref() {
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
fn connect_to (jack: &JackClient, output: &Port<MidiOut>, ports: &[String]) -> Usually<()> {
|
fn connect_to (jack: &JackClient, output: &Port<MidiOut>, ports: &[String]) -> Usually<()> {
|
||||||
for port in ports.iter() {
|
for port in ports.iter() {
|
||||||
if let Some(port) = jack.port_by_name(port).as_ref() {
|
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(())
|
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;
|
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.
|
/// A color in OKHSL and RGB representations.
|
||||||
#[derive(Debug, Default, Copy, Clone, PartialEq)]
|
#[derive(Debug, Default, Copy, Clone, PartialEq)]
|
||||||
pub struct ItemColor {
|
pub struct ItemColor {
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,17 @@ use crate::*;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
(<$E:ty>|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => {
|
(<$E:ty>|$self:ident:$Struct:ident$(<
|
||||||
impl $(<$($L),*$($T $(: $U)?),*>)? Render<$E> for $Struct $(<$($L),*$($T),*>)? {
|
$($($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> {
|
fn min_size (&$self, to: <$E as Engine>::Size) -> Perhaps<<$E as Engine>::Size> {
|
||||||
$cb.min_size(to)
|
$cb.min_size(to)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use KeyCode::{Char, Delete, Tab, Up, Down, Left, Right};
|
||||||
|
|
||||||
pub struct GrooveboxTui {
|
pub struct GrooveboxTui {
|
||||||
pub sequencer: SequencerTui,
|
pub sequencer: SequencerTui,
|
||||||
pub sampler: SamplerTui,
|
pub sampler: SamplerTui,
|
||||||
pub split: u16,
|
pub split: u16,
|
||||||
pub focus: GrooveboxFocus
|
pub focus: GrooveboxFocus
|
||||||
}
|
}
|
||||||
|
|
||||||
from_jack!(|jack|GrooveboxTui {
|
from_jack!(|jack|GrooveboxTui {
|
||||||
let mut sequencer = SequencerTui::try_from(jack)?;
|
let mut sequencer = SequencerTui::try_from(jack)?;
|
||||||
sequencer.status = false;
|
sequencer.status = false;
|
||||||
|
|
@ -20,29 +23,42 @@ from_jack!(|jack|GrooveboxTui {
|
||||||
sequencer,
|
sequencer,
|
||||||
sampler: SamplerTui::try_from(jack)?,
|
sampler: SamplerTui::try_from(jack)?,
|
||||||
split: 16,
|
split: 16,
|
||||||
focus: GrooveboxFocus::Sampler,
|
focus: GrooveboxFocus::Sequencer,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
pub enum GrooveboxFocus {
|
pub enum GrooveboxFocus {
|
||||||
Sequencer,
|
Sequencer,
|
||||||
Sampler
|
Sampler
|
||||||
}
|
}
|
||||||
|
|
||||||
audio!(|self:GrooveboxTui,_client,_process|Control::Continue);
|
audio!(|self:GrooveboxTui,_client,_process|Control::Continue);
|
||||||
|
|
||||||
render!(<Tui>|self:GrooveboxTui|Bsp::n(
|
render!(<Tui>|self:GrooveboxTui|Bsp::n(
|
||||||
Fixed::h(2, SequencerStatus::from(&self.sequencer)),
|
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 {
|
pub enum GrooveboxCommand {
|
||||||
Sequencer(SequencerCommand),
|
Sequencer(SequencerCommand),
|
||||||
Sampler(SamplerCommand),
|
Sampler(SamplerCommand),
|
||||||
}
|
}
|
||||||
handle!(<Tui>|self:GrooveboxTui,input|GrooveboxCommand::execute_with_state(self, input));
|
|
||||||
input_to_command!(GrooveboxCommand: <Tui>|state:GrooveboxTui,input|match state.focus {
|
handle!(<Tui>|self: GrooveboxTui, input|GrooveboxCommand::execute_with_state(self, input));
|
||||||
GrooveboxFocus::Sequencer => GrooveboxCommand::Sequencer(
|
|
||||||
SequencerCommand::input_to_command(&state.sequencer, input)?),
|
input_to_command!(GrooveboxCommand: <Tui>|state: GrooveboxTui,input|match input.event() {
|
||||||
GrooveboxFocus::Sampler => GrooveboxCommand::Sampler(
|
key_pat!(Up) |
|
||||||
SamplerCommand::input_to_command(&state.sampler, input)?),
|
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 {
|
command!(|self:GrooveboxCommand,state:GrooveboxTui|match self {
|
||||||
GrooveboxCommand::Sequencer(command) =>
|
GrooveboxCommand::Sequencer(command) =>
|
||||||
command.execute(&mut state.sequencer)?.map(GrooveboxCommand::Sequencer),
|
command.execute(&mut state.sequencer)?.map(GrooveboxCommand::Sequencer),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use super::*;
|
use super::{*, piano_h::PianoHorizontalKeys};
|
||||||
|
|
||||||
use KeyCode::Char;
|
use KeyCode::Char;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use symphonia::{
|
use symphonia::{
|
||||||
|
|
@ -13,12 +14,36 @@ use symphonia::{
|
||||||
},
|
},
|
||||||
default::get_codecs,
|
default::get_codecs,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct SamplerTui {
|
pub struct SamplerTui {
|
||||||
pub state: Sampler,
|
pub state: Sampler,
|
||||||
pub cursor: (usize, usize),
|
pub cursor: (usize, usize),
|
||||||
pub editing: Option<Arc<RwLock<Sample>>>,
|
pub editing: Option<Arc<RwLock<Sample>>>,
|
||||||
pub mode: Option<SamplerMode>,
|
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 {
|
pub enum SamplerMode {
|
||||||
// Load sample from path
|
// Load sample from path
|
||||||
Import(usize, FileBrowser),
|
Import(usize, FileBrowser),
|
||||||
|
|
@ -33,6 +58,7 @@ from_jack!(|jack|SamplerTui{
|
||||||
cursor: (0, 0),
|
cursor: (0, 0),
|
||||||
editing: None,
|
editing: None,
|
||||||
mode: None,
|
mode: None,
|
||||||
|
size: Measure::new(),
|
||||||
state: Sampler {
|
state: Sampler {
|
||||||
jack: jack.clone(),
|
jack: jack.clone(),
|
||||||
name: "Sampler".into(),
|
name: "Sampler".into(),
|
||||||
|
|
@ -64,16 +90,10 @@ input_to_command!(SamplerCommand:<Tui>|state:SamplerTui,input|match state.mode {
|
||||||
_ => match input.event() {
|
_ => match input.event() {
|
||||||
// load sample
|
// load sample
|
||||||
key_pat!(Char('l')) => Self::Import(FileBrowserCommand::Begin),
|
key_pat!(Char('l')) => Self::Import(FileBrowserCommand::Begin),
|
||||||
|
key_pat!(KeyCode::Up) => { todo!() },
|
||||||
|
key_pat!(KeyCode::Down) => { todo!() },
|
||||||
_ => return None
|
_ => 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() {
|
//key_pat!(KeyCode::Char('p')) => if let Some(sample) = self.sample() {
|
||||||
//voices.write().unwrap().push(Sample::play(sample, 0, &100.into()));
|
//voices.write().unwrap().push(Sample::play(sample, 0, &100.into()));
|
||||||
//},
|
//},
|
||||||
|
|
@ -133,37 +153,6 @@ audio!(|self: SamplerTui, _client, scope|{
|
||||||
Control::Continue
|
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 {
|
pub struct AddSampleModal {
|
||||||
exited: bool,
|
exited: bool,
|
||||||
dir: PathBuf,
|
dir: PathBuf,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,15 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
mod piano_h_cursor; use self::piano_h_cursor::*;
|
mod piano_h_cursor;
|
||||||
mod piano_h_keys; use self::piano_h_keys::*;
|
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_notes; use self::piano_h_notes::*;
|
||||||
|
|
||||||
mod piano_h_time; use self::piano_h_time::*;
|
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)> {
|
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|{
|
render!(<Tui>|self: PianoHorizontal|{
|
||||||
|
let keys_width = 5;
|
||||||
let keys = move||PianoHorizontalKeys(self);
|
let keys = move||PianoHorizontalKeys(self);
|
||||||
let timeline = move||PianoHorizontalTimeline(self);
|
let timeline = move||PianoHorizontalTimeline(self);
|
||||||
let notes = move||PianoHorizontalNotes(self);
|
let notes = move||PianoHorizontalNotes(self);
|
||||||
let cursor = move||PianoHorizontalCursor(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 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)]);
|
let with_border = |x|lay!([border, Tui::inset_xy(1, 1, &x)]);
|
||||||
with_border(Fill::wh(Bsp::s(
|
with_border(Fill::wh(Bsp::s(
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@ use crate::*;
|
||||||
use super::note_y_iter;
|
use super::note_y_iter;
|
||||||
|
|
||||||
pub struct PianoHorizontalKeys<'a>(pub(crate) &'a PianoHorizontal);
|
pub struct PianoHorizontalKeys<'a>(pub(crate) &'a PianoHorizontal);
|
||||||
render!(<Tui>|self: PianoHorizontalKeys<'a>|render(|to|Ok({
|
render!(<Tui>|self: PianoHorizontalKeys<'a>|render(|to|Ok(render_keys_v(to, self))));
|
||||||
let color = self.0.color;
|
has_color!(|self: PianoHorizontalKeys<'a>|self.0.color);
|
||||||
let note_lo = self.0.note_lo().get();
|
|
||||||
let note_hi = self.0.note_hi();
|
pub fn render_keys_v <E: Engine, T: HasColor + MidiRange> (to: &mut E::Output, state: &T) {
|
||||||
let note_point = self.0.note_point();
|
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 [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 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)));
|
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)
|
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 {
|
fn to_key (note: usize) -> &'static str {
|
||||||
match note % 12 {
|
match note % 12 {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue