UNPKG

@tonaljs/chord-detect

Version:

Detect chord name based on note names

82 lines 2.82 kB
// index.ts import { all } from "@tonaljs/chord-type"; import { modes } from "@tonaljs/pcset"; import { note } from "@tonaljs/pitch-note"; var namedSet = (notes) => { const pcToName = notes.reduce((record, n) => { const chroma = note(n).chroma; if (chroma !== void 0) { record[chroma] = record[chroma] || note(n).name; } return record; }, {}); return (chroma) => pcToName[chroma]; }; function detect(source, options = {}) { const notes = source.map((n) => note(n).pc).filter((x) => x); if (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 = note(tonic).chroma; const noteName = namedSet(notes); const allModes = modes(notes, false); const found = []; allModes.forEach((mode, index) => { const modeWithPerfectFifth = options.assumePerfectFifth && withPerfectFifth(mode); const chordTypes = 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 }; export { chord_detect_default as default, detect }; //# sourceMappingURL=index.mjs.map