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 ['dictionary', 'dict'], // which dictionary to use for the voicings
['anchor'], // the top note to align the voicing to, defaults to c5 ['anchor'], // the top note to align the voicing to, defaults to c5
['offset'], // how the voicing is offset from the anchored position ['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 [['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; 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)); notes = notes.map((note) => (typeof note === 'string' ? noteToMidi(note) : note));
const octOffset = Math.floor(offset / notes.length) * 12; const octOffset = Math.floor(offset / notes.length) * octaves * 12;
offset = _mod(offset, 12); offset = _mod(offset, notes.length);
return notes[offset % notes.length] + octOffset; return notes[offset] + octOffset;
} }
// different ways to resolve the note to compare the anchor to (see renderVoicing) // different ways to resolve the note to compare the anchor to (see renderVoicing)
@ -90,7 +90,7 @@ let modeTarget = {
above: (v) => v[0], 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 [root, symbol] = tokenizeChord(chord);
const rootChroma = pc2chroma(root); const rootChroma = pc2chroma(root);
anchor = anchor?.note || anchor; 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)); notes = notes.filter((_, i) => voicingMidi[i] !== noteToMidi(anchor));
} }
if (n !== undefined) { if (n !== undefined) {
return [scaleStep(notes, n)]; return [scaleStep(notes, n, octaves)];
} }
return notes; return notes;
} }

View File

@ -196,10 +196,10 @@ export const voicing = register('voicing', function (pat) {
.fmap((value) => { .fmap((value) => {
// destructure voicing controls out // destructure voicing controls out
value = typeof value === 'string' ? { chord: value } : value; 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 = dictionary =
typeof dictionary === 'string' ? voicingRegistry[dictionary] : { dictionary, mode: 'below', anchor: 'c5' }; 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) return stack(...notes)
.note() .note()