@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.
- package/README.md +27 -28
- package/build/index.cjs +24 -26
- package/build/index.mjs +1 -26
- package/build/types/main.d.ts +2 -35
- package/package.json +9 -2
- package/src/abstract-event.ts +8 -0
- package/src/chunks/chunk.ts +6 -0
- package/src/chunks/header.ts +29 -0
- package/src/chunks/track.ts +345 -0
- package/src/constants.ts +18 -0
- package/src/main.ts +48 -0
- package/src/meta-events/copyright-event.ts +35 -0
- package/src/meta-events/cue-point-event.ts +35 -0
- package/src/meta-events/end-track-event.ts +29 -0
- package/src/meta-events/instrument-name-event.ts +35 -0
- package/src/meta-events/key-signature-event.ts +73 -0
- package/src/meta-events/lyric-event.ts +35 -0
- package/src/meta-events/marker-event.ts +35 -0
- package/src/meta-events/meta-event.ts +7 -0
- package/src/meta-events/tempo-event.ts +37 -0
- package/src/meta-events/text-event.ts +35 -0
- package/src/meta-events/time-signature-event.ts +32 -0
- package/src/meta-events/track-name-event.ts +35 -0
- package/src/midi-events/controller-change-event.ts +30 -0
- package/src/midi-events/midi-event.ts +11 -0
- package/src/midi-events/note-event.ts +164 -0
- package/src/midi-events/note-off-event.ts +55 -0
- package/src/midi-events/note-on-event.ts +69 -0
- package/src/midi-events/pitch-bend-event.ts +40 -0
- package/src/midi-events/program-change-event.ts +28 -0
- package/src/utils.ts +263 -0
- package/src/vexflow.ts +96 -0
- package/src/writer.ts +99 -0
- package/.editorconfig +0 -24
- package/.eslintignore +0 -3
- package/.eslintrc.js +0 -18
- package/.nvmrc +0 -1
- package/.travis.yml +0 -3
- package/browser/midiwriter.js +0 -1367
- package/jsdoc.json +0 -5
- package/postinstall.js +0 -1
- package/rollup.config.js +0 -22
- package/runkit.js +0 -18
- package/tsconfig.json +0 -13
- package/typedoc.json +0 -5
package/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
♬ MidiWriterJS
|
|
2
2
|
===============
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-

