Harden freqs_to_midis against NaN/Inf; badges read exact tuningMidis

Follow-ups to #829 (CodeRabbit's review nit + the consumer adoption the PR
body promised):

- freqs_to_midis: reject non-finite frequencies (NaN/Infinity) — a provider
  handing one through would otherwise raise inside int(round(...)) and 500
  GET /api/tunings. Tests cover nan/inf/-inf alongside the existing garbage
  cases.

- v3 instrument badge: TUNING_NOTE now prefers the exact integer midis the
  server serves (tuningMidis) over reconstructing the note from the lowest
  string's frequency via log2 against a hardcoded 440 — which can land a
  semitone off at non-440 reference pitches. Frequency path kept as the
  fallback for older cached responses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MS2YFb6UUSwJVV6CmEa25i
Signed-off-by: ChrisBeWithYou <christian.a.cowan@gmail.com>
This commit is contained in:
ChrisBeWithYou
2026-07-12 15:24:39 -05:00
co-authored by Claude Fable 5
parent 8d3db5f42c
commit ffc52f13ce
3 changed files with 18 additions and 4 deletions
+11 -2
View File
@@ -120,11 +120,20 @@
if (!r.ok) return;
const data = await r.json();
_tuningsByKey = data.tunings || {};
// Build TUNING_NOTE from the first (lowest) string frequency of each tuning.
// Build TUNING_NOTE from the lowest string of each tuning. Prefer the
// exact integer midis the server now sends (tuningMidis, #829) — the
// frequency path reconstructs the note via log2 against a hardcoded
// 440 and can land a semitone off at non-440 reference pitches.
// Frequencies remain the fallback for older cached responses.
const midisByKey = data.tuningMidis || {};
TUNING_NOTE = {};
for (const key of Object.keys(_tuningsByKey)) {
for (const [name, freqs] of Object.entries(_tuningsByKey[key])) {
if (!(name in TUNING_NOTE) && Array.isArray(freqs) && freqs.length > 0) {
if (name in TUNING_NOTE) continue;
const midis = midisByKey[key] && midisByKey[key][name];
if (Array.isArray(midis) && midis.length > 0 && Number.isFinite(midis[0])) {
TUNING_NOTE[name] = NOTE_NAMES[((midis[0] % 12) + 12) % 12];
} else if (Array.isArray(freqs) && freqs.length > 0) {
TUNING_NOTE[name] = _freqToNote(freqs[0]);
}
}