spessasynth_lib 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/modules.xml +8 -0
- package/.idea/spessasynth_lib.iml +12 -0
- package/.idea/vcs.xml +6 -0
- package/copy_version.sh +38 -0
- package/index.js +73 -0
- package/package/@types/externals/stbvorbis_sync/stbvorbis_sync.min.d.ts +1 -0
- package/package/@types/index.d.ts +34 -0
- package/package/@types/midi_handler/midi_handler.d.ts +39 -0
- package/package/@types/midi_handler/web_midi_link.d.ts +12 -0
- package/package/@types/midi_parser/midi_data.d.ts +95 -0
- package/package/@types/midi_parser/midi_editor.d.ts +45 -0
- package/package/@types/midi_parser/midi_loader.d.ts +100 -0
- package/package/@types/midi_parser/midi_message.d.ts +154 -0
- package/package/@types/midi_parser/midi_writer.d.ts +6 -0
- package/package/@types/midi_parser/rmidi_writer.d.ts +9 -0
- package/package/@types/midi_parser/used_keys_loaded.d.ts +7 -0
- package/package/@types/sequencer/sequencer.d.ts +180 -0
- package/package/@types/sequencer/worklet_sequencer/sequencer_message.d.ts +28 -0
- package/package/@types/soundfont/read/generators.d.ts +98 -0
- package/package/@types/soundfont/read/instruments.d.ts +50 -0
- package/package/@types/soundfont/read/modulators.d.ts +73 -0
- package/package/@types/soundfont/read/presets.d.ts +87 -0
- package/package/@types/soundfont/read/riff_chunk.d.ts +31 -0
- package/package/@types/soundfont/read/samples.d.ts +134 -0
- package/package/@types/soundfont/read/zones.d.ts +141 -0
- package/package/@types/soundfont/soundfont.d.ts +76 -0
- package/package/@types/soundfont/write/ibag.d.ts +6 -0
- package/package/@types/soundfont/write/igen.d.ts +6 -0
- package/package/@types/soundfont/write/imod.d.ts +6 -0
- package/package/@types/soundfont/write/inst.d.ts +6 -0
- package/package/@types/soundfont/write/pbag.d.ts +6 -0
- package/package/@types/soundfont/write/pgen.d.ts +6 -0
- package/package/@types/soundfont/write/phdr.d.ts +6 -0
- package/package/@types/soundfont/write/pmod.d.ts +6 -0
- package/package/@types/soundfont/write/sdta.d.ts +11 -0
- package/package/@types/soundfont/write/shdr.d.ts +8 -0
- package/package/@types/soundfont/write/soundfont_trimmer.d.ts +6 -0
- package/package/@types/soundfont/write/write.d.ts +21 -0
- package/package/@types/synthetizer/audio_effects/effects_config.d.ts +29 -0
- package/package/@types/synthetizer/audio_effects/fancy_chorus.d.ts +93 -0
- package/package/@types/synthetizer/audio_effects/reverb.d.ts +7 -0
- package/package/@types/synthetizer/synth_event_handler.d.ts +161 -0
- package/package/@types/synthetizer/synthetizer.d.ts +294 -0
- package/package/@types/synthetizer/worklet_system/message_protocol/worklet_message.d.ts +89 -0
- package/package/@types/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.d.ts +134 -0
- package/package/@types/synthetizer/worklet_url.d.ts +5 -0
- package/package/@types/utils/buffer_to_wav.d.ts +8 -0
- package/package/@types/utils/byte_functions/big_endian.d.ts +13 -0
- package/package/@types/utils/byte_functions/little_endian.d.ts +35 -0
- package/package/@types/utils/byte_functions/string.d.ts +22 -0
- package/package/@types/utils/byte_functions/variable_length_quantity.d.ts +12 -0
- package/package/@types/utils/indexed_array.d.ts +21 -0
- package/package/@types/utils/loggin.d.ts +26 -0
- package/package/@types/utils/other.d.ts +32 -0
- package/package/LICENSE +26 -0
- package/package/README.md +84 -0
- package/package/externals/NOTICE +9 -0
- package/package/externals/libvorbis/@types/OggVorbisEncoder.d.ts +34 -0
- package/package/externals/libvorbis/OggVorbisEncoder.min.js +1 -0
- package/package/externals/stbvorbis_sync/@types/stbvorbis_sync.d.ts +12 -0
- package/package/externals/stbvorbis_sync/LICENSE +202 -0
- package/package/externals/stbvorbis_sync/stbvorbis_sync.min.js +1 -0
- package/package/index.js +73 -0
- package/package/midi_handler/README.md +3 -0
- package/package/midi_handler/midi_handler.js +118 -0
- package/package/midi_handler/web_midi_link.js +41 -0
- package/package/midi_parser/README.md +3 -0
- package/package/midi_parser/midi_data.js +121 -0
- package/package/midi_parser/midi_editor.js +557 -0
- package/package/midi_parser/midi_loader.js +502 -0
- package/package/midi_parser/midi_message.js +234 -0
- package/package/midi_parser/midi_writer.js +95 -0
- package/package/midi_parser/rmidi_writer.js +271 -0
- package/package/midi_parser/used_keys_loaded.js +172 -0
- package/package/package.json +43 -0
- package/package/sequencer/README.md +23 -0
- package/package/sequencer/sequencer.js +439 -0
- package/package/sequencer/worklet_sequencer/events.js +92 -0
- package/package/sequencer/worklet_sequencer/play.js +309 -0
- package/package/sequencer/worklet_sequencer/process_event.js +167 -0
- package/package/sequencer/worklet_sequencer/process_tick.js +85 -0
- package/package/sequencer/worklet_sequencer/sequencer_message.js +39 -0
- package/package/sequencer/worklet_sequencer/song_control.js +193 -0
- package/package/sequencer/worklet_sequencer/worklet_sequencer.js +218 -0
- package/package/soundfont/README.md +8 -0
- package/package/soundfont/read/generators.js +212 -0
- package/package/soundfont/read/instruments.js +125 -0
- package/package/soundfont/read/modulators.js +249 -0
- package/package/soundfont/read/presets.js +300 -0
- package/package/soundfont/read/riff_chunk.js +81 -0
- package/package/soundfont/read/samples.js +398 -0
- package/package/soundfont/read/zones.js +310 -0
- package/package/soundfont/soundfont.js +357 -0
- package/package/soundfont/write/ibag.js +39 -0
- package/package/soundfont/write/igen.js +75 -0
- package/package/soundfont/write/imod.js +46 -0
- package/package/soundfont/write/inst.js +34 -0
- package/package/soundfont/write/pbag.js +39 -0
- package/package/soundfont/write/pgen.js +77 -0
- package/package/soundfont/write/phdr.js +42 -0
- package/package/soundfont/write/pmod.js +46 -0
- package/package/soundfont/write/sdta.js +72 -0
- package/package/soundfont/write/shdr.js +54 -0
- package/package/soundfont/write/soundfont_trimmer.js +169 -0
- package/package/soundfont/write/write.js +180 -0
- package/package/synthetizer/README.md +6 -0
- package/package/synthetizer/audio_effects/effects_config.js +21 -0
- package/package/synthetizer/audio_effects/fancy_chorus.js +120 -0
- package/package/synthetizer/audio_effects/impulse_response_2.flac +0 -0
- package/package/synthetizer/audio_effects/reverb.js +24 -0
- package/package/synthetizer/synth_event_handler.js +156 -0
- package/package/synthetizer/synthetizer.js +766 -0
- package/package/synthetizer/worklet_processor.min.js +13 -0
- package/package/synthetizer/worklet_system/README.md +6 -0
- package/package/synthetizer/worklet_system/main_processor.js +363 -0
- package/package/synthetizer/worklet_system/message_protocol/handle_message.js +197 -0
- package/package/synthetizer/worklet_system/message_protocol/message_sending.js +74 -0
- package/package/synthetizer/worklet_system/message_protocol/worklet_message.js +121 -0
- package/package/synthetizer/worklet_system/minify_processor.sh +4 -0
- package/package/synthetizer/worklet_system/worklet_methods/controller_control.js +230 -0
- package/package/synthetizer/worklet_system/worklet_methods/data_entry.js +277 -0
- package/package/synthetizer/worklet_system/worklet_methods/note_off.js +109 -0
- package/package/synthetizer/worklet_system/worklet_methods/note_on.js +91 -0
- package/package/synthetizer/worklet_system/worklet_methods/program_control.js +183 -0
- package/package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +177 -0
- package/package/synthetizer/worklet_system/worklet_methods/snapshot.js +129 -0
- package/package/synthetizer/worklet_system/worklet_methods/system_exclusive.js +272 -0
- package/package/synthetizer/worklet_system/worklet_methods/tuning_control.js +195 -0
- package/package/synthetizer/worklet_system/worklet_methods/vibrato_control.js +29 -0
- package/package/synthetizer/worklet_system/worklet_methods/voice_control.js +233 -0
- package/package/synthetizer/worklet_system/worklet_processor.js +9 -0
- package/package/synthetizer/worklet_system/worklet_utilities/lfo.js +23 -0
- package/package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +130 -0
- package/package/synthetizer/worklet_system/worklet_utilities/modulation_envelope.js +73 -0
- package/package/synthetizer/worklet_system/worklet_utilities/modulator_curves.js +86 -0
- package/package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +81 -0
- package/package/synthetizer/worklet_system/worklet_utilities/unit_converter.js +66 -0
- package/package/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +265 -0
- package/package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +83 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +234 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +116 -0
- package/package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +272 -0
- package/package/synthetizer/worklet_url.js +5 -0
- package/package/utils/README.md +4 -0
- package/package/utils/buffer_to_wav.js +101 -0
- package/package/utils/byte_functions/big_endian.js +28 -0
- package/package/utils/byte_functions/little_endian.js +74 -0
- package/package/utils/byte_functions/string.js +97 -0
- package/package/utils/byte_functions/variable_length_quantity.js +37 -0
- package/package/utils/encode_vorbis.js +30 -0
- package/package/utils/indexed_array.js +41 -0
- package/package/utils/loggin.js +79 -0
- package/package/utils/other.js +54 -0
- package/package.json +43 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { DEFAULT_PERCUSSION, DEFAULT_SYNTH_MODE, VOICE_CAP } from '../synthetizer.js'
|
|
2
|
+
import {
|
|
3
|
+
createWorkletChannel,
|
|
4
|
+
} from './worklet_utilities/worklet_processor_channel.js'
|
|
5
|
+
|
|
6
|
+
import { SoundFont2 } from '../../soundfont/soundfont.js'
|
|
7
|
+
import { handleMessage } from './message_protocol/handle_message.js'
|
|
8
|
+
import { systemExclusive } from './worklet_methods/system_exclusive.js'
|
|
9
|
+
import { noteOn } from './worklet_methods/note_on.js'
|
|
10
|
+
import { dataEntryCoarse, dataEntryFine } from './worklet_methods/data_entry.js'
|
|
11
|
+
import { killNote, noteOff, stopAll, stopAllChannels } from './worklet_methods/note_off.js'
|
|
12
|
+
import {
|
|
13
|
+
controllerChange, muteChannel, setMasterGain, setMasterPan, setMIDIVolume,
|
|
14
|
+
} from './worklet_methods/controller_control.js'
|
|
15
|
+
import { callEvent, post, sendChannelProperties } from './message_protocol/message_sending.js'
|
|
16
|
+
import {
|
|
17
|
+
channelPressure,
|
|
18
|
+
pitchWheel, polyPressure,
|
|
19
|
+
setChannelTuning, setChannelTuningSemitones,
|
|
20
|
+
setMasterTuning, setModulationDepth,
|
|
21
|
+
transposeAllChannels,
|
|
22
|
+
transposeChannel,
|
|
23
|
+
} from './worklet_methods/tuning_control.js'
|
|
24
|
+
import {
|
|
25
|
+
programChange,
|
|
26
|
+
reloadSoundFont,
|
|
27
|
+
sampleDump,
|
|
28
|
+
sendPresetList,
|
|
29
|
+
setDrums,
|
|
30
|
+
setPreset,
|
|
31
|
+
} from './worklet_methods/program_control.js'
|
|
32
|
+
import { disableAndLockVibrato, setVibrato } from './worklet_methods/vibrato_control.js'
|
|
33
|
+
import { WorkletSequencer } from '../../sequencer/worklet_sequencer/worklet_sequencer.js'
|
|
34
|
+
import { SpessaSynthInfo } from '../../utils/loggin.js'
|
|
35
|
+
import { applySynthesizerSnapshot, sendSynthesizerSnapshot } from './worklet_methods/snapshot.js'
|
|
36
|
+
import { consoleColors } from '../../utils/other.js'
|
|
37
|
+
import { PAN_SMOOTHING_FACTOR, releaseVoice, renderVoice, voiceKilling } from './worklet_methods/voice_control.js'
|
|
38
|
+
import { returnMessageType } from './message_protocol/worklet_message.js'
|
|
39
|
+
import { stbvorbis } from '../../externals/stbvorbis_sync/stbvorbis_sync.min.js'
|
|
40
|
+
import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from './worklet_utilities/volume_envelope.js'
|
|
41
|
+
import { resetAllControllers, resetControllers, resetParameters } from './worklet_methods/reset_controllers.js'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* worklet_processor.js
|
|
46
|
+
* purpose: manages the synthesizer (and worklet sequencer) from the AudioWorkletGlobalScope and renders the audio data
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
export const MIN_NOTE_LENGTH = 0.07; // if the note is released faster than that, it forced to last that long
|
|
50
|
+
|
|
51
|
+
export const SYNTHESIZER_GAIN = 1.0;
|
|
52
|
+
|
|
53
|
+
class SpessaSynthProcessor extends AudioWorkletProcessor {
|
|
54
|
+
/**
|
|
55
|
+
* Creates a new worklet synthesis system. contains all channels
|
|
56
|
+
* @param options {{
|
|
57
|
+
* processorOptions: {
|
|
58
|
+
* midiChannels: number,
|
|
59
|
+
* soundfont: ArrayBuffer,
|
|
60
|
+
* enableEventSystem: boolean,
|
|
61
|
+
* startRenderingData: {
|
|
62
|
+
* parsedMIDI: MIDI,
|
|
63
|
+
* snapshot: SynthesizerSnapshot,
|
|
64
|
+
* oneOutput: boolean
|
|
65
|
+
* }
|
|
66
|
+
* }}}
|
|
67
|
+
*/
|
|
68
|
+
constructor(options) {
|
|
69
|
+
super();
|
|
70
|
+
this.oneOutputMode = options.processorOptions?.startRenderingData?.oneOutput === true;
|
|
71
|
+
this._outputsAmount = this.oneOutputMode ? 1 : options.processorOptions.midiChannels;
|
|
72
|
+
|
|
73
|
+
this.enableEventSystem = options.processorOptions.enableEventSystem;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @type {function}
|
|
77
|
+
*/
|
|
78
|
+
this.processTickCallback = undefined;
|
|
79
|
+
|
|
80
|
+
this.sequencer = new WorkletSequencer(this);
|
|
81
|
+
|
|
82
|
+
this.transposition = 0;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The volume gain, set by user
|
|
86
|
+
* @type {number}
|
|
87
|
+
*/
|
|
88
|
+
this.masterGain = SYNTHESIZER_GAIN;
|
|
89
|
+
|
|
90
|
+
this.midiVolume = 1;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Maximum number of voices allowed at once
|
|
94
|
+
* @type {number}
|
|
95
|
+
*/
|
|
96
|
+
this.voiceCap = VOICE_CAP;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* -1 to 1
|
|
100
|
+
* @type {number}
|
|
101
|
+
*/
|
|
102
|
+
this.pan = 0.0;
|
|
103
|
+
/**
|
|
104
|
+
* the pan of the left channel
|
|
105
|
+
* @type {number}
|
|
106
|
+
*/
|
|
107
|
+
this.panLeft = 0.5 * this.currentGain;
|
|
108
|
+
|
|
109
|
+
this.highPerformanceMode = false;
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* the pan of the right channel
|
|
113
|
+
* @type {number}
|
|
114
|
+
*/
|
|
115
|
+
this.panRight = 0.5 * this.currentGain;
|
|
116
|
+
try
|
|
117
|
+
{
|
|
118
|
+
/**
|
|
119
|
+
* @type {SoundFont2}
|
|
120
|
+
*/
|
|
121
|
+
this.soundfont = new SoundFont2(options.processorOptions.soundfont);
|
|
122
|
+
}
|
|
123
|
+
catch (e)
|
|
124
|
+
{
|
|
125
|
+
this.post({
|
|
126
|
+
messageType: returnMessageType.soundfontError,
|
|
127
|
+
messageData: e
|
|
128
|
+
});
|
|
129
|
+
throw e;
|
|
130
|
+
}
|
|
131
|
+
this.sendPresetList();
|
|
132
|
+
|
|
133
|
+
this.defaultPreset = this.soundfont.getPreset(0, 0);
|
|
134
|
+
this.drumPreset = this.soundfont.getPreset(128, 0);
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @type {Float32Array[]}
|
|
138
|
+
*/
|
|
139
|
+
this.workletDumpedSamplesList = [];
|
|
140
|
+
/**
|
|
141
|
+
* contains all the channels with their voices on the processor size
|
|
142
|
+
* @type {WorkletProcessorChannel[]}
|
|
143
|
+
*/
|
|
144
|
+
this.workletProcessorChannels = [];
|
|
145
|
+
for (let i = 0; i < options.processorOptions.midiChannels; i++)
|
|
146
|
+
{
|
|
147
|
+
this.createWorkletChannel(false);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
this.workletProcessorChannels[DEFAULT_PERCUSSION].preset = this.drumPreset;
|
|
151
|
+
this.workletProcessorChannels[DEFAULT_PERCUSSION].drumChannel = true;
|
|
152
|
+
|
|
153
|
+
// in seconds, time between two samples (very, very short)
|
|
154
|
+
this.sampleTime = 1 / sampleRate;
|
|
155
|
+
|
|
156
|
+
// these smoothing factors were tested on 44100Hz, adjust them here
|
|
157
|
+
this.volumeEnvelopeSmoothingFactor = VOLUME_ENVELOPE_SMOOTHING_FACTOR * (sampleRate / 44100);
|
|
158
|
+
this.panSmoothingFactor = PAN_SMOOTHING_FACTOR * (sampleRate / 44100);
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Controls the system
|
|
162
|
+
* @typedef {"gm"|"gm2"|"gs"|"xg"} SynthSystem
|
|
163
|
+
*/
|
|
164
|
+
/*
|
|
165
|
+
* @type {SynthSystem}
|
|
166
|
+
*/
|
|
167
|
+
this.system = DEFAULT_SYNTH_MODE;
|
|
168
|
+
|
|
169
|
+
this.totalVoicesAmount = 0;
|
|
170
|
+
|
|
171
|
+
this.port.onmessage = e => this.handleMessage(e.data);
|
|
172
|
+
|
|
173
|
+
// if sent, start rendering
|
|
174
|
+
if(options.processorOptions.startRenderingData)
|
|
175
|
+
{
|
|
176
|
+
if (options.processorOptions.startRenderingData.snapshot)
|
|
177
|
+
{
|
|
178
|
+
this.applySynthesizerSnapshot(options.processorOptions.startRenderingData.snapshot);
|
|
179
|
+
this.resetAllControllers();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
SpessaSynthInfo("%cRendering enabled! Starting render.", consoleColors.info)
|
|
183
|
+
if (options.processorOptions.startRenderingData.parsedMIDI)
|
|
184
|
+
{
|
|
185
|
+
this.sequencer.loadNewSongList([options.processorOptions.startRenderingData.parsedMIDI]);
|
|
186
|
+
this.sequencer.loop = false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
stbvorbis.isInitialized.then(() => {
|
|
191
|
+
this.post({
|
|
192
|
+
messageType: returnMessageType.ready,
|
|
193
|
+
messageData: undefined
|
|
194
|
+
});
|
|
195
|
+
SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* @returns {number}
|
|
201
|
+
*/
|
|
202
|
+
get currentGain()
|
|
203
|
+
{
|
|
204
|
+
return this.masterGain * this.midiVolume;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
debugMessage()
|
|
208
|
+
{
|
|
209
|
+
SpessaSynthInfo({
|
|
210
|
+
channels: this.workletProcessorChannels,
|
|
211
|
+
voicesAmount: this.totalVoicesAmount,
|
|
212
|
+
outputAmount: this._outputsAmount,
|
|
213
|
+
dumpedSamples: this.workletDumpedSamplesList
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Syntesizes the voice to buffers
|
|
219
|
+
* @param inputs {Float32Array[][]} required by WebAudioAPI
|
|
220
|
+
* @param outputs {Float32Array[][]} the outputs to write to, only the first 2 channels are populated
|
|
221
|
+
* @returns {boolean} true
|
|
222
|
+
*/
|
|
223
|
+
process(inputs, outputs) {
|
|
224
|
+
if(this.processTickCallback)
|
|
225
|
+
{
|
|
226
|
+
this.processTickCallback();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// for every channel
|
|
230
|
+
let totalCurrentVoices = 0;
|
|
231
|
+
this.workletProcessorChannels.forEach((channel, index) => {
|
|
232
|
+
if(channel.voices.length < 1 || channel.isMuted)
|
|
233
|
+
{
|
|
234
|
+
// skip the channels
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
let outputIndex;
|
|
238
|
+
let outputLeft;
|
|
239
|
+
let outputRight;
|
|
240
|
+
let reverbChannels;
|
|
241
|
+
let chorusChannels;
|
|
242
|
+
if(this.oneOutputMode)
|
|
243
|
+
{
|
|
244
|
+
// first output only
|
|
245
|
+
const output = outputs[0];
|
|
246
|
+
// reverb and chorus are disabled. 32 output channels: two for each midi channels
|
|
247
|
+
outputIndex = (index % 16) * 2;
|
|
248
|
+
outputLeft = output[outputIndex];
|
|
249
|
+
outputRight = output[outputIndex + 1];
|
|
250
|
+
}
|
|
251
|
+
else
|
|
252
|
+
{
|
|
253
|
+
// 2 first outputs are reverb and chorus, other are for channels
|
|
254
|
+
outputIndex = (index % this._outputsAmount) + 2;
|
|
255
|
+
outputLeft = outputs[outputIndex][0];
|
|
256
|
+
outputRight = outputs[outputIndex][1];
|
|
257
|
+
reverbChannels = outputs[0];
|
|
258
|
+
chorusChannels = outputs[1];
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const tempV = channel.voices;
|
|
262
|
+
|
|
263
|
+
// reset voices
|
|
264
|
+
channel.voices = [];
|
|
265
|
+
|
|
266
|
+
// for every voice
|
|
267
|
+
tempV.forEach(v => {
|
|
268
|
+
// render voice
|
|
269
|
+
this.renderVoice(
|
|
270
|
+
channel,
|
|
271
|
+
v,
|
|
272
|
+
outputLeft, outputRight,
|
|
273
|
+
reverbChannels,
|
|
274
|
+
chorusChannels
|
|
275
|
+
);
|
|
276
|
+
if(!v.finished)
|
|
277
|
+
{
|
|
278
|
+
// if not finished, add it back
|
|
279
|
+
channel.voices.push(v);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
totalCurrentVoices += tempV.length;
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// if voice count changed, update voice amount
|
|
287
|
+
if(totalCurrentVoices !== this.totalVoicesAmount)
|
|
288
|
+
{
|
|
289
|
+
this.totalVoicesAmount = totalCurrentVoices;
|
|
290
|
+
this.sendChannelProperties();
|
|
291
|
+
}
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// include other methods
|
|
297
|
+
// voice related
|
|
298
|
+
SpessaSynthProcessor.prototype.renderVoice = renderVoice;
|
|
299
|
+
SpessaSynthProcessor.prototype.releaseVoice = releaseVoice;
|
|
300
|
+
SpessaSynthProcessor.prototype.voiceKilling = voiceKilling;
|
|
301
|
+
|
|
302
|
+
// message port related
|
|
303
|
+
SpessaSynthProcessor.prototype.handleMessage = handleMessage;
|
|
304
|
+
SpessaSynthProcessor.prototype.post = post;
|
|
305
|
+
SpessaSynthProcessor.prototype.sendChannelProperties = sendChannelProperties;
|
|
306
|
+
SpessaSynthProcessor.prototype.callEvent = callEvent;
|
|
307
|
+
|
|
308
|
+
// system exlcusive related
|
|
309
|
+
SpessaSynthProcessor.prototype.systemExclusive = systemExclusive;
|
|
310
|
+
|
|
311
|
+
// note messages related
|
|
312
|
+
SpessaSynthProcessor.prototype.noteOn = noteOn;
|
|
313
|
+
SpessaSynthProcessor.prototype.noteOff = noteOff;
|
|
314
|
+
SpessaSynthProcessor.prototype.polyPressure = polyPressure;
|
|
315
|
+
SpessaSynthProcessor.prototype.killNote = killNote;
|
|
316
|
+
SpessaSynthProcessor.prototype.stopAll = stopAll;
|
|
317
|
+
SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
|
|
318
|
+
SpessaSynthProcessor.prototype.muteChannel = muteChannel;
|
|
319
|
+
|
|
320
|
+
// custom vibrato related
|
|
321
|
+
SpessaSynthProcessor.prototype.setVibrato = setVibrato;
|
|
322
|
+
SpessaSynthProcessor.prototype.disableAndLockVibrato = disableAndLockVibrato;
|
|
323
|
+
|
|
324
|
+
// data entry related
|
|
325
|
+
SpessaSynthProcessor.prototype.dataEntryCoarse = dataEntryCoarse;
|
|
326
|
+
SpessaSynthProcessor.prototype.dataEntryFine = dataEntryFine;
|
|
327
|
+
|
|
328
|
+
// channel related
|
|
329
|
+
SpessaSynthProcessor.prototype.createWorkletChannel = createWorkletChannel;
|
|
330
|
+
SpessaSynthProcessor.prototype.controllerChange = controllerChange;
|
|
331
|
+
SpessaSynthProcessor.prototype.channelPressure = channelPressure;
|
|
332
|
+
SpessaSynthProcessor.prototype.resetAllControllers = resetAllControllers;
|
|
333
|
+
SpessaSynthProcessor.prototype.resetControllers = resetControllers;
|
|
334
|
+
SpessaSynthProcessor.prototype.resetParameters = resetParameters;
|
|
335
|
+
|
|
336
|
+
// master parameter related
|
|
337
|
+
SpessaSynthProcessor.prototype.setMasterGain = setMasterGain;
|
|
338
|
+
SpessaSynthProcessor.prototype.setMasterPan = setMasterPan;
|
|
339
|
+
SpessaSynthProcessor.prototype.setMIDIVolume = setMIDIVolume;
|
|
340
|
+
|
|
341
|
+
// tuning related
|
|
342
|
+
SpessaSynthProcessor.prototype.transposeAllChannels = transposeAllChannels;
|
|
343
|
+
SpessaSynthProcessor.prototype.transposeChannel = transposeChannel;
|
|
344
|
+
SpessaSynthProcessor.prototype.setChannelTuning = setChannelTuning;
|
|
345
|
+
SpessaSynthProcessor.prototype.setChannelTuningSemitones = setChannelTuningSemitones;
|
|
346
|
+
SpessaSynthProcessor.prototype.setMasterTuning = setMasterTuning;
|
|
347
|
+
SpessaSynthProcessor.prototype.setModulationDepth = setModulationDepth;
|
|
348
|
+
SpessaSynthProcessor.prototype.pitchWheel = pitchWheel;
|
|
349
|
+
|
|
350
|
+
// program related
|
|
351
|
+
SpessaSynthProcessor.prototype.programChange = programChange;
|
|
352
|
+
SpessaSynthProcessor.prototype.setPreset = setPreset;
|
|
353
|
+
SpessaSynthProcessor.prototype.setDrums = setDrums;
|
|
354
|
+
SpessaSynthProcessor.prototype.reloadSoundFont = reloadSoundFont;
|
|
355
|
+
SpessaSynthProcessor.prototype.sampleDump = sampleDump;
|
|
356
|
+
SpessaSynthProcessor.prototype.sendPresetList = sendPresetList;
|
|
357
|
+
|
|
358
|
+
// snapshot related
|
|
359
|
+
SpessaSynthProcessor.prototype.sendSynthesizerSnapshot = sendSynthesizerSnapshot;
|
|
360
|
+
SpessaSynthProcessor.prototype.applySynthesizerSnapshot = applySynthesizerSnapshot;
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
export { SpessaSynthProcessor }
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { ALL_CHANNELS_OR_DIFFERENT_ACTION, masterParameterType, workletMessageType } from './worklet_message.js'
|
|
2
|
+
import { SpessaSynthLogging, SpessaSynthWarn } from '../../../utils/loggin.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @this {SpessaSynthProcessor}
|
|
6
|
+
* @param message
|
|
7
|
+
*/
|
|
8
|
+
export function handleMessage(message)
|
|
9
|
+
{
|
|
10
|
+
const data = message.messageData;
|
|
11
|
+
const channel = message.channelNumber;
|
|
12
|
+
/**
|
|
13
|
+
* @type {WorkletProcessorChannel}
|
|
14
|
+
*/
|
|
15
|
+
let channelObject = {};
|
|
16
|
+
if(channel >= 0)
|
|
17
|
+
{
|
|
18
|
+
channelObject = this.workletProcessorChannels[channel];
|
|
19
|
+
}
|
|
20
|
+
switch (message.messageType) {
|
|
21
|
+
case workletMessageType.noteOn:
|
|
22
|
+
this.noteOn(channel, data[0], data[1], data[2]);
|
|
23
|
+
break;
|
|
24
|
+
|
|
25
|
+
case workletMessageType.noteOff:
|
|
26
|
+
this.noteOff(channel, data);
|
|
27
|
+
break;
|
|
28
|
+
|
|
29
|
+
case workletMessageType.pitchWheel:
|
|
30
|
+
this.pitchWheel(channel, data[0], data[1]);
|
|
31
|
+
break;
|
|
32
|
+
|
|
33
|
+
case workletMessageType.ccChange:
|
|
34
|
+
this.controllerChange(channel, data[0], data[1], data[2]);
|
|
35
|
+
break;
|
|
36
|
+
|
|
37
|
+
case workletMessageType.customcCcChange:
|
|
38
|
+
// custom controller change
|
|
39
|
+
channelObject.customControllers[data[0]] = data[1];
|
|
40
|
+
break;
|
|
41
|
+
|
|
42
|
+
case workletMessageType.killNote:
|
|
43
|
+
this.killNote(channel, data);
|
|
44
|
+
break;
|
|
45
|
+
|
|
46
|
+
case workletMessageType.programChange:
|
|
47
|
+
this.programChange(channel, data[0], data[1]);
|
|
48
|
+
break;
|
|
49
|
+
|
|
50
|
+
case workletMessageType.channelPressure:
|
|
51
|
+
this.channelPressure(channel, data);
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
case workletMessageType.polyPressure:
|
|
55
|
+
this.polyPressure(channel, data[0], data[1]);
|
|
56
|
+
break;
|
|
57
|
+
|
|
58
|
+
case workletMessageType.ccReset:
|
|
59
|
+
if(channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
60
|
+
{
|
|
61
|
+
this.resetAllControllers();
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
{
|
|
65
|
+
this.resetControllers(channel);
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
|
|
69
|
+
case workletMessageType.systemExclusive:
|
|
70
|
+
this.systemExclusive(data);
|
|
71
|
+
break;
|
|
72
|
+
|
|
73
|
+
case workletMessageType.setChannelVibrato:
|
|
74
|
+
if(channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
75
|
+
{
|
|
76
|
+
for (let i = 0; i < this.workletProcessorChannels.length; i++)
|
|
77
|
+
{
|
|
78
|
+
if(data.rate === -1)
|
|
79
|
+
{
|
|
80
|
+
this.disableAndLockVibrato(i);
|
|
81
|
+
}
|
|
82
|
+
else
|
|
83
|
+
{
|
|
84
|
+
this.setVibrato(i, data.depth, data.rate, data.delay);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if(data.rate === -1)
|
|
89
|
+
{
|
|
90
|
+
this.disableAndLockVibrato(channel);
|
|
91
|
+
}
|
|
92
|
+
else
|
|
93
|
+
{
|
|
94
|
+
this.setVibrato(channel, data.depth, data.rate, data.delay);
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
|
|
98
|
+
case workletMessageType.reloadSoundFont:
|
|
99
|
+
this.reloadSoundFont(data);
|
|
100
|
+
break;
|
|
101
|
+
|
|
102
|
+
case workletMessageType.stopAll:
|
|
103
|
+
if(channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
104
|
+
{
|
|
105
|
+
this.stopAllChannels(data === 1);
|
|
106
|
+
}
|
|
107
|
+
else
|
|
108
|
+
{
|
|
109
|
+
this.stopAll(channel, data === 1);
|
|
110
|
+
}
|
|
111
|
+
break;
|
|
112
|
+
|
|
113
|
+
case workletMessageType.killNotes:
|
|
114
|
+
this.voiceKilling(data);
|
|
115
|
+
break;
|
|
116
|
+
|
|
117
|
+
case workletMessageType.muteChannel:
|
|
118
|
+
this.muteChannel(channel, data);
|
|
119
|
+
break;
|
|
120
|
+
|
|
121
|
+
case workletMessageType.addNewChannel:
|
|
122
|
+
this.createWorkletChannel(true);
|
|
123
|
+
break;
|
|
124
|
+
|
|
125
|
+
case workletMessageType.debugMessage:
|
|
126
|
+
this.debugMessage();
|
|
127
|
+
break;
|
|
128
|
+
|
|
129
|
+
case workletMessageType.setMasterParameter:
|
|
130
|
+
/**
|
|
131
|
+
* @type {masterParameterType}
|
|
132
|
+
*/
|
|
133
|
+
const type = data[0];
|
|
134
|
+
const value = data[1];
|
|
135
|
+
switch (type)
|
|
136
|
+
{
|
|
137
|
+
case masterParameterType.masterPan:
|
|
138
|
+
this.setMasterPan(value);
|
|
139
|
+
break;
|
|
140
|
+
|
|
141
|
+
case masterParameterType.mainVolume:
|
|
142
|
+
this.setMasterGain(value);
|
|
143
|
+
break;
|
|
144
|
+
|
|
145
|
+
case masterParameterType.voicesCap:
|
|
146
|
+
this.voiceCap = value;
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
case workletMessageType.setDrums:
|
|
152
|
+
this.setDrums(channel, data);
|
|
153
|
+
break;
|
|
154
|
+
|
|
155
|
+
case workletMessageType.transpose:
|
|
156
|
+
if(channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
157
|
+
{
|
|
158
|
+
this.transposeAllChannels(data[0], data[1]);
|
|
159
|
+
}
|
|
160
|
+
else
|
|
161
|
+
{
|
|
162
|
+
this.transposeChannel(channel, data[0], data[1]);
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
|
|
166
|
+
case workletMessageType.highPerformanceMode:
|
|
167
|
+
this.highPerformanceMode = data;
|
|
168
|
+
break;
|
|
169
|
+
|
|
170
|
+
case workletMessageType.lockController:
|
|
171
|
+
if(data[0] === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
172
|
+
{
|
|
173
|
+
channelObject.lockPreset = data[1];
|
|
174
|
+
}
|
|
175
|
+
else
|
|
176
|
+
{
|
|
177
|
+
channelObject.lockedControllers[data[0]] = data[1];
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
|
|
181
|
+
case workletMessageType.sequencerSpecific:
|
|
182
|
+
this.sequencer.processMessage(data.messageType, data.messageData);
|
|
183
|
+
break;
|
|
184
|
+
|
|
185
|
+
case workletMessageType.requestSynthesizerSnapshot:
|
|
186
|
+
this.sendSynthesizerSnapshot();
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case workletMessageType.setLogLevel:
|
|
190
|
+
SpessaSynthLogging(data[0], data[1], data[2], data[3]);
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
default:
|
|
194
|
+
SpessaSynthWarn("Unrecognized event:", data);
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { returnMessageType } from './worklet_message.js'
|
|
2
|
+
import { NON_CC_INDEX_OFFSET } from '../worklet_utilities/worklet_processor_channel.js'
|
|
3
|
+
import { modulatorSources } from '../../../soundfont/read/modulators.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Calls synth event from the worklet side
|
|
7
|
+
* @param eventName {EventTypes} the event name
|
|
8
|
+
* @param eventData {any}
|
|
9
|
+
* @this {SpessaSynthProcessor}
|
|
10
|
+
*/
|
|
11
|
+
export function callEvent(eventName, eventData)
|
|
12
|
+
{
|
|
13
|
+
if(!this.enableEventSystem)
|
|
14
|
+
{
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
this.post({
|
|
18
|
+
messageType: returnMessageType.eventCall,
|
|
19
|
+
messageData: {
|
|
20
|
+
eventName: eventName,
|
|
21
|
+
eventData: eventData
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param data {WorkletReturnMessage}
|
|
28
|
+
* @this {SpessaSynthProcessor}
|
|
29
|
+
*/
|
|
30
|
+
export function post(data)
|
|
31
|
+
{
|
|
32
|
+
if(!this.enableEventSystem)
|
|
33
|
+
{
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.port.postMessage(data);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @typedef {Object} ChannelProperty
|
|
41
|
+
* @property {number} voicesAmount
|
|
42
|
+
* @property {number} pitchBend - from -8192 do 8192
|
|
43
|
+
* @property {number} pitchBendRangeSemitones - in semitones
|
|
44
|
+
* @property {boolean} isMuted
|
|
45
|
+
* @property {boolean} isDrum
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @this {SpessaSynthProcessor}
|
|
50
|
+
*/
|
|
51
|
+
export function sendChannelProperties()
|
|
52
|
+
{
|
|
53
|
+
if(!this.enableEventSystem)
|
|
54
|
+
{
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @type {ChannelProperty[]}
|
|
59
|
+
*/
|
|
60
|
+
const data = this.workletProcessorChannels.map(c => {
|
|
61
|
+
const range = (c.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] >> 7) + (c.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheelRange] & 0x7F) / 127;
|
|
62
|
+
return {
|
|
63
|
+
voicesAmount: c.voices.length,
|
|
64
|
+
pitchBend: c.midiControllers[NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel],
|
|
65
|
+
pitchBendRangeSemitones: range,
|
|
66
|
+
isMuted: c.isMuted,
|
|
67
|
+
isDrum: c.drumChannel
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
this.post({
|
|
71
|
+
messageType: returnMessageType.channelProperties,
|
|
72
|
+
messageData: data
|
|
73
|
+
});
|
|
74
|
+
}
|