UNPKG

@tonaljs/chord-detect

Version:

Detect chord name based on note names

107 lines (106 loc) 4.03 kB
"use strict"; 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