UNPKG

moment-of-symmetry

Version:

Moment of Symmetry (MOS) musical scale generation and analysis for Javascript

126 lines 3.93 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateNotation = exports.nthNominal = exports.DIAMOND_MOS_ALPHABET = void 0; const xen_dev_utils_1 = require("xen-dev-utils"); const helpers_1 = require("./helpers"); /** Single characters of valid nominals. */ exports.DIAMOND_MOS_ALPHABET = [ 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ]; /** * Obtain the 0-indexed nth generalized Diamond-mos nominal. * @param n Index of the nominal. * @returns A single character from J through Z or a multi-character string like 'JJ' starting from n = 17. */ function nthNominal(n) { if (n < 0 || !Number.isInteger(n)) { throw new Error('Invalid nominal index'); } if (n >= exports.DIAMOND_MOS_ALPHABET.length) { return (nthNominal(Math.floor(n / exports.DIAMOND_MOS_ALPHABET.length) - 1) + exports.DIAMOND_MOS_ALPHABET[n % exports.DIAMOND_MOS_ALPHABET.length]); } return exports.DIAMOND_MOS_ALPHABET[n]; } exports.nthNominal = nthNominal; /** * Generate configuration for [Diamond mos notation](https://en.xen.wiki/w/Diamond-mos_notation). * * Always based on J and 0-indexed even if scale is diatonic. * @param mode Mode of a MOS scale such as 'LLsLLLs'. * @returns Configuration for notation software. */ function generateNotation(mode) { const scale = new Map(); let i = 0; const monzo = [0, 0]; let hasLarge = false; let hasSmall = false; for (const character of mode) { scale.set(nthNominal(i++), [...monzo]); if (character === 'L') { monzo[0]++; hasLarge = true; } else if (character === 's') { monzo[1]++; hasSmall = true; } else { throw new Error(`Invalid abstract step '${character}'.`); } } if (!hasLarge || !hasSmall) { throw new Error("The scale must contain both 'L' and 's' steps."); } const equave = [...monzo]; const numPeriods = (0, xen_dev_utils_1.gcd)(equave[0], equave[1]); const period = [equave[0] / numPeriods, equave[1] / numPeriods]; const gen = (0, helpers_1.mosGeneratorMonzo)(period[0], period[1]); const numUnique = period[0] + period[1]; const edoperiod = 2 * period[0] + period[1]; const basic = [ [0, [0, 0], true, undefined], ]; // Exception for nL ns if (numUnique === 2) { basic.push([2, [gen[0] - 0.5, gen[1] + 0.5], false, undefined]); } else { // Dark mid basic.push([ edoperiod - 2 * gen[0] - gen[1], [period[0] - gen[0], period[1] - gen[1]], true, [period[0] - gen[0] + 0.5, period[1] - gen[1] - 0.5], ]); let edostep = 2 * gen[0] + gen[1]; // Bright mid basic.push([edostep, [...gen], true, [gen[0] - 0.5, gen[1] + 0.5]]); monzo[0] = gen[0]; monzo[1] = gen[1]; for (let i = 2; i < numUnique - 1; ++i) { edostep += 2 * gen[0] + gen[1]; monzo[0] += gen[0]; monzo[1] += gen[1]; while (edostep >= edoperiod) { edostep -= edoperiod; monzo[0] -= period[0]; monzo[1] -= period[1]; } // Imperfect central basic.push([edostep, [monzo[0] - 0.5, monzo[1] + 0.5], false, undefined]); } } basic.sort((a, b) => a[0] - b[0]); const degrees = []; // eslint-disable-next-line @typescript-eslint/no-unused-vars for (const [_, center, perfect, mid] of basic) { degrees.push({ center, perfect, mid }); } return { scale, degrees, equave, period, brightGenerator: gen, }; } exports.generateNotation = generateNotation; //# sourceMappingURL=notation.js.map