feat: proper velocity curves
This commit is contained in:
parent
444a86a4d4
commit
4fcc682406
@ -47,235 +47,400 @@ async fn piano_task(pin_driver: pins::TransparentPins) {
|
||||
// Notes for each key
|
||||
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,
|
||||
N1(GS5),
|
||||
N1(AS5),
|
||||
N1(C6),
|
||||
NOP,
|
||||
N1(F5),
|
||||
NOP,
|
||||
NOP,
|
||||
N1(G5),
|
||||
N1(A5),
|
||||
N1(B5),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(FS5),
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(A0, 64),
|
||||
N1(F1),
|
||||
NOP,
|
||||
N(CS1, 64),
|
||||
N(B0, 64),
|
||||
N1(A1),
|
||||
N1(G1),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(DS1, 64),
|
||||
N(E1, 64),
|
||||
N(C1, 64),
|
||||
N(D1, 64),
|
||||
N(AS0, 64),
|
||||
N1(B1),
|
||||
N1(C2),
|
||||
N1(GS1),
|
||||
N1(AS1),
|
||||
N1(FS1),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(F1, 64),
|
||||
N1(A0),
|
||||
NOP,
|
||||
N(A1, 64),
|
||||
N(G1, 64),
|
||||
N1(CS1),
|
||||
N1(B0),
|
||||
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),
|
||||
N1(DS1),
|
||||
N1(E1),
|
||||
N1(C1),
|
||||
N1(D1),
|
||||
N1(AS0),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(F3, 64),
|
||||
N1(CS2),
|
||||
NOP,
|
||||
N(A3, 64),
|
||||
N(G3, 64),
|
||||
N1(F2),
|
||||
N1(DS2),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(B3, 64),
|
||||
N(C4, 64),
|
||||
N(GS3, 64),
|
||||
N(AS3, 64),
|
||||
N(FS3, 64),
|
||||
N1(G2),
|
||||
N1(GS2),
|
||||
N1(E2),
|
||||
N1(FS2),
|
||||
N1(D2),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(CS4, 64),
|
||||
N2(A0),
|
||||
NOP,
|
||||
N(F4, 64),
|
||||
N(DS4, 64),
|
||||
N2(CS1),
|
||||
N2(B0),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(G4, 64),
|
||||
N(GS4, 64),
|
||||
N(E4, 64),
|
||||
N(FS4, 64),
|
||||
N(D4, 64),
|
||||
N2(DS1),
|
||||
N2(E1),
|
||||
N2(C1),
|
||||
N2(D1),
|
||||
N2(AS0),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(CS2, 64),
|
||||
N2(F1),
|
||||
NOP,
|
||||
N(F2, 64),
|
||||
N(DS2, 64),
|
||||
N2(A1),
|
||||
N2(G1),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(G2, 64),
|
||||
N(GS2, 64),
|
||||
N(E2, 64),
|
||||
N(FS2, 64),
|
||||
N(D2, 64),
|
||||
N2(B1),
|
||||
N2(C2),
|
||||
N2(GS1),
|
||||
N2(AS1),
|
||||
N2(FS1),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
N2(GS5),
|
||||
N2(AS5),
|
||||
N2(C6),
|
||||
NOP,
|
||||
N2(F5),
|
||||
NOP,
|
||||
NOP,
|
||||
N2(G5),
|
||||
N2(A5),
|
||||
N2(B5),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(FS5),
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
N2(C7),
|
||||
N2(D7),
|
||||
N2(E7),
|
||||
NOP,
|
||||
N2(A6),
|
||||
NOP,
|
||||
NOP,
|
||||
N2(B6),
|
||||
N2(CS7),
|
||||
N2(DS7),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(AS6),
|
||||
],
|
||||
[
|
||||
NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP,
|
||||
N2(E6),
|
||||
N2(FS6),
|
||||
N2(GS6),
|
||||
NOP,
|
||||
N2(CS6),
|
||||
NOP,
|
||||
NOP,
|
||||
N2(DS6),
|
||||
N2(F6),
|
||||
N2(G6),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(D6),
|
||||
],
|
||||
[
|
||||
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,
|
||||
N1(A2),
|
||||
NOP,
|
||||
N1(CS3),
|
||||
N1(B2),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(DS3),
|
||||
N1(E3),
|
||||
N1(C3),
|
||||
N1(D3),
|
||||
N1(AS2),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(CS4),
|
||||
NOP,
|
||||
N1(F4),
|
||||
N1(DS4),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(G4),
|
||||
N1(GS4),
|
||||
N1(E4),
|
||||
N1(FS4),
|
||||
N1(D4),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(F3),
|
||||
NOP,
|
||||
N1(A3),
|
||||
N1(G3),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(B3),
|
||||
N1(C4),
|
||||
N1(GS3),
|
||||
N1(AS3),
|
||||
N1(FS3),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(A2),
|
||||
NOP,
|
||||
N2(CS3),
|
||||
N2(B2),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(DS3),
|
||||
N2(E3),
|
||||
N2(C3),
|
||||
N2(D3),
|
||||
N2(AS2),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(A4),
|
||||
NOP,
|
||||
N1(CS5),
|
||||
N1(B4),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(DS5),
|
||||
N1(E5),
|
||||
N1(C5),
|
||||
N1(D5),
|
||||
N1(AS4),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(A4),
|
||||
NOP,
|
||||
N2(CS5),
|
||||
N2(B4),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(DS5),
|
||||
N2(E5),
|
||||
N2(C5),
|
||||
N2(D5),
|
||||
N2(AS4),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(F3),
|
||||
NOP,
|
||||
N2(A3),
|
||||
N2(G3),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(B3),
|
||||
N2(C4),
|
||||
N2(GS3),
|
||||
N2(AS3),
|
||||
N2(FS3),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(CS4),
|
||||
NOP,
|
||||
N2(F4),
|
||||
N2(DS4),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(G4),
|
||||
N2(GS4),
|
||||
N2(E4),
|
||||
N2(FS4),
|
||||
N2(D4),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(CS2),
|
||||
NOP,
|
||||
N2(F2),
|
||||
N2(DS2),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(G2),
|
||||
N2(GS2),
|
||||
N2(E2),
|
||||
N2(FS2),
|
||||
N2(D2),
|
||||
NOP,
|
||||
],
|
||||
[
|
||||
N1(E6),
|
||||
N1(FS6),
|
||||
N1(GS6),
|
||||
NOP,
|
||||
N1(CS6),
|
||||
NOP,
|
||||
NOP,
|
||||
N1(DS6),
|
||||
N1(F6),
|
||||
N1(G6),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N(FS7, 64),
|
||||
N1(D6),
|
||||
],
|
||||
[
|
||||
N1(C7),
|
||||
N1(D7),
|
||||
N1(E7),
|
||||
NOP,
|
||||
N1(A6),
|
||||
NOP,
|
||||
NOP,
|
||||
N1(B6),
|
||||
N1(CS7),
|
||||
N1(DS7),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(AS6),
|
||||
],
|
||||
[
|
||||
N1(GS7),
|
||||
N1(AS7),
|
||||
N1(C8),
|
||||
NOP,
|
||||
N1(F7),
|
||||
NOP,
|
||||
NOP,
|
||||
N1(G7),
|
||||
N1(A7),
|
||||
N1(B7),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N1(FS7),
|
||||
],
|
||||
[
|
||||
N2(GS7),
|
||||
N2(AS7),
|
||||
N2(C8),
|
||||
NOP,
|
||||
N2(F7),
|
||||
NOP,
|
||||
NOP,
|
||||
N2(G7),
|
||||
N2(A7),
|
||||
N2(B7),
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
NOP,
|
||||
N2(FS7),
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
use crate::midi;
|
||||
use crate::pins;
|
||||
use crate::unwrap;
|
||||
use core::cmp::min;
|
||||
use core::cmp::{min};
|
||||
use embassy_rp::gpio;
|
||||
use embassy_time::{Duration, Instant, Ticker};
|
||||
|
||||
@ -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(13));
|
||||
let mut ticker = Ticker::every(Duration::from_millis(8));
|
||||
|
||||
let chan = midi::MidiChannel::new(0);
|
||||
const MAX_NOTES: usize = 128;
|
||||
@ -66,7 +66,14 @@ impl<const N_ROWS: usize, const N_COLS: usize> KeyMatrix<N_ROWS, N_COLS> {
|
||||
// (for velocity detection) moment key is first touched
|
||||
let mut note_first: [Option<Instant>; MAX_NOTES] = [None; MAX_NOTES];
|
||||
|
||||
let mut counter = 0;
|
||||
|
||||
loop {
|
||||
counter += 1;
|
||||
counter %= 50;
|
||||
let profile = counter == 0;
|
||||
let prof_start = Instant::now();
|
||||
|
||||
for (i, col) in self.col_pins.iter().enumerate() {
|
||||
unwrap(pin_driver.set_output(*col)).await;
|
||||
let input = unwrap(pin_driver.read_all()).await;
|
||||
@ -79,11 +86,12 @@ impl<const N_ROWS: usize, const N_COLS: usize> KeyMatrix<N_ROWS, N_COLS> {
|
||||
let key_active = mask & (1 << row) != 0;
|
||||
match key_action {
|
||||
midi::KeyAction::N1(note) => {
|
||||
if !note_on[note as usize]
|
||||
&& note_first[note as usize].is_none()
|
||||
&& key_active
|
||||
{
|
||||
note_first[note as usize] = Some(Instant::now());
|
||||
if key_active {
|
||||
if note_first[note as usize].is_none() {
|
||||
note_first[note as usize] = Some(Instant::now());
|
||||
}
|
||||
} else if note_first[note as usize].is_some() {
|
||||
note_first[note as usize] = None;
|
||||
}
|
||||
}
|
||||
midi::KeyAction::N2(note) => {
|
||||
@ -92,15 +100,17 @@ impl<const N_ROWS: usize, const N_COLS: usize> KeyMatrix<N_ROWS, N_COLS> {
|
||||
// millisecond duration of keypress
|
||||
let dur =
|
||||
note_first[note as usize].unwrap().elapsed().as_millis();
|
||||
// 1905 millis is the minimum velocity
|
||||
let velocity: u8 = 127 - min(dur / 15, 127) as u8;
|
||||
note_first[note as usize] = None;
|
||||
let velocity: u8 = if dur <= 80 {
|
||||
(127 - dur) as u8
|
||||
} else {
|
||||
(127 - min(dur, 250) / 5 - 70) as u8
|
||||
};
|
||||
log::debug!("{note:?} velocity {velocity} from dur {dur}ms");
|
||||
note_on[note as usize] = true;
|
||||
chan.note_on(note, velocity).await;
|
||||
}
|
||||
} else if note_on[note as usize] {
|
||||
note_on[note as usize] = false;
|
||||
note_first[note as usize] = None;
|
||||
chan.note_off(note, 0).await;
|
||||
}
|
||||
}
|
||||
@ -114,11 +124,15 @@ 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 => {},
|
||||
}
|
||||
midi::KeyAction::NOP => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if profile {
|
||||
log::trace!("profile: scan took {}ms", prof_start.elapsed().as_millis())
|
||||
}
|
||||
ticker.next().await;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ Use cargo fmt to de-messify the output once pasted in the source.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
import sys
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -62,7 +63,7 @@ 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")
|
||||
print(f"{len(row_pins)} rows, {len(col_pins)} cols", file=sys.stderr)
|
||||
print(f"row pins: [{', '.join([str(i) for i in row_pins])}]", file=sys.stderr)
|
||||
print(f"col pins: [{', '.join([str(i) for i in col_pins])}]", file=sys.stderr)
|
||||
print(f"{empty_counter} empty cells", file=sys.stderr)
|
||||
|
@ -86,7 +86,7 @@ impl MidiMsg {
|
||||
/// Note identifiers
|
||||
///
|
||||
/// See src/midi/note_def.py for how this is generated
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Note {
|
||||
A0 = 21,
|
||||
AS0 = 22,
|
||||
|
Loading…
Reference in New Issue
Block a user