- Install Deno
- Run
deno run https://gist.githubusercontent.com/5t111111/698c87f8ee518f4db737bd00502695f1/raw/get-music-key-from-notes.ts C E FThe above will output:
{ candidates: [ "C", "F", "Dm", "Am" ] }| /* | |
| * Get music key (scale) from notes used | |
| * | |
| * Usage: | |
| * | |
| * deno run https://gist.githubusercontent.com/5t111111/698c87f8ee518f4db737bd00502695f1/raw/get-music-key-from-notes.ts C E F | |
| * | |
| * The above example shows you `{ candidates: [ "C", "F", "Dm", "Am" ] }` | |
| */ | |
| const notes = ["C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"]; | |
| const majorIntervals = [0, 2, 2, 1, 2, 2, 2]; | |
| const majorIntervalsFromRoot = majorIntervals.map((interval, idx) => | |
| majorIntervals.slice(0, idx + 1).reduce<number>((acc, curr) => acc + curr, 0) | |
| ); | |
| const minorIntervals = [0, 2, 1, 2, 2, 1, 2]; | |
| const minorIntervalsFromRoot = minorIntervals.map((interval, idx) => | |
| minorIntervals.slice(0, idx + 1).reduce<number>((acc, curr) => acc + curr, 0) | |
| ); | |
| const getNotesOfScale = (intervals: number[], root: string) => | |
| intervals.reduce<string[]>((acc, curr) => { | |
| const index = notes.findIndex((elem) => elem === root) + curr; | |
| const noteIndex = index >= 12 ? index - 12 : index; | |
| acc.push(notes[noteIndex]); | |
| return acc; | |
| }, []); | |
| const majorScales = notes | |
| .map((note) => { | |
| const notesOfScale = getNotesOfScale(majorIntervalsFromRoot, note); | |
| return { | |
| [note]: notesOfScale, | |
| }; | |
| }) | |
| .reduce((acc, curr) => Object.assign(acc, curr), {}); | |
| const minorScales = notes | |
| .map((note) => { | |
| const notesOfScale = getNotesOfScale(minorIntervalsFromRoot, note); | |
| return { | |
| [`${note}m`]: notesOfScale, | |
| }; | |
| }) | |
| .reduce((acc, curr) => Object.assign(acc, curr), {}); | |
| const scales = Object.assign(majorScales, minorScales); | |
| // console.log(scales); | |
| const notesToFind: string[] = Array.from( | |
| new Set(Deno.args.filter((arg) => notes.includes(arg))) | |
| ); | |
| let candidates: string[] = []; | |
| for (const scaleKey in scales) { | |
| if (notesToFind.every((curr) => scales[scaleKey].includes(curr))) { | |
| candidates.push(scaleKey); | |
| } | |
| } | |
| console.log({ candidates }); |