fix(piano): dedupe duplicate absolute notes in pianoVoicing (task L-13)
A forced rootless sus2 collided the suspended-2nd and synthesized 9th onto one key (notes [26,26,31]). Dedupe at the single funnel point ([...new Set(placed)]); pcs/bass derived from the deduped array. No-op on default/chain path (L-11 render unaffected). Critic: Set merges only identical absolute integers (same key) so no needed note is lost. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+9
-1
@@ -339,7 +339,15 @@ export function pianoVoicing(chord, opts = {}) {
|
||||
const style = BUILDERS[opts.style] ? opts.style : defaultStyle(quality)
|
||||
|
||||
const base = BUILDERS[style](rootPc, quality)
|
||||
const notes = placeVoicing(base, rootPc, opts.prev)
|
||||
const placed = placeVoicing(base, rootPc, opts.prev)
|
||||
|
||||
// Dedupe ABSOLUTE notes: two voices can collide on one key (e.g. a forced
|
||||
// rootless sus2 lands the suspended-2nd "3rd-slot" tone and the synthesized
|
||||
// 9th on the same absolute key, pc 2 → notes [26,26,31]). A duplicate value
|
||||
// would stack two highlights on one key in the renderer, so we drop the
|
||||
// redundant collided note here — the single point every style/quality flows
|
||||
// through — keeping the first occurrence. A no-op when nothing collides.
|
||||
const notes = [...new Set(placed)]
|
||||
|
||||
// pcs in the voiced order (low→high), deduped — what keys light up.
|
||||
const pcs = [...new Set(notes.map((n) => mod12(n)))]
|
||||
|
||||
Reference in New Issue
Block a user