@karaplay/file-coder 1.5.0 → 1.5.2

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.
@@ -0,0 +1,325 @@
1
+ "use strict";
2
+ /**
3
+ * MIDIEvents : Read and edit events from various sources (ArrayBuffer, Stream)
4
+ */
5
+ // MIDIEvents is a namespace-like object with static constants and methods
6
+ class MIDIEvents {
7
+ // Prevent instantiation
8
+ constructor() {
9
+ throw new Error('MIDIEvents function not intended to be run.');
10
+ }
11
+ // Create an event stream parser
12
+ static createParser(stream, startAt, strictMode) {
13
+ let eventTypeByte;
14
+ let event;
15
+ // MIDI events vars
16
+ let MIDIEventType;
17
+ let MIDIEventChannel;
18
+ let MIDIEventParam1;
19
+ // Wrap DataView into a data stream
20
+ let wrappedStream;
21
+ if (stream instanceof DataView) {
22
+ wrappedStream = {
23
+ position: startAt || 0,
24
+ buffer: stream,
25
+ readUint8: function () {
26
+ return this.buffer.getUint8(this.position++);
27
+ },
28
+ readUint16: function () {
29
+ const v = this.buffer.getUint16(this.position);
30
+ this.position = this.position + 2;
31
+ return v;
32
+ },
33
+ readUint32: function () {
34
+ const v = this.buffer.getUint32(this.position);
35
+ this.position = this.position + 4;
36
+ return v;
37
+ },
38
+ readVarInt: function () {
39
+ let v = 0;
40
+ let i = 0;
41
+ let b;
42
+ while (4 > i++) {
43
+ b = this.readUint8();
44
+ if (b & 0x80) {
45
+ v += (b & 0x7f);
46
+ v <<= 7;
47
+ }
48
+ else {
49
+ return v + b;
50
+ }
51
+ }
52
+ throw new Error('0x' + this.position.toString(16) + ':' +
53
+ ' Variable integer length cannot exceed 4 bytes');
54
+ },
55
+ readBytes: function (length) {
56
+ const bytes = [];
57
+ for (; 0 < length; length--) {
58
+ bytes.push(this.readUint8());
59
+ }
60
+ return bytes;
61
+ },
62
+ pos: function () {
63
+ return '0x' + (this.buffer.byteOffset + this.position).toString(16);
64
+ },
65
+ end: function () {
66
+ return this.position === this.buffer.byteLength;
67
+ },
68
+ };
69
+ startAt = 0;
70
+ }
71
+ else {
72
+ wrappedStream = stream;
73
+ }
74
+ // Consume stream till not at start index
75
+ if (startAt && 0 < startAt) {
76
+ while (startAt--) {
77
+ wrappedStream.readUint8();
78
+ }
79
+ }
80
+ // creating the parser object
81
+ return {
82
+ // Read the next event
83
+ next: function () {
84
+ // Check available datas
85
+ if (wrappedStream.end()) {
86
+ return null;
87
+ }
88
+ // Creating the event
89
+ event = {
90
+ // Memoize the event index
91
+ index: wrappedStream.pos(),
92
+ // Read the delta time
93
+ delta: wrappedStream.readVarInt(),
94
+ type: 0, // Will be set below
95
+ };
96
+ // Read the eventTypeByte
97
+ eventTypeByte = wrappedStream.readUint8();
98
+ if (0xF0 === (eventTypeByte & 0xF0)) {
99
+ // Meta events
100
+ if (eventTypeByte === MIDIEvents.EVENT_META) {
101
+ event.type = MIDIEvents.EVENT_META;
102
+ event.subtype = wrappedStream.readUint8();
103
+ event.length = wrappedStream.readVarInt();
104
+ switch (event.subtype) {
105
+ case MIDIEvents.EVENT_META_SEQUENCE_NUMBER:
106
+ if (strictMode && 2 !== event.length) {
107
+ throw new Error(wrappedStream.pos() + ' Bad metaevent length.');
108
+ }
109
+ event.msb = wrappedStream.readUint8();
110
+ event.lsb = wrappedStream.readUint8();
111
+ return event;
112
+ case MIDIEvents.EVENT_META_TEXT:
113
+ case MIDIEvents.EVENT_META_COPYRIGHT_NOTICE:
114
+ case MIDIEvents.EVENT_META_TRACK_NAME:
115
+ case MIDIEvents.EVENT_META_INSTRUMENT_NAME:
116
+ case MIDIEvents.EVENT_META_LYRICS:
117
+ case MIDIEvents.EVENT_META_MARKER:
118
+ case MIDIEvents.EVENT_META_CUE_POINT:
119
+ event.data = wrappedStream.readBytes(event.length);
120
+ return event;
121
+ case MIDIEvents.EVENT_META_MIDI_CHANNEL_PREFIX:
122
+ if (strictMode && 1 !== event.length) {
123
+ throw new Error(wrappedStream.pos() + ' Bad metaevent length.');
124
+ }
125
+ event.prefix = wrappedStream.readUint8();
126
+ return event;
127
+ case MIDIEvents.EVENT_META_END_OF_TRACK:
128
+ if (strictMode && 0 !== event.length) {
129
+ throw new Error(wrappedStream.pos() + ' Bad metaevent length.');
130
+ }
131
+ return event;
132
+ case MIDIEvents.EVENT_META_SET_TEMPO:
133
+ if (strictMode && 3 !== event.length) {
134
+ throw new Error(wrappedStream.pos() + ' Tempo meta event length must be 3.');
135
+ }
136
+ event.tempo = ((wrappedStream.readUint8() << 16) +
137
+ (wrappedStream.readUint8() << 8) +
138
+ wrappedStream.readUint8());
139
+ event.tempoBPM = 60000000 / event.tempo;
140
+ return event;
141
+ case MIDIEvents.EVENT_META_SMTPE_OFFSET:
142
+ if (strictMode && 5 !== event.length) {
143
+ throw new Error(wrappedStream.pos() + ' Bad metaevent length.');
144
+ }
145
+ event.hour = wrappedStream.readUint8();
146
+ if (strictMode && 23 < event.hour) {
147
+ throw new Error(wrappedStream.pos() + ' SMTPE offset hour value must' +
148
+ ' be part of 0-23.');
149
+ }
150
+ event.minutes = wrappedStream.readUint8();
151
+ if (strictMode && 59 < event.minutes) {
152
+ throw new Error(wrappedStream.pos() + ' SMTPE offset minutes value' +
153
+ ' must be part of 0-59.');
154
+ }
155
+ event.seconds = wrappedStream.readUint8();
156
+ if (strictMode && 59 < event.seconds) {
157
+ throw new Error(wrappedStream.pos() + ' SMTPE offset seconds value' +
158
+ ' must be part of 0-59.');
159
+ }
160
+ event.frames = wrappedStream.readUint8();
161
+ if (strictMode && 30 < event.frames) {
162
+ throw new Error(wrappedStream.pos() + ' SMTPE offset frames value must' +
163
+ ' be part of 0-30.');
164
+ }
165
+ event.subframes = wrappedStream.readUint8();
166
+ if (strictMode && 99 < event.subframes) {
167
+ throw new Error(wrappedStream.pos() + ' SMTPE offset subframes value must' +
168
+ ' be part of 0-99.');
169
+ }
170
+ return event;
171
+ case MIDIEvents.EVENT_META_KEY_SIGNATURE:
172
+ if (strictMode && 2 !== event.length) {
173
+ throw new Error(wrappedStream.pos() + ' Bad metaevent length.');
174
+ }
175
+ event.key = wrappedStream.readUint8();
176
+ if (strictMode && (-7 > event.key || 7 < event.key)) {
177
+ throw new Error(wrappedStream.pos() + ' Bad metaevent length.');
178
+ }
179
+ event.scale = wrappedStream.readUint8();
180
+ if (strictMode && 0 !== event.scale && 1 !== event.scale) {
181
+ throw new Error(wrappedStream.pos() + ' Key signature scale value must' +
182
+ ' be 0 or 1.');
183
+ }
184
+ return event;
185
+ case MIDIEvents.EVENT_META_TIME_SIGNATURE:
186
+ if (strictMode && 4 !== event.length) {
187
+ throw new Error(wrappedStream.pos() + ' Bad metaevent length.');
188
+ }
189
+ event.data = wrappedStream.readBytes(event.length);
190
+ event.param1 = event.data[0];
191
+ event.param2 = event.data[1];
192
+ event.param3 = event.data[2];
193
+ event.param4 = event.data[3];
194
+ return event;
195
+ case MIDIEvents.EVENT_META_SEQUENCER_SPECIFIC:
196
+ event.data = wrappedStream.readBytes(event.length);
197
+ return event;
198
+ default:
199
+ if (strictMode) {
200
+ throw new Error(wrappedStream.pos() + ' Unknown meta event type ' +
201
+ '(' + event.subtype.toString(16) + ').');
202
+ }
203
+ event.data = wrappedStream.readBytes(event.length);
204
+ return event;
205
+ }
206
+ // System events
207
+ }
208
+ else if (eventTypeByte === MIDIEvents.EVENT_SYSEX ||
209
+ eventTypeByte === MIDIEvents.EVENT_DIVSYSEX) {
210
+ event.type = eventTypeByte;
211
+ event.length = wrappedStream.readVarInt();
212
+ event.data = wrappedStream.readBytes(event.length);
213
+ return event;
214
+ // Unknown event, assuming it's system like event
215
+ }
216
+ else {
217
+ if (strictMode) {
218
+ throw new Error(wrappedStream.pos() + ' Unknown event type ' +
219
+ eventTypeByte.toString(16) + ', Delta: ' + event.delta + '.');
220
+ }
221
+ event.type = eventTypeByte;
222
+ event.badsubtype = wrappedStream.readVarInt();
223
+ event.length = wrappedStream.readUint8();
224
+ event.data = wrappedStream.readBytes(event.length);
225
+ return event;
226
+ }
227
+ // MIDI events
228
+ }
229
+ else {
230
+ // running status
231
+ if (0 === (eventTypeByte & 0x80)) {
232
+ if (!MIDIEventType) {
233
+ throw new Error(wrappedStream.pos() + ' Running status without previous event');
234
+ }
235
+ MIDIEventParam1 = eventTypeByte;
236
+ }
237
+ else {
238
+ MIDIEventType = eventTypeByte >> 4;
239
+ MIDIEventChannel = eventTypeByte & 0x0F;
240
+ MIDIEventParam1 = wrappedStream.readUint8();
241
+ }
242
+ event.type = MIDIEvents.EVENT_MIDI;
243
+ event.subtype = MIDIEventType;
244
+ event.channel = MIDIEventChannel;
245
+ event.param1 = MIDIEventParam1;
246
+ switch (MIDIEventType) {
247
+ case MIDIEvents.EVENT_MIDI_NOTE_OFF:
248
+ event.param2 = wrappedStream.readUint8();
249
+ return event;
250
+ case MIDIEvents.EVENT_MIDI_NOTE_ON:
251
+ event.param2 = wrappedStream.readUint8();
252
+ // If velocity is 0, it's a note off event in fact
253
+ if (!event.param2) {
254
+ event.subtype = MIDIEvents.EVENT_MIDI_NOTE_OFF;
255
+ event.param2 = 127; // Find a standard telling what to do here
256
+ }
257
+ return event;
258
+ case MIDIEvents.EVENT_MIDI_NOTE_AFTERTOUCH:
259
+ event.param2 = wrappedStream.readUint8();
260
+ return event;
261
+ case MIDIEvents.EVENT_MIDI_CONTROLLER:
262
+ event.param2 = wrappedStream.readUint8();
263
+ return event;
264
+ case MIDIEvents.EVENT_MIDI_PROGRAM_CHANGE:
265
+ return event;
266
+ case MIDIEvents.EVENT_MIDI_CHANNEL_AFTERTOUCH:
267
+ return event;
268
+ case MIDIEvents.EVENT_MIDI_PITCH_BEND:
269
+ event.param2 = wrappedStream.readUint8();
270
+ return event;
271
+ default:
272
+ if (strictMode) {
273
+ throw new Error(wrappedStream.pos() + ' Unknown MIDI event type ' +
274
+ '(' + MIDIEventType.toString(16) + ').');
275
+ }
276
+ return event;
277
+ }
278
+ }
279
+ },
280
+ };
281
+ }
282
+ }
283
+ // Static constants
284
+ // Event types
285
+ MIDIEvents.EVENT_META = 0xFF;
286
+ MIDIEvents.EVENT_SYSEX = 0xF0;
287
+ MIDIEvents.EVENT_DIVSYSEX = 0xF7;
288
+ MIDIEvents.EVENT_MIDI = 0x8;
289
+ // Meta event types
290
+ MIDIEvents.EVENT_META_SEQUENCE_NUMBER = 0x00;
291
+ MIDIEvents.EVENT_META_TEXT = 0x01;
292
+ MIDIEvents.EVENT_META_COPYRIGHT_NOTICE = 0x02;
293
+ MIDIEvents.EVENT_META_TRACK_NAME = 0x03;
294
+ MIDIEvents.EVENT_META_INSTRUMENT_NAME = 0x04;
295
+ MIDIEvents.EVENT_META_LYRICS = 0x05;
296
+ MIDIEvents.EVENT_META_MARKER = 0x06;
297
+ MIDIEvents.EVENT_META_CUE_POINT = 0x07;
298
+ MIDIEvents.EVENT_META_MIDI_CHANNEL_PREFIX = 0x20;
299
+ MIDIEvents.EVENT_META_END_OF_TRACK = 0x2F;
300
+ MIDIEvents.EVENT_META_SET_TEMPO = 0x51;
301
+ MIDIEvents.EVENT_META_SMTPE_OFFSET = 0x54;
302
+ MIDIEvents.EVENT_META_TIME_SIGNATURE = 0x58;
303
+ MIDIEvents.EVENT_META_KEY_SIGNATURE = 0x59;
304
+ MIDIEvents.EVENT_META_SEQUENCER_SPECIFIC = 0x7F;
305
+ // MIDI event types
306
+ MIDIEvents.EVENT_MIDI_NOTE_OFF = 0x8;
307
+ MIDIEvents.EVENT_MIDI_NOTE_ON = 0x9;
308
+ MIDIEvents.EVENT_MIDI_NOTE_AFTERTOUCH = 0xA;
309
+ MIDIEvents.EVENT_MIDI_CONTROLLER = 0xB;
310
+ MIDIEvents.EVENT_MIDI_PROGRAM_CHANGE = 0xC;
311
+ MIDIEvents.EVENT_MIDI_CHANNEL_AFTERTOUCH = 0xD;
312
+ MIDIEvents.EVENT_MIDI_PITCH_BEND = 0xE;
313
+ // MIDI event sizes
314
+ MIDIEvents.MIDI_1PARAM_EVENTS = [
315
+ MIDIEvents.EVENT_MIDI_PROGRAM_CHANGE,
316
+ MIDIEvents.EVENT_MIDI_CHANNEL_AFTERTOUCH,
317
+ ];
318
+ MIDIEvents.MIDI_2PARAMS_EVENTS = [
319
+ MIDIEvents.EVENT_MIDI_NOTE_OFF,
320
+ MIDIEvents.EVENT_MIDI_NOTE_ON,
321
+ MIDIEvents.EVENT_MIDI_NOTE_AFTERTOUCH,
322
+ MIDIEvents.EVENT_MIDI_CONTROLLER,
323
+ MIDIEvents.EVENT_MIDI_PITCH_BEND,
324
+ ];
325
+ module.exports = MIDIEvents;