Spaces:
Running
Running
Download scripts/note_correspondence.py from jasonmdong/notepilot: direct link, hf CLI and curl.
- Browser
- Download file 2.83 kB
-
https://huggingface.co/spaces/jasonmdong/notepilot/resolve/main/scripts/note_correspondence.py
- Command line
-
hf download hf://spaces/jasonmdong/notepilot/scripts/note_correspondence.py
-
curl -L -o note_correspondence.py https://huggingface.co/spaces/jasonmdong/notepilot/resolve/main/scripts/note_correspondence.py
2.83 kB
| # Note-by-note: what was PLAYED vs what the detector SAW vs what REGISTERED, | |
| # and for every mismatch, which of the three failures it was. | |
| import json, sys, bisect, collections, statistics | |
| NAMES=['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'] | |
| def pn(m): return f"{NAMES[m%12]}{m//12-1}" | |
| d=json.load(open(sys.argv[1])); evs=d['events'] | |
| keys=[(e['t'], e['midi'], e.get('name')) for e in evs if e.get('type')=='input.key' and e.get('midi')] | |
| notes=[(e['t'], e.get('midi')) for e in evs if e.get('type')=='mic.note' and e.get('midi')] | |
| notes.sort(); nts=[n[0] for n in notes] | |
| fr=[e for e in evs if e.get('type')=='follow.frame'] | |
| fr.sort(key=lambda e: e.get('t',0)); fts=[e.get('t',0) for e in fr] | |
| holds=collections.defaultdict(list) | |
| for e in evs: | |
| if e.get('type')=='follow.hold': holds[round(e.get('t',0)/100)].append(e.get('reason')) | |
| WIN=800 | |
| rows=[]; why=collections.Counter() | |
| for t,m,name in keys: | |
| i=bisect.bisect_left(nts,t); j=bisect.bisect_left(nts,t+WIN) | |
| got=[p for _,p in notes[i:j]] | |
| hit = m in got | |
| a=bisect.bisect_left(fts,t); b=bisect.bisect_left(fts,t+WIN) | |
| seg=fr[a:b] | |
| peaks=[e.get('peak') for e in seg if isinstance(e.get('peak'),int)] | |
| pmax=max((e.get('p') or 0) for e in seg) if seg else 0 | |
| evmax=max((e.get('ev') or 0) for e in seg) if seg else 0 | |
| gated=sum(1 for e in seg if not e.get('inf')) | |
| ownrow=[] | |
| for e in seg: | |
| sal=e.get('sal') | |
| if sal and 0 <= m-33 < len(sal): ownrow.append(sal[m-33]) | |
| own=max(ownrow) if ownrow else 0 | |
| med=statistics.median([statistics.median(e['sal']) for e in seg if e.get('sal')]) if seg else 0 | |
| sawit = any(p==m for p in peaks) | |
| hs=[] | |
| for k in range(round(t/100), round((t+WIN)/100)+1): hs += holds.get(k,[]) | |
| top = collections.Counter(hs).most_common(1) | |
| if hit: cls='OK' | |
| elif not seg: cls='no frames' | |
| elif gated == len(seg): cls='UNDER GATE (level)' | |
| elif own <= med: cls='NOT HEARD (no energy at its pitch)' | |
| elif sawit: cls='HEARD, REFUSED (peak was the right note)' | |
| else: cls='HEARD SOMETHING ELSE (peak != your note)' | |
| if not hit: why[cls]+=1 | |
| rows.append((t,name,cls,pn(max(set(peaks),key=peaks.count)) if peaks else '-',round(pmax,2),round(evmax,1),round(own),round(med),top[0][0] if top else '')) | |
| print(f"PLAYED {len(keys)} notes; detected {sum(1 for r in rows if r[2]=='OK')} ({100*sum(1 for r in rows if r[2]=='OK')/len(rows):.0f}%)\n") | |
| print("WHY EACH MISS HAPPENED") | |
| for k,v in why.most_common(): print(f" {v:4} {k}") | |
| print(f"\n{'time':>8} {'played':>7} {'peak':>6} {'p':>5} {'ev':>6} {'ownRow':>7} {'median':>7} verdict / hold") | |
| for t,name,cls,peak,pmax,evmax,own,med,hold in rows[:40]: | |
| flag='' if cls=='OK' else ' *' | |
| print(f"{t/1000:8.2f} {str(name):>7} {peak:>6} {pmax:5.2f} {evmax:6.1f} {own:7} {med:7} {cls}{(' / '+hold) if hold else ''}{flag}") | |