tonal
is a modular, functional music theory library. Built from a collection of modules, it's able to create and manipulate tonal elements of music (pitches, chords, scales, keys). It deals with abstractions (not actual music) and while is designed for algorithmic composition and music generation, can be used to develop any kind of midi or audio software.
Although this library is under active development, the modules more than 1.0.0 are considered more or less stable.
## Example
var tonal = require('tonal')
// notes and intervals
tonal.note.fromMidi(60) // => 'C4'
tonal.note.midi('A4') // => 69
tonal.note.fromFreq(220) // => 'A3'
tonal.note.freq('C2') // => 65.40639132514966
// transposition and distances
tonal.tranpose('D4', '2M') // => 'E#4'
tonal.distance('C', 'G') // => '5P'
['c', 'd', 'e'].map(tonal.transpose('3M')) // => ['E4', 'F#4', 'G#4']
// scales and chords
tonal.scale('A major') // => ['A', 'B', 'C#', 'D', 'E', 'F#', 'G#']
tonal.chord('Cmaj7') // => ['C', 'E', 'G', 'B']
// harmonizers
var major = tonal.harmonizer('1 3 5')
major('C#') // => ['C#', 'E#', 'G#']
major('E5') /// => ['E5', 'G#5', 'B5']
var V7 = tonal.harmonizer('1 3 5 7m')
var V7ofV = function(tonic) { V7(tonal.transpose(tonic, '5P')) }
var V7ofV('D') // => ['A4', 'C#5', 'E5', 'G7']
// keys
key('###') // => 'A major'
key.signature('A major') // => '###'
key.altNotes('A major') // => ['F#', 'C#']
key.relative('minor', 'A major') // => 'F minor'
Although tonal
is a work in progress, currently is implemented (but not all released):
- Note, intervals, transposition, distances, enharmonics
- Midi and frequency conversion
- Scales, chords, dictionaries
- Work with collection of notes: gamut, harmonizer
- Pitch sets and binary representations
- Keys, keys signatures, key scales and chords, key detection
This library is evolving with this ideas in mind:
- Notes and intervals are represented with strings, instead of objects. Easy and concise code.
- Functional: no classes, no side effects, no mutations. Just functions, data-in data-out. Most of the functions has the data to operate on as last argument and lot of functions are currified.
- Small and fast
- Modular: lot of modules (all integrated in tonal). You can require exactly the functions you need, or get the whole thing.
- Different notations: scientific notation by default. Helmholtz coming. Change it easily.
- Documented: all public functions are documented inside the code. Aside the generated documentation (in API.md file) a 'usage' guides are provided for each module.
- Learneable: since all the modules share the same philosophy is easy to work with them.
- Tested: carefully tested with coverage support.
- Advanced features: chord and scale detection, binary sets, chord progressions, key signatures...
First of all, because I want to learn:
Reinventing the wheel is bad for business, but it’s great for learning *
Also, I want a complete library, where I can model all what I learn, with some (for me) esoteric features like interval classes, binary scales and other weird stuff.
This is a detailed how to of what can be done with tonal.
tonal
is a collection of modules. They all live in this
multi package repository (monorepo). Take a look inside packages
. The object from tonal
package is a facade with access to all the modules, perfect if you are not concern with javascript size (currently about 30kb minified)
The code examples shows both requiring each package or using the tonal facade. To use the tonal facade you must require tonal
library:
var tonal = require('tonal')
Notes in tonal
are just strings with notes in scientific notation. Midi numbers are integers from 0 to 127.
How to get midi number from note name?
var midi = require('note-midi')
midi('A4') // => 69
// or
tonal.note.midi('c2') // => 36
How to get note name from midi number?
var note = require('midi-note')
note(69) // => 'A4'
// or
tonal.midi.note(36) // => 'C2'
How to get frequency from a note name or midi number?
var freq = require('note-freq')
freq(440, 'A3') // => 220
freq(440, 69) // => 440
// or partially applied
var standard = freq(440)
standard('A2') // => 110
// or
tonal.note.freq(440, 'C2')
How to get note enharmonics?
var enharmonics = require('enharmonics')
enharmonics('B#') // => [ 'A###', 'B#', 'C' ]
// or
tonal.enharmonics('B#')
How to simplify note name?
Select the less altered note from it's enharmonics:
enharmonics.simplify('B#3') // => 'C4'
// or
tonal.enharmonics.simplify('B#3') // => 'C4'
How to get pitch class of a note?
var pc = require('pitch-class')
pc('c#2') // => 'C#'
// or
tonal.pitchClass(57) // => A (yes, it works with midi too)
How to transpose a note by an interval?
var transpose = require('note-transposer')
transpose('C3', '3m') // => 'Eb3'
// create a transposer
var major3th = transpose('3M')
['C', 'D', 'E'].map(major3th) // => ['E', 'F#', 'G#']
// using tonal
tonal.transpose('C3', '3m') // => 'Eb3'
How to harmonize a note from an interval list?
var harmonize = require('note-harmonizer')
// harmonize a note
harmonize('P1 M3 P5', 'G2') // => ['G2', 'B2', 'D3']
// create an harmonizer
var maj7Chord = harmonize('1 3 5 7')
var maj7Chord('A4') // => ['A4', 'C#5', 'E5', 'G#5']
// or
tonal.harmonize('1 3 5', 'G2') // => ['G2', 'B2', 'D3']
How to find the interval between two notes?
var interval = require('note-interval')
interval('C4', 'E4') // => '3M'
interval('D2', 'C2') // => '-2M'
// partially applied
var fromC = interval('C')
fromC('D') // => '2M'
// or
tonal.note.interval('C3', 'G#3') // => 'A5'
How to get the size in semitones of an interval?
var semitones = require('semitones')
semitones('P4') // => 5
['P1', 'M2', 'M3', 'P4', 'P5', 'M6', 'M7'].map(semitones)
// => [ 0, 2, 4, 5, 7, 9, 11, 12 ]
// or
tonal.semitones('P4') // => 5
How to know if an interval is ascending or descending?
return semitones('M3') < 0 ? 'descending' : 'ascending'
How to simplify an interval?
var simplify = require('interval-simplify')
simplify('9M') // => '2M'
// or
tonal.interval.simplify('8P') // => '8P'
How to know if an interval is simple or compound?
simplify(interval) === interval ? 'simple' : 'compound'
How to invert an interval?
var invert = require('interval-invert')
invert('2M') // => '7m'
// or
tonal.interval.invert('3m') // => '6M'
How to get the interval class of an interval?
var ic = require('interval-class')
ic('P4') // => 5
['1P', '2M', '3M', '4P', '5P', '6M', '7M'].map(ic)
// => [ 0, 2, 4, 5, 5, 3, 1, 0 ]
// using semitones
ic(7) // => 5 (a perfect fifth)
// or
tonal.ic(7) // => 5 (a perfect fifth)
How to create a scale from a scale name?
var scale = require('music-scale')
// get scale from name
scale('A major') // => ['A', 'B', 'C#', 'D', 'E', 'F#', 'G#']
// or
tonal.scale('A major') // => ['A', 'B', 'C#', 'D', 'E', 'F#', 'G#']
How to create a scale from name and tonic?
tonal.scale('major', 'A4') // => ['A4', 'B4', 'C#4', 'D4', 'E4', 'F#4', 'G#4']
// partially applied
var major = tonal.scale('major')
major('A4') // => ['A4', 'B4', 'C#4', 'D4', 'E4', 'F#4', 'G#4']
How to create a scale from intervals and tonic?
tonal.scale('1 2 3 4 5 6 7', 'A') // => ['A', 'B', 'C#' 'D', 'E', 'F#', 'G#']
tonal.scale('1 2 3 4 5 6 7', 'A4') // => ['A4', 'B4', 'C#4', 'D4', 'E4', 'F#4', 'G#4']
How to create a chord from a chord name?
var chord = require('music-chord')
chord('Cmaj7') // => ['C', 'E', 'G', 'B']
// or
tonal.chord('Cmaj7') // => ['C', 'E', 'G', 'B']
How to create a chord from name and tonic?
tonal.chord('maj7', 'A') // => ['A', 'C#', 'E', 'G#']
// partially applied
var maj7 = tonal.chord('maj7')
var maj7('A4') // => ['A4', 'C#5', 'E5', 'G#5']
How to create a chord from intervals and tonic?
tonal.chord('1 3 5 7', 'A4') // => ['A4', 'C#5', 'E5', 'G#5']
How to sort a collection of notes?
var sort = require('pitch-sort')
sort(true, 'c2 d5 f0 gb g#2 db-1 c# ab2 h6 b3') // true means ascending
// => [null, 'C#', 'Gb', 'Db-1', 'F0', 'C2', 'G#2', 'Ab2', 'B3', 'D5']
// or use tonal.sort, tonal.sortAsc and tonal.sortDesc:
tonal.sortAsc('A G F C E') // => ['C', 'E', 'F', 'G', 'A']
How to filter a collection of notes?
var filter = require('note-filter')
filter('G A B C', 'c2 eb3 g5 gb6 a4 a#4 b5') // => ['D2', 'G5', 'A4', 'B5']
// or
tonal.filter('C', 'c d e f g') // => ['C']
- music-notation:
Parse notes, intervals, scale names, alterations...
- note-midi:
Note name to midi
- midi-note:
Midi to note name
- midi-freq:
Given a midi note, get it's frequency
- enharmonics:
Get note enharmonics
- semitones:
Get the size in semitones of an interval
- interval-class:
Get the interval-class of an interval
- note-transposer:
Transpose notes
- note-harmonizer:
Harmonize notes or create harmonizer functions
- note-interval:
Find the interval between two notes
- pitch-set:
Create pitch sets
- pitch-sort:
Sort collection of notes or intervals
- note-filter: Filter notes
- music-scale:
Create music scales
- music-chord:
Create music chords
- scale-dictionary:
A music scales dictionary
- chord-dictionary:
A music chords dictionary
- note-range:
Create ranges of notes
- music-gamut:
Work with collection of notes
- interval-density:
Analyze the intervals of a collection of notes
- music-key:
Get key accidentals, relative major and minor, key notes and key alterations
- scale-triads:
Create triads from scales
- binary-set:
Binary pitch set manipulation
- chord-type:
Get the type of a chord
- chord-progression:
Create chord progressions
Install via npm: npm i --save tonal
Then you can load the whole library:
var tonal = require('tonal')
tonal.transpose(tonal.note.fromMidi(60), '2M') // => 'D4'
... or install and require individual modules:
var midiNote = require('midi-note')
var transpose = require('note-transposer')
transpose(midiNote(60), '2M') // => 'D4'
## Documentation and tests
The functions are extensively documented inside the code. The generated documentation can be read here
To run the tests, clone this repository and run:
make
This library takes inspiration from lot of places:
- Teoria: https://github.com/saebekassebil/teoria
- Impro-Visor: https://www.cs.hmc.edu/~keller/jazz/improvisor/
- MusicKit: https://github.com/benzguo/MusicKit
- Music21: http://web.mit.edu/music21/doc/index.html
- leipzig: https://github.com/ctford/leipzig
While developing, I read/study part of this resources:
The binary representation of the scales are based on the awesome book Arpeggio & Scale Resources by Rich Cochrane. Additional scale stuff (like scale spaces) are inspired by the works of Walter Zettel and William Zeitler
Trying to get the correct name of the things: http://music.stackexchange.com/questions/17780/naming-pitch-and-interval-collections
Interval analysis stuff are based on the book Harmonic Materials of Modern Music of Howard Hanson.
Other things this library can be related to:
- A Corpus Study of Rock Music: http://theory.esm.rochester.edu/rock_corpus/index.html
- Musical futures: https://www.musicalfutures.org/
- Music JSON proposal: https://github.com/soundio/music-json
- Staff notation: http://opusmodus.com/omn.html
MIT License