75 lines
3.3 KiB
JavaScript
75 lines
3.3 KiB
JavaScript
/*
|
|
edo.mjs - Equal division of the octave (EDO) scale functions for strudel
|
|
Copyright (C) 2025 Rob McKinnon and Strudel contributors - see <https://codeberg.org/uzu/strudel/src/branch/main/packages/edo/edo.mjs>
|
|
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { register, pure, noteToMidi, isNote, tokenizeNote } from '@strudel/core';
|
|
import { EdoScale } from './edoscale.mjs';
|
|
import { Intervals } from './intervals.mjs';
|
|
import { Pitches } from './pitches.mjs';
|
|
|
|
const pitchesCache = new Map();
|
|
|
|
export const edoScale = register(
|
|
'edoScale',
|
|
function (scaleDefinition, pat) {
|
|
// console.log(scaleDefinition);
|
|
|
|
// if (Array.isArray(scale)) {
|
|
const key = scaleDefinition.flat().join(':');
|
|
// }
|
|
// console.log(scaleDefinition);
|
|
let pitches;
|
|
if (pitchesCache.has(key)) {
|
|
pitches = pitchesCache.get(key);
|
|
} else {
|
|
// console.log({ key });
|
|
const [base_note, sequence, large, small] = scaleDefinition;
|
|
const root_octave = tokenizeNote(base_note)[2] || 3;
|
|
// console.log({ root_octave });
|
|
const scale = new EdoScale(large, small, sequence);
|
|
const intervals = new Intervals(scale);
|
|
// console.log({ intervals });
|
|
pitches = new Pitches(scale, intervals, 440, noteToMidi(base_note), root_octave);
|
|
pitchesCache.set(key, pitches);
|
|
// console.log({ base_note: noteToMidi(base_note) });
|
|
// console.log({ sequence });
|
|
// console.log({ edivisions: scale.edivisions });
|
|
// console.log({ intervals });
|
|
// console.log({ pat });
|
|
// console.log({ pitches: pitches });
|
|
}
|
|
return pat
|
|
.fmap((value) => {
|
|
const isObject = typeof value === 'object';
|
|
const n = isObject ? value.n : value;
|
|
if (isObject) {
|
|
delete value.n; // remove n so it won't cause trouble
|
|
}
|
|
if (isNote(n)) {
|
|
// legacy..
|
|
return pure(n);
|
|
}
|
|
const deg = (typeof n === 'string' ? parseInt(n, 10) : n) + 1;
|
|
|
|
const [oct, degree] = pitches.octdeg(deg);
|
|
const freq = pitches.octdegfreq(oct, degree);
|
|
const note = pitches.octdegmidi(oct, degree);
|
|
const edo = pitches.scale.edivisions;
|
|
const root = pitches.base_freq;
|
|
const degreeIndexes = pitches.scale.divisions;
|
|
const intLabels = pitches.intervals.intLabels;
|
|
// const color = 'red';
|
|
value = pure(isObject ? { ...value, degree, degreeIndexes, intLabels, root, freq, edo } : note);
|
|
// value = pure(isObject ? { ...value, key } : note);
|
|
// value = pure(isObject ? { ...value, edo } : note);
|
|
// console.log({ value });
|
|
return value;
|
|
})
|
|
.outerJoin()
|
|
.withHap((hap) => hap.setContext({ ...hap.context, scaleDefinition }));
|
|
},
|
|
true,
|
|
true, // preserve tactus
|
|
);
|