[[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"
[[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]]
[[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"
"libc",
"redox_syscall",
"smallvec",
- "windows-sys",
+ "windows-sys 0.45.0",
]
[[package]]
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]]
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]]
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"
use rand::{thread_rng, Rng};
use std::{cell::Cell, ops::Deref};
+use super::*;
+
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum State {
Dead,
}
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 }
}
}
}
State::Alive => {
- if count < 3 || count > 4 {
+ if !(3..=4).contains(&count) {
cell.set(State::Dead);
}
}
('●', '◌')
}
- 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) {
use super::*;
use crossterm::{
- cursor::{Hide, MoveTo},
+ cursor::MoveTo,
+ event::{poll, read, Event},
execute, queue,
style::{Print, SetAttributes, SetColors},
terminal,
use eyre::Result;
use std::{
io::{stdout, Write},
+ sync::{Arc, Mutex},
thread,
time::Duration,
};
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<T>(map: &mut impl Map<T>, area: &Area) -> Result<()> {
+pub fn draw_map<T>(map: &impl Map<T>, 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()) {
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),
} else {
queue!(
stdout(),
- Hide,
MoveTo(x_off, y_off),
SetAttributes(style_off),
SetColors(off_colors),
Ok(())
}
-pub fn run_map<T>(map: &mut impl Map<T>, 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<T>(
- map: &mut (impl Map<T> + Clone),
+ map: &(impl Map<T> + Clone),
area: &Area,
time: Duration,
steps: usize,
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);
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,
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<Mutex<&'a mut GlobalState>>;
+
+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(())
}
use crossterm::{
- cursor::{Hide, MoveTo},
+ cursor::MoveTo,
queue,
style::{
Color::{Black, White},
use super::*;
-#[derive(Clone, Debug, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq, Copy)]
pub struct Area {
pub origin: Point,
pub max: Point,
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("┃"),
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("━")
)?;
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())
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,
}
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),
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,
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,
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,
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::*;
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<Option<Note>>,
+ pub mask: Array2<Note>,
+ 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::<f64>() > val {
+ let notes = scale.to_notes();
+ *f = notes[rng.gen::<usize>() % notes.len()];
+ }
+ }
}
}
impl Deref for Mask {
- type Target = Array2<Option<Note>>;
+ type Target = Array2<Note>;
fn deref(&self) -> &Self::Target {
&self.mask
}
}
-impl Map<Option<Note>> for Mask {
+impl DerefMut for Mask {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.mask
+ }
+}
+
+impl Map<Note> for Mask {
fn x_size(&self) -> usize {
self.ncols()
}
}
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<Option<Note>> {
+ fn get_point(&self, point: Point) -> Option<Note> {
self.get((point.y, point.x)).copied()
}
('■', '□')
}
- 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) {
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,
use crate::Point;
use crossterm::{
cursor, queue,
- style::{self, Attribute, Color},
+ style::{self, Attribute, Attributes, Color, Colors},
};
use eyre::Result;
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 {
}
}
+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(())
+++ /dev/null
-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,
- }
-}
pub fn exit() -> Result<()> {
crossterm::terminal::disable_raw_mode()?;
+ crossterm::execute!(std::io::stdout(), crossterm::cursor::Show)?;
std::process::exit(0);
}
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(),
pub use scale::*;
pub use transport::*;
-pub type NoteMask = [Option<Note>; 12];
+use std::fmt::Display;
pub struct TimeSignature {
pub top: usize,
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Note {
+ Off,
A(Acc),
B(Acc),
C(Acc),
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}")
+ }
+}
}
impl Scale {
- pub fn to_notes(&self) -> NoteMask {
+ pub fn to_notes(&self) -> Vec<Note> {
let octave = [
C(Nat),
C(Shp),
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<Note> {
+ 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
}};
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(),
}
}
}
+mod transport;
+
+pub use transport::*;
+
use super::*;
pub struct GlobalState {
pub song: Song,
pub mask: Vec<Mask>,
pub channels: Vec<MidiChannel>,
+ pub cursor: Cursor,
}
impl GlobalState {
pub fn build() -> Result<Self> {
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<Mask> = 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 {
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),
- }
- }
-}
--- /dev/null
+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(())
+ }
+}
--- /dev/null
+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,
+ }
+ }
+}