spessasynth_lib 3.23.6 → 3.23.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@types/midi_parser/basic_midi.d.ts +41 -21
- package/@types/midi_parser/midi_builder.d.ts +0 -4
- package/@types/midi_parser/midi_data.d.ts +38 -20
- package/@types/sequencer/sequencer.d.ts +2 -1
- package/@types/soundfont/basic_soundfont/basic_sample.d.ts +5 -0
- package/@types/soundfont/basic_soundfont/basic_soundfont.d.ts +8 -0
- package/@types/soundfont/dls/dls_sample.d.ts +0 -5
- package/@types/soundfont/read_sf2/samples.d.ts +0 -1
- package/midi_parser/basic_midi.js +269 -109
- package/midi_parser/midi_builder.js +1 -106
- package/midi_parser/midi_data.js +140 -90
- package/midi_parser/midi_loader.js +2 -0
- package/package.json +1 -1
- package/sequencer/sequencer.js +3 -2
- package/soundfont/basic_soundfont/basic_sample.js +15 -6
- package/soundfont/basic_soundfont/basic_soundfont.js +64 -0
- package/soundfont/dls/dls_sample.js +9 -8
- package/synthetizer/synth_event_handler.js +23 -2
- package/synthetizer/synth_soundfont_manager.js +0 -0
- package/synthetizer/worklet_processor.min.js +9 -9
package/midi_parser/midi_data.js
CHANGED
|
@@ -5,118 +5,167 @@
|
|
|
5
5
|
export class MidiData
|
|
6
6
|
{
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* The time division of the sequence, representing the number of ticks per beat.
|
|
9
|
+
* @type {number}
|
|
10
|
+
*/
|
|
11
|
+
timeDivision = 0;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The duration of the sequence, in seconds.
|
|
15
|
+
* @type {number}
|
|
16
|
+
*/
|
|
17
|
+
duration = 0;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The tempo changes in the sequence, ordered from the last change to the first.
|
|
21
|
+
* Each change is represented by an object with a tick position and a tempo value in beats per minute.
|
|
22
|
+
* @type {{ticks: number, tempo: number}[]}
|
|
23
|
+
*/
|
|
24
|
+
tempoChanges = [{ ticks: 0, tempo: 120 }];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A string containing the copyright information for the MIDI sequence.
|
|
28
|
+
* @type {string}
|
|
29
|
+
*/
|
|
30
|
+
copyright = "";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The number of tracks in the MIDI sequence.
|
|
34
|
+
* @type {number}
|
|
35
|
+
*/
|
|
36
|
+
tracksAmount = 0;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* An array containing the lyrics of the sequence, stored as binary chunks (Uint8Array).
|
|
40
|
+
* @type {Uint8Array[]}
|
|
41
|
+
*/
|
|
42
|
+
lyrics = [];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The tick position of the first note-on event in the MIDI sequence.
|
|
46
|
+
* @type {number}
|
|
47
|
+
*/
|
|
48
|
+
firstNoteOn = 0;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The MIDI key range used in the sequence, represented by a minimum and maximum note value.
|
|
52
|
+
* @type {{min: number, max: number}}
|
|
53
|
+
*/
|
|
54
|
+
keyRange = { min: 0, max: 127 };
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The tick position of the last voice event (such as note-on, note-off, or control change) in the sequence.
|
|
58
|
+
* @type {number}
|
|
59
|
+
*/
|
|
60
|
+
lastVoiceEventTick = 0;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* An array of MIDI port numbers used by each track in the sequence.
|
|
64
|
+
* @type {number[]}
|
|
65
|
+
*/
|
|
66
|
+
midiPorts = [0];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* An array of channel offsets for each MIDI port, using the SpessaSynth method.
|
|
70
|
+
* @type {number[]}
|
|
71
|
+
*/
|
|
72
|
+
midiPortChannelOffsets = [0];
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* A list of sets, where each set contains the MIDI channels used by each track in the sequence.
|
|
76
|
+
* @type {Set<number>[]}
|
|
77
|
+
*/
|
|
78
|
+
usedChannelsOnTrack = [];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The loop points (in ticks) of the sequence, including both start and end points.
|
|
82
|
+
* @type {{start: number, end: number}}
|
|
83
|
+
*/
|
|
84
|
+
loop = { start: 0, end: 0 };
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The name of the MIDI sequence.
|
|
88
|
+
* @type {string}
|
|
89
|
+
*/
|
|
90
|
+
midiName = "";
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* A boolean indicating if the sequence's name is the same as the file name.
|
|
94
|
+
* @type {boolean}
|
|
95
|
+
*/
|
|
96
|
+
midiNameUsesFileName = false;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The file name of the MIDI sequence, if provided by the MIDI class.
|
|
100
|
+
* @type {string}
|
|
101
|
+
*/
|
|
102
|
+
fileName = "";
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The raw, encoded MIDI name, represented as a Uint8Array.
|
|
106
|
+
* @type {Uint8Array}
|
|
107
|
+
*/
|
|
108
|
+
rawMidiName = undefined;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A boolean indicating if the MIDI file contains an embedded soundfont.
|
|
112
|
+
* If the embedded soundfont is undefined, this will be false.
|
|
113
|
+
* @type {boolean}
|
|
114
|
+
*/
|
|
115
|
+
isEmbedded = false;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* The MIDI file's format, which can be 0, 1, or 2, indicating the type of the MIDI file.
|
|
119
|
+
* @type {number}
|
|
120
|
+
*/
|
|
121
|
+
format = 0;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The RMID (Resource Interchangeable MIDI) info data, if the file is RMID formatted.
|
|
125
|
+
* Otherwise, this field is undefined.
|
|
126
|
+
* @type {Object<string, IndexedByteArray>}
|
|
127
|
+
*/
|
|
128
|
+
RMIDInfo = {};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The bank offset used for RMID files.
|
|
132
|
+
* @type {number}
|
|
133
|
+
*/
|
|
134
|
+
bankOffset = 0;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Constructor that copies data from a BasicMIDI instance, except for tracks and embeddedSoundFont.
|
|
138
|
+
* @param {BasicMIDI} midi - The BasicMIDI instance to copy data from.
|
|
9
139
|
*/
|
|
10
140
|
constructor(midi)
|
|
11
141
|
{
|
|
12
|
-
/**
|
|
13
|
-
* The time division of the sequence
|
|
14
|
-
* @type {number}
|
|
15
|
-
*/
|
|
16
142
|
this.timeDivision = midi.timeDivision;
|
|
17
|
-
/**
|
|
18
|
-
* The duration of the sequence, in seconds
|
|
19
|
-
* @type {number}
|
|
20
|
-
*/
|
|
21
143
|
this.duration = midi.duration;
|
|
22
|
-
/**
|
|
23
|
-
* The tempo changes in the sequence, ordered from last to first
|
|
24
|
-
* @type {{ticks: number, tempo: number}[]}
|
|
25
|
-
*/
|
|
26
144
|
this.tempoChanges = midi.tempoChanges;
|
|
27
|
-
/**
|
|
28
|
-
* Contains the copyright strings
|
|
29
|
-
* @type {string}
|
|
30
|
-
*/
|
|
31
145
|
this.copyright = midi.copyright;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* The amount of tracks in the sequence
|
|
35
|
-
* @type {number}
|
|
36
|
-
*/
|
|
37
146
|
this.tracksAmount = midi.tracksAmount;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* The lyrics of the sequence as binary chunks
|
|
41
|
-
* @type {Uint8Array[]}
|
|
42
|
-
*/
|
|
43
147
|
this.lyrics = midi.lyrics;
|
|
44
|
-
|
|
45
148
|
this.firstNoteOn = midi.firstNoteOn;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* The MIDI's key range
|
|
49
|
-
* @type {{min: number, max: number}}
|
|
50
|
-
*/
|
|
51
149
|
this.keyRange = midi.keyRange;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* The last voice (note on, off, cc change etc.) event tick
|
|
55
|
-
* @type {number}
|
|
56
|
-
*/
|
|
57
150
|
this.lastVoiceEventTick = midi.lastVoiceEventTick;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Midi port numbers for each track
|
|
61
|
-
* @type {number[]}
|
|
62
|
-
*/
|
|
63
151
|
this.midiPorts = midi.midiPorts;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Channel offsets for each port, using the SpessaSynth method
|
|
67
|
-
* @type {number[]}
|
|
68
|
-
*/
|
|
69
152
|
this.midiPortChannelOffsets = midi.midiPortChannelOffsets;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* All channels that each track uses
|
|
73
|
-
* @type {Set<number>[]}
|
|
74
|
-
*/
|
|
75
153
|
this.usedChannelsOnTrack = midi.usedChannelsOnTrack;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* The loop points (in ticks) of the sequence
|
|
79
|
-
* @type {{start: number, end: number}}
|
|
80
|
-
*/
|
|
81
154
|
this.loop = midi.loop;
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* The sequence's name
|
|
85
|
-
* @type {string}
|
|
86
|
-
*/
|
|
87
155
|
this.midiName = midi.midiName;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* The file name of the sequence, if provided in the MIDI class
|
|
91
|
-
* @type {string}
|
|
92
|
-
*/
|
|
156
|
+
this.midiNameUsesFileName = midi.midiNameUsesFileName;
|
|
93
157
|
this.fileName = midi.fileName;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* The raw, encoded MIDI name.
|
|
97
|
-
* @type {Uint8Array}
|
|
98
|
-
*/
|
|
99
158
|
this.rawMidiName = midi.rawMidiName;
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Indicates if the midi has an embedded soundfont
|
|
103
|
-
* @type {boolean}
|
|
104
|
-
*/
|
|
105
|
-
this.isEmbedded = midi.embeddedSoundFont !== undefined;
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* The RMID Info data if RMID, otherwise undefined
|
|
109
|
-
* @type {Object<string, IndexedByteArray>}
|
|
110
|
-
*/
|
|
159
|
+
this.format = midi.format;
|
|
111
160
|
this.RMIDInfo = midi.RMIDInfo;
|
|
112
|
-
/**
|
|
113
|
-
* The bank offset for RMIDI
|
|
114
|
-
* @type {number}
|
|
115
|
-
*/
|
|
116
161
|
this.bankOffset = midi.bankOffset;
|
|
162
|
+
|
|
163
|
+
// Set isEmbedded based on the presence of an embeddedSoundFont
|
|
164
|
+
this.isEmbedded = midi.embeddedSoundFont !== undefined;
|
|
117
165
|
}
|
|
118
166
|
}
|
|
119
167
|
|
|
168
|
+
|
|
120
169
|
/**
|
|
121
170
|
*
|
|
122
171
|
* @type {MidiData}
|
|
@@ -143,6 +192,7 @@ export const DUMMY_MIDI_DATA = {
|
|
|
143
192
|
timeDivision: 0,
|
|
144
193
|
keyRange: { min: 0, max: 127 },
|
|
145
194
|
isEmbedded: false,
|
|
146
|
-
RMIDInfo:
|
|
147
|
-
bankOffset: 0
|
|
195
|
+
RMIDInfo: {},
|
|
196
|
+
bankOffset: 0,
|
|
197
|
+
midiNameUsesFileName: false
|
|
148
198
|
};
|
|
@@ -582,6 +582,7 @@ class MIDI extends BasicMIDI
|
|
|
582
582
|
|
|
583
583
|
this.fileName = fileName;
|
|
584
584
|
this.midiName = this.midiName.trim();
|
|
585
|
+
this.midiNameUsesFileName = false;
|
|
585
586
|
// if midiName is "", use the file name
|
|
586
587
|
if (this.midiName.length === 0)
|
|
587
588
|
{
|
|
@@ -590,6 +591,7 @@ class MIDI extends BasicMIDI
|
|
|
590
591
|
consoleColors.info
|
|
591
592
|
);
|
|
592
593
|
this.midiName = formatTitle(fileName);
|
|
594
|
+
this.midiNameUsesFileName = true;
|
|
593
595
|
// encode it too
|
|
594
596
|
this.rawMidiName = new Uint8Array(this.midiName.length);
|
|
595
597
|
for (let i = 0; i < this.midiName.length; i++)
|
package/package.json
CHANGED
package/sequencer/sequencer.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
} from "./worklet_sequencer/sequencer_message.js";
|
|
9
9
|
import { SpessaSynthWarn } from "../utils/loggin.js";
|
|
10
10
|
import { DUMMY_MIDI_DATA, MidiData } from "../midi_parser/midi_data.js";
|
|
11
|
+
import { BasicMIDI } from "../midi_parser/basic_midi.js";
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* sequencer.js
|
|
@@ -95,7 +96,7 @@ export class Sequencer
|
|
|
95
96
|
this.absoluteStartTime = this.synth.currentTime;
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
|
-
* @type {function(
|
|
99
|
+
* @type {function(BasicMIDI)}
|
|
99
100
|
* @private
|
|
100
101
|
*/
|
|
101
102
|
this._getMIDIResolve = undefined;
|
|
@@ -466,7 +467,7 @@ export class Sequencer
|
|
|
466
467
|
case WorkletSequencerReturnMessageType.getMIDI:
|
|
467
468
|
if (this._getMIDIResolve)
|
|
468
469
|
{
|
|
469
|
-
this._getMIDIResolve(messageData);
|
|
470
|
+
this._getMIDIResolve(BasicMIDI.copyFrom(messageData));
|
|
470
471
|
}
|
|
471
472
|
}
|
|
472
473
|
}
|
|
@@ -87,6 +87,12 @@ export class BasicSample
|
|
|
87
87
|
* @type {number}
|
|
88
88
|
*/
|
|
89
89
|
this.useCount = 0;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The sample's audio data
|
|
93
|
+
* @type {Float32Array}
|
|
94
|
+
*/
|
|
95
|
+
this.sampleData = undefined;
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
/**
|
|
@@ -94,9 +100,14 @@ export class BasicSample
|
|
|
94
100
|
*/
|
|
95
101
|
getRawData()
|
|
96
102
|
{
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
103
|
+
const uint8 = new Uint8Array(this.sampleData.length * 2);
|
|
104
|
+
for (let i = 0; i < this.sampleData.length; i++)
|
|
105
|
+
{
|
|
106
|
+
const sample = Math.floor(this.sampleData[i] * 32768);
|
|
107
|
+
uint8[i * 2] = sample & 0xFF; // lower byte
|
|
108
|
+
uint8[i * 2 + 1] = (sample >> 8) & 0xFF; // upper byte
|
|
109
|
+
}
|
|
110
|
+
return uint8;
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
/**
|
|
@@ -133,8 +144,6 @@ export class BasicSample
|
|
|
133
144
|
*/
|
|
134
145
|
getAudioData()
|
|
135
146
|
{
|
|
136
|
-
|
|
137
|
-
e.name = "NotImplementedError";
|
|
138
|
-
throw e;
|
|
147
|
+
return this.sampleData;
|
|
139
148
|
}
|
|
140
149
|
}
|
|
@@ -3,6 +3,11 @@ import { consoleColors } from "../../utils/other.js";
|
|
|
3
3
|
import { write } from "./write_sf2/write.js";
|
|
4
4
|
import { defaultModulators, Modulator } from "./modulator.js";
|
|
5
5
|
import { writeDLS } from "./write_dls/write_dls.js";
|
|
6
|
+
import { BasicSample } from "./basic_sample.js";
|
|
7
|
+
import { BasicInstrumentZone, BasicPresetZone } from "./basic_zones.js";
|
|
8
|
+
import { Generator, generatorTypes } from "./generator.js";
|
|
9
|
+
import { BasicInstrument } from "./basic_instrument.js";
|
|
10
|
+
import { BasicPreset } from "./basic_preset.js";
|
|
6
11
|
|
|
7
12
|
class BasicSoundFont
|
|
8
13
|
{
|
|
@@ -75,6 +80,65 @@ class BasicSoundFont
|
|
|
75
80
|
return new BasicSoundFont({ presets: presets, info: mainSf.soundFontInfo });
|
|
76
81
|
}
|
|
77
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Creates a simple soundfont with one saw wave preset.
|
|
85
|
+
* @returns {ArrayBufferLike}
|
|
86
|
+
*/
|
|
87
|
+
static getDummySoundfontFile()
|
|
88
|
+
{
|
|
89
|
+
const font = new BasicSoundFont();
|
|
90
|
+
const sample = new BasicSample(
|
|
91
|
+
"Saw",
|
|
92
|
+
44100,
|
|
93
|
+
65,
|
|
94
|
+
20,
|
|
95
|
+
0,
|
|
96
|
+
0,
|
|
97
|
+
0,
|
|
98
|
+
127
|
|
99
|
+
);
|
|
100
|
+
sample.sampleData = new Float32Array(128);
|
|
101
|
+
for (let i = 0; i < 128; i++)
|
|
102
|
+
{
|
|
103
|
+
sample.sampleData[i] = (i / 128) * 2 - 1;
|
|
104
|
+
}
|
|
105
|
+
font.samples.push(sample);
|
|
106
|
+
|
|
107
|
+
const gZone = new BasicInstrumentZone();
|
|
108
|
+
gZone.isGlobal = true;
|
|
109
|
+
gZone.generators.push(new Generator(generatorTypes.initialAttenuation, 375));
|
|
110
|
+
gZone.generators.push(new Generator(generatorTypes.releaseVolEnv, -1000));
|
|
111
|
+
gZone.generators.push(new Generator(generatorTypes.sampleModes, 1));
|
|
112
|
+
|
|
113
|
+
const zone1 = new BasicInstrumentZone();
|
|
114
|
+
zone1.sample = sample;
|
|
115
|
+
|
|
116
|
+
const zone2 = new BasicInstrumentZone();
|
|
117
|
+
zone2.sample = sample;
|
|
118
|
+
zone2.generators.push(new Generator(generatorTypes.fineTune, -9));
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
const inst = new BasicInstrument();
|
|
122
|
+
inst.instrumentName = "Saw Wave";
|
|
123
|
+
inst.instrumentZones.push(gZone);
|
|
124
|
+
inst.instrumentZones.push(zone1);
|
|
125
|
+
inst.instrumentZones.push(zone2);
|
|
126
|
+
font.instruments.push(inst);
|
|
127
|
+
|
|
128
|
+
const pZone = new BasicPresetZone();
|
|
129
|
+
pZone.instrument = inst;
|
|
130
|
+
|
|
131
|
+
const preset = new BasicPreset(font.defaultModulators);
|
|
132
|
+
preset.presetName = "Saw Wave";
|
|
133
|
+
preset.presetZones.push(pZone);
|
|
134
|
+
font.presets.push(preset);
|
|
135
|
+
|
|
136
|
+
font.soundFontInfo["ifil"] = "2.1";
|
|
137
|
+
font.soundFontInfo["isng"] = "EMU8000";
|
|
138
|
+
font.soundFontInfo["INAM"] = "Dummy";
|
|
139
|
+
return font.write().buffer;
|
|
140
|
+
}
|
|
141
|
+
|
|
78
142
|
removeUnusedElements()
|
|
79
143
|
{
|
|
80
144
|
this.instruments.forEach(i =>
|
|
@@ -62,13 +62,14 @@ export class DLSSample extends BasicSample
|
|
|
62
62
|
}
|
|
63
63
|
return this.compressedData;
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
return super.getRawData();
|
|
66
|
+
// const uint8 = new Uint8Array(this.sampleData.length * 2);
|
|
67
|
+
// for (let i = 0; i < this.sampleData.length; i++)
|
|
68
|
+
// {
|
|
69
|
+
// const sample = Math.floor(this.sampleData[i] * 32768);
|
|
70
|
+
// uint8[i * 2] = sample & 0xFF; // lower byte
|
|
71
|
+
// uint8[i * 2 + 1] = (sample >> 8) & 0xFF; // upper byte
|
|
72
|
+
// }
|
|
73
|
+
// return uint8;
|
|
73
74
|
}
|
|
74
75
|
}
|
|
@@ -161,12 +161,33 @@ export class EventHandler
|
|
|
161
161
|
{
|
|
162
162
|
setTimeout(() =>
|
|
163
163
|
{
|
|
164
|
-
Object.values(this.events[name]).forEach(ev =>
|
|
164
|
+
Object.values(this.events[name]).forEach(ev =>
|
|
165
|
+
{
|
|
166
|
+
try
|
|
167
|
+
{
|
|
168
|
+
ev(eventData);
|
|
169
|
+
}
|
|
170
|
+
catch (e)
|
|
171
|
+
{
|
|
172
|
+
console.error(`Error while executing an event callback for ${name}:`, e);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
165
175
|
}, this.timeDelay * 1000);
|
|
166
176
|
}
|
|
167
177
|
else
|
|
168
178
|
{
|
|
169
|
-
Object.values(this.events[name]).forEach(ev =>
|
|
179
|
+
Object.values(this.events[name]).forEach(ev =>
|
|
180
|
+
{
|
|
181
|
+
try
|
|
182
|
+
{
|
|
183
|
+
ev(eventData);
|
|
184
|
+
}
|
|
185
|
+
catch (e)
|
|
186
|
+
{
|
|
187
|
+
console.error(`Error while executing an event callback for ${name}:`, e);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
);
|
|
170
191
|
}
|
|
171
192
|
}
|
|
172
193
|
}
|
|
File without changes
|