From 185c6b5b34074cf5356d09317d8559b0f8cbc5be Mon Sep 17 00:00:00 2001 From: unspeaker Date: Fri, 21 Jun 2024 12:57:46 +0300 Subject: [PATCH] switch between sequences; layout unfucking --- src/device/chain.rs | 99 +++++++++++++-------- src/device/sequencer.rs | 189 ++++++++++++++++++++++++---------------- src/draw.rs | 48 ---------- src/layout/container.rs | 62 +++++++++++-- src/layout/focus.rs | 26 +++--- src/main.rs | 24 +---- src/prelude.rs | 6 +- 7 files changed, 253 insertions(+), 201 deletions(-) diff --git a/src/device/chain.rs b/src/device/chain.rs index 33b2c9f5..e8eea1de 100644 --- a/src/device/chain.rs +++ b/src/device/chain.rs @@ -5,6 +5,14 @@ pub struct Chain { focused: bool, focus: usize, items: Vec>, + 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 { 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 { diff --git a/src/device/sequencer.rs b/src/device/sequencer.rs index 770b8e1a..db6ae117 100644 --- a/src/device/sequencer.rs +++ b/src/device/sequencer.rs @@ -24,8 +24,10 @@ pub struct Sequencer { notes_on: Vec, /// Write input to sequence. recording: bool, + /// Sequence selector + sequence: usize, /// Map: tick -> MIDI events at tick - sequence: Sequence, + sequences: Vec, /// 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) -> Usually> { 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 { 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 { 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 { - area.x = area.x + 13; +fn draw_keys_horizontal (s: &Sequencer, buf: &mut Buffer, mut area: Rect) -> Usually { 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 { + 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] = 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 { + move |s: &mut Sequencer| { + s.sequence = i; + Ok(true) + } +} + fn nop (_: &mut Sequencer) -> Usually { Ok(false) } @@ -475,15 +509,16 @@ fn note_add (s: &mut Sequencer) -> Usually { 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) } diff --git a/src/draw.rs b/src/draw.rs index c9d8594f..f639c046 100644 --- a/src/draw.rs +++ b/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