@tonaljs/pitch-interval
Version:
Parse intervals in shorthand notation
116 lines • 3.13 kB
JavaScript
// index.ts
import {
coordinates,
isNamedPitch,
isPitch,
pitch
} from "@tonaljs/pitch";
var fillStr = (s, n) => Array(Math.abs(n) + 1).join(s);
var NoInterval = Object.freeze({
empty: true,
name: "",
num: NaN,
q: "",
type: "",
step: NaN,
alt: NaN,
dir: NaN,
simple: NaN,
semitones: NaN,
chroma: NaN,
coord: [],
oct: NaN
});
var INTERVAL_TONAL_REGEX = "([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})";
var INTERVAL_SHORTHAND_REGEX = "(AA|A|P|M|m|d|dd)([-+]?\\d+)";
var REGEX = new RegExp(
"^" + INTERVAL_TONAL_REGEX + "|" + INTERVAL_SHORTHAND_REGEX + "$"
);
function tokenizeInterval(str) {
const m = REGEX.exec(`${str}`);
if (m === null) {
return ["", ""];
}
return m[1] ? [m[1], m[2]] : [m[4], m[3]];
}
var cache = {};
function interval(src) {
return typeof src === "string" ? cache[src] || (cache[src] = parse(src)) : isPitch(src) ? interval(pitchName(src)) : isNamedPitch(src) ? interval(src.name) : NoInterval;
}
var SIZES = [0, 2, 4, 5, 7, 9, 11];
var TYPES = "PMMPPMM";
function parse(str) {
const tokens = tokenizeInterval(str);
if (tokens[0] === "") {
return NoInterval;
}
const num = +tokens[0];
const q = tokens[1];
const step = (Math.abs(num) - 1) % 7;
const t = TYPES[step];
if (t === "M" && q === "P") {
return NoInterval;
}
const type = t === "M" ? "majorable" : "perfectable";
const name = "" + num + q;
const dir = num < 0 ? -1 : 1;
const simple = num === 8 || num === -8 ? num : dir * (step + 1);
const alt = qToAlt(type, q);
const oct = Math.floor((Math.abs(num) - 1) / 7);
const semitones = dir * (SIZES[step] + alt + 12 * oct);
const chroma = (dir * (SIZES[step] + alt) % 12 + 12) % 12;
const coord = coordinates({ step, alt, oct, dir });
return {
empty: false,
name,
num,
q,
step,
alt,
dir,
type,
simple,
semitones,
chroma,
coord,
oct
};
}
function coordToInterval(coord, forceDescending) {
const [f, o = 0] = coord;
const isDescending = f * 7 + o * 12 < 0;
const ivl = forceDescending || isDescending ? [-f, -o, -1] : [f, o, 1];
return interval(pitch(ivl));
}
function qToAlt(type, q) {
return q === "M" && type === "majorable" || q === "P" && type === "perfectable" ? 0 : q === "m" && type === "majorable" ? -1 : /^A+$/.test(q) ? q.length : /^d+$/.test(q) ? -1 * (type === "perfectable" ? q.length : q.length + 1) : 0;
}
function pitchName(props) {
const { step, alt, oct = 0, dir } = props;
if (!dir) {
return "";
}
const calcNum = step + 1 + 7 * oct;
const num = calcNum === 0 ? step + 1 : calcNum;
const d = dir < 0 ? "-" : "";
const type = TYPES[step] === "M" ? "majorable" : "perfectable";
const name = d + num + altToQ(type, alt);
return name;
}
function altToQ(type, alt) {
if (alt === 0) {
return type === "majorable" ? "M" : "P";
} else if (alt === -1 && type === "majorable") {
return "m";
} else if (alt > 0) {
return fillStr("A", alt);
} else {
return fillStr("d", type === "perfectable" ? alt : alt + 1);
}
}
export {
coordToInterval,
interval,
tokenizeInterval
};
//# sourceMappingURL=index.mjs.map