mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 04:06:45 +01:00
control transport values
This commit is contained in:
parent
33e5f47526
commit
45021bc77a
10 changed files with 168 additions and 86 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{core::*, handle, App, AppSection};
|
use crate::{core::*, handle, App, AppSection};
|
||||||
|
|
||||||
pubmod!{ arranger chain mixer plugin sampler sequencer }
|
pubmod!{ arranger chain focus mixer plugin sampler sequencer transport }
|
||||||
|
|
||||||
handle!{
|
handle!{
|
||||||
App |self, e| {
|
App |self, e| {
|
||||||
|
|
@ -15,10 +15,10 @@ handle!{
|
||||||
Ok(if self.entered {
|
Ok(if self.entered {
|
||||||
handle_focused(self, e)?
|
handle_focused(self, e)?
|
||||||
|| handle_keymap(self, e, KEYMAP)?
|
|| handle_keymap(self, e, KEYMAP)?
|
||||||
|| handle_keymap(self, e, KEYMAP_FOCUS)?
|
|| handle_keymap(self, e, crate::control::focus::KEYMAP_FOCUS)?
|
||||||
} else {
|
} else {
|
||||||
handle_keymap(self, e, KEYMAP)?
|
handle_keymap(self, e, KEYMAP)?
|
||||||
|| handle_keymap(self, e, KEYMAP_FOCUS)?
|
|| handle_keymap(self, e, crate::control::focus::KEYMAP_FOCUS)?
|
||||||
|| handle_focused(self, e)?
|
|| handle_focused(self, e)?
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -27,13 +27,14 @@ handle!{
|
||||||
fn handle_focused (state: &mut App, e: &AppEvent) -> Usually<bool> {
|
fn handle_focused (state: &mut App, e: &AppEvent) -> Usually<bool> {
|
||||||
match state.section {
|
match state.section {
|
||||||
AppSection::Transport =>
|
AppSection::Transport =>
|
||||||
Ok(false),
|
handle_keymap(state, e, crate::control::transport::KEYMAP_TRANSPORT),
|
||||||
AppSection::Arranger =>
|
AppSection::Arranger =>
|
||||||
handle_keymap(state, e, crate::control::arranger::KEYMAP_ARRANGER),
|
handle_keymap(state, e, crate::control::arranger::KEYMAP_ARRANGER),
|
||||||
AppSection::Sequencer =>
|
AppSection::Sequencer =>
|
||||||
handle_keymap(state, e, crate::control::sequencer::KEYMAP_SEQUENCER),
|
handle_keymap(state, e, crate::control::sequencer::KEYMAP_SEQUENCER),
|
||||||
AppSection::Chain => Ok(if state.entered {
|
AppSection::Chain => Ok(if state.entered {
|
||||||
handle_device(state, e)? || handle_keymap(state, e, crate::control::chain::KEYMAP_CHAIN)?
|
handle_device(state, e)? ||
|
||||||
|
handle_keymap(state, e, crate::control::chain::KEYMAP_CHAIN)?
|
||||||
} else {
|
} else {
|
||||||
handle_keymap(state, e, crate::control::chain::KEYMAP_CHAIN)? || handle_device(state, e)?
|
handle_keymap(state, e, crate::control::chain::KEYMAP_CHAIN)? || handle_device(state, e)?
|
||||||
})
|
})
|
||||||
|
|
@ -48,32 +49,7 @@ fn handle_device (state: &mut App, e: &AppEvent) -> Usually<bool> {
|
||||||
.map(|x|x.unwrap_or(false))
|
.map(|x|x.unwrap_or(false))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const KEYMAP_FOCUS: &'static [KeyBinding<App>] = keymap!(App {
|
|
||||||
[Char(';'), NONE, "command", "open command palette", |app: &mut App| {
|
|
||||||
app.modal = Some(Box::new(crate::view::HelpModal::new()));
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Tab, NONE, "focus_next", "focus next area", focus_next],
|
|
||||||
[Tab, SHIFT, "focus_prev", "focus previous area", focus_prev],
|
|
||||||
[Esc, NONE, "focus_exit", "unfocus", |app: &mut App|{
|
|
||||||
app.entered = false;
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
[Enter, NONE, "focus_enter", "activate item at cursor", |app: &mut App|{
|
|
||||||
app.entered = true;
|
|
||||||
Ok(true)
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
|
|
||||||
pub const KEYMAP: &'static [KeyBinding<App>] = keymap!(App {
|
pub const KEYMAP: &'static [KeyBinding<App>] = keymap!(App {
|
||||||
[Up, NONE, "focus_prev", "focus previous area", |app: &mut App|match app.track_cursor {
|
|
||||||
0 => {app.section = AppSection::Arranger;Ok(true)},
|
|
||||||
_ => focus_prev(app)
|
|
||||||
}],
|
|
||||||
[Down, NONE, "focus_next", "focus next area", |app: &mut App|match app.track_cursor {
|
|
||||||
0 => {app.section = AppSection::Chain;Ok(true)},
|
|
||||||
_ => focus_next(app)
|
|
||||||
}],
|
|
||||||
[Char(' '), NONE, "play_toggle", "play or pause", |app: &mut App| {
|
[Char(' '), NONE, "play_toggle", "play or pause", |app: &mut App| {
|
||||||
app.transport.toggle_play()?;
|
app.transport.toggle_play()?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
|
|
@ -144,15 +120,3 @@ pub const KEYMAP: &'static [KeyBinding<App>] = keymap!(App {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
|
|
||||||
fn focus_next (app: &mut App) -> Usually<bool> {
|
|
||||||
app.section.next();
|
|
||||||
app.transport.focused = app.section == AppSection::Transport;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn focus_prev (app: &mut App) -> Usually<bool> {
|
|
||||||
app.section.prev();
|
|
||||||
app.transport.focused = app.section == AppSection::Transport;
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
32
src/control/focus.rs
Normal file
32
src/control/focus.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
use crate::{core::*, model::{App, AppSection}};
|
||||||
|
|
||||||
|
pub const KEYMAP_FOCUS: &'static [KeyBinding<App>] = keymap!(App {
|
||||||
|
[Char(';'), NONE, "command", "open command palette", |app: &mut App| {
|
||||||
|
app.modal = Some(Box::new(crate::view::HelpModal::new()));
|
||||||
|
Ok(true)
|
||||||
|
}],
|
||||||
|
[Tab, NONE, "focus_next", "focus next area", focus_next],
|
||||||
|
[Tab, SHIFT, "focus_prev", "focus previous area", focus_prev],
|
||||||
|
[Esc, NONE, "focus_exit", "unfocus", |app: &mut App|{
|
||||||
|
app.entered = false;
|
||||||
|
Ok(true)
|
||||||
|
}],
|
||||||
|
[Enter, NONE, "focus_enter", "activate item at cursor", |app: &mut App|{
|
||||||
|
app.entered = true;
|
||||||
|
Ok(true)
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn focus_next (app: &mut App) -> Usually<bool> {
|
||||||
|
app.section.next();
|
||||||
|
app.transport.focused = app.section == AppSection::Transport;
|
||||||
|
app.transport.entered = app.entered;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn focus_prev (app: &mut App) -> Usually<bool> {
|
||||||
|
app.section.prev();
|
||||||
|
app.transport.focused = app.section == AppSection::Transport;
|
||||||
|
app.transport.entered = app.entered;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
40
src/control/transport.rs
Normal file
40
src/control/transport.rs
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
use crate::{core::*, model::{App, TransportFocus}};
|
||||||
|
|
||||||
|
pub const KEYMAP_TRANSPORT: &'static [KeyBinding<App>] = keymap!(App {
|
||||||
|
[Left, NONE, "transport_prev", "select previous control", |app: &mut App| Ok({
|
||||||
|
app.transport.selected.prev();
|
||||||
|
true
|
||||||
|
})],
|
||||||
|
[Right, NONE, "transport_next", "select next control", |app: &mut App| Ok({
|
||||||
|
app.transport.selected.next();
|
||||||
|
true
|
||||||
|
})],
|
||||||
|
[Char('.'), NONE, "transport_increment", "increment value at cursor", |app: &mut App| {
|
||||||
|
match app.transport.selected {
|
||||||
|
TransportFocus::BPM => {
|
||||||
|
app.transport.timebase.bpm.fetch_add(1.0, Ordering::Relaxed);
|
||||||
|
},
|
||||||
|
TransportFocus::Quant => {
|
||||||
|
app.transport.quant = next_note_length(app.transport.quant)
|
||||||
|
},
|
||||||
|
TransportFocus::Sync => {
|
||||||
|
app.transport.sync = next_note_length(app.transport.sync)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(true)
|
||||||
|
}],
|
||||||
|
[Char(','), NONE, "transport_decrement", "decrement value at cursor", |app: &mut App| {
|
||||||
|
match app.transport.selected {
|
||||||
|
TransportFocus::BPM => {
|
||||||
|
app.transport.timebase.bpm.fetch_sub(1.0, Ordering::Relaxed);
|
||||||
|
},
|
||||||
|
TransportFocus::Quant => {
|
||||||
|
app.transport.quant = prev_note_length(app.transport.quant);
|
||||||
|
},
|
||||||
|
TransportFocus::Sync => {
|
||||||
|
app.transport.sync = prev_note_length(app.transport.sync);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(true)
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
@ -34,23 +34,33 @@ pub fn write_midi_output (writer: &mut ::jack::MidiWriter, output: &MIDIChunk, f
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (ppq, name)
|
/// (ppq, name)
|
||||||
pub const NOTE_DURATIONS: [(usize, &str);16] = [
|
pub const NOTE_DURATIONS: [(usize, &str);26] = [
|
||||||
(1, "1/384"),
|
(1, "1/384"),
|
||||||
(2, "1/192"),
|
(2, "1/192"),
|
||||||
(3, "1/128"),
|
(3, "1/128"),
|
||||||
(4, "1/96"),
|
(4, "1/96"),
|
||||||
(6, "1/64"),
|
(6, "1/64"),
|
||||||
(8, "1/48"),
|
(8, "1/48"),
|
||||||
(12, "1/32"),
|
(12, "1/32"),
|
||||||
(16, "1/24"),
|
(16, "1/24"),
|
||||||
(24, "1/16"),
|
(24, "1/16"),
|
||||||
(32, "1/12"),
|
(32, "1/12"),
|
||||||
(48, "1/8"),
|
(48, "1/8"),
|
||||||
(64, "1/6"),
|
(64, "1/6"),
|
||||||
(96, "1/4"),
|
(96, "1/4"),
|
||||||
(128, "1/3"),
|
(128, "1/3"),
|
||||||
(192, "1/2"),
|
(192, "1/2"),
|
||||||
(384, "1/1"),
|
(256, "2/3"),
|
||||||
|
(384, "1/1"),
|
||||||
|
(512, "4/3"),
|
||||||
|
(576, "3/2"),
|
||||||
|
(768, "2/1"),
|
||||||
|
(1152, "3/1"),
|
||||||
|
(1536, "4/1"),
|
||||||
|
(2304, "6/1"),
|
||||||
|
(3072, "8/1"),
|
||||||
|
(3456, "9/1"),
|
||||||
|
(6144, "16/1"),
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn ppq_to_name (ppq: usize) -> &'static str {
|
pub fn ppq_to_name (ppq: usize) -> &'static str {
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,11 @@ use atomic_float::AtomicF64;
|
||||||
/// Keeps track of global time units.
|
/// Keeps track of global time units.
|
||||||
pub struct Timebase {
|
pub struct Timebase {
|
||||||
/// Frames per second
|
/// Frames per second
|
||||||
rate: AtomicF64,
|
pub rate: AtomicF64,
|
||||||
/// Beats per minute
|
/// Beats per minute
|
||||||
bpm: AtomicF64,
|
pub bpm: AtomicF64,
|
||||||
/// Ticks per beat
|
/// Ticks per beat
|
||||||
ppq: AtomicF64,
|
pub ppq: AtomicF64,
|
||||||
}
|
}
|
||||||
impl Default for Timebase {
|
impl Default for Timebase {
|
||||||
fn default () -> Self {
|
fn default () -> Self {
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ pub use self::track::Track;
|
||||||
pub use self::sampler::{Sampler, Sample, read_sample_data};
|
pub use self::sampler::{Sampler, Sample, read_sample_data};
|
||||||
pub use self::mixer::Mixer;
|
pub use self::mixer::Mixer;
|
||||||
pub use self::plugin::{Plugin, PluginKind, lv2::LV2Plugin};
|
pub use self::plugin::{Plugin, PluginKind, lv2::LV2Plugin};
|
||||||
pub use self::transport::TransportToolbar;
|
pub use self::transport::{TransportToolbar, TransportFocus};
|
||||||
|
|
||||||
use crate::{core::*, view::*};
|
use crate::{core::*, view::*};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,43 @@
|
||||||
use crate::core::*;
|
use crate::core::*;
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum TransportFocus {
|
||||||
|
BPM,
|
||||||
|
Quant,
|
||||||
|
Sync,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TransportFocus {
|
||||||
|
pub fn prev (&mut self) {
|
||||||
|
*self = match self {
|
||||||
|
Self::BPM => Self::Sync,
|
||||||
|
Self::Quant => Self::BPM,
|
||||||
|
Self::Sync => Self::Quant,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn next (&mut self) {
|
||||||
|
*self = match self {
|
||||||
|
Self::BPM => Self::Quant,
|
||||||
|
Self::Quant => Self::Sync,
|
||||||
|
Self::Sync => Self::BPM,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TransportToolbar {
|
pub struct TransportToolbar {
|
||||||
pub metronome: bool,
|
pub metronome: bool,
|
||||||
pub mode: bool,
|
pub mode: bool,
|
||||||
pub focused: bool,
|
pub focused: bool,
|
||||||
pub entered: bool,
|
pub entered: bool,
|
||||||
|
pub selected: TransportFocus,
|
||||||
/// Current sample rate, tempo, and PPQ.
|
/// Current sample rate, tempo, and PPQ.
|
||||||
pub timebase: Arc<Timebase>,
|
pub timebase: Arc<Timebase>,
|
||||||
/// JACK transport handle.
|
/// JACK transport handle.
|
||||||
transport: Option<Transport>,
|
transport: Option<Transport>,
|
||||||
/// Quantization factor
|
/// Quantization factor
|
||||||
pub quant: usize,
|
pub quant: usize,
|
||||||
|
/// Global sync quant
|
||||||
|
pub sync: usize,
|
||||||
/// Current transport state
|
/// Current transport state
|
||||||
pub playing: Option<TransportState>,
|
pub playing: Option<TransportState>,
|
||||||
/// Current position according to transport
|
/// Current position according to transport
|
||||||
|
|
@ -21,17 +48,20 @@ pub struct TransportToolbar {
|
||||||
|
|
||||||
impl TransportToolbar {
|
impl TransportToolbar {
|
||||||
pub fn new (transport: Option<Transport>) -> Self {
|
pub fn new (transport: Option<Transport>) -> Self {
|
||||||
|
let timebase = Arc::new(Timebase::default());
|
||||||
Self {
|
Self {
|
||||||
transport,
|
selected: TransportFocus::BPM,
|
||||||
metronome: false,
|
metronome: false,
|
||||||
mode: false,
|
mode: false,
|
||||||
focused: false,
|
focused: false,
|
||||||
entered: false,
|
entered: false,
|
||||||
timebase: Arc::new(Timebase::default()),
|
|
||||||
playhead: 0,
|
playhead: 0,
|
||||||
playing: Some(TransportState::Stopped),
|
playing: Some(TransportState::Stopped),
|
||||||
started: None,
|
started: None,
|
||||||
quant: 24,
|
quant: 24,
|
||||||
|
sync: timebase.ppq() as usize * 4,
|
||||||
|
transport,
|
||||||
|
timebase,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn toggle_play (&mut self) -> Usually<()> {
|
pub fn toggle_play (&mut self) -> Usually<()> {
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
let mut width = 0;
|
let mut width = 0;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
"MIX".blit(buf, area.x + 1, area.y + y, style1)?;
|
//"MIX".blit(buf, area.x + 1, area.y + y, style1)?;
|
||||||
} else if y % 2 == 1 {
|
} else if y % 2 == 1 {
|
||||||
let index = y as usize / 2;
|
let index = y as usize / 2;
|
||||||
if let Some(track) = self.tracks.get(index) {
|
if let Some(track) = self.tracks.get(index) {
|
||||||
|
|
@ -156,7 +156,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
" MON ".blit(buf, area.x, area.y + y, style2)?;
|
//" MON ".blit(buf, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 1 {
|
} else if y % 2 == 1 {
|
||||||
let index = y as usize / 2;
|
let index = y as usize / 2;
|
||||||
if let Some(track) = self.tracks.get(index) {
|
if let Some(track) = self.tracks.get(index) {
|
||||||
|
|
@ -179,7 +179,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
" REC ".blit(buf, area.x, area.y + y, style2)?;
|
//" REC ".blit(buf, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 1 {
|
} else if y % 2 == 1 {
|
||||||
let index = y as usize / 2;
|
let index = y as usize / 2;
|
||||||
if let Some(track) = self.tracks.get(index) {
|
if let Some(track) = self.tracks.get(index) {
|
||||||
|
|
@ -202,7 +202,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
" OVR ".blit(buf, area.x, area.y + y, style2)?;
|
//" OVR ".blit(buf, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 1 {
|
} else if y % 2 == 1 {
|
||||||
let index = y as usize / 2;
|
let index = y as usize / 2;
|
||||||
if let Some(track) = self.tracks.get(index) {
|
if let Some(track) = self.tracks.get(index) {
|
||||||
|
|
@ -225,7 +225,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
" DEL ".blit(buf, area.x, area.y + y, style2)?;
|
//" DEL ".blit(buf, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 1 {
|
} else if y % 2 == 1 {
|
||||||
let index = y as usize / 2;
|
let index = y as usize / 2;
|
||||||
if let Some(_) = self.tracks.get(index) {
|
if let Some(_) = self.tracks.get(index) {
|
||||||
|
|
@ -244,7 +244,7 @@ impl<'a> ArrangerView<'a> {
|
||||||
area.x = area.x + 1;
|
area.x = area.x + 1;
|
||||||
for y in 0..area.height {
|
for y in 0..area.height {
|
||||||
if y == 0 {
|
if y == 0 {
|
||||||
" GAIN ".blit(buf, area.x, area.y + y, style2)?;
|
//" GAIN ".blit(buf, area.x, area.y + y, style2)?;
|
||||||
} else if y % 2 == 1 {
|
} else if y % 2 == 1 {
|
||||||
let index = y as usize / 2;
|
let index = y as usize / 2;
|
||||||
if let Some(_) = self.tracks.get(index) {
|
if let Some(_) = self.tracks.get(index) {
|
||||||
|
|
|
||||||
|
|
@ -48,11 +48,11 @@ render!(HelpModal |self, buf, area|{
|
||||||
let y = y + 1;
|
let y = y + 1;
|
||||||
for i in 0..height-3 {
|
for i in 0..height-3 {
|
||||||
let y = y + i;
|
let y = y + i;
|
||||||
if let Some(command) = crate::control::KEYMAP_FOCUS.get(i as usize) {
|
if let Some(command) = crate::control::focus::KEYMAP_FOCUS.get(i as usize) {
|
||||||
format!("{:?}", command.0).blit(buf, x, y, Some(Style::default().white().bold()))?;
|
format!("{:?}", command.0).blit(buf, x, y, Some(Style::default().white().bold()))?;
|
||||||
command.2.blit(buf, x + 11, y, Some(Style::default().white().bold()))?;
|
command.2.blit(buf, x + 11, y, Some(Style::default().white().bold()))?;
|
||||||
command.3.blit(buf, x + 26, y, Some(Style::default().white().dim()))?;
|
command.3.blit(buf, x + 26, y, Some(Style::default().white().dim()))?;
|
||||||
} else if let Some(command) = crate::control::KEYMAP.get((i as usize) - crate::control::KEYMAP_FOCUS.len()) {
|
} else if let Some(command) = crate::control::KEYMAP.get((i as usize) - crate::control::focus::KEYMAP_FOCUS.len()) {
|
||||||
format!("{:?}", command.0).blit(buf, x, y, Some(Style::default().white().bold()))?;
|
format!("{:?}", command.0).blit(buf, x, y, Some(Style::default().white().bold()))?;
|
||||||
command.2.blit(buf, x + 11, y, Some(Style::default().white().bold()))?;
|
command.2.blit(buf, x + 11, y, Some(Style::default().white().bold()))?;
|
||||||
command.3.blit(buf, x + 26, y, Some(Style::default().white().dim()))?;
|
command.3.blit(buf, x + 26, y, Some(Style::default().white().dim()))?;
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ render!(TransportToolbar |self, buf, area| {
|
||||||
let bpm = self.bpm();
|
let bpm = self.bpm();
|
||||||
let pulse = self.pulse();
|
let pulse = self.pulse();
|
||||||
let usecs = self.usecs();
|
let usecs = self.usecs();
|
||||||
let Self { quant, focused, entered, .. } = self;
|
let Self { quant, sync, focused, entered, .. } = self;
|
||||||
fill_bg(buf, area, Nord::bg_lo(*focused, *entered));
|
fill_bg(buf, area, Nord::bg_lo(*focused, *entered));
|
||||||
let area = Split::right([
|
Split::right([
|
||||||
|
|
||||||
// Play/Pause button
|
// Play/Pause button
|
||||||
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||||
|
|
@ -38,21 +38,33 @@ render!(TransportToolbar |self, buf, area| {
|
||||||
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||||
"BPM".blit(buf, x, y, Some(not_dim))?;
|
"BPM".blit(buf, x, y, Some(not_dim))?;
|
||||||
let width = format!("{}.{:03}", bpm, bpm % 1).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
let width = format!("{}.{:03}", bpm, bpm % 1).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
||||||
Ok(Rect { x, y, width: (width + 2).max(10), height: 2 })
|
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||||
|
if self.focused && self.entered && self.selected == TransportFocus::BPM {
|
||||||
|
Corners(Style::default().green().not_dim()).draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||||
|
}
|
||||||
|
Ok(area)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Quantization
|
// Quantization
|
||||||
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||||
"QUANT".blit(buf, x, y, Some(not_dim))?;
|
"QUANT".blit(buf, x, y, Some(not_dim))?;
|
||||||
let width = ppq_to_name(*quant).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
let width = ppq_to_name(*quant).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
||||||
Ok(Rect { x, y, width: (width + 2).max(10), height: 2 })
|
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||||
|
if self.focused && self.entered && self.selected == TransportFocus::Quant {
|
||||||
|
Corners(Style::default().green().not_dim()).draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||||
|
}
|
||||||
|
Ok(area)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Clip launch sync
|
// Clip launch sync
|
||||||
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
|
||||||
"SYNC".blit(buf, x, y, Some(not_dim))?;
|
"SYNC".blit(buf, x, y, Some(not_dim))?;
|
||||||
let width = "4/4".blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
let width = ppq_to_name(*sync).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
|
||||||
Ok(Rect { x, y, width: (width + 2).max(10), height: 2 })
|
let area = Rect { x, y, width: (width + 2).max(10), height: 2 };
|
||||||
|
if self.focused && self.entered && self.selected == TransportFocus::Sync {
|
||||||
|
Corners(Style::default().green().not_dim()).draw(buf, Rect { x: area.x - 1, ..area })?;
|
||||||
|
}
|
||||||
|
Ok(area)
|
||||||
},
|
},
|
||||||
|
|
||||||
// Clock
|
// Clock
|
||||||
|
|
@ -65,13 +77,7 @@ render!(TransportToolbar |self, buf, area| {
|
||||||
timer.blit(buf, x + width - timer.len() as u16 - 1, y, Some(not_dim))
|
timer.blit(buf, x + width - timer.len() as u16 - 1, y, Some(not_dim))
|
||||||
}
|
}
|
||||||
|
|
||||||
]).render(buf, area)?;
|
]).render(buf, area)
|
||||||
|
|
||||||
Ok(if *focused && *entered {
|
|
||||||
Corners(Style::default().green().not_dim()).draw(buf, area)?
|
|
||||||
} else {
|
|
||||||
area
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Record button/indicator
|
// Record button/indicator
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue