mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
switch between sequences; layout unfucking
This commit is contained in:
parent
c18aa2cbbd
commit
185c6b5b34
7 changed files with 253 additions and 201 deletions
|
|
@ -5,6 +5,14 @@ pub struct Chain {
|
|||
focused: bool,
|
||||
focus: usize,
|
||||
items: Vec<Box<dyn Device>>,
|
||||
view: ChainView
|
||||
}
|
||||
|
||||
pub enum ChainView {
|
||||
Hidden,
|
||||
Compact,
|
||||
Row,
|
||||
Column,
|
||||
}
|
||||
|
||||
impl Chain {
|
||||
|
|
@ -14,6 +22,7 @@ impl Chain {
|
|||
focused: false,
|
||||
focus: 0,
|
||||
items,
|
||||
view: ChainView::Column
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
@ -26,44 +35,60 @@ pub fn render (state: &Chain, buf: &mut Buffer, area: Rect)
|
|||
-> Usually<Rect>
|
||||
{
|
||||
let Rect { x, y, .. } = area;
|
||||
let area = Rect { x, y, width: 40, height: 30 };
|
||||
let mut y = area.y;
|
||||
buf.set_string(area.x, y, "│", Style::default().black());
|
||||
buf.set_string(area.x + area.width - 1, y, "│", Style::default().black());
|
||||
buf.set_string(area.x + 2, y, "Input...", Style::default().dim());
|
||||
let mut x = 0u16;
|
||||
for (i, device) in state.items.iter().enumerate() {
|
||||
let result = device.render(buf, Rect {
|
||||
x: area.x,
|
||||
y,
|
||||
width: area.width,
|
||||
height: 21//area.height.saturating_sub(y)
|
||||
})?;
|
||||
if i == state.focus {
|
||||
if state.focused {
|
||||
draw_box_styled(buf, result, Some(Style::default().green().not_dim()))
|
||||
} else {
|
||||
draw_box_styled_dotted(buf, result, Some(Style::default().green().dim()))
|
||||
};
|
||||
};
|
||||
//let result = Rect { x: 0, y: 0, width: result.width, height: 21 };
|
||||
x = x.max(result.width);
|
||||
y = y + result.height;
|
||||
Ok(match state.view {
|
||||
ChainView::Hidden => Rect { x, y, width: 0, height: 0 },
|
||||
ChainView::Compact => {
|
||||
let area = Rect { x, y, width: (state.name.len() + 4) as u16, height: 3 };
|
||||
buf.set_string(area.x + 2, area.y + 1, &state.name, Style::default());
|
||||
draw_box_styled(buf, area, Some(Style::default().dim()))
|
||||
},
|
||||
ChainView::Row => {
|
||||
let (area, areas) = draw_row(&state.items, buf, area, 0)?;
|
||||
draw_box_styled(buf, area, Some(Style::default().dim()))
|
||||
},
|
||||
ChainView::Column => {
|
||||
let (area, areas) = draw_column(&state.items, buf, area, 0)?;
|
||||
draw_box_styled(buf, area, Some(Style::default().dim()))
|
||||
},
|
||||
})
|
||||
//let area = Rect { x, y, width: 40, height: 30 };
|
||||
//let mut y = area.y;
|
||||
//buf.set_string(area.x, y, "│", Style::default().black());
|
||||
//buf.set_string(area.x + area.width - 1, y, "│", Style::default().black());
|
||||
//buf.set_string(area.x + 2, y, "Input...", Style::default().dim());
|
||||
//let mut x = 0u16;
|
||||
//for (i, device) in state.items.iter().enumerate() {
|
||||
//let result = device.render(buf, Rect {
|
||||
//x: area.x,
|
||||
//y,
|
||||
//width: area.width,
|
||||
//height: 21//area.height.saturating_sub(y)
|
||||
//})?;
|
||||
//if i == state.focus {
|
||||
//if state.focused {
|
||||
//draw_box_styled(buf, result, Some(Style::default().green().not_dim()))
|
||||
//} else {
|
||||
//draw_box_styled_dotted(buf, result, Some(Style::default().green().dim()))
|
||||
//};
|
||||
//};
|
||||
////let result = Rect { x: 0, y: 0, width: result.width, height: 21 };
|
||||
//x = x.max(result.width);
|
||||
//y = y + result.height;
|
||||
////y = y + 1;
|
||||
//buf.set_string(area.x, y, "│", Style::default().black());
|
||||
//buf.set_string(area.x + area.width - 1, y, "│", Style::default().black());
|
||||
//buf.set_string(area.x + 2, y, " Patch in ┐ │ └ Patch out", Style::default().dim());
|
||||
//y = y + 1;
|
||||
buf.set_string(area.x, y, "│", Style::default().black());
|
||||
buf.set_string(area.x + area.width - 1, y, "│", Style::default().black());
|
||||
buf.set_string(area.x + 2, y, " Patch in ┐ │ └ Patch out", Style::default().dim());
|
||||
y = y + 1;
|
||||
//buf.set_string(area.x, y, format!("{y}---BOT---"), Style::default().red());
|
||||
//buf.set_string(area.x + area.width - 1, area.y + 1, "│", Style::default().black());
|
||||
//buf.set_string(area.x + 2, y, "Patch...", Style::default().dim());
|
||||
}
|
||||
Ok(draw_box(buf, Rect {
|
||||
x: area.x,
|
||||
y: area.y,
|
||||
width: area.width,
|
||||
height: y - area.y,
|
||||
}))
|
||||
////buf.set_string(area.x, y, format!("{y}---BOT---"), Style::default().red());
|
||||
////buf.set_string(area.x + area.width - 1, area.y + 1, "│", Style::default().black());
|
||||
////buf.set_string(area.x + 2, y, "Patch...", Style::default().dim());
|
||||
//}
|
||||
//Ok(draw_box(buf, Rect {
|
||||
//x: area.x,
|
||||
//y: area.y,
|
||||
//width: area.width,
|
||||
//height: y - area.y,
|
||||
//}))
|
||||
}
|
||||
|
||||
impl Focus for Chain {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ pub struct Sequencer {
|
|||
notes_on: Vec<bool>,
|
||||
/// Write input to sequence.
|
||||
recording: bool,
|
||||
/// Sequence selector
|
||||
sequence: usize,
|
||||
/// Map: tick -> MIDI events at tick
|
||||
sequence: Sequence,
|
||||
sequences: Vec<Sequence>,
|
||||
/// Don't delete when recording.
|
||||
overdub: bool,
|
||||
/// Play sequence to output.
|
||||
|
|
@ -58,7 +60,7 @@ enum SequencerView {
|
|||
impl Sequencer {
|
||||
pub fn new (name: &str, timebase: &Arc<Timebase>) -> Usually<DynamicDevice<Self>> {
|
||||
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||
DynamicDevice::new(render, handle, process, Self {
|
||||
DynamicDevice::new(render, handle, Self::process, Self {
|
||||
name: name.into(),
|
||||
transport: client.transport(),
|
||||
timebase: timebase.clone(),
|
||||
|
|
@ -69,9 +71,10 @@ impl Sequencer {
|
|||
input_connect: vec!["nanoKEY Studio * (capture): *".into()],
|
||||
monitoring: true,
|
||||
notes_on: vec![false;128],
|
||||
recording: true,
|
||||
recording: false,
|
||||
overdub: true,
|
||||
sequence: std::collections::BTreeMap::new(),
|
||||
sequence: 0,
|
||||
sequences: vec![std::collections::BTreeMap::new();8],
|
||||
playing: true,
|
||||
output_port: client.register_port("out", MidiOut::default())?,
|
||||
output_connect: vec![],
|
||||
|
|
@ -83,12 +86,12 @@ impl Sequencer {
|
|||
time_cursor: 0,
|
||||
}).activate(client)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process (s: &mut Sequencer, _: &Client, scope: &ProcessScope) -> Control {
|
||||
process_out(s, scope);
|
||||
process_in(s, scope);
|
||||
Control::Continue
|
||||
fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||
process_out(self, scope);
|
||||
process_in(self, scope);
|
||||
Control::Continue
|
||||
}
|
||||
}
|
||||
|
||||
fn process_in (s: &mut Sequencer, scope: &ProcessScope) {
|
||||
|
|
@ -100,7 +103,7 @@ fn process_in (s: &mut Sequencer, scope: &ProcessScope) {
|
|||
let steps = usecs / s.timebase.usec_per_step(s.resolution as usize);
|
||||
let step = steps % s.steps;
|
||||
let tick = step * s.timebase.ppq() / s.resolution;
|
||||
|
||||
let mut sequence = &mut s.sequences[s.sequence];
|
||||
for event in s.input_port.iter(scope) {
|
||||
match midly::live::LiveEvent::parse(event.bytes).unwrap() {
|
||||
|
||||
|
|
@ -108,19 +111,21 @@ fn process_in (s: &mut Sequencer, scope: &ProcessScope) {
|
|||
|
||||
midly::MidiMessage::NoteOn { key, vel: _ } => {
|
||||
s.notes_on[key.as_int() as usize] = true;
|
||||
if s.sequence.contains_key(&(tick as u32)) {
|
||||
s.sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
let contains = sequence.contains_key(&(tick as u32));
|
||||
if contains {
|
||||
sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
} else {
|
||||
s.sequence.insert(tick as u32, vec![message.clone()]);
|
||||
sequence.insert(tick as u32, vec![message.clone()]);
|
||||
}
|
||||
},
|
||||
|
||||
midly::MidiMessage::NoteOff { key, vel: _ } => {
|
||||
s.notes_on[key.as_int() as usize] = false;
|
||||
if s.sequence.contains_key(&(tick as u32)) {
|
||||
s.sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
let contains = sequence.contains_key(&(tick as u32));
|
||||
if contains {
|
||||
sequence.get_mut(&(tick as u32)).unwrap().push(message.clone());
|
||||
} else {
|
||||
s.sequence.insert(tick as u32, vec![message.clone()]);
|
||||
sequence.insert(tick as u32, vec![message.clone()]);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -150,7 +155,7 @@ fn process_out (s: &mut Sequencer, scope: &ProcessScope) {
|
|||
);
|
||||
let mut writer = s.output_port.writer(scope);
|
||||
for (time, tick) in ticks.iter() {
|
||||
if let Some(events) = s.sequence.get(&(*tick as u32)) {
|
||||
if let Some(events) = s.sequences[s.sequence].get(&(*tick as u32)) {
|
||||
for message in events.iter() {
|
||||
let mut buf = vec![];
|
||||
::midly::live::LiveEvent::Midi {
|
||||
|
|
@ -208,47 +213,44 @@ fn draw_header (s: &Sequencer, buf: &mut Buffer, area: Rect, beat: usize) -> Usu
|
|||
let step = beat % s.steps;
|
||||
let reps = s.steps / s.resolution;
|
||||
let steps = s.steps % s.resolution;
|
||||
let Rect { x, y, .. } = area;
|
||||
let Rect { x, y, width, .. } = area;
|
||||
let style = Style::default().gray();
|
||||
buf.set_string(x + 2, y + 1,
|
||||
&format!("{rep}.{step:2} / {reps}.{steps}"),
|
||||
style);
|
||||
buf.set_string(x + 2, y + 2,
|
||||
&format!("⏹ STOP"),
|
||||
style);
|
||||
let timer = format!("{rep}.{step:02} / {reps}.{steps}");
|
||||
buf.set_string(x + width - 2 - timer.len() as u16, y + 1, &timer, style.bold().not_dim());
|
||||
buf.set_string(x + 2, y + 1, &format!("⏹ STOP"), style.not_dim().white().bold());
|
||||
//buf.set_string(x + 2, y + 2,
|
||||
//&format!("▶ PLAY"), if s.playing {
|
||||
//Style::default().green()
|
||||
//} else {
|
||||
//Style::default().dim()
|
||||
//});
|
||||
buf.set_string(x + 10, y + 2,
|
||||
buf.set_string(x + 10, y + 1,
|
||||
&format!("⏺ REC"), if s.recording {
|
||||
Style::default().red()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
Style::default().bold().dim()
|
||||
});
|
||||
buf.set_string(x + 17, y + 2,
|
||||
buf.set_string(x + 17, y + 1,
|
||||
&format!("⏺ DUB"), if s.overdub {
|
||||
Style::default().yellow()
|
||||
} else {
|
||||
Style::default().dim()
|
||||
Style::default().bold().dim()
|
||||
});
|
||||
let clips = draw_clips(s, buf, area)?;
|
||||
Ok(Rect { x, y, width: area.width, height: 4 })
|
||||
Ok(Rect { x, y, width: area.width, height: 3 })
|
||||
}
|
||||
|
||||
fn draw_clips (s: &Sequencer, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, .. } = area;
|
||||
let style = Style::default().gray();
|
||||
buf.set_string(x + 2, y + 4, &format!("▶ {}", &s.name), style.white().bold());
|
||||
buf.set_string(x + 2, y + 6, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 8, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 10, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 12, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 14, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 16, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 18, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 3, &format!("▶ {}", &s.name), style.not_dim().bold());
|
||||
buf.set_string(x + 2, y + 5, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 7, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 9, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 11, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 13, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 15, &format!("⏺ {}", &s.name), style.dim());
|
||||
buf.set_string(x + 2, y + 17, &format!("⏺ {}", &s.name), style.dim());
|
||||
Ok(Rect { x, y, width: 14, height: 14 })
|
||||
}
|
||||
|
||||
|
|
@ -256,6 +258,34 @@ const KEYS_VERTICAL: [&'static str; 6] = [
|
|||
"▀", "▀", "▀", "█", "▄", "▄",
|
||||
];
|
||||
|
||||
fn draw_keys_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) {
|
||||
let ppq = s.timebase.ppq() as u32;
|
||||
let Rect { x, y, .. } = area;
|
||||
let (note0, note1) = s.note_axis;
|
||||
for key in note0..note1 {
|
||||
let x = x + 5 + key - note0;
|
||||
if key % 12 == 0 {
|
||||
let octave = format!("C{}", (key / 12) as i8 - 4);
|
||||
buf.set_string(x, y, &octave, Style::default());
|
||||
}
|
||||
let mut color = KEY_STYLE[key as usize % 12];
|
||||
let mut is_on = s.notes_on[key as usize];
|
||||
let step = beat % s.steps;
|
||||
let (a, b, c) = (
|
||||
(step + 0) as u32 * ppq / s.resolution as u32,
|
||||
(step + 1) as u32 * ppq / s.resolution as u32,
|
||||
(step + 2) as u32 * ppq / s.resolution as u32,
|
||||
);
|
||||
let key = ::midly::num::u7::from(key as u8);
|
||||
is_on = is_on || contains_note_on(&s.sequences[s.sequence], key, a, b);
|
||||
is_on = is_on || contains_note_on(&s.sequences[s.sequence], key, b, c);
|
||||
if is_on {
|
||||
color = Style::default().red();
|
||||
}
|
||||
buf.set_string(x, y - 1, &format!("▄"), color);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize) -> Usually<Rect> {
|
||||
let ppq = s.timebase.ppq() as u32;
|
||||
area.x = area.x + 13;
|
||||
|
|
@ -264,28 +294,7 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize)
|
|||
let (note0, note1) = s.note_axis;
|
||||
let bw = Style::default().dim().on_black();
|
||||
let bg = Style::default().on_black();
|
||||
for key in note0..note1 {
|
||||
let x = x + 5 + key - note0;
|
||||
if key % 12 == 0 {
|
||||
let octave = format!("C{}", (key / 12) as i8 - 4);
|
||||
buf.set_string(x, y, &octave, Style::default());
|
||||
}
|
||||
let mut color = KEY_HORIZONTAL_STYLE[key as usize % 12];
|
||||
let mut is_on = s.notes_on[key as usize];
|
||||
let step = beat % s.steps;
|
||||
let (a, b, c) = (
|
||||
(step + 0) as u32 * ppq / s.resolution as u32,
|
||||
(step + 1) as u32 * ppq / s.resolution as u32,
|
||||
(step + 2) as u32 * ppq / s.resolution as u32,
|
||||
);
|
||||
let key = ::midly::num::u7::from(key as u8);
|
||||
is_on = is_on || contains_note_on(&s.sequence, key, a, b);
|
||||
is_on = is_on || contains_note_on(&s.sequence, key, b, c);
|
||||
if is_on {
|
||||
color = Style::default().red();
|
||||
}
|
||||
buf.set_string(x, y - 1, &format!("▄"), color);
|
||||
}
|
||||
draw_keys_vertical(s, buf, area, beat);
|
||||
for step in time0..time1 {
|
||||
let y = y - time0 + step / 2;
|
||||
let step = step as usize;
|
||||
|
|
@ -302,8 +311,8 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize)
|
|||
(step + 2) as u32 * ppq / s.resolution as u32,
|
||||
);
|
||||
let (character, style) = match (
|
||||
contains_note_on(&s.sequence, key, a, b),
|
||||
contains_note_on(&s.sequence, key, b, c),
|
||||
contains_note_on(&s.sequences[s.sequence], key, a, b),
|
||||
contains_note_on(&s.sequences[s.sequence], key, b, c),
|
||||
) {
|
||||
(true, true) => ("█", bg),
|
||||
(true, false) => ("▀", bg),
|
||||
|
|
@ -319,7 +328,7 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize)
|
|||
let _color = if s.notes_on[key as usize] {
|
||||
Style::default().red()
|
||||
} else {
|
||||
KEY_HORIZONTAL_STYLE[key as usize % 12]
|
||||
KEY_STYLE[key as usize % 12]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -341,7 +350,7 @@ fn draw_vertical (s: &Sequencer, buf: &mut Buffer, mut area: Rect, beat: usize)
|
|||
if s.time_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||
Style::default()
|
||||
);
|
||||
Ok(Rect { x, y, width: area.width, height })
|
||||
Ok(Rect { x, y, width: area.width, height: height + 3 })
|
||||
}
|
||||
|
||||
fn contains_note_on (sequence: &Sequence, k: ::midly::num::u7, start: u32, end: u32) -> bool {
|
||||
|
|
@ -377,16 +386,15 @@ const KEY_BLACK: Style = Style {
|
|||
sub_modifier: ::ratatui::style::Modifier::empty(),
|
||||
};
|
||||
|
||||
const KEY_HORIZONTAL_STYLE: [Style;12] = [
|
||||
const KEY_STYLE: [Style;12] = [
|
||||
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
||||
KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE, KEY_BLACK, KEY_WHITE,
|
||||
];
|
||||
|
||||
fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
area.x = area.x + 13;
|
||||
fn draw_keys_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
let Rect { x, y, .. } = area;
|
||||
let (time0, time1) = s.time_axis;
|
||||
let (note0, note1) = s.note_axis;
|
||||
let (time0, time1) = s.time_axis;
|
||||
let bw = Style::default().dim();
|
||||
let bg = Style::default().on_black();
|
||||
for i in 0..32.max(note1-note0)/2 {
|
||||
|
|
@ -396,13 +404,24 @@ fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<
|
|||
buf.set_string(x + 6, y, &"·".repeat((time1 - time0) as usize), bg.dim());
|
||||
if i % 6 == 0 {
|
||||
let octave = format!("C{}", ((note1 - i) / 6) as i8 - 4);
|
||||
buf.set_string(x+5, y, &octave, Style::default());
|
||||
buf.set_string(x + 4, y, &octave, Style::default());
|
||||
}
|
||||
}
|
||||
Ok(area)
|
||||
}
|
||||
|
||||
fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<Rect> {
|
||||
area.x = area.x + 13;
|
||||
let Rect { x, y, .. } = area;
|
||||
let (time0, time1) = s.time_axis;
|
||||
let (note0, note1) = s.note_axis;
|
||||
let bw = Style::default().dim();
|
||||
let bg = Style::default().on_black();
|
||||
draw_keys_horizontal(s, buf, area)?;
|
||||
for step in time0..time1 {
|
||||
let time_start = step as u32 * s.timebase.ppq() as u32;
|
||||
let time_end = (step + 1) as u32 * s.timebase.ppq() as u32;
|
||||
for (_, (_, events)) in s.sequence.range(time_start..time_end).enumerate() {
|
||||
for (_, (_, events)) in s.sequences[s.sequence].range(time_start..time_end).enumerate() {
|
||||
if events.len() > 0 {
|
||||
buf.set_string(x + 5 + step as u16, y, "█", bw);
|
||||
}
|
||||
|
|
@ -420,7 +439,7 @@ fn draw_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually<
|
|||
s.time_axis.1,
|
||||
), Style::default().dim());
|
||||
buf.set_string(
|
||||
x + 5 + s.time_cursor,
|
||||
x + 6 + s.time_cursor,
|
||||
y + s.note_cursor / 2,
|
||||
if s.note_cursor % 2 == 0 { "▀" } else { "▄" },
|
||||
Style::default()
|
||||
|
|
@ -460,9 +479,24 @@ pub const KEYMAP: &'static [KeyBinding<Sequencer>] = keymap!(Sequencer {
|
|||
[Char('n'), NONE, "note_axis", "Focus note axis", nop],
|
||||
[Char('t'), NONE, "time_axis", "Focus time axis", nop],
|
||||
[Char('v'), NONE, "variations", "Focus variation selector", nop],
|
||||
[Char('s'), SHIFT, "sync", "Focus sync selector", nop]
|
||||
[Char('s'), SHIFT, "sync", "Focus sync selector", nop],
|
||||
[Char('1'), NONE, "focus_1", "Sequence 1", focus_seq(0)],
|
||||
[Char('2'), NONE, "focus_1", "Sequence 1", focus_seq(1)],
|
||||
[Char('3'), NONE, "focus_1", "Sequence 1", focus_seq(2)],
|
||||
[Char('4'), NONE, "focus_1", "Sequence 1", focus_seq(3)],
|
||||
[Char('5'), NONE, "focus_1", "Sequence 1", focus_seq(4)],
|
||||
[Char('6'), NONE, "focus_1", "Sequence 1", focus_seq(5)],
|
||||
[Char('7'), NONE, "focus_1", "Sequence 1", focus_seq(6)],
|
||||
[Char('8'), NONE, "focus_1", "Sequence 1", focus_seq(7)],
|
||||
});
|
||||
|
||||
const fn focus_seq (i: usize) -> impl Fn(&mut Sequencer)->Usually<bool> {
|
||||
move |s: &mut Sequencer| {
|
||||
s.sequence = i;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn nop (_: &mut Sequencer) -> Usually<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
|
@ -475,15 +509,16 @@ fn note_add (s: &mut Sequencer) -> Usually<bool> {
|
|||
let key = ::midly::num::u7::from_int_lossy((s.note_cursor + s.note_axis.0) as u8);
|
||||
let note_on = ::midly::MidiMessage::NoteOn { key, vel: 100.into() };
|
||||
let note_off = ::midly::MidiMessage::NoteOff { key, vel: 100.into() };
|
||||
if s.sequence.contains_key(&start) {
|
||||
s.sequence.get_mut(&start).unwrap().push(note_on.clone());
|
||||
let mut sequence = &mut s.sequences[s.sequence];
|
||||
if sequence.contains_key(&start) {
|
||||
sequence.get_mut(&start).unwrap().push(note_on.clone());
|
||||
} else {
|
||||
s.sequence.insert(start, vec![note_on]);
|
||||
sequence.insert(start, vec![note_on]);
|
||||
}
|
||||
if s.sequence.contains_key(&end) {
|
||||
s.sequence.get_mut(&end).unwrap().push(note_off.clone());
|
||||
if sequence.contains_key(&end) {
|
||||
sequence.get_mut(&end).unwrap().push(note_off.clone());
|
||||
} else {
|
||||
s.sequence.insert(end, vec![note_off]);
|
||||
sequence.insert(end, vec![note_off]);
|
||||
};
|
||||
Ok(true)
|
||||
}
|
||||
|
|
|
|||
48
src/draw.rs
48
src/draw.rs
|
|
@ -38,54 +38,6 @@ pub fn draw_leaf (buffer: &mut Buffer, area: Rect, y: u16, x: u16, text: &str) {
|
|||
buffer.set_string(area.x + x, area.y + 1 + y, bottom, border);
|
||||
}
|
||||
|
||||
pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
let border = Style::default().gray().dim();
|
||||
let top = format!("╭{}╮", "─".repeat((area.width - 2).into()));
|
||||
let bottom = format!("╰{}╯", "─".repeat((area.width - 2).into()));
|
||||
buffer.set_string(area.x, area.y, top, border);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
buffer.set_string(area.x, y, format!("│"), border);
|
||||
buffer.set_string(area.x + area.width - 1, y, format!("│"), border);
|
||||
}
|
||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, border);
|
||||
area
|
||||
}
|
||||
|
||||
pub fn draw_box_styled (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
let style = style.unwrap_or(Style::default());
|
||||
let top = format!("╭{}╮", "─".repeat((area.width - 2).into()));
|
||||
let bottom = format!("╰{}╯", "─".repeat((area.width - 2).into()));
|
||||
buffer.set_string(area.x, area.y, top, style);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
buffer.set_string(area.x, y, format!("┇"), style);
|
||||
buffer.set_string(area.x + area.width - 1, y, format!("┇"), style);
|
||||
}
|
||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
||||
area
|
||||
}
|
||||
|
||||
pub fn draw_box_styled_dotted (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
let style = style.unwrap_or(Style::default());
|
||||
let top = format!("╭{}╮", "┅".repeat((area.width - 2).into()));
|
||||
let bottom = format!("╰{}╯", "┅".repeat((area.width - 2).into()));
|
||||
buffer.set_string(area.x, area.y, top, style);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
buffer.set_string(area.x, y, format!("┇"), style);
|
||||
buffer.set_string(area.x + area.width - 1, y, format!("┇"), style);
|
||||
}
|
||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
||||
area
|
||||
}
|
||||
|
||||
pub fn draw_corners (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
let style = style.unwrap_or(Style::default());
|
||||
buffer.set_string(area.x, area.y, "╭", style);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,53 @@
|
|||
use crate::prelude::*;
|
||||
|
||||
pub fn draw_box (buffer: &mut Buffer, area: Rect) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
let border = Style::default().gray().dim();
|
||||
let top = format!("╭{}╮", "─".repeat((area.width - 2).into()));
|
||||
let bottom = format!("╰{}╯", "─".repeat((area.width - 2).into()));
|
||||
buffer.set_string(area.x, area.y, top, border);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
buffer.set_string(area.x, y, format!("│"), border);
|
||||
buffer.set_string(area.x + area.width - 1, y, format!("│"), border);
|
||||
}
|
||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, border);
|
||||
area
|
||||
}
|
||||
|
||||
pub fn draw_box_styled (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
let style = style.unwrap_or(Style::default());
|
||||
let top = format!("╭{}╮", "─".repeat((area.width - 2).into()));
|
||||
let bottom = format!("╰{}╯", "─".repeat((area.width - 2).into()));
|
||||
buffer.set_string(area.x, area.y, top, style);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
buffer.set_string(area.x, y, format!("┇"), style);
|
||||
buffer.set_string(area.x + area.width - 1, y, format!("┇"), style);
|
||||
}
|
||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
||||
area
|
||||
}
|
||||
|
||||
pub fn draw_box_styled_dotted (buffer: &mut Buffer, area: Rect, style: Option<Style>) -> Rect {
|
||||
if area.width < 1 || area.height < 1 {
|
||||
return area
|
||||
}
|
||||
let style = style.unwrap_or(Style::default());
|
||||
let top = format!("╭{}╮", "┅".repeat((area.width - 2).into()));
|
||||
let bottom = format!("╰{}╯", "┅".repeat((area.width - 2).into()));
|
||||
buffer.set_string(area.x, area.y, top, style);
|
||||
for y in (area.y + 1)..(area.y + area.height - 1) {
|
||||
buffer.set_string(area.x, y, format!("┇"), style);
|
||||
buffer.set_string(area.x + area.width - 1, y, format!("┇"), style);
|
||||
}
|
||||
buffer.set_string(area.x, area.y + area.height - 1, bottom, style);
|
||||
area
|
||||
}
|
||||
|
||||
pub struct Column(pub Vec<Box<dyn Device>>);
|
||||
|
||||
impl Column {
|
||||
|
|
@ -18,24 +66,25 @@ impl Row {
|
|||
|
||||
impl Device for Column {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
Ok(render_column(&self.0, buf, area)?.0)
|
||||
Ok(draw_column(&self.0, buf, area, 0)?.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Row {
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
Ok(render_row(&self.0, buf, area)?.0)
|
||||
Ok(draw_row(&self.0, buf, area, 0)?.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_column (items: &[Box<dyn Device>], buf: &mut Buffer, area: Rect)
|
||||
pub fn draw_column (items: &[Box<dyn Device>], buf: &mut Buffer, area: Rect, space: i16)
|
||||
-> Usually<(Rect, Vec<Rect>)>
|
||||
{
|
||||
let mut w = 0u16;
|
||||
let mut h = 0u16;
|
||||
let mut rects = vec![];
|
||||
for (i, device) in items.iter().enumerate() {
|
||||
let rect = Rect { x: area.x, y: area.y + h, width: area.width, height: area.height - h };
|
||||
let y = ((area.y + h) as i16).saturating_add(space) as u16;
|
||||
let rect = Rect { x: area.x, y, width: area.width, height: area.height - h };
|
||||
let result = device.render(buf, rect)?;
|
||||
rects.push(result);
|
||||
w = w.max(result.width);
|
||||
|
|
@ -44,14 +93,15 @@ pub fn render_column (items: &[Box<dyn Device>], buf: &mut Buffer, area: Rect)
|
|||
Ok((Rect { x: area.x, y: area.y, width: w, height: h }, rects))
|
||||
}
|
||||
|
||||
pub fn render_row (items: &[Box<dyn Device>], buf: &mut Buffer, area: Rect)
|
||||
pub fn draw_row (items: &[Box<dyn Device>], buf: &mut Buffer, area: Rect, space: i16)
|
||||
-> Usually<(Rect, Vec<Rect>)>
|
||||
{
|
||||
let mut w = 0u16;
|
||||
let mut h = 0u16;
|
||||
let mut rects = vec![];
|
||||
for (i, device) in items.iter().enumerate() {
|
||||
let rect = Rect { x: area.x + w, y: area.y, width: area.width - w, height: area.height };
|
||||
let x = ((area.x + w) as i16).saturating_add(space) as u16;
|
||||
let rect = Rect { x, y: area.y, width: area.width - w, height: area.height };
|
||||
let result = device.render(buf, rect)?;
|
||||
rects.push(result);
|
||||
w = w + result.width;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ impl Device for FocusColumn {
|
|||
handle_focus(self, event, KEYMAP_FOCUS_COLUMN)
|
||||
}
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let (rect, rects) = super::render_column(self.1.0.as_ref(), buf, area, )?;
|
||||
let (rect, rects) = draw_column(self.1.0.as_ref(), buf, area, 0)?;
|
||||
//if i == self.focus {
|
||||
//if self.focused {
|
||||
//draw_box_styled(buf, result, Some(Style::default().white().not_dim()))
|
||||
|
|
@ -110,23 +110,25 @@ pub struct FocusRow(pub Option<usize>, pub Row);
|
|||
|
||||
impl Device for FocusRow {
|
||||
fn handle (&mut self, event: &AppEvent) -> Usually<bool> {
|
||||
handle_focus(self, event, keymap!(Self {
|
||||
[Left, NONE, "focus_up", "focus row above",
|
||||
|s: &mut Self|s.handle_focus(&FocusEvent::Backward)],
|
||||
[Right, NONE, "focus_down", "focus row below",
|
||||
|s: &mut Self|s.handle_focus(&FocusEvent::Forward)],
|
||||
[Enter, NONE, "focus_down", "focus row below",
|
||||
|s: &mut Self|s.handle_focus(&FocusEvent::Inward)],
|
||||
[Esc, NONE, "focus_down", "focus row below",
|
||||
|s: &mut Self|s.handle_focus(&FocusEvent::Outward)]
|
||||
}))
|
||||
handle_focus(self, event, KEYMAP_FOCUS_ROW)
|
||||
}
|
||||
fn render (&self, buf: &mut Buffer, area: Rect) -> Usually<Rect> {
|
||||
let (rect, rects) = super::render_row(&self.1.0, buf, area)?;
|
||||
let (rect, rects) = draw_row(&self.1.0, buf, area, 0)?;
|
||||
Ok(rect)
|
||||
}
|
||||
}
|
||||
|
||||
const KEYMAP_FOCUS_ROW: &'static [KeyBinding<FocusRow>] = keymap!(FocusRow {
|
||||
[Left, NONE, "focus_up", "focus row above",
|
||||
|s: &mut FocusRow|s.handle_focus(&FocusEvent::Backward)],
|
||||
[Right, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusRow|s.handle_focus(&FocusEvent::Forward)],
|
||||
[Enter, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusRow|s.handle_focus(&FocusEvent::Inward)],
|
||||
[Esc, NONE, "focus_down", "focus row below",
|
||||
|s: &mut FocusRow|s.handle_focus(&FocusEvent::Outward)]
|
||||
});
|
||||
|
||||
impl Focus for FocusRow {
|
||||
fn unfocus (&mut self) {
|
||||
self.0 = None
|
||||
|
|
|
|||
24
src/main.rs
24
src/main.rs
|
|
@ -20,26 +20,10 @@ fn main () -> Result<(), Box<dyn Error>> {
|
|||
let _cli = cli::Cli::parse();
|
||||
let xdg = microxdg::XdgApp::new("dawdle")?;
|
||||
crate::config::create_dirs(&xdg)?;
|
||||
//crate::device::run(Sequencer::new("Rhythm#000")?)
|
||||
let transport = Transport::new("Transport")?;
|
||||
let timebase = transport.state.lock().unwrap().timebase();
|
||||
crate::device::run(FocusColumn(Some(0), Column::new(vec![
|
||||
Box::new(transport) as Box<dyn Device>,
|
||||
Box::new(Chain::new("Chain#0000", vec![
|
||||
Box::new(Sequencer::new("Melody#000", &timebase)?),
|
||||
Box::new(Plugin::new("Plugin#000")?),
|
||||
])?),
|
||||
//Box::new(Columns::new(false, vec![
|
||||
//Box::new(Chain::new("Chain#00", vec![
|
||||
//Box::new(Sequencer::new("Rhythm#000")?),
|
||||
//Box::new(Sampler::new("Sampler#00")?),
|
||||
//])?),
|
||||
//Box::new(Chain::new("Chain#01", vec![
|
||||
//Box::new(Sequencer::new("Melody#000")?),
|
||||
//Box::new(Plugin::new("Plugin#000")?),
|
||||
//])?),
|
||||
//])),
|
||||
//Box::new(Mixer::new("Mixer#000")?),
|
||||
//Box::new(Sequencer::new("Rhythm#000")?),
|
||||
])))
|
||||
crate::device::run(Chain::new("Chain#0000", vec![
|
||||
Box::new(Sequencer::new("Melody#000", &timebase)?),
|
||||
Box::new(Plugin::new("Plugin#000")?),
|
||||
])?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@ pub use crate::layout::{
|
|||
FocusColumn,
|
||||
Focus,
|
||||
FocusEvent,
|
||||
handle_focus
|
||||
handle_focus,
|
||||
draw_box,
|
||||
draw_box_styled,
|
||||
draw_row,
|
||||
draw_column,
|
||||
};
|
||||
|
||||
pub use std::error::Error;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue