spessasynth_core 4.2.0 → 4.2.1

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
@@ -1053,27 +1053,9 @@ interface InsertionProcessor {
1053
1053
  interface InsertionProcessorSnapshot {
1054
1054
  type: number;
1055
1055
  /**
1056
- * Parameters for the effect, 255 means "no change"
1056
+ * 20 parameters for the effect, 255 means "no change" + 3 effect sends (index 20, 21, 22)
1057
1057
  */
1058
1058
  params: Uint8Array;
1059
- /**
1060
- * 0-127
1061
- * This parameter sets the amount of insertion sound that will be sent to the reverb.
1062
- * Higher values result in more sound being sent.
1063
- */
1064
- sendLevelToReverb: number;
1065
- /**
1066
- * 0-127
1067
- * This parameter sets the amount of insertion sound that will be sent to the chorus.
1068
- * Higher values result in more sound being sent.
1069
- */
1070
- sendLevelToChorus: number;
1071
- /**
1072
- * 0-127
1073
- * This parameter sets the amount of insertion sound that will be sent to the delay.
1074
- * Higher values result in more sound being sent.
1075
- */
1076
- sendLevelToDelay: number;
1077
1059
  /**
1078
1060
  * A boolean list for channels that have the insertion effect enabled.
1079
1061
  */
@@ -3040,6 +3022,8 @@ declare class SynthesizerCore {
3040
3022
  protected portSelectChannelOffset: number;
3041
3023
  /**
3042
3024
  * For insertion snapshot tracking
3025
+ * 20 parameters (0-19) + 3 sends
3026
+ * Index to gs is Addr3 - 3 (for example EFX PARAMETER 1 is 0x03 and here it's 0)
3043
3027
  * note: 255 means "no change"
3044
3028
  * @protected
3045
3029
  */
@@ -3159,6 +3143,7 @@ declare class SynthesizerCore {
3159
3143
  * Copied callback so MIDI channels can call it.
3160
3144
  */
3161
3145
  callEvent<K extends keyof SynthProcessorEventData>(eventName: K, eventData: SynthProcessorEventData[K]): void;
3146
+ protected resetInsertionParams(): void;
3162
3147
  protected resetInsertion(): void;
3163
3148
  /**
3164
3149
  * @param volume {number} 0 to 1
@@ -4793,9 +4778,9 @@ declare class SpessaSynthSequencer {
4793
4778
  songs: BasicMIDI[];
4794
4779
  /**
4795
4780
  * The shuffled song indexes.
4796
- * This is used when shuffleMode is enabled.
4781
+ * This is used when shuffle mode is enabled.
4797
4782
  */
4798
- shuffledSongIndexes: number[];
4783
+ readonly shuffledSongIndexes: number[];
4799
4784
  /**
4800
4785
  * The synthesizer connected to the sequencer.
4801
4786
  */
@@ -4910,23 +4895,25 @@ declare class SpessaSynthSequencer {
4910
4895
  protected _songIndex: number;
4911
4896
  /**
4912
4897
  * The current song index in the song list.
4913
- * If shuffleMode is enabled, this is the index of the shuffled song list.
4898
+ * If shuffle mode is enabled, this is the index of the shuffled song list.
4914
4899
  */
4915
4900
  get songIndex(): number;
4916
4901
  /**
4917
4902
  * The current song index in the song list.
4918
- * If shuffleMode is enabled, this is the index of the shuffled song list.
4903
+ * If shuffle mode is enabled, this is the index of the shuffled song list.
4919
4904
  */
4920
4905
  set songIndex(value: number);
4921
4906
  protected _shuffleMode: boolean;
4922
4907
  /**
4923
4908
  * Controls if the sequencer should shuffle the songs in the song list.
4924
4909
  * If true, the sequencer will play the songs in a random order.
4910
+ * Songs are shuffled on a `loadNewSongList` call.
4925
4911
  */
4926
4912
  get shuffleMode(): boolean;
4927
4913
  /**
4928
4914
  * Controls if the sequencer should shuffle the songs in the song list.
4929
4915
  * If true, the sequencer will play the songs in a random order.
4916
+ * Songs are shuffled on a `loadNewSongList` call.
4930
4917
  */
4931
4918
  set shuffleMode(on: boolean);
4932
4919
  /**
@@ -5012,7 +4999,6 @@ declare class SpessaSynthSequencer {
5012
4999
  protected sendMIDINoteOn(channel: number, midiNote: number, velocity: number): void;
5013
5000
  protected sendMIDINoteOff(channel: number, midiNote: number): void;
5014
5001
  protected sendMIDICC(channel: number, type: MIDIController, value: number): void;
5015
- protected sendMIDIProgramChange(channel: number, program: number): void;
5016
5002
  /**
5017
5003
  * Sets the pitch of the given channel
5018
5004
  * @param channel usually 0-15: the channel to change pitch
package/dist/index.js CHANGED
@@ -1022,6 +1022,7 @@ var MIN_NOTE_LENGTH = 0.03;
1022
1022
  var MIN_EXCLUSIVE_LENGTH = 0.07;
1023
1023
  var SYNTHESIZER_GAIN = 1;
1024
1024
  var SPESSA_BUFSIZE = 128;
1025
+ var EFX_SENDS_GAIN_CORRECTION = 1 / Math.cos(Math.PI / 4) ** 2;
1025
1026
 
1026
1027
  // src/utils/midi_hacks.ts
1027
1028
  var XG_SFX_VOICE = 64;
@@ -2316,10 +2317,7 @@ function modifyMIDIInternal(midi, {
2316
2317
  sendAddress(targetTicks, 64, 3, 0, [
2317
2318
  p.type >> 8,
2318
2319
  p.type & 127
2319
- ]),
2320
- sendAddress(targetTicks, 64, 3, 23, [p.sendLevelToReverb]),
2321
- sendAddress(targetTicks, 64, 3, 24, [p.sendLevelToChorus]),
2322
- sendAddress(targetTicks, 64, 3, 25, [p.sendLevelToDelay])
2320
+ ])
2323
2321
  );
2324
2322
  }
2325
2323
  if (!addedGs && programChanges.length > 0) {
@@ -5066,7 +5064,7 @@ var SpessaSynthSequencer = class {
5066
5064
  songs = [];
5067
5065
  /**
5068
5066
  * The shuffled song indexes.
5069
- * This is used when shuffleMode is enabled.
5067
+ * This is used when shuffle mode is enabled.
5070
5068
  */
5071
5069
  shuffledSongIndexes = [];
5072
5070
  /**
@@ -5191,7 +5189,7 @@ var SpessaSynthSequencer = class {
5191
5189
  // noinspection JSUnusedGlobalSymbols
5192
5190
  /**
5193
5191
  * The current song index in the song list.
5194
- * If shuffleMode is enabled, this is the index of the shuffled song list.
5192
+ * If shuffle mode is enabled, this is the index of the shuffled song list.
5195
5193
  */
5196
5194
  get songIndex() {
5197
5195
  return this._songIndex;
@@ -5199,7 +5197,7 @@ var SpessaSynthSequencer = class {
5199
5197
  // noinspection JSUnusedGlobalSymbols
5200
5198
  /**
5201
5199
  * The current song index in the song list.
5202
- * If shuffleMode is enabled, this is the index of the shuffled song list.
5200
+ * If shuffle mode is enabled, this is the index of the shuffled song list.
5203
5201
  */
5204
5202
  set songIndex(value) {
5205
5203
  this._songIndex = value;
@@ -5211,6 +5209,7 @@ var SpessaSynthSequencer = class {
5211
5209
  /**
5212
5210
  * Controls if the sequencer should shuffle the songs in the song list.
5213
5211
  * If true, the sequencer will play the songs in a random order.
5212
+ * Songs are shuffled on a `loadNewSongList` call.
5214
5213
  */
5215
5214
  get shuffleMode() {
5216
5215
  return this._shuffleMode;
@@ -5219,16 +5218,10 @@ var SpessaSynthSequencer = class {
5219
5218
  /**
5220
5219
  * Controls if the sequencer should shuffle the songs in the song list.
5221
5220
  * If true, the sequencer will play the songs in a random order.
5221
+ * Songs are shuffled on a `loadNewSongList` call.
5222
5222
  */
5223
5223
  set shuffleMode(on) {
5224
5224
  this._shuffleMode = on;
5225
- if (on) {
5226
- this.shuffleSongIndexes();
5227
- this._songIndex = 0;
5228
- this.loadCurrentSong();
5229
- } else {
5230
- this._songIndex = this.shuffledSongIndexes[this._songIndex];
5231
- }
5232
5225
  }
5233
5226
  /**
5234
5227
  * Internal playback rate.
@@ -5457,7 +5450,7 @@ var SpessaSynthSequencer = class {
5457
5450
  }
5458
5451
  shuffleSongIndexes() {
5459
5452
  const indexes = this.songs.map((_, i) => i);
5460
- this.shuffledSongIndexes = [];
5453
+ this.shuffledSongIndexes.length = 0;
5461
5454
  while (indexes.length > 0) {
5462
5455
  const index = indexes[Math.floor(Math.random() * indexes.length)];
5463
5456
  this.shuffledSongIndexes.push(index);
@@ -5553,17 +5546,6 @@ var SpessaSynthSequencer = class {
5553
5546
  value
5554
5547
  ]);
5555
5548
  }
5556
- sendMIDIProgramChange(channel, program) {
5557
- if (!this.externalMIDIPlayback) {
5558
- this.synth.programChange(channel, program);
5559
- return;
5560
- }
5561
- channel %= 16;
5562
- this.sendMIDIMessage([
5563
- midiMessageTypes.programChange | channel,
5564
- program
5565
- ]);
5566
- }
5567
5549
  /**
5568
5550
  * Sets the pitch of the given channel
5569
5551
  * @param channel usually 0-15: the channel to change pitch
@@ -6511,6 +6493,7 @@ var DelayLine = class {
6511
6493
  };
6512
6494
 
6513
6495
  // src/synthesizer/audio_engine/effects/reverb/reverb.ts
6496
+ var DELAY_GAIN = 1.5;
6514
6497
  var SpessaSynthReverb = class {
6515
6498
  /**
6516
6499
  * Dattorro reverb processor.
@@ -6841,7 +6824,7 @@ var SpessaSynthReverb = class {
6841
6824
  }
6842
6825
  updateGain() {
6843
6826
  this.dattorro.gain = this._level / 348 * this.characterGainCoefficient;
6844
- this.delayGain = this._level / 127;
6827
+ this.delayGain = this._level / 127 * DELAY_GAIN;
6845
6828
  }
6846
6829
  updateTime() {
6847
6830
  const t = this._time / 127;
@@ -6858,6 +6841,7 @@ var SpessaSynthReverb = class {
6858
6841
  };
6859
6842
 
6860
6843
  // src/synthesizer/audio_engine/effects/chorus/chorus.ts
6844
+ var CHORUS_GAIN = 1.3;
6861
6845
  var SpessaSynthChorus = class {
6862
6846
  /**
6863
6847
  * Cutoff frequency
@@ -6879,7 +6863,7 @@ var SpessaSynthChorus = class {
6879
6863
  sampleRate;
6880
6864
  phase = 0;
6881
6865
  write = 0;
6882
- gain = 1;
6866
+ gain = 0.5;
6883
6867
  reverbGain = 0;
6884
6868
  delayGain = 0;
6885
6869
  depthSamples = 0;
@@ -6962,7 +6946,7 @@ var SpessaSynthChorus = class {
6962
6946
  return this._level;
6963
6947
  }
6964
6948
  set level(value) {
6965
- this.gain = value / 127;
6949
+ this.gain = value / 127 * CHORUS_GAIN;
6966
6950
  this._level = value;
6967
6951
  }
6968
6952
  process(input, outputLeft, outputRight, outputReverb, outputDelay, startIndex, sampleCount) {
@@ -7035,7 +7019,6 @@ var SpessaSynthChorus = class {
7035
7019
  };
7036
7020
  }
7037
7021
  };
7038
- var chorus_default = SpessaSynthChorus;
7039
7022
 
7040
7023
  // src/synthesizer/audio_engine/effects/delay/delay.ts
7041
7024
  var delayTimeSegments = [
@@ -7049,6 +7032,7 @@ var delayTimeSegments = [
7049
7032
  { start: 90, end: 105, timeStart: 200, resolution: 20 },
7050
7033
  { start: 105, end: 116, timeStart: 500, resolution: 50 }
7051
7034
  ];
7035
+ var DELAY_GAIN2 = 1.66;
7052
7036
  var SpessaSynthDelay = class {
7053
7037
  /**
7054
7038
  * Cutoff frequency
@@ -7117,7 +7101,7 @@ var SpessaSynthDelay = class {
7117
7101
  }
7118
7102
  set level(value) {
7119
7103
  this._level = value;
7120
- this.gain = value / 127;
7104
+ this.gain = value / 127 * DELAY_GAIN2;
7121
7105
  }
7122
7106
  _levelCenter = 127;
7123
7107
  get levelCenter() {
@@ -7253,7 +7237,7 @@ function getDefaultSynthOptions(sampleRate) {
7253
7237
  return {
7254
7238
  ...DEFAULT_SYNTH_OPTIONS,
7255
7239
  reverbProcessor: new SpessaSynthReverb(sampleRate),
7256
- chorusProcessor: new chorus_default(sampleRate),
7240
+ chorusProcessor: new SpessaSynthChorus(sampleRate),
7257
7241
  delayProcessor: new SpessaSynthDelay(sampleRate)
7258
7242
  };
7259
7243
  }
@@ -7685,9 +7669,6 @@ var SynthesizerSnapshot = class _SynthesizerSnapshot {
7685
7669
  is.type >> 8,
7686
7670
  is.type & 127
7687
7671
  ]);
7688
- sendAddress2(processor, 64, 3, 23, [is.sendLevelToReverb]);
7689
- sendAddress2(processor, 64, 3, 24, [is.sendLevelToChorus]);
7690
- sendAddress2(processor, 64, 3, 25, [is.sendLevelToDelay]);
7691
7672
  for (let i = 0; i < is.params.length; i++) {
7692
7673
  if (is.params[i] !== 255)
7693
7674
  sendAddress2(processor, 64, 3, 3 + i, [is.params[i]]);
@@ -12936,8 +12917,8 @@ function setMasterParameterInternal(parameter, value) {
12936
12917
  case "masterPan": {
12937
12918
  let pan = value;
12938
12919
  pan = pan / 2 + 0.5;
12939
- this.panLeft = 1 - pan;
12940
- this.panRight = pan;
12920
+ this.panLeft = Math.cos(Math.PI / 2 * pan);
12921
+ this.panRight = Math.sin(Math.PI / 2 * pan);
12941
12922
  break;
12942
12923
  }
12943
12924
  case "voiceCap": {
@@ -13586,12 +13567,13 @@ function handleGS(syx, channelOffset = 0) {
13586
13567
  if (addr2 === 3) {
13587
13568
  if (this.masterParameters.insertionEffectLock)
13588
13569
  return;
13570
+ if (addr3 >= 3 && addr3 <= 25)
13571
+ this.insertionParams[addr3 - 3] = data;
13589
13572
  if (addr3 >= 3 && addr3 <= 22) {
13590
13573
  this.insertionProcessor.setParameter(
13591
13574
  addr3,
13592
13575
  data
13593
13576
  );
13594
- this.insertionParams[addr3 - 3] = data;
13595
13577
  coolInfo2(`EFX Parameter ${addr3 - 2}`, data);
13596
13578
  this.callEvent("effectChange", {
13597
13579
  effect: "insertion",
@@ -13620,7 +13602,7 @@ function handleGS(syx, channelOffset = 0) {
13620
13602
  consoleColors.warn
13621
13603
  );
13622
13604
  }
13623
- this.insertionParams.fill(255);
13605
+ this.resetInsertionParams();
13624
13606
  this.insertionProcessor.reset();
13625
13607
  this.callEvent("effectChange", {
13626
13608
  effect: "insertion",
@@ -13630,7 +13612,7 @@ function handleGS(syx, channelOffset = 0) {
13630
13612
  return;
13631
13613
  }
13632
13614
  case 23: {
13633
- this.insertionProcessor.sendLevelToReverb = data / 127;
13615
+ this.insertionProcessor.sendLevelToReverb = data / 127 * EFX_SENDS_GAIN_CORRECTION;
13634
13616
  coolInfo2("EFX Send Level to Reverb", data);
13635
13617
  this.callEvent("effectChange", {
13636
13618
  effect: "insertion",
@@ -13640,7 +13622,7 @@ function handleGS(syx, channelOffset = 0) {
13640
13622
  return;
13641
13623
  }
13642
13624
  case 24: {
13643
- this.insertionProcessor.sendLevelToChorus = data / 127;
13625
+ this.insertionProcessor.sendLevelToChorus = data / 127 * EFX_SENDS_GAIN_CORRECTION;
13644
13626
  coolInfo2("EFX Send Level to Chorus", data);
13645
13627
  this.callEvent("effectChange", {
13646
13628
  effect: "insertion",
@@ -13650,7 +13632,7 @@ function handleGS(syx, channelOffset = 0) {
13650
13632
  return;
13651
13633
  }
13652
13634
  case 25: {
13653
- this.insertionProcessor.sendLevelToDelay = data / 127;
13635
+ this.insertionProcessor.sendLevelToDelay = data / 127 * EFX_SENDS_GAIN_CORRECTION;
13654
13636
  this.delayActive = true;
13655
13637
  coolInfo2("EFX Send Level to Delay", data);
13656
13638
  this.callEvent("effectChange", {
@@ -16636,11 +16618,13 @@ var SynthesizerCore = class {
16636
16618
  /**
16637
16619
  * The pan of the left channel.
16638
16620
  */
16639
- panLeft = 0.5;
16621
+ panLeft = Math.cos(Math.PI / 4);
16622
+ // Center
16640
16623
  /**
16641
16624
  * The pan of the right channel.
16642
16625
  */
16643
- panRight = 0.5;
16626
+ panRight = Math.cos(Math.PI / 4);
16627
+ // Center
16644
16628
  /**
16645
16629
  * Synth's default (reset) preset.
16646
16630
  */
@@ -16734,10 +16718,12 @@ var SynthesizerCore = class {
16734
16718
  portSelectChannelOffset = 0;
16735
16719
  /**
16736
16720
  * For insertion snapshot tracking
16721
+ * 20 parameters (0-19) + 3 sends
16722
+ * Index to gs is Addr3 - 3 (for example EFX PARAMETER 1 is 0x03 and here it's 0)
16737
16723
  * note: 255 means "no change"
16738
16724
  * @protected
16739
16725
  */
16740
- insertionParams = new Uint8Array(20).fill(255);
16726
+ insertionParams = new Uint8Array(23).fill(255);
16741
16727
  /**
16742
16728
  * Last time the priorities were assigned.
16743
16729
  * Used to prevent assigning priorities multiple times when more than one voice is triggered during a quantum.
@@ -16767,6 +16753,7 @@ var SynthesizerCore = class {
16767
16753
  this.delayProcessor = options.delayProcessor;
16768
16754
  for (const insertion of insertionList)
16769
16755
  this.registerInsertionProcessor(insertion);
16756
+ this.resetInsertionParams();
16770
16757
  this.voices = [];
16771
16758
  for (let i = 0; i < this.masterParameters.voiceCap; i++) {
16772
16759
  this.voices.push(new Voice(this.sampleRate));
@@ -17210,15 +17197,6 @@ var SynthesizerCore = class {
17210
17197
  getInsertionSnapshot() {
17211
17198
  return {
17212
17199
  type: this.insertionProcessor.type,
17213
- sendLevelToReverb: Math.floor(
17214
- this.insertionProcessor.sendLevelToReverb * 127
17215
- ),
17216
- sendLevelToChorus: Math.floor(
17217
- this.insertionProcessor.sendLevelToChorus * 127
17218
- ),
17219
- sendLevelToDelay: Math.floor(
17220
- this.insertionProcessor.sendLevelToDelay * 127
17221
- ),
17222
17200
  params: this.insertionParams.slice(),
17223
17201
  channels: this.midiChannels.map((c) => c.insertionEnabled)
17224
17202
  };
@@ -17229,15 +17207,21 @@ var SynthesizerCore = class {
17229
17207
  callEvent(eventName, eventData) {
17230
17208
  this.eventCallbackHandler(eventName, eventData);
17231
17209
  }
17210
+ resetInsertionParams() {
17211
+ this.insertionParams.fill(255);
17212
+ this.insertionParams[20] = 40;
17213
+ this.insertionParams[21] = 0;
17214
+ this.insertionParams[22] = 0;
17215
+ }
17232
17216
  resetInsertion() {
17233
17217
  if (this.masterParameters.insertionEffectLock) return;
17234
17218
  this.insertionActive = false;
17235
17219
  this.insertionProcessor = this.insertionFallback;
17236
17220
  this.insertionProcessor.reset();
17237
- this.insertionProcessor.sendLevelToReverb = 40 / 127;
17221
+ this.insertionProcessor.sendLevelToReverb = 40 / 127 * EFX_SENDS_GAIN_CORRECTION;
17238
17222
  this.insertionProcessor.sendLevelToChorus = 0;
17239
17223
  this.insertionProcessor.sendLevelToDelay = 0;
17240
- this.insertionParams.fill(255);
17224
+ this.resetInsertionParams();
17241
17225
  this.callEvent("effectChange", {
17242
17226
  effect: "insertion",
17243
17227
  parameter: 0,
@@ -17286,7 +17270,7 @@ var SynthesizerCore = class {
17286
17270
  * This is a special delay in which the delayed sounds move left and right.
17287
17271
  * It is effective when you are listening in stereo.
17288
17272
  */
17289
- default: {
17273
+ case 0: {
17290
17274
  rev.character = 0;
17291
17275
  rev.preLowpass = 3;
17292
17276
  rev.time = 80;
@@ -17336,6 +17320,10 @@ var SynthesizerCore = class {
17336
17320
  rev.delayFeedback = 32;
17337
17321
  break;
17338
17322
  }
17323
+ default: {
17324
+ SpessaSynthWarn(`Invalid reverb macro: ${macro}`);
17325
+ return;
17326
+ }
17339
17327
  }
17340
17328
  this.callEvent("effectChange", {
17341
17329
  effect: "reverb",
@@ -17369,7 +17357,7 @@ var SynthesizerCore = class {
17369
17357
  * Short Delay (FB)
17370
17358
  * This is a short delay with many repeats.
17371
17359
  */
17372
- default: {
17360
+ case 0: {
17373
17361
  chr.feedback = 0;
17374
17362
  chr.delay = 112;
17375
17363
  chr.rate = 3;
@@ -17425,6 +17413,10 @@ var SynthesizerCore = class {
17425
17413
  chr.depth = 127;
17426
17414
  break;
17427
17415
  }
17416
+ default: {
17417
+ SpessaSynthWarn(`Invalid chorus macro: ${macro}`);
17418
+ return;
17419
+ }
17428
17420
  }
17429
17421
  this.callEvent("effectChange", {
17430
17422
  effect: "chorus",
@@ -17464,8 +17456,7 @@ var SynthesizerCore = class {
17464
17456
  * but the pan positioning is different from the effects listed above.
17465
17457
  * It is effective when listening in stereo.
17466
17458
  */
17467
- case 0:
17468
- default: {
17459
+ case 0: {
17469
17460
  dly.timeCenter = 97;
17470
17461
  dly.timeRatioRight = dly.timeRatioLeft = 1;
17471
17462
  dly.feedback = 80;
@@ -17550,6 +17541,10 @@ var SynthesizerCore = class {
17550
17541
  dly.feedback = 40;
17551
17542
  break;
17552
17543
  }
17544
+ default: {
17545
+ SpessaSynthWarn(`Invalid delay macro: ${macro}`);
17546
+ return;
17547
+ }
17553
17548
  }
17554
17549
  this.callEvent("effectChange", {
17555
17550
  effect: "delay",