|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
7
|
|
|
8
8
|
MidiWriterJS is a JavaScript library providing an API for generating expressive multi-track MIDI files.
|
|
9
9
|
|
|
@@ -14,32 +14,32 @@ Note that the `master` branch is in active development so if you're looking for
|
|
|
14
14
|
Install
|
|
15
15
|
------------
|
|
16
16
|
```sh
|
|
17
|
-
npm install midi-writer-
|
|
17
|
+
npm install @huffduff/midi-writer-ts
|
|
18
18
|
```
|
|
19
19
|
Getting Started
|
|
20
20
|
------------
|
|
21
21
|
|
|
22
|
-
```
|
|
23
|
-
import
|
|
22
|
+
```typescript
|
|
23
|
+
import { Track, Writer, NoteEvent, ProgramChangeEvent } from '@huffduff/midi-writer-ts';
|
|
24
24
|
|
|
25
25
|
// Start with a new track
|
|
26
|
-
const track = new
|
|
26
|
+
const track = new Track();
|
|
27
27
|
|
|
28
28
|
// Define an instrument (optional):
|
|
29
|
-
track.addEvent(new
|
|
29
|
+
track.addEvent(new ProgramChangeEvent({instrument: 1}));
|
|
30
30
|
|
|
31
31
|
// Add some notes:
|
|
32
|
-
const note = new
|
|
32
|
+
const note = new NoteEvent({pitch: ['C4', 'D4', 'E4'], duration: '4'});
|
|
33
33
|
track.addEvent(note);
|
|
34
34
|
|
|
35
35
|
// Generate a data URI
|
|
36
|
-
const write = new
|
|
36
|
+
const write = new Writer(track);
|
|
37
37
|
console.log(write.dataUri());
|
|
38
38
|
```
|
|
39
39
|
Documentation
|
|
40
40
|
------------
|
|
41
41
|
|
|
42
|
-
### `
|
|
42
|
+
### `Track()`
|
|
43
43
|
|
|
44
44
|
- `addEvent({event}, mapFunction)`
|
|
45
45
|
- `setTempo(tempo)`
|
|
@@ -52,7 +52,7 @@ Documentation
|
|
|
52
52
|
- `addLyric(text)`
|
|
53
53
|
- `setTimeSignature(numerator, denominator)`
|
|
54
54
|
|
|
55
|
-
### `
|
|
55
|
+
### `NoteEvent({options})`
|
|
56
56
|
|
|
57
57
|
The MIDI spec defines that each note must have a `NoteOnEvent` and `NoteOffEvent` (or `NoteOnEvent` with zero velocity) event, marking the beginning and end of the sounding note. While it's possible to manually add these events to a track with `Track.addEvent()`, the `NoteEvent` provides a more intuitive interface for doing this with a single, "pseudo" event. Under the hood, the `NoteEvent` event generates the relevant `NoteOnEvent` and `NoteOffEvent` events.
|
|
58
58
|
|
|
@@ -150,7 +150,7 @@ The `NoteEvent` supports these options:
|
|
|
150
150
|
</table>
|
|
151
151
|
|
|
152
152
|
|
|
153
|
-
### `
|
|
153
|
+
### `Writer(tracks)`
|
|
154
154
|
The `Writer` class provides a few ways to output the file:
|
|
155
155
|
- `buildFile()` *Uint8Array*
|
|
156
156
|
- `base64()` *string*
|
|
@@ -160,22 +160,21 @@ The `Writer` class provides a few ways to output the file:
|
|
|
160
160
|
### Hot Cross Buns
|
|
161
161
|
Here's an example of how everyone's favorite song "Hot Cross Buns" could be written. Note use of the mapping function passed as the second argument of `addEvent()`. This can be used to apply specific properties to all events. With some
|
|
162
162
|
street smarts you could also use it for programmatic crescendos and other property 'animation'.
|
|
163
|
-
```
|
|
164
|
-
import
|
|
163
|
+
```typescript
|
|
164
|
+
import { Track, NoteEvent, Writer } from 'midi-writer-js';
|
|
165
165
|
|
|
166
|
-
const track = new
|
|
166
|
+
const track = new Track();
|
|
167
167
|
|
|
168
168
|
track.addEvent([
|
|
169
|
-
new
|
|
170
|
-
new
|
|
171
|
-
new
|
|
172
|
-
new
|
|
173
|
-
new
|
|
174
|
-
new
|
|
175
|
-
new
|
|
176
|
-
],
|
|
177
|
-
|
|
178
|
-
}
|
|
169
|
+
new NoteEvent({pitch: ['E4','D4'], duration: '4'}),
|
|
170
|
+
new NoteEvent({pitch: ['C4'], duration: '2'}),
|
|
171
|
+
new NoteEvent({pitch: ['E4','D4'], duration: '4'}),
|
|
172
|
+
new NoteEvent({pitch: ['C4'], duration: '2'}),
|
|
173
|
+
new NoteEvent({pitch: ['C4', 'C4', 'C4', 'C4', 'D4', 'D4', 'D4', 'D4'], duration: '8'}),
|
|
174
|
+
new NoteEvent({pitch: ['E4','D4'], duration: '4'}),
|
|
175
|
+
new NoteEvent({pitch: ['C4'], duration: '2'})
|
|
176
|
+
],
|
|
177
|
+
(event, index) => ({ sequential: true }),
|
|
179
178
|
);
|
|
180
179
|
|
|
181
180
|
const write = new MidiWriter.Writer(track);
|
|
@@ -184,7 +183,7 @@ console.log(write.dataUri());
|
|
|
184
183
|
|
|
185
184
|
### VexFlow Integration
|
|
186
185
|
MidiWriterJS can export MIDI from VexFlow voices, though this feature is still experimental. Current usage is to use `MidiWriter.VexFlow.trackFromVoice(voice)` to create a MidiWriterJS `Track` object:
|
|
187
|
-
```
|
|
186
|
+
```typescript
|
|
188
187
|
|
|
189
188
|
// ...VexFlow code defining notes
|
|
190
189
|
const voice = create_4_4_voice().addTickables(notes);
|
package/build/index.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
3
5
|
var midi = require('@tonaljs/midi');
|
|
4
6
|
|
|
5
7
|
/**
|
|
@@ -1191,29 +1193,25 @@ class Writer {
|
|
|
1191
1193
|
}
|
|
1192
1194
|
}
|
|
1193
1195
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
Writer
|
|
1217
|
-
};
|
|
1218
|
-
|
|
1219
|
-
module.exports = main;
|
|
1196
|
+
exports.Constants = Constants;
|
|
1197
|
+
exports.ControllerChangeEvent = ControllerChangeEvent;
|
|
1198
|
+
exports.CopyrightEvent = CopyrightEvent;
|
|
1199
|
+
exports.CuePointEvent = CuePointEvent;
|
|
1200
|
+
exports.EndTrackEvent = EndTrackEvent;
|
|
1201
|
+
exports.InstrumentNameEvent = InstrumentNameEvent;
|
|
1202
|
+
exports.KeySignatureEvent = KeySignatureEvent;
|
|
1203
|
+
exports.LyricEvent = LyricEvent;
|
|
1204
|
+
exports.MarkerEvent = MarkerEvent;
|
|
1205
|
+
exports.NoteEvent = NoteEvent;
|
|
1206
|
+
exports.NoteOffEvent = NoteOffEvent;
|
|
1207
|
+
exports.NoteOnEvent = NoteOnEvent;
|
|
1208
|
+
exports.PitchBendEvent = PitchBendEvent;
|
|
1209
|
+
exports.ProgramChangeEvent = ProgramChangeEvent;
|
|
1210
|
+
exports.TempoEvent = TempoEvent;
|
|
1211
|
+
exports.TextEvent = TextEvent;
|
|
1212
|
+
exports.TimeSignatureEvent = TimeSignatureEvent;
|
|
1213
|
+
exports.Track = Track;
|
|
1214
|
+
exports.TrackNameEvent = TrackNameEvent;
|
|
1215
|
+
exports.Utils = Utils;
|
|
1216
|
+
exports.VexFlow = VexFlow;
|
|
1217
|
+
exports.Writer = Writer;
|
package/build/index.mjs
CHANGED
|
@@ -1189,29 +1189,4 @@ class Writer {
|
|
|
1189
1189
|
}
|
|
1190
1190
|
}
|
|
1191
1191
|
|
|
1192
|
-
|
|
1193
|
-
Constants,
|
|
1194
|
-
ControllerChangeEvent,
|
|
1195
|
-
CopyrightEvent,
|
|
1196
|
-
CuePointEvent,
|
|
1197
|
-
EndTrackEvent,
|
|
1198
|
-
InstrumentNameEvent,
|
|
1199
|
-
KeySignatureEvent,
|
|
1200
|
-
LyricEvent,
|
|
1201
|
-
MarkerEvent,
|
|
1202
|
-
NoteOnEvent,
|
|
1203
|
-
NoteOffEvent,
|
|
1204
|
-
NoteEvent,
|
|
1205
|
-
PitchBendEvent,
|
|
1206
|
-
ProgramChangeEvent,
|
|
1207
|
-
TempoEvent,
|
|
1208
|
-
TextEvent,
|
|
1209
|
-
TimeSignatureEvent,
|
|
1210
|
-
Track,
|
|
1211
|
-
TrackNameEvent,
|
|
1212
|
-
Utils,
|
|
1213
|
-
VexFlow,
|
|
1214
|
-
Writer
|
|
1215
|
-
};
|
|
1216
|
-
|
|
1217
|
-
export { main as default };
|
|
1192
|
+
export { Constants, ControllerChangeEvent, CopyrightEvent, CuePointEvent, EndTrackEvent, InstrumentNameEvent, KeySignatureEvent, LyricEvent, MarkerEvent, NoteEvent, NoteOffEvent, NoteOnEvent, PitchBendEvent, ProgramChangeEvent, TempoEvent, TextEvent, TimeSignatureEvent, Track, TrackNameEvent, Utils, VexFlow, Writer };
|
package/build/types/main.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Constants } from './constants';
|
|
1
2
|
import { ControllerChangeEvent } from './midi-events/controller-change-event';
|
|
2
3
|
import { CopyrightEvent } from './meta-events/copyright-event';
|
|
3
4
|
import { CuePointEvent } from './meta-events/cue-point-event';
|
|
@@ -19,38 +20,4 @@ import { TrackNameEvent } from './meta-events/track-name-event';
|
|
|
19
20
|
import { Utils } from './utils';
|
|
20
21
|
import { VexFlow } from './vexflow';
|
|
21
22
|
import { Writer } from './writer';
|
|
22
|
-
|
|
23
|
-
Constants: {
|
|
24
|
-
VERSION: string;
|
|
25
|
-
HEADER_CHUNK_TYPE: number[];
|
|
26
|
-
HEADER_CHUNK_LENGTH: number[];
|
|
27
|
-
HEADER_CHUNK_FORMAT0: number[];
|
|
28
|
-
HEADER_CHUNK_FORMAT1: number[];
|
|
29
|
-
HEADER_CHUNK_DIVISION: number[];
|
|
30
|
-
TRACK_CHUNK_TYPE: number[];
|
|
31
|
-
META_EVENT_ID: number;
|
|
32
|
-
META_SMTPE_OFFSET: number;
|
|
33
|
-
};
|
|
34
|
-
ControllerChangeEvent: typeof ControllerChangeEvent;
|
|
35
|
-
CopyrightEvent: typeof CopyrightEvent;
|
|
36
|
-
CuePointEvent: typeof CuePointEvent;
|
|
37
|
-
EndTrackEvent: typeof EndTrackEvent;
|
|
38
|
-
InstrumentNameEvent: typeof InstrumentNameEvent;
|
|
39
|
-
KeySignatureEvent: typeof KeySignatureEvent;
|
|
40
|
-
LyricEvent: typeof LyricEvent;
|
|
41
|
-
MarkerEvent: typeof MarkerEvent;
|
|
42
|
-
NoteOnEvent: typeof NoteOnEvent;
|
|
43
|
-
NoteOffEvent: typeof NoteOffEvent;
|
|
44
|
-
NoteEvent: typeof NoteEvent;
|
|
45
|
-
PitchBendEvent: typeof PitchBendEvent;
|
|
46
|
-
ProgramChangeEvent: typeof ProgramChangeEvent;
|
|
47
|
-
TempoEvent: typeof TempoEvent;
|
|
48
|
-
TextEvent: typeof TextEvent;
|
|
49
|
-
TimeSignatureEvent: typeof TimeSignatureEvent;
|
|
50
|
-
Track: typeof Track;
|
|
51
|
-
TrackNameEvent: typeof TrackNameEvent;
|
|
52
|
-
Utils: typeof Utils;
|
|
53
|
-
VexFlow: typeof VexFlow;
|
|
54
|
-
Writer: typeof Writer;
|
|
55
|
-
};
|
|
56
|
-
export default _default;
|
|
23
|
+
export { Constants, ControllerChangeEvent, CopyrightEvent, CuePointEvent, EndTrackEvent, InstrumentNameEvent, KeySignatureEvent, LyricEvent, MarkerEvent, NoteOnEvent, NoteOffEvent, NoteEvent, PitchBendEvent, ProgramChangeEvent, TempoEvent, TextEvent, TimeSignatureEvent, Track, TrackNameEvent, Utils, VexFlow, Writer };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huffduff/midi-writer-ts",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "A fork of midi-writer-js, a library providing an API for generating MIDI files.",
|
|
5
5
|
"main": "build/index.cjs",
|
|
6
6
|
"module": "build/index.mjs",
|
|
@@ -47,6 +47,14 @@
|
|
|
47
47
|
"example": "examples",
|
|
48
48
|
"test": "test"
|
|
49
49
|
},
|
|
50
|
+
"files": [
|
|
51
|
+
"build/**/*",
|
|
52
|
+
"LICENSE",
|
|
53
|
+
"README.md",
|
|
54
|
+
"./src/**/*",
|
|
55
|
+
"examples/**/*",
|
|
56
|
+
"test/**/*"
|
|
57
|
+
],
|
|
50
58
|
"scripts": {
|
|
51
59
|
"build": "mkdir -p build && rollup -c",
|
|
52
60
|
"docs": "npx typedoc --options typedoc.json",
|
|
@@ -57,7 +65,6 @@
|
|
|
57
65
|
"watch": "watch 'npm run build' src",
|
|
58
66
|
"postinstall": "node postinstall.js"
|
|
59
67
|
},
|
|
60
|
-
"runkitExampleFilename": "runkit.js",
|
|
61
68
|
"repository": {
|
|
62
69
|
"type": "git",
|
|
63
70
|
"url": "git+https://github.com/tommyo/MidiWriterJS.git"
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {Chunk} from './chunk';
|
|
2
|
+
import {Constants} from '../constants';
|
|
3
|
+
import {Utils} from '../utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Object representation of a header chunk section of a MIDI file.
|
|
7
|
+
* @param {number} numberOfTracks - Number of tracks
|
|
8
|
+
* @return {Header}
|
|
9
|
+
*/
|
|
10
|
+
class Header implements Chunk {
|
|
11
|
+
data: number[];
|
|
12
|
+
type: number[];
|
|
13
|
+
size: number[];
|
|
14
|
+
|
|
15
|
+
constructor(numberOfTracks) {
|
|
16
|
+
this.type = Constants.HEADER_CHUNK_TYPE;
|
|
17
|
+
|
|
18
|
+
const trackType = numberOfTracks > 1? Constants.HEADER_CHUNK_FORMAT1 : Constants.HEADER_CHUNK_FORMAT0;
|
|
19
|
+
|
|
20
|
+
this.data = trackType.concat(
|
|
21
|
+
Utils.numberToBytes(numberOfTracks, 2), // two bytes long,
|
|
22
|
+
Constants.HEADER_CHUNK_DIVISION
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
this.size = [0, 0, 0, this.data.length];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export {Header};
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { AbstractEvent } from '../abstract-event';
|
|
2
|
+
import {Chunk} from './chunk';
|
|
3
|
+
import {Constants} from '../constants';
|
|
4
|
+
import {ControllerChangeEvent} from '../midi-events/controller-change-event';
|
|
5
|
+
import {CopyrightEvent} from '../meta-events/copyright-event';
|
|
6
|
+
import {CuePointEvent} from '../meta-events/cue-point-event';
|
|
7
|
+
import {EndTrackEvent} from '../meta-events/end-track-event';
|
|
8
|
+
import {InstrumentNameEvent} from '../meta-events/instrument-name-event';
|
|
9
|
+
import {KeySignatureEvent} from '../meta-events/key-signature-event';
|
|
10
|
+
import {LyricEvent} from '../meta-events/lyric-event';
|
|
11
|
+
import {MarkerEvent} from '../meta-events/marker-event';
|
|
12
|
+
import {NoteEvent} from '../midi-events/note-event';
|
|
13
|
+
import {NoteOnEvent} from '../midi-events/note-on-event';
|
|
14
|
+
import {NoteOffEvent} from '../midi-events/note-off-event';
|
|
15
|
+
import {PitchBendEvent} from '../midi-events/pitch-bend-event';
|
|
16
|
+
import {TempoEvent} from '../meta-events/tempo-event';
|
|
17
|
+
import {TextEvent} from '../meta-events/text-event';
|
|
18
|
+
import {TimeSignatureEvent} from '../meta-events/time-signature-event';
|
|
19
|
+
import {TrackNameEvent} from '../meta-events/track-name-event';
|
|
20
|
+
import {Utils} from '../utils';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Holds all data for a track.
|
|
24
|
+
* @param {object} fields {type: number, data: array, size: array, events: array}
|
|
25
|
+
* @return {Track}
|
|
26
|
+
*/
|
|
27
|
+
class Track implements Chunk {
|
|
28
|
+
data: number[];
|
|
29
|
+
events: AbstractEvent[];
|
|
30
|
+
explicitTickEvents: NoteEvent[];
|
|
31
|
+
size: number[];
|
|
32
|
+
type: number[];
|
|
33
|
+
tickPointer: number;
|
|
34
|
+
|
|
35
|
+
constructor() {
|
|
36
|
+
this.type = Constants.TRACK_CHUNK_TYPE;
|
|
37
|
+
this.data = [];
|
|
38
|
+
this.size = [];
|
|
39
|
+
this.events = [];
|
|
40
|
+
this.explicitTickEvents = [];
|
|
41
|
+
|
|
42
|
+
// If there are any events with an explicit tick defined then we will create a "sub" track for those
|
|
43
|
+
// and merge them in and the end.
|
|
44
|
+
this.tickPointer = 0; // Each time an event is added this will increase
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Adds any event type to the track.
|
|
49
|
+
* Events without a specific startTick property are assumed to be added in order of how they should output.
|
|
50
|
+
* Events with a specific startTick property are set aside for now will be merged in during build process.
|
|
51
|
+
*
|
|
52
|
+
* TODO: Don't put startTick events in their own array. Just lump everything together and sort it out during buildData();
|
|
53
|
+
* @param {(NoteEvent|ProgramChangeEvent)} events - Event object or array of Event objects.
|
|
54
|
+
* @param {Function} mapFunction - Callback which can be used to apply specific properties to all events.
|
|
55
|
+
* @return {Track}
|
|
56
|
+
*/
|
|
57
|
+
addEvent(events: (AbstractEvent|AbstractEvent[]), mapFunction?: (i: number, event: AbstractEvent) => object): Track {
|
|
58
|
+
Utils.toArray(events).forEach((event, i) => {
|
|
59
|
+
if (event instanceof NoteEvent) {
|
|
60
|
+
// Handle map function if provided
|
|
61
|
+
if (typeof mapFunction === 'function') {
|
|
62
|
+
const properties = mapFunction(i, event);
|
|
63
|
+
|
|
64
|
+
if (typeof properties === 'object') {
|
|
65
|
+
Object.assign(event, properties);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// If this note event has an explicit startTick then we need to set aside for now
|
|
70
|
+
if (event.tick !== null) {
|
|
71
|
+
this.explicitTickEvents.push(event);
|
|
72
|
+
|
|
73
|
+
} else {
|
|
74
|
+
// Push each on/off event to track's event stack
|
|
75
|
+
event.buildData().events.forEach((e) => this.events.push(e));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
} else {
|
|
79
|
+
this.events.push(event);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Builds int array of all events.
|
|
88
|
+
* @param {object} options
|
|
89
|
+
* @return {Track}
|
|
90
|
+
*/
|
|
91
|
+
buildData(options = {}) {
|
|
92
|
+
// Reset
|
|
93
|
+
this.data = [];
|
|
94
|
+
this.size = [];
|
|
95
|
+
this.tickPointer = 0;
|
|
96
|
+
|
|
97
|
+
let precisionLoss = 0;
|
|
98
|
+
|
|
99
|
+
this.events.forEach((event) => {
|
|
100
|
+
// Build event & add to total tick duration
|
|
101
|
+
if (event instanceof NoteOnEvent || event instanceof NoteOffEvent) {
|
|
102
|
+
const built = event.buildData(this, precisionLoss, options);
|
|
103
|
+
precisionLoss = Utils.getPrecisionLoss(event.deltaWithPrecisionCorrection || 0);
|
|
104
|
+
this.data = this.data.concat(built.data);
|
|
105
|
+
this.tickPointer = Utils.getRoundedIfClose(event.tick);
|
|
106
|
+
|
|
107
|
+
} else if (event instanceof TempoEvent) {
|
|
108
|
+
this.tickPointer = Utils.getRoundedIfClose(event.tick);
|
|
109
|
+
this.data = this.data.concat(event.data);
|
|
110
|
+
|
|
111
|
+
} else {
|
|
112
|
+
this.data = this.data.concat(event.data);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
this.mergeExplicitTickEvents();
|
|
117
|
+
|
|
118
|
+
// If the last event isn't EndTrackEvent, then tack it onto the data.
|
|
119
|
+
if (!this.events.length || !(this.events[this.events.length - 1] instanceof EndTrackEvent)) {
|
|
120
|
+
this.data = this.data.concat((new EndTrackEvent).data);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.size = Utils.numberToBytes(this.data.length, 4); // 4 bytes long
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
mergeExplicitTickEvents() {
|
|
128
|
+
if (!this.explicitTickEvents.length) return;
|
|
129
|
+
|
|
130
|
+
// First sort asc list of events by startTick
|
|
131
|
+
this.explicitTickEvents.sort((a, b) => a.tick - b.tick);
|
|
132
|
+
|
|
133
|
+
// Now this.explicitTickEvents is in correct order, and so is this.events naturally.
|
|
134
|
+
// For each explicit tick event, splice it into the main list of events and
|
|
135
|
+
// adjust the delta on the following events so they still play normally.
|
|
136
|
+
this.explicitTickEvents.forEach((noteEvent) => {
|
|
137
|
+
// Convert NoteEvent to it's respective NoteOn/NoteOff events
|
|
138
|
+
// Note that as we splice in events the delta for the NoteOff ones will
|
|
139
|
+
// Need to change based on what comes before them after the splice.
|
|
140
|
+
noteEvent.buildData().events.forEach((e) => e.buildData(this));
|
|
141
|
+
|
|
142
|
+
// Merge each event individually into this track's event list.
|
|
143
|
+
noteEvent.events.forEach((event) => this.mergeSingleEvent(event));
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Hacky way to rebuild track with newly spliced events. Need better solution.
|
|
147
|
+
this.explicitTickEvents = [];
|
|
148
|
+
this.buildData();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Merges another track's events with this track.
|
|
153
|
+
* @param {Track} track
|
|
154
|
+
* @return {Track}
|
|
155
|
+
*/
|
|
156
|
+
mergeTrack(track: Track): Track {
|
|
157
|
+
// First build this track to populate each event's tick property
|
|
158
|
+
this.buildData();
|
|
159
|
+
|
|
160
|
+
// Then build track to be merged so that tick property is populated on all events & merge each event.
|
|
161
|
+
track.buildData().events.forEach((event) => this.mergeSingleEvent(event));
|
|
162
|
+
return this;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Merges a single event into this track's list of events based on event.tick property.
|
|
167
|
+
* @param {AbstractEvent} - event
|
|
168
|
+
* @return {Track}
|
|
169
|
+
*/
|
|
170
|
+
mergeSingleEvent(event: AbstractEvent): Track {
|
|
171
|
+
// There are no events yet, so just add it in.
|
|
172
|
+
if (!this.events.length) {
|
|
173
|
+
this.addEvent(event);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Find index of existing event we need to follow with
|
|
178
|
+
let lastEventIndex;
|
|
179
|
+
|
|
180
|
+
for (let i = 0; i < this.events.length; i++) {
|
|
181
|
+
if (this.events[i].tick > event.tick) break;
|
|
182
|
+
lastEventIndex = i;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const splicedEventIndex = lastEventIndex + 1;
|
|
186
|
+
|
|
187
|
+
// Need to adjust the delta of this event to ensure it falls on the correct tick.
|
|
188
|
+
event.delta = event.tick - this.events[lastEventIndex].tick;
|
|
189
|
+
|
|
190
|
+
// Splice this event at lastEventIndex + 1
|
|
191
|
+
this.events.splice(splicedEventIndex, 0, event);
|
|
192
|
+
|
|
193
|
+
// Now adjust delta of all following events
|
|
194
|
+
for (let i = splicedEventIndex + 1; i < this.events.length; i++) {
|
|
195
|
+
// Since each existing event should have a tick value at this point we just need to
|
|
196
|
+
// adjust delta to that the event still falls on the correct tick.
|
|
197
|
+
this.events[i].delta = this.events[i].tick - this.events[i - 1].tick;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Removes all events matching specified type.
|
|
203
|
+
* @param {string} eventName - Event type
|
|
204
|
+
* @return {Track}
|
|
205
|
+
*/
|
|
206
|
+
removeEventsByName(eventName: string): Track {
|
|
207
|
+
this.events.forEach((event, index) => {
|
|
208
|
+
if (event.name === eventName) {
|
|
209
|
+
this.events.splice(index, 1);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
return this;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Sets tempo of the MIDI file.
|
|
218
|
+
* @param {number} bpm - Tempo in beats per minute.
|
|
219
|
+
* @param {number} tick - Start tick.
|
|
220
|
+
* @return {Track}
|
|
221
|
+
*/
|
|
222
|
+
setTempo(bpm: number, tick = 0): Track {
|
|
223
|
+
return this.addEvent(new TempoEvent({bpm, tick}));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Sets time signature.
|
|
228
|
+
* @param {number} numerator - Top number of the time signature.
|
|
229
|
+
* @param {number} denominator - Bottom number of the time signature.
|
|
230
|
+
* @param {number} midiclockspertick - Defaults to 24.
|
|
231
|
+
* @param {number} notespermidiclock - Defaults to 8.
|
|
232
|
+
* @return {Track}
|
|
233
|
+
*/
|
|
234
|
+
setTimeSignature(numerator: number, denominator: number, midiclockspertick: number, notespermidiclock: number): Track {
|
|
235
|
+
return this.addEvent(new TimeSignatureEvent(numerator, denominator, midiclockspertick, notespermidiclock));
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Sets key signature.
|
|
240
|
+
* @param {*} sf -
|
|
241
|
+
* @param {*} mi -
|
|
242
|
+
* @return {Track}
|
|
243
|
+
*/
|
|
244
|
+
setKeySignature(sf, mi) {
|
|
245
|
+
return this.addEvent(new KeySignatureEvent(sf, mi));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Adds text to MIDI file.
|
|
250
|
+
* @param {string} text - Text to add.
|
|
251
|
+
* @return {Track}
|
|
252
|
+
*/
|
|
253
|
+
addText(text: string): Track {
|
|
254
|
+
return this.addEvent(new TextEvent({text}));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Adds copyright to MIDI file.
|
|
259
|
+
* @param {string} text - Text of copyright line.
|
|
260
|
+
* @return {Track}
|
|
261
|
+
*/
|
|
262
|
+
addCopyright(text: string): Track {
|
|
263
|
+
return this.addEvent(new CopyrightEvent({text}));
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Adds Sequence/Track Name.
|
|
268
|
+
* @param {string} text - Text of track name.
|
|
269
|
+
* @return {Track}
|
|
270
|
+
*/
|
|
271
|
+
addTrackName(text: string): Track {
|
|
272
|
+
return this.addEvent(new TrackNameEvent({text}));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Sets instrument name of track.
|
|
277
|
+
* @param {string} text - Name of instrument.
|
|
278
|
+
* @return {Track}
|
|
279
|
+
*/
|
|
280
|
+
addInstrumentName(text: string): Track {
|
|
281
|
+
return this.addEvent(new InstrumentNameEvent({text}));
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Adds marker to MIDI file.
|
|
286
|
+
* @param {string} text - Marker text.
|
|
287
|
+
* @return {Track}
|
|
288
|
+
*/
|
|
289
|
+
addMarker(text: string): Track {
|
|
290
|
+
return this.addEvent(new MarkerEvent({text}));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Adds cue point to MIDI file.
|
|
295
|
+
* @param {string} text - Text of cue point.
|
|
296
|
+
* @return {Track}
|
|
297
|
+
*/
|
|
298
|
+
addCuePoint(text: string): Track {
|
|
299
|
+
return this.addEvent(new CuePointEvent({text}));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Adds lyric to MIDI file.
|
|
304
|
+
* @param {string} text - Lyric text to add.
|
|
305
|
+
* @return {Track}
|
|
306
|
+
*/
|
|
307
|
+
addLyric(text: string): Track {
|
|
308
|
+
return this.addEvent(new LyricEvent({text}));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Channel mode messages
|
|
313
|
+
* @return {Track}
|
|
314
|
+
*/
|
|
315
|
+
polyModeOn(): Track {
|
|
316
|
+
const event = new NoteOnEvent({data: [0x00, 0xB0, 0x7E, 0x00]});
|
|
317
|
+
return this.addEvent(event);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Sets a pitch bend.
|
|
323
|
+
* @param {float} bend - Bend value ranging [-1,1], zero meaning no bend.
|
|
324
|
+
* @return {Track}
|
|
325
|
+
*/
|
|
326
|
+
setPitchBend(bend: number): Track {
|
|
327
|
+
return this.addEvent(new PitchBendEvent({bend}));
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Adds a controller change event
|
|
333
|
+
* @param {number} number - Control number.
|
|
334
|
+
* @param {number} value - Control value.
|
|
335
|
+
* @param {number} channel - Channel to send controller change event on (1-based).
|
|
336
|
+
* @param {number} delta - Track tick offset for cc event.
|
|
337
|
+
* @return {Track}
|
|
338
|
+
*/
|
|
339
|
+
controllerChange(number: number, value: number, channel?: number, delta?: number): Track {
|
|
340
|
+
return this.addEvent(new ControllerChangeEvent({controllerNumber: number, controllerValue: value, channel: channel, delta: delta}));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export {Track};
|