Я пытаюсь реализовать некоторые особенности звукового чипа Yamaha YM3812 (иначе OPL2 http://en.wikipedia.org/wiki/YM3812) в JavaScript с использованием Audiolet (синтез библиотека, http://oampo.github.io/Audiolet/ api.html)
Аудиопетлет позволяет создавать синтезатор в виде графика узлов (осцилляторов, DSP, генераторов огибающей и т.д.).
OPL2 имеет девять каналов с двумя операторами (осцилляторами) каждый. Обычно один генератор в каждом канале модулирует частоту другого. Чтобы имитировать это, я создал цепочку узлов для каждого канала:
Синтетическая цепочка node (один из девяти каналов)
Node создание цепочки и код подключения:
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 (опять же, я не уверен).