control transport values

This commit is contained in:
🪞👃🪞 2024-07-12 16:56:19 +03:00
parent 33e5f47526
commit 45021bc77a
10 changed files with 168 additions and 86 deletions

View file

@ -1,6 +1,6 @@
use crate::{core::*, handle, App, AppSection};
pubmod!{ arranger chain mixer plugin sampler sequencer }
pubmod!{ arranger chain focus mixer plugin sampler sequencer transport }
handle!{
App |self, e| {
@ -15,10 +15,10 @@ handle!{
Ok(if self.entered {
handle_focused(self, e)?
|| handle_keymap(self, e, KEYMAP)?
|| handle_keymap(self, e, KEYMAP_FOCUS)?
|| handle_keymap(self, e, crate::control::focus::KEYMAP_FOCUS)?
} else {
handle_keymap(self, e, KEYMAP)?
|| handle_keymap(self, e, KEYMAP_FOCUS)?
|| handle_keymap(self, e, crate::control::focus::KEYMAP_FOCUS)?
|| handle_focused(self, e)?
})
}
@ -27,13 +27,14 @@ handle!{
fn handle_focused (state: &mut App, e: &AppEvent) -> Usually<bool> {
match state.section {
AppSection::Transport =>
Ok(false),
handle_keymap(state, e, crate::control::transport::KEYMAP_TRANSPORT),
AppSection::Arranger =>
handle_keymap(state, e, crate::control::arranger::KEYMAP_ARRANGER),
AppSection::Sequencer =>
handle_keymap(state, e, crate::control::sequencer::KEYMAP_SEQUENCER),
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 {
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))
}
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 {
[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| {
app.transport.toggle_play()?;
Ok(true)
@ -144,15 +120,3 @@ pub const KEYMAP: &'static [KeyBinding<App>] = keymap!(App {
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
View 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
View 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)
}],
});

View file

@ -34,23 +34,33 @@ pub fn write_midi_output (writer: &mut ::jack::MidiWriter, output: &MIDIChunk, f
}
/// (ppq, name)
pub const NOTE_DURATIONS: [(usize, &str);16] = [
(1, "1/384"),
(2, "1/192"),
(3, "1/128"),
(4, "1/96"),
(6, "1/64"),
(8, "1/48"),
(12, "1/32"),
(16, "1/24"),
(24, "1/16"),
(32, "1/12"),
(48, "1/8"),
(64, "1/6"),
(96, "1/4"),
(128, "1/3"),
(192, "1/2"),
(384, "1/1"),
pub const NOTE_DURATIONS: [(usize, &str);26] = [
(1, "1/384"),
(2, "1/192"),
(3, "1/128"),
(4, "1/96"),
(6, "1/64"),
(8, "1/48"),
(12, "1/32"),
(16, "1/24"),
(24, "1/16"),
(32, "1/12"),
(48, "1/8"),
(64, "1/6"),
(96, "1/4"),
(128, "1/3"),
(192, "1/2"),
(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 {

View file

@ -4,11 +4,11 @@ use atomic_float::AtomicF64;
/// Keeps track of global time units.
pub struct Timebase {
/// Frames per second
rate: AtomicF64,
pub rate: AtomicF64,
/// Beats per minute
bpm: AtomicF64,
pub bpm: AtomicF64,
/// Ticks per beat
ppq: AtomicF64,
pub ppq: AtomicF64,
}
impl Default for Timebase {
fn default () -> Self {

View file

@ -13,7 +13,7 @@ pub use self::track::Track;
pub use self::sampler::{Sampler, Sample, read_sample_data};
pub use self::mixer::Mixer;
pub use self::plugin::{Plugin, PluginKind, lv2::LV2Plugin};
pub use self::transport::TransportToolbar;
pub use self::transport::{TransportToolbar, TransportFocus};
use crate::{core::*, view::*};

View file

@ -1,16 +1,43 @@
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 metronome: bool,
pub mode: bool,
pub focused: bool,
pub entered: bool,
pub selected: TransportFocus,
/// Current sample rate, tempo, and PPQ.
pub timebase: Arc<Timebase>,
/// JACK transport handle.
transport: Option<Transport>,
/// Quantization factor
pub quant: usize,
/// Global sync quant
pub sync: usize,
/// Current transport state
pub playing: Option<TransportState>,
/// Current position according to transport
@ -21,17 +48,20 @@ pub struct TransportToolbar {
impl TransportToolbar {
pub fn new (transport: Option<Transport>) -> Self {
let timebase = Arc::new(Timebase::default());
Self {
transport,
selected: TransportFocus::BPM,
metronome: false,
mode: false,
focused: false,
entered: false,
timebase: Arc::new(Timebase::default()),
playhead: 0,
playing: Some(TransportState::Stopped),
started: None,
quant: 24,
sync: timebase.ppq() as usize * 4,
transport,
timebase,
}
}
pub fn toggle_play (&mut self) -> Usually<()> {

View file

@ -130,7 +130,7 @@ impl<'a> ArrangerView<'a> {
let mut width = 0;
for y in 0..area.height {
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 {
let index = y as usize / 2;
if let Some(track) = self.tracks.get(index) {
@ -156,7 +156,7 @@ impl<'a> ArrangerView<'a> {
area.x = area.x + 1;
for y in 0..area.height {
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 {
let index = y as usize / 2;
if let Some(track) = self.tracks.get(index) {
@ -179,7 +179,7 @@ impl<'a> ArrangerView<'a> {
area.x = area.x + 1;
for y in 0..area.height {
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 {
let index = y as usize / 2;
if let Some(track) = self.tracks.get(index) {
@ -202,7 +202,7 @@ impl<'a> ArrangerView<'a> {
area.x = area.x + 1;
for y in 0..area.height {
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 {
let index = y as usize / 2;
if let Some(track) = self.tracks.get(index) {
@ -225,7 +225,7 @@ impl<'a> ArrangerView<'a> {
area.x = area.x + 1;
for y in 0..area.height {
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 {
let index = y as usize / 2;
if let Some(_) = self.tracks.get(index) {
@ -244,7 +244,7 @@ impl<'a> ArrangerView<'a> {
area.x = area.x + 1;
for y in 0..area.height {
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 {
let index = y as usize / 2;
if let Some(_) = self.tracks.get(index) {

View file

@ -48,11 +48,11 @@ render!(HelpModal |self, buf, area|{
let y = y + 1;
for i in 0..height-3 {
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()))?;
command.2.blit(buf, x + 11, y, Some(Style::default().white().bold()))?;
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()))?;
command.2.blit(buf, x + 11, y, Some(Style::default().white().bold()))?;
command.3.blit(buf, x + 26, y, Some(Style::default().white().dim()))?;

View file

@ -11,9 +11,9 @@ render!(TransportToolbar |self, buf, area| {
let bpm = self.bpm();
let pulse = self.pulse();
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));
let area = Split::right([
Split::right([
// Play/Pause button
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
@ -38,21 +38,33 @@ render!(TransportToolbar |self, buf, area| {
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
"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;
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
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
"QUANT".blit(buf, x, y, Some(not_dim))?;
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
&|buf: &mut Buffer, Rect { x, y, .. }: Rect|{
"SYNC".blit(buf, x, y, Some(not_dim))?;
let width = "4/4".blit(buf, x, y + 1, Some(not_dim_bold))?.width;
Ok(Rect { x, y, width: (width + 2).max(10), height: 2 })
let width = ppq_to_name(*sync).blit(buf, x, y + 1, Some(not_dim_bold))?.width;
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
@ -65,13 +77,7 @@ render!(TransportToolbar |self, buf, area| {
timer.blit(buf, x + width - timer.len() as u16 - 1, y, Some(not_dim))
}
]).render(buf, area)?;
Ok(if *focused && *entered {
Corners(Style::default().green().not_dim()).draw(buf, area)?
} else {
area
})
]).render(buf, area)
});
// Record button/indicator