spessasynth_lib 3.25.17 → 3.25.18
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/midi_builder.js +2 -1
- package/package.json +1 -1
- package/sequencer/worklet_sequencer/play.js +1 -1
- package/soundfont/dls/read_articulation.js +35 -52
- package/synthetizer/synth_event_handler.js +0 -1
- package/synthetizer/synthetizer.js +121 -143
- package/synthetizer/worklet_processor.min.js +11 -11
- package/synthetizer/worklet_system/main_processor.js +107 -8
- package/synthetizer/worklet_system/message_protocol/handle_message.js +2 -34
- package/synthetizer/worklet_system/message_protocol/worklet_message.js +25 -38
- package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +1 -2
- package/synthetizer/worklet_system/worklet_methods/note_on.js +10 -16
- package/synthetizer/worklet_system/worklet_methods/program_change.js +2 -4
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +12 -16
|
@@ -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();
|
|
@@ -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
|
{
|
|
@@ -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,6 +31,18 @@ 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
|
+
*/
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @type {SynthMethodOptions}
|
|
41
|
+
*/
|
|
42
|
+
const DEFAULT_SYNTH_METHOD_OPTIONS = {
|
|
43
|
+
time: 0
|
|
44
|
+
};
|
|
45
|
+
|
|
35
46
|
|
|
36
47
|
export class Synthetizer
|
|
37
48
|
{
|
|
@@ -566,20 +577,53 @@ export class Synthetizer
|
|
|
566
577
|
});
|
|
567
578
|
}
|
|
568
579
|
|
|
580
|
+
/**
|
|
581
|
+
* sends a raw MIDI message to the synthesizer.
|
|
582
|
+
* @param message {number[]|Uint8Array} the midi message, each number is a byte.
|
|
583
|
+
* @param channelOffset {number} the channel offset of the message.
|
|
584
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
585
|
+
*/
|
|
586
|
+
sendMessage(message, channelOffset = 0, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
587
|
+
{
|
|
588
|
+
this._sendInternal(message, channelOffset, false, eventOptions);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* @param message {number[]|Uint8Array}
|
|
593
|
+
* @param offset {number}
|
|
594
|
+
* @param force {boolean}
|
|
595
|
+
* @param eventOptions {SynthMethodOptions}
|
|
596
|
+
* @private
|
|
597
|
+
*/
|
|
598
|
+
_sendInternal(message, offset, force = false, eventOptions)
|
|
599
|
+
{
|
|
600
|
+
const opts = fillWithDefaults(eventOptions ?? {}, DEFAULT_SYNTH_METHOD_OPTIONS);
|
|
601
|
+
this.post({
|
|
602
|
+
messageType: workletMessageType.midiMessage,
|
|
603
|
+
messageData: [new Uint8Array(message), offset, force, opts]
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
|
|
569
608
|
/**
|
|
570
609
|
* Starts playing a note
|
|
571
610
|
* @param channel {number} usually 0-15: the channel to play the note.
|
|
572
611
|
* @param midiNote {number} 0-127 the key number of the note.
|
|
573
612
|
* @param velocity {number} 0-127 the velocity of the note (generally controls loudness).
|
|
574
|
-
* @param
|
|
613
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
575
614
|
*/
|
|
576
|
-
noteOn(channel, midiNote, velocity,
|
|
615
|
+
noteOn(channel, midiNote, velocity, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
577
616
|
{
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
617
|
+
const ch = channel % 16;
|
|
618
|
+
const offset = channel - ch;
|
|
619
|
+
midiNote %= 128;
|
|
620
|
+
velocity %= 128;
|
|
621
|
+
// check for legacy "enableDebugging"
|
|
622
|
+
if (eventOptions === true)
|
|
623
|
+
{
|
|
624
|
+
eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS;
|
|
625
|
+
}
|
|
626
|
+
this.sendMessage([messageTypes.noteOn | ch, midiNote, velocity], offset, eventOptions);
|
|
583
627
|
}
|
|
584
628
|
|
|
585
629
|
/**
|
|
@@ -587,25 +631,15 @@ export class Synthetizer
|
|
|
587
631
|
* @param channel {number} usually 0-15: the channel of the note.
|
|
588
632
|
* @param midiNote {number} 0-127 the key number of the note.
|
|
589
633
|
* @param force {boolean} instantly kills the note if true.
|
|
634
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
590
635
|
*/
|
|
591
|
-
noteOff(channel, midiNote, force = false)
|
|
636
|
+
noteOff(channel, midiNote, force = false, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
592
637
|
{
|
|
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
|
-
}
|
|
638
|
+
midiNote %= 128;
|
|
639
|
+
|
|
640
|
+
const ch = channel % 16;
|
|
641
|
+
const offset = channel - ch;
|
|
642
|
+
this._sendInternal([messageTypes.noteOff | ch, midiNote], offset, force, eventOptions);
|
|
609
643
|
}
|
|
610
644
|
|
|
611
645
|
/**
|
|
@@ -628,20 +662,25 @@ export class Synthetizer
|
|
|
628
662
|
* @param controllerNumber {number} 0-127 the MIDI CC number.
|
|
629
663
|
* @param controllerValue {number} 0-127 the controller value.
|
|
630
664
|
* @param force {boolean} forces the controller-change message, even if it's locked or gm system is set and the cc is bank select.
|
|
665
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
631
666
|
*/
|
|
632
|
-
controllerChange(channel, controllerNumber, controllerValue, force = false)
|
|
667
|
+
controllerChange(channel, controllerNumber, controllerValue, force = false, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
633
668
|
{
|
|
634
669
|
if (controllerNumber > 127 || controllerNumber < 0)
|
|
635
670
|
{
|
|
636
671
|
throw new Error(`Invalid controller number: ${controllerNumber}`);
|
|
637
672
|
}
|
|
638
|
-
controllerValue = Math.floor(controllerValue);
|
|
639
|
-
controllerNumber = Math.floor(controllerNumber);
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
673
|
+
controllerValue = Math.floor(controllerValue) % 128;
|
|
674
|
+
controllerNumber = Math.floor(controllerNumber) % 128;
|
|
675
|
+
// controller change has its own message for the force property
|
|
676
|
+
const ch = channel % 16;
|
|
677
|
+
const offset = channel - ch;
|
|
678
|
+
this._sendInternal(
|
|
679
|
+
[messageTypes.controllerChange | ch, controllerNumber, controllerValue],
|
|
680
|
+
offset,
|
|
681
|
+
force,
|
|
682
|
+
eventOptions
|
|
683
|
+
);
|
|
645
684
|
}
|
|
646
685
|
|
|
647
686
|
/**
|
|
@@ -660,14 +699,14 @@ export class Synthetizer
|
|
|
660
699
|
* Applies pressure to a given channel.
|
|
661
700
|
* @param channel {number} usually 0-15: the channel to change the controller.
|
|
662
701
|
* @param pressure {number} 0-127: the pressure to apply.
|
|
702
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
663
703
|
*/
|
|
664
|
-
channelPressure(channel, pressure)
|
|
704
|
+
channelPressure(channel, pressure, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
665
705
|
{
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
});
|
|
706
|
+
const ch = channel % 16;
|
|
707
|
+
const offset = channel - ch;
|
|
708
|
+
pressure %= 128;
|
|
709
|
+
this.sendMessage([messageTypes.channelPressure | ch, pressure], offset, eventOptions);
|
|
671
710
|
}
|
|
672
711
|
|
|
673
712
|
/**
|
|
@@ -675,14 +714,29 @@ export class Synthetizer
|
|
|
675
714
|
* @param channel {number} usually 0-15: the channel to change the controller.
|
|
676
715
|
* @param midiNote {number} 0-127: the MIDI note.
|
|
677
716
|
* @param pressure {number} 0-127: the pressure to apply.
|
|
717
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
678
718
|
*/
|
|
679
|
-
polyPressure(channel, midiNote, pressure)
|
|
719
|
+
polyPressure(channel, midiNote, pressure, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
680
720
|
{
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
721
|
+
const ch = channel % 16;
|
|
722
|
+
const offset = channel - ch;
|
|
723
|
+
midiNote %= 128;
|
|
724
|
+
pressure %= 128;
|
|
725
|
+
this.sendMessage([messageTypes.polyPressure | ch, midiNote, pressure], offset, eventOptions);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Sets the pitch of the given channel.
|
|
730
|
+
* @param channel {number} usually 0-15: the channel to change pitch.
|
|
731
|
+
* @param MSB {number} SECOND byte of the MIDI pitchWheel message.
|
|
732
|
+
* @param LSB {number} FIRST byte of the MIDI pitchWheel message.
|
|
733
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
734
|
+
*/
|
|
735
|
+
pitchWheel(channel, MSB, LSB, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
736
|
+
{
|
|
737
|
+
const ch = channel % 16;
|
|
738
|
+
const offset = channel - ch;
|
|
739
|
+
this.sendMessage([messageTypes.pitchBend | ch, LSB, MSB], offset, eventOptions);
|
|
686
740
|
}
|
|
687
741
|
|
|
688
742
|
/**
|
|
@@ -697,21 +751,6 @@ export class Synthetizer
|
|
|
697
751
|
this.worklet.port.postMessage(data);
|
|
698
752
|
}
|
|
699
753
|
|
|
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
754
|
/**
|
|
716
755
|
* Transposes the synthetizer's pitch by given semitones amount (percussion channels don’t get affected).
|
|
717
756
|
* @param semitones {number} the semitones to transpose by.
|
|
@@ -776,16 +815,14 @@ export class Synthetizer
|
|
|
776
815
|
* Changes the patch for a given channel
|
|
777
816
|
* @param channel {number} usually 0-15: the channel to change
|
|
778
817
|
* @param programNumber {number} 0-127 the MIDI patch number
|
|
779
|
-
* @param userChange {boolean} indicates if user has called the program change.
|
|
780
818
|
* defaults to false
|
|
781
819
|
*/
|
|
782
|
-
programChange(channel, programNumber
|
|
820
|
+
programChange(channel, programNumber)
|
|
783
821
|
{
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
});
|
|
822
|
+
const ch = channel % 16;
|
|
823
|
+
const offset = channel - ch;
|
|
824
|
+
programNumber %= 128;
|
|
825
|
+
this.sendMessage([messageTypes.programChange | ch, programNumber], offset);
|
|
789
826
|
}
|
|
790
827
|
|
|
791
828
|
/**
|
|
@@ -796,11 +833,14 @@ export class Synthetizer
|
|
|
796
833
|
*/
|
|
797
834
|
velocityOverride(channel, velocity)
|
|
798
835
|
{
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
836
|
+
const ch = channel % 16;
|
|
837
|
+
const offset = channel - ch;
|
|
838
|
+
this._sendInternal(
|
|
839
|
+
[messageTypes.controllerChange | ch, channelConfiguration.velocityOverride, velocity],
|
|
840
|
+
offset,
|
|
841
|
+
true,
|
|
842
|
+
DEFAULT_SYNTH_METHOD_OPTIONS
|
|
843
|
+
);
|
|
804
844
|
}
|
|
805
845
|
|
|
806
846
|
/**
|
|
@@ -851,14 +891,16 @@ export class Synthetizer
|
|
|
851
891
|
* @param messageData {number[]|ArrayLike|Uint8Array} the message's data
|
|
852
892
|
* (excluding the F0 byte, but including the F7 at the end).
|
|
853
893
|
* @param channelOffset {number} channel offset for the system exclusive message, defaults to zero.
|
|
894
|
+
* @param eventOptions {SynthMethodOptions} additional options for this command.
|
|
854
895
|
*/
|
|
855
|
-
systemExclusive(messageData, channelOffset = 0)
|
|
896
|
+
systemExclusive(messageData, channelOffset = 0, eventOptions = DEFAULT_SYNTH_METHOD_OPTIONS)
|
|
856
897
|
{
|
|
857
|
-
this.
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
898
|
+
this._sendInternal(
|
|
899
|
+
[messageTypes.systemExclusive, ...Array.from(messageData)],
|
|
900
|
+
channelOffset,
|
|
901
|
+
false,
|
|
902
|
+
eventOptions
|
|
903
|
+
);
|
|
862
904
|
}
|
|
863
905
|
|
|
864
906
|
// noinspection JSUnusedGlobalSymbols
|
|
@@ -919,70 +961,6 @@ export class Synthetizer
|
|
|
919
961
|
});
|
|
920
962
|
}
|
|
921
963
|
|
|
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
964
|
/**
|
|
987
965
|
* Updates the reverb processor with a new impulse response.
|
|
988
966
|
* @param buffer {AudioBuffer} the new reverb impulse response.
|