import { Progression , note } from "@tonaljs/tonal" ; import type { ChordType } from "@tonaljs/chord-type" ; import type { Chord } from "@tonaljs/chord" ; import { Note , transpose } from "@tonaljs/core" ; import { detect } from "@tonaljs/chord-detect" ; import { get , getChord } from "@tonaljs/chord" ; export class ChordNotes { readonly chord : Chord ; readonly notes : Note [ ] ; private constructor ( chord , notes ) { this . chord = chord ; this . notes = [ ... notes ] ; } equals ( chordNotes ) { const midiA = new Set ( this . notes . map ( n => n . midi ) ) ; const midiB = new Set ( chordNotes . notes . map ( n => n . midi ) ) ; if ( midiA . size !== midiB . size ) return false ; for ( let a of midiA ) if ( ! midiB . has ( a ) ) return false ; return true ; } static fromChord ( chord , octave ) { if ( chord . empty ) { throw new Error ( 'Invalid chord' ) ; } const notes = chord . intervals . map ( interval => transpose ( `${ chord . tonic }${ octave }` , interval ) ) . map ( n => note ( n ) as Note ) ; return new ChordNotes ( chord , notes ) ; } static fromChordType ( chordType , tonic , octave ) { return ChordNotes . fromChord ( getChord ( chordType . name || chordType . aliases [ 0 ] , tonic ) , octave ) ; } static fromNotes ( notes ) { let chordNotes = notes . map ( ( n ) => n . pc ) ; for ( let i = 0 ; i < chordNotes . length ; i ++ ) { let chords = detect ( chordNotes ) ; for ( const currentChord of chords ) { let parsedChord = get ( currentChord ) ; if ( parsedChord . empty ) { continue ; } if ( parsedChord . type === "minor augmented" ) { continue ; } return new ChordNotes ( parsedChord , notes ) ; } const firstNote = chordNotes . shift ( ) ; chordNotes . push ( firstNote ) ; } return null ; } static fromProgression ( progression , tonic , octave ) { let chords ; try { chords = Progression . fromRomanNumerals ( tonic , progression ) ; } catch ( e ) { throw new Error ( 'Invalid chord progression: unable to parse chord progression' ) ; } if ( chords . length < 2 ) { throw new Error ( 'Invalid chord progression: must have at least 2 chords' ) ; } const chordNotes = [ ] ; for ( const chord of chords ) { chordNotes . push ( ChordNotes . fromChord ( get ( chord ) , octave ) ) ; } return chordNotes ; } }