spessasynth_lib 3.25.17 → 3.25.19
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/index.js +4 -0
- package/midi_parser/midi_builder.js +2 -1
- package/package.json +1 -1
- package/sequencer/worklet_sequencer/play.js +3 -3
- package/sequencer/worklet_sequencer/worklet_sequencer.js +4 -3
- package/soundfont/basic_soundfont/generator.js +1 -1
- package/soundfont/dls/read_articulation.js +35 -52
- package/soundfont/dls/read_region.js +1 -5
- package/synthetizer/synth_event_handler.js +0 -1
- package/synthetizer/synthetizer.js +122 -143
- package/synthetizer/worklet_processor.min.js +12 -12
- package/synthetizer/worklet_system/README.md +14 -1
- package/synthetizer/worklet_system/main_processor.js +199 -102
- package/synthetizer/worklet_system/message_protocol/worklet_message.js +25 -38
- package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +1 -2
- package/synthetizer/worklet_system/worklet_methods/note_on.js +10 -17
- package/synthetizer/worklet_system/worklet_methods/program_change.js +2 -4
- package/synthetizer/worklet_system/worklet_methods/render_voice.js +2 -3
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/kill_note.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/note_off.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/stopping_notes/stop_all_notes.js +2 -2
- package/synthetizer/worklet_system/worklet_processor.js +334 -3
- package/synthetizer/worklet_system/worklet_utilities/lowpass_filter.js +20 -3
- package/synthetizer/worklet_system/worklet_utilities/stereo_panner.js +2 -2
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +1 -1
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +24 -27
- package/synthetizer/worklet_system/message_protocol/handle_message.js +0 -242
- package/synthetizer/worklet_system/snapshot/send_synthesizer_snapshot.js +0 -14
package/index.js
CHANGED
|
@@ -27,6 +27,8 @@ import { DEFAULT_SYNTH_CONFIG } from "./synthetizer/audio_effects/effects_config
|
|
|
27
27
|
import { WORKLET_URL_ABSOLUTE } from './synthetizer/worklet_url.js';
|
|
28
28
|
import { SynthesizerSnapshot} from "./synthetizer/worklet_system/snapshot/synthesizer_snapshot.js";
|
|
29
29
|
import { ChannelSnapshot } from "./synthetizer/worklet_system/snapshot/channel_snapshot.js";
|
|
30
|
+
import { NON_CC_INDEX_OFFSET } from "./synthetizer/worklet_system/worklet_utilities/controller_tables.js";
|
|
31
|
+
import { ALL_CHANNELS_OR_DIFFERENT_ACTION } from "./synthetizer/worklet_system/message_protocol/worklet_message.js";
|
|
30
32
|
|
|
31
33
|
// Export modules
|
|
32
34
|
export {
|
|
@@ -38,6 +40,8 @@ export {
|
|
|
38
40
|
DEFAULT_PERCUSSION,
|
|
39
41
|
VOICE_CAP,
|
|
40
42
|
DEFAULT_SYNTH_CONFIG,
|
|
43
|
+
ALL_CHANNELS_OR_DIFFERENT_ACTION,
|
|
44
|
+
NON_CC_INDEX_OFFSET,
|
|
41
45
|
|
|
42
46
|
// SoundFont
|
|
43
47
|
BasicSoundBank,
|
|
@@ -80,7 +80,8 @@ export class MIDIBuilder extends BasicMIDI
|
|
|
80
80
|
}
|
|
81
81
|
if (event === messageTypes.endOfTrack)
|
|
82
82
|
{
|
|
83
|
-
SpessaSynthWarn(
|
|
83
|
+
SpessaSynthWarn(
|
|
84
|
+
"The EndOfTrack is added automatically and does not influence the duration. Consider adding a voice event instead.");
|
|
84
85
|
return;
|
|
85
86
|
}
|
|
86
87
|
// remove the end of track
|
package/package.json
CHANGED
|
@@ -316,7 +316,7 @@ export function play(resetTime = false)
|
|
|
316
316
|
{
|
|
317
317
|
this.playingNotes.forEach(n =>
|
|
318
318
|
{
|
|
319
|
-
this.synth.noteOn(n.channel, n.midiNote, n.velocity
|
|
319
|
+
this.synth.noteOn(n.channel, n.midiNote, n.velocity);
|
|
320
320
|
});
|
|
321
321
|
}
|
|
322
322
|
this.setProcessHandler();
|
|
@@ -333,7 +333,7 @@ export function setTimeTicks(ticks)
|
|
|
333
333
|
this.pausedTime = undefined;
|
|
334
334
|
this.post(
|
|
335
335
|
WorkletSequencerReturnMessageType.timeChange,
|
|
336
|
-
|
|
336
|
+
this.synth.currentSynthTime - this.midiData.MIDIticksToSeconds(ticks)
|
|
337
337
|
);
|
|
338
338
|
const isNotFinished = this._playTo(0, ticks);
|
|
339
339
|
this._recalculateStartTime(this.playedTime);
|
|
@@ -351,5 +351,5 @@ export function setTimeTicks(ticks)
|
|
|
351
351
|
*/
|
|
352
352
|
export function _recalculateStartTime(time)
|
|
353
353
|
{
|
|
354
|
-
this.absoluteStartTime =
|
|
354
|
+
this.absoluteStartTime = this.synth.currentSynthTime - time / this._playbackRate;
|
|
355
355
|
}
|
|
@@ -83,7 +83,7 @@ class WorkletSequencer
|
|
|
83
83
|
* Absolute playback startTime, bases on the synth's time
|
|
84
84
|
* @type {number}
|
|
85
85
|
*/
|
|
86
|
-
absoluteStartTime =
|
|
86
|
+
absoluteStartTime = 0;
|
|
87
87
|
/**
|
|
88
88
|
* Currently playing notes (for pausing and resuming)
|
|
89
89
|
* @type {{
|
|
@@ -142,6 +142,7 @@ class WorkletSequencer
|
|
|
142
142
|
constructor(spessasynthProcessor)
|
|
143
143
|
{
|
|
144
144
|
this.synth = spessasynthProcessor;
|
|
145
|
+
this.absoluteStartTime = this.synth.currentSynthTime;
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
/**
|
|
@@ -169,7 +170,7 @@ class WorkletSequencer
|
|
|
169
170
|
return this.pausedTime;
|
|
170
171
|
}
|
|
171
172
|
|
|
172
|
-
return (
|
|
173
|
+
return (this.synth.currentSynthTime - this.absoluteStartTime) * this._playbackRate;
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
set currentTime(time)
|
|
@@ -199,7 +200,7 @@ class WorkletSequencer
|
|
|
199
200
|
this.playingNotes = [];
|
|
200
201
|
const wasPaused = this.paused && this.preservePlaybackState;
|
|
201
202
|
this.pausedTime = undefined;
|
|
202
|
-
this.post(WorkletSequencerReturnMessageType.timeChange,
|
|
203
|
+
this.post(WorkletSequencerReturnMessageType.timeChange, this.synth.currentSynthTime - time);
|
|
203
204
|
if (this.midiData.duration === 0)
|
|
204
205
|
{
|
|
205
206
|
SpessaSynthWarn("No duration!");
|
|
@@ -129,7 +129,7 @@ generatorLimits[generatorTypes.startloopAddrsCoarseOffset] = { min: -32768, max:
|
|
|
129
129
|
generatorLimits[generatorTypes.keyNum] = { min: -1, max: 127, def: -1 };
|
|
130
130
|
generatorLimits[generatorTypes.velocity] = { min: -1, max: 127, def: -1 };
|
|
131
131
|
|
|
132
|
-
generatorLimits[generatorTypes.initialAttenuation] = { min:
|
|
132
|
+
generatorLimits[generatorTypes.initialAttenuation] = { min: 0, max: 1440, def: 0 };
|
|
133
133
|
|
|
134
134
|
generatorLimits[generatorTypes.endloopAddrsCoarseOffset] = { min: -32768, max: 32768, def: 0 };
|
|
135
135
|
|
|
@@ -39,6 +39,14 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
39
39
|
const scale = readLittleEndian(artData, 4) | 0;
|
|
40
40
|
const value = scale >> 16; // convert it to 16 bit as soundfont uses that
|
|
41
41
|
|
|
42
|
+
// modulatorConverterDebug(
|
|
43
|
+
// source,
|
|
44
|
+
// control,
|
|
45
|
+
// destination,
|
|
46
|
+
// value,
|
|
47
|
+
// transform
|
|
48
|
+
// );
|
|
49
|
+
|
|
42
50
|
// interpret this somehow...
|
|
43
51
|
// if source and control are both zero, it's a generator
|
|
44
52
|
if (source === 0 && control === 0 && transform === 0)
|
|
@@ -147,6 +155,29 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
147
155
|
// if not, modulator?
|
|
148
156
|
{
|
|
149
157
|
let isGenerator = true;
|
|
158
|
+
|
|
159
|
+
const applyKeyToCorrection = (value, keyToGen, realGen) =>
|
|
160
|
+
{
|
|
161
|
+
// according to viena and another strange (with modulators) rendition of gm.dls in sf2,
|
|
162
|
+
// it shall be divided by -128
|
|
163
|
+
// and a strange correction needs to be applied to the real value:
|
|
164
|
+
// real + (60 / 128) * scale
|
|
165
|
+
const keyToGenValue = value / -128;
|
|
166
|
+
generators.push(new Generator(keyToGen, keyToGenValue));
|
|
167
|
+
// airfont 340 fix
|
|
168
|
+
if (keyToGenValue <= 120)
|
|
169
|
+
{
|
|
170
|
+
const correction = Math.round((60 / 128) * value);
|
|
171
|
+
generators.forEach(g =>
|
|
172
|
+
{
|
|
173
|
+
if (g.generatorType === realGen)
|
|
174
|
+
{
|
|
175
|
+
g.generatorValue += correction;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
|
|
150
181
|
// a few special cases which are generators:
|
|
151
182
|
if (control === DLSSources.none)
|
|
152
183
|
{
|
|
@@ -197,73 +228,25 @@ export function readArticulation(chunk, disableVibrato)
|
|
|
197
228
|
// key to vol env hold
|
|
198
229
|
if (source === DLSSources.keyNum && destination === DLSDestinations.volEnvHold)
|
|
199
230
|
{
|
|
200
|
-
|
|
201
|
-
// it shall be divided by -128
|
|
202
|
-
// and a strange correction needs to be applied to the real value:
|
|
203
|
-
// real + (60 / 128) * scale
|
|
204
|
-
generators.push(new Generator(generatorTypes.keyNumToVolEnvHold, value / -128));
|
|
205
|
-
const correction = Math.round((60 / 128) * value);
|
|
206
|
-
generators.forEach(g =>
|
|
207
|
-
{
|
|
208
|
-
if (g.generatorType === generatorTypes.holdVolEnv)
|
|
209
|
-
{
|
|
210
|
-
g.generatorValue += correction;
|
|
211
|
-
}
|
|
212
|
-
});
|
|
231
|
+
applyKeyToCorrection(value, generatorTypes.keyNumToVolEnvHold, generatorTypes.holdVolEnv);
|
|
213
232
|
}
|
|
214
233
|
else
|
|
215
234
|
// key to vol env decay
|
|
216
235
|
if (source === DLSSources.keyNum && destination === DLSDestinations.volEnvDecay)
|
|
217
236
|
{
|
|
218
|
-
|
|
219
|
-
// it shall be divided by -128
|
|
220
|
-
// and a strange correction needs to be applied to the real value:
|
|
221
|
-
// real + (60 / 128) * scale
|
|
222
|
-
generators.push(new Generator(generatorTypes.keyNumToVolEnvDecay, value / -128));
|
|
223
|
-
const correction = Math.round((60 / 128) * value);
|
|
224
|
-
generators.forEach(g =>
|
|
225
|
-
{
|
|
226
|
-
if (g.generatorType === generatorTypes.decayVolEnv)
|
|
227
|
-
{
|
|
228
|
-
g.generatorValue += correction;
|
|
229
|
-
}
|
|
230
|
-
});
|
|
237
|
+
applyKeyToCorrection(value, generatorTypes.keyNumToVolEnvDecay, generatorTypes.decayVolEnv);
|
|
231
238
|
}
|
|
232
239
|
else
|
|
233
240
|
// key to mod env hold
|
|
234
241
|
if (source === DLSSources.keyNum && destination === DLSDestinations.modEnvHold)
|
|
235
242
|
{
|
|
236
|
-
|
|
237
|
-
// it shall be divided by -128
|
|
238
|
-
// and a strange correction needs to be applied to the real value:
|
|
239
|
-
// real + (60 / 128) * scale
|
|
240
|
-
generators.push(new Generator(generatorTypes.keyNumToModEnvHold, value / -128));
|
|
241
|
-
const correction = Math.round((60 / 128) * value);
|
|
242
|
-
generators.forEach(g =>
|
|
243
|
-
{
|
|
244
|
-
if (g.generatorType === generatorTypes.holdModEnv)
|
|
245
|
-
{
|
|
246
|
-
g.generatorValue += correction;
|
|
247
|
-
}
|
|
248
|
-
});
|
|
243
|
+
applyKeyToCorrection(value, generatorTypes.keyNumToModEnvHold, generatorTypes.holdModEnv);
|
|
249
244
|
}
|
|
250
245
|
else
|
|
251
246
|
// key to mod env decay
|
|
252
247
|
if (source === DLSSources.keyNum && destination === DLSDestinations.modEnvDecay)
|
|
253
248
|
{
|
|
254
|
-
|
|
255
|
-
// it shall be divided by -128
|
|
256
|
-
// and a strange correction needs to be applied to the real value:
|
|
257
|
-
// real + (60 / 128) * scale
|
|
258
|
-
generators.push(new Generator(generatorTypes.keyNumToModEnvDecay, value / -128));
|
|
259
|
-
const correction = Math.round((60 / 128) * value);
|
|
260
|
-
generators.forEach(g =>
|
|
261
|
-
{
|
|
262
|
-
if (g.generatorType === generatorTypes.decayModEnv)
|
|
263
|
-
{
|
|
264
|
-
g.generatorValue += correction;
|
|
265
|
-
}
|
|
266
|
-
});
|
|
249
|
+
applyKeyToCorrection(value, generatorTypes.keyNumToModEnvDecay, generatorTypes.decayModEnv);
|
|
267
250
|
}
|
|
268
251
|
else
|
|
269
252
|
{
|
|
@@ -37,11 +37,7 @@ export function readRegion(chunk)
|
|
|
37
37
|
velMax = 127;
|
|
38
38
|
velMin = 0;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
keyMax = 127;
|
|
43
|
-
keyMin = 0;
|
|
44
|
-
}
|
|
40
|
+
// cannot do the same to key zones sadly
|
|
45
41
|
|
|
46
42
|
const zone = new DLSZone(
|
|
47
43
|
{ min: keyMin, max: keyMax },
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { IndexedByteArray } from "../utils/indexed_array.js";
|
|
2
1
|
import { consoleColors } from "../utils/other.js";
|
|
3
|
-
import {
|
|
2
|
+
import { messageTypes, midiControllers } from "../midi_parser/midi_message.js";
|
|
4
3
|
import { EventHandler } from "./synth_event_handler.js";
|
|
5
4
|
import { FancyChorus } from "./audio_effects/fancy_chorus.js";
|
|
6
5
|
import { getReverbProcessor } from "./audio_effects/reverb.js";
|
|
@@ -32,7 +31,20 @@ import { DEFAULT_SEQUENCER_OPTIONS } from "../sequencer/default_sequencer_option
|
|
|
32
31
|
* purpose: responds to midi messages and called functions, managing the channels and passing the messages to them
|
|
33
32
|
*/
|
|
34
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} SynthMethodOptions
|
|
36
|
+
* @property {number} time - the audio context time when the event should execute, in seconds.
|
|
37
|
+
*/
|
|
35
38
|
|
|
39
|
+
/**
|
|
40
|
+
* @type {SynthMethodOptions}
|
|
41
|
+
*/
|
|
42
|
+
const DEFAULT_SYNTH_METHOD_OPTIONS = {
|
|
43
|
+
time: 0
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// the "remote controller" of the worklet processor in the audio thread from the main thread
|
|
36
48
|
export class Synthetizer
|
|
37
49
|
{
|
|
38
50
|
|
|
@@ -566,20 +578,53 @@ export class Synthetizer
|
|
|
566
578
|
});
|
|
567
579
|
}
|
|
568
580
|
|
|
581
|
+
/**
|
|
582
|
+
* sends a raw MIDI message to the synthesizer.
|
|
583
|
+
* @param message {number[]|Uint8Array} the midi message, each number is a byte.
|
|
584
|
+
* @param channelOffset {number} the channel offset of the message.
|
|
585
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
586
|
+
*/
|
|
587
|
+
sendMessage(message, channelOffset = 0, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
588
|
+
{
|
|
589
|
+
this._sendInternal(message, channelOffset, false, eventOptions);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* @param message {number[]|Uint8Array}
|
|
594
|
+
* @param offset {number}
|
|
595
|
+
* @param force {boolean}
|
|
596
|
+
* @param eventOptions {SynthMethodOptions}
|
|
597
|
+
* @private
|
|
598
|
+
*/
|
|
599
|
+
_sendInternal(message, offset, force = false, eventOptions)
|
|
600
|
+
{
|
|
601
|
+
const opts = fillWithDefaults(eventOptions ?? {}, DEFAULT_SYNTH_METHOD_OPTIONS);
|
|
602
|
+
this.post({
|
|
603
|
+
messageType: workletMessageType.midiMessage,
|
|
604
|
+
messageData: [new Uint8Array(message), offset, force, opts]
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
|
|
569
609
|
/**
|
|
570
610
|
* Starts playing a note
|
|
571
611
|
* @param channel {number} usually 0-15: the channel to play the note.
|
|
572
612
|
* @param midiNote {number} 0-127 the key number of the note.
|
|
573
613
|
* @param velocity {number} 0-127 the velocity of the note (generally controls loudness).
|
|
574
|
-
* @param
|
|
614
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
575
615
|
*/
|
|
576
|
-
noteOn(channel, midiNote, velocity,
|
|
616
|
+
noteOn(channel, midiNote, velocity, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
577
617
|
{
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
618
|
+
const ch = channel % 16;
|
|
619
|
+
const offset = channel - ch;
|
|
620
|
+
midiNote %= 128;
|
|
621
|
+
velocity %= 128;
|
|
622
|
+
// check for legacy "enableDebugging"
|
|
623
|
+
if (eventOptions === true)
|
|
624
|
+
{
|
|
625
|
+
eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS;
|
|
626
|
+
}
|
|
627
|
+
this.sendMessage([messageTypes.noteOn | ch, midiNote, velocity], offset, eventOptions);
|
|
583
628
|
}
|
|
584
629
|
|
|
585
630
|
/**
|
|
@@ -587,25 +632,15 @@ export class Synthetizer
|
|
|
587
632
|
* @param channel {number} usually 0-15: the channel of the note.
|
|
588
633
|
* @param midiNote {number} 0-127 the key number of the note.
|
|
589
634
|
* @param force {boolean} instantly kills the note if true.
|
|
635
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
590
636
|
*/
|
|
591
|
-
noteOff(channel, midiNote, force = false)
|
|
637
|
+
noteOff(channel, midiNote, force = false, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
592
638
|
{
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
messageData: midiNote
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
else
|
|
602
|
-
{
|
|
603
|
-
this.post({
|
|
604
|
-
channelNumber: channel,
|
|
605
|
-
messageType: workletMessageType.noteOff,
|
|
606
|
-
messageData: midiNote
|
|
607
|
-
});
|
|
608
|
-
}
|
|
639
|
+
midiNote %= 128;
|
|
640
|
+
|
|
641
|
+
const ch = channel % 16;
|
|
642
|
+
const offset = channel - ch;
|
|
643
|
+
this._sendInternal([messageTypes.noteOff | ch, midiNote], offset, force, eventOptions);
|
|
609
644
|
}
|
|
610
645
|
|
|
611
646
|
/**
|
|
@@ -628,20 +663,25 @@ export class Synthetizer
|
|
|
628
663
|
* @param controllerNumber {number} 0-127 the MIDI CC number.
|
|
629
664
|
* @param controllerValue {number} 0-127 the controller value.
|
|
630
665
|
* @param force {boolean} forces the controller-change message, even if it's locked or gm system is set and the cc is bank select.
|
|
666
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
631
667
|
*/
|
|
632
|
-
controllerChange(channel, controllerNumber, controllerValue, force = false)
|
|
668
|
+
controllerChange(channel, controllerNumber, controllerValue, force = false, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
633
669
|
{
|
|
634
670
|
if (controllerNumber > 127 || controllerNumber < 0)
|
|
635
671
|
{
|
|
636
672
|
throw new Error(`Invalid controller number: ${controllerNumber}`);
|
|
637
673
|
}
|
|
638
|
-
controllerValue = Math.floor(controllerValue);
|
|
639
|
-
controllerNumber = Math.floor(controllerNumber);
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
674
|
+
controllerValue = Math.floor(controllerValue) % 128;
|
|
675
|
+
controllerNumber = Math.floor(controllerNumber) % 128;
|
|
676
|
+
// controller change has its own message for the force property
|
|
677
|
+
const ch = channel % 16;
|
|
678
|
+
const offset = channel - ch;
|
|
679
|
+
this._sendInternal(
|
|
680
|
+
[messageTypes.controllerChange | ch, controllerNumber, controllerValue],
|
|
681
|
+
offset,
|
|
682
|
+
force,
|
|
683
|
+
eventOptions
|
|
684
|
+
);
|
|
645
685
|
}
|
|
646
686
|
|
|
647
687
|
/**
|
|
@@ -660,14 +700,14 @@ export class Synthetizer
|
|
|
660
700
|
* Applies pressure to a given channel.
|
|
661
701
|
* @param channel {number} usually 0-15: the channel to change the controller.
|
|
662
702
|
* @param pressure {number} 0-127: the pressure to apply.
|
|
703
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
663
704
|
*/
|
|
664
|
-
channelPressure(channel, pressure)
|
|
705
|
+
channelPressure(channel, pressure, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
665
706
|
{
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
});
|
|
707
|
+
const ch = channel % 16;
|
|
708
|
+
const offset = channel - ch;
|
|
709
|
+
pressure %= 128;
|
|
710
|
+
this.sendMessage([messageTypes.channelPressure | ch, pressure], offset, eventOptions);
|
|
671
711
|
}
|
|
672
712
|
|
|
673
713
|
/**
|
|
@@ -675,14 +715,29 @@ export class Synthetizer
|
|
|
675
715
|
* @param channel {number} usually 0-15: the channel to change the controller.
|
|
676
716
|
* @param midiNote {number} 0-127: the MIDI note.
|
|
677
717
|
* @param pressure {number} 0-127: the pressure to apply.
|
|
718
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
678
719
|
*/
|
|
679
|
-
polyPressure(channel, midiNote, pressure)
|
|
720
|
+
polyPressure(channel, midiNote, pressure, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
680
721
|
{
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
722
|
+
const ch = channel % 16;
|
|
723
|
+
const offset = channel - ch;
|
|
724
|
+
midiNote %= 128;
|
|
725
|
+
pressure %= 128;
|
|
726
|
+
this.sendMessage([messageTypes.polyPressure | ch, midiNote, pressure], offset, eventOptions);
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Sets the pitch of the given channel.
|
|
731
|
+
* @param channel {number} usually 0-15: the channel to change pitch.
|
|
732
|
+
* @param MSB {number} SECOND byte of the MIDI pitchWheel message.
|
|
733
|
+
* @param LSB {number} FIRST byte of the MIDI pitchWheel message.
|
|
734
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
735
|
+
*/
|
|
736
|
+
pitchWheel(channel, MSB, LSB, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
737
|
+
{
|
|
738
|
+
const ch = channel % 16;
|
|
739
|
+
const offset = channel - ch;
|
|
740
|
+
this.sendMessage([messageTypes.pitchBend | ch, LSB, MSB], offset, eventOptions);
|
|
686
741
|
}
|
|
687
742
|
|
|
688
743
|
/**
|
|
@@ -697,21 +752,6 @@ export class Synthetizer
|
|
|
697
752
|
this.worklet.port.postMessage(data);
|
|
698
753
|
}
|
|
699
754
|
|
|
700
|
-
/**
|
|
701
|
-
* Sets the pitch of the given channel.
|
|
702
|
-
* @param channel {number} usually 0-15: the channel to change pitch.
|
|
703
|
-
* @param MSB {number} SECOND byte of the MIDI pitchWheel message.
|
|
704
|
-
* @param LSB {number} FIRST byte of the MIDI pitchWheel message.
|
|
705
|
-
*/
|
|
706
|
-
pitchWheel(channel, MSB, LSB)
|
|
707
|
-
{
|
|
708
|
-
this.post({
|
|
709
|
-
channelNumber: channel,
|
|
710
|
-
messageType: workletMessageType.pitchWheel,
|
|
711
|
-
messageData: [MSB, LSB]
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
|
|
715
755
|
/**
|
|
716
756
|
* Transposes the synthetizer's pitch by given semitones amount (percussion channels don’t get affected).
|
|
717
757
|
* @param semitones {number} the semitones to transpose by.
|
|
@@ -776,16 +816,14 @@ export class Synthetizer
|
|
|
776
816
|
* Changes the patch for a given channel
|
|
777
817
|
* @param channel {number} usually 0-15: the channel to change
|
|
778
818
|
* @param programNumber {number} 0-127 the MIDI patch number
|
|
779
|
-
* @param userChange {boolean} indicates if user has called the program change.
|
|
780
819
|
* defaults to false
|
|
781
820
|
*/
|
|
782
|
-
programChange(channel, programNumber
|
|
821
|
+
programChange(channel, programNumber)
|
|
783
822
|
{
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
});
|
|
823
|
+
const ch = channel % 16;
|
|
824
|
+
const offset = channel - ch;
|
|
825
|
+
programNumber %= 128;
|
|
826
|
+
this.sendMessage([messageTypes.programChange | ch, programNumber], offset);
|
|
789
827
|
}
|
|
790
828
|
|
|
791
829
|
/**
|
|
@@ -796,11 +834,14 @@ export class Synthetizer
|
|
|
796
834
|
*/
|
|
797
835
|
velocityOverride(channel, velocity)
|
|
798
836
|
{
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
837
|
+
const ch = channel % 16;
|
|
838
|
+
const offset = channel - ch;
|
|
839
|
+
this._sendInternal(
|
|
840
|
+
[messageTypes.controllerChange | ch, channelConfiguration.velocityOverride, velocity],
|
|
841
|
+
offset,
|
|
842
|
+
true,
|
|
843
|
+
DEFAULT_SYNTH_METHOD_OPTIONS
|
|
844
|
+
);
|
|
804
845
|
}
|
|
805
846
|
|
|
806
847
|
/**
|
|
@@ -851,14 +892,16 @@ export class Synthetizer
|
|
|
851
892
|
* @param messageData {number[]|ArrayLike|Uint8Array} the message's data
|
|
852
893
|
* (excluding the F0 byte, but including the F7 at the end).
|
|
853
894
|
* @param channelOffset {number} channel offset for the system exclusive message, defaults to zero.
|
|
895
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
854
896
|
*/
|
|
855
|
-
systemExclusive(messageData, channelOffset = 0)
|
|
897
|
+
systemExclusive(messageData, channelOffset = 0, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
856
898
|
{
|
|
857
|
-
this.
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
899
|
+
this._sendInternal(
|
|
900
|
+
[messageTypes.systemExclusive, ...Array.from(messageData)],
|
|
901
|
+
channelOffset,
|
|
902
|
+
false,
|
|
903
|
+
eventOptions
|
|
904
|
+
);
|
|
862
905
|
}
|
|
863
906
|
|
|
864
907
|
// noinspection JSUnusedGlobalSymbols
|
|
@@ -919,70 +962,6 @@ export class Synthetizer
|
|
|
919
962
|
});
|
|
920
963
|
}
|
|
921
964
|
|
|
922
|
-
/**
|
|
923
|
-
* sends a raw MIDI message to the synthesizer.
|
|
924
|
-
* @param message {number[]|Uint8Array} the midi message, each number is a byte.
|
|
925
|
-
* @param channelOffset {number} the channel offset of the message.
|
|
926
|
-
*/
|
|
927
|
-
sendMessage(message, channelOffset = 0)
|
|
928
|
-
{
|
|
929
|
-
// discard as soon as possible if high perf
|
|
930
|
-
const statusByteData = getEvent(message[0]);
|
|
931
|
-
|
|
932
|
-
statusByteData.channel += channelOffset;
|
|
933
|
-
// process the event
|
|
934
|
-
switch (statusByteData.status)
|
|
935
|
-
{
|
|
936
|
-
case messageTypes.noteOn:
|
|
937
|
-
const velocity = message[2];
|
|
938
|
-
if (velocity > 0)
|
|
939
|
-
{
|
|
940
|
-
this.noteOn(statusByteData.channel, message[1], velocity);
|
|
941
|
-
}
|
|
942
|
-
else
|
|
943
|
-
{
|
|
944
|
-
this.noteOff(statusByteData.channel, message[1]);
|
|
945
|
-
}
|
|
946
|
-
break;
|
|
947
|
-
|
|
948
|
-
case messageTypes.noteOff:
|
|
949
|
-
this.noteOff(statusByteData.channel, message[1]);
|
|
950
|
-
break;
|
|
951
|
-
|
|
952
|
-
case messageTypes.pitchBend:
|
|
953
|
-
this.pitchWheel(statusByteData.channel, message[2], message[1]);
|
|
954
|
-
break;
|
|
955
|
-
|
|
956
|
-
case messageTypes.controllerChange:
|
|
957
|
-
this.controllerChange(statusByteData.channel, message[1], message[2]);
|
|
958
|
-
break;
|
|
959
|
-
|
|
960
|
-
case messageTypes.programChange:
|
|
961
|
-
this.programChange(statusByteData.channel, message[1]);
|
|
962
|
-
break;
|
|
963
|
-
|
|
964
|
-
case messageTypes.polyPressure:
|
|
965
|
-
this.polyPressure(statusByteData.channel, message[0], message[1]);
|
|
966
|
-
break;
|
|
967
|
-
|
|
968
|
-
case messageTypes.channelPressure:
|
|
969
|
-
this.channelPressure(statusByteData.channel, message[1]);
|
|
970
|
-
break;
|
|
971
|
-
|
|
972
|
-
case messageTypes.systemExclusive:
|
|
973
|
-
this.systemExclusive(new IndexedByteArray(message.slice(1)));
|
|
974
|
-
break;
|
|
975
|
-
|
|
976
|
-
case messageTypes.reset:
|
|
977
|
-
this.stopAll(true);
|
|
978
|
-
this.resetControllers();
|
|
979
|
-
break;
|
|
980
|
-
|
|
981
|
-
default:
|
|
982
|
-
break;
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
965
|
/**
|
|
987
966
|
* Updates the reverb processor with a new impulse response.
|
|
988
967
|
* @param buffer {AudioBuffer} the new reverb impulse response.
|