add --bpm flag

This commit is contained in:
🪞👃🪞 2025-01-02 16:38:04 +01:00
parent 42e2ef2a50
commit 94491a323a
11 changed files with 78 additions and 57 deletions

View file

@ -38,15 +38,6 @@ render!(Tui: (self: TransportView<'a>) => Outer(
OutputStats::new(self.compact, self.clock),
)));
struct Field<'a>(ItemPalette, &'a str, &'a str);
render!(Tui: (self: Field<'a>) => row!(
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.darker.rgb, "")),
Tui::bg(self.0.darker.rgb, Tui::fg(self.0.lighter.rgb,
Tui::bold(true, format!("{}", self.1)))),
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.darker.rgb, "")),
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.lightest.rgb,
Tui::bold(true, format!("{} ", self.2))))));
pub struct PlayPause { pub compact: bool, pub playing: bool }
render!(Tui: (self: PlayPause) => Tui::bg(
if self.playing{Color::Rgb(0,128,0)}else{Color::Rgb(128,64,0)},

22
src/field.rs Normal file
View file

@ -0,0 +1,22 @@
use crate::*;
pub struct Field<T, U>(pub ItemPalette, pub T, pub U)
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync;
impl<T, U> Content<Tui> for Field<T, U>
where T: AsRef<str> + Send + Sync, U: AsRef<str> + Send + Sync
{
fn content (&self) -> impl Content<Tui> {
row!(
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.darker.rgb, "")),
Tui::bg(self.0.darker.rgb, Tui::fg(self.0.lighter.rgb,
Tui::bold(true, format!("{}", self.1.as_ref())))),
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.darker.rgb, "")),
Tui::bg(self.0.darkest.rgb, Tui::fg(self.0.lightest.rgb,
format!("{} ", self.2.as_ref())))
)
}
}

View file

@ -131,20 +131,16 @@ render!(Tui: (self: Groovebox) => {
})),
),
Bsp::n(
Bsp::e(
Fixed::y(1, SamplerStatus(&self.sampler, note_pt)),
MidiEditStatus(&self.editor),
lay!(
Align::w(Fixed::y(1, SamplerStatus(&self.sampler, note_pt))),
Align::x(Fixed::y(1, MidiEditStatus(&self.editor))),
),
Bsp::w(
Fixed::x(pool_w, Align::e(Fill::y(PoolView(&self.pool)))),
Fill::xy(Bsp::e(
Fixed::x(sampler_w, sampler),
Bsp::s(
selector,
&self.editor,
),
),
)
Fixed::x(sampler_w, Push::y(3, sampler)),
Bsp::s(selector, &self.editor),
)),
),
)
)

View file

@ -49,6 +49,7 @@ pub mod border; pub use self::border::*;
pub mod clock; pub use self::clock::*;
pub mod color; pub use self::color::*;
pub mod command; pub use self::command::*;
pub mod field; pub use self::field::*;
pub mod file; pub use self::file::*;
pub mod focus; pub use self::focus::*;
pub mod groovebox; pub use self::groovebox::*;

View file

@ -13,6 +13,7 @@ pub(crate) mod midi_point; pub(crate) use midi_point::*;
pub(crate) mod midi_view; pub(crate) use midi_view::*;
pub(crate) mod midi_editor; pub(crate) use midi_editor::*;
pub(crate) mod midi_status; pub(crate) use midi_status::*;
/// Add "all notes off" to the start of a buffer.
pub fn all_notes_off (output: &mut [Vec<Vec<u8>>]) {

25
src/midi/midi_status.rs Normal file
View file

@ -0,0 +1,25 @@
use crate::*;
pub struct MidiEditStatus<'a>(pub &'a MidiEditor);
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 time_point = self.0.time_point();
let time_start = self.0.time_start();
let time_end = self.0.time_end();
let time_axis = self.0.time_axis().get();
let time_zoom = self.0.time_zoom().get();
let time_lock = if self.0.time_lock().get() { "[lock]" } else { " " };
let time_field = Field(color, "Time", format!("{length}/{time_zoom}+{time_point} {time_lock}"));
Tui::bg(color.darkest.rgb, Fill::x(Tui::fg(color.lightest.rgb, Bsp::e(
time_field,
Field(color, "Note", format!("{} ({}) {} | {}-{} ({})",
self.0.note_point(), Note::pitch_to_name(self.0.note_point()), self.0.note_len(),
Note::pitch_to_name(self.0.note_lo().get()), Note::pitch_to_name(self.0.note_hi()),
self.0.note_axis().get()))
))))
});

View file

@ -7,14 +7,8 @@ pub struct PhraseSelector {
pub(crate) time: String,
}
// TODO: Display phrases always in order of appearance
render!(Tui: (self: PhraseSelector) => Fixed::xy(24, 1, row!(
Tui::fg(self.color.lightest.rgb, Tui::bold(true, &self.title)),
Tui::fg_bg(self.color.lighter.rgb, self.color.base.rgb, row!(
format!("{:8}", &self.name[0..8.min(self.name.len())]),
Tui::bg(self.color.dark.rgb, &self.time),
)),
)));
render!(Tui: (self: PhraseSelector) =>
Field(self.color, self.title, format!("{} {}", self.time, self.name)));
impl PhraseSelector {
@ -31,7 +25,7 @@ impl PhraseSelector {
} else {
String::from(" ")
};
Self { title: "Now:|", time, name, color, }
Self { title: "Now", time, name, color, }
}
// beats until switchover
@ -59,7 +53,7 @@ impl PhraseSelector {
} else {
(" ".into(), " ".into(), TuiTheme::g(64).into())
};
Self { title: " Next|", time, name, color, }
Self { title: "Next", time, name, color, }
}
}

View file

@ -181,6 +181,9 @@ impl Sampler {
voices.write().unwrap().push(Sample::play(sample, time as usize, vel));
}
},
MidiMessage::Controller { controller, value } => {
// TODO
}
_ => {}
}
}

View file

@ -1,32 +1,5 @@
use crate::*;
pub struct MidiEditStatus<'a>(pub &'a MidiEditor);
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 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.lightest.rgb, color.dark.rgb, y),
);
let bg = color.darkest.rgb;
let fg = color.lightest.rgb;
Tui::bg(bg, Fill::x(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(), Note::pitch_to_name(self.0.note_point()), self.0.note_len(),
Note::pitch_to_name(self.0.note_lo().get()), Note::pitch_to_name(self.0.note_hi()),
self.0.note_axis().get()))
))))
});
/// Status bar for sequencer app
#[derive(Clone)]
pub struct SequencerStatus {