@huffduff/midi-writer-ts 3.2.0 → 3.2.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.
Files changed (45) hide show
  1. package/README.md +27 -28
  2. package/build/index.cjs +24 -26
  3. package/build/index.mjs +1 -26
  4. package/build/types/main.d.ts +2 -35
  5. package/package.json +9 -2
  6. package/src/abstract-event.ts +8 -0
  7. package/src/chunks/chunk.ts +6 -0
  8. package/src/chunks/header.ts +29 -0
  9. package/src/chunks/track.ts +345 -0
  10. package/src/constants.ts +18 -0
  11. package/src/main.ts +48 -0
  12. package/src/meta-events/copyright-event.ts +35 -0
  13. package/src/meta-events/cue-point-event.ts +35 -0
  14. package/src/meta-events/end-track-event.ts +29 -0
  15. package/src/meta-events/instrument-name-event.ts +35 -0
  16. package/src/meta-events/key-signature-event.ts +73 -0
  17. package/src/meta-events/lyric-event.ts +35 -0
  18. package/src/meta-events/marker-event.ts +35 -0
  19. package/src/meta-events/meta-event.ts +7 -0
  20. package/src/meta-events/tempo-event.ts +37 -0
  21. package/src/meta-events/text-event.ts +35 -0
  22. package/src/meta-events/time-signature-event.ts +32 -0
  23. package/src/meta-events/track-name-event.ts +35 -0
  24. package/src/midi-events/controller-change-event.ts +30 -0
  25. package/src/midi-events/midi-event.ts +11 -0
  26. package/src/midi-events/note-event.ts +164 -0
  27. package/src/midi-events/note-off-event.ts +55 -0
  28. package/src/midi-events/note-on-event.ts +69 -0
  29. package/src/midi-events/pitch-bend-event.ts +40 -0
  30. package/src/midi-events/program-change-event.ts +28 -0
  31. package/src/utils.ts +263 -0
  32. package/src/vexflow.ts +96 -0
  33. package/src/writer.ts +99 -0
  34. package/.editorconfig +0 -24
  35. package/.eslintignore +0 -3
  36. package/.eslintrc.js +0 -18
  37. package/.nvmrc +0 -1
  38. package/.travis.yml +0 -3
  39. package/browser/midiwriter.js +0 -1367
  40. package/jsdoc.json +0 -5
  41. package/postinstall.js +0 -1
  42. package/rollup.config.js +0 -22
  43. package/runkit.js +0 -18
  44. package/tsconfig.json +0 -13
  45. package/typedoc.json +0 -5
