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.
Files changed (34) hide show
  1. package/midi_parser/basic_midi.js +2 -0
  2. package/midi_parser/midi_message.js +1 -1
  3. package/package.json +1 -1
  4. package/soundfont/basic_soundfont/basic_preset.js +4 -2
  5. package/soundfont/basic_soundfont/basic_sample.js +2 -2
  6. package/soundfont/basic_soundfont/modulator.js +19 -4
  7. package/soundfont/dls/articulator_converter.js +14 -5
  8. package/soundfont/dls/dls_zone.js +1 -1
  9. package/soundfont/dls/read_articulation.js +3 -3
  10. package/soundfont/dls/read_region.js +21 -9
  11. package/soundfont/read_sf2/samples.js +9 -5
  12. package/synthetizer/synth_soundfont_manager.js +2 -2
  13. package/synthetizer/synthetizer.js +86 -55
  14. package/synthetizer/worklet_processor.min.js +11 -11
  15. package/synthetizer/worklet_system/main_processor.js +189 -96
  16. package/synthetizer/worklet_system/message_protocol/handle_message.js +2 -1
  17. package/synthetizer/worklet_system/message_protocol/message_sending.js +5 -4
  18. package/synthetizer/worklet_system/snapshot/channel_snapshot.js +1 -0
  19. package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +1 -1
  20. package/synthetizer/worklet_system/worklet_methods/note_on.js +9 -4
  21. package/synthetizer/worklet_system/worklet_methods/render_voice.js +26 -22
  22. package/synthetizer/worklet_system/worklet_methods/soundfont_management/clear_sound_font.js +1 -5
  23. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_master_tuning.js +1 -1
  24. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_modulation_depth.js +1 -1
  25. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_octave_tuning.js +5 -1
  26. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning.js +1 -1
  27. package/synthetizer/worklet_system/worklet_methods/tuning_control/set_tuning_semitones.js +1 -1
  28. package/synthetizer/worklet_system/worklet_methods/tuning_control/transpose_channel.js +5 -2
  29. package/synthetizer/worklet_system/worklet_utilities/controller_tables.js +11 -2
  30. package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +38 -15
  31. package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +13 -13
  32. package/synthetizer/worklet_system/worklet_utilities/worklet_modulator.js +2 -2
  33. package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +35 -20
  34. package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +20 -16
@@ -96,6 +96,8 @@ export class BasicMIDI extends MIDISequenceData
96
96
  this.midiPorts = [];
97
97
  this.midiPortChannelOffsets = [];
98
98
 
99
+ this.keyRange = { max: 0, min: 127 };
100
+
99
101
  /**
100
102
  * Will be joined with "\n" to form the final string
101
103
  * @type {string[]}
@@ -204,7 +204,7 @@ export const midiControllers = {
204
204
  legatoFootswitch: 68,
205
205
  hold2Pedal: 69,
206
206
  soundVariation: 70,
207
- timbreHarmonicContent: 71,
207
+ filterResonance: 71,
208
208
  releaseTime: 72,
209
209
  attackTime: 73,
210
210
  brightness: 74,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.24.18",
3
+ "version": "3.24.22",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "type": "module",
@@ -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 (this makes a new modulator because otherwise it would overwrite the one in the soundfont!!!
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
- * Sums transform and creates a NEW modulator
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: 1200,
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 q) to filterq
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.timbreHarmonicContent
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
- // lfos
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 the result is the same and bipolar concontroller is technically 0
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
- // Idk, it makes it work with ROCK.RMI and NOKIA_S30.dls
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;
@@ -82,7 +82,7 @@ export class DLSZone extends BasicInstrumentZone
82
82
  }
83
83
  }
84
84
  }
85
- // correct key if needed
85
+ // correct the key if needed
86
86
  if (sampleKey !== sample.samplePitch)
87
87
  {
88
88
  this.generators.push(new Generator(generatorTypes.overridingRootKey, sampleKey));
@@ -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
- // 12800 means the regular scale (100)
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
- const keyMin = readLittleEndian(regionHeader.chunkData, 2);
29
- const keyMax = readLittleEndian(regionHeader.chunkData, 2);
28
+ let keyMin = readLittleEndian(regionHeader.chunkData, 2);
29
+ let keyMax = readLittleEndian(regionHeader.chunkData, 2);
30
30
  // vel range
31
- const velMin = readLittleEndian(regionHeader.chunkData, 2);
32
- const velMax = readLittleEndian(regionHeader.chunkData, 2);
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
- // wsmpl: wave sample chunk
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: Each unit of gain represents 1/655360 dB
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
- // no wave link = no sample. What? Why is it even here then????
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
- // phasse group
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 999999 before we decode it
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
- // eos, do not do anything
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 bit data, false means float32
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
- // readt the sample pitch correction
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.resolveWhenReady = r);
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.resolveWhenReady = r);
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.resolveWhenReady = undefined;
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.resolveWhenReady = resolve);
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
- * The maximum number of voices allowed at once.
249
- * @returns {number}
259
+ * current voice amount
260
+ * @type {number}
261
+ * @private
250
262
  */
251
- get voiceCap()
252
- {
253
- return this._voiceCap;
254
- }
263
+ _voicesAmount = 0;
255
264
 
256
265
  /**
257
- * The maximum number of voices allowed at once.
258
- * @param value {number}
266
+ * @returns {number} the current number of voices playing.
259
267
  */
260
- set voiceCap(value)
268
+ get voicesAmount()
261
269
  {
262
- this._setMasterParam(masterParameterType.voicesCap, value);
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
- * @returns {number} the audioContext's current time.
298
+ * @type {number}
299
+ * @private
286
300
  */
287
- get currentTime()
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.context.currentTime;
309
+ return this._voiceCap;
290
310
  }
291
311
 
292
312
  /**
293
- * @returns {number} the current number of voices playing.
313
+ * The maximum number of voices allowed at once.
314
+ * @param value {number}
294
315
  */
295
- get voicesAmount()
316
+ set voiceCap(value)
296
317
  {
297
- return this._voicesAmount;
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.resolveWhenReady();
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
  {