@tonaljs/chord-detect
Version:
Detect chord name based on note names
107 lines (106 loc) • 4.03 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all2) => {
for (var name in all2)
__defProp(target, name, { get: all2[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.ts
var chord_detect_exports = {};
__export(chord_detect_exports, {
default: () => chord_detect_default,
detect: () => detect
});
module.exports = __toCommonJS(chord_detect_exports);
var import_chord_type = require("@tonaljs/chord-type");
var import_pcset = require("@tonaljs/pcset");
var import_pitch_note = require("@tonaljs/pitch-note");
var namedSet = (notes) => {
const pcToName = notes.reduce((record, n) => {
const chroma = (0, import_pitch_note.note)(n).chroma;
if (chroma !== void 0) {
record[chroma] = record[chroma] || (0, import_pitch_note.note)(n).name;
}
return record;
}, {});
return (chroma) => pcToName[chroma];
};
function detect(source, options = {}) {
const notes = source.map((n) => (0, import_pitch_note.note)(n).pc).filter((x) => x);
if (import_pitch_note.note.length === 0) {
return [];
}
const found = findMatches(notes, 1, options);
return found.filter((chord) => chord.weight).sort((a, b) => b.weight - a.weight).map((chord) => chord.name);
}
var BITMASK = {
// 3m 000100000000
// 3M 000010000000
anyThirds: 384,
// 5P 000000010000
perfectFifth: 16,
// 5d 000000100000
// 5A 000000001000
nonPerfectFifths: 40,
anySeventh: 3
};
var testChromaNumber = (bitmask) => (chromaNumber) => Boolean(chromaNumber & bitmask);
var hasAnyThird = testChromaNumber(BITMASK.anyThirds);
var hasPerfectFifth = testChromaNumber(BITMASK.perfectFifth);
var hasAnySeventh = testChromaNumber(BITMASK.anySeventh);
var hasNonPerfectFifth = testChromaNumber(BITMASK.nonPerfectFifths);
function hasAnyThirdAndPerfectFifthAndAnySeventh(chordType) {
const chromaNumber = parseInt(chordType.chroma, 2);
return hasAnyThird(chromaNumber) && hasPerfectFifth(chromaNumber) && hasAnySeventh(chromaNumber);
}
function withPerfectFifth(chroma) {
const chromaNumber = parseInt(chroma, 2);
return hasNonPerfectFifth(chromaNumber) ? chroma : (chromaNumber | 16).toString(2);
}
function findMatches(notes, weight, options) {
const tonic = notes[0];
const tonicChroma = (0, import_pitch_note.note)(tonic).chroma;
const noteName = namedSet(notes);
const allModes = (0, import_pcset.modes)(notes, false);
const found = [];
allModes.forEach((mode, index) => {
const modeWithPerfectFifth = options.assumePerfectFifth && withPerfectFifth(mode);
const chordTypes = (0, import_chord_type.all)().filter((chordType) => {
if (options.assumePerfectFifth && hasAnyThirdAndPerfectFifthAndAnySeventh(chordType)) {
return chordType.chroma === modeWithPerfectFifth;
}
return chordType.chroma === mode;
});
chordTypes.forEach((chordType) => {
const chordName = chordType.aliases[0];
const baseNote = noteName(index);
const isInversion = index !== tonicChroma;
if (isInversion) {
found.push({
weight: 0.5 * weight,
name: `${baseNote}${chordName}/${tonic}`
});
} else {
found.push({ weight: 1 * weight, name: `${baseNote}${chordName}` });
}
});
});
return found;
}
var chord_detect_default = { detect };
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
detect
});
//# sourceMappingURL=index.js.map