这个简单的FM合成器设计出了什么问题?

bsa*_*bsa 38 javascript signal-processing sound-synthesis audiolet web-audio-api

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

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

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

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

已实施OPL2频道

节点链创建和连接代码:

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
};
Run Code Online (Sandbox Code Playgroud)

但是,当我设置调制器和载波节点的参数(振荡器波形,相对频率,衰减,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);
};
Run Code Online (Sandbox Code Playgroud)

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

dro*_*nus 2

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

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

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

基本上,该行

output.samples[0] = Math.sin(this.phase);
Run Code Online (Sandbox Code Playgroud)

载波振荡器使用的Sine内容必须是这样的

output.samples[0] = Math.sin(this.phase+phase_offset);
Run Code Online (Sandbox Code Playgroud)

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

  • 著名的雅马哈 DX7 键盘与 OPL2 大约在同一时间由同一制造商生产,尽管也使用相位调制,但仍被标记为“FM 合成”。 (2认同)