Public
Edited
Jun 25, 2023
Importers
Insert cell
Insert cell
voiceLeadChords("Eb6", "Edim7")
Insert cell
function voiceLeadNotes(sourceNotes, targetNotes) {
const len = targetNotes.length;

const rotations = Tonal.Range.numeric([0, len - 1]).map(i => ({inversion: i, source: sourceNotes, target: rotate(targetNotes, i)}));

const distances = rotations.map(rot => ({...rot, distance: rot.target.map((r, i) => semitoneDistance(sourceNotes[i], rot.target[i]))}))

function compare(a, b) {
return sumAbs(a.distance) < sumAbs(b.distance)
}

return distances.reduce((smallest, curr) => compare(curr, smallest) ? curr : smallest, distances[0])
}

Insert cell
function voiceLeadChords(source, target) {
return voiceLeadNotes(Tonal.Chord.get(source).notes, Tonal.Chord.get(target).notes)
}
Insert cell
rotate = (arr, count = 0) => {
return [...arr.slice(count, arr.length), ...arr.slice(0, count)];
};
Insert cell
function sumAbs(arr) {
return arr.reduce((acc, num) => acc + Math.abs(num), 0);
}
Insert cell
function semitoneDistance(source, target) {
const dist = Tonal.Interval.distance(source, target);
const inverted = Tonal.Interval.distance(target, source);
const interval = Tonal.Interval.get(dist);
const intervalInverted = Tonal.Interval.get(inverted);

return interval.semitones <= intervalInverted.semitones ?
interval.semitones :
intervalInverted.semitones > 0 ? intervalInverted.semitones * -1 : 0;
}
Insert cell
Insert cell
test("Voice leading", t => {
t.deepEqual(voiceLeadChords("F6", "D7"), [1, 0, 0, 0])
t.deepEqual(voiceLeadChords("Bb6", "C#m6"), [0, -1, -1, 1])
t.deepEqual(voiceLeadChords("Cdim7", "Bdim7"), [-1, -1, -1, -1])
})
Insert cell
({
targetInversion: 1,
movement: [1, 0, 0, 0],
absDistance: 1
})
Insert cell
import {test} from "@davidblurton/test"
Insert cell
Tonal = import('https://unpkg.com/tonal@5.0.0/dist/index.mjs?module')
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more