# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# + slideshow={"slide_type": "skip"}
# %matplotlib inline
import numpy, scipy, matplotlib.pyplot as plt, IPython.display as ipd
import librosa, librosa.display
import stanford_mir; stanford_mir.init()

# + [markdown] slideshow={"slide_type": "skip"}
# [&larr; Back to Index](index.html)

# + [markdown] slideshow={"slide_type": "slide"}
# # Autocorrelation

# + [markdown] slideshow={"slide_type": "notes"}
# The [autocorrelation](http://en.wikipedia.org/wiki/Autocorrelation) of a signal describes the similarity of a signal against a time-shifted version of itself. For a signal $x$, the autocorrelation $r$ is:
#
# $$ r(k) = \sum_n x(n) x(n-k) $$
# -

# In this equation, $k$ is often called the **lag** parameter. $r(k)$ is maximized at $k = 0$ and is symmetric about $k$.

# The autocorrelation is useful for finding repeated patterns in a signal. For example, at short lags, the autocorrelation can tell us something about the signal's fundamental frequency. For longer lags, the autocorrelation may tell us something about the tempo of a musical signal.

# + [markdown] slideshow={"slide_type": "skip"}
# Let's load a file:

# + slideshow={"slide_type": "subslide"}
x, sr = librosa.load('audio/c_strum.wav')
ipd.Audio(x, rate=sr)

# + slideshow={"slide_type": "subslide"}
plt.figure(figsize=(14, 5))
librosa.display.waveplot(x, sr)
# -

# ### `numpy.correlate`

# There are two ways we can compute the autocorrelation in Python. The first method is [`numpy.correlate`](http://docs.scipy.org/doc/numpy/reference/generated/numpy.correlate.html):

# Because the autocorrelation produces a symmetric signal, we only care about the "right half".
r = numpy.correlate(x, x, mode='full')[len(x)-1:]
print(x.shape, r.shape)

# Plot the autocorrelation:

# + slideshow={"slide_type": "skip"}
plt.figure(figsize=(14, 5))
plt.plot(r[:10000])
plt.xlabel('Lag (samples)')
plt.xlim(0, 10000)
# -

# ### `librosa.autocorrelate`

# + [markdown] slideshow={"slide_type": "notes"}
# The second method is [`librosa.autocorrelate`](http://bmcfee.github.io/librosa/generated/librosa.core.autocorrelate.html#librosa.core.autocorrelate):
# -

r = librosa.autocorrelate(x, max_size=10000)
print(r.shape)

# + slideshow={"slide_type": "subslide"}
plt.figure(figsize=(14, 5))
plt.plot(r)
plt.xlabel('Lag (samples)')
plt.xlim(0, 10000)
# -

# `librosa.autocorrelate` conveniently only keeps one half of the autocorrelation function, since the autocorrelation is symmetric. Also, the `max_size` parameter prevents unnecessary calculations.

# ## Pitch Estimation

# The autocorrelation is used to find repeated patterns within a signal. For musical signals, a repeated pattern can correspond to a pitch period. We can therefore use the autocorrelation function to estimate the pitch in a musical signal.

x, sr = librosa.load('audio/oboe_c6.wav')
ipd.Audio(x, rate=sr)

# Compute and plot the autocorrelation:

r = librosa.autocorrelate(x, max_size=5000)
plt.figure(figsize=(14, 5))
plt.plot(r[:200])

# The autocorrelation always has a maximum at zero, i.e. zero lag. We want to identify the maximum outside of the peak centered at zero. Therefore, we might choose only to search within a range of reasonable pitches:

midi_hi = 120.0
midi_lo = 12.0
f_hi = librosa.midi_to_hz(midi_hi)
f_lo = librosa.midi_to_hz(midi_lo)
t_lo = sr/f_hi
t_hi = sr/f_lo

print(f_lo, f_hi)
print(t_lo, t_hi)

# Set invalid pitch candidates to zero:

r[:int(t_lo)] = 0
r[int(t_hi):] = 0

plt.figure(figsize=(14, 5))
plt.plot(r[:1400])

# Find the location of the maximum:

t_max = r.argmax()
print(t_max)

# Finally, estimate the pitch in Hertz:

float(sr)/t_max

# Indeed, that is very close to the true frequency of C6:

librosa.midi_to_hz(84)

# ## Tempo Estimation

# When perfomed upon a novelty function, an autocorrelation can provide some notion of tempo.

# For more, see the notebook **Tempo Estimation**.

# + [markdown] slideshow={"slide_type": "skip"}
# [&larr; Back to Index](index.html)

StarcoderdataJupyterScriptsDedupFiltered