musy

Python toolbox for analyzing, creating and visualizing music.

musy is a comprehensive toolbox for analyzing and visualizing music. It lays the foundation for the Musy web apps.

At its core it has basic building blocks from which all theory can be derived:

For visualization these objects can be placed on instrument surfaces:

Installation

pip install musy

Note

Initialization

The Note is the basic building block from which you can create chords, scales, intervals and songs.

from musy import Note

c_sharp = Note("C#")
c_sharp
C#4

You can get compact representations of Note objects. For example, their MIDI numbers and binary representations.

c_sharp.midi, bin(c_sharp), hex(c_sharp)
(49, '0b110001', '0x31')

Addition and subtraction

Notes can be added and subtracted to form new notes. Each added integer represents a semitone.

c_sharp + 1
D4
c_sharp - 1
C4
c_sharp + 14
D#5

Comparison

Notes can be compared using familiar Python operators.

c = Note("C")
g = Note("G")

c < g
True

Octaves can make a difference in comparisons.

Note("C", oct=4) >= Note("G", oct=3)
True

Relative Major/Minor

Notes can be converted to its relative major or minor. As can be found on the circle of fifths.

Note("C").minor()
A3
Note("C#").major()
E4

Interval

Initialization

Interval objects can be obtained by calling interval on two notes or using the & operator.

f_sharp = Note("F#")

P4 = c_sharp & f_sharp
P4
perfect fourth (4)
P4.semitones, P4.long, P4.short, P4.type(), P4.is_contextual(), P4.is_consonant()
(5, 'perfect fourth', '4', 'Contextual', True, False)

Comparison

Intervals can also be compared.

P5 = c & g
P5
perfect fifth (5)
P5.semitones, P5.long, P5.short, P5.type(), P5.is_consonant(), P5.is_perfect()
(7, 'perfect fifth', '5', 'Perfect Consonant', True, True)
P4 != P5
True
P4 < P5
True

Chord

Initialization

The Chord is a stack of Note objects played together.

from musy import Chord

c_major = Chord(["C", "E", "G"])
c_major
Chord: 'C major triad'. Notes: ['C4', 'E4', 'G4']

Chord objects can be initialized from shorthand notation.

cmaj7 = Chord.from_short("Cmaj7")
cmaj7
Chord: 'C major seventh'. Notes: ['C4', 'E4', 'G4', 'B4']

Analysis

You can easily retrieve intervals, related chords and extensions from a Chord object.

