spessasynth_core 4.0.22 → 4.0.24

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
@@ -3007,6 +3007,32 @@ var BasicMIDI2 = class _BasicMIDI {
3007
3007
  getUsedProgramsAndKeys(soundbank) {
3008
3008
  return getUsedProgramsAndKeys(this, soundbank);
3009
3009
  }
3010
+ /**
3011
+ * Preloads all voices for this sequence in a given synth.
3012
+ * This caches all the needed voices for playing back this sequencer, resulting in a smooth playback.
3013
+ * The sequencer calls this function by default when loading the songs.
3014
+ * @param synth
3015
+ */
3016
+ preloadSynth(synth) {
3017
+ SpessaSynthGroupCollapsed(
3018
+ `%cPreloading samples...`,
3019
+ consoleColors.info
3020
+ );
3021
+ const used = this.getUsedProgramsAndKeys(synth.soundBankManager);
3022
+ used.forEach((combos, preset) => {
3023
+ SpessaSynthInfo(
3024
+ `%cPreloading used samples on %c${preset.name}%c...`,
3025
+ consoleColors.info,
3026
+ consoleColors.recognized,
3027
+ consoleColors.info
3028
+ );
3029
+ for (const combo of combos) {
3030
+ const [midiNote, velocity] = combo.split("-").map(Number);
3031
+ synth.getVoicesForPreset(preset, midiNote, velocity, midiNote);
3032
+ }
3033
+ });
3034
+ SpessaSynthGroupEnd();
3035
+ }
3010
3036
  /**
3011
3037
  * Updates all internal values of the MIDI.
3012
3038
  * @param sortEvents if the events should be sorted by ticks. Recommended to be true.
@@ -3438,6 +3464,10 @@ var BasicMIDI2 = class _BasicMIDI {
3438
3464
  loopEnd = this.lastVoiceEventTick;
3439
3465
  }
3440
3466
  this.loop = { start: loopStart, end: loopEnd, type: loopType };
3467
+ this.lastVoiceEventTick = Math.max(
3468
+ this.lastVoiceEventTick,
3469
+ this.loop.end
3470
+ );
3441
3471
  SpessaSynthInfo(
3442
3472
  `%cLoop points: start: %c${this.loop.start}%c end: %c${this.loop.end}`,
3443
3473
  consoleColors.info,
@@ -3958,33 +3988,9 @@ function loadNewSequenceInternal(parsedMidi) {
3958
3988
  this._midiData.embeddedSoundBank,
3959
3989
  this._midiData.bankOffset
3960
3990
  );
3961
- }
3962
- if (this.preload) {
3963
- SpessaSynthGroupCollapsed(
3964
- "%cPreloading samples...",
3965
- consoleColors.info
3966
- );
3967
- const used = this._midiData.getUsedProgramsAndKeys(
3968
- this.synth.soundBankManager
3969
- );
3970
- used.forEach((combos, preset) => {
3971
- SpessaSynthInfo(
3972
- `%cPreloading used samples on %c${preset.name}%c...`,
3973
- consoleColors.info,
3974
- consoleColors.recognized,
3975
- consoleColors.info
3976
- );
3977
- for (const combo of combos) {
3978
- const [midiNote, velocity] = combo.split("-").map(Number);
3979
- this.synth.getVoicesForPreset(
3980
- preset,
3981
- midiNote,
3982
- velocity,
3983
- midiNote
3984
- );
3985
- }
3986
- });
3987
- SpessaSynthGroupEnd();
3991
+ if (this.preload) {
3992
+ this._midiData.preloadSynth(this.synth);
3993
+ }
3988
3994
  }
3989
3995
  this.currentMIDIPorts = this._midiData.tracks.map((t) => t.port);
3990
3996
  this.midiPortChannelOffset = 0;
@@ -4582,8 +4588,6 @@ setResetValue(midiControllers.registeredParameterLSB, 127);
4582
4588
  setResetValue(midiControllers.registeredParameterMSB, 127);
4583
4589
  setResetValue(midiControllers.nonRegisteredParameterLSB, 127);
4584
4590
  setResetValue(midiControllers.nonRegisteredParameterMSB, 127);
4585
- var PORTAMENTO_CONTROL_UNSET = 1;
4586
- defaultMIDIControllerValues[midiControllers.portamentoControl] = PORTAMENTO_CONTROL_UNSET;
4587
4591
  setResetValue(
4588
4592
  NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel,
4589
4593
  64
@@ -4597,12 +4601,9 @@ var customResetArray = new Float32Array(CUSTOM_CONTROLLER_TABLE_SIZE);
4597
4601
  customResetArray[customControllers.modulationMultiplier] = 1;
4598
4602
 
4599
4603
  // src/synthesizer/audio_engine/engine_methods/controller_control/reset_controllers.ts
4600
- function resetAllControllersInternal(log = true) {
4601
- if (log) {
4602
- SpessaSynthInfo("%cResetting all controllers!", consoleColors.info);
4603
- }
4604
+ function resetAllControllersInternal(system = DEFAULT_SYNTH_MODE) {
4604
4605
  this.privateProps.callEvent("allControllerReset", void 0);
4605
- this.setMasterParameter("midiSystem", DEFAULT_SYNTH_MODE);
4606
+ this.setMasterParameter("midiSystem", system);
4606
4607
  this.privateProps.tunings.length = 0;
4607
4608
  for (let i = 0; i < 128; i++) {
4608
4609
  this.privateProps.tunings.push([]);
@@ -4642,27 +4643,34 @@ function resetAllControllersInternal(log = true) {
4642
4643
  }
4643
4644
  }
4644
4645
  }
4646
+ function resetPortamento(sendCC) {
4647
+ if (this.lockedControllers[midiControllers.portamentoControl]) return;
4648
+ if (this.channelSystem === "xg") {
4649
+ this.controllerChange(midiControllers.portamentoControl, 60, sendCC);
4650
+ } else {
4651
+ this.controllerChange(midiControllers.portamentoControl, 0, sendCC);
4652
+ }
4653
+ }
4645
4654
  function resetControllers(sendCCEvents = true) {
4646
4655
  this.channelOctaveTuning.fill(0);
4647
- for (let i = 0; i < defaultMIDIControllerValues.length; i++) {
4648
- if (this.lockedControllers[i]) {
4656
+ for (let cc = 0; cc < defaultMIDIControllerValues.length; cc++) {
4657
+ if (this.lockedControllers[cc]) {
4649
4658
  continue;
4650
4659
  }
4651
- const resetValue = defaultMIDIControllerValues[i];
4652
- if (this.midiControllers[i] !== resetValue && i < 127) {
4653
- if (i === midiControllers.portamentoControl) {
4654
- this.midiControllers[i] = PORTAMENTO_CONTROL_UNSET;
4655
- } else if (i !== midiControllers.portamentoControl && i !== midiControllers.dataEntryMSB && i !== midiControllers.registeredParameterMSB && i !== midiControllers.registeredParameterLSB && i !== midiControllers.nonRegisteredParameterMSB && i !== midiControllers.nonRegisteredParameterLSB) {
4660
+ const resetValue = defaultMIDIControllerValues[cc];
4661
+ if (this.midiControllers[cc] !== resetValue && cc < 127) {
4662
+ if (cc !== midiControllers.portamentoControl && cc !== midiControllers.dataEntryMSB && cc !== midiControllers.registeredParameterMSB && cc !== midiControllers.registeredParameterLSB && cc !== midiControllers.nonRegisteredParameterMSB && cc !== midiControllers.nonRegisteredParameterLSB) {
4656
4663
  this.controllerChange(
4657
- i,
4664
+ cc,
4658
4665
  resetValue >> 7,
4659
4666
  sendCCEvents
4660
4667
  );
4661
4668
  }
4662
4669
  } else {
4663
- this.midiControllers[i] = resetValue;
4670
+ this.midiControllers[cc] = resetValue;
4664
4671
  }
4665
4672
  }
4673
+ resetPortamento.call(this, sendCCEvents);
4666
4674
  this.channelVibrato = { rate: 0, depth: 0, delay: 0 };
4667
4675
  this.randomPan = false;
4668
4676
  this.sysExModulators.resetModulators();
@@ -4708,13 +4716,12 @@ function resetControllersRP15Compliant() {
4708
4716
  for (let i = 0; i < 128; i++) {
4709
4717
  const resetValue = defaultMIDIControllerValues[i];
4710
4718
  if (!nonResettableCCs.has(i) && resetValue !== this.midiControllers[i]) {
4711
- if (i === midiControllers.portamentoControl) {
4712
- this.midiControllers[i] = PORTAMENTO_CONTROL_UNSET;
4713
- } else {
4719
+ if (i !== midiControllers.portamentoControl) {
4714
4720
  this.controllerChange(i, resetValue >> 7);
4715
4721
  }
4716
4722
  }
4717
4723
  }
4724
+ resetPortamento.call(this, true);
4718
4725
  this.resetGeneratorOverrides();
4719
4726
  this.resetGeneratorOffsets();
4720
4727
  }
@@ -5166,6 +5173,15 @@ var SpessaSynthSequencer = class {
5166
5173
  this._songIndex = 0;
5167
5174
  this.shuffleSongIndexes();
5168
5175
  this.callEvent("songListChange", { newSongList: [...this.songs] });
5176
+ if (this.preload) {
5177
+ SpessaSynthGroup("%cPreloading all songs...", consoleColors.info);
5178
+ this.songs.forEach((song) => {
5179
+ if (song.embeddedSoundBank === void 0) {
5180
+ song.preloadSynth(this.synth);
5181
+ }
5182
+ });
5183
+ SpessaSynthGroupEnd();
5184
+ }
5169
5185
  this.loadCurrentSong();
5170
5186
  }
5171
5187
  callEvent(type, data) {
@@ -5202,7 +5218,7 @@ var SpessaSynthSequencer = class {
5202
5218
  this.sendMIDIAllOff();
5203
5219
  }
5204
5220
  /**
5205
- * @returns the index of the first to the current played time
5221
+ * @returns The track number of the next closest event, based on eventIndexes.
5206
5222
  */
5207
5223
  findFirstEventIndex() {
5208
5224
  let index = 0;
@@ -5302,26 +5318,27 @@ var SpessaSynthSequencer = class {
5302
5318
  }
5303
5319
  /**
5304
5320
  * Jumps to a MIDI tick without any further processing.
5305
- * @param tick The MIDI tick to jump to.
5321
+ * @param targetTicks The MIDI tick to jump to.
5306
5322
  * @protected
5307
5323
  */
5308
- jumpToTick(tick) {
5324
+ jumpToTick(targetTicks) {
5309
5325
  if (!this._midiData) {
5310
5326
  return;
5311
5327
  }
5312
- const seconds = this._midiData.midiTicksToSeconds(tick);
5328
+ this.sendMIDIAllOff();
5329
+ const seconds = this._midiData.midiTicksToSeconds(targetTicks);
5313
5330
  this.callEvent("timeChange", { newTime: seconds });
5314
5331
  this.recalculateStartTime(seconds);
5315
5332
  this.playedTime = seconds;
5316
5333
  this.eventIndexes.length = 0;
5317
5334
  for (const track of this._midiData.tracks) {
5318
- this.eventIndexes.push(
5319
- Math.max(
5320
- 0,
5321
- track.events.findIndex((e) => e.ticks >= tick)
5322
- )
5323
- );
5335
+ const idx = track.events.findIndex((e) => e.ticks >= targetTicks);
5336
+ this.eventIndexes.push(idx < 0 ? track.events.length : idx);
5324
5337
  }
5338
+ const targetTempo = this._midiData.tempoChanges.find(
5339
+ (t) => t.ticks <= targetTicks
5340
+ );
5341
+ this.oneTickToSeconds = 60 / (targetTempo.tempo * this._midiData.timeDivision);
5325
5342
  }
5326
5343
  /*
5327
5344
  SEND MIDI METHOD ABSTRACTIONS
@@ -6901,20 +6918,6 @@ var defaultSoundFont2Modulators = [
6901
6918
  ];
6902
6919
  var defaultSpessaSynthModulators = [
6903
6920
  // Custom modulators heck yeah
6904
- // Poly pressure to vibrato
6905
- new DecodedModulator(
6906
- getModSourceEnum(
6907
- modulatorCurveTypes.linear,
6908
- false,
6909
- false,
6910
- false,
6911
- modulatorSources.polyPressure
6912
- ),
6913
- 0,
6914
- generatorTypes.vibLfoToPitch,
6915
- 50,
6916
- 0
6917
- ),
6918
6921
  // Cc 92 (tremolo) to modLFO volume
6919
6922
  new DecodedModulator(
6920
6923
  getModSourceEnum(
@@ -6962,6 +6965,22 @@ var defaultSpessaSynthModulators = [
6962
6965
  3600,
6963
6966
  0
6964
6967
  ),
6968
+ // Cc 75 (decay time) to vol env decay
6969
+ new DecodedModulator(
6970
+ getModSourceEnum(
6971
+ modulatorCurveTypes.linear,
6972
+ true,
6973
+ false,
6974
+ true,
6975
+ midiControllers.decayTime
6976
+ ),
6977
+ // Linear forward bipolar cc 75
6978
+ 0,
6979
+ // No controller
6980
+ generatorTypes.decayVolEnv,
6981
+ 3600,
6982
+ 0
6983
+ ),
6965
6984
  // Cc 74 (brightness) to filterFc
6966
6985
  new DecodedModulator(
6967
6986
  getModSourceEnum(
@@ -6975,7 +6994,7 @@ var defaultSpessaSynthModulators = [
6975
6994
  0,
6976
6995
  // No controller
6977
6996
  generatorTypes.initialFilterFc,
6978
- 6e3,
6997
+ 9600,
6979
6998
  0
6980
6999
  ),
6981
7000
  // Cc 71 (filter Q) to filter Q (default resonant modulator)
@@ -6984,7 +7003,55 @@ var defaultSpessaSynthModulators = [
6984
7003
  0,
6985
7004
  // No controller
6986
7005
  generatorTypes.initialFilterQ,
6987
- 250,
7006
+ 200,
7007
+ 0
7008
+ ),
7009
+ // Cc 67 (soft pedal) to attenuation
7010
+ new DecodedModulator(
7011
+ getModSourceEnum(
7012
+ modulatorCurveTypes.switch,
7013
+ false,
7014
+ false,
7015
+ true,
7016
+ midiControllers.softPedal
7017
+ ),
7018
+ // Switch unipolar positive 67
7019
+ 0,
7020
+ // No controller
7021
+ generatorTypes.initialAttenuation,
7022
+ 50,
7023
+ 0
7024
+ ),
7025
+ // Cc 67 (soft pedal) to filter fc
7026
+ new DecodedModulator(
7027
+ getModSourceEnum(
7028
+ modulatorCurveTypes.switch,
7029
+ false,
7030
+ false,
7031
+ true,
7032
+ midiControllers.softPedal
7033
+ ),
7034
+ // Switch unipolar positive 67
7035
+ 0,
7036
+ // No controller
7037
+ generatorTypes.initialFilterFc,
7038
+ -2400,
7039
+ 0
7040
+ ),
7041
+ // Cc 8 (balance) to pan
7042
+ new DecodedModulator(
7043
+ getModSourceEnum(
7044
+ modulatorCurveTypes.linear,
7045
+ true,
7046
+ false,
7047
+ true,
7048
+ midiControllers.balance
7049
+ ),
7050
+ // Linear bipolar positive 8
7051
+ 0,
7052
+ // No controller
7053
+ generatorTypes.pan,
7054
+ 500,
6988
7055
  0
6989
7056
  )
6990
7057
  ];
@@ -8273,7 +8340,7 @@ function panAndMixVoice(voice, inputBuffer, outputLeft, outputRight, reverbLeft,
8273
8340
  }
8274
8341
 
8275
8342
  // src/synthesizer/audio_engine/engine_components/dsp_chain/lowpass_filter.ts
8276
- var FILTER_SMOOTHING_FACTOR = 0.1;
8343
+ var FILTER_SMOOTHING_FACTOR = 0.03;
8277
8344
  var LowpassFilter = class _LowpassFilter {
8278
8345
  /**
8279
8346
  * Cached coefficient calculations.
@@ -8954,7 +9021,29 @@ function getVoicesInternal(channel, midiNote, velocity, realKey) {
8954
9021
  return this.getVoicesForPreset(preset, midiNote, velocity, realKey);
8955
9022
  }
8956
9023
 
8957
- // src/synthesizer/audio_engine/engine_methods/system_exclusive.ts
9024
+ // src/synthesizer/audio_engine/engine_methods/system_exclusive/helpers.ts
9025
+ function sysExLogging(syx, channel, value, what, units) {
9026
+ SpessaSynthInfo(
9027
+ `%cChannel %c${channel}%c ${what}. %c${value} ${units}%c, with %c${arrayToHexString(syx)}`,
9028
+ consoleColors.info,
9029
+ consoleColors.recognized,
9030
+ consoleColors.info,
9031
+ consoleColors.value,
9032
+ consoleColors.info,
9033
+ consoleColors.value
9034
+ );
9035
+ }
9036
+ function sysExNotRecognized(syx, what) {
9037
+ SpessaSynthInfo(
9038
+ `%cUnrecognized %c${what} %cSysEx: %c${arrayToHexString(syx)}`,
9039
+ consoleColors.warn,
9040
+ consoleColors.recognized,
9041
+ consoleColors.warn,
9042
+ consoleColors.unrecognized
9043
+ );
9044
+ }
9045
+
9046
+ // src/synthesizer/audio_engine/engine_methods/system_exclusive/handle_gm.ts
8958
9047
  function getTuning(byte1, byte2, byte3) {
8959
9048
  const midiNote = byte1;
8960
9049
  const fraction = byte2 << 7 | byte3;
@@ -8963,900 +9052,971 @@ function getTuning(byte1, byte2, byte3) {
8963
9052
  }
8964
9053
  return { midiNote, centTuning: fraction * 61e-4 };
8965
9054
  }
8966
- function systemExclusiveInternal(syx, channelOffset = 0) {
8967
- const manufacturer = syx[0];
8968
- if (this.privateProps.masterParameters.deviceID !== ALL_CHANNELS_OR_DIFFERENT_ACTION && syx[1] !== 127) {
8969
- if (this.privateProps.masterParameters.deviceID !== syx[1]) {
8970
- return;
9055
+ function handleGM(syx, channelOffset = 0) {
9056
+ switch (syx[2]) {
9057
+ case 4: {
9058
+ let cents;
9059
+ switch (syx[3]) {
9060
+ case 1: {
9061
+ const vol = syx[5] << 7 | syx[4];
9062
+ this.setMIDIVolume(vol / 16384);
9063
+ SpessaSynthInfo(
9064
+ `%cMaster Volume. Volume: %c${vol}`,
9065
+ consoleColors.info,
9066
+ consoleColors.value
9067
+ );
9068
+ break;
9069
+ }
9070
+ case 2: {
9071
+ const balance = syx[5] << 7 | syx[4];
9072
+ const pan = (balance - 8192) / 8192;
9073
+ this.setMasterParameter("masterPan", pan);
9074
+ SpessaSynthInfo(
9075
+ `%cMaster Pan. Pan: %c${pan}`,
9076
+ consoleColors.info,
9077
+ consoleColors.value
9078
+ );
9079
+ break;
9080
+ }
9081
+ case 3: {
9082
+ const tuningValue = (syx[5] << 7 | syx[6]) - 8192;
9083
+ cents = Math.floor(tuningValue / 81.92);
9084
+ this.setMasterTuning(cents);
9085
+ SpessaSynthInfo(
9086
+ `%cMaster Fine Tuning. Cents: %c${cents}`,
9087
+ consoleColors.info,
9088
+ consoleColors.value
9089
+ );
9090
+ break;
9091
+ }
9092
+ case 4: {
9093
+ const semitones = syx[5] - 64;
9094
+ cents = semitones * 100;
9095
+ this.setMasterTuning(cents);
9096
+ SpessaSynthInfo(
9097
+ `%cMaster Coarse Tuning. Cents: %c${cents}`,
9098
+ consoleColors.info,
9099
+ consoleColors.value
9100
+ );
9101
+ break;
9102
+ }
9103
+ default:
9104
+ SpessaSynthInfo(
9105
+ `%cUnrecognized MIDI Device Control Real-time message: %c${arrayToHexString(syx)}`,
9106
+ consoleColors.warn,
9107
+ consoleColors.unrecognized
9108
+ );
9109
+ }
9110
+ break;
8971
9111
  }
8972
- }
8973
- function niceLogging(channel, value, what, units) {
8974
- SpessaSynthInfo(
8975
- `%cChannel %c${channel}%c ${what}. %c${value} ${units}%c, with %c${arrayToHexString(syx)}`,
8976
- consoleColors.info,
8977
- consoleColors.recognized,
8978
- consoleColors.info,
8979
- consoleColors.value,
8980
- consoleColors.info,
8981
- consoleColors.value
8982
- );
8983
- }
8984
- switch (manufacturer) {
8985
- default:
8986
- SpessaSynthInfo(
8987
- `%cUnrecognized SysEx: %c${arrayToHexString(syx)}`,
8988
- consoleColors.warn,
8989
- consoleColors.unrecognized
8990
- );
9112
+ case 9:
9113
+ if (syx[3] === 1) {
9114
+ SpessaSynthInfo("%cGM1 system on", consoleColors.info);
9115
+ this.resetAllControllers("gm");
9116
+ } else if (syx[3] === 3) {
9117
+ SpessaSynthInfo("%cGM2 system on", consoleColors.info);
9118
+ this.resetAllControllers("gm2");
9119
+ } else {
9120
+ SpessaSynthInfo(
9121
+ "%cGM system off, defaulting to GS",
9122
+ consoleColors.info
9123
+ );
9124
+ this.setMasterParameter("midiSystem", "gs");
9125
+ }
8991
9126
  break;
8992
- // Non realtime GM
8993
- case 126:
8994
- // Realtime GM
8995
- case 127:
8996
- switch (syx[2]) {
8997
- case 4: {
8998
- let cents;
8999
- switch (syx[3]) {
9000
- case 1: {
9001
- const vol = syx[5] << 7 | syx[4];
9002
- this.setMIDIVolume(vol / 16384);
9003
- SpessaSynthInfo(
9004
- `%cMaster Volume. Volume: %c${vol}`,
9005
- consoleColors.info,
9006
- consoleColors.value
9007
- );
9008
- break;
9009
- }
9010
- case 2: {
9011
- const balance = syx[5] << 7 | syx[4];
9012
- const pan = (balance - 8192) / 8192;
9013
- this.setMasterParameter("masterPan", pan);
9014
- SpessaSynthInfo(
9015
- `%cMaster Pan. Pan: %c${pan}`,
9016
- consoleColors.info,
9017
- consoleColors.value
9018
- );
9019
- break;
9020
- }
9021
- case 3: {
9022
- const tuningValue = (syx[5] << 7 | syx[6]) - 8192;
9023
- cents = Math.floor(tuningValue / 81.92);
9024
- this.setMasterTuning(cents);
9025
- SpessaSynthInfo(
9026
- `%cMaster Fine Tuning. Cents: %c${cents}`,
9027
- consoleColors.info,
9028
- consoleColors.value
9029
- );
9030
- break;
9031
- }
9032
- case 4: {
9033
- const semitones = syx[5] - 64;
9034
- cents = semitones * 100;
9035
- this.setMasterTuning(cents);
9036
- SpessaSynthInfo(
9037
- `%cMaster Coarse Tuning. Cents: %c${cents}`,
9038
- consoleColors.info,
9039
- consoleColors.value
9040
- );
9041
- break;
9042
- }
9043
- default:
9044
- SpessaSynthInfo(
9045
- `%cUnrecognized MIDI Device Control Real-time message: %c${arrayToHexString(syx)}`,
9046
- consoleColors.warn,
9047
- consoleColors.unrecognized
9048
- );
9127
+ // MIDI Tuning standard
9128
+ // https://midi.org/midi-tuning-updated-specification
9129
+ case 8: {
9130
+ let currentMessageIndex = 4;
9131
+ switch (syx[3]) {
9132
+ // Bulk tuning dump: all 128 notes
9133
+ case 1: {
9134
+ const program = syx[currentMessageIndex++];
9135
+ const tuningName = readBinaryString(
9136
+ syx,
9137
+ 16,
9138
+ currentMessageIndex
9139
+ );
9140
+ currentMessageIndex += 16;
9141
+ if (syx.length < 384) {
9142
+ SpessaSynthWarn(
9143
+ `The Bulk Tuning Dump is too short! (${syx.length} bytes, at least 384 are expected)`
9144
+ );
9145
+ return;
9146
+ }
9147
+ for (let i = 0; i < 128; i++) {
9148
+ this.privateProps.tunings[program][i] = getTuning(
9149
+ syx[currentMessageIndex++],
9150
+ syx[currentMessageIndex++],
9151
+ syx[currentMessageIndex++]
9152
+ );
9049
9153
  }
9154
+ SpessaSynthInfo(
9155
+ `%cBulk Tuning Dump %c${tuningName}%c Program: %c${program}`,
9156
+ consoleColors.info,
9157
+ consoleColors.value,
9158
+ consoleColors.info,
9159
+ consoleColors.recognized
9160
+ );
9161
+ break;
9162
+ }
9163
+ // Single note change
9164
+ // Single note change bank
9165
+ case 2:
9166
+ case 7: {
9167
+ if (syx[3] === 7) {
9168
+ currentMessageIndex++;
9169
+ }
9170
+ const tuningProgram = syx[currentMessageIndex++];
9171
+ const numberOfChanges = syx[currentMessageIndex++];
9172
+ for (let i = 0; i < numberOfChanges; i++) {
9173
+ this.privateProps.tunings[tuningProgram][syx[currentMessageIndex++]] = getTuning(
9174
+ syx[currentMessageIndex++],
9175
+ syx[currentMessageIndex++],
9176
+ syx[currentMessageIndex++]
9177
+ );
9178
+ }
9179
+ SpessaSynthInfo(
9180
+ `%cSingle Note Tuning. Program: %c${tuningProgram}%c Keys affected: %c${numberOfChanges}`,
9181
+ consoleColors.info,
9182
+ consoleColors.recognized,
9183
+ consoleColors.info,
9184
+ consoleColors.recognized
9185
+ );
9050
9186
  break;
9051
9187
  }
9188
+ // Octave tuning (1 byte)
9189
+ // And octave tuning (2 bytes)
9052
9190
  case 9:
9053
- if (syx[3] === 1) {
9054
- SpessaSynthInfo("%cGM1 system on", consoleColors.info);
9055
- this.setMasterParameter("midiSystem", "gm");
9056
- } else if (syx[3] === 3) {
9057
- SpessaSynthInfo("%cGM2 system on", consoleColors.info);
9058
- this.setMasterParameter("midiSystem", "gm2");
9191
+ case 8: {
9192
+ const newOctaveTuning = new Int8Array(12);
9193
+ if (syx[3] === 8) {
9194
+ for (let i = 0; i < 12; i++) {
9195
+ newOctaveTuning[i] = syx[7 + i] - 64;
9196
+ }
9059
9197
  } else {
9060
- SpessaSynthInfo(
9061
- "%cGM system off, defaulting to GS",
9062
- consoleColors.info
9198
+ for (let i = 0; i < 24; i += 2) {
9199
+ const tuning = (syx[7 + i] << 7 | syx[8 + i]) - 8192;
9200
+ newOctaveTuning[i / 2] = Math.floor(tuning / 81.92);
9201
+ }
9202
+ }
9203
+ if ((syx[4] & 1) === 1) {
9204
+ this.midiChannels[14 + channelOffset].setOctaveTuning(
9205
+ newOctaveTuning
9063
9206
  );
9064
- this.setMasterParameter("midiSystem", "gs");
9065
9207
  }
9208
+ if ((syx[4] >> 1 & 1) === 1) {
9209
+ this.midiChannels[15 + channelOffset].setOctaveTuning(
9210
+ newOctaveTuning
9211
+ );
9212
+ }
9213
+ for (let i = 0; i < 7; i++) {
9214
+ const bit = syx[5] >> i & 1;
9215
+ if (bit === 1) {
9216
+ this.midiChannels[7 + i + channelOffset].setOctaveTuning(newOctaveTuning);
9217
+ }
9218
+ }
9219
+ for (let i = 0; i < 7; i++) {
9220
+ const bit = syx[6] >> i & 1;
9221
+ if (bit === 1) {
9222
+ this.midiChannels[i + channelOffset].setOctaveTuning(newOctaveTuning);
9223
+ }
9224
+ }
9225
+ SpessaSynthInfo(
9226
+ `%cMIDI Octave Scale ${syx[3] === 8 ? "(1 byte)" : "(2 bytes)"} tuning via Tuning: %c${newOctaveTuning.join(" ")}`,
9227
+ consoleColors.info,
9228
+ consoleColors.value
9229
+ );
9066
9230
  break;
9067
- // MIDI Tuning standard
9068
- // https://midi.org/midi-tuning-updated-specification
9069
- case 8: {
9070
- let currentMessageIndex = 4;
9071
- switch (syx[3]) {
9072
- // Bulk tuning dump: all 128 notes
9073
- case 1: {
9074
- const program = syx[currentMessageIndex++];
9075
- const tuningName = readBinaryString(
9076
- syx,
9077
- 16,
9078
- currentMessageIndex
9079
- );
9080
- currentMessageIndex += 16;
9081
- if (syx.length < 384) {
9082
- SpessaSynthWarn(
9083
- `The Bulk Tuning Dump is too short! (${syx.length} bytes, at least 384 are expected)`
9231
+ }
9232
+ default:
9233
+ sysExNotRecognized(syx, "MIDI Tuning Standard");
9234
+ break;
9235
+ }
9236
+ break;
9237
+ }
9238
+ default:
9239
+ sysExNotRecognized(syx, "General MIDI");
9240
+ }
9241
+ }
9242
+
9243
+ // src/synthesizer/audio_engine/engine_methods/system_exclusive/handle_gs.ts
9244
+ function handleGS(syx, channelOffset = 0) {
9245
+ if (syx[3] === 18) {
9246
+ switch (syx[2]) {
9247
+ case 66: {
9248
+ const messageValue = syx[7];
9249
+ if (syx[4] === 64 || syx[4] === 0 && syx[6] === 127) {
9250
+ if ((syx[5] & 16) > 0) {
9251
+ const channel = [
9252
+ 9,
9253
+ 0,
9254
+ 1,
9255
+ 2,
9256
+ 3,
9257
+ 4,
9258
+ 5,
9259
+ 6,
9260
+ 7,
9261
+ 8,
9262
+ 10,
9263
+ 11,
9264
+ 12,
9265
+ 13,
9266
+ 14,
9267
+ 15
9268
+ ][syx[5] & 15] + channelOffset;
9269
+ const channelObject = this.midiChannels[channel];
9270
+ switch (syx[6]) {
9271
+ default:
9272
+ sysExNotRecognized(syx, "Roland GS");
9273
+ break;
9274
+ case 21: {
9275
+ const isDrums = messageValue > 0 && syx[5] >> 4 > 0;
9276
+ channelObject.setGSDrums(isDrums);
9277
+ SpessaSynthInfo(
9278
+ `%cChannel %c${channel}%c ${isDrums ? "is now a drum channel" : "now isn't a drum channel"}%c via: %c${arrayToHexString(syx)}`,
9279
+ consoleColors.info,
9280
+ consoleColors.value,
9281
+ consoleColors.recognized,
9282
+ consoleColors.info,
9283
+ consoleColors.value
9084
9284
  );
9085
9285
  return;
9086
9286
  }
9087
- for (let i = 0; i < 128; i++) {
9088
- this.privateProps.tunings[program][i] = getTuning(
9089
- syx[currentMessageIndex++],
9090
- syx[currentMessageIndex++],
9091
- syx[currentMessageIndex++]
9287
+ case 22: {
9288
+ const keyShift = messageValue - 64;
9289
+ channelObject.setCustomController(
9290
+ customControllers.channelKeyShift,
9291
+ keyShift
9092
9292
  );
9093
- }
9094
- SpessaSynthInfo(
9095
- `%cBulk Tuning Dump %c${tuningName}%c Program: %c${program}`,
9096
- consoleColors.info,
9097
- consoleColors.value,
9098
- consoleColors.info,
9099
- consoleColors.recognized
9100
- );
9101
- break;
9102
- }
9103
- // Single note change
9104
- // Single note change bank
9105
- case 2:
9106
- case 7: {
9107
- if (syx[3] === 7) {
9108
- currentMessageIndex++;
9109
- }
9110
- const tuningProgram = syx[currentMessageIndex++];
9111
- const numberOfChanges = syx[currentMessageIndex++];
9112
- for (let i = 0; i < numberOfChanges; i++) {
9113
- this.privateProps.tunings[tuningProgram][syx[currentMessageIndex++]] = getTuning(
9114
- syx[currentMessageIndex++],
9115
- syx[currentMessageIndex++],
9116
- syx[currentMessageIndex++]
9293
+ sysExLogging(
9294
+ syx,
9295
+ channel,
9296
+ keyShift,
9297
+ "key shift",
9298
+ "keys"
9117
9299
  );
9300
+ return;
9118
9301
  }
9119
- SpessaSynthInfo(
9120
- `%cSingle Note Tuning. Program: %c${tuningProgram}%c Keys affected: %c${numberOfChanges}`,
9121
- consoleColors.info,
9122
- consoleColors.recognized,
9123
- consoleColors.info,
9124
- consoleColors.recognized
9125
- );
9126
- break;
9127
- }
9128
- // Octave tuning (1 byte)
9129
- // And octave tuning (2 bytes)
9130
- case 9:
9131
- case 8: {
9132
- const newOctaveTuning = new Int8Array(12);
9133
- if (syx[3] === 8) {
9134
- for (let i = 0; i < 12; i++) {
9135
- newOctaveTuning[i] = syx[7 + i] - 64;
9136
- }
9137
- } else {
9138
- for (let i = 0; i < 24; i += 2) {
9139
- const tuning = (syx[7 + i] << 7 | syx[8 + i]) - 8192;
9140
- newOctaveTuning[i / 2] = Math.floor(
9141
- tuning / 81.92
9302
+ // Pan position
9303
+ case 28: {
9304
+ const panPosition = messageValue;
9305
+ if (panPosition === 0) {
9306
+ channelObject.randomPan = true;
9307
+ SpessaSynthInfo(
9308
+ `%cRandom pan is set to %cON%c for %c${channel}`,
9309
+ consoleColors.info,
9310
+ consoleColors.recognized,
9311
+ consoleColors.info,
9312
+ consoleColors.value
9313
+ );
9314
+ } else {
9315
+ channelObject.randomPan = false;
9316
+ channelObject.controllerChange(
9317
+ midiControllers.pan,
9318
+ panPosition
9142
9319
  );
9143
9320
  }
9321
+ break;
9144
9322
  }
9145
- if ((syx[4] & 1) === 1) {
9146
- this.midiChannels[14 + channelOffset].setOctaveTuning(newOctaveTuning);
9147
- }
9148
- if ((syx[4] >> 1 & 1) === 1) {
9149
- this.midiChannels[15 + channelOffset].setOctaveTuning(newOctaveTuning);
9150
- }
9151
- for (let i = 0; i < 7; i++) {
9152
- const bit = syx[5] >> i & 1;
9153
- if (bit === 1) {
9154
- this.midiChannels[7 + i + channelOffset].setOctaveTuning(newOctaveTuning);
9155
- }
9156
- }
9157
- for (let i = 0; i < 7; i++) {
9158
- const bit = syx[6] >> i & 1;
9159
- if (bit === 1) {
9160
- this.midiChannels[i + channelOffset].setOctaveTuning(newOctaveTuning);
9323
+ // Chorus send
9324
+ case 33:
9325
+ channelObject.controllerChange(
9326
+ midiControllers.chorusDepth,
9327
+ messageValue
9328
+ );
9329
+ break;
9330
+ // Reverb send
9331
+ case 34:
9332
+ channelObject.controllerChange(
9333
+ midiControllers.reverbDepth,
9334
+ messageValue
9335
+ );
9336
+ break;
9337
+ case 64:
9338
+ case 65:
9339
+ case 66:
9340
+ case 67:
9341
+ case 68:
9342
+ case 69:
9343
+ case 70:
9344
+ case 71:
9345
+ case 72:
9346
+ case 73:
9347
+ case 74:
9348
+ case 75: {
9349
+ const tuningBytes = syx.length - 9;
9350
+ const newTuning = new Int8Array(12);
9351
+ for (let i = 0; i < tuningBytes; i++) {
9352
+ newTuning[i] = syx[i + 7] - 64;
9161
9353
  }
9354
+ channelObject.setOctaveTuning(newTuning);
9355
+ const cents = messageValue - 64;
9356
+ sysExLogging(
9357
+ syx,
9358
+ channel,
9359
+ newTuning.join(" "),
9360
+ "octave scale tuning",
9361
+ "cents"
9362
+ );
9363
+ channelObject.setTuning(cents);
9364
+ break;
9162
9365
  }
9163
- SpessaSynthInfo(
9164
- `%cMIDI Octave Scale ${syx[3] === 8 ? "(1 byte)" : "(2 bytes)"} tuning via Tuning: %c${newOctaveTuning.join(" ")}`,
9165
- consoleColors.info,
9166
- consoleColors.value
9167
- );
9168
- break;
9169
9366
  }
9170
- default:
9171
- SpessaSynthInfo(
9172
- `%cUnrecognized MIDI Tuning standard message: %c${arrayToHexString(syx)}`,
9173
- consoleColors.warn,
9174
- consoleColors.unrecognized
9175
- );
9176
- break;
9177
- }
9178
- break;
9179
- }
9180
- default:
9181
- SpessaSynthInfo(
9182
- `%cUnrecognized MIDI Realtime/non realtime message: %c${arrayToHexString(syx)}`,
9183
- consoleColors.warn,
9184
- consoleColors.unrecognized
9185
- );
9186
- }
9187
- break;
9188
- // This is a roland sysex
9189
- // http://www.bandtrax.com.au/sysex.htm
9190
- // https://cdn.roland.com/assets/media/pdf/AT-20R_30R_MI.pdf
9191
- case 65:
9192
- if (syx[3] === 18) {
9193
- let notRecognized2 = function() {
9194
- SpessaSynthInfo(
9195
- `%cUnrecognized Roland %cGS %cSysEx: %c${arrayToHexString(syx)}`,
9196
- consoleColors.warn,
9197
- consoleColors.recognized,
9198
- consoleColors.warn,
9199
- consoleColors.unrecognized
9200
- );
9201
- };
9202
- var notRecognized = notRecognized2;
9203
- switch (syx[2]) {
9204
- case 66: {
9205
- const messageValue = syx[7];
9206
- if (syx[4] === 64 || syx[4] === 0 && syx[6] === 127) {
9207
- if ((syx[5] & 16) > 0) {
9208
- const channel = [
9209
- 9,
9210
- 0,
9211
- 1,
9212
- 2,
9213
- 3,
9214
- 4,
9215
- 5,
9216
- 6,
9217
- 7,
9218
- 8,
9219
- 10,
9220
- 11,
9221
- 12,
9222
- 13,
9223
- 14,
9224
- 15
9225
- ][syx[5] & 15] + channelOffset;
9226
- const channelObject = this.midiChannels[channel];
9227
- switch (syx[6]) {
9228
- default:
9229
- notRecognized2();
9230
- break;
9231
- case 21: {
9232
- const isDrums = messageValue > 0 && syx[5] >> 4 > 0;
9233
- channelObject.setGSDrums(isDrums);
9234
- SpessaSynthInfo(
9235
- `%cChannel %c${channel}%c ${isDrums ? "is now a drum channel" : "now isn't a drum channel"}%c via: %c${arrayToHexString(syx)}`,
9236
- consoleColors.info,
9237
- consoleColors.value,
9238
- consoleColors.recognized,
9239
- consoleColors.info,
9240
- consoleColors.value
9241
- );
9242
- return;
9243
- }
9244
- case 22: {
9245
- const keyShift = messageValue - 64;
9246
- channelObject.setCustomController(
9247
- customControllers.channelKeyShift,
9248
- keyShift
9249
- );
9250
- niceLogging(
9251
- channel,
9252
- keyShift,
9253
- "key shift",
9254
- "keys"
9255
- );
9256
- return;
9257
- }
9258
- // Pan position
9259
- case 28: {
9260
- const panPosition = messageValue;
9261
- if (panPosition === 0) {
9262
- channelObject.randomPan = true;
9263
- SpessaSynthInfo(
9264
- `%cRandom pan is set to %cON%c for %c${channel}`,
9265
- consoleColors.info,
9266
- consoleColors.recognized,
9267
- consoleColors.info,
9268
- consoleColors.value
9269
- );
9270
- } else {
9271
- channelObject.randomPan = false;
9272
- channelObject.controllerChange(
9273
- midiControllers.pan,
9274
- panPosition
9275
- );
9276
- }
9277
- break;
9278
- }
9279
- // Chorus send
9280
- case 33:
9367
+ return;
9368
+ } else if ((syx[5] & 32) > 0) {
9369
+ const channel = [
9370
+ 9,
9371
+ 0,
9372
+ 1,
9373
+ 2,
9374
+ 3,
9375
+ 4,
9376
+ 5,
9377
+ 6,
9378
+ 7,
9379
+ 8,
9380
+ 10,
9381
+ 11,
9382
+ 12,
9383
+ 13,
9384
+ 14,
9385
+ 15
9386
+ ][syx[5] & 15] + channelOffset;
9387
+ const channelObject = this.midiChannels[channel];
9388
+ const centeredValue = messageValue - 64;
9389
+ const normalizedValue = centeredValue / 64;
9390
+ const normalizedNotCentered = messageValue / 128;
9391
+ const setupReceivers = (source, sourceName, bipolar = false) => {
9392
+ switch (syx[6] & 15) {
9393
+ case 0:
9394
+ if (source === NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel) {
9281
9395
  channelObject.controllerChange(
9282
- midiControllers.chorusDepth,
9283
- messageValue
9396
+ midiControllers.registeredParameterMSB,
9397
+ 0
9284
9398
  );
9285
- break;
9286
- // Reverb send
9287
- case 34:
9288
9399
  channelObject.controllerChange(
9289
- midiControllers.reverbDepth,
9290
- messageValue
9291
- );
9292
- break;
9293
- case 64:
9294
- case 65:
9295
- case 66:
9296
- case 67:
9297
- case 68:
9298
- case 69:
9299
- case 70:
9300
- case 71:
9301
- case 72:
9302
- case 73:
9303
- case 74:
9304
- case 75: {
9305
- const tuningBytes = syx.length - 9;
9306
- const newTuning = new Int8Array(12);
9307
- for (let i = 0; i < tuningBytes; i++) {
9308
- newTuning[i] = syx[i + 7] - 64;
9309
- }
9310
- channelObject.setOctaveTuning(
9311
- newTuning
9312
- );
9313
- const cents = messageValue - 64;
9314
- niceLogging(
9315
- channel,
9316
- newTuning.join(" "),
9317
- "octave scale tuning",
9318
- "cents"
9319
- );
9320
- channelObject.setTuning(cents);
9321
- break;
9322
- }
9323
- }
9324
- return;
9325
- } else if ((syx[5] & 32) > 0) {
9326
- const channel = [
9327
- 9,
9328
- 0,
9329
- 1,
9330
- 2,
9331
- 3,
9332
- 4,
9333
- 5,
9334
- 6,
9335
- 7,
9336
- 8,
9337
- 10,
9338
- 11,
9339
- 12,
9340
- 13,
9341
- 14,
9342
- 15
9343
- ][syx[5] & 15] + channelOffset;
9344
- const channelObject = this.midiChannels[channel];
9345
- const centeredValue = messageValue - 64;
9346
- const normalizedValue = centeredValue / 64;
9347
- const normalizedNotCentered = messageValue / 128;
9348
- const setupReceivers = (source, sourceName, bipolar = false) => {
9349
- switch (syx[6] & 15) {
9350
- case 0:
9351
- if (source === NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel) {
9352
- channelObject.controllerChange(
9353
- midiControllers.registeredParameterMSB,
9354
- 0
9355
- );
9356
- channelObject.controllerChange(
9357
- midiControllers.registeredParameterLSB,
9358
- 0
9359
- );
9360
- channelObject.controllerChange(
9361
- midiControllers.dataEntryMSB,
9362
- Math.floor(centeredValue)
9363
- );
9364
- } else {
9365
- channelObject.sysExModulators.setModulator(
9366
- source,
9367
- generatorTypes.fineTune,
9368
- centeredValue * 100,
9369
- bipolar
9370
- );
9371
- niceLogging(
9372
- channel,
9373
- centeredValue,
9374
- `${sourceName} pitch control`,
9375
- "semitones"
9376
- );
9377
- }
9378
- break;
9379
- case 1:
9380
- channelObject.sysExModulators.setModulator(
9381
- source,
9382
- generatorTypes.initialFilterFc,
9383
- normalizedValue * 9600,
9384
- bipolar
9385
- );
9386
- niceLogging(
9387
- channel,
9388
- normalizedValue * 9600,
9389
- `${sourceName} pitch control`,
9390
- "cents"
9391
- );
9392
- break;
9393
- case 2:
9394
- channelObject.sysExModulators.setModulator(
9395
- source,
9396
- generatorTypes.initialAttenuation,
9397
- normalizedValue * 960,
9398
- // Spec says "100%" so 960cB in sf2
9399
- bipolar
9400
- );
9401
- niceLogging(
9402
- channel,
9403
- normalizedValue * 960,
9404
- `${sourceName} amplitude`,
9405
- "cB"
9406
- );
9407
- break;
9408
- // Rate control is ignored as it is in hertz
9409
- case 4:
9410
- channelObject.sysExModulators.setModulator(
9411
- source,
9412
- generatorTypes.vibLfoToPitch,
9413
- normalizedNotCentered * 600,
9414
- bipolar
9415
- );
9416
- niceLogging(
9417
- channel,
9418
- normalizedNotCentered * 600,
9419
- `${sourceName} LFO1 pitch depth`,
9420
- "cents"
9421
- );
9422
- break;
9423
- case 5:
9424
- channelObject.sysExModulators.setModulator(
9425
- source,
9426
- generatorTypes.vibLfoToFilterFc,
9427
- normalizedNotCentered * 2400,
9428
- bipolar
9429
- );
9430
- niceLogging(
9431
- channel,
9432
- normalizedNotCentered * 2400,
9433
- `${sourceName} LFO1 filter depth`,
9434
- "cents"
9435
- );
9436
- break;
9437
- case 6:
9438
- channelObject.sysExModulators.setModulator(
9439
- source,
9440
- generatorTypes.vibLfoToVolume,
9441
- normalizedValue * 960,
9442
- bipolar
9443
- );
9444
- niceLogging(
9445
- channel,
9446
- normalizedValue * 960,
9447
- `${sourceName} LFO1 amplitude depth`,
9448
- "cB"
9449
- );
9450
- break;
9451
- // Rate control is ignored as it is in hertz
9452
- case 8:
9453
- channelObject.sysExModulators.setModulator(
9454
- source,
9455
- generatorTypes.modLfoToPitch,
9456
- normalizedNotCentered * 600,
9457
- bipolar
9458
- );
9459
- niceLogging(
9460
- channel,
9461
- normalizedNotCentered * 600,
9462
- `${sourceName} LFO2 pitch depth`,
9463
- "cents"
9464
- );
9465
- break;
9466
- case 9:
9467
- channelObject.sysExModulators.setModulator(
9468
- source,
9469
- generatorTypes.modLfoToFilterFc,
9470
- normalizedNotCentered * 2400,
9471
- bipolar
9472
- );
9473
- niceLogging(
9474
- channel,
9475
- normalizedNotCentered * 2400,
9476
- `${sourceName} LFO2 filter depth`,
9477
- "cents"
9478
- );
9479
- break;
9480
- case 10:
9481
- channelObject.sysExModulators.setModulator(
9482
- source,
9483
- generatorTypes.modLfoToVolume,
9484
- normalizedValue * 960,
9485
- bipolar
9486
- );
9487
- niceLogging(
9488
- channel,
9489
- normalizedValue * 960,
9490
- `${sourceName} LFO2 amplitude depth`,
9491
- "cB"
9492
- );
9493
- break;
9494
- }
9495
- };
9496
- switch (syx[6] & 240) {
9497
- default:
9498
- notRecognized2();
9499
- break;
9500
- case 0:
9501
- setupReceivers(
9502
- midiControllers.modulationWheel,
9503
- "mod wheel"
9504
- );
9505
- break;
9506
- case 16:
9507
- setupReceivers(
9508
- NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel,
9509
- "pitch wheel",
9510
- true
9511
- );
9512
- break;
9513
- case 32:
9514
- setupReceivers(
9515
- NON_CC_INDEX_OFFSET + modulatorSources.channelPressure,
9516
- "channel pressure"
9400
+ midiControllers.registeredParameterLSB,
9401
+ 0
9517
9402
  );
9518
- break;
9519
- case 48:
9520
- setupReceivers(
9521
- NON_CC_INDEX_OFFSET + modulatorSources.polyPressure,
9522
- "poly pressure"
9523
- );
9524
- break;
9525
- }
9526
- return;
9527
- } else if (syx[5] === 0) {
9528
- switch (syx[6]) {
9529
- default:
9530
- notRecognized2();
9531
- break;
9532
- case 127:
9533
- if (messageValue === 0) {
9534
- SpessaSynthInfo(
9535
- "%cGS Reset received!",
9536
- consoleColors.info
9537
- );
9538
- this.resetAllControllers(false);
9539
- this.setMasterParameter(
9540
- "midiSystem",
9541
- "gs"
9542
- );
9543
- } else if (messageValue === 127) {
9544
- SpessaSynthInfo(
9545
- "%cGS system off, switching to GM",
9546
- consoleColors.info
9547
- );
9548
- this.resetAllControllers(false);
9549
- this.setMasterParameter(
9550
- "midiSystem",
9551
- "gm"
9552
- );
9553
- }
9554
- break;
9555
- case 6:
9556
- SpessaSynthInfo(
9557
- `%cRoland GS Master Pan set to: %c${messageValue}%c with: %c${arrayToHexString(
9558
- syx
9559
- )}`,
9560
- consoleColors.info,
9561
- consoleColors.value,
9562
- consoleColors.info,
9563
- consoleColors.value
9564
- );
9565
- this.setMasterParameter(
9566
- "masterPan",
9567
- (messageValue - 64) / 64
9568
- );
9569
- break;
9570
- case 4:
9571
- SpessaSynthInfo(
9572
- `%cRoland GS Master Volume set to: %c${messageValue}%c with: %c${arrayToHexString(
9573
- syx
9574
- )}`,
9575
- consoleColors.info,
9576
- consoleColors.value,
9577
- consoleColors.info,
9578
- consoleColors.value
9403
+ channelObject.controllerChange(
9404
+ midiControllers.dataEntryMSB,
9405
+ Math.floor(centeredValue)
9579
9406
  );
9580
- this.setMIDIVolume(messageValue / 127);
9581
- break;
9582
- case 5: {
9583
- const transpose = messageValue - 64;
9584
- SpessaSynthInfo(
9585
- `%cRoland GS Master Key-Shift set to: %c${transpose}%c with: %c${arrayToHexString(
9586
- syx
9587
- )}`,
9588
- consoleColors.info,
9589
- consoleColors.value,
9590
- consoleColors.info,
9591
- consoleColors.value
9407
+ } else {
9408
+ channelObject.sysExModulators.setModulator(
9409
+ source,
9410
+ generatorTypes.fineTune,
9411
+ centeredValue * 100,
9412
+ bipolar
9592
9413
  );
9593
- this.setMasterTuning(transpose * 100);
9594
- break;
9595
- }
9596
- }
9597
- return;
9598
- } else if (syx[5] === 1) {
9599
- switch (syx[6]) {
9600
- default:
9601
- notRecognized2();
9602
- break;
9603
- case 0: {
9604
- const patchName = readBinaryString(
9414
+ sysExLogging(
9605
9415
  syx,
9606
- 16,
9607
- 7
9608
- );
9609
- SpessaSynthInfo(
9610
- `%cGS Patch name: %c${patchName}`,
9611
- consoleColors.info,
9612
- consoleColors.value
9416
+ channel,
9417
+ centeredValue,
9418
+ `${sourceName} pitch control`,
9419
+ "semitones"
9613
9420
  );
9614
- break;
9615
9421
  }
9616
- case 51:
9617
- SpessaSynthInfo(
9618
- `%cGS Reverb level: %c${messageValue}`,
9619
- consoleColors.info,
9620
- consoleColors.value
9621
- );
9622
- this.privateProps.reverbSend = messageValue / 64;
9623
- break;
9624
- // Unsupported reverb params
9625
- case 48:
9626
- case 49:
9627
- case 50:
9628
- case 52:
9629
- case 53:
9630
- case 55:
9631
- SpessaSynthInfo(
9632
- `%cUnsupported GS Reverb Parameter: %c${syx[6].toString(16)}`,
9633
- consoleColors.warn,
9634
- consoleColors.unrecognized
9635
- );
9636
- break;
9637
- case 58:
9638
- SpessaSynthInfo(
9639
- `%cGS Chorus level: %c${messageValue}`,
9640
- consoleColors.info,
9641
- consoleColors.value
9642
- );
9643
- this.privateProps.chorusSend = messageValue / 64;
9644
- break;
9645
- // Unsupported chorus params
9646
- case 56:
9647
- case 57:
9648
- case 59:
9649
- case 60:
9650
- case 61:
9651
- case 62:
9652
- case 63:
9653
- case 64:
9654
- SpessaSynthInfo(
9655
- `%cUnsupported GS Chorus Parameter: %c${syx[6].toString(16)}`,
9656
- consoleColors.warn,
9657
- consoleColors.unrecognized
9658
- );
9659
- break;
9660
- }
9422
+ break;
9423
+ case 1:
9424
+ channelObject.sysExModulators.setModulator(
9425
+ source,
9426
+ generatorTypes.initialFilterFc,
9427
+ normalizedValue * 9600,
9428
+ bipolar
9429
+ );
9430
+ sysExLogging(
9431
+ syx,
9432
+ channel,
9433
+ normalizedValue * 9600,
9434
+ `${sourceName} pitch control`,
9435
+ "cents"
9436
+ );
9437
+ break;
9438
+ case 2:
9439
+ channelObject.sysExModulators.setModulator(
9440
+ source,
9441
+ generatorTypes.initialAttenuation,
9442
+ normalizedValue * 960,
9443
+ // Spec says "100%" so 960cB in sf2
9444
+ bipolar
9445
+ );
9446
+ sysExLogging(
9447
+ syx,
9448
+ channel,
9449
+ normalizedValue * 960,
9450
+ `${sourceName} amplitude`,
9451
+ "cB"
9452
+ );
9453
+ break;
9454
+ // Rate control is ignored as it is in hertz
9455
+ case 4:
9456
+ channelObject.sysExModulators.setModulator(
9457
+ source,
9458
+ generatorTypes.vibLfoToPitch,
9459
+ normalizedNotCentered * 600,
9460
+ bipolar
9461
+ );
9462
+ sysExLogging(
9463
+ syx,
9464
+ channel,
9465
+ normalizedNotCentered * 600,
9466
+ `${sourceName} LFO1 pitch depth`,
9467
+ "cents"
9468
+ );
9469
+ break;
9470
+ case 5:
9471
+ channelObject.sysExModulators.setModulator(
9472
+ source,
9473
+ generatorTypes.vibLfoToFilterFc,
9474
+ normalizedNotCentered * 2400,
9475
+ bipolar
9476
+ );
9477
+ sysExLogging(
9478
+ syx,
9479
+ channel,
9480
+ normalizedNotCentered * 2400,
9481
+ `${sourceName} LFO1 filter depth`,
9482
+ "cents"
9483
+ );
9484
+ break;
9485
+ case 6:
9486
+ channelObject.sysExModulators.setModulator(
9487
+ source,
9488
+ generatorTypes.vibLfoToVolume,
9489
+ normalizedValue * 960,
9490
+ bipolar
9491
+ );
9492
+ sysExLogging(
9493
+ syx,
9494
+ channel,
9495
+ normalizedValue * 960,
9496
+ `${sourceName} LFO1 amplitude depth`,
9497
+ "cB"
9498
+ );
9499
+ break;
9500
+ // Rate control is ignored as it is in hertz
9501
+ case 8:
9502
+ channelObject.sysExModulators.setModulator(
9503
+ source,
9504
+ generatorTypes.modLfoToPitch,
9505
+ normalizedNotCentered * 600,
9506
+ bipolar
9507
+ );
9508
+ sysExLogging(
9509
+ syx,
9510
+ channel,
9511
+ normalizedNotCentered * 600,
9512
+ `${sourceName} LFO2 pitch depth`,
9513
+ "cents"
9514
+ );
9515
+ break;
9516
+ case 9:
9517
+ channelObject.sysExModulators.setModulator(
9518
+ source,
9519
+ generatorTypes.modLfoToFilterFc,
9520
+ normalizedNotCentered * 2400,
9521
+ bipolar
9522
+ );
9523
+ sysExLogging(
9524
+ syx,
9525
+ channel,
9526
+ normalizedNotCentered * 2400,
9527
+ `${sourceName} LFO2 filter depth`,
9528
+ "cents"
9529
+ );
9530
+ break;
9531
+ case 10:
9532
+ channelObject.sysExModulators.setModulator(
9533
+ source,
9534
+ generatorTypes.modLfoToVolume,
9535
+ normalizedValue * 960,
9536
+ bipolar
9537
+ );
9538
+ sysExLogging(
9539
+ syx,
9540
+ channel,
9541
+ normalizedValue * 960,
9542
+ `${sourceName} LFO2 amplitude depth`,
9543
+ "cB"
9544
+ );
9545
+ break;
9661
9546
  }
9662
- } else {
9663
- notRecognized2();
9547
+ };
9548
+ switch (syx[6] & 240) {
9549
+ default:
9550
+ sysExNotRecognized(syx, "Roland GS");
9551
+ break;
9552
+ case 0:
9553
+ setupReceivers(
9554
+ midiControllers.modulationWheel,
9555
+ "mod wheel"
9556
+ );
9557
+ break;
9558
+ case 16:
9559
+ setupReceivers(
9560
+ NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel,
9561
+ "pitch wheel",
9562
+ true
9563
+ );
9564
+ break;
9565
+ case 32:
9566
+ setupReceivers(
9567
+ NON_CC_INDEX_OFFSET + modulatorSources.channelPressure,
9568
+ "channel pressure"
9569
+ );
9570
+ break;
9571
+ case 48:
9572
+ setupReceivers(
9573
+ NON_CC_INDEX_OFFSET + modulatorSources.polyPressure,
9574
+ "poly pressure"
9575
+ );
9576
+ break;
9664
9577
  }
9665
9578
  return;
9666
- }
9667
- case 69: {
9668
- if (syx[4] === 16) {
9669
- if (syx[5] === 0) {
9670
- this.privateProps.callEvent(
9671
- "synthDisplay",
9672
- Array.from(syx)
9579
+ } else if (syx[5] === 0) {
9580
+ switch (syx[6]) {
9581
+ default:
9582
+ sysExNotRecognized(syx, "Roland GS");
9583
+ break;
9584
+ case 127:
9585
+ if (messageValue === 0) {
9586
+ SpessaSynthInfo(
9587
+ "%cGS Reset received!",
9588
+ consoleColors.info
9589
+ );
9590
+ this.resetAllControllers("gs");
9591
+ } else if (messageValue === 127) {
9592
+ SpessaSynthInfo(
9593
+ "%cGS system off, switching to GM",
9594
+ consoleColors.info
9595
+ );
9596
+ this.resetAllControllers("gm");
9597
+ }
9598
+ break;
9599
+ case 6:
9600
+ SpessaSynthInfo(
9601
+ `%cRoland GS Master Pan set to: %c${messageValue}%c with: %c${arrayToHexString(
9602
+ syx
9603
+ )}`,
9604
+ consoleColors.info,
9605
+ consoleColors.value,
9606
+ consoleColors.info,
9607
+ consoleColors.value
9608
+ );
9609
+ this.setMasterParameter(
9610
+ "masterPan",
9611
+ (messageValue - 64) / 64
9612
+ );
9613
+ break;
9614
+ case 4:
9615
+ SpessaSynthInfo(
9616
+ `%cRoland GS Master Volume set to: %c${messageValue}%c with: %c${arrayToHexString(
9617
+ syx
9618
+ )}`,
9619
+ consoleColors.info,
9620
+ consoleColors.value,
9621
+ consoleColors.info,
9622
+ consoleColors.value
9673
9623
  );
9674
- } else if (syx[5] === 1) {
9675
- this.privateProps.callEvent(
9676
- "synthDisplay",
9677
- Array.from(syx)
9624
+ this.setMIDIVolume(messageValue / 127);
9625
+ break;
9626
+ case 5: {
9627
+ const transpose = messageValue - 64;
9628
+ SpessaSynthInfo(
9629
+ `%cRoland GS Master Key-Shift set to: %c${transpose}%c with: %c${arrayToHexString(
9630
+ syx
9631
+ )}`,
9632
+ consoleColors.info,
9633
+ consoleColors.value,
9634
+ consoleColors.info,
9635
+ consoleColors.value
9678
9636
  );
9679
- } else {
9680
- notRecognized2();
9637
+ this.setMasterTuning(transpose * 100);
9638
+ break;
9681
9639
  }
9682
9640
  }
9683
9641
  return;
9684
- }
9685
- case 22:
9686
- if (syx[4] === 16) {
9687
- this.setMIDIVolume(syx[7] / 100);
9688
- SpessaSynthInfo(
9689
- `%cRoland Master Volume control set to: %c${syx[7]}%c via: %c${arrayToHexString(
9690
- syx
9691
- )}`,
9692
- consoleColors.info,
9693
- consoleColors.value,
9694
- consoleColors.info,
9695
- consoleColors.value
9696
- );
9697
- return;
9698
- }
9699
- }
9700
- } else {
9701
- SpessaSynthInfo(
9702
- `%cUnrecognized Roland SysEx: %c${arrayToHexString(syx)}`,
9703
- consoleColors.warn,
9704
- consoleColors.unrecognized
9705
- );
9706
- return;
9707
- }
9708
- break;
9709
- // Yamaha
9710
- // http://www.studio4all.de/htmle/main91.html
9711
- case 67:
9712
- if (syx[2] === 76) {
9713
- if (syx[3] === 0 && syx[4] === 0) {
9714
- switch (syx[5]) {
9715
- // Master volume
9716
- case 4: {
9717
- const vol = syx[6];
9718
- this.setMIDIVolume(vol / 127);
9719
- SpessaSynthInfo(
9720
- `%cXG master volume. Volume: %c${vol}`,
9721
- consoleColors.info,
9722
- consoleColors.recognized
9723
- );
9724
- break;
9725
- }
9726
- // Master transpose
9727
- case 6: {
9728
- const transpose = syx[6] - 64;
9729
- this.setMasterParameter("transposition", transpose);
9730
- SpessaSynthInfo(
9731
- `%cXG master transpose. Volume: %c${transpose}`,
9732
- consoleColors.info,
9733
- consoleColors.recognized
9734
- );
9735
- break;
9736
- }
9737
- // XG on
9738
- case 126:
9739
- SpessaSynthInfo(
9740
- "%cXG system on",
9741
- consoleColors.info
9742
- );
9743
- this.resetAllControllers(false);
9744
- this.setMasterParameter("midiSystem", "xg");
9745
- break;
9746
- }
9747
- } else if (syx[3] === 8) {
9748
- if (!BankSelectHacks.isSystemXG(
9749
- this.privateProps.masterParameters.midiSystem
9750
- )) {
9751
- return;
9752
- }
9753
- const channel = syx[4] + channelOffset;
9754
- if (channel >= this.midiChannels.length) {
9755
- return;
9756
- }
9757
- const channelObject = this.midiChannels[channel];
9758
- const value = syx[6];
9759
- switch (syx[5]) {
9760
- // Bank-select MSB
9761
- case 1:
9762
- channelObject.controllerChange(
9763
- midiControllers.bankSelect,
9764
- value
9765
- );
9766
- break;
9767
- // Bank-select LSB
9768
- case 2:
9769
- channelObject.controllerChange(
9770
- midiControllers.bankSelectLSB,
9771
- value
9772
- );
9773
- break;
9774
- // Program change
9775
- case 3:
9776
- channelObject.programChange(value);
9777
- break;
9778
- // Note shift
9779
- case 8: {
9780
- if (channelObject.drumChannel) {
9781
- return;
9642
+ } else if (syx[5] === 1) {
9643
+ switch (syx[6]) {
9644
+ default:
9645
+ sysExNotRecognized(syx, "Roland GS");
9646
+ break;
9647
+ case 0: {
9648
+ const patchName = readBinaryString(syx, 16, 7);
9649
+ SpessaSynthInfo(
9650
+ `%cGS Patch name: %c${patchName}`,
9651
+ consoleColors.info,
9652
+ consoleColors.value
9653
+ );
9654
+ break;
9782
9655
  }
9783
- channelObject.channelTransposeKeyShift = value - 64;
9784
- break;
9785
- }
9786
- // Volume
9787
- case 11:
9788
- channelObject.controllerChange(
9789
- midiControllers.mainVolume,
9790
- value
9791
- );
9792
- break;
9793
- // Pan position
9794
- case 14: {
9795
- const pan = value;
9796
- if (pan === 0) {
9797
- channelObject.randomPan = true;
9656
+ case 51:
9798
9657
  SpessaSynthInfo(
9799
- `%cRandom pan is set to %cON%c for %c${channel}`,
9658
+ `%cGS Reverb level: %c${messageValue}`,
9800
9659
  consoleColors.info,
9801
- consoleColors.recognized,
9660
+ consoleColors.value
9661
+ );
9662
+ this.privateProps.reverbSend = messageValue / 64;
9663
+ break;
9664
+ // Unsupported reverb params
9665
+ case 48:
9666
+ case 49:
9667
+ case 50:
9668
+ case 52:
9669
+ case 53:
9670
+ case 55:
9671
+ SpessaSynthInfo(
9672
+ `%cUnsupported GS Reverb Parameter: %c${syx[6].toString(16)}`,
9673
+ consoleColors.warn,
9674
+ consoleColors.unrecognized
9675
+ );
9676
+ break;
9677
+ case 58:
9678
+ SpessaSynthInfo(
9679
+ `%cGS Chorus level: %c${messageValue}`,
9802
9680
  consoleColors.info,
9803
9681
  consoleColors.value
9804
9682
  );
9805
- } else {
9806
- channelObject.controllerChange(
9807
- midiControllers.pan,
9808
- pan
9683
+ this.privateProps.chorusSend = messageValue / 64;
9684
+ break;
9685
+ // Unsupported chorus params
9686
+ case 56:
9687
+ case 57:
9688
+ case 59:
9689
+ case 60:
9690
+ case 61:
9691
+ case 62:
9692
+ case 63:
9693
+ case 64:
9694
+ SpessaSynthInfo(
9695
+ `%cUnsupported GS Chorus Parameter: %c${syx[6].toString(16)}`,
9696
+ consoleColors.warn,
9697
+ consoleColors.unrecognized
9809
9698
  );
9810
- }
9811
- break;
9699
+ break;
9812
9700
  }
9813
- // Reverb
9814
- case 19:
9815
- channelObject.controllerChange(
9816
- midiControllers.reverbDepth,
9817
- value
9818
- );
9819
- break;
9820
- // Chorus
9821
- case 18:
9822
- channelObject.controllerChange(
9823
- midiControllers.chorusDepth,
9824
- value
9825
- );
9826
- break;
9827
- default:
9828
- SpessaSynthInfo(
9829
- `%cUnrecognized Yamaha XG Part Setup: %c${syx[5].toString(16).toUpperCase()}`,
9830
- consoleColors.warn,
9831
- consoleColors.unrecognized
9832
- );
9833
9701
  }
9834
- } else if (syx[3] === 6 && // XG System parameter
9835
- syx[4] === 0) {
9836
- this.privateProps.callEvent(
9837
- "synthDisplay",
9838
- Array.from(syx)
9702
+ } else {
9703
+ sysExNotRecognized(syx, "Roland GS");
9704
+ }
9705
+ return;
9706
+ }
9707
+ case 69: {
9708
+ if (syx[4] === 16) {
9709
+ if (syx[5] === 0) {
9710
+ this.privateProps.callEvent(
9711
+ "synthDisplay",
9712
+ Array.from(syx)
9713
+ );
9714
+ } else if (syx[5] === 1) {
9715
+ this.privateProps.callEvent(
9716
+ "synthDisplay",
9717
+ Array.from(syx)
9718
+ );
9719
+ } else {
9720
+ sysExNotRecognized(syx, "Roland GS");
9721
+ }
9722
+ }
9723
+ return;
9724
+ }
9725
+ case 22:
9726
+ if (syx[4] === 16) {
9727
+ this.setMIDIVolume(syx[7] / 100);
9728
+ SpessaSynthInfo(
9729
+ `%cRoland Master Volume control set to: %c${syx[7]}%c via: %c${arrayToHexString(
9730
+ syx
9731
+ )}`,
9732
+ consoleColors.info,
9733
+ consoleColors.value,
9734
+ consoleColors.info,
9735
+ consoleColors.value
9839
9736
  );
9840
- } else if (BankSelectHacks.isSystemXG(
9841
- this.privateProps.masterParameters.midiSystem
9842
- )) {
9737
+ return;
9738
+ }
9739
+ }
9740
+ } else {
9741
+ sysExNotRecognized(syx, "Roland GS");
9742
+ return;
9743
+ }
9744
+ }
9745
+
9746
+ // src/synthesizer/audio_engine/engine_methods/system_exclusive/handle_xg.ts
9747
+ function handleXG(syx, channelOffset = 0) {
9748
+ if (syx[2] === 76) {
9749
+ const a1 = syx[3];
9750
+ const a2 = syx[4];
9751
+ if (a1 === 0 && a2 === 0) {
9752
+ switch (syx[5]) {
9753
+ // Master tune
9754
+ case 0:
9755
+ {
9756
+ const tune = (syx[6] & 15) << 12 | (syx[7] & 15) << 8 | (syx[8] & 15) << 4 | syx[9] & 15;
9757
+ const cents = (tune - 1024) / 10;
9758
+ this.setMasterTuning(cents);
9759
+ SpessaSynthInfo(
9760
+ `%cXG master tune. Cents: %c${cents}`,
9761
+ consoleColors.info,
9762
+ consoleColors.recognized
9763
+ );
9764
+ }
9765
+ break;
9766
+ // Master volume
9767
+ case 4: {
9768
+ const vol = syx[6];
9769
+ this.setMIDIVolume(vol / 127);
9843
9770
  SpessaSynthInfo(
9844
- `%cUnrecognized Yamaha XG SysEx: %c${arrayToHexString(syx)}`,
9845
- consoleColors.warn,
9846
- consoleColors.unrecognized
9771
+ `%cXG master volume. Volume: %c${vol}`,
9772
+ consoleColors.info,
9773
+ consoleColors.recognized
9847
9774
  );
9775
+ break;
9848
9776
  }
9849
- } else {
9850
- if (BankSelectHacks.isSystemXG(
9851
- this.privateProps.masterParameters.midiSystem
9852
- )) {
9777
+ // Master attenuation
9778
+ case 5: {
9779
+ const vol = 127 - syx[6];
9780
+ this.setMIDIVolume(vol / 127);
9853
9781
  SpessaSynthInfo(
9854
- `%cUnrecognized Yamaha SysEx: %c${arrayToHexString(syx)}`,
9855
- consoleColors.warn,
9856
- consoleColors.unrecognized
9782
+ `%cXG master attenuation. Volume: %c${vol}`,
9783
+ consoleColors.info,
9784
+ consoleColors.recognized
9785
+ );
9786
+ break;
9787
+ }
9788
+ // Master transpose
9789
+ case 6: {
9790
+ const transpose = syx[6] - 64;
9791
+ this.setMasterParameter("transposition", transpose);
9792
+ SpessaSynthInfo(
9793
+ `%cXG master transpose. Volume: %c${transpose}`,
9794
+ consoleColors.info,
9795
+ consoleColors.recognized
9796
+ );
9797
+ break;
9798
+ }
9799
+ //
9800
+ // XG on
9801
+ case 126:
9802
+ SpessaSynthInfo("%cXG system on", consoleColors.info);
9803
+ this.resetAllControllers("xg");
9804
+ break;
9805
+ }
9806
+ } else if (a1 === 2 && a2 === 1) {
9807
+ let effectType;
9808
+ const effect = syx[5];
9809
+ if (effect <= 21) effectType = "Reverb";
9810
+ else if (effect <= 35) effectType = "Chorus";
9811
+ else effectType = "Variation";
9812
+ SpessaSynthInfo(
9813
+ `%cUnsupported XG ${effectType} Parameter: %c${effect.toString(16)}`,
9814
+ consoleColors.warn,
9815
+ consoleColors.unrecognized
9816
+ );
9817
+ } else if (a1 === 8) {
9818
+ if (!BankSelectHacks.isSystemXG(
9819
+ this.privateProps.masterParameters.midiSystem
9820
+ )) {
9821
+ return;
9822
+ }
9823
+ const channel = a2 + channelOffset;
9824
+ if (channel >= this.midiChannels.length) {
9825
+ return;
9826
+ }
9827
+ const channelObject = this.midiChannels[channel];
9828
+ const value = syx[6];
9829
+ switch (syx[5]) {
9830
+ // Bank-select MSB
9831
+ case 1:
9832
+ channelObject.controllerChange(
9833
+ midiControllers.bankSelect,
9834
+ value
9835
+ );
9836
+ break;
9837
+ // Bank-select LSB
9838
+ case 2:
9839
+ channelObject.controllerChange(
9840
+ midiControllers.bankSelectLSB,
9841
+ value
9842
+ );
9843
+ break;
9844
+ // Program change
9845
+ case 3:
9846
+ channelObject.programChange(value);
9847
+ break;
9848
+ // Part mode
9849
+ case 7:
9850
+ channelObject.setDrums(value != 0);
9851
+ break;
9852
+ // Note shift
9853
+ case 8: {
9854
+ if (channelObject.drumChannel) {
9855
+ break;
9856
+ }
9857
+ channelObject.setCustomController(
9858
+ customControllers.channelKeyShift,
9859
+ value - 64
9860
+ );
9861
+ break;
9862
+ }
9863
+ // Volume
9864
+ case 11:
9865
+ channelObject.controllerChange(
9866
+ midiControllers.mainVolume,
9867
+ value
9857
9868
  );
9869
+ break;
9870
+ // Pan position
9871
+ case 14: {
9872
+ const pan = value;
9873
+ if (pan === 0) {
9874
+ channelObject.randomPan = true;
9875
+ SpessaSynthInfo(
9876
+ `%cRandom pan is set to %cON%c for %c${channel}`,
9877
+ consoleColors.info,
9878
+ consoleColors.recognized,
9879
+ consoleColors.info,
9880
+ consoleColors.value
9881
+ );
9882
+ } else {
9883
+ channelObject.controllerChange(
9884
+ midiControllers.pan,
9885
+ pan
9886
+ );
9887
+ }
9888
+ break;
9858
9889
  }
9890
+ // Dry
9891
+ case 17:
9892
+ channelObject.controllerChange(
9893
+ midiControllers.mainVolume,
9894
+ value
9895
+ );
9896
+ break;
9897
+ // Chorus
9898
+ case 18:
9899
+ channelObject.controllerChange(
9900
+ midiControllers.chorusDepth,
9901
+ value
9902
+ );
9903
+ break;
9904
+ // Reverb
9905
+ case 19:
9906
+ channelObject.controllerChange(
9907
+ midiControllers.reverbDepth,
9908
+ value
9909
+ );
9910
+ break;
9911
+ // Vibrato rate
9912
+ case 21:
9913
+ channelObject.controllerChange(
9914
+ midiControllers.vibratoRate,
9915
+ value
9916
+ );
9917
+ break;
9918
+ // Vibrato depth
9919
+ case 22:
9920
+ channelObject.controllerChange(
9921
+ midiControllers.vibratoDepth,
9922
+ value
9923
+ );
9924
+ break;
9925
+ // Vibrato delay
9926
+ case 23:
9927
+ channelObject.controllerChange(
9928
+ midiControllers.vibratoDelay,
9929
+ value
9930
+ );
9931
+ break;
9932
+ // Filter cutoff
9933
+ case 24:
9934
+ channelObject.controllerChange(
9935
+ midiControllers.brightness,
9936
+ value
9937
+ );
9938
+ break;
9939
+ // Filter resonance
9940
+ case 25:
9941
+ channelObject.controllerChange(
9942
+ midiControllers.filterResonance,
9943
+ value
9944
+ );
9945
+ break;
9946
+ // Attack time
9947
+ case 26:
9948
+ channelObject.controllerChange(
9949
+ midiControllers.attackTime,
9950
+ value
9951
+ );
9952
+ break;
9953
+ // Decay time
9954
+ case 27:
9955
+ channelObject.controllerChange(
9956
+ midiControllers.decayTime,
9957
+ value
9958
+ );
9959
+ break;
9960
+ // Release time
9961
+ case 28:
9962
+ channelObject.controllerChange(
9963
+ midiControllers.releaseTime,
9964
+ value
9965
+ );
9966
+ break;
9967
+ default:
9968
+ SpessaSynthInfo(
9969
+ `%cUnsupported Yamaha XG Part Setup: %c${syx[5].toString(16).toUpperCase()}%c for channel ${channel}`,
9970
+ consoleColors.warn,
9971
+ consoleColors.unrecognized,
9972
+ consoleColors.warn
9973
+ );
9859
9974
  }
9975
+ } else if (a1 === 6 && // XG System parameter
9976
+ a2 === 0) {
9977
+ this.privateProps.callEvent("synthDisplay", Array.from(syx));
9978
+ } else if (BankSelectHacks.isSystemXG(
9979
+ this.privateProps.masterParameters.midiSystem
9980
+ )) {
9981
+ sysExNotRecognized(syx, "Yamaha XG");
9982
+ }
9983
+ } else {
9984
+ sysExNotRecognized(syx, "Yamaha");
9985
+ }
9986
+ }
9987
+
9988
+ // src/synthesizer/audio_engine/engine_methods/system_exclusive.ts
9989
+ function systemExclusiveInternal(syx, channelOffset = 0) {
9990
+ const manufacturer = syx[0];
9991
+ if (
9992
+ // The device ID can be set to "all" which it is by default
9993
+ this.privateProps.masterParameters.deviceID !== ALL_CHANNELS_OR_DIFFERENT_ACTION && syx[1] !== 127
9994
+ ) {
9995
+ if (this.privateProps.masterParameters.deviceID !== syx[1]) {
9996
+ return;
9997
+ }
9998
+ }
9999
+ switch (manufacturer) {
10000
+ default:
10001
+ SpessaSynthInfo(
10002
+ `%cUnrecognized SysEx: %c${arrayToHexString(syx)} (unknown manufacturer)`,
10003
+ consoleColors.warn,
10004
+ consoleColors.unrecognized
10005
+ );
10006
+ break;
10007
+ // Non realtime GM
10008
+ case 126:
10009
+ // Realtime GM
10010
+ case 127:
10011
+ handleGM.call(this, syx, channelOffset);
10012
+ break;
10013
+ // Roland
10014
+ case 65:
10015
+ handleGS.call(this, syx, channelOffset);
10016
+ break;
10017
+ // Yamaha
10018
+ case 67:
10019
+ handleXG.call(this, syx, channelOffset);
9860
10020
  break;
9861
10021
  }
9862
10022
  }
@@ -10250,8 +10410,7 @@ var ProtectedSynthValues = class {
10250
10410
  voiceKilling;
10251
10411
  /**
10252
10412
  * Cached voices for all presets for this synthesizer.
10253
- * Nesting goes like this:
10254
- * this.cachedVoices[bankMSB][bankLSB][programNumber][midiNote][velocity] = a list of voices for that.
10413
+ * Nesting is calculated in getCachedVoiceIndex, returns a list of voices for this note.
10255
10414
  */
10256
10415
  cachedVoices = [];
10257
10416
  constructor(eventCallbackHandler, getVoices, voiceKillingFunction, volumeEnvelopeSmoothingFactor, panSmoothingFactor, filterSmoothingFactor) {
@@ -10605,13 +10764,14 @@ var nonRegisteredMSB = {
10605
10764
  awe32: 127,
10606
10765
  SF2: 120
10607
10766
  };
10608
- var nonRegisteredGSLSB = {
10767
+ var nonRegisteredLSB = {
10609
10768
  vibratoRate: 8,
10610
10769
  vibratoDepth: 9,
10611
10770
  vibratoDelay: 10,
10612
10771
  TVFFilterCutoff: 32,
10613
10772
  TVFFilterResonance: 33,
10614
10773
  EGAttackTime: 99,
10774
+ EGDecayTime: 100,
10615
10775
  EGReleaseTime: 102
10616
10776
  };
10617
10777
  function dataEntryCoarse(dataValue) {
@@ -10640,7 +10800,7 @@ function dataEntryCoarse(dataValue) {
10640
10800
  default:
10641
10801
  case dataEntryStates.Idle:
10642
10802
  break;
10643
- // Process GS NRPNs
10803
+ // Process NRPNs
10644
10804
  case dataEntryStates.NRPFine: {
10645
10805
  if (this.lockGSNRPNParams) {
10646
10806
  return;
@@ -10667,7 +10827,7 @@ function dataEntryCoarse(dataValue) {
10667
10827
  consoleColors.value
10668
10828
  );
10669
10829
  break;
10670
- // Part parameters: vibrato, cutoff
10830
+ // Part parameters
10671
10831
  case nonRegisteredMSB.partParameter:
10672
10832
  switch (NRPNFine) {
10673
10833
  default:
@@ -10686,8 +10846,8 @@ function dataEntryCoarse(dataValue) {
10686
10846
  consoleColors.value
10687
10847
  );
10688
10848
  break;
10689
- // Vibrato rate
10690
- case nonRegisteredGSLSB.vibratoRate:
10849
+ // Vibrato rate (custom vibrato)
10850
+ case nonRegisteredLSB.vibratoRate:
10691
10851
  if (dataValue === 64) {
10692
10852
  return;
10693
10853
  }
@@ -10699,8 +10859,8 @@ function dataEntryCoarse(dataValue) {
10699
10859
  "Hz"
10700
10860
  );
10701
10861
  break;
10702
- // Vibrato depth
10703
- case nonRegisteredGSLSB.vibratoDepth:
10862
+ // Vibrato depth (custom vibrato)
10863
+ case nonRegisteredLSB.vibratoDepth:
10704
10864
  if (dataValue === 64) {
10705
10865
  return;
10706
10866
  }
@@ -10712,8 +10872,8 @@ function dataEntryCoarse(dataValue) {
10712
10872
  "cents of detune"
10713
10873
  );
10714
10874
  break;
10715
- // Vibrato delay
10716
- case nonRegisteredGSLSB.vibratoDelay:
10875
+ // Vibrato delay (custom vibrato)
10876
+ case nonRegisteredLSB.vibratoDelay:
10717
10877
  if (dataValue === 64) {
10718
10878
  return;
10719
10879
  }
@@ -10726,15 +10886,26 @@ function dataEntryCoarse(dataValue) {
10726
10886
  );
10727
10887
  break;
10728
10888
  // Filter cutoff
10729
- case nonRegisteredGSLSB.TVFFilterCutoff:
10889
+ case nonRegisteredLSB.TVFFilterCutoff:
10730
10890
  this.controllerChange(
10731
10891
  midiControllers.brightness,
10732
10892
  dataValue
10733
10893
  );
10734
10894
  coolInfo("Filter cutoff", dataValue.toString(), "");
10735
10895
  break;
10896
+ case nonRegisteredLSB.TVFFilterResonance:
10897
+ this.controllerChange(
10898
+ midiControllers.filterResonance,
10899
+ dataValue
10900
+ );
10901
+ coolInfo(
10902
+ "Filter resonance",
10903
+ dataValue.toString(),
10904
+ ""
10905
+ );
10906
+ break;
10736
10907
  // Attack time
10737
- case nonRegisteredGSLSB.EGAttackTime:
10908
+ case nonRegisteredLSB.EGAttackTime:
10738
10909
  this.controllerChange(
10739
10910
  midiControllers.attackTime,
10740
10911
  dataValue
@@ -10745,8 +10916,15 @@ function dataEntryCoarse(dataValue) {
10745
10916
  ""
10746
10917
  );
10747
10918
  break;
10919
+ case nonRegisteredLSB.EGDecayTime:
10920
+ this.controllerChange(
10921
+ midiControllers.decayTime,
10922
+ dataValue
10923
+ );
10924
+ coolInfo("EG decay time", dataValue.toString(), "");
10925
+ break;
10748
10926
  // Release time
10749
- case nonRegisteredGSLSB.EGReleaseTime:
10927
+ case nonRegisteredLSB.EGReleaseTime:
10750
10928
  this.controllerChange(
10751
10929
  midiControllers.releaseTime,
10752
10930
  dataValue
@@ -11163,9 +11341,10 @@ var portamentoLookup = {
11163
11341
  124: 80,
11164
11342
  127: 480
11165
11343
  };
11166
- function getLookup(value) {
11344
+ function portaTimeToRate(value) {
11345
+ let portaTime = 0;
11167
11346
  if (portamentoLookup[value] !== void 0) {
11168
- return portamentoLookup[value];
11347
+ portaTime = portamentoLookup[value];
11169
11348
  }
11170
11349
  let lower = null;
11171
11350
  let upper = null;
@@ -11181,12 +11360,12 @@ function getLookup(value) {
11181
11360
  if (lower !== null && upper !== null) {
11182
11361
  const lowerTime = portamentoLookup[lower];
11183
11362
  const upperTime = portamentoLookup[upper];
11184
- return lowerTime + (value - lower) * (upperTime - lowerTime) / (upper - lower);
11363
+ portaTime = lowerTime + (value - lower) * (upperTime - lowerTime) / (upper - lower);
11185
11364
  }
11186
- return 0;
11365
+ return portaTime / 40;
11187
11366
  }
11188
11367
  function portamentoTimeToSeconds(time, distance) {
11189
- return getLookup(time) * (distance / 36);
11368
+ return portaTimeToRate(time) * distance;
11190
11369
  }
11191
11370
 
11192
11371
  // src/synthesizer/audio_engine/engine_methods/note_on.ts
@@ -11230,16 +11409,15 @@ function noteOn(midiNote, velocity) {
11230
11409
  let portamentoFromKey = -1;
11231
11410
  let portamentoDuration = 0;
11232
11411
  const portamentoTime = this.midiControllers[midiControllers.portamentoTime] >> 7;
11233
- const control = this.midiControllers[midiControllers.portamentoControl];
11234
- const currentFromKey = control >> 7;
11412
+ const portaControl = this.midiControllers[midiControllers.portamentoControl] >> 7;
11235
11413
  if (!this.drumChannel && // No portamento on drum channel
11236
- currentFromKey !== internalMidiNote && // If the same note, there's no portamento
11414
+ portaControl !== internalMidiNote && // If the same note, there's no portamento
11237
11415
  this.midiControllers[midiControllers.portamentoOnOff] >= 8192 && // (64 << 7)
11238
11416
  portamentoTime > 0) {
11239
- if (control !== 1) {
11240
- const diff = Math.abs(internalMidiNote - currentFromKey);
11417
+ if (portaControl > 0) {
11418
+ const diff = Math.abs(internalMidiNote - portaControl);
11241
11419
  portamentoDuration = portamentoTimeToSeconds(portamentoTime, diff);
11242
- portamentoFromKey = currentFromKey;
11420
+ portamentoFromKey = portaControl;
11243
11421
  }
11244
11422
  this.controllerChange(
11245
11423
  midiControllers.portamentoControl,
@@ -17057,16 +17235,12 @@ var SpessaSynthProcessor3 = class {
17057
17235
  });
17058
17236
  }
17059
17237
  getCachedVoice(patch, midiNote, velocity) {
17060
- let bankMSB = patch.bankMSB;
17061
- let bankLSB = patch.bankLSB;
17062
- const { isGMGSDrum, program } = patch;
17063
- if (isGMGSDrum) {
17064
- bankMSB = 128;
17065
- bankLSB = 0;
17066
- }
17067
- return this.privateProps.cachedVoices?.[bankMSB]?.[bankLSB]?.[program]?.[midiNote]?.[velocity];
17238
+ return this.privateProps.cachedVoices?.[this.getCachedVoiceIndex(patch, midiNote, velocity)];
17068
17239
  }
17069
17240
  setCachedVoice(patch, midiNote, velocity, voices) {
17241
+ this.privateProps.cachedVoices[this.getCachedVoiceIndex(patch, midiNote, velocity)] = voices;
17242
+ }
17243
+ getCachedVoiceIndex(patch, midiNote, velocity) {
17070
17244
  let bankMSB = patch.bankMSB;
17071
17245
  let bankLSB = patch.bankLSB;
17072
17246
  const { isGMGSDrum, program } = patch;
@@ -17074,19 +17248,11 @@ var SpessaSynthProcessor3 = class {
17074
17248
  bankMSB = 128;
17075
17249
  bankLSB = 0;
17076
17250
  }
17077
- if (!this.privateProps.cachedVoices[bankMSB]) {
17078
- this.privateProps.cachedVoices[bankMSB] = [];
17079
- }
17080
- if (!this.privateProps.cachedVoices[bankMSB][bankLSB]) {
17081
- this.privateProps.cachedVoices[bankMSB][bankLSB] = [];
17082
- }
17083
- if (!this.privateProps.cachedVoices[bankMSB][bankLSB][program]) {
17084
- this.privateProps.cachedVoices[bankMSB][bankLSB][program] = [];
17085
- }
17086
- if (!this.privateProps.cachedVoices[bankMSB][bankLSB][program][midiNote]) {
17087
- this.privateProps.cachedVoices[bankMSB][bankLSB][program][midiNote] = [];
17088
- }
17089
- this.privateProps.cachedVoices[bankMSB][bankLSB][program][midiNote][velocity] = voices;
17251
+ return bankMSB + // 128 ^ 0
17252
+ bankLSB * 128 + // 128 ^ 1
17253
+ program * 16384 + // 128 ^ 2
17254
+ 2097152 * midiNote + // 128 ^ 3
17255
+ 268435456 * velocity;
17090
17256
  }
17091
17257
  createMIDIChannelInternal(sendEvent) {
17092
17258
  const channel = new MIDIChannel(
@@ -17110,7 +17276,7 @@ var SpessaSynthProcessor3 = class {
17110
17276
  this.midiChannels.forEach((c) => {
17111
17277
  c.setPresetLock(false);
17112
17278
  });
17113
- this.resetAllControllers(false);
17279
+ this.resetAllControllers();
17114
17280
  }
17115
17281
  getDefaultPresets() {
17116
17282
  this.privateProps.defaultPreset = this.soundBankManager.getPreset(
@@ -17164,7 +17330,6 @@ export {
17164
17330
  Modulator,
17165
17331
  ModulatorSource,
17166
17332
  NON_CC_INDEX_OFFSET,
17167
- PORTAMENTO_CONTROL_UNSET,
17168
17333
  SoundBankLoader,
17169
17334
  SpessaSynthCoreUtils,
17170
17335
  SpessaSynthLogging,