nx_song_grade.nx
buildroot/runtime/nx_song_grade.nx
about
nx_song_grade.nx -- INSTRUMENT-LEARN-GAME (Rocksmith-class) rung LG-2: the
score-follower / grader. Given a SONG (a sequence of target notes with
timings) and the PLAYER's recorded audio, it grades every note the player
attempted -- HIT (right note), WRONG (a note, but the wrong one), or MISS
(nothing played) -- and returns an overall score. This is the core teaching
loop of a learn-game: play along, get graded, see what you missed.
Works for ANY monophonic instrument (guitar single notes, bass, violin, voice,
wind) -- it stands on nx_pitch (F0) -> nx_note (F0 -> MIDI). Polyphony
(chords) is a later rung (LG-3, needs the nx_fft spectral path).
SONG layout (caller-owned *i64, 3 words per note):
score[i*3+0] = target MIDI note
score[i*3+1] = note start sample
score[i*3+2] = note duration in samples
nx_song_grade(score, n_notes, player_pcm, fs, tbl, verdicts) -> score_permille
verdicts[i] = SG_HIT / SG_WRONG / SG_MISS
returns hits * 1000 / n_notes (per-mille accuracy)
tbl = a 128-entry note table from nx_note_table.
license_tier: ORIGINAL
module: nishi-core.audio.song_grade
depends: nishi-core.audio.note, nishi-core.audio.pitch
capability: AUDIO_LEARN_GAME
dependencies 3 imports · 1 importers
imports: nx_syscalls_x86_64.nxnx_note.nxnx_pitch.nx
imported by: nx_song_grade_gate.nx
structs
| none |
consts
| 30 | const SG_HIT: i64 = 0 |
| 31 | const SG_WRONG: i64 = 1 |
| 32 | const SG_MISS: i64 = 2 |
| 34 | const SG_SKIP: i64 = 200 // skip the note's attack/onset transient |
| 35 | const SG_WIN: i64 = 600 // analysis window |
| 36 | const SG_MINLAG: i64 = 12 // ~667 Hz max |
| 37 | const SG_MAXLAG: i64 = 200 // ~40 Hz min (covers bass to treble) |
functions
| 39 | func nx_song_grade(score: *i64, n_notes: i64, player: *u8, fs: i64, |