moment-of-symmetry
Version:
Moment of Symmetry (MOS) musical scale generation and analysis for Javascript
667 lines • 28.1 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generatorRanges = exports.allForEdo = exports.anyForEdo = exports.makeEdoMap = exports.daughterMos = exports.mosScaleInfo = exports.parentMos = exports.splitMosPattern = exports.modeInfo = exports.mosModes = exports.mosWithDaughter = exports.mosWithParent = exports.mos = exports.stepString = exports.brightGeneratorMonzo = exports.euclid = void 0;
const hardness_1 = require("./hardness");
const names_1 = require("./names");
const helpers_1 = require("./helpers");
const xen_dev_utils_1 = require("xen-dev-utils");
__exportStar(require("./hardness"), exports);
__exportStar(require("./names"), exports);
__exportStar(require("./generator-ratio"), exports);
__exportStar(require("./info"), exports);
__exportStar(require("./notation"), exports);
/**
* Produce an array of booleans that is mixed as evenly as possible.
* @param numberOfTrue Number of true elements
* @param numberOfFalse Number of false elements
* @returns The array of evenly mixed booleans
*/
function euclid(numberOfTrue, numberOfFalse) {
return (0, helpers_1.bjorklund)(numberOfTrue, numberOfFalse, true, false);
}
exports.euclid = euclid;
/**
* Obtain the bright generator of the MOS scale expressed as multipliers of the size of the large and small steps.
* @param numberOfLargeSteps Number of large steps in the MOS pattern.
* @param numberOfSmallSteps Number of small steps in the MOS pattern.
* @returns An array of [number of large steps in the bright generator, number of small steps in the bright generator].
*/
function brightGeneratorMonzo(numberOfLargeSteps, numberOfSmallSteps) {
const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
return [
...(0, helpers_1.mosGeneratorMonzo)(numberOfLargeSteps / numPeriods, numberOfSmallSteps / numPeriods),
];
}
exports.brightGeneratorMonzo = brightGeneratorMonzo;
function getDown(options, period, numPeriods) {
let down = 0;
if (options.up !== undefined) {
down = period * numPeriods - numPeriods - options.up;
if (options.down !== undefined && down !== options.down) {
throw new Error('Incompatible up and down with the scale size');
}
}
else if (options.down !== undefined) {
down = options.down;
}
if (down < 0) {
throw new Error('Down must not be negative');
}
if (down >= period * numPeriods) {
throw new Error('Up must not be negative');
}
if (down % numPeriods !== 0) {
throw new Error('Up/down must be divisible by the number of periods');
}
return down;
}
/**
* Obtain an abstract string like 'LLsLLLs' corresponding to the mode specified.
* @param numberOfLargeSteps Number of large steps in the MOS pattern.
* @param numberOfSmallSteps Number of small steps in the MOS pattern.
* @param options Options for brightness of the scale.
* @returns String with the given number of 'L' and 's' characters in the specified mode.
*/
function stepString(numberOfLargeSteps, numberOfSmallSteps, options) {
if (!numberOfLargeSteps) {
return 's'.repeat(numberOfSmallSteps);
}
if (!numberOfSmallSteps) {
return 'L'.repeat(numberOfLargeSteps);
}
options !== null && options !== void 0 ? options : (options = {});
const brightest = (0, helpers_1.bjorklundStr)(numberOfLargeSteps, numberOfSmallSteps);
let mode = brightest;
const modes = [];
while (true) {
modes.push(mode);
mode = mode.slice(1) + mode[0];
if (mode === brightest) {
break;
}
}
// Lexicographic order corresponds to brightness.
modes.sort();
const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
const period = (numberOfLargeSteps + numberOfSmallSteps) / numPeriods;
const brightGeneratorsDown = getDown(options, period, numPeriods);
return modes[brightGeneratorsDown / numPeriods];
}
exports.stepString = stepString;
/**
* Generate MOS pattern as a subset of an EDO.
* @param numberOfLargeSteps Number of large steps in the MOS pattern.
* @param numberOfSmallSteps Number of small steps in the MOS pattern.
* @param options Options for sizes of the steps and brightness of the scale.
* @returns An array of integers representing the EDO subset. The 0 degree is not included, but the final degree representing the size of the EDO is.
*/
function mos(numberOfLargeSteps, numberOfSmallSteps, options) {
var _a, _b;
const abstract = stepString(numberOfLargeSteps, numberOfSmallSteps, options);
const sizeOfLargeStep = (_a = options === null || options === void 0 ? void 0 : options.sizeOfLargeStep) !== null && _a !== void 0 ? _a : 2;
const sizeOfSmallStep = (_b = options === null || options === void 0 ? void 0 : options.sizeOfSmallStep) !== null && _b !== void 0 ? _b : 1;
let step = 0;
const result = [];
for (const character of abstract) {
if (character === 'L') {
step += sizeOfLargeStep;
}
else {
step += sizeOfSmallStep;
}
result.push(step);
}
return result;
}
exports.mos = mos;
/**
* Generate MOS pattern as a subset of an EDO with parent MOS relationship indicated.
* @param numberOfLargeSteps Number of large steps in the MOS pattern.
* @param numberOfSmallSteps Number of small steps in the MOS pattern.
* @param options Options for sizes of the steps, brightness of the scale and flat/sharp relationship.
* @returns A map of integers representing the EDO subset to booleans indicating if the scale degree belongs to the parent MOS or not.
* The 0 degree is not included, but the final degree representing the size of the EDO is.
*/
function mosWithParent(numberOfLargeSteps, numberOfSmallSteps, options) {
var _a, _b;
options !== null && options !== void 0 ? options : (options = {});
const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
const period = (numberOfLargeSteps + numberOfSmallSteps) / numPeriods;
const sizeOfLargeStep = (_a = options.sizeOfLargeStep) !== null && _a !== void 0 ? _a : 2;
const sizeOfSmallStep = (_b = options.sizeOfSmallStep) !== null && _b !== void 0 ? _b : 1;
const brightGeneratorsDown = getDown(options, period, numPeriods);
const l = numberOfLargeSteps / numPeriods;
const s = numberOfSmallSteps / numPeriods;
const d = brightGeneratorsDown / numPeriods;
const p = l * sizeOfLargeStep + s * sizeOfSmallStep;
const gMonzo = (0, helpers_1.mosGeneratorMonzo)(l, s);
const g = gMonzo[0] * sizeOfLargeStep + gMonzo[1] * sizeOfSmallStep;
const parentPeriod = Math.max(l, s);
const base = new Map();
for (let i = 0; i < period; ++i) {
let isParent;
if (options.accidentals === 'flat') {
isParent = period - i <= parentPeriod;
}
else {
isParent = i < parentPeriod;
}
base.set((0, xen_dev_utils_1.mmod)((i - d) * g, p), isParent);
}
const edoDegrees = [...base.keys()].sort((a, b) => a - b);
let result = new Map();
for (let i = 0; i < numPeriods; ++i) {
edoDegrees.forEach(degree => {
result = result.set(degree + i * p, base.get(degree));
});
}
const rootIsParent = result.get(0);
result.delete(0);
result.set(numPeriods * p, rootIsParent);
return result;
}
exports.mosWithParent = mosWithParent;
/**
* Generate the daughter MOS pattern as a subset of an EDO with parent MOS relationship indicated.
* @param numberOfLargeSteps Number of large steps in the parent MOS.
* @param numberOfSmallSteps Number of small steps in the parent MOS.
* @param options Options for sizes of the steps, brightness of the scale and flat/sharp relationship.
* @returns A map of integers representing the EDO subset to booleans indicating if the scale degree belongs to the parent MOS or not.
* The 0 degree is not included, but the final degree representing the size of the EDO is.
*/
function mosWithDaughter(numberOfLargeSteps, numberOfSmallSteps, options) {
var _a, _b, _c;
options !== null && options !== void 0 ? options : (options = {});
const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
const period = (numberOfLargeSteps + numberOfSmallSteps) / numPeriods;
const sizeOfLargeStep = (_a = options.sizeOfLargeStep) !== null && _a !== void 0 ? _a : 2;
const sizeOfSmallStep = (_b = options.sizeOfSmallStep) !== null && _b !== void 0 ? _b : 1;
const brightGeneratorsDown = getDown(options, period, numPeriods);
const l = numberOfLargeSteps / numPeriods;
const s = numberOfSmallSteps / numPeriods;
const d = brightGeneratorsDown / numPeriods;
const p = l * sizeOfLargeStep + s * sizeOfSmallStep;
const gMonzo = (0, helpers_1.mosGeneratorMonzo)(l, s);
const g = gMonzo[0] * sizeOfLargeStep + gMonzo[1] * sizeOfSmallStep;
const daughterPeriod = 2 * l + s;
const base = new Map();
for (let i = 0; i < period; ++i) {
base.set((0, xen_dev_utils_1.mmod)((i - d) * g, p), 'parent');
}
const accs = (_c = options.accidentals) !== null && _c !== void 0 ? _c : 'sharp';
if (accs === 'flat' || (accs === 'both' && sizeOfLargeStep > 2)) {
for (let i = period - daughterPeriod; i < 0; ++i) {
base.set((0, xen_dev_utils_1.mmod)((i - d) * g, p), 'flat');
}
}
if (accs === 'sharp' || accs === 'both') {
const acc = sizeOfLargeStep === 2 ? 'both' : 'sharp';
for (let i = period; i < daughterPeriod; ++i) {
base.set((0, xen_dev_utils_1.mmod)((i - d) * g, p), acc);
}
}
const edoDegrees = [...base.keys()].sort((a, b) => a - b);
let result = new Map();
for (let i = 0; i < numPeriods; ++i) {
edoDegrees.forEach(degree => {
result = result.set(degree + i * p, base.get(degree));
});
}
const rootIsParent = result.get(0);
result.delete(0);
result.set(numPeriods * p, rootIsParent);
return result;
}
exports.mosWithDaughter = mosWithDaughter;
/**
* Information about the modes of a MOS scale.
* @param numberOfLargeSteps Number of large steps in the MOS pattern.
* @param numberOfSmallSteps Number of small steps in the MOS pattern.
* @param extraNames If true adds extra mode names in parenthesis such as Ionian (Major).
* @returns An array of mode information.
*/
function mosModes(numberOfLargeSteps, numberOfSmallSteps, extraNames = false) {
const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
const period = (numberOfLargeSteps + numberOfSmallSteps) / numberOfPeriods;
const l = numberOfLargeSteps / numberOfPeriods;
const s = numberOfSmallSteps / numberOfPeriods;
const p = l * 2 + s;
const gMonzo = (0, helpers_1.mosGeneratorMonzo)(l, s);
const g = gMonzo[0] * 2 + gMonzo[1];
const result = [];
for (let u = 0; u < period; ++u) {
const base = [];
for (let i = 0; i < period; ++i) {
base.push((0, xen_dev_utils_1.mmod)((u - i) * g, p));
}
base.sort((a, b) => a - b);
let scale = base;
for (let i = 1; i < numberOfPeriods; ++i) {
scale = scale.concat(base.map(s => s + i * p));
}
scale.push(numberOfPeriods * p);
let pattern = '';
for (let i = 1; i < scale.length; ++i) {
if (scale[i] - scale[i - 1] === 2) {
pattern += 'L';
}
else {
pattern += 's';
}
}
const modeName_ = (0, names_1.modeName)(pattern, extraNames);
let udp = `${u * numberOfPeriods}|${(period - 1 - u) * numberOfPeriods}`;
if (numberOfPeriods > 1) {
udp += `(${numberOfPeriods})`;
}
result.push({
period,
numberOfPeriods,
udp,
mode: pattern,
modeName: modeName_,
});
}
return result;
}
exports.mosModes = mosModes;
/**
* Information about a mode of a MOS scale.
* @param numberOfLargeSteps Number of large steps in the MOS pattern.
* @param numberOfSmallSteps Number of small steps in the MOS pattern.
* @param options Options for brightness of the scale and for adding extra names like Ionian (Major).
* @returns An array of mode information.
*/
function modeInfo(numberOfLargeSteps, numberOfSmallSteps, options) {
options !== null && options !== void 0 ? options : (options = {});
const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
const period = (numberOfLargeSteps + numberOfSmallSteps) / numberOfPeriods;
const brightGeneratorsDown = getDown(options, period, numberOfPeriods);
const scale = mos(numberOfLargeSteps, numberOfSmallSteps, options);
scale.unshift(0);
let pattern = '';
for (let i = 1; i < scale.length; ++i) {
if (scale[i] - scale[i - 1] === 2) {
pattern += 'L';
}
else {
pattern += 's';
}
}
const modeName_ = (0, names_1.modeName)(pattern, options.extraNames);
const brightGeneratorsUp = (period - 1) * numberOfPeriods - brightGeneratorsDown;
let udp = `${brightGeneratorsUp}|${brightGeneratorsDown}`;
if (numberOfPeriods > 1) {
udp += `(${numberOfPeriods})`;
}
return {
period,
numberOfPeriods,
udp,
mode: pattern,
modeName: modeName_,
};
}
exports.modeInfo = modeInfo;
/**
* Split a string like "5L 2s" into [5, 2].
* @param mosPattern MOS pattern such as "5L 2s".
* @returns A pair of intergers representing the number of large and small steps.
*/
function splitMosPattern(mosPattern) {
const [l, s] = mosPattern.split('L');
const numberOfLargeSteps = parseInt(l.trim(), 10);
const numberOfSmallSteps = parseInt(s.split('s')[0].trim(), 10);
return [numberOfLargeSteps, numberOfSmallSteps];
}
exports.splitMosPattern = splitMosPattern;
function parentMos(patternOrLarge, numberOfSmallSteps) {
let numberOfLargeSteps;
if (typeof patternOrLarge === 'string') {
[numberOfLargeSteps, numberOfSmallSteps] = splitMosPattern(patternOrLarge);
}
else {
numberOfLargeSteps = patternOrLarge;
if (typeof numberOfSmallSteps !== 'number') {
throw new Error('Number of small steps must be given');
}
}
// Calculate the parent's size.
const size = Math.max(numberOfLargeSteps, numberOfSmallSteps);
numberOfLargeSteps = Math.min(numberOfLargeSteps, numberOfSmallSteps);
numberOfSmallSteps = size - numberOfLargeSteps;
const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`;
const info = {
size,
numberOfLargeSteps,
numberOfSmallSteps,
mosPattern,
};
Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern));
return info;
}
exports.parentMos = parentMos;
function mosScaleInfo(numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep = 2, sizeOfSmallStep = 1) {
const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`;
const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
const edo = numberOfLargeSteps * sizeOfLargeStep + numberOfSmallSteps * sizeOfSmallStep;
const period = edo / numberOfPeriods;
const periodMonzo = [
numberOfLargeSteps / numberOfPeriods,
numberOfSmallSteps / numberOfPeriods,
];
const brightGeneratorMonzo = (0, helpers_1.mosGeneratorMonzo)(...periodMonzo);
const brightGenerator = (0, xen_dev_utils_1.dot)(brightGeneratorMonzo, [
sizeOfLargeStep,
sizeOfSmallStep,
]);
const info = {
numberOfLargeSteps,
numberOfSmallSteps,
sizeOfLargeStep,
sizeOfSmallStep,
edo,
numberOfPeriods,
period,
brightGenerator,
darkGenerator: period - brightGenerator,
periodMonzo,
brightGeneratorMonzo,
mosPattern,
hardness: (0, hardness_1.getHardness)(sizeOfLargeStep, sizeOfSmallStep),
};
Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern));
return info;
}
exports.mosScaleInfo = mosScaleInfo;
function daughterMos(numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep, sizeOfSmallStep) {
const size = numberOfLargeSteps + numberOfSmallSteps;
if (sizeOfLargeStep >= 2 * sizeOfSmallStep) {
numberOfSmallSteps = size;
sizeOfLargeStep -= sizeOfSmallStep;
}
else {
numberOfSmallSteps = numberOfLargeSteps;
numberOfLargeSteps = size;
const temp = sizeOfSmallStep;
sizeOfSmallStep = sizeOfLargeStep - sizeOfSmallStep;
sizeOfLargeStep = temp;
}
return mosScaleInfo(numberOfLargeSteps, numberOfSmallSteps, sizeOfLargeStep, sizeOfSmallStep);
}
exports.daughterMos = daughterMos;
// One entry in the EDO map for each hardness class
const STEP_SIZES = [
[2, 1], // basic
[3, 2], // soft
[3, 1], // hard
[4, 3], // supersoft
[4, 1], // superhard
[5, 3], // semisoft
[5, 2], // semihard
[5, 4], // ultrasoft
[5, 1], // ultrahard
[7, 5], // parasoft
[7, 4], // minisoft
[7, 3], // minihard
[7, 2], // parahard
[8, 5], // quasisoft
[8, 3], // quasihard
];
/**
* Construct a mapping from EDO size to supported MOS scales.
* @param maxSize Maximum size of the MOS patterns to include.
* @returns A mapping from EDO size to an array of information about the supported MOS scales.
*/
function makeEdoMap(maxSize = 12) {
const result = new Map();
STEP_SIZES.forEach(([sizeOfLargeStep, sizeOfSmallStep]) => {
const hardness = (0, hardness_1.getHardness)(sizeOfLargeStep, sizeOfSmallStep);
for (let size = 2; size <= maxSize; ++size) {
for (let numberOfLargeSteps = 1; numberOfLargeSteps < size; ++numberOfLargeSteps) {
const numberOfSmallSteps = size - numberOfLargeSteps;
const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`;
const edo = numberOfLargeSteps * sizeOfLargeStep +
numberOfSmallSteps * sizeOfSmallStep;
const info = {
mosPattern,
numberOfLargeSteps,
numberOfSmallSteps,
sizeOfLargeStep,
sizeOfSmallStep,
hardness,
};
Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern));
const infos = result.get(edo) || [];
infos.push(info);
result.set(edo, infos);
}
}
});
return result;
}
exports.makeEdoMap = makeEdoMap;
const STEP_COUNTS = [
[5, 2], // diatonic
[4, 3], // smitonic
[3, 4], // mosh
[2, 5], // antidiatonic
[3, 5], // sensoid
[5, 3], // oneirotonic
[6, 2], // echinoid
[2, 6], // antiechinoid
[4, 2], // lemon
[2, 4], // antilemon
[5, 1], // machinoid
[2, 3], // pentic
[3, 2], // antipentic
[1, 4], // machinoid (subset)
[1, 3], // manic
[1, 2], // happy
[2, 1], // grumpy
[1, 1], // trivial
];
/**
* Find a MOS scale supported by the given EDO.
* @param edo Size of the EDO.
* @returns Information about the supported MOS scale.
*/
function anyForEdo(edo) {
if (edo <= 1) {
throw new Error('Minimum size is 2');
}
if (edo === 2) {
return {
mosPattern: '1L 1s',
numberOfLargeSteps: 1,
numberOfSmallSteps: 1,
sizeOfLargeStep: 1,
sizeOfSmallStep: 1,
edo,
numberOfPeriods: 1,
period: edo,
brightGenerator: 1,
darkGenerator: 1,
periodMonzo: [1, 1],
brightGeneratorMonzo: [1, 0],
hardness: 'equalized',
name: 'trivial',
subset: false,
};
}
for (let i = 0; i < STEP_COUNTS.length; ++i) {
const [numberOfLargeSteps, numberOfSmallSteps] = STEP_COUNTS[i];
let sizeOfLargeStep = 2;
while (true) {
const largePart = sizeOfLargeStep * numberOfLargeSteps;
const smallPart = edo - largePart;
if (smallPart <= 0) {
break;
}
if (smallPart % numberOfSmallSteps === 0) {
const sizeOfSmallStep = smallPart / numberOfSmallSteps;
if (sizeOfLargeStep <= 3 * sizeOfSmallStep &&
3 * sizeOfSmallStep <= 2 * sizeOfLargeStep) {
const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`;
const hardness = (0, hardness_1.getHardness)(sizeOfLargeStep, sizeOfSmallStep);
const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
const period = edo / numberOfPeriods;
const periodMonzo = [
numberOfLargeSteps / numberOfPeriods,
numberOfSmallSteps / numberOfPeriods,
];
const brightGeneratorMonzo = (0, helpers_1.mosGeneratorMonzo)(...periodMonzo);
const brightGenerator = (0, xen_dev_utils_1.dot)(brightGeneratorMonzo, [
sizeOfLargeStep,
sizeOfSmallStep,
]);
const info = {
mosPattern,
numberOfLargeSteps,
numberOfSmallSteps,
sizeOfLargeStep,
sizeOfSmallStep,
edo,
numberOfPeriods,
period,
brightGenerator,
darkGenerator: period - brightGenerator,
periodMonzo,
brightGeneratorMonzo,
hardness,
};
Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern));
return info;
}
}
sizeOfLargeStep++;
}
}
throw new Error(`Failed to find MOS pattern for ${edo}`);
}
exports.anyForEdo = anyForEdo;
/**
* Find all MOS scales supported by the given EDO within the given constraints.
* @param edo Size of the EDO.
* @param minSize Minimum size of a MOS scale in the result.
* @param maxSize Maximum size of a MOS scale in the result.
* @param maxHardness Maximum hardness of the step ratio L/s.
* @returns Array of information about the supported MOS scales.
*/
function allForEdo(edo, minSize = 2, maxSize, maxHardness) {
if (maxSize === undefined) {
maxSize = edo;
}
if (minSize < 2) {
throw new Error('Minimum size must be at least 2');
}
if (maxSize > edo) {
throw new Error(`Maximum size must be smaller or equal to edo (${edo})`);
}
const result = [];
for (let numberOfLargeSteps = 1; numberOfLargeSteps < maxSize; ++numberOfLargeSteps) {
for (let numberOfSmallSteps = Math.max(1, minSize - numberOfLargeSteps); numberOfSmallSteps <= maxSize - numberOfLargeSteps; numberOfSmallSteps++) {
for (const hardness of (0, xen_dev_utils_1.fareyInterior)(edo - numberOfSmallSteps)) {
const { n: sizeOfSmallStep, d: sizeOfLargeStep } = hardness;
if (maxHardness && sizeOfLargeStep > sizeOfSmallStep * maxHardness) {
continue;
}
if (numberOfLargeSteps * sizeOfLargeStep +
numberOfSmallSteps * sizeOfSmallStep ===
edo) {
const mosPattern = `${numberOfLargeSteps}L ${numberOfSmallSteps}s`;
const hardness = (0, hardness_1.getHardness)(sizeOfLargeStep, sizeOfSmallStep);
const numberOfPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
const period = edo / numberOfPeriods;
const periodMonzo = [
numberOfLargeSteps / numberOfPeriods,
numberOfSmallSteps / numberOfPeriods,
];
const brightGeneratorMonzo = (0, helpers_1.mosGeneratorMonzo)(...periodMonzo);
const brightGenerator = (0, xen_dev_utils_1.dot)(brightGeneratorMonzo, [
sizeOfLargeStep,
sizeOfSmallStep,
]);
const info = {
mosPattern,
numberOfLargeSteps,
numberOfSmallSteps,
sizeOfLargeStep,
sizeOfSmallStep,
hardness,
edo,
numberOfPeriods,
period,
brightGenerator,
darkGenerator: period - brightGenerator,
periodMonzo,
brightGeneratorMonzo,
};
Object.assign(info, (0, names_1.tamnamsInfo)(mosPattern));
result.push(info);
}
}
}
}
return result;
}
exports.allForEdo = allForEdo;
/**
* Find the ranges of all (equally tempered) fractions of the equave that span MOS scales.
* @param size Size of the scales to consider.
* @param includeMultiPeriods Include scales that split the equave into multiple periods.
* @returns Information about the ranges of generator that span MOS. Ranges are grouped by period and otherwise sorted in ascending order.
*/
function generatorRanges(size, includeMultiPeriods = false) {
const result = [];
for (let numberOfLargeSteps = 1; numberOfLargeSteps < size; numberOfLargeSteps++) {
const numberOfSmallSteps = size - numberOfLargeSteps;
const numPeriods = (0, xen_dev_utils_1.gcd)(numberOfLargeSteps, numberOfSmallSteps);
if (!includeMultiPeriods && numPeriods !== 1) {
continue;
}
const period = new xen_dev_utils_1.Fraction(1, numPeriods);
const monzo = (0, helpers_1.mosGeneratorMonzo)(numberOfLargeSteps / numPeriods, numberOfSmallSteps / numPeriods);
// Collapsed endpoint
let lowerBound = new xen_dev_utils_1.Fraction(monzo[0], numberOfLargeSteps);
// Equalized endpoint
let upperBound = new xen_dev_utils_1.Fraction(monzo[0] + monzo[1], size);
if (lowerBound.compare(upperBound) > 0) {
[lowerBound, upperBound] = [upperBound, lowerBound];
}
result.push({
period,
lowerBound,
upperBound,
numberOfLargeSteps,
numberOfSmallSteps,
bright: true,
});
result.push({
period,
lowerBound: period.sub(upperBound),
upperBound: period.sub(lowerBound),
numberOfLargeSteps,
numberOfSmallSteps,
bright: false,
});
}
result.sort((a, b) => a.period.compare(b.period) || a.lowerBound.compare(b.lowerBound));
return result;
}
exports.generatorRanges = generatorRanges;
//# sourceMappingURL=index.js.map