Musical instruments¶
In this notebook we do some signal processing with recordings of musical instruments (downloaded from https://philharmonia.co.uk/resources/sound-samples/)
import numpy as np
import scipy
import scipy.io.wavfile as wav
import matplotlib.pyplot as plt
import pandas as pd
from IPython.display import Audio
We have two recordings for each of the instruments listed below. Each recording is only a few seconds long, with the instrument playing a single note. The two clarinet recordings are clarinet0.wav and clarinet1.wav, the two drum recordings are drum0.wav and drum1.wav and so on.
instruments = [
'clarinet','drum','flute','gong','guitar','oboe','saxophone','trumpet','violin'
]
data_dir = '../data/audio/instruments/'
The following function will display a widget with a button that you can click to play the specified recording.
def play_sample(f):
display(Audio(filename=data_dir + f + '.wav'))
play_sample('clarinet0')
The function below reads in a sound file (using scipy.io.wavfile.read()) and converts it to a numpy array. Typically there is some silence at the beginning and the end. To get rid of that, we let m be the maximum intensity, then we let a be the first time and b the last time where the intensity is at least m/2. We then focus on times between a and b. We take the Fourier transform of the intensity and call it p. This tells us the amount of power in the signal at different frequencies. Usually there is a primary frequency q with a smaller amount of energy especially at multiples of q and perhaps at some other frequencies as well. We make a plot in which a small part of the original signal is shown in the top half, and the Fourier transform is shown in the bottom half. In the Fourier transform plot, the axes and scales are arranged so that the peak is at (1,1).
def show_harmonics(f):
u = wav.read(data_dir + f + '.wav')[1].astype(np.float32) / (2 ** 15)
m = np.max(np.abs(u))
a = np.argmax(np.abs(u) >= m/2)
b = len(u) - np.argmax(np.abs(u[::-1]) >= m/2)
u = u[a:b]
p = np.abs(scipy.fft.fft(u))[:(b-a)//2]
q = np.argmax(p)
n = np.max(p)
fig, ax = plt.subplots(2,1)
ax[0].plot(u[:1000])
ax[1].plot(np.arange(8*q)/q, p[:8*q]/n)
The flute has a very pure tone: almost all of the power is at the primary frequency.
show_harmonics('flute0')
The trumpet has much stronger harmonics at multiples or half-multiples of the primary frequency.
show_harmonics('trumpet1')
show_harmonics('guitar0')
show_harmonics('gong0')