Skip to content

fix: correct hz_to_index to firmware quarter-tone scale#136

Merged
g4bri3lDev merged 2 commits into
OpenDisplay:mainfrom
davelee98:fix/buzzer-hz-index
Jul 17, 2026
Merged

fix: correct hz_to_index to firmware quarter-tone scale#136
g4bri3lDev merged 2 commits into
OpenDisplay:mainfrom
davelee98:fix/buzzer-hz-index

Conversation

@davelee98

Copy link
Copy Markdown
Contributor

Summary

hz_to_index() still implemented the pre-firmware-PR-98 linear Hz→index map, so every pitch chosen by Hz (including the Home Assistant activate_buzzer service) played the wrong note — e.g. hz_to_index(440) returned 2, which the firmware folds to ~466 Hz (A♯4), a semitone sharp of A4.

This replaces it with the inverse of the firmware's quarter-tone scale (Freq(idx) = 13.75 · 2^(idx/24)):

idx = clamp(round(24 · log2(hz / 13.75)), 1, 255)   for hz > 0
idx = 0                                              for hz <= 0 (rest)

Landmarks verified against BUZZER_MUSIC_PROTOCOL_REFERENCE.md §4: 440→120 (A4 exact), 880→144 (octave = 24 steps), 400→117, 6200→212, 12000→234.

Hz-based input surface is unchanged (targeted correctness fix, not an API redesign); no wire-format or folding changes. Fix plan: opendisplay-protocol/docs/buzzer-hz-index-fix-plan.md.

Release note

Intended to ship in the same minor as #135 (melody notation) so downstream consumers bump their pin once.

🤖 Generated with Claude Code

hz_to_index() still implemented the pre-PR-98 linear pitch map
(idx = round(1 + (hz-400)*254/11600)), so every Hz-selected pitch played
the wrong note -- e.g. 440 Hz returned index 2, which the firmware folds
to A#4 (~466 Hz), a semitone sharp of the requested A4.

Replace it with the inverse of the firmware's quarter-tone equal-tempered
scale Freq(idx) = 13.75 * 2**(idx/24) Hz:

    idx = clamp(round(24 * log2(hz / 13.75)), 1, 255)   for hz > 0
    idx = 0                                              for hz <= 0

Now hz_to_index(440) == 120 (exact A4) and the octave delta is 24.
The Hz-based input surface is unchanged; the firmware still octave-folds
out-of-window indices, so the host does not replicate that.

Rewrite TestHzToIndex for the new mapping and add landmark, octave, and
firmware-forward round-trip coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@davelee98
davelee98 requested a review from g4bri3lDev as a code owner July 16, 2026 05:37
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Resolves the conflict with OpenDisplay#135 (melody notation parser), which landed on
main and edited the same imports/constants block in buzzer_activate.py.
The changes are complementary:

- imports: keep math (this PR) + re/Sequence (OpenDisplay#135)
- constants: keep _ANCHOR_HZ, dedupe the duplicated _STEPS_PER_OCTAVE
- hz_to_index: keep this PR's quarter-tone fix; OpenDisplay#135's melody code unchanged

Full suite (852 tests), mypy --strict, and ruff pass on the merge result.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V74DxueL8qykYiobc3kmyJ
@g4bri3lDev
g4bri3lDev merged commit 0f9efa1 into OpenDisplay:main Jul 17, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants