Skip to content

Commit

Permalink
The Worklet Update part 2.2
Browse files Browse the repository at this point in the history
I have fixed calliope lead
  • Loading branch information
spessasus committed Sep 6, 2023
1 parent c06bbdf commit d100f85
Show file tree
Hide file tree
Showing 8 changed files with 161 additions and 166 deletions.
12 changes: 6 additions & 6 deletions src/spessasynth_lib/soundfont/chunk/generators.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ export const generatorTypes = {
export const generatorLimits = [];
// offsets
generatorLimits[generatorTypes.startAddrsOffset] = {min: 0, max: 32768, def: 0};
generatorLimits[generatorTypes.endAddrOffset] = {min: 0, max: 32768, def: 0};
generatorLimits[generatorTypes.startloopAddrsOffset] = {min: 0, max: 32768, def: 0};
generatorLimits[generatorTypes.endloopAddrsOffset] = {min: 0, max: 32768, def: 0};
generatorLimits[generatorTypes.endAddrOffset] = {min: -32768, max: 32768, def: 0};
generatorLimits[generatorTypes.startloopAddrsOffset] = {min: -32768, max: 32768, def: 0};
generatorLimits[generatorTypes.endloopAddrsOffset] = {min: -32768, max: 32768, def: 0};
generatorLimits[generatorTypes.startAddrsCoarseOffset] = {min: 0, max: 32768, def: 0};

// pitch influence
Expand All @@ -88,7 +88,7 @@ generatorLimits[generatorTypes.initialFilterQ] = {min: 0, max: 960, def: 0};
generatorLimits[generatorTypes.modLfoToFilterFc] = {min: -12000, max: 12000, def: 0};
generatorLimits[generatorTypes.modEnvToFilterFc] = {min: -12000, max: 12000, def: 0};

generatorLimits[generatorTypes.endAddrsCoarseOffset] = {min: 0, max: 32768, def: 0};
generatorLimits[generatorTypes.endAddrsCoarseOffset] = {min: -32768, max: 32768, def: 0};

generatorLimits[generatorTypes.modLfoToVolume] = {min: -960, max: 960, def: 0};

Expand Down Expand Up @@ -125,13 +125,13 @@ generatorLimits[generatorTypes.releaseVolEnv] = {min: -7200, max: 8000, def: -72
generatorLimits[generatorTypes.keyNumToVolEnvHold] = {min: -1200, max: 1200, def: 0};
generatorLimits[generatorTypes.keyNumToVolEnvDecay] = {min: -1200, max: 1200, def: 0};

generatorLimits[generatorTypes.startloopAddrsCoarseOffset] = {min: 0, max: 32768, def: 0};
generatorLimits[generatorTypes.startloopAddrsCoarseOffset] = {min: -32768, max: 32768, def: 0};
generatorLimits[generatorTypes.keyNum] = {min: -1, max: 127, def: -1};
generatorLimits[generatorTypes.velocity] = {min: -1, max: 127, def: -1};

generatorLimits[generatorTypes.initialAttenuation] = {min: -250, max: 1440, def: 0}; // soundblaster allows 10dB of gain

generatorLimits[generatorTypes.endloopAddrsCoarseOffset] = {min: 0, max: 32768, def: 0};
generatorLimits[generatorTypes.endloopAddrsCoarseOffset] = {min: -32768, max: 32768, def: 0};

generatorLimits[generatorTypes.coarseTune] = {min: -120, max: 120, def: 0};
generatorLimits[generatorTypes.fineTune] = {min: -99, max: 99, def: 0};
Expand Down
3 changes: 2 additions & 1 deletion src/spessasynth_lib/soundfont/chunk/modulators.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MOD_PRECOMPUTED_LENGTH,
} from '../../synthetizer/worklet_channel/worklet_utilities/modulator_curves.js'
import { generatorTypes } from './generators.js'
import { consoleColors } from '../../utils/other.js'

export const modulatorSources = {
noController: 0,
Expand Down Expand Up @@ -113,7 +114,7 @@ export const defaultModulators = [
new Modulator({srcEnum: 0x058B, dest: generatorTypes.initialAttenuation, amt: 1440, secSrcEnum: 0x0, transform: 0}) // expression to attenuation
]

console.log(defaultModulators)
console.log("%cDefault Modulators:", consoleColors.recognized, defaultModulators)

/**
* Reads the modulator chunk
Expand Down
27 changes: 19 additions & 8 deletions src/spessasynth_lib/soundfont/chunk/samples.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ export class Sample {
const outputLength = Math.round(audioData.length / lengthRatio);
const outputData = new Float32Array(outputLength);


for (let i = 0; i < outputLength; i++) {
const index = i * lengthRatio;
const indexPrev = Math.floor(index);
Expand Down Expand Up @@ -253,16 +254,26 @@ export class Sample {
}
// read the sample data
let audioData = new Float32Array(this.sampleLength / 2 + 1);
const dataStartIndex = smplArr.currentIndex
const dataStartIndex = smplArr.currentIndex;

if((this.sampleType & 0x10) > 0)
{
const buff = smplArr.slice(this.sampleStartIndex, this.sampleEndIndex + 1).buffer;
this.b = buff
console.log("COMP", this.b);
audioData = new AudioContext().decodeAudioData(buff);
}

for (let i = this.sampleStartIndex; i < this.sampleEndIndex; i += 2) {
// convert 2 uint8 bytes to singed int16
let val = (smplArr[dataStartIndex + i + 1] << 8) | smplArr[dataStartIndex + i];
if (val > 32767) {
val -= 65536
}
else {
for (let i = this.sampleStartIndex; i < this.sampleEndIndex; i += 2) {
// convert 2 uint8 bytes to singed int16
let val = (smplArr[dataStartIndex + i + 1] << 8) | smplArr[dataStartIndex + i];
if (val > 32767) {
val -= 65536
}

audioData[(i - this.sampleStartIndex) / 2] = val / 32768;
audioData[(i - this.sampleStartIndex) / 2] = val / 32768;
}
}
return audioData;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@ import { midiControllers } from '../../midi_parser/midi_message.js';
import { generatorTypes } from '../../soundfont/chunk/generators.js';
import { getOscillatorValue } from './worklet_utilities/wavetable_oscillator.js';
import { modulatorSources } from '../../soundfont/chunk/modulators.js';
import { computeModulators, getModulated } from './worklet_utilities/worklet_modulator.js'
import { getModulated } from './worklet_utilities/worklet_modulator.js'
import {
getVolEnvReleaseMultiplier,
getVolumeEnvelopeValue,
volumeEnvelopePhases,
} from './worklet_utilities/volume_envelope.js'
import {
absCentsToHz,
Expand All @@ -16,6 +15,7 @@ import {
timecentsToSeconds,
} from './worklet_utilities/unit_converter.js'
import { getLFOValue } from './worklet_utilities/lfo.js';
import { consoleColors } from '../../utils/other.js'

export const MIN_AUDIBLE_GAIN = 0.0001;

Expand All @@ -40,7 +40,7 @@ class ChannelProcessor extends AudioWorkletProcessor {
this.channelVibrato = {rate: 0, depth: 0, delay: 0};

/**
* grouped by midi note
* contains all the voices currently playing
* @type {WorkletVoice[]}
*/
this.voices = [];
Expand Down Expand Up @@ -96,7 +96,7 @@ class ChannelProcessor extends AudioWorkletProcessor {
this.channelVibrato = data;
break;

case workletMessageType.clearSamples:
case workletMessageType.clearCache:
this.samples = [];
}
}
Expand Down Expand Up @@ -136,30 +136,29 @@ class ChannelProcessor extends AudioWorkletProcessor {
}


// MODULATORS
computeModulators(voice, this.midiControllers);
// MODULATORS are computed in getModulated if needed.

// TUNING
// get the root key
let key = voice.sample.rootKey;
const overrideKey = getModulated(voice, generatorTypes.overridingRootKey);
const overrideKey = getModulated(voice, generatorTypes.overridingRootKey, this.midiControllers);
if(overrideKey !== -1)
{
key = overrideKey;
}
// calculate tuning
let cents = getModulated(voice, generatorTypes.fineTune);
let semitones = getModulated(voice, generatorTypes.coarseTune) + parseFloat(this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.channelTuning] >> 7);
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);
cents += (voice.midiNote - key) * getModulated(voice, generatorTypes.scaleTuning, this.midiControllers);

