@tonaljs/scale
Version:
Musical scales and its relations
176 lines • 4.96 kB
JavaScript
// index.ts
import { all as chordTypes } from "@tonaljs/chord-type";
import { range as nums, rotate } from "@tonaljs/collection";
import { enharmonic, fromMidi, sortedUniqNames } from "@tonaljs/note";
import {
chroma,
isChroma,
isSubsetOf,
isSupersetOf,
modes
} from "@tonaljs/pcset";
import { tonicIntervalsTransposer, transpose } from "@tonaljs/pitch-distance";
import { note } from "@tonaljs/pitch-note";
import {
all,
get as getScaleType,
names as scaleTypeNames,
all as scaleTypes
} from "@tonaljs/scale-type";
var NoScale = {
empty: true,
name: "",
type: "",
tonic: null,
setNum: NaN,
chroma: "",
normalized: "",
aliases: [],
notes: [],
intervals: []
};
function tokenize(name) {
if (typeof name !== "string") {
return ["", ""];
}
const i = name.indexOf(" ");
const tonic = note(name.substring(0, i));
if (tonic.empty) {
const n = note(name);
return n.empty ? ["", name.toLowerCase()] : [n.name, ""];
}
const type = name.substring(tonic.name.length + 1).toLowerCase();
return [tonic.name, type.length ? type : ""];
}
var names = scaleTypeNames;
function get(src) {
const tokens = Array.isArray(src) ? src : tokenize(src);
const tonic = note(tokens[0]).name;
const st = getScaleType(tokens[1]);
if (st.empty) {
return NoScale;
}
const type = st.name;
const notes = tonic ? st.intervals.map((i) => transpose(tonic, i)) : [];
const name = tonic ? tonic + " " + type : type;
return { ...st, name, type, tonic, notes };
}
var scale = get;
function detect(notes, options = {}) {
const notesChroma = chroma(notes);
const tonic = note(options.tonic ?? notes[0] ?? "");
const tonicChroma = tonic.chroma;
if (tonicChroma === void 0) {
return [];
}
const pitchClasses = notesChroma.split("");
pitchClasses[tonicChroma] = "1";
const scaleChroma = rotate(tonicChroma, pitchClasses).join("");
const match = all().find((scaleType) => scaleType.chroma === scaleChroma);
const results = [];
if (match) {
results.push(tonic.name + " " + match.name);
}
if (options.match === "exact") {
return results;
}
extended(scaleChroma).forEach((scaleName) => {
results.push(tonic.name + " " + scaleName);
});
return results;
}
function scaleChords(name) {
const s = get(name);
const inScale = isSubsetOf(s.chroma);
return chordTypes().filter((chord) => inScale(chord.chroma)).map((chord) => chord.aliases[0]);
}
function extended(name) {
const chroma2 = isChroma(name) ? name : get(name).chroma;
const isSuperset = isSupersetOf(chroma2);
return scaleTypes().filter((scale2) => isSuperset(scale2.chroma)).map((scale2) => scale2.name);
}
function reduced(name) {
const isSubset = isSubsetOf(get(name).chroma);
return scaleTypes().filter((scale2) => isSubset(scale2.chroma)).map((scale2) => scale2.name);
}
function scaleNotes(notes) {
const pcset = notes.map((n) => note(n).pc).filter((x) => x);
const tonic = pcset[0];
const scale2 = sortedUniqNames(pcset);
return rotate(scale2.indexOf(tonic), scale2);
}
function modeNames(name) {
const s = get(name);
if (s.empty) {
return [];
}
const tonics = s.tonic ? s.notes : s.intervals;
return modes(s.chroma).map((chroma2, i) => {
const modeName = get(chroma2).name;
return modeName ? [tonics[i], modeName] : ["", ""];
}).filter((x) => x[0]);
}
function getNoteNameOf(scale2) {
const names2 = Array.isArray(scale2) ? scaleNotes(scale2) : get(scale2).notes;
const chromas = names2.map((name) => note(name).chroma);
return (noteOrMidi) => {
const currNote = typeof noteOrMidi === "number" ? note(fromMidi(noteOrMidi)) : note(noteOrMidi);
const height = currNote.height;
if (height === void 0) return void 0;
const chroma2 = height % 12;
const position = chromas.indexOf(chroma2);
if (position === -1) return void 0;
return enharmonic(currNote.name, names2[position]);
};
}
function rangeOf(scale2) {
const getName = getNoteNameOf(scale2);
return (fromNote, toNote) => {
const from = note(fromNote).height;
const to = note(toNote).height;
if (from === void 0 || to === void 0) return [];
return nums(from, to).map(getName).filter((x) => x);
};
}
function degrees(scaleName) {
const { intervals, tonic } = get(scaleName);
const transpose2 = tonicIntervalsTransposer(intervals, tonic);
return (degree) => degree ? transpose2(degree > 0 ? degree - 1 : degree) : "";
}
function steps(scaleName) {
const { intervals, tonic } = get(scaleName);
return tonicIntervalsTransposer(intervals, tonic);
}
var index_default = {
degrees,
detect,
extended,
get,
modeNames,
names,
rangeOf,
reduced,
scaleChords,
scaleNotes,
steps,
tokenize,
// deprecated
scale
};
export {
index_default as default,
degrees,
detect,
extended,
get,
modeNames,
names,
rangeOf,
reduced,
scale,
scaleChords,
scaleNotes,
steps,
tokenize
};
//# sourceMappingURL=index.mjs.map