常見的波形圖
波長為1的鋸齒波 \(f(x)=x-\lfloor x \rfloor\)
波長為1的三角波 \(f(x)=2 |2*(x - \lfloor x+ \frac 1 2 \rfloor)|-1\)
圖片生成網址:https://www.desmos.com/
以三角波為例,不同的頻率產生不同聲音
網上可以查到C4調,D4調,E4調的頻率
C4 | D4 | E4 | F4 | G4 | A4 | B4 |
---|---|---|---|---|---|---|
261.63 | 293.66 | 329.63 | 349.23 | 392.00 | 440.00 | 493.88 |
常用的音頻采樣頻率有8kHz、11.025kHz、22.05kHz、16kHz、37.8kHz、44.1kHz、48kHz等
from wave import open
from struct import Struct
from math import floor
sampler_rate=11025
"""對連續三角波函數進行離散取樣"""
def tri(frequency, amplitude=0.3):
"""a continuous triangle wave"""
wave_length = sampler_rate // frequency
# range from -0.3 to 0.3
def sampler(t):
saw_wave = t / wave_length - floor(t / wave_length + 0.5)
tri_wave = 2 * abs(2 * saw_wave) - 1
return amplitude * tri_wave
return sampler
def encode(x):
"""Encode float x between -1 and 1 as two bytes.
(See https://docs.python.org/3/library/struct.html)
"""
i = int(16384 * x)
return Struct('h').pack(i)
def play(sampler, name="song.wav", seconds=2):
"""
當輸出持續2s的聲音文件時,需要采樣 2*sampler_rate=22250次
"""
out = open(name, "wb")
out.setnchannels(1)
out.setsampwidth(2)
out.setframerate(sample_rate)
t = 0
while t < seconds * sample_rate:
sample = sampler(t)
out.writeframes(encode(sample))
t = t + 1
out.close()
play(tri(e_freq))
這樣輸出的音頻很難聽,還需要對波形做一些調整。
def note(f, start, end, fade=.01):
"""Play f for a fixed duration."""
def sampler(t):
seconds = t / sampler_rate
if seconds < start:
return 0
elif seconds > end:
return 0
elif seconds < start + fade:
return (seconds - start) / fade * f(t)
elif seconds > end - fade:
return (end - seconds) / fade * f(t)
else:
return f(t)
return sampler
play(note(tri(e_freq), 1, 1.5)) #在(1,1.5)之間播放E4調
輸出一段馬里奧前奏
def both(f, g):
return lambda t: f(t) + g(t)
def mario(c, e, g, low_g):
z = 0
song = note(e, z, z + 1 / 8)
z += 1 / 8
song = both(song, note(e, z, z + 1 / 8))
z += 1 / 4
song = both(song, note(e, z, z + 1 / 8))
z += 1 / 4
song = both(song, note(c, z, z + 1 / 8))
z += 1 / 8
song = both(song, note(e, z, z + 1 / 8))
z += 1 / 4
song = both(song, note(g, z, z + 1 / 4))
z += 1 / 2
song = both(song, note(low_g, z, z + 1 / 4))
return song
def mario_at(octave):
c = tri(octave * c_freq)
e = tri(octave * e_freq)
g = tri(octave * g_freq)
low_g = tri(octave * g_freq / 2)
return mario(c, e, g, low_g)
play(both(mario_at(1), mario_at(1 / 2)))