UNPKG

@tonaljs/scale

Version:

Musical scales and its relations

176 lines 4.96 kB
// 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