spessasynth_core 4.0.23 → 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) {
@@ -6902,20 +6918,6 @@ var defaultSoundFont2Modulators = [
6902
6918
  ];
6903
6919
  var defaultSpessaSynthModulators = [
6904
6920
  // Custom modulators heck yeah
6905
- // Poly pressure to vibrato
6906
- new DecodedModulator(
6907
- getModSourceEnum(
6908
- modulatorCurveTypes.linear,
6909
- false,
6910
- false,
6911
- false,
6912
- modulatorSources.polyPressure
6913
- ),
6914
- 0,
6915
- generatorTypes.vibLfoToPitch,
6916
- 50,
6917
- 0
6918
- ),
6919
6921
  // Cc 92 (tremolo) to modLFO volume
6920
6922
  new DecodedModulator(
6921
6923
  getModSourceEnum(
@@ -6963,6 +6965,22 @@ var defaultSpessaSynthModulators = [
6963
6965
  3600,
6964
6966
  0
6965
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
+ ),
6966
6984
  // Cc 74 (brightness) to filterFc
6967
6985
  new DecodedModulator(
6968
6986
  getModSourceEnum(
@@ -6976,7 +6994,7 @@ var defaultSpessaSynthModulators = [
6976
6994
  0,
6977
6995
  // No controller
6978
6996
  generatorTypes.initialFilterFc,
6979
- 6e3,
6997
+ 9600,
6980
6998
  0
6981
6999
  ),
6982
7000
  // Cc 71 (filter Q) to filter Q (default resonant modulator)
@@ -6985,7 +7003,55 @@ var defaultSpessaSynthModulators = [
6985
7003
  0,
6986
7004
  // No controller
6987
7005
  generatorTypes.initialFilterQ,
6988
- 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,
6989
7055
  0
6990
7056
  )
6991
7057
  ];
@@ -8274,7 +8340,7 @@ function panAndMixVoice(voice, inputBuffer, outputLeft, outputRight, reverbLeft,
8274
8340
  }
8275
8341
 
8276
8342
  // src/synthesizer/audio_engine/engine_components/dsp_chain/lowpass_filter.ts
8277
- var FILTER_SMOOTHING_FACTOR = 0.1;
8343
+ var FILTER_SMOOTHING_FACTOR = 0.03;
8278
8344
  var LowpassFilter = class _LowpassFilter {
8279
8345
  /**
8280
8346
  * Cached coefficient calculations.
@@ -8955,7 +9021,29 @@ function getVoicesInternal(channel, midiNote, velocity, realKey) {
8955
9021
  return this.getVoicesForPreset(preset, midiNote, velocity, realKey);
8956
9022
  }
8957
9023
 
8958
- // 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
8959
9047
  function getTuning(byte1, byte2, byte3) {
8960
9048
  const midiNote = byte1;
8961
9049
  const fraction = byte2 << 7 | byte3;
@@ -8964,900 +9052,971 @@ function getTuning(byte1, byte2, byte3) {
8964
9052
  }
8965
9053
  return { midiNote, centTuning: fraction * 61e-4 };
8966
9054
  }
8967
- function systemExclusiveInternal(syx, channelOffset = 0) {
8968
- const manufacturer = syx[0];
8969
- if (this.privateProps.masterParameters.deviceID !== ALL_CHANNELS_OR_DIFFERENT_ACTION && syx[1] !== 127) {
8970
- if (this.privateProps.masterParameters.deviceID !== syx[1]) {
8971
- 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;
8972
9111
  }
8973
- }
8974
- function niceLogging(channel, value, what, units) {
8975
- SpessaSynthInfo(
8976
- `%cChannel %c${channel}%c ${what}. %c${value} ${units}%c, with %c${arrayToHexString(syx)}`,
8977
- consoleColors.info,
8978
- consoleColors.recognized,
8979
- consoleColors.info,
8980
- consoleColors.value,
8981
- consoleColors.info,
8982
- consoleColors.value
8983
- );
8984
- }
8985
- switch (manufacturer) {
8986
- default:
8987
- SpessaSynthInfo(
8988
- `%cUnrecognized SysEx: %c${arrayToHexString(syx)}`,
8989
- consoleColors.warn,
8990
- consoleColors.unrecognized
8991
- );
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
+ }
8992
9126
  break;
8993
- // Non realtime GM
8994
- case 126:
8995
- // Realtime GM
8996
- case 127:
8997
- switch (syx[2]) {
8998
- case 4: {
8999
- let cents;
9000
- switch (syx[3]) {
9001
- case 1: {
9002
- const vol = syx[5] << 7 | syx[4];
9003
- this.setMIDIVolume(vol / 16384);
9004
- SpessaSynthInfo(
9005
- `%cMaster Volume. Volume: %c${vol}`,
9006
- consoleColors.info,
9007
- consoleColors.value
9008
- );
9009
- break;
9010
- }
9011
- case 2: {
9012
- const balance = syx[5] << 7 | syx[4];
9013
- const pan = (balance - 8192) / 8192;
9014
- this.setMasterParameter("masterPan", pan);
9015
- SpessaSynthInfo(
9016
- `%cMaster Pan. Pan: %c${pan}`,
9017
- consoleColors.info,
9018
- consoleColors.value
9019
- );
9020
- break;
9021
- }
9022
- case 3: {
9023
- const tuningValue = (syx[5] << 7 | syx[6]) - 8192;
9024
- cents = Math.floor(tuningValue / 81.92);
9025
- this.setMasterTuning(cents);
9026
- SpessaSynthInfo(
9027
- `%cMaster Fine Tuning. Cents: %c${cents}`,
9028
- consoleColors.info,
9029
- consoleColors.value
9030
- );
9031
- break;
9032
- }
9033
- case 4: {
9034
- const semitones = syx[5] - 64;
9035
- cents = semitones * 100;
9036
- this.setMasterTuning(cents);
9037
- SpessaSynthInfo(
9038
- `%cMaster Coarse Tuning. Cents: %c${cents}`,
9039
- consoleColors.info,
9040
- consoleColors.value
9041
- );
9042
- break;
9043
- }
9044
- default:
9045
- SpessaSynthInfo(
9046
- `%cUnrecognized MIDI Device Control Real-time message: %c${arrayToHexString(syx)}`,
9047
- consoleColors.warn,
9048
- consoleColors.unrecognized
9049
- );
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
+ );
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++;
9050
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
+ );
9051
9186
  break;
9052
9187
  }
9188
+ // Octave tuning (1 byte)
9189
+ // And octave tuning (2 bytes)
9053
9190
  case 9:
9054
- if (syx[3] === 1) {
9055
- SpessaSynthInfo("%cGM1 system on", consoleColors.info);
9056
- this.setMasterParameter("midiSystem", "gm");
9057
- } else if (syx[3] === 3) {
9058
- SpessaSynthInfo("%cGM2 system on", consoleColors.info);
9059
- 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
+ }
9060
9197
  } else {
9061
- SpessaSynthInfo(
9062
- "%cGM system off, defaulting to GS",
9063
- 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
9064
9206
  );
9065
- this.setMasterParameter("midiSystem", "gs");
9066
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
+ );
9067
9230
  break;
9068
- // MIDI Tuning standard
9069
- // https://midi.org/midi-tuning-updated-specification
9070
- case 8: {
9071
- let currentMessageIndex = 4;
9072
- switch (syx[3]) {
9073
- // Bulk tuning dump: all 128 notes
9074
- case 1: {
9075
- const program = syx[currentMessageIndex++];
9076
- const tuningName = readBinaryString(
9077
- syx,
9078
- 16,
9079
- currentMessageIndex
9080
- );
9081
- currentMessageIndex += 16;
9082
- if (syx.length < 384) {
9083
- SpessaSynthWarn(
9084
- `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
9085
9284
  );
9086
9285
  return;
9087
9286
  }
9088
- for (let i = 0; i < 128; i++) {
9089
- this.privateProps.tunings[program][i] = getTuning(
9090
- syx[currentMessageIndex++],
9091
- syx[currentMessageIndex++],
9092
- syx[currentMessageIndex++]
9287
+ case 22: {
9288
+ const keyShift = messageValue - 64;
9289
+ channelObject.setCustomController(
9290
+ customControllers.channelKeyShift,
9291
+ keyShift
9093
9292
  );
9094
- }
9095
- SpessaSynthInfo(
9096
- `%cBulk Tuning Dump %c${tuningName}%c Program: %c${program}`,
9097
- consoleColors.info,
9098
- consoleColors.value,
9099
- consoleColors.info,
9100
- consoleColors.recognized
9101
- );
9102
- break;
9103
- }
9104
- // Single note change
9105
- // Single note change bank
9106
- case 2:
9107
- case 7: {
9108
- if (syx[3] === 7) {
9109
- currentMessageIndex++;
9110
- }
9111
- const tuningProgram = syx[currentMessageIndex++];
9112
- const numberOfChanges = syx[currentMessageIndex++];
9113
- for (let i = 0; i < numberOfChanges; i++) {
9114
- this.privateProps.tunings[tuningProgram][syx[currentMessageIndex++]] = getTuning(
9115
- syx[currentMessageIndex++],
9116
- syx[currentMessageIndex++],
9117
- syx[currentMessageIndex++]
9293
+ sysExLogging(
9294
+ syx,
9295
+ channel,
9296
+ keyShift,
9297
+ "key shift",
9298
+ "keys"
9118
9299
  );
9300
+ return;
9119
9301
  }
9120
- SpessaSynthInfo(
9121
- `%cSingle Note Tuning. Program: %c${tuningProgram}%c Keys affected: %c${numberOfChanges}`,
9122
- consoleColors.info,
9123
- consoleColors.recognized,
9124
- consoleColors.info,
9125
- consoleColors.recognized
9126
- );
9127
- break;
9128
- }
9129
- // Octave tuning (1 byte)
9130
- // And octave tuning (2 bytes)
9131
- case 9:
9132
- case 8: {
9133
- const newOctaveTuning = new Int8Array(12);
9134
- if (syx[3] === 8) {
9135
- for (let i = 0; i < 12; i++) {
9136
- newOctaveTuning[i] = syx[7 + i] - 64;
9137
- }
9138
- } else {
9139
- for (let i = 0; i < 24; i += 2) {
9140
- const tuning = (syx[7 + i] << 7 | syx[8 + i]) - 8192;
9141
- newOctaveTuning[i / 2] = Math.floor(
9142
- 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
9143
9319
  );
9144
9320
  }
9321
+ break;
9145
9322
  }
9146
- if ((syx[4] & 1) === 1) {
9147
- this.midiChannels[14 + channelOffset].setOctaveTuning(newOctaveTuning);
9148
- }
9149
- if ((syx[4] >> 1 & 1) === 1) {
9150
- this.midiChannels[15 + channelOffset].setOctaveTuning(newOctaveTuning);
9151
- }
9152
- for (let i = 0; i < 7; i++) {
9153
- const bit = syx[5] >> i & 1;
9154
- if (bit === 1) {
9155
- this.midiChannels[7 + i + channelOffset].setOctaveTuning(newOctaveTuning);
9156
- }
9157
- }
9158
- for (let i = 0; i < 7; i++) {
9159
- const bit = syx[6] >> i & 1;
9160
- if (bit === 1) {
9161
- 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;
9162
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;
9163
9365
  }
9164
- SpessaSynthInfo(
9165
- `%cMIDI Octave Scale ${syx[3] === 8 ? "(1 byte)" : "(2 bytes)"} tuning via Tuning: %c${newOctaveTuning.join(" ")}`,
9166
- consoleColors.info,
9167
- consoleColors.value
9168
- );
9169
- break;
9170
9366
  }
9171
- default:
9172
- SpessaSynthInfo(
9173
- `%cUnrecognized MIDI Tuning standard message: %c${arrayToHexString(syx)}`,
9174
- consoleColors.warn,
9175
- consoleColors.unrecognized
9176
- );
9177
- break;
9178
- }
9179
- break;
9180
- }
9181
- default:
9182
- SpessaSynthInfo(
9183
- `%cUnrecognized MIDI Realtime/non realtime message: %c${arrayToHexString(syx)}`,
9184
- consoleColors.warn,
9185
- consoleColors.unrecognized
9186
- );
9187
- }
9188
- break;
9189
- // This is a roland sysex
9190
- // http://www.bandtrax.com.au/sysex.htm
9191
- // https://cdn.roland.com/assets/media/pdf/AT-20R_30R_MI.pdf
9192
- case 65:
9193
- if (syx[3] === 18) {
9194
- let notRecognized2 = function() {
9195
- SpessaSynthInfo(
9196
- `%cUnrecognized Roland %cGS %cSysEx: %c${arrayToHexString(syx)}`,
9197
- consoleColors.warn,
9198
- consoleColors.recognized,
9199
- consoleColors.warn,
9200
- consoleColors.unrecognized
9201
- );
9202
- };
9203
- var notRecognized = notRecognized2;
9204
- switch (syx[2]) {
9205
- case 66: {
9206
- const messageValue = syx[7];
9207
- if (syx[4] === 64 || syx[4] === 0 && syx[6] === 127) {
9208
- if ((syx[5] & 16) > 0) {
9209
- const channel = [
9210
- 9,
9211
- 0,
9212
- 1,
9213
- 2,
9214
- 3,
9215
- 4,
9216
- 5,
9217
- 6,
9218
- 7,
9219
- 8,
9220
- 10,
9221
- 11,
9222
- 12,
9223
- 13,
9224
- 14,
9225
- 15
9226
- ][syx[5] & 15] + channelOffset;
9227
- const channelObject = this.midiChannels[channel];
9228
- switch (syx[6]) {
9229
- default:
9230
- notRecognized2();
9231
- break;
9232
- case 21: {
9233
- const isDrums = messageValue > 0 && syx[5] >> 4 > 0;
9234
- channelObject.setGSDrums(isDrums);
9235
- SpessaSynthInfo(
9236
- `%cChannel %c${channel}%c ${isDrums ? "is now a drum channel" : "now isn't a drum channel"}%c via: %c${arrayToHexString(syx)}`,
9237
- consoleColors.info,
9238
- consoleColors.value,
9239
- consoleColors.recognized,
9240
- consoleColors.info,
9241
- consoleColors.value
9242
- );
9243
- return;
9244
- }
9245
- case 22: {
9246
- const keyShift = messageValue - 64;
9247
- channelObject.setCustomController(
9248
- customControllers.channelKeyShift,
9249
- keyShift
9250
- );
9251
- niceLogging(
9252
- channel,
9253
- keyShift,
9254
- "key shift",
9255
- "keys"
9256
- );
9257
- return;
9258
- }
9259
- // Pan position
9260
- case 28: {
9261
- const panPosition = messageValue;
9262
- if (panPosition === 0) {
9263
- channelObject.randomPan = true;
9264
- SpessaSynthInfo(
9265
- `%cRandom pan is set to %cON%c for %c${channel}`,
9266
- consoleColors.info,
9267
- consoleColors.recognized,
9268
- consoleColors.info,
9269
- consoleColors.value
9270
- );
9271
- } else {
9272
- channelObject.randomPan = false;
9273
- channelObject.controllerChange(
9274
- midiControllers.pan,
9275
- panPosition
9276
- );
9277
- }
9278
- break;
9279
- }
9280
- // Chorus send
9281
- 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) {
9282
9395
  channelObject.controllerChange(
9283
- midiControllers.chorusDepth,
9284
- messageValue
9396
+ midiControllers.registeredParameterMSB,
9397
+ 0
9285
9398
  );
9286
- break;
9287
- // Reverb send
9288
- case 34:
9289
9399
  channelObject.controllerChange(
9290
- midiControllers.reverbDepth,
9291
- messageValue
9292
- );
9293
- break;
9294
- case 64:
9295
- case 65:
9296
- case 66:
9297
- case 67:
9298
- case 68:
9299
- case 69:
9300
- case 70:
9301
- case 71:
9302
- case 72:
9303
- case 73:
9304
- case 74:
9305
- case 75: {
9306
- const tuningBytes = syx.length - 9;
9307
- const newTuning = new Int8Array(12);
9308
- for (let i = 0; i < tuningBytes; i++) {
9309
- newTuning[i] = syx[i + 7] - 64;
9310
- }
9311
- channelObject.setOctaveTuning(
9312
- newTuning
9313
- );
9314
- const cents = messageValue - 64;
9315
- niceLogging(
9316
- channel,
9317
- newTuning.join(" "),
9318
- "octave scale tuning",
9319
- "cents"
9320
- );
9321
- channelObject.setTuning(cents);
9322
- break;
9323
- }
9324
- }
9325
- return;
9326
- } else if ((syx[5] & 32) > 0) {
9327
- const channel = [
9328
- 9,
9329
- 0,
9330
- 1,
9331
- 2,
9332
- 3,
9333
- 4,
9334
- 5,
9335
- 6,
9336
- 7,
9337
- 8,
9338
- 10,
9339
- 11,
9340
- 12,
9341
- 13,
9342
- 14,
9343
- 15
9344
- ][syx[5] & 15] + channelOffset;
9345
- const channelObject = this.midiChannels[channel];
9346
- const centeredValue = messageValue - 64;
9347
- const normalizedValue = centeredValue / 64;
9348
- const normalizedNotCentered = messageValue / 128;
9349
- const setupReceivers = (source, sourceName, bipolar = false) => {
9350
- switch (syx[6] & 15) {
9351
- case 0:
9352
- if (source === NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel) {
9353
- channelObject.controllerChange(
9354
- midiControllers.registeredParameterMSB,
9355
- 0
9356
- );
9357
- channelObject.controllerChange(
9358
- midiControllers.registeredParameterLSB,
9359
- 0
9360
- );
9361
- channelObject.controllerChange(
9362
- midiControllers.dataEntryMSB,
9363
- Math.floor(centeredValue)
9364
- );
9365
- } else {
9366
- channelObject.sysExModulators.setModulator(
9367
- source,
9368
- generatorTypes.fineTune,
9369
- centeredValue * 100,
9370
- bipolar
9371
- );
9372
- niceLogging(
9373
- channel,
9374
- centeredValue,
9375
- `${sourceName} pitch control`,
9376
- "semitones"
9377
- );
9378
- }
9379
- break;
9380
- case 1:
9381
- channelObject.sysExModulators.setModulator(
9382
- source,
9383
- generatorTypes.initialFilterFc,
9384
- normalizedValue * 9600,
9385
- bipolar
9386
- );
9387
- niceLogging(
9388
- channel,
9389
- normalizedValue * 9600,
9390
- `${sourceName} pitch control`,
9391
- "cents"
9392
- );
9393
- break;
9394
- case 2:
9395
- channelObject.sysExModulators.setModulator(
9396
- source,
9397
- generatorTypes.initialAttenuation,
9398
- normalizedValue * 960,
9399
- // Spec says "100%" so 960cB in sf2
9400
- bipolar
9401
- );
9402
- niceLogging(
9403
- channel,
9404
- normalizedValue * 960,
9405
- `${sourceName} amplitude`,
9406
- "cB"
9407
- );
9408
- break;
9409
- // Rate control is ignored as it is in hertz
9410
- case 4:
9411
- channelObject.sysExModulators.setModulator(
9412
- source,
9413
- generatorTypes.vibLfoToPitch,
9414
- normalizedNotCentered * 600,
9415
- bipolar
9416
- );
9417
- niceLogging(
9418
- channel,
9419
- normalizedNotCentered * 600,
9420
- `${sourceName} LFO1 pitch depth`,
9421
- "cents"
9422
- );
9423
- break;
9424
- case 5:
9425
- channelObject.sysExModulators.setModulator(
9426
- source,
9427
- generatorTypes.vibLfoToFilterFc,
9428
- normalizedNotCentered * 2400,
9429
- bipolar
9430
- );
9431
- niceLogging(
9432
- channel,
9433
- normalizedNotCentered * 2400,
9434
- `${sourceName} LFO1 filter depth`,
9435
- "cents"
9436
- );
9437
- break;
9438
- case 6:
9439
- channelObject.sysExModulators.setModulator(
9440
- source,
9441
- generatorTypes.vibLfoToVolume,
9442
- normalizedValue * 960,
9443
- bipolar
9444
- );
9445
- niceLogging(
9446
- channel,
9447
- normalizedValue * 960,
9448
- `${sourceName} LFO1 amplitude depth`,
9449
- "cB"
9450
- );
9451
- break;
9452
- // Rate control is ignored as it is in hertz
9453
- case 8:
9454
- channelObject.sysExModulators.setModulator(
9455
- source,
9456
- generatorTypes.modLfoToPitch,
9457
- normalizedNotCentered * 600,
9458
- bipolar
9459
- );
9460
- niceLogging(
9461
- channel,
9462
- normalizedNotCentered * 600,
9463
- `${sourceName} LFO2 pitch depth`,
9464
- "cents"
9465
- );
9466
- break;
9467
- case 9:
9468
- channelObject.sysExModulators.setModulator(
9469
- source,
9470
- generatorTypes.modLfoToFilterFc,
9471
- normalizedNotCentered * 2400,
9472
- bipolar
9473
- );
9474
- niceLogging(
9475
- channel,
9476
- normalizedNotCentered * 2400,
9477
- `${sourceName} LFO2 filter depth`,
9478
- "cents"
9479
- );
9480
- break;
9481
- case 10:
9482
- channelObject.sysExModulators.setModulator(
9483
- source,
9484
- generatorTypes.modLfoToVolume,
9485
- normalizedValue * 960,
9486
- bipolar
9487
- );
9488
- niceLogging(
9489
- channel,
9490
- normalizedValue * 960,
9491
- `${sourceName} LFO2 amplitude depth`,
9492
- "cB"
9493
- );
9494
- break;
9495
- }
9496
- };
9497
- switch (syx[6] & 240) {
9498
- default:
9499
- notRecognized2();
9500
- break;
9501
- case 0:
9502
- setupReceivers(
9503
- midiControllers.modulationWheel,
9504
- "mod wheel"
9505
- );
9506
- break;
9507
- case 16:
9508
- setupReceivers(
9509
- NON_CC_INDEX_OFFSET + modulatorSources.pitchWheel,
9510
- "pitch wheel",
9511
- true
9400
+ midiControllers.registeredParameterLSB,
9401
+ 0
9512
9402
  );
9513
- break;
9514
- case 32:
9515
- setupReceivers(
9516
- NON_CC_INDEX_OFFSET + modulatorSources.channelPressure,
9517
- "channel pressure"
9518
- );
9519
- break;
9520
- case 48:
9521
- setupReceivers(
9522
- NON_CC_INDEX_OFFSET + modulatorSources.polyPressure,
9523
- "poly pressure"
9524
- );
9525
- break;
9526
- }
9527
- return;
9528
- } else if (syx[5] === 0) {
9529
- switch (syx[6]) {
9530
- default:
9531
- notRecognized2();
9532
- break;
9533
- case 127:
9534
- if (messageValue === 0) {
9535
- SpessaSynthInfo(
9536
- "%cGS Reset received!",
9537
- consoleColors.info
9538
- );
9539
- this.resetAllControllers(false);
9540
- this.setMasterParameter(
9541
- "midiSystem",
9542
- "gs"
9543
- );
9544
- } else if (messageValue === 127) {
9545
- SpessaSynthInfo(
9546
- "%cGS system off, switching to GM",
9547
- consoleColors.info
9548
- );
9549
- this.resetAllControllers(false);
9550
- this.setMasterParameter(
9551
- "midiSystem",
9552
- "gm"
9553
- );
9554
- }
9555
- break;
9556
- case 6:
9557
- SpessaSynthInfo(
9558
- `%cRoland GS Master Pan set to: %c${messageValue}%c with: %c${arrayToHexString(
9559
- syx
9560
- )}`,
9561
- consoleColors.info,
9562
- consoleColors.value,
9563
- consoleColors.info,
9564
- consoleColors.value
9565
- );
9566
- this.setMasterParameter(
9567
- "masterPan",
9568
- (messageValue - 64) / 64
9569
- );
9570
- break;
9571
- case 4:
9572
- SpessaSynthInfo(
9573
- `%cRoland GS Master Volume set to: %c${messageValue}%c with: %c${arrayToHexString(
9574
- syx
9575
- )}`,
9576
- consoleColors.info,
9577
- consoleColors.value,
9578
- consoleColors.info,
9579
- consoleColors.value
9403
+ channelObject.controllerChange(
9404
+ midiControllers.dataEntryMSB,
9405
+ Math.floor(centeredValue)
9580
9406
  );
9581
- this.setMIDIVolume(messageValue / 127);
9582
- break;
9583
- case 5: {
9584
- const transpose = messageValue - 64;
9585
- SpessaSynthInfo(
9586
- `%cRoland GS Master Key-Shift set to: %c${transpose}%c with: %c${arrayToHexString(
9587
- syx
9588
- )}`,
9589
- consoleColors.info,
9590
- consoleColors.value,
9591
- consoleColors.info,
9592
- consoleColors.value
9407
+ } else {
9408
+ channelObject.sysExModulators.setModulator(
9409
+ source,
9410
+ generatorTypes.fineTune,
9411
+ centeredValue * 100,
9412
+ bipolar
9593
9413
  );
9594
- this.setMasterTuning(transpose * 100);
9595
- break;
9596
- }
9597
- }
9598
- return;
9599
- } else if (syx[5] === 1) {
9600
- switch (syx[6]) {
9601
- default:
9602
- notRecognized2();
9603
- break;
9604
- case 0: {
9605
- const patchName = readBinaryString(
9414
+ sysExLogging(
9606
9415
  syx,
9607
- 16,
9608
- 7
9609
- );
9610
- SpessaSynthInfo(
9611
- `%cGS Patch name: %c${patchName}`,
9612
- consoleColors.info,
9613
- consoleColors.value
9416
+ channel,
9417
+ centeredValue,
9418
+ `${sourceName} pitch control`,
9419
+ "semitones"
9614
9420
  );
9615
- break;
9616
9421
  }
9617
- case 51:
9618
- SpessaSynthInfo(
9619
- `%cGS Reverb level: %c${messageValue}`,
9620
- consoleColors.info,
9621
- consoleColors.value
9622
- );
9623
- this.privateProps.reverbSend = messageValue / 64;
9624
- break;
9625
- // Unsupported reverb params
9626
- case 48:
9627
- case 49:
9628
- case 50:
9629
- case 52:
9630
- case 53:
9631
- case 55:
9632
- SpessaSynthInfo(
9633
- `%cUnsupported GS Reverb Parameter: %c${syx[6].toString(16)}`,
9634
- consoleColors.warn,
9635
- consoleColors.unrecognized
9636
- );
9637
- break;
9638
- case 58:
9639
- SpessaSynthInfo(
9640
- `%cGS Chorus level: %c${messageValue}`,
9641
- consoleColors.info,
9642
- consoleColors.value
9643
- );
9644
- this.privateProps.chorusSend = messageValue / 64;
9645
- break;
9646
- // Unsupported chorus params
9647
- case 56:
9648
- case 57:
9649
- case 59:
9650
- case 60:
9651
- case 61:
9652
- case 62:
9653
- case 63:
9654
- case 64:
9655
- SpessaSynthInfo(
9656
- `%cUnsupported GS Chorus Parameter: %c${syx[6].toString(16)}`,
9657
- consoleColors.warn,
9658
- consoleColors.unrecognized
9659
- );
9660
- break;
9661
- }
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;
9662
9546
  }
9663
- } else {
9664
- 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;
9665
9577
  }
9666
9578
  return;
9667
- }
9668
- case 69: {
9669
- if (syx[4] === 16) {
9670
- if (syx[5] === 0) {
9671
- this.privateProps.callEvent(
9672
- "synthDisplay",
9673
- 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
9674
9608
  );
9675
- } else if (syx[5] === 1) {
9676
- this.privateProps.callEvent(
9677
- "synthDisplay",
9678
- Array.from(syx)
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
9623
+ );
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
9679
9636
  );
9680
- } else {
9681
- notRecognized2();
9637
+ this.setMasterTuning(transpose * 100);
9638
+ break;
9682
9639
  }
9683
9640
  }
9684
9641
  return;
9685
- }
9686
- case 22:
9687
- if (syx[4] === 16) {
9688
- this.setMIDIVolume(syx[7] / 100);
9689
- SpessaSynthInfo(
9690
- `%cRoland Master Volume control set to: %c${syx[7]}%c via: %c${arrayToHexString(
9691
- syx
9692
- )}`,
9693
- consoleColors.info,
9694
- consoleColors.value,
9695
- consoleColors.info,
9696
- consoleColors.value
9697
- );
9698
- return;
9699
- }
9700
- }
9701
- } else {
9702
- SpessaSynthInfo(
9703
- `%cUnrecognized Roland SysEx: %c${arrayToHexString(syx)}`,
9704
- consoleColors.warn,
9705
- consoleColors.unrecognized
9706
- );
9707
- return;
9708
- }
9709
- break;
9710
- // Yamaha
9711
- // http://www.studio4all.de/htmle/main91.html
9712
- case 67:
9713
- if (syx[2] === 76) {
9714
- if (syx[3] === 0 && syx[4] === 0) {
9715
- switch (syx[5]) {
9716
- // Master volume
9717
- case 4: {
9718
- const vol = syx[6];
9719
- this.setMIDIVolume(vol / 127);
9720
- SpessaSynthInfo(
9721
- `%cXG master volume. Volume: %c${vol}`,
9722
- consoleColors.info,
9723
- consoleColors.recognized
9724
- );
9725
- break;
9726
- }
9727
- // Master transpose
9728
- case 6: {
9729
- const transpose = syx[6] - 64;
9730
- this.setMasterParameter("transposition", transpose);
9731
- SpessaSynthInfo(
9732
- `%cXG master transpose. Volume: %c${transpose}`,
9733
- consoleColors.info,
9734
- consoleColors.recognized
9735
- );
9736
- break;
9737
- }
9738
- // XG on
9739
- case 126:
9740
- SpessaSynthInfo(
9741
- "%cXG system on",
9742
- consoleColors.info
9743
- );
9744
- this.resetAllControllers(false);
9745
- this.setMasterParameter("midiSystem", "xg");
9746
- break;
9747
- }
9748
- } else if (syx[3] === 8) {
9749
- if (!BankSelectHacks.isSystemXG(
9750
- this.privateProps.masterParameters.midiSystem
9751
- )) {
9752
- return;
9753
- }
9754
- const channel = syx[4] + channelOffset;
9755
- if (channel >= this.midiChannels.length) {
9756
- return;
9757
- }
9758
- const channelObject = this.midiChannels[channel];
9759
- const value = syx[6];
9760
- switch (syx[5]) {
9761
- // Bank-select MSB
9762
- case 1:
9763
- channelObject.controllerChange(
9764
- midiControllers.bankSelect,
9765
- value
9766
- );
9767
- break;
9768
- // Bank-select LSB
9769
- case 2:
9770
- channelObject.controllerChange(
9771
- midiControllers.bankSelectLSB,
9772
- value
9773
- );
9774
- break;
9775
- // Program change
9776
- case 3:
9777
- channelObject.programChange(value);
9778
- break;
9779
- // Note shift
9780
- case 8: {
9781
- if (channelObject.drumChannel) {
9782
- 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;
9783
9655
  }
9784
- channelObject.channelTransposeKeyShift = value - 64;
9785
- break;
9786
- }
9787
- // Volume
9788
- case 11:
9789
- channelObject.controllerChange(
9790
- midiControllers.mainVolume,
9791
- value
9792
- );
9793
- break;
9794
- // Pan position
9795
- case 14: {
9796
- const pan = value;
9797
- if (pan === 0) {
9798
- channelObject.randomPan = true;
9656
+ case 51:
9799
9657
  SpessaSynthInfo(
9800
- `%cRandom pan is set to %cON%c for %c${channel}`,
9658
+ `%cGS Reverb level: %c${messageValue}`,
9801
9659
  consoleColors.info,
9802
- 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}`,
9803
9680
  consoleColors.info,
9804
9681
  consoleColors.value
9805
9682
  );
9806
- } else {
9807
- channelObject.controllerChange(
9808
- midiControllers.pan,
9809
- 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
9810
9698
  );
9811
- }
9812
- break;
9699
+ break;
9813
9700
  }
9814
- // Reverb
9815
- case 19:
9816
- channelObject.controllerChange(
9817
- midiControllers.reverbDepth,
9818
- value
9819
- );
9820
- break;
9821
- // Chorus
9822
- case 18:
9823
- channelObject.controllerChange(
9824
- midiControllers.chorusDepth,
9825
- value
9826
- );
9827
- break;
9828
- default:
9829
- SpessaSynthInfo(
9830
- `%cUnrecognized Yamaha XG Part Setup: %c${syx[5].toString(16).toUpperCase()}`,
9831
- consoleColors.warn,
9832
- consoleColors.unrecognized
9833
- );
9834
9701
  }
9835
- } else if (syx[3] === 6 && // XG System parameter
9836
- syx[4] === 0) {
9837
- this.privateProps.callEvent(
9838
- "synthDisplay",
9839
- 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
9840
9736
  );
9841
- } else if (BankSelectHacks.isSystemXG(
9842
- this.privateProps.masterParameters.midiSystem
9843
- )) {
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);
9844
9770
  SpessaSynthInfo(
9845
- `%cUnrecognized Yamaha XG SysEx: %c${arrayToHexString(syx)}`,
9846
- consoleColors.warn,
9847
- consoleColors.unrecognized
9771
+ `%cXG master volume. Volume: %c${vol}`,
9772
+ consoleColors.info,
9773
+ consoleColors.recognized
9848
9774
  );
9775
+ break;
9849
9776
  }
9850
- } else {
9851
- if (BankSelectHacks.isSystemXG(
9852
- this.privateProps.masterParameters.midiSystem
9853
- )) {
9777
+ // Master attenuation
9778
+ case 5: {
9779
+ const vol = 127 - syx[6];
9780
+ this.setMIDIVolume(vol / 127);
9854
9781
  SpessaSynthInfo(
9855
- `%cUnrecognized Yamaha SysEx: %c${arrayToHexString(syx)}`,
9856
- consoleColors.warn,
9857
- 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
9858
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;
9859
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
+ );
9860
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);
9861
10020
  break;
9862
10021
  }
9863
10022
  }
@@ -10251,8 +10410,7 @@ var ProtectedSynthValues = class {
10251
10410
  voiceKilling;
10252
10411
  /**
10253
10412
  * Cached voices for all presets for this synthesizer.
10254
- * Nesting goes like this:
10255
- * 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.
10256
10414
  */
10257
10415
  cachedVoices = [];
10258
10416
  constructor(eventCallbackHandler, getVoices, voiceKillingFunction, volumeEnvelopeSmoothingFactor, panSmoothingFactor, filterSmoothingFactor) {
@@ -10606,13 +10764,14 @@ var nonRegisteredMSB = {
10606
10764
  awe32: 127,
10607
10765
  SF2: 120
10608
10766
  };
10609
- var nonRegisteredGSLSB = {
10767
+ var nonRegisteredLSB = {
10610
10768
  vibratoRate: 8,
10611
10769
  vibratoDepth: 9,
10612
10770
  vibratoDelay: 10,
10613
10771
  TVFFilterCutoff: 32,
10614
10772
  TVFFilterResonance: 33,
10615
10773
  EGAttackTime: 99,
10774
+ EGDecayTime: 100,
10616
10775
  EGReleaseTime: 102
10617
10776
  };
10618
10777
  function dataEntryCoarse(dataValue) {
@@ -10641,7 +10800,7 @@ function dataEntryCoarse(dataValue) {
10641
10800
  default:
10642
10801
  case dataEntryStates.Idle:
10643
10802
  break;
10644
- // Process GS NRPNs
10803
+ // Process NRPNs
10645
10804
  case dataEntryStates.NRPFine: {
10646
10805
  if (this.lockGSNRPNParams) {
10647
10806
  return;
@@ -10668,7 +10827,7 @@ function dataEntryCoarse(dataValue) {
10668
10827
  consoleColors.value
10669
10828
  );
10670
10829
  break;
10671
- // Part parameters: vibrato, cutoff
10830
+ // Part parameters
10672
10831
  case nonRegisteredMSB.partParameter:
10673
10832
  switch (NRPNFine) {
10674
10833
  default:
@@ -10687,8 +10846,8 @@ function dataEntryCoarse(dataValue) {
10687
10846
  consoleColors.value
10688
10847
  );
10689
10848
  break;
10690
- // Vibrato rate
10691
- case nonRegisteredGSLSB.vibratoRate:
10849
+ // Vibrato rate (custom vibrato)
10850
+ case nonRegisteredLSB.vibratoRate:
10692
10851
  if (dataValue === 64) {
10693
10852
  return;
10694
10853
  }
@@ -10700,8 +10859,8 @@ function dataEntryCoarse(dataValue) {
10700
10859
  "Hz"
10701
10860
  );
10702
10861
  break;
10703
- // Vibrato depth
10704
- case nonRegisteredGSLSB.vibratoDepth:
10862
+ // Vibrato depth (custom vibrato)
10863
+ case nonRegisteredLSB.vibratoDepth:
10705
10864
  if (dataValue === 64) {
10706
10865
  return;
10707
10866
  }
@@ -10713,8 +10872,8 @@ function dataEntryCoarse(dataValue) {
10713
10872
  "cents of detune"
10714
10873
  );
10715
10874
  break;
10716
- // Vibrato delay
10717
- case nonRegisteredGSLSB.vibratoDelay:
10875
+ // Vibrato delay (custom vibrato)
10876
+ case nonRegisteredLSB.vibratoDelay:
10718
10877
  if (dataValue === 64) {
10719
10878
  return;
10720
10879
  }
@@ -10727,15 +10886,26 @@ function dataEntryCoarse(dataValue) {
10727
10886
  );
10728
10887
  break;
10729
10888
  // Filter cutoff
10730
- case nonRegisteredGSLSB.TVFFilterCutoff:
10889
+ case nonRegisteredLSB.TVFFilterCutoff:
10731
10890
  this.controllerChange(
10732
10891
  midiControllers.brightness,
10733
10892
  dataValue
10734
10893
  );
10735
10894
  coolInfo("Filter cutoff", dataValue.toString(), "");
10736
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;
10737
10907
  // Attack time
10738
- case nonRegisteredGSLSB.EGAttackTime:
10908
+ case nonRegisteredLSB.EGAttackTime:
10739
10909
  this.controllerChange(
10740
10910
  midiControllers.attackTime,
10741
10911
  dataValue
@@ -10746,8 +10916,15 @@ function dataEntryCoarse(dataValue) {
10746
10916
  ""
10747
10917
  );
10748
10918
  break;
10919
+ case nonRegisteredLSB.EGDecayTime:
10920
+ this.controllerChange(
10921
+ midiControllers.decayTime,
10922
+ dataValue
10923
+ );
10924
+ coolInfo("EG decay time", dataValue.toString(), "");
10925
+ break;
10749
10926
  // Release time
10750
- case nonRegisteredGSLSB.EGReleaseTime:
10927
+ case nonRegisteredLSB.EGReleaseTime:
10751
10928
  this.controllerChange(
10752
10929
  midiControllers.releaseTime,
10753
10930
  dataValue
@@ -11164,9 +11341,10 @@ var portamentoLookup = {
11164
11341
  124: 80,
11165
11342
  127: 480
11166
11343
  };
11167
- function getLookup(value) {
11344
+ function portaTimeToRate(value) {
11345
+ let portaTime = 0;
11168
11346
  if (portamentoLookup[value] !== void 0) {
11169
- return portamentoLookup[value];
11347
+ portaTime = portamentoLookup[value];
11170
11348
  }
11171
11349
  let lower = null;
11172
11350
  let upper = null;
@@ -11182,12 +11360,12 @@ function getLookup(value) {
11182
11360
  if (lower !== null && upper !== null) {
11183
11361
  const lowerTime = portamentoLookup[lower];
11184
11362
  const upperTime = portamentoLookup[upper];
11185
- return lowerTime + (value - lower) * (upperTime - lowerTime) / (upper - lower);
11363
+ portaTime = lowerTime + (value - lower) * (upperTime - lowerTime) / (upper - lower);
11186
11364
  }
11187
- return 0;
11365
+ return portaTime / 40;
11188
11366
  }
11189
11367
  function portamentoTimeToSeconds(time, distance) {
11190
- return getLookup(time) * (distance / 36);
11368
+ return portaTimeToRate(time) * distance;
11191
11369
  }
11192
11370
 
11193
11371
  // src/synthesizer/audio_engine/engine_methods/note_on.ts
@@ -11231,16 +11409,15 @@ function noteOn(midiNote, velocity) {
11231
11409
  let portamentoFromKey = -1;
11232
11410
  let portamentoDuration = 0;
11233
11411
  const portamentoTime = this.midiControllers[midiControllers.portamentoTime] >> 7;
11234
- const control = this.midiControllers[midiControllers.portamentoControl];
11235
- const currentFromKey = control >> 7;
11412
+ const portaControl = this.midiControllers[midiControllers.portamentoControl] >> 7;
11236
11413
  if (!this.drumChannel && // No portamento on drum channel
11237
- currentFromKey !== internalMidiNote && // If the same note, there's no portamento
11414
+ portaControl !== internalMidiNote && // If the same note, there's no portamento
11238
11415
  this.midiControllers[midiControllers.portamentoOnOff] >= 8192 && // (64 << 7)
11239
11416
  portamentoTime > 0) {
11240
- if (control !== 1) {
11241
- const diff = Math.abs(internalMidiNote - currentFromKey);
11417
+ if (portaControl > 0) {
11418
+ const diff = Math.abs(internalMidiNote - portaControl);
11242
11419
  portamentoDuration = portamentoTimeToSeconds(portamentoTime, diff);
11243
- portamentoFromKey = currentFromKey;
11420
+ portamentoFromKey = portaControl;
11244
11421
  }
11245
11422
  this.controllerChange(
11246
11423
  midiControllers.portamentoControl,
@@ -17058,16 +17235,12 @@ var SpessaSynthProcessor3 = class {
17058
17235
  });
17059
17236
  }
17060
17237
  getCachedVoice(patch, midiNote, velocity) {
17061
- let bankMSB = patch.bankMSB;
17062
- let bankLSB = patch.bankLSB;
17063
- const { isGMGSDrum, program } = patch;
17064
- if (isGMGSDrum) {
17065
- bankMSB = 128;
17066
- bankLSB = 0;
17067
- }
17068
- return this.privateProps.cachedVoices?.[bankMSB]?.[bankLSB]?.[program]?.[midiNote]?.[velocity];
17238
+ return this.privateProps.cachedVoices?.[this.getCachedVoiceIndex(patch, midiNote, velocity)];
17069
17239
  }
17070
17240
  setCachedVoice(patch, midiNote, velocity, voices) {
17241
+ this.privateProps.cachedVoices[this.getCachedVoiceIndex(patch, midiNote, velocity)] = voices;
17242
+ }
17243
+ getCachedVoiceIndex(patch, midiNote, velocity) {
17071
17244
  let bankMSB = patch.bankMSB;
17072
17245
  let bankLSB = patch.bankLSB;
17073
17246
  const { isGMGSDrum, program } = patch;
@@ -17075,19 +17248,11 @@ var SpessaSynthProcessor3 = class {
17075
17248
  bankMSB = 128;
17076
17249
  bankLSB = 0;
17077
17250
  }
17078
- if (!this.privateProps.cachedVoices[bankMSB]) {
17079
- this.privateProps.cachedVoices[bankMSB] = [];
17080
- }
17081
- if (!this.privateProps.cachedVoices[bankMSB][bankLSB]) {
17082
- this.privateProps.cachedVoices[bankMSB][bankLSB] = [];
17083
- }
17084
- if (!this.privateProps.cachedVoices[bankMSB][bankLSB][program]) {
17085
- this.privateProps.cachedVoices[bankMSB][bankLSB][program] = [];
17086
- }
17087
- if (!this.privateProps.cachedVoices[bankMSB][bankLSB][program][midiNote]) {
17088
- this.privateProps.cachedVoices[bankMSB][bankLSB][program][midiNote] = [];
17089
- }
17090
- 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;
17091
17256
  }
17092
17257
  createMIDIChannelInternal(sendEvent) {
17093
17258
  const channel = new MIDIChannel(
@@ -17111,7 +17276,7 @@ var SpessaSynthProcessor3 = class {
17111
17276
  this.midiChannels.forEach((c) => {
17112
17277
  c.setPresetLock(false);
17113
17278
  });
17114
- this.resetAllControllers(false);
17279
+ this.resetAllControllers();
17115
17280
  }
17116
17281
  getDefaultPresets() {
17117
17282
  this.privateProps.defaultPreset = this.soundBankManager.getPreset(
@@ -17165,7 +17330,6 @@ export {
17165
17330
  Modulator,
17166
17331
  ModulatorSource,
17167
17332
  NON_CC_INDEX_OFFSET,
17168
- PORTAMENTO_CONTROL_UNSET,
17169
17333
  SoundBankLoader,
17170
17334
  SpessaSynthCoreUtils,
17171
17335
  SpessaSynthLogging,