advance play cursor in sequencer

This commit is contained in:
🪞👃🪞 2024-06-03 06:51:53 +03:00
parent 4fd208d53f
commit 7c1dc9ce9b
9 changed files with 53 additions and 37 deletions

View file

@ -101,7 +101,7 @@ impl Engine {
stdout stdout
.queue(crossterm::terminal::EnterAlternateScreen)? .queue(crossterm::terminal::EnterAlternateScreen)?
.flush()?; .flush()?;
let sleep = std::time::Duration::from_millis(16); let sleep = std::time::Duration::from_millis(20);
loop { loop {
stdout stdout
.queue(crossterm::terminal::BeginSynchronizedUpdate)? .queue(crossterm::terminal::BeginSynchronizedUpdate)?
@ -122,7 +122,7 @@ impl Engine {
crossterm::terminal::disable_raw_mode()?; crossterm::terminal::disable_raw_mode()?;
break break
} }
std::thread::sleep(sleep); //std::thread::sleep(sleep);
} }
Ok(()) Ok(())
} }

View file

@ -12,7 +12,6 @@ pub fn render (
.queue(move_to(0, 1))?.queue(PrintStyledContent(" Metronome [ ] ████ Track 1".bold()))? .queue(move_to(0, 1))?.queue(PrintStyledContent(" Metronome [ ] ████ Track 1".bold()))?
.queue(move_to(0, 2))?.queue(PrintStyledContent(" Loop 1 [ ] ████ Track 1".bold()))? .queue(move_to(0, 2))?.queue(PrintStyledContent(" Loop 1 [ ] ████ Track 1".bold()))?
.queue(move_to(0, 3))?.queue(PrintStyledContent(" Loop 2 [ ] ████████ Track 2".bold()))? .queue(move_to(0, 3))?.queue(PrintStyledContent(" Loop 2 [ ] ████████ Track 2".bold()))?
.queue(move_to(0, 4))?.queue(PrintStyledContent(" Loop 3 [ ] ████████ Track 3".bold()))? .queue(move_to(0, 4))?.queue(PrintStyledContent(" Loop 3 [ ] ████████ Track 3".bold()))?;
.flush()?;
Ok(()) Ok(())
} }

View file

