diff --git a/crates/tek_core/src/space.rs b/crates/tek_core/src/space.rs index 3dd60a40..6e94f5be 100644 --- a/crates/tek_core/src/space.rs +++ b/crates/tek_core/src/space.rs @@ -1,5 +1,16 @@ use crate::*; +pub struct FixedAxis { + pub start: T, + pub point: Option, + pub clamp: Option, +} +pub struct ScaledAxis { + pub start: T, + pub scale: T, + pub point: Option, + pub clamp: Option, +} macro_rules! impl_axis_common { ($A:ident $T:ty) => { impl $A<$T> { #[inline] pub fn start_inc (&mut self, n: $T) -> $T { @@ -20,23 +31,8 @@ macro_rules! impl_axis_common { ($A:ident $T:ty) => { } } } } - -pub struct FixedAxis { - pub start: T, - pub point: Option, - pub clamp: Option, -} - impl_axis_common!(FixedAxis u16); impl_axis_common!(FixedAxis usize); - -pub struct ScaledAxis { - pub start: T, - pub scale: T, - pub point: Option, - pub clamp: Option, -} - impl_axis_common!(ScaledAxis u16); impl_axis_common!(ScaledAxis usize); diff --git a/crates/tek_sequencer/src/sequencer.rs b/crates/tek_sequencer/src/sequencer.rs index 32170b49..8cd895b3 100644 --- a/crates/tek_sequencer/src/sequencer.rs +++ b/crates/tek_sequencer/src/sequencer.rs @@ -88,9 +88,9 @@ pub struct PhraseEditor { /// The full piano roll is rendered to this buffer pub buffer: BigBuffer, /// Cursor/scroll/zoom in pitch axis - pub note_axis: FixedAxis, + pub note_axis: RwLock>, /// Cursor/scroll/zoom in time axis - pub time_axis: ScaledAxis, + pub time_axis: RwLock>, /// Whether this widget is focused pub focused: bool, /// Whether note enter mode is enabled @@ -235,14 +235,23 @@ impl PhraseEditor { notes_out: Arc::new(RwLock::new([false;128])), keys: keys_vert(), buffer: Default::default(), - note_axis: FixedAxis { start: 12, point: Some(36), clamp: Some(127) }, - time_axis: ScaledAxis { start: 00, point: Some(00), clamp: Some(000), scale: 24 }, focused: false, entered: false, mode: false, now: Arc::new(0.into()), width: 0.into(), height: 0.into(), + note_axis: RwLock::new(FixedAxis { + start: 12, + point: Some(36), + clamp: Some(127) + }), + time_axis: RwLock::new(ScaledAxis { + start: 00, + point: Some(00), + clamp: Some(000), + scale: 24 + }), } } } diff --git a/crates/tek_sequencer/src/sequencer_cmd.rs b/crates/tek_sequencer/src/sequencer_cmd.rs index 82e186f5..738c1be9 100644 --- a/crates/tek_sequencer/src/sequencer_cmd.rs +++ b/crates/tek_sequencer/src/sequencer_cmd.rs @@ -89,35 +89,45 @@ impl Handle for PhraseEditor { key!(KeyCode::Char('`')) => { self.mode = !self.mode; }, key!(KeyCode::Enter) => { self.entered = true; }, key!(KeyCode::Esc) => { self.entered = false; }, - key!(KeyCode::PageUp) => { self.note_axis.start_dec(3); }, - key!(KeyCode::PageDown) => { self.note_axis.start_inc(3); }, + key!(KeyCode::PageUp) => { self.note_axis.write().unwrap().start_dec(3); }, + key!(KeyCode::PageDown) => { self.note_axis.write().unwrap().start_inc(3); }, key!(KeyCode::Up) => match self.entered { - true => { self.note_axis.point_dec(1); }, - false => { self.note_axis.start_dec(1); }, + true => { self.note_axis.write().unwrap().point_dec(1); }, + false => { self.note_axis.write().unwrap().start_dec(1); }, }, key!(KeyCode::Down) => match self.entered { - true => { self.note_axis.point_inc(1); }, - false => { self.note_axis.start_inc(1); }, + true => { self.note_axis.write().unwrap().point_inc(1); }, + false => { self.note_axis.write().unwrap().start_inc(1); }, }, - key!(KeyCode::Left) => match self.entered { - true => { self.time_axis.point_dec(self.time_axis.scale); }, - false => { self.time_axis.start_dec(self.time_axis.scale); }, + key!(KeyCode::Left) => { + let scale = self.time_axis.read().unwrap().scale; + match self.entered { + true => { self.time_axis.write().unwrap().point_dec(scale); }, + false => { self.time_axis.write().unwrap().start_dec(scale); }, + }; }, - key!(KeyCode::Right) => match self.entered { - true => { self.time_axis.point_inc(self.time_axis.scale); }, - false => { self.time_axis.start_inc(self.time_axis.scale); }, + key!(KeyCode::Right) => { + let scale = self.time_axis.read().unwrap().scale; + match self.entered { + true => { self.time_axis.write().unwrap().point_inc(scale); }, + false => { self.time_axis.write().unwrap().start_inc(scale); }, + } }, key!(KeyCode::Char('-')) => { - self.time_axis.scale = next_note_length(self.time_axis.scale) + let scale = self.time_axis.read().unwrap().scale; + self.time_axis.write().unwrap().scale = next_note_length(scale) }, key!(KeyCode::Char('_')) => { - self.time_axis.scale = next_note_length(self.time_axis.scale) + let scale = self.time_axis.read().unwrap().scale; + self.time_axis.write().unwrap().scale = next_note_length(scale) }, key!(KeyCode::Char('=')) => { - self.time_axis.scale = prev_note_length(self.time_axis.scale) + let scale = self.time_axis.read().unwrap().scale; + self.time_axis.write().unwrap().scale = prev_note_length(scale) }, key!(KeyCode::Char('+')) => { - self.time_axis.scale = prev_note_length(self.time_axis.scale) + let scale = self.time_axis.read().unwrap().scale; + self.time_axis.write().unwrap().scale = prev_note_length(scale) }, key!(KeyCode::Char('[')) => if self.entered { self.note_len = prev_note_length(self.note_len) @@ -127,8 +137,10 @@ impl Handle for PhraseEditor { }, key!(KeyCode::Char('a')) => if self.entered { self.put(); - self.time_axis.point = self.time_axis.point.map(|time|(time + self.note_len) - % self.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1)); + let point = self.time_axis.read().unwrap().point; + let length = self.phrase.as_ref().map(|p|p.read().unwrap().length).unwrap_or(1); + let forward = |time|(time + self.note_len) % length; + self.time_axis.write().unwrap().point = point.map(forward); }, key!(KeyCode::Char('s')) => if self.entered { self.put(); diff --git a/crates/tek_sequencer/src/sequencer_tui.rs b/crates/tek_sequencer/src/sequencer_tui.rs index 0b7cf30e..a18f120a 100644 --- a/crates/tek_sequencer/src/sequencer_tui.rs +++ b/crates/tek_sequencer/src/sequencer_tui.rs @@ -52,12 +52,18 @@ impl Content for PhraseEditor { let Self { focused, entered, time_axis, note_axis, keys, phrase, buffer, note_len, .. } = self; + let FixedAxis { + start: note_start, point: note_point, clamp: note_clamp + } = *self.note_axis.read().unwrap(); + let ScaledAxis { + start: time_start, point: time_point, clamp: time_clamp, scale: time_scale + } = *self.time_axis.read().unwrap(); let color = Color::Rgb(0,255,0); let color = phrase.as_ref().map(|p|p.read().unwrap().color).unwrap_or(color); let keys = CustomWidget::new(|to:[u16;2]|Ok(Some(to.clip_w(5))), move|to: &mut TuiOutput|{ if to.area().h() >= 2 { to.buffer_update(to.area().set_w(5), &|cell, x, y|{ - let y = y + note_axis.start as u16; + let y = y + note_start as u16; if x < keys.area.width && y < keys.area.height { *cell = keys.get(x, y).clone() } @@ -73,8 +79,8 @@ impl Content for PhraseEditor { let area = to.area(); to.buffer_update(area, &move |cell, x, y|{ cell.set_bg(notes_bg_null); - let src_x = (x as usize + time_axis.start) * time_axis.scale; - let src_y = y as usize + note_axis.start; + let src_x = (x as usize + time_start) * time_scale; + let src_y = y as usize + note_start; if src_x < buffer.width && src_y < buffer.height - 1 { buffer.get(src_x, buffer.height - src_y).map(|src|{ cell.set_symbol(src.symbol()); @@ -89,9 +95,9 @@ impl Content for PhraseEditor { let cursor = CustomWidget::new(|to|Ok(Some(to)), move|to: &mut TuiOutput|{ if *entered { let area = to.area(); - if let (Some(time), Some(note)) = (time_axis.point, note_axis.point) { - let x1 = area.x() + (time / time_axis.scale) as u16; - let x2 = x1 + (self.note_len / time_axis.scale) as u16; + if let (Some(time), Some(note)) = (time_point, note_point) { + let x1 = area.x() + (time / time_scale) as u16; + let x2 = x1 + (self.note_len / time_scale) as u16; let y = area.y() + 1 + note as u16 / 2; let c = if note % 2 == 0 { "▀" } else { "▄" }; for x in x1..x2 { @@ -108,11 +114,11 @@ impl Content for PhraseEditor { move|to: &mut TuiOutput|{ if let Some(_) = phrase { let now = self.now.load(Ordering::Relaxed); // TODO FIXME: self.now % phrase.read().unwrap().length; - let ScaledAxis { start: first_beat, scale: time_zoom, clamp, .. } = time_axis; - let clamp = clamp.expect("time_axis of sequencer expected to be clamped"); - for x in 0..clamp/time_zoom { - let this_step = (x * time_zoom + first_beat + 0) * time_zoom; - let next_step = (x * time_zoom + first_beat + 1) * time_zoom; + let time_clamp = time_clamp + .expect("time_axis of sequencer expected to be clamped"); + for x in 0..time_clamp/time_scale { + let this_step = (x * time_scale + time_start + 0) * time_scale; + let next_step = (x * time_scale + time_start + 1) * time_scale; let x = to.area().x() + x as u16; let active = this_step <= now && now < next_step; let character = if active { "|" } else { "·" }; @@ -136,18 +142,18 @@ impl Content for PhraseEditor { upper_left = format!("{upper_left}: {}", phrase.read().unwrap().name); } let mut upper_right = format!("Zoom: {} (+{}:{}*{}|{})", - ppq_to_name(time_axis.scale), - time_axis.start, - time_axis.point.unwrap_or(0), - time_axis.scale, - time_axis.clamp.unwrap_or(0), + ppq_to_name(time_scale), + time_start, + time_point.unwrap_or(0), + time_scale, + time_clamp.unwrap_or(0), ); if *focused && *entered { upper_right = format!("Note: {} (+{}:{}|{}) {upper_right}", ppq_to_name(*note_len), - note_axis.start, - note_axis.point.unwrap_or(0), - note_axis.clamp.unwrap_or(0), + note_start, + note_point.unwrap_or(0), + note_clamp.unwrap_or(0), ); } lay!( @@ -161,8 +167,8 @@ impl PhraseEditor { pub fn put (&mut self) { if let (Some(phrase), Some(time), Some(note)) = ( &self.phrase, - self.time_axis.point, - self.note_axis.point, + self.time_axis.read().unwrap().point, + self.note_axis.read().unwrap().point, ) { let mut phrase = phrase.write().unwrap(); let key: u7 = u7::from((127 - note) as u8); @@ -178,11 +184,11 @@ impl PhraseEditor { pub fn show (&mut self, phrase: Option<&Arc>>) { if let Some(phrase) = phrase { self.phrase = Some(phrase.clone()); - self.time_axis.clamp = Some(phrase.read().unwrap().length); + self.time_axis.write().unwrap().clamp = Some(phrase.read().unwrap().length); self.buffer = Self::redraw(&*phrase.read().unwrap()); } else { self.phrase = None; - self.time_axis.clamp = Some(0); + self.time_axis.write().unwrap().clamp = Some(0); self.buffer = Default::default(); } }