spessasynth_core 4.3.14 → 4.3.16

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.d.ts CHANGED
@@ -95,6 +95,19 @@ declare class SpessaLog {
95
95
  * If grouping of the log messages is allowed. Recommended for the `info` verbosity level.
96
96
  */
97
97
  static groupEnabled: boolean;
98
+ /**
99
+ * The log functions that get called with the data.
100
+ * By default, they use `console` object.
101
+ *
102
+ * This may be overridden to capture the spessasynth log data.
103
+ */
104
+ static logFunctions: {
105
+ info: (...data: any[]) => void;
106
+ warn: (...data: any[]) => void;
107
+ group: (...data: any[]) => void;
108
+ groupEnd: () => void;
109
+ groupCollapsed: (...data: any[]) => void;
110
+ };
98
111
  /**
99
112
  * Enables or disables logging.
100
113
  * @param enableInfo enables info.
package/dist/index.js CHANGED
@@ -247,6 +247,18 @@ function writeVariableLengthQuantity(number) {
247
247
  * Other.ts
248
248
  * purpose: contains some useful functions that don't belong in any specific category
249
249
  */
250
+ function splitmix32(a) {
251
+ return function() {
252
+ a |= 0;
253
+ a = a + 2654435769 | 0;
254
+ let t = a ^ a >>> 16;
255
+ t = Math.imul(t, 569420461);
256
+ t = t ^ t >>> 15;
257
+ t = Math.imul(t, 1935289751);
258
+ return ((t ^ t >>> 15) >>> 0) / 4294967296;
259
+ };
260
+ }
261
+ const randomGenerator = splitmix32(81572);
250
262
  /**
251
263
  * Formats the given seconds to nice readable time
252
264
  * @param totalSeconds time in seconds
@@ -879,6 +891,19 @@ var SpessaLog = class SpessaLog {
879
891
  */
880
892
  static groupEnabled = false;
881
893
  /**
894
+ * The log functions that get called with the data.
895
+ * By default, they use `console` object.
896
+ *
897
+ * This may be overridden to capture the spessasynth log data.
898
+ */
899
+ static logFunctions = {
900
+ info: console.info.bind(console),
901
+ warn: console.warn.bind(console),
902
+ group: console.group.bind(console),
903
+ groupEnd: console.groupEnd.bind(console),
904
+ groupCollapsed: console.groupCollapsed.bind(console)
905
+ };
906
+ /**
882
907
  * Enables or disables logging.
883
908
  * @param enableInfo enables info.
884
909
  * @param enableWarn enables warning.
@@ -890,19 +915,19 @@ var SpessaLog = class SpessaLog {
890
915
  this.groupEnabled = enableGroup;
891
916
  }
892
917
  static info(...message) {
893
- if (this.infoEnabled) console.info(...message);
918
+ if (this.infoEnabled) this.logFunctions.info(...message);
894
919
  }
895
920
  static warn(...message) {
896
- if (this.warnEnabled) console.warn(...message);
921
+ if (this.warnEnabled) this.logFunctions.warn(...message);
897
922
  }
898
923
  static group(...message) {
899
- if (this.groupEnabled) console.group(...message);
924
+ if (this.groupEnabled) this.logFunctions.group(...message);
900
925
  }
901
926
  static groupCollapsed(...message) {
902
- if (this.groupEnabled) console.groupCollapsed(...message);
927
+ if (this.groupEnabled) this.logFunctions.groupCollapsed(...message);
903
928
  }
904
929
  static groupEnd() {
905
- if (this.groupEnabled) console.groupEnd();
930
+ if (this.groupEnabled) this.logFunctions.groupEnd();
906
931
  }
907
932
  /**
908
933
  * @internal
@@ -2116,12 +2141,19 @@ var MIDIUtils = class MIDIUtils {
2116
2141
  controller: MIDIControllers.mainVolume,
2117
2142
  value: data
2118
2143
  };
2119
- case 14: return {
2120
- type: "Controller Change",
2121
- channel,
2122
- controller: MIDIControllers.pan,
2123
- value: data
2124
- };
2144
+ case 14:
2145
+ if (data === 0) return {
2146
+ type: "Channel MIDI Param",
2147
+ channel,
2148
+ parameter: "randomPan",
2149
+ value: true
2150
+ };
2151
+ return {
2152
+ type: "Controller Change",
2153
+ channel,
2154
+ controller: MIDIControllers.pan,
2155
+ value: data
2156
+ };
2125
2157
  case 18: return {
2126
2158
  type: "Controller Change",
2127
2159
  channel,
@@ -2291,12 +2323,19 @@ var MIDIUtils = class MIDIUtils {
2291
2323
  parameter: "velocitySenseOffset",
2292
2324
  value: data
2293
2325
  };
2294
- case 28: return {
2295
- type: "Controller Change",
2296
- channel,
2297
- controller: MIDIControllers.pan,
2298
- value: data
2299
- };
2326
+ case 28:
2327
+ if (data === 0) return {
2328
+ type: "Channel MIDI Param",
2329
+ channel,
2330
+ parameter: "randomPan",
2331
+ value: true
2332
+ };
2333
+ return {
2334
+ type: "Controller Change",
2335
+ channel,
2336
+ controller: MIDIControllers.pan,
2337
+ value: data
2338
+ };
2300
2339
  case 31: return {
2301
2340
  type: "Channel MIDI Param",
2302
2341
  channel,
@@ -6457,9 +6496,11 @@ function applySnapshot$1(snapshot) {
6457
6496
  const is = snapshot.insertionProcessor;
6458
6497
  this.systemExclusive(MIDIUtils.gs(64, 3, 0, [is.type >> 8, is.type & 127]));
6459
6498
  for (let i = 0; i < is.params.length; i++) if (is.params[i] !== 255) this.systemExclusive(MIDIUtils.gs(64, 3, 3 + i, [is.params[i]]));
6499
+ for (const parameter of Object.keys(snapshot.lockedMIDIParameters)) this.lockMIDIParameter(parameter, false);
6460
6500
  for (const [parameter, value] of Object.entries(snapshot.midiParameters)) this.setMIDIParameter(parameter, value);
6461
6501
  for (const [parameter, isLocked] of Object.entries(snapshot.lockedMIDIParameters)) this.lockMIDIParameter(parameter, isLocked);
6462
6502
  for (const [parameter, value] of Object.entries(snapshot.systemParameters)) this.setSystemParameter(parameter, value);
6503
+ this.updateActiveEffects();
6463
6504
  }
6464
6505
  function getSynthesizerSnapshot() {
6465
6506
  return {
@@ -10484,6 +10525,9 @@ var ConnectionBlock = class ConnectionBlock {
10484
10525
  default:
10485
10526
  SpessaLog.info(`%cFailed converting DLS articulator into SF generator: %c${this.toString()}%c\n(invalid destination)`, ConsoleColors.warn, ConsoleColors.value, ConsoleColors.unrecognized);
10486
10527
  return;
10528
+ case DLSDestinations.keyNum:
10529
+ zone.setGenerator(GeneratorTypes.keyNum, value);
10530
+ break;
10487
10531
  case DLSDestinations.pan:
10488
10532
  zone.setGenerator(GeneratorTypes.pan, value);
10489
10533
  break;
@@ -10633,7 +10677,7 @@ var ConnectionBlock = class ConnectionBlock {
10633
10677
  newAmount: -amount
10634
10678
  };
10635
10679
  case DLSDestinations.pitch: return GeneratorTypes.fineTune;
10636
- case DLSDestinations.keyNum: return GeneratorTypes.overridingRootKey;
10680
+ case DLSDestinations.keyNum: return GeneratorTypes.keyNum;
10637
10681
  case DLSDestinations.volEnvDelay: return GeneratorTypes.delayVolEnv;
10638
10682
  case DLSDestinations.volEnvAttack: return GeneratorTypes.attackVolEnv;
10639
10683
  case DLSDestinations.volEnvHold: return GeneratorTypes.holdVolEnv;
@@ -10744,7 +10788,7 @@ var DownloadableSoundsArticulation = class extends DLSVerifier {
10744
10788
  writeDword(art2Data, this.connectionBlocks.length);
10745
10789
  const out = this.connectionBlocks.map((a) => a.write());
10746
10790
  const art2 = RIFFChunk.getParts(this.mode === "dls2" ? "art2" : "art1", [art2Data, ...out]);
10747
- return RIFFChunk.getParts(this.mode === "dls2" ? "lar2" : "lart", art2, true);
10791
+ return RIFFChunk.getParts(this.mode === "dls2" ? "lar2" : "lart", art2, false, true);
10748
10792
  }
10749
10793
  /**
10750
10794
  * Converts DLS articulation into an SF zone.
@@ -11247,16 +11291,18 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
11247
11291
  const drumAliases = pgalData.slice(pgalData.currentIndex, pgalData.currentIndex + 128);
11248
11292
  pgalData.currentIndex += 128;
11249
11293
  for (let keyNum = 0; keyNum < 128; keyNum++) {
11250
- const alias = drumAliases[keyNum];
11251
- if (alias === keyNum) continue;
11252
- const region = drumInstrument.regions.find((r) => r.keyRange.max === alias && r.keyRange.min === alias);
11294
+ const source = drumAliases[keyNum];
11295
+ if (source === keyNum) continue;
11296
+ const region = drumInstrument.regions.find((r) => r.keyRange.max === source && r.keyRange.min === source);
11253
11297
  if (!region) {
11254
- SpessaLog.warn(`Invalid drum alias ${keyNum} to ${alias}: region does not exist.`);
11298
+ SpessaLog.warn(`Invalid drum alias ${keyNum} to ${source}: region does not exist.`);
11255
11299
  continue;
11256
11300
  }
11257
11301
  const copied = DownloadableSoundsRegion.copyFrom(region);
11258
11302
  copied.keyRange.max = keyNum;
11259
11303
  copied.keyRange.min = keyNum;
11304
+ const fixedKeyBlock = new ConnectionBlock(new ConnectionSource(DLSSources.none, 0, false, false), new ConnectionSource(DLSSources.none, 0, false, false), DLSDestinations.keyNum, 0, source << 16);
11305
+ copied.articulation.connectionBlocks.push(fixedKeyBlock);
11260
11306
  drumInstrument.regions.push(copied);
11261
11307
  }
11262
11308
  pgalData.currentIndex += 4;
@@ -12632,7 +12678,7 @@ function controllerChange(controller, value, sendEvent = true) {
12632
12678
  this.setBankLSB(value);
12633
12679
  break;
12634
12680
  case MIDIControllers.variationDepth:
12635
- this.synthCore.delayActive = true;
12681
+ this.synthCore.updateActiveEffects();
12636
12682
  break;
12637
12683
  case MIDIControllers.registeredParameterLSB:
12638
12684
  case MIDIControllers.registeredParameterMSB:
@@ -13149,12 +13195,12 @@ function noteOn(midiNote, velocity, emit = true) {
13149
13195
  let reverbSend = 1;
13150
13196
  let chorusSend = 1;
13151
13197
  let delaySend = 1;
13152
- if (this._midiParameters.randomPan) panOverride = Math.round(Math.random() * 1e3 - 500);
13198
+ if (this._midiParameters.randomPan) panOverride = Math.round(randomGenerator() * 1e3 - 500);
13153
13199
  if (this._drumChannel) {
13154
13200
  const p = this.drumParams[midiNote];
13155
13201
  if (!p.rxNoteOn) return;
13156
13202
  const drumPan = p.pan - 64;
13157
- if (drumPan !== 0) if (drumPan === -64) panOverride = Math.round(Math.random() * 1e3 - 500);
13203
+ if (drumPan !== 0) if (drumPan === -64) panOverride = Math.round(randomGenerator() * 1e3 - 500);
13158
13204
  else {
13159
13205
  const channelPan = (this._midiControllers[MIDIControllers.pan] >> 7) - 64;
13160
13206
  panOverride = Math.max(-63, Math.min(drumPan + channelPan, 63)) / 63 * 500 || 1;
@@ -13602,6 +13648,7 @@ function applySnapshot(snapshot) {
13602
13648
  this.setSystemParameter("presetLock", false);
13603
13649
  if (snapshot.patch) this.setPatch(snapshot.patch);
13604
13650
  this.lockedSystem = snapshot.lockedSystem;
13651
+ for (const parameter of Object.keys(snapshot.lockedMIDIParameters)) this.lockMIDIParameter(parameter, false);
13605
13652
  for (const [parameter, value] of Object.entries(snapshot.midiParameters)) this.setMIDIParameter(parameter, value);
13606
13653
  for (const [parameter, isLocked] of Object.entries(snapshot.lockedMIDIParameters)) this.lockMIDIParameter(parameter, isLocked);
13607
13654
  for (const [parameter, value] of Object.entries(snapshot.systemParameters)) this.setSystemParameter(parameter, value);
@@ -14690,7 +14737,6 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
14690
14737
  if (isReverb && this.systemParameters.reverbLock) return;
14691
14738
  if (isChorus && this.systemParameters.chorusLock) return;
14692
14739
  if (isDelay && this.systemParameters.delayLock) return;
14693
- this.delayActive ||= a3 === 64 || isDelay;
14694
14740
  switch (a3) {
14695
14741
  default:
14696
14742
  SpessaLog.gsFail("Patch Common Parameter", [a3]);
@@ -14829,6 +14875,7 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
14829
14875
  break;
14830
14876
  case 64:
14831
14877
  this.chorusProcessor.sendLevelToDelay = data;
14878
+ this.updateActiveEffects();
14832
14879
  SpessaLog.gsInfo("Chorus Send Level To Delay", data);
14833
14880
  this.callEvent("effectChange", {
14834
14881
  effect: "chorus",
@@ -14989,7 +15036,7 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
14989
15036
  return;
14990
15037
  case 25:
14991
15038
  this.insertionProcessor.sendLevelToDelay = data / 127 * EFX_SENDS_GAIN_CORRECTION;
14992
- this.delayActive = true;
15039
+ this.updateActiveEffects();
14993
15040
  SpessaLog.gsInfo("EFX Send Level to Delay", data);
14994
15041
  this.callEvent("effectChange", {
14995
15042
  effect: "insertion",
@@ -15168,8 +15215,8 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
15168
15215
  case 34: {
15169
15216
  const efx = data === 1;
15170
15217
  ch.setMIDIParameter("efxAssign", efx);
15171
- this.insertionActive ||= efx;
15172
15218
  SpessaLog.gsInfo(`EFX assign for ${channel}`, efx ? "EFX" : "BYPASS");
15219
+ this.updateActiveEffects();
15173
15220
  }
15174
15221
  }
15175
15222
  return;
@@ -20296,7 +20343,7 @@ var SynthesizerCore = class {
20296
20343
  this.resetInsertion();
20297
20344
  if (!this.drumPreset || !this.defaultPreset) return;
20298
20345
  for (const ch of this.midiChannels) ch.reset(false);
20299
- if (!this.systemParameters.delayLock) this.delayActive = this.midiChannels.some((c) => c.midiControllers[MIDIControllers.variationDepth] > 0);
20346
+ this.updateActiveEffects();
20300
20347
  }
20301
20348
  process(left, right, startIndex = 0, sampleCount = 0) {
20302
20349
  this.processSplit([[left, right]], left, right, startIndex, sampleCount);
@@ -20417,6 +20464,14 @@ var SynthesizerCore = class {
20417
20464
  callEvent(eventName, eventData) {
20418
20465
  this.eventCallbackHandler(eventName, eventData);
20419
20466
  }
20467
+ /**
20468
+ * @internal
20469
+ * Checks if we can disable insertion and delay effects.
20470
+ */
20471
+ updateActiveEffects() {
20472
+ if (!this.systemParameters.insertionEffectLock) this.insertionActive = this.midiChannels.some((c) => c.midiParameters.efxAssign);
20473
+ if (!this.systemParameters.delayLock) this.delayActive = this.midiParameters.system === "xg" ? false : this.chorusProcessor.sendLevelToDelay > 0 || this.insertionProcessor.sendLevelToDelay > 0 || this.midiChannels.some((c) => c.midiControllers[MIDIControllers.variationDepth] > 0);
20474
+ }
20420
20475
  getInsertionSnapshot() {
20421
20476
  return {
20422
20477
  type: this.insertionProcessor.type,
@@ -20431,7 +20486,6 @@ var SynthesizerCore = class {
20431
20486
  }
20432
20487
  resetInsertion() {
20433
20488
  if (this.systemParameters.insertionEffectLock) return;
20434
- this.insertionActive = false;
20435
20489
  this.insertionProcessor = this.insertionFallback;
20436
20490
  this.insertionProcessor.reset();
20437
20491
  this.resetInsertionParams();