Skip to content

Commit

Permalink
Implement midi notes on life example.
Browse files Browse the repository at this point in the history
  • Loading branch information
iandoesallthethings committed Jul 28, 2023
1 parent e848874 commit 3f486fb
Show file tree
Hide file tree
Showing 11 changed files with 210 additions and 106 deletions.
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"rust-analyzer.linkedProjects": [
"./Cargo.toml",
"./Cargo.toml"
]
}
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
# Ronome
Dipping a technicolor claw into the wonderful, buttery vat of C++ that is the Monome.

## Hardware
This is just a light wrapper around `monome-rs` that attempts to simplify reasoning
about the grid and add support for common use cases like MIDI. I intend to also expand
it to include pixel-by-pixel color for NeoTrellis along with modified firmware to support
it (at least for the chip I have 😬).

### Getting Started
The main module operates on a builder pattern.
```rust
Grid::new()
.on_key_down()
.on_key_up()
.on_frame()
.run();
```

Event handlers look like this:
```rust
fn play_note(&mut grid, x: u8, y: u8) {
// do something
}
```

`on_frame()` is just for updating the state in arbitrary ways. It just takes `&mut grid`, runs after inputs, and before drawing.

### API
`Grid` exposes useful
```rust

```



### Hardware
* NeoTrellis
* ItsyBitsy M0 (subject to change)

## Software
### Software
* [Firmware](https://github.com/iandoesallthethings/neotrellis-grid-paletted)
* [monome-rs](https://github.com/padenot/monome-rs)

Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions src/keyboard.rs → src/examples/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ fn draw_c(grid: &mut Grid) {
if note % 12 == 0 {
let intensity = (note / 12) * 2;
grid.set_pixel(x, y, intensity);
return intensity;
intensity
} else {
0
}

0
});
}

Expand Down
146 changes: 146 additions & 0 deletions src/examples/life.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use crate::grid::Grid as GenericGrid;
use std::thread::sleep;
use std::time::Duration;

type Grid = GenericGrid<Context>;
type Control = (i32, i32);
const RUN: Control = (0, 0);
const STEP: Control = (1, 0);

struct Context {
running: bool,
step_interval: Duration,
}
pub fn main() {
let context = Context {
running: false,
step_interval: Duration::from_millis(100),
};

Grid::new_with_context(context)
.on_key_down(controls)
.on_frame(step_if_running)
.run();
}

fn controls(grid: &mut Grid, x: i32, y: i32) {
match (x, y) {
RUN => {
grid.context.running = !grid.context.running;

let intensity = if grid.context.running { 15 } else { 0 };
grid.set_pixel(x, y, intensity);
}

STEP => {
step(grid);
}

(2..=15, 0) => {
grid.set_row(0, 0);
let previous_setting = f64::sqrt(grid.context.step_interval.as_millis() as f64);
grid.set_pixel(previous_setting as i32, 0, 0);

grid.context.step_interval = Duration::from_millis(u64::pow(x as u64, 2));

grid.set_pixel(x, 0, x as u8)
}

_ => {
let current = grid.get_pixel(x, y);
let new = match current {
0 => 15,
_ => 0,
};

grid.set_pixel(x, y, new);
}
}
}

fn step_if_running(grid: &mut Grid) {
if grid.context.running {
step(grid);
}
}

fn play_notes_if_on(grid: &mut Grid) {
grid.map_pixels(|grid, x, y, intensity, _| {
if y == 0 {
return 0;
}

let note = isometric_fourths(x, y);

match intensity {
0 => {
grid.midi.note_off(note, 127);
}
1..=15 => {
grid.midi.note_on(note, 127);
}
_ => {}
};

0
});
}

fn isometric_fourths(x: i32, y: i32) -> u8 {
let octave_offset = 12 * 3; // Push up to usable octaves
let x_offset = -5; // Sliding down so c is in the bottom left
let y_offset = (8 - y) * 5; // reverse y axis * perfect 4th

(x + x_offset + y_offset + octave_offset) as u8
}

fn step(grid: &mut Grid) {
let new_pixels = grid.map_pixels(conway_rules);

grid.set_all(new_pixels);

play_notes_if_on(grid);

sleep(grid.context.step_interval);
}

fn conway_rules(grid: &mut Grid, x: i32, y: i32, intensity: u8, _index: usize) -> u8 {
// First row is controls - I'll bet there's a way to refactor controls()
// to just omit the actual control keys.
if y == 0 {
return intensity;
}

let neighbors = num_living_neighbors(grid, x, y);
let alive = intensity > 0;

(match (alive, neighbors) {
(true, 0..=1) => 0, // Starved
(true, 4..=8) => 0, // Crowded
(true, _) => 15 - x, // Safe
(false, 3) => 15 - x, // Fertile
(false, _) => 0, // Barren
} as u8)
}

fn num_living_neighbors(grid: &mut Grid, x: i32, y: i32) -> usize {
NEIGHBOR_MASK
.iter()
.filter_map(|(dx, dy)| match grid.get_pixel(x + dx, y + dy) > 0 {
true => Some(true),
false => None,
})
.collect::<Vec<bool>>()
.len()
}

const NEIGHBOR_MASK: [(i32, i32); 8] = [
(-1, -1),
(-1, 0),
(-1, 1),
(0, -1),
(0, 1),
(1, -1),
(1, 0),
(1, 1),
];
4 changes: 4 additions & 0 deletions src/examples/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod flash;
pub mod keyboard;
pub mod life;
pub mod modes;
File renamed without changes.
24 changes: 16 additions & 8 deletions src/grid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ impl<Context> Grid<Context> {
let index = self.coordinate_to_index(x, y);

if index > 0 && index < self.pixels.len() {
return self.pixels[index];
self.pixels[index]
} else {
0
}

return 0;
}

pub fn set_pixel(&mut self, x: i32, y: i32, intensity: u8) {
Expand All @@ -56,6 +56,17 @@ impl<Context> Grid<Context> {
self.pixels[index] = intensity;
}
}
pub fn set_row(&mut self, y: i32, intensity: u8) {
for x in 0..16 {
self.set_pixel(x, y, intensity);
}
}

pub fn set_column(&mut self, x: i32, intensity: u8) {
for y in 0..8 {
self.set_pixel(x, y, intensity);
}
}

pub fn set_all(&mut self, new_pixels: Vec<u8>) {
self.pixels = new_pixels;
Expand All @@ -70,8 +81,7 @@ impl<Context> Grid<Context> {
&mut self,
mapper: fn(grid: &mut Grid<Context>, x: i32, y: i32, intensity: u8, index: usize) -> u8,
) -> Vec<u8> {
let new_pixels: Vec<u8> = self
.pixels
self.pixels
.to_owned()
.iter()
.enumerate()
Expand All @@ -80,9 +90,7 @@ impl<Context> Grid<Context> {

mapper(self, *x, *y, intensity, index)
})
.collect::<Vec<u8>>();

new_pixels
.collect::<Vec<u8>>()
}

pub fn clear(&mut self) {
Expand Down
87 changes: 0 additions & 87 deletions src/life.rs

This file was deleted.

11 changes: 5 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use std::env::args;

mod flash;
mod examples;
mod grid;
mod keyboard;
mod life;
mod midi;
mod modes;

use examples::{flash, keyboard, life, modes};

fn main() {
let args: Vec<String> = args().collect();
Expand All @@ -19,6 +17,7 @@ fn main() {
_ => println!("Demo not found."),
}
} else {
keyboard::main();
// keyboard::main();
life::main();
}
}
1 change: 1 addition & 0 deletions src/midi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl Midi {
},
);

dbg!(&event);
self.send(event);
}

Expand Down

0 comments on commit 3f486fb

Please sign in to comment.