diff --git a/src/spessasynth_lib/sequencer/sequencer.js b/src/spessasynth_lib/sequencer/sequencer.js index 33470093..6c60d241 100644 --- a/src/spessasynth_lib/sequencer/sequencer.js +++ b/src/spessasynth_lib/sequencer/sequencer.js @@ -1,6 +1,6 @@ import {MIDI} from "../midi_parser/midi_loader.js"; import { DEFAULT_PERCUSSION, Synthetizer } from '../synthetizer/synthetizer.js'; -import {getEvent, messageTypes, MidiMessage} from "../midi_parser/midi_message.js"; +import { getEvent, messageTypes, midiControllers, MidiMessage } from '../midi_parser/midi_message.js' import { consoleColors, formatTime } from '../utils/other.js' import {readBytesAsUintBigEndian} from "../utils/byte_functions.js"; @@ -599,7 +599,10 @@ export class Sequencer { { clearInterval(this.playbackInterval); this.playbackInterval = undefined; - this.synth.stopAll(true); + for (let i = 0; i < 16; i++) { + this.synth.controllerChange(i, midiControllers.sustainPedal, 0); + } + this.synth.stopAll(); if(this.MIDIout) { for (let c = 0; c < 16; c++) diff --git a/src/spessasynth_lib/soundfont/chunk/modulators.js b/src/spessasynth_lib/soundfont/chunk/modulators.js index 6e221184..bedecb9b 100644 --- a/src/spessasynth_lib/soundfont/chunk/modulators.js +++ b/src/spessasynth_lib/soundfont/chunk/modulators.js @@ -26,6 +26,16 @@ export const modulatorCurveTypes = { switch: 3 } +/** + * + * type, polarity, direction + * @type {Float32Array[][][]} + */ +export const precomputedTransforms = []; +for (let i = 0; i < 4; i++) { + precomputedTransforms.push([[], []]); +} + export class Modulator{ /** * Creates a modulator @@ -81,37 +91,67 @@ export class Modulator{ return; } - for (let i = 0; i < MOD_PRECOMPUTED_LENGTH; i++) { - this.sourceTransformed[i] = getModulatorValue( - this.sourceDirection, - this.sourceCurveType, - i / MOD_PRECOMPUTED_LENGTH, - this.sourceIsBipolar); - - this.secondarySrcTransformed[i] = getModulatorValue( - this.secSrcDirection, - this.secSrcCurveType, - i / MOD_PRECOMPUTED_LENGTH, - this.secSrcIsBipolar); - if(isNaN(this.sourceTransformed[i])) - { - this.sourceTransformed[i] = 1; - } - if(isNaN(this.secondarySrcTransformed[i])) - { - this.secondarySrcTransformed[i] = 1; + + // read the cached table + let sourceCached = false; + if(precomputedTransforms[this.sourceCurveType][this.sourceIsBipolar][this.sourceDirection]) + { + this.sourceTransformed = new Float32Array(precomputedTransforms[this.sourceCurveType][this.sourceIsBipolar][this.sourceDirection]); + sourceCached = true; + } + + let secondarySourceCached = false; + if(precomputedTransforms[this.secSrcCurveType][this.secSrcIsBipolar][this.secSrcDirection]) + { + this.secondarySrcTransformed = new Float32Array(precomputedTransforms[this.secSrcCurveType][this.secSrcIsBipolar][this.secSrcDirection]); + secondarySourceCached = true; + } + + if(!secondarySourceCached || !sourceCached) { + for (let i = 0; i < MOD_PRECOMPUTED_LENGTH; i++) { + if (!sourceCached) { + this.sourceTransformed[i] = getModulatorValue( + this.sourceDirection, + this.sourceCurveType, + i / MOD_PRECOMPUTED_LENGTH, + this.sourceIsBipolar); + if (isNaN(this.sourceTransformed[i])) { + this.sourceTransformed[i] = 1; + } + } + + if (!secondarySourceCached) { + this.secondarySrcTransformed[i] = getModulatorValue( + this.secSrcDirection, + this.secSrcCurveType, + i / MOD_PRECOMPUTED_LENGTH, + this.secSrcIsBipolar); + if (isNaN(this.secondarySrcTransformed[i])) { + this.secondarySrcTransformed[i] = 1; + } + } } } + + if(!sourceCached) + { + precomputedTransforms[this.sourceCurveType][this.sourceIsBipolar][this.sourceDirection] = this.sourceTransformed; + } + + if(!secondarySourceCached) + { + precomputedTransforms[this.secSrcCurveType][this.secSrcIsBipolar][this.secSrcDirection] = this.secondarySrcTransformed; + } } } export const defaultModulators = [ - new Modulator({srcEnum: 0x0502, dest: generatorTypes.initialAttenuation, amt: 1440, secSrcEnum: 0x0, transform: 0}), // vel to attenuation + new Modulator({srcEnum: 0x0502, dest: generatorTypes.initialAttenuation, amt: 960, secSrcEnum: 0x0, transform: 0}), // vel to attenuation new Modulator({srcEnum: 0x0081, dest: generatorTypes.vibLfoToPitch, amt: 50, secSrcEnum: 0x0, transform: 0}), // mod to vibrato - new Modulator({srcEnum: 0x0587, dest: generatorTypes.initialAttenuation, amt: 1440, secSrcEnum: 0x0, transform: 0}), // vol to attenuation + new Modulator({srcEnum: 0x0587, dest: generatorTypes.initialAttenuation, amt: 960, secSrcEnum: 0x0, transform: 0}), // vol to attenuation new Modulator({srcEnum: 0x020E, dest: generatorTypes.fineTune, amt: 12700, secSrcEnum: 0x0010, transform: 0}), // pitch to tuning new Modulator({srcEnum: 0x028A, dest: generatorTypes.pan, amt: 1000, secSrcEnum: 0x0, transform: 0}), // pan to uhh, pan - new Modulator({srcEnum: 0x058B, dest: generatorTypes.initialAttenuation, amt: 1440, secSrcEnum: 0x0, transform: 0}) // expression to attenuation + new Modulator({srcEnum: 0x058B, dest: generatorTypes.initialAttenuation, amt: 960, secSrcEnum: 0x0, transform: 0}) // expression to attenuation ] console.log("%cDefault Modulators:", consoleColors.recognized, defaultModulators) diff --git a/src/spessasynth_lib/soundfont/chunk/samples.js b/src/spessasynth_lib/soundfont/chunk/samples.js index 49d66685..788737e1 100644 --- a/src/spessasynth_lib/soundfont/chunk/samples.js +++ b/src/spessasynth_lib/soundfont/chunk/samples.js @@ -149,6 +149,7 @@ export class Sample { this.sampleLength = this.sampleEndIndex - this.sampleStartIndex; this.indexRatio = 1; this.sampleDataArray = smplArr; + this.sampleLengthSeconds = this.sampleLength / (this.sampleRate * 2); if (this.sampleLength < 1 || this.sampleName.substring(0, 3).toLowerCase() === "eos") { return; diff --git a/src/spessasynth_lib/synthetizer/buffer_voice/generator_translator.js b/src/spessasynth_lib/synthetizer/buffer_voice/generator_translator.js index 087ed254..d8a8484c 100644 --- a/src/spessasynth_lib/synthetizer/buffer_voice/generator_translator.js +++ b/src/spessasynth_lib/synthetizer/buffer_voice/generator_translator.js @@ -243,9 +243,9 @@ export class GeneratorTranslator { const decayTime = this.timecentsToSeconds(this.decayTime + ((60 - this.midiNote) * this.keyToVolDecay)); const sustainLevel = this._getSustainLevel() * velocityGain; let releaseTime = this.timecentsToSeconds(this.releaseTime); - if(releaseTime > 5) + if (releaseTime > 15) { - releaseTime = 5; + releaseTime = 15; } return { attenuation: attenuation, diff --git a/src/spessasynth_lib/synthetizer/buffer_voice/midi_channel.js b/src/spessasynth_lib/synthetizer/buffer_voice/midi_channel.js index 28e2e143..268f24e9 100644 --- a/src/spessasynth_lib/synthetizer/buffer_voice/midi_channel.js +++ b/src/spessasynth_lib/synthetizer/buffer_voice/midi_channel.js @@ -308,7 +308,7 @@ export class MidiChannel { get voicesAmount() { - return this.notes.size; + return this.playingNotes.reduce((amt, voice) => amt + voice.sampleNodes.length, 0) + this.stoppingNotes.reduce((amt, voice) => amt + voice.sampleNodes.length, 0); } setVolume(volume) { @@ -477,34 +477,32 @@ export class MidiChannel { return; } - let notes = this.playingNotes.filter(n => n.midiNote === midiNote); - if(notes.length < 1) + let note = this.playingNotes.find(n => n.midiNote === midiNote); + if(!note) { return } - for(let note of notes) { - // add note as a fading one - this.stoppingNotes.push(note); + // add note as a fading one + this.stoppingNotes.push(note); - // and remove it from the main array - this.playingNotes.splice(this.playingNotes.indexOf(note), 1); + // and remove it from the main array + this.playingNotes.splice(this.playingNotes.indexOf(note), 1); - if(highPerf) - { - note.killNote().then(() => { - this.notes.delete(midiNote); - note.disconnectNote(); - delete this.stoppingNotes.splice(this.stoppingNotes.indexOf(note), 1); - }); - } - else { - note.stopNote().then(() => { - this.notes.delete(midiNote); - note.disconnectNote(); - delete this.stoppingNotes.splice(this.stoppingNotes.indexOf(note), 1); - }); - } + if(highPerf) + { + note.killNote().then(() => { + this.notes.delete(midiNote); + note.disconnectNote(); + delete this.stoppingNotes.splice(this.stoppingNotes.indexOf(note), 1); + }); + } + else { + note.stopNote().then(() => { + this.notes.delete(midiNote); + note.disconnectNote(); + delete this.stoppingNotes.splice(this.stoppingNotes.indexOf(note), 1); + }); } } diff --git a/src/spessasynth_lib/synthetizer/buffer_voice/synthesis_model.js b/src/spessasynth_lib/synthetizer/buffer_voice/synthesis_model.js index d9828647..d9476136 100644 --- a/src/spessasynth_lib/synthetizer/buffer_voice/synthesis_model.js +++ b/src/spessasynth_lib/synthetizer/buffer_voice/synthesis_model.js @@ -194,6 +194,9 @@ export class SynthesisModel this.wavetableOscillator.start(); } + /** + * @returns {number} the release time + */ stop() { // looping mode 3 @@ -230,6 +233,12 @@ export class SynthesisModel this.lowpassFilter.frequency.setValueAtTime(this.lowpassFilter.frequency.value, this.now); this.lowpassFilter.frequency.linearRampToValueAtTime(this.filEnv.endHz, this.now + this.filEnv.releaseTime); } + + if(this.volEnv.releaseTime > this.synthesisOptions.sample.sampleLengthSeconds && !this.wavetableOscillator.loop) + { + return this.synthesisOptions.sample.sampleLengthSeconds; + } + return this.volEnv.releaseTime; } disconnect() diff --git a/src/spessasynth_lib/synthetizer/buffer_voice/voice.js b/src/spessasynth_lib/synthetizer/buffer_voice/voice.js index 3dd93967..8c8af16b 100644 --- a/src/spessasynth_lib/synthetizer/buffer_voice/voice.js +++ b/src/spessasynth_lib/synthetizer/buffer_voice/voice.js @@ -92,10 +92,7 @@ export class Voice */ async stopNote(){ // find the longest release time - let maxFadeout = Math.max(...this.sampleNodes.map(s => s.volEnv.releaseTime)); - - // stop every sample - this.sampleNodes.forEach(s => s.stop()); + let maxFadeout = Math.max(...this.sampleNodes.map(s => s.stop())); // so .then() can be added to delete the note after it finished await new Promise(r => setTimeout(r, maxFadeout * 1000)); diff --git a/src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js b/src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js index 18a3083d..58f0e2d1 100644 --- a/src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js +++ b/src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js @@ -57,11 +57,12 @@ class ChannelProcessor extends AudioWorkletProcessor { // note off case workletMessageType.noteOff: this.voices.forEach(v => { - if(v.midiNote === data) + if(v.midiNote !== data) { - v.isInRelease = true; - v.releaseStartTime = currentTime; + return; } + v.releaseStartTime = currentTime; + v.isInRelease = true; }); break; @@ -139,19 +140,13 @@ class ChannelProcessor extends AudioWorkletProcessor { // MODULATORS are computed in getModulated if needed. // TUNING - // get the root key - let key = voice.sample.rootKey; - const overrideKey = getModulated(voice, generatorTypes.overridingRootKey, this.midiControllers); - if(overrideKey !== -1) - { - key = overrideKey; - } + // calculate tuning let cents = getModulated(voice, generatorTypes.fineTune, this.midiControllers); let semitones = getModulated(voice, generatorTypes.coarseTune, this.midiControllers) + parseFloat(this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.channelTuning] >> 7); // calculate tuning by key - cents += (voice.midiNote - key) * getModulated(voice, generatorTypes.scaleTuning, this.midiControllers); + cents += (voice.targetKey - voice.sample.rootKey) * getModulated(voice, generatorTypes.scaleTuning, this.midiControllers); // vibrato LFO const vibratoDepth = getModulated(voice, generatorTypes.vibLfoToPitch, this.midiControllers); @@ -170,7 +165,7 @@ class ChannelProcessor extends AudioWorkletProcessor { const modPitchDepth = getModulated(voice, generatorTypes.modLfoToPitch, this.midiControllers); const modVolDepth = getModulated(voice, generatorTypes.modLfoToVolume, this.midiControllers); let modLfoCentibels = 0; - if(modPitchDepth + modVolDepth > 0) + if(modPitchDepth > 0 || modVolDepth > 0) { const modStart = voice.startTime + timecentsToSeconds(getModulated(voice, generatorTypes.delayModLFO, this.midiControllers)); const modFreqHz = absCentsToHz(getModulated(voice, generatorTypes.freqModLFO, this.midiControllers)); @@ -209,20 +204,6 @@ class ChannelProcessor extends AudioWorkletProcessor { decay = timecentsToSeconds(getModulated(voice, generatorTypes.decayVolEnv, this.midiControllers) + ((60 - voice.midiNote) * getModulated(voice, generatorTypes.keyNumToVolEnvDecay, this.midiControllers))); } - // WAVETABLE OSCILLATOR - // get offsets - let loopStart = voice.sample.loopStart + getModulated(voice, generatorTypes.startloopAddrsOffset, this.midiControllers) + (getModulated(voice, generatorTypes.startloopAddrsCoarseOffset, this.midiControllers) * 32768); - let loopEnd = voice.sample.loopEnd + getModulated(voice, generatorTypes.endloopAddrsOffset, this.midiControllers) + (getModulated(voice, generatorTypes.endloopAddrsCoarseOffset, this.midiControllers) * 32768); - const startOffset = getModulated(voice, generatorTypes.startAddrsOffset, this.midiControllers) + (getModulated(voice, generatorTypes.startAddrsCoarseOffset, this.midiControllers) * 32768); - const endIndex = getModulated(voice, generatorTypes.endAddrOffset, this.midiControllers) + (getModulated(voice, generatorTypes.endAddrsCoarseOffset, this.midiControllers) * 32768) + this.samples[voice.sample.sampleID].length; - const mode = getModulated(voice, generatorTypes.sampleModes, this.midiControllers); - const loop = mode === 1 || (mode === 3 && !voice.isInRelease); - if(loopStart >= loopEnd) - { - loopStart = voice.sample.loopStart; - loopEnd = voice.sample.loopEnd; - } - // PANNING const pan = ( (Math.max(-500, Math.min(500, getModulated(voice, generatorTypes.pan, this.midiControllers) )) + 500) / 1000) ; // 0 to 1 const panLeft = Math.cos(HALF_PI * pan); @@ -233,14 +214,10 @@ class ChannelProcessor extends AudioWorkletProcessor { for (let outputSampleIndex = 0; outputSampleIndex < outputLeft.length; outputSampleIndex++) { // Read the sample - let sample = getOscillatorValue(voice, + let sample = getOscillatorValue( + voice, this.samples[voice.sample.sampleID], - playbackRate, - startOffset, - endIndex, - loop, - loopStart, - loopEnd + playbackRate ); // apply the volenv diff --git a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js index 1a8370fa..bf7ec75f 100644 --- a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js +++ b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js @@ -5,6 +5,8 @@ * rootKey: number, * loopStart: number, * loopEnd: number, + * end: number, + * loopingMode: 0|1|2, * }} WorkletSample * * @typedef {{ @@ -19,6 +21,7 @@ * startTime: number, * midiNote: number, * releaseStartTime: number, + * targetKey: number, * }} WorkletVoice */ @@ -106,7 +109,9 @@ export class WorkletChannel { this.preset = defaultPreset; this.bank = this.preset.bank; this.channelVolume = 1; - this.channelExpression = 1 + this.channelExpression = 1; + + this.channelTuningSemitones = 0; /** * @type {number[]} @@ -260,7 +265,8 @@ export class WorkletChannel { workletVoices = cached; workletVoices.forEach(v => { v.startTime = this.ctx.currentTime; - }) + this.actualVoices.push(midiNote); + }); } else { @@ -279,6 +285,26 @@ export class WorkletChannel { }); } + // create the generator list + const generators = new Int16Array(60); + // apply and sum the gens + for (let i = 0; i < 60; i++) { + generators[i] = addAndClampGenerator(i, sampleAndGenerators.presetGenerators, sampleAndGenerators.instrumentGenerators); + } + + // key override + let rootKey = sampleAndGenerators.sample.samplePitch; + if(generators[generatorTypes.overridingRootKey] > -1) + { + rootKey = generators[generatorTypes.overridingRootKey]; + } + + let targetKey = midiNote; + if(generators[generatorTypes.keyNum] > -1) + { + targetKey = generators[generatorTypes.keyNum]; + } + /** * create the worklet sample * @type {WorkletSample} @@ -286,17 +312,19 @@ export class WorkletChannel { const workletSample = { sampleID: sampleAndGenerators.sampleID, playbackStep: (sampleAndGenerators.sample.sampleRate / this.ctx.sampleRate) * Math.pow(2, sampleAndGenerators.sample.samplePitchCorrection / 1200),// cent tuning - cursor: 0, - rootKey: sampleAndGenerators.sample.samplePitch, - loopStart: sampleAndGenerators.sample.sampleLoopStartIndex / 2, - loopEnd: sampleAndGenerators.sample.sampleLoopEndIndex / 2 + cursor: generators[generatorTypes.startAddrsOffset] + (generators[generatorTypes.startAddrsCoarseOffset] * 32768), + rootKey: rootKey, + loopStart: (sampleAndGenerators.sample.sampleLoopStartIndex / 2) + (generators[generatorTypes.startloopAddrsOffset] + (generators[generatorTypes.startloopAddrsCoarseOffset] * 32768)), + loopEnd: (sampleAndGenerators.sample.sampleLoopEndIndex / 2) + (generators[generatorTypes.endloopAddrsOffset] + (generators[generatorTypes.endloopAddrsCoarseOffset] * 32768)), + end: sampleAndGenerators.sample.sampleLength / 2 + 1 + (generators[generatorTypes.endAddrOffset] + (generators[generatorTypes.endAddrsCoarseOffset] * 32768)), + loopingMode: generators[generatorTypes.sampleModes] }; - // create the generator list - const generators = new Int16Array(60); - // apply and sum the gens - for (let i = 0; i < 60; i++) { - generators[i] = addAndClampGenerator(i, sampleAndGenerators.presetGenerators, sampleAndGenerators.instrumentGenerators); + + // velocity override + if(generators[generatorTypes.velocity] > -1) + { + velocity = generators[generatorTypes.velocity]; } /** @@ -334,10 +362,14 @@ export class WorkletChannel { midiNote: midiNote, startTime: this.ctx.currentTime, isInRelease: false, - releaseStartTime: 0 + releaseStartTime: 0, + targetKey: targetKey }; }); + + // cache the voice + this.cachedWorkletVoices[midiNote][velocity] = workletVoices; } if(debug) @@ -345,9 +377,6 @@ export class WorkletChannel { console.log(workletVoices) } - // cache the voice - this.cachedWorkletVoices[midiNote][velocity] = workletVoices; - this.post({ messageType: workletMessageType.noteOn, messageData: workletVoices diff --git a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/lfo.js b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/lfo.js index 401aae94..d9101c50 100644 --- a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/lfo.js +++ b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/lfo.js @@ -12,5 +12,5 @@ export function getLFOValue(startTime, frequency, currentTime) { const xVal = (currentTime - startTime) / (1 / frequency); // triangle, not sine - return Math.abs(xVal - ~~(xVal + 0.5)) * 4 - 1; + return Math.abs(xVal - (~~(xVal + 0.5))) * 4 - 1; } diff --git a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/volume_envelope.js b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/volume_envelope.js index 82a4279d..b740a95d 100644 --- a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/volume_envelope.js +++ b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/volume_envelope.js @@ -63,14 +63,14 @@ export function getVolumeEnvelopeValue(delay, attack, peak, hold, sustain, decay // exponential const gain = releaseExpoLookupTable[Math.trunc(((currentTime - holdEnd) / decay) * 1000)] * (peak - sustain) + sustain if (gain < MIN_AUDIBLE_GAIN) { - return -1; + return -0.001; } return gain; } // sustain else { if (sustain < MIN_AUDIBLE_GAIN) { - return -1; + return -0.001; } return sustain; } diff --git a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/wavetable_oscillator.js b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/wavetable_oscillator.js index 80aba36c..143a6f10 100644 --- a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/wavetable_oscillator.js +++ b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/wavetable_oscillator.js @@ -2,42 +2,37 @@ * @param voice {WorkletVoice} * @param sampleData {Float32Array} * @param playbackRate {number} - * @param startI {number} index - * @param endI {number} index - * @param loop {boolean} - * @param loopStart {number} index - * @param loopEnd {number} index * @returns {number} */ -export function getOscillatorValue(voice, sampleData, playbackRate, startI, endI, loop, loopStart, loopEnd) +export function getOscillatorValue(voice, sampleData, playbackRate) { - const cur = voice.sample.cursor + startI; + const cur = voice.sample.cursor; // linear interpolation - const ceiling = Math.ceil(cur); const floor = ~~cur; + const ceil = floor + 1; const fraction = cur - floor; // grab the samples and interpolate - const upper = sampleData[ceiling]; + const upper = sampleData[ceil]; const lower = sampleData[floor]; // advance the sample voice.sample.cursor += voice.sample.playbackStep * playbackRate; - if(!loop) + if((voice.sample.loopingMode === 1) || (voice.sample.loopingMode === 3 && !voice.isInRelease)) { - // if not looping, flag the voice as finished - if(ceiling >= endI) - { - voice.finished = true; - return 0; + if (voice.sample.cursor > voice.sample.loopEnd) { + voice.sample.cursor -= voice.sample.loopEnd - voice.sample.loopStart; } } else { - while (voice.sample.cursor > loopEnd) { - voice.sample.cursor -= loopEnd - loopStart; + // if not looping, flag the voice as finished + if(ceil >= voice.sample.end) + { + voice.finished = true; + return 0; } } diff --git a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/worklet_modulator.js b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/worklet_modulator.js index 3979444f..364db65a 100644 --- a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/worklet_modulator.js +++ b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_utilities/worklet_modulator.js @@ -95,7 +95,7 @@ export function computeWorkletModulator(controllerTable, modulator, midiNote, ve // compute the modulator - const computedValue = Math.floor(sourceValue * secondSrcValue * modulator.transformAmount); + const computedValue = sourceValue * secondSrcValue * modulator.transformAmount; if(modulator.transformType === 2) { @@ -111,15 +111,22 @@ export function computeWorkletModulator(controllerTable, modulator, midiNote, ve * @param controllerTable {Int16Array} * @returns {number} the computed number */ -export function getModulated(voice, generatorType, controllerTable) -{ - const genVal = voice.generators[generatorType]; - if(!voice.modulators[generatorType].length) - { +export function getModulated(voice, generatorType, controllerTable) { + const modLen = voice.modulators[generatorType].length; + if (modLen < 1) { // if no mods, just return gen - return genVal; + return voice.generators[generatorType]; + } + else if(modLen === 1) + { + return voice.generators[generatorType] + computeWorkletModulator(controllerTable, voice.modulators[generatorType][0], voice.midiNote, voice.velocity) + } + else { + // if mods, sum them + let sum = voice.generators[generatorType]; + for (let i = 0; i < modLen; i++) { + sum += computeWorkletModulator(controllerTable, voice.modulators[generatorType][i], voice.midiNote, voice.velocity); + } + return sum; } - // if mods, sum them - return genVal + voice.modulators[generatorType] - .reduce((value, mod) => value + computeWorkletModulator(controllerTable, mod, voice.midiNote, voice.velocity), 0); } \ No newline at end of file diff --git a/src/website/ui/synthetizer_ui.js b/src/website/ui/synthetizer_ui.js index 94712b1c..6ca57c0b 100644 --- a/src/website/ui/synthetizer_ui.js +++ b/src/website/ui/synthetizer_ui.js @@ -212,7 +212,7 @@ export class SynthetizerUI const voiceMeter = new Meter(this.channelColors[channelNumber], "Voices: ", 0, - 50); + 100); voiceMeter.bar.classList.add("voice_meter_bar_smooth"); controller.appendChild(voiceMeter.div);