From cb046a3548a2380971fa58ca84d3d54f73fc9178 Mon Sep 17 00:00:00 2001 From: eefano <77832+eefano@users.noreply.github.com> Date: Mon, 1 Jun 2026 02:16:48 +0200 Subject: [PATCH] fixes 'above' and 'root' modes in renderVoiciing() --- packages/tonal/tonleiter.mjs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/tonal/tonleiter.mjs b/packages/tonal/tonleiter.mjs index 233129641..791602835 100644 --- a/packages/tonal/tonleiter.mjs +++ b/packages/tonal/tonleiter.mjs @@ -1,4 +1,4 @@ -import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote } from '@strudel/core'; +import { isNote, isNoteWithOctave, _mod, noteToMidi, tokenizeNote, undegrade } from '@strudel/core'; import { Interval, Scale } from '@tonaljs/tonal'; // https://codesandbox.io/s/stateless-voicings-g2tmz0?file=/src/lib.js:0-2515 @@ -135,6 +135,12 @@ let modeTarget = { above: (v) => v[0], root: (v) => v[0], }; +let modeMult = { + below: 1, + duck: 1, + above: -1, + root: -1, +}; export function renderVoicing({ chord, dictionary, offset = 0, n, mode = 'below', anchor = 'c5', octaves = 1 }) { const [root, symbol] = tokenizeChord(chord); @@ -144,17 +150,18 @@ export function renderVoicing({ chord, dictionary, offset = 0, n, mode = 'below' const voicings = dictionary[symbol].map((voicing) => (typeof voicing === 'string' ? voicing.split(' ') : voicing).map(step2semitones), ); + const mult = modeMult[mode]; let minDistance, bestIndex; // calculate distances up from voicing top notes let chromaDiffs = voicings.map((v, i) => { const targetStep = modeTarget[mode](v); - const diff = _mod(anchorChroma - targetStep - rootChroma, 12); + const diff = _mod((anchorChroma - targetStep - rootChroma) * mult, 12); if (minDistance === undefined || diff < minDistance) { minDistance = diff; bestIndex = i; } - return diff; + return diff * mult; }); if (mode === 'root') { bestIndex = 0;