diff --git a/README.md b/README.md index 9e29acd..80d8f61 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/bin/piano_firmware.rs b/src/bin/piano_firmware.rs index 3bfb222..7a26ee8 100644 --- a/src/bin/piano_firmware.rs +++ b/src/bin/piano_firmware.rs @@ -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; } diff --git a/src/matrix.rs b/src/matrix.rs index 4d7bf5d..849f814 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -26,7 +26,7 @@ pub struct KeyMatrix { 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 KeyMatrix { @@ -39,7 +39,7 @@ impl KeyMatrix { 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 KeyMatrix { // 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 KeyMatrix { // 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 KeyMatrix { note_on[note as usize] = false; chan.note_off(note, 0).await; } - } + }, + midi::KeyAction::NOP => {}, } } } diff --git a/src/midi/keymap.py b/src/midi/keymap.py new file mode 100644 index 0000000..a48d462 --- /dev/null +++ b/src/midi/keymap.py @@ -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") diff --git a/src/midi/mod.rs b/src/midi/mod.rs index b485718..b8acde4 100644 --- a/src/midi/mod.rs +++ b/src/midi/mod.rs @@ -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 {}