// vibrato LFO
const vibratoDepth = getModulated(voice, generatorTypes.vibLfoToPitch);
const vibratoDepth = getModulated(voice, generatorTypes.vibLfoToPitch, this.midiControllers);
if(vibratoDepth > 0)
{
const vibStart = voice.startTime + timecentsToSeconds(getModulated(voice, generatorTypes.delayVibLFO));
const vibFreqHz = absCentsToHz(getModulated(voice, generatorTypes.freqVibLFO));
const vibStart = voice.startTime + timecentsToSeconds(getModulated(voice, generatorTypes.delayVibLFO, this.midiControllers));
const vibFreqHz = absCentsToHz(getModulated(voice, generatorTypes.freqVibLFO, this.midiControllers));
const lfoVal = getLFOValue(vibStart, vibFreqHz, currentTime);
if(lfoVal)
{
Expand All @@ -168,13 +167,13 @@ class ChannelProcessor extends AudioWorkletProcessor {
}

// mod LFO
const modPitchDepth = getModulated(voice, generatorTypes.modLfoToPitch);
const modVolDepth = getModulated(voice, generatorTypes.modLfoToVolume);
const modPitchDepth = getModulated(voice, generatorTypes.modLfoToPitch, this.midiControllers);
const modVolDepth = getModulated(voice, generatorTypes.modLfoToVolume, this.midiControllers);
let modLfoCentibels = 0;
if(modPitchDepth + modVolDepth > 0)
{
const modStart = voice.startTime + timecentsToSeconds(getModulated(voice, generatorTypes.delayModLFO));
const modFreqHz = absCentsToHz(getModulated(voice, generatorTypes.freqModLFO));
const modStart = voice.startTime + timecentsToSeconds(getModulated(voice, generatorTypes.delayModLFO, this.midiControllers));
const modFreqHz = absCentsToHz(getModulated(voice, generatorTypes.freqModLFO, this.midiControllers));
const modLfo = getLFOValue(modStart, modFreqHz, currentTime);
if(modLfo) {
cents += (modLfo * modPitchDepth);
Expand All @@ -193,34 +192,39 @@ class ChannelProcessor extends AudioWorkletProcessor {
}

// finally calculate the playback rate
const playbackRate = Math.pow(2,(cents + semitones * 100) / 1200);
const playbackRate = Math.pow(2,(cents / 100 + semitones) / 12);

// VOLUME ENVELOPE
let attenuation, sustain, delay, attack, hold, decay, release;
attenuation = decibelAttenuationToGain((getModulated(voice, generatorTypes.initialAttenuation) / 25) + modLfoCentibels);
attenuation = decibelAttenuationToGain((getModulated(voice, generatorTypes.initialAttenuation, this.midiControllers) / 25) + modLfoCentibels);
if(voice.isInRelease)
{
release = timecentsToSeconds(getModulated(voice, generatorTypes.releaseVolEnv));
release = timecentsToSeconds(getModulated(voice, generatorTypes.releaseVolEnv, this.midiControllers));
}
else {
sustain = attenuation * decibelAttenuationToGain(getModulated(voice, generatorTypes.sustainVolEnv) / 10);
delay = timecentsToSeconds(getModulated(voice, generatorTypes.delayVolEnv));
attack = timecentsToSeconds(getModulated(voice, generatorTypes.attackVolEnv));
hold = timecentsToSeconds(getModulated(voice, generatorTypes.holdVolEnv) + ((60 - voice.midiNote) * getModulated(voice, generatorTypes.keyNumToVolEnvHold)));
decay = timecentsToSeconds(getModulated(voice, generatorTypes.decayVolEnv) + ((60 - voice.midiNote) * getModulated(voice, generatorTypes.keyNumToVolEnvDecay)));
sustain = attenuation * decibelAttenuationToGain(getModulated(voice, generatorTypes.sustainVolEnv, this.midiControllers) / 10);
delay = timecentsToSeconds(getModulated(voice, generatorTypes.delayVolEnv, this.midiControllers));
attack = timecentsToSeconds(getModulated(voice, generatorTypes.attackVolEnv, this.midiControllers));
hold = timecentsToSeconds(getModulated(voice, generatorTypes.holdVolEnv, this.midiControllers) + ((60 - voice.midiNote) * getModulated(voice, generatorTypes.keyNumToVolEnvHold, this.midiControllers)));
decay = timecentsToSeconds(getModulated(voice, generatorTypes.decayVolEnv, this.midiControllers) + ((60 - voice.midiNote) * getModulated(voice, generatorTypes.keyNumToVolEnvDecay, this.midiControllers)));
}

// WAVETABLE OSCILLATOR
// get offsets
const loopStart = voice.sample.loopStart + getModulated(voice, generatorTypes.startloopAddrsOffset) + (getModulated(voice, generatorTypes.startloopAddrsCoarseOffset) * 32768);
const loopEnd = voice.sample.loopEnd + getModulated(voice, generatorTypes.endloopAddrsOffset) + (getModulated(voice, generatorTypes.endloopAddrsCoarseOffset) * 32768);
const startOffset = getModulated(voice, generatorTypes.startAddrsOffset) + (getModulated(voice, generatorTypes.startAddrsCoarseOffset) * 32768);
const endIndex = getModulated(voice, generatorTypes.endAddrOffset) + (getModulated(voice, generatorTypes.endAddrsCoarseOffset) * 32768) + this.samples[voice.sample.sampleID].length;
const mode = getModulated(voice, generatorTypes.sampleModes);
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) )) + 500) / 1000) ; // 0 to 1
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);
const panRight = Math.sin(HALF_PI * pan);

