moment-of-symmetry
Version:
Moment of Symmetry (MOS) musical scale generation and analysis for Javascript
124 lines • 4.29 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMosScale = exports.createMosPattern = exports.generateScale = exports.generatePattern = exports.getDown = exports.calculatePeriodSize = exports.calculatePeriods = void 0;
const xen_dev_utils_1 = require("xen-dev-utils");
const bjorklund_1 = require("./bjorklund");
const types_1 = require("../types");
/**
* Calculate the number of periods in a MOS pattern
*/
function calculatePeriods(largeSteps, smallSteps) {
return (0, xen_dev_utils_1.gcd)(largeSteps, smallSteps);
}
exports.calculatePeriods = calculatePeriods;
/**
* Calculate the period size of a MOS pattern
*/
function calculatePeriodSize(largeSteps, smallSteps) {
const periods = calculatePeriods(largeSteps, smallSteps);
return (largeSteps + smallSteps) / periods;
}
exports.calculatePeriodSize = calculatePeriodSize;
/**
* Get the number of bright generators to go down based on options
*/
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 types_1.InvalidParametersError('Incompatible up and down with the scale size');
}
}
else if (options.down !== undefined) {
down = options.down;
}
if (down < 0) {
throw new types_1.InvalidParametersError('Down must not be negative');
}
if (down >= period * numPeriods) {
throw new types_1.InvalidParametersError('Up must not be negative');
}
if (down % numPeriods !== 0) {
throw new types_1.InvalidParametersError('Up/down must be divisible by the number of periods');
}
return down;
}
exports.getDown = getDown;
/**
* Generate a MOS pattern string
*/
function generatePattern(largeSteps, smallSteps, options) {
if (!largeSteps) {
return 's'.repeat(smallSteps);
}
if (!smallSteps) {
return 'L'.repeat(largeSteps);
}
const brightest = (0, bjorklund_1.bjorklundStr)(largeSteps, smallSteps);
const modes = [];
let mode = brightest;
while (true) {
modes.push(mode);
mode = mode.slice(1) + mode[0];
if (mode === brightest) {
break;
}
}
// Lexicographic order corresponds to brightness
modes.sort();
const numPeriods = calculatePeriods(largeSteps, smallSteps);
const period = calculatePeriodSize(largeSteps, smallSteps);
const brightGeneratorsDown = getDown(options !== null && options !== void 0 ? options : {}, period, numPeriods);
return modes[brightGeneratorsDown / numPeriods];
}
exports.generatePattern = generatePattern;
/**
* Generate a MOS scale from a pattern
*/
function generateScale(pattern, sizeOfLargeStep, sizeOfSmallStep) {
let step = 0;
const result = [];
for (const character of pattern) {
if (character === 'L') {
step += sizeOfLargeStep;
}
else {
step += sizeOfSmallStep;
}
result.push(step);
}
return result;
}
exports.generateScale = generateScale;
/**
* Create a complete MOS pattern object
*/
function createMosPattern(largeSteps, smallSteps, options) {
const periods = calculatePeriods(largeSteps, smallSteps);
const pattern = generatePattern(largeSteps, smallSteps, options);
return {
largeSteps,
smallSteps,
pattern,
periods,
};
}
exports.createMosPattern = createMosPattern;
/**
* Create a complete MOS scale object
*/
function createMosScale(largeSteps, smallSteps, options) {
var _a, _b;
const mosPattern = createMosPattern(largeSteps, smallSteps, 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;
return {
...mosPattern,
degrees: generateScale(mosPattern.pattern, sizeOfLargeStep, sizeOfSmallStep),
sizeOfLargeStep,
sizeOfSmallStep,
};
}
exports.createMosScale = createMosScale;
//# sourceMappingURL=pattern.js.map