From a4b95dbd447733c5a54d3df6cb798778e36adf94 Mon Sep 17 00:00:00 2001 From: Huck Boles Date: Thu, 1 Jun 2023 21:04:29 -0500 Subject: [PATCH] added: mask map rendering --- Cargo.lock | 101 +++++++++++++++++++++++++++++++-------- src/cells.rs | 16 +++---- src/graphics.rs | 78 ++++++++++++++++++------------ src/graphics/actions.rs | 74 ++++++++++++++++++++-------- src/graphics/area.rs | 14 +++--- src/graphics/keys.rs | 43 +++++------------ src/graphics/layout.rs | 8 ++-- src/graphics/map.rs | 56 +++++++++++++++------- src/graphics/point.rs | 2 +- src/graphics/selector.rs | 37 ++++++++------ src/keys.rs | 100 -------------------------------------- src/lib.rs | 1 + src/main.rs | 4 +- src/music.rs | 32 ++++++++++++- src/music/scale.rs | 44 +++++++++-------- src/state.rs | 42 ++++++++-------- src/state/transport.rs | 52 ++++++++++++++++++++ src/transport.rs | 19 ++++++++ 18 files changed, 423 insertions(+), 300 deletions(-) delete mode 100644 src/keys.rs create mode 100644 src/state/transport.rs create mode 100644 src/transport.rs diff --git a/Cargo.lock b/Cargo.lock index 53878fe..4d18abd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,12 +100,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" [[package]] name = "matrixmultiply" @@ -119,14 +116,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.48.0", ] [[package]] @@ -172,9 +169,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" [[package]] name = "parking_lot" @@ -196,7 +193,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] [[package]] @@ -326,7 +323,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -335,13 +341,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -350,38 +371,80 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/src/cells.rs b/src/cells.rs index 57f14f0..36d3473 100644 --- a/src/cells.rs +++ b/src/cells.rs @@ -8,6 +8,8 @@ use ndarray::Array2; use rand::{thread_rng, Rng}; use std::{cell::Cell, ops::Deref}; +use super::*; + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum State { Dead, @@ -20,8 +22,8 @@ pub struct World { } impl World { - pub fn new(x: usize, y: usize) -> Self { - let map = Array2::from_elem((x, y), Cell::from(State::Dead)); + pub fn new(area: Area) -> Self { + let map = Array2::from_elem((area.width(), area.height()), Cell::from(State::Dead)); Self { map } } @@ -72,7 +74,7 @@ impl Map> for World { } } State::Alive => { - if count < 3 || count > 4 { + if !(3..=4).contains(&count) { cell.set(State::Dead); } } @@ -92,12 +94,8 @@ impl Map> for World { ('●', '◌') } - fn on_colors(&self) -> Colors { - Colors::new(Green, Black) - } - - fn off_colors(&self) -> Colors { - Colors::new(Grey, Black) + fn colors(&self) -> (Colors, Colors) { + (Colors::new(Green, Black), Colors::new(Grey, Black)) } fn styles(&self) -> (Attributes, Attributes) { diff --git a/src/graphics.rs b/src/graphics.rs index a886a02..b35f082 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -17,7 +17,8 @@ pub use selector::*; use super::*; use crossterm::{ - cursor::{Hide, MoveTo}, + cursor::MoveTo, + event::{poll, read, Event}, execute, queue, style::{Print, SetAttributes, SetColors}, terminal, @@ -25,6 +26,7 @@ use crossterm::{ use eyre::Result; use std::{ io::{stdout, Write}, + sync::{Arc, Mutex}, thread, time::Duration, }; @@ -34,34 +36,64 @@ pub fn render_loop(state: &mut GlobalState) -> Result<()> { state.layout.draw_outlines()?; + let world = Arc::new(Mutex::new(state.world.clone())); + let state_arc = Arc::new(Mutex::new(state)); + loop { - let tick = state.transport.tick; + let arc = Arc::clone(&state_arc); + let tick = arc.lock().unwrap().tick(); let timer = std::thread::spawn(move || thread::sleep(tick)); - if !state.transport.running { - timer.join().unwrap(); - continue; + let event = std::thread::spawn(move || { + if poll(tick).unwrap() { + match read().unwrap() { + Event::Key(key) => key_event(key), + _ => Action::None, + } + } else { + Action::None + } + }); + + let mut maps = std::thread::spawn(|| {}); + if arc.lock().unwrap().transport.running { + let map = world.clone(); + let mut mask = arc.lock().unwrap().mask.clone(); + let cell_area = arc.lock().unwrap().layout.cells; + let mask_area = arc.lock().unwrap().layout.mask; + + maps = std::thread::spawn(move || { + let mut map = map.lock().unwrap(); + map.update(); + let tmp = map.clone(); + draw_map(&tmp, &cell_area).unwrap(); + mask[0].update(); + let tmp = mask[0].clone(); + draw_map(&tmp, &mask_area).unwrap(); + }); } - state.world.update(); + let area = arc.lock().unwrap().layout.transport; - draw_map(&mut state.world, &state.layout.cells)?; - // draw_map(&mut state.mask[0], &state.layout.mask)?; + arc.lock().unwrap().transport.render(area)?; - timer.join().unwrap(); + action(event.join().unwrap(), arc.clone())?; + maps.join().unwrap(); + arc.lock().unwrap().cursor.render()?; + + timer.join().unwrap(); stdout().flush()?; } } -pub fn draw_map(map: &mut impl Map, area: &Area) -> Result<()> { +pub fn draw_map(map: &impl Map, area: &Area) -> Result<()> { let ((x_zero, y_zero), (x_max, y_max)) = area.to_u16()?; let origin = Point::new(x_zero.into(), y_zero.into()); let (char_on, char_off) = map.characters(); - let on_colors = map.on_colors(); - let off_colors = map.off_colors(); + let (on_colors, off_colors) = map.colors(); let (style_on, style_off) = map.styles(); for x in 0..=(map.x_size()) { @@ -69,14 +101,13 @@ pub fn draw_map(map: &mut impl Map, area: &Area) -> Result<()> { let point = Point::new(x, y); let (x_off, y_off) = origin.u16_offset(point)?; - if x_off <= x_zero || x_off >= x_max || y_off <= y_zero || y_off >= y_max { + if x_off <= x_zero || x_off >= x_max || y_off <= y_zero || y_off >= y_max - 1 { continue; } if map.try_point(point) { queue!( stdout(), - Hide, MoveTo(x_off, y_off), SetAttributes(style_on), SetColors(on_colors), @@ -85,7 +116,6 @@ pub fn draw_map(map: &mut impl Map, area: &Area) -> Result<()> { } else { queue!( stdout(), - Hide, MoveTo(x_off, y_off), SetAttributes(style_off), SetColors(off_colors), @@ -100,22 +130,8 @@ pub fn draw_map(map: &mut impl Map, area: &Area) -> Result<()> { Ok(()) } -pub fn run_map(map: &mut impl Map, area: &Area, time: Duration) -> Result<()> { - loop { - map.update(); - - execute!(stdout(), terminal::Clear(terminal::ClearType::All))?; - - draw_map(map, area)?; - - stdout().flush()?; - - thread::sleep(time); - } -} - pub fn loop_map( - map: &mut (impl Map + Clone), + map: &(impl Map + Clone), area: &Area, time: Duration, steps: usize, @@ -124,7 +140,7 @@ pub fn loop_map( let mut tmp = map.clone(); for _ in 0..steps { execute!(stdout(), terminal::Clear(terminal::ClearType::All))?; - draw_map(&mut tmp, area)?; + draw_map(&tmp, area)?; stdout().flush()?; tmp.update(); thread::sleep(time); diff --git a/src/graphics/actions.rs b/src/graphics/actions.rs index 8db825e..3c67dbb 100644 --- a/src/graphics/actions.rs +++ b/src/graphics/actions.rs @@ -1,14 +1,14 @@ use super::*; -use crossterm::event::{poll, read, Event}; #[derive(Debug)] pub enum Action { None, Move(Direction), - Transport(Clock), + Clock(Clock), Channel(usize), Select, SelectArea, + SwitchPanel, Reload, Randomize, Exit, @@ -33,24 +33,60 @@ pub enum Direction { Right, } -pub fn action_loop(speed: usize) -> Result<()> { - loop { - if poll(Duration::from_millis(speed.try_into()?))? { - if let Event::Key(key) = read()? { - match key_event(key) { - Action::None => continue, - Action::Exit => crate::exit()?, - Action::Move(_direction) => todo!(), - Action::Channel(_channel) => todo!(), - Action::Transport(_clock) => todo!(), - Action::Edit => todo!(), - Action::Select => todo!(), - Action::SelectArea => todo!(), - Action::Reload => todo!(), - Action::Randomize => todo!(), - Action::Help => todo!(), - } +type AMmGS<'a> = Arc>; + +pub fn action(action: Action, state: AMmGS) -> Result<()> { + match action { + Action::None => Ok(()), + Action::Exit => exit(), + Action::Move(direction) => move_cursor(direction, state), + Action::Clock(clock) => transport_action(clock, state), + Action::Channel(_) => Ok(()), + Action::Select => Ok(()), + Action::SelectArea => Ok(()), + Action::Reload => Ok(()), + Action::Randomize => Ok(()), + Action::Help => Ok(()), + Action::Edit => Ok(()), + Action::SwitchPanel => Ok(()), + } +} + +fn transport_action(clock: Clock, state: AMmGS) -> Result<()> { + let mut state = state.lock().unwrap(); + match clock { + Clock::Stop => state.transport.running = false, + Clock::Start => state.transport.running = true, + Clock::Pause => state.transport.running = !state.transport.running, + Clock::Faster(n) => state.transport.bpm += n, + Clock::Slower(n) => state.transport.bpm -= n, + }; + Ok(()) +} + +fn move_cursor(direction: Direction, state: AMmGS) -> Result<()> { + let mut state = state.lock().unwrap(); + match direction { + Direction::Up => { + if state.cursor.position.y > 1 { + state.cursor.position.y -= 1 + } + } + Direction::Down => { + if state.cursor.position.y < state.cursor.area.height() { + state.cursor.position.y += 1 + } + } + Direction::Left => { + if state.cursor.position.x > 1 { + state.cursor.position.x -= 1 + } + } + Direction::Right => { + if state.cursor.position.x < state.cursor.area.width() { + state.cursor.position.x += 1 } } } + Ok(()) } diff --git a/src/graphics/area.rs b/src/graphics/area.rs index d36e50b..b9b12f0 100644 --- a/src/graphics/area.rs +++ b/src/graphics/area.rs @@ -1,5 +1,5 @@ use crossterm::{ - cursor::{Hide, MoveTo}, + cursor::MoveTo, queue, style::{ Color::{Black, White}, @@ -10,7 +10,7 @@ use eyre::Result; use super::*; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Copy)] pub struct Area { pub origin: Point, pub max: Point, @@ -52,10 +52,9 @@ impl Area { let colors = Colors::new(White, Black); let ((x_zero, y_zero), (x_max, y_max)) = self.to_u16()?; - for y in y_zero + 1..y_max { + for y in y_zero + 1..y_max - 1 { queue!( stdout(), - Hide, MoveTo(x_zero, y), SetColors(colors), Print("┃"), @@ -68,11 +67,10 @@ impl Area { for x in x_zero + 1..x_max { queue!( stdout(), - Hide, MoveTo(x, y_zero), SetColors(colors), Print("━"), - MoveTo(x, y_max), + MoveTo(x, y_max - 1), SetColors(colors), Print("━") )?; @@ -81,8 +79,8 @@ impl Area { for ((x, y), c) in [ (x_zero, y_zero), (x_max, y_zero), - (x_zero, y_max), - (x_max, y_max), + (x_zero, y_max - 1), + (x_max, y_max - 1), ] .iter() .zip(['┏', '┓', '┗', '┛'].iter()) diff --git a/src/graphics/keys.rs b/src/graphics/keys.rs index 36b13e9..ee3f4c2 100644 --- a/src/graphics/keys.rs +++ b/src/graphics/keys.rs @@ -1,12 +1,12 @@ use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use super::*; +use crate::Clock; pub fn key_event(event: KeyEvent) -> Action { match event.modifiers { KeyModifiers::NONE => match_normal_key(event.code), KeyModifiers::CONTROL => match_ctl_key(event.code), - KeyModifiers::ALT => match_alt_key(event.code), KeyModifiers::SHIFT => match_shift_key(event.code), _ => Action::None, } @@ -14,8 +14,9 @@ pub fn key_event(event: KeyEvent) -> Action { pub fn match_normal_key(key: KeyCode) -> Action { match key { - KeyCode::Enter => Action::Transport(Clock::Pause), + KeyCode::Enter => Action::Clock(Clock::Pause), KeyCode::Esc => Action::Exit, + KeyCode::Tab => Action::SwitchPanel, KeyCode::Up => Action::Move(Direction::Up), KeyCode::Down => Action::Move(Direction::Down), KeyCode::Right => Action::Move(Direction::Right), @@ -23,26 +24,16 @@ pub fn match_normal_key(key: KeyCode) -> Action { KeyCode::Char(c) => match c { '?' => Action::Help, ' ' => Action::Select, + '[' => Action::Clock(Clock::Slower(1)), + ']' => Action::Clock(Clock::Faster(1)), + '{' => Action::Clock(Clock::Slower(5)), + '}' => Action::Clock(Clock::Faster(5)), 'q' => Action::Exit, + 'r' => Action::Reload, 'j' => Action::Move(Direction::Down), 'k' => Action::Move(Direction::Up), 'h' => Action::Move(Direction::Left), 'l' => Action::Move(Direction::Right), - 'r' => Action::Reload, - '[' => Action::Transport(Clock::Slower(1)), - ']' => Action::Transport(Clock::Faster(1)), - '{' => Action::Transport(Clock::Slower(5)), - '}' => Action::Transport(Clock::Faster(5)), - '0' => Action::Channel(0), - '1' => Action::Channel(1), - '2' => Action::Channel(2), - '3' => Action::Channel(3), - '4' => Action::Channel(4), - '5' => Action::Channel(5), - '6' => Action::Channel(6), - '7' => Action::Channel(7), - '8' => Action::Channel(8), - '9' => Action::Channel(9), _ => Action::None, }, _ => Action::None, @@ -51,27 +42,17 @@ pub fn match_normal_key(key: KeyCode) -> Action { pub fn match_ctl_key(key: KeyCode) -> Action { match key { - KeyCode::Char(c) => match c { - 'c' => Action::Exit, - _ => Action::None, - }, + KeyCode::Char('c') => Action::Exit, _ => Action::None, } } pub fn match_shift_key(key: KeyCode) -> Action { match key { + KeyCode::Enter => Action::Clock(Clock::Stop), KeyCode::Char(c) => match c { - 'R' => Action::Randomize, - _ => Action::None, - }, - _ => Action::None, - } -} - -pub fn match_alt_key(key: KeyCode) -> Action { - match key { - KeyCode::Char(c) => match c { + ' ' => Action::SelectArea, + 'r' => Action::Randomize, _ => Action::None, }, _ => Action::None, diff --git a/src/graphics/layout.rs b/src/graphics/layout.rs index 014c4ba..2f5fc7d 100644 --- a/src/graphics/layout.rs +++ b/src/graphics/layout.rs @@ -20,10 +20,10 @@ impl Layout { let row: usize = row.into(); let screen = Area::new(0, 0, col, row); - let cells = Area::new(1, 1, col / 2 - 5, row / 2 - 2); - let mask = Area::new(col / 2 + 5, 1, col - 2, row / 2 - 2); - let channels = Area::new(1, row / 2 + 2, col - 2, row - 2); - let transport = Area::new(col / 2 - 4, 4, col / 2 + 4, 8); + let cells = Area::new(1, 0, col / 2 - 6, row / 2 - 1); + let mask = Area::new(col / 2 + 6, 0, col - 2, row / 2 - 1); + let channels = Area::new(1, row / 2 + 1, col - 2, row - 1); + let transport = Area::new(col / 2 - 5, 4, col / 2 + 5, 9); Ok(Self { screen, diff --git a/src/graphics/map.rs b/src/graphics/map.rs index 79d27cf..c73646b 100644 --- a/src/graphics/map.rs +++ b/src/graphics/map.rs @@ -1,11 +1,12 @@ use crate::Point; use crossterm::style::{ Attribute, Attributes, - Color::{Black, Grey, White}, + Color::{Black, White}, Colors, }; use ndarray::Array2; -use std::ops::Deref; +use rand::{thread_rng, Rng}; +use std::ops::{Deref, DerefMut}; use super::*; @@ -15,32 +16,51 @@ pub trait Map { fn x_size(&self) -> usize; fn y_size(&self) -> usize; fn characters(&self) -> (char, char); - fn on_colors(&self) -> Colors; - fn off_colors(&self) -> Colors; + fn colors(&self) -> (Colors, Colors); fn styles(&self) -> (Attributes, Attributes); fn update(&mut self); } #[derive(Debug, Clone)] pub struct Mask { - pub mask: Array2>, + pub mask: Array2, + pub colors: (Colors, Colors), } impl Mask { - pub fn new(x: usize, y: usize) -> Self { - let mask = Array2::from_elem((y, x), None); - Self { mask } + pub fn new(area: Area) -> Self { + let mask = Array2::from_elem((area.width(), area.height()), Note::Off); + Self { + mask, + colors: (Colors::new(White, Black), Colors::new(Black, Black)), + } + } + + pub fn randomize(&mut self, val: f64, scale: Scale) { + let mut rng = thread_rng(); + for f in self.iter_mut() { + if rng.gen::() > val { + let notes = scale.to_notes(); + *f = notes[rng.gen::() % notes.len()]; + } + } } } impl Deref for Mask { - type Target = Array2>; + type Target = Array2; fn deref(&self) -> &Self::Target { &self.mask } } -impl Map> for Mask { +impl DerefMut for Mask { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.mask + } +} + +impl Map for Mask { fn x_size(&self) -> usize { self.ncols() } @@ -50,10 +70,14 @@ impl Map> for Mask { } fn try_point(&self, point: Point) -> bool { - self.get((point.y, point.x)).is_some() + if let Some(note) = self.get((point.y, point.x)) { + *note != Note::Off + } else { + false + } } - fn get_point(&self, point: Point) -> Option> { + fn get_point(&self, point: Point) -> Option { self.get((point.y, point.x)).copied() } @@ -61,12 +85,8 @@ impl Map> for Mask { ('■', '□') } - fn on_colors(&self) -> Colors { - Colors::new(White, Black) - } - - fn off_colors(&self) -> Colors { - Colors::new(Grey, Black) + fn colors(&self) -> (Colors, Colors) { + self.colors } fn styles(&self) -> (Attributes, Attributes) { diff --git a/src/graphics/point.rs b/src/graphics/point.rs index 0acc247..dcb78fd 100644 --- a/src/graphics/point.rs +++ b/src/graphics/point.rs @@ -11,7 +11,7 @@ pub struct Pixel { pub style: Attribute, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] pub struct Point { pub x: usize, pub y: usize, diff --git a/src/graphics/selector.rs b/src/graphics/selector.rs index 7edf45b..8e7785f 100644 --- a/src/graphics/selector.rs +++ b/src/graphics/selector.rs @@ -1,7 +1,7 @@ use crate::Point; use crossterm::{ cursor, queue, - style::{self, Attribute, Color}, + style::{self, Attribute, Attributes, Color, Colors}, }; use eyre::Result; use std::{ @@ -9,12 +9,15 @@ use std::{ ops::{Deref, DerefMut}, }; +use super::*; + +#[derive(Debug)] pub struct Cursor { pub position: Point, - pub fg: Color, - pub bg: Color, - pub style: Attribute, + pub color: Colors, + pub style: Attributes, pub char: char, + pub area: Area, } impl Deref for Cursor { @@ -30,25 +33,31 @@ impl DerefMut for Cursor { } } +impl Default for Cursor { + fn default() -> Self { + Self::new(Area::new(0, 0, 0, 0)) + } +} + impl Cursor { - pub fn new() -> Self { + pub fn new(area: Area) -> Self { Self { - position: Point::new(0, 0), - fg: Color::White, - bg: Color::Black, - char: '*', - style: Attribute::Reset, + position: Point::new(1, 1), + color: Colors::new(Color::White, Color::Black), + char: '█', + style: Attributes::from(Attribute::Bold), + area, } } pub fn render(&self) -> Result<()> { + let offset = self.area.origin + self.position; queue!( stdout(), cursor::Hide, - cursor::MoveTo(self.x.try_into().unwrap(), self.y.try_into().unwrap()), - style::SetAttribute(self.style), - style::SetForegroundColor(self.fg), - style::SetBackgroundColor(self.bg), + cursor::MoveTo(offset.x.try_into().unwrap(), offset.y.try_into().unwrap()), + style::SetAttributes(self.style), + style::SetColors(self.color), style::Print(self.char.to_string()) )?; Ok(()) diff --git a/src/keys.rs b/src/keys.rs deleted file mode 100644 index f110cd8..0000000 --- a/src/keys.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crossterm::{ - event::{poll, read, Event, KeyCode, KeyEvent, KeyModifiers}, - execute, queue, terminal, -}; - -use super::*; - -pub enum Action { - None, - Move(Direction), - Resize(Direction), - Transport(Transport), - Select, - UnSelect, - Exit, -} - -pub enum Transport { - Stop, - Start, - Pause, - Faster(usize), - Slower(usize), -} - -pub enum Direction { - Up, - Down, - Left, - Right, -} - -pub fn key_event(event: KeyEvent) -> Action { - match event.modifiers { - KeyModifiers::NONE => match_normal_key(event.code), - KeyModifiers::CONTROL => match_ctl_key(event.code), - KeyModifiers::ALT => match_alt_key(event.code), - KeyModifiers::SHIFT => match_shift_key(event.code), - _ => Action::None, - } -} - -pub fn match_normal_key(key: KeyCode) -> Action { - match key { - KeyCode::Enter => Action::Transport(Transport::Pause), - KeyCode::Esc => Action::Exit, - KeyCode::Up => Action::Move(Direction::Up), - KeyCode::Down => Action::Move(Direction::Down), - KeyCode::Right => Action::Move(Direction::Right), - KeyCode::Left => Action::Move(Direction::Left), - KeyCode::Char(c) => match c { - ' ' => Action::Select, - 'q' => Action::Exit, - 'j' => Action::Move(Direction::Down), - 'k' => Action::Move(Direction::Up), - 'h' => Action::Move(Direction::Left), - 'l' => Action::Move(Direction::Right), - _ => Action::None, - }, - _ => Action::None, - } -} - -pub fn match_ctl_key(key: KeyCode) -> Action { - match key { - KeyCode::Char(c) => match c { - 'c' => Action::Exit, - _ => Action::None, - }, - _ => Action::None, - } -} - -pub fn match_shift_key(key: KeyCode) -> Action { - match key { - KeyCode::Enter => Action::Transport(Transport::Stop), - KeyCode::Up => Action::Resize(Direction::Up), - KeyCode::Down => Action::Resize(Direction::Down), - KeyCode::Right => Action::Resize(Direction::Right), - KeyCode::Left => Action::Resize(Direction::Left), - KeyCode::Char(c) => match c { - ' ' => Action::UnSelect, - 'j' => Action::Resize(Direction::Down), - 'k' => Action::Resize(Direction::Up), - 'h' => Action::Resize(Direction::Left), - 'l' => Action::Resize(Direction::Right), - _ => Action::None, - }, - _ => Action::None, - } -} - -pub fn match_alt_key(key: KeyCode) -> Action { - match key { - KeyCode::Char(c) => match c { - _ => Action::None, - }, - _ => Action::None, - } -} diff --git a/src/lib.rs b/src/lib.rs index 3638b07..97a4a32 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ use std::time::Duration; pub fn exit() -> Result<()> { crossterm::terminal::disable_raw_mode()?; + crossterm::execute!(std::io::stdout(), crossterm::cursor::Show)?; std::process::exit(0); } diff --git a/src/main.rs b/src/main.rs index 4a5d0d4..4e9ff64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,15 @@ use cellseq::*; -use crossterm::terminal; +use crossterm::{cursor, execute, terminal}; use eyre::Result; fn main() -> Result<()> { terminal::enable_raw_mode()?; + execute!(std::io::stdout(), cursor::Hide)?; let mut state = GlobalState::build()?; state.world.randomize(0.75); + state.mask[0].randomize(0.75, Scale::Aeolian); match render_loop(&mut state) { Ok(_) => exit(), diff --git a/src/music.rs b/src/music.rs index 943d5dd..a5e91c6 100644 --- a/src/music.rs +++ b/src/music.rs @@ -4,7 +4,7 @@ mod transport; pub use scale::*; pub use transport::*; -pub type NoteMask = [Option; 12]; +use std::fmt::Display; pub struct TimeSignature { pub top: usize, @@ -22,6 +22,7 @@ impl From<(usize, usize)> for TimeSignature { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Note { + Off, A(Acc), B(Acc), C(Acc), @@ -31,9 +32,38 @@ pub enum Note { G(Acc), } +impl Display for Note { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + Note::Off => "".to_string(), + Note::A(a) => format!("A{a}"), + Note::B(a) => format!("B{a}"), + Note::C(a) => format!("C{a}"), + Note::D(a) => format!("D{a}"), + Note::E(a) => format!("E{a}"), + Note::F(a) => format!("F{a}"), + Note::G(a) => format!("G{a}"), + }; + + write!(f, "{str}") + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Acc { Flt, Nat, Shp, } + +impl Display for Acc { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = match self { + Acc::Flt => "b", + Acc::Nat => "", + Acc::Shp => "#", + }; + + write!(f, "{str}") + } +} diff --git a/src/music/scale.rs b/src/music/scale.rs index a57d8b3..33f8773 100644 --- a/src/music/scale.rs +++ b/src/music/scale.rs @@ -23,7 +23,7 @@ pub enum Scale { } impl Scale { - pub fn to_notes(&self) -> NoteMask { + pub fn to_notes(&self) -> Vec { let octave = [ C(Nat), C(Shp), @@ -43,18 +43,20 @@ impl Scale { let pentatonic = [1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0]; let whole_tone = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]; - fn mask_scale(notes: &[Note; 12], mask: [u8; 12]) -> NoteMask { - let mut output = [None; 12]; - for (i, (note, mask)) in notes.iter().zip(mask.iter()).enumerate() { - if *mask == 1 { - output[i] = Some(*note) - } - } - output + fn mask_scale(notes: &[Note; 12], mask: [u8; 12]) -> Vec { + notes + .iter() + .zip(mask.iter()) + .filter_map(|(note, mask)| if *mask == 1 { Some(*note) } else { None }) + .collect() } macro_rules! rotate { - ($s:expr,$n:expr) => {{ + ($s:expr,r,$n:expr) => {{ + $s.rotate_right($n); + $s + }}; + ($s:expr,l,$n:expr) => {{ $s.rotate_left($n); $s }}; @@ -62,19 +64,19 @@ impl Scale { match self { Scale::Ionian => mask_scale(&octave, diatonic), - Scale::Dorian => rotate!(mask_scale(&octave, diatonic), 2), - Scale::Phrygian => rotate!(mask_scale(&octave, diatonic), 4), - Scale::Lydian => rotate!(mask_scale(&octave, diatonic), 5), - Scale::Mixolydian => rotate!(mask_scale(&octave, diatonic), 7), - Scale::Aeolian => rotate!(mask_scale(&octave, diatonic), 9), - Scale::Locrian => rotate!(mask_scale(&octave, diatonic), 11), + Scale::Dorian => rotate!(mask_scale(&octave, diatonic), l, 2), + Scale::Phrygian => rotate!(mask_scale(&octave, diatonic), l, 4), + Scale::Lydian => rotate!(mask_scale(&octave, diatonic), l, 5), + Scale::Mixolydian => rotate!(mask_scale(&octave, diatonic), r, 5), + Scale::Aeolian => rotate!(mask_scale(&octave, diatonic), r, 3), + Scale::Locrian => rotate!(mask_scale(&octave, diatonic), r, 1), Scale::MajPent => mask_scale(&octave, pentatonic), - Scale::SusEgypt => rotate!(mask_scale(&octave, pentatonic), 2), - Scale::BlueMinPen => rotate!(mask_scale(&octave, pentatonic), 4), - Scale::BlueMajPent => rotate!(mask_scale(&octave, pentatonic), 7), - Scale::MinPent => rotate!(mask_scale(&octave, pentatonic), 9), + Scale::SusEgypt => rotate!(mask_scale(&octave, pentatonic), l, 2), + Scale::BlueMinPen => rotate!(mask_scale(&octave, pentatonic), l, 4), + Scale::BlueMajPent => rotate!(mask_scale(&octave, pentatonic), r, 5), + Scale::MinPent => rotate!(mask_scale(&octave, pentatonic), r, 3), Scale::WholeTone => mask_scale(&octave, whole_tone), - Scale::Chromatic => octave.map(|n| Some(n)), + Scale::Chromatic => octave.to_vec(), } } } diff --git a/src/state.rs b/src/state.rs index 7bbf6bc..7b9d5e4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,3 +1,7 @@ +mod transport; + +pub use transport::*; + use super::*; pub struct GlobalState { @@ -7,19 +11,26 @@ pub struct GlobalState { pub song: Song, pub mask: Vec, pub channels: Vec, + pub cursor: Cursor, } impl GlobalState { pub fn build() -> Result { let layout = Layout::build()?; - let world = World::new(layout.cells.width(), layout.cells.height() + 1); + let world = World::new(layout.cells); - let channels = Vec::new(); - let mask = Vec::new(); + let channels = Vec::with_capacity(10); + let mut mask: Vec = Vec::with_capacity(10); + + for _ in 0..10 { + mask.push(Mask::new(layout.mask)); + } let transport = Transport::new(4, 4, 120); + let cursor = Cursor::new(layout.mask); + let song = Song::new(None, None); Ok(Self { @@ -29,31 +40,16 @@ impl GlobalState { song, mask, channels, + cursor, }) } + + pub fn tick(&self) -> Duration { + bpm_to_ms(self.transport.bpm) + } } pub struct MidiChannel { pub num: usize, pub poly_num: usize, } - -pub struct Transport { - pub running: bool, - pub bpm: usize, - pub sig: TimeSignature, - pub tick: Duration, - pub repeat: usize, -} - -impl Transport { - pub fn new(top: usize, bottom: usize, bpm: usize) -> Self { - Self { - sig: TimeSignature { top, bottom }, - bpm, - running: true, - repeat: 0, - tick: bpm_to_ms(bpm), - } - } -} diff --git a/src/state/transport.rs b/src/state/transport.rs new file mode 100644 index 0000000..f544fe4 --- /dev/null +++ b/src/state/transport.rs @@ -0,0 +1,52 @@ +use super::*; + +use crossterm::{ + cursor::MoveTo, + queue, + style::{Attribute, Color, Colors, Print, SetAttributes, SetColors}, +}; +use std::io::stdout; + +pub struct Transport { + pub running: bool, + pub bpm: usize, + pub sig: TimeSignature, + pub repeat: usize, +} + +impl Transport { + pub fn new(top: usize, bottom: usize, bpm: usize) -> Self { + Self { + sig: TimeSignature { top, bottom }, + bpm, + running: true, + repeat: 0, + } + } + + pub fn render(&self, area: Area) -> Result<()> { + let ((x_zero, y_zero), (_, _)) = area.to_u16()?; + + let bpm = format!("bpm: {}", self.bpm); + let sig = format!("sig: {}/{}", self.sig.top, self.sig.bottom); + let rep = format!("rep: {}", self.repeat); + + queue!( + stdout(), + MoveTo(x_zero + 1, y_zero + 1), + SetAttributes(Attribute::Bold.into()), + SetColors(Colors::new(Color::White, Color::Black)), + Print(bpm), + MoveTo(x_zero + 1, y_zero + 2), + SetAttributes(Attribute::Bold.into()), + SetColors(Colors::new(Color::White, Color::Black)), + Print(sig), + MoveTo(x_zero + 1, y_zero + 3), + SetAttributes(Attribute::Bold.into()), + SetColors(Colors::new(Color::White, Color::Black)), + Print(rep), + )?; + + Ok(()) + } +} diff --git a/src/transport.rs b/src/transport.rs new file mode 100644 index 0000000..a9c46cb --- /dev/null +++ b/src/transport.rs @@ -0,0 +1,19 @@ +use super::*; + +pub struct Transport { + pub running: bool, + pub bpm: usize, + pub sig: TimeSignature, + pub repeat: usize, +} + +impl Transport { + pub fn new(top: usize, bottom: usize, bpm: usize) -> Self { + Self { + sig: TimeSignature { top, bottom }, + bpm, + running: true, + repeat: 0, + } + } +} -- 2.45.2