add octaves control for arpeggios

This commit is contained in:
Felix Roos 2023-07-23 21:18:34 +02:00
parent d231ded7a2
commit 52b8d9d361
3 changed files with 9 additions and 8 deletions

View File

@ -579,6 +579,7 @@ const generic_params = [
['dictionary', 'dict'], // which dictionary to use for the voicings
['anchor'], // the top note to align the voicing to, defaults to c5
['offset'], // how the voicing is offset from the anchored position
['octaves'], // how many octaves are voicing steps spread apart, defaults to 1
[['mode', 'anchor']], // below = anchor note will be removed from the voicing, useful for melody harmonization
/**

View File

@ -76,11 +76,11 @@ export const midi2note = (midi, sharp = false) => {
return pc + oct;
};
export function scaleStep(notes, offset) {
export function scaleStep(notes, offset, octaves = 1) {
notes = notes.map((note) => (typeof note === 'string' ? noteToMidi(note) : note));
const octOffset = Math.floor(offset / notes.length) * 12;
offset = _mod(offset, 12);
return notes[offset % notes.length] + octOffset;
const octOffset = Math.floor(offset / notes.length) * octaves * 12;
offset = _mod(offset, notes.length);
return notes[offset] + octOffset;
}
// different ways to resolve the note to compare the anchor to (see renderVoicing)
@ -90,7 +90,7 @@ let modeTarget = {
above: (v) => v[0],
};
export function renderVoicing({ chord, dictionary, offset = 0, n, mode = 'below', anchor = 'c5' }) {
export function renderVoicing({ chord, dictionary, offset = 0, n, mode = 'below', anchor = 'c5', octaves = 1 }) {
const [root, symbol] = tokenizeChord(chord);
const rootChroma = pc2chroma(root);
anchor = anchor?.note || anchor;
@ -124,7 +124,7 @@ export function renderVoicing({ chord, dictionary, offset = 0, n, mode = 'below'
notes = notes.filter((_, i) => voicingMidi[i] !== noteToMidi(anchor));
}
if (n !== undefined) {
return [scaleStep(notes, n)];
return [scaleStep(notes, n, octaves)];
}
return notes;
}

View File

@ -196,10 +196,10 @@ export const voicing = register('voicing', function (pat) {
.fmap((value) => {
// destructure voicing controls out
value = typeof value === 'string' ? { chord: value } : value;
let { dictionary = 'default', chord, anchor, offset, mode, n, ...rest } = value;
let { dictionary = 'default', chord, anchor, offset, mode, n, octaves, ...rest } = value;
dictionary =
typeof dictionary === 'string' ? voicingRegistry[dictionary] : { dictionary, mode: 'below', anchor: 'c5' };
let notes = renderVoicing({ ...dictionary, chord, anchor, offset, mode, n });
let notes = renderVoicing({ ...dictionary, chord, anchor, offset, mode, n, octaves });
return stack(...notes)
.note()