spessasynth_lib 3.15.0 → 3.16.0
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/@types/midi_parser/used_keys_loaded.d.ts +4 -2
- package/@types/soundfont/read/presets.d.ts +1 -0
- package/@types/soundfont/soundfont.d.ts +12 -0
- package/@types/synthetizer/synth_soundfont_manager.d.ts +52 -0
- package/@types/synthetizer/synthetizer.d.ts +13 -1
- package/@types/synthetizer/worklet_system/message_protocol/worklet_message.d.ts +2 -2
- package/@types/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/sfman_message.d.ts +7 -0
- package/README.md +1 -0
- package/midi_parser/midi_loader.js +1 -1
- package/midi_parser/used_keys_loaded.js +1 -1
- package/package.json +1 -1
- package/sequencer/worklet_sequencer/song_control.js +2 -2
- package/soundfont/read/presets.js +2 -0
- package/soundfont/soundfont.js +25 -0
- package/synthetizer/synth_soundfont_manager.js +111 -0
- package/synthetizer/synthetizer.js +19 -12
- package/synthetizer/worklet_processor.min.js +6 -6
- package/synthetizer/worklet_system/main_processor.js +132 -9
- package/synthetizer/worklet_system/message_protocol/handle_message.js +6 -5
- package/synthetizer/worklet_system/message_protocol/message_sending.js +0 -13
- package/synthetizer/worklet_system/message_protocol/worklet_message.js +4 -3
- package/synthetizer/worklet_system/worklet_methods/note_off.js +5 -4
- package/synthetizer/worklet_system/worklet_methods/note_on.js +0 -1
- package/synthetizer/worklet_system/worklet_methods/program_control.js +11 -16
- package/synthetizer/worklet_system/worklet_methods/voice_control.js +20 -12
- package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/sfman_message.js +9 -0
- package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +231 -0
- package/synthetizer/worklet_system/worklet_processor.js +1 -1
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +5 -5
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +1 -3
- package/utils/buffer_to_wav.js +42 -38
- package/synthetizer/worklet_system/combine_class.js +0 -104
|
@@ -3,10 +3,42 @@ import { SoundFont2 } from '../../soundfont/soundfont.js'
|
|
|
3
3
|
import { WorkletSequencer } from '../../sequencer/worklet_sequencer/worklet_sequencer.js'
|
|
4
4
|
import { SpessaSynthInfo } from '../../utils/loggin.js'
|
|
5
5
|
import { consoleColors } from '../../utils/other.js'
|
|
6
|
-
import { PAN_SMOOTHING_FACTOR } from './worklet_methods/voice_control.js'
|
|
6
|
+
import { PAN_SMOOTHING_FACTOR, releaseVoice, renderVoice, voiceKilling } from './worklet_methods/voice_control.js'
|
|
7
7
|
import { ALL_CHANNELS_OR_DIFFERENT_ACTION, returnMessageType } from './message_protocol/worklet_message.js'
|
|
8
8
|
import { stbvorbis } from '../../externals/stbvorbis_sync/stbvorbis_sync.min.js'
|
|
9
9
|
import { VOLUME_ENVELOPE_SMOOTHING_FACTOR } from './worklet_utilities/volume_envelope.js'
|
|
10
|
+
import { handleMessage } from './message_protocol/handle_message.js'
|
|
11
|
+
import { callEvent, sendChannelProperties } from './message_protocol/message_sending.js'
|
|
12
|
+
import { systemExclusive } from './worklet_methods/system_exclusive.js'
|
|
13
|
+
import { noteOn } from './worklet_methods/note_on.js'
|
|
14
|
+
import { killNote, noteOff, stopAll, stopAllChannels } from './worklet_methods/note_off.js'
|
|
15
|
+
import {
|
|
16
|
+
channelPressure, pitchWheel,
|
|
17
|
+
polyPressure, setChannelTuning, setChannelTuningSemitones, setMasterTuning, setModulationDepth, setOctaveTuning,
|
|
18
|
+
transposeAllChannels,
|
|
19
|
+
transposeChannel,
|
|
20
|
+
} from './worklet_methods/tuning_control.js'
|
|
21
|
+
import {
|
|
22
|
+
controllerChange,
|
|
23
|
+
muteChannel,
|
|
24
|
+
setMasterGain,
|
|
25
|
+
setMasterPan,
|
|
26
|
+
setMIDIVolume,
|
|
27
|
+
} from './worklet_methods/controller_control.js'
|
|
28
|
+
import { disableAndLockVibrato, setVibrato } from './worklet_methods/vibrato_control.js'
|
|
29
|
+
import { dataEntryCoarse, dataEntryFine } from './worklet_methods/data_entry.js'
|
|
30
|
+
import { createWorkletChannel } from './worklet_utilities/worklet_processor_channel.js'
|
|
31
|
+
import { resetAllControllers, resetControllers, resetParameters } from './worklet_methods/reset_controllers.js'
|
|
32
|
+
import {
|
|
33
|
+
clearSoundFont,
|
|
34
|
+
getPreset,
|
|
35
|
+
programChange,
|
|
36
|
+
reloadSoundFont, sampleDump, sendPresetList,
|
|
37
|
+
setDrums,
|
|
38
|
+
setPreset,
|
|
39
|
+
} from './worklet_methods/program_control.js'
|
|
40
|
+
import { applySynthesizerSnapshot, sendSynthesizerSnapshot } from './worklet_methods/snapshot.js'
|
|
41
|
+
import { WorkletSoundfontManager } from './worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js'
|
|
10
42
|
|
|
11
43
|
|
|
12
44
|
/**
|
|
@@ -18,7 +50,7 @@ export const MIN_NOTE_LENGTH = 0.07; // if the note is released faster than that
|
|
|
18
50
|
|
|
19
51
|
export const SYNTHESIZER_GAIN = 1.0;
|
|
20
52
|
|
|
21
|
-
|
|
53
|
+
class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
22
54
|
{
|
|
23
55
|
/**
|
|
24
56
|
* Creates a new worklet synthesis system. contains all channels
|
|
@@ -114,9 +146,9 @@ export class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
114
146
|
try
|
|
115
147
|
{
|
|
116
148
|
/**
|
|
117
|
-
* @type {
|
|
149
|
+
* @type {WorkletSoundfontManager}
|
|
118
150
|
*/
|
|
119
|
-
this.
|
|
151
|
+
this.soundfontManager = new WorkletSoundfontManager(options.processorOptions.soundfont, this.postReady.bind(this));
|
|
120
152
|
}
|
|
121
153
|
catch (e)
|
|
122
154
|
{
|
|
@@ -186,14 +218,35 @@ export class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
186
218
|
}
|
|
187
219
|
|
|
188
220
|
stbvorbis.isInitialized.then(() => {
|
|
189
|
-
this.
|
|
190
|
-
messageType: returnMessageType.ready,
|
|
191
|
-
messageData: undefined
|
|
192
|
-
});
|
|
221
|
+
this.postReady();
|
|
193
222
|
SpessaSynthInfo("%cSpessaSynth is ready!", consoleColors.recognized);
|
|
194
223
|
});
|
|
195
224
|
}
|
|
196
225
|
|
|
226
|
+
/**
|
|
227
|
+
* @param data {WorkletReturnMessage}
|
|
228
|
+
*/
|
|
229
|
+
post(data)
|
|
230
|
+
{
|
|
231
|
+
if(!this.enableEventSystem)
|
|
232
|
+
{
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.port.postMessage(data);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
postReady()
|
|
239
|
+
{
|
|
240
|
+
if(!this.enableEventSystem)
|
|
241
|
+
{
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
this.port.postMessage({
|
|
245
|
+
messageType: returnMessageType.ready,
|
|
246
|
+
messageData: undefined
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
197
250
|
/**
|
|
198
251
|
* @returns {number}
|
|
199
252
|
*/
|
|
@@ -289,4 +342,74 @@ export class SpessaSynthProcessor extends AudioWorkletProcessor
|
|
|
289
342
|
}
|
|
290
343
|
return true;
|
|
291
344
|
}
|
|
292
|
-
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// include other methods
|
|
348
|
+
// voice related
|
|
349
|
+
SpessaSynthProcessor.prototype.renderVoice = renderVoice;
|
|
350
|
+
SpessaSynthProcessor.prototype.releaseVoice = releaseVoice;
|
|
351
|
+
SpessaSynthProcessor.prototype.voiceKilling = voiceKilling;
|
|
352
|
+
|
|
353
|
+
// message port related
|
|
354
|
+
SpessaSynthProcessor.prototype.handleMessage = handleMessage;
|
|
355
|
+
SpessaSynthProcessor.prototype.sendChannelProperties = sendChannelProperties;
|
|
356
|
+
SpessaSynthProcessor.prototype.callEvent = callEvent;
|
|
357
|
+
|
|
358
|
+
// system exlcusive related
|
|
359
|
+
SpessaSynthProcessor.prototype.systemExclusive = systemExclusive;
|
|
360
|
+
|
|
361
|
+
// note messages related
|
|
362
|
+
SpessaSynthProcessor.prototype.noteOn = noteOn;
|
|
363
|
+
SpessaSynthProcessor.prototype.noteOff = noteOff;
|
|
364
|
+
SpessaSynthProcessor.prototype.polyPressure = polyPressure;
|
|
365
|
+
SpessaSynthProcessor.prototype.killNote = killNote;
|
|
366
|
+
SpessaSynthProcessor.prototype.stopAll = stopAll;
|
|
367
|
+
SpessaSynthProcessor.prototype.stopAllChannels = stopAllChannels;
|
|
368
|
+
SpessaSynthProcessor.prototype.muteChannel = muteChannel;
|
|
369
|
+
|
|
370
|
+
// custom vibrato related
|
|
371
|
+
SpessaSynthProcessor.prototype.setVibrato = setVibrato;
|
|
372
|
+
SpessaSynthProcessor.prototype.disableAndLockVibrato = disableAndLockVibrato;
|
|
373
|
+
|
|
374
|
+
// data entry related
|
|
375
|
+
SpessaSynthProcessor.prototype.dataEntryCoarse = dataEntryCoarse;
|
|
376
|
+
SpessaSynthProcessor.prototype.dataEntryFine = dataEntryFine;
|
|
377
|
+
|
|
378
|
+
// channel related
|
|
379
|
+
SpessaSynthProcessor.prototype.createWorkletChannel = createWorkletChannel;
|
|
380
|
+
SpessaSynthProcessor.prototype.controllerChange = controllerChange;
|
|
381
|
+
SpessaSynthProcessor.prototype.channelPressure = channelPressure;
|
|
382
|
+
SpessaSynthProcessor.prototype.resetAllControllers = resetAllControllers;
|
|
383
|
+
SpessaSynthProcessor.prototype.resetControllers = resetControllers;
|
|
384
|
+
SpessaSynthProcessor.prototype.resetParameters = resetParameters;
|
|
385
|
+
|
|
386
|
+
// master parameter related
|
|
387
|
+
SpessaSynthProcessor.prototype.setMasterGain = setMasterGain;
|
|
388
|
+
SpessaSynthProcessor.prototype.setMasterPan = setMasterPan;
|
|
389
|
+
SpessaSynthProcessor.prototype.setMIDIVolume = setMIDIVolume;
|
|
390
|
+
|
|
391
|
+
// tuning related
|
|
392
|
+
SpessaSynthProcessor.prototype.transposeAllChannels = transposeAllChannels;
|
|
393
|
+
SpessaSynthProcessor.prototype.transposeChannel = transposeChannel;
|
|
394
|
+
SpessaSynthProcessor.prototype.setChannelTuning = setChannelTuning;
|
|
395
|
+
SpessaSynthProcessor.prototype.setChannelTuningSemitones = setChannelTuningSemitones;
|
|
396
|
+
SpessaSynthProcessor.prototype.setMasterTuning = setMasterTuning;
|
|
397
|
+
SpessaSynthProcessor.prototype.setModulationDepth = setModulationDepth;
|
|
398
|
+
SpessaSynthProcessor.prototype.pitchWheel = pitchWheel;
|
|
399
|
+
SpessaSynthProcessor.prototype.setOctaveTuning = setOctaveTuning;
|
|
400
|
+
|
|
401
|
+
// program related
|
|
402
|
+
SpessaSynthProcessor.prototype.programChange = programChange;
|
|
403
|
+
SpessaSynthProcessor.prototype.getPreset = getPreset;
|
|
404
|
+
SpessaSynthProcessor.prototype.setPreset = setPreset;
|
|
405
|
+
SpessaSynthProcessor.prototype.setDrums = setDrums;
|
|
406
|
+
SpessaSynthProcessor.prototype.reloadSoundFont = reloadSoundFont;
|
|
407
|
+
SpessaSynthProcessor.prototype.clearSoundFont = clearSoundFont;
|
|
408
|
+
SpessaSynthProcessor.prototype.sampleDump = sampleDump;
|
|
409
|
+
SpessaSynthProcessor.prototype.sendPresetList = sendPresetList;
|
|
410
|
+
|
|
411
|
+
// snapshot related
|
|
412
|
+
SpessaSynthProcessor.prototype.sendSynthesizerSnapshot = sendSynthesizerSnapshot;
|
|
413
|
+
SpessaSynthProcessor.prototype.applySynthesizerSnapshot = applySynthesizerSnapshot;
|
|
414
|
+
|
|
415
|
+
export { SpessaSynthProcessor }
|
|
@@ -3,7 +3,7 @@ import { SpessaSynthLogging, SpessaSynthWarn } from '../../../utils/loggin.js'
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @this {SpessaSynthProcessor}
|
|
6
|
-
* @param message
|
|
6
|
+
* @param message {WorkletMessage}
|
|
7
7
|
*/
|
|
8
8
|
export function handleMessage(message)
|
|
9
9
|
{
|
|
@@ -95,10 +95,6 @@ export function handleMessage(message)
|
|
|
95
95
|
}
|
|
96
96
|
break;
|
|
97
97
|
|
|
98
|
-
case workletMessageType.reloadSoundFont:
|
|
99
|
-
this.reloadSoundFont(data);
|
|
100
|
-
break;
|
|
101
|
-
|
|
102
98
|
case workletMessageType.stopAll:
|
|
103
99
|
if(channel === ALL_CHANNELS_OR_DIFFERENT_ACTION)
|
|
104
100
|
{
|
|
@@ -182,6 +178,11 @@ export function handleMessage(message)
|
|
|
182
178
|
this.sequencer.processMessage(data.messageType, data.messageData);
|
|
183
179
|
break;
|
|
184
180
|
|
|
181
|
+
case workletMessageType.soundFontManager:
|
|
182
|
+
this.soundfontManager.handleMessage(data[0], data[1]);
|
|
183
|
+
this.clearSoundFont(true, false);
|
|
184
|
+
break;
|
|
185
|
+
|
|
185
186
|
case workletMessageType.requestSynthesizerSnapshot:
|
|
186
187
|
this.sendSynthesizerSnapshot();
|
|
187
188
|
break;
|
|
@@ -23,19 +23,6 @@ export function callEvent(eventName, eventData)
|
|
|
23
23
|
})
|
|
24
24
|
}
|
|
25
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
26
|
/**
|
|
40
27
|
* @typedef {Object} ChannelProperty
|
|
41
28
|
* @property {number} voicesAmount
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @property {number} killNote - 6 -> midiNote<number>
|
|
11
11
|
* @property {number} ccReset - 7 -> (no data) note: if channel is -1 then reset all channels
|
|
12
12
|
* @property {number} setChannelVibrato - 8 -> {frequencyHz: number, depthCents: number, delaySeconds: number} note: if channel is -1 then stop all channels note 2: if rate is -1, it means locking
|
|
13
|
-
* @property {number}
|
|
13
|
+
* @property {number} soundFontManager - 9 -> [messageType<WorkletSoundfontManagerMessageType> messageData<any>] note: refer to sfman_message.js
|
|
14
14
|
* @property {number} stopAll - 10 -> force<number> (0 false, 1 true) note: if channel is -1 then stop all channels
|
|
15
15
|
* @property {number} killNotes - 11 -> amount<number>
|
|
16
16
|
* @property {number} muteChannel - 12 -> isMuted<boolean>
|
|
@@ -38,7 +38,7 @@ export const workletMessageType = {
|
|
|
38
38
|
killNote: 6,
|
|
39
39
|
ccReset: 7,
|
|
40
40
|
setChannelVibrato: 8,
|
|
41
|
-
|
|
41
|
+
soundFontManager: 9,
|
|
42
42
|
stopAll: 10,
|
|
43
43
|
killNotes: 11,
|
|
44
44
|
muteChannel: 12,
|
|
@@ -96,7 +96,8 @@ export const ALL_CHANNELS_OR_DIFFERENT_ACTION = -1;
|
|
|
96
96
|
* |PresetListElement[]
|
|
97
97
|
* |string
|
|
98
98
|
* |{messageType: WorkletSequencerReturnMessageType, messageData: any}
|
|
99
|
-
* |SynthesizerSnapshot
|
|
99
|
+
* |SynthesizerSnapshot
|
|
100
|
+
* |[WorkletSoundfontManagerMessageType, any]} messageData - the message's data
|
|
100
101
|
*
|
|
101
102
|
* 0 - channel properties -> [...<ChannelProperty>] see message_sending.js line 29
|
|
102
103
|
* 1 - event call -> {eventName<string>, eventData:<the event's data>}
|
|
@@ -15,7 +15,8 @@ export function noteOff(channel, midiNote)
|
|
|
15
15
|
SpessaSynthWarn(`Received a noteOn for note`, midiNote, "Ignoring.");
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
const channelObject = this.workletProcessorChannels[channel];
|
|
19
|
+
const actualNote = midiNote + channelObject.channelTransposeKeyShift;
|
|
19
20
|
|
|
20
21
|
// if high performance mode, kill notes instead of stopping them
|
|
21
22
|
if(this.highPerformanceMode)
|
|
@@ -23,14 +24,14 @@ export function noteOff(channel, midiNote)
|
|
|
23
24
|
// if the channel is percussion channel, do not kill the notes
|
|
24
25
|
if(!this.workletProcessorChannels[channel].drumChannel)
|
|
25
26
|
{
|
|
26
|
-
this.killNote(channel,
|
|
27
|
+
this.killNote(channel, actualNote);
|
|
27
28
|
return;
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const channelVoices = this.workletProcessorChannels[channel].voices;
|
|
32
33
|
channelVoices.forEach(v => {
|
|
33
|
-
if(v.midiNote !==
|
|
34
|
+
if(v.midiNote !== actualNote || v.isInRelease === true)
|
|
34
35
|
{
|
|
35
36
|
return;
|
|
36
37
|
}
|
|
@@ -44,7 +45,7 @@ export function noteOff(channel, midiNote)
|
|
|
44
45
|
}
|
|
45
46
|
});
|
|
46
47
|
this.callEvent("noteoff", {
|
|
47
|
-
midiNote: midiNote
|
|
48
|
+
midiNote: midiNote,
|
|
48
49
|
channel: channel
|
|
49
50
|
});
|
|
50
51
|
}
|
|
@@ -57,7 +57,6 @@ export function noteOn(channel, midiNote, velocity, enableDebugging = false, sen
|
|
|
57
57
|
data.sampleData
|
|
58
58
|
),
|
|
59
59
|
channelObject.cachedVoices,
|
|
60
|
-
channelObject.presetUsesOverride ? this.soundfont.samples.length : 0, // this is done to prevent samples overlapping
|
|
61
60
|
enableDebugging
|
|
62
61
|
);
|
|
63
62
|
|
|
@@ -43,21 +43,21 @@ export function programChange(channel, programNumber, userChange=false)
|
|
|
43
43
|
if(this.soundfontBankOffset === 0)
|
|
44
44
|
{
|
|
45
45
|
preset = this.overrideSoundfont.getPreset(0, programNumber);
|
|
46
|
+
sentBank = preset.bank;
|
|
46
47
|
}
|
|
47
48
|
else
|
|
48
49
|
{
|
|
49
|
-
preset = this.
|
|
50
|
+
preset = this.soundfontManager.getPreset(bank, programNumber);
|
|
50
51
|
sentBank = preset.bank;
|
|
51
52
|
channelObject.presetUsesOverride = false;
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
55
|
else
|
|
55
56
|
{
|
|
56
|
-
preset = this.
|
|
57
|
+
preset = this.soundfontManager.getPreset(bank, programNumber);
|
|
57
58
|
sentBank = preset.bank;
|
|
58
59
|
channelObject.presetUsesOverride = false;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
61
|
this.setPreset(channel, preset);
|
|
62
62
|
this.callEvent("programchange",{
|
|
63
63
|
channel: channel,
|
|
@@ -90,7 +90,7 @@ export function getPreset(bank, program)
|
|
|
90
90
|
return this.overrideSoundfont.getPreset(0, program);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
return this.
|
|
93
|
+
return this.soundfontManager.getPreset(bank, program);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
|
|
@@ -159,9 +159,10 @@ export function setDrums(channel, isDrum)
|
|
|
159
159
|
*/
|
|
160
160
|
export function sendPresetList()
|
|
161
161
|
{
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
/**
|
|
163
|
+
* @type {{bank: number, presetName: string, program: number}[]}
|
|
164
|
+
*/
|
|
165
|
+
const mainFont = this.soundfontManager.getPresetList();
|
|
165
166
|
if(this.overrideSoundfont !== undefined)
|
|
166
167
|
{
|
|
167
168
|
this.overrideSoundfont.presets.forEach(p => {
|
|
@@ -223,23 +224,17 @@ export function clearSoundFont(sendPresets = true, clearOverride = true)
|
|
|
223
224
|
export function reloadSoundFont(buffer, isOverride = false)
|
|
224
225
|
{
|
|
225
226
|
this.clearSoundFont(false, isOverride);
|
|
226
|
-
if(!isOverride)
|
|
227
|
-
{
|
|
228
|
-
delete this.soundfont.dataArray;
|
|
229
|
-
this.soundfont.samples.length = 0;
|
|
230
|
-
this.soundfont.instruments.length = 0;
|
|
231
|
-
this.soundfont.presets.length = 0;
|
|
232
|
-
delete this.soundfont;
|
|
233
|
-
}
|
|
234
227
|
try
|
|
235
228
|
{
|
|
236
229
|
if(isOverride)
|
|
237
230
|
{
|
|
238
231
|
this.overrideSoundfont = new SoundFont2(buffer);
|
|
232
|
+
// assign sample offset
|
|
233
|
+
this.overrideSoundfont.setSampleIDOffset(this.soundfontManager.totalSoundfontOffset)
|
|
239
234
|
}
|
|
240
235
|
else
|
|
241
236
|
{
|
|
242
|
-
this.
|
|
237
|
+
this.soundfontManager.reloadManager(buffer);
|
|
243
238
|
}
|
|
244
239
|
}
|
|
245
240
|
catch (e)
|
|
@@ -58,36 +58,37 @@ export function renderVoice(
|
|
|
58
58
|
let targetKey = voice.targetKey;
|
|
59
59
|
|
|
60
60
|
// calculate tuning
|
|
61
|
-
let cents = voice.modulatedGenerators[generatorTypes.fineTune]
|
|
62
|
-
+ channel.customControllers[customControllers.channelTuning]
|
|
63
|
-
+ channel.customControllers[customControllers.channelTransposeFine]
|
|
64
|
-
+ channel.customControllers[customControllers.masterTuning]
|
|
65
|
-
+ channel.channelOctaveTuning[voice.midiNote % 12];
|
|
66
|
-
let semitones = voice.modulatedGenerators[generatorTypes.coarseTune]
|
|
67
|
-
+ channel.customControllers[customControllers.channelTuningSemitones];
|
|
61
|
+
let cents = voice.modulatedGenerators[generatorTypes.fineTune] // soundfont fine tune
|
|
62
|
+
+ channel.customControllers[customControllers.channelTuning] // RPN channel fine tuning
|
|
63
|
+
+ channel.customControllers[customControllers.channelTransposeFine] // custom tuning (synth.transpose)
|
|
64
|
+
+ channel.customControllers[customControllers.masterTuning] // master tuning, set by sysEx
|
|
65
|
+
+ channel.channelOctaveTuning[voice.midiNote % 12]; // MTS octave tuning
|
|
66
|
+
let semitones = voice.modulatedGenerators[generatorTypes.coarseTune] // soundfont coarse tuning
|
|
67
|
+
+ channel.customControllers[customControllers.channelTuningSemitones]; // RPN channel coarse tuning
|
|
68
68
|
|
|
69
69
|
// midi tuning standard
|
|
70
70
|
const tuning = this.tunings[channel.preset.program]?.[targetKey];
|
|
71
71
|
if(tuning?.midiNote >= 0)
|
|
72
72
|
{
|
|
73
|
+
// override key
|
|
73
74
|
targetKey = tuning.midiNote;
|
|
75
|
+
// add microtonal tuning
|
|
74
76
|
cents += tuning.centTuning;
|
|
75
77
|
}
|
|
76
78
|
|
|
77
|
-
// calculate tuning by key
|
|
79
|
+
// calculate tuning by key using soundfont's scale tuning
|
|
78
80
|
cents += (targetKey - voice.sample.rootKey) * voice.modulatedGenerators[generatorTypes.scaleTuning];
|
|
79
81
|
|
|
80
82
|
// vibrato LFO
|
|
81
83
|
const vibratoDepth = voice.modulatedGenerators[generatorTypes.vibLfoToPitch];
|
|
82
84
|
if(vibratoDepth !== 0)
|
|
83
85
|
{
|
|
86
|
+
// calculate start time and lfo value
|
|
84
87
|
const vibStart = voice.startTime + timecentsToSeconds(voice.modulatedGenerators[generatorTypes.delayVibLFO]);
|
|
85
88
|
const vibFreqHz = absCentsToHz(voice.modulatedGenerators[generatorTypes.freqVibLFO]);
|
|
86
89
|
const lfoVal = getLFOValue(vibStart, vibFreqHz, currentTime);
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
cents += lfoVal * (vibratoDepth * channel.customControllers[customControllers.modulationMultiplier]);
|
|
90
|
-
}
|
|
90
|
+
// use modulation multiplier (RPN modulation depth)
|
|
91
|
+
cents += lfoVal * (vibratoDepth * channel.customControllers[customControllers.modulationMultiplier]);
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
// lowpass frequency
|
|
@@ -100,17 +101,22 @@ export function renderVoice(
|
|
|
100
101
|
let modLfoCentibels = 0;
|
|
101
102
|
if(modPitchDepth + modFilterDepth + modVolDepth !== 0)
|
|
102
103
|
{
|
|
104
|
+
// calculate start time and lfo value
|
|
103
105
|
const modStart = voice.startTime + timecentsToSeconds(voice.modulatedGenerators[generatorTypes.delayModLFO]);
|
|
104
106
|
const modFreqHz = absCentsToHz(voice.modulatedGenerators[generatorTypes.freqModLFO]);
|
|
105
107
|
const modLfoValue = getLFOValue(modStart, modFreqHz, currentTime);
|
|
108
|
+
// use modulation multiplier (RPN modulation depth)
|
|
106
109
|
cents += modLfoValue * (modPitchDepth * channel.customControllers[customControllers.modulationMultiplier]);
|
|
110
|
+
// volenv volume offset
|
|
107
111
|
modLfoCentibels = modLfoValue * modVolDepth;
|
|
112
|
+
// lowpass frequency
|
|
108
113
|
lowpassCents += modLfoValue * modFilterDepth;
|
|
109
114
|
}
|
|
110
115
|
|
|
111
116
|
// channel vibrato (GS NRPN)
|
|
112
117
|
if(channel.channelVibrato.depth > 0)
|
|
113
118
|
{
|
|
119
|
+
// same as others
|
|
114
120
|
const channelVibrato = getLFOValue(voice.startTime + channel.channelVibrato.delay, channel.channelVibrato.rate, currentTime);
|
|
115
121
|
if(channelVibrato)
|
|
116
122
|
{
|
|
@@ -122,6 +128,7 @@ export function renderVoice(
|
|
|
122
128
|
const modEnvPitchDepth = voice.modulatedGenerators[generatorTypes.modEnvToPitch];
|
|
123
129
|
const modEnvFilterDepth = voice.modulatedGenerators[generatorTypes.modEnvToFilterFc];
|
|
124
130
|
const modEnv = getModEnvValue(voice, currentTime);
|
|
131
|
+
// apply values
|
|
125
132
|
lowpassCents += modEnv * modEnvFilterDepth;
|
|
126
133
|
cents += modEnv * modEnvPitchDepth;
|
|
127
134
|
|
|
@@ -152,6 +159,7 @@ export function renderVoice(
|
|
|
152
159
|
voice.currentPan += (pan - voice.currentPan) * this.panSmoothingFactor; // smooth out pan to prevent clicking
|
|
153
160
|
const panLeft = Math.cos(HALF_PI * voice.currentPan) * this.panLeft;
|
|
154
161
|
const panRight = Math.sin(HALF_PI * voice.currentPan) * this.panRight;
|
|
162
|
+
// disable reverb and chorus in one output mode
|
|
155
163
|
const reverb = this.oneOutputMode ? 0 : voice.modulatedGenerators[generatorTypes.reverbEffectsSend];
|
|
156
164
|
const chorus = this.oneOutputMode ? 0 : voice.modulatedGenerators[generatorTypes.chorusEffectsSend];
|
|
157
165
|
panVoice(
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @enum {number}
|
|
3
|
+
*/
|
|
4
|
+
export const WorkletSoundfontManagerMessageType = {
|
|
5
|
+
reloadSoundFont: 0, // buffer<ArrayBuffer>
|
|
6
|
+
addNewSoundFont: 2, // [buffer<ArrayBuffer>, id<string>, bankOffset<number>]
|
|
7
|
+
deleteSoundFont: 3, // id<string>
|
|
8
|
+
rearrangeSoundFonts: 4, // newOrder<string[]> // where string is the id
|
|
9
|
+
}
|