import * as tonal from 'tonal' ; import { BOTTOM_A , MIN_PITCH , MAX_PITCH , MODE_NAMES , KEY_NAMES , } from './constants' ; import { range } from 'lodash' ; import { ScaleValue } from './editor' ; export function getNoteDetails ( noteValue ) { const noteText = tonal . Note . fromMidi ( noteValue ) ; const letter = noteText [ 0 ] ; const accidental = noteText . length > 2 ? noteText [ 1 ] : '' ; const octave = Number ( noteText [ noteText . length - 1 ] ) ; return { letter , accidental , octave } ; } const RAW_NOTE_NAMES = 'ABCDEFG' ; const { octave : BOTTOM_A_OCTAVE } = getNoteDetails ( BOTTOM_A ) ; export function getStaffLineNumber ( name , octave ) { const deltaOctave = octave - BOTTOM_A_OCTAVE ; const deltaNote = RAW_NOTE_NAMES . indexOf ( name ) - RAW_NOTE_NAMES . indexOf ( 'A' ) ; return deltaOctave * 7 + deltaNote ; } export function getNoteDetailsFromLine ( lineDivision ) { const name = RAW_NOTE_NAMES [ lineDivision % 7 ] ; const octave = Math . floor ( lineDivision / 7 ) + BOTTOM_A_OCTAVE ; return { name , octave } ; } export function makeNoteScale ( max = MAX_PITCH , min = MIN_PITCH ) { return range ( max , min - 1 ) . map ( pitch => { const { letter , accidental , octave } = getNoteDetails ( pitch ) ; return { pitch , name : `${ letter }${ accidental }` , octave , } ; } ) ; } export function makeNoteScaleForKey ( key , mode ) { const noteNames = tonal . Scale . notes ( key , mode ) ; const simplified = noteNames . map ( noteName => tonal . Note . simplify ( noteName ) ) ; const scaleNotes = new Set < string > ( simplified ) ; const scale = makeNoteScale ( MAX_PITCH , MIN_PITCH ) ; return scale . filter ( note => { const { pitch } = note ; const { accidental , letter } = getNoteDetails ( pitch ) ; const name = `${ letter }${ accidental }` ; const alternate = tonal . Note . enharmonic ( name ) ; return scaleNotes . has ( name ) || scaleNotes . has ( alternate ) ; } ) ; } const CHORD_MODES = new Set < string > ( [ 'M' , 'm' , 'dim' , 'aug' ] ) ; const TRIAD_QUALITIES = { major : [ 'M' , 'm' , 'm' , 'M' , 'M' , 'm' , 'm' ] , minor : [ 'm' , 'dim' , 'M' , 'm' , 'm' , 'M' , 'M' ] , 'harmonic minor' : [ 'm' , 'dim' , 'aug' , 'm' , 'M' , 'M' , 'dim' ] , } ; export function makeNotesTriadForKey ( key , mode ) { if ( ! CHORD_MODES . has ( mode ) ) { console . error ( `Violated contract for makeNotesTriadForKey; (mode = ${ mode }) is not one of ${ JSON . stringify ( CHORD_MODES ) }` ) ; return ; } const noteNames = tonal . Chord . notes ( key , mode ) ; const simplified = noteNames . map ( noteName => tonal . Note . simplify ( noteName ) ) ; const chordNotes = new Set < string > ( simplified ) ; const scale = makeNoteScale ( MIN_PITCH , MAX_PITCH ) ; return scale . filter ( note => { const { pitch } = note ; const { accidental , letter } = getNoteDetails ( pitch ) ; const name = `${ letter }${ accidental }` ; const alternate = tonal . Note . enharmonic ( name ) ; return chordNotes . has ( name ) || chordNotes . has ( alternate ) ; } ) ; } interface TriadValue { key : string ; quality : string ; } export function getTriadsForKey ( key , mode ) { if ( ! MODE_NAMES . includes ( mode ) ) { console . error ( `Violated contract for getTriadsForKey; (mode = ${ mode }) is not one of ${ JSON . stringify ( CHORD_MODES ) }` ) ; return ; } const scaleNotes = tonal . Scale . notes ( key , mode ) ; return scaleNotes . map ( ( noteName , i ) => { return { key : noteName , quality : TRIAD_QUALITIES [ mode ] [ i ] , } ; } ) ; } export function getHappyTriadsForKey ( key , mode ) { const triads = getTriadsForKey ( key , mode ) ; return triads . filter ( triad => { return triad . quality === 'M' ; } ) ; } export function getSadTriadsForKey ( key , mode ) { const triads = getTriadsForKey ( key , mode ) ; return triads . filter ( triad => { return triad . quality !== 'M' ; } ) ; } export function getAllHappyTriads ( ) { return KEY_NAMES . map ( keyName => { return { key : keyName , quality : 'M' , } ; } ) ; } export function getAllSadTriads ( ) { const nonMajorQualities = [ 'm' , 'aug' , 'dim' ] ; const triads = [ ] ; KEY_NAMES . forEach ( keyName => { nonMajorQualities . forEach ( quality => { triads . push ( { key : keyName , quality : quality } ) ; } ) ; } ) ; return triads ; }