spessasynth_lib 3.12.2 → 3.13.0
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/used_keys_loaded.d.ts +1 -0
- package/@types/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.d.ts +5 -0
- package/@types/utils/buffer_to_wav.d.ts +9 -10
- package/midi_parser/rmidi_writer.js +215 -164
- package/midi_parser/used_keys_loaded.js +1 -0
- package/package.json +1 -1
- package/sequencer/worklet_sequencer/song_control.js +8 -3
- package/synthetizer/worklet_processor.min.js +6 -6
- package/synthetizer/worklet_system/main_processor.js +11 -2
- package/synthetizer/worklet_system/worklet_methods/note_on.js +14 -8
- package/synthetizer/worklet_system/worklet_methods/program_control.js +121 -26
- package/synthetizer/worklet_system/worklet_methods/reset_controllers.js +1 -0
- package/synthetizer/worklet_system/worklet_utilities/wavetable_oscillator.js +4 -0
- package/synthetizer/worklet_system/worklet_utilities/worklet_processor_channel.js +2 -0
- package/synthetizer/worklet_system/worklet_utilities/worklet_voice.js +6 -3
- package/utils/buffer_to_wav.js +58 -7
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* @property {Preset} preset - the channel's preset
|
|
17
17
|
* @property {boolean} lockPreset - indicates whether the program on the channel is locked
|
|
18
|
+
* @property {boolean} presetUsesOverride - indcates if the channel uses a preset from the override soundfont.
|
|
18
19
|
*
|
|
19
20
|
* @property {boolean} lockVibrato - indicates whether the custom vibrato is locked
|
|
20
21
|
* @property {Object} channelVibrato - vibrato settings for the channel
|
|
@@ -103,6 +104,10 @@ export type WorkletProcessorChannel = {
|
|
|
103
104
|
* - indicates whether the program on the channel is locked
|
|
104
105
|
*/
|
|
105
106
|
lockPreset: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* - indcates if the channel uses a preset from the override soundfont.
|
|
109
|
+
*/
|
|
110
|
+
presetUsesOverride: boolean;
|
|
106
111
|
/**
|
|
107
112
|
* - indicates whether the custom vibrato is locked
|
|
108
113
|
*/
|
|
@@ -1,28 +1,27 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {Object} WaveMetadata
|
|
3
|
-
* @property {string} title - the song's title
|
|
4
|
-
* @property {string} album - the song's album
|
|
5
|
-
* @property {string} genre - the song's genre
|
|
6
|
-
*/
|
|
7
1
|
/**
|
|
8
2
|
*
|
|
9
3
|
* @param audioBuffer {AudioBuffer}
|
|
10
4
|
* @param normalizeAudio {boolean} find the max sample point and set it to 1, and scale others with it
|
|
11
5
|
* @param channelOffset {number} channel offset and channel offset + 1 get saved
|
|
6
|
+
* @param metadata {WaveMetadata}
|
|
12
7
|
* @returns {Blob}
|
|
13
8
|
*/
|
|
14
|
-
export function audioBufferToWav(audioBuffer: AudioBuffer, normalizeAudio?: boolean, channelOffset?: number): Blob;
|
|
9
|
+
export function audioBufferToWav(audioBuffer: AudioBuffer, normalizeAudio?: boolean, channelOffset?: number, metadata?: WaveMetadata): Blob;
|
|
15
10
|
export type WaveMetadata = {
|
|
16
11
|
/**
|
|
17
12
|
* - the song's title
|
|
18
13
|
*/
|
|
19
|
-
title: string;
|
|
14
|
+
title: string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* - the song's artist
|
|
17
|
+
*/
|
|
18
|
+
artist: string | undefined;
|
|
20
19
|
/**
|
|
21
20
|
* - the song's album
|
|
22
21
|
*/
|
|
23
|
-
album: string;
|
|
22
|
+
album: string | undefined;
|
|
24
23
|
/**
|
|
25
24
|
* - the song's genre
|
|
26
25
|
*/
|
|
27
|
-
genre: string;
|
|
26
|
+
genre: string | undefined;
|
|
28
27
|
};
|
|
@@ -61,6 +61,7 @@ export function writeRMIDI(soundfontBinary, mid, soundfont, bankOffset = 0, enco
|
|
|
61
61
|
consoleColors.info,
|
|
62
62
|
consoleColors.value);
|
|
63
63
|
SpessaSynthInfo("metadata", metadata);
|
|
64
|
+
SpessaSynthInfo("Initial bank offset", mid.bankOffset);
|
|
64
65
|
// add offset to bank. See wiki About-RMIDI
|
|
65
66
|
// also fix presets that don't exists since midiplayer6 doesn't seem to default to 0 when nonextistent...
|
|
66
67
|
let system = "gm";
|
|
@@ -69,202 +70,252 @@ export function writeRMIDI(soundfontBinary, mid, soundfont, bankOffset = 0, enco
|
|
|
69
70
|
* @type {{tNum: number, e: MidiMessage}[]}
|
|
70
71
|
*/
|
|
71
72
|
let unwantedSystems = [];
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* @type {MidiMessage[]}
|
|
86
|
-
*/
|
|
87
|
-
let lastBankChanges = [];
|
|
88
|
-
/**
|
|
89
|
-
* @type {boolean[]}
|
|
90
|
-
*/
|
|
91
|
-
let drums = Array(16).fill(false);
|
|
92
|
-
drums[DEFAULT_PERCUSSION] = true;
|
|
93
|
-
let programs = Array(16).fill(0);
|
|
94
|
-
const portOffset = mid.midiPortChannelOffsets[mid.midiPorts[trackNum]];
|
|
95
|
-
t.forEach(e => {
|
|
96
|
-
const status = e.messageStatusByte & 0xF0;
|
|
97
|
-
if(
|
|
98
|
-
status !== messageTypes.controllerChange &&
|
|
99
|
-
status !== messageTypes.programChange &&
|
|
100
|
-
status !== messageTypes.systemExclusive
|
|
101
|
-
)
|
|
73
|
+
/**
|
|
74
|
+
* indexes for tracks
|
|
75
|
+
* @type {number[]}
|
|
76
|
+
*/
|
|
77
|
+
const eventIndexes = Array(mid.tracks.length).fill(0);
|
|
78
|
+
let remainingTracks = mid.tracks.length;
|
|
79
|
+
function findFirstEventIndex()
|
|
80
|
+
{
|
|
81
|
+
let index = 0;
|
|
82
|
+
let ticks = Infinity;
|
|
83
|
+
mid.tracks.forEach((track, i) => {
|
|
84
|
+
if(eventIndexes[i] >= track.length)
|
|
102
85
|
{
|
|
103
86
|
return;
|
|
104
87
|
}
|
|
105
|
-
|
|
106
|
-
if(status === messageTypes.systemExclusive)
|
|
88
|
+
if(track[eventIndexes[i]].ticks < ticks)
|
|
107
89
|
{
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
e.messageData[0] !== 0x41 || // roland
|
|
111
|
-
e.messageData[2] !== 0x42 || // GS
|
|
112
|
-
e.messageData[3] !== 0x12 || // GS
|
|
113
|
-
e.messageData[4] !== 0x40 || // system parameter
|
|
114
|
-
(e.messageData[5] & 0x10 ) === 0 || // part parameter
|
|
115
|
-
e.messageData[6] !== 0x15 // drum part
|
|
116
|
-
)
|
|
117
|
-
{
|
|
118
|
-
// check for XG
|
|
119
|
-
if(
|
|
120
|
-
e.messageData[0] === 0x43 && // yamaha
|
|
121
|
-
e.messageData[2] === 0x4C && // sXG ON
|
|
122
|
-
e.messageData[5] === 0x7E &&
|
|
123
|
-
e.messageData[6] === 0x00
|
|
124
|
-
)
|
|
125
|
-
{
|
|
126
|
-
system = "xg";
|
|
127
|
-
}
|
|
128
|
-
else
|
|
129
|
-
if(
|
|
130
|
-
e.messageData[0] === 0x41 // roland
|
|
131
|
-
&& e.messageData[2] === 0x42 // GS
|
|
132
|
-
&& e.messageData[6] === 0x7F // Mode set
|
|
133
|
-
)
|
|
134
|
-
{
|
|
135
|
-
system = "gs";
|
|
136
|
-
}
|
|
137
|
-
else
|
|
138
|
-
if(
|
|
139
|
-
e.messageData[0] === 0x7E // non realtime
|
|
140
|
-
&& e.messageData[2] === 0x09 // gm system
|
|
141
|
-
)
|
|
142
|
-
{
|
|
143
|
-
system = "gm";
|
|
144
|
-
unwantedSystems.push({
|
|
145
|
-
tNum: trackNum,
|
|
146
|
-
e: e
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
const sysexChannel = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15][e.messageData[5] & 0x0F];
|
|
152
|
-
drums[sysexChannel] = !!(e.messageData[7] > 0 && e.messageData[5] >> 4);
|
|
153
|
-
return;
|
|
90
|
+
index = i;
|
|
91
|
+
ticks = track[eventIndexes[i]].ticks;
|
|
154
92
|
}
|
|
93
|
+
});
|
|
94
|
+
return index;
|
|
95
|
+
}
|
|
96
|
+
// it copies midiPorts everywhere else, but here 0 works so DO NOT CHANGE!!!!!!!
|
|
97
|
+
const ports = Array(mid.tracks.length).fill(0);
|
|
98
|
+
const channelsAmount = 16 + mid.midiPortChannelOffsets.reduce((max, cur) => cur > max ? cur: max);
|
|
99
|
+
/**
|
|
100
|
+
* @type {{
|
|
101
|
+
* program: number,
|
|
102
|
+
* drums: boolean,
|
|
103
|
+
* lastBank: MidiMessage,
|
|
104
|
+
* hasBankSelect: boolean
|
|
105
|
+
* }[]}
|
|
106
|
+
*/
|
|
107
|
+
const channelsInfo = [];
|
|
108
|
+
for (let i = 0; i < channelsAmount; i++)
|
|
109
|
+
{
|
|
110
|
+
channelsInfo.push({
|
|
111
|
+
program: 0,
|
|
112
|
+
drums: i % 16 === DEFAULT_PERCUSSION, // drums appear on 9 every 16 channels,
|
|
113
|
+
lastBank: undefined,
|
|
114
|
+
hasBankSelect: false,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
while(remainingTracks > 0)
|
|
118
|
+
{
|
|
119
|
+
let trackNum = findFirstEventIndex();
|
|
120
|
+
const track = mid.tracks[trackNum];
|
|
121
|
+
if(eventIndexes[trackNum] >= track.length)
|
|
122
|
+
{
|
|
123
|
+
remainingTracks--;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const e = track[eventIndexes[trackNum]];
|
|
127
|
+
eventIndexes[trackNum]++;
|
|
128
|
+
|
|
129
|
+
let portOffset = mid.midiPortChannelOffsets[ports[trackNum]];
|
|
130
|
+
if(e.messageStatusByte === messageTypes.midiPort)
|
|
131
|
+
{
|
|
132
|
+
ports[trackNum] = e.messageData[0];
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const status = e.messageStatusByte & 0xF0;
|
|
136
|
+
if(
|
|
137
|
+
status !== messageTypes.controllerChange &&
|
|
138
|
+
status !== messageTypes.programChange &&
|
|
139
|
+
status !== messageTypes.systemExclusive
|
|
140
|
+
)
|
|
141
|
+
{
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
155
144
|
|
|
156
|
-
|
|
157
|
-
|
|
145
|
+
if(status === messageTypes.systemExclusive)
|
|
146
|
+
{
|
|
147
|
+
// check for drum sysex
|
|
148
|
+
if(
|
|
149
|
+
e.messageData[0] !== 0x41 || // roland
|
|
150
|
+
e.messageData[2] !== 0x42 || // GS
|
|
151
|
+
e.messageData[3] !== 0x12 || // GS
|
|
152
|
+
e.messageData[4] !== 0x40 || // system parameter
|
|
153
|
+
(e.messageData[5] & 0x10 ) === 0 || // part parameter
|
|
154
|
+
e.messageData[6] !== 0x15 // drum part
|
|
155
|
+
)
|
|
158
156
|
{
|
|
159
|
-
// check
|
|
160
|
-
if(
|
|
157
|
+
// check for XG
|
|
158
|
+
if(
|
|
159
|
+
e.messageData[0] === 0x43 && // yamaha
|
|
160
|
+
e.messageData[2] === 0x4C && // sXG ON
|
|
161
|
+
e.messageData[5] === 0x7E &&
|
|
162
|
+
e.messageData[6] === 0x00
|
|
163
|
+
)
|
|
161
164
|
{
|
|
162
|
-
|
|
163
|
-
{
|
|
164
|
-
// doesn't exist. pick any preset that has the 128 bank.
|
|
165
|
-
e.messageData[0] = soundfont.presets.find(p => p.bank === 128)?.program || 0;
|
|
166
|
-
}
|
|
165
|
+
system = "xg";
|
|
167
166
|
}
|
|
168
167
|
else
|
|
168
|
+
if(
|
|
169
|
+
e.messageData[0] === 0x41 // roland
|
|
170
|
+
&& e.messageData[2] === 0x42 // GS
|
|
171
|
+
&& e.messageData[6] === 0x7F // Mode set
|
|
172
|
+
)
|
|
169
173
|
{
|
|
170
|
-
|
|
171
|
-
{
|
|
172
|
-
// doesn't exist. pick any preset that does not have the 128 bank.
|
|
173
|
-
e.messageData[0] = soundfont.presets.find(p => p.bank !== 128)?.program || 0;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
programs[e.messageStatusByte & 0xf] = e.messageData[0];
|
|
177
|
-
// check if this preset exists for program and bank
|
|
178
|
-
const realBank = lastBankChanges[chNum]?.messageData[1] - mid.bankOffset; // make sure to take the previous bank offset into account
|
|
179
|
-
const bank = drums[chNum] ? 128 : realBank;
|
|
180
|
-
if(lastBankChanges[chNum] === undefined)
|
|
181
|
-
{
|
|
182
|
-
return;
|
|
174
|
+
system = "gs";
|
|
183
175
|
}
|
|
184
|
-
|
|
176
|
+
else
|
|
177
|
+
if(
|
|
178
|
+
e.messageData[0] === 0x7E // non realtime
|
|
179
|
+
&& e.messageData[2] === 0x09 // gm system
|
|
180
|
+
)
|
|
185
181
|
{
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
182
|
+
system = "gm";
|
|
183
|
+
unwantedSystems.push({
|
|
184
|
+
tNum: trackNum,
|
|
185
|
+
e: e
|
|
186
|
+
});
|
|
189
187
|
}
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
const sysexChannel = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15][e.messageData[5] & 0x0F] + portOffset;
|
|
191
|
+
channelsInfo[sysexChannel].drums = !!(e.messageData[7] > 0 && e.messageData[5] >> 4);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
190
194
|
|
|
191
|
-
|
|
195
|
+
// program change
|
|
196
|
+
const chNum = (e.messageStatusByte & 0xF) + portOffset;
|
|
197
|
+
const channel = channelsInfo[chNum];
|
|
198
|
+
if(status === messageTypes.programChange)
|
|
199
|
+
{
|
|
200
|
+
// check if the preset for this program exists
|
|
201
|
+
if(channel.drums)
|
|
202
|
+
{
|
|
203
|
+
if(soundfont.presets.findIndex(p => p.program === e.messageData[0] && p.bank === 128) === -1)
|
|
192
204
|
{
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
lastBankChanges[chNum].messageData[1] = targetPreset;
|
|
196
|
-
SpessaSynthInfo(`%cNo preset %c${bank}:${e.messageData[0]}%c. Changing bank to ${targetPreset}.`,
|
|
197
|
-
consoleColors.info,
|
|
198
|
-
consoleColors.recognized,
|
|
199
|
-
consoleColors.info);
|
|
205
|
+
// doesn't exist. pick any preset that has the 128 bank.
|
|
206
|
+
e.messageData[0] = soundfont.presets.find(p => p.bank === 128)?.program || 0;
|
|
200
207
|
}
|
|
201
|
-
|
|
208
|
+
}
|
|
209
|
+
else
|
|
210
|
+
{
|
|
211
|
+
if (soundfont.presets.findIndex(p => p.program === e.messageData[0] && p.bank !== 128) === -1)
|
|
202
212
|
{
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
SpessaSynthInfo(`%cPreset %c${bank}:${e.messageData[0]}%c exists. Changing bank to ${lastBankChanges[chNum].messageData[1]}.`,
|
|
206
|
-
consoleColors.info,
|
|
207
|
-
consoleColors.recognized,
|
|
208
|
-
consoleColors.info);
|
|
213
|
+
// doesn't exist. pick any preset that does not have the 128 bank.
|
|
214
|
+
e.messageData[0] = soundfont.presets.find(p => p.bank !== 128)?.program || 0;
|
|
209
215
|
}
|
|
210
|
-
return;
|
|
211
216
|
}
|
|
212
|
-
|
|
217
|
+
channel.program = e.messageData[0];
|
|
218
|
+
// check if this preset exists for program and bank
|
|
219
|
+
const realBank = Math.max(0,channel.lastBank?.messageData[1] - mid.bankOffset); // make sure to take the previous bank offset into account
|
|
220
|
+
const bank = channel.drums ? 128 : realBank;
|
|
221
|
+
if(channel.lastBank === undefined)
|
|
213
222
|
{
|
|
214
|
-
|
|
223
|
+
continue;
|
|
215
224
|
}
|
|
216
|
-
|
|
217
|
-
hasBankSelects[chNum] = true;
|
|
218
|
-
channelHasBankSelects[chNum + portOffset] = true;
|
|
219
|
-
if(system === "xg")
|
|
225
|
+
if(system === "xg" && channel.drums)
|
|
220
226
|
{
|
|
221
|
-
//
|
|
222
|
-
|
|
227
|
+
// drums override: set bank to 127
|
|
228
|
+
channelsInfo[chNum].lastBank.messageData[1] = 127;
|
|
223
229
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// add all bank selects that are missing for this track
|
|
227
|
-
hasBankSelects.forEach((has, ch) => {
|
|
228
|
-
if(has === true)
|
|
230
|
+
|
|
231
|
+
if(soundfont.presets.findIndex(p => p.bank === bank && p.program === e.messageData[0]) === -1)
|
|
229
232
|
{
|
|
230
|
-
|
|
233
|
+
// no preset with this bank. find this program with any bank
|
|
234
|
+
const targetBank = (soundfont.presets.find(p => p.program === e.messageData[0])?.bank + bankOffset) || bankOffset;
|
|
235
|
+
channel.lastBank.messageData[1] = targetBank;
|
|
236
|
+
SpessaSynthInfo(`%cNo preset %c${bank}:${e.messageData[0]}%c. Changing bank to ${targetBank}.`,
|
|
237
|
+
consoleColors.info,
|
|
238
|
+
consoleColors.recognized,
|
|
239
|
+
consoleColors.info);
|
|
231
240
|
}
|
|
232
|
-
|
|
233
|
-
if(channelHasBankSelects[ch + portOffset] === true)
|
|
241
|
+
else
|
|
234
242
|
{
|
|
235
|
-
|
|
243
|
+
// there is a preset with this bank. add offset. For drums add the normal offset.
|
|
244
|
+
const newBank = (bank === 128 ? 0 : realBank) + bankOffset;
|
|
245
|
+
channel.lastBank.messageData[1] = newBank;
|
|
246
|
+
SpessaSynthInfo(`%cPreset %c${bank}:${e.messageData[0]}%c exists. Changing bank to ${newBank}.`,
|
|
247
|
+
consoleColors.info,
|
|
248
|
+
consoleColors.recognized,
|
|
249
|
+
consoleColors.info);
|
|
236
250
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
// we only care about bank select
|
|
254
|
+
if(e.messageData[0] !== midiControllers.bankSelect)
|
|
255
|
+
{
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
// bank select
|
|
259
|
+
channel.hasBankSelect = true;
|
|
260
|
+
if(system === "xg")
|
|
261
|
+
{
|
|
262
|
+
// check for xg drums
|
|
263
|
+
channel.drums = e.messageData[1] === 120 || e.messageData[1] === 126 || e.messageData[1] === 127;
|
|
264
|
+
}
|
|
265
|
+
channel.lastBank = e;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// add missing bank selects
|
|
269
|
+
// add all bank selects that are missing for this track
|
|
270
|
+
channelsInfo.forEach((has, ch) => {
|
|
271
|
+
if(has.hasBankSelect === true)
|
|
272
|
+
{
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
// find first program change (for the given channel)
|
|
276
|
+
const midiChannel = ch % 16;
|
|
277
|
+
const status = messageTypes.programChange | midiChannel;
|
|
278
|
+
// find track with this channel being used
|
|
279
|
+
const portOffset = Math.floor(ch / 16) * 16;
|
|
280
|
+
const port = mid.midiPortChannelOffsets.indexOf(portOffset);
|
|
281
|
+
const track = mid.tracks.find((t, tNum) => mid.midiPorts[tNum] === port && mid.usedChannelsOnTrack[tNum].has(midiChannel));
|
|
282
|
+
if(track === undefined)
|
|
283
|
+
{
|
|
284
|
+
// this channel is not used at all
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
let indexToAdd = track.findIndex(e => e.messageStatusByte === status);
|
|
288
|
+
if(indexToAdd === -1)
|
|
289
|
+
{
|
|
290
|
+
// no program change...
|
|
291
|
+
// add programs if they are missing from the track (need them to activate bank 1 for the embedded sfont)
|
|
292
|
+
const programIndex = track.findIndex(e => (e.messageStatusByte > 0x80 && e.messageStatusByte < 0xF0) && (e.messageStatusByte & 0xF) === midiChannel);
|
|
293
|
+
if(programIndex === -1)
|
|
241
294
|
{
|
|
242
|
-
// no
|
|
243
|
-
|
|
244
|
-
const programIndex = t.findIndex(e => (e.messageStatusByte > 0x80 && e.messageStatusByte < 0xF0) && (e.messageStatusByte & 0xF) === ch);
|
|
245
|
-
if(programIndex === -1)
|
|
246
|
-
{
|
|
247
|
-
// no voices??? skip
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
const programTicks = t[programIndex].ticks;
|
|
251
|
-
const targetProgram = soundfont.getPreset(0, 0).program;
|
|
252
|
-
t.splice(programIndex, 0, new MidiMessage(
|
|
253
|
-
programTicks,
|
|
254
|
-
messageTypes.programChange | ch,
|
|
255
|
-
new IndexedByteArray([targetProgram])
|
|
256
|
-
));
|
|
257
|
-
indexToAdd = programIndex;
|
|
295
|
+
// no voices??? skip
|
|
296
|
+
return;
|
|
258
297
|
}
|
|
259
|
-
const
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
messageTypes.
|
|
264
|
-
new IndexedByteArray([
|
|
298
|
+
const programTicks = track[programIndex].ticks;
|
|
299
|
+
const targetProgram = soundfont.getPreset(0, 0).program;
|
|
300
|
+
track.splice(programIndex, 0, new MidiMessage(
|
|
301
|
+
programTicks,
|
|
302
|
+
messageTypes.programChange | midiChannel,
|
|
303
|
+
new IndexedByteArray([targetProgram])
|
|
265
304
|
));
|
|
266
|
-
|
|
305
|
+
indexToAdd = programIndex;
|
|
306
|
+
}
|
|
307
|
+
SpessaSynthInfo(`%cAdding bank select for %c${ch}`,
|
|
308
|
+
consoleColors.info,
|
|
309
|
+
consoleColors.recognized)
|
|
310
|
+
const ticks = track[indexToAdd].ticks;
|
|
311
|
+
const targetBank = (soundfont.getPreset(0, has.program)?.bank + bankOffset) || bankOffset;
|
|
312
|
+
track.splice(indexToAdd,0, new MidiMessage(
|
|
313
|
+
ticks,
|
|
314
|
+
messageTypes.controllerChange | midiChannel,
|
|
315
|
+
new IndexedByteArray([midiControllers.bankSelect, targetBank])
|
|
316
|
+
));
|
|
267
317
|
});
|
|
318
|
+
|
|
268
319
|
// make sure to put xg if gm
|
|
269
320
|
if(system !== "gs" && system !== "xg")
|
|
270
321
|
{
|
package/package.json
CHANGED
|
@@ -57,14 +57,19 @@ export function loadNewSequence(parsedMidi)
|
|
|
57
57
|
if(this.midiData.embeddedSoundFont !== undefined)
|
|
58
58
|
{
|
|
59
59
|
SpessaSynthInfo("%cEmbedded soundfont detected! Using it.", consoleColors.recognized);
|
|
60
|
-
this.synth.reloadSoundFont(this.midiData.embeddedSoundFont);
|
|
61
60
|
// set offet
|
|
62
61
|
this.synth.soundfontBankOffset = this.midiData.bankOffset;
|
|
62
|
+
this.synth.reloadSoundFont(this.midiData.embeddedSoundFont, true);
|
|
63
63
|
// preload all samples
|
|
64
|
-
this.synth.
|
|
64
|
+
this.synth.overrideSoundfont.samples.forEach(s => s.getAudioData());
|
|
65
65
|
}
|
|
66
66
|
else
|
|
67
67
|
{
|
|
68
|
+
if(this.synth.overrideSoundfont)
|
|
69
|
+
{
|
|
70
|
+
// clean up the emdeeded soundfont
|
|
71
|
+
this.synth.clearSoundFont();
|
|
72
|
+
}
|
|
68
73
|
SpessaSynthGroupCollapsed("%cPreloading samples...", consoleColors.info);
|
|
69
74
|
// smart preloading: load only samples used in the midi!
|
|
70
75
|
const used = getUsedProgramsAndKeys(this.midiData, this.synth.soundfont);
|
|
@@ -72,7 +77,7 @@ export function loadNewSequence(parsedMidi)
|
|
|
72
77
|
{
|
|
73
78
|
const bank = parseInt(programBank.split(":")[0]);
|
|
74
79
|
const program = parseInt(programBank.split(":")[1]);
|
|
75
|
-
const preset = this.synth.
|
|
80
|
+
const preset = this.synth.getPreset(bank, program);
|
|
76
81
|
SpessaSynthInfo(`%cPreloading used samples on %c${preset.presetName}%c...`,
|
|
77
82
|
consoleColors.info,
|
|
78
83
|
consoleColors.recognized,
|