]> git.huck.website - cellseq.git/commitdiff
refactored: render loop into main loop
authorHuck Boles <huck@huck.website>
Fri, 2 Jun 2023 19:29:43 +0000 (14:29 -0500)
committerHuck Boles <huck@huck.website>
Fri, 2 Jun 2023 19:29:43 +0000 (14:29 -0500)
14 files changed:
Cargo.lock
Cargo.toml
src/cells.rs
src/graphics.rs
src/graphics/actions.rs
src/graphics/keys.rs
src/graphics/render.rs [new file with mode: 0644]
src/graphics/selector.rs
src/main.rs
src/music.rs
src/music/scale.rs
src/music/transport.rs
src/state.rs
src/state/transport.rs

index 4d18abd8ddabdd14d98f8abbddc13da3941364ac..e52137ca2283dd9f7f64b54722a82942179880b0 100644 (file)
@@ -18,6 +18,7 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 name = "cellseq"
 version = "0.1.0"
 dependencies = [
+ "crossbeam",
  "crossterm",
  "eyre",
  "ndarray",
@@ -30,6 +31,73 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "crossbeam"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c"
+dependencies = [
+ "cfg-if",
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
+dependencies = [
+ "cfg-if",
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "crossbeam-utils",
+ "memoffset",
+ "scopeguard",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
+dependencies = [
+ "cfg-if",
+]
+
 [[package]]
 name = "crossterm"
 version = "0.26.1"
@@ -114,6 +182,15 @@ dependencies = [
  "rawpointer",
 ]
 
+[[package]]
+name = "memoffset"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "mio"
 version = "0.8.8"
index c5e719c9707e6af78f11fd3b5f65ef07faaa92a9..6dc1a4971793cf97dfc2844f118457187a6ea1f2 100644 (file)
@@ -10,3 +10,4 @@ ndarray = "0.15"
 rand = "0.8"
 crossterm = "0.26"
 eyre = "0.6"
+crossbeam = "0.8.2"
index 36d34732b2c190b1cf59beb9616a1c2e5c915236..1506ebe6e64e5156eb661507ddf978172266b2b9 100644 (file)
@@ -16,14 +16,14 @@ pub enum State {
     Alive,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Debug, Clone)]
 pub struct World {
     pub map: Array2<Cell<State>>,
 }
 
 impl World {
     pub fn new(area: Area) -> Self {
-        let map = Array2::from_elem((area.width(), area.height()), Cell::from(State::Dead));
+        let map = Array2::from_elem((area.width(), area.height()), Cell::new(State::Dead));
         Self { map }
     }
 
@@ -31,9 +31,9 @@ impl World {
         let mut rng = thread_rng();
         for cell in self.map.iter() {
             if rng.gen::<f64>() > val {
-                cell.set(State::Alive)
+                cell.set(State::Alive);
             } else {
-                cell.set(State::Dead)
+                cell.set(State::Dead);
             }
         }
     }
@@ -42,7 +42,7 @@ impl World {
         macro_rules! wrap {
             ($portal:expr,$wall:expr) => {
                 for (portal, wall) in $portal.iter().zip($wall) {
-                    portal.set(wall.get())
+                    portal.set(wall.get());
                 }
             };
         }
@@ -110,6 +110,7 @@ impl Map<Cell<State>> for World {
         }
         false
     }
+
     fn get_point(&self, point: Point) -> Option<Cell<State>> {
         self.get((point.y, point.x)).cloned()
     }
index b35f082b7bc87d0c3e5da7e1869b2a1481ac8248..9fbf217539cd6d69d5bc514371608bbbff284077 100644 (file)
@@ -4,6 +4,7 @@ mod keys;
 mod layout;
 mod map;
 mod point;
+mod render;
 mod selector;
 
 pub use actions::*;
@@ -12,13 +13,14 @@ pub use keys::*;
 pub use layout::*;
 pub use map::*;
 pub use point::*;
+pub use render::*;
 pub use selector::*;
 
 use super::*;
 
 use crossterm::{
     cursor::MoveTo,
-    event::{poll, read, Event},
+    event::{read, Event},
     execute, queue,
     style::{Print, SetAttributes, SetColors},
     terminal,
@@ -31,62 +33,6 @@ use std::{
     time::Duration,
 };
 
-pub fn render_loop(state: &mut GlobalState) -> Result<()> {
-    execute!(stdout(), terminal::Clear(terminal::ClearType::All))?;
-
-    state.layout.draw_outlines()?;
-
-    let world = Arc::new(Mutex::new(state.world.clone()));
-    let state_arc = Arc::new(Mutex::new(state));
-
-    loop {
-        let arc = Arc::clone(&state_arc);
-        let tick = arc.lock().unwrap().tick();
-        let timer = std::thread::spawn(move || thread::sleep(tick));
-
-        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();
-            });
-        }
-
-        let area = arc.lock().unwrap().layout.transport;
-
-        arc.lock().unwrap().transport.render(area)?;
-
-        action(event.join().unwrap(), arc.clone())?;
-        maps.join().unwrap();
-
-        arc.lock().unwrap().cursor.render()?;
-
-        timer.join().unwrap();
-        stdout().flush()?;
-    }
-}
-
 pub fn draw_map<T>(map: &impl Map<T>, area: &Area) -> Result<()> {
     let ((x_zero, y_zero), (x_max, y_max)) = area.to_u16()?;
 
@@ -127,6 +73,8 @@ pub fn draw_map<T>(map: &impl Map<T>, area: &Area) -> Result<()> {
 
     area.outline_area()?;
 
+    stdout().flush()?;
+
     Ok(())
 }
 
index 3c67dbbf87b71b6e92f74c6b101fa5ca075a9cc1..15f734cf47284c08f73194f3c36fe04bc9629e56 100644 (file)
@@ -33,26 +33,24 @@ pub enum Direction {
     Right,
 }
 
-type AMmGS<'a> = Arc<Mutex<&'a mut GlobalState>>;
-
-pub fn action(action: Action, state: AMmGS) -> Result<()> {
+pub fn action(action: Action, state: ArcState) -> 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::Channel(n) => change_channel(n, state),
         Action::Select => Ok(()),
         Action::SelectArea => Ok(()),
         Action::Reload => Ok(()),
         Action::Randomize => Ok(()),
         Action::Help => Ok(()),
         Action::Edit => Ok(()),
-        Action::SwitchPanel => Ok(()),
+        Action::SwitchPanel => switch_panel(state),
     }
 }
 
-fn transport_action(clock: Clock, state: AMmGS) -> Result<()> {
+fn transport_action(clock: Clock, state: ArcState) -> Result<()> {
     let mut state = state.lock().unwrap();
     match clock {
         Clock::Stop => state.transport.running = false,
@@ -64,7 +62,7 @@ fn transport_action(clock: Clock, state: AMmGS) -> Result<()> {
     Ok(())
 }
 
-fn move_cursor(direction: Direction, state: AMmGS) -> Result<()> {
+fn move_cursor(direction: Direction, state: ArcState) -> Result<()> {
     let mut state = state.lock().unwrap();
     match direction {
         Direction::Up => {
@@ -90,3 +88,20 @@ fn move_cursor(direction: Direction, state: AMmGS) -> Result<()> {
     }
     Ok(())
 }
+
+fn switch_panel(state: ArcState) -> Result<()> {
+    let mut mutex = state.lock().unwrap();
+    mutex.cursor.area = if mutex.cursor.area == mutex.layout.mask {
+        mutex.layout.cells
+    } else {
+        mutex.layout.mask
+    };
+    Ok(())
+}
+
+fn change_channel(channel: usize, state: ArcState) -> Result<()> {
+    let mut mutex = state.lock().unwrap();
+    mutex.channels.0 = channel;
+    draw_map(&mutex.mask[channel], &mutex.layout.mask)?;
+    Ok(())
+}
index ee3f4c20cecbdeaae940caa75fba4a056fe07476..b91ee0f379bebb403c33b0ec92dfb5e30a3be45e 100644 (file)
@@ -34,6 +34,16 @@ pub fn match_normal_key(key: KeyCode) -> Action {
             'k' => Action::Move(Direction::Up),
             'h' => Action::Move(Direction::Left),
             'l' => Action::Move(Direction::Right),
+            '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),
+            '0' => Action::Channel(10),
             _ => Action::None,
         },
         _ => Action::None,
diff --git a/src/graphics/render.rs b/src/graphics/render.rs
new file mode 100644 (file)
index 0000000..b911bc5
--- /dev/null
@@ -0,0 +1,135 @@
+use super::*;
+
+use crossbeam::channel::{bounded, Receiver, Sender};
+
+pub type ArcState = Arc<Mutex<GlobalState>>;
+
+pub fn main_loop(state: GlobalState) -> Result<()> {
+    execute!(stdout(), terminal::Clear(terminal::ClearType::All))?;
+
+    state.layout.draw_outlines()?;
+
+    let (clock, sync) = bounded::<()>(0);
+    let state = Arc::new(Mutex::new(state));
+
+    let timer_state = Arc::clone(&state);
+    let world_state = Arc::clone(&state);
+    let action_state = Arc::clone(&state);
+    // let cursor_state = Arc::clone(&state);
+
+    let timer = std::thread::spawn(move || timer_loop(timer_state, clock));
+    let world = std::thread::spawn(move || world_loop(world_state, sync));
+    let action = std::thread::spawn(move || action_loop(action_state));
+    // let cursor = std::thread::spawn(move || cursor_loop(cursor_state));
+
+    timer.join().unwrap()?;
+    world.join().unwrap()?;
+    action.join().unwrap()?;
+    // cursor.join().unwrap()?;
+
+    Ok(())
+}
+
+fn timer_loop(state: ArcState, clock: Sender<()>) -> Result<()> {
+    loop {
+        let tick = state.lock().unwrap().tick();
+        thread::sleep(tick);
+        clock.send(())?;
+    }
+}
+
+pub fn world_loop(state: ArcState, sync: Receiver<()>) -> Result<()> {
+    loop {
+        let mutex = state.lock().unwrap();
+        if mutex.transport.running {
+            let mut world = mutex.world.clone();
+            let mask = mutex.mask[mutex.channels.0].clone();
+            let world_layout = mutex.layout.cells;
+            let mask_layout = mutex.layout.mask;
+            drop(mutex);
+
+            world.update();
+            draw_map(&world, &world_layout)?;
+            draw_map(&mask, &mask_layout)?;
+            sync.recv()?;
+            state.lock().as_mut().unwrap().world = world;
+        } else {
+            drop(mutex);
+            sync.recv()?;
+        }
+    }
+}
+
+pub fn action_loop(state: ArcState) -> Result<()> {
+    while let Ok(event) = read() {
+        match event {
+            Event::Key(key) => action(key_event(key), state.clone())?,
+            Event::Resize(_, _) => state.lock().unwrap().layout = Layout::build()?,
+            _ => (),
+        }
+    }
+    Ok(())
+}
+
+pub fn cursor_loop(state: ArcState) -> Result<()> {
+    loop {
+        state.lock().unwrap().cursor.render()?;
+        std::thread::sleep(Duration::from_millis(100))
+    }
+}
+
+// pub fn render_loop(state: &mut GlobalState) -> Result<()> {
+//     execute!(stdout(), terminal::Clear(terminal::ClearType::All))?;
+
+//     state.layout.draw_outlines()?;
+
+//     let world = Arc::new(Mutex::new(state.world.clone()));
+//     let state_arc = Arc::new(Mutex::new(state));
+
+//     loop {
+//         let arc = Arc::clone(&state_arc);
+//         let tick = arc.lock().unwrap().tick();
+//         let timer = std::thread::spawn(move || thread::sleep(tick));
+
+//         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();
+//             });
+//         }
+
+//         let area = arc.lock().unwrap().layout.transport;
+
+//         arc.lock().unwrap().transport.render(area)?;
+
+//         action(event.join().unwrap(), arc.clone())?;
+//         maps.join().unwrap();
+
+//         arc.lock().unwrap().cursor.render()?;
+
+//         timer.join().unwrap();
+//         stdout().flush()?;
+//     }
+// }
index 8e7785fc903ae8958d02607d309d3cfa1601c995..becfb2752db4665b0a96f2c746c996f943f7002f 100644 (file)
@@ -11,7 +11,7 @@ use std::{
 
 use super::*;
 
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
 pub struct Cursor {
     pub position: Point,
     pub color: Colors,
@@ -58,6 +58,7 @@ impl Cursor {
             cursor::MoveTo(offset.x.try_into().unwrap(), offset.y.try_into().unwrap()),
             style::SetAttributes(self.style),
             style::SetColors(self.color),
+            cursor::Show,
             style::Print(self.char.to_string())
         )?;
         Ok(())
index 4e9ff64e76dbfd1a40c65c539c19f32a2eeecda9..c53dab30e63a1b49599b8f73f9e21c3809fc3f7e 100644 (file)
@@ -1,17 +1,18 @@
 use cellseq::*;
-use crossterm::{cursor, execute, terminal};
+use crossterm::{cursor::Hide, execute, terminal};
 use eyre::Result;
+use std::io::stdout;
 
 fn main() -> Result<()> {
     terminal::enable_raw_mode()?;
-    execute!(std::io::stdout(), cursor::Hide)?;
+    execute!(stdout(), Hide)?;
 
     let mut state = GlobalState::build()?;
 
     state.world.randomize(0.75);
     state.mask[0].randomize(0.75, Scale::Aeolian);
 
-    match render_loop(&mut state) {
+    match main_loop(state) {
         Ok(_) => exit(),
         Err(e) => {
             eprintln!("{}", e);
index a5e91c6ed39e2e6b87705357846eab1168d81f90..c0185e737bbe98ba6c04ae31bffccef5e9dc26d9 100644 (file)
@@ -6,6 +6,7 @@ pub use transport::*;
 
 use std::fmt::Display;
 
+#[derive(Clone, Copy, Debug)]
 pub struct TimeSignature {
     pub top: usize,
     pub bottom: usize,
index 33f8773160779c3e7ccf9f16e8e182dc7ad974a4..486bfbd86f5433a422df335da6c3a5e379662ed1 100644 (file)
@@ -5,6 +5,7 @@ use super::{
     Note::{A, B, C, D, E, F, G},
 };
 
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum Scale {
     Ionian,
     Dorian,
index 59face80ba47a5b1e8a449cd9dab8f34051418ff..415f2e28a359bfd0ce14b731ea38684a1cc9615b 100644 (file)
@@ -1,5 +1,6 @@
 use super::*;
 
+#[derive(Clone, Copy, Debug)]
 pub struct Song {
     pub key: Option<Note>,
     pub scale: Option<Scale>,
index 7b9d5e4e190e36c85f50c5492fc716e9e9d852bb..159c265e5b5360714c3f6392147feaae9020067d 100644 (file)
@@ -2,16 +2,20 @@ mod transport;
 
 pub use transport::*;
 
+use crossbeam::channel::{bounded, Receiver, Sender};
+
 use super::*;
 
+#[derive(Debug, Clone)]
 pub struct GlobalState {
     pub world: World,
     pub layout: Layout,
     pub transport: Transport,
     pub song: Song,
     pub mask: Vec<Mask>,
-    pub channels: Vec<MidiChannel>,
+    pub channels: (usize, Vec<MidiChannel>),
     pub cursor: Cursor,
+    pub update_mask: (Sender<usize>, Receiver<usize>),
 }
 
 impl GlobalState {
@@ -33,14 +37,17 @@ impl GlobalState {
 
         let song = Song::new(None, None);
 
+        let update_mask = bounded::<usize>(0);
+
         Ok(Self {
             world,
             layout,
             transport,
             song,
             mask,
-            channels,
+            channels: (0, channels),
             cursor,
+            update_mask,
         })
     }
 
@@ -49,6 +56,7 @@ impl GlobalState {
     }
 }
 
+#[derive(Clone, Copy, Debug)]
 pub struct MidiChannel {
     pub num: usize,
     pub poly_num: usize,
index f544fe41a560f91a4e52ba285ac2f55b7b9710c4..b2309287f23167edf4573789b12470447206b4a6 100644 (file)
@@ -7,6 +7,7 @@ use crossterm::{
 };
 use std::io::stdout;
 
+#[derive(Debug, Clone, Copy)]
 pub struct Transport {
     pub running: bool,
     pub bpm: usize,