# F1/F2/F3 in Hz with relative level. Measured vowel resonances, not tuned by ear.
VOWELS = {
'aa': [(730, 1.00), (1090, 0.50), (2440, 0.22)], # father
'eh': [(530, 1.00), (1840, 0.45), (2480, 0.30)], # bed
'ee': [(270, 1.00), (2290, 0.40), (3010, 0.28)], # see
'oh': [(570, 1.00), (840, 0.42), (2410, 0.14)], # go
'oo': [(300, 1.00), (870, 0.28), (2240, 0.10)], # boot
'uh': [(640, 1.00), (1190, 0.48), (2390, 0.20)], # but
}
def voice(freq, n, vowel='aa', glide=0.0, vib=5.2, breath=0.05, q=11.0):
"""Sung vowel. Returns a mono buffer."""
freq = nf(freq)
fr = vibrato(bend(freq, n, glide, 0.05), rate=vib, depth=0.009, onset=0.14)
ph = (np.cumsum(fr) / SR) % 1.0
# A soft asymmetric pulse, not a saw. The asymmetry is what stops it sounding like
# a filtered oscillator: a glottis snaps shut faster than it opens.
src = np.exp(-ph * 5.5) - 0.28
src = src - src.mean()
out = np.zeros(n)
for hz, lvl in VOWELS[vowel]:
out += reso(src, hz, q, lvl)
out += hp(lp(noise(n), 5200), 1600) * breath
return lp(out, 6500) * env(n, 0.022, n / SR * 0.45, 1.9) * 0.5