spessasynth_lib 3.9.13 → 3.9.14
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/midi_parser/midi_data.d.ts +5 -0
- package/@types/midi_parser/midi_loader.d.ts +5 -0
- package/midi_parser/midi_data.js +7 -0
- package/midi_parser/midi_editor.js +149 -128
- package/midi_parser/midi_loader.js +19 -1
- package/midi_parser/used_keys_loaded.js +125 -90
- package/package.json +2 -5
- package/sequencer/worklet_sequencer/song_control.js +3 -3
- package/synthetizer/worklet_processor.min.js +7 -7
- package/synthetizer/worklet_system/worklet_utilities/volume_envelope.js +1 -0
|
@@ -12,9 +12,8 @@ export function getUsedProgramsAndKeys(mid, soundfont)
|
|
|
12
12
|
SpessaSynthGroupCollapsed("%cSearching for all used programs and keys...",
|
|
13
13
|
consoleColors.info);
|
|
14
14
|
// find every bank:program combo and every key:velocity for each. Make sure to care about ports and drums
|
|
15
|
-
const channelsAmount = 16 +
|
|
15
|
+
const channelsAmount = 16 + Math.max.apply(undefined, mid.midiPortChannelOffsets);
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
17
|
* @type {{program: number, bank: number, drums: boolean, string: string}[]}
|
|
19
18
|
*/
|
|
20
19
|
const channelPresets = [];
|
|
@@ -24,7 +23,7 @@ export function getUsedProgramsAndKeys(mid, soundfont)
|
|
|
24
23
|
program: 0,
|
|
25
24
|
bank: bank,
|
|
26
25
|
drums: i % 16 === DEFAULT_PERCUSSION, // drums appear on 9 every 16 channels,
|
|
27
|
-
string: `${bank}:0
|
|
26
|
+
string: `${bank}:0`,
|
|
28
27
|
});
|
|
29
28
|
}
|
|
30
29
|
|
|
@@ -54,109 +53,145 @@ export function getUsedProgramsAndKeys(mid, soundfont)
|
|
|
54
53
|
* @type {Object<string, Set<string>>}
|
|
55
54
|
*/
|
|
56
55
|
const usedProgramsAndKeys = {};
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* indexes for tracks
|
|
59
|
+
* @type {number[]}
|
|
60
|
+
*/
|
|
61
|
+
const eventIndexes = Array(mid.tracks.length).fill(0);
|
|
62
|
+
let remainingTracks = mid.tracks.length;
|
|
63
|
+
function findFirstEventIndex()
|
|
64
|
+
{
|
|
65
|
+
let index = 0;
|
|
66
|
+
let ticks = Infinity;
|
|
67
|
+
mid.tracks.forEach((track, i) => {
|
|
68
|
+
if(eventIndexes[i] >= track.length)
|
|
70
69
|
{
|
|
71
|
-
|
|
70
|
+
return;
|
|
72
71
|
}
|
|
73
|
-
|
|
74
|
-
let ch = channelPresets[channel];
|
|
75
|
-
switch(status)
|
|
72
|
+
if(track[eventIndexes[i]].ticks < ticks)
|
|
76
73
|
{
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
index = i;
|
|
75
|
+
ticks = track[eventIndexes[i]].ticks;
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
return index;
|
|
79
|
+
}
|
|
80
|
+
const ports = mid.midiPorts.slice();
|
|
81
|
+
// check for xg
|
|
82
|
+
let system = "gs";
|
|
83
|
+
while(remainingTracks > 0)
|
|
84
|
+
{
|
|
85
|
+
let trackNum = findFirstEventIndex();
|
|
86
|
+
const track = mid.tracks[trackNum];
|
|
87
|
+
if(eventIndexes[trackNum] >= track.length)
|
|
88
|
+
{
|
|
89
|
+
remainingTracks--;
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const event = track[eventIndexes[trackNum]];
|
|
93
|
+
eventIndexes[trackNum]++;
|
|
81
94
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
continue;
|
|
107
|
-
}
|
|
108
|
-
channelPresets[channel].bank = bank;
|
|
109
|
-
// do not update the data, bank change doesnt change the preset
|
|
110
|
-
break;
|
|
95
|
+
if(event.messageStatusByte === messageTypes.midiPort)
|
|
96
|
+
{
|
|
97
|
+
ports[trackNum] = event.messageData[0];
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const status = event.messageStatusByte & 0xF0;
|
|
101
|
+
if(
|
|
102
|
+
status !== messageTypes.noteOn &&
|
|
103
|
+
status !== messageTypes.controllerChange &&
|
|
104
|
+
status !== messageTypes.programChange &&
|
|
105
|
+
status !== messageTypes.systemExclusive
|
|
106
|
+
)
|
|
107
|
+
{
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const channel = (event.messageStatusByte & 0xF) + mid.midiPortChannelOffsets[ports[trackNum]] || 0;
|
|
111
|
+
let ch = channelPresets[channel];
|
|
112
|
+
switch(status)
|
|
113
|
+
{
|
|
114
|
+
case messageTypes.programChange:
|
|
115
|
+
ch.program = event.messageData[0];
|
|
116
|
+
updateString(ch);
|
|
117
|
+
break;
|
|
111
118
|
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
case messageTypes.controllerChange:
|
|
120
|
+
if(event.messageData[0] !== midiControllers.bankSelect)
|
|
121
|
+
{
|
|
122
|
+
// we only care about bank select
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if(system === "gs" && ch.drums)
|
|
126
|
+
{
|
|
127
|
+
// gs drums get changed via sysex, ignore here
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const bank = event.messageData[1];
|
|
131
|
+
if(system === "xg")
|
|
132
|
+
{
|
|
133
|
+
// check for xg drums
|
|
134
|
+
const drumsBool = bank === 120 || bank === 126 || bank === 127;
|
|
135
|
+
if(drumsBool !== ch.drums)
|
|
114
136
|
{
|
|
115
|
-
//
|
|
116
|
-
|
|
137
|
+
// drum change is a program change
|
|
138
|
+
ch.drums = drumsBool;
|
|
139
|
+
ch.bank = ch.drums ? 128 : bank;
|
|
140
|
+
updateString(ch);
|
|
117
141
|
}
|
|
118
|
-
|
|
142
|
+
else
|
|
119
143
|
{
|
|
120
|
-
|
|
144
|
+
ch.bank = ch.drums ? 128 : bank;
|
|
121
145
|
}
|
|
122
|
-
|
|
123
|
-
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
channelPresets[channel].bank = bank;
|
|
149
|
+
// do not update the data, bank change doesnt change the preset
|
|
150
|
+
break;
|
|
124
151
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
152
|
+
case messageTypes.noteOn:
|
|
153
|
+
if(event.messageData[1] === 0)
|
|
154
|
+
{
|
|
155
|
+
// that's a note off
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
usedProgramsAndKeys[ch.string].add(`${event.messageData[0]}-${event.messageData[1]}`);
|
|
159
|
+
break;
|
|
160
|
+
|
|
161
|
+
case messageTypes.systemExclusive:
|
|
162
|
+
// check for drum sysex
|
|
163
|
+
if(
|
|
164
|
+
event.messageData[0] !== 0x41 || // roland
|
|
165
|
+
event.messageData[2] !== 0x42 || // GS
|
|
166
|
+
event.messageData[3] !== 0x12 || // GS
|
|
167
|
+
event.messageData[4] !== 0x40 || // system parameter
|
|
168
|
+
(event.messageData[5] & 0x10 ) === 0 || // part parameter
|
|
169
|
+
event.messageData[6] !== 0x15 // drum pars
|
|
134
170
|
|
|
171
|
+
)
|
|
172
|
+
{
|
|
173
|
+
// check for XG
|
|
174
|
+
if(
|
|
175
|
+
event.messageData[0] === 0x43 && // yamaha
|
|
176
|
+
event.messageData[2] === 0x4C && // sXG ON
|
|
177
|
+
event.messageData[5] === 0x7E &&
|
|
178
|
+
event.messageData[6] === 0x00
|
|
135
179
|
)
|
|
136
180
|
{
|
|
137
|
-
|
|
138
|
-
if(
|
|
139
|
-
event.messageData[0] === 0x43 && // yamaha
|
|
140
|
-
event.messageData[2] === 0x4C && // sXG ON
|
|
141
|
-
event.messageData[5] === 0x7E &&
|
|
142
|
-
event.messageData[6] === 0x00
|
|
143
|
-
)
|
|
144
|
-
{
|
|
145
|
-
system = "xg";
|
|
146
|
-
}
|
|
147
|
-
continue;
|
|
181
|
+
system = "xg";
|
|
148
182
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const sysexChannel = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15][event.messageData[5] & 0x0F] + mid.midiPortChannelOffsets[ports[trackNum]];
|
|
186
|
+
const isDrum = !!(event.messageData[7] > 0 && event.messageData[5] >> 4);
|
|
187
|
+
ch = channelPresets[sysexChannel];
|
|
188
|
+
ch.drums = isDrum;
|
|
189
|
+
ch.bank = isDrum ? 128 : 0;
|
|
190
|
+
updateString(ch);
|
|
191
|
+
break;
|
|
156
192
|
|
|
157
|
-
}
|
|
158
193
|
}
|
|
159
|
-
}
|
|
194
|
+
}
|
|
160
195
|
for(const key of Object.keys(usedProgramsAndKeys))
|
|
161
196
|
{
|
|
162
197
|
if(usedProgramsAndKeys[key].size === 0)
|
package/package.json
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spessasynth_lib",
|
|
3
|
-
"version": "3.9.
|
|
4
|
-
"description": "No compromise
|
|
3
|
+
"version": "3.9.14",
|
|
4
|
+
"description": "No compromise MIDI SoundFont2 library",
|
|
5
5
|
"browser": "index.js",
|
|
6
6
|
"types": "@types/index.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"directories": {
|
|
9
|
-
"lib": "lib"
|
|
10
|
-
},
|
|
11
8
|
"scripts": {
|
|
12
9
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
10
|
},
|
|
@@ -88,12 +88,12 @@ export function loadNewSequence(parsedMidi)
|
|
|
88
88
|
*/
|
|
89
89
|
this.tracks = this.midiData.tracks;
|
|
90
90
|
|
|
91
|
-
// clear last port data
|
|
92
|
-
this.midiPortChannelOffset = 0;
|
|
93
|
-
this.midiPortChannelOffsets = {};
|
|
94
91
|
// copy over the port data
|
|
95
92
|
this.midiPorts = this.midiData.midiPorts;
|
|
96
93
|
|
|
94
|
+
// clear last port data
|
|
95
|
+
this.midiPortChannelOffset = 0;
|
|
96
|
+
this.midiPortChannelOffsets = {};
|
|
97
97
|
// assign port offsets
|
|
98
98
|
this.midiData.midiPorts.forEach((port, trackIndex) => {
|
|
99
99
|
this.assignMIDIPort(trackIndex, port);
|