Millón de Monos

Weblog de Manuel Aristarán

Data-driven stylistic analysis of Charlie Parker solos

A quick exploration of the music21 toolkit for computational musicology using Charlie Parker’s solos, as transcribed in this MusicXML version of the venerable Omnibook.

No big findings here, just fooling around with the awesome music21 library. If you are interested in serious computational and statistical analysis of jazz solos, head over to the Jazzomat Project

import glob
import music21
import pandas as pd
import functools
import altair as alt

Distribution of notes (expressed as intervals from the chord root) by chord quality

What intervals from the chord root does Bird usually play, depending on the chord’s quality?

@functools.lru_cache(maxsize=4096)
def isChordTone(chord, tone_name):
    """ True if note """
    return tone_name in [p.name for p in chord.pitches]

notes = []
for fname in glob.glob('Omnibook/MusicXml/*.xml'):
    piece = music21.converter.parse(fname)
    for m in [m for m in piece.parts[0] if type(m) == music21.stream.Measure]:
        currentChord = None
        currentChordOffset = None
        for thing in m.notesAndRests:
            if type(thing) == music21.harmony.ChordSymbol:
                currentChord = thing
                currentChordOffset = thing.offset
            elif type(thing) == music21.note.Note:
                if currentChord is None:
                    continue
                interval = music21.interval.Interval(thing.pitch, currentChord.root())
                notes.append({
                    'score': fname,
                    'measure': m.measureNumber,
                    'offset': thing.offset,
                    'chord_kind': currentChord.chordKind,
                    'figure': currentChord.figure,
                    'note': thing.name,
                    'interval': interval.simpleName,
                    'interval_semitones': interval.chromatic.mod12,
                    'is_chord_tone': isChordTone(currentChord, thing.name)
                })

notes = pd.DataFrame(notes)

data = notes.groupby(['chord_kind', 'interval']).count().reset_index()
alt.Chart(data[data.chord_kind.isin(['dominant', 'major', 'minor'])]).mark_bar().encode(
    x='interval:N',
    y='figure:Q',
    row='chord_kind'
).configure(background='#fafafa') \
.properties(title="Frequency of notes as intervals from the root by chord quality")

png