feat: basic piano functionality complete
piano is janky but can play sounds now
This commit is contained in:
parent
574105de7b
commit
444a86a4d4
@ -112,4 +112,10 @@ I used male-to-female jumpers with the female end trimmed to reveal the metal pa
|
||||
This was necessary to attach to the short pins on the jumpers.
|
||||
The opening in the plastic on the female end should face inwards when connected to the sockets.
|
||||
|
||||
Then, plug the ribbon cables from your piano into the sockets.
|
||||
Plugging this many jumper cables into a single socket can cause [crosstalk](https://en.m.wikipedia.org/wiki/Crosstalk).
|
||||
Twisting cables and spacing them from each other may help prevent this.
|
||||
To test for crosstalk, run `pin_scanner` and connect the socket contacts with each other with a jumper cable.
|
||||
Each scan should return exactly two connections, these being the forward and reverse connection over the jumper cable.
|
||||
If there are more connections, that means there is crosstalk.
|
||||
|
||||
Once the wiring is done, plug the ribbon cables from your piano into the sockets.
|
||||
|
@ -39,11 +39,246 @@ async fn piano_task(pin_driver: pins::TransparentPins) {
|
||||
use geode_piano::midi::Note::*;
|
||||
|
||||
// GND pins
|
||||
let col_pins = [23, 24];
|
||||
let col_pins = [32, 33, 34, 4, 36, 6, 7, 37, 38, 39, 15, 19, 24, 25, 26, 31];
|
||||
// Input pins
|
||||
let row_pins = [20, 15, 4];
|
||||
let row_pins = [
|
||||
1, 2, 3, 5, 8, 9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 23, 27, 28, 29, 30, 35,
|
||||
];
|
||||
// Notes for each key
|
||||
let keymap = [[N1(C4), N1(D4), N1(E4)], [N1(C4), N2(D4), N2(E4)]];
|
||||
let keymap = [
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(A0, 64),
|
||||
NOP,
|
||||
N(CS1, 64),
|
||||
N(B0, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(DS1, 64),
|
||||
N(E1, 64),
|
||||
N(C1, 64),
|
||||
N(D1, 64),
|
||||
N(AS0, 64),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(F1, 64),
|
||||
NOP,
|
||||
N(A1, 64),
|
||||
N(G1, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(B1, 64),
|
||||
N(C2, 64),
|
||||
N(GS1, 64),
|
||||
N(AS1, 64),
|
||||
N(FS1, 64),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
N(GS5, 64),
|
||||
N(AS5, 64),
|
||||
N(C6, 64),
|
||||
NOP,
|
||||
N(F5, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
N(G5, 64),
|
||||
N(A5, 64),
|
||||
N(B5, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(FS5, 64),
|
||||
],
|
||||
[
|
||||
N(C7, 64),
|
||||
N(D7, 64),
|
||||
N(E7, 64),
|
||||
NOP,
|
||||
N(A6, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
N(B6, 64),
|
||||
N(CS7, 64),
|
||||
N(DS7, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(AS6, 64),
|
||||
],
|
||||
[
|
||||
N(E6, 64),
|
||||
N(FS6, 64),
|
||||
N(GS6, 64),
|
||||
NOP,
|
||||
N(CS6, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
N(DS6, 64),
|
||||
N(F6, 64),
|
||||
N(G6, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(D6, 64),
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(A2, 64),
|
||||
NOP,
|
||||
N(CS3, 64),
|
||||
N(B2, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(DS3, 64),
|
||||
N(E3, 64),
|
||||
N(C3, 64),
|
||||
N(D3, 64),
|
||||
N(AS2, 64),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(A4, 64),
|
||||
NOP,
|
||||
N(CS5, 64),
|
||||
N(B4, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(DS5, 64),
|
||||
N(E5, 64),
|
||||
N(C5, 64),
|
||||
N(D5, 64),
|
||||
N(AS4, 64),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(F3, 64),
|
||||
NOP,
|
||||
N(A3, 64),
|
||||
N(G3, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(B3, 64),
|
||||
N(C4, 64),
|
||||
N(GS3, 64),
|
||||
N(AS3, 64),
|
||||
N(FS3, 64),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(CS4, 64),
|
||||
NOP,
|
||||
N(F4, 64),
|
||||
N(DS4, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(G4, 64),
|
||||
N(GS4, 64),
|
||||
N(E4, 64),
|
||||
N(FS4, 64),
|
||||
N(D4, 64),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(CS2, 64),
|
||||
NOP,
|
||||
N(F2, 64),
|
||||
N(DS2, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(G2, 64),
|
||||
N(GS2, 64),
|
||||
N(E2, 64),
|
||||
N(FS2, 64),
|
||||
N(D2, 64),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
],
|
||||
[
|
||||
N(GS7, 64),
|
||||
N(AS7, 64),
|
||||
N(C8, 64),
|
||||
NOP,
|
||||
N(F7, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
N(G7, 64),
|
||||
N(A7, 64),
|
||||
N(B7, 64),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(FS7, 64),
|
||||
],
|
||||
];
|
||||
|
||||
let mut mat = KeyMatrix::new(col_pins, row_pins, keymap);
|
||||
mat.scan(pin_driver).await;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ pub struct KeyMatrix<const N_ROWS: usize, const N_COLS: usize> {
|
||||
col_pins: [u8; N_COLS],
|
||||
/// Input pins at the left of each row
|
||||
row_pins: [u8; N_ROWS],
|
||||
keymap: [[midi::KeyAction; N_ROWS]; N_COLS],
|
||||
keymap: [[midi::KeyAction; N_COLS]; N_ROWS],
|
||||
}
|
||||
|
||||
impl<const N_ROWS: usize, const N_COLS: usize> KeyMatrix<N_ROWS, N_COLS> {
|
||||
@ -39,7 +39,7 @@ impl<const N_ROWS: usize, const N_COLS: usize> KeyMatrix<N_ROWS, N_COLS> {
|
||||
pub fn new(
|
||||
col_pins: [u8; N_COLS],
|
||||
row_pins: [u8; N_ROWS],
|
||||
keymap: [[midi::KeyAction; N_ROWS]; N_COLS],
|
||||
keymap: [[midi::KeyAction; N_COLS]; N_ROWS],
|
||||
) -> Self {
|
||||
KeyMatrix {
|
||||
col_pins,
|
||||
@ -56,7 +56,7 @@ impl<const N_ROWS: usize, const N_COLS: usize> KeyMatrix<N_ROWS, N_COLS> {
|
||||
|
||||
// scan frequency
|
||||
// this might(?) panic if the scan takes longer than the tick
|
||||
let mut ticker = Ticker::every(Duration::from_millis(2));
|
||||
let mut ticker = Ticker::every(Duration::from_millis(13));
|
||||
|
||||
let chan = midi::MidiChannel::new(0);
|
||||
const MAX_NOTES: usize = 128;
|
||||
@ -75,7 +75,7 @@ impl<const N_ROWS: usize, const N_COLS: usize> KeyMatrix<N_ROWS, N_COLS> {
|
||||
// values that are logical ON
|
||||
let mask = input ^ (((1 << pin_driver.n_usable_pins()) - 1) ^ (1 << col));
|
||||
for (j, row) in self.row_pins.iter().enumerate() {
|
||||
let key_action = self.keymap[i][j];
|
||||
let key_action = self.keymap[j][i];
|
||||
let key_active = mask & (1 << row) != 0;
|
||||
match key_action {
|
||||
midi::KeyAction::N1(note) => {
|
||||
@ -114,7 +114,8 @@ impl<const N_ROWS: usize, const N_COLS: usize> KeyMatrix<N_ROWS, N_COLS> {
|
||||
note_on[note as usize] = false;
|
||||
chan.note_off(note, 0).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
midi::KeyAction::NOP => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
68
src/midi/keymap.py
Normal file
68
src/midi/keymap.py
Normal file
@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Helper to generate keymaps.
|
||||
|
||||
Takes in data through stdin with this format:
|
||||
|
||||
[note name] [GND pin]
|
||||
[n1 input pin]
|
||||
[n2 input pin]
|
||||
|
||||
Use cargo fmt to de-messify the output once pasted in the source.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Note:
|
||||
note_name: str
|
||||
gnd_pin: int
|
||||
n1_pin: int
|
||||
n2_pin: int
|
||||
|
||||
|
||||
data: list[Note] = []
|
||||
col_dedup: set[int] = set()
|
||||
row_dedup: set[int] = set()
|
||||
|
||||
for i in range(88):
|
||||
note_name, gnd_pin = input().split()
|
||||
gnd_pin = int(gnd_pin)
|
||||
n1_pin = int(input())
|
||||
n2_pin = int(input())
|
||||
data.append(Note(note_name, gnd_pin, n1_pin, n2_pin))
|
||||
col_dedup.add(gnd_pin)
|
||||
row_dedup.add(n1_pin)
|
||||
row_dedup.add(n2_pin)
|
||||
|
||||
col_pins = list(col_dedup)
|
||||
row_pins = list(row_dedup)
|
||||
|
||||
# [row][column]
|
||||
mat = [["" for _ in range(len(col_pins))] for _ in range(len(row_pins))]
|
||||
|
||||
for d in data:
|
||||
# this is inefficient but whatever
|
||||
mat[row_pins.index(d.n1_pin)][col_pins.index(d.gnd_pin)] = f"N1({d.note_name})"
|
||||
mat[row_pins.index(d.n1_pin)][col_pins.index(d.gnd_pin)] = f"NOP"
|
||||
mat[row_pins.index(d.n2_pin)][col_pins.index(d.gnd_pin)] = f"N({d.note_name}, 64)"
|
||||
|
||||
empty_counter = 0
|
||||
|
||||
for i in range(len(mat)):
|
||||
for j in range(len(mat[i])):
|
||||
if mat[i][j] == "":
|
||||
mat[i][j] = "NOP"
|
||||
empty_counter += 1
|
||||
|
||||
|
||||
print("[")
|
||||
for col in mat:
|
||||
print(f"[{', '.join(col)}],")
|
||||
print("]")
|
||||
|
||||
print(f"{len(row_pins)} rows, {len(col_pins)} cols")
|
||||
print(f"row pins: [{', '.join([str(i) for i in row_pins])}]")
|
||||
print(f"col pins: [{', '.join([str(i) for i in col_pins])}]")
|
||||
print(f"{empty_counter} empty cells")
|
@ -197,6 +197,8 @@ pub enum KeyAction {
|
||||
N2(Note),
|
||||
/// Basic switch with fixed velocity. Be careful not to mix with actions with velocity detection.
|
||||
N(Note, u8),
|
||||
/// NOP
|
||||
NOP,
|
||||
}
|
||||
|
||||
pub struct Disconnected {}
|
||||
|
Loading…
Reference in New Issue
Block a user