Что не так с этим простым дизайном FM-синтезатора?
Я пытаюсь реализовать некоторые функции звукового чипа Yamaha YM3812 (также известного как OPL2 http://en.wikipedia.org/wiki/YM3812) в JavaScript, используя Audiolet (библиотека синтеза, http://oampo.github.io/Audiolet/)
Audiolet позволяет вам построить синтезатор в виде графа узлов (осцилляторы, DSP, генераторы огибающей и т. Д.).
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
};
Однако, когда я устанавливаю параметры узлов модулятора и несущей (формы сигналов генератора, относительные частоты, затухание, параметры 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 (опять же, я не уверен).
1 ответ
Большинство синтезаторов, помеченных как "FM", на самом деле имеют фазовую модуляцию (см. 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
контролируется генератором мод вместо частоты.