Music theory library for Python. Notes, intervals, scales and chords.
To install:
$ pip install musthe
To install as development:
(Optional) Create a virtualenv:
$ python -m venv env
$ source env/bin/activate
Then install from repository directory:
$ pip install -e .
Try it interactively in a python shell (e.g. bpython).
First, import everything:
>>> from musthe import *
>>> a = Note('A4')
>>> a
Note('A4')
>>> str(a)
'A4'
>>> a.frequency
440.0
>>> a.midi_note
69
By default, pitch classes, notes, etc will be displayed using unicode symbols:
>>> Note('C#4'), Note('D##2'), Note('Bb3'), Note('Dbb4')
(Note('C♯4'), Note('D𝄪2'), Note('B♭3'), Note('D𝄫4'))
You can switch to ASCII output by setting:
>>> PitchClass.rendering_options.use_unicode = False
Notes are recognized also with unicode symbols and/or solfège notation (same applies to pitch classes, chords and scales):
>>> Note('Sol♯5')
Note('G♯5')
You can set pitch classes to be displayed as solfège by setting:
>>> PitchClass.rendering_options.use_solfege = True
>>> Note('C4'), Note('D4'), Note('Eb4')
(Note('Do4'), Note('Re4'), Note('Mi♭4'))
Intervals can be added to notes:
>>> Note('C4') + Interval('P5') # P5 is a perfect fifth
Note('G4')
Intervals can be computed by subtracting one note from another:
>>> Note('E4') - Note('C4')
Interval('M3')
Intervals can be added together:
>>> Interval('P4') + Interval('M2')
Interval('P5')
or subtracted:
>>> Interval('P4') - Interval('M2')
Interval('m3')
An interval is compound if greater than an octave:
>>> Interval('m17').is_compound()
True
You can use reduce or split on compound intervals:
>>> Interval('m10').split()
[Interval('P8'), Interval('m3')]
>>> Interval('m17').reduce()
Interval('m3')
Intervals have their complimentary:
>>> Interval('m3').complement()
Interval('M6')
Adding an interval and its complement gives the octave:
>>> i = Interval('d4')
>>> i + i.complement()
Interval('P8')
Intervals are used in the creation of chords and scales.
A chord is made from a pitch class and a list of intervals.
There are some recipes for widely used chords:
>>> Chord('Amaj')
Chord('Amaj')
You can see which intervals make up a chord:
>>> Chord('Amaj').intervals
(Interval('P1'), Interval('M3'), Interval('P5'))
And you can even build chords simply by specifying a pitch class and a list of intervals:
>>> Chord('C', ('P1', 'm3', 'd5'))
Chord('Cdim')
Retrieve the actual notes of a chord:
>>> Chord('Cdim').notes()
[Note('C4'), Note('E♭4'), Note('G♭4')]
You can work with chord inversions. Here's an example of a first inversion of Cdim:
>>> Chord('Cdim').invert()
Chord('Cdim/E♭')
Tip: you can quickly perform second (or third, fourth, etc.) inversions using a numeric argument.
>>> Chord('Cdim').invert(1).notes()
[Note('E♭4'), Note('G♭4'), Note('C5')]
Pitch classes, notes, and chords, can be created from a string argument, or by using explicit types:
>>> Chord(PitchClass('C'), (Interval('P1'), Interval('m3'), Interval('d5'))) == Chord('C', ('P1', 'm3', 'd5'))
True
A scale is made from a pitch class and a list of intervals.
There are some recipes for widely used scales:
>>> s = Scale('B', 'major')
Scale('B', 'major')
Examining intervals:
>>> s.intervals
(Interval('P1'), Interval('M2'), Interval('M3'), Interval('P4'), Interval('P5'), Interval('M6'), Interval('M7'))
You can obtain the actual notes of a scale by using array indexing (scales start at the fourth octave):
>>> s[0]
Note('B4')
>>> s[0], s[2], s[4]
(Note('B4'), Note('D♯5'), Note('F♯5'))
Negative indices work too:
>>> s[-7]
Note('B3')
Walk up the scale for an octave:
>>> [s[i] for i in range(8)]
[Note('B4'), Note('C♯5'), Note('D♯5'), Note('E5'), Note('F♯5'), Note('G♯5'), Note('A♯5'), Note('B5')]
You can check if a pitch class, note or chord is contained in a scale, using in:
>>> PitchClass('D#') in s
True
>>> Note('D#3') in s
True
>>> Note('F3') in s
False
>>> Chord('C#min') in s
True
>>> Chord('Cmaj') in s
False
Here are a few more advanced things you can do with this library.
Hint: read source code to discover even more features not mentioned in this guide.
>>> set(Chord.identify_from_notes([Note('F4'), Note('A4'), Note('C#5')]))
{Chord('Faug')}
Note: Chord identification algorithms can be complex, and sometimes multiple solutions may be returned.
It is also possible that identification results in multiple valid solutions. Here's one such example:
>>> set(Chord.identify_from_notes(Chord('Fm7').notes()))
{Chord('Fmin7'), Chord('A♭maj6/F')}
>>> chords = [Chord('Cm'), Chord('Fm7'), Chord('Gm')]
>>> for scale in Scale.all():
... if chords in scale: print(scale)
...
C natural_minor
E♭ major
Harmonization involves constructing chords by using the degrees of a scale as the foundation.
>>> s = Scale('A', 'natural_minor')
>>> for i in range(len(s)):
... print(next(Chord.identify_from_notes(s[i:i+5:2])))
...
Amin
Bdim
Cmaj
Dmin
Emin
Fmaj
Gmaj
Or include the seventh as well:
>>> for i in range(len(s)):
... print(next(Chord.identify_from_notes(s[i:i+7:2])))
...
Amin7
Bmin7dim5
Cmaj7
Dmin7
Emin7
Fmaj7
Gdom7
This works by finding all scales whose set of pitches, when intersected with the set of pitches from the starting scale, reduces the size of the set by 1.
>>> s0 = Scale('A', 'natural_minor')
>>> for s in Scale.all():
... if len(s0) - len(set(s.pitches) & set(s0.pitches)) == 1:
... print(s)
...
C melodic_minor
D natural_minor
D melodic_minor
E natural_minor
F major
G major
A harmonic_minor
This is implemented in Scale.find_similar().
>>> s = Scale('C', 'major')
>>> for i in range(7):
... c = next(Chord.identify_from_notes(s[i*4:(i+1)*4+1:2]))
... print(c)
...
Cmaj
Gmaj
Dmin
Amin
Emin
Bdim
Fmaj
If you have lilypond installed, you can make small melodies using this program. An example is provided in 'lilypond_example.py'
- Federico Ferri
- Gonzalo Ciruelos
- zsinx6
- David H
- nvoster
- Sylvain
- Edgar Gavrik
- Sri Raghavan
- Augustus Wynn
- Marco Heins
Want to contribute? See CONTRIBUTING.
See the license file.