JSPM

kamasi

0.3.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1
  • Score
    100M100P100Q52574F
  • License MIT

Music theory library

Package Exports

  • kamasi
  • kamasi/data/chords
  • kamasi/data/intervals
  • kamasi/data/scales
  • kamasi/interval
  • kamasi/note
  • kamasi/notelist
  • kamasi/search

Readme

Kamasi

Music theory library for node and browsers.

Quick Intro

To explore Kamasi, start with the functions interval(), note(), notes(), scale(), chord(), and search(). They all accept a string input, and return an object with the functions listed in the reference.

Here are a few examples of what you can do:

// Transpose or find interval between notes
note("G").intervalTo("Cb").toString(); // 'd4'
note("G4").transpose("d4").toString(); // 'Cb5'

// Lookup a chord or a scale
chord("F4 dim").toString(); // 'F4 Ab4 Cb5'
scale("D minor").toString(); // 'D E F G A Bb C'

// Use operations on custom or known note lists
notes("Fbb4 B##4 Cb5").simplify().sort().toString(); // 'D#4 B4 C#5'
scale("Dm").add("C#").transpose("m2").toString(); // 'Eb F Gb Ab Bb Cb Db D'

// Reverses lookup from intervals or notes
search("P1 m3 P5").exact().chord(); // 'minor'
notes("F Gb A Bb C").supersets().scales(); // [ 'chromatic', 'major double harmonic', ...]
scale("minor").subsets().chords(); // [ 'minor', 'minor seventh', ... ]

// Use nesting and chaining to make a query as complex as you want
scale("minor").includesAll(chord("sus4")); // true
chord("D4 minor").add("C#5").add(note("C#5").transpose("m3")).exact().chord(); // 'minor-major ninth'

Reference

Intervals

An interval is the difference between two pitches or pitch classes. It is represented by its shorthand notation, which specifies the direction, quality, and diatonic number of the interval.

In the example below, we show that a major second (M2) and a minor third (m3) following each other is enharmonically equivalent to a perfect fourth (P4):

interval("M2").add("m3").isEnharmonic("P4"); // true

You can use chaining to combine different actions:

Interval.fromSteps(4, 7) // Interval: 'P5'
  .add("M7") // Interval: 'A11'
  .simpleTerm() // Interval: 'A4'
  .frequencyRatio(); // 1.4142135623730951

Only the last value will be returned, but you can store intermediate value and continue the chaining:

const interval = Interval.fromSteps(4, 7) // Interval: 'P5'
  .add("M7") // Interval: 'A11'
  .simpleTerm(); // Interval: 'A4'

interval.cents(); // 600
interval.add("m2").cents(); // 700

