Toolbox for analyzing, creating and visualizing music
Project description
musy
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: -
Note: The
basic atomic unit of music. -
Chord and
PolyChord:
A stack of notes played together. -
Scale: Stack
of intervals from which harmony, (diatonic) chords, melody, etc. can be
derived.
For visualization these objects can be placed on instrument surfaces: -
Piano: Basic
key layout -
Guitar:
Guitar fretboard
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']
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 C°). 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']]
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#"))
<style>
.piano { background: #222; padding: 20px 0; position: relative; }
.white-keys { display: flex; }
.white-key, .black-key { text-align: center; font-family: Arial; position: relative; }
.white-key { width: 40px; height: 125px; background: #fff; border: 1px solid #000;
color: #111; font-size: 18px; line-height: 200px; z-index: 1; }
.black-key { width: 20px; height: 80px; background: #000; color: #fff; border: 1px solid #333;
position: absolute; z-index: 2; line-height: 100px; font-size: 14px; top: 20px; }
.highlight { background: #ff0 !important; color: #000 !important; }
</style>Here is an example of visualize a Cmaj9/B chord where we need multiple
octaves.
cmaj9_over_b = Chord([Note("B", 2), Note("C", 4), Note("E", 3), Note("G", 3), Note("D", 4)])
Piano().visualize_chord(cmaj9_over_b)
<style>
.piano { background: #222; padding: 20px 0; position: relative; }
.white-keys { display: flex; }
.white-key, .black-key { text-align: center; font-family: Arial; position: relative; }
.white-key { width: 40px; height: 125px; background: #fff; border: 1px solid #000;
color: #111; font-size: 18px; line-height: 200px; z-index: 1; }
.black-key { width: 20px; height: 80px; background: #000; color: #fff; border: 1px solid #333;
position: absolute; z-index: 2; line-height: 100px; font-size: 14px; top: 20px; }
.highlight { background: #ff0 !important; color: #000 !important; }
</style>For scale visualization on
Piano we show
2 octaves by default, but this can be controlled with the octs
parameter.
Piano().visualize_scale(Scale("major"), root="D")
<style>
.piano { background: #222; padding: 20px 0; position: relative; }
.white-keys { display: flex; }
.white-key, .black-key { text-align: center; font-family: Arial; position: relative; }
.white-key { width: 40px; height: 125px; background: #fff; border: 1px solid #000;
color: #111; font-size: 18px; line-height: 200px; z-index: 1; }
.black-key { width: 20px; height: 80px; background: #000; color: #fff; border: 1px solid #333;
position: absolute; z-index: 2; line-height: 100px; font-size: 14px; top: 20px; }
.highlight { background: #ff0 !important; color: #000 !important; }
</style>Piano().visualize_scale(Scale("double harmonic major"), root="C", octs=3)
<style>
.piano { background: #222; padding: 20px 0; position: relative; }
.white-keys { display: flex; }
.white-key, .black-key { text-align: center; font-family: Arial; position: relative; }
.white-key { width: 40px; height: 125px; background: #fff; border: 1px solid #000;
color: #111; font-size: 18px; line-height: 200px; z-index: 1; }
.black-key { width: 20px; height: 80px; background: #000; color: #fff; border: 1px solid #333;
position: absolute; z-index: 2; line-height: 100px; font-size: 14px; top: 20px; }
.highlight { background: #ff0 !important; color: #000 !important; }
</style>Guitar
visualize_note shows you different option for a given note in a
certain octave. Here a C4.
from musy.viz import Guitar
Guitar().visualize_note(Note("C", oct=4))
<style>
.guitar-fretboard { background: #c49e60; border-radius: 8px; padding: 8px; display: inline-block; }
.guitar-fret-nums { display: flex; font-size: 12px; color: #333; margin-bottom: 2px; }
.guitar-fret-num { width: 32px; text-align: center; font-weight: bold; border-right: 2.5px solid #888; }
.guitar-dot { height: 6px; text-align: center; color: #444; font-size: 10px; }
.guitar-row { display: flex; align-items: center; position: relative; }
.guitar-string-name { width: 24px; text-align: right; margin-right: 0px; font-weight: bold; color: #444; }
.guitar-cell, .guitar-note { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-right: 2.5px solid #888; position: relative; }
.guitar-cell { border-bottom: 1.2px solid #bbb; background: none; }
.guitar-note { background: rgba(255, 255, 0, 0.7); color: #222;
</style>You can for example get
Note objects
with different octaves to shows all notes on the fretboard. For example
all D notes.
Guitar().visualize_notes([Note("D", n) for n in range(1, 8)])
<style>
.guitar-fretboard { background: #c49e60; border-radius: 8px; padding: 8px; display: inline-block; }
.guitar-fret-nums { display: flex; font-size: 12px; color: #333; margin-bottom: 2px; }
.guitar-fret-num { width: 32px; text-align: center; font-weight: bold; border-right: 2.5px solid #888; }
.guitar-dot { height: 6px; text-align: center; color: #444; font-size: 10px; }
.guitar-row { display: flex; align-items: center; position: relative; }
.guitar-string-name { width: 24px; text-align: right; margin-right: 0px; font-weight: bold; color: #444; }
.guitar-cell, .guitar-note { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-right: 2.5px solid #888; position: relative; }
.guitar-cell { border-bottom: 1.2px solid #bbb; background: none; }
.guitar-note { background: rgba(255, 255, 0, 0.7); color: #222;
</style>visualize_chord shows you all the notes across octaves so you can spot
different voicings.
Guitar().visualize_chord(Chord.from_short("Cmaj9"))
<style>
.guitar-fretboard { background: #c49e60; border-radius: 8px; padding: 8px; display: inline-block; }
.guitar-fret-nums { display: flex; font-size: 12px; color: #333; margin-bottom: 2px; }
.guitar-fret-num { width: 32px; text-align: center; font-weight: bold; border-right: 2.5px solid #888; }
.guitar-dot { height: 6px; text-align: center; color: #444; font-size: 10px; }
.guitar-row { display: flex; align-items: center; position: relative; }
.guitar-string-name { width: 24px; text-align: right; margin-right: 0px; font-weight: bold; color: #444; }
.guitar-cell, .guitar-note { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-right: 2.5px solid #888; position: relative; }
.guitar-cell { border-bottom: 1.2px solid #bbb; background: none; }
.guitar-note { background: rgba(255, 255, 0, 0.7); color: #222;
</style>visualize_scale shows all note in the scale from a given root note.
Guitar().visualize_scale(Scale("phrygian dominant"), root="C")
<style>
.guitar-fretboard { background: #c49e60; border-radius: 8px; padding: 8px; display: inline-block; }
.guitar-fret-nums { display: flex; font-size: 12px; color: #333; margin-bottom: 2px; }
.guitar-fret-num { width: 32px; text-align: center; font-weight: bold; border-right: 2.5px solid #888; }
.guitar-dot { height: 6px; text-align: center; color: #444; font-size: 10px; }
.guitar-row { display: flex; align-items: center; position: relative; }
.guitar-string-name { width: 24px; text-align: right; margin-right: 0px; font-weight: bold; color: #444; }
.guitar-cell, .guitar-note { width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-right: 2.5px solid #888; position: relative; }
.guitar-cell { border-bottom: 1.2px solid #bbb; background: none; }
.guitar-note { background: rgba(255, 255, 0, 0.7); color: #222;
</style>Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file musy-0.0.9.tar.gz.
File metadata
- Download URL: musy-0.0.9.tar.gz
- Upload date:
- Size: 34.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
334d75d9aec18e734c817f0cdcb6823629cfc5c08e808ff609acc1d963c9b955
|
|
| MD5 |
ed153186a927c8a74c3ae3e5a8673ee2
|
|
| BLAKE2b-256 |
33d1aa95ed17780a5602c82dc35a450befbf16538cba863912bf1d91b7a8bf23
|
File details
Details for the file musy-0.0.9-py3-none-any.whl.
File metadata
- Download URL: musy-0.0.9-py3-none-any.whl
- Upload date:
- Size: 24.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
57bf1f824fec8922793bb50c228cc7e646a808437e6529c5d9fadac846fe22ba
|
|
| MD5 |
88aae75d5780ee6ae3ec0534bfc13bed
|
|
| BLAKE2b-256 |
98445d429eb3746b28b96b1452470bae00f5b4da0e6f41057d929fa6a3a1940d
|