fix(song): make bass detection instrument-type-aware, not name-only (#1019)
ship-ci / ci (push) Waiting to run

Editor now authors an arrangement's instrument as first-class data (a manifest
'type' field). Core dropped it: the sloppak loader never read 'type', and
'is this a bass?' was defined three different ways across call sites (name-only
in note_pitch_midi and the highway scale-degree path; path_bass+name in bass
selection; name-only in arrangement_string_count). So an authored type=bass
chart not named 'bass' got 6-string lane counts and guitar open-string MIDI.

- Add optional Arrangement.type; sloppak load_song lifts the manifest type onto it
- Add arrangement_is_bass(arr) = type=='bass' OR path_bass OR 'bass' in name
  (None/whitespace safe), and route string count, note_pitch_midi, the highway
  scale-degree base, and bass-player selection through it
- Back-compat: no bass signal -> unchanged 6-string / guitar behavior

Companion to editor #335 (first-class instrument type). Scale degrees are
display-only and never feed a grader.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Byron Gamatos
2026-07-21 13:30:54 +02:00
committed by GitHub
co-authored by Claude Opus 4.8
parent 605dbdfd25
commit e0270e5c30
4 changed files with 130 additions and 10 deletions
+3 -3
View File
@@ -26,6 +26,7 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from song import (
anchor_to_wire,
arrangement_is_bass,
arrangement_string_count,
base_open_string_midis,
chord_template_to_wire,
@@ -273,9 +274,8 @@ async def highway_ws(websocket: WebSocket, filename: str, arrangement: int = -1,
bass_idxs = [
i
for i, a in enumerate(song.arrangements)
if getattr(a, "path_bass", False)
if arrangement_is_bass(a)
or (smart_names[i] or "").lower().startswith("bass")
or "bass" in (getattr(a, "name", "") or "").lower()
]
if bass_idxs:
# Among the bass parts: (1) honor the saved default-arrangement
@@ -1014,7 +1014,7 @@ async def highway_ws(websocket: WebSocket, filename: str, arrangement: int = -1,
# base[string] + offset + capo + fret (matches the tuner / open-string
# labels). arrangement_string_count is O(notes), so compute once here.
_base = base_open_string_midis(
arrangement_string_count(arr), "bass" in (arr.name or "").lower())
arrangement_string_count(arr), arrangement_is_bass(arr))
_capo = int(getattr(arr, "capo", 0) or 0)
def _fill_scale_degree(wire: dict, n, t: float) -> None: