mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-07-21 20:31:21 +00:00
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:
parent
8d3db5f42c
commit
ffc52f13ce
@ -101,14 +101,16 @@ def open_midis_to_freqs(midis: list[int], reference_pitch: float = DEFAULT_REFER
|
||||
def freqs_to_midis(freqs: list[float], reference_pitch: float = DEFAULT_REFERENCE_PITCH) -> list[int] | None:
|
||||
"""Return absolute open-string MIDI notes for frequencies at the supplied
|
||||
A4 reference — the inverse of open_midis_to_freqs. None if any entry is
|
||||
non-numeric or non-positive (a provider could hand us anything)."""
|
||||
non-numeric, non-finite, or non-positive (a provider could hand us
|
||||
anything; NaN/Infinity would otherwise raise inside int(round(...)) and
|
||||
500 the /api/tunings endpoint)."""
|
||||
out: list[int] = []
|
||||
for f in freqs:
|
||||
try:
|
||||
f = float(f)
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
if f <= 0:
|
||||
if not math.isfinite(f) or f <= 0:
|
||||
return None
|
||||
out.append(int(round(69 + 12 * math.log2(f / reference_pitch))))
|
||||
return out
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -275,4 +275,7 @@ def test_freqs_to_midis_rejects_garbage():
|
||||
from tunings import freqs_to_midis
|
||||
assert freqs_to_midis([82.41, 0]) is None # non-positive
|
||||
assert freqs_to_midis([82.41, "x"]) is None # non-numeric
|
||||
assert freqs_to_midis([float("nan")]) is None # non-finite (would raise in int(round(...)))
|
||||
assert freqs_to_midis([float("inf")]) is None # non-finite
|
||||
assert freqs_to_midis([float("-inf")]) is None # non-finite
|
||||
assert freqs_to_midis([]) == [] # vacuously fine
|
||||
|
||||
Loading…
Reference in New Issue
Block a user