/* edo.mjs - Equal division of the octave (EDO) scale functions for strudel Copyright (C) 2025 Rob McKinnon and Strudel contributors - see 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 . */ 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 );