# Root of Cmaj7 -> C
cmaj7.root
C4
# Dominant (V7) chord of Cmaj7 -> G7.
cmaj7.dominant()
Chord: 'G dominant seventh'. Notes: ['G4', 'B4', 'D5', 'F5']
# Upper extensions (b9, 9, #9, b11, #11, b13, #13)
cmaj7.root.ext()
[C#5, D5, D#5, F5, F#5, G#5, A5]
# Altered extensions (b9, #9, #11, b13)
cmaj7.root.alt_ext()
[C#5, D#5, F#5, G#5]

Extension can be added and removed.

# Cmaj7#9b13
cmaj7_sharp9_flat13 = cmaj7.add_ext("#9").add_ext("b13")
cmaj7_sharp9_flat13
Chord: 'BM6|CM'. Notes: ['C4', 'E4', 'G4', 'B4', 'D#5', 'G#5']
# Cmaj7(no3)
cmaj7.remove_ext("3")
Chord: 'C major seventh'. Notes: ['C4', 'G4', 'B4']
# Cmaj7(add2)
cmaj7.add2()
Chord: 'No chord found.'. Notes: ['C4', 'D4', 'E4', 'G4', 'B4']

Inversion

Chords can also be inverted with invert.

# Get 1st inversion chord of C major 7th
cmaj7.invert(1)
Chord: 'C major seventh, first inversion'. Notes: ['E4', 'G4', 'B4', 'C5']

Transposition

Like Note objects, Chord objects can be added and subtracted to transpose them.

cmaj7 + 2
Chord: 'D major seventh'. Notes: ['D4', 'F#4', 'A4', 'C#5']

Notes can be multiplied to create chords.

Note("C") * Note("E") * Note("G")
Chord: 'C major triad'. Notes: ['C4', 'E4', 'G4']

Table

Each chord can be displayed in a Pandas DataFrame table, which gives a quick overview of the notes and intervals in the chord.

cmaj7.to_frame()
Notes Relative Degree Relative Interval Absolute Interval Absolute Degree
0 C 1 unison unison 1
1 E 3 major third major third 3
2 G 5 perfect fifth minor third b3
3 B 7 major seventh major third 3

This get more interesting when we want to analyze more complicated chords and progressions. Take for example this chord:

Cdim6maj7 = Chord([Note("C"), Note("D#"), Note("F#"), Note("A"), Note("B")])
Cdim6maj7.to_frame()
Notes Relative Degree Relative Interval Absolute Interval Absolute Degree
0 C 1 unison unison 1
1 D# b3 minor third minor third b3
2 F# b5 tritone minor third b3
3 A 6 major sixth minor third b3
4 B 7 major seventh major second 2

We can immediately see that there are 2 minor 3rds (i.e. b3 and b5) so the base is a diminished chord (Cdim or ). It is extended with a major 6th (6) and a major 7th (maj7). So we can describe this as a C°6maj7 chord.

PolyChord

Initialization

For polyphonic use cases you can create PolyChord objects. This objects inherits the same functionality as Chord objects.

from musy import PolyChord

c = Chord.from_short("C")
bbmaj7_3_inv = Chord.from_short("Bbmaj7").invert(3)

poly_chord = PolyChord([c, bbmaj7_3_inv])
poly_chord
PolyChord: 'C major triad|Bb major seventh, third inversion'. Notes: ['C4', 'E4', 'G4', 'A4', 'Bb5', 'D5', 'F5']

Within PolyChord objects we can treat it as a single chord or analyze the underlying chords separately. For example, here we display 2 tables to analyze the underlying chords of the PolyChord object.

poly_chord_tables = poly_chord.to_frame()
print(f"Chord 1: {poly_chord.chords[0].name}")
display(poly_chord_tables[0])
print(f"Chord 2: {poly_chord.chords[1].name}")
display(poly_chord_tables[1])
Chord 1: C major triad
Notes Relative Degree Relative Interval Absolute Interval Absolute Degree
0 C 1 unison unison 1
1 E 3 major third major third 3
2 G 5 perfect fifth minor third b3
Chord 2: Bb major seventh, third inversion
Notes Relative Degree Relative Interval Absolute Interval Absolute Degree
0 A 1 unison unison 1
1 Bb b9 minor ninth minor ninth b9
2 D 4 perfect fourth minor sixth b6
3 F b6 minor sixth minor third b3

Scale

Initialization

Scale objects are collections of intervals from which we can generate notes and chords around a root note.

from musy import Scale

dorian = Scale("dorian")
dorian
Scale: Dorian. Intervals: ['1', '2', 'b3', '4', '5', '6', 'b7']

Note Generation

When given a root note, Scale generates the notes of the scale.

dorian.get_notes("C")
[C4, D4, D#4, F4, G4, A4, A#4]

Mode Generation

All the modes of any Scale can be generated.

dorian.get_modes()
[Scale: Dorian. Intervals: ['1', '2', 'b3', '4', '5', '6', 'b7'],
 Scale: Phrygian. Intervals: ['1', 'b2', 'b3', '4', '5', 'b6', 'b7'],
 Scale: Lydian. Intervals: ['1', '2', '3', '#4', '5', '6', '7'],
 Scale: Mixolydian. Intervals: ['1', '2', '3', '4', '5', '6', 'b7'],
 Scale: Minor. Intervals: ['1', '2', 'b3', '4', '5', 'b6', 'b7'],
 Scale: Locrian. Intervals: ['1', 'b2', 'b3', '4', 'b5', 'b6', 'b7'],
 Scale: Ionian. Intervals: ['1', '2', '3', '4', '5', '6', '7']]

Triad Generation

Triads and seventh chords in the scale can be generated around a root note.

dorian.get_triads("D")
[Chord: 'D minor triad'. Notes: ['D4', 'F4', 'A4'],
 Chord: 'E minor triad'. Notes: ['E4', 'G4', 'B4'],
 Chord: 'F major triad'. Notes: ['F4', 'A4', 'C4'],
 Chord: 'G major triad'. Notes: ['G4', 'B4', 'D5'],
 Chord: 'A minor triad'. Notes: ['A4', 'C4', 'E5'],
 Chord: 'B diminished triad'. Notes: ['B4', 'D5', 'F5'],
 Chord: 'C major triad'. Notes: ['C5', 'E6', 'G6']]

Secondary Dominants

You can get all the secondary dominants (i.e. V7 chords) of a given Scale.

Scale("major").secondary_dominants("C")
[Chord: 'G dominant seventh'. Notes: ['G4', 'B4', 'D5', 'F5'],
 Chord: 'A dominant seventh'. Notes: ['A4', 'C#5', 'E5', 'G5'],
 Chord: 'B dominant seventh'. Notes: ['B4', 'D#5', 'F#5', 'A5'],
 Chord: 'No chord found.'. Notes: ['C5', 'E5', 'G5', 'A#5'],
 Chord: 'D dominant seventh'. Notes: ['D5', 'F#5', 'A5', 'C6'],
 Chord: 'E dominant seventh'. Notes: ['E5', 'G#5', 'B5', 'D6'],
 Chord: 'F# dominant seventh'. Notes: ['F#5', 'A#5', 'C#6', 'E6']]

Seventh Chord Generation

dorian.get_sevenths("E")
[Chord: 'E minor seventh'. Notes: ['E4', 'G4', 'B4', 'D4'],
 Chord: 'F# minor seventh'. Notes: ['F#4', 'A4', 'C#4', 'E5'],
 Chord: 'G major seventh'. Notes: ['G4', 'B4', 'D4', 'F#5'],
 Chord: 'A dominant seventh'. Notes: ['A4', 'C#4', 'E5', 'G5'],
 Chord: 'B minor seventh'. Notes: ['B4', 'D4', 'F#5', 'A5'],
 Chord: 'C# half diminished seventh'. Notes: ['C#5', 'E6', 'G6', 'B6'],
 Chord: 'D major seventh'. Notes: ['D5', 'F#6', 'A6', 'C#6']]

All information can be conveniently retrieved and displayed as a Pandas DataFrame with to_frame.

Table

dorian.to_frame(root="E")
Degree Relative Interval Mode Relative Semitones Absolute Semitones Notes Triad Seventh Chord
0 1 unison dorian 0 2 E E minor triad E minor seventh
1 2 major second phrygian 2 1 F# F# minor triad F# minor seventh
2 b3 minor third lydian 3 2 G G major triad G major seventh
3 4 perfect fourth mixolydian 5 2 A A major triad A dominant seventh
4 5 perfect fifth minor 7 2 B B minor triad B minor seventh
5 6 major sixth locrian 9 1 C# C# diminished triad C# half diminished seventh
6 b7 minor seventh ionian 10 2 D D major triad D major seventh

Custom Scales

Consult Scale.available_scales for a list of available scales. If a scale is not available, you can create your own scale from intervals.

persian = Scale.from_intervals(["1", "b2", "3", "4", "b5", "b6", "7"], "persian")
persian
Scale: Persian. Intervals: ['1', 'b2', '3', '4', 'b5', 'b6', '7']
persian.get_notes("C")
[C4, C#4, E4, F4, F#4, G#4, B4]
persian.get_triads("C")
[Chord: 'No chord found.'. Notes: ['C4', 'E4', 'F#4'],
 Chord: 'No chord found.'. Notes: ['C#4', 'F4', 'G#4'],
 Chord: 'E suspended second triad'. Notes: ['E4', 'F#4', 'B4'],
 Chord: 'No chord found.'. Notes: ['F4', 'G#4', 'C5'],
 Chord: 'F# suspended fourth triad'. Notes: ['F#4', 'B4', 'C#5'],
 Chord: 'C augmented triad, second inversion'. Notes: ['G#4', 'C5', 'E5'],
 Chord: 'No chord found.'. Notes: ['B4', 'C#5', 'F5']]

Listening

Note, Chord, PolyChord and Scale objects can all be heard by calling the play method on them. Check out the musy documentation on Note, Chord, PolyChord and Scale for example code and audio playbacks.

Visualization

musy objects can be visualized on a piano or guitar by providing a list of Note objects to the rendering method. Notes can easily be retrieved from Chord and Scale objects.

Piano

musy will show the minimum octaves on the piano needed to show the object. Here for example a single C# on one octave.

from musy.viz import Piano

Piano().visualize_note(Note("C#"))
C
D
E
F
G
A
B
C#
D#
F#
G#
A#

Here is an example of visualizing a Cadd9/F chord where we need multiple octaves.

cadd9_over_f = Chord([Note("F", 2), Note("C", 3), Note("E", 3), Note("G", 3), Note("D", 4)])
Piano().visualize_chord(cadd9_over_f)
C
D
E
F
G
A
B
C
D
E
F
G
A
B
C
D
E
F
G
A
B
C#
D#
F#
G#
A#
C#
D#
F#
G#
A#
C#
D#
F#
G#
A#

For scale visualization on Piano we show 2 octaves by default. The number of octaves can be controlled with the octs parameter.

Piano().visualize_scale(Scale("double harmonic major"), root="C", octs=3)
C
D
E
F
G
A
B
C
D
E
F
G
A
B
C
D
E
F
G
A
B
C#
D#
F#
G#
A#
C#
D#
F#
G#
A#
C#
D#
F#
G#
A#

Guitar

visualize_note shows options for a given note in a certain octave. For example a C4 is shown below.

from musy.viz import Guitar

Guitar().visualize_note(Note("C", oct=4))
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
E
B
G
D
A
C
E
C

visualize_chord shows you all the notes across octaves to spot different voicings.

Guitar().visualize_chord(Chord.from_short("Cmaj9"))
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
E
E
G
B
C
D
E
G
B
C
D
B
B
C
D
E
G
B
C
D
E
G
G
G
B
C
D
E
G
B
C
D
E
D
D
E
G
B
C
D
E
G
B
C
A
B
C
D
E
G
B
C
D
E
G
E
E
G
B
C
D
E
G
B
C
D

visualize_scale shows all notes in the scale from a given root note.

Guitar().visualize_scale(Scale("phrygian dominant"), root="C")
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
E
E
F
G
G#
A#
C
C#
E
F
G
G#
A#
C
C#
B
C
C#
E
F
G
G#
A#
C
C#
E
F
G
G#
G
G
G#
A#
C
C#
E
F
G
G#
A#
C
C#
E
F
D
E
F
G
G#
A#
C
C#
E
F
G
G#
A#
C
A
A#
C
C#
E
F
G
G#
A#
C
C#
E
F
G
E
E
F
G
G#
A#
C
C#
E
F
G
G#
A#
C
C#