String and other sounds can be creating by repeating a random signal while filtering out the high frequency components.
In the note function, we take average of two nearby samples, which is a simple low-pass filter.
Below we have 2 programs. The only function of the first program is to define a dictionary Hz. You can see Dictionary Example. Here I go over the frequency dictionary in more detail.
In the second program, which imports the dictionary, we iterate over the elements of notes list. The duration is the fraction of a second for that particular note. Thus the first two notes are C4 played for 0.25 seconds each.
# Hz.py
Notes = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']
Hz ={}
for i in range(88):
Octave = (i+9)/12
Pos = (i+9)%12
S = Notes[Pos]+str(Octave)
Hz[S] = 27.5*(2**(i/12.0))
# audio1.py
from __future__ import division
import numpy as np
from scipy.io import wavfile
from Hz import Hz
SR = 44100
notes = [('C4', 4), ('C4', 4), ('C4', 3), ('D4', 6),
('E4', 4), ('E4', 3), ('D4', 6), ('E4', 3),
('F4', 6), ('G4', 2), ('C5', 6), ('C5', 6),
('C5', 6), ('G4', 6), ('G4', 6), ('G4', 6),
('E4', 6), ('E4', 6), ('E4', 6), ('C4', 6),
('C4', 6), ('C4', 6), ('G4', 3), ('F4', 6),
('E4', 3), ('D4', 6), ('C4', 2)]
max_pos=32767
def note(f,num):
buf=np.random.rand(SR//f)-0.5
samples=[]
for i in range(num):
samples.append(buf[0])
avg=0.5*(buf[0]+buf[1])
buf = np.append(buf[1:],avg)
return np.array([x*max_pos for x in samples])
if __name__ == '__main__':
fname='row.wav'
out= np.zeros(10)
for i in range(len(notes)):
buff = note(Hz[notes[i][0]],SR//notes[i][1])
out = np.concatenate((out,buff))
fade_dist = 5000
x = np.arange(fade_dist-1,-1,-1)
fade_out = (1-np.exp(-x/fade_dist))/(1-np.exp(-1))
out[-fade_dist:] *= fade_out
wavfile.write(fname,SR,out.astype('int16'))