mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 19:29:33 +00:00
feat(tuner): mic-verify — promote working tuning assumed→verified via a per-string check (working-tuning PR 9b) (#670)
* feat(tuner): mic-verify — promote the working tuning assumed→verified via a per-string check (working-tuning PR 9b) Adds the choreographed per-string mic verification the design reserved for 'verified' provenance (audio-engine's honesty rule — nothing else may claim it): the player plays each string, and once every one reads in tune (±6 cents) and holds stable for 8 frames, the tuner stamps the working tuning provenance:'verified' + verifiedStrings via workingTuning.set. - screen.js: a pure verify state machine (verifyStart/verifyFeed/verifyCancel/ verifyState, exposed on the tuner API) + the set-verified writer; cancels on close. - ui.js: updateUI feeds each processed frame (matched string + cents) into the session; a "Verify tuning" button + per-string progress + status, shown for a selected (non-free) tuning. Pairs with the 9a lifecycle: a 'verified' decays back to 'assumed' on the next song load, so mic-verify is a per-session confidence boost, never a sticky claim. Tests: tests/js/tuner_auto_open.test.js +4 (all-strings->verified, out-of-tune never completes, streak resets on drift, API exposed / only it claims verified) — 33/33. The state machine is headless-verified with synthetic frames; the real per-string mic detection + the button flow need an on-device pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF * fix(tuner): mic-verify writes the confirmed tuning + no clobber + stricter streak (PR #670 review) Review fixes for mic-verify (working-tuning PR 9b): - 'verified' could attach to STALE offsets: _publishVerified stamped provenance without writing offsets, so the slot's pre-tuning offsets got marked verified. verifyStart(targets, offsets) now captures the confirmed tuning's offsets, and _publishVerified writes offsets + stringCount + instrument + referencePitch + verifiedStrings ATOMICALLY with provenance:'verified' into the selected slot (and refuses to stamp verified with no concrete offsets). - The assumed publish-on-clear immediately clobbered a just-earned 'verified': disable() now skips it when a mic-verify wrote verified this session (_verifiedPublished). - The per-string streak could accumulate across silence / wrong-string frames. verifyFeed now requires CONSECUTIVE in-tune frames: the one confirmed string advances, every other unfinished string resets each frame. - A mid-verify tuning change (song switch) left stale captured offsets; verify is now cancelled in _syncCurrentTuning when the song tuning changes. Tests: verify writes the confirmed offsets (not stale); source-guard for the no-clobber path. 47 tuner + 77 tuner/capability tests green. Codex-reviewed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: byrongamatos <xasiklas@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
byrongamatos
parent
115c96a3f0
commit
2910a3c8cb
@@ -697,3 +697,56 @@ test('a transient /api/settings failure is not cached — the next coverage read
|
||||
const r2 = await api.coverageReport(DROP_D); // retry: settings now readable → a real report
|
||||
assert.equal(r2.retune.length, 1, 'the retry actually computes coverage (Drop-D low string vs standard)');
|
||||
});
|
||||
// ── PR 9b: mic-verify (assumed -> verified via a per-string check) ──────────
|
||||
const VERIFY_TARGETS = [82.41, 110, 146.83, 196, 246.94, 329.63]; // guitar-6 standard freqs
|
||||
|
||||
test('mic-verify: all strings in-tune-and-stable promotes to verified (with the confirmed offsets)', () => {
|
||||
const sandbox = createTunerSandbox();
|
||||
const sets = [];
|
||||
sandbox.window.feedBack.workingTuning = { get: () => ({ offsets: [0, 0, 0, 0, 0, 0] }), set: (n, o) => sets.push({ n, o }) };
|
||||
const api = sandbox.window._tunerAutoOpen;
|
||||
assert.ok(api.verifyStart(VERIFY_TARGETS, [-2, 0, 0, 0, 0, 0])); // verifying a Drop-D tuning
|
||||
for (let i = 0; i < 8; i++) api.verifyFeed(82.41, 2); // one string alone isn't enough
|
||||
assert.equal(api.verifyState().complete, false);
|
||||
for (const f of VERIFY_TARGETS) { for (let i = 0; i < 8; i++) api.verifyFeed(f, 2); }
|
||||
assert.equal(api.verifyState().complete, true);
|
||||
assert.equal(sets.length, 1);
|
||||
assert.equal(sets[0].o.provenance, 'verified');
|
||||
// The stamp carries the CONFIRMED offsets, not whatever stale offsets the slot held.
|
||||
assert.deepEqual(sets[0].n.offsets, [-2, 0, 0, 0, 0, 0]);
|
||||
assert.equal(sets[0].n.verifiedStrings.length, 6);
|
||||
});
|
||||
|
||||
test('mic-verify: an out-of-tune string never completes; cancel clears', () => {
|
||||
const sandbox = createTunerSandbox();
|
||||
sandbox.window.feedBack.workingTuning = { get: () => ({}), set() {} };
|
||||
const api = sandbox.window._tunerAutoOpen;
|
||||
api.verifyStart([82.41, 110]);
|
||||
for (let i = 0; i < 30; i++) api.verifyFeed(82.41, 25); // 25c off -> never passes
|
||||
assert.equal(api.verifyState().complete, false);
|
||||
assert.deepEqual(api.verifyState().done, [false, false]);
|
||||
api.verifyCancel();
|
||||
assert.equal(api.verifyState(), null);
|
||||
});
|
||||
|
||||
test('mic-verify: the in-tune streak resets if a frame drifts out', () => {
|
||||
const sandbox = createTunerSandbox();
|
||||
sandbox.window.feedBack.workingTuning = { get: () => ({}), set() {} };
|
||||
const api = sandbox.window._tunerAutoOpen;
|
||||
api.verifyStart([100]);
|
||||
for (let i = 0; i < 5; i++) api.verifyFeed(100, 2); // 5 in tune (need 8)
|
||||
api.verifyFeed(100, 30); // drift out -> streak resets
|
||||
for (let i = 0; i < 7; i++) api.verifyFeed(100, 2); // 7 more (not yet 8 in a row)
|
||||
assert.equal(api.verifyState().done[0], false);
|
||||
api.verifyFeed(100, 2); // 8th consecutive -> done
|
||||
assert.equal(api.verifyState().complete, true);
|
||||
});
|
||||
|
||||
test('the tuner exposes the mic-verify API and only it claims verified (screen.js)', () => {
|
||||
const src = fs.readFileSync(TUNER_SCREEN_JS, 'utf8');
|
||||
assert.match(src, /verifyStart:/);
|
||||
assert.match(src, /verifyFeed:/);
|
||||
assert.match(src, /provenance: 'verified'/); // the only place that stamps verified
|
||||
// A just-earned 'verified' is not clobbered by the assumed publish-on-clear.
|
||||
assert.match(src, /wasAutoOpened && !_verifiedPublished/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user