spessasynth_lib 3.27.8 → 4.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/README.md +48 -47
- package/dist/index.d.ts +1328 -0
- package/dist/index.js +3212 -0
- package/dist/index.js.map +1 -0
- package/dist/spessasynth_processor.min.js +21 -0
- package/dist/spessasynth_processor.min.js.map +7 -0
- package/package.json +26 -14
- package/index.js +0 -29
- package/src/external_midi/README.md +0 -4
- package/src/external_midi/midi_handler.js +0 -130
- package/src/external_midi/web_midi_link.js +0 -43
- package/src/sequencer/README.md +0 -30
- package/src/sequencer/default_sequencer_options.js +0 -9
- package/src/sequencer/midi_data.js +0 -67
- package/src/sequencer/sequencer.js +0 -813
- package/src/sequencer/sequencer_message.js +0 -53
- package/src/synthetizer/README.md +0 -30
- package/src/synthetizer/audio_effects/effects_config.js +0 -25
- package/src/synthetizer/audio_effects/fancy_chorus.js +0 -166
- package/src/synthetizer/audio_effects/rb_compressed.min.js +0 -1
- package/src/synthetizer/audio_effects/reverb.js +0 -35
- package/src/synthetizer/audio_effects/reverb_as_binary.js +0 -18
- package/src/synthetizer/key_modifier_manager.js +0 -113
- package/src/synthetizer/sfman_message.js +0 -9
- package/src/synthetizer/synth_event_handler.js +0 -217
- package/src/synthetizer/synth_soundfont_manager.js +0 -115
- package/src/synthetizer/synthetizer.js +0 -1033
- package/src/synthetizer/worklet_message.js +0 -121
- package/src/synthetizer/worklet_processor.js +0 -654
- package/src/synthetizer/worklet_url.js +0 -18
- package/src/utils/buffer_to_wav.js +0 -40
- package/src/utils/fill_with_defaults.js +0 -21
- package/src/utils/other.js +0 -11
- package/synthetizer/worklet_processor.min.js +0 -22
|
@@ -1,654 +0,0 @@
|
|
|
1
|
-
import { consoleColors } from "../utils/other.js";
|
|
2
|
-
import {
|
|
3
|
-
ALL_CHANNELS_OR_DIFFERENT_ACTION,
|
|
4
|
-
BasicMIDI,
|
|
5
|
-
loadSoundFont,
|
|
6
|
-
masterParameterType,
|
|
7
|
-
MIDI,
|
|
8
|
-
MIDI_CHANNEL_COUNT,
|
|
9
|
-
SpessaSynthCoreUtils as util,
|
|
10
|
-
SpessaSynthLogging,
|
|
11
|
-
SpessaSynthProcessor,
|
|
12
|
-
SpessaSynthSequencer,
|
|
13
|
-
SynthesizerSnapshot
|
|
14
|
-
} from "spessasynth_core";
|
|
15
|
-
import { returnMessageType, workletMessageType } from "./worklet_message.js";
|
|
16
|
-
import { WORKLET_PROCESSOR_NAME } from "./worklet_url.js";
|
|
17
|
-
import { workletKeyModifierMessageType } from "./key_modifier_manager.js";
|
|
18
|
-
import { WorkletSoundfontManagerMessageType } from "./sfman_message.js";
|
|
19
|
-
import {
|
|
20
|
-
SongChangeType,
|
|
21
|
-
SpessaSynthSequencerMessageType,
|
|
22
|
-
SpessaSynthSequencerReturnMessageType
|
|
23
|
-
} from "../sequencer/sequencer_message.js";
|
|
24
|
-
import { fillWithDefaults } from "../utils/fill_with_defaults.js";
|
|
25
|
-
import { DEFAULT_SEQUENCER_OPTIONS } from "../sequencer/default_sequencer_options.js";
|
|
26
|
-
import { MIDIData } from "../sequencer/midi_data.js";
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// a worklet processor wrapper for the synthesizer core
|
|
30
|
-
class WorkletSpessaProcessor extends AudioWorkletProcessor
|
|
31
|
-
{
|
|
32
|
-
/**
|
|
33
|
-
* If the worklet is alive
|
|
34
|
-
* @type {boolean}
|
|
35
|
-
*/
|
|
36
|
-
alive = true;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Instead of 18 stereo outputs, there's one with 32 channels (no effects)
|
|
40
|
-
* @type {boolean}
|
|
41
|
-
*/
|
|
42
|
-
oneOutputMode = false;
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Creates a new worklet synthesis system. contains all channels
|
|
46
|
-
* @param options {{
|
|
47
|
-
* processorOptions: {
|
|
48
|
-
* midiChannels: number,
|
|
49
|
-
* soundfont: ArrayBuffer,
|
|
50
|
-
* enableEventSystem: boolean,
|
|
51
|
-
* startRenderingData: StartRenderingDataConfig
|
|
52
|
-
* }}}
|
|
53
|
-
*/
|
|
54
|
-
constructor(options)
|
|
55
|
-
{
|
|
56
|
-
super();
|
|
57
|
-
const opts = options.processorOptions;
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
// one output is indicated by setting midiChannels to 1
|
|
61
|
-
this.oneOutputMode = opts.midiChannels === 1;
|
|
62
|
-
|
|
63
|
-
// prepare synthesizer connections
|
|
64
|
-
/**
|
|
65
|
-
* @param t {returnMessageType}
|
|
66
|
-
* @param d {any}
|
|
67
|
-
*/
|
|
68
|
-
const postSyn = (t, d) =>
|
|
69
|
-
{
|
|
70
|
-
// noinspection JSCheckFunctionSignatures
|
|
71
|
-
this.postMessageToMainThread({
|
|
72
|
-
messageType: t,
|
|
73
|
-
messageData: d
|
|
74
|
-
});
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
// start rendering data
|
|
78
|
-
const startRenderingData = opts?.startRenderingData;
|
|
79
|
-
/**
|
|
80
|
-
* The snapshot that synth was restored from
|
|
81
|
-
* @type {SynthesizerSnapshot|undefined}
|
|
82
|
-
* @private
|
|
83
|
-
*/
|
|
84
|
-
const snapshot = startRenderingData?.snapshot;
|
|
85
|
-
|
|
86
|
-
// noinspection JSUnresolvedReference
|
|
87
|
-
/**
|
|
88
|
-
* Initialize the synthesis engine
|
|
89
|
-
* @type {SpessaSynthProcessor}
|
|
90
|
-
*/
|
|
91
|
-
this.synthesizer = new SpessaSynthProcessor(
|
|
92
|
-
sampleRate, // AudioWorkletGlobalScope
|
|
93
|
-
{
|
|
94
|
-
effectsEnabled: !this.oneOutputMode, // one output mode disables effects
|
|
95
|
-
enableEventSystem: opts?.enableEventSystem, // enable message port?
|
|
96
|
-
midiChannels: MIDI_CHANNEL_COUNT, // midi channel count (16)
|
|
97
|
-
initialTime: currentTime // AudioWorkletGlobalScope, sync with audioContext time
|
|
98
|
-
}
|
|
99
|
-
);
|
|
100
|
-
this.synthesizer.onEventCall = (t, d) =>
|
|
101
|
-
{
|
|
102
|
-
postSyn(returnMessageType.eventCall, {
|
|
103
|
-
eventName: t,
|
|
104
|
-
eventData: d
|
|
105
|
-
});
|
|
106
|
-
};
|
|
107
|
-
this.synthesizer.onChannelPropertyChange = (p, n) => postSyn(returnMessageType.channelPropertyChange, [n, p]);
|
|
108
|
-
this.synthesizer.onMasterParameterChange = (t, v) => postSyn(returnMessageType.masterParameterChange, [t, v]);
|
|
109
|
-
|
|
110
|
-
const bank = loadSoundFont(opts.soundfont);
|
|
111
|
-
this.synthesizer.soundfontManager.reloadManager(bank);
|
|
112
|
-
|
|
113
|
-
this.synthesizer.processorInitialized.then(() =>
|
|
114
|
-
{
|
|
115
|
-
// initialize the sequencer engine
|
|
116
|
-
this.sequencer = new SpessaSynthSequencer(this.synthesizer);
|
|
117
|
-
|
|
118
|
-
const postSeq = (type, data) =>
|
|
119
|
-
{
|
|
120
|
-
this.postMessageToMainThread({
|
|
121
|
-
messageType: returnMessageType.sequencerSpecific,
|
|
122
|
-
messageData: {
|
|
123
|
-
messageType: type,
|
|
124
|
-
messageData: data
|
|
125
|
-
}
|
|
126
|
-
});
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
// receive messages from the main thread
|
|
130
|
-
this.port.onmessage = e => this.handleMessage(e.data);
|
|
131
|
-
|
|
132
|
-
// sequencer events
|
|
133
|
-
this.sequencer.onMIDIMessage = m =>
|
|
134
|
-
{
|
|
135
|
-
postSeq(SpessaSynthSequencerReturnMessageType.midiEvent, m);
|
|
136
|
-
};
|
|
137
|
-
this.sequencer.onTimeChange = t =>
|
|
138
|
-
{
|
|
139
|
-
postSeq(SpessaSynthSequencerReturnMessageType.timeChange, t);
|
|
140
|
-
};
|
|
141
|
-
this.sequencer.onPlaybackStop = p =>
|
|
142
|
-
{
|
|
143
|
-
postSeq(SpessaSynthSequencerReturnMessageType.pause, p);
|
|
144
|
-
};
|
|
145
|
-
this.sequencer.onSongChange = (i, a) =>
|
|
146
|
-
{
|
|
147
|
-
postSeq(SpessaSynthSequencerReturnMessageType.songChange, [i, a]);
|
|
148
|
-
};
|
|
149
|
-
this.sequencer.onMetaEvent = (e, i) =>
|
|
150
|
-
{
|
|
151
|
-
postSeq(SpessaSynthSequencerReturnMessageType.metaEvent, [e, i]);
|
|
152
|
-
};
|
|
153
|
-
this.sequencer.onLoopCountChange = c =>
|
|
154
|
-
{
|
|
155
|
-
postSeq(SpessaSynthSequencerReturnMessageType.loopCountChange, c);
|
|
156
|
-
};
|
|
157
|
-
this.sequencer.onSongListChange = l =>
|
|
158
|
-
{
|
|
159
|
-
const midiDataList = l.map(s => new MIDIData(s));
|
|
160
|
-
this.postMessageToMainThread({
|
|
161
|
-
messageType: returnMessageType.sequencerSpecific,
|
|
162
|
-
messageData: {
|
|
163
|
-
messageType: SpessaSynthSequencerReturnMessageType.songListChange,
|
|
164
|
-
messageData: midiDataList
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
if (snapshot !== undefined)
|
|
170
|
-
{
|
|
171
|
-
this.synthesizer.applySynthesizerSnapshot(snapshot);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// if sent, start rendering
|
|
175
|
-
if (startRenderingData)
|
|
176
|
-
{
|
|
177
|
-
|
|
178
|
-
util.SpessaSynthInfo("%cRendering enabled! Starting render.", consoleColors.info);
|
|
179
|
-
if (startRenderingData.parsedMIDI)
|
|
180
|
-
{
|
|
181
|
-
if (startRenderingData?.loopCount !== undefined)
|
|
182
|
-
{
|
|
183
|
-
this.sequencer.loopCount = startRenderingData?.loopCount;
|
|
184
|
-
this.sequencer.loop = true;
|
|
185
|
-
}
|
|
186
|
-
else
|
|
187
|
-
{
|
|
188
|
-
this.sequencer.loop = false;
|
|
189
|
-
}
|
|
190
|
-
// set voice cap to unlimited
|
|
191
|
-
this.synthesizer.voiceCap = Infinity;
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* set options
|
|
195
|
-
* @type {SequencerOptions}
|
|
196
|
-
*/
|
|
197
|
-
const seqOptions = fillWithDefaults(
|
|
198
|
-
startRenderingData.sequencerOptions,
|
|
199
|
-
DEFAULT_SEQUENCER_OPTIONS
|
|
200
|
-
);
|
|
201
|
-
this.sequencer.skipToFirstNoteOn = seqOptions.skipToFirstNoteOn;
|
|
202
|
-
this.sequencer.preservePlaybackState = seqOptions.preservePlaybackState;
|
|
203
|
-
this.sequencer.playbackRate = seqOptions.initialPlaybackRate;
|
|
204
|
-
// autoplay is ignored
|
|
205
|
-
try
|
|
206
|
-
{
|
|
207
|
-
// cloned objects don't have methods
|
|
208
|
-
this.sequencer.loadNewSongList([BasicMIDI.copyFrom(startRenderingData.parsedMIDI)]);
|
|
209
|
-
}
|
|
210
|
-
catch (e)
|
|
211
|
-
{
|
|
212
|
-
console.error(e);
|
|
213
|
-
postSeq(SpessaSynthSequencerReturnMessageType.midiError, e);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
this.postReady();
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
postReady()
|
|
223
|
-
{
|
|
224
|
-
this.postMessageToMainThread({
|
|
225
|
-
messageType: returnMessageType.isFullyInitialized,
|
|
226
|
-
messageData: undefined
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* @param data {WorkletReturnMessage}
|
|
232
|
-
*/
|
|
233
|
-
postMessageToMainThread(data)
|
|
234
|
-
{
|
|
235
|
-
this.port.postMessage(data);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/**
|
|
239
|
-
* @this {WorkletSpessaProcessor}
|
|
240
|
-
* @param message {WorkletMessage}
|
|
241
|
-
*/
|
|
242
|
-
handleMessage(message)
|
|
243
|
-
{
|
|
244
|
-
const data = message.messageData;
|
|
245
|
-
const channel = message.channelNumber;
|
|
246
|
-
|
|
247
|
-
let channelObject;
|
|
248
|
-
if (channel >= 0)
|
|
249
|
-
{
|
|
250
|
-
channelObject = this.synthesizer.midiAudioChannels[channel];
|
|
251
|
-
if (channelObject === undefined)
|
|
252
|
-
{
|
|
253
|
-
util.SpessaSynthWarn(`Trying to access channel ${channel} which does not exist... ignoring!`);
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
switch (message.messageType)
|
|
258
|
-
{
|
|
259
|
-
case workletMessageType.midiMessage:
|
|
260
|
-
this.synthesizer.processMessage(...data);
|
|
261
|
-
break;
|
|
262
|
-
|
|
263
|
-
case workletMessageType.customCcChange:
|
|
264
|
-
// custom controller change
|
|
265
|
-
channelObject.setCustomController(data[0], data[1]);
|
|
266
|
-
break;
|
|
267
|
-
|
|
268
|
-
case workletMessageType.ccReset:
|
|
269
|
-
if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
270
|
-
{
|
|
271
|
-
this.synthesizer.resetAllControllers();
|
|
272
|
-
}
|
|
273
|
-
else
|
|
274
|
-
{
|
|
275
|
-
channelObject.resetControllers();
|
|
276
|
-
}
|
|
277
|
-
break;
|
|
278
|
-
|
|
279
|
-
case workletMessageType.setChannelVibrato:
|
|
280
|
-
if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
281
|
-
{
|
|
282
|
-
for (let i = 0; i < this.synthesizer.midiAudioChannels.length; i++)
|
|
283
|
-
{
|
|
284
|
-
const chan = this.synthesizer.midiAudioChannels[i];
|
|
285
|
-
if (data.rate === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
286
|
-
{
|
|
287
|
-
chan.disableAndLockGSNRPN();
|
|
288
|
-
}
|
|
289
|
-
else
|
|
290
|
-
{
|
|
291
|
-
chan.setVibrato(data.depth, data.rate, data.delay);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
else if (data.rate === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
296
|
-
{
|
|
297
|
-
channelObject.disableAndLockGSNRPN();
|
|
298
|
-
}
|
|
299
|
-
else
|
|
300
|
-
{
|
|
301
|
-
channelObject.setVibrato(data.depth, data.rate, data.delay);
|
|
302
|
-
}
|
|
303
|
-
break;
|
|
304
|
-
|
|
305
|
-
case workletMessageType.stopAll:
|
|
306
|
-
if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
307
|
-
{
|
|
308
|
-
this.synthesizer.stopAllChannels(data === 1);
|
|
309
|
-
}
|
|
310
|
-
else
|
|
311
|
-
{
|
|
312
|
-
channelObject.stopAllNotes(data === 1);
|
|
313
|
-
}
|
|
314
|
-
break;
|
|
315
|
-
|
|
316
|
-
case workletMessageType.killNotes:
|
|
317
|
-
this.synthesizer.voiceKilling(data);
|
|
318
|
-
break;
|
|
319
|
-
|
|
320
|
-
case workletMessageType.muteChannel:
|
|
321
|
-
channelObject.muteChannel(data);
|
|
322
|
-
break;
|
|
323
|
-
|
|
324
|
-
case workletMessageType.addNewChannel:
|
|
325
|
-
this.synthesizer.createMidiChannel(true);
|
|
326
|
-
break;
|
|
327
|
-
|
|
328
|
-
case workletMessageType.debugMessage:
|
|
329
|
-
console.debug(this.synthesizer);
|
|
330
|
-
break;
|
|
331
|
-
|
|
332
|
-
case workletMessageType.setMasterParameter:
|
|
333
|
-
/**
|
|
334
|
-
* @type {masterParameterType}
|
|
335
|
-
*/
|
|
336
|
-
const type = data[0];
|
|
337
|
-
const value = data[1];
|
|
338
|
-
this.synthesizer.setMasterParameter(type, value);
|
|
339
|
-
break;
|
|
340
|
-
|
|
341
|
-
case workletMessageType.setDrums:
|
|
342
|
-
channelObject.setDrums(data);
|
|
343
|
-
break;
|
|
344
|
-
|
|
345
|
-
case workletMessageType.transpose:
|
|
346
|
-
if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
347
|
-
{
|
|
348
|
-
this.synthesizer.transposeAllChannels(data[0], data[1]);
|
|
349
|
-
}
|
|
350
|
-
else
|
|
351
|
-
{
|
|
352
|
-
channelObject.transposeChannel(data[0], data[1]);
|
|
353
|
-
}
|
|
354
|
-
break;
|
|
355
|
-
|
|
356
|
-
case workletMessageType.highPerformanceMode:
|
|
357
|
-
this.synthesizer.highPerformanceMode = data;
|
|
358
|
-
break;
|
|
359
|
-
|
|
360
|
-
case workletMessageType.lockController:
|
|
361
|
-
if (data[0] === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
362
|
-
{
|
|
363
|
-
channelObject.setPresetLock(data[1]);
|
|
364
|
-
}
|
|
365
|
-
else
|
|
366
|
-
{
|
|
367
|
-
channelObject.lockedControllers[data[0]] = data[1];
|
|
368
|
-
}
|
|
369
|
-
break;
|
|
370
|
-
|
|
371
|
-
case workletMessageType.sequencerSpecific:
|
|
372
|
-
const seq = this.sequencer;
|
|
373
|
-
const messageData = data.messageData;
|
|
374
|
-
const messageType = data.messageType;
|
|
375
|
-
switch (messageType)
|
|
376
|
-
{
|
|
377
|
-
default:
|
|
378
|
-
break;
|
|
379
|
-
|
|
380
|
-
case SpessaSynthSequencerMessageType.loadNewSongList:
|
|
381
|
-
try
|
|
382
|
-
{
|
|
383
|
-
/**
|
|
384
|
-
* @type {(BasicMIDI|{binary: ArrayBuffer, altName: string})[]}
|
|
385
|
-
*/
|
|
386
|
-
const sList = messageData[0];
|
|
387
|
-
const songMap = sList.map(s =>
|
|
388
|
-
{
|
|
389
|
-
if (s.duration)
|
|
390
|
-
{
|
|
391
|
-
// cloned objects don't have methods
|
|
392
|
-
return BasicMIDI.copyFrom(s);
|
|
393
|
-
}
|
|
394
|
-
return new MIDI(s.binary, s.altName);
|
|
395
|
-
});
|
|
396
|
-
seq.loadNewSongList(songMap, messageData[1]);
|
|
397
|
-
}
|
|
398
|
-
catch (e)
|
|
399
|
-
{
|
|
400
|
-
console.error(e);
|
|
401
|
-
this.postMessageToMainThread({
|
|
402
|
-
messageType: returnMessageType.sequencerSpecific,
|
|
403
|
-
messageData: {
|
|
404
|
-
messageType: SpessaSynthSequencerReturnMessageType.midiError,
|
|
405
|
-
messageData: e
|
|
406
|
-
}
|
|
407
|
-
});
|
|
408
|
-
}
|
|
409
|
-
break;
|
|
410
|
-
|
|
411
|
-
case SpessaSynthSequencerMessageType.pause:
|
|
412
|
-
seq.pause();
|
|
413
|
-
break;
|
|
414
|
-
|
|
415
|
-
case SpessaSynthSequencerMessageType.play:
|
|
416
|
-
seq.play(messageData);
|
|
417
|
-
break;
|
|
418
|
-
|
|
419
|
-
case SpessaSynthSequencerMessageType.stop:
|
|
420
|
-
seq.stop();
|
|
421
|
-
break;
|
|
422
|
-
|
|
423
|
-
case SpessaSynthSequencerMessageType.setTime:
|
|
424
|
-
seq.currentTime = messageData;
|
|
425
|
-
break;
|
|
426
|
-
|
|
427
|
-
case SpessaSynthSequencerMessageType.changeMIDIMessageSending:
|
|
428
|
-
seq.sendMIDIMessages = messageData;
|
|
429
|
-
break;
|
|
430
|
-
|
|
431
|
-
case SpessaSynthSequencerMessageType.setPlaybackRate:
|
|
432
|
-
seq.playbackRate = messageData;
|
|
433
|
-
break;
|
|
434
|
-
|
|
435
|
-
case SpessaSynthSequencerMessageType.setLoop:
|
|
436
|
-
const [loop, count] = messageData;
|
|
437
|
-
seq.loop = loop;
|
|
438
|
-
if (count === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
439
|
-
{
|
|
440
|
-
seq.loopCount = Infinity;
|
|
441
|
-
}
|
|
442
|
-
else
|
|
443
|
-
{
|
|
444
|
-
seq.loopCount = count;
|
|
445
|
-
}
|
|
446
|
-
break;
|
|
447
|
-
|
|
448
|
-
case SpessaSynthSequencerMessageType.changeSong:
|
|
449
|
-
switch (messageData[0])
|
|
450
|
-
{
|
|
451
|
-
case SongChangeType.forwards:
|
|
452
|
-
seq.nextSong();
|
|
453
|
-
break;
|
|
454
|
-
|
|
455
|
-
case SongChangeType.backwards:
|
|
456
|
-
seq.previousSong();
|
|
457
|
-
break;
|
|
458
|
-
|
|
459
|
-
case SongChangeType.shuffleOff:
|
|
460
|
-
seq.shuffleMode = false;
|
|
461
|
-
seq.songIndex = seq.shuffledSongIndexes[seq.songIndex];
|
|
462
|
-
break;
|
|
463
|
-
|
|
464
|
-
case SongChangeType.shuffleOn:
|
|
465
|
-
seq.shuffleMode = true;
|
|
466
|
-
seq.shuffleSongIndexes();
|
|
467
|
-
seq.songIndex = 0;
|
|
468
|
-
seq.loadCurrentSong();
|
|
469
|
-
break;
|
|
470
|
-
|
|
471
|
-
case SongChangeType.index:
|
|
472
|
-
seq.songIndex = messageData[1];
|
|
473
|
-
seq.loadCurrentSong();
|
|
474
|
-
break;
|
|
475
|
-
}
|
|
476
|
-
break;
|
|
477
|
-
|
|
478
|
-
case SpessaSynthSequencerMessageType.getMIDI:
|
|
479
|
-
this.postMessageToMainThread({
|
|
480
|
-
messageType: returnMessageType.sequencerSpecific,
|
|
481
|
-
messageData: {
|
|
482
|
-
messageType: SpessaSynthSequencerReturnMessageType.getMIDI,
|
|
483
|
-
messageData: seq.midiData
|
|
484
|
-
}
|
|
485
|
-
});
|
|
486
|
-
break;
|
|
487
|
-
|
|
488
|
-
case SpessaSynthSequencerMessageType.setSkipToFirstNote:
|
|
489
|
-
seq.skipToFirstNoteOn = messageData;
|
|
490
|
-
break;
|
|
491
|
-
|
|
492
|
-
case SpessaSynthSequencerMessageType.setPreservePlaybackState:
|
|
493
|
-
seq.preservePlaybackState = messageData;
|
|
494
|
-
}
|
|
495
|
-
break;
|
|
496
|
-
|
|
497
|
-
case workletMessageType.soundFontManager:
|
|
498
|
-
try
|
|
499
|
-
{
|
|
500
|
-
const sfManager = this.synthesizer.soundfontManager;
|
|
501
|
-
const type = data[0];
|
|
502
|
-
const messageData = data[1];
|
|
503
|
-
let font;
|
|
504
|
-
switch (type)
|
|
505
|
-
{
|
|
506
|
-
case WorkletSoundfontManagerMessageType.addNewSoundFont:
|
|
507
|
-
font = loadSoundFont(messageData[0]);
|
|
508
|
-
sfManager.addNewSoundFont(font, messageData[1], messageData[2]);
|
|
509
|
-
this.postMessageToMainThread({
|
|
510
|
-
messageType: returnMessageType.isFullyInitialized,
|
|
511
|
-
messageData: undefined
|
|
512
|
-
});
|
|
513
|
-
break;
|
|
514
|
-
|
|
515
|
-
case WorkletSoundfontManagerMessageType.reloadSoundFont:
|
|
516
|
-
font = loadSoundFont(messageData);
|
|
517
|
-
sfManager.reloadManager(font);
|
|
518
|
-
this.postMessageToMainThread({
|
|
519
|
-
messageType: returnMessageType.isFullyInitialized,
|
|
520
|
-
messageData: undefined
|
|
521
|
-
});
|
|
522
|
-
break;
|
|
523
|
-
|
|
524
|
-
case WorkletSoundfontManagerMessageType.deleteSoundFont:
|
|
525
|
-
sfManager.deleteSoundFont(messageData);
|
|
526
|
-
break;
|
|
527
|
-
|
|
528
|
-
case WorkletSoundfontManagerMessageType.rearrangeSoundFonts:
|
|
529
|
-
sfManager.rearrangeSoundFonts(messageData);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
catch (e)
|
|
533
|
-
{
|
|
534
|
-
this.postMessageToMainThread({
|
|
535
|
-
messageType: returnMessageType.soundfontError,
|
|
536
|
-
messageData: e
|
|
537
|
-
});
|
|
538
|
-
}
|
|
539
|
-
break;
|
|
540
|
-
|
|
541
|
-
case workletMessageType.keyModifierManager:
|
|
542
|
-
/**
|
|
543
|
-
* @type {workletKeyModifierMessageType}
|
|
544
|
-
*/
|
|
545
|
-
const keyMessageType = data[0];
|
|
546
|
-
const man = this.synthesizer.keyModifierManager;
|
|
547
|
-
const keyMessageData = data[1];
|
|
548
|
-
switch (keyMessageType)
|
|
549
|
-
{
|
|
550
|
-
default:
|
|
551
|
-
return;
|
|
552
|
-
|
|
553
|
-
case workletKeyModifierMessageType.addMapping:
|
|
554
|
-
man.addMapping(...keyMessageData);
|
|
555
|
-
break;
|
|
556
|
-
|
|
557
|
-
case workletKeyModifierMessageType.clearMappings:
|
|
558
|
-
man.clearMappings();
|
|
559
|
-
break;
|
|
560
|
-
|
|
561
|
-
case workletKeyModifierMessageType.deleteMapping:
|
|
562
|
-
man.deleteMapping(...keyMessageData);
|
|
563
|
-
}
|
|
564
|
-
break;
|
|
565
|
-
|
|
566
|
-
case workletMessageType.requestSynthesizerSnapshot:
|
|
567
|
-
const snapshot = SynthesizerSnapshot.createSynthesizerSnapshot(this.synthesizer);
|
|
568
|
-
this.postMessageToMainThread({
|
|
569
|
-
messageType: returnMessageType.synthesizerSnapshot,
|
|
570
|
-
messageData: snapshot
|
|
571
|
-
});
|
|
572
|
-
break;
|
|
573
|
-
|
|
574
|
-
case workletMessageType.setLogLevel:
|
|
575
|
-
SpessaSynthLogging(data[0], data[1], data[2], data[3]);
|
|
576
|
-
break;
|
|
577
|
-
|
|
578
|
-
case workletMessageType.setEffectsGain:
|
|
579
|
-
this.synthesizer.reverbGain = data[0];
|
|
580
|
-
this.synthesizer.chorusGain = data[1];
|
|
581
|
-
break;
|
|
582
|
-
|
|
583
|
-
case workletMessageType.destroyWorklet:
|
|
584
|
-
this.alive = false;
|
|
585
|
-
this.synthesizer.destroySynthProcessor();
|
|
586
|
-
delete this.synthesizer;
|
|
587
|
-
delete this.sequencer.midiData;
|
|
588
|
-
delete this.sequencer;
|
|
589
|
-
break;
|
|
590
|
-
|
|
591
|
-
default:
|
|
592
|
-
util.SpessaSynthWarn("Unrecognized event:", data);
|
|
593
|
-
break;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
// noinspection JSUnusedGlobalSymbols
|
|
598
|
-
/**
|
|
599
|
-
* the audio worklet processing logic
|
|
600
|
-
* @param inputs {Float32Array[][]} required by WebAudioAPI
|
|
601
|
-
* @param outputs {Float32Array[][]} the outputs to write to, only the first two channels of each are populated
|
|
602
|
-
* @returns {boolean} true unless it's not alive
|
|
603
|
-
*/
|
|
604
|
-
process(inputs, outputs)
|
|
605
|
-
{
|
|
606
|
-
if (!this.alive)
|
|
607
|
-
{
|
|
608
|
-
return false;
|
|
609
|
-
}
|
|
610
|
-
// process sequencer
|
|
611
|
-
this.sequencer.processTick();
|
|
612
|
-
|
|
613
|
-
if (this.oneOutputMode)
|
|
614
|
-
{
|
|
615
|
-
const out = outputs[0];
|
|
616
|
-
// 1 output with 32 channels.
|
|
617
|
-
// channels are ordered as follows:
|
|
618
|
-
// midiChannel1L, midiChannel1R,
|
|
619
|
-
// midiChannel2L, midiChannel2R
|
|
620
|
-
// and so on
|
|
621
|
-
/**
|
|
622
|
-
* @type {Float32Array[][]}
|
|
623
|
-
*/
|
|
624
|
-
const channelMap = [];
|
|
625
|
-
for (let i = 0; i < 32; i += 2)
|
|
626
|
-
{
|
|
627
|
-
channelMap.push([out[i], out[i + 1]]);
|
|
628
|
-
}
|
|
629
|
-
this.synthesizer.renderAudioSplit(
|
|
630
|
-
[], [], // effects are disabled
|
|
631
|
-
channelMap
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
else
|
|
635
|
-
{
|
|
636
|
-
// 18 outputs, each a stereo one
|
|
637
|
-
// 0: reverb
|
|
638
|
-
// 1: chorus
|
|
639
|
-
// 2: channel 1
|
|
640
|
-
// 3: channel 2
|
|
641
|
-
// and so on
|
|
642
|
-
this.synthesizer.renderAudioSplit(
|
|
643
|
-
outputs[0], // reverb
|
|
644
|
-
outputs[1], // chorus
|
|
645
|
-
outputs.slice(2)
|
|
646
|
-
);
|
|
647
|
-
}
|
|
648
|
-
return true;
|
|
649
|
-
}
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// noinspection JSUnresolvedReference
|
|
653
|
-
registerProcessor(WORKLET_PROCESSOR_NAME, WorkletSpessaProcessor);
|
|
654
|
-
util.SpessaSynthInfo("%cProcessor successfully registered!", consoleColors.recognized);
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { BasicMIDI, SynthesizerSnapshot } from "spessasynth_core";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The absolute path (from the spessasynth_lib folder) to the worklet module
|
|
5
|
-
* @type {string}
|
|
6
|
-
*/
|
|
7
|
-
export const WORKLET_URL_ABSOLUTE = "synthetizer/worklet_processor.min.js";
|
|
8
|
-
/**
|
|
9
|
-
* @typedef {Object} StartRenderingDataConfig
|
|
10
|
-
* @property {BasicMIDI} parsedMIDI - the MIDI to render
|
|
11
|
-
* @property {SynthesizerSnapshot?} snapshot - the snapshot to apply
|
|
12
|
-
* @property {boolean?} oneOutput - if synth should use one output with 32 channels (2 audio channels for each midi channel).
|
|
13
|
-
* this disabled chorus and reverb.
|
|
14
|
-
* @property {number?} loopCount - the times to loop the song
|
|
15
|
-
* @property {SequencerOptions?} sequencerOptions - the options to pass to the sequencer
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
export const WORKLET_PROCESSOR_NAME = "spessasynth-worklet-processor";
|