Peak Picking

Contents

Peak Picking#

import IPython.display as ipd
import matplotlib.pyplot as plt
import librosa.display
import numpy

from mirdotcom import mirdotcom

mirdotcom.init()

Peak picking is the act of locating peaks in a signal. For example, in onset detection, we may want to find peaks in a novelty function. These peaks would correspond to the musical onsets.

Let’s load an example audio file.

filename = mirdotcom.get_audio("58bpm.wav")
x, sr = librosa.load(filename)
print(x.shape, sr)
(182464,) 22050

Listen to the audio file:

ipd.Audio(x, rate=sr)

Plot the signal:

plt.figure(figsize=(14, 5))
librosa.display.waveshow(x, sr=sr)
plt.ylabel("Amplitude")
Text(117.47222222222221, 0.5, 'Amplitude')
../../_images/c9f185a96d3d5b386357e7cec35bdd865baa65d41538f92003a4459bf762eb08.png

Compute an onset envelope:

hop_length = 256
onset_envelope = librosa.onset.onset_strength(y=x, sr=sr, hop_length=hop_length)
onset_envelope.shape
(713,)

Generate a time variable:

N = len(x)
T = N / float(sr)
t = numpy.linspace(0, T, len(onset_envelope))

Plot the onset envelope:

plt.figure(figsize=(14, 5))
plt.plot(t, onset_envelope)
plt.xlabel("Time (sec)")
plt.ylabel("Onset envelope")
plt.xlim(xmin=0)
plt.ylim(0)
(0.0, 31.181869506835938)
../../_images/d981a17f7ca15fde3741f6c2d149c57045e578c3569b4b7de002b9ac46324d72.png

In this onset strength envelope, we clearly see many peaks. Some correspond to onsets, and others don’t. How do we create peak picker that will detect true peaks while avoiding unwanted spurious peaks?

librosa.util has a peak_pick method. We can control the parameters based upon our signal. Let’s see how it works:

def peak_pick(x, pre_max, post_max, pre_avg, post_avg, delta, wait):
    '''Uses a flexible heuristic to pick peaks in a signal.

    A sample n is selected as a peak if the corresponding x[n]
    fulfills the following three conditions:

    1. `x[n] == max(x[n - pre_max:n + post_max])`
    2. `x[n] >= mean(x[n - pre_avg:n + post_avg]) + delta`
    3. `n - previous_n > wait`

    where `previous_n` is the last sample picked as a peak (greedily).

This implementation is based on Boeck, Sebastian, Florian Krebs, and Markus Schedl. “Evaluating the Online Capabilities of Onset Detection Methods.” ISMIR. 2012.

Get the frame indices of the peaks:

onset_frames = librosa.util.peak_pick(
    onset_envelope, pre_max=7, post_max=7, pre_avg=7, post_avg=7, delta=0.5, wait=5
)
onset_frames
array([  5,  78,  91, 136, 168, 180, 225, 255, 268, 314, 347, 358, 403,
       433, 447, 492, 522, 537, 581, 611, 625, 659, 670, 703])

Plot the onset envelope along with the detected peaks:

plt.figure(figsize=(14, 5))
plt.plot(t, onset_envelope)
plt.grid(False)
plt.vlines(t[onset_frames], 0, onset_envelope.max(), color="r", alpha=0.7)
plt.xlabel("Time (sec)")
plt.ylabel("Onset envelope")
plt.xlim(0, T)
plt.ylim(0)
(0.0, 31.181869506835938)
../../_images/71faa9879a14ef8862c4a4b6faba43b4fc06d6784cde1b746e07d222c484492e.png

Superimpose a click track upon the original:

clicks = librosa.clicks(frames=onset_frames, sr=22050, hop_length=hop_length, length=N)
ipd.Audio(x + clicks, rate=sr)

Using the parameters above, we find that the peak picking algorithm seems to have high precision, e.g. few false positives. However, recall can be improved, i.e. it is missing several onsets that actually occur in the audio signal.

Questions#

Adjust the hop length from 512 to 256 or 1024. How does that affect the onset envelope, and consequently, the peak picking?

Adjust the peak_pick parameters, pre_max, post_max, pre_avg, post_avg, delta, and wait. How do the detected peaks change?

Try this notebook again on other audio files:

mirdotcom.list_audio()
simple_piano.wav
latin_groove.mp3
clarinet_c6.wav
cowbell.wav
classic_rock_beat.mp3
oboe_c6.wav
sir_duke_trumpet_fast.mp3
sir_duke_trumpet_slow.mp3
jangle_pop.mp3
125_bounce.wav
brahms_hungarian_dance_5.mp3
58bpm.wav
conga_groove.wav
funk_groove.mp3
tone_440.wav
sir_duke_piano_fast.mp3
thx_original.mp3
simple_loop.wav
classic_rock_beat.wav
c_strum.wav
prelude_cmaj.wav
sir_duke_piano_slow.mp3
busta_rhymes_hits_for_days.mp3