diff --git a/screen.js b/screen.js index bd9025c..ffda211 100644 --- a/screen.js +++ b/screen.js @@ -13628,8 +13628,19 @@ function createNoteDetector(options = {}) { const info = (hw && hw.getSongInfo) ? hw.getSongInfo() : null; if (!info) return; if (info.arrangement) currentArrangement = _ndArrangementKindFromName(info.arrangement); - if (Array.isArray(info.tuning)) tuningOffsets = info.tuning; - if (Number.isFinite(info.capo)) capo = info.capo; + // Tuning and capo of the chart as the highway shows it. A chart- + // transform provider (core #952) can re-fret the notes and change the + // tuning to match, e.g. a drop tuning shown as standard shapes; + // getSongInfo() keeps the ORIGINAL metadata, so scoring the transformed + // frets against it would expect the wrong pitch on re-fretted strings. + // Older hosts without the effective getters fall back to songInfo. + let effTuning = null, effCapo = null; + try { effTuning = (hw && typeof hw.getTuning === 'function') ? hw.getTuning() : null; } catch (_) {} + try { effCapo = (hw && typeof hw.getCapo === 'function') ? hw.getCapo() : null; } catch (_) {} + const tuning = Array.isArray(effTuning) && effTuning.length ? effTuning : info.tuning; + if (Array.isArray(tuning)) tuningOffsets = tuning; + if (Number.isFinite(effCapo)) capo = effCapo; + else if (Number.isFinite(info.capo)) capo = info.capo; // String-count resolution, in precedence order: // 1. hw.getStringCount() — host's authoritative count. Asked @@ -13654,7 +13665,7 @@ function createNoteDetector(options = {}) { if (Number.isFinite(hostStringCount)) { currentStringCount = hostStringCount; } else if (info.arrangement) { - const tuneLen = Array.isArray(info.tuning) ? info.tuning.length : null; + const tuneLen = Array.isArray(tuning) ? tuning.length : null; const consistent = currentArrangement === 'bass' ? (tuneLen === 4 || tuneLen === 5) : (tuneLen === 6 || tuneLen === 7 || tuneLen === 8); @@ -13663,8 +13674,8 @@ function createNoteDetector(options = {}) { } else { currentStringCount = currentArrangement === 'bass' ? 4 : 6; } - } else if (Array.isArray(info.tuning)) { - currentStringCount = info.tuning.length; + } else if (Array.isArray(tuning)) { + currentStringCount = tuning.length; } } diff --git a/test/chart_state_sync.test.js b/test/chart_state_sync.test.js index 16445e3..3c892d5 100644 --- a/test/chart_state_sync.test.js +++ b/test/chart_state_sync.test.js @@ -230,3 +230,54 @@ test('older host without getStringCount: bass with arrangement XML-padded 6-entr 'bass + tuning.length=6 (arrangement XML pad) must fall back to bass-4'); det.destroy(); }); + +test('a chart transform\'s effective tuning and capo win over the original song info', () => { + // A provider re-fretted a Drop D chart into D-standard shapes: the + // highway's getTuning() reports the uniform tuning the frets now match, + // while getSongInfo() keeps the original Drop D metadata. + const core = loadDetectionCore(); + const det = mountDetectorWithSong(core, { + arrangement: 'Lead', tuning: [-2, 0, 0, 0, 0, 0], capo: 0, stringCount: 6, + }); + core.highway.getTuning = () => [-2, -2, -2, -2, -2, -2]; + core.highway.getCapo = () => 0; + det._syncChartStateFromHw(); + let state = det._getChartState(); + assert.deepEqual([...state.tuningOffsets], [-2, -2, -2, -2, -2, -2]); + assert.equal(state.capo, 0); + + // A transform that moves the capo is honoured too. + core.highway.getCapo = () => 3; + det._syncChartStateFromHw(); + state = det._getChartState(); + assert.equal(state.capo, 3); + det.destroy(); +}); + +test('older hosts without getTuning/getCapo keep using the song info', () => { + const core = loadDetectionCore(); + const det = mountDetectorWithSong(core, { + arrangement: 'Lead', tuning: [-2, 0, 0, 0, 0, 0], capo: 2, stringCount: 6, + }); + delete core.highway.getTuning; + delete core.highway.getCapo; + det._syncChartStateFromHw(); + const state = det._getChartState(); + assert.deepEqual([...state.tuningOffsets], [-2, 0, 0, 0, 0, 0]); + assert.equal(state.capo, 2); + det.destroy(); +}); + +test('an empty or throwing effective tuning falls back to the song info', () => { + const core = loadDetectionCore(); + const det = mountDetectorWithSong(core, { + arrangement: 'Bass', tuning: [-1, -1, -1, -1], capo: 0, stringCount: 4, + }); + core.highway.getTuning = () => []; + core.highway.getCapo = () => { throw new Error('boom'); }; + det._syncChartStateFromHw(); + const state = det._getChartState(); + assert.deepEqual([...state.tuningOffsets], [-1, -1, -1, -1]); + assert.equal(state.capo, 0); + det.destroy(); +});