NoteList

Create your first NoteList

NoteList is an abstract class, so you can’t instantiate it. We create a Mock class that extends NoteList in order to instantiate it.

from babs import Note, NoteList

class Mock(NoteList):
    pass

m = Mock(Note(name='C'), Note(name='D'), Note(name='E'))

Now you have create the C chord and you can access to the notes attribute.

print(len(m.notes)
for n in m.notes:
    print(n)

# 3
# C4
# D4
# E4

strict

As we said a NoteList consists of one or more note. So if we create an empty NoteList we’ll get a NoteListException.

from babs import Note, NoteList

class Mock(NoteList):
    pass

m = Mock()

# NoteListException "Invalid notes given."

If you need to create an “invalid” NoteList you can use strict parameter.

m = Mock(strict=False)
print(len(m.notes))  # 0

If you set strict to False you can also pass an invalid Note to NoteList.

m = Mock('invalid', 'notes', strict=False)
print(len(m.notes))  # 3

notes attribute

You can only get the notes attribute but you can’t set it!

m = Mock(strict=False)

m.notes = [Note(name='C'), Note(name='E'), Note(name='G'), Note(name='C', octave=5)]

# AttributeError: can't set attribute

add note

You can use this method if you need to add a Note to the current list.

m = Mock(Note(name='C'), Note(name='D'), Note(name='C'))

c.add_note(note=Note(name='E'))

print(len(c.notes))  # 4

for n in c.notes:
    print(n)

# C4
# D4
# C4
# E4

By default strict is set to True, so if you try to add an invalid Note you will get a NoteListException

m.add_note('invalid')  # Add a string instead of a Note

# NoteListException: Instance of Note expected, str given.

m = Mock(Note(name='C'), Note(name='D'))
m.add_note('invalid', strict=False)

print(len(m.notes))  # 3

remove note

If you need to remove a Note you can use the remove_note() method. You can remove a note by Note(), name, frequency or octave.

m = Mock(Note(name='C'), Note(name='E'), Note(name='G'))
c.remove_note(note=Note(name='G'))

print(len(m.notes))  # 2

for n in m.notes:
    print(n)

# C4
# E4

By default, as before, strict is set to True, so if the list will be invalid after remove you will have a NoteListException. If a NoteListException is raised the notes in the list will be restored as they were before the remove.

m = Mock(Note(name='C'))

m.remove_note(note=Note(name='C'))
# Invalid Mock.

print(len(c.notes))  # 1

Removing a Note by octave or name can remove multiple notes.

m = Mock(Note(name='C'), Note(name='E'), Note(name='G'), Note(name='C', octave=5))

print(len(m.notes))  # 4

m.remove_note(name='C')
print(len(m.notes))  # 2

for n in m.notes:
    print(n)

# E4
# G4

m = Mock(Note(name='C'), Note(name='E'), Note(name='G'), Note(name='C', octave=5))
m.remove_note(octave=4)

print(len(m.notes))  # 1

for n in m.notes:
    print(n)

# C5

is valid

If you need to know if the actual list is valid you can use is_valid method. A NoteList is valid if has one or more notes and if all notes are valid Note object (instance of Note)

Comparison

NoteList support equal and not equal comparison operator. Two NoteList are the same if they have the same notes (check note comparison for more details). The strict attribute in create doesn’t affect the list comparison.

Mock(Note(name='A'), Note(name='C')) == Mock(Note(name='A'), Note(name='C'))  # True
Mock(Note(name='A'), Note(name='C'), strict=True) == Mock(Note(name='A'), Note(name='C'), strict=False)  # True
Mock(Note(name='A'), Note(name='C')) == Mock(Note(name='A'), Note(name='C'), Note(name='E'))  # False
Mock(Note(name='A'), Note(name='C')) != Mock(Note(name='A'), Note(name='C'))  # False

get_notes_from_root

You can easily get a list of notes from a root chord using the get_notes_from_root classmethod. Suppose you want to get a basic list of note C - E - G.

notes = Mock.get_notes_from_root(root=Note(name='C'), note_list_type=[4, 7])
for n in c.notes:
    print(n)

# C4
# E4
# G4

That’s it, you’ve got a C - E - G list. So how it works? get_notes_from_root use a note_list_type, which is a list of notes distance from root note. So the [4, 7]. 4 is the distance between root from 3d and between the root and the 5th. The distance is based on the Note.NOTES list: NOTES = [‘C’, ‘C#/Db’, ‘D’, ‘D#/Eb’, ‘E’, ‘F’, ‘F#/Gb’, ‘G’, ‘G#/Ab’, ‘A’, ‘A#/Bb’, ‘B’] So let’s say we want a major seven chord.

notes = Mock.get_notes_from_root(root=Note(name='C'), note_list_type=[4, 7, 11])
for n in notes:
    print(n)

# C4
# E4
# G4
# B4

This works because the index the E is distant 4 elements from the root (C) in the Note.NOTES list, the G is distant 7 elements and the B is distant 11 elements By default the octave of other notes will be the same as the root note. To change that behaviour you can use the octave param. The root note will not be affected by the octave param. octave could be :

  • an integer, so that every note will have that specific octave.
notes = Mock.get_notes_from_root(root=Note(name='C'), note_list_type=[2, 4], octave=3)

for n in notes:
    print(n)

# C4
# D3
# E3
  • a string ‘root’ NoteList.OCTAVE_TYPE_ROOT or ‘from_root’ NoteList.OCTAVE_TYPE_FROM_ROOT:
    • ‘root’, the default behaviour, use the root.octave.
    • ‘from_root’ use the root.octave as the starting octave. So if you have a G-B-D, the starting octave is 4 so we have a G4, then we have a B4 and at least the D Note goes to the next octave so it is a D5.
notes = NoteList.get_notes_from_root(root=Note(name='G'), note_list_type=[4, 7], octave=NoteList.OCTAVE_TYPE_FROM_ROOT)

for n in notes:
    print(n)

# G4
# B4
# D5
  • a callable, that must return an integer. In the callable you have access to root_octave, i (the idx of list distance iteration, starting from 0) and the distance between the current note and the root note. So suppose you want to have a list that looks like G4, B5, D6.
notes = NoteList.get_notes_from_root(
        root=Note(name='G'),
        note_list_type=[4, 7], octave=lambda root_octave, i, distance: root_octave + i + 1
    )

for n in notes:
    print(n)

# G4
# B5
# D6

If the octave is invalid, for example a string different from ‘root’ and ‘from_root’ or a float number, it will be set to 4.

You can also specify the alteration of the notes using the alt param (by default is ‘sharp’ Note.SHARP) that works in the same way as for single note.

notes = NoteList.get_notes_from_root(root=Note(name='G'), note_list_type=[4, 7, 11], alt=Note.SHARP)

for n in notes:
    print(n)

# G4
# B4
# D4
# F#4

notes = NoteList.get_notes_from_root(root=Note(name='G'), note_list_type=[4, 7, 11], alt=Note.FLAT)

for n in notes:
    print(n)

# G4
# D4
# B4
# Gb4