Sliding window analysis

Sliding window analysis#

This example demonstrates how to perform a sliding window analysis on a musical score to compute the mean pitch height (mean key_num) from each window and plot the result.

First, we’ll import the required modules.

import matplotlib.pyplot as plt

from amads.algorithms.slice.window import sliding_window
from amads.io.pt_midi_import import partitura_midi_import
from amads.music import example

Load an example MIDI file and import it using partitura.

midi_file = example.fullpath("midi/twochan.mid")
my_score = partitura_midi_import(midi_file, ptprint=False)
/opt/hostedtoolcache/Python/3.10.17/x64/lib/python3.10/site-packages/partitura/io/importmidi.py:575: UserWarning: pitch spelling
  warnings.warn("pitch spelling")
/opt/hostedtoolcache/Python/3.10.17/x64/lib/python3.10/site-packages/partitura/io/importmidi.py:658: UserWarning: create_part
  part = create_part(
/opt/hostedtoolcache/Python/3.10.17/x64/lib/python3.10/site-packages/partitura/io/importmidi.py:658: UserWarning: add notes
  part = create_part(
/opt/hostedtoolcache/Python/3.10.17/x64/lib/python3.10/site-packages/partitura/io/importmidi.py:658: UserWarning: add time sigs and measures
  part = create_part(
/opt/hostedtoolcache/Python/3.10.17/x64/lib/python3.10/site-packages/partitura/io/importmidi.py:658: UserWarning: tie notes
  part = create_part(
/opt/hostedtoolcache/Python/3.10.17/x64/lib/python3.10/site-packages/partitura/io/importmidi.py:658: UserWarning: find tuplets
  part = create_part(
/opt/hostedtoolcache/Python/3.10.17/x64/lib/python3.10/site-packages/partitura/io/importmidi.py:658: UserWarning: done create_part
  part = create_part(
partitura_convert_part: after pass 1, measures are
[(1, 0, 2400), ('timesig', 4, 4), (2, 2400, 4800), (3, 4800, 7200), (4, 7200, 9600), (5, 9600, 9601)]
div_to_quarter: div 2400 qtrs 4.0
div_to_quarter: div 0 qtrs 0.0
div_to_quarter: div 4800 qtrs 8.0
div_to_quarter: div 2400 qtrs 4.0
div_to_quarter: div 7200 qtrs 12.0
div_to_quarter: div 4800 qtrs 8.0
div_to_quarter: div 9600 qtrs 16.0
div_to_quarter: div 7200 qtrs 12.0
div_to_quarter: div 9601 qtrs 16.0
div_to_quarter: div 9600 qtrs 16.0
ignoring 0-- Clef sign=G line=2 number=1
ignoring 0--2400 Measure number=1 name=1
ignoring 0-- TimeSignature 4/4
div_to_quarter: div 0 qtrs 0.0
Tempo start 0.0 tempo 1.6666666666666667
append_beat_tempo 1.6666666666666667 <amads.core.time_map.MapBeat object at 0x7f9610243730>
div_to_quarter: div 1 qtrs 0.0016666666666666668
div_to_quarter: div 601 qtrs 1.0016666666666667
div_to_quarter: div 1201 qtrs 2.0016666666666665
div_to_quarter: div 1801 qtrs 3.0016666666666665
div_to_quarter: div 2400 qtrs 4.0
ignoring 2400--4800 Measure number=2 name=2
div_to_quarter: div 2401 qtrs 4.001666666666667
div_to_quarter: div 3001 qtrs 5.001666666666667
div_to_quarter: div 3601 qtrs 6.001666666666667
div_to_quarter: div 4201 qtrs 7.001666666666667
div_to_quarter: div 4800 qtrs 8.0
ignoring 4800--7200 Measure number=3 name=3
div_to_quarter: div 4801 qtrs 8.001666666666667
div_to_quarter: div 5401 qtrs 9.001666666666667
div_to_quarter: div 6001 qtrs 10.001666666666667
div_to_quarter: div 6601 qtrs 11.001666666666667
div_to_quarter: div 7200 qtrs 12.0
ignoring 7200--9600 Measure number=4 name=4
div_to_quarter: div 7201 qtrs 12.001666666666667
div_to_quarter: div 7801 qtrs 13.001666666666667
div_to_quarter: div 8401 qtrs 14.001666666666667
div_to_quarter: div 9001 qtrs 15.001666666666667
div_to_quarter: div 9600 qtrs 16.0
ignoring 9600--9601 Measure number=5 name=5
BEGIN retie_note ['Note', 3.0016666666666665, 0.9983333333333333, None, 65, 'n3', True, <partitura.score.Note object at 0x7f9610243340>]
GROUP BEFORE:  [['Note', 3.0016666666666665, 0.9983333333333333, None, 65, 'n3', True, <partitura.score.Note object at 0x7f9610243340>], ['Note', 4.0, 0.0016666666666666668, None, 65, 'n3a', True, <partitura.score.Note object at 0x7f9610243160>]]
BEGIN retie_note ['Note', 4.0, 0, None, 65, 'n3a', True, <partitura.score.Note object at 0x7f9610243160>]
BEGIN retie_note ['Note', 7.001666666666667, 0.9983333333333333, None, 71, 'n7', True, <partitura.score.Note object at 0x7f961bdf46a0>]
GROUP BEFORE:  [['Note', 7.001666666666667, 0.9983333333333333, None, 71, 'n7', True, <partitura.score.Note object at 0x7f961bdf46a0>], ['Note', 8.0, 0.0016666666666666668, None, 71, 'n7a', True, <partitura.score.Note object at 0x7f9610243d00>]]
BEGIN retie_note ['Note', 8.0, 0, None, 71, 'n7a', True, <partitura.score.Note object at 0x7f9610243d00>]
BEGIN retie_note ['Note', 11.001666666666667, 0.9983333333333333, None, 71, 'n11', True, <partitura.score.Note object at 0x7f961bdf7bb0>]
GROUP BEFORE:  [['Note', 11.001666666666667, 0.9983333333333333, None, 71, 'n11', True, <partitura.score.Note object at 0x7f961bdf7bb0>], ['Note', 12.0, 0.0016666666666666668, None, 71, 'n11a', True, <partitura.score.Note object at 0x7f961bdf63e0>]]
BEGIN retie_note ['Note', 12.0, 0, None, 71, 'n11a', True, <partitura.score.Note object at 0x7f961bdf63e0>]
BEGIN retie_note ['Note', 15.001666666666667, 0.9983333333333333, None, 67, 'n15', True, <partitura.score.Note object at 0x7f961bdf7190>]
GROUP BEFORE:  [['Note', 15.001666666666667, 0.9983333333333333, None, 67, 'n15', True, <partitura.score.Note object at 0x7f961bdf7190>], ['Note', 16.0, 0.0016666666666666668, None, 67, 'n15a', True, <partitura.score.Note object at 0x7f961bdf5600>]]
BEGIN retie_note ['Note', 16.0, 0, None, 67, 'n15a', True, <partitura.score.Note object at 0x7f961bdf5600>]
Something is wrong; could not find measure for ['Note', 16.0, 0, None, 67, 'n15a', True, <partitura.score.Note object at 0x7f961bdf5600>]
partitura_convert_part: after pass 1, measures are
[(1, 0, 2400), ('timesig', 4, 4), (2, 2400, 4800), (3, 4800, 7200), (4, 7200, 9600), (5, 9600, 9601)]
div_to_quarter: div 2400 qtrs 4.0
div_to_quarter: div 0 qtrs 0.0
div_to_quarter: div 4800 qtrs 8.0
div_to_quarter: div 2400 qtrs 4.0
div_to_quarter: div 7200 qtrs 12.0
div_to_quarter: div 4800 qtrs 8.0
div_to_quarter: div 9600 qtrs 16.0
div_to_quarter: div 7200 qtrs 12.0
div_to_quarter: div 9601 qtrs 16.0
div_to_quarter: div 9600 qtrs 16.0
ignoring 0-- Clef sign=F line=4 number=1
ignoring 0--2400 Measure number=1 name=1
ignoring 0-- TimeSignature 4/4
div_to_quarter: div 1 qtrs 0.0016666666666666668
div_to_quarter: div 1201 qtrs 2.0016666666666665
div_to_quarter: div 2400 qtrs 4.0
ignoring 2400--4800 Measure number=2 name=2
div_to_quarter: div 2401 qtrs 4.001666666666667
div_to_quarter: div 3601 qtrs 6.001666666666667
div_to_quarter: div 4800 qtrs 8.0
ignoring 4800--7200 Measure number=3 name=3
div_to_quarter: div 4801 qtrs 8.001666666666667
div_to_quarter: div 6001 qtrs 10.001666666666667
div_to_quarter: div 7200 qtrs 12.0
ignoring 7200--9600 Measure number=4 name=4
div_to_quarter: div 7201 qtrs 12.001666666666667
div_to_quarter: div 8401 qtrs 14.001666666666667
div_to_quarter: div 9600 qtrs 16.0
ignoring 9600--9601 Measure number=5 name=5
BEGIN retie_note ['Note', 2.0016666666666665, 1.9983333333333333, None, 43, 'n17', True, <partitura.score.Note object at 0x7f961bdf4370>]
GROUP BEFORE:  [['Note', 2.0016666666666665, 1.9983333333333333, None, 43, 'n17', True, <partitura.score.Note object at 0x7f961bdf4370>], ['Note', 4.0, 0.0016666666666666668, None, 43, 'n17a', True, <partitura.score.Note object at 0x7f961bdf5ae0>]]
BEGIN retie_note ['Note', 4.0, 0, None, 43, 'n17a', True, <partitura.score.Note object at 0x7f961bdf5ae0>]
BEGIN retie_note ['Note', 6.001666666666667, 1.9983333333333333, None, 43, 'n19', True, <partitura.score.Note object at 0x7f961bdf4e50>]
GROUP BEFORE:  [['Note', 6.001666666666667, 1.9983333333333333, None, 43, 'n19', True, <partitura.score.Note object at 0x7f961bdf4e50>], ['Note', 8.0, 0.0016666666666666668, None, 43, 'n19a', True, <partitura.score.Note object at 0x7f961bdf53f0>]]
BEGIN retie_note ['Note', 8.0, 0, None, 43, 'n19a', True, <partitura.score.Note object at 0x7f961bdf53f0>]
BEGIN retie_note ['Note', 10.001666666666667, 1.9983333333333333, None, 38, 'n21', True, <partitura.score.Note object at 0x7f961bdf5300>]
GROUP BEFORE:  [['Note', 10.001666666666667, 1.9983333333333333, None, 38, 'n21', True, <partitura.score.Note object at 0x7f961bdf5300>], ['Note', 12.0, 0.0016666666666666668, None, 38, 'n21a', True, <partitura.score.Note object at 0x7f961bdf7370>]]
BEGIN retie_note ['Note', 12.0, 0, None, 38, 'n21a', True, <partitura.score.Note object at 0x7f961bdf7370>]
BEGIN retie_note ['Note', 14.001666666666667, 1.9983333333333333, None, 47, 'n23', True, <partitura.score.Note object at 0x7f961bdf56c0>]
GROUP BEFORE:  [['Note', 14.001666666666667, 1.9983333333333333, None, 47, 'n23', True, <partitura.score.Note object at 0x7f961bdf56c0>], ['Note', 16.0, 0.0016666666666666668, None, 47, 'n23a', True, <partitura.score.Note object at 0x7f961bdf4790>]]
BEGIN retie_note ['Note', 16.0, 0, None, 47, 'n23a', True, <partitura.score.Note object at 0x7f961bdf4790>]
Something is wrong; could not find measure for ['Note', 16.0, 0, None, 47, 'n23a', True, <partitura.score.Note object at 0x7f961bdf4790>]

Set up parameters for the sliding window analysis. We’ll use a window size of 4.0 (one bar) and a small step size of 0.01 for smooth results.

size = 4.0  # one bar
step = 0.01

Flatten the score to get a single sequence of notes.

flattened_score = my_score.flatten(collapse=True)

Perform the sliding window analysis. Each window will contain notes that are sounding within its time boundaries.

windows = sliding_window(
    flattened_score,
    size=size,
    step=step,
    align="center",
)

Compute the duration-weighted mean pitch height for each window. For empty windows, we’ll use None as the default value.

times = []
mean_pitch_heights = []
for window in windows:
    times.append(window.time)

    if len(window.content) == 0:
        mean_pitch_heights.append(None)
        continue

    total_duration = sum(note.duration for note in window.content)
    weighted_sum = sum(note.key_num * note.duration for note in window.content)
    mean_pitch_heights.append(weighted_sum / total_duration)

Finally, plot the results to visualize how mean pitch height changes over time.

plt.figure(figsize=(10, 6))
plt.plot(times, mean_pitch_heights)
plt.xlabel("Time")
plt.ylabel("Mean pitch height")
plt.show()
plot sliding window

Total running time of the script: (0 minutes 1.287 seconds)

Gallery generated by Sphinx-Gallery