mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-07 12:16:42 +01:00
chore: tidy up
This commit is contained in:
parent
5afed6f055
commit
dc09ea901f
10 changed files with 321 additions and 160 deletions
13
src/cli.rs
13
src/cli.rs
|
|
@ -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
37
src/device/launcher.rs
Normal 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
0
src/device/lv2_host.rs
Normal 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,32 +115,16 @@ 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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_table (
|
|
||||||
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);
|
|
||||||
stdout.queue(
|
|
||||||
move_to(0, 0)
|
|
||||||
)?.queue(
|
|
||||||
Print(" Name Gain FX1 Pan Level FX2 Route")
|
|
||||||
)?;
|
|
||||||
for (i, track) in state.tracks.iter().enumerate() {
|
|
||||||
let row = (i + 1) as u16;
|
|
||||||
for (j, (column, field)) in [
|
for (j, (column, field)) in [
|
||||||
(0, format!(" {:7} ", track.name)),
|
(0, format!(" {:7} ", track.name)),
|
||||||
(12, format!(" {:.1}dB ", track.gain)),
|
(12, format!(" {:.1}dB ", track.gain)),
|
||||||
|
|
@ -149,33 +134,43 @@ fn render_table (
|
||||||
(45, format!(" [ ] ")),
|
(45, format!(" [ ] ")),
|
||||||
(51, format!(" {:7} ", track.route)),
|
(51, format!(" {:7} ", track.route)),
|
||||||
].into_iter().enumerate() {
|
].into_iter().enumerate() {
|
||||||
stdout.queue(move_to(column, row))?;
|
buf.set_string(
|
||||||
if state.selected_track == i && state.selected_column == j {
|
x + column as u16,
|
||||||
stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
y + i as u16,
|
||||||
|
field,
|
||||||
|
if self.selected_track == i && self.selected_column == j {
|
||||||
|
Style::default().white().bold().not_dim()
|
||||||
} else {
|
} else {
|
||||||
stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
Style::default().not_dim()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
//stdout.queue(move_to(column, row))?;
|
||||||
|
//if state.selected_track == i && state.selected_column == j {
|
||||||
|
//stdout.queue(PrintStyledContent(field.to_string().bold().reverse()))?;
|
||||||
|
//} else {
|
||||||
|
//stdout.queue(PrintStyledContent(field.to_string().bold()))?;
|
||||||
|
//}
|
||||||
|
//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(())
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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
0
src/device/port_list.rs
Normal 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;
|
||||||
|
|
@ -53,6 +72,9 @@ impl Sequencer {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
exited: exited.clone(),
|
exited: exited.clone(),
|
||||||
|
playing: playing.clone(),
|
||||||
|
recording: recording.clone(),
|
||||||
|
overdub: overdub.clone(),
|
||||||
sequence: sequence.clone(),
|
sequence: sequence.clone(),
|
||||||
cursor: (11, 0, 0),
|
cursor: (11, 0, 0),
|
||||||
timesig: (4.0, 4.0),
|
timesig: (4.0, 4.0),
|
||||||
|
|
@ -69,7 +91,19 @@ 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);
|
|
||||||
|
// Write MIDI notes from input to sequence
|
||||||
|
if recording.fetch_and(true, Ordering::Relaxed) {
|
||||||
|
//let overdub = overdub.fetch_and(true, Ordering::Relaxed);
|
||||||
|
for event in input.iter(scope) {
|
||||||
|
let ::jack::RawMidi { time, bytes } = event;
|
||||||
|
println!("\n{time} {bytes:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write MIDI notes from sequence to output
|
||||||
|
if playing.fetch_and(true, Ordering::Relaxed) {
|
||||||
|
let mut writer = output.writer(scope);
|
||||||
let sequence = sequence.lock().unwrap();
|
let sequence = sequence.lock().unwrap();
|
||||||
for frame in 0..chunk_size {
|
for frame in 0..chunk_size {
|
||||||
let value = frame_steps[(start_looped + frame as usize) % loop_frames];
|
let value = frame_steps[(start_looped + frame as usize) % loop_frames];
|
||||||
|
|
@ -96,6 +130,7 @@ impl Sequencer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Control::Continue
|
Control::Continue
|
||||||
})
|
})
|
||||||
)?
|
)?
|
||||||
|
|
@ -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: ");
|
fn draw_sequencer (sequencer: &Sequencer, buf: &mut Buffer, mut area: Rect) {
|
||||||
draw_leaf(buf, area, 13, 0, "Outputs: ");
|
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();
|
let mut area = area.clone();
|
||||||
area.height = 18;
|
//area.y = area.y + 1;
|
||||||
draw_box(buf, area);
|
area.x = area.x + 19;
|
||||||
}
|
area.height = area.height - 2;
|
||||||
{
|
draw_sequence_keys(area, buf,
|
||||||
let mut area = area.inner(&Margin { horizontal: 1, vertical: 3, });
|
&sequencer.jack_client.as_client().transport().query().unwrap(),
|
||||||
area.x = area.x + 14;
|
&sequencer.sequence,
|
||||||
draw_sequence_keys(area, buf, &self.jack_client.as_client().transport().query().unwrap(), &self.sequence);
|
&sequencer.cursor);
|
||||||
draw_sequence_header(area, buf);
|
draw_sequence_cursor(area, buf,
|
||||||
draw_sequence_cursor(area, buf, self.cursor);
|
&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_header (
|
fn draw_sequence_button (
|
||||||
area: Rect, buf: &mut Buffer
|
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;
|
||||||
|
|
|
||||||
|
|
@ -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 {}.{}.{}",
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
));
|
|
||||||
let rate = position.pos.frame_rate().unwrap();
|
let rate = position.pos.frame_rate().unwrap();
|
||||||
let frame = position.pos.frame();
|
let frame = position.pos.frame();
|
||||||
|
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;
|
||||||
|
//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 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();
|
||||||
|
|
|
||||||
35
src/main.rs
35
src/main.rs
|
|
@ -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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue