Note

Basic atomic building block for music

The Note is the basic atomic unit in music.


source

Note

 Note (note:str, oct:int=4)

Base class for objects needing a basic __repr__

Initialization

Every note has an accompanying octave oct associated with it. This is useful later for determining intervals and playing the sound of the notes.

a_sharp = Note("A#")
a_sharp
A#4

Integer Representation

Each note has an integer value which shows its place in a C octave (C == 0, C# == 1, B == 11, etc.)

In this example A# should denote 10 as its integer value.

a_sharp
A#4
assert int(a_sharp) == 10
int(a_sharp)
10

You can also do the inverse and initialize from an integer or MIDI note number.

ten = Note.from_int(10)
assert ten == a_sharp
a_sharp.from_int(10)
A#4

If you would like to take the octave into account, you can use the midi property to get the MIDI note number.

a_sharp.midi
58

And initialize from a MIDI note number.

fifthy_eight = Note.from_midi(58)
assert fifthy_eight == a_sharp
fifthy_eight.from_int(10)
A#4

String Representation

assert str(a_sharp) == "A#"
a_sharp.note
'A#'
c_sharp = Note("C#")
c_sharp
C#4

Binary representation

We use the MIDI note number to represent notes.

Using the MIDI note number we can get binary representations to convert to other formats. It also allows for very compact representations.

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

Basic Properties

There are a number of ways to analyze Note objects.

Each Note has an accompanying octave number.

c_sharp.oct
4

C# has a single sharp, is not a natural, has an accidental.

assert c_sharp.sharp
assert not c_sharp.natural
assert c_sharp.accidental
assert c_sharp.num_sharps == 1
c_sharp.sharp, c_sharp.natural, c_sharp.accidental, c_sharp.num_sharps
(True, False, True, 1)
Dbb = Note("Dbb")
assert Dbb.double_flat
assert not Dbb.double_sharp
assert Dbb.double_accidental
assert Dbb.num_flats == 2
assert Dbb.num_sharps == 0
Dbb.double_flat, Dbb.double_sharp, Dbb.double_accidental, Dbb.num_flats, Dbb.num_sharps
(True, False, True, 2, 0)

Comparison

Notes can be compared with each other using familiar Python operators. Here are some examples of comparison between Note objects.

assert Note("E#") == Note("F")
assert Note("B#") == Note("C")
assert Note("C##") == Note("D")
assert Note("Fb") == Note("E")
assert Note("Abb") == Note("G")
assert Note("Bbb") == Note("A")
assert Note("Cb") == Note("B")
assert Note("C") == Note("C")
assert Note("E#") == Note("F")
assert Note("A#") != Note("B")
assert Note("F") > Note("C#")
assert Note("B#") <= Note("C")
assert Note("C") < Note("B")
assert Note("E") >= Note("E")
assert Note("C", oct=4) > Note("B", oct=3)
assert Note("C", oct=4) != Note("C", oct=5)
assert Note("C", oct=4) < Note("C", oct=5)

Adding

Semitones

Adding semitones to a note will return a new note with n semitones added above the original note.

For example, adding 1 semitone to A# (A Sharp) will return B.

Adding Note objects together will form a Chord. More on that in the Chord section.


source

Note.__add__

 Note.__add__ (other)

Add n semitones.

for i in range(1, 13):
    print(a_sharp+i)
B
C
C#
D
D#
E
F
F#
G
G#
A
A#

The octave of the note is automatically updated when adding.

assert (a_sharp+1).oct == a_sharp.oct
assert (a_sharp+2).oct == a_sharp.oct + 1
assert (Note("C")+11).oct == Note("C").oct
assert (Note("C")+14).oct > Note("C").oct
assert Note("C")+11 == Note("B")
assert str(a_sharp+1) == "B"
assert str(a_sharp+11) == "A"
assert str(a_sharp+12) == "A#"
assert str(a_sharp+13) == "B"
assert str(a_sharp+47) == "A"
assert str(a_sharp+0) == "A#"

Whole Notes

The % operator is a shortcut for adding whole notes.


source

Note.__mod__

 Note.__mod__ (other)

Add n whole notes.

assert a_sharp % 1 == Note("C", oct=5)
a_sharp % 1
C5
for i in range(1, 7):
    print(a_sharp % i)
C
D
E
F#
G#
A#
assert str(a_sharp % 1) == "C"
assert str(a_sharp % 0) == "A#"
assert str(a_sharp % 6) == "A#"
assert str(a_sharp % 6 + 1) == "B"

We define some shortcuts to do common transpositions.


source

Note.alt_ext

 Note.alt_ext ()

source

Note.ext

 Note.ext ()

source

Note.M13

 Note.M13 ()

source

Note.m13

 Note.m13 ()

source

Note.A11

 Note.A11 ()

source

Note.P11

 Note.P11 ()

source

Note.A9

 Note.A9 ()

source

Note.M9

 Note.M9 ()

source

Note.m9

 Note.m9 ()

source

Note.P8

 Note.P8 ()

source

Note.M7

 Note.M7 ()

source

Note.m7

 Note.m7 ()

source

Note.M6

 Note.M6 ()

source

Note.m6

 Note.m6 ()

source

Note.P5

 Note.P5 ()

source

Note.TT

 Note.TT ()

source

Note.P4

 Note.P4 ()

source

Note.M3

 Note.M3 ()

source

Note.m3

 Note.m3 ()

source

Note.M2

 Note.M2 ()

source

Note.m2

 Note.m2 ()
c = Note("C")
assert c.m2() == Note("C#")
assert c.M2() == Note("D")
assert c.m3() == Note("Eb")
assert c.M3() == Note("E")
assert c.P4() == Note("F")
assert c.TT() == Note("F#")
assert c.P5() == Note("G")
assert c.m6() == Note("Ab")
assert c.M6() == Note("A")
assert c.m7() == Note("Bb")
assert c.M7() == Note("B")
assert c.P8() == Note("C", oct=c.oct+1)
assert c.m9() == Note("Db", oct=c.oct+1)
assert c.M9() == Note("D", oct=c.oct+1)
assert c.A9() == Note("D#", oct=c.oct+1)
assert c.P11() == Note("F", oct=c.oct+1)
assert c.A11() == Note("F#", oct=c.oct+1)
assert c.m13() == Note("G#", oct=c.oct+1)
assert c.M13() == Note("A", oct=c.oct+1)

For quick analysis of upper extensions o a Chord, we add an ext and alt_ext method.

Upper extensions -> b9, 9, #9, 11, #11, b13, #13

assert c.ext() == [c.m9(), c.M9(), c.A9(), c.P11(), c.A11(), c.m13(), c.M13()]
c.ext()
[C#5, D5, D#5, F5, F#5, G#5, A5]

Altered extensions -> b9, #9, #11, b13

assert c.alt_ext() == [c.m9(), c.A9(), c.A11(), c.m13()]
c.alt_ext()
[C#5, D#5, F#5, G#5]

Subtraction

Semitones

Subtracting semitones from a Note returns a new note with n semitones subtracted from the original note.

For example, subtracting 1 semitone from C returns B. Subtracting 1 semitone from A# returns A.


source

Note.__sub__

 Note.__sub__ (other)

Subtract n semitones from a note.

for i in range(1, 13):
    print(a_sharp-i)
A
G#
G
F#
F
E
D#
D
C#
C
B
A#
assert (a_sharp-1).oct == a_sharp.oct
assert (a_sharp-11).oct == a_sharp.oct - 1
assert (a_sharp-6).oct == a_sharp.oct
assert (a_sharp-12).oct == a_sharp.oct - 1
assert str(a_sharp-0) == "A#"
assert str(a_sharp-1) == "A"
assert str(a_sharp-11) == "B"
assert str(a_sharp-12) == "A#"
assert str(a_sharp-13) == "A"
assert str(a_sharp-25) == "A"

Whole Notes

The // operator is a shortcut for subtracting whole notes.


source

Note.__floordiv__

 Note.__floordiv__ (other)

Subtract n whole notes

assert a_sharp // 1 == Note("G#", oct=4)
a_sharp // 1
G#4
for i in range(1, 7):
    print(a_sharp // i)
G#
F#
E
D
C
A#
assert str(a_sharp // 1) == "G#"
assert str(a_sharp // 0) == "A#"
assert str(a_sharp // 6) == "A#"
assert str(a_sharp // 6 + 1) == "B"

We also define shortcuts for downward intervals with d{INTERVAL}.


source

Note.d_alt_ext

 Note.d_alt_ext ()

source

Note.d_ext

 Note.d_ext ()

source

Note.dM13

 Note.dM13 ()

source

Note.dm13

 Note.dm13 ()

source

Note.dA11

 Note.dA11 ()

source

Note.dP11

 Note.dP11 ()

source

Note.dA9

 Note.dA9 ()

source

Note.dM9

 Note.dM9 ()

source

Note.dm9

 Note.dm9 ()

source

Note.dP8

 Note.dP8 ()

source

Note.dM7

 Note.dM7 ()

source

Note.dm7

 Note.dm7 ()

source

Note.dM6

 Note.dM6 ()

source

Note.dm6

 Note.dm6 ()

source

Note.dP5

 Note.dP5 ()

source

Note.dTT

 Note.dTT ()

source

Note.dP4

 Note.dP4 ()

source

Note.dM3

 Note.dM3 ()

source

Note.dm3

 Note.dm3 ()

source

Note.dM2

 Note.dM2 ()

source

Note.dm2

 Note.dm2 ()
b = Note("B")
assert b.dm2() == Note("A#")
assert b.dM2() == Note("A")
assert b.dm3() == Note("G#")
assert b.dM3() == Note("G")
assert b.dP4() == Note("F#")
assert b.dTT() == Note("F")
assert b.dP5() == Note("E")
assert b.dm6() == Note("D#")
assert b.dM6() == Note("D")
assert b.dm7() == Note("C#")
assert b.dM7() == Note("C")
assert b.dP8() == Note("B", oct=b.oct-1)
assert b.dm9() == Note("A#", oct=b.oct-1)
assert b.dM9() == Note("A", oct=b.oct-1)
assert b.dA9() == Note("G#", oct=b.oct-1)
assert b.dP11() == Note("F#", oct=b.oct-1)
assert b.dA11() == Note("F", oct=b.oct-1)
assert b.dm13() == Note("D#", oct=b.oct-1)
assert b.dM13() == Note("D", oct=b.oct-1)

As with the upper extensions, we also add methods to get the descending extensions with d_ext and d_alt_ext.

assert c.d_ext() == [c.dm9(), c.dM9(), c.dA9(), c.dP11(), c.dA11(), c.dm13(), c.dM13()]
c.d_ext()
[B2, A#2, A2, G2, F#2, E2, D#2]
assert c.d_alt_ext() == [c.dm9(), c.dA9(), c.dA11(), c.dm13()]
c.d_alt_ext()
[B2, A2, F#2, E2]

Relative Conversion

Note objects can be converted to its relative major or minor. How this is converted is visualized on the circle of fifths.

Circle of Fifths

For example, the relative minor of C is A. The relative major of C# is E.

Relative Minor

minor converts an arbitrary note to its relative minor. This means 3 semitones are subtracted from the note.


source

Note.minor

 Note.minor ()
c = Note("C")
assert str(c.minor()) == "A"
c.minor()
A3

Relative Major

major converts an arbitrary note to its relative major. This means 3 semitones are added to the note.


source

Note.major

 Note.major ()
assert str(c_sharp.major()) == "E"
c_sharp.major()
E4

Audio

Every Note can be played as audio. A Note is first transformed into a bytestring.


source

Note.get_audio_bytes

 Note.get_audio_bytes (length=1, sr=44100)

source

Note.get_audio_array

 Note.get_audio_array (length=1, sr=44100)
c.get_audio_bytes(length=5)[:50]
b'RIFF\xcc\xba\x06\x00WAVEfmt \x10\x00\x00\x00\x01\x00\x01\x00D\xac\x00\x00\x88X\x01\x00\x02\x00\x10\x00data\xa8\xba\x06\x00\x00\x00\xc5\x04\x88\t'

For convenience, we can also play the audio directly.


source

Note.play

 Note.play (length=1)
c.play()

The length of a specific note can be specified for the audio.

c.play(length=2)
a_sharp.play()

Also here, method chaining is possible.

(a_sharp - 6).play()

Interval

Two Note objects can be combined to form an Interval.


source

Interval

 Interval (note1:__main__.Note, note2:__main__.Note)

Initialize self. See help(type(self)) for accurate signature.

Initialization

Calling interval on a Note object requires providing another Note object and returns an Interval object. A shortcut is to use the & operator and has the same effect.


source

Note.__and__

 Note.__and__ (other:__main__.Note)

source

Note.interval

 Note.interval (other:__main__.Note)

Comparison

The full name of the interval between A# and B is a minor second.

b = Note("B")
m2 = a_sharp.interval(b)
m2
minor second (b2)

From here on in these docs we’ll use the more compact & syntax.

assert m2 == a_sharp & b
a_sharp & b
minor second (b2)
assert m2.semitones == 1
m2.semitones
1

The shorthand for a minor second interval is b2.

assert m2.long == "minor second"
m2.long
'minor second'
assert m2.short == "b2"
m2.short
'b2'

Interval objects work on upper extensions up to 2 octaves.

ninth = Note("G#", oct=5) & Note("A", oct=6)
assert ninth.semitones == 13
assert ninth.long == "minor ninth"
assert ninth.short == "b9"
ninth
minor ninth (b9)

Interval objects can be compared with each other.

assert m2 <= ninth
assert m2 != ninth
assert not m2 > ninth

C# compared with A# is treated as a (negative) minor 3rd.

low_a_sharp = Note("A#", oct=3)
neg_m3 = c_sharp & low_a_sharp
neg_m3
minor third (b3)
neg_m3.semitones
-3
assert neg_m3 < m2
assert neg_m3 != m2
assert (Note("C", oct=5) & Note("D", oct=5)).semitones == 2
assert (Note("C", oct=2) & Note("E", oct=2)).short == "3"
assert (Note("C", oct=5) & Note("C", oct=5)).long == "unison"
assert (Note("C", oct=5) & Note("A", oct=5)).long == "major sixth"
assert (Note("C", oct=5) & Note("B", oct=5)).long == "major seventh"
assert (Note("C", oct=5) & Note("D", oct=5)).long == "major second"
assert (Note("C", oct=5) & Note("C", oct=6)).long == "octave"
assert (Note("C", oct=5) & Note("B", oct=7)).long == "major seventh"
assert (Note("C", oct=5) & Note("F", oct=8)).long == "perfect eleventh"
assert (Note("C", oct=5) & Note("D", oct=6)).notes == [Note("C", oct=5), Note("D", oct=6)]
assert (Note("C", oct=5) & Note("D", oct=5)).notes == [Note("C", oct=5), Note("D", oct=5)]

Interval Type

An Interval can also determine its type. An interval can be:

  • Perfect consonant (Unison, Octave and 5th)
  • Soft consonant (3rds and 6ths),
  • Mild Dissonant (Minor 7th and Major 2nd),
  • Sharp Dissonant (Major 7th and Minor 2nd),
  • Contextual (4th)
  • Neutral (Tritone).

source

Interval.is_contextual

 Interval.is_contextual ()

source

Interval.is_perfect

 Interval.is_perfect ()

source

Interval.is_dissonant

 Interval.is_dissonant ()

source

Interval.is_consonant

 Interval.is_consonant ()

source

Interval.type

 Interval.type ()

A fifth is a Perfect Consonant.

fifth = Note("C") & Note("G")
assert fifth.type() == "Perfect Consonant"
assert fifth.is_consonant() and fifth.is_perfect()
fifth.type()
'Perfect Consonant'

A minor second is Sharp Dissonant.

m2 = Note("C") & Note("C#")
assert m2.type() == "Sharp Dissonant"
assert m2.is_dissonant() and not m2.is_perfect()
m2.type()
'Sharp Dissonant'

A special case is the fourth which is Contextual. This can be soft consonant or dissonant depending on the context of harmonic movement and is not feasible to determine within one interval.

fourth = Note("C") & Note("F")
assert fourth.type() == "Contextual"
assert fourth.is_contextual()
fourth.type()
'Contextual'

The Interval.type method handles upper extensions by comparing it within the same octave. For example a flat 9 (b9) is treated as a minor second and therefore Sharp Dissonant.

ninth = Note("B", oct=4) & Note("C", oct=5)
ninth.type()
'Sharp Dissonant'

Adding and subtracting

You can add to Interval objects to augment or diminish them.


source

Interval.__sub__

 Interval.__sub__ (other)

source

Interval.__add__

 Interval.__add__ (other)

In this example, we augment a minor 9th interval to a major ninth (m9 -> 9).

m9 = Note("C", oct=4) & Note("C#", oct=5)
m9
minor ninth (b9)
assert (m9+1).long == "major ninth"
m9+1
major ninth (9)

If we diminish the minor 9th interval it becomes an octave.

assert (m9 - 1).long == "octave"
m9 - 1
octave (8)