public void On(Sf2File.SampleHeader sample, Sf2Zone zone) { count = lastLfoCount = lastEnvCount = 0; // when (count & xxxMask) == 0, update xxx and xxx's dependents lfoMask = 0xff; envMask = 0xff; killed = false; var gs = zone.gens; // voice start = sample.start; startOffset = (uint)((gs[GenType.startAddrsCoarseOffset].value << 15) + gs[GenType.startAddrsOffset].value); // start = (uint)(sample.start + (gs[GenType.startAddrsCoarseOffset].value << 15) + gs[GenType.startAddrsOffset].value); uint end = (uint)(sample.end + (gs[GenType.endAddrsCoarseOffset].value << 15) + gs[GenType.endAddrsOffset].value); uint startloop = (uint)(sample.startloop + (gs[GenType.startloopAddrsCoarseOffset].value << 15) + gs[GenType.startloopAddrsOffset].value); uint endloop = (uint)(sample.endloop + (gs[GenType.endloopAddrsCoarseOffset].value << 15) + gs[GenType.endloopAddrsOffset].value); duration = end - start; loopEnd = endloop - start; loopDuration = endloop - startloop; phase = startOffset; // Console.Log(start, startOffset, end, startloop, endloop, duration, loopEnd, loopDuration); short root = gs[GenType.overridingRootKey].value; // MIDI ky# short scaleTuning = gs[GenType.scaleTuning].value; // cent/key if (root < 0) { root = sample.originalKey; // root = -1 means not set } curStep = step = sample.sampleRate * table.sampleRateRecip * table.semi2Pitch[Table.Semi2PitchCenter + gs[GenType.coarseTune].value] * (float)Table.Cent2Pitch((note - root) * scaleTuning + sample.correction + gs[GenType.fineTune].value); mode = gs[GenType.sampleModes].value; attenuation = (float)Table.Db2Gain(-gs[GenType.initialAttenuation].value * .1); // cB short pan = gs[GenType.pan].value; // 0.1% byte bytePan = (byte)(64 + 127 * (pan * .001f)); panLeft = table.pan2Left[bytePan]; panRight = table.pan2Right[bytePan]; // Console.Log("\tpan", pan, panLeft, panRight); // filter short initialFilterFc = gs[GenType.initialFilterFc].value; // cent short initialFilterQ = gs[GenType.initialFilterQ].value; // cB filterFc = (float)Table.AbsCent2Freq(initialFilterFc); filter.h1 = filter.h2 = 0; filter.Set(table, filterFc, (float)Table.Db2Gain(initialFilterQ * .1 - 3.01)); // useFilter may be set by modLfo and/or modEnv if they set the fc, so just init whatsoever useFilter = initialFilterFc < 13500; // vibLfo vibLfoToPitch = gs[GenType.vibLfoToPitch].value; // cent fs if (vibLfoToPitch != 0) { useVibLfo = true; short delayVibLfo = gs[GenType.delayVibLfo].value; // timecent short freqVibLfo = gs[GenType.freqVibLfo].value; // cent vibLfo.On(table, (float)Table.Timecent2Sec(delayVibLfo), (float)Table.AbsCent2Freq(freqVibLfo)); } else { useVibLfo = false; } // modLfo modLfoToPitch = gs[GenType.modLfoToPitch].value; // cent fs modLfoToFilterFc = gs[GenType.modLfoToFilterFc].value; // cent fs modLfoToVolume = gs[GenType.modLfoToVolume].value; // cB fs useModLfoToPitch = modLfoToPitch != 0; useModLfoToFilterFc = modLfoToFilterFc != 0; useModLfoToVolume = modLfoToVolume != 0; useModLfo = useModLfoToPitch || useModLfoToFilterFc || useModLfoToVolume; if (useModLfo) { // modLfo affects filter, so use filter if (useModLfoToFilterFc) { useFilter = true; } short delayModLfo = gs[GenType.delayModLfo].value; // timecent short freqModLfo = gs[GenType.freqModLfo].value; // cent modLfo.On(table, (float)Table.Timecent2Sec(delayModLfo), (float)Table.AbsCent2Freq(freqModLfo)); } // volEnv short delayVolEnv = gs[GenType.delayVolEnv].value; // timecent short attackVolEnv = gs[GenType.attackVolEnv].value; // timecent short holdVolEnv = gs[GenType.holdVolEnv].value; // timecent short decayVolEnv = gs[GenType.decayVolEnv].value; // timecent short releaseVolEnv = gs[GenType.releaseVolEnv].value; // timecent short sustainVolEnv = gs[GenType.sustainVolEnv].value; // cB attn volEnv.On(table, (float)Table.Timecent2Sec(delayVolEnv), (float)Table.Timecent2Sec(attackVolEnv), (float)Table.Timecent2Sec(holdVolEnv), (float)Table.Timecent2Sec(decayVolEnv), (float)Table.Timecent2Sec(releaseVolEnv), (float)Table.Db2Gain(-(double)sustainVolEnv * .1)); // Console.Log("\tv", id, "on volEnv", delayVolEnv, attackVolEnv, holdVolEnv, decayVolEnv, releaseVolEnv, sustainVolEnv, -(double)sustainVolEnv * .1, Table.Db2Gain(-(double)sustainVolEnv * .1)); // modEnv modEnvToPitch = gs[GenType.modEnvToPitch].value; // cent fs modEnvToFilterFc = gs[GenType.modEnvToFilterFc].value; // cent fs useModEnvToPitch = modEnvToPitch != 0; useModEnvToFilterFc = modEnvToFilterFc != 0; useModEnv = useModEnvToPitch || useModEnvToFilterFc; if (useModEnv) { // modEnv affects filter, so use filter if (useModEnvToFilterFc) { useFilter = true; } short delayModEnv = gs[GenType.delayModEnv].value; // timecent short attackModEnv = gs[GenType.attackModEnv].value; // timecent short holdModEnv = gs[GenType.holdModEnv].value; // timecent short decayModEnv = gs[GenType.decayModEnv].value; // timecent short releaseModEnv = gs[GenType.releaseModEnv].value; // timecent short sustainModEnv = gs[GenType.sustainModEnv].value; // cB attn modEnv.On(table, (float)Table.Timecent2Sec(delayModEnv), (float)Table.Timecent2Sec(attackModEnv), (float)Table.Timecent2Sec(holdModEnv), (float)Table.Timecent2Sec(decayModEnv), (float)Table.Timecent2Sec(releaseModEnv), (float)Table.Db2Gain(-(double)sustainModEnv * .1)); // Console.Log("\tv", id, "on modEnv", delayModEnv, attackModEnv, holdModEnv, decayModEnv, releaseModEnv, sustainVolEnv); } // Console.Log("v", id, "on", note, // "vibLfo", vibLfoToPitch, // "modLfo", modLfoToPitch, modLfoToFilterFc, modLfoToVolume, // "modEnv", modEnvToPitch, modEnvToFilterFc, // "useVibLfo", useVibLfo, "useModLfo", useModLfo, "useModEnv", useModEnv, "useFilter", useFilter, // "filter", filterFc, filter.q, sample.sampleName); }