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.
@@ -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 + Math.max.apply(undefined, mid.midiPorts) * 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
- // check for xg
58
- let system = "gs";
59
- mid.tracks.forEach((t, trackNum) => {
60
- const portOffset = mid.midiPorts[trackNum] * 16;
61
- for(const event of t)
62
- {
63
- const status = event.messageStatusByte & 0xF0;
64
- if(
65
- status !== messageTypes.noteOn &&
66
- status !== messageTypes.controllerChange &&
67
- status !== messageTypes.programChange &&
68
- status !== messageTypes.systemExclusive
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
- continue;
70
+ return;
72
71
  }
73
- const channel = (event.messageStatusByte & 0xF) + portOffset;
74
- let ch = channelPresets[channel];
75
- switch(status)
72
+ if(track[eventIndexes[i]].ticks < ticks)
76
73
  {
77
- case messageTypes.programChange:
78
- ch.program = event.messageData[0];
79
- updateString(ch);
80
- break;
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
- case messageTypes.controllerChange:
83
- if(event.messageData[0] !== midiControllers.bankSelect)
84
- {
85
- continue;
86
- }
87
- if(system === "gs" && ch.drums)
88
- {
89
- continue;
90
- }
91
- const bank = event.messageData[1];
92
- if(system === "xg")
93
- {
94
- const drumsBool = bank === 120 || bank === 126 || bank === 127;
95
- if(drumsBool !== ch.drums)
96
- {
97
- // drum change is a program change
98
- ch.drums = drumsBool;
99
- ch.bank = ch.drums ? 128 : bank;
100
- updateString(ch);
101
- }
102
- else
103
- {
104
- ch.bank = ch.drums ? 128 : bank;
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
- case messageTypes.noteOn:
113
- if(event.messageData[1] === 0)
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
- // that's a note off
116
- continue;
137
+ // drum change is a program change
138
+ ch.drums = drumsBool;
139
+ ch.bank = ch.drums ? 128 : bank;
140
+ updateString(ch);
117
141
  }
118
- if(!usedProgramsAndKeys[ch.string])
142
+ else
119
143
  {
120
- usedProgramsAndKeys[ch.string] = new Set();
144
+ ch.bank = ch.drums ? 128 : bank;
121
145
  }
122
- usedProgramsAndKeys[ch.string].add(`${event.messageData[0]}-${event.messageData[1]}`);
123
- break;
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
- case messageTypes.systemExclusive:
126
- // check for drum sysex
127
- if(
128
- event.messageData[0] !== 0x41 || // roland
129
- event.messageData[2] !== 0x42 || // GS
130
- event.messageData[3] !== 0x12 || // GS
131
- event.messageData[4] !== 0x40 || // system parameter
132
- (event.messageData[5] & 0x10 ) === 0 || // part parameter
133
- event.messageData[6] !== 0x15 // drum pars
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
- // check for XG
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
- const sysexChannel = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15][event.messageData[5] & 0x0F] + portOffset;
150
- const isDrum = !!(event.messageData[7] > 0 && event.messageData[5] >> 4);
151
- ch = channelPresets[sysexChannel];
152
- ch.drums = isDrum;
153
- ch.bank = isDrum ? 128 : 0;
154
- updateString(ch);
155
- break;
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.13",
4
- "description": "No compromise SoundFont and MIDI library and player",
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);