From d100f85093ded28d7d3f81741eedd91e709f0ea6 Mon Sep 17 00:00:00 2001 From: spessasus <95608008+spessasus@users.noreply.github.com> Date: Thu, 7 Sep 2023 00:24:00 +0200 Subject: [PATCH] The Worklet Update part 2.2 I have fixed calliope lead --- .../soundfont/chunk/generators.js | 12 +- .../soundfont/chunk/modulators.js | 3 +- .../soundfont/chunk/samples.js | 27 ++-- .../worklet_channel/channel_processor.js | 68 ++++----- .../worklet_channel/worklet_channel.js | 47 ++----- .../worklet_utilities/wavetable_oscillator.js | 5 +- .../worklet_utilities/worklet_modulator.js | 32 ++--- src/website/manager.js | 133 ++++++++++-------- 8 files changed, 161 insertions(+), 166 deletions(-) diff --git a/src/spessasynth_lib/soundfont/chunk/generators.js b/src/spessasynth_lib/soundfont/chunk/generators.js index ebf1eeac..e6656622 100644 --- a/src/spessasynth_lib/soundfont/chunk/generators.js +++ b/src/spessasynth_lib/soundfont/chunk/generators.js @@ -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 @@ -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}; @@ -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}; diff --git a/src/spessasynth_lib/soundfont/chunk/modulators.js b/src/spessasynth_lib/soundfont/chunk/modulators.js index b282f3a0..6e221184 100644 --- a/src/spessasynth_lib/soundfont/chunk/modulators.js +++ b/src/spessasynth_lib/soundfont/chunk/modulators.js @@ -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, @@ -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 diff --git a/src/spessasynth_lib/soundfont/chunk/samples.js b/src/spessasynth_lib/soundfont/chunk/samples.js index 1b280960..49d66685 100644 --- a/src/spessasynth_lib/soundfont/chunk/samples.js +++ b/src/spessasynth_lib/soundfont/chunk/samples.js @@ -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); @@ -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; } diff --git a/src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js b/src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js index c51a16aa..18a3083d 100644 --- a/src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js +++ b/src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js @@ -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, @@ -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; @@ -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 = []; @@ -96,7 +96,7 @@ class ChannelProcessor extends AudioWorkletProcessor { this.channelVibrato = data; break; - case workletMessageType.clearSamples: + case workletMessageType.clearCache: this.samples = []; } } @@ -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) { @@ -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); @@ -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); @@ -291,4 +295,4 @@ class ChannelProcessor extends AudioWorkletProcessor { registerProcessor("worklet-channel-processor", ChannelProcessor); -console.log("Processor succesfully registered!"); \ No newline at end of file +console.log("%cProcessor succesfully registered!", consoleColors.recognized); \ No newline at end of file diff --git a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js index 9ed3f44a..1a8370fa 100644 --- a/src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js +++ b/src/spessasynth_lib/synthetizer/worklet_channel/worklet_channel.js @@ -10,8 +10,7 @@ * @typedef {{ * sample: WorkletSample, * generators: Int16Array, - * modulators: WorkletModulator[], - * modulatorResults: Int16Array + * modulators: WorkletModulator[][], * finished: boolean, * isInRelease: boolean, * velocity: number, @@ -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; @@ -51,7 +50,7 @@ export const workletMessageType = { killNote: 4, ccReset: 5, setChannelVibrato: 6, - clearSamples: 7 + clearCache: 7, }; /** @@ -73,7 +72,7 @@ export const workletMessageType = { * 4 - note off instantly -> midiNote * 5 - controllers reset * 6 - channel vibrato -> {frequencyHz: number, depthCents: number, delaySeconds: number} - * 7 - clear samples + * 7 - clear cached samples */ @@ -234,7 +233,6 @@ export class WorkletChannel { } } - /** * @param midiNote {number} 0-127 * @param velocity {number} 0-127 @@ -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, @@ -317,7 +319,7 @@ export class WorkletChannel { secondarySrcIndex: mod.secSrcIndex, secondarySrcUsesCC: mod.secSrcUsesCC, secondarySrcTransformed: mod.secondarySrcTransformed - } + }); }); this.actualVoices.push(midiNote); @@ -325,7 +327,6 @@ export class WorkletChannel { generators: generators, sample: workletSample, modulators: modulators, - modulatorResults: new Int16Array(60), finished: false, velocity: velocity, currentGain: 0, @@ -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(); @@ -640,7 +622,8 @@ export class WorkletChannel { resetSamples() { this.post({ - messageType: workletMessageType.clearSamples + messageType: workletMessageType.clearCache, + messageData: undefined }); this.dumpedSamples.clear(); this.cachedWorkletVoices = []; 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 714c4e2e..80aba36c 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 @@ -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 } } 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 40a9caa7..3979444f 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 @@ -1,12 +1,10 @@ import { NON_CC_INDEX_OFFSET } from '../worklet_channel.js' import { modulatorSources } from '../../../soundfont/chunk/modulators.js' -import { generatorLimits } from '../../../soundfont/chunk/generators.js' /** * @typedef {{ * transformAmount: number, * transformType: 0|2, - * destination: number, * * sourceTransformed: Float32Array * sourceIndex: number, @@ -107,29 +105,21 @@ export function computeWorkletModulator(controllerTable, modulator, midiNote, ve return computedValue; } -const emptyArr = new Int16Array(60); - -/** - * Here's how it works: - * we compute all the modulators and put their results into an array filled with zeros - * and if we want to get a generator, we sum the generators + modulatorResults - * @param voice {WorkletVoice} - * @param controllerTable {Int16Array} - */ -export function computeModulators(voice, controllerTable) -{ - voice.modulatorResults.set(emptyArr); - voice.modulators.forEach(mod => { - voice.modulatorResults[mod.destination] += computeWorkletModulator(controllerTable, mod, voice.midiNote, voice.velocity); - }); -} - /** * @param voice {WorkletVoice} * @param generatorType {number} + * @param controllerTable {Int16Array} * @returns {number} the computed number */ -export function getModulated(voice, generatorType) +export function getModulated(voice, generatorType, controllerTable) { - return voice.generators[generatorType] + voice.modulatorResults[generatorType]; + const genVal = voice.generators[generatorType]; + if(!voice.modulators[generatorType].length) + { + // if no mods, just return gen + return genVal; + } + // 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/manager.js b/src/website/manager.js index 6f48a0aa..1cc6ebd4 100644 --- a/src/website/manager.js +++ b/src/website/manager.js @@ -10,8 +10,7 @@ import {SynthetizerUI} from "./ui/synthetizer_ui.js"; import { MIDIDeviceHandler } from '../spessasynth_lib/midi_handler/midi_handler.js' import { WebMidiLinkHandler } from '../spessasynth_lib/midi_handler/web_midi_link.js' -export class Manager -{ +export class Manager { channelColors = [ 'rgba(255, 99, 71, 1)', // tomato 'rgba(255, 165, 0, 1)', // orange @@ -22,7 +21,7 @@ export class Manager 'rgba(0, 191, 255, 1)', // deepskyblue 'rgba(65, 105, 225, 1)', // royalblue 'rgba(138, 43, 226, 1)', // blueviolet - 'rgba(50, 120, 125, 1)', //'rgba(218, 112, 214, 1)', // percission color + 'rgba(50, 120, 125, 1)', //'rgba(218, 112, 214, 1)', // percission color 'rgba(255, 0, 255, 1)', // magenta 'rgba(255, 20, 147, 1)', // deeppink 'rgba(218, 112, 214, 1)', // orchid @@ -37,73 +36,83 @@ export class Manager * @param soundFont {SoundFont2} */ constructor(context, soundFont) { - //context.audioWorklet.addModule("/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js").then(() => { - // set up soundfont - this.soundFont = soundFont; + this.context = context; + this.initializeContext(context, soundFont); - // set up synthetizer - this.synth = new Synthetizer(context.destination, this.soundFont); + } + + async initializeContext(context, soundFont) { + try { + await context.audioWorklet.addModule("/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js"); + } + catch (e) { + await context.audioWorklet.addModule("src/spessasynth_lib/synthetizer/worklet_channel/channel_processor.js"); + } + // set up soundfont + this.soundFont = soundFont; - // set up midi access - this.midHandler = new MIDIDeviceHandler(); + // set up synthetizer + this.synth = new Synthetizer(context.destination, this.soundFont); - // set up web midi link - this.wml = new WebMidiLinkHandler(this.synth); + // set up midi access + this.midHandler = new MIDIDeviceHandler(); - // set up keyboard - this.keyboard = new MidiKeyboard(this.channelColors, this.synth, this.midHandler); + // set up web midi link + this.wml = new WebMidiLinkHandler(this.synth); - // set up renderer - const canvas = document.getElementById("note_canvas"); + // set up keyboard + this.keyboard = new MidiKeyboard(this.channelColors, this.synth, this.midHandler); + // set up renderer + const canvas = document.getElementById("note_canvas"); + + canvas.width = window.innerWidth * window.devicePixelRatio; + canvas.height = window.innerHeight * window.devicePixelRatio; + + window.addEventListener("resize", () => { canvas.width = window.innerWidth * window.devicePixelRatio; canvas.height = window.innerHeight * window.devicePixelRatio; - - window.addEventListener("resize", () => { - canvas.width = window.innerWidth * window.devicePixelRatio; - canvas.height = window.innerHeight * window.devicePixelRatio; - }); - - this.renderer = new Renderer(this.channelColors, this.synth, canvas); - this.renderer.render(true); - - // connect the synth to keyboard - this.synth.onNoteOn = (note, chan, vel, vol, exp) => this.keyboard.pressNote(note, chan, vel, vol, exp); - this.synth.onNoteOff = note => this.keyboard.releaseNote(note); - - // set up synth UI - this.synthUI = new SynthetizerUI(this.channelColors); - this.synthUI.connectSynth(this.synth); - - // create an UI for sequencer - this.seqUI = new SequencerUI(); - - document.addEventListener("keypress", e => { - switch (e.key.toLowerCase()) { - case "c": - e.preventDefault(); - const response = window.prompt("Cinematic mode activated!\n Paste the link to the image for canvas (leave blank to disable)", ""); - - if (response === null) { - return; - } - canvas.style.background = `linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), center center / cover url("${response}")`; - document.getElementsByClassName("top_part")[0].style.display = "none"; - document.getElementsByClassName("bottom_part")[0].style.display = "none"; - document.body.requestFullscreen().then(); - break; - - case "n": - // secret - for (let i = 0; i < 16; i++) { - this.synth.midiChannels[i].lockPreset = false; - this.synth.programChange(i, (this.synth.midiChannels[i].preset.program + 1) % 127); - this.synth.midiChannels[i].lockPreset = true; - } - break; - } - }); - //}); + }); + + this.renderer = new Renderer(this.channelColors, this.synth, canvas); + this.renderer.render(true); + + // connect the synth to keyboard + this.synth.onNoteOn = (note, chan, vel, vol, exp) => this.keyboard.pressNote(note, chan, vel, vol, exp); + this.synth.onNoteOff = note => this.keyboard.releaseNote(note); + + // set up synth UI + this.synthUI = new SynthetizerUI(this.channelColors); + this.synthUI.connectSynth(this.synth); + + // create an UI for sequencer + this.seqUI = new SequencerUI(); + + document.addEventListener("keypress", e => { + switch (e.key.toLowerCase()) { + case "c": + e.preventDefault(); + const response = window.prompt("Cinematic mode activated!\n Paste the link to the image for canvas (leave blank to disable)", ""); + + if (response === null) { + return; + } + canvas.style.background = `linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)), center center / cover url("${response}")`; + document.getElementsByClassName("top_part")[0].style.display = "none"; + document.getElementsByClassName("bottom_part")[0].style.display = "none"; + document.body.requestFullscreen().then(); + break; + + case "n": + // secret + for (let i = 0; i < 16; i++) { + this.synth.midiChannels[i].lockPreset = false; + this.synth.programChange(i, (this.synth.midiChannels[i].preset.program + 1) % 127); + this.synth.midiChannels[i].lockPreset = true; + } + break; + } + }); } /**