This notebook plays a chord that is specified by a list of numbers. An octave is divided into 12 logarihmically spaced pieces. The chord is determined by a list. The sound is produced by making use of the Karplus-Strong method for guitar string sound generation.
The 'root' is selected as 87.31 Hz by default, which corresponds to F (or la). This is denoted as '0'.
BaseFreq = 87.31 # frequency corresponding to F
T = 5.0 # duration in seconds
fs = 16000 # sampling rate in Hz
import numpy as np
#from IPython.display import Audio
import IPython
import matplotlib.pyplot as plt
def KarplusStrong(fs, f0, T):
# np.random.seed(0)
P = np.round(fs / f0).astype(int)
K = np.round(T * fs).astype(int)
y = np.zeros(K)
y[:P+1] = np.random.normal(0,1,P+1)
for k in np.arange(P+1,K):
y[k] = 0.5 * (y[k-P] + y[k - P - 1])
y[:P-10] = 0
return y
def ConstructChord(NoteList):
K = np.round(T * fs).astype(int)
x = np.zeros((K, len(NoteList)))
w = np.linspace(0, fs, x.shape[0])
wind = (w < BaseFreq * 2**4) & (w > BaseFreq/2)
# create figure
fig = plt.figure(figsize = (15,5))
ax = fig.add_subplot(111)
for ind, k in enumerate(NoteList):
f0 = BaseFreq * np.power(2, k / 12)
x[:,ind] = KarplusStrong(fs, f0, T)
X = np.fft.fft(x[:,ind])
ax.plot(w[wind], np.abs(X[wind]), label = '{}'.format(k))
ax.legend()
ticks = BaseFreq * (2**(np.arange(-1,4,1/12)))
ticklabels = [''] * ticks.size
ticklabels[::12] = 'F' * 5
ax.set_xscale('log', basex = 2.0)
ax.set_xticks(ticks, minor = False)
ax.set_xticklabels(ticklabels)
ax.set_yscale('log')
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Magnitude (dB)')
ax.grid(axis='x')
return x
Let us now declare some chords according to the numbering convention described above.
Set_of_Chords = {}
Set_of_Chords['F7'] = [0, 4, 10, 14]
Set_of_Chords['Fmaj'] = [0, 4, 7]
Set_of_Chords['Fmin'] = [0, 3, 7]
Set_of_Chords['bB7'] = [-7, 3, 9]
Set_of_Chords['C7'] = [7, 11, 17, 21]
Set_of_Chords['D'] = [0, 7, 12, 16]
Set_of_Chords['Fpower'] = [0, 7, 12]
We now show the spectrograms for these chords.
padT = 0.1 # duration of silence before the chord, in seconds
pad = np.zeros(int(fs * padT))
for key in Set_of_Chords:
Notelist = Set_of_Chords[key]
x0 = ConstructChord(Notelist)
plt.title(key)
print(key)
IPython.display.display(IPython.display.Audio(np.concatenate((pad, np.sum(x0, axis = 1))), rate=fs))
İlker Bayram, ibayram@ieee.org, 2019