【问题标题】:What's wrong with this simple FM synth design?这个简单的 FM 合成器设计有什么问题?
【发布时间】:2013-05-31 04:06:17
【问题描述】:

我正在尝试使用 Audiolet(合成库,http://oampo.github.io/Audiolet/api.html)在 JavaScript 中实现 Yamaha YM3812 声音芯片(又名 OPL2 http://en.wikipedia.org/wiki/YM3812)的一些功能

Audiolet 允许您将合成器构建为节点图(振荡器、DSP、包络发生器等)。

OPL2 有九个通道,每个通道有两个运算符(振荡器)。通常,每个通道中的一个振荡器调制另一个的频率。为了模拟这一点,我为每个通道建立了一个节点链:

Synth 节点链(九个通道之一)

节点链创建及连接代码:

var FmChannel = function(audiolet) {
    this.car = new ModifiedSine(audiolet);
    this.carMult = 1;
    this.setCarrierWaveform(this.SIN);
    this.mod = new ModifiedSine(audiolet);
    this.modMult = 1;
    this.setModulatorWaveform(this.SIN);
    this.modMulAdd = new MulAdd(audiolet);
    this.carGain = new Gain(audiolet);
    this.carEnv = new ADSREnvelope(audiolet, 0, 0.1, 0.1, 0.1, 0.1,
        function() {
            this.carEnv.reset();
        }.bind(this)
    );
    this.carAtten = new Multiply(audiolet);
    this.modGain = new Gain(audiolet);
    this.modEnv = new ADSREnvelope(audiolet, 0, 0.1, 0.1, 0.1, 0.1,
        function() {
            this.modEnv.reset();
        }.bind(this)
    );
    this.modAtten = new Multiply(audiolet);

    this.modEnv.connect(this.modGain, 0, 1);
    this.mod.connect(this.modGain);
    this.modGain.connect(this.modAtten);
    this.modAtten.connect(this.modMulAdd);
    this.modMulAdd.connect(this.car);
    this.carEnv.connect(this.carGain, 0, 1);
    this.car.connect(this.carGain); 
    this.carGain.connect(this.carAtten);
    // connect carAtten to the mixer from outside
};

但是,当我设置调制器和载波节点的参数(振荡器波形、相对频率、衰减、ADSR 参数)和触发音符时,输出与具有大致相同参数的体面 OPL2 仿真器几乎没有相似之处。有些声音在球场上。其他人则相当不愉快。

我对如何继续有一些想法(我想在不同阶段绘制输出将是一个很好的起点),但我希望有经验的人可以为我指出正确的方向,或者指出一些明显错误的地方我在做什么。我没有信号处理或强大的数学背景。我对FM没有深刻的直观理解。

我怀疑的一些问题是:

1) 我的 FM 实现(如上所示)根本上是错误的。此外,播放音符的功能可能存在问题(在触发 ADSR 包络之前设置振荡器频率,并缩放和偏移调制器):

FmChannel.prototype.noteOn = function (frq) {
    var Fc = frq*this.carMult;
    this.car.reset(Fc);
    this.mod.reset(frq*this.modMult);
    // scale and offset modulator from range (-1, 1) to (0, 2*Fc)
    // (scale and offset is after ADSR gain and fixed attenuation is applied)
    this.modMulAdd.mul.setValue(Fc);
    this.modMulAdd.add.setValue(Fc);
    this.carEnv.reset();
    this.modEnv.reset();
    this.carEnv.gate.setValue(1);
    Thethis.modEnv.gate.setValue(1);
};

2) FM 合成器的输出可能对调制器 ADSR 包络形状的微小差异高度敏感(请告诉我这是不是真的!),我的 ADSR 包络最多是实际 ADSR 的粗略近似OPL2。我的实现还缺少一些看起来相对不重要的功能(例如键缩放),但可能会显着影响 FM 合成器的声音(同样,我不确定)。

【问题讨论】:

  • 看你的图像,调制器应该与载波频率而不是增益相关联(例如:en.wikipedia.org/wiki/Frequency_modulation#/media/…)。
  • 通常,您需要小心调制器的增益级,不要应用太多增益,从而调制。如果调制太大,您会得到“令人不快的声音”,调制在载波上占主导地位。我不确定这就是你所描述的。
  • Elric,根据图表,它确实看起来像调制器与增益相关联。自从我从事此工作或查看 Audiolet 以来已经很长时间了,我现在真的不确定这是否正在发生!我会深入研究一下。
  • 本体,我认为您可能在正确的道路上。我会看看我是否可以复活它,看看调制器增益减少会发生什么。

标签: javascript signal-processing web-audio-api sound-synthesis audiolet


【解决方案1】:

大多数标有“FM”的合成器实际上都进行相位调制(PM,参见https://en.wikipedia.org/wiki/Phase_modulation)。有一些好处(主要是在较大的音调范围内产生更稳定的声音)。 OPL2 也可能使用这个,我没有找到明确的证据,但维基百科的文章也使用了“相位调制”一词。

简而言之,许多标记为“FM”的音乐合成器实际上具有“PM”,因此您可以尝试使用它,并检查它是否更适合预期的 OPL2 声音。

快速浏览一下 Audiolet 源,我猜Sine 振荡器正在做真正的 FM,因此您可能需要更换它并添加相位输入以允许相位调制。

基本上就是一行

output.samples[0] = Math.sin(this.phase);

由Sine 使用的载波振荡器必须读取类似

output.samples[0] = Math.sin(this.phase+phase_offset);

phase_offset 由调制振荡器而不是频率控制。

【讨论】:

  • 著名的 Yamaha DX7 键盘,与 OPL2 大约同时由同一制造商生产,尽管也使用了相位调制,但仍被标记为“FM 合成”。
  • 感谢您的回答。你是对的,OPL 确实使用相位调制,我认为我的实现是在做“真正的”FM,但我已经很久没有看过它了,我现在真的不能说 :)。我的理解是,对于正弦波,相位调制和频率调制是等效的,所以我虽然可以通过这种方式获得类似的声音。
  • 不,FM 和 PM 除了共享许多属性外,还有很大的不同。然而,临时频率变化会随着时间的推移对 FM 进行整合,以增加永久相移。在 PM 上,临时调制会增加临时相移。
  • 在更高的层次上,改变 FM 上相对于载波频率的调制频率会改变载波频率和边带频率之间的功率分布,这会使 FM 声音在音调范围内发生巨大变化,例如.乐器根据音符的音高改变它们的声音更容易感知。对于 PM 声音,同样的效果要柔和得多,这使得设计过程更加可控。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-31
  • 1970-01-01
相关资源
最近更新 更多