@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.
- package/DEMO_ENHANCED.md +207 -134
- package/DOCUMENTATION_INDEX.md +317 -0
- package/EMK_REFERENCE_DATA.json +190 -0
- package/EMK_SONGS_INFO.md +336 -0
- package/EMK_TEST_SUITE_README.md +456 -0
- package/EMK_TEST_SUITE_SUMMARY.txt +197 -0
- package/README.md +90 -0
- package/RELEASE_v1.5.1.md +190 -0
- package/RELEASE_v1.5.2.md +238 -0
- package/SONG_LIST.txt +268 -0
- package/TEMPO_TRICKS_SUMMARY.md +240 -0
- package/demo-libs/KarFile.js +391 -0
- package/demo-libs/MIDIEvents.js +325 -0
- package/demo-libs/MIDIFile.js +450 -0
- package/demo-libs/MIDIFileHeader.js +144 -0
- package/demo-libs/MIDIFileTrack.js +111 -0
- package/demo-libs/TextEncoding.js +275 -0
- package/demo-libs/UTF8.js +151 -0
- package/demo-server.js +78 -1
- package/demo-simple.html +287 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -1
- package/dist/kar-validator.d.ts +66 -0
- package/dist/kar-validator.js +152 -0
- package/dist/ncntokar.browser.js +13 -1
- package/dist/ncntokar.js +13 -1
- package/package.json +4 -1
- package/verify-emk-reference.js +230 -0
- package/analyze-emk-cursor.js +0 -169
- package/analyze-emk-simple.js +0 -124
- package/check-real-duration.js +0 -69
- package/temp/test_output.kar +0 -0
- package/test-all-emk-durations.js +0 -109
- package/test-convert-001.js +0 -130
|
@@ -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;
|