数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 177|回复: 11

[影音] 揭秘雅马哈YAMAHA音源原理:FM合成器实现python

[复制链接]
发表于 5 天前 | 显示全部楼层 |阅读模式
在上世纪80年代,雅马哈音源芯片风靡一时,各种YM开头的芯片提供了十分丰富的声音。YM芯片现在虽然已经全面淘汰,但是其所用的FM音乐调制算法,却仍然在音乐制作领域拥有不可替代的地位,DX7、Sytrus、Serum等合成器都集成了FM算法。本文旨在介绍YM芯片的原理,对于音乐制作者、YM音源复刻者很有帮助。

一、为什么是FM?
音乐合成方法有很多。比如电子管风琴使用加法合成,MOOG合成器使用减法合成。为什么雅马哈要使用FM合成呢?简单来说,加法合成与减法合成都需要进行乘法甚至浮点数乘法才能实现,这在80年代缺乏DSP的环境下几乎无法实现。而FM合成器,只需要几次乘法就可以完成一个通道的合成,这是十分划算的。更重要的是FM合成器的音色变化及其丰富,从宁静的正弦波到富有金属质感的噪声,FM都能实现。
二、基本原理
FM合成是使用一个信号改变另一个信号的频率。假设两个信号为y1=sin(w1 * t) y2=sin(w2 * t),则FM合成可以表示为
y=sin(w1 * t + K * ∫y2 dt)


这与FM广播所使用的FM调制没有什么本质不同。信号经过FM调制后,会产生丰富的谐波分量,从载频w1 w2,到二次谐波w1+w2 w1-w2,再到三次谐波2*w1-w2、2
*w2-w1等等,形成极其多变的时域波形,这就是FM音色的来源。
三、实现
如果直接照搬公式y=sin(w1 * t + K * ∫y2 dt)进行实现,那就要实现一个正弦、两次乘法、一次积分,这显然不能承受。但是实际上我们可以进行一些化简,将FM合成算法简化许多。
1、首先要去掉积分,我们知道三角函数的积分还是三角函数,所以干脆直接去掉积分
y = sin(w1 * t + k * y2)


2、然后处理两个乘法。虽然看着很唬人,但是计算机里时间是离散的,只能取特定值。我们可以定义一个变量x,来表示上一时刻的相位值
x = w1 * t + k * y2(prev)


其中y2(prev)表示上一时刻y2的值。这样,我们FM的式子就变成了这样
y = sin(x + w1 * T + k * (y2 - y2(prev)))


T是系统的采样周期,w1 * T事实上是每个周期中,相位的增量。
3、接下来要处理y1 - y1(prev),根据和差公式,我们可以直接认为
y2 - y2(prev) ≈ a * y2


a的值可以由三角函数计算得到。
4、最后,我们把正弦值放到一个数组里,这样就不用算正弦函数了。
y = sinTable[w1 * T + ka * sinTable[w2 * t] ]


至此,我们已经去掉了绝大多数的乘法。当然,这个式子也已经不是标准的FM调制了,不过音乐合成中不是很在乎FM信号是否标准。
四、包络
不同乐器的音量特点是不一样的,对于弹奏乐器,其声音是逐渐衰减的。所以我们需要一个函数来模拟这种变化。一般使用ADSR来模拟乐器音量变化,这里不展开叙述。
五、如何使用FM合成器
无论是YM芯片,还是现代的FM合成器,都有大量的预设参数。但是制作音乐时常常要自己制作音色,此时就必须清楚参数对于音色的影响。
根据FM合成公式y = sinTable[w1 * T + ka * sinTable[w2 * t] ],我们可以调节三个参数,w1 w2与ka。ka决定了音色是宁静还是聒噪,ka为0,输出正弦波,音色非常纯净,ka越大,谐波越大,音色越噪,ka特别大时,音色甚至近似于噪声。w1 w2的比值决定了音色是否和谐,一般来说,w1与w2成整数比或简单小数比,音色比较和谐,成无理数比,则音色比较刺耳。具体效果还请读者自己探索。
六、代码
代码虽然用python写成,但是实际上用的是C语言的书写思维,并未使用python强大的数组运算功能。有经验者可以快速将代码改成C语言,布置在单片机上。
论坛会吞代码缩进,可以下载原始文件。



  1. import numpy as np
  2. from scipy.io import wavfile
  3. import matplotlib.pyplot as plt

  4. fs = 44100  # 采样率
  5. T = 5  # 持续时间
  6. f1 = 440  # 频率
  7. f2 = 440 * 2.5
  8. amplitude = 0.3  # 振幅

  9. #生成正弦波表
  10. t = np.linspace(0, 2 * np.pi, 4410, endpoint=False)
  11. sinTable = np.sin(t)

  12. out = []
  13. freq1 = 40 #文中w1
  14. phase1 = 0

  15. freq2 = 80 #文中w2
  16. phase2 = 0
  17. rate = 1280 #文中ka

  18. statu = 0
  19. a_vec = 100 #attack速度
  20. d_vec = 5 #decay速度
  21. d_amp = 0 #sustain阶段的声音强度
  22. r_vec = 100 #remain速度
  23. adrs_out = 0

  24. trig = 0 #触发声音合成

  25. for step in range(fs * T) :
  26.     # op1
  27.     osc1 = sinTable[phase1]
  28.     phase1 += freq1
  29.     if phase1 >= 4410 :
  30.         phase1 -= 4410

  31.     # op2
  32.     osc2 = sinTable[phase2]
  33.     phase2 += freq2 + int(osc1 * rate)
  34.     if phase2 >= 4410 :
  35.         phase2 -= 4410
  36.     if phase2 < 0 :
  37.         phase2 += 4410

  38.     # adsr
  39.     if statu == 0 and trig == 1 : #trig
  40.         statu = 1
  41.     if statu == 1 : # attack
  42.         adrs_out += a_vec
  43.         if adrs_out > 65535 :
  44.             statu = 2
  45.             adrs_out = 65535
  46.         if trig == 0 :
  47.             statu = 4
  48.     if statu == 2 : # decay
  49.         adrs_out -= d_vec
  50.         if adrs_out < d_amp :
  51.             statu = 3
  52.             adrs_out = d_amp
  53.         if trig == 0 :
  54.             statu = 4
  55.     if statu == 3 : # sustain
  56.         if trig == 0 :
  57.             statu = 4
  58.     if statu == 4 : # remain
  59.         adrs_out -= r_vec
  60.         if adrs_out < 0 :
  61.             adrs_out = 0
  62.             statu = 1

  63.     # trig
  64.     if step > 10 :
  65.         trig = 1
  66.     if step > 30000 :
  67.         trig = 0
  68.     if step > 40000 :
  69.         trig = 1
  70.     if step > 50000 :
  71.         trig = 0

  72.     out.append(adrs_out / 65535 * osc2)

  73. plt.plot(out)
  74. plt.show()

  75. out = np.array(out) * amplitude * 65536 / 2
  76. out = out.astype(np.int16)
  77. output_path = 'sine_wave.wav'
  78. wavfile.write(output_path, fs, out)
复制代码


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x

打赏

参与人数 1家元 +50 收起 理由
不长叶子的树 + 50

查看全部打赏

发表于 5 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 5 天前 来自手机浏览器 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 5 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 1 反对 0

使用道具 举报

发表于 5 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 4 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 3 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册 微信登录

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2026-3-24 14:18 , Processed in 0.202801 second(s), 13 queries , Gzip On, Redis On.

Powered by Discuz!

© MyDigit.Net Since 2006

快速回复 返回顶部 返回列表