@@ -1,1367 +0,0 @@
1
- var MidiWriter = (function () {
2
- 'use strict';
3
-
4
- /**
5
- * MIDI file format constants.
6
- * @return {Constants}
7
- */
8
- var Constants = {
9
- VERSION: '3.1.1',
10
- HEADER_CHUNK_TYPE: [0x4d, 0x54, 0x68, 0x64],
11
- HEADER_CHUNK_LENGTH: [0x00, 0x00, 0x00, 0x06],
12
- HEADER_CHUNK_FORMAT0: [0x00, 0x00],
13
- HEADER_CHUNK_FORMAT1: [0x00, 0x01],
14
- HEADER_CHUNK_DIVISION: [0x00, 0x80],
15
- TRACK_CHUNK_TYPE: [0x4d, 0x54, 0x72, 0x6b],
16
- META_EVENT_ID: 0xFF,
17
- META_SMTPE_OFFSET: 0x54
18
- };
19
-
20
- // src/utils.ts
21
- var fillStr = (s, n) => Array(Math.abs(n) + 1).join(s);
22
-
23
- // src/named.ts
24
- function isNamed(src) {
25
- return src !== null && typeof src === "object" && typeof src.name === "string" ? true : false;
26
- }
27
-
28
- // src/pitch.ts
29
- function isPitch(pitch) {
30
- return pitch !== null && typeof pitch === "object" && typeof pitch.step === "number" && typeof pitch.alt === "number" ? true : false;
31
- }
32
- var FIFTHS = [0, 2, 4, -1, 1, 3, 5];
33
- var STEPS_TO_OCTS = FIFTHS.map(
34
- (fifths) => Math.floor(fifths * 7 / 12)
35
- );
36
- function encode(pitch) {
37
- const { step, alt, oct, dir = 1 } = pitch;
38
- const f = FIFTHS[step] + 7 * alt;
39
- if (oct === void 0) {
40
- return [dir * f];
41
- }
42
- const o = oct - STEPS_TO_OCTS[step] - 4 * alt;
43
- return [dir * f, dir * o];
44
- }
45
-
46
- // src/note.ts
47
- var NoNote = { empty: true, name: "", pc: "", acc: "" };
48
- var cache = /* @__PURE__ */ new Map();
49
- var stepToLetter = (step) => "CDEFGAB".charAt(step);
50
- var altToAcc = (alt) => alt < 0 ? fillStr("b", -alt) : fillStr("#", alt);
51
- var accToAlt = (acc) => acc[0] === "b" ? -acc.length : acc.length;
52
- function note(src) {
53
- const stringSrc = JSON.stringify(src);
54
- const cached = cache.get(stringSrc);
55
- if (cached) {
56
- return cached;
57
- }
58
- const value = typeof src === "string" ? parse(src) : isPitch(src) ? note(pitchName(src)) : isNamed(src) ? note(src.name) : NoNote;
59
- cache.set(stringSrc, value);
60
- return value;
61
- }
62
- var REGEX = /^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/;
63
- function tokenizeNote(str) {
64
- const m = REGEX.exec(str);
65
- return [m[1].toUpperCase(), m[2].replace(/x/g, "##"), m[3], m[4]];
66
- }
67
- var mod = (n, m) => (n % m + m) % m;
68
- var SEMI = [0, 2, 4, 5, 7, 9, 11];
69
- function parse(noteName) {
70
- const tokens = tokenizeNote(noteName);
71
- if (tokens[0] === "" || tokens[3] !== "") {
72
- return NoNote;
73
- }
74
- const letter = tokens[0];
75
- const acc = tokens[1];
76
- const octStr = tokens[2];
77
- const step = (letter.charCodeAt(0) + 3) % 7;
78
- const alt = accToAlt(acc);
79
- const oct = octStr.length ? +octStr : void 0;
80
- const coord = encode({ step, alt, oct });
81
- const name = letter + acc + octStr;
82
- const pc = letter + acc;
83
- const chroma = (SEMI[step] + alt + 120) % 12;
84
- const height = oct === void 0 ? mod(SEMI[step] + alt, 12) - 12 * 99 : SEMI[step] + alt + 12 * (oct + 1);
85
- const midi = height >= 0 && height <= 127 ? height : null;
86
- const freq = oct === void 0 ? null : Math.pow(2, (height - 69) / 12) * 440;
87
- return {
88
- empty: false,
89
- acc,
90
- alt,
91
- chroma,
92
- coord,
93
- freq,
94
- height,
95
- letter,
96
- midi,
97
- name,
98
- oct,
99
- pc,
100
- step
101
- };
102
- }
103
- function pitchName(props) {
104
- const { step, alt, oct } = props;
105
- const letter = stepToLetter(step);
106
- if (!letter) {
107
- return "";
108
- }
109
- const pc = letter + altToAcc(alt);
110
- return oct || oct === 0 ? pc + oct : pc;
111
- }
112
-
113
- // index.ts
114
- function isMidi(arg) {
115
- return +arg >= 0 && +arg <= 127;
116
- }
117
- function toMidi(note$1) {
118
- if (isMidi(note$1)) {
119
- return +note$1;
120
- }
121
- const n = note(note$1);
122
- return n.empty ? null : n.midi;
123
- }
124
-
125
- /**
126
- * Static utility functions used throughout the library.
127
- */
128
- var Utils = /** @class */ (function () {
129
- function Utils() {
130
- }
131
- /**
132
- * Gets MidiWriterJS version number.
133
- * @return {string}
134
- */
135
- Utils.version = function () {
136
- return Constants.VERSION;
137
- };
138
- /**
139
- * Convert a string to an array of bytes
140
- * @param {string} string
141
- * @return {array}
142
- */
143
- Utils.stringToBytes = function (string) {
144
- return string.split('').map(function (char) { return char.charCodeAt(0); });
145
- };
146
- /**
147
- * Checks if argument is a valid number.
148
- * @param {*} n - Value to check
149
- * @return {boolean}
150
- */
151
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
152
- Utils.isNumeric = function (n) {
153
- return !isNaN(parseFloat(n)) && isFinite(n);
154
- };
155
- /**
156
- * Returns the correct MIDI number for the specified pitch.
157
- * Uses Tonal Midi - https://github.com/danigb/tonal/tree/master/packages/midi
158
- * @param {(string|number)} pitch - 'C#4' or midi note code
159
- * @param {string} middleC
160
- * @return {number}
161
- */
162
- Utils.getPitch = function (pitch, middleC) {
163
- if (middleC === void 0) { middleC = 'C4'; }
164
- return 60 - toMidi(middleC) + toMidi(pitch);
165
- };
166
- /**
167
- * Translates number of ticks to MIDI timestamp format, returning an array of
168
- * hex strings with the time values. Midi has a very particular time to express time,
169
- * take a good look at the spec before ever touching this function.
170
- * Thanks to https://github.com/sergi/jsmidi
171
- *
172
- * @param {number} ticks - Number of ticks to be translated
173
- * @return {array} - Bytes that form the MIDI time value
174
- */
175
- Utils.numberToVariableLength = function (ticks) {
176
- ticks = Math.round(ticks);
177
- var buffer = ticks & 0x7F;
178
- // eslint-disable-next-line no-cond-assign
179
- while (ticks = ticks >> 7) {
180
- buffer <<= 8;
181
- buffer |= ((ticks & 0x7F) | 0x80);
182
- }
183
- var bList = [];
184
- // eslint-disable-next-line no-constant-condition
185
- while (true) {
186
- bList.push(buffer & 0xff);
187
- if (buffer & 0x80)
188
- buffer >>= 8;
189
- else {
190
- break;
191
- }
192
- }
193
- return bList;
194
- };
195
- /**
196
- * Counts number of bytes in string
197
- * @param {string} s
198
- * @return {number}
199
- */
200
- Utils.stringByteCount = function (s) {
201
- return encodeURI(s).split(/%..|./).length - 1;
202
- };
203
- /**
204
- * Get an int from an array of bytes.
205
- * @param {array} bytes
206
- * @return {number}
207
- */
208
- Utils.numberFromBytes = function (bytes) {
209
- var hex = '';
210
- var stringResult;
211
- bytes.forEach(function (byte) {
212
- stringResult = byte.toString(16);
213
- // ensure string is 2 chars
214
- if (stringResult.length == 1)
215
- stringResult = "0" + stringResult;
216
- hex += stringResult;
217
- });
218
- return parseInt(hex, 16);
219
- };
220
- /**
221
- * Takes a number and splits it up into an array of bytes. Can be padded by passing a number to bytesNeeded
222
- * @param {number} number
223
- * @param {number} bytesNeeded
224
- * @return {array} - Array of bytes
225
- */
226
- Utils.numberToBytes = function (number, bytesNeeded) {
227
- bytesNeeded = bytesNeeded || 1;
228
- var hexString = number.toString(16);
229
- if (hexString.length & 1) { // Make sure hex string is even number of chars
230
- hexString = '0' + hexString;
231
- }
232
- // Split hex string into an array of two char elements
233
- var hexArray = hexString.match(/.{2}/g);
234
- // Now parse them out as integers
235
- var intArray = hexArray.map(function (item) { return parseInt(item, 16); });
236
- // Prepend empty bytes if we don't have enough
237
- if (intArray.length < bytesNeeded) {
238
- while (bytesNeeded - intArray.length > 0) {
239
- intArray.unshift(0);
240
- }
241
- }
242
- return intArray;
243
- };
244
- /**
245
- * Converts value to array if needed.
246
- * @param {any} value
247
- * @return {array}
248
- */
249
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
250
- Utils.toArray = function (value) {
251
- if (Array.isArray(value))
252
- return value;
253
- return [value];
254
- };
255
- /**
256
- * Converts velocity to value 0-127
257
- * @param {number} velocity - Velocity value 1-100
258
- * @return {number}
259
- */
260
- Utils.convertVelocity = function (velocity) {
261
- // Max passed value limited to 100
262
- velocity = velocity > 100 ? 100 : velocity;
263
- return Math.round(velocity / 100 * 127);
264
- };
265
- /**
266
- * Gets the total number of ticks of a specified duration.
267
- * Note: type=='note' defaults to quarter note, type==='rest' defaults to 0
268
- * @param {(string|array)} duration
269
- * @return {number}
270
- */
271
- Utils.getTickDuration = function (duration) {
272
- if (Array.isArray(duration)) {
273
- // Recursively execute this method for each item in the array and return the sum of tick durations.
274
- return duration.map(function (value) {
275
- return Utils.getTickDuration(value);
276
- }).reduce(function (a, b) {
277
- return a + b;
278
- }, 0);
279
- }
280
- duration = duration.toString();
281
- if (duration.toLowerCase().charAt(0) === 't') {
282
- // If duration starts with 't' then the number that follows is an explicit tick count
283
- var ticks = parseInt(duration.substring(1));
284
- if (isNaN(ticks) || ticks < 0) {
285
- throw new Error(duration + ' is not a valid duration.');
286
- }
287
- return ticks;
288
- }
289
- // Need to apply duration here. Quarter note == Constants.HEADER_CHUNK_DIVISION
290
- var quarterTicks = Utils.numberFromBytes(Constants.HEADER_CHUNK_DIVISION);
291
- var tickDuration = quarterTicks * Utils.getDurationMultiplier(duration);
292
- return Utils.getRoundedIfClose(tickDuration);
293
- };
294
- /**
295
- * Due to rounding errors in JavaScript engines,
296
- * it's safe to round when we're very close to the actual tick number
297
- *
298
- * @static
299
- * @param {number} tick
300
- * @return {number}
301
- */
302
- Utils.getRoundedIfClose = function (tick) {
303
- var roundedTick = Math.round(tick);
304
- return Math.abs(roundedTick - tick) < 0.000001 ? roundedTick : tick;
305
- };
306
- /**
307
- * Due to low precision of MIDI,
308
- * we need to keep track of rounding errors in deltas.
309
- * This function will calculate the rounding error for a given duration.
310
- *
311
- * @static
312
- * @param {number} tick
313
- * @return {number}
314
- */
315
- Utils.getPrecisionLoss = function (tick) {
316
- var roundedTick = Math.round(tick);
317
- return roundedTick - tick;
318
- };
319
- /**
320
- * Gets what to multiple ticks/quarter note by to get the specified duration.
321
- * Note: type=='note' defaults to quarter note, type==='rest' defaults to 0
322
- * @param {string} duration
323
- * @return {number}
324
- */
325
- Utils.getDurationMultiplier = function (duration) {
326
- // Need to apply duration here.
327
- // Quarter note == Constants.HEADER_CHUNK_DIVISION ticks.
328
- if (duration === '0')
329
- return 0;
330
- var match = duration.match(/^(?<dotted>d+)?(?<base>\d+)(?:t(?<tuplet>\d*))?/);
331
- if (match) {
332
- var base = Number(match.groups.base);
333
- // 1 or any power of two:
334
- var isValidBase = base === 1 || ((base & (base - 1)) === 0);
335
- if (isValidBase) {
336
- // how much faster or slower is this note compared to a quarter?
337
- var ratio = base / 4;
338
- var durationInQuarters = 1 / ratio;
339
- var _a = match.groups, dotted = _a.dotted, tuplet = _a.tuplet;
340
- if (dotted) {
341
- var thisManyDots = dotted.length;
342
- var divisor = Math.pow(2, thisManyDots);
343
- durationInQuarters = durationInQuarters + (durationInQuarters * ((divisor - 1) / divisor));
344
- }
345
- if (typeof tuplet === 'string') {
346
- var fitInto = durationInQuarters * 2;
347
- // default to triplet:
348
- var thisManyNotes = Number(tuplet || '3');
349
- durationInQuarters = fitInto / thisManyNotes;
350
- }
351
- return durationInQuarters;
352
- }
353
- }
354
- throw new Error(duration + ' is not a valid duration.');
355
- };
356
- return Utils;
357
- }());
358
-
359
- /**
360
- * Holds all data for a "controller change" MIDI event
361
- * @param {object} fields {controllerNumber: integer, controllerValue: integer, delta: integer}
362
- * @return {ControllerChangeEvent}
363
- */
364
- var ControllerChangeEvent = /** @class */ (function () {
365
- function ControllerChangeEvent(fields) {
366
- this.channel = fields.channel - 1 || 0;
367
- this.controllerValue = fields.controllerValue;
368
- this.controllerNumber = fields.controllerNumber;
369
- this.delta = fields.delta || 0x00;
370
- this.name = 'ControllerChangeEvent';
371
- this.status = 0xB0;
372
- this.data = Utils.numberToVariableLength(fields.delta).concat(this.status | this.channel, this.controllerNumber, this.controllerValue);
373
- }
374
- return ControllerChangeEvent;
375
- }());
376
-
377
- /**
378
- * Object representation of a tempo meta event.
379
- * @param {object} fields {text: string, delta: integer}
380
- * @return {CopyrightEvent}
381
- */
382
- var CopyrightEvent = /** @class */ (function () {
383
- function CopyrightEvent(fields) {
384
- this.delta = fields.delta || 0x00;
385
- this.name = 'CopyrightEvent';
386
- this.text = fields.text;
387
- this.type = 0x02;
388
- var textBytes = Utils.stringToBytes(this.text);
389
- // Start with zero time delta
390
- this.data = Utils.numberToVariableLength(this.delta).concat(Constants.META_EVENT_ID, this.type, Utils.numberToVariableLength(textBytes.length), // Size
391
- textBytes);
392
- }
393
- return CopyrightEvent;
394
- }());
395
-
396
- /**
397
- * Object representation of a cue point meta event.
398
- * @param {object} fields {text: string, delta: integer}
399
- * @return {CuePointEvent}
400
- */
401
- var CuePointEvent = /** @class */ (function () {
402
- function CuePointEvent(fields) {
403
- this.delta = fields.delta || 0x00;
404
- this.name = 'CuePointEvent';
405
- this.text = fields.text;
406
- this.type = 0x07;
407
- var textBytes = Utils.stringToBytes(this.text);
408
- // Start with zero time delta
409
- this.data = Utils.numberToVariableLength(this.delta).concat(Constants.META_EVENT_ID, this.type, Utils.numberToVariableLength(textBytes.length), // Size
410
- textBytes);
411
- }
412
- return CuePointEvent;
413
- }());
414
-
415
- /**
416
- * Object representation of a end track meta event.
417
- * @param {object} fields {delta: integer}
418
- * @return {EndTrackEvent}
419
- */
420
- var EndTrackEvent = /** @class */ (function () {
421
- function EndTrackEvent(fields) {
422
- this.delta = (fields === null || fields === void 0 ? void 0 : fields.delta) || 0x00;
423
- this.name = 'EndTrackEvent';
424
- this.type = [0x2F, 0x00];
425
- // Start with zero time delta
426
- this.data = Utils.numberToVariableLength(this.delta).concat(Constants.META_EVENT_ID, this.type);
427
- }
428
- return EndTrackEvent;
429
- }());
430
-
431
- /**
432
- * Object representation of an instrument name meta event.
433
- * @param {object} fields {text: string, delta: integer}
434
- * @return {InstrumentNameEvent}
435
- */
436
- var InstrumentNameEvent = /** @class */ (function () {
437
- function InstrumentNameEvent(fields) {
438
- this.delta = fields.delta || 0x00;
439
- this.name = 'InstrumentNameEvent';
440
- this.text = fields.text;
441
- this.type = 0x04;
442
- var textBytes = Utils.stringToBytes(this.text);
443
- // Start with zero time delta
444
- this.data = Utils.numberToVariableLength(this.delta).concat(Constants.META_EVENT_ID, this.type, Utils.numberToVariableLength(textBytes.length), // Size
445
- textBytes);
446
- }
447
- return InstrumentNameEvent;
448
- }());
449
-
450
- /**
451
- * Object representation of a key signature meta event.
452
- * @return {KeySignatureEvent}
453
- */
454
- var KeySignatureEvent = /** @class */ (function () {
455
- function KeySignatureEvent(sf, mi) {
456
- this.name = 'KeySignatureEvent';
457
- this.type = 0x59;
458
- var mode = mi || 0;
459
- sf = sf || 0;
460
- // Function called with string notation
461
- if (typeof mi === 'undefined') {
462
- var fifths = [
463
- ['Cb', 'Gb', 'Db', 'Ab', 'Eb', 'Bb', 'F', 'C', 'G', 'D', 'A', 'E', 'B', 'F#', 'C#'],
464
- ['ab', 'eb', 'bb', 'f', 'c', 'g', 'd', 'a', 'e', 'b', 'f#', 'c#', 'g#', 'd#', 'a#']
465
- ];
466
- var _sflen = sf.length;
467
- var note = sf || 'C';
468
- if (sf[0] === sf[0].toLowerCase())
469
- mode = 1;
470
- if (_sflen > 1) {
471
- switch (sf.charAt(_sflen - 1)) {
472
- case 'm':
473
- mode = 1;
474
- note = sf.charAt(0).toLowerCase();
475
- note = note.concat(sf.substring(1, _sflen - 1));
476
- break;
477
- case '-':
478
- mode = 1;
479
- note = sf.charAt(0).toLowerCase();
480
- note = note.concat(sf.substring(1, _sflen - 1));
481
- break;
482
- case 'M':
483
- mode = 0;
484
- note = sf.charAt(0).toUpperCase();
485
- note = note.concat(sf.substring(1, _sflen - 1));
486
- break;
487
- case '+':
488
- mode = 0;
489
- note = sf.charAt(0).toUpperCase();
490
- note = note.concat(sf.substring(1, _sflen - 1));
491
- break;
492
- }
493
- }
494
- var fifthindex = fifths[mode].indexOf(note);
495
- sf = fifthindex === -1 ? 0 : fifthindex - 7;
496
- }
497
- // Start with zero time delta
498
- this.data = Utils.numberToVariableLength(0x00).concat(Constants.META_EVENT_ID, this.type, [0x02], // Size
499
- Utils.numberToBytes(sf, 1), // Number of sharp or flats ( < 0 flat; > 0 sharp)
500
- Utils.numberToBytes(mode, 1));
501
- }
502
- return KeySignatureEvent;
503
- }());
504
-
505
- /**
506
- * Object representation of a lyric meta event.
507
- * @param {object} fields {text: string, delta: integer}
508
- * @return {LyricEvent}
509
- */
510
- var LyricEvent = /** @class */ (function () {
511
- function LyricEvent(fields) {
512
- this.delta = fields.delta || 0x00;
513
- this.name = 'LyricEvent';
514
- this.text = fields.text;
515
- this.type = 0x05;
516
- var textBytes = Utils.stringToBytes(this.text);
517
- // Start with zero time delta
518
- this.data = Utils.numberToVariableLength(this.delta).concat(Constants.META_EVENT_ID, this.type, Utils.numberToVariableLength(textBytes.length), // Size
519
- textBytes);
520
- }
521
- return LyricEvent;
522
- }());
523
-
524
- /**
525
- * Object representation of a marker meta event.
526
- * @param {object} fields {text: string, delta: integer}
527
- * @return {MarkerEvent}
528
- */
529
- var MarkerEvent = /** @class */ (function () {
530
- function MarkerEvent(fields) {
531
- this.delta = fields.delta || 0x00;
532
- this.name = 'MarkerEvent';
533
- this.text = fields.text;
534
- this.type = 0x06;
535
- var textBytes = Utils.stringToBytes(this.text);
536
- // Start with zero time delta
537
- this.data = Utils.numberToVariableLength(this.delta).concat(Constants.META_EVENT_ID, this.type, Utils.numberToVariableLength(textBytes.length), // Size
538
- textBytes);
539
- }
540
- return MarkerEvent;
541
- }());
542
-
543
- /**
544
- * Holds all data for a "note on" MIDI event
545
- * @param {object} fields {data: []}
546
- * @return {NoteOnEvent}
547
- */
548
- var NoteOnEvent = /** @class */ (function () {
549
- function NoteOnEvent(fields) {
550
- this.name = 'NoteOnEvent';
551
- this.channel = fields.channel || 1;
552
- this.pitch = fields.pitch;
553
- this.wait = fields.wait || 0;
554
- this.velocity = fields.velocity || 50;
555
- this.tick = fields.tick || null;
556
- this.delta = null;
557
- this.data = fields.data;
558
- this.status = 0x90;
559
- }
560
- /**
561
- * Builds int array for this event.
562
- * @param {Track} track - parent track
563
- * @return {NoteOnEvent}
564
- */
565
- NoteOnEvent.prototype.buildData = function (track, precisionDelta, options) {
566
- if (options === void 0) { options = {}; }
567
- this.data = [];
568
- // Explicitly defined startTick event
569
- if (this.tick) {
570
- this.tick = Utils.getRoundedIfClose(this.tick);
571
- // If this is the first event in the track then use event's starting tick as delta.
572
- if (track.tickPointer == 0) {
573
- this.delta = this.tick;
574
- }
575
- }
576
- else {
577
- this.delta = Utils.getTickDuration(this.wait);
578
- this.tick = Utils.getRoundedIfClose(track.tickPointer + this.delta);
579
- }
580
- this.deltaWithPrecisionCorrection = Utils.getRoundedIfClose(this.delta - precisionDelta);
581
- this.data = Utils.numberToVariableLength(this.deltaWithPrecisionCorrection)
582
- .concat(this.status | this.channel - 1, Utils.getPitch(this.pitch, options.middleC), Utils.convertVelocity(this.velocity));
583
- return this;
584
- };
585
- return NoteOnEvent;
586
- }());
587
-
588
- /**
589
- * Holds all data for a "note off" MIDI event
590
- * @param {object} fields {data: []}
591
- * @return {NoteOffEvent}
592
- */
593
- var NoteOffEvent = /** @class */ (function () {
594
- function NoteOffEvent(fields) {
595
- this.name = 'NoteOffEvent';
596
- this.channel = fields.channel || 1;
597
- this.pitch = fields.pitch;
598
- this.velocity = fields.velocity || 50;
599
- this.tick = fields.tick || null;
600
- this.data = fields.data;
601
- this.delta = fields.delta || Utils.getTickDuration(fields.duration);
602
- this.status = 0x80;
603
- }
604
- /**
605
- * Builds int array for this event.
606
- * @param {Track} track - parent track
607
- * @return {NoteOffEvent}
608
- */
609
- NoteOffEvent.prototype.buildData = function (track, precisionDelta, options) {
610
- if (options === void 0) { options = {}; }
611
- if (this.tick === null) {
612
- this.tick = Utils.getRoundedIfClose(this.delta + track.tickPointer);
613
- }
614
- this.deltaWithPrecisionCorrection = Utils.getRoundedIfClose(this.delta - precisionDelta);
615
- this.data = Utils.numberToVariableLength(this.deltaWithPrecisionCorrection)
616
- .concat(this.status | this.channel - 1, Utils.getPitch(this.pitch, options.middleC), Utils.convertVelocity(this.velocity));
617
- return this;
618
- };
619
- return NoteOffEvent;
620
- }());
621
-
622
- /**
623
- * Wrapper for noteOnEvent/noteOffEvent objects that builds both events.
624
- * @param {object} fields - {pitch: '[C4]', duration: '4', wait: '4', velocity: 1-100}
625
- * @return {NoteEvent}
626
- */
627
- var NoteEvent = /** @class */ (function () {
628
- function NoteEvent(fields) {
629
- this.data = [];
630
- this.name = 'NoteEvent';
631
- this.pitch = Utils.toArray(fields.pitch);
632
- this.channel = fields.channel || 1;
633
- this.duration = fields.duration || '4';
634
- this.grace = fields.grace;
635
- this.repeat = fields.repeat || 1;
636
- this.sequential = fields.sequential || false;
637
- this.tick = fields.startTick || fields.tick || null;
638
- this.velocity = fields.velocity || 50;
639
- this.wait = fields.wait || 0;
640
- this.tickDuration = Utils.getTickDuration(this.duration);
641
- this.restDuration = Utils.getTickDuration(this.wait);
642
- this.events = []; // Hold actual NoteOn/NoteOff events
643
- }
644
- /**
645
- * Builds int array for this event.
646
- * @return {NoteEvent}
647
- */
648
- NoteEvent.prototype.buildData = function () {
649
- var _this = this;
650
- // Reset data array
651
- this.data = [];
652
- // Apply grace note(s) and subtract ticks (currently 1 tick per grace note) from tickDuration so net value is the same
653
- if (this.grace) {
654
- var graceDuration_1 = 1;
655
- this.grace = Utils.toArray(this.grace);
656
- this.grace.forEach(function () {
657
- var noteEvent = new NoteEvent({ pitch: _this.grace, duration: 'T' + graceDuration_1 });
658
- _this.data = _this.data.concat(noteEvent.data);
659
- });
660
- }
661
- // fields.pitch could be an array of pitches.
662
- // If so create note events for each and apply the same duration.
663
- // By default this is a chord if it's an array of notes that requires one NoteOnEvent.
664
- // If this.sequential === true then it's a sequential string of notes that requires separate NoteOnEvents.
665
- if (!this.sequential) {
666
- // Handle repeat
667
- for (var j = 0; j < this.repeat; j++) {
668
- // Note on
669
- this.pitch.forEach(function (p, i) {
670
- var noteOnNew;
671
- if (i == 0) {
672
- noteOnNew = new NoteOnEvent({
673
- channel: _this.channel,
674
- wait: _this.wait,
675
- delta: Utils.getTickDuration(_this.wait),
676
- velocity: _this.velocity,
677
- pitch: p,
678
- tick: _this.tick,
679
- });
680
- }
681
- else {
682
- // Running status (can ommit the note on status)
683
- //noteOn = new NoteOnEvent({data: [0, Utils.getPitch(p), Utils.convertVelocity(this.velocity)]});
684
- noteOnNew = new NoteOnEvent({
685
- channel: _this.channel,
686
- wait: 0,
687
- delta: 0,
688
- velocity: _this.velocity,
689
- pitch: p,
690
- tick: _this.tick,
691
- });
692
- }
693
- _this.events.push(noteOnNew);
694
- });
695
- // Note off
696
- this.pitch.forEach(function (p, i) {
697
- var noteOffNew;
698
- if (i == 0) {
699
- //noteOff = new NoteOffEvent({data: Utils.numberToVariableLength(tickDuration).concat(this.getNoteOffStatus(), Utils.getPitch(p), Utils.convertVelocity(this.velocity))});
700
- noteOffNew = new NoteOffEvent({
701
- channel: _this.channel,
702
- duration: _this.duration,
703
- velocity: _this.velocity,
704
- pitch: p,
705
- tick: _this.tick !== null ? Utils.getTickDuration(_this.duration) + _this.tick : null,
706
- });
707
- }
708
- else {
709
- // Running status (can omit the note off status)
710
- //noteOff = new NoteOffEvent({data: [0, Utils.getPitch(p), Utils.convertVelocity(this.velocity)]});
711
- noteOffNew = new NoteOffEvent({
712
- channel: _this.channel,
713
- duration: 0,
714
- velocity: _this.velocity,
715
- pitch: p,
716
- tick: _this.tick !== null ? Utils.getTickDuration(_this.duration) + _this.tick : null,
717
- });
718
- }
719
- _this.events.push(noteOffNew);
720
- });
721
- }
722
- }
723
- else {
724
- // Handle repeat
725
- for (var j = 0; j < this.repeat; j++) {
726
- this.pitch.forEach(function (p, i) {
727
- var noteOnNew = new NoteOnEvent({
728
- channel: _this.channel,
729
- wait: (i > 0 ? 0 : _this.wait),
730
- delta: (i > 0 ? 0 : Utils.getTickDuration(_this.wait)),
731
- velocity: _this.velocity,
732
- pitch: p,
733
- tick: _this.tick,
734
- });
735
- var noteOffNew = new NoteOffEvent({
736
- channel: _this.channel,
737
- duration: _this.duration,
738
- velocity: _this.velocity,
739
- pitch: p,
740
- });
741
- _this.events.push(noteOnNew, noteOffNew);
742
- });
743
- }
744
- }
745
- return this;
746
- };
747
- return NoteEvent;
748
- }());
749
-
750
- /**
751
- * Holds all data for a "Pitch Bend" MIDI event
752
- * [ -1.0, 0, 1.0 ] -> [ 0, 8192, 16383]
753
- * @param {object} fields { bend : float, channel : int, delta: int }
754
- * @return {PitchBendEvent}
755
- */
756
- var PitchBendEvent = /** @class */ (function () {
757
- function PitchBendEvent(fields) {
758
- this.channel = fields.channel || 0;
759
- this.delta = fields.delta || 0x00;
760
- this.name = 'PitchBendEvent';
761
- this.status = 0xE0;
762
- var bend14 = this.scale14bits(fields.bend);
763
- var lsbValue = bend14 & 0x7f;
764
- var msbValue = (bend14 >> 7) & 0x7f;
765
- this.data = Utils.numberToVariableLength(this.delta).concat(this.status | this.channel, lsbValue, msbValue);
766
- }
767
- PitchBendEvent.prototype.scale14bits = function (zeroOne) {
768
- if (zeroOne <= 0) {
769
- return Math.floor(16384 * (zeroOne + 1) / 2);
770
- }
771
- return Math.floor(16383 * (zeroOne + 1) / 2);
772
- };
773
- return PitchBendEvent;
774
- }());
775
-
776
- /**
777
- * Holds all data for a "program change" MIDI event
778
- * @param {object} fields {instrument: integer, delta: integer}
779
- * @return {ProgramChangeEvent}
780
- */
781
- var ProgramChangeEvent = /** @class */ (function () {
782
- function ProgramChangeEvent(fields) {
783
- this.channel = fields.channel || 0;
784
- this.delta = fields.delta || 0x00;
785
- this.instrument = fields.instrument;
786
- this.status = 0xC0;
787
- this.name = 'ProgramChangeEvent';
788
- // delta time defaults to 0.
789
- this.data = Utils.numberToVariableLength(this.delta).concat(this.status | this.channel, this.instrument);
790
- }
791
- return ProgramChangeEvent;
792
- }());
793
-
794
- /**
795
- * Object representation of a tempo meta event.
796
- * @param {object} fields {bpm: integer, delta: integer}
797
- * @return {TempoEvent}
798
- */
799
- var TempoEvent = /** @class */ (function () {
800
- function TempoEvent(fields) {
801
- this.bpm = fields.bpm;
802
- this.delta = fields.delta || 0x00;
803
- this.tick = fields.tick;
804
- this.name = 'TempoEvent';
805
- this.type = 0x51;
806
- var tempo = Math.round(60000000 / this.bpm);
807
- // Start with zero time delta
808
- this.data = Utils.numberToVariableLength(this.delta).concat(Constants.META_EVENT_ID, this.type, [0x03], // Size
809
- Utils.numberToBytes(tempo, 3));
810
- }
811
- return TempoEvent;
812
- }());
813
-
814
- /**
815
- * Object representation of a tempo meta event.
816
- * @param {object} fields {text: string, delta: integer}
817
- * @return {TextEvent}
818
- */
819
- var TextEvent = /** @class */ (function () {
820
- function TextEvent(fields) {
821
- this.delta = fields.delta || 0x00;
822
- this.text = fields.text;
823
- this.name = 'TextEvent';
824
- this.type = 0x01;
825
- var textBytes = Utils.stringToBytes(this.text);
826
- // Start with zero time delta
827
- this.data = Utils.numberToVariableLength(fields.delta).concat(Constants.META_EVENT_ID, this.type, Utils.numberToVariableLength(textBytes.length), // Size
828
- textBytes);
829
- }
830
- return TextEvent;
831
- }());
832
-
833
- /**
834
- * Object representation of a time signature meta event.
835
- * @return {TimeSignatureEvent}
836
- */
837
- var TimeSignatureEvent = /** @class */ (function () {
838
- function TimeSignatureEvent(numerator, denominator, midiclockspertick, notespermidiclock) {
839
- this.name = 'TimeSignatureEvent';
840
- this.type = 0x58;
841
- // Start with zero time delta
842
- this.data = Utils.numberToVariableLength(0x00).concat(Constants.META_EVENT_ID, this.type, [0x04], // Size
843
- Utils.numberToBytes(numerator, 1), // Numerator, 1 bytes
844
- Utils.numberToBytes(Math.log2(denominator), 1), // Denominator is expressed as pow of 2, 1 bytes
845
- Utils.numberToBytes(midiclockspertick || 24, 1), // MIDI Clocks per tick, 1 bytes
846
- Utils.numberToBytes(notespermidiclock || 8, 1));
847
- }
848
- return TimeSignatureEvent;
849
- }());
850
-
851
- /**
852
- * Object representation of a tempo meta event.
853
- * @param {object} fields {text: string, delta: integer}
854
- * @return {TrackNameEvent}
855
- */
856
- var TrackNameEvent = /** @class */ (function () {
857
- function TrackNameEvent(fields) {
858
- this.delta = fields.delta || 0x00;
859
- this.name = 'TrackNameEvent';
860
- this.text = fields.text;
861
- this.type = 0x03;
862
- var textBytes = Utils.stringToBytes(this.text);
863
- // Start with zero time delta
864
- this.data = Utils.numberToVariableLength(this.delta).concat(Constants.META_EVENT_ID, this.type, Utils.numberToVariableLength(textBytes.length), // Size
865
- textBytes);
866
- }
867
- return TrackNameEvent;
868
- }());
869
-
870
- /**
871
- * Holds all data for a track.
872
- * @param {object} fields {type: number, data: array, size: array, events: array}
873
- * @return {Track}
874
- */
875
- var Track = /** @class */ (function () {
876
- function Track() {
877
- this.type = Constants.TRACK_CHUNK_TYPE;
878
- this.data = [];
879
- this.size = [];
880
- this.events = [];
881
- this.explicitTickEvents = [];
882
- // If there are any events with an explicit tick defined then we will create a "sub" track for those
883
- // and merge them in and the end.
884
- this.tickPointer = 0; // Each time an event is added this will increase
885
- }
886
- /**
887
- * Adds any event type to the track.
888
- * Events without a specific startTick property are assumed to be added in order of how they should output.
889
- * Events with a specific startTick property are set aside for now will be merged in during build process.
890
- *
891
- * TODO: Don't put startTick events in their own array. Just lump everything together and sort it out during buildData();
892
- * @param {(NoteEvent|ProgramChangeEvent)} events - Event object or array of Event objects.
893
- * @param {Function} mapFunction - Callback which can be used to apply specific properties to all events.
894
- * @return {Track}
895
- */
896
- Track.prototype.addEvent = function (events, mapFunction) {
897
- var _this = this;
898
- Utils.toArray(events).forEach(function (event, i) {
899
- if (event instanceof NoteEvent) {
900
- // Handle map function if provided
901
- if (typeof mapFunction === 'function') {
902
- var properties = mapFunction(i, event);
903
- if (typeof properties === 'object') {
904
- Object.assign(event, properties);
905
- }
906
- }
907
- // If this note event has an explicit startTick then we need to set aside for now
908
- if (event.tick !== null) {
909
- _this.explicitTickEvents.push(event);
910
- }
911
- else {
912
- // Push each on/off event to track's event stack
913
- event.buildData().events.forEach(function (e) { return _this.events.push(e); });
914
- }
915
- }
916
- else {
917
- _this.events.push(event);
918
- }
919
- });
920
- return this;
921
- };
922
- /**
923
- * Builds int array of all events.
924
- * @param {object} options
925
- * @return {Track}
926
- */
927
- Track.prototype.buildData = function (options) {
928
- var _this = this;
929
- if (options === void 0) { options = {}; }
930
- // Reset
931
- this.data = [];
932
- this.size = [];
933
- this.tickPointer = 0;
934
- var precisionLoss = 0;
935
- this.events.forEach(function (event) {
936
- // Build event & add to total tick duration
937
- if (event instanceof NoteOnEvent || event instanceof NoteOffEvent) {
938
- var built = event.buildData(_this, precisionLoss, options);
939
- precisionLoss = Utils.getPrecisionLoss(event.deltaWithPrecisionCorrection || 0);
940
- _this.data = _this.data.concat(built.data);
941
- _this.tickPointer = Utils.getRoundedIfClose(event.tick);
942
- }
943
- else if (event instanceof TempoEvent) {
944
- _this.tickPointer = Utils.getRoundedIfClose(event.tick);
945
- _this.data = _this.data.concat(event.data);
946
- }
947
- else {
948
- _this.data = _this.data.concat(event.data);
949
- }
950
- });
951
- this.mergeExplicitTickEvents();
952
- // If the last event isn't EndTrackEvent, then tack it onto the data.
953
- if (!this.events.length || !(this.events[this.events.length - 1] instanceof EndTrackEvent)) {
954
- this.data = this.data.concat((new EndTrackEvent).data);
955
- }
956
- this.size = Utils.numberToBytes(this.data.length, 4); // 4 bytes long
957
- return this;
958
- };
959
- Track.prototype.mergeExplicitTickEvents = function () {
960
- var _this = this;
961
- if (!this.explicitTickEvents.length)
962
- return;
963
- // First sort asc list of events by startTick
964
- this.explicitTickEvents.sort(function (a, b) { return a.tick - b.tick; });
965
- // Now this.explicitTickEvents is in correct order, and so is this.events naturally.
966
- // For each explicit tick event, splice it into the main list of events and
967
- // adjust the delta on the following events so they still play normally.
968
- this.explicitTickEvents.forEach(function (noteEvent) {
969
- // Convert NoteEvent to it's respective NoteOn/NoteOff events
970
- // Note that as we splice in events the delta for the NoteOff ones will
971
- // Need to change based on what comes before them after the splice.
972
- noteEvent.buildData().events.forEach(function (e) { return e.buildData(_this); });
973
- // Merge each event individually into this track's event list.
974
- noteEvent.events.forEach(function (event) { return _this.mergeSingleEvent(event); });
975
- });
976
- // Hacky way to rebuild track with newly spliced events. Need better solution.
977
- this.explicitTickEvents = [];
978
- this.buildData();
979
- };
980
- /**
981
- * Merges another track's events with this track.
982
- * @param {Track} track
983
- * @return {Track}
984
- */
985
- Track.prototype.mergeTrack = function (track) {
986
- var _this = this;
987
- // First build this track to populate each event's tick property
988
- this.buildData();
989
- // Then build track to be merged so that tick property is populated on all events & merge each event.
990
- track.buildData().events.forEach(function (event) { return _this.mergeSingleEvent(event); });
991
- return this;
992
- };
993
- /**
994
- * Merges a single event into this track's list of events based on event.tick property.
995
- * @param {AbstractEvent} - event
996
- * @return {Track}
997
- */
998
- Track.prototype.mergeSingleEvent = function (event) {
999
- // There are no events yet, so just add it in.
1000
- if (!this.events.length) {
1001
- this.addEvent(event);
1002
- return;
1003
- }
1004
- // Find index of existing event we need to follow with
1005
- var lastEventIndex;
1006
- for (var i = 0; i < this.events.length; i++) {
1007
- if (this.events[i].tick > event.tick)
1008
- break;
1009
- lastEventIndex = i;
1010
- }
1011
- var splicedEventIndex = lastEventIndex + 1;
1012
- // Need to adjust the delta of this event to ensure it falls on the correct tick.
1013
- event.delta = event.tick - this.events[lastEventIndex].tick;
1014
- // Splice this event at lastEventIndex + 1
1015
- this.events.splice(splicedEventIndex, 0, event);
1016
- // Now adjust delta of all following events
1017
- for (var i = splicedEventIndex + 1; i < this.events.length; i++) {
1018
- // Since each existing event should have a tick value at this point we just need to
1019
- // adjust delta to that the event still falls on the correct tick.
1020
- this.events[i].delta = this.events[i].tick - this.events[i - 1].tick;
1021
- }
1022
- };
1023
- /**
1024
- * Removes all events matching specified type.
1025
- * @param {string} eventName - Event type
1026
- * @return {Track}
1027
- */
1028
- Track.prototype.removeEventsByName = function (eventName) {
1029
- var _this = this;
1030
- this.events.forEach(function (event, index) {
1031
- if (event.name === eventName) {
1032
- _this.events.splice(index, 1);
1033
- }
1034
- });
1035
- return this;
1036
- };
1037
- /**
1038
- * Sets tempo of the MIDI file.
1039
- * @param {number} bpm - Tempo in beats per minute.
1040
- * @param {number} tick - Start tick.
1041
- * @return {Track}
1042
- */
1043
- Track.prototype.setTempo = function (bpm, tick) {
1044
- if (tick === void 0) { tick = 0; }
1045
- return this.addEvent(new TempoEvent({ bpm: bpm, tick: tick }));
1046
- };
1047
- /**
1048
- * Sets time signature.
1049
- * @param {number} numerator - Top number of the time signature.
1050
- * @param {number} denominator - Bottom number of the time signature.
1051
- * @param {number} midiclockspertick - Defaults to 24.
1052
- * @param {number} notespermidiclock - Defaults to 8.
1053
- * @return {Track}
1054
- */
1055
- Track.prototype.setTimeSignature = function (numerator, denominator, midiclockspertick, notespermidiclock) {
1056
- return this.addEvent(new TimeSignatureEvent(numerator, denominator, midiclockspertick, notespermidiclock));
1057
- };
1058
- /**
1059
- * Sets key signature.
1060
- * @param {*} sf -
1061
- * @param {*} mi -
1062
- * @return {Track}
1063
- */
1064
- Track.prototype.setKeySignature = function (sf, mi) {
1065
- return this.addEvent(new KeySignatureEvent(sf, mi));
1066
- };
1067
- /**
1068
- * Adds text to MIDI file.
1069
- * @param {string} text - Text to add.
1070
- * @return {Track}
1071
- */
1072
- Track.prototype.addText = function (text) {
1073
- return this.addEvent(new TextEvent({ text: text }));
1074
- };
1075
- /**
1076
- * Adds copyright to MIDI file.
1077
- * @param {string} text - Text of copyright line.
1078
- * @return {Track}
1079
- */
1080
- Track.prototype.addCopyright = function (text) {
1081
- return this.addEvent(new CopyrightEvent({ text: text }));
1082
- };
1083
- /**
1084
- * Adds Sequence/Track Name.
1085
- * @param {string} text - Text of track name.
1086
- * @return {Track}
1087
- */
1088
- Track.prototype.addTrackName = function (text) {
1089
- return this.addEvent(new TrackNameEvent({ text: text }));
1090
- };
1091
- /**
1092
- * Sets instrument name of track.
1093
- * @param {string} text - Name of instrument.
1094
- * @return {Track}
1095
- */
1096
- Track.prototype.addInstrumentName = function (text) {
1097
- return this.addEvent(new InstrumentNameEvent({ text: text }));
1098
- };
1099
- /**
1100
- * Adds marker to MIDI file.
1101
- * @param {string} text - Marker text.
1102
- * @return {Track}
1103
- */
1104
- Track.prototype.addMarker = function (text) {
1105
- return this.addEvent(new MarkerEvent({ text: text }));
1106
- };
1107
- /**
1108
- * Adds cue point to MIDI file.
1109
- * @param {string} text - Text of cue point.
1110
- * @return {Track}
1111
- */
1112
- Track.prototype.addCuePoint = function (text) {
1113
- return this.addEvent(new CuePointEvent({ text: text }));
1114
- };
1115
- /**
1116
- * Adds lyric to MIDI file.
1117
- * @param {string} text - Lyric text to add.
1118
- * @return {Track}
1119
- */
1120
- Track.prototype.addLyric = function (text) {
1121
- return this.addEvent(new LyricEvent({ text: text }));
1122
- };
1123
- /**
1124
- * Channel mode messages
1125
- * @return {Track}
1126
- */
1127
- Track.prototype.polyModeOn = function () {
1128
- var event = new NoteOnEvent({ data: [0x00, 0xB0, 0x7E, 0x00] });
1129
- return this.addEvent(event);
1130
- };
1131
- /**
1132
- * Sets a pitch bend.
1133
- * @param {float} bend - Bend value ranging [-1,1], zero meaning no bend.
1134
- * @return {Track}
1135
- */
1136
- Track.prototype.setPitchBend = function (bend) {
1137
- return this.addEvent(new PitchBendEvent({ bend: bend }));
1138
- };
1139
- /**
1140
- * Adds a controller change event
1141
- * @param {number} number - Control number.
1142
- * @param {number} value - Control value.
1143
- * @param {number} channel - Channel to send controller change event on (1-based).
1144
- * @param {number} delta - Track tick offset for cc event.
1145
- * @return {Track}
1146
- */
1147
- Track.prototype.controllerChange = function (number, value, channel, delta) {
1148
- return this.addEvent(new ControllerChangeEvent({ controllerNumber: number, controllerValue: value, channel: channel, delta: delta }));
1149
- };
1150
- return Track;
1151
- }());
1152
-
1153
- var VexFlow = /** @class */ (function () {
1154
- function VexFlow() {
1155
- }
1156
- /**
1157
- * Support for converting VexFlow voice into MidiWriterJS track
1158
- * @return MidiWriter.Track object
1159
- */
1160
- VexFlow.prototype.trackFromVoice = function (voice, options) {
1161
- var _this = this;
1162
- if (options === void 0) { options = { addRenderedAccidentals: false }; }
1163
- var track = new Track;
1164
- var wait = [];
1165
- voice.tickables.forEach(function (tickable) {
1166
- if (tickable.noteType === 'n') {
1167
- track.addEvent(new NoteEvent({
1168
- pitch: tickable.keys.map(function (pitch, index) { return _this.convertPitch(pitch, index, tickable, options.addRenderedAccidentals); }),
1169
- duration: _this.convertDuration(tickable),
1170
- wait: wait
1171
- }));
1172
- // reset wait
1173
- wait = [];
1174
- }
1175
- else if (tickable.noteType === 'r') {
1176
- // move on to the next tickable and add this to the stack
1177
- // of the `wait` property for the next note event
1178
- wait.push(_this.convertDuration(tickable));
1179
- }
1180
- });
1181
- // There may be outstanding rests at the end of the track,
1182
- // pad with a ghost note (zero duration and velocity), just to capture the wait.
1183
- if (wait.length > 0) {
1184
- track.addEvent(new NoteEvent({ pitch: '[c4]', duration: '0', wait: wait, velocity: '0' }));
1185
- }
1186
- return track;
1187
- };
1188
- /**
1189
- * Converts VexFlow pitch syntax to MidiWriterJS syntax
1190
- * @param pitch string
1191
- * @param index pitch index
1192
- * @param note struct from Vexflow
1193
- * @param addRenderedAccidentals adds Vexflow rendered accidentals
1194
- */
1195
- VexFlow.prototype.convertPitch = function (pitch, index, note, addRenderedAccidentals) {
1196
- var _a;
1197
- if (addRenderedAccidentals === void 0) { addRenderedAccidentals = false; }
1198
- // Splits note name from octave
1199
- var pitchParts = pitch.split('/');
1200
- // Retrieves accidentals from pitch
1201
- // Removes natural accidentals since they are not accepted in Tonal Midi
1202
- var accidentals = pitchParts[0].substring(1).replace('n', '');
1203
- if (addRenderedAccidentals) {
1204
- (_a = note.getAccidentals()) === null || _a === void 0 ? void 0 : _a.forEach(function (accidental) {
1205
- if (accidental.index === index) {
1206
- if (accidental.type === 'n') {
1207
- accidentals = '';
1208
- }
1209
- else {
1210
- accidentals += accidental.type;
1211
- }
1212
- }
1213
- });
1214
- }
1215
- return pitchParts[0][0] + accidentals + pitchParts[1];
1216
- };
1217
- /**
1218
- * Converts VexFlow duration syntax to MidiWriterJS syntax
1219
- * @param note struct from VexFlow
1220
- */
1221
- VexFlow.prototype.convertDuration = function (note) {
1222
- return 'd'.repeat(note.dots) + this.convertBaseDuration(note.duration) + (note.tuplet ? 't' + note.tuplet.num_notes : '');
1223
- };
1224
- /**
1225
- * Converts VexFlow base duration syntax to MidiWriterJS syntax
1226
- * @param duration Vexflow duration
1227
- * @returns MidiWriterJS duration
1228
- */
1229
- VexFlow.prototype.convertBaseDuration = function (duration) {
1230
- switch (duration) {
1231
- case 'w':
1232
- return '1';
1233
- case 'h':
1234
- return '2';
1235
- case 'q':
1236
- return '4';
1237
- default:
1238
- return duration;
1239
- }
1240
- };
1241
- return VexFlow;
1242
- }());
1243
-
1244
- /**
1245
- * Object representation of a header chunk section of a MIDI file.
1246
- * @param {number} numberOfTracks - Number of tracks
1247
- * @return {Header}
1248
- */
1249
- var Header = /** @class */ (function () {
1250
- function Header(numberOfTracks) {
1251
- this.type = Constants.HEADER_CHUNK_TYPE;
1252
- var trackType = numberOfTracks > 1 ? Constants.HEADER_CHUNK_FORMAT1 : Constants.HEADER_CHUNK_FORMAT0;
1253
- this.data = trackType.concat(Utils.numberToBytes(numberOfTracks, 2), // two bytes long,
1254
- Constants.HEADER_CHUNK_DIVISION);
1255
- this.size = [0, 0, 0, this.data.length];
1256
- }
1257
- return Header;
1258
- }());
1259
-
1260
- /**
1261
- * Object that puts together tracks and provides methods for file output.
1262
- * @param {array|Track} tracks - A single {Track} object or an array of {Track} objects.
1263
- * @param {object} options - {middleC: 'C4'}
1264
- * @return {Writer}
1265
- */
1266
- var Writer = /** @class */ (function () {
1267
- function Writer(tracks, options) {
1268
- if (options === void 0) { options = {}; }
1269
- // Ensure tracks is an array
1270
- this.tracks = Utils.toArray(tracks);
1271
- this.options = options;
1272
- }
1273
- /**
1274
- * Builds array of data from chunkschunks.
1275
- * @return {array}
1276
- */
1277
- Writer.prototype.buildData = function () {
1278
- var _this = this;
1279
- var data = [];
1280
- data.push(new Header(this.tracks.length));
1281
- // For each track add final end of track event and build data
1282
- this.tracks.forEach(function (track) {
1283
- data.push(track.buildData(_this.options));
1284
- });
1285
- return data;
1286
- };
1287
- /**
1288
- * Builds the file into a Uint8Array
1289
- * @return {Uint8Array}
1290
- */
1291
- Writer.prototype.buildFile = function () {
1292
- var build = [];
1293
- // Data consists of chunks which consists of data
1294
- this.buildData().forEach(function (d) { return build = build.concat(d.type, d.size, d.data); });
1295
- return new Uint8Array(build);
1296
- };
1297
- /**
1298
- * Convert file buffer to a base64 string. Different methods depending on if browser or node.
1299
- * @return {string}
1300
- */
1301
- Writer.prototype.base64 = function () {
1302
- if (typeof btoa === 'function') {
1303
- var binary = '';
1304
- var bytes = this.buildFile();
1305
- var len = bytes.byteLength;
1306
- for (var i = 0; i < len; i++) {
1307
- binary += String.fromCharCode(bytes[i]);
1308
- }
1309
- return btoa(binary);
1310
- }
1311
- return Buffer.from(this.buildFile()).toString('base64');
1312
- };
1313
- /**
1314
- * Get the data URI.
1315
- * @return {string}
1316
- */
1317
- Writer.prototype.dataUri = function () {
1318
- return 'data:audio/midi;base64,' + this.base64();
1319
- };
1320
- /**
1321
- * Set option on instantiated Writer.
1322
- * @param {string} key
1323
- * @param {any} value
1324
- * @return {Writer}
1325
- */
1326
- Writer.prototype.setOption = function (key, value) {
1327
- this.options[key] = value;
1328
- return this;
1329
- };
1330
- /**
1331
- * Output to stdout
1332
- * @return {string}
1333
- */
1334
- Writer.prototype.stdout = function () {
1335
- return process.stdout.write(Buffer.from(this.buildFile()));
1336
- };
1337
- return Writer;
1338
- }());
1339
-
1340
- var main = {
1341
- Constants: Constants,
1342
- ControllerChangeEvent: ControllerChangeEvent,
1343
- CopyrightEvent: CopyrightEvent,
1344
- CuePointEvent: CuePointEvent,
1345
- EndTrackEvent: EndTrackEvent,
1346
- InstrumentNameEvent: InstrumentNameEvent,
1347
- KeySignatureEvent: KeySignatureEvent,
1348
- LyricEvent: LyricEvent,
1349
- MarkerEvent: MarkerEvent,
1350
- NoteOnEvent: NoteOnEvent,
1351
- NoteOffEvent: NoteOffEvent,
1352
- NoteEvent: NoteEvent,
1353
- PitchBendEvent: PitchBendEvent,
1354
- ProgramChangeEvent: ProgramChangeEvent,
1355
- TempoEvent: TempoEvent,
1356
- TextEvent: TextEvent,
1357
- TimeSignatureEvent: TimeSignatureEvent,
1358
- Track: Track,
1359
- TrackNameEvent: TrackNameEvent,
1360
- Utils: Utils,
1361
- VexFlow: VexFlow,
1362
- Writer: Writer
1363
- };
1364
-
1365
- return main;
1366
-
1367
- })();