spessasynth_lib 3.24.18 → 3.24.22
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/midi_parser/basic_midi.js +2 -0
- package/midi_parser/midi_message.js +1 -1
- package/package.json +1 -1
- package/soundfont/basic_soundfont/basic_preset.js +4 -2
- package/soundfont/basic_soundfont/basic_sample.js +2 -2
- package/soundfont/basic_soundfont/modulator.js +19 -4
- package/soundfont/dls/articulator_converter.js +14 -5
- package/soundfont/dls/dls_zone.js +1 -1
- package/soundfont/dls/read_articulation.js +3 -3
- package/soundfont/dls/read_region.js +21 -9
- package/soundfont/read_sf2/samples.js +9 -5
- package/synthetizer/synth_soundfont_manager.js +2 -2
- package/synthetizer/synthetizer.js +86 -55
- package/synthetizer/worklet_processor.min.js +11 -11
- package/synthetizer/worklet_system/main_processor.js +189 -96
- package/synthetizer/worklet_system/message_protocol/handle_message.js +2 -1
- package/synthetizer/worklet_system/message_protocol/message_sending.js +5 -4
- package/synthetizer/worklet_system/snapshot/channel_snapshot.js +1 -0
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/note_on.js +9 -4
- package/synthetizer/worklet_system/worklet_methods/render_voice.js +26 -22
- package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +1 -5
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_master_tuning.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_modulation_depth.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_octave_tuning.js +5 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning_semitones.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_channel.js +5 -2
- package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +11 -2
- package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +38 -15
- package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +13 -13
- package/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +2 -2
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +35 -20
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +20 -16
package/package.json
CHANGED
|
@@ -93,6 +93,7 @@ export class BasicPreset
|
|
|
93
93
|
this.presetZones.splice(index, 1);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
// noinspection JSUnusedGlobalSymbols
|
|
96
97
|
/**
|
|
97
98
|
* Preloads all samples (async)
|
|
98
99
|
*/
|
|
@@ -210,7 +211,7 @@ export class BasicPreset
|
|
|
210
211
|
|
|
211
212
|
presetZonesInRange.forEach(zone =>
|
|
212
213
|
{
|
|
213
|
-
// global zone is already taken into account earlier
|
|
214
|
+
// the global zone is already taken into account earlier
|
|
214
215
|
if (zone.instrument.instrumentZones.length < 1)
|
|
215
216
|
{
|
|
216
217
|
return;
|
|
@@ -288,7 +289,8 @@ export class BasicPreset
|
|
|
288
289
|
m => Modulator.isIdentical(mod, m));
|
|
289
290
|
if (identicalInstrumentModulator !== -1)
|
|
290
291
|
{
|
|
291
|
-
// sum the amounts
|
|
292
|
+
// sum the amounts
|
|
293
|
+
// (this makes a new modulator because otherwise it would overwrite the one in the soundfont!
|
|
292
294
|
finalModulatorList[identicalInstrumentModulator] = finalModulatorList[identicalInstrumentModulator].sumTransform(
|
|
293
295
|
mod);
|
|
294
296
|
}
|
|
@@ -63,12 +63,12 @@ export class BasicSample
|
|
|
63
63
|
*/
|
|
64
64
|
this.sampleType = sampleType;
|
|
65
65
|
/**
|
|
66
|
-
* Relative to start of the sample in sample points
|
|
66
|
+
* Relative to the start of the sample in sample points
|
|
67
67
|
* @type {number}
|
|
68
68
|
*/
|
|
69
69
|
this.sampleLoopStartIndex = loopStart;
|
|
70
70
|
/**
|
|
71
|
-
* Relative to start of the sample in sample points
|
|
71
|
+
* Relative to the start of the sample in sample points
|
|
72
72
|
* @type {number}
|
|
73
73
|
*/
|
|
74
74
|
this.sampleLoopEndIndex = loopEnd;
|
|
@@ -121,7 +121,7 @@ export class Modulator
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
124
|
+
* Sum transform and create a NEW modulator
|
|
125
125
|
* @param modulator {Modulator}
|
|
126
126
|
* @returns {Modulator}
|
|
127
127
|
*/
|
|
@@ -279,6 +279,21 @@ const customModulators = [
|
|
|
279
279
|
transform: 0
|
|
280
280
|
}),
|
|
281
281
|
|
|
282
|
+
// cc 73 (attack time) to volEnv attack
|
|
283
|
+
new Modulator({
|
|
284
|
+
srcEnum: getModSourceEnum(
|
|
285
|
+
modulatorCurveTypes.convex,
|
|
286
|
+
1,
|
|
287
|
+
0,
|
|
288
|
+
1,
|
|
289
|
+
midiControllers.attackTime
|
|
290
|
+
), // linear forward bipolar cc 72
|
|
291
|
+
dest: generatorTypes.attackVolEnv,
|
|
292
|
+
amt: 6000,
|
|
293
|
+
secSrcEnum: 0x0, // no controller
|
|
294
|
+
transform: 0
|
|
295
|
+
}),
|
|
296
|
+
|
|
282
297
|
// cc 72 (release time) to volEnv release
|
|
283
298
|
new Modulator({
|
|
284
299
|
srcEnum: getModSourceEnum(
|
|
@@ -289,7 +304,7 @@ const customModulators = [
|
|
|
289
304
|
midiControllers.releaseTime
|
|
290
305
|
), // linear forward bipolar cc 72
|
|
291
306
|
dest: generatorTypes.releaseVolEnv,
|
|
292
|
-
amt:
|
|
307
|
+
amt: 3600,
|
|
293
308
|
secSrcEnum: 0x0, // no controller
|
|
294
309
|
transform: 0
|
|
295
310
|
}),
|
|
@@ -309,14 +324,14 @@ const customModulators = [
|
|
|
309
324
|
transform: 0
|
|
310
325
|
}),
|
|
311
326
|
|
|
312
|
-
// cc 71 (filter
|
|
327
|
+
// cc 71 (filter Q) to filter Q
|
|
313
328
|
new Modulator({
|
|
314
329
|
srcEnum: getModSourceEnum(
|
|
315
330
|
modulatorCurveTypes.linear,
|
|
316
331
|
1,
|
|
317
332
|
0,
|
|
318
333
|
1,
|
|
319
|
-
midiControllers.
|
|
334
|
+
midiControllers.filterResonance
|
|
320
335
|
), // linear forwards bipolar cc 74
|
|
321
336
|
dest: generatorTypes.initialFilterQ,
|
|
322
337
|
amt: 250,
|
|
@@ -136,7 +136,7 @@ function getSF2GeneratorFromDLS(destination, amount)
|
|
|
136
136
|
case DLSDestinations.reverbSend:
|
|
137
137
|
return generatorTypes.reverbEffectsSend;
|
|
138
138
|
|
|
139
|
-
//
|
|
139
|
+
// lfo
|
|
140
140
|
case DLSDestinations.modLfoFreq:
|
|
141
141
|
return generatorTypes.freqModLFO;
|
|
142
142
|
case DLSDestinations.modLfoDelay:
|
|
@@ -319,8 +319,9 @@ export function getSF2ModulatorFromArticulator(
|
|
|
319
319
|
if (isSourceNoController)
|
|
320
320
|
{
|
|
321
321
|
// we force it into this state because before it was some strange value,
|
|
322
|
-
// like vibrato lfo bipolar for example
|
|
323
|
-
// since we turn it into NoController -> vibLfoToPitch
|
|
322
|
+
// like vibrato lfo bipolar, for example,
|
|
323
|
+
// since we turn it into NoController -> vibLfoToPitch,
|
|
324
|
+
// the result is the same and bipolar controller is technically 0
|
|
324
325
|
sourceEnumFinal = 0x0;
|
|
325
326
|
}
|
|
326
327
|
else
|
|
@@ -328,7 +329,7 @@ export function getSF2ModulatorFromArticulator(
|
|
|
328
329
|
// output transform is ignored as it's not a thing in sfont format
|
|
329
330
|
// unless the curve type of source is linear, then output is copied
|
|
330
331
|
const outputTransform = transform & 0b1111;
|
|
331
|
-
// source curve type maps to desfont curve type in section 2.10, table 9
|
|
332
|
+
// source curve type maps to a desfont curve type in section 2.10, table 9
|
|
332
333
|
let sourceTransform = (transform >> 10) & 0b1111;
|
|
333
334
|
if (sourceTransform === modulatorCurveTypes.linear && outputTransform !== modulatorCurveTypes.linear)
|
|
334
335
|
{
|
|
@@ -341,7 +342,7 @@ export function getSF2ModulatorFromArticulator(
|
|
|
341
342
|
{
|
|
342
343
|
// if the value is negative, the source shall be negative!
|
|
343
344
|
// why?
|
|
344
|
-
//
|
|
345
|
+
// IDK, it makes it work with ROCK.RMI and NOKIA_S30.dls
|
|
345
346
|
if (value < 0)
|
|
346
347
|
{
|
|
347
348
|
sourceIsNegative = 1;
|
|
@@ -356,6 +357,14 @@ export function getSF2ModulatorFromArticulator(
|
|
|
356
357
|
);
|
|
357
358
|
}
|
|
358
359
|
|
|
360
|
+
// a corrupted rendition of gm.dls was found under
|
|
361
|
+
// https://sembiance.com/fileFormatSamples/audio/downloadableSoundBank/
|
|
362
|
+
// which specifies a whopping -32,768 decibels of attenuation
|
|
363
|
+
if (destinationGenerator === generatorTypes.initialAttenuation)
|
|
364
|
+
{
|
|
365
|
+
newValue = Math.max(960, Math.min(0, newValue));
|
|
366
|
+
}
|
|
367
|
+
|
|
359
368
|
const secSourceTransform = (transform >> 4) & 0b1111;
|
|
360
369
|
const secSourceIsBipolar = (transform >> 8) & 1;
|
|
361
370
|
const secSourceIsNegative = transform >> 9 & 1;
|
|
@@ -76,7 +76,7 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
76
76
|
generator = new Generator(generatorTypes.delayVibLFO, value);
|
|
77
77
|
break;
|
|
78
78
|
|
|
79
|
-
// vol env: all times are timecents like sf2
|
|
79
|
+
// vol. env: all times are timecents like sf2
|
|
80
80
|
case DLSDestinations.volEnvDelay:
|
|
81
81
|
generator = new Generator(generatorTypes.delayVolEnv, value);
|
|
82
82
|
break;
|
|
@@ -144,7 +144,7 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
else
|
|
147
|
-
// if not, modulator
|
|
147
|
+
// if not, modulator?
|
|
148
148
|
{
|
|
149
149
|
let isGenerator = true;
|
|
150
150
|
// a few special cases which are generators:
|
|
@@ -190,7 +190,7 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
190
190
|
if (source === DLSSources.keyNum && destination === DLSDestinations.pitch)
|
|
191
191
|
{
|
|
192
192
|
// this is just a soundfont generator, but the amount must be changed
|
|
193
|
-
//
|
|
193
|
+
// 12,800 means the regular scale (100)
|
|
194
194
|
generators.push(new Generator(generatorTypes.scaleTuning, value / 128));
|
|
195
195
|
}
|
|
196
196
|
else
|
|
@@ -25,11 +25,23 @@ export function readRegion(chunk)
|
|
|
25
25
|
// region header
|
|
26
26
|
const regionHeader = regionChunks.find(c => c.header === "rgnh");
|
|
27
27
|
// key range
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
let keyMin = readLittleEndian(regionHeader.chunkData, 2);
|
|
29
|
+
let keyMax = readLittleEndian(regionHeader.chunkData, 2);
|
|
30
30
|
// vel range
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
let velMin = readLittleEndian(regionHeader.chunkData, 2);
|
|
32
|
+
let velMax = readLittleEndian(regionHeader.chunkData, 2);
|
|
33
|
+
|
|
34
|
+
// a fix for not cool files
|
|
35
|
+
if (velMin === 0 && velMax === 0)
|
|
36
|
+
{
|
|
37
|
+
velMax = 127;
|
|
38
|
+
velMin = 0;
|
|
39
|
+
}
|
|
40
|
+
if (keyMin === 0 && keyMax === 0)
|
|
41
|
+
{
|
|
42
|
+
keyMax = 127;
|
|
43
|
+
keyMin = 0;
|
|
44
|
+
}
|
|
33
45
|
|
|
34
46
|
const zone = new DLSZone(
|
|
35
47
|
{ min: keyMin, max: keyMax },
|
|
@@ -51,7 +63,7 @@ export function readRegion(chunk)
|
|
|
51
63
|
const lar2 = findRIFFListType(regionChunks, "lar2");
|
|
52
64
|
this.readLart(lart, lar2, zone);
|
|
53
65
|
|
|
54
|
-
//
|
|
66
|
+
// wsmp: wave sample chunk
|
|
55
67
|
zone.isGlobal = false;
|
|
56
68
|
const waveSampleChunk = regionChunks.find(c => c.header === "wsmp");
|
|
57
69
|
// cbSize
|
|
@@ -64,7 +76,7 @@ export function readRegion(chunk)
|
|
|
64
76
|
waveSampleChunk.chunkData[waveSampleChunk.chunkData.currentIndex++]
|
|
65
77
|
);
|
|
66
78
|
|
|
67
|
-
// gain correction:
|
|
79
|
+
// gain correction: Each unit of gain represents 1/655360 dB
|
|
68
80
|
// it is set after linking the sample
|
|
69
81
|
const gainCorrection = readLittleEndian(waveSampleChunk.chunkData, 4);
|
|
70
82
|
// convert to signed and turn into attenuation (invert)
|
|
@@ -87,7 +99,7 @@ export function readRegion(chunk)
|
|
|
87
99
|
// ignore cbSize
|
|
88
100
|
readLittleEndian(waveSampleChunk.chunkData, 4);
|
|
89
101
|
// loop type: loop normally or loop until release (like soundfont)
|
|
90
|
-
const loopType = readLittleEndian(waveSampleChunk.chunkData, 4); // why is it long
|
|
102
|
+
const loopType = readLittleEndian(waveSampleChunk.chunkData, 4); // why is it long?
|
|
91
103
|
if (loopType === 0)
|
|
92
104
|
{
|
|
93
105
|
loopingMode = 1;
|
|
@@ -105,13 +117,13 @@ export function readRegion(chunk)
|
|
|
105
117
|
const waveLinkChunk = regionChunks.find(c => c.header === "wlnk");
|
|
106
118
|
if (waveLinkChunk === undefined)
|
|
107
119
|
{
|
|
108
|
-
//
|
|
120
|
+
// No wave link means no sample. What? Why is it even here then?
|
|
109
121
|
return undefined;
|
|
110
122
|
}
|
|
111
123
|
|
|
112
124
|
// flags
|
|
113
125
|
readLittleEndian(waveLinkChunk.chunkData, 2);
|
|
114
|
-
//
|
|
126
|
+
// phase group
|
|
115
127
|
readLittleEndian(waveLinkChunk.chunkData, 2);
|
|
116
128
|
// channel
|
|
117
129
|
readLittleEndian(waveLinkChunk.chunkData, 4);
|
|
@@ -65,7 +65,7 @@ export class LoadedSample extends BasicSample
|
|
|
65
65
|
// correct loop points
|
|
66
66
|
this.sampleLoopStartIndex += this.sampleStartIndex / 2;
|
|
67
67
|
this.sampleLoopEndIndex += this.sampleStartIndex / 2;
|
|
68
|
-
this.sampleLength = 99999999; // set to
|
|
68
|
+
this.sampleLength = 99999999; // set to 999,999 before we decode it
|
|
69
69
|
}
|
|
70
70
|
this.isDataRaw = isDataRaw;
|
|
71
71
|
}
|
|
@@ -121,6 +121,10 @@ export class LoadedSample extends BasicSample
|
|
|
121
121
|
*/
|
|
122
122
|
const vorbis = stbvorbis.decode(buff.buffer);
|
|
123
123
|
this.sampleData = vorbis.data[0];
|
|
124
|
+
if (this.sampleData === undefined)
|
|
125
|
+
{
|
|
126
|
+
SpessaSynthWarn(`Error decoding sample ${this.sampleName}: Vorbis decode returned undefined.`);
|
|
127
|
+
}
|
|
124
128
|
}
|
|
125
129
|
catch (e)
|
|
126
130
|
{
|
|
@@ -138,10 +142,10 @@ export class LoadedSample extends BasicSample
|
|
|
138
142
|
{
|
|
139
143
|
if (!this.isSampleLoaded)
|
|
140
144
|
{
|
|
141
|
-
// start loading data if not loaded
|
|
145
|
+
// start loading data if it is not loaded
|
|
142
146
|
if (this.sampleLength < 1)
|
|
143
147
|
{
|
|
144
|
-
|
|
148
|
+
SpessaSynthWarn(`Invalid sample ${this.sampleName}! Invalid length: ${this.sampleLength}`);
|
|
145
149
|
return new Float32Array(1);
|
|
146
150
|
}
|
|
147
151
|
|
|
@@ -240,7 +244,7 @@ export function readSamples(sampleHeadersChunk, smplChunkData, isSmplDataRaw = t
|
|
|
240
244
|
* @param index {number}
|
|
241
245
|
* @param sampleHeaderData {IndexedByteArray}
|
|
242
246
|
* @param smplArrayData {IndexedByteArray|Float32Array}
|
|
243
|
-
* @param isDataRaw {boolean} true means binary 16
|
|
247
|
+
* @param isDataRaw {boolean} true means binary 16-bit data, false means float32
|
|
244
248
|
* @returns {LoadedSample}
|
|
245
249
|
*/
|
|
246
250
|
function readSample(index, sampleHeaderData, smplArrayData, isDataRaw)
|
|
@@ -272,7 +276,7 @@ function readSample(index, sampleHeaderData, smplArrayData, isDataRaw)
|
|
|
272
276
|
samplePitch = 60;
|
|
273
277
|
}
|
|
274
278
|
|
|
275
|
-
//
|
|
279
|
+
// read the sample pitch correction
|
|
276
280
|
let samplePitchCorrection = signedInt8(sampleHeaderData[sampleHeaderData.currentIndex++]);
|
|
277
281
|
|
|
278
282
|
|
|
@@ -61,7 +61,7 @@ export class SoundfontManager
|
|
|
61
61
|
throw new Error("Cannot overwrite the existing soundfont. Use soundfontManager.delete(id) instead.");
|
|
62
62
|
}
|
|
63
63
|
this._sendToWorklet(WorkletSoundfontManagerMessageType.addNewSoundFont, [soundfontBuffer, id, bankOffset]);
|
|
64
|
-
await new Promise(r => this.synth.
|
|
64
|
+
await new Promise(r => this.synth._resolveWhenReady = r);
|
|
65
65
|
this.soundfontList.push({
|
|
66
66
|
id: id,
|
|
67
67
|
bankOffset: bankOffset
|
|
@@ -106,6 +106,6 @@ export class SoundfontManager
|
|
|
106
106
|
async reloadManager(newBuffer)
|
|
107
107
|
{
|
|
108
108
|
this._sendToWorklet(WorkletSoundfontManagerMessageType.reloadSoundFont, newBuffer);
|
|
109
|
-
await new Promise(r => this.synth.
|
|
109
|
+
await new Promise(r => this.synth._resolveWhenReady = r);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -42,6 +42,51 @@ export const DEFAULT_SYNTH_MODE = "gs";
|
|
|
42
42
|
|
|
43
43
|
export class Synthetizer
|
|
44
44
|
{
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Allows setting up custom event listeners for the synthesizer
|
|
48
|
+
* @type {EventHandler}
|
|
49
|
+
*/
|
|
50
|
+
eventHandler = new EventHandler();
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Synthesizer's parent AudioContext instance
|
|
54
|
+
* @type {BaseAudioContext}
|
|
55
|
+
*/
|
|
56
|
+
context;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Synthesizer's output node
|
|
60
|
+
* @type {AudioNode}
|
|
61
|
+
*/
|
|
62
|
+
targetNode;
|
|
63
|
+
/**
|
|
64
|
+
* @type {boolean}
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
_destroyed = false;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* the new channels will have their audio sent to the moduled output by this constant.
|
|
71
|
+
* what does that mean?
|
|
72
|
+
* e.g., if outputsAmount is 16, then channel's 16 audio data will be sent to channel 0
|
|
73
|
+
* @type {number}
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
_outputsAmount = MIDI_CHANNEL_COUNT;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The current number of MIDI channels the synthesizer has
|
|
80
|
+
* @type {number}
|
|
81
|
+
*/
|
|
82
|
+
channelsAmount = this._outputsAmount;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Synth's current channel properties
|
|
86
|
+
* @type {ChannelProperty[]}
|
|
87
|
+
*/
|
|
88
|
+
channelProperties = [];
|
|
89
|
+
|
|
45
90
|
/**
|
|
46
91
|
* Creates a new instance of the SpessaSynth synthesizer.
|
|
47
92
|
* @param targetNode {AudioNode}
|
|
@@ -63,57 +108,23 @@ export class Synthetizer
|
|
|
63
108
|
const oneOutputMode = startRenderingData?.oneOutput === true;
|
|
64
109
|
|
|
65
110
|
/**
|
|
66
|
-
* Allows setting up custom event listeners for the synthesizer
|
|
67
|
-
* @type {EventHandler}
|
|
68
|
-
*/
|
|
69
|
-
this.eventHandler = new EventHandler();
|
|
70
|
-
|
|
71
|
-
this._voiceCap = VOICE_CAP;
|
|
72
|
-
this._destroyed = false;
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* the new channels will have their audio sent to the moduled output by this constant.
|
|
76
|
-
* what does that mean? e.g., if outputsAmount is 16, then channel's 16 audio data will be sent to channel 0
|
|
77
|
-
* @type {number}
|
|
78
111
|
* @private
|
|
79
|
-
*/
|
|
80
|
-
this._outputsAmount = MIDI_CHANNEL_COUNT;
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* the number of midi channels
|
|
84
|
-
* @type {number}
|
|
85
|
-
*/
|
|
86
|
-
this.channelsAmount = this._outputsAmount;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
112
|
* @type {function}
|
|
90
113
|
*/
|
|
91
|
-
this.
|
|
114
|
+
this._resolveWhenReady = undefined;
|
|
92
115
|
|
|
93
116
|
/**
|
|
94
117
|
* Indicates if the synth is fully ready
|
|
95
118
|
* @type {Promise<void>}
|
|
96
119
|
*/
|
|
97
|
-
this.isReady = new Promise(resolve => this.
|
|
120
|
+
this.isReady = new Promise(resolve => this._resolveWhenReady = resolve);
|
|
98
121
|
|
|
99
122
|
|
|
100
|
-
/**
|
|
101
|
-
* individual channel voices amount
|
|
102
|
-
* @type {ChannelProperty[]}
|
|
103
|
-
*/
|
|
104
|
-
this.channelProperties = [];
|
|
105
123
|
for (let i = 0; i < this.channelsAmount; i++)
|
|
106
124
|
{
|
|
107
125
|
this.addNewChannel(false);
|
|
108
126
|
}
|
|
109
127
|
this.channelProperties[DEFAULT_PERCUSSION].isDrum = true;
|
|
110
|
-
this._voicesAmount = 0;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* For Black MIDI's - forces release time to 50 ms
|
|
114
|
-
* @type {boolean}
|
|
115
|
-
*/
|
|
116
|
-
this._highPerformanceMode = false;
|
|
117
128
|
|
|
118
129
|
// create a worklet processor
|
|
119
130
|
let processorChannelCount = Array(this._outputsAmount + 2).fill(2);
|
|
@@ -245,24 +256,26 @@ export class Synthetizer
|
|
|
245
256
|
}
|
|
246
257
|
|
|
247
258
|
/**
|
|
248
|
-
*
|
|
249
|
-
* @
|
|
259
|
+
* current voice amount
|
|
260
|
+
* @type {number}
|
|
261
|
+
* @private
|
|
250
262
|
*/
|
|
251
|
-
|
|
252
|
-
{
|
|
253
|
-
return this._voiceCap;
|
|
254
|
-
}
|
|
263
|
+
_voicesAmount = 0;
|
|
255
264
|
|
|
256
265
|
/**
|
|
257
|
-
*
|
|
258
|
-
* @param value {number}
|
|
266
|
+
* @returns {number} the current number of voices playing.
|
|
259
267
|
*/
|
|
260
|
-
|
|
268
|
+
get voicesAmount()
|
|
261
269
|
{
|
|
262
|
-
this.
|
|
263
|
-
this._voiceCap = value;
|
|
270
|
+
return this._voicesAmount;
|
|
264
271
|
}
|
|
265
272
|
|
|
273
|
+
/**
|
|
274
|
+
* For Black MIDI's - forces release time to 50 ms
|
|
275
|
+
* @type {boolean}
|
|
276
|
+
*/
|
|
277
|
+
_highPerformanceMode = false;
|
|
278
|
+
|
|
266
279
|
get highPerformanceMode()
|
|
267
280
|
{
|
|
268
281
|
return this._highPerformanceMode;
|
|
@@ -282,19 +295,36 @@ export class Synthetizer
|
|
|
282
295
|
}
|
|
283
296
|
|
|
284
297
|
/**
|
|
285
|
-
* @
|
|
298
|
+
* @type {number}
|
|
299
|
+
* @private
|
|
286
300
|
*/
|
|
287
|
-
|
|
301
|
+
_voiceCap = VOICE_CAP;
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* The maximum number of voices allowed at once.
|
|
305
|
+
* @returns {number}
|
|
306
|
+
*/
|
|
307
|
+
get voiceCap()
|
|
288
308
|
{
|
|
289
|
-
return this.
|
|
309
|
+
return this._voiceCap;
|
|
290
310
|
}
|
|
291
311
|
|
|
292
312
|
/**
|
|
293
|
-
*
|
|
313
|
+
* The maximum number of voices allowed at once.
|
|
314
|
+
* @param value {number}
|
|
294
315
|
*/
|
|
295
|
-
|
|
316
|
+
set voiceCap(value)
|
|
296
317
|
{
|
|
297
|
-
|
|
318
|
+
this._setMasterParam(masterParameterType.voicesCap, value);
|
|
319
|
+
this._voiceCap = value;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* @returns {number} the audioContext's current time.
|
|
324
|
+
*/
|
|
325
|
+
get currentTime()
|
|
326
|
+
{
|
|
327
|
+
return this.context.currentTime;
|
|
298
328
|
}
|
|
299
329
|
|
|
300
330
|
/**
|
|
@@ -377,7 +407,7 @@ export class Synthetizer
|
|
|
377
407
|
break;
|
|
378
408
|
|
|
379
409
|
case returnMessageType.ready:
|
|
380
|
-
this.
|
|
410
|
+
this._resolveWhenReady();
|
|
381
411
|
break;
|
|
382
412
|
|
|
383
413
|
case returnMessageType.soundfontError:
|
|
@@ -420,7 +450,8 @@ export class Synthetizer
|
|
|
420
450
|
pitchBend: 0,
|
|
421
451
|
pitchBendRangeSemitones: 0,
|
|
422
452
|
isMuted: false,
|
|
423
|
-
isDrum: false
|
|
453
|
+
isDrum: false,
|
|
454
|
+
transposition: 0
|
|
424
455
|
});
|
|
425
456
|
if (!postMessage)
|
|
426
457
|
{
|