Expand Down Expand Up @@ -291,4 +295,4 @@ class ChannelProcessor extends AudioWorkletProcessor {


registerProcessor("worklet-channel-processor", ChannelProcessor);
console.log("Processor succesfully registered!");
console.log("%cProcessor succesfully registered!", consoleColors.recognized);
47 changes: 15 additions & 32 deletions src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
* @typedef {{
* sample: WorkletSample,
* generators: Int16Array,
* modulators: WorkletModulator[],
* modulatorResults: Int16Array
* modulators: WorkletModulator[][],
* finished: boolean,
* isInRelease: boolean,
* velocity: number,
Expand All @@ -26,8 +25,8 @@
import { Preset } from '../../soundfont/chunk/presets.js'
import { consoleColors } from '../../utils/other.js'
import { modulatorSources } from '../../soundfont/chunk/modulators.js'
import { addAndClampGenerator, generatorTypes } from '../../soundfont/chunk/generators.js'
import { midiControllers } from '../../midi_parser/midi_message.js'
import { addAndClampGenerator, generatorTypes } from '../../soundfont/chunk/generators.js'

const CHANNEL_GAIN = 0.5;

Expand All @@ -51,7 +50,7 @@ export const workletMessageType = {
killNote: 4,
ccReset: 5,
setChannelVibrato: 6,
clearSamples: 7
clearCache: 7,
};

/**
Expand All @@ -73,7 +72,7 @@ export const workletMessageType = {
* 4 - note off instantly -> midiNote<number>
* 5 - controllers reset
* 6 - channel vibrato -> {frequencyHz: number, depthCents: number, delaySeconds: number}
* 7 - clear samples
* 7 - clear cached samples
*/


Expand Down Expand Up @@ -234,7 +233,6 @@ export class WorkletChannel {
}
}


/**
* @param midiNote {number} 0-127
* @param velocity {number} 0-127
Expand Down Expand Up @@ -302,12 +300,16 @@ export class WorkletChannel {
}

/**
* @type {WorkletModulator[]}
* grouped by destination
* @type {WorkletModulator[][]}
*/
const modulators = sampleAndGenerators.modulators.map(mod => {
return {
const modulators = []
for (let i = 0; i < 60; i++) {
modulators.push([]);
}
sampleAndGenerators.modulators.forEach(mod => {
modulators[mod.modulatorDestination].push({
transformAmount: mod.modulationAmount,
destination: mod.modulatorDestination,
transformType: mod.transformType,

sourceIndex: mod.sourceIndex,
Expand All @@ -317,15 +319,14 @@ export class WorkletChannel {
secondarySrcIndex: mod.secSrcIndex,
secondarySrcUsesCC: mod.secSrcUsesCC,
secondarySrcTransformed: mod.secondarySrcTransformed
}
});
});

this.actualVoices.push(midiNote);
return {
generators: generators,
sample: workletSample,
modulators: modulators,
modulatorResults: new Int16Array(60),
finished: false,
velocity: velocity,
currentGain: 0,
Expand Down Expand Up @@ -591,27 +592,8 @@ export class WorkletChannel {

resetControllers()
{
// Create an Int16Array with 127 elements
this.midiControllers.forEach((v, i) => {
this.midiControllers[i] = 0;
})
this.midiControllers[midiControllers.mainVolume] = 100 << 7;
this.midiControllers[midiControllers.expressionController] = 127 << 7;
this.midiControllers[midiControllers.pan] = 64 << 7;

this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel] = 8192;
this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] = 2 << 7;
this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.channelPressure] = 127 << 7;
this.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.channelTuning] = 0;

this.channelPitchBendRange = 2;
this.holdPedal = false;

// this.post({
// messageType: workletMessageType.ccReset,
// messageData: 0
// })

this.vibrato = {depth: 0, rate: 0, delay: 0};

this.resetParameters();
Expand Down Expand Up @@ -640,7 +622,8 @@ export class WorkletChannel {
resetSamples()
{
this.post({
messageType: workletMessageType.clearSamples
messageType: workletMessageType.clearCache,
messageData: undefined
});
this.dumpedSamples.clear();
this.cachedWorkletVoices = [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,8 @@ export function getOscillatorValue(voice, sampleData, playbackRate, startI, endI
}
else
{
if(voice.sample.cursor >= loopEnd)
{
// FIXME: CALLIOPE LEAD
while (voice.sample.cursor > loopEnd) {
voice.sample.cursor -= loopEnd - loopStart;
//voice.sample.cursor = (voice.sample.cursor - loopStart) % loopEnd
}
}

Expand Down
Loading

0 comments on commit d100f85

Please sign in to comment.