@ -72,7 +72,7 @@ fn run_one (command: &cli::Command) -> Result<(), Box<dyn Error>> {
), ),
cli::Command::Sequencer => engine.run( cli::Command::Sequencer => engine.run(
&mut sequencer::Sequencer::new()?, &mut sequencer::Sequencer::new(engine.jack_client.as_client())?,
|state, stdout, mut offset| { |state, stdout, mut offset| {
let (w, h) = render::render_toolbar_vertical(stdout, offset, &sequencer::ACTIONS)?; let (w, h) = render::render_toolbar_vertical(stdout, offset, &sequencer::ACTIONS)?;
offset.0 = offset.0 + w + 2; offset.0 = offset.0 + w + 2;
@ -122,10 +122,10 @@ fn run_all () -> Result<(), Box<dyn Error>> {
exited: false, exited: false,
mode: Mode::Sequencer, mode: Mode::Sequencer,
transport: transport::Transport::new(engine.jack_client.as_client())?, transport: transport::Transport::new(engine.jack_client.as_client())?,
sequencer: sequencer::Sequencer::new(engine.jack_client.as_client())?,
mixer: mixer::Mixer::new()?, mixer: mixer::Mixer::new()?,
looper: looper::Looper::new()?, looper: looper::Looper::new()?,
sampler: sampler::Sampler::new()?, sampler: sampler::Sampler::new()?,
sequencer: sequencer::Sequencer::new()?,
}; };
let render = |state: &mut App, stdout: &mut Stdout, mut offset: (u16, u16)| { let render = |state: &mut App, stdout: &mut Stdout, mut offset: (u16, u16)| {

View file

@ -8,7 +8,6 @@ pub fn render (
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
render_table(state, stdout, offset)?; render_table(state, stdout, offset)?;
render_meters(state, stdout, offset)?; render_meters(state, stdout, offset)?;
stdout.flush()?;
Ok(()) Ok(())
} }

View file

@ -8,7 +8,6 @@ pub fn render (
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
render_table(state, stdout, offset)?; render_table(state, stdout, offset)?;
render_meters(state, stdout, offset)?; render_meters(state, stdout, offset)?;
stdout.flush()?;
Ok(()) Ok(())
} }

View file

@ -14,10 +14,12 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [
]; ];
pub struct Sequencer { pub struct Sequencer {
exited: bool, exited: bool,
jack: Jack<Notifications>, cursor: (u16, u16, u16),
cursor: (u16, u16, u16), sequence: Vec<Vec<Option<Event>>>,
sequence: Vec<Vec<Option<Event>>>, transport: ::jack::Transport,
bpm: f64,
timesig: (f32, f32),
} }
#[derive(Clone)] #[derive(Clone)]
@ -27,24 +29,15 @@ pub enum Event {
} }
impl Sequencer { impl Sequencer {
pub fn new () -> Result<Self, Box<dyn Error>> { pub fn new (client: &Client) -> Result<Self, Box<dyn Error>> {
let (client, status) = Client::new( let transport = client.transport();
"bloop-sequencer",
ClientOptions::NO_START_SERVER
)?;
let jack = client.activate_async(
Notifications,
ClosureProcessHandler::new(Box::new(
move |_: &Client, _: &ProcessScope| -> Control {
Control::Continue
}) as Box<dyn FnMut(&Client, &ProcessScope)->Control + Send>
)
)?;
Ok(Self { Ok(Self {
exited: false, exited: false,
cursor: (0, 0, 0), cursor: (0, 0, 0),
jack, transport,
sequence: vec![vec![None;64];4], sequence: vec![vec![None;64];4],
bpm: 120.0,
timesig: (4.0, 4.0)
}) })
} }
} }

View file

@ -18,7 +18,6 @@ fn render_grid (
offset: (u16, u16) offset: (u16, u16)
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {
let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row); let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
let bg = "┊···············┊···············┊···············┊···············";
let cursor: String = if state.cursor.2 == 0 { let cursor: String = if state.cursor.2 == 0 {
"".into() "".into()
} else { } else {
@ -26,6 +25,17 @@ fn render_grid (
.take(state.cursor.2 as usize) .take(state.cursor.2 as usize)
.collect() .collect()
}; };
let transport = state.transport.query()?;
let frame = transport.pos.frame();
let rate = transport.pos.frame_rate().unwrap();
let second = (frame as f64) / (rate as f64);
let minute = second / 60f64;
let bpm = 120f64;
let div = 4;
let beats = minute * bpm;
let bars = beats as u32 / div as u32;
let beat = beats as u32 % div as u32 + 1;
let beat_sub = beats % 1.0;
stdout stdout
.queue(move_to(1, 3))?.queue(Print("1.1"))? .queue(move_to(1, 3))?.queue(Print("1.1"))?
.queue(move_to(17, 3))?.queue(Print("1.2"))? .queue(move_to(17, 3))?.queue(Print("1.2"))?
@ -33,14 +43,23 @@ fn render_grid (
.queue(move_to(49, 3))?.queue(Print("1.4"))?; .queue(move_to(49, 3))?.queue(Print("1.4"))?;
for (index, row) in state.sequence.iter().enumerate() { for (index, row) in state.sequence.iter().enumerate() {
let y = index as u16 + 4; let y = index as u16 + 4;
stdout.queue(move_to(1, y))?.queue( for x in 0u16..64 {
PrintStyledContent(bg.grey()) let bg = if x as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 {
)?; crossterm::style::Color::Black
for (index, step) in row.iter().enumerate() { } else {
if step.is_some() { crossterm::style::Color::Reset
let x = index as u16 + 1; };
stdout.queue(move_to(x, y))?.queue( if let Some(step) = &row[x as usize] {
PrintStyledContent("X".white().bold()) stdout.queue(move_to(1 + x, y))?.queue(
PrintStyledContent("X".white().bold().on(bg))
)?;
} else if x % 16 == 0 {
stdout.queue(move_to(1 + x, y))?.queue(
PrintStyledContent("".grey().on(bg))
)?;
} else {
stdout.queue(move_to(1 + x, y))?.queue(
PrintStyledContent("·".grey().on(bg))
)?; )?;
} }
} }

View file

@ -16,14 +16,19 @@ pub struct Transport {
exited: bool, exited: bool,
title: String, title: String,
transport: ::jack::Transport, transport: ::jack::Transport,
bpm: f64,
timesig: (f32, f32),
} }
impl Transport { impl Transport {
pub fn new (client: &Client) -> Result<Self, Box<dyn Error>> { pub fn new (client: &Client) -> Result<Self, Box<dyn Error>> {
let transport = client.transport();
Ok(Self { Ok(Self {
exited: false, exited: false,
title: String::from("Untitled project"), title: String::from("Untitled project"),
transport: client.transport(), bpm: 93.0,
timesig: (4.0, 4.0),
transport,
}) })
} }

View file

@ -16,7 +16,9 @@ pub fn render (
if let Ok(position) = state.transport.query() { if let Ok(position) = state.transport.query() {
let frame = position.pos.frame(); let frame = position.pos.frame();
let rate = position.pos.frame_rate(); let rate = position.pos.frame_rate();
let bbt = position.pos.bbt(); let bbt = position.pos.bbt().map(|mut bbt|*bbt
.with_bpm(state.bpm)
.with_timesig(state.timesig.0, state.timesig.1));
stdout stdout
.queue(move_to( 1, 1))?.queue(Print("Frame: "))? .queue(move_to( 1, 1))?.queue(Print("Frame: "))?
.queue(move_to( 1, 2))?.queue( .queue(move_to( 1, 2))?.queue(