Skip to content

Commit

Permalink
Clean up LFO and Voice params
Browse files Browse the repository at this point in the history
  • Loading branch information
mmontag committed Jan 7, 2016
1 parent 62bdedd commit 9431d6c
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 40 deletions.
31 changes: 17 additions & 14 deletions src/lfo-dx7.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
var config = require('./config');

var PARAMS = {};
var PERIOD = config.period;
var PERIOD_HALF = config.period / 2;
var PERIOD_RECIP = 1 / config.period;
Expand Down Expand Up @@ -68,10 +67,10 @@ var sampleHoldRandom = 0;
var delayTimes = [0, 0, 0];
var delayIncrements = [0, 0, 0];
var delayVals = [0, 0, 1];
var params = {};

function LfoDX7(opIdx, params) {
PARAMS = params;
this.operatorIndex = opIdx;
function LfoDX7(opParams) {
this.opParams = opParams;
this.phase = 0;
this.pitchVal = 0;
this.counter = 0;
Expand All @@ -86,7 +85,7 @@ function LfoDX7(opIdx, params) {
LfoDX7.prototype.render = function() {
var amp;
if (this.counter % LFO_SAMPLE_PERIOD == 0) {
switch (PARAMS.lfoWaveform) {
switch (params.lfoWaveform) {
case LFO_MODE_TRIANGLE:
if (this.phase < PERIOD_HALF)
amp = 4 * this.phase * PERIOD_RECIP - 1;
Expand Down Expand Up @@ -126,13 +125,13 @@ LfoDX7.prototype.render = function() {
// if (this.counter % 10000 == 0 && this.operatorIndex === 0) console.log("lfo amp value", this.ampVal);
amp *= this.delayVal;
pitchModDepth = 1 +
LFO_PITCH_MOD_TABLE[PARAMS.lfoPitchModSens] * (PARAMS.controllerModVal + PARAMS.lfoPitchModDepth / 99);
LFO_PITCH_MOD_TABLE[params.lfoPitchModSens] * (params.controllerModVal + params.lfoPitchModDepth / 99);
this.pitchVal = Math.pow(pitchModDepth, amp);
// TODO: Simplify ampValTarget calculation.
// ampValTarget range = 0 to 1. lfoAmpModSens range = -3 to 3. ampModDepth range = 0 to 1. amp range = -1 to 1.
var ampSensDepth = Math.abs(PARAMS.operators[this.operatorIndex].lfoAmpModSens) * 0.333333;
var phase = (PARAMS.operators[this.operatorIndex].lfoAmpModSens > 0) ? 1 : -1;
this.ampValTarget = 1 - ((ampModDepth + PARAMS.controllerModVal) * ampSensDepth * (amp * phase + 1) * 0.5);
var ampSensDepth = Math.abs(this.opParams.lfoAmpModSens) * 0.333333;
var phase = (this.opParams.lfoAmpModSens > 0) ? 1 : -1;
this.ampValTarget = 1 - ((ampModDepth + params.controllerModVal) * ampSensDepth * (amp * phase + 1) * 0.5);
this.ampIncrement = (this.ampValTarget - this.ampVal) / LFO_SAMPLE_PERIOD;
this.phase += phaseStep;
if (this.phase >= PERIOD) {
Expand All @@ -149,13 +148,17 @@ LfoDX7.prototype.renderAmp = function() {
return this.ampVal;
};

LfoDX7.setParams = function(globalParams) {
params = globalParams;
};

LfoDX7.update = function() {
var frequency = LFO_FREQUENCY_TABLE[PARAMS.lfoSpeed];
var frequency = LFO_FREQUENCY_TABLE[params.lfoSpeed];
phaseStep = PERIOD * frequency/LFO_RATE; // radians per sample
ampModDepth = PARAMS.lfoAmpModDepth * 0.01;
// ignoring amp mod table for now. it seems shallow LFO_AMP_MOD_TABLE[PARAMS.lfoAmpModDepth];
delayTimes[LFO_DELAY_ONSET] = (LFO_RATE * 0.001753 * Math.pow(PARAMS.lfoDelay, 3.10454) + 169.344 - 168) / 1000;
delayTimes[LFO_DELAY_RAMP] = (LFO_RATE * 0.321877 * Math.pow(PARAMS.lfoDelay, 2.01163) + 494.201 - 168) / 1000;
ampModDepth = params.lfoAmpModDepth * 0.01;
// ignoring amp mod table for now. it seems shallow LFO_AMP_MOD_TABLE[params.lfoAmpModDepth];
delayTimes[LFO_DELAY_ONSET] = (LFO_RATE * 0.001753 * Math.pow(params.lfoDelay, 3.10454) + 169.344 - 168) / 1000;
delayTimes[LFO_DELAY_RAMP] = (LFO_RATE * 0.321877 * Math.pow(params.lfoDelay, 2.01163) + 494.201 - 168) / 1000;
delayIncrements[LFO_DELAY_RAMP] = 1 / (delayTimes[LFO_DELAY_RAMP] - delayTimes[LFO_DELAY_ONSET]);
};

Expand Down
50 changes: 24 additions & 26 deletions src/voice-dx7.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
var Operator = require('./operator');
var EnvelopeDX7 = require('./envelope-dx7');
var LfoDX7 = require('./lfo-dx7');
var config = require('./config');

var PARAMS = {};
var SAMPLE_RATE = config.sampleRate;
var PERIOD = config.period;

var OUTPUT_LEVEL_TABLE = [
0.000000, 0.000337, 0.000476, 0.000674, 0.000952, 0.001235, 0.001602, 0.001905, 0.002265, 0.002694,
Expand Down Expand Up @@ -71,6 +66,8 @@ var ALGORITHMS = [
{ outputMix: [0,1,2,3,4,5], modulationMatrix: [[], [], [], [], [], [5]] } //32
];

var params = {};

function FMVoice(note, velocity) {
this.down = true;
this.note = parseInt(note, 10);
Expand All @@ -82,16 +79,16 @@ function FMVoice(note, velocity) {
// see https://github.com/smbolton/hexter/blob/621202b4f6ac45ee068a5d6586d3abe91db63eaf/src/dx7_voice.c#L789
// https://github.com/asb2m10/dexed/blob/1eda313316411c873f8388f971157664827d1ac9/Source/msfa/dx7note.cc#L55
// https://groups.yahoo.com/neo/groups/YamahaDX/conversations/messages/15919
var params = PARAMS.operators[i];
var opParams = params.operators[i];
var op = new Operator(
params,
opParams,
this.frequency,
new EnvelopeDX7(params.levels, params.rates),
new LfoDX7(i, PARAMS)
//new EnvelopeDX7(PARAMS.pitchEnvelope.levels, PARAMS.pitchEnvelope.rates, true)
new EnvelopeDX7(opParams.levels, opParams.rates),
new LfoDX7(opParams)
//new EnvelopeDX7(params.pitchEnvelope.levels, params.pitchEnvelope.rates, true)
);
// TODO: DX7 accurate velocity sensitivity map
op.outputLevel = (1 + (this.velocity - 1) * (params.velocitySens / 7)) * params.outputLevel;
op.outputLevel = (1 + (this.velocity - 1) * (opParams.velocitySens / 7)) * opParams.outputLevel;
this.operators[i] = op;
}
this.updatePitchBend();
Expand All @@ -105,20 +102,21 @@ FMVoice.frequencyFromNoteNumber = function(note) {
return 440 * Math.pow(2,(note-69)/12);
};

FMVoice.setParams = function(params) {
PARAMS = params;
FMVoice.setParams = function(globalParams) {
LfoDX7.setParams(globalParams);
params = globalParams;
};

FMVoice.setFeedback = function(value) {
PARAMS.fbRatio = Math.pow(2, (value - 7)); // feedback of range 0 to 7
params.fbRatio = Math.pow(2, (value - 7)); // feedback of range 0 to 7
};

FMVoice.setOutputLevel = function(operatorIndex, value) {
PARAMS.operators[operatorIndex].outputLevel = this.mapOutputLevel(value);
params.operators[operatorIndex].outputLevel = this.mapOutputLevel(value);
};

FMVoice.updateFrequency = function(operatorIndex) {
var op = PARAMS.operators[operatorIndex];
var op = params.operators[operatorIndex];
if (op.oscMode == 0) {
var freqCoarse = op.freqCoarse || 0.5; // freqCoarse of 0 is used for ratio of 0.5
op.freqRatio = freqCoarse * (1 + op.freqFine / 100);
Expand All @@ -132,7 +130,7 @@ FMVoice.updateLFO = function() {
};

FMVoice.setPan = function(operatorIndex, value) {
var op = PARAMS.operators[operatorIndex];
var op = params.operators[operatorIndex];
op.ampL = Math.cos(Math.PI / 2 * (value + 50) / 100);
op.ampR = Math.sin(Math.PI / 2 * (value + 50) / 100);
};
Expand All @@ -153,35 +151,35 @@ FMVoice.modulationWheel = function(value) {
};

FMVoice.updateMod = function() {
var aftertouch = PARAMS.aftertouchEnabled ? FMVoice.aftertouch : 0;
PARAMS.controllerModVal = Math.min(1.27, aftertouch + FMVoice.mod); // Allow 27% overdrive
var aftertouch = params.aftertouchEnabled ? FMVoice.aftertouch : 0;
params.controllerModVal = Math.min(1.27, aftertouch + FMVoice.mod); // Allow 27% overdrive
};

FMVoice.pitchBend = function(value) {
this.bend = value;
};

FMVoice.prototype.render = function() {
var algorithmIdx = PARAMS.algorithm - 1;
var algorithmIdx = params.algorithm - 1;
var modulationMatrix = ALGORITHMS[algorithmIdx].modulationMatrix;
var outputMix = ALGORITHMS[algorithmIdx].outputMix;
var outputScaling = 1 / outputMix.length;
var outputL = 0;
var outputR = 0;
for (var i = 5; i >= 0; i--) {
var mod = 0;
if (PARAMS.operators[i].enabled) {
if (params.operators[i].enabled) {
for (var j = 0, length = modulationMatrix[i].length; j < length; j++) {
var modulator = modulationMatrix[i][j];
if (PARAMS.operators[modulator].enabled) {
if (params.operators[modulator].enabled) {
var modOp = this.operators[modulator];
if (modulator === i) {
// Operator modulates itself; use feedback ratio
// TODO: implement 2-sample feedback averaging (anti-hunting filter)
// http://d.pr/i/1kuZ7/3h7jQN7w
// https://code.google.com/p/music-synthesizer-for-android/wiki/Dx7Hardware
// http://music.columbia.edu/pipermail/music-dsp/2006-June/065486.html
mod += modOp.val * PARAMS.fbRatio;
mod += modOp.val * params.fbRatio;
} else {
mod += modOp.val * modOp.outputLevel;
}
Expand All @@ -191,9 +189,9 @@ FMVoice.prototype.render = function() {
this.operators[i].render(mod);
}
for (var k = 0, length = outputMix.length; k < length; k++) {
if (PARAMS.operators[outputMix[k]].enabled) {
if (params.operators[outputMix[k]].enabled) {
var carrier = this.operators[outputMix[k]];
var carrierParams = PARAMS.operators[outputMix[k]];
var carrierParams = params.operators[outputMix[k]];
var carrierLevel = carrier.val * carrier.outputLevel;
outputL += carrierLevel * carrierParams.ampL;
outputR += carrierLevel * carrierParams.ampR;
Expand All @@ -217,7 +215,7 @@ FMVoice.prototype.updatePitchBend = function() {
};

FMVoice.prototype.isFinished = function() {
var outputMix = ALGORITHMS[PARAMS.algorithm - 1].outputMix;
var outputMix = ALGORITHMS[params.algorithm - 1].outputMix;
for (var i = 0; i < outputMix.length; i++) {
if (!this.operators[outputMix[i]].isFinished()) return false;
}
Expand Down

0 comments on commit 9431d6c

Please sign in to comment.