Karplus-Strong 算法合成吉他弦声

大家有没有听过音叉发出的声音?音叉振动产生的声波很接近正弦波。而计算机合成的纯正正弦波,点击下面的音频即可试听。

音频地址:

https://mp.weixin.qq.com/s/g0dT-eFvk_wuHCszQtwRhA

怎么说呢,和我们平时听到的乐器声完全不一样。这是因为,正弦波只包含一种频率,而我们平时听到的乐器的声波里掺杂着各式各样的频率。其中,我们把声波中强度最高的频率称为基本频率,把基本频率的倍数频率成为泛音。而基本频率与泛音的配合,使得乐器的音色如此优美。

正弦波的声音

path 参数为音频文件保存地址,freq 为正弦波的频率。rate 为正弦波的采样率,即每秒钟采样点的个数,duration 为正弦波持续时间。np.linspace 在 0 到 duration 范围内产生 samples 个采样点,vals 求得每个点的幅值。接着我们将幅值扩大 (2 ** 15 - 1) 倍,使得结果在 - 32767 ~ 32767 之间。即使用十六位二进制保存数据值,之后将数据转换为字节串,方便写入文件。

def write_sine(path:str, freq:float, rate:int=44100, duration:int=5):
    samples = rate * duration
    x = np.linspace(0, duration, samples)
    vals = np.sin(2 * np.pi * freq * x)
    data = np.array(vals * (2 ** 15 - 1), 'int16').tostring()
    write_wave(path, data)

高斯白噪声

高斯白噪声直接使用 np.random.randn 函数即可。

def white_noise(length:int):
    return np.random.randn(length)

工作原理

建立环形缓冲区,充当两端固定的弦。

设缓冲区的新产生尾部元素为 y,缓冲区长度为 p,则 y = 0.5 * [y(t-p) + y(t-p-1)] * alpha。y(t-p) 显然是缓冲区的第一个元素,由于缓冲区是环形的,y(t-p-1) 实际上是缓冲区最后一个元素。我们在缓冲区尾部添加这个元素,并删除缓冲区第一个元素,从而保证环形缓冲区长度不变。求平均值,相当于均值滤波,使得声波更加平滑,相当于低通滤波器,从而去除高次谐波,保留基波分量。最后,使用衰减因子而模拟振动的能量损失。

  • 创建环形缓冲区,长度N = S / f,S 为采样频率,f 为基本频率。

  • 向环形缓冲区填充高斯白噪声,作为初始值。

  • 从环形缓冲区取出第一个元素,放入样本区。

  • 将第一个元素与环形缓冲区的最后一个元素相加,并求得平均值,将得到的结果乘以衰减系数 alpha。

  • 将得到的计算结果存入环形缓冲区尾部。

  • 删除环形缓冲区的第一个值。

def generate_wave(freq:float, rate:int=44100, sample_rate:int=44100, duration:float=1,alpha=0.996):
    
    length = int(sample_rate / freq)
    buf = deque(white_noise(length))
    points = int(sample_rate * duration) 
    samples = np.zeros(points, 'float32')
    for i in range(points):
        samples[i] = buf[0]
        avg = alpha * 0.5 * (buf.popleft() + buf[-1])
        buf.append(avg)
        
    samples = np.array(samples * Aconst.amplitude.value, 'int16')
    return samples, samples.tostring()

保存音频

最后我们使用 wave 来保存音频数据。setparams 接受5 个参数,分别为声道数、样本宽度、样本采样率、样本帧数,‘NONE’ 和 ‘uncompressed’ 表明音频是非压缩的。

def write_wave(path:str, data:bytes, frame_rate:int=44100, frames:int=44100):
    with wave.open(path, 'wb') as w:
        w.setparams((Aconst.channels.value, \
                     Aconst.sample_width.value, \
                     frame_rate, \
                     frames, \
                     'NONE', 'uncompressed'))
        w.writeframes(data)

下面是 五声音阶中 C4 音符的合成音频。

音频地址:

https://mp.weixin.qq.com/s/g0dT-eFvk_wuHCszQtwRhA

播放音频

可以使用标准库中的 winsound 的 PlaySound 函数,来播放 wav 音频。

还可以参考我之前的文章。

最后,根据 karplus 算法生成了下面这些频率的合集。

{"A0": 27.5, "A#0": 29.14, "B0": 30.87, "C1": 32.7, "C#1": 34.65, "D1": 36.71, … , "C8": 4186.01}

音频地址:
https://mp.weixin.qq.com/s/g0dT-eFvk_wuHCszQtwRhA

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言: 记载资料多为网络搜集,侵删。 根据最近接触的整机项目做了一些整机音频相关基础知识的总结,如有不足或表述问题...
    Gawain_Knowknow阅读 8,363评论 0 4
  • 这篇文章是系列文章的第3篇。 第一篇在这里声波配网原理。 关于声波传输编码的部分在这里声波传输编码。 前面说过,声...
    作死少女88阅读 5,784评论 7 14
  • 1 数字音频基础知识# 1.1 声波### 声音始于空气中的振动,这些振动一起推动邻近的空气分子,而轻微增加空气压...
    朱细细阅读 18,417评论 0 25
  • 第一章:AV Foundation入门: 1.1 AV Foundation的含义 AV Foundation是苹...
    武当霍元甲阅读 1,343评论 0 2
  • iOS 苹果官方Demo合集 字数10517阅读21059评论18喜欢144 其实, 开发了这么久, 不得不说, ...
    bingo居然被占了阅读 10,362评论 2 31