mirror of
https://github.com/eliasstepanik/strudel-docker.git
synced 2026-01-14 15:18:33 +00:00
feat: support n for voicing scales
+ simplify voicing logic
This commit is contained in:
parent
36fa9d81fb
commit
d5c0309885
@ -18,6 +18,7 @@ import {
|
||||
note2midi,
|
||||
midi2note,
|
||||
voiceBelow,
|
||||
scaleStep,
|
||||
} from '../tonleiter.mjs';
|
||||
|
||||
describe('tonleiter', () => {
|
||||
@ -102,6 +103,17 @@ describe('tonleiter', () => {
|
||||
expect(midi2note(61)).toBe('Db4');
|
||||
expect(midi2note(61, true)).toBe('C#4');
|
||||
});
|
||||
test('scaleStep', () => {
|
||||
expect(scaleStep([60, 63, 67], 0)).toBe(60);
|
||||
expect(scaleStep([60, 63, 67], 1)).toBe(63);
|
||||
expect(scaleStep([60, 63, 67], 2)).toBe(67);
|
||||
expect(scaleStep([60, 63, 67], 3)).toBe(72);
|
||||
expect(scaleStep([60, 63, 67], 4)).toBe(75);
|
||||
expect(scaleStep([60, 63, 67], -1)).toBe(55);
|
||||
expect(scaleStep([60, 63, 67], -2)).toBe(51);
|
||||
expect(scaleStep([60, 63, 67], -3)).toBe(48);
|
||||
expect(scaleStep([60, 63, 67], -4)).toBe(43);
|
||||
});
|
||||
test('voiceBelow', () => {
|
||||
const voicingDictionary = {
|
||||
m7: [
|
||||
|
||||
@ -61,7 +61,14 @@ export const midi2note = (midi, sharp = false) => {
|
||||
return pc + oct;
|
||||
};
|
||||
|
||||
export function voiceBelow(maxNote, chord, voicingDictionary, offset = 0) {
|
||||
export function scaleStep(notes, offset) {
|
||||
notes = notes.map((note) => (typeof note === 'string' ? note2midi(note) : note));
|
||||
const octOffset = Math.floor(offset / notes.length) * 12;
|
||||
offset = _mod(offset, 12);
|
||||
return notes[offset % notes.length] + octOffset;
|
||||
}
|
||||
|
||||
export function voiceBelow(maxNote, chord, voicingDictionary, offset = 0, n) {
|
||||
const [root, symbol] = tokenizeChord(chord);
|
||||
const maxPc = note2pc(maxNote);
|
||||
const maxChroma = pc2chroma(maxPc);
|
||||
@ -81,15 +88,18 @@ export function voiceBelow(maxNote, chord, voicingDictionary, offset = 0) {
|
||||
return diff;
|
||||
});
|
||||
|
||||
const octDiff =
|
||||
offset >= 0 ? Math.ceil(offset / voicings.length) * 12 : Math.floor(Math.abs(offset) / voicings.length) * -12;
|
||||
bestIndex = _mod(bestIndex + offset, voicings.length);
|
||||
const voicing = voicings[bestIndex];
|
||||
const octDiff = Math.ceil(offset / voicings.length) * 12;
|
||||
const indexWithOffset = _mod(bestIndex + offset, voicings.length);
|
||||
const voicing = voicings[indexWithOffset];
|
||||
const maxMidi = note2midi(maxNote);
|
||||
const topMidi = maxMidi - chromaDiffs[bestIndex] + octDiff;
|
||||
const topMidi = maxMidi - chromaDiffs[indexWithOffset] + octDiff;
|
||||
|
||||
const voicingMidi = voicing.map((v) => topMidi - voicing[voicing.length - 1] + v);
|
||||
return voicingMidi.map((n) => midi2note(n));
|
||||
const notes = voicingMidi.map((n) => midi2note(n));
|
||||
if (n !== undefined) {
|
||||
return [scaleStep(notes, n)];
|
||||
}
|
||||
return notes;
|
||||
}
|
||||
|
||||
// https://github.com/tidalcycles/strudel/blob/14184993d0ee7d69c47df57ac864a1a0f99a893f/packages/tonal/tonleiter.mjs
|
||||
|
||||
@ -139,13 +139,13 @@ export const rootNotes = register('rootNotes', function (octave, pat) {
|
||||
export const voicing = register('voicing', function (dictionary, pat) {
|
||||
return pat
|
||||
.fmap((value) => {
|
||||
let { voiceMax: max, voiceBelow: below, voiceOffset: offset, chord, ...rest } = value;
|
||||
let { voiceMax: max, voiceBelow: below, voiceOffset: offset, chord, n, ...rest } = value;
|
||||
let top = max || below;
|
||||
top = top?.note || top || 'c5';
|
||||
if (typeof dictionary === 'string') {
|
||||
dictionary = voicingRegistry[dictionary]?.dictionary;
|
||||
}
|
||||
let notes = voiceBelow(top, chord, dictionary, offset);
|
||||
let notes = voiceBelow(top, chord, dictionary, offset, n);
|
||||
if (below) {
|
||||
notes = notes.filter((n) => note2midi(n) !== note2midi(top));
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user