Constructors:

  • new Interval(quality, number[, sign])
  • Interval.fromString(string) – Create from shorthand notation
  • Interval.fromSemitones(semitones) – Create interval spanning the specified number of semitones
  • Interval.fromSteps(diatonicSteps, semitones) – Create interval spanning specified number of steps (not guaranteed to have a valid answer)
  • Interval.isValidInterval(string) – Check if a string is valid interval notation (returns boolean, doesn't throw)

Methods:

  • interval.add(interval) Add two intervals
  • interval.sub(interval) Subtract one interval from another
  • interval.simpleTerm() Subtract all octaves from a compound interval
  • interval.simplify() Find the enharmonic interval with the simplest quality possible
  • interval.frequencyRatio() Returns the frequency ratio of interval as a float
  • interval.cents() Returns the interval size in cents as an int
  • interval.invert() Returns the invert of the interval
  • interval.isCompound() Returns true if the interval spans more than one octave
  • interval.isEnharmonic(interval) Checks if the interval is enharmonically equivalent to another
  • interval.toString() Returns the shorthand notation as a string

Notes

A note represents a specific pitch or a general pitch class. It is represented by its scientific pitch notation, which specifies the note letter, accidentals, and the octave (empty for pitch classes).

note("Fbb").simplify().toString(); // 'D#'
note("Fbb").isEnharmonic("D#"); // true
note("C#4").frequency(); // 277.1826309768721

You can also create notes from MIDI numbers or frequencies:

Note.fromMidi(60).toString(); // 'C4'
Note.fromFrequency(440).toString(); // 'A4'

A key feature is the ability to transpose notes using intervals. The reverse operation is also supported, allowing you to find the interval between two notes:

note("C").transpose("P5").toString(); // 'G'
note("D#").intervalTo("A").toString(); // 'd5'
note("Eb5").transpose("-A5").toString(); // 'Abb4'

You can use chaining to perform several actions in one line. If you just need the pitch class, and find 'Abb4' confusing, it can be simplified:

note("Eb5")
  .transpose("-A5") // Note: 'Abb4'
  .toPitchClass() // Note: 'Abb'
  .simplify() // Note: 'G'
  .toString(); // 'G'

Constructors:

  • new Note(letter, accidentals[, octave]) Create a new pitch (with octave) or pitch class
  • Note.fromString(string) Create a note from its scientific pitch notation
  • Note.fromMidi(number) Create a note from a MIDI number (0-127, where 60 = C4)
  • Note.fromFrequency(hz) Create a note from a frequency in Hz (A4 = 440Hz)
  • Note.isValidNote(string) Check if a string is valid scientific pitch notation (returns boolean, doesn't throw)

Methods:

  • note.transpose(interval) Transpose a note by the specified interval
  • note.distance(note) Returns the distance between two notes in semitones
  • note.intervalTo(note) Returns the interval from this to another note
  • note.intervalFrom(note) Returns the interval from another note to this
  • note.frequency() Returns the frequency in Hz as a float (A4 = 440Hz)
  • note.midi() Returns the MIDI code of the note
  • note.simplify() Returns the enharmonic note with the fewest possible accidentals
  • note.isEqual(note) Check if two notes are identical
  • note.isEnharmonic(note) Check if the note is enharmonically equivalent to another note
  • note.isPitch() Check if the note is a specific pitch
  • note.isPitchClass() Check if the note is a pitch class
  • note.toPitch(octave) Convert a pitch class to a pitch in the specified octave
  • note.toPitchClass() Convert a pitch to a pitch class by removing the octave
  • note.toString() Returns the scientific pitch notation as a string

NoteLists

If you want to represent a sequence of notes without being restricted to names scales or chords, you can use the NoteList class:

notes("C4 D#4 Ab4 D5").transpose("-m6").toString(); // 'E3 F##3 C4 F#4'

Chaining still works for the lists:

notes("Abb E4 C")
  .toPitches(4) // NoteList: 'Abb4 E4 C4'
  .simplify() // NoteList: 'G4 E4 C4'
  .remove("C4") // NoteList: 'G4 E4
  .sort() // NoteList: 'E4 G4'
  .toString(); // 'E4 G4'

Constructors:

  • new NoteList(notes) Create a NoteList from an array of notes
  • NoteList.fromString(string) Create a NoteList from a space separated list of notes
  • NoteList.fromIntervals(root, intervals) Create a NoteList from a root note and array of intervals

Methods:

  • notelist.transpose(interval) Transpose all notes in list
  • notelist.simplify() Simplify all notes in list
  • notelist.add(note) Add note to the end of the list
  • notelist.remove(note[, enharmonic]) Remove (enharmonic) notes from list
  • notelist.toggle(note[, enharmonic]) Toggle (enharmonic) notes in list
  • notelist.includes(note[, enharmonic]) True if list contains (enharmonic) note
  • notelist.includesAll(notelist[, enharmonic]) True if list contains all (enharmonic) notes
  • notelist.sort() Return a sorted copy of the list
  • notelist.root() Return the root note of the list
  • notelist.search([enharmonic]) See search
  • notelist.subsets([enharmonic]) See search
  • notelist.supersets([enharmonic]) See search
  • notelist.isEmpty() True if list is empty
  • notelist.isMixed() True if list is mix of pitches and pitch classes
  • notelist.isPitches() True if list is only specific pitches
  • notelist.isPitchClasses() True if list is only pitch classes
  • notelist.toPitches(octave) Convert all notes to pitches in specified octave
  • notelist.toPitchClasses() Convert all notes to pitch classes
  • notelist.toStringArray() Return array of scientific pitch notation
  • notelist.toString() Return scientific pitch notation as a string

Scales

A scale is just a NoteList, but you can create it using a known name:

scale("E augmented").toString(); // 'E G G# B B# D#'

It returns a NoteList object, so all methods in the last section can be used:

scale("Bb man gong").transpose("d5").simplify().toString(); // 'E G A C D'
  • NoteList.fromScale(tonic, name) Create a NoteList from a tonic note and a scale name
  • scale(name) Create a NoteList from a scale name string

If you only care about the intervals, you can skip the tonic note. 'C' will be selected by default:

scale("ionian").exact().scales(); // [ 'major', 'ionian' ]

These functions both return a NoteList, allowing normal use.

To find a scale from a NoteList, see the search section.

Chords

A chord is just a NoteList, but you can create it using a known name:

chord("A# dom7").toString(); // 'A# C## E# G#'

It returns a NoteList object, so all methods in the last section can be used:

chord("F minor").transpose("M2").includesAll(["G", "Bb", "D"]); // true
  • NoteList.fromChord(tonic, name) Create a NoteList from a tonic note and a chord name
  • chord(name) Create a NoteList from a chord name string

If you only care about the intervals, you can skip the tonic note. 'C' will be selected by default:

chord("sus4").supersets().chords(); // [ 'add fourth', 'suspended fourth' ]

These functions both return a NoteList, allowing normal use.

To find a chord from a NoteList, see the search section.

There are two ways to search for chords or scales using kamasi. To search with intervals, use the top-level search() function with chaining:

search("P1 M3 P5 M7").exact().chord(); // 'major seventh'

If you want to search using notes rather than intervals, the same interface is available from the notelist class:

notes("C E G").search().exact().chord(); // 'major'
notes("C D E F G A B").subsets().chords(); // [ 'major', 'major sixth', ... ]
notes("C Eb G").supersets().scales(); // [ 'minor', 'minor harmonic', ... ]

Note that scale() and chord() returns NoteList, allowing you to find sub and supersets of scales and chords:

scale("minor").supersets().scales(); // [ 'minor', 'chromatic', ... ]
scale("minor").subsets().chords(); // [ 'minor', 'minor seventh', ... ]
chord("7").subsets().chords(); // [ 'major' ]
chord("dim").supersets().scales(); // [ 'chromatic', 'blues hexatonic', ... ]

The function definitions are:

  • search(intervals, enharmonic) Find scales and chords from a space separated list of (enharmonic) intervals

If you want to search based on notes, use notelist:

  • notelist.exact([enharmonic]) Find scales and chords from a notelist
  • notelist.subsets([enharmonic]) Find chords from a notelist
  • notelist.supersets([enharmonic]) Find scales from a notelist

All of these functions will return an object with functions you can use to narrow the search. The search itself is performed lazily.

The top level lets you choose one of four functions:

  • exact() Scale/chord has the exact same intervals as search object
  • subsets() Scale/chord contains only intervals in the search object
  • supersets() Scale/chord contains all intervals in the search object

The second level contains four functions:

  • chord() Narrow search object down to a single chord
  • scale() Narrow search object down to scales scale
  • chords() Narrow search object down to chords
  • scales() Narrow search object down to scales

If multiple chords/scales match, the singlular form will return the first. This may or may not be the best match.