spessasynth_lib 3.24.28 → 3.24.35

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.
Files changed (74) hide show
  1. package/README.md +1 -1
  2. package/external_midi/web_midi_link.js +1 -1
  3. package/index.js +8 -52
  4. package/midi_parser/basic_midi.js +15 -30
  5. package/midi_parser/midi_builder.js +6 -6
  6. package/midi_parser/midi_data.js +2 -2
  7. package/midi_parser/midi_editor.js +15 -15
  8. package/midi_parser/midi_loader.js +3 -3
  9. package/midi_parser/midi_message.js +2 -2
  10. package/midi_parser/midi_sequence.js +32 -4
  11. package/midi_parser/midi_writer.js +8 -8
  12. package/midi_parser/rmidi_writer.js +20 -21
  13. package/midi_parser/used_keys_loaded.js +8 -6
  14. package/package.json +1 -1
  15. package/sequencer/sequencer.js +6 -6
  16. package/sequencer/worklet_sequencer/events.js +2 -1
  17. package/sequencer/worklet_sequencer/play.js +2 -3
  18. package/sequencer/worklet_sequencer/process_event.js +1 -1
  19. package/sequencer/worklet_sequencer/process_tick.js +1 -1
  20. package/sequencer/worklet_sequencer/sequencer_message.js +1 -1
  21. package/sequencer/worklet_sequencer/song_control.js +6 -7
  22. package/sequencer/worklet_sequencer/worklet_sequencer.js +2 -1
  23. package/soundfont/basic_soundfont/basic_soundfont.js +197 -13
  24. package/soundfont/basic_soundfont/generator.js +18 -16
  25. package/soundfont/basic_soundfont/modulator.js +3 -0
  26. package/soundfont/basic_soundfont/riff_chunk.js +2 -2
  27. package/soundfont/basic_soundfont/write_dls/art2.js +1 -1
  28. package/soundfont/basic_soundfont/write_dls/combine_zones.js +12 -10
  29. package/soundfont/basic_soundfont/write_dls/ins.js +2 -2
  30. package/soundfont/basic_soundfont/write_dls/lins.js +1 -1
  31. package/soundfont/basic_soundfont/write_dls/modulator_converter.js +9 -9
  32. package/soundfont/basic_soundfont/write_dls/rgn2.js +5 -5
  33. package/soundfont/basic_soundfont/write_dls/wave.js +2 -1
  34. package/soundfont/basic_soundfont/write_dls/write_dls.js +1 -1
  35. package/soundfont/basic_soundfont/write_dls/wsmp.js +2 -2
  36. package/soundfont/basic_soundfont/write_dls/wvpl.js +1 -1
  37. package/soundfont/basic_soundfont/write_sf2/ibag.js +2 -2
  38. package/soundfont/basic_soundfont/write_sf2/igen.js +20 -17
  39. package/soundfont/basic_soundfont/write_sf2/imod.js +3 -3
  40. package/soundfont/basic_soundfont/write_sf2/inst.js +2 -2
  41. package/soundfont/basic_soundfont/write_sf2/pbag.js +2 -2
  42. package/soundfont/basic_soundfont/write_sf2/pgen.js +21 -18
  43. package/soundfont/basic_soundfont/write_sf2/phdr.js +2 -2
  44. package/soundfont/basic_soundfont/write_sf2/pmod.js +2 -2
  45. package/soundfont/basic_soundfont/write_sf2/sdta.js +1 -1
  46. package/soundfont/basic_soundfont/write_sf2/shdr.js +1 -1
  47. package/soundfont/basic_soundfont/write_sf2/write.js +7 -6
  48. package/soundfont/dls/articulator_converter.js +1 -0
  49. package/soundfont/dls/dls_destinations.js +3 -3
  50. package/soundfont/dls/dls_preset.js +1 -1
  51. package/soundfont/dls/dls_soundfont.js +7 -7
  52. package/soundfont/dls/read_instrument.js +1 -1
  53. package/soundfont/dls/read_region.js +1 -0
  54. package/soundfont/dls/read_samples.js +4 -5
  55. package/soundfont/load_soundfont.js +1 -1
  56. package/soundfont/read_sf2/presets.js +1 -1
  57. package/soundfont/read_sf2/soundfont.js +5 -5
  58. package/synthetizer/audio_effects/effects_config.js +3 -3
  59. package/synthetizer/audio_effects/fancy_chorus.js +7 -7
  60. package/synthetizer/audio_effects/reverb.js +5 -5
  61. package/synthetizer/audio_effects/reverb_as_binary.js +15 -0
  62. package/synthetizer/audio_effects/reverb_buffer.min.js +1 -0
  63. package/synthetizer/synth_constants.js +14 -0
  64. package/synthetizer/synthetizer.js +1 -17
  65. package/synthetizer/worklet_processor.min.js +14 -12
  66. package/synthetizer/worklet_system/main_processor.js +2 -2
  67. package/synthetizer/worklet_system/worklet_methods/controller_control/controller_change.js +1 -1
  68. package/synthetizer/worklet_system/worklet_methods/controller_control/reset_controllers.js +1 -1
  69. package/synthetizer/worklet_system/worklet_methods/create_worklet_channel.js +2 -1
  70. package/synthetizer/worklet_system/worklet_methods/worklet_soundfont_manager/worklet_soundfont_manager.js +2 -2
  71. package/synthetizer/worklet_system/worklet_processor.js +1 -1
  72. package/soundfont/basic_soundfont/write_sf2/soundfont_trimmer.js +0 -185
  73. package/synthetizer/audio_effects/impulse_response_2.flac +0 -0
  74. package/synthetizer/worklet_system/minify_processor.sh +0 -4
