chore: tidy up

This commit is contained in:
🪞👃🪞 2024-06-10 18:30:10 +03:00
parent 5afed6f055
commit dc09ea901f
10 changed files with 321 additions and 160 deletions

View file

@ -11,12 +11,17 @@ pub struct Cli {
pub enum Command { pub enum Command {
/// Launch or control a master transport /// Launch or control a master transport
Transport, Transport,
/// Launch or control a sequencer
Sequencer {
#[arg(long="input")]
inputs: Vec<Option<String>>,
#[arg(long="output")]
outputs: Vec<Option<String>>,
},
/// Launch or control a sampler
Sampler,
/// Launch or control a mixer /// Launch or control a mixer
Mixer, Mixer,
/// Launch or control a looper /// Launch or control a looper
Looper, Looper,
/// Launch or control a sampler
Sampler,
/// Launch or control a sequencer
Sequencer,
} }

37
src/device/launcher.rs Normal file
View file

@ -0,0 +1,37 @@
let mut x = areas[1].x;
for (index, track) in [
"Track 1",
"Track 2",
"Track 3",
"Track 4",
"Track 5",
"Bus 1",
"Bus 2",
"Mix",
].iter().enumerate() {
buffer.set_string(
x + 10 * (index + 1) as u16, areas[1].y,
"", Style::default().not_bold().dim()
);
buffer.set_string(
x + 10 * (index + 1) as u16, areas[1].y + areas[1].height - 1,
"", Style::default().not_bold().dim()
);
for y in areas[1].y+1..areas[1].y+areas[1].height - 1 {
buffer.set_string(
x + 10 * (index + 1) as u16, y,
"", Style::default().not_bold().gray().dim()
);
}
for y in areas[1].y+2..areas[1].y+areas[1].height - 1 {
buffer.set_string(
x + 10 * index as u16 + 1, y,
"--------", Style::default().not_bold().gray().dim()
);
}
buffer.set_string(
x + 10 * index as u16 + 1, areas[1].y + 1,
track, Style::default().bold().not_dim()
);
}

0
src/device/lv2_host.rs Normal file
View file

View file

@ -55,10 +55,11 @@ impl Mixer {
selected_column: 0, selected_column: 0,
selected_track: 1, selected_track: 1,
tracks: vec![ tracks: vec![
Track::new(&jack.as_client(), 1, "Kick")?, Track::new(&jack.as_client(), 1, "Mono 1")?,
Track::new(&jack.as_client(), 1, "Snare")?, Track::new(&jack.as_client(), 1, "Mono 2")?,
Track::new(&jack.as_client(), 2, "Hihats")?, Track::new(&jack.as_client(), 2, "Stereo 1")?,
Track::new(&jack.as_client(), 2, "Sample")?, Track::new(&jack.as_client(), 2, "Stereo 2")?,
Track::new(&jack.as_client(), 2, "Stereo 3")?,
Track::new(&jack.as_client(), 2, "Bus 1")?, Track::new(&jack.as_client(), 2, "Bus 1")?,
Track::new(&jack.as_client(), 2, "Bus 2")?, Track::new(&jack.as_client(), 2, "Bus 2")?,
Track::new(&jack.as_client(), 2, "Mix")?, Track::new(&jack.as_client(), 2, "Mix")?,
@ -114,68 +115,62 @@ impl Exitable for Mixer {
impl WidgetRef for Mixer { impl WidgetRef for Mixer {
fn render_ref (&self, area: Rect, buf: &mut Buffer) { fn render_ref (&self, area: Rect, buf: &mut Buffer) {
} use ratatui::style::Stylize;
} draw_box(buf, area);
let x = area.x + 1;
pub fn render ( let y = area.y + 1;
state: &mut Mixer, let h = area.height - 2;
stdout: &mut Stdout, for (i, track) in self.tracks.iter().enumerate() {
mut offset: (u16, u16) //buf.set_string(
) -> Result<(), Box<dyn Error>> { //x, y + index as u16,
render_table(state, stdout, offset)?; //&track.name, Style::default().bold().not_dim()
render_meters(state, stdout, offset)?; //);
Ok(()) for (j, (column, field)) in [
} (0, format!(" {:7} ", track.name)),
(12, format!(" {:.1}dB ", track.gain)),
fn render_table ( (22, format!(" [ ] ")),
state: &mut Mixer, (30, format!(" C ")),
stdout: &mut Stdout, (35, format!(" {:.1}dB ", track.level)),
offset: (u16, u16) (45, format!(" [ ] ")),
) -> Result<(), Box<dyn Error>> { (51, format!(" {:7} ", track.route)),
let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row); ].into_iter().enumerate() {
stdout.queue( buf.set_string(
move_to(0, 0) x + column as u16,
)?.queue( y + i as u16,
Print(" Name Gain FX1 Pan Level FX2 Route") field,
)?; if self.selected_track == i && self.selected_column == j {
for (i, track) in state.tracks.iter().enumerate() { Style::default().white().bold().not_dim()
let row = (i + 1) as u16; } else {
for (j, (column, field)) in [ Style::default().not_dim()
(0, format!(" {:7} ", track.name)), }
(12, format!(" {:.1}dB ", track.gain)), );
(22, format!(" [ ] ")), //stdout.queue(move_to(column, row))?;
(30, format!(" C ")), //if state.selected_track == i && state.selected_column == j {
(35, format!(" {:.1}dB ", track.level)), //stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
(45, format!(" [ ] ")), //} else {
(51, format!(" {:7} ", track.route)), //stdout.queue(PrintStyledContent(field.to_string().bold()))?;
].into_iter().enumerate() { //}
stdout.queue(move_to(column, row))?; //fn render_meters (
if state.selected_track == i && state.selected_column == j { //state: &mut Mixer,
stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?; //stdout: &mut Stdout,
} else { //offset: (u16, u16)
stdout.queue(PrintStyledContent(field.to_string().bold()))?; //) -> Result<(), Box<dyn Error>> {
//let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
//for (i, track) in state.tracks.iter().enumerate() {
//let row = (i + 1) as u16;
//stdout
//.queue(move_to(10, row))?.queue(PrintStyledContent("▁".green()))?
//.queue(move_to(20, row))?.queue(PrintStyledContent("▁".green()))?
//.queue(move_to(28, row))?.queue(PrintStyledContent("▁".green()))?
//.queue(move_to(43, row))?.queue(PrintStyledContent("▁".green()))?;
//}
//Ok(())
//}
} }
} }
} }
Ok(())
} }
fn render_meters (
state: &mut Mixer,
stdout: &mut Stdout,
offset: (u16, u16)
) -> Result<(), Box<dyn Error>> {
let move_to = |col, row| crossterm::cursor::MoveTo(offset.0 + col, offset.1 + row);
for (i, track) in state.tracks.iter().enumerate() {
let row = (i + 1) as u16;
stdout
.queue(move_to(10, row))?.queue(PrintStyledContent("".green()))?
.queue(move_to(20, row))?.queue(PrintStyledContent("".green()))?
.queue(move_to(28, row))?.queue(PrintStyledContent("".green()))?
.queue(move_to(43, row))?.queue(PrintStyledContent("".green()))?;
}
Ok(())
}
impl HandleInput for Mixer { impl HandleInput for Mixer {
fn handle (&mut self, event: &Event) -> Result<(), Box<dyn Error>> { fn handle (&mut self, event: &Event) -> Result<(), Box<dyn Error>> {

0
src/device/port_list.rs Normal file
View file

View file

@ -11,6 +11,9 @@ pub const ACTIONS: [(&'static str, &'static str);4] = [
pub struct Sequencer { pub struct Sequencer {
name: Arc<str>, name: Arc<str>,
exited: Arc<AtomicBool>, exited: Arc<AtomicBool>,
playing: Arc<AtomicBool>,
recording: Arc<AtomicBool>,
overdub: Arc<AtomicBool>,
sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>>, sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>>,
cursor: (u16, u16, u16), cursor: (u16, u16, u16),
timesig: (f32, f32), timesig: (f32, f32),
@ -25,12 +28,11 @@ pub enum Event {
impl Sequencer { impl Sequencer {
pub fn new (name: Option<&str>) -> Result<Self, Box<dyn Error>> { pub fn new (
let exited = Arc::new(AtomicBool::new(false)); name: Option<&str>,
let name = name.unwrap_or("sequencer"); connect_inputs: Option<&[String]>,
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?; connect_outputs: Option<&[String]>,
let mut port = client.register_port("sequence", ::jack::MidiOut::default())?; ) -> Result<Self, Box<dyn Error>> {
let sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>> = Arc::new(Mutex::new(vec![vec![None;64];128]));
let beats = 4; let beats = 4;
let steps = 16; let steps = 16;
let bpm = 120.0; let bpm = 120.0;
@ -40,6 +42,23 @@ impl Sequencer {
let t_beat = 60.0 / bpm; // msec let t_beat = 60.0 / bpm; // msec
let t_loop = t_beat * beats as f64; // msec let t_loop = t_beat * beats as f64; // msec
let t_step = t_beat / steps as f64; // msec let t_step = t_beat / steps as f64; // msec
let exited = Arc::new(AtomicBool::new(false));
let playing = Arc::new(AtomicBool::new(true));
let recording = Arc::new(AtomicBool::new(true));
let overdub = Arc::new(AtomicBool::new(false));
let name = name.unwrap_or("sequencer");
let (client, _status) = Client::new(
name, ClientOptions::NO_START_SERVER
)?;
let mut input = client.register_port(
"input", ::jack::MidiIn::default()
)?;
let mut output = client.register_port(
"output", ::jack::MidiOut::default()
)?;
let sequence: Arc<Mutex<Vec<Vec<Option<Event>>>>> = Arc::new(
Mutex::new(vec![vec![None;64];128])
);
let mut step_frames = vec![]; let mut step_frames = vec![];
for step in 0..beats*steps { for step in 0..beats*steps {
let step_index = (step as f64 * t_step / frame) as usize; let step_index = (step as f64 * t_step / frame) as usize;
@ -51,11 +70,14 @@ impl Sequencer {
frame_steps[*frame] = Some(index); frame_steps[*frame] = Some(index);
} }
Ok(Self { Ok(Self {
name: name.into(), name: name.into(),
exited: exited.clone(), exited: exited.clone(),
sequence: sequence.clone(), playing: playing.clone(),
cursor: (11, 0, 0), recording: recording.clone(),
timesig: (4.0, 4.0), overdub: overdub.clone(),
sequence: sequence.clone(),
cursor: (11, 0, 0),
timesig: (4.0, 4.0),
jack_client: crate::engine::activate_jack_client( jack_client: crate::engine::activate_jack_client(
client, client,
Notifications, Notifications,
@ -69,28 +91,41 @@ impl Sequencer {
let chunk_end = chunk_start + chunk_size; let chunk_end = chunk_start + chunk_size;
let start_looped = chunk_start as usize % loop_frames; let start_looped = chunk_start as usize % loop_frames;
let end_looped = chunk_end as usize % loop_frames; let end_looped = chunk_end as usize % loop_frames;
let mut writer = port.writer(scope);
let sequence = sequence.lock().unwrap(); // Write MIDI notes from input to sequence
for frame in 0..chunk_size { if recording.fetch_and(true, Ordering::Relaxed) {
let value = frame_steps[(start_looped + frame as usize) % loop_frames]; //let overdub = overdub.fetch_and(true, Ordering::Relaxed);
if let Some(step) = value { for event in input.iter(scope) {
for track in sequence.iter() { let ::jack::RawMidi { time, bytes } = event;
for event in track[step].iter() { println!("\n{time} {bytes:?}");
writer.write(&::jack::RawMidi { }
time: frame as u32, }
bytes: &match event {
Event::NoteOn(pitch, velocity) => [ // Write MIDI notes from sequence to output
0b10010000, if playing.fetch_and(true, Ordering::Relaxed) {
*pitch, let mut writer = output.writer(scope);
*velocity let sequence = sequence.lock().unwrap();
], for frame in 0..chunk_size {
Event::NoteOff(pitch) => [ let value = frame_steps[(start_looped + frame as usize) % loop_frames];
0b10000000, if let Some(step) = value {
*pitch, for track in sequence.iter() {
0b00000000 for event in track[step].iter() {
], writer.write(&::jack::RawMidi {
} time: frame as u32,
}).unwrap() bytes: &match event {
Event::NoteOn(pitch, velocity) => [
0b10010000,
*pitch,
*velocity
],
Event::NoteOff(pitch) => [
0b10000000,
*pitch,
0b00000000
],
}
}).unwrap()
}
} }
} }
} }
@ -186,44 +221,88 @@ const KEYS: [&'static str; 6] = [
]; ];
impl WidgetRef for Sequencer { impl WidgetRef for Sequencer {
fn render_ref (&self, area: Rect, buf: &mut Buffer) { fn render_ref (&self, mut area: Rect, buf: &mut Buffer) {
draw_leaf(buf, area, 1, 0, &format!("{} ", &self.name)); draw_sequencer(self, buf, area)
draw_leaf(buf, area, 3, 0, "Channel: 01");
draw_leaf(buf, area, 5, 0, "Zoom: 1/64");
draw_leaf(buf, area, 7, 0, "Rate: 1/1");
draw_leaf(buf, area, 10, 0, "Inputs: ");
draw_leaf(buf, area, 13, 0, "Outputs: ");
{
let mut area = area.clone();
area.height = 18;
draw_box(buf, area);
}
{
let mut area = area.inner(&Margin { horizontal: 1, vertical: 3, });
area.x = area.x + 14;
draw_sequence_keys(area, buf, &self.jack_client.as_client().transport().query().unwrap(), &self.sequence);
draw_sequence_header(area, buf);
draw_sequence_cursor(area, buf, self.cursor);
}
} }
} }
fn draw_sequence_header ( fn draw_sequencer (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect) {
area: Rect, buf: &mut Buffer area.height = 15;
draw_box(buf, area);
let Rect { x, y, width, height } = area;
let mut command = |y2: u16, c: &str, ommand: &str, value: &str| {
buf.set_string(x + 1, y + y2, c, Style::default().bold());
buf.set_string(x + 2, y + y2, ommand, Style::default().dim());
buf.set_string(x + 4 + ommand.len() as u16, y + y2, value, Style::default().bold());
};
for (y, c, ommand, value) in [
(5, "I", "nputs", "[+]"),
(6, "O", "utputs", "[+]"),
(7, "C", "hannel", "01"),
(8, "G", "rid", "1/16"),
(9, "Z", "oom", "1/64"),
(10, "R", "ate", "1/1"),
(11, "S", "ync", "1 bar"),
(12, "A", "dd note", ""),
(13, "D", "elete note", ""),
] {
command(y, c, ommand, value)
}
{
let mut area = area.clone();
//area.y = area.y + 1;
area.x = area.x + 19;
area.height = area.height - 2;
draw_sequence_keys(area, buf,
&sequencer.jack_client.as_client().transport().query().unwrap(),
&sequencer.sequence,
&sequencer.cursor);
draw_sequence_cursor(area, buf,
&sequencer.cursor);
}
draw_box(buf, Rect { x, y, width: 18, height });
draw_rec_dub_button(buf, x, y + 2);
draw_sequence_button(buf, x, y, &sequencer.name);
}
fn draw_sequence_button (
buf: &mut Buffer,
x: u16,
y: u16,
name: &str
) { ) {
buf.set_string(area.x + 3, area.y, "╭1.1.", Style::default().dim()); draw_box(buf, Rect { x, y, width: 18, height: 3 });
buf.set_string(area.x + 3 + 16, area.y, "╭1.2.", Style::default().dim()); buf.set_string(x + 1, y + 1, &format!("{} ", name),
buf.set_string(area.x + 3 + 32, area.y, "╭1.3.", Style::default().dim()); Style::default().white().bold().not_dim());
buf.set_string(area.x + 3 + 48, area.y, "╭1.4.", Style::default().dim()); buf.set_string(x + 4, y + 0, "", Style::default().gray());
buf.set_string(x + 4, y + 1, "", Style::default().gray().dim());
buf.set_string(x + 4, y + 2, "", Style::default().gray());
}
fn draw_rec_dub_button (
buf: &mut Buffer,
x: u16,
y: u16,
) {
draw_box(buf, Rect { x, y, width: 18, height: 3 });
buf.set_string(x + 1, y + 1, " ⏺ REC DUB ",
Style::default().white().bold().not_dim());
buf.set_string(x + 4, y + 0, "", Style::default().gray());
buf.set_string(x + 4, y + 1, "", Style::default().gray().dim());
buf.set_string(x + 4, y + 2, "", Style::default().gray());
} }
fn draw_sequence_keys ( fn draw_sequence_keys (
area: Rect, area: Rect,
buf: &mut Buffer, buf: &mut Buffer,
transport: &::jack::TransportStatePosition, transport: &::jack::TransportStatePosition,
sequence: &Arc<Mutex<Vec<Vec<Option<self::Event>>>>> sequence: &Arc<Mutex<Vec<Vec<Option<self::Event>>>>>,
cursor: &(u16, u16, u16)
) { ) {
buf.set_string(area.x + 3, area.y, "╭1.1.", Style::default().dim());
buf.set_string(area.x + 3 + 16, area.y, "╭1.2.", Style::default().dim());
buf.set_string(area.x + 3 + 32, area.y, "╭1.3.", Style::default().dim());
buf.set_string(area.x + 3 + 48, area.y, "╭1.4.", Style::default().dim());
//buf.set_string(area.x + 2, area.y, "╭", Style::default().dim()); //buf.set_string(area.x + 2, area.y, "╭", Style::default().dim());
//buf.set_string(area.x + 2, area.y + 13, "╰", Style::default().dim()); //buf.set_string(area.x + 2, area.y + 13, "╰", Style::default().dim());
buf.set_string(area.x + 2 + 65, area.y, "", Style::default().dim()); buf.set_string(area.x + 2 + 65, area.y, "", Style::default().dim());
@ -238,17 +317,21 @@ fn draw_sequence_keys (
let bars = beats as u32 / div as u32; let bars = beats as u32 / div as u32;
let beat = beats as u32 % div as u32 + 1; let beat = beats as u32 % div as u32 + 1;
let beat_sub = beats % 1.0; let beat_sub = beats % 1.0;
//buf.set_string(
//area.x - 18, area.y + area.height,
//format!("BBT {bars:04}:{beat:02}.{:02}", (beat_sub * 16.0) as u32),
//Style::default()
//);
let sequence = sequence.lock().unwrap(); let sequence = sequence.lock().unwrap();
buf.set_string(
area.x + area.width - 25, area.y - 2, format!("{bars:04}:{beat:02}.{:02}", (beat_sub * 16.0) as u32), Style::default()
);
for key in 0..12 { for key in 0..12 {
buf.set_string(area.x, area.y + 1 + key, KEYS[(key % 6) as usize], buf.set_string(area.x, area.y + 1 + key, KEYS[(key % 6) as usize],
Style::default().black()); Style::default().dim());
buf.set_string(area.x + 1, area.y + 1 + key, "", buf.set_string(area.x + 1, area.y + 1 + key, "",
Style::default().black()); Style::default().dim());
for step in 0..64 { for step in 0..64 {
let bg = if step as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 { let bg = if step as u32 == (beat - 1) * 16 + (beat_sub * 16.0) as u32 {
ratatui::style::Color::Gray
} else if step == cursor.1 as usize || key == cursor.0/2 {
ratatui::style::Color::Black ratatui::style::Color::Black
} else { } else {
ratatui::style::Color::Reset ratatui::style::Color::Reset
@ -258,22 +341,22 @@ fn draw_sequence_keys (
match (top, bottom) { match (top, bottom) {
(true, true) => { (true, true) => {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "", buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "",
Style::default().yellow().bold().bg(bg)); Style::default().yellow().not_dim().bold().bg(bg));
}, },
(true, false) => { (true, false) => {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "", buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "",
Style::default().yellow().bold().bg(bg)); Style::default().yellow().not_dim().bold().bg(bg));
}, },
(false, true) => { (false, true) => {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "", buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "",
Style::default().yellow().bold().bg(bg)); Style::default().yellow().not_dim().bold().bg(bg));
}, },
(false, false) => if step % 16 == 0 { (false, false) => if step % 16 == 0 {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "", buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "",
Style::default().black().bg(bg)) Style::default().dim().bg(bg))
} else { } else {
buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "·", buf.set_string(area.x + 3 + step as u16, area.y + 1 + key, "·",
Style::default().black().bg(bg)) Style::default().dim().bg(bg))
}, },
} }
} }
@ -289,7 +372,7 @@ fn draw_sequence_keys (
} }
fn draw_sequence_cursor ( fn draw_sequence_cursor (
area: Rect, buf: &mut Buffer, cursor: (u16, u16, u16) area: Rect, buf: &mut Buffer, cursor: &(u16, u16, u16)
) { ) {
let cursor_character = match cursor.0 % 2 { let cursor_character = match cursor.0 % 2 {
0 => "", 0 => "",
@ -297,11 +380,13 @@ fn draw_sequence_cursor (
_ => unreachable!() _ => unreachable!()
}; };
let cursor_y = cursor.0 / 2; let cursor_y = cursor.0 / 2;
buf.set_string(area.x + cursor.1 + 3, area.y + 1 + cursor_y, if cursor.2 == 0 { let cursor_text = if cursor.2 == 0 {
cursor_character.into() cursor_character.into()
} else { } else {
cursor_character.repeat(cursor.2 as usize) cursor_character.repeat(cursor.2 as usize)
}, Style::default().yellow()); };
let style = Style::default().yellow().dim();
buf.set_string(area.x + cursor.1 + 3, area.y + 1 + cursor_y, cursor_text, style);
} }
pub struct Notifications; pub struct Notifications;

View file

@ -66,8 +66,7 @@ impl WidgetRef for Transport {
draw_leaf(buf, area, 0, 28, "START"); draw_leaf(buf, area, 0, 28, "START");
draw_leaf(buf, area, 0, 35, "Project: Witty Gerbil - Sha Na Na "); draw_leaf(buf, area, 0, 35, "Project: Witty Gerbil - Sha Na Na ");
let position = self.transport.query().expect("failed to query transport"); let position = self.transport.query().expect("failed to query transport");
draw_leaf(buf, area, 2, 0, &format!( draw_leaf(buf, area, 2, 0, &format!("BPM {:03}.{:03}",
"BPM {:03}.{:03}",
self.bpm as u64, self.bpm as u64,
((self.bpm % 1.0) * 1000.0) as u64 ((self.bpm % 1.0) * 1000.0) as u64
)); ));
@ -75,27 +74,37 @@ impl WidgetRef for Transport {
//.with_bpm(self.bpm) //.with_bpm(self.bpm)
//.with_timesig(self.timesig.0, self.timesig.1)); //.with_timesig(self.timesig.0, self.timesig.1));
//.unwrap(); //.unwrap();
draw_leaf(buf, area, 2, 13, &format!("BBT {}.{}.{}", let rate = position.pos.frame_rate().unwrap();
0, let frame = position.pos.frame();
0, let second = (frame as f64) / (rate as f64);
0, 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;
//buf.set_string(
//area.x - 18, area.y + area.height,
//format!("BBT {bars:04}:{beat:02}.{:02}", (beat_sub * 16.0) as u32),
//Style::default()
//);
draw_leaf(buf, area, 2, 13, &format!("BBT {bars:04}:{beat:02}.{:02}",
(beat_sub * 16.0) as u32
)); ));
let rate = position.pos.frame_rate().unwrap();
let frame = position.pos.frame();
let time = frame as f64 / rate as f64; let time = frame as f64 / rate as f64;
let seconds = time % 60.0; let seconds = time % 60.0;
let msec = seconds % 1.0; let msec = seconds % 1.0;
let minutes = (time / 60.0) % 60.0; let minutes = (time / 60.0) % 60.0;
let hours = time / 3600.0; let hours = time / 3600.0;
draw_leaf(buf, area, 2, 27, &format!( draw_leaf(buf, area, 2, 29, &format!("Time {:02}:{:02}:{:02}.{:03}",
"Time {:02}:{:02}:{:02}.{:03}",
hours as u64, hours as u64,
minutes as u64, minutes as u64,
seconds as u64, seconds as u64,
(msec * 1000.0) as u64 (msec * 1000.0) as u64
)); ));
draw_leaf(buf, area, 2, 46, &format!("Rate {:>6}Hz", rate)); draw_leaf(buf, area, 2, 48, &format!("Rate {:>6}Hz", rate));
draw_leaf(buf, area, 2, 61, &format!("Frame {:>12}", frame)); draw_leaf(buf, area, 2, 63, &format!("Frame {:>10}", frame));
//Line::from("Project:").render(area, buf); //Line::from("Project:").render(area, buf);
//if let Ok(position) = self.transport.query() { //if let Ok(position) = self.transport.query() {
//let frame = position.pos.frame(); //let frame = position.pos.frame();

View file

@ -33,9 +33,13 @@ fn main () -> Result<(), Box<dyn Error>> {
Some(cli::Command::Sampler) => engine.run( Some(cli::Command::Sampler) => engine.run(
crate::device::sampler::Sampler::new()?, crate::device::sampler::Sampler::new()?,
), ),
Some(cli::Command::Sequencer) => engine.run( Some(cli::Command::Sequencer { inputs, outputs }) => {
crate::device::sequencer::Sequencer::new(Some("Sequencer"))?, engine.run(crate::device::sequencer::Sequencer::new(
), Some("Sequencer"),
Some(&inputs.into_iter().map(|x|x.unwrap()).collect::<Vec<_>>()),
Some(&outputs.into_iter().map(|x|x.unwrap()).collect::<Vec<_>>()),
)?)
},
None => engine.run(App { None => engine.run(App {
exited: false, exited: false,
mode: Mode::Sequencer, mode: Mode::Sequencer,
@ -44,18 +48,26 @@ fn main () -> Result<(), Box<dyn Error>> {
)?, )?,
focus: 0, focus: 0,
devices: vec![ devices: vec![
crate::device::Device::Sequencer( crate::device::Device::Mixer(
crate::device::sequencer::Sequencer::new(Some("Melody#000"))? crate::device::mixer::Mixer::new()?
), ),
crate::device::Device::Sequencer( crate::device::Device::Sequencer(
crate::device::sequencer::Sequencer::new(Some("Rhythm#000"))? crate::device::sequencer::Sequencer::new(
Some("Melody#000"),
None,
None
)?
),
crate::device::Device::Sequencer(
crate::device::sequencer::Sequencer::new(
Some("Rhythm#000"),
None,
None,
)?
), ),
crate::device::Device::Sampler( crate::device::Device::Sampler(
crate::device::sampler::Sampler::new()? crate::device::sampler::Sampler::new()?
), ),
crate::device::Device::Mixer(
crate::device::mixer::Mixer::new()?
),
crate::device::Device::Looper( crate::device::Device::Looper(
crate::device::looper::Looper::new()? crate::device::looper::Looper::new()?
), ),
@ -95,6 +107,7 @@ impl WidgetRef for App {
use ratatui::style::Stylize; use ratatui::style::Stylize;
let mut constraints = vec![ let mut constraints = vec![
Constraint::Length(4), Constraint::Length(4),
Constraint::Length(10),
Constraint::Max(18), Constraint::Max(18),
Constraint::Max(18), Constraint::Max(18),
Constraint::Max(0), Constraint::Max(0),
@ -105,9 +118,13 @@ impl WidgetRef for App {
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints(&constraints) .constraints(&constraints)
.split(area); .split(area);
self.transport.render(areas[0], buffer); self.transport.render(areas[0], buffer);
for (index, device) in self.devices.iter().enumerate() { for (index, device) in self.devices.iter().enumerate() {
device.render(areas[index + 1], buffer); device.render(areas[index + 1], buffer);
if index == self.focus {
draw_focus_corners(buffer, areas[index+1]);
}
} }
} }
} }

View file

@ -62,4 +62,8 @@ pub use crate::engine::{
HandleInput, HandleInput,
Event Event
}; };
pub use crate::render::{draw_box, draw_leaf}; pub use crate::render::{
draw_box,
draw_leaf,
draw_focus_corners
};

View file

@ -52,6 +52,15 @@ pub fn draw_box (buffer: &mut Buffer, area: Rect) {
buffer.set_string(area.x, area.y + area.height - 1, bottom, border); buffer.set_string(area.x, area.y + area.height - 1, bottom, border);
} }
pub fn draw_focus_corners (buffer: &mut Buffer, area: Rect) {
use ratatui::style::{Style, Stylize};
let focus = Style::default().yellow().bold().not_dim();
buffer.set_string(area.x, area.y, "", focus);
buffer.set_string(area.x + area.width - 1, area.y, "", focus);
buffer.set_string(area.x, area.y + area.height - 1, "", focus);
buffer.set_string(area.x + area.width - 1, area.y + area.height - 1, "", focus);
}
pub fn render_toolbar_vertical ( pub fn render_toolbar_vertical (
stdout: &mut std::io::Stdout, stdout: &mut std::io::Stdout,
offset: (u16, u16), offset: (u16, u16),