name = "cellseq"
version = "0.1.0"
dependencies = [
+ "crossbeam",
"crossterm",
"eyre",
"ndarray",
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"
"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"
rand = "0.8"
crossterm = "0.26"
eyre = "0.6"
+crossbeam = "0.8.2"
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 }
}
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);
}
}
}
macro_rules! wrap {
($portal:expr,$wall:expr) => {
for (portal, wall) in $portal.iter().zip($wall) {
- portal.set(wall.get())
+ portal.set(wall.get());
}
};
}
}
false
}
+
fn get_point(&self, point: Point) -> Option<Cell<State>> {
self.get((point.y, point.x)).cloned()
}
mod layout;
mod map;
mod point;
+mod render;
mod selector;
pub use actions::*;
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,
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()?;
area.outline_area()?;
+ stdout().flush()?;
+
Ok(())
}
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,
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 => {
}
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(())
+}
'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,
--- /dev/null
+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()?;
+// }
+// }
use super::*;
-#[derive(Debug)]
+#[derive(Debug, Clone, Copy)]
pub struct Cursor {
pub position: Point,
pub color: Colors,
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(())
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);
use std::fmt::Display;
+#[derive(Clone, Copy, Debug)]
pub struct TimeSignature {
pub top: usize,
pub bottom: usize,
Note::{A, B, C, D, E, F, G},
};
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Scale {
Ionian,
Dorian,
use super::*;
+#[derive(Clone, Copy, Debug)]
pub struct Song {
pub key: Option<Note>,
pub scale: Option<Scale>,
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 {
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,
})
}
}
}
+#[derive(Clone, Copy, Debug)]
pub struct MidiChannel {
pub num: usize,
pub poly_num: usize,
};
use std::io::stdout;
+#[derive(Debug, Clone, Copy)]
pub struct Transport {
pub running: bool,
pub bpm: usize,