Extracting Harmonies from Leadsheets#
Say you have one or a lot of leadsheets in musicXML format such as Effendi’s Realbook collection
[1]:
from music21 import chord, key, stream, converter, harmony
from pprint import pprint
import json
[2]:
f = chord.Chord('F4 C5 A5')
kf = key.Key('F')
sf = stream.Measure([kf, f])
sf.show()
Loading a Song#
Assuming you have already downloaded Effendi’s Whole collection to your local machine you can use music21 to open them and get a lot of information out.
[ ]:
bm = converter.parse('/mnt/Datos/RealBooks/effendi.me/jazz/repo/II/Black Ice.xml')
bm.show()
[ ]:
bm.analyze('key')
<music21.key.Key of d minor>
[ ]:
bm.metadata.all()
[('composer', 'Jeff Lorber\nArr. 6EQUJ5'),
('copyright', '©2016 Finmusic/effendi.me 5/21/2016'),
('movementName', 'Black Ice')]
[ ]:
bm.metadata.title
'Black Ice'
[ ]:
bm.metadata.composer
'Jeff Lorber\nArr. 6EQUJ5'
[ ]:
bm.getTimeSignatures().show()
<<<<<<< local <modified: >
/home/fccoelho/Documentos/Projects_software/musicir/.venv/lib/python3.10/site-packages/music21/musicxml/m21ToXml.py:477: MusicXMLWarning: <music21.stream.Score 0x7f629d7409a0> is not well-formed; see isWellFormedNotation()
warnings.warn(f'{scOut} is not well-formed; see isWellFormedNotation()',
=======
/home/fccoelho/Documentos/Software_projects/musicir/venv/lib/python3.10/site-packages/music21/musicxml/m21ToXml.py:477: MusicXMLWarning: <music21.stream.Score 0x7ff0b0746ef0> is not well-formed; see isWellFormedNotation()
warnings.warn(f'{scOut} is not well-formed; see isWellFormedNotation()',
>>>>>>> remote <modified: >
[ ]:
m = bm.measure(1)
[ ]:
m.show()
However, if you want to extract the harmony, i.e. the sequence of chords, measure by measure. Music21 does not help much. That’s when you will resort to MusicIR SongParser.
[ ]:
from musicir.leadsheets.musicxml import ChordParser, SongParser, import_into_db
[ ]:
SP = SongParser("/mnt/Datos/RealBooks/effendi.me/jazz/repo/II/Black Ice.xml")
SP.number_of_measures
62
[ ]:
c= SP.get_measure_chords(1)[0]
[ ]:
Ch = ChordParser(c)
Ch
B -
[15]:
pprint(json.loads(SP.chords_as_json()))
[{'chords': [], 'measure': 0},
{'chords': ['B - '], 'measure': 1},
{'chords': ['E - ', 'F - '], 'measure': 2},
{'chords': ['E - ', 'A - '], 'measure': 3},
{'chords': ['E - ', 'F - '], 'measure': 4},
{'chords': ['E - ', 'A - '], 'measure': 5},
{'chords': ['A - '], 'measure': 6},
{'chords': ['E - ', 'F - '], 'measure': 7},
{'chords': ['E - ', 'A - '], 'measure': 8},
{'chords': ['E - ', 'F - '], 'measure': 9},
{'chords': ['E - ', 'A - '], 'measure': 10},
{'chords': ['E - ', 'F - '], 'measure': 11},
{'chords': ['E - ', 'A - '], 'measure': 12},
{'chords': ['E - ', 'F - '], 'measure': 13},
{'chords': ['E - ', 'A - '], 'measure': 14},
{'chords': ['E - ', 'D - ', 'G - '], 'measure': 15},
{'chords': ['C - ', 'B - '], 'measure': 16},
{'chords': ['D - ', 'G - ', 'C - ', 'E♭ - '], 'measure': 17},
{'chords': ['B♭ - ', 'A♭ - ', 'G - '], 'measure': 18},
{'chords': ['F♯ - '], 'measure': 19},
{'chords': ['C♯ - '], 'measure': 20},
{'chords': ['F♯ - '], 'measure': 21},
{'chords': ['C♯ - '], 'measure': 22},
{'chords': ['F♯ - '], 'measure': 23},
{'chords': ['C♯ - '], 'measure': 24},
{'chords': ['F♯ - '], 'measure': 25},
{'chords': ['G - ', 'B - '], 'measure': 26},
{'chords': ['E - ', 'F - '], 'measure': 27},
{'chords': ['E - ', 'A - '], 'measure': 28},
{'chords': ['E - ', 'F - '], 'measure': 29},
{'chords': ['E - ', 'A - '], 'measure': 30},
{'chords': ['E - ', 'F - '], 'measure': 31},
{'chords': ['E - ', 'A - '], 'measure': 32},
{'chords': ['E - ', 'F - '], 'measure': 33},
{'chords': ['E - ', 'A - '], 'measure': 34},
{'chords': ['E - ', 'F - '], 'measure': 35},
{'chords': ['E - ', 'A - '], 'measure': 36},
{'chords': ['E - ', 'A - '], 'measure': 37},
{'chords': ['E - ', 'D - ', 'G - '], 'measure': 38},
{'chords': ['C - ', 'B - '], 'measure': 39},
{'chords': ['D - ', 'G - ', 'C - ', 'E♭ - '], 'measure': 40},
{'chords': ['B♭ - ', 'A♭ - ', 'G - '], 'measure': 41},
{'chords': ['F♯ - '], 'measure': 42},
{'chords': ['C♯ - '], 'measure': 43},
{'chords': [], 'measure': 44},
{'chords': [], 'measure': 45},
{'chords': [], 'measure': 46},
{'chords': [], 'measure': 47},
{'chords': [], 'measure': 48},
{'chords': [], 'measure': 49},
{'chords': [], 'measure': 50},
{'chords': [], 'measure': 51},
{'chords': [], 'measure': 52},
{'chords': [], 'measure': 53},
{'chords': [], 'measure': 54},
{'chords': [], 'measure': 55},
{'chords': ['F♯ - '], 'measure': 56},
{'chords': ['B - '], 'measure': 57},
{'chords': ['F♯ - '], 'measure': 58},
{'chords': ['C♯ - '], 'measure': 59},
{'chords': ['F♯ - '], 'measure': 60},
{'chords': ['G - ', 'B - '], 'measure': 61}]
[24]:
for m in range(SP.number_of_measures):
h = SP.get_measure_chords(m)
if h:
print(f"Measure {m}: ", end='')
for i, c in enumerate(h):
if i < len(h) - 1:
print(ChordParser(c), end=', ')
else:
print(f" {ChordParser(c)}")
Measure 1: B -
Measure 2: E - , F -
Measure 3: E - , A -
Measure 4: E - , F -
Measure 5: E - , A -
Measure 6: A -
Measure 7: E - , F -
Measure 8: E - , A -
Measure 9: E - , F -
Measure 10: E - , A -
Measure 11: E - , F -
Measure 12: E - , A -
Measure 13: E - , F -
Measure 14: E - , A -
Measure 15: E - , D - , G -
Measure 16: C - , B -
Measure 17: D - , G - , C - , E♭ -
Measure 18: B♭ - , A♭ - , G -
Measure 19: F♯ -
Measure 20: C♯ -
Measure 21: F♯ -
Measure 22: C♯ -
Measure 23: F♯ -
Measure 24: C♯ -
Measure 25: F♯ -
Measure 26: G - , B -
Measure 27: E - , F -
Measure 28: E - , A -
Measure 29: E - , F -
Measure 30: E - , A -
Measure 31: E - , F -
Measure 32: E - , A -
Measure 33: E - , F -
Measure 34: E - , A -
Measure 35: E - , F -
Measure 36: E - , A -
Measure 37: E - , A -
Measure 38: E - , D - , G -
Measure 39: C - , B -
Measure 40: D - , G - , C - , E♭ -
Measure 41: B♭ - , A♭ - , G -
Measure 42: F♯ -
Measure 43: C♯ -
Measure 56: F♯ -
Measure 57: B -
Measure 58: F♯ -
Measure 59: C♯ -
Measure 60: F♯ -
Measure 61: G - , B -
[25]:
SP.get_measure_melody(2)[0]
<Element 'note' at 0x7f629aff0450>
[21]:
SP.melody_as_json()
[21]:
'[{"measure": 0, "notes": ["E"]}, {"measure": 1, "notes": ["G", "A", "A\\u266f", "B", "D", "E", "G", "D", "D"]}, {"measure": 2, "notes": ["Rest whole"]}, {"measure": 3, "notes": ["Rest whole"]}, {"measure": 4, "notes": ["Rest whole"]}, {"measure": 5, "notes": ["Rest whole"]}, {"measure": 6, "notes": ["Rest quarter", "Rest quarter", "Rest quarter", "E", "D", "B\\u266d", "A"]}, {"measure": 7, "notes": ["A", "B\\u266d", "A", "A"]}, {"measure": 8, "notes": ["Rest quarter", "Rest eighth", "B", "D", "D\\u266f", "F", "D", "E", "F", "G", "A"]}, {"measure": 9, "notes": ["B", "G", "F", "E", "A", "G", "Rest eighth", "Rest half"]}, {"measure": 10, "notes": ["Rest half", "Rest quarter", "G", "E", "B\\u266d", "A"]}, {"measure": 11, "notes": ["A", "B\\u266d", "A", "A"]}, {"measure": 12, "notes": ["Rest half", "D", "B\\u266d", "A", "G", "A", "G", "D", "E"]}, {"measure": 13, "notes": ["E", "G", "Rest quarter"]}, {"measure": 14, "notes": ["Rest half", "Rest quarter", "E", "D", "B\\u266d", "A"]}, {"measure": 15, "notes": ["Rest half", "Rest quarter", "B", "D", "B"]}, {"measure": 16, "notes": ["G", "A", "Rest 16th", "E", "G", "G", "G", "A", "C", "D"]}, {"measure": 17, "notes": ["D", "E\\u266d", "D", "C", "D", "Rest 16th", "A", "C", "C", "B\\u266d", "G", "D"]}, {"measure": 18, "notes": ["F", "B\\u266d", "Rest 16th", "C", "F", "F", "E\\u266d"]}, {"measure": 19, "notes": ["Rest whole"]}, {"measure": 20, "notes": ["Rest whole"]}, {"measure": 21, "notes": ["Rest whole"]}, {"measure": 22, "notes": ["Rest whole"]}, {"measure": 23, "notes": ["Rest whole"]}, {"measure": 24, "notes": ["Rest whole"]}, {"measure": 25, "notes": ["Rest half", "Rest eighth", "E"]}, {"measure": 26, "notes": ["G\\u266f", "A", "A\\u266f", "B", "D", "E", "G", "D", "D"]}, {"measure": 27, "notes": ["Rest whole"]}, {"measure": 28, "notes": ["Rest whole"]}, {"measure": 29, "notes": ["Rest whole"]}, {"measure": 30, "notes": ["Rest whole"]}, {"measure": 31, "notes": ["Rest whole"]}, {"measure": 32, "notes": ["Rest whole"]}, {"measure": 33, "notes": ["Rest whole"]}, {"measure": 34, "notes": ["Rest whole"]}, {"measure": 35, "notes": ["Rest whole"]}, {"measure": 36, "notes": ["Rest whole"]}, {"measure": 37, "notes": ["Rest whole"]}, {"measure": 38, "notes": ["Rest whole"]}, {"measure": 39, "notes": ["Rest whole"]}, {"measure": 40, "notes": ["Rest whole"]}, {"measure": 41, "notes": ["Rest whole"]}, {"measure": 42, "notes": ["Rest whole"]}, {"measure": 43, "notes": ["Rest whole"]}, {"measure": 44, "notes": ["Rest whole"]}, {"measure": 45, "notes": ["Rest whole"]}, {"measure": 46, "notes": ["Rest whole"]}, {"measure": 47, "notes": ["Rest whole"]}, {"measure": 48, "notes": ["Rest whole"]}, {"measure": 49, "notes": ["Rest whole"]}, {"measure": 50, "notes": ["Rest whole"]}, {"measure": 51, "notes": ["Rest whole"]}, {"measure": 52, "notes": ["Rest whole"]}, {"measure": 53, "notes": ["Rest whole"]}, {"measure": 54, "notes": ["Rest whole"]}, {"measure": 55, "notes": ["Rest whole"]}, {"measure": 56, "notes": ["Rest half", "Rest eighth", "E"]}, {"measure": 57, "notes": ["G\\u266f", "A", "A\\u266f", "B", "D", "E", "G", "D", "D"]}, {"measure": 58, "notes": ["Rest whole"]}, {"measure": 59, "notes": ["Rest whole"]}, {"measure": 60, "notes": ["Rest half", "Rest eighth", "E"]}, {"measure": 61, "notes": ["G\\u266f", "A", "A\\u266f", "B", "D", "E", "G", "D", "D"]}]'
[ ]:
Suppose you want to store the analyzed songs into a database for further analysys
[32]:
from glob import glob
[33]:
songs = glob('/home/fccoelho/Downloads/effendi.me/jazz/repo/*.xml', recursive=True)
[22]:
import_into_db('/home/fccoelho/Downloads/effendi.me/jazz/repo/')
[ ]: