In [1]:
import numpy as np
import matplotlib.pyplot as plt
import IPython.display as ipd

Physically-Motivated Plucked String Synthesis

As we saw, string support a base frequency $f_0$ followed by its harmonics or overtones, which are integer multiples of the base frequency. When a string is plucked, many of these are activated at once. They all decay over time due to friction, but the higher frequency ones decay more quickly. Below, let's create some arrays that we will element-wise multiply by frequencies to decay them exponentially

In [5]:
sr = 44100
f0 = 440
t = np.arange(sr*2)/sr
decay = np.exp(-2*t)
plt.plot(t, decay)
plt.plot(t, decay**2)
plt.plot(t, decay**3)
plt.legend(["Decay $f_0$", "Decay 1st harmonic", "Decay 2nd harmonic"])
Out[5]:
<matplotlib.legend.Legend at 0x222809a7748>

Finally, let's add in the base frequency and the first harmonic, each decaying successively more rapidly, to get a physically inspired plucked string synthesis

In [3]:
y = np.zeros_like(t)
y += np.cos(2*np.pi*f0*t)*decay # Fundamental frequency
y += np.cos(2*np.pi*2*f0*t)*(decay**2) # First harmonic (overtone)
y += np.cos(2*np.pi*3*f0*t)*(decay**3) # Second harmonic
y += np.cos(2*np.pi*4*f0*t)*(decay**4) # Third harmonic
y += np.cos(2*np.pi*5*f0*t)*(decay**5) # Fourth harmonic
y += np.cos(2*np.pi*6*f0*t)*(decay**6) # Fifth harmonic
y += np.cos(2*np.pi*7*f0*t)*(decay**7) # Sixth harmonic
ipd.Audio(y, rate=sr)
Out[3]:

For loops

The basic for loop in python is quite simple, as shown by an example below that generates 20 random numbers in an array and loops through them.

In [4]:
np.random.seed(0)
x = np.random.randn(20)
for i in range(5, len(x), 3):
    print(i, x[i])
5 -0.977277879876411
8 -0.10321885179355784
11 1.454273506962975
14 0.44386323274542566
17 -0.20515826376580087
In [ ]: