spessasynth_lib 4.0.19 → 4.1.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/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { KeyModifier } from "spessasynth_core";
5
5
  function fillWithDefaults(obj, defObj) {
6
6
  return {
7
7
  ...defObj,
8
- ...obj ?? {}
8
+ ...obj
9
9
  };
10
10
  }
11
11
 
@@ -147,13 +147,13 @@ var SoundBankManager = class {
147
147
  );
148
148
  await this.awaitResponse();
149
149
  const found = this.soundBankList.find((s) => s.id === id);
150
- if (found !== void 0) {
151
- found.bankOffset = bankOffset;
152
- } else {
150
+ if (found === void 0) {
153
151
  this.soundBankList.push({
154
152
  id,
155
153
  bankOffset
156
154
  });
155
+ } else {
156
+ found.bankOffset = bankOffset;
157
157
  }
158
158
  }
159
159
  // noinspection JSUnusedGlobalSymbols
@@ -168,7 +168,7 @@ var SoundBankManager = class {
168
168
  );
169
169
  return;
170
170
  }
171
- if (this.soundBankList.findIndex((s) => s.id === id) === -1) {
171
+ if (!this.soundBankList.some((s) => s.id === id)) {
172
172
  SpessaSynthCoreUtils.SpessaSynthWarn(
173
173
  `No sound banks with id of "${id}" found. Aborting!`
174
174
  );
@@ -264,20 +264,21 @@ var SynthEventHandler = class {
264
264
  /**
265
265
  * Calls the given event.
266
266
  * INTERNAL USE ONLY!
267
+ * @internal
267
268
  */
268
269
  callEventInternal(name, eventData) {
269
270
  const eventList = this.events[name];
270
271
  const callback = () => {
271
- eventList.forEach((callback2) => {
272
+ for (const callback2 of eventList.values()) {
272
273
  try {
273
274
  callback2(eventData);
274
- } catch (e) {
275
+ } catch (error) {
275
276
  console.error(
276
277
  `Error while executing an event callback for ${name}:`,
277
- e
278
+ error
278
279
  );
279
280
  }
280
- });
281
+ }
281
282
  };
282
283
  if (this.timeDelay > 0) {
283
284
  setTimeout(callback.bind(this), this.timeDelay * 1e3);
@@ -525,7 +526,10 @@ var ReverbProcessor = class extends BasicEffectsProcessor {
525
526
  };
526
527
 
527
528
  // src/synthesizer/basic/snapshot.ts
528
- import { ChannelSnapshot, SynthesizerSnapshot } from "spessasynth_core";
529
+ import {
530
+ ChannelSnapshot,
531
+ SynthesizerSnapshot
532
+ } from "spessasynth_core";
529
533
  var LibSynthesizerSnapshot = class extends SynthesizerSnapshot {
530
534
  /**
531
535
  * Chorus configuration of this synthesizer.
@@ -590,8 +594,10 @@ var BasicSynthesizer = class {
590
594
  presetList = [];
591
595
  /**
592
596
  * INTERNAL USE ONLY!
597
+ * @internal
598
+ * All sequencer callbacks
593
599
  */
594
- sequencerCallbackFunction;
600
+ sequencers = new Array();
595
601
  /**
596
602
  * Resolves when the synthesizer is ready.
597
603
  */
@@ -608,6 +614,7 @@ var BasicSynthesizer = class {
608
614
  chorusProcessor;
609
615
  /**
610
616
  * INTERNAL USE ONLY!
617
+ * @internal
611
618
  */
612
619
  post;
613
620
  worklet;
@@ -1191,10 +1198,25 @@ var BasicSynthesizer = class {
1191
1198
  * INTERNAL USE ONLY!
1192
1199
  * @param type INTERNAL USE ONLY!
1193
1200
  * @param resolve INTERNAL USE ONLY!
1201
+ * @internal
1194
1202
  */
1195
1203
  awaitWorkerResponse(type, resolve) {
1196
1204
  this.resolveMap.set(type, resolve);
1197
1205
  }
1206
+ /**
1207
+ * INTERNAL USE ONLY!
1208
+ * @param callback the sequencer callback
1209
+ * @internal
1210
+ */
1211
+ assignNewSequencer(callback) {
1212
+ this.post({
1213
+ channelNumber: -1,
1214
+ type: "requestNewSequencer",
1215
+ data: null
1216
+ });
1217
+ this.sequencers.push(callback);
1218
+ return this.sequencers.length - 1;
1219
+ }
1198
1220
  assignProgressTracker(type, progressFunction) {
1199
1221
  if (this.renderingProgressTracker.get(type)) {
1200
1222
  throw new Error("Something is already being rendered!");
@@ -1226,24 +1248,29 @@ var BasicSynthesizer = class {
1226
1248
  */
1227
1249
  handleMessage(m) {
1228
1250
  switch (m.type) {
1229
- case "eventCall":
1251
+ case "eventCall": {
1230
1252
  this.eventHandler.callEventInternal(m.data.type, m.data.data);
1231
1253
  break;
1232
- case "sequencerReturn":
1233
- this.sequencerCallbackFunction?.(m.data);
1254
+ }
1255
+ case "sequencerReturn": {
1256
+ this.sequencers[m.data.id](m.data);
1234
1257
  break;
1235
- case "isFullyInitialized":
1258
+ }
1259
+ case "isFullyInitialized": {
1236
1260
  this.workletResponds(m.data.type, m.data.data);
1237
1261
  break;
1238
- case "soundBankError":
1262
+ }
1263
+ case "soundBankError": {
1239
1264
  util.SpessaSynthWarn(m.data);
1240
1265
  this.eventHandler.callEventInternal("soundBankError", m.data);
1241
1266
  break;
1242
- case "renderingProgress":
1267
+ }
1268
+ case "renderingProgress": {
1243
1269
  this.renderingProgressTracker.get(m.data.type)?.(
1244
1270
  // @ts-expect-error I can't use generics with map
1245
1271
  m.data.data
1246
1272
  );
1273
+ }
1247
1274
  }
1248
1275
  }
1249
1276
  addNewChannelInternal(post) {
@@ -1282,7 +1309,7 @@ var WorkletSynthesizer = class extends BasicSynthesizer {
1282
1309
  */
1283
1310
  constructor(context, config = DEFAULT_SYNTH_CONFIG) {
1284
1311
  const synthConfig = fillWithDefaults(config, DEFAULT_SYNTH_CONFIG);
1285
- let outputChannelCount = Array(18).fill(2);
1312
+ let outputChannelCount = new Array(18).fill(2);
1286
1313
  let numberOfOutputs = 18;
1287
1314
  if (synthConfig.oneOutput) {
1288
1315
  outputChannelCount = [36];
@@ -1301,8 +1328,8 @@ var WorkletSynthesizer = class extends BasicSynthesizer {
1301
1328
  enableEventSystem: synthConfig.enableEventSystem
1302
1329
  }
1303
1330
  });
1304
- } catch (e) {
1305
- console.error(e);
1331
+ } catch (error) {
1332
+ console.error(error);
1306
1333
  throw new Error(
1307
1334
  "Could not create the AudioWorkletNode. Did you forget to addModule()?"
1308
1335
  );
@@ -1466,7 +1493,8 @@ var DEFAULT_WORKER_RENDER_AUDIO_OPTIONS = {
1466
1493
  loopCount: 0,
1467
1494
  progressCallback: void 0,
1468
1495
  preserveSynthParams: true,
1469
- enableEffects: true
1496
+ enableEffects: true,
1497
+ sequencerID: 0
1470
1498
  };
1471
1499
  var RENDER_BLOCKS_PER_PROGRESS = 64;
1472
1500
  var BLOCK_SIZE = 128;
@@ -1475,20 +1503,20 @@ function renderAudioWorker(sampleRate, options) {
1475
1503
  enableEventSystem: false
1476
1504
  });
1477
1505
  const rendererSeq = new SpessaSynthSequencer(rendererSynth);
1478
- this.synthesizer.soundBankManager.soundBankList.forEach(
1479
- (entry) => rendererSynth.soundBankManager.addSoundBank(
1506
+ for (const entry of this.synthesizer.soundBankManager.soundBankList)
1507
+ rendererSynth.soundBankManager.addSoundBank(
1480
1508
  entry.soundBank,
1481
1509
  entry.id,
1482
1510
  entry.bankOffset
1483
- )
1484
- );
1511
+ );
1485
1512
  rendererSynth.soundBankManager.priorityOrder = this.synthesizer.soundBankManager.priorityOrder;
1486
1513
  this.stopAudioLoop();
1487
- const parsedMid = this.sequencer.midiData;
1514
+ const seq = this.sequencers[options.sequencerID];
1515
+ const parsedMid = seq.midiData;
1488
1516
  if (!parsedMid) {
1489
1517
  throw new Error("No MIDI is loaded!");
1490
1518
  }
1491
- const playbackRate = this.sequencer.playbackRate;
1519
+ const playbackRate = seq.playbackRate;
1492
1520
  const loopStartAbsolute = parsedMid.midiTicksToSeconds(parsedMid.loop.start) / playbackRate;
1493
1521
  const loopEndAbsolute = parsedMid.midiTicksToSeconds(parsedMid.loop.end) / playbackRate;
1494
1522
  const loopDuration = loopEndAbsolute - loopStartAbsolute;
@@ -1496,15 +1524,12 @@ function renderAudioWorker(sampleRate, options) {
1496
1524
  const sampleDuration = sampleRate * duration;
1497
1525
  rendererSeq.loopCount = options.loopCount;
1498
1526
  if (options.preserveSynthParams) {
1499
- rendererSeq.playbackRate = this.sequencer.playbackRate;
1527
+ rendererSeq.playbackRate = seq.playbackRate;
1500
1528
  const snapshot = this.synthesizer.getSnapshot();
1501
1529
  rendererSynth.applySynthesizerSnapshot(snapshot);
1502
1530
  }
1503
1531
  rendererSeq.loadNewSongList([parsedMid]);
1504
1532
  rendererSeq.play();
1505
- const progressCallback = (progress) => {
1506
- this.postProgress("renderAudio", progress);
1507
- };
1508
1533
  const reverb = [
1509
1534
  new Float32Array(sampleDuration),
1510
1535
  new Float32Array(sampleDuration)
@@ -1554,7 +1579,7 @@ function renderAudioWorker(sampleRate, options) {
1554
1579
  );
1555
1580
  index += BLOCK_SIZE;
1556
1581
  }
1557
- progressCallback(index / sampleDuration);
1582
+ this.postProgress("renderAudio", index / sampleDuration);
1558
1583
  }
1559
1584
  } else {
1560
1585
  const dry = [
@@ -1587,7 +1612,7 @@ function renderAudioWorker(sampleRate, options) {
1587
1612
  );
1588
1613
  index += BLOCK_SIZE;
1589
1614
  }
1590
- progressCallback(index / sampleDuration);
1615
+ this.postProgress("renderAudio", index / sampleDuration);
1591
1616
  }
1592
1617
  }
1593
1618
  }
@@ -1596,7 +1621,8 @@ function renderAudioWorker(sampleRate, options) {
1596
1621
  var DEFAULT_BANK_WRITE_OPTIONS = {
1597
1622
  trim: true,
1598
1623
  bankID: "",
1599
- writeEmbeddedSoundBank: true
1624
+ writeEmbeddedSoundBank: true,
1625
+ sequencerID: 0
1600
1626
  };
1601
1627
  var DEFAULT_SF2_WRITE_OPTIONS = {
1602
1628
  ...DEFAULT_BANK_WRITE_OPTIONS,
@@ -1645,7 +1671,7 @@ var WorkerSynthesizer = class extends BasicSynthesizer {
1645
1671
  context,
1646
1672
  PLAYBACK_WORKLET_PROCESSOR_NAME,
1647
1673
  {
1648
- outputChannelCount: Array(18).fill(2),
1674
+ outputChannelCount: new Array(18).fill(2),
1649
1675
  numberOfOutputs: 18,
1650
1676
  processorOptions: {
1651
1677
  oneOutput: synthConfig.oneOutput,
@@ -1653,8 +1679,8 @@ var WorkerSynthesizer = class extends BasicSynthesizer {
1653
1679
  }
1654
1680
  }
1655
1681
  );
1656
- } catch (e) {
1657
- console.error(e);
1682
+ } catch (error) {
1683
+ console.error(error);
1658
1684
  throw new Error(
1659
1685
  "Could not create the AudioWorkletNode. Did you forget to registerPlaybackWorklet()?"
1660
1686
  );
@@ -1955,11 +1981,7 @@ var MIDIData = class _MIDIData extends BasicMIDI {
1955
1981
  super();
1956
1982
  super.copyMetadataFrom(mid);
1957
1983
  this.tracks = mid.tracks.map((t) => new MIDIDataTrack(t));
1958
- if (mid instanceof _MIDIData) {
1959
- this.embeddedSoundBankSize = mid.embeddedSoundBankSize;
1960
- } else {
1961
- this.embeddedSoundBankSize = mid?.embeddedSoundBank?.byteLength;
1962
- }
1984
+ this.embeddedSoundBankSize = mid instanceof _MIDIData ? mid.embeddedSoundBankSize : mid?.embeddedSoundBank?.byteLength;
1963
1985
  }
1964
1986
  };
1965
1987
 
@@ -1976,7 +1998,7 @@ var songChangeType = {
1976
1998
  // src/synthesizer/basic/basic_synthesizer_core.ts
1977
1999
  var BasicSynthesizerCore = class {
1978
2000
  synthesizer;
1979
- sequencer;
2001
+ sequencers = new Array();
1980
2002
  post;
1981
2003
  /**
1982
2004
  * Indicates if the processor is alive.
@@ -1985,7 +2007,6 @@ var BasicSynthesizerCore = class {
1985
2007
  alive = false;
1986
2008
  constructor(sampleRate, options, postMessage) {
1987
2009
  this.synthesizer = new SpessaSynthProcessor2(sampleRate, options);
1988
- this.sequencer = new SpessaSynthSequencer2(this.synthesizer);
1989
2010
  this.post = postMessage;
1990
2011
  this.synthesizer.onEventCall = (event) => {
1991
2012
  this.post({
@@ -1994,7 +2015,12 @@ var BasicSynthesizerCore = class {
1994
2015
  currentTime: this.synthesizer.currentSynthTime
1995
2016
  });
1996
2017
  };
1997
- this.sequencer.onEventCall = (e) => {
2018
+ }
2019
+ createNewSequencer() {
2020
+ const sequencer = new SpessaSynthSequencer2(this.synthesizer);
2021
+ const sequencerID = this.sequencers.length;
2022
+ this.sequencers.push(sequencer);
2023
+ sequencer.onEventCall = (e) => {
1998
2024
  if (e.type === "songListChange") {
1999
2025
  const songs = e.data.newSongList;
2000
2026
  const midiDatas = songs.map((s) => {
@@ -2004,7 +2030,8 @@ var BasicSynthesizerCore = class {
2004
2030
  type: "sequencerReturn",
2005
2031
  data: {
2006
2032
  type: e.type,
2007
- data: { newSongList: midiDatas }
2033
+ data: { newSongList: midiDatas },
2034
+ id: sequencerID
2008
2035
  },
2009
2036
  currentTime: this.synthesizer.currentSynthTime
2010
2037
  });
@@ -2012,7 +2039,7 @@ var BasicSynthesizerCore = class {
2012
2039
  }
2013
2040
  this.post({
2014
2041
  type: "sequencerReturn",
2015
- data: e,
2042
+ data: { ...e, id: sequencerID },
2016
2043
  currentTime: this.synthesizer.currentSynthTime
2017
2044
  });
2018
2045
  };
@@ -2058,7 +2085,7 @@ var BasicSynthesizerCore = class {
2058
2085
  }
2059
2086
  }
2060
2087
  switch (m.type) {
2061
- case "midiMessage":
2088
+ case "midiMessage": {
2062
2089
  this.synthesizer.processMessage(
2063
2090
  m.data.messageData,
2064
2091
  m.data.channelOffset,
@@ -2066,20 +2093,23 @@ var BasicSynthesizerCore = class {
2066
2093
  m.data.options
2067
2094
  );
2068
2095
  break;
2069
- case "customCcChange":
2096
+ }
2097
+ case "customCcChange": {
2070
2098
  channelObject?.setCustomController(
2071
2099
  m.data.ccNumber,
2072
2100
  m.data.ccValue
2073
2101
  );
2074
2102
  break;
2075
- case "ccReset":
2103
+ }
2104
+ case "ccReset": {
2076
2105
  if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION2) {
2077
2106
  this.synthesizer.resetAllControllers();
2078
2107
  } else {
2079
2108
  channelObject?.resetControllers();
2080
2109
  }
2081
2110
  break;
2082
- case "setChannelVibrato":
2111
+ }
2112
+ case "setChannelVibrato": {
2083
2113
  if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION2) {
2084
2114
  for (const chan of this.synthesizer.midiChannels) {
2085
2115
  if (m.data.rate === ALL_CHANNELS_OR_DIFFERENT_ACTION2) {
@@ -2102,32 +2132,36 @@ var BasicSynthesizerCore = class {
2102
2132
  );
2103
2133
  }
2104
2134
  break;
2105
- case "stopAll":
2135
+ }
2136
+ case "stopAll": {
2106
2137
  if (channel === ALL_CHANNELS_OR_DIFFERENT_ACTION2) {
2107
2138
  this.synthesizer.stopAllChannels(m.data === 1);
2108
2139
  } else {
2109
2140
  channelObject?.stopAllNotes(m.data === 1);
2110
2141
  }
2111
2142
  break;
2112
- case "killNotes":
2113
- this.synthesizer.killVoices(m.data);
2114
- break;
2115
- case "muteChannel":
2143
+ }
2144
+ case "muteChannel": {
2116
2145
  channelObject?.muteChannel(m.data);
2117
2146
  break;
2118
- case "addNewChannel":
2147
+ }
2148
+ case "addNewChannel": {
2119
2149
  this.synthesizer.createMIDIChannel();
2120
2150
  break;
2121
- case "setMasterParameter":
2151
+ }
2152
+ case "setMasterParameter": {
2122
2153
  this.synthesizer.setMasterParameter(m.data.type, m.data.data);
2123
2154
  break;
2124
- case "setDrums":
2155
+ }
2156
+ case "setDrums": {
2125
2157
  channelObject?.setDrums(m.data);
2126
2158
  break;
2127
- case "transposeChannel":
2159
+ }
2160
+ case "transposeChannel": {
2128
2161
  channelObject?.transposeChannel(m.data.semitones, m.data.force);
2129
2162
  break;
2130
- case "lockController":
2163
+ }
2164
+ case "lockController": {
2131
2165
  if (m.data.controllerNumber === ALL_CHANNELS_OR_DIFFERENT_ACTION2) {
2132
2166
  channelObject?.setPresetLock(m.data.isLocked);
2133
2167
  } else {
@@ -2137,16 +2171,18 @@ var BasicSynthesizerCore = class {
2137
2171
  channelObject.lockedControllers[m.data.controllerNumber] = m.data.isLocked;
2138
2172
  }
2139
2173
  break;
2174
+ }
2140
2175
  case "sequencerSpecific": {
2141
- if (!this.sequencer) {
2176
+ const seq = this.sequencers[m.data.id];
2177
+ if (!seq) {
2142
2178
  return;
2143
2179
  }
2144
- const seq = this.sequencer;
2145
2180
  const seqMsg = m.data;
2146
2181
  switch (seqMsg.type) {
2147
- default:
2182
+ default: {
2148
2183
  break;
2149
- case "loadNewSongList":
2184
+ }
2185
+ case "loadNewSongList": {
2150
2186
  try {
2151
2187
  const sList = seqMsg.data;
2152
2188
  const songMap = sList.map((s) => {
@@ -2159,53 +2195,64 @@ var BasicSynthesizerCore = class {
2159
2195
  );
2160
2196
  });
2161
2197
  seq.loadNewSongList(songMap);
2162
- } catch (e) {
2163
- console.error(e);
2198
+ } catch (error) {
2199
+ console.error(error);
2164
2200
  this.post({
2165
2201
  type: "sequencerReturn",
2166
2202
  data: {
2167
2203
  type: "midiError",
2168
- data: e
2204
+ data: error,
2205
+ id: m.data.id
2169
2206
  },
2170
2207
  currentTime: this.synthesizer.currentSynthTime
2171
2208
  });
2172
2209
  }
2173
2210
  break;
2174
- case "pause":
2211
+ }
2212
+ case "pause": {
2175
2213
  seq.pause();
2176
2214
  break;
2177
- case "play":
2215
+ }
2216
+ case "play": {
2178
2217
  seq.play();
2179
2218
  break;
2180
- case "setTime":
2219
+ }
2220
+ case "setTime": {
2181
2221
  seq.currentTime = seqMsg.data;
2182
2222
  break;
2183
- case "changeMIDIMessageSending":
2223
+ }
2224
+ case "changeMIDIMessageSending": {
2184
2225
  seq.externalMIDIPlayback = seqMsg.data;
2185
2226
  break;
2186
- case "setPlaybackRate":
2227
+ }
2228
+ case "setPlaybackRate": {
2187
2229
  seq.playbackRate = seqMsg.data;
2188
2230
  break;
2189
- case "setLoopCount":
2231
+ }
2232
+ case "setLoopCount": {
2190
2233
  seq.loopCount = seqMsg.data;
2191
2234
  break;
2192
- case "changeSong":
2235
+ }
2236
+ case "changeSong": {
2193
2237
  switch (seqMsg.data.changeType) {
2194
- case songChangeType.shuffleOff:
2238
+ case songChangeType.shuffleOff: {
2195
2239
  seq.shuffleMode = false;
2196
2240
  break;
2197
- case songChangeType.shuffleOn:
2241
+ }
2242
+ case songChangeType.shuffleOn: {
2198
2243
  seq.shuffleMode = true;
2199
2244
  break;
2200
- case songChangeType.index:
2245
+ }
2246
+ case songChangeType.index: {
2201
2247
  if (seqMsg.data.data !== void 0) {
2202
- console.log("INDEX", seqMsg.data);
2203
2248
  seq.songIndex = seqMsg.data.data;
2204
2249
  }
2205
2250
  break;
2251
+ }
2206
2252
  }
2207
2253
  break;
2208
- case "getMIDI":
2254
+ }
2255
+ case "getMIDI": {
2209
2256
  if (!seq.midiData) {
2210
2257
  throw new Error("No MIDI is loaded!");
2211
2258
  }
@@ -2213,24 +2260,27 @@ var BasicSynthesizerCore = class {
2213
2260
  type: "sequencerReturn",
2214
2261
  data: {
2215
2262
  type: "getMIDI",
2216
- data: seq.midiData
2263
+ data: seq.midiData,
2264
+ id: m.data.id
2217
2265
  },
2218
2266
  currentTime: this.synthesizer.currentSynthTime
2219
2267
  });
2220
2268
  break;
2221
- case "setSkipToFirstNote":
2269
+ }
2270
+ case "setSkipToFirstNote": {
2222
2271
  seq.skipToFirstNoteOn = seqMsg.data;
2223
2272
  break;
2273
+ }
2224
2274
  }
2225
2275
  break;
2226
2276
  }
2227
- case "soundBankManager":
2277
+ case "soundBankManager": {
2228
2278
  try {
2229
2279
  const sfManager = this.synthesizer.soundBankManager;
2230
2280
  const sfManMsg = m.data;
2231
2281
  let font;
2232
2282
  switch (sfManMsg.type) {
2233
- case "addSoundBank":
2283
+ case "addSoundBank": {
2234
2284
  font = SoundBankLoader.fromArrayBuffer(
2235
2285
  sfManMsg.data.soundBankBuffer
2236
2286
  );
@@ -2241,43 +2291,51 @@ var BasicSynthesizerCore = class {
2241
2291
  );
2242
2292
  this.postReady("soundBankManager", null);
2243
2293
  break;
2244
- case "deleteSoundBank":
2294
+ }
2295
+ case "deleteSoundBank": {
2245
2296
  sfManager.deleteSoundBank(sfManMsg.data);
2246
2297
  this.postReady("soundBankManager", null);
2247
2298
  break;
2248
- case "rearrangeSoundBanks":
2299
+ }
2300
+ case "rearrangeSoundBanks": {
2249
2301
  sfManager.priorityOrder = sfManMsg.data;
2250
2302
  this.postReady("soundBankManager", null);
2303
+ }
2251
2304
  }
2252
- } catch (e) {
2305
+ } catch (error) {
2253
2306
  this.post({
2254
2307
  type: "soundBankError",
2255
- data: e,
2308
+ data: error,
2256
2309
  currentTime: this.synthesizer.currentSynthTime
2257
2310
  });
2258
2311
  }
2259
2312
  break;
2313
+ }
2260
2314
  case "keyModifierManager": {
2261
2315
  const kmMsg = m.data;
2262
2316
  const man = this.synthesizer.keyModifierManager;
2263
2317
  switch (kmMsg.type) {
2264
- default:
2318
+ default: {
2265
2319
  return;
2266
- case "addMapping":
2320
+ }
2321
+ case "addMapping": {
2267
2322
  man.addMapping(
2268
2323
  kmMsg.data.channel,
2269
2324
  kmMsg.data.midiNote,
2270
2325
  kmMsg.data.mapping
2271
2326
  );
2272
2327
  break;
2273
- case "clearMappings":
2328
+ }
2329
+ case "clearMappings": {
2274
2330
  man.clearMappings();
2275
2331
  break;
2276
- case "deleteMapping":
2332
+ }
2333
+ case "deleteMapping": {
2277
2334
  man.deleteMapping(
2278
2335
  kmMsg.data.channel,
2279
2336
  kmMsg.data.midiNote
2280
2337
  );
2338
+ }
2281
2339
  }
2282
2340
  break;
2283
2341
  }
@@ -2286,21 +2344,28 @@ var BasicSynthesizerCore = class {
2286
2344
  this.postReady("synthesizerSnapshot", snapshot);
2287
2345
  break;
2288
2346
  }
2289
- case "setLogLevel":
2347
+ case "requestNewSequencer": {
2348
+ this.createNewSequencer();
2349
+ break;
2350
+ }
2351
+ case "setLogLevel": {
2290
2352
  SpessaSynthLogging(
2291
2353
  m.data.enableInfo,
2292
2354
  m.data.enableWarning,
2293
2355
  m.data.enableGroup
2294
2356
  );
2295
2357
  break;
2296
- case "destroyWorklet":
2358
+ }
2359
+ case "destroyWorklet": {
2297
2360
  this.alive = false;
2298
2361
  this.synthesizer.destroySynthProcessor();
2299
2362
  this.destroy();
2300
2363
  break;
2301
- default:
2364
+ }
2365
+ default: {
2302
2366
  util2.SpessaSynthWarn("Unrecognized event!", m);
2303
2367
  break;
2368
+ }
2304
2369
  }
2305
2370
  }
2306
2371
  };
@@ -2320,14 +2385,15 @@ async function writeSF2Worker(opts) {
2320
2385
  });
2321
2386
  throw e;
2322
2387
  }
2388
+ const sq = this.sequencers[opts.sequencerID];
2323
2389
  if (opts.trim) {
2324
- if (!this.sequencer.midiData) {
2390
+ if (!sq.midiData) {
2325
2391
  throw new Error(
2326
2392
  "Sound bank MIDI trimming is enabled but no MIDI is loaded!"
2327
2393
  );
2328
2394
  }
2329
2395
  const sfCopy = BasicSoundBank.copyFrom(sf);
2330
- sfCopy.trimSoundBank(this.sequencer.midiData);
2396
+ sfCopy.trimSoundBank(sq.midiData);
2331
2397
  sf = sfCopy;
2332
2398
  }
2333
2399
  let compressionFunction;
@@ -2357,14 +2423,15 @@ async function writeSF2Worker(opts) {
2357
2423
  }
2358
2424
  async function writeDLSWorker(opts) {
2359
2425
  let sf = this.getBank(opts);
2426
+ const sq = this.sequencers[opts.sequencerID];
2360
2427
  if (opts.trim) {
2361
- if (!this.sequencer.midiData) {
2428
+ if (!sq.midiData) {
2362
2429
  throw new Error(
2363
2430
  "Sound bank MIDI trimming is enabled but no MIDI is loaded!"
2364
2431
  );
2365
2432
  }
2366
2433
  const sfCopy = BasicSoundBank.copyFrom(sf);
2367
- sfCopy.trimSoundBank(this.sequencer.midiData);
2434
+ sfCopy.trimSoundBank(sq.midiData);
2368
2435
  sf = sfCopy;
2369
2436
  }
2370
2437
  const b = await sf.writeDLS({
@@ -2387,7 +2454,8 @@ async function writeDLSWorker(opts) {
2387
2454
  // src/synthesizer/worker/write_rmi_worker.ts
2388
2455
  import { BasicMIDI as BasicMIDI3 } from "spessasynth_core";
2389
2456
  async function writeRMIDIWorker(opts) {
2390
- if (!this.sequencer.midiData) {
2457
+ const sq = this.sequencers[opts.sequencerID];
2458
+ if (!sq.midiData) {
2391
2459
  throw new Error("No MIDI is loaded!");
2392
2460
  }
2393
2461
  let sf;
@@ -2401,7 +2469,7 @@ async function writeRMIDIWorker(opts) {
2401
2469
  sfBin = bin.binary;
2402
2470
  sf = bin.bank;
2403
2471
  }
2404
- const mid = BasicMIDI3.copyFrom(this.sequencer.midiData);
2472
+ const mid = BasicMIDI3.copyFrom(sq.midiData);
2405
2473
  return mid.writeRMIDI(sfBin, {
2406
2474
  soundBank: sf,
2407
2475
  ...opts
@@ -2449,21 +2517,21 @@ var WorkerSynthesizerCore = class extends BasicSynthesizerCore {
2449
2517
  */
2450
2518
  handleMessage(m) {
2451
2519
  switch (m.type) {
2452
- case "renderAudio":
2520
+ case "renderAudio": {
2453
2521
  const rendered = renderAudioWorker.call(
2454
2522
  this,
2455
2523
  m.data.sampleRate,
2456
2524
  m.data.options
2457
2525
  );
2458
2526
  const transferable = [];
2459
- rendered.reverb.forEach((r) => transferable.push(r.buffer));
2460
- rendered.chorus.forEach((c) => transferable.push(c.buffer));
2461
- rendered.dry.forEach(
2462
- (d) => transferable.push(...d.map((c) => c.buffer))
2463
- );
2527
+ for (const r of rendered.reverb) transferable.push(r.buffer);
2528
+ for (const c of rendered.chorus) transferable.push(c.buffer);
2529
+ for (const d of rendered.dry)
2530
+ transferable.push(...d.map((c) => c.buffer));
2464
2531
  this.postReady("renderAudio", rendered, transferable);
2465
2532
  break;
2466
- case "writeRMIDI":
2533
+ }
2534
+ case "writeRMIDI": {
2467
2535
  this.stopAudioLoop();
2468
2536
  void writeRMIDIWorker.call(this, m.data).then((data) => {
2469
2537
  this.postReady(
@@ -2477,7 +2545,8 @@ var WorkerSynthesizerCore = class extends BasicSynthesizerCore {
2477
2545
  this.startAudioLoop();
2478
2546
  });
2479
2547
  break;
2480
- case "writeSF2":
2548
+ }
2549
+ case "writeSF2": {
2481
2550
  this.stopAudioLoop();
2482
2551
  void writeSF2Worker.call(this, m.data).then((data) => {
2483
2552
  this.postReady(
@@ -2491,7 +2560,8 @@ var WorkerSynthesizerCore = class extends BasicSynthesizerCore {
2491
2560
  this.startAudioLoop();
2492
2561
  });
2493
2562
  break;
2494
- case "writeDLS":
2563
+ }
2564
+ case "writeDLS": {
2495
2565
  this.stopAudioLoop();
2496
2566
  void writeDLSWorker.call(this, m.data).then((data) => {
2497
2567
  this.postReady(
@@ -2505,20 +2575,17 @@ var WorkerSynthesizerCore = class extends BasicSynthesizerCore {
2505
2575
  this.startAudioLoop();
2506
2576
  });
2507
2577
  break;
2508
- default:
2578
+ }
2579
+ default: {
2509
2580
  super.handleMessage(m);
2581
+ }
2510
2582
  }
2511
2583
  }
2512
2584
  getBank(opts) {
2513
- let sf;
2514
- if (opts.writeEmbeddedSoundBank && this.sequencer.midiData?.embeddedSoundBank) {
2515
- sf = SoundBankLoader2.fromArrayBuffer(
2516
- this.sequencer.midiData.embeddedSoundBank
2517
- );
2518
- } else
2519
- sf = this.synthesizer.soundBankManager.soundBankList.find(
2520
- (b) => b.id === opts.bankID
2521
- )?.soundBank;
2585
+ const sq = this.sequencers[opts.sequencerID];
2586
+ const sf = opts.writeEmbeddedSoundBank && sq.midiData?.embeddedSoundBank ? SoundBankLoader2.fromArrayBuffer(sq.midiData.embeddedSoundBank) : this.synthesizer.soundBankManager.soundBankList.find(
2587
+ (b) => b.id === opts.bankID
2588
+ )?.soundBank;
2522
2589
  if (!sf) {
2523
2590
  const e = new Error(
2524
2591
  `${opts.bankID} does not exist in the sound bank list!`
@@ -2534,7 +2601,9 @@ var WorkerSynthesizerCore = class extends BasicSynthesizerCore {
2534
2601
  }
2535
2602
  stopAudioLoop() {
2536
2603
  this.synthesizer.stopAllChannels(true);
2537
- this.sequencer.pause();
2604
+ for (const seq of this.sequencers) {
2605
+ seq.pause();
2606
+ }
2538
2607
  this.alive = false;
2539
2608
  }
2540
2609
  startAudioLoop() {
@@ -2570,7 +2639,9 @@ var WorkerSynthesizerCore = class extends BasicSynthesizerCore {
2570
2639
  const dryR = new Float32Array(data.buffer, byteOffset, BLOCK_SIZE2);
2571
2640
  dry.push([dryL, dryR]);
2572
2641
  }
2573
- this.sequencer.processTick();
2642
+ for (const seq of this.sequencers) {
2643
+ seq.processTick();
2644
+ }
2574
2645
  this.synthesizer.renderAudioSplit(rev, chr, dry);
2575
2646
  this.workletMessagePort.postMessage(data, [data.buffer]);
2576
2647
  }
@@ -2627,20 +2698,21 @@ var SeqEventHandler = class {
2627
2698
  /**
2628
2699
  * Calls the given event.
2629
2700
  * Internal use only.
2701
+ * @internal
2630
2702
  */
2631
2703
  callEventInternal(name, eventData) {
2632
2704
  const eventList = this.events[name];
2633
2705
  const callback = () => {
2634
- eventList.forEach((callback2) => {
2706
+ for (const callback2 of eventList.values()) {
2635
2707
  try {
2636
2708
  callback2(eventData);
2637
- } catch (e) {
2709
+ } catch (error) {
2638
2710
  console.error(
2639
2711
  `Error while executing a sequencer event callback for ${name}:`,
2640
- e
2712
+ error
2641
2713
  );
2642
2714
  }
2643
- });
2715
+ }
2644
2716
  };
2645
2717
  if (this.timeDelay > 0) {
2646
2718
  setTimeout(callback.bind(this), this.timeDelay * 1e3);
@@ -2688,6 +2760,10 @@ var Sequencer = class {
2688
2760
  * Absolute playback startTime, bases on the synth's time.
2689
2761
  */
2690
2762
  absoluteStartTime;
2763
+ /**
2764
+ * For sending the messages to the correct SpessaSynthSequencer in core
2765
+ */
2766
+ sequencerID;
2691
2767
  /**
2692
2768
  * Creates a new MIDI sequencer for playing back MIDI files.
2693
2769
  * @param synth synth to send events to.
@@ -2696,7 +2772,9 @@ var Sequencer = class {
2696
2772
  constructor(synth, options = DEFAULT_SEQUENCER_OPTIONS) {
2697
2773
  this.synth = synth;
2698
2774
  this.absoluteStartTime = this.synth.currentTime;
2699
- this.synth.sequencerCallbackFunction = this.handleMessage.bind(this);
2775
+ this.sequencerID = this.synth.assignNewSequencer(
2776
+ this.handleMessage.bind(this)
2777
+ );
2700
2778
  this._skipToFirstNoteOn = options?.skipToFirstNoteOn ?? true;
2701
2779
  if (options?.initialPlaybackRate !== 1) {
2702
2780
  this.playbackRate = options?.initialPlaybackRate ?? 1;
@@ -2916,16 +2994,15 @@ var Sequencer = class {
2916
2994
  }
2917
2995
  handleMessage(m) {
2918
2996
  switch (m.type) {
2919
- case "midiMessage":
2997
+ case "midiMessage": {
2920
2998
  const midiEventData = m.data.message;
2921
- if (this.midiOut) {
2922
- if (midiEventData[0] >= 128) {
2923
- this.midiOut.send(midiEventData);
2924
- return;
2925
- }
2999
+ if (this.midiOut && midiEventData[0] >= 128) {
3000
+ this.midiOut.send(midiEventData);
3001
+ return;
2926
3002
  }
2927
3003
  break;
2928
- case "songChange":
3004
+ }
3005
+ case "songChange": {
2929
3006
  this._songIndex = m.data.songIndex;
2930
3007
  const songChangeData = this.songListData[this._songIndex];
2931
3008
  this.midiData = songChangeData;
@@ -2933,35 +3010,41 @@ var Sequencer = class {
2933
3010
  this.absoluteStartTime = 0;
2934
3011
  this.callEventInternal("songChange", songChangeData);
2935
3012
  break;
2936
- case "timeChange":
3013
+ }
3014
+ case "timeChange": {
2937
3015
  const time = m.data.newTime;
2938
3016
  this.recalculateStartTime(time);
2939
3017
  this.callEventInternal("timeChange", time);
2940
3018
  break;
2941
- case "pause":
3019
+ }
3020
+ case "pause": {
2942
3021
  this.pausedTime = this.currentTime;
2943
3022
  this.isFinished = m.data.isFinished;
2944
3023
  if (this.isFinished) {
2945
3024
  this.callEventInternal("songEnded", null);
2946
3025
  }
2947
3026
  break;
2948
- case "midiError":
3027
+ }
3028
+ case "midiError": {
2949
3029
  this.callEventInternal("midiError", m.data);
2950
3030
  throw new Error(`MIDI parsing error: ${m.data}`);
2951
- case "getMIDI":
3031
+ }
3032
+ case "getMIDI": {
2952
3033
  if (this.getMIDICallback) {
2953
3034
  this.getMIDICallback(BasicMIDI4.copyFrom(m.data));
2954
3035
  }
2955
3036
  break;
2956
- case "metaEvent":
3037
+ }
3038
+ case "metaEvent": {
2957
3039
  const event = m.data.event;
2958
3040
  switch (event.statusByte) {
2959
- case midiMessageTypes2.setTempo:
3041
+ case midiMessageTypes2.setTempo: {
2960
3042
  this._currentTempo = 6e7 / SpessaSynthCoreUtils4.readBytesAsUintBigEndian(
2961
3043
  event.data,
2962
3044
  3
2963
3045
  );
2964
3046
  break;
3047
+ }
2965
3048
  case midiMessageTypes2.text:
2966
3049
  case midiMessageTypes2.lyric:
2967
3050
  case midiMessageTypes2.copyright:
@@ -2969,7 +3052,7 @@ var Sequencer = class {
2969
3052
  case midiMessageTypes2.marker:
2970
3053
  case midiMessageTypes2.cuePoint:
2971
3054
  case midiMessageTypes2.instrumentName:
2972
- case midiMessageTypes2.programName:
3055
+ case midiMessageTypes2.programName: {
2973
3056
  if (!this.midiData) {
2974
3057
  break;
2975
3058
  }
@@ -2995,25 +3078,30 @@ var Sequencer = class {
2995
3078
  lyricsIndex
2996
3079
  });
2997
3080
  break;
3081
+ }
2998
3082
  }
2999
3083
  this.callEventInternal("metaEvent", {
3000
3084
  event: m.data.event,
3001
3085
  trackNumber: m.data.trackIndex
3002
3086
  });
3003
3087
  break;
3004
- case "loopCountChange":
3088
+ }
3089
+ case "loopCountChange": {
3005
3090
  this._loopCount = m.data.newCount;
3006
3091
  if (this._loopCount === 0) {
3007
3092
  }
3008
3093
  break;
3009
- case "songListChange":
3094
+ }
3095
+ case "songListChange": {
3010
3096
  this.songListData = m.data.newSongList.map(
3011
3097
  (m2) => new MIDIData(m2)
3012
3098
  );
3013
3099
  this.midiData = this.songListData[this._songIndex];
3014
3100
  break;
3015
- default:
3101
+ }
3102
+ default: {
3016
3103
  break;
3104
+ }
3017
3105
  }
3018
3106
  }
3019
3107
  callEventInternal(type, data) {
@@ -3042,7 +3130,8 @@ var Sequencer = class {
3042
3130
  type: "sequencerSpecific",
3043
3131
  data: {
3044
3132
  type: messageType,
3045
- data: messageData
3133
+ data: messageData,
3134
+ id: this.sequencerID
3046
3135
  }
3047
3136
  });
3048
3137
  }
@@ -3105,9 +3194,11 @@ var LibMIDIInput = class extends LibMIDIPort {
3105
3194
  connectedSynths = /* @__PURE__ */ new Set();
3106
3195
  constructor(input) {
3107
3196
  super(input);
3108
- input.onmidimessage = (e) => this.connectedSynths.forEach((s) => {
3109
- if (e.data) s.sendMessage(e.data);
3110
- });
3197
+ input.onmidimessage = (e) => {
3198
+ for (const s of this.connectedSynths) {
3199
+ if (e.data) s.sendMessage(e.data);
3200
+ }
3201
+ };
3111
3202
  }
3112
3203
  // noinspection JSUnusedGlobalSymbols
3113
3204
  /**
@@ -3159,12 +3250,12 @@ var MIDIDeviceHandler = class _MIDIDeviceHandler {
3159
3250
  */
3160
3251
  outputs = /* @__PURE__ */ new Map();
3161
3252
  constructor(access) {
3162
- access.inputs.forEach((value, key) => {
3253
+ for (const [key, value] of access.inputs.entries()) {
3163
3254
  this.inputs.set(key, new LibMIDIInput(value));
3164
- });
3165
- access.outputs.forEach((value, key) => {
3255
+ }
3256
+ for (const [key, value] of access.outputs.entries()) {
3166
3257
  this.outputs.set(key, new LibMIDIOutput(value));
3167
- });
3258
+ }
3168
3259
  }
3169
3260
  /**
3170
3261
  * Attempts to initialize the MIDI Device Handler.
@@ -3183,9 +3274,9 @@ var MIDIDeviceHandler = class _MIDIDeviceHandler {
3183
3274
  consoleColors.recognized
3184
3275
  );
3185
3276
  return new _MIDIDeviceHandler(response);
3186
- } catch (e) {
3187
- util3.SpessaSynthWarn(`Could not get MIDI Devices:`, e);
3188
- throw e;
3277
+ } catch (error) {
3278
+ util3.SpessaSynthWarn(`Could not get MIDI Devices:`, error);
3279
+ throw error;
3189
3280
  }
3190
3281
  } else {
3191
3282
  util3.SpessaSynthWarn(
@@ -3214,7 +3305,7 @@ var WebMIDILinkHandler = class {
3214
3305
  return;
3215
3306
  }
3216
3307
  data.shift();
3217
- const midiData = data.map((byte) => parseInt(byte, 16));
3308
+ const midiData = data.map((byte) => Number.parseInt(byte, 16));
3218
3309
  synth.sendMessage(midiData);
3219
3310
  });
3220
3311
  SpessaSynthCoreUtils5.SpessaSynthInfo(