spessasynth_lib 3.23.11 → 3.23.13
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/@types/index.d.ts +8 -1
- package/@types/midi_parser/basic_midi.d.ts +7 -116
- package/@types/midi_parser/midi_builder.d.ts +3 -0
- package/@types/midi_parser/midi_data.d.ts +27 -118
- package/@types/midi_parser/midi_editor.d.ts +71 -27
- package/@types/midi_parser/midi_loader.d.ts +4 -0
- package/@types/midi_parser/midi_sequence.d.ts +124 -0
- package/@types/midi_parser/midi_writer.d.ts +1 -1
- package/@types/midi_parser/rmidi_writer.d.ts +1 -0
- package/@types/sequencer/sequencer.d.ts +3 -1
- package/@types/soundfont/basic_soundfont/basic_sample.d.ts +9 -8
- package/@types/soundfont/read_sf2/samples.d.ts +1 -1
- package/index.js +17 -3
- package/midi_parser/basic_midi.js +7 -124
- package/midi_parser/midi_builder.js +3 -0
- package/midi_parser/midi_data.js +11 -127
- package/midi_parser/midi_editor.js +32 -18
- package/midi_parser/midi_loader.js +5 -0
- package/midi_parser/midi_sequence.js +133 -0
- package/midi_parser/midi_writer.js +5 -1
- package/midi_parser/rmidi_writer.js +6 -1
- package/package.json +1 -1
- package/sequencer/sequencer.js +3 -6
- package/sequencer/worklet_sequencer/process_event.js +12 -1
- package/sequencer/worklet_sequencer/sequencer_message.js +1 -1
- package/sequencer/worklet_sequencer/worklet_sequencer.js +0 -1
- package/soundfont/basic_soundfont/basic_sample.js +38 -10
- package/soundfont/basic_soundfont/write_sf2/sdta.js +5 -2
- package/soundfont/read_sf2/samples.js +6 -3
- package/synthetizer/worklet_processor.min.js +11 -11
- package/synthetizer/worklet_system/main_processor.js +1 -1
- package/synthetizer/worklet_system/snapshot/snapshot.js +311 -0
- package/synthetizer/worklet_system/worklet_methods/data_entry.js +0 -14
- package/synthetizer/worklet_system/worklet_methods/note_off.js +1 -1
- package/synthetizer/worklet_system/worklet_methods/snapshot.js +0 -145
package/index.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// Import modules
|
|
2
2
|
import { loadSoundFont } from "./soundfont/load_soundfont.js";
|
|
3
3
|
import { BasicSoundFont } from "./soundfont/basic_soundfont/basic_soundfont.js";
|
|
4
|
+
import { BasicSample } from "./soundfont/basic_soundfont/basic_sample.js";
|
|
5
|
+
import { BasicInstrumentZone } from "./soundfont/basic_soundfont/basic_zones.js";
|
|
6
|
+
import { BasicInstrument } from "./soundfont/basic_soundfont/basic_instrument.js";
|
|
7
|
+
import { Generator } from "./soundfont/basic_soundfont/generator.js";
|
|
8
|
+
import { Modulator } from "./soundfont/basic_soundfont/modulator.js";
|
|
9
|
+
import { BasicPresetZone } from "./soundfont/basic_soundfont/basic_zones.js";
|
|
10
|
+
import { BasicPreset } from "./soundfont/basic_soundfont/basic_preset.js";
|
|
4
11
|
import { MIDI } from './midi_parser/midi_loader.js';
|
|
5
12
|
import { MIDIticksToSeconds } from './midi_parser/basic_midi.js';
|
|
6
13
|
import { MIDIBuilder } from "./midi_parser/midi_builder.js";
|
|
@@ -38,13 +45,20 @@ export {
|
|
|
38
45
|
Synthetizer,
|
|
39
46
|
DEFAULT_PERCUSSION,
|
|
40
47
|
VOICE_CAP,
|
|
41
|
-
|
|
48
|
+
|
|
42
49
|
// SoundFont
|
|
43
50
|
BasicSoundFont,
|
|
51
|
+
BasicSample,
|
|
52
|
+
BasicInstrumentZone,
|
|
53
|
+
BasicInstrument,
|
|
54
|
+
BasicPreset,
|
|
55
|
+
BasicPresetZone,
|
|
56
|
+
Generator,
|
|
57
|
+
Modulator,
|
|
44
58
|
loadSoundFont,
|
|
45
59
|
trimSoundfont,
|
|
46
60
|
modulatorSources,
|
|
47
|
-
|
|
61
|
+
|
|
48
62
|
// MIDI
|
|
49
63
|
MIDI,
|
|
50
64
|
MIDIBuilder,
|
|
@@ -54,7 +68,7 @@ export {
|
|
|
54
68
|
applySnapshotToMIDI,
|
|
55
69
|
modifyMIDI,
|
|
56
70
|
MIDIticksToSeconds,
|
|
57
|
-
|
|
71
|
+
|
|
58
72
|
// Utilities
|
|
59
73
|
audioBufferToWav,
|
|
60
74
|
SpessaSynthLogging,
|
|
@@ -1,111 +1,14 @@
|
|
|
1
1
|
import { messageTypes } from "./midi_message.js";
|
|
2
2
|
import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
|
|
3
|
+
import { MIDISequenceData } from "./midi_sequence.js";
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
/**
|
|
6
|
+
* BasicMIDI is the base of a complete MIDI file, used by the sequencer internally.
|
|
7
|
+
* BasicMIDI is not available on the main thread, as it contains the actual track data which can be large.
|
|
8
|
+
* It can be accessed by calling getMIDI() on the Sequencer.
|
|
9
|
+
*/
|
|
10
|
+
export class BasicMIDI extends MIDISequenceData
|
|
5
11
|
{
|
|
6
|
-
/**
|
|
7
|
-
* The time division of the sequence, representing the number of ticks per beat.
|
|
8
|
-
* @type {number}
|
|
9
|
-
*/
|
|
10
|
-
timeDivision = 0;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* The duration of the sequence, in seconds.
|
|
14
|
-
* @type {number}
|
|
15
|
-
*/
|
|
16
|
-
duration = 0;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* The tempo changes in the sequence, ordered from the last change to the first.
|
|
20
|
-
* Each change is represented by an object with a tick position and a tempo value in beats per minute.
|
|
21
|
-
* @type {{ticks: number, tempo: number}[]}
|
|
22
|
-
*/
|
|
23
|
-
tempoChanges = [{ ticks: 0, tempo: 120 }];
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* A string containing the copyright information for the MIDI sequence if detected.
|
|
27
|
-
* @type {string}
|
|
28
|
-
*/
|
|
29
|
-
copyright = "";
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The number of tracks in the MIDI sequence.
|
|
33
|
-
* @type {number}
|
|
34
|
-
*/
|
|
35
|
-
tracksAmount = 0;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* An array containing the lyrics of the sequence, stored as binary chunks (Uint8Array).
|
|
39
|
-
* @type {Uint8Array[]}
|
|
40
|
-
*/
|
|
41
|
-
lyrics = [];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* The tick position of the first note-on event in the MIDI sequence.
|
|
45
|
-
* @type {number}
|
|
46
|
-
*/
|
|
47
|
-
firstNoteOn = 0;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* The MIDI key range used in the sequence, represented by a minimum and maximum note value.
|
|
51
|
-
* @type {{min: number, max: number}}
|
|
52
|
-
*/
|
|
53
|
-
keyRange = { min: 0, max: 127 };
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* The tick position of the last voice event (such as note-on, note-off, or control change) in the sequence.
|
|
57
|
-
* @type {number}
|
|
58
|
-
*/
|
|
59
|
-
lastVoiceEventTick = 0;
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* An array of MIDI port numbers used by each track in the sequence.
|
|
63
|
-
* @type {number[]}
|
|
64
|
-
*/
|
|
65
|
-
midiPorts = [0];
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* An array of channel offsets for each MIDI port, using the SpessaSynth method.
|
|
69
|
-
* @type {number[]}
|
|
70
|
-
*/
|
|
71
|
-
midiPortChannelOffsets = [0];
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* A list of sets, where each set contains the MIDI channels used by each track in the sequence.
|
|
75
|
-
* @type {Set<number>[]}
|
|
76
|
-
*/
|
|
77
|
-
usedChannelsOnTrack = [];
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* The loop points (in ticks) of the sequence, including both start and end points.
|
|
81
|
-
* @type {{start: number, end: number}}
|
|
82
|
-
*/
|
|
83
|
-
loop = { start: 0, end: 0 };
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The name of the MIDI sequence.
|
|
87
|
-
* @type {string}
|
|
88
|
-
*/
|
|
89
|
-
midiName = "";
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* A boolean indicating if the sequence's name is the same as the file name.
|
|
93
|
-
* @type {boolean}
|
|
94
|
-
*/
|
|
95
|
-
midiNameUsesFileName = false;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* The file name of the MIDI sequence, if provided during parsing.
|
|
99
|
-
* @type {string}
|
|
100
|
-
*/
|
|
101
|
-
fileName = "";
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* The raw, encoded MIDI name, represented as a Uint8Array.
|
|
105
|
-
* Useful when the MIDI file uses a different code page.
|
|
106
|
-
* @type {Uint8Array}
|
|
107
|
-
*/
|
|
108
|
-
rawMidiName = undefined;
|
|
109
12
|
|
|
110
13
|
/**
|
|
111
14
|
* The embedded soundfont in the MIDI file, represented as an ArrayBuffer, if available.
|
|
@@ -113,26 +16,6 @@ export class BasicMIDI
|
|
|
113
16
|
*/
|
|
114
17
|
embeddedSoundFont = undefined;
|
|
115
18
|
|
|
116
|
-
/**
|
|
117
|
-
* The format of the MIDI file, which can be 0, 1, or 2, indicating the type of the MIDI file.
|
|
118
|
-
* @type {number}
|
|
119
|
-
*/
|
|
120
|
-
format = 0;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* The RMID (Resource Interchangeable MIDI) info data, if the file is RMID formatted.
|
|
124
|
-
* Otherwise, this field is undefined.
|
|
125
|
-
* Chunk type (e.g. "INAM"): Chunk data as binary array.
|
|
126
|
-
* @type {Object<string, IndexedByteArray>}
|
|
127
|
-
*/
|
|
128
|
-
RMIDInfo = {};
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* The bank offset used for RMID files.
|
|
132
|
-
* @type {number}
|
|
133
|
-
*/
|
|
134
|
-
bankOffset = 0;
|
|
135
|
-
|
|
136
19
|
/**
|
|
137
20
|
* The actual track data of the MIDI file, represented as an array of tracks.
|
|
138
21
|
* Tracks are arrays of MidiMessage objects.
|
|
@@ -3,6 +3,9 @@ import { messageTypes, MidiMessage } from "./midi_message.js";
|
|
|
3
3
|
import { IndexedByteArray } from "../utils/indexed_array.js";
|
|
4
4
|
import { SpessaSynthWarn } from "../utils/loggin.js";
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* A class that helps to build a MIDI file from scratch.
|
|
8
|
+
*/
|
|
6
9
|
export class MIDIBuilder extends BasicMIDI
|
|
7
10
|
{
|
|
8
11
|
/**
|
package/midi_parser/midi_data.js
CHANGED
|
@@ -1,111 +1,12 @@
|
|
|
1
|
+
import { MIDISequenceData } from "./midi_sequence.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
* A simplified version of the MIDI, accessible at all times from the Sequencer.
|
|
3
|
-
*
|
|
4
|
+
* A simplified version of the MIDI, accessible at all times from the Sequencer.
|
|
5
|
+
* Use getMIDI() to get the actual sequence.
|
|
6
|
+
* This class contains all properties that MIDI does, except for tracks and the embedded soundfont.
|
|
4
7
|
*/
|
|
5
|
-
export class MidiData
|
|
8
|
+
export class MidiData extends MIDISequenceData
|
|
6
9
|
{
|
|
7
|
-
/**
|
|
8
|
-
* The time division of the sequence, representing the number of ticks per beat.
|
|
9
|
-
* @type {number}
|
|
10
|
-
*/
|
|
11
|
-
timeDivision = 0;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* The duration of the sequence, in seconds.
|
|
15
|
-
* @type {number}
|
|
16
|
-
*/
|
|
17
|
-
duration = 0;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* The tempo changes in the sequence, ordered from the last change to the first.
|
|
21
|
-
* Each change is represented by an object with a tick position and a tempo value in beats per minute.
|
|
22
|
-
* @type {{ticks: number, tempo: number}[]}
|
|
23
|
-
*/
|
|
24
|
-
tempoChanges = [{ ticks: 0, tempo: 120 }];
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* A string containing the copyright information for the MIDI sequence.
|
|
28
|
-
* @type {string}
|
|
29
|
-
*/
|
|
30
|
-
copyright = "";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The number of tracks in the MIDI sequence.
|
|
34
|
-
* @type {number}
|
|
35
|
-
*/
|
|
36
|
-
tracksAmount = 0;
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* An array containing the lyrics of the sequence, stored as binary chunks (Uint8Array).
|
|
40
|
-
* @type {Uint8Array[]}
|
|
41
|
-
*/
|
|
42
|
-
lyrics = [];
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* The tick position of the first note-on event in the MIDI sequence.
|
|
46
|
-
* @type {number}
|
|
47
|
-
*/
|
|
48
|
-
firstNoteOn = 0;
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* The MIDI key range used in the sequence, represented by a minimum and maximum note value.
|
|
52
|
-
* @type {{min: number, max: number}}
|
|
53
|
-
*/
|
|
54
|
-
keyRange = { min: 0, max: 127 };
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* The tick position of the last voice event (such as note-on, note-off, or control change) in the sequence.
|
|
58
|
-
* @type {number}
|
|
59
|
-
*/
|
|
60
|
-
lastVoiceEventTick = 0;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* An array of MIDI port numbers used by each track in the sequence.
|
|
64
|
-
* @type {number[]}
|
|
65
|
-
*/
|
|
66
|
-
midiPorts = [0];
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* An array of channel offsets for each MIDI port, using the SpessaSynth method.
|
|
70
|
-
* @type {number[]}
|
|
71
|
-
*/
|
|
72
|
-
midiPortChannelOffsets = [0];
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* A list of sets, where each set contains the MIDI channels used by each track in the sequence.
|
|
76
|
-
* @type {Set<number>[]}
|
|
77
|
-
*/
|
|
78
|
-
usedChannelsOnTrack = [];
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* The loop points (in ticks) of the sequence, including both start and end points.
|
|
82
|
-
* @type {{start: number, end: number}}
|
|
83
|
-
*/
|
|
84
|
-
loop = { start: 0, end: 0 };
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* The name of the MIDI sequence.
|
|
88
|
-
* @type {string}
|
|
89
|
-
*/
|
|
90
|
-
midiName = "";
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* A boolean indicating if the sequence's name is the same as the file name.
|
|
94
|
-
* @type {boolean}
|
|
95
|
-
*/
|
|
96
|
-
midiNameUsesFileName = false;
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* The file name of the MIDI sequence, if provided by the MIDI class.
|
|
100
|
-
* @type {string}
|
|
101
|
-
*/
|
|
102
|
-
fileName = "";
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* The raw, encoded MIDI name, represented as a Uint8Array.
|
|
106
|
-
* @type {Uint8Array}
|
|
107
|
-
*/
|
|
108
|
-
rawMidiName = undefined;
|
|
109
10
|
|
|
110
11
|
/**
|
|
111
12
|
* A boolean indicating if the MIDI file contains an embedded soundfont.
|
|
@@ -115,30 +16,12 @@ export class MidiData
|
|
|
115
16
|
isEmbedded = false;
|
|
116
17
|
|
|
117
18
|
/**
|
|
118
|
-
*
|
|
119
|
-
* @type {number}
|
|
120
|
-
*/
|
|
121
|
-
format = 0;
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* The RMID (Resource Interchangeable MIDI) info data, if the file is RMID formatted.
|
|
125
|
-
* Otherwise, this field is undefined.
|
|
126
|
-
* @type {Object<string, IndexedByteArray>}
|
|
127
|
-
*/
|
|
128
|
-
RMIDInfo = {};
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* The bank offset used for RMID files.
|
|
132
|
-
* @type {number}
|
|
133
|
-
*/
|
|
134
|
-
bankOffset = 0;
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Constructor that copies data from a BasicMIDI instance, except for tracks and embeddedSoundFont.
|
|
19
|
+
* Constructor that copies data from a BasicMIDI instance.
|
|
138
20
|
* @param {BasicMIDI} midi - The BasicMIDI instance to copy data from.
|
|
139
21
|
*/
|
|
140
22
|
constructor(midi)
|
|
141
23
|
{
|
|
24
|
+
super();
|
|
142
25
|
this.timeDivision = midi.timeDivision;
|
|
143
26
|
this.duration = midi.duration;
|
|
144
27
|
this.tempoChanges = midi.tempoChanges;
|
|
@@ -167,7 +50,7 @@ export class MidiData
|
|
|
167
50
|
|
|
168
51
|
|
|
169
52
|
/**
|
|
170
|
-
*
|
|
53
|
+
* Temporary MIDI data used when the MIDI is not loaded.
|
|
171
54
|
* @type {MidiData}
|
|
172
55
|
*/
|
|
173
56
|
export const DUMMY_MIDI_DATA = {
|
|
@@ -194,5 +77,6 @@ export const DUMMY_MIDI_DATA = {
|
|
|
194
77
|
isEmbedded: false,
|
|
195
78
|
RMIDInfo: {},
|
|
196
79
|
bankOffset: 0,
|
|
197
|
-
midiNameUsesFileName: false
|
|
80
|
+
midiNameUsesFileName: false,
|
|
81
|
+
format: 0
|
|
198
82
|
};
|
|
@@ -75,24 +75,38 @@ function getDrumChange(channel, ticks)
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
/**
|
|
78
|
-
*
|
|
79
|
-
* @
|
|
80
|
-
* @
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* }
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
*
|
|
95
|
-
* }
|
|
78
|
+
* @typedef {Object} DesiredProgramChange
|
|
79
|
+
* @property {number} channel - The channel number.
|
|
80
|
+
* @property {number} program - The program number.
|
|
81
|
+
* @property {number} bank - The bank number.
|
|
82
|
+
* @property {boolean} isDrum - Indicates if the channel is a drum channel.
|
|
83
|
+
* If it is, then the bank number is ignored.
|
|
84
|
+
*/
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @typedef {Object} DesiredControllerChange
|
|
88
|
+
* @property {number} channel - The channel number.
|
|
89
|
+
* @property {number} controllerNumber - The MIDI controller number.
|
|
90
|
+
* @property {number} controllerValue - The new controller value.
|
|
91
|
+
*/
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @typedef {Object} DesiredChanneltranspose
|
|
95
|
+
* @property {number} channel - The channel number.
|
|
96
|
+
* @property {number} keyShift - The number of semitones to transpose.
|
|
97
|
+
* Note that this can use floating point numbers,
|
|
98
|
+
* which will be used to fine-tune the pitch in cents using RPN.
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Allows easy editing of the file by removing channels, changing programs,
|
|
103
|
+
* changing controllers and transposing channels. Note that this modifies the MIDI in-place.
|
|
104
|
+
*
|
|
105
|
+
* @param {BasicMIDI} midi - The MIDI file to modify.
|
|
106
|
+
* @param {DesiredProgramChange[]} desiredProgramChanges - The programs to set on given channels.
|
|
107
|
+
* @param {DesiredControllerChange[]} desiredControllerChanges - The controllers to set on given channels.
|
|
108
|
+
* @param {number[]} desiredChannelsToClear - The channels to remove from the sequence.
|
|
109
|
+
* @param {DesiredChanneltranspose[]} desiredChannelsToTranspose - The channels to transpose.
|
|
96
110
|
*/
|
|
97
111
|
export function modifyMIDI(
|
|
98
112
|
midi,
|
|
@@ -17,6 +17,11 @@ const GS_TEXT_HEADER = new Uint8Array([0x41, 0x10, 0x45, 0x12, 0x10, 0x00, 0x00]
|
|
|
17
17
|
* midi_loader.js
|
|
18
18
|
* purpose: parses a midi file for the seqyencer, including things like marker or CC 2/4 loop detection, copyright detection etc.
|
|
19
19
|
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The MIDI class is a MIDI file parser that reads a MIDI file and extracts all the necessary information from it.
|
|
23
|
+
* Supported formats are .mid and .rmi files.
|
|
24
|
+
*/
|
|
20
25
|
class MIDI extends BasicMIDI
|
|
21
26
|
{
|
|
22
27
|
/**
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is the base type for MIDI files. It contains all the "metadata" and information.
|
|
3
|
+
* It extends to:
|
|
4
|
+
* - BasicMIDI, which contains the actual track data of the MIDI file. Essentially the MIDI file itself.
|
|
5
|
+
* - MidiData, which contains all properties that MIDI does, except for tracks and the embedded soundfont.
|
|
6
|
+
* MidiData is the "shell" of the file which is available on the main thread at all times, containing the metadata.
|
|
7
|
+
*/
|
|
8
|
+
export class MIDISequenceData
|
|
9
|
+
{
|
|
10
|
+
/**
|
|
11
|
+
* The time division of the sequence, representing the number of ticks per beat.
|
|
12
|
+
* @type {number}
|
|
13
|
+
*/
|
|
14
|
+
timeDivision = 0;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The duration of the sequence, in seconds.
|
|
18
|
+
* @type {number}
|
|
19
|
+
*/
|
|
20
|
+
duration = 0;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The tempo changes in the sequence, ordered from the last change to the first.
|
|
24
|
+
* Each change is represented by an object with a tick position and a tempo value in beats per minute.
|
|
25
|
+
* @type {{ticks: number, tempo: number}[]}
|
|
26
|
+
*/
|
|
27
|
+
tempoChanges = [{ ticks: 0, tempo: 120 }];
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A string containing the copyright information for the MIDI sequence if detected.
|
|
31
|
+
* @type {string}
|
|
32
|
+
*/
|
|
33
|
+
copyright = "";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The number of tracks in the MIDI sequence.
|
|
37
|
+
* @type {number}
|
|
38
|
+
*/
|
|
39
|
+
tracksAmount = 0;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* An array containing the lyrics of the sequence, stored as binary chunks (Uint8Array).
|
|
43
|
+
* @type {Uint8Array[]}
|
|
44
|
+
*/
|
|
45
|
+
lyrics = [];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The tick position of the first note-on event in the MIDI sequence.
|
|
49
|
+
* @type {number}
|
|
50
|
+
*/
|
|
51
|
+
firstNoteOn = 0;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The MIDI key range used in the sequence, represented by a minimum and maximum note value.
|
|
55
|
+
* @type {{min: number, max: number}}
|
|
56
|
+
*/
|
|
57
|
+
keyRange = { min: 0, max: 127 };
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The tick position of the last voice event (such as note-on, note-off, or control change) in the sequence.
|
|
61
|
+
* @type {number}
|
|
62
|
+
*/
|
|
63
|
+
lastVoiceEventTick = 0;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* An array of MIDI port numbers used by each track in the sequence.
|
|
67
|
+
* @type {number[]}
|
|
68
|
+
*/
|
|
69
|
+
midiPorts = [0];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* An array of channel offsets for each MIDI port, using the SpessaSynth method.
|
|
73
|
+
* @type {number[]}
|
|
74
|
+
*/
|
|
75
|
+
midiPortChannelOffsets = [0];
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A list of sets, where each set contains the MIDI channels used by each track in the sequence.
|
|
79
|
+
* @type {Set<number>[]}
|
|
80
|
+
*/
|
|
81
|
+
usedChannelsOnTrack = [];
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* The loop points (in ticks) of the sequence, including both start and end points.
|
|
85
|
+
* @type {{start: number, end: number}}
|
|
86
|
+
*/
|
|
87
|
+
loop = { start: 0, end: 0 };
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The name of the MIDI sequence.
|
|
91
|
+
* @type {string}
|
|
92
|
+
*/
|
|
93
|
+
midiName = "";
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A boolean indicating if the sequence's name is the same as the file name.
|
|
97
|
+
* @type {boolean}
|
|
98
|
+
*/
|
|
99
|
+
midiNameUsesFileName = false;
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The file name of the MIDI sequence, if provided during parsing.
|
|
103
|
+
* @type {string}
|
|
104
|
+
*/
|
|
105
|
+
fileName = "";
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The raw, encoded MIDI name, represented as a Uint8Array.
|
|
109
|
+
* Useful when the MIDI file uses a different code page.
|
|
110
|
+
* @type {Uint8Array}
|
|
111
|
+
*/
|
|
112
|
+
rawMidiName = undefined;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The format of the MIDI file, which can be 0, 1, or 2, indicating the type of the MIDI file.
|
|
116
|
+
* @type {number}
|
|
117
|
+
*/
|
|
118
|
+
format = 0;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The RMID (Resource Interchangeable MIDI) info data, if the file is RMID formatted.
|
|
122
|
+
* Otherwise, this field is undefined.
|
|
123
|
+
* Chunk type (e.g. "INAM"): Chunk data as binary array.
|
|
124
|
+
* @type {Object<string, IndexedByteArray>}
|
|
125
|
+
*/
|
|
126
|
+
RMIDInfo = {};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The bank offset used for RMID files.
|
|
130
|
+
* @type {number}
|
|
131
|
+
*/
|
|
132
|
+
bankOffset = 0;
|
|
133
|
+
}
|
|
@@ -4,11 +4,15 @@ import { writeBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js
|
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Exports the midi as a .mid file
|
|
7
|
-
* @param midi {BasicMIDI}
|
|
7
|
+
* @param midi {BasicMIDI} the midi to export
|
|
8
8
|
* @returns {Uint8Array} the binary .mid file data
|
|
9
9
|
*/
|
|
10
10
|
export function writeMIDIFile(midi)
|
|
11
11
|
{
|
|
12
|
+
if (!midi.tracks)
|
|
13
|
+
{
|
|
14
|
+
throw new Error("MIDI has no tracks!");
|
|
15
|
+
}
|
|
12
16
|
/**
|
|
13
17
|
* @type {Uint8Array[]}
|
|
14
18
|
*/
|
|
@@ -15,6 +15,7 @@ import { writeLittleEndian } from "../utils/byte_functions/little_endian.js";
|
|
|
15
15
|
export const RMIDINFOChunks = {
|
|
16
16
|
name: "INAM",
|
|
17
17
|
album: "IPRD",
|
|
18
|
+
album2: "IALB",
|
|
18
19
|
artist: "IART",
|
|
19
20
|
genre: "IGNR",
|
|
20
21
|
picture: "IPIC",
|
|
@@ -371,7 +372,7 @@ export function writeRMIDI(
|
|
|
371
372
|
*/
|
|
372
373
|
const infoContent = [getStringBytes("INFO")];
|
|
373
374
|
const encoder = new TextEncoder();
|
|
374
|
-
// software
|
|
375
|
+
// software (SpessaSynth)
|
|
375
376
|
infoContent.push(
|
|
376
377
|
writeRIFFOddSize(RMIDINFOChunks.software, encoder.encode("SpessaSynth"), true)
|
|
377
378
|
);
|
|
@@ -430,10 +431,14 @@ export function writeRMIDI(
|
|
|
430
431
|
// album
|
|
431
432
|
if (metadata.album !== undefined)
|
|
432
433
|
{
|
|
434
|
+
// note that there are two album chunks: IPRD and IALB
|
|
433
435
|
encoding = FORCED_ENCODING;
|
|
434
436
|
infoContent.push(
|
|
435
437
|
writeRIFFOddSize(RMIDINFOChunks.album, encoder.encode(metadata.album), true)
|
|
436
438
|
);
|
|
439
|
+
infoContent.push(
|
|
440
|
+
writeRIFFOddSize(RMIDINFOChunks.album2, encoder.encode(metadata.album), true)
|
|
441
|
+
);
|
|
437
442
|
}
|
|
438
443
|
// artist
|
|
439
444
|
if (metadata.artist !== undefined)
|
package/package.json
CHANGED
package/sequencer/sequencer.js
CHANGED
|
@@ -61,8 +61,10 @@ export class Sequencer
|
|
|
61
61
|
onSongChange = {};
|
|
62
62
|
/**
|
|
63
63
|
* Fires on text event
|
|
64
|
+
* @type {function}
|
|
64
65
|
* @param data {Uint8Array} the data text
|
|
65
66
|
* @param type {number} the status byte of the message (the meta status byte)
|
|
67
|
+
* @param lyricsIndex {number} if the text is a lyric, the index of the lyric in midiData.lyrics, otherwise -1
|
|
66
68
|
*/
|
|
67
69
|
onTextEvent;
|
|
68
70
|
/**
|
|
@@ -304,7 +306,6 @@ export class Sequencer
|
|
|
304
306
|
addOnSongChangeEvent(callback, id)
|
|
305
307
|
{
|
|
306
308
|
this.onSongChange[id] = callback;
|
|
307
|
-
callback(this.midiData);
|
|
308
309
|
}
|
|
309
310
|
|
|
310
311
|
/**
|
|
@@ -419,13 +420,9 @@ export class Sequencer
|
|
|
419
420
|
break;
|
|
420
421
|
|
|
421
422
|
case WorkletSequencerReturnMessageType.textEvent:
|
|
422
|
-
/**
|
|
423
|
-
* @type {[Uint8Array, number]}
|
|
424
|
-
*/
|
|
425
|
-
let textEventData = messageData;
|
|
426
423
|
if (this.onTextEvent)
|
|
427
424
|
{
|
|
428
|
-
this.onTextEvent(
|
|
425
|
+
this.onTextEvent(...(messageData));
|
|
429
426
|
}
|
|
430
427
|
break;
|
|
431
428
|
|
|
@@ -117,7 +117,18 @@ export function _processEvent(event, trackIndex)
|
|
|
117
117
|
case messageTypes.cuePoint:
|
|
118
118
|
case messageTypes.instrumentName:
|
|
119
119
|
case messageTypes.programName:
|
|
120
|
-
|
|
120
|
+
let lyricsIndex = -1;
|
|
121
|
+
if (statusByteData.status === messageTypes.lyric)
|
|
122
|
+
{
|
|
123
|
+
lyricsIndex = Math.min(
|
|
124
|
+
this.midiData.lyrics.indexOf(event.messageData) + 1,
|
|
125
|
+
this.midiData.lyrics.length - 1
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
this.post(
|
|
129
|
+
WorkletSequencerReturnMessageType.textEvent,
|
|
130
|
+
[event.messageData, statusByteData.status, lyricsIndex]
|
|
131
|
+
);
|
|
121
132
|
break;
|
|
122
133
|
|
|
123
134
|
case messageTypes.midiPort:
|