Skip to content

fferri/musthe

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

musthe

Music theory library for Python. Notes, intervals, scales and chords.

Installation

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 .

Usage

Try it interactively in a python shell (e.g. bpython).

First, import everything:

>>> from musthe import *

Working with notes

>>> 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'))

Working with intervals

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.

Working with chords

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

Working with scales

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

Advanced use cases

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.

Identify a chord from notes:

>>> 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')}

Given a list of chords, find all the scales that contain them:

>>> chords = [Chord('Cm'), Chord('Fm7'), Chord('Gm')]
>>> for scale in Scale.all():
...     if chords in scale: print(scale)
...
C natural_minor
E♭ major

Harmonize a scale and print the chord names:

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

Starting from a scale, find all the other scales that differ by only one note:

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().

Circle of fifths on harmonized C major:

>>> 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

Lilypond

If you have lilypond installed, you can make small melodies using this program. An example is provided in 'lilypond_example.py'

Contributors

Want to contribute? See CONTRIBUTING.

License

See the license file.

About

Music theory implemented in Python. Notes, intervals, scales and chords.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 100.0%