File size: 6,498 Bytes
b0bfea8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/**
 * synth_event_handler.js
 * purpose: manages the synthesizer's event system, calling assinged functions when synthesizer requests dispatching the event
 */

/**
 * @typedef {Object} NoteOnCallback
 * @property {number} midiNote - The MIDI note number.
 * @property {number} channel - The MIDI channel number.
 * @property {number} velocity - The velocity of the note.
 */

/**
 * @typedef {Object} NoteOffCallback
 * @property {number} midiNote - The MIDI note number.
 * @property {number} channel - The MIDI channel number.
 */

/**
 * @typedef {Object} DrumChangeCallback
 * @property {number} channel - The MIDI channel number.
 * @property {boolean} isDrumChannel - Indicates if the channel is a drum channel.
 */

/**
 * @typedef {Object} ProgramChangeCallback
 * @property {number} channel - The MIDI channel number.
 * @property {number} program - The program number.
 * @property {number} bank - The bank number.
 */

/**
 * @typedef {Object} ControllerChangeCallback
 * @property {number} channel - The MIDI channel number.
 * @property {number} controllerNumber - The controller number.
 * @property {number} controllerValue - The value of the controller.
 */

/**
 * @typedef {Object} MuteChannelCallback
 * @property {number} channel - The MIDI channel number.
 * @property {boolean} isMuted - Indicates if the channel is muted.
 */

/**
 * @typedef {Object} PresetListChangeCallbackSingle
 * @property {string} presetName - The name of the preset.
 * @property {number} bank - The bank number.
 * @property {number} program - The program number.
 */

/**
 * @typedef {PresetListChangeCallbackSingle[]} PresetListChangeCallback - A list of preset objects.
 */

/**
 * @typedef {Object} SynthDisplayCallback
 * @property {Uint8Array} displayData - The data to display.
 * @property {SynthDisplayType} displayType - The type of display.
 */

/**
 * @typedef {Object} PitchWheelCallback
 * @property {number} channel - The MIDI channel number.
 * @property {number} MSB - The most significant byte of the pitch wheel value.
 * @property {number} LSB - The least significant byte of the pitch wheel value.
 */

/**
 * @typedef {Object} ChannelPressureCallback
 * @property {number} channel - The MIDI channel number.
 * @property {number} pressure - The pressure value.
 */

/**
 * @typedef {Error} SoundfontErrorCallback - The error message for soundfont errors.
 */

/**
 * @typedef {
 *     NoteOnCallback |
 *     NoteOffCallback |
 *     DrumChangeCallback |
 *     ProgramChangeCallback |
 *     ControllerChangeCallback |
 *     MuteChannelCallback |
 *     PresetListChangeCallback |
 *     PitchWheelCallback |
 *     SoundfontErrorCallback |
 *     ChannelPressureCallback |
 *     SynthDisplayCallback |
 *     undefined
 * } EventCallbackData
 */

/**
 * @typedef {
 * "noteon"|
 * "noteoff"|
 * "pitchwheel"|
 * "controllerchange"|
 * "programchange"|
 * "channelpressure"|
 * "polypressure" |
 * "drumchange"|
 * "stopall"|
 * "newchannel"|
 * "mutechannel"|
 * "presetlistchange"|
 * "allcontrollerreset"|
 * "soundfonterror"|
 * "synthdisplay"} EventTypes
 */
export class EventHandler
{
    /**
     * A new synthesizer event handler
     */
    constructor()
    {
        /**
         * The main list of events
         * @type {Object<EventTypes, Object<string, function(EventCallbackData)>>}
         */
        this.events = {
            "noteoff": {},              // called on note off message
            "noteon": {},               // called on note on message
            "pitchwheel": {},           // called on pitch wheel change
            "controllerchange": {},     // called on controller change
            "programchange": {},        // called on program change
            "channelpressure": {},      // called on channel pressure message
            "polypressure": {},         // called on poly pressure message
            "drumchange": {},           // called when channel type changes
            "stopall": {},              // called when synth receives stop all command
            "newchannel": {},           // called when a new channel is created
            "mutechannel": {},          // called when a channel is muted/unmuted
            "presetlistchange": {},     // called when the preset list changes (soundfont gets reloaded)
            "allcontrollerreset": {},   // called when all controllers are reset
            "soundfonterror": {},       // called when a soundfont parsing error occurs
            "synthdisplay": {}          // called when there's a SysEx message to display some text
        };
        
        /**
         * Set to 0 to disabled, otherwise in seconds
         * @type {number}
         */
        this.timeDelay = 0;
    }
    
    /**
     * Adds a new event listener
     * @param name {EventTypes}
     * @param id {string} the unique identifier for the event (to delete it
     * @param callback {function(EventCallbackData)}
     */
    addEvent(name, id, callback)
    {
        this.events[name][id] = callback;
    }
    
    /**
     * Removes an event listener
     * @param name {EventTypes}
     * @param id {string}
     */
    removeEvent(name, id)
    {
        delete this.events[name][id];
    }
    
    /**
     * Calls the given event
     * @param name {EventTypes}
     * @param eventData {EventCallbackData}
     */
    callEvent(name, eventData)
    {
        if (this.events[name])
        {
            if (this.timeDelay > 0)
            {
                setTimeout(() =>
                {
                    Object.values(this.events[name]).forEach(ev =>
                    {
                        try
                        {
                            ev(eventData);
                        }
                        catch (e)
                        {
                            console.error(`Error while executing an event callback for ${name}:`, e);
                        }
                    });
                }, this.timeDelay * 1000);
            }
            else
            {
                Object.values(this.events[name]).forEach(ev =>
                    {
                        try
                        {
                            ev(eventData);
                        }
                        catch (e)
                        {
                            console.error(`Error while executing an event callback for ${name}:`, e);
                        }
                    }
                );
            }
        }
    }
}