@@ -1,20 +1,22 @@
1
1
  import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../utils/loggin.js";
2
2
  import { consoleColors } from "../utils/other.js";
3
- import { DEFAULT_PERCUSSION } from "../synthetizer/synthetizer.js";
4
3
  import { messageTypes, midiControllers } from "./midi_message.js";
4
+ import { DEFAULT_PERCUSSION } from "../synthetizer/synth_constants.js";
5
5
 
6
6
  /**
7
- * @param mid {BasicMIDI}
8
- * @param soundfont {{getPreset: function(number, number): BasicPreset}}
7
+ * Gets the used programs and keys for this MIDI file with a given sound bank
8
+ * @this {BasicMIDI}
9
+ * @param soundfont {{getPreset: function(number, number): BasicPreset}} - the sound bank
9
10
  * @returns {Object<string, Set<string>>}
10
11
  */
11
- export function getUsedProgramsAndKeys(mid, soundfont)
12
+ export function getUsedProgramsAndKeys(soundfont)
12
13
  {
14
+ const mid = this;
13
15
  SpessaSynthGroupCollapsed(
14
16
  "%cSearching for all used programs and keys...",
15
17
  consoleColors.info
16
18
  );
17
- // find every bank:program combo and every key:velocity for each. Make sure to care about ports and drums
19
+ // Find every bank:program combo and every key:velocity for each. Make sure to care about ports and drums
18
20
  const channelsAmount = 16 + mid.midiPortChannelOffsets.reduce((max, cur) => cur > max ? cur : max);
19
21
  /**
20
22
  * @type {{program: number, bank: number, drums: boolean, string: string}[]}
@@ -152,7 +154,7 @@ export function getUsedProgramsAndKeys(mid, soundfont)
152
154
  continue;
153
155
  }
154
156
  channelPresets[channel].bank = realBank;
155
- // do not update the data, bank change doesnt change the preset
157
+ // do not update the data, bank change doesn't change the preset
156
158
  break;
157
159
 
158
160
  case messageTypes.noteOn:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spessasynth_lib",
3
- "version": "3.24.28",
3
+ "version": "3.24.35",
4
4
  "description": "MIDI and SoundFont2/DLS library with no compromises",
5
5
  "browser": "index.js",
6
6
  "type": "module",
@@ -8,7 +8,7 @@ import {
8
8
  WorkletSequencerReturnMessageType
9
9
  } from "./worklet_sequencer/sequencer_message.js";
10
10
  import { SpessaSynthWarn } from "../utils/loggin.js";
11
- import { DUMMY_MIDI_DATA, MidiData } from "../midi_parser/midi_data.js";
11
+ import { DUMMY_MIDI_DATA, MIDIData } from "../midi_parser/midi_data.js";
12
12
  import { BasicMIDI } from "../midi_parser/basic_midi.js";
13
13
  import { readBytesAsUintBigEndian } from "../utils/byte_functions/big_endian.js";
14
14
  import { IndexedByteArray } from "../utils/indexed_array.js";
@@ -68,12 +68,12 @@ export class Sequencer
68
68
 
69
69
  /**
70
70
  * The sequence's data, except for the track data.
71
- * @type {MidiData}
71
+ * @type {MIDIData}
72
72
  */
73
73
  midiData;
74
74
 
75
75
  /**
76
- * @type {Object<string, function(MidiData)>}
76
+ * @type {Object<string, function(MIDIData)>}
77
77
  * @private
78
78
  */
79
79
  onSongChange = {};
@@ -398,7 +398,7 @@ export class Sequencer
398
398
 
399
399
  /**
400
400
  * Adds a new event that gets called when the song changes
401
- * @param callback {function(MidiData)}
401
+ * @param callback {function(MIDIData)}
402
402
  * @param id {string} must be unique
403
403
  */
404
404
  addOnSongChangeEvent(callback, id)
@@ -538,8 +538,8 @@ export class Sequencer
538
538
 
539
539
  case WorkletSequencerReturnMessageType.songChange:
540
540
  /**
541
- * messageData is expected to be {MidiData}
542
- * @type {MidiData}
541
+ * messageData is expected to be {MIDIData}
542
+ * @type {MIDIData}
543
543
  */
544
544
  let songChangeData = messageData[0];
545
545
  this.songIndex = messageData[1];
@@ -4,7 +4,8 @@ import {
4
4
  } from "../../synthetizer/worklet_system/message_protocol/worklet_message.js";
5
5
  import { SongChangeType, WorkletSequencerMessageType, WorkletSequencerReturnMessageType } from "./sequencer_message.js";
6
6
  import { messageTypes, midiControllers } from "../../midi_parser/midi_message.js";
7
- import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synthetizer.js";
7
+
8
+ import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synth_constants.js";
8
9
 
9
10
  /**
10
11
  * @param messageType {WorkletSequencerMessageType}
@@ -1,6 +1,5 @@
1
1
  import { getEvent, messageTypes, midiControllers } from "../../midi_parser/midi_message.js";
2
2
  import { WorkletSequencerReturnMessageType } from "./sequencer_message.js";
3
- import { MIDIticksToSeconds } from "../../midi_parser/basic_midi.js";
4
3
  import { resetArray } from "../../synthetizer/worklet_system/worklet_utilities/controller_tables.js";
5
4
  import {
6
5
  nonResetableCCs
@@ -158,7 +157,7 @@ export function _playTo(time, ticks = undefined)
158
157
  }
159
158
  else if (controllerNumber === midiControllers.resetAllControllers)
160
159
  {
161
- resetAllControlllers(savedControllers[channel]);
160
+ resetAllControlllers(channel);
162
161
  }
163
162
  if (this.sendMIDIMessages)
164
163
  {
@@ -324,7 +323,7 @@ export function setTimeTicks(ticks)
324
323
  this.pausedTime = undefined;
325
324
  this.post(
326
325
  WorkletSequencerReturnMessageType.timeChange,
327
- currentTime - MIDIticksToSeconds(ticks, this.midiData)
326
+ currentTime - this.midiData.MIDIticksToSeconds(ticks)
328
327
  );
329
328
  const isNotFinished = this._playTo(0, ticks);
330
329
  this._recalculateStartTime(this.playedTime);
@@ -6,7 +6,7 @@ import { readBytesAsUintBigEndian } from "../../utils/byte_functions/big_endian.
6
6
 
7
7
  /**
8
8
  * Processes a single event
9
- * @param event {MidiMessage}
9
+ * @param event {MIDIMessage}
10
10
  * @param trackIndex {number}
11
11
  * @this {WorkletSequencer}
12
12
  * @private
@@ -55,7 +55,7 @@ export function processTick()
55
55
  this.setTimeTicks(this.midiData.loop.start);
56
56
  return;
57
57
  }
58
- // if the song has endeed
58
+ // if the song has ended
59
59
  else if (current >= this.duration)
60
60
  {
61
61
  if (canLoop)
@@ -41,7 +41,7 @@ export const WorkletSequencerMessageType = {
41
41
  */
42
42
  export const WorkletSequencerReturnMessageType = {
43
43
  midiEvent: 0, // [...midiEventBytes<number>]
44
- songChange: 1, // [midiData<MidiData>, songIndex<number>, isAutoPlayed<boolean>]
44
+ songChange: 1, // [midiData<MIDIData>, songIndex<number>, isAutoPlayed<boolean>]
45
45
  textEvent: 2, // [messageData<number[]>, statusByte<number>, lyricsIndex<number>]
46
46
  timeChange: 3, // newAbsoluteTime<number>
47
47
  pause: 4, // no data
@@ -6,10 +6,9 @@ import {
6
6
  SpessaSynthInfo,
7
7
  SpessaSynthWarn
8
8
  } from "../../utils/loggin.js";
9
- import { MidiData } from "../../midi_parser/midi_data.js";
9
+ import { MIDIData } from "../../midi_parser/midi_data.js";
10
10
  import { MIDI } from "../../midi_parser/midi_loader.js";
11
- import { getUsedProgramsAndKeys } from "../../midi_parser/used_keys_loaded.js";
12
- import { MIDIticksToSeconds } from "../../midi_parser/basic_midi.js";
11
+
13
12
 
14
13
  /**
15
14
  * @param trackNum {number}
@@ -80,7 +79,7 @@ export function loadNewSequence(parsedMidi, autoPlay = true)
80
79
  }
81
80
  SpessaSynthGroupCollapsed("%cPreloading samples...", consoleColors.info);
82
81
  // smart preloading: load only samples used in the midi!
83
- const used = getUsedProgramsAndKeys(this.midiData, this.synth.soundfontManager);
82
+ const used = this.midiData.getUsedProgramsAndKeys(this.synth.soundfontManager);
84
83
  for (const [programBank, combos] of Object.entries(used))
85
84
  {
86
85
  const bank = parseInt(programBank.split(":")[0]);
@@ -103,7 +102,7 @@ export function loadNewSequence(parsedMidi, autoPlay = true)
103
102
 
104
103
  /**
105
104
  * the midi track data
106
- * @type {MidiMessage[][]}
105
+ * @type {MIDIMessage[][]}
107
106
  */
108
107
  this.tracks = this.midiData.tracks;
109
108
 
@@ -124,10 +123,10 @@ export function loadNewSequence(parsedMidi, autoPlay = true)
124
123
  * @type {number}
125
124
  */
126
125
  this.duration = this.midiData.duration;
127
- this.firstNoteTime = MIDIticksToSeconds(this.midiData.firstNoteOn, this.midiData);
126
+ this.firstNoteTime = this.midiData.MIDIticksToSeconds(this.midiData.firstNoteOn);
128
127
  SpessaSynthInfo(`%cTotal song time: ${formatTime(Math.ceil(this.duration)).time}`, consoleColors.recognized);
129
128
 
130
- this.post(WorkletSequencerReturnMessageType.songChange, [new MidiData(this.midiData), this.songIndex, autoPlay]);
129
+ this.post(WorkletSequencerReturnMessageType.songChange, [new MIDIData(this.midiData), this.songIndex, autoPlay]);
131
130
 
132
131
  if (this.duration <= 1)
133
132
  {
@@ -14,7 +14,8 @@ import {
14
14
  sendMIDIReset
15
15
  } from "./events.js";
16
16
  import { SpessaSynthWarn } from "../../utils/loggin.js";
17
- import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synthetizer.js";
17
+
18
+ import { MIDI_CHANNEL_COUNT } from "../../synthetizer/synth_constants.js";
18
19
 
19
20
  class WorkletSequencer
20
21
  {
@@ -1,4 +1,10 @@
1
- import { SpessaSynthWarn } from "../../utils/loggin.js";
1
+ import {
2
+ SpessaSynthGroup,
3
+ SpessaSynthGroupCollapsed,
4
+ SpessaSynthGroupEnd,
5
+ SpessaSynthInfo,
6
+ SpessaSynthWarn
7
+ } from "../../utils/loggin.js";
2
8
  import { consoleColors } from "../../utils/other.js";
3
9
  import { write } from "./write_sf2/write.js";
4
10
  import { defaultModulators, Modulator } from "./modulator.js";
@@ -9,7 +15,7 @@ import { Generator, generatorTypes } from "./generator.js";
9
15
  import { BasicInstrument } from "./basic_instrument.js";
10
16
  import { BasicPreset } from "./basic_preset.js";
11
17
 
12
- class BasicSoundFont
18
+ class BasicSoundBank
13
19
  {
14
20
  /**
15
21
  * Creates a new basic soundfont template
@@ -18,7 +24,7 @@ class BasicSoundFont
18
24
  constructor(data = undefined)
19
25
  {
20
26
  /**
21
- * Soundfont's info stored as name: value. ifil and iver are stored as string representation of float (e.g. 2.1)
27
+ * Soundfont's info stored as name: value. ifil and iver are stored as string representation of float (e.g., 2.1)
22
28
  * @type {Object<string, string|IndexedByteArray>}
23
29
  */
24
30
  this.soundFontInfo = {};
@@ -56,10 +62,10 @@ class BasicSoundFont
56
62
 
57
63
  /**
58
64
  * Merges soundfonts with the given order. Keep in mind that the info read is copied from the first one
59
- * @param soundfonts {...BasicSoundFont} the soundfonts to merge, the first overwrites the last
60
- * @returns {BasicSoundFont}
65
+ * @param soundfonts {...BasicSoundBank} the soundfonts to merge, the first overwrites the last
66
+ * @returns {BasicSoundBank}
61
67
  */
62
- static mergeSoundfonts(...soundfonts)
68
+ static mergeSoundBanks(...soundfonts)
63
69
  {
64
70
  const mainSf = soundfonts.shift();
65
71
  const presets = mainSf.presets;
@@ -77,7 +83,7 @@ class BasicSoundFont
77
83
  });
78
84
  }
79
85
 
80
- return new BasicSoundFont({ presets: presets, info: mainSf.soundFontInfo });
86
+ return new BasicSoundBank({ presets: presets, info: mainSf.soundFontInfo });
81
87
  }
82
88
 
83
89
  /**
@@ -86,7 +92,7 @@ class BasicSoundFont
86
92
  */
87
93
  static getDummySoundfontFile()
88
94
  {
89
- const font = new BasicSoundFont();
95
+ const font = new BasicSoundBank();
90
96
  const sample = new BasicSample(
91
97
  "Saw",
92
98
  44100,
@@ -139,6 +145,184 @@ class BasicSoundFont
139
145
  return font.write().buffer;
140
146
  }
141
147
 
148
+ /**
149
+ * Trims a sound bank to only contain samples in a given MIDI file
150
+ * @param mid {BasicMIDI} - the MIDI file
151
+ */
152
+ trimSoundBank(mid)
153
+ {
154
+ const soundfont = this;
155
+
156
+ /**
157
+ * @param instrument {Instrument}
158
+ * @param combos {{key: number, velocity: number}[]}
159
+ * @returns {number}
160
+ */
161
+ function trimInstrumentZones(instrument, combos)
162
+ {
163
+ let trimmedIZones = 0;
164
+ for (let iZoneIndex = 0; iZoneIndex < instrument.instrumentZones.length; iZoneIndex++)
165
+ {
166
+ const iZone = instrument.instrumentZones[iZoneIndex];
167
+ if (iZone.isGlobal)
168
+ {
169
+ continue;
170
+ }
171
+ const iKeyRange = iZone.keyRange;
172
+ const iVelRange = iZone.velRange;
173
+ let isIZoneUsed = false;
174
+ for (const iCombo of combos)
175
+ {
176
+ if (
177
+ (iCombo.key >= iKeyRange.min && iCombo.key <= iKeyRange.max) &&
178
+ (iCombo.velocity >= iVelRange.min && iCombo.velocity <= iVelRange.max)
179
+ )
180
+ {
181
+ isIZoneUsed = true;
182
+ break;
183
+ }
184
+ }
185
+ if (!isIZoneUsed)
186
+ {
187
+ SpessaSynthInfo(
188
+ `%c${iZone.sample.sampleName} %cremoved from %c${instrument.instrumentName}%c. Use count: %c${iZone.useCount - 1}`,
189
+ consoleColors.recognized,
190
+ consoleColors.info,
191
+ consoleColors.recognized,
192
+ consoleColors.info,
193
+ consoleColors.recognized
194
+ );
195
+ if (instrument.safeDeleteZone(iZoneIndex))
196
+ {
197
+ trimmedIZones++;
198
+ iZoneIndex--;
199
+ SpessaSynthInfo(
200
+ `%c${iZone.sample.sampleName} %cdeleted`,
201
+ consoleColors.recognized,
202
+ consoleColors.info
203
+ );
204
+ }
205
+ if (iZone.sample.useCount < 1)
206
+ {
207
+ soundfont.deleteSample(iZone.sample);
208
+ }
209
+ }
210
+
211
+ }
212
+ return trimmedIZones;
213
+ }
214
+
215
+ SpessaSynthGroup(
216
+ "%cTrimming soundfont...",
217
+ consoleColors.info
218
+ );
219
+ const usedProgramsAndKeys = mid.getUsedProgramsAndKeys(soundfont);
220
+
221
+ SpessaSynthGroupCollapsed(
222
+ "%cModifying soundfont...",
223
+ consoleColors.info
224
+ );
225
+ SpessaSynthInfo("Detected keys for midi:", usedProgramsAndKeys);
226
+ // modify the soundfont to only include programs and samples that are used
227
+ for (let presetIndex = 0; presetIndex < soundfont.presets.length; presetIndex++)
228
+ {
229
+ const p = soundfont.presets[presetIndex];
230
+ const string = p.bank + ":" + p.program;
231
+ const used = usedProgramsAndKeys[string];
232
+ if (used === undefined)
233
+ {
234
+ SpessaSynthInfo(
235
+ `%cDeleting preset %c${p.presetName}%c and its zones`,
236
+ consoleColors.info,
237
+ consoleColors.recognized,
238
+ consoleColors.info
239
+ );
240
+ soundfont.deletePreset(p);
241
+ presetIndex--;
242
+ }
243
+ else
244
+ {
245
+ const combos = [...used].map(s =>
246
+ {
247
+ const split = s.split("-");
248
+ return {
249
+ key: parseInt(split[0]),
250
+ velocity: parseInt(split[1])
251
+ };
252
+ });
253
+ SpessaSynthGroupCollapsed(
254
+ `%cTrimming %c${p.presetName}`,
255
+ consoleColors.info,
256
+ consoleColors.recognized
257
+ );
258
+ SpessaSynthInfo(`Keys for ${p.presetName}:`, combos);
259
+ let trimmedZones = 0;
260
+ // clean the preset to only use zones that are used
261
+ for (let zoneIndex = 0; zoneIndex < p.presetZones.length; zoneIndex++)
262
+ {
263
+ const zone = p.presetZones[zoneIndex];
264
+ if (zone.isGlobal)
265
+ {
266
+ continue;
267
+ }
268
+ const keyRange = zone.keyRange;
269
+ const velRange = zone.velRange;
270
+ // check if any of the combos matches the zone
271
+ let isZoneUsed = false;
272
+ for (const combo of combos)
273
+ {
274
+ if (
275
+ (combo.key >= keyRange.min && combo.key <= keyRange.max) &&
276
+ (combo.velocity >= velRange.min && combo.velocity <= velRange.max)
277
+ )
278
+ {
279
+ // zone is used, trim the instrument zones
280
+ isZoneUsed = true;
281
+ const trimmedIZones = trimInstrumentZones(zone.instrument, combos);
282
+ SpessaSynthInfo(
283
+ `%cTrimmed off %c${trimmedIZones}%c zones from %c${zone.instrument.instrumentName}`,
284
+ consoleColors.info,
285
+ consoleColors.recognized,
286
+ consoleColors.info,
287
+ consoleColors.recognized
288
+ );
289
+ break;
290
+ }
291
+ }
292
+ if (!isZoneUsed)
293
+ {
294
+ trimmedZones++;
295
+ p.deleteZone(zoneIndex);
296
+ if (zone.instrument.useCount < 1)
297
+ {
298
+ soundfont.deleteInstrument(zone.instrument);
299
+ }
300
+ zoneIndex--;
301
+ }
302
+ }
303
+ SpessaSynthInfo(
304
+ `%cTrimmed off %c${trimmedZones}%c zones from %c${p.presetName}`,
305
+ consoleColors.info,
306
+ consoleColors.recognized,
307
+ consoleColors.info,
308
+ consoleColors.recognized
309
+ );
310
+ SpessaSynthGroupEnd();
311
+ }
312
+ }
313
+ soundfont.removeUnusedElements();
314
+
315
+ soundfont.soundFontInfo["ICMT"] = `NOTE: This soundfont was trimmed by SpessaSynth to only contain presets used in "${mid.midiName}"\n\n`
316
+ + soundfont.soundFontInfo["ICMT"];
317
+
318
+ SpessaSynthInfo(
319
+ "%cSoundfont modified!",
320
+ consoleColors.recognized
321
+ );
322
+ SpessaSynthGroupEnd();
323
+ SpessaSynthGroupEnd();
324
+ }
325
+
142
326
  removeUnusedElements()
143
327
  {
144
328
  this.instruments.forEach(i =>
@@ -254,7 +438,7 @@ class BasicSoundFont
254
438
  }
255
439
  else
256
440
  {
257
- // non drum preset: find any preset with the given program that is not a drum preset
441
+ // non-drum preset: find any preset with the given program that is not a drum preset
258
442
  preset = this.presets.find(p => p.program === programNr && p.bank !== 128);
259
443
  }
260
444
  if (preset)
@@ -299,7 +483,7 @@ class BasicSoundFont
299
483
  throw new Error(`SF parsing error: ${error} The file may be corrupted.`);
300
484
  }
301
485
 
302
- destroySoundfont()
486
+ destroySoundBank()
303
487
  {
304
488
  delete this.presets;
305
489
  delete this.instruments;
@@ -307,7 +491,7 @@ class BasicSoundFont
307
491
  }
308
492
  }
309
493
 
310
- BasicSoundFont.prototype.write = write;
311
- BasicSoundFont.prototype.writeDLS = writeDLS;
494
+ BasicSoundBank.prototype.write = write;
495
+ BasicSoundBank.prototype.writeDLS = writeDLS;
312
496
 
313
- export { BasicSoundFont };
497
+ export { BasicSoundBank };
@@ -7,7 +7,7 @@ export const generatorTypes = {
7
7
  endAddrOffset: 1, // sample control - moves sample end point
8
8
  startloopAddrsOffset: 2, // loop control - moves loop start point
9
9
  endloopAddrsOffset: 3, // loop control - moves loop end point
10
- startAddrsCoarseOffset: 4, // sample control - moves sample start point in 32768 increments
10
+ startAddrsCoarseOffset: 4, // sample control - moves sample start point in 32,768 increments
11
11
  modLfoToPitch: 5, // pitch modulation - modulation lfo pitch modulation in cents
12
12
  vibLfoToPitch: 6, // pitch modulation - vibrato lfo pitch modulation in cents
13
13
  modEnvToPitch: 7, // pitch modulation - modulation envelope pitch modulation in cents
@@ -15,7 +15,7 @@ export const generatorTypes = {
15
15
  initialFilterQ: 9, // filter - lowpass filter resonance
16
16
  modLfoToFilterFc: 10, // filter modulation - modulation lfo lowpass filter cutoff in cents
17
17
  modEnvToFilterFc: 11, // filter modulation - modulation envelope lowpass filter cutoff in cents
18
- endAddrsCoarseOffset: 12, // ample control - moves sample end point in 32768 increments
18
+ endAddrsCoarseOffset: 12, // ample control - move sample end point in 32,768 increments
19
19
  modLfoToVolume: 13, // modulation lfo - volume (tremolo), where 100 = 10dB
20
20
  unused1: 14, // unused
21
21
  chorusEffectsSend: 15, // effect send - how much is sent to chorus 0 - 1000
@@ -24,11 +24,11 @@ export const generatorTypes = {
24
24
  unused2: 18, // unused
25
25
  unused3: 19, // unused
26
26
  unused4: 20, // unused
27
- delayModLFO: 21, // mod lfo - delay for mod lfo to start from zero (weird scale)
28
- freqModLFO: 22, // mod lfo - frequency of mod lfo, 0 = 8.176Hz, unit: f => 1200log2(f/8.176)
29
- delayVibLFO: 23, // vib lfo - delay for vibrato lfo to start from zero (weird scale)
27
+ delayModLFO: 21, // mod lfo - delay for mod lfo to start from zero
28
+ freqModLFO: 22, // mod lfo - frequency of mod lfo, 0 = 8.176 Hz, units: f => 1200log2(f/8.176)
29
+ delayVibLFO: 23, // vib lfo - delay for vibrato lfo to start from zero
30
30
  freqVibLFO: 24, // vib lfo - frequency of vibrato lfo, 0 = 8.176Hz, unit: f => 1200log2(f/8.176)
31
- delayModEnv: 25, // mod env - 0 = 1s declay till mod env starts
31
+ delayModEnv: 25, // mod env - 0 = 1 s decay till mod env starts
32
32
  attackModEnv: 26, // mod env - attack of mod env
33
33
  holdModEnv: 27, // mod env - hold of mod env
34
34
  decayModEnv: 28, // mod env - decay of mod env
@@ -48,16 +48,16 @@ export const generatorTypes = {
48
48
  reserved1: 42, // reserved
49
49
  keyRange: 43, // zone - key range for which preset / instrument zone is active
50
50
  velRange: 44, // zone - velocity range for which preset / instrument zone is active
51
- startloopAddrsCoarseOffset: 45, // sample control - moves sample loop start point in 32768 increments
51
+ startloopAddrsCoarseOffset: 45, // sample control - moves sample loop start point in 32,768 increments
52
52
  keyNum: 46, // zone - instrument only: always use this midi number (ignore what's pressed)
53
53
  velocity: 47, // zone - instrument only: always use this velocity (ignore what's pressed)
54
54
  initialAttenuation: 48, // zone - allows turning down the volume, 10 = -1dB
55
55
  reserved2: 49, // reserved
56
- endloopAddrsCoarseOffset: 50, // sample control - moves sample loop end point in 32768 increments
56
+ endloopAddrsCoarseOffset: 50, // sample control - moves sample loop end point in 32,768 increments
57
57
  coarseTune: 51, // tune - pitch offset in semitones
58
58
  fineTune: 52, // tune - pitch offset in cents
59
59
  sampleID: 53, // sample - instrument zone only: which sample to use
60
- sampleModes: 54, // sample - 0 = no loop, 1 = loop, 2 = reserved, 3 = loop and play till end in release phase
60
+ sampleModes: 54, // sample - 0 = no loop, 1 = loop, 2 = reserved, 3 = loop and play till the end in release phase
61
61
  reserved3: 55, // reserved
62
62
  scaleTuning: 56, // sample - the degree to which MIDI key number influences pitch, 100 = default
63
63
  exclusiveClass: 57, // sample - = cut = choke group
@@ -103,13 +103,14 @@ generatorLimits[generatorTypes.delayVibLFO] = { min: -12000, max: 5000, def: -12
103
103
  generatorLimits[generatorTypes.freqVibLFO] = { min: -16000, max: 4500, def: 0 };
104
104
 
105
105
  // mod env
106
- generatorLimits[generatorTypes.delayModEnv] = { min: -32768, max: 5000, def: -32768 }; // -32768 indicates instant phase, this is done to prevent click at the start of filter modenv
106
+ generatorLimits[generatorTypes.delayModEnv] = { min: -32768, max: 5000, def: -32768 }; // -32,768 indicates instant phase,
107
+ // this is done to prevent click at the start of filter modenv
107
108
  generatorLimits[generatorTypes.attackModEnv] = { min: -32768, max: 8000, def: -32768 };
108
109
  generatorLimits[generatorTypes.holdModEnv] = { min: -12000, max: 5000, def: -12000 };
109
110
  generatorLimits[generatorTypes.decayModEnv] = { min: -12000, max: 8000, def: -12000 };
110
111
  generatorLimits[generatorTypes.sustainModEnv] = { min: 0, max: 1000, def: 0 };
111
112
  generatorLimits[generatorTypes.releaseModEnv] = { min: -7200, max: 8000, def: -12000 }; // min is set to -7200 to prevent lowpass clicks
112
- // keynum to mod env
113
+ // key num to mod env
113
114
  generatorLimits[generatorTypes.keyNumToModEnvHold] = { min: -1200, max: 1200, def: 0 };
114
115
  generatorLimits[generatorTypes.keyNumToModEnvDecay] = { min: -1200, max: 1200, def: 0 };
115
116
 
@@ -119,8 +120,8 @@ generatorLimits[generatorTypes.attackVolEnv] = { min: -12000, max: 8000, def: -1
119
120
  generatorLimits[generatorTypes.holdVolEnv] = { min: -12000, max: 5000, def: -12000 };
120
121
  generatorLimits[generatorTypes.decayVolEnv] = { min: -12000, max: 8000, def: -12000 };
121
122
  generatorLimits[generatorTypes.sustainVolEnv] = { min: 0, max: 1440, def: 0 };
122
- generatorLimits[generatorTypes.releaseVolEnv] = { min: -7200, max: 8000, def: -12000 }; // min is set to -7200 prevent clicks
123
- // keynum to vol env
123
+ generatorLimits[generatorTypes.releaseVolEnv] = { min: -7200, max: 8000, def: -12000 }; // min is set to -7200 prevent clicks
124
+ // key num to vol env
124
125
  generatorLimits[generatorTypes.keyNumToVolEnvHold] = { min: -1200, max: 1200, def: 0 };
125
126
  generatorLimits[generatorTypes.keyNumToVolEnvDecay] = { min: -1200, max: 1200, def: 0 };
126
127
 
@@ -128,7 +129,7 @@ generatorLimits[generatorTypes.startloopAddrsCoarseOffset] = { min: -32768, max:
128
129
  generatorLimits[generatorTypes.keyNum] = { min: -1, max: 127, def: -1 };
129
130
  generatorLimits[generatorTypes.velocity] = { min: -1, max: 127, def: -1 };
130
131
 
131
- generatorLimits[generatorTypes.initialAttenuation] = { min: -250, max: 1440, def: 0 }; // soundblaster allows 10dB of gain (divide by 0.4)
132
+ generatorLimits[generatorTypes.initialAttenuation] = { min: -250, max: 1440, def: 0 }; // sound blaster allows 10dB of gain (divide by 0.4)
132
133
 
133
134
  generatorLimits[generatorTypes.endloopAddrsCoarseOffset] = { min: -32768, max: 32768, def: 0 };
134
135
 
@@ -180,7 +181,8 @@ export class Generator
180
181
 
181
182
  /**
182
183
  * generator.js
183
- * purpose: contains enums for generators and their limis parses reads soundfont generators, sums them and applies limits
184
+ * purpose: contains enums for generators,
185
+ * and their limis parses reads soundfont generators, sums them and applies limits
184
186
  */
185
187
  /**
186
188
  * @param generatorType {number}
@@ -206,7 +208,7 @@ export function addAndClampGenerator(generatorType, presetGens, instrumentGens)
206
208
 
207
209
  let value = instruValue + presetValue;
208
210
 
209
- // special case, intial attenuation.
211
+ // Special case, initial attenuation.
210
212
  // Shall get clamped in the volume envelope,
211
213
  // so the modulators can be affected by negative generators (the "Brass" patch was problematic...)
212
214
  if (generatorType === generatorTypes.initialAttenuation)
@@ -372,4 +372,7 @@ const customModulators = [
372
372
  )
373
373
  ];
374
374
 
375
+ /**
376
+ * @type {Modulator[]}
377
+ */
375
378
  export const defaultModulators = soundFontModulators.concat(customModulators);
@@ -74,7 +74,7 @@ export function writeRIFFChunk(chunk, prepend = undefined)
74
74
  size += prepend.length;
75
75
  }
76
76
  const array = new IndexedByteArray(size);
77
- // prepend data (for example type before the read)
77
+ // prepend data (for example, type before the read)
78
78
  if (prepend)
79
79
  {
80
80
  array.set(prepend, array.currentIndex);
@@ -82,7 +82,7 @@ export function writeRIFFChunk(chunk, prepend = undefined)
82
82
  }
83
83
  // write header
84
84
  writeStringAsBytes(array, chunk.header);
85
- // write size (excluding header and the size itself) and the prepend if specified
85
+ // write size (excluding header and the size itself) and then prepend if specified
86
86
  writeDword(array, size - 8 - (prepend?.length || 0));
87
87
  // write data
88
88
  array.set(chunk.chunkData, array.currentIndex);
@@ -66,7 +66,7 @@ export function writeArticulator(zone)
66
66
 
67
67
  // read_articulation.js:
68
68
  // according to viena and another strange (with modulators) rendition of gm.dls in sf2,
69
- // it shall be divided by -128
69
+ // it shall be divided by -128,
70
70
  // and a strange correction needs to be applied to the real value:
71
71
  // real + (60 / 128) * scale
72
72
  // we invert this here