spessasynth_core 4.4.5 → 4.4.7
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/README.md +19 -16
- package/dist/index.d.ts +630 -132
- package/dist/index.js +1116 -313
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -90,7 +90,6 @@ function readLittleEndian(dataArray, bytesAmount, offset = 0) {
|
|
|
90
90
|
* @param dataArray the IndexedByteArray to write to.
|
|
91
91
|
* @param number the number to write.
|
|
92
92
|
* @param byteTarget the amount of bytes to use. Excess bytes will be set to zero.
|
|
93
|
-
* @returns the little endian representation of the number.
|
|
94
93
|
*/
|
|
95
94
|
function writeLittleEndianIndexed(dataArray, number, byteTarget) {
|
|
96
95
|
for (let i = 0; i < byteTarget; i++) dataArray[dataArray.currentIndex++] = number >> i * 8 & 255;
|
|
@@ -359,8 +358,8 @@ var RIFFChunk = class RIFFChunk {
|
|
|
359
358
|
* Writes a RIFF chunk correctly.
|
|
360
359
|
* @param header the fourCC code of the header.
|
|
361
360
|
* @param data the binary chunk data.
|
|
362
|
-
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
363
361
|
* @param rf64 if the chunk uses a 64-bit size.
|
|
362
|
+
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
364
363
|
* @returns the binary data.
|
|
365
364
|
*/
|
|
366
365
|
static write(header, data, rf64 = false, isList = false) {
|
|
@@ -391,8 +390,8 @@ var RIFFChunk = class RIFFChunk {
|
|
|
391
390
|
* This allows avoiding large array allocations and only one writeParts call at the end.
|
|
392
391
|
* @param header the fourCC code of the header.
|
|
393
392
|
* @param chunks binary chunk data parts, will be combined in order.
|
|
394
|
-
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
395
393
|
* @param rf64 if the chunk uses a 64-bit size.
|
|
394
|
+
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
396
395
|
* @returns the chunk as binary blobs.
|
|
397
396
|
*/
|
|
398
397
|
static getParts(header, chunks, rf64 = false, isList = false) {
|
|
@@ -421,8 +420,8 @@ var RIFFChunk = class RIFFChunk {
|
|
|
421
420
|
* It merges them together into data and allocates one large array.
|
|
422
421
|
* @param header the fourCC code of the header.
|
|
423
422
|
* @param chunks binary chunk data parts, will be combined in order.
|
|
424
|
-
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
425
423
|
* @param rf64 if the chunk uses a 64-bit size.
|
|
424
|
+
* @param isList if a "LIST" should be set as the chunk type and the actual type should be written at the start of the data.
|
|
426
425
|
* @returns the binary data.
|
|
427
426
|
*/
|
|
428
427
|
static writeParts(header, chunks, rf64 = false, isList = false) {
|
|
@@ -673,8 +672,9 @@ var SpessaLog = class {
|
|
|
673
672
|
/**
|
|
674
673
|
* Enables or disables logging at various levels.
|
|
675
674
|
* All the input variables are booleans corresponding to the things SpessaSynth logs.
|
|
676
|
-
* @param enableInfo enables info: all general info such as parsing sound banks, MIDI files, RPN changes,
|
|
677
|
-
*
|
|
675
|
+
* @param enableInfo enables info: all general info such as parsing sound banks, MIDI files, RPN changes,
|
|
676
|
+
* messages unrecognized by the synthesizer, etc.
|
|
677
|
+
* @param enableWarn enables warning: unexpected and erroneous behavior, such as invalid data or missing presets.
|
|
678
678
|
* @param enableGroup enables groups: the groups for parsing the sound banks and MIDI files.
|
|
679
679
|
*/
|
|
680
680
|
static setLogLevel(enableInfo, enableWarn, enableGroup) {
|
|
@@ -831,7 +831,7 @@ const MIDIMessageTypes = {
|
|
|
831
831
|
sequenceSpecific: 127
|
|
832
832
|
};
|
|
833
833
|
/**
|
|
834
|
-
* All MIDI 1.0 controller
|
|
834
|
+
* All MIDI 1.0 controller numbers.
|
|
835
835
|
*
|
|
836
836
|
* > **Tip**
|
|
837
837
|
* >
|
|
@@ -888,10 +888,10 @@ const MIDIControllers = {
|
|
|
888
888
|
effectControl2LSB: 45,
|
|
889
889
|
undefinedCC14LSB: 46,
|
|
890
890
|
undefinedCC15LSB: 47,
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
891
|
+
generalPurposeController1LSB: 48,
|
|
892
|
+
generalPurposeController2LSB: 49,
|
|
893
|
+
generalPurposeController3LSB: 50,
|
|
894
|
+
generalPurposeController4LSB: 51,
|
|
895
895
|
undefinedCC20LSB: 52,
|
|
896
896
|
undefinedCC21LSB: 53,
|
|
897
897
|
undefinedCC22LSB: 54,
|
|
@@ -942,32 +942,120 @@ const MIDIControllers = {
|
|
|
942
942
|
nonRegisteredParameterMSB: 99,
|
|
943
943
|
registeredParameterLSB: 100,
|
|
944
944
|
registeredParameterMSB: 101,
|
|
945
|
+
undefinedCC102: 102,
|
|
946
|
+
undefinedCC103: 103,
|
|
947
|
+
undefinedCC104: 104,
|
|
948
|
+
undefinedCC105: 105,
|
|
949
|
+
undefinedCC106: 106,
|
|
950
|
+
undefinedCC107: 107,
|
|
951
|
+
undefinedCC108: 108,
|
|
952
|
+
undefinedCC109: 109,
|
|
953
|
+
undefinedCC110: 110,
|
|
954
|
+
undefinedCC111: 111,
|
|
955
|
+
undefinedCC112: 112,
|
|
956
|
+
undefinedCC113: 113,
|
|
957
|
+
undefinedCC114: 114,
|
|
958
|
+
undefinedCC115: 115,
|
|
959
|
+
undefinedCC116: 116,
|
|
960
|
+
undefinedCC117: 117,
|
|
961
|
+
undefinedCC118: 118,
|
|
962
|
+
undefinedCC119: 119,
|
|
963
|
+
allSoundOff: 120,
|
|
964
|
+
resetAllControllers: 121,
|
|
965
|
+
localControlOnOff: 122,
|
|
966
|
+
allNotesOff: 123,
|
|
967
|
+
omniModeOff: 124,
|
|
968
|
+
omniModeOn: 125,
|
|
969
|
+
monoModeOn: 126,
|
|
970
|
+
polyModeOn: 127,
|
|
971
|
+
/**
|
|
972
|
+
* @deprecated Use {@link MIDIControllers.generalPurposeController1LSB} instead.
|
|
973
|
+
*/
|
|
974
|
+
undefinedCC16LSB: 48,
|
|
975
|
+
/**
|
|
976
|
+
* @deprecated Use {@link MIDIControllers.generalPurposeController2LSB} instead.
|
|
977
|
+
*/
|
|
978
|
+
undefinedCC17LSB: 49,
|
|
979
|
+
/**
|
|
980
|
+
* @deprecated Use {@link MIDIControllers.generalPurposeController3LSB} instead.
|
|
981
|
+
*/
|
|
982
|
+
undefinedCC18LSB: 50,
|
|
983
|
+
/**
|
|
984
|
+
* @deprecated Use {@link MIDIControllers.generalPurposeController4LSB} instead.
|
|
985
|
+
*/
|
|
986
|
+
undefinedCC19LSB: 51,
|
|
987
|
+
/**
|
|
988
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC102} instead.
|
|
989
|
+
*/
|
|
945
990
|
undefinedCC102LSB: 102,
|
|
991
|
+
/**
|
|
992
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC103} instead.
|
|
993
|
+
*/
|
|
946
994
|
undefinedCC103LSB: 103,
|
|
995
|
+
/**
|
|
996
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC104} instead.
|
|
997
|
+
*/
|
|
947
998
|
undefinedCC104LSB: 104,
|
|
999
|
+
/**
|
|
1000
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC105} instead.
|
|
1001
|
+
*/
|
|
948
1002
|
undefinedCC105LSB: 105,
|
|
1003
|
+
/**
|
|
1004
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC106} instead.
|
|
1005
|
+
*/
|
|
949
1006
|
undefinedCC106LSB: 106,
|
|
1007
|
+
/**
|
|
1008
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC107} instead.
|
|
1009
|
+
*/
|
|
950
1010
|
undefinedCC107LSB: 107,
|
|
1011
|
+
/**
|
|
1012
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC108} instead.
|
|
1013
|
+
*/
|
|
951
1014
|
undefinedCC108LSB: 108,
|
|
1015
|
+
/**
|
|
1016
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC109} instead.
|
|
1017
|
+
*/
|
|
952
1018
|
undefinedCC109LSB: 109,
|
|
1019
|
+
/**
|
|
1020
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC110} instead.
|
|
1021
|
+
*/
|
|
953
1022
|
undefinedCC110LSB: 110,
|
|
1023
|
+
/**
|
|
1024
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC111} instead.
|
|
1025
|
+
*/
|
|
954
1026
|
undefinedCC111LSB: 111,
|
|
1027
|
+
/**
|
|
1028
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC112} instead.
|
|
1029
|
+
*/
|
|
955
1030
|
undefinedCC112LSB: 112,
|
|
1031
|
+
/**
|
|
1032
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC113} instead.
|
|
1033
|
+
*/
|
|
956
1034
|
undefinedCC113LSB: 113,
|
|
1035
|
+
/**
|
|
1036
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC114} instead.
|
|
1037
|
+
*/
|
|
957
1038
|
undefinedCC114LSB: 114,
|
|
1039
|
+
/**
|
|
1040
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC115} instead.
|
|
1041
|
+
*/
|
|
958
1042
|
undefinedCC115LSB: 115,
|
|
1043
|
+
/**
|
|
1044
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC116} instead.
|
|
1045
|
+
*/
|
|
959
1046
|
undefinedCC116LSB: 116,
|
|
1047
|
+
/**
|
|
1048
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC117} instead.
|
|
1049
|
+
*/
|
|
960
1050
|
undefinedCC117LSB: 117,
|
|
1051
|
+
/**
|
|
1052
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC118} instead.
|
|
1053
|
+
*/
|
|
961
1054
|
undefinedCC118LSB: 118,
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
allNotesOff: 123,
|
|
967
|
-
omniModeOff: 124,
|
|
968
|
-
omniModeOn: 125,
|
|
969
|
-
monoModeOn: 126,
|
|
970
|
-
polyModeOn: 127
|
|
1055
|
+
/**
|
|
1056
|
+
* @deprecated Use {@link MIDIControllers.undefinedCC119} instead.
|
|
1057
|
+
*/
|
|
1058
|
+
undefinedCC119LSB: 119
|
|
971
1059
|
};
|
|
972
1060
|
/**
|
|
973
1061
|
* General MIDI Registered Parameter Numbers.
|
|
@@ -1024,7 +1112,7 @@ const NonRegisteredParameterTypesLSB = {
|
|
|
1024
1112
|
// src/midi/midi_message.ts
|
|
1025
1113
|
/**
|
|
1026
1114
|
* Midi_message.ts
|
|
1027
|
-
* purpose: contains
|
|
1115
|
+
* purpose: contains the MIDIMessage class and functions to create common messages
|
|
1028
1116
|
*/
|
|
1029
1117
|
/**
|
|
1030
1118
|
* This class represents a single MIDI 1.0 message.
|
|
@@ -1251,7 +1339,7 @@ const EMBEDDED_SOUND_BANK_ID = `SPESSASYNTH_EMBEDDED_BANK_${Math.random()}_DO_NO
|
|
|
1251
1339
|
const GENERATOR_OVERRIDE_NO_CHANGE_VALUE = 32767;
|
|
1252
1340
|
const DEFAULT_SYNTH_METHOD_OPTIONS = { time: 0 };
|
|
1253
1341
|
/**
|
|
1254
|
-
* If the note is released faster than that, it forced to last that long
|
|
1342
|
+
* If the note is released faster than that, it is forced to last that long
|
|
1255
1343
|
* This is used mostly for drum channels, where a lot of midis like to send instant note off after a note on
|
|
1256
1344
|
* @internal
|
|
1257
1345
|
*/
|
|
@@ -1900,13 +1988,13 @@ const GeneratorLimits = Object.freeze({
|
|
|
1900
1988
|
},
|
|
1901
1989
|
[GeneratorTypes.vibLfoWaveform]: {
|
|
1902
1990
|
min: 0,
|
|
1903
|
-
max:
|
|
1991
|
+
max: 2,
|
|
1904
1992
|
def: 0,
|
|
1905
1993
|
nrpn: 0
|
|
1906
1994
|
},
|
|
1907
1995
|
[GeneratorTypes.modLfoWaveform]: {
|
|
1908
1996
|
min: 0,
|
|
1909
|
-
max:
|
|
1997
|
+
max: 2,
|
|
1910
1998
|
def: 0,
|
|
1911
1999
|
nrpn: 0
|
|
1912
2000
|
},
|
|
@@ -2051,7 +2139,7 @@ const ModulatorControllerSources = {
|
|
|
2051
2139
|
*/
|
|
2052
2140
|
pitchWheel: 14,
|
|
2053
2141
|
/**
|
|
2054
|
-
* The current pitch wheel range (where 1 means
|
|
2142
|
+
* The current pitch wheel range (where 1 means 128 semitones) on the channel.
|
|
2055
2143
|
*/
|
|
2056
2144
|
pitchWheelRange: 16,
|
|
2057
2145
|
/**
|
|
@@ -2074,7 +2162,7 @@ const ModulatorCurveTypes = {
|
|
|
2074
2162
|
*/
|
|
2075
2163
|
concave: 1,
|
|
2076
2164
|
/**
|
|
2077
|
-
*
|
|
2165
|
+
* Exponential ramp, the inverse of concave.
|
|
2078
2166
|
*/
|
|
2079
2167
|
convex: 2,
|
|
2080
2168
|
/**
|
|
@@ -2136,6 +2224,7 @@ DEFAULT_DRUM_REVERB[36] = 0;
|
|
|
2136
2224
|
* Reset all controllers for channel.
|
|
2137
2225
|
* This will reset all controllers to their default values,
|
|
2138
2226
|
* except for the locked controllers.
|
|
2227
|
+
* @param sendCCEvents If controller change events should be emitted for the reset controllers.
|
|
2139
2228
|
*/
|
|
2140
2229
|
function resetChannelInternal(sendCCEvents = true) {
|
|
2141
2230
|
for (let cc = 0; cc < 128; cc++) {
|
|
@@ -2157,6 +2246,7 @@ function resetChannelInternal(sendCCEvents = true) {
|
|
|
2157
2246
|
this.resetMIDIParameter("modulationDepth");
|
|
2158
2247
|
this.resetMIDIParameter("rxChannel");
|
|
2159
2248
|
this.resetMIDIParameter("efxAssign");
|
|
2249
|
+
this.resetMIDIParameter("eqEnabled");
|
|
2160
2250
|
this.resetMIDIParameter("polyMode");
|
|
2161
2251
|
this.resetMIDIParameter("keyShift");
|
|
2162
2252
|
this.resetMIDIParameter("fineTune");
|
|
@@ -2170,6 +2260,7 @@ function resetChannelInternal(sendCCEvents = true) {
|
|
|
2170
2260
|
this.resetMIDIParameter("dryLevel");
|
|
2171
2261
|
this.octaveTuning.fill(0);
|
|
2172
2262
|
this.lastPortamentoNote = this.channelSystem === "xg" ? 60 : -1;
|
|
2263
|
+
this.portamentoForce = false;
|
|
2173
2264
|
this.resetDrumParams();
|
|
2174
2265
|
this.resetGeneratorOverrides();
|
|
2175
2266
|
this.resetGeneratorOffsets();
|
|
@@ -2293,7 +2384,7 @@ var DrumParameterUtils = class {
|
|
|
2293
2384
|
dest.rxNoteOn = source.rxNoteOn;
|
|
2294
2385
|
}
|
|
2295
2386
|
/**
|
|
2296
|
-
* Checks if this
|
|
2387
|
+
* Checks if this drum parameter is the default.
|
|
2297
2388
|
* @param param the param to check.
|
|
2298
2389
|
* @param midiNote the MIDI note of this parameter.
|
|
2299
2390
|
*/
|
|
@@ -2377,6 +2468,7 @@ function applySnapshotInternal(midi, snapshot) {
|
|
|
2377
2468
|
gsReverbParams: snapshot.systemParameters.gsReverbLock ? snapshot.gsReverbProcessor : void 0,
|
|
2378
2469
|
gsChorusParams: snapshot.systemParameters.gsChorusLock ? snapshot.gsChorusProcessor : void 0,
|
|
2379
2470
|
gsDelayParams: snapshot.systemParameters.gsDelayLock ? snapshot.gsDelayProcessor : void 0,
|
|
2471
|
+
gsEqualizerParams: snapshot.systemParameters.gsEqualizerLock ? snapshot.gsEqualizerProcessor : void 0,
|
|
2380
2472
|
gsInsertionParams: snapshot.systemParameters.gsInsertionLock ? snapshot.gsInsertionBlock : void 0,
|
|
2381
2473
|
xgReverbParams: snapshot.systemParameters.xgReverbLock ? snapshot.xgReverbBlock : void 0,
|
|
2382
2474
|
xgChorusParams: snapshot.systemParameters.xgChorusLock ? snapshot.xgChorusBlock : void 0,
|
|
@@ -2389,6 +2481,7 @@ function applySnapshotInternal(midi, snapshot) {
|
|
|
2389
2481
|
// src/midi/midi_tools/get_note_times.ts
|
|
2390
2482
|
/**
|
|
2391
2483
|
* Calculates all note times in seconds.
|
|
2484
|
+
* MIDI ports are ignored, every port is merged into the same 16 channels.
|
|
2392
2485
|
* @param midi the midi to use
|
|
2393
2486
|
* @param minDrumLength the shortest a drum note (channel 10) can be, in seconds.
|
|
2394
2487
|
* @returns an array of 16 channels, each channel containing its notes,
|
|
@@ -2462,26 +2555,32 @@ var MIDIPatchTools = class MIDIPatchTools {
|
|
|
2462
2555
|
/**
|
|
2463
2556
|
* Converts a given {@link MIDIPatch} to a string.
|
|
2464
2557
|
* The format is:
|
|
2465
|
-
* - `DRUM:program` for `isGMGSDrum` set to `true`.
|
|
2558
|
+
* - `DRUM:bankMSB:bankLSB:program` for `isGMGSDrum` set to `true`.
|
|
2466
2559
|
* - `bankLSB:bankMSB:program` for `isGMGSDrum` set to `false`.
|
|
2560
|
+
* @param patch The patch to convert.
|
|
2467
2561
|
*/
|
|
2468
2562
|
static toMIDIString(patch) {
|
|
2469
|
-
if (patch.isGMGSDrum) return `DRUM:${patch.program}`;
|
|
2563
|
+
if (patch.isGMGSDrum) return `DRUM:${patch.bankMSB}:${patch.bankLSB}:${patch.program}`;
|
|
2470
2564
|
return `${patch.bankLSB}:${patch.bankMSB}:${patch.program}`;
|
|
2471
2565
|
}
|
|
2472
2566
|
/**
|
|
2473
2567
|
* Gets {@link MIDIPatch} from a given string.
|
|
2568
|
+
* @param string The string in the {@link MIDIPatchTools.toMIDIString} format.
|
|
2474
2569
|
*/
|
|
2475
2570
|
static fromMIDIString(string) {
|
|
2476
2571
|
const parts = string.split(":");
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2572
|
+
if (string.startsWith("DRUM")) {
|
|
2573
|
+
if (parts.length !== 4 && parts.length !== 2) throw new Error(`Invalid MIDI string: ${string}`);
|
|
2574
|
+
const long = parts.length === 4;
|
|
2575
|
+
return {
|
|
2576
|
+
bankMSB: long ? Number.parseInt(parts[1]) : 0,
|
|
2577
|
+
bankLSB: long ? Number.parseInt(parts[2]) : 0,
|
|
2578
|
+
program: Number.parseInt(parts[parts.length - 1]),
|
|
2579
|
+
isGMGSDrum: true
|
|
2580
|
+
};
|
|
2581
|
+
}
|
|
2582
|
+
if (parts.length !== 3) throw new Error(`Invalid MIDI string: ${string}`);
|
|
2583
|
+
return {
|
|
2485
2584
|
bankLSB: Number.parseInt(parts[0]),
|
|
2486
2585
|
bankMSB: Number.parseInt(parts[1]),
|
|
2487
2586
|
program: Number.parseInt(parts[2]),
|
|
@@ -2493,12 +2592,14 @@ var MIDIPatchTools = class MIDIPatchTools {
|
|
|
2493
2592
|
* The format is:
|
|
2494
2593
|
* - `<MIDIPatch string> D <name>` for `isDrum` set to `true`.
|
|
2495
2594
|
* - `<MIDIPatch string> M <name>` for `isDrum` set to `false`.
|
|
2595
|
+
* @param patch The patch to convert.
|
|
2496
2596
|
*/
|
|
2497
2597
|
static toFullMIDIString(patch) {
|
|
2498
2598
|
return `${this.toMIDIString(patch)} ${patch.isDrum ? "D" : "M"} ${patch.name}`;
|
|
2499
2599
|
}
|
|
2500
2600
|
/**
|
|
2501
2601
|
* Gets {@link MIDIPatchFull} from a given string.
|
|
2602
|
+
* @param string The string in the {@link MIDIPatchTools.toFullMIDIString} format.
|
|
2502
2603
|
*/
|
|
2503
2604
|
static fromFullMIDIString(string) {
|
|
2504
2605
|
const firstSpace = string.indexOf(" ");
|
|
@@ -2515,14 +2616,18 @@ var MIDIPatchTools = class MIDIPatchTools {
|
|
|
2515
2616
|
}
|
|
2516
2617
|
/**
|
|
2517
2618
|
* Checks if two MIDI patches represent the same one.
|
|
2619
|
+
* @param patch1 The first patch.
|
|
2620
|
+
* @param patch2 The second patch.
|
|
2518
2621
|
*/
|
|
2519
2622
|
static matches(patch1, patch2) {
|
|
2520
|
-
if (patch1.isGMGSDrum || patch2.isGMGSDrum) return patch1.isGMGSDrum === patch2.isGMGSDrum && patch1.program === patch2.program;
|
|
2623
|
+
if (patch1.isGMGSDrum || patch2.isGMGSDrum) return patch1.isGMGSDrum === patch2.isGMGSDrum && patch1.program === patch2.program && patch1.bankMSB === patch2.bankMSB && patch1.bankLSB === patch2.bankLSB;
|
|
2521
2624
|
return patch1.program === patch2.program && patch1.bankLSB === patch2.bankLSB && patch1.bankMSB === patch2.bankMSB;
|
|
2522
2625
|
}
|
|
2523
2626
|
/**
|
|
2524
2627
|
* A comparison function for `.sort()` or `.toSorted()`,
|
|
2525
|
-
* ordering the patches in ascending order.
|
|
2628
|
+
* ordering the patches in ascending order: by program, then bank MSB, then bank LSB, with GM/GS drums last.
|
|
2629
|
+
* @param a The first patch.
|
|
2630
|
+
* @param b The second patch.
|
|
2526
2631
|
*/
|
|
2527
2632
|
static compare(a, b) {
|
|
2528
2633
|
if (a.isGMGSDrum && !b.isGMGSDrum) return 1;
|
|
@@ -2533,6 +2638,7 @@ var MIDIPatchTools = class MIDIPatchTools {
|
|
|
2533
2638
|
}
|
|
2534
2639
|
/**
|
|
2535
2640
|
* Checks if the given {@link MIDIPatchFull} is an XG/GM2 drum patch.
|
|
2641
|
+
* @param p The patch to check.
|
|
2536
2642
|
*/
|
|
2537
2643
|
static isXGDrum(p) {
|
|
2538
2644
|
return p.isDrum && !p.isGMGSDrum;
|
|
@@ -2544,7 +2650,7 @@ var MIDIPatchTools = class MIDIPatchTools {
|
|
|
2544
2650
|
* @param patch The {@link MIDIPatch} to select.
|
|
2545
2651
|
* @param system The MIDI system to select for.
|
|
2546
2652
|
* @returns The selected patch.
|
|
2547
|
-
* @throws Error An error if the array is empty
|
|
2653
|
+
* @throws Error An error if the array is empty.
|
|
2548
2654
|
*/
|
|
2549
2655
|
static selectPatch(patches, patch, system) {
|
|
2550
2656
|
if (patches.length === 0) throw new Error("No presets!");
|
|
@@ -2563,7 +2669,7 @@ var MIDIPatchTools = class MIDIPatchTools {
|
|
|
2563
2669
|
SpessaLog.info(`%cPreset %c${MIDIPatchTools.toMIDIString(patch)}%c not found. (${system}) Replaced with %c${this.toFullMIDIString(pres)}`, ConsoleColors.warn, ConsoleColors.unrecognized, ConsoleColors.warn, ConsoleColors.value);
|
|
2564
2670
|
};
|
|
2565
2671
|
if (isGMGSDrum) {
|
|
2566
|
-
let p = patches.find((p) => p.isGMGSDrum && p.program === program);
|
|
2672
|
+
let p = patches.find((p) => p.isGMGSDrum && p.program === program && p.bankLSB === bankLSB) ?? patches.find((p) => p.isGMGSDrum && p.program === program);
|
|
2567
2673
|
if (p) {
|
|
2568
2674
|
returnReplacement(p);
|
|
2569
2675
|
return p;
|
|
@@ -2675,7 +2781,8 @@ const GSInsertionAddressMap = {
|
|
|
2675
2781
|
controlSource1: 27,
|
|
2676
2782
|
controlDepth1: 28,
|
|
2677
2783
|
controlSource2: 29,
|
|
2678
|
-
controlDepth2: 30
|
|
2784
|
+
controlDepth2: 30,
|
|
2785
|
+
sendEQSwitch: 31
|
|
2679
2786
|
};
|
|
2680
2787
|
const GSChorusAddressMap = {
|
|
2681
2788
|
macro: 56,
|
|
@@ -2701,6 +2808,12 @@ const GSDelayAddressMap = {
|
|
|
2701
2808
|
feedback: 89,
|
|
2702
2809
|
sendLevelToReverb: 90
|
|
2703
2810
|
};
|
|
2811
|
+
const GSEqualizerAddressMap = {
|
|
2812
|
+
lowFrequency: 0,
|
|
2813
|
+
lowGain: 1,
|
|
2814
|
+
highFrequency: 2,
|
|
2815
|
+
highGain: 3
|
|
2816
|
+
};
|
|
2704
2817
|
/**
|
|
2705
2818
|
* The SysEx addresses of the channel MIDI parameters that GS and XG both set per part.
|
|
2706
2819
|
* `gs` is the third address byte of GS PATCH PART (`40 1x xx`) and `xg` is the third address byte of
|
|
@@ -2771,6 +2884,10 @@ const NRPNDrumParamMap = {
|
|
|
2771
2884
|
const NRPNDrumParamByMSB = new Map(Object.entries(NRPNDrumParamMap).map(([parameter, msb]) => [msb, parameter]));
|
|
2772
2885
|
// src/midi/midi_tools/midi_utils.ts
|
|
2773
2886
|
/**
|
|
2887
|
+
* The GS equalizer parameters by their address (`40 02 xx`).
|
|
2888
|
+
*/
|
|
2889
|
+
const GS_EQUALIZER_PARAMETER_BY_ADDRESS = new Map(Object.entries(GSEqualizerAddressMap).map(([name, address]) => [address, name]));
|
|
2890
|
+
/**
|
|
2774
2891
|
* The named GS insertion parameters by their address (`40 03 xx`).
|
|
2775
2892
|
*/
|
|
2776
2893
|
const GS_INSERTION_PARAMETER_BY_ADDRESS = new Map(Object.entries(GSInsertionAddressMap).map(([name, address]) => [address, name]));
|
|
@@ -2811,7 +2928,7 @@ var MIDIUtils = class MIDIUtils {
|
|
|
2811
2928
|
* @param channel The MIDI channel number.
|
|
2812
2929
|
* @param rpn The 14-bit RPN number.
|
|
2813
2930
|
* @param value The 14-bit value for that number.
|
|
2814
|
-
* @returns The analysis result
|
|
2931
|
+
* @returns The analysis result of the message.
|
|
2815
2932
|
*/
|
|
2816
2933
|
static analyzeRPN(channel, rpn, value) {
|
|
2817
2934
|
switch (rpn) {
|
|
@@ -2850,7 +2967,7 @@ var MIDIUtils = class MIDIUtils {
|
|
|
2850
2967
|
* @param value The 14-bit value for that number.
|
|
2851
2968
|
* @param drumPitchStep The drum pitch coarse resolution in semitones.
|
|
2852
2969
|
* 0.5 for GS (SC-88 and above), 1 for XG and the SC-55 drum map (bank LSB 1).
|
|
2853
|
-
* @returns The analysis result
|
|
2970
|
+
* @returns The analysis result of the message.
|
|
2854
2971
|
*/
|
|
2855
2972
|
static analyzeNRPN(channel, nrpn, value, drumPitchStep = .5) {
|
|
2856
2973
|
const msb = nrpn >> 7;
|
|
@@ -2913,20 +3030,33 @@ var MIDIUtils = class MIDIUtils {
|
|
|
2913
3030
|
return this.gsMessage(ticks, 64, 1, a3, [value]);
|
|
2914
3031
|
}
|
|
2915
3032
|
/**
|
|
3033
|
+
* Returns a MIDI event needed to set the given GS Equalizer Parameter.
|
|
3034
|
+
* @param ticks The MIDI tick time for the output event.
|
|
3035
|
+
* @param parameter The parameter to set.
|
|
3036
|
+
* @param value The value to set it to.
|
|
3037
|
+
* @returns The {@link MIDIMessage} needed to set this GS Equalizer Parameter.
|
|
3038
|
+
*/
|
|
3039
|
+
static setGSEqualizerParameter(ticks, parameter, value) {
|
|
3040
|
+
const a3 = GSEqualizerAddressMap[parameter];
|
|
3041
|
+
if (a3 === void 0) throw new Error(`Invalid equalizer parameter: ${parameter}`);
|
|
3042
|
+
return this.gsMessage(ticks, 64, 2, a3, [value]);
|
|
3043
|
+
}
|
|
3044
|
+
/**
|
|
2916
3045
|
* Returns a MIDI event needed to set the given GS Insertion Parameter.
|
|
2917
3046
|
* @param ticks The MIDI tick time for the output event.
|
|
2918
3047
|
* @param parameter The parameter to set: `"type"`, a send level or EFX control name (see {@link GSInsertionParameter}),
|
|
2919
3048
|
* or a 0-based effect-specific parameter number (0-19).
|
|
2920
|
-
* @param value The value to set it to.
|
|
3049
|
+
* @param value The value to set it to. A boolean for `"sendEQSwitch"`.
|
|
2921
3050
|
* @returns The {@link MIDIMessage} needed to set this GS Insertion Parameter.
|
|
2922
3051
|
*/
|
|
2923
3052
|
static setGSInsertionParameter(ticks, parameter, value) {
|
|
3053
|
+
const data = typeof value === "boolean" ? value ? 1 : 0 : value;
|
|
2924
3054
|
switch (parameter) {
|
|
2925
|
-
case "type": return this.gsMessage(ticks, 64, 3, 0, [
|
|
3055
|
+
case "type": return this.gsMessage(ticks, 64, 3, 0, [data >> 8 & 127, data & 127]);
|
|
2926
3056
|
default:
|
|
2927
|
-
if (typeof parameter === "string") return this.gsMessage(ticks, 64, 3, GSInsertionAddressMap[parameter], [
|
|
3057
|
+
if (typeof parameter === "string") return this.gsMessage(ticks, 64, 3, GSInsertionAddressMap[parameter], [data]);
|
|
2928
3058
|
if (!Number.isInteger(parameter) || parameter < 0 || parameter > 19) throw new Error(`Invalid insertion parameter: ${parameter}`);
|
|
2929
|
-
return this.gsMessage(ticks, 64, 3, parameter + 3, [
|
|
3059
|
+
return this.gsMessage(ticks, 64, 3, parameter + 3, [data]);
|
|
2930
3060
|
}
|
|
2931
3061
|
}
|
|
2932
3062
|
/**
|
|
@@ -3134,6 +3264,7 @@ var MIDIUtils = class MIDIUtils {
|
|
|
3134
3264
|
case "drumMap":
|
|
3135
3265
|
case "velocitySenseDepth":
|
|
3136
3266
|
case "velocitySenseOffset": return [MIDIUtils.partParamMessage(ticks, channel, system, parameter, value)];
|
|
3267
|
+
case "eqEnabled": return [MIDIUtils.gsMessage(ticks, 64, 64 | MIDIUtils.channelToGSPart(channel), 32, [value ? 1 : 0])];
|
|
3137
3268
|
case "efxAssign": return [MIDIUtils.gsMessage(ticks, 64, 64 | MIDIUtils.channelToGSPart(channel), 34, [value ? 1 : 0])];
|
|
3138
3269
|
case "dryLevel": return [MIDIUtils.xgMessage(ticks, 8, channel, 17, [value])];
|
|
3139
3270
|
}
|
|
@@ -4334,6 +4465,15 @@ var MIDIUtils = class MIDIUtils {
|
|
|
4334
4465
|
value
|
|
4335
4466
|
}];
|
|
4336
4467
|
}
|
|
4468
|
+
if (a2 === 2) {
|
|
4469
|
+
const parameter = GS_EQUALIZER_PARAMETER_BY_ADDRESS.get(a3);
|
|
4470
|
+
if (parameter === void 0) return [OTHER];
|
|
4471
|
+
return [{
|
|
4472
|
+
type: "GS Equalizer Param",
|
|
4473
|
+
parameter,
|
|
4474
|
+
value
|
|
4475
|
+
}];
|
|
4476
|
+
}
|
|
4337
4477
|
if (a2 === 3) {
|
|
4338
4478
|
switch (a3) {
|
|
4339
4479
|
case 0: return [{
|
|
@@ -4343,6 +4483,11 @@ var MIDIUtils = class MIDIUtils {
|
|
|
4343
4483
|
}];
|
|
4344
4484
|
}
|
|
4345
4485
|
const named = GS_INSERTION_PARAMETER_BY_ADDRESS.get(a3);
|
|
4486
|
+
if (named === "sendEQSwitch") return [{
|
|
4487
|
+
type: "GS Insertion Param",
|
|
4488
|
+
parameter: named,
|
|
4489
|
+
value: value !== 0
|
|
4490
|
+
}];
|
|
4346
4491
|
if (named !== void 0) return [{
|
|
4347
4492
|
type: "GS Insertion Param",
|
|
4348
4493
|
parameter: named,
|
|
@@ -4546,6 +4691,12 @@ var MIDIUtils = class MIDIUtils {
|
|
|
4546
4691
|
controller: MIDIControllers.bankSelectLSB,
|
|
4547
4692
|
value
|
|
4548
4693
|
}];
|
|
4694
|
+
case 32: return [{
|
|
4695
|
+
type: "Channel MIDI Param",
|
|
4696
|
+
channel,
|
|
4697
|
+
parameter: "eqEnabled",
|
|
4698
|
+
value: value === 1
|
|
4699
|
+
}];
|
|
4549
4700
|
case 34: return [{
|
|
4550
4701
|
type: "Channel MIDI Param",
|
|
4551
4702
|
channel,
|
|
@@ -4877,7 +5028,7 @@ var MIDIEditor = class {
|
|
|
4877
5028
|
* GM -> GS -> note data
|
|
4878
5029
|
*
|
|
4879
5030
|
* That GM reset is meaningless, so it can stay unmodified as the actual reset that matters is GS.
|
|
4880
|
-
* Meaningful ones are replaced with
|
|
5031
|
+
* Meaningful GM ones are replaced with the fallback reset in-place
|
|
4881
5032
|
* (unless the `system` parameter is explicitly set).
|
|
4882
5033
|
*/
|
|
4883
5034
|
meaningfulResets = new Array();
|
|
@@ -4919,6 +5070,7 @@ var MIDIEditor = class {
|
|
|
4919
5070
|
gsReverbParams;
|
|
4920
5071
|
gsChorusParams;
|
|
4921
5072
|
gsDelayParams;
|
|
5073
|
+
gsEqualizerParams;
|
|
4922
5074
|
gsInsertionParams;
|
|
4923
5075
|
xgReverbParams;
|
|
4924
5076
|
xgChorusParams;
|
|
@@ -4946,10 +5098,11 @@ var MIDIEditor = class {
|
|
|
4946
5098
|
constructor(midi, opts) {
|
|
4947
5099
|
this.midi = midi;
|
|
4948
5100
|
SpessaLog.groupCollapsed("%cApplying changes to the MIDI file...", ConsoleColors.info);
|
|
4949
|
-
const { channels, gsReverbParams, gsChorusParams, gsDelayParams, gsInsertionParams, xgReverbParams, xgChorusParams, xgVariationParams, xgInsertionParams, userDrumParams, midiParams } = opts;
|
|
5101
|
+
const { channels, gsReverbParams, gsChorusParams, gsDelayParams, gsEqualizerParams, gsInsertionParams, xgReverbParams, xgChorusParams, xgVariationParams, xgInsertionParams, userDrumParams, midiParams } = opts;
|
|
4950
5102
|
this.gsReverbParams = gsReverbParams;
|
|
4951
5103
|
this.gsChorusParams = gsChorusParams;
|
|
4952
5104
|
this.gsDelayParams = gsDelayParams;
|
|
5105
|
+
this.gsEqualizerParams = gsEqualizerParams;
|
|
4953
5106
|
this.gsInsertionParams = gsInsertionParams;
|
|
4954
5107
|
this.xgReverbParams = xgReverbParams;
|
|
4955
5108
|
this.xgChorusParams = xgChorusParams;
|
|
@@ -5022,12 +5175,12 @@ var MIDIEditor = class {
|
|
|
5022
5175
|
}
|
|
5023
5176
|
if (this.lastReset !== void 0 && replacedEvents.has(this.lastReset)) this.system = this.fallbackReset ?? "gs";
|
|
5024
5177
|
}
|
|
5025
|
-
if (!this.addedReset && this.midiParams?.system !== "clear" && (this.midiParams?.system !== void 0 || this.fallbackReset !== void 0 || this.gsReverbParams && this.gsReverbParams !== "clear" || this.gsChorusParams && this.gsChorusParams !== "clear" || this.gsDelayParams && this.gsDelayParams !== "clear" || this.gsInsertionParams && this.gsInsertionParams !== "clear" || this.xgReverbParams && this.xgReverbParams !== "clear" || this.xgChorusParams && this.xgChorusParams !== "clear" || this.xgVariationParams && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear") || this.userDrumSetParams?.size || [...this.channelChanges.values()].some((c) => c.patch && c.patch !== "clear"))) {
|
|
5178
|
+
if (!this.addedReset && this.midiParams?.system !== "clear" && (this.midiParams?.system !== void 0 || this.fallbackReset !== void 0 || this.gsReverbParams && this.gsReverbParams !== "clear" || this.gsChorusParams && this.gsChorusParams !== "clear" || this.gsDelayParams && this.gsDelayParams !== "clear" || this.gsEqualizerParams && this.gsEqualizerParams !== "clear" || this.gsInsertionParams && this.gsInsertionParams !== "clear" || this.xgReverbParams && this.xgReverbParams !== "clear" || this.xgChorusParams && this.xgChorusParams !== "clear" || this.xgVariationParams && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear") || this.userDrumSetParams?.size || [...this.channelChanges.values()].some((c) => c.patch && c.patch !== "clear"))) {
|
|
5026
5179
|
let index = 0;
|
|
5027
5180
|
const firstTrack = this.midi.tracks[0];
|
|
5028
5181
|
while (index < firstTrack.events.length && firstTrack.events[index].statusByte < MIDIMessageTypes.noteOff) index++;
|
|
5029
5182
|
const hasXG = this.xgReverbParams !== void 0 && this.xgReverbParams !== "clear" || this.xgChorusParams !== void 0 && this.xgChorusParams !== "clear" || this.xgVariationParams !== void 0 && this.xgVariationParams !== "clear" || [...this.xgInsertionParams?.values() ?? []].some((p) => p !== "clear");
|
|
5030
|
-
const hasGS = this.gsReverbParams !== void 0 && this.gsReverbParams !== "clear" || this.gsChorusParams !== void 0 && this.gsChorusParams !== "clear" || this.gsDelayParams !== void 0 && this.gsDelayParams !== "clear" || this.gsInsertionParams !== void 0 && this.gsInsertionParams !== "clear";
|
|
5183
|
+
const hasGS = this.gsReverbParams !== void 0 && this.gsReverbParams !== "clear" || this.gsChorusParams !== void 0 && this.gsChorusParams !== "clear" || this.gsDelayParams !== void 0 && this.gsDelayParams !== "clear" || this.gsEqualizerParams !== void 0 && this.gsEqualizerParams !== "clear" || this.gsInsertionParams !== void 0 && this.gsInsertionParams !== "clear";
|
|
5031
5184
|
const targetSystem = this.midiParams?.system ?? this.fallbackReset ?? (hasXG && !hasGS ? "xg" : "gs");
|
|
5032
5185
|
const event = MIDIUtils.reset(0, targetSystem);
|
|
5033
5186
|
firstTrack.addEvents(index, event);
|
|
@@ -5267,6 +5420,12 @@ var MIDIEditor = class {
|
|
|
5267
5420
|
return;
|
|
5268
5421
|
}
|
|
5269
5422
|
break;
|
|
5423
|
+
case "GS Equalizer Param":
|
|
5424
|
+
if (this.gsEqualizerParams) {
|
|
5425
|
+
this.deleteCurrentEvent();
|
|
5426
|
+
return;
|
|
5427
|
+
}
|
|
5428
|
+
break;
|
|
5270
5429
|
case "GS Insertion Param":
|
|
5271
5430
|
if (this.gsInsertionParams) {
|
|
5272
5431
|
this.deleteCurrentEvent();
|
|
@@ -5681,6 +5840,10 @@ var MIDIEditor = class {
|
|
|
5681
5840
|
const p = this.gsDelayParams;
|
|
5682
5841
|
output.push(MIDIUtils.setGSDelayParameter(targetTicks, "level", p.level), MIDIUtils.setGSDelayParameter(targetTicks, "preLowpass", p.preLowpass), MIDIUtils.setGSDelayParameter(targetTicks, "timeCenter", p.timeCenter), MIDIUtils.setGSDelayParameter(targetTicks, "timeRatioLeft", p.timeRatioLeft), MIDIUtils.setGSDelayParameter(targetTicks, "timeRatioRight", p.timeRatioRight), MIDIUtils.setGSDelayParameter(targetTicks, "levelCenter", p.levelCenter), MIDIUtils.setGSDelayParameter(targetTicks, "levelLeft", p.levelLeft), MIDIUtils.setGSDelayParameter(targetTicks, "levelRight", p.levelRight), MIDIUtils.setGSDelayParameter(targetTicks, "feedback", p.feedback), MIDIUtils.setGSDelayParameter(targetTicks, "sendLevelToReverb", p.sendLevelToReverb));
|
|
5683
5842
|
}
|
|
5843
|
+
if (this.gsEqualizerParams && this.gsEqualizerParams !== "clear") {
|
|
5844
|
+
const p = this.gsEqualizerParams;
|
|
5845
|
+
for (const name of Object.keys(GSEqualizerAddressMap)) output.push(MIDIUtils.setGSEqualizerParameter(targetTicks, name, p[name]));
|
|
5846
|
+
}
|
|
5684
5847
|
if (this.gsInsertionParams && this.gsInsertionParams !== "clear") {
|
|
5685
5848
|
const p = this.gsInsertionParams;
|
|
5686
5849
|
output.push(MIDIUtils.setGSInsertionParameter(targetTicks, "type", p.type));
|
|
@@ -5946,7 +6109,7 @@ var MIDITrack = class MIDITrack {
|
|
|
5946
6109
|
*/
|
|
5947
6110
|
port = 0;
|
|
5948
6111
|
/**
|
|
5949
|
-
* A set that contains the MIDI channel numbers
|
|
6112
|
+
* A set that contains the MIDI channel numbers (0-15) that have note on events in this track.
|
|
5950
6113
|
*/
|
|
5951
6114
|
channels = /* @__PURE__ */ new Set();
|
|
5952
6115
|
/**
|
|
@@ -5975,7 +6138,7 @@ var MIDITrack = class MIDITrack {
|
|
|
5975
6138
|
}
|
|
5976
6139
|
/**
|
|
5977
6140
|
* Adds events to the track.
|
|
5978
|
-
* @param index The index at which to add these
|
|
6141
|
+
* @param index The index at which to add these events.
|
|
5979
6142
|
* @param events The events to add.
|
|
5980
6143
|
*/
|
|
5981
6144
|
addEvents(index, ...events) {
|
|
@@ -7043,7 +7206,7 @@ var BasicSample = class {
|
|
|
7043
7206
|
*
|
|
7044
7207
|
* > **Warning**
|
|
7045
7208
|
* >
|
|
7046
|
-
* > Do not change this value directly.
|
|
7209
|
+
* > Do not change this value directly. Use {@link BasicSample.setSampleType},
|
|
7047
7210
|
* > {@link BasicSample.setLinkedSample} or {@link BasicSample.unlinkSample} instead.
|
|
7048
7211
|
*/
|
|
7049
7212
|
sampleType;
|
|
@@ -7105,7 +7268,7 @@ var BasicSample = class {
|
|
|
7105
7268
|
* @param sampleRate The sample's rate in Hz.
|
|
7106
7269
|
* @param originalKey The sample's pitch as a MIDI note number.
|
|
7107
7270
|
* @param pitchCorrection The sample's pitch correction in cents.
|
|
7108
|
-
* @param sampleType The sample's type,
|
|
7271
|
+
* @param sampleType The sample's type, one of the {@link SampleTypes}.
|
|
7109
7272
|
* @param loopStart The sample's loop start relative to the sample start in sample points.
|
|
7110
7273
|
* @param loopEnd The sample's loop end relative to the sample start in sample points. Exclusive.
|
|
7111
7274
|
*/
|
|
@@ -7705,7 +7868,7 @@ var ModulatorSource = class ModulatorSource {
|
|
|
7705
7868
|
/**
|
|
7706
7869
|
* If this field is set true, the direction of the controller should be from the maximum value to the minimum value. So, for
|
|
7707
7870
|
* example, if the controller source is Key Number, then a Key Number value of 0 corresponds to the maximum possible
|
|
7708
|
-
* controller output, and the Key Number value of 127 corresponds to the minimum possible controller
|
|
7871
|
+
* controller output, and the Key Number value of 127 corresponds to the minimum possible controller output.
|
|
7709
7872
|
*/
|
|
7710
7873
|
isNegative;
|
|
7711
7874
|
/**
|
|
@@ -8519,7 +8682,7 @@ var BasicPreset = class BasicPreset {
|
|
|
8519
8682
|
* >
|
|
8520
8683
|
* > Consider reading about [the DLS conversion problem](../../../docs/extra/dls-conversion-problem.md).
|
|
8521
8684
|
*
|
|
8522
|
-
* @returns The instrument containing the flattened zones. If flattening was successful, it should exactly the same as this preset.
|
|
8685
|
+
* @returns The instrument containing the flattened zones. If flattening was successful, it should sound exactly the same as this preset.
|
|
8523
8686
|
*/
|
|
8524
8687
|
toFlattenedInstrument() {
|
|
8525
8688
|
const addUnique = (main, adder) => {
|
|
@@ -8710,7 +8873,7 @@ var BasicInstrument = class {
|
|
|
8710
8873
|
return zone;
|
|
8711
8874
|
}
|
|
8712
8875
|
/**
|
|
8713
|
-
* Links the instrument
|
|
8876
|
+
* Links the instrument to a given preset.
|
|
8714
8877
|
* @param preset The preset to link to.
|
|
8715
8878
|
* @internal
|
|
8716
8879
|
*/
|
|
@@ -10170,8 +10333,8 @@ var DownloadableSoundsArticulation = class extends DLSVerifier {
|
|
|
10170
10333
|
for (const modulator of zone.modulators) ConnectionBlock.fromSFModulator(modulator, this);
|
|
10171
10334
|
}
|
|
10172
10335
|
/**
|
|
10173
|
-
*
|
|
10174
|
-
* @param chunks
|
|
10336
|
+
* Reads the articulation from a chunk list.
|
|
10337
|
+
* @param chunks Chunk list for the region/instrument (containing lar2 or lart)
|
|
10175
10338
|
*/
|
|
10176
10339
|
read(chunks) {
|
|
10177
10340
|
const lart = RIFFChunk.findListType(chunks, "lart");
|
|
@@ -10493,7 +10656,6 @@ var DownloadableSoundsRegion = class DownloadableSoundsRegion extends DLSVerifie
|
|
|
10493
10656
|
// src/soundbank/downloadable_sounds/instrument.ts
|
|
10494
10657
|
/**
|
|
10495
10658
|
* Represents a proper DLS instrument, with regions and articulation.
|
|
10496
|
-
* DLS
|
|
10497
10659
|
*/
|
|
10498
10660
|
var DownloadableSoundsInstrument = class DownloadableSoundsInstrument extends DLSVerifier {
|
|
10499
10661
|
articulation = new DownloadableSoundsArticulation();
|
|
@@ -10765,7 +10927,7 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
10765
10927
|
for (let i = 0; i < bank.samples.length; i++) {
|
|
10766
10928
|
const s = bank.samples[i];
|
|
10767
10929
|
dls.samples.push(DownloadableSoundsSample.fromSFSample(s));
|
|
10768
|
-
progressFunc?.(i / bank.samples.length);
|
|
10930
|
+
progressFunc?.((i + 1) / bank.samples.length);
|
|
10769
10931
|
}
|
|
10770
10932
|
for (const p of bank.presets) dls.instruments.push(DownloadableSoundsInstrument.fromSFPreset(p, bank.samples));
|
|
10771
10933
|
SpessaLog.info("%cConversion complete!", ConsoleColors.recognized);
|
|
@@ -10799,12 +10961,12 @@ var DownloadableSounds = class DownloadableSounds extends DLSVerifier {
|
|
|
10799
10961
|
let written = 0;
|
|
10800
10962
|
for (const s of this.samples) {
|
|
10801
10963
|
const out = s.write();
|
|
10964
|
+
written++;
|
|
10802
10965
|
options.progressFunction?.(written / this.samples.length);
|
|
10803
10966
|
SpessaLog.info(`%cWrote sample %c${written}. ${s.name}%c of %c${this.samples.length}.`, ConsoleColors.info, ConsoleColors.recognized, ConsoleColors.info, ConsoleColors.recognized);
|
|
10804
10967
|
ptblOffsets.push(currentIndex);
|
|
10805
10968
|
currentIndex += out.reduce((sum, cur) => sum + cur.length, 0);
|
|
10806
10969
|
samples.push(...out);
|
|
10807
|
-
written++;
|
|
10808
10970
|
}
|
|
10809
10971
|
const wvpl = RIFFChunk.getParts("wvpl", samples, false, true);
|
|
10810
10972
|
SpessaLog.info("%cSucceeded!", ConsoleColors.recognized);
|
|
@@ -11016,9 +11178,8 @@ var SoundBankCloner = class {
|
|
|
11016
11178
|
* This module handles parsing and writing SoundFont2 (`.sf2`, `.sf3` and `.sfogg`) files.
|
|
11017
11179
|
* It represents a single sound bank.
|
|
11018
11180
|
*
|
|
11019
|
-
* It also contains support for `.dls` files and experimental read and write support for [64-bit SFE](https://
|
|
11181
|
+
* It also contains support for `.dls` files and experimental read and write support for [64-bit SFE](https://sfe-team-was-taken.github.io/SFE/).
|
|
11020
11182
|
*
|
|
11021
|
-
|
|
11022
11183
|
* **Specifications:**
|
|
11023
11184
|
*
|
|
11024
11185
|
* - [SoundFont2 Specification](http://www.synthfont.com/sfspec24.pdf)
|
|
@@ -11062,7 +11223,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11062
11223
|
}
|
|
11063
11224
|
};
|
|
11064
11225
|
/**
|
|
11065
|
-
* An array of all presets in the bank, ordered by
|
|
11226
|
+
* An array of all presets in the bank, ordered by program and bank number, with GM/GS drum presets last.
|
|
11066
11227
|
*/
|
|
11067
11228
|
presets = [];
|
|
11068
11229
|
/**
|
|
@@ -11129,8 +11290,9 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11129
11290
|
return b;
|
|
11130
11291
|
}
|
|
11131
11292
|
/**
|
|
11132
|
-
* Creates a simple
|
|
11293
|
+
* Creates a simple SF2 file with a single saw wave preset.
|
|
11133
11294
|
* Useful for testing synthesizer's functionality without providing a file.
|
|
11295
|
+
* @returns The binary representation of the SF2 file.
|
|
11134
11296
|
*/
|
|
11135
11297
|
static getSampleSoundBankFile() {
|
|
11136
11298
|
const font = new BasicSoundBank();
|
|
@@ -11158,12 +11320,17 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11158
11320
|
return font.writeSF2();
|
|
11159
11321
|
}
|
|
11160
11322
|
/**
|
|
11161
|
-
* Copies a given sound bank,
|
|
11323
|
+
* Copies a given sound bank, cloning all presets, instruments and samples.
|
|
11324
|
+
* The sample data is shared with the original, not copied.
|
|
11325
|
+
* Elements sharing a name are all copied, the repeats get a free name like `Piano-1`.
|
|
11162
11326
|
* @param bank The sound bank to copy.
|
|
11163
11327
|
*/
|
|
11164
11328
|
static copyFrom(bank) {
|
|
11165
11329
|
const copied = new BasicSoundBank(bank.type);
|
|
11166
|
-
const cloner = new SoundBankCloner(copied
|
|
11330
|
+
const cloner = new SoundBankCloner(copied, {
|
|
11331
|
+
reuseByName: false,
|
|
11332
|
+
reuseChildrenByName: false
|
|
11333
|
+
});
|
|
11167
11334
|
for (const p of bank.presets) cloner.clonePreset(p);
|
|
11168
11335
|
copied.soundBankInfo = { ...bank.soundBankInfo };
|
|
11169
11336
|
copied.defaultModulators = bank.defaultModulators.map((m) => Modulator.copyFrom(m));
|
|
@@ -11189,8 +11356,8 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11189
11356
|
*
|
|
11190
11357
|
* > **Warning**
|
|
11191
11358
|
* >
|
|
11192
|
-
* > Note that
|
|
11193
|
-
* > in permanent sample quality loss!
|
|
11359
|
+
* > Note that compressing (sample format `compressed`) usually results
|
|
11360
|
+
* > in permanent sample quality loss! Decompressing does not restore it.
|
|
11194
11361
|
* > This method is memory and CPU intensive with large sound banks.
|
|
11195
11362
|
*
|
|
11196
11363
|
* @param options Options associated with setting the sample format.
|
|
@@ -11234,7 +11401,7 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11234
11401
|
}
|
|
11235
11402
|
/**
|
|
11236
11403
|
* Writes out a DLS Level 2 sound bank. This may not be 100% accurate.
|
|
11237
|
-
*
|
|
11404
|
+
* Sample data is always written in the S16LE PCM encoding.
|
|
11238
11405
|
*
|
|
11239
11406
|
* > **Note**
|
|
11240
11407
|
* >
|
|
@@ -11357,16 +11524,15 @@ var BasicSoundBank = class BasicSoundBank {
|
|
|
11357
11524
|
/**
|
|
11358
11525
|
* Trims the sound bank _in-place_ to only contain samples in a given MIDI file.
|
|
11359
11526
|
*
|
|
11360
|
-
|
|
11361
11527
|
* Absent presets will be removed from the sound bank,
|
|
11362
11528
|
* and samples that don't get activated in the remaining presets will be removed as well.
|
|
11363
11529
|
*
|
|
11364
11530
|
* > **Note**
|
|
11365
11531
|
* >
|
|
11366
|
-
* > This exact type is returned from {@link BasicMIDI.getUsedProgramsAndKeys}
|
|
11532
|
+
* > This exact type is returned from {@link BasicMIDI.getUsedProgramsAndKeys}.
|
|
11367
11533
|
* > Consider reading the page for more explanation about the parameter.
|
|
11368
11534
|
*
|
|
11369
|
-
* @param presetData
|
|
11535
|
+
* @param presetData A `Map`: `BasicPreset` -> `Map<MIDI note, Set<velocity>>`.
|
|
11370
11536
|
* Absent presets will be removed from the sound bank,
|
|
11371
11537
|
* and samples that don't get activated in the remaining presets will be removed as well.
|
|
11372
11538
|
*/
|
|
@@ -13766,11 +13932,13 @@ const DEFAULT_RMIDI_WRITE_OPTIONS = {
|
|
|
13766
13932
|
*/
|
|
13767
13933
|
function writeRMIDIInternal(mid, soundBankBinary, options) {
|
|
13768
13934
|
const metadata = { ...options.metadata };
|
|
13769
|
-
if (options.correctBankOffset && !options.soundBank) throw new Error("Sound bank must be provided if correcting bank offset.");
|
|
13770
13935
|
SpessaLog.group("%cWriting the RMIDI File...", ConsoleColors.info);
|
|
13771
13936
|
SpessaLog.info("metadata", metadata);
|
|
13772
13937
|
SpessaLog.info("Initial bank offset", mid.bankOffset);
|
|
13773
|
-
if (options.correctBankOffset
|
|
13938
|
+
if (options.correctBankOffset) {
|
|
13939
|
+
if (options.soundBank) correctBankOffsetInternal(mid, options.bankOffset, options.soundBank);
|
|
13940
|
+
else SpessaLog.warn("No sound bank provided, skipping the bank offset correction.");
|
|
13941
|
+
}
|
|
13774
13942
|
const newMid = new IndexedByteArray(mid.writeMIDI());
|
|
13775
13943
|
metadata.name ??= mid.getName();
|
|
13776
13944
|
metadata.creationDate ??= /* @__PURE__ */ new Date();
|
|
@@ -13958,7 +14126,8 @@ var BasicMIDI = class BasicMIDI {
|
|
|
13958
14126
|
* The loop points (in ticks) of the sequence, including both start and end points.
|
|
13959
14127
|
*
|
|
13960
14128
|
* If there's nothing detected, the loop will start from the first note on event and end will be the last voice message.
|
|
13961
|
-
* Current looping detection is: CC 2/4,
|
|
14129
|
+
* Current looping detection is: CC 2/4 (Touhou), CC 111 (RPG Maker), CC 116/117 and 118/119 (EMIDI/XMI)
|
|
14130
|
+
* and the "start", "loopStart" and "loopEnd" markers.
|
|
13962
14131
|
*/
|
|
13963
14132
|
loop = {
|
|
13964
14133
|
start: 0,
|
|
@@ -13976,6 +14145,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
13976
14145
|
format = 0;
|
|
13977
14146
|
/**
|
|
13978
14147
|
* The RMID (Resource-Interchangeable MIDI) info data, if the file is RMID formatted.
|
|
14148
|
+
* XMF metadata and converted tracker modules are stored here as well.
|
|
13979
14149
|
* Otherwise, this object is empty.
|
|
13980
14150
|
* Info type: Chunk data as a binary array.
|
|
13981
14151
|
*
|
|
@@ -14047,7 +14217,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14047
14217
|
* This function reads the MIDI file format, extracts the header and track chunks,
|
|
14048
14218
|
* and populates the BasicMIDI instance with the parsed data.
|
|
14049
14219
|
* It supports Standard MIDI Files (SMF), RIFF MIDI (RMIDI), and Extensible Music Format (XMF).
|
|
14050
|
-
* It also handles embedded soundbanks in RMIDI files.
|
|
14220
|
+
* It also handles embedded soundbanks in RMIDI and XMF files.
|
|
14051
14221
|
* Tracker modules (ProTracker MOD, Fast Tracker 2 XM, Scream Tracker 3 S3M) are converted
|
|
14052
14222
|
* into MIDI with an embedded sound bank.
|
|
14053
14223
|
*/
|
|
@@ -14086,7 +14256,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14086
14256
|
return m;
|
|
14087
14257
|
}
|
|
14088
14258
|
/**
|
|
14089
|
-
* Copies another instance this `BasicMIDI` instance, including track data.
|
|
14259
|
+
* Copies another instance into this `BasicMIDI` instance, including track data.
|
|
14090
14260
|
* @param mid The MIDI to copy.
|
|
14091
14261
|
*/
|
|
14092
14262
|
copyFrom(mid) {
|
|
@@ -14159,7 +14329,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14159
14329
|
}
|
|
14160
14330
|
/**
|
|
14161
14331
|
* Preloads all voices for this sequence in a given {@link SpessaSynthProcessor}.
|
|
14162
|
-
* This caches all the needed voices for playing back this
|
|
14332
|
+
* This caches all the needed voices for playing back this sequence, resulting in a smooth playback.
|
|
14163
14333
|
* The sequencer calls this function by default when loading the songs. (it can be disabled: {@link SpessaSynthSequencer.preload}).
|
|
14164
14334
|
* @param synth The synthesizer to preload.
|
|
14165
14335
|
*/
|
|
@@ -14189,6 +14359,12 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14189
14359
|
}
|
|
14190
14360
|
/**
|
|
14191
14361
|
* Calculates all note times in seconds.
|
|
14362
|
+
*
|
|
14363
|
+
* > **Note**
|
|
14364
|
+
* >
|
|
14365
|
+
* > MIDI ports are ignored, so the channels of every port are merged into the 16 channels.
|
|
14366
|
+
* > For example, channel 1 of port 2 is returned as channel 1.
|
|
14367
|
+
*
|
|
14192
14368
|
* @param minDrumLength In seconds, represents the minimum allowed time for a drum note,
|
|
14193
14369
|
* since they sometimes have a length of 0.
|
|
14194
14370
|
* @returns An array of 16 channels, each channel containing its notes,
|
|
@@ -14241,7 +14417,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14241
14417
|
return writeMIDIInternal(this);
|
|
14242
14418
|
}
|
|
14243
14419
|
/**
|
|
14244
|
-
* Writes out an RMIDI file (MIDI + SF2).
|
|
14420
|
+
* Writes out an RMIDI file (MIDI + SF2 or DLS).
|
|
14245
14421
|
* [See more info about this format](https://github.com/spessasus/sf2-rmidi-specification#readme).
|
|
14246
14422
|
*
|
|
14247
14423
|
* Note that this method modifies the MIDI file in-place.
|
|
@@ -14250,11 +14426,16 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14250
14426
|
* The method is called on a `BasicMIDI` instance;
|
|
14251
14427
|
* that instance is the MIDI file to embed.
|
|
14252
14428
|
*
|
|
14429
|
+
* > **Warning**
|
|
14430
|
+
* >
|
|
14431
|
+
* > Do not call in audioWorkletGlobalScope as it uses TextDecoder and TextEncoder.
|
|
14432
|
+
* > Bank offset correction (on by default) needs `soundBank`. Without it, the correction is skipped with a warning.
|
|
14433
|
+
*
|
|
14253
14434
|
* > **Tip**
|
|
14254
14435
|
* >
|
|
14255
14436
|
* > Use {@link BasicSoundBank.trim} to drastically reduce the file size.
|
|
14256
|
-
* >
|
|
14257
|
-
* > (using these
|
|
14437
|
+
* > Consider also using compression (like shown in example) to save even more space.
|
|
14438
|
+
* > (using both of these methods, I managed to shrink a 1GB sound bank into a 5MB RMIDI!)
|
|
14258
14439
|
*
|
|
14259
14440
|
* @param soundBankBinary The binary sound bank (SF2 or DLS) to embed into the file.
|
|
14260
14441
|
* @param configuration Extra options for writing the file.
|
|
@@ -14292,7 +14473,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14292
14473
|
*
|
|
14293
14474
|
* // trim the soundfont
|
|
14294
14475
|
* soundBank.trim(midi.getUsedProgramsAndKeys(soundBank));
|
|
14295
|
-
* // write out with compression to save space
|
|
14476
|
+
* // write out with compression to save space
|
|
14296
14477
|
* await soundBank.setSampleFormat({
|
|
14297
14478
|
* format: "compressed",
|
|
14298
14479
|
* compressionFunction: SampleEncodingFunction // Remember to get your compression function
|
|
@@ -14330,6 +14511,8 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14330
14511
|
* and inserts/deletes messages appropriately.
|
|
14331
14512
|
*
|
|
14332
14513
|
* This modifies the MIDI sequence _in-place_.
|
|
14514
|
+
*
|
|
14515
|
+
* @param opts The modifications to apply.
|
|
14333
14516
|
*/
|
|
14334
14517
|
modify(opts) {
|
|
14335
14518
|
new MIDIEditor(this, opts).apply();
|
|
@@ -14339,8 +14522,10 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14339
14522
|
* This means changing the programs and controllers if they are locked.
|
|
14340
14523
|
*
|
|
14341
14524
|
* For example, if channel 1 has locked preset on `Drawbar Organ`,
|
|
14342
|
-
* this will remove all program changes for channel 1 and add one
|
|
14525
|
+
* this will remove all program changes for channel 1 and add one before the channel's first note on
|
|
14526
|
+
* (and again after every system reset) to change the program to
|
|
14343
14527
|
* `Drawbar organ` (using bank MSB/LSB and program change).
|
|
14528
|
+
* Muted channels are removed from the sequence.
|
|
14344
14529
|
*
|
|
14345
14530
|
* > **Warning**
|
|
14346
14531
|
* >
|
|
@@ -14413,11 +14598,31 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14413
14598
|
* > **Note**
|
|
14414
14599
|
* >
|
|
14415
14600
|
* > This sets the Info encoding to `utf-8`.
|
|
14601
|
+
* > Text chunks that are already present get re-encoded from their old encoding (IENC) to UTF-8,
|
|
14602
|
+
* > so they stay readable. Without IENC, they are assumed to be UTF-8 already, like {@link BasicMIDI.getRMIDInfo} does.
|
|
14603
|
+
*
|
|
14604
|
+
* > **Warning**
|
|
14605
|
+
* >
|
|
14606
|
+
* > Do not call in audioWorkletGlobalScope as it uses TextDecoder and TextEncoder.
|
|
14416
14607
|
*
|
|
14417
14608
|
* @param infoType The type to set.
|
|
14418
14609
|
* @param infoData The value to set it to.
|
|
14419
14610
|
*/
|
|
14420
14611
|
setRMIDInfo(infoType, infoData) {
|
|
14612
|
+
const oldEncoding = this.infoEncoding;
|
|
14613
|
+
if (oldEncoding !== void 0 && !/^utf-?8$/i.test(oldEncoding.trim())) try {
|
|
14614
|
+
const decoder = new TextDecoder(oldEncoding.trim());
|
|
14615
|
+
const encoder = new TextEncoder();
|
|
14616
|
+
for (const key of Object.keys(this.rmidiInfo)) {
|
|
14617
|
+
if (key === "picture" || key === "creationDate" || key === "infoEncoding") continue;
|
|
14618
|
+
const data = this.rmidiInfo[key];
|
|
14619
|
+
if (!data) continue;
|
|
14620
|
+
const text = decoder.decode(data[data.length - 1] === 0 ? data.subarray(0, -1) : data);
|
|
14621
|
+
this.rmidiInfo[key] = new Uint8Array([...encoder.encode(text), 0]);
|
|
14622
|
+
}
|
|
14623
|
+
} catch (error) {
|
|
14624
|
+
SpessaLog.warn(`Failed to re-encode the RMIDI info from ${oldEncoding}: ${error}`);
|
|
14625
|
+
}
|
|
14421
14626
|
this.rmidiInfo.infoEncoding = getStringBytes("utf-8", true);
|
|
14422
14627
|
if (infoType === "picture") this.rmidiInfo.picture = new Uint8Array(infoData);
|
|
14423
14628
|
else if (infoType === "creationDate") this.rmidiInfo.creationDate = getStringBytes(toISODateString(infoData), true);
|
|
@@ -14428,6 +14633,11 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14428
14633
|
}
|
|
14429
14634
|
/**
|
|
14430
14635
|
* Gets a given chunk from the RMIDI information.
|
|
14636
|
+
*
|
|
14637
|
+
* > **Warning**
|
|
14638
|
+
* >
|
|
14639
|
+
* > Do not call in audioWorkletGlobalScope as it uses TextDecoder.
|
|
14640
|
+
*
|
|
14431
14641
|
* @param infoType The metadata type.
|
|
14432
14642
|
* @returns `string`, `Date`, `ArrayBuffer` or undefined if the info is not set.
|
|
14433
14643
|
*/
|
|
@@ -14455,7 +14665,7 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14455
14665
|
* > if you are not editing the MIDI file in your loop.
|
|
14456
14666
|
* > It is usually a faster solution and allows custom loops.
|
|
14457
14667
|
*
|
|
14458
|
-
*
|
|
14668
|
+
* If the track data is being edited, remember to call {@link BasicMIDI.flush} after editing!
|
|
14459
14669
|
*
|
|
14460
14670
|
* @param callback The callback function to process each event.
|
|
14461
14671
|
*/
|
|
@@ -14562,11 +14772,15 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14562
14772
|
case MIDIMessageTypes.controllerChange:
|
|
14563
14773
|
switch (e.data[0]) {
|
|
14564
14774
|
case MIDIControllers.breathController:
|
|
14565
|
-
case MIDIControllers.undefinedCC111LSB:
|
|
14566
14775
|
if (e.data[1] === 0) loopStart = e.ticks;
|
|
14567
14776
|
break;
|
|
14568
|
-
case MIDIControllers.
|
|
14569
|
-
|
|
14777
|
+
case MIDIControllers.undefinedCC111:
|
|
14778
|
+
if (e.data[1] !== 0) break;
|
|
14779
|
+
loopStart = e.ticks;
|
|
14780
|
+
loopType = "soft";
|
|
14781
|
+
break;
|
|
14782
|
+
case MIDIControllers.undefinedCC116:
|
|
14783
|
+
case MIDIControllers.undefinedCC118:
|
|
14570
14784
|
loopStart = e.ticks;
|
|
14571
14785
|
break;
|
|
14572
14786
|
case MIDIControllers.footController:
|
|
@@ -14579,8 +14793,8 @@ var BasicMIDI = class BasicMIDI {
|
|
|
14579
14793
|
loopType = "hard";
|
|
14580
14794
|
}
|
|
14581
14795
|
break;
|
|
14582
|
-
case MIDIControllers.
|
|
14583
|
-
case MIDIControllers.
|
|
14796
|
+
case MIDIControllers.undefinedCC117:
|
|
14797
|
+
case MIDIControllers.undefinedCC119:
|
|
14584
14798
|
loopEnd = loopEnd === null ? e.ticks : 0;
|
|
14585
14799
|
loopType = "hard";
|
|
14586
14800
|
break;
|
|
@@ -14776,6 +14990,7 @@ const DEFAULT_MIDI_BUILDER_OPTIONS = {
|
|
|
14776
14990
|
* > **Warning**
|
|
14777
14991
|
* >
|
|
14778
14992
|
* > Remember to call {@link BasicMIDI.flush} after you're done building the file!
|
|
14993
|
+
* > Do not use in audioWorkletGlobalScope as it uses TextEncoder.
|
|
14779
14994
|
*
|
|
14780
14995
|
* @group MIDI.Files
|
|
14781
14996
|
*/
|
|
@@ -15071,7 +15286,7 @@ function processEventInternal(event, trackIndex) {
|
|
|
15071
15286
|
}
|
|
15072
15287
|
// src/sequencer/process_tick.ts
|
|
15073
15288
|
/**
|
|
15074
|
-
* Processes a single MIDI
|
|
15289
|
+
* Processes a single sequencer tick, playing all MIDI events up to the current time.
|
|
15075
15290
|
* Call this every rendering quantum to process the sequencer events in real-time.
|
|
15076
15291
|
*/
|
|
15077
15292
|
function processTick() {
|
|
@@ -17958,7 +18173,8 @@ function wordToFloat(word, mode) {
|
|
|
17958
18173
|
return mode === 1 ? signed / 128 : signed / 512;
|
|
17959
18174
|
}
|
|
17960
18175
|
/**
|
|
17961
|
-
*
|
|
18176
|
+
* Adds the DLL's half step bias (away from zero) to a scaled coefficient.
|
|
18177
|
+
* Truncating the result rounds half away from zero, like the DLL's biased truncation.
|
|
17962
18178
|
* @param value The exact scaled value.
|
|
17963
18179
|
*/
|
|
17964
18180
|
function biasedRound(value) {
|
|
@@ -18487,6 +18703,8 @@ var XGDSPProgram = class {
|
|
|
18487
18703
|
var DefaultXGEffect = class {
|
|
18488
18704
|
/**
|
|
18489
18705
|
* The currently used processor.
|
|
18706
|
+
* Hidden from the docs as the processor types are internal.
|
|
18707
|
+
* @hidden
|
|
18490
18708
|
*/
|
|
18491
18709
|
processor;
|
|
18492
18710
|
outputLeft;
|
|
@@ -18506,8 +18724,9 @@ var DefaultXGEffect = class {
|
|
|
18506
18724
|
* @param defaultType The default effect type.
|
|
18507
18725
|
* @param sampleRate The sample rate, in Hertz.
|
|
18508
18726
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
18509
|
-
*
|
|
18727
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
18510
18728
|
* @param variation True for the variation and insertion blocks.
|
|
18729
|
+
* @hidden
|
|
18511
18730
|
*/
|
|
18512
18731
|
constructor(effectMap, fallbackProcessor, defaultType, sampleRate, maxBufferSize, variation) {
|
|
18513
18732
|
let memoryLength = getMemorySize(sampleRate, fallbackProcessor.memorySize);
|
|
@@ -18569,11 +18788,17 @@ for (let pan = MIN_PAN$1; pan <= MAX_PAN$1; pan++) {
|
|
|
18569
18788
|
var DefaultXGSystemEffect = class extends DefaultXGEffect {
|
|
18570
18789
|
return = 64;
|
|
18571
18790
|
pan = 64;
|
|
18791
|
+
/**
|
|
18792
|
+
* Resets this block to default values, including the processor type.
|
|
18793
|
+
*/
|
|
18572
18794
|
reset() {
|
|
18573
18795
|
super.reset();
|
|
18574
18796
|
this.return = 64;
|
|
18575
18797
|
this.pan = 64;
|
|
18576
18798
|
}
|
|
18799
|
+
/**
|
|
18800
|
+
* Gets a snapshot of this XG effect block.
|
|
18801
|
+
*/
|
|
18577
18802
|
getSnapshot() {
|
|
18578
18803
|
return {
|
|
18579
18804
|
...super.getSnapshot(),
|
|
@@ -18581,6 +18806,10 @@ var DefaultXGSystemEffect = class extends DefaultXGEffect {
|
|
|
18581
18806
|
pan: this.pan
|
|
18582
18807
|
};
|
|
18583
18808
|
}
|
|
18809
|
+
/**
|
|
18810
|
+
* Restores this XG effect block from a snapshot.
|
|
18811
|
+
* @param snapshot The snapshot to restore.
|
|
18812
|
+
*/
|
|
18584
18813
|
applySnapshot(snapshot) {
|
|
18585
18814
|
super.applySnapshot(snapshot);
|
|
18586
18815
|
this.return = snapshot.return;
|
|
@@ -18721,9 +18950,9 @@ const TIME_OFFSETS = new Int8Array([
|
|
|
18721
18950
|
107
|
|
18722
18951
|
]);
|
|
18723
18952
|
/**
|
|
18724
|
-
* Damping line
|
|
18953
|
+
* Damping line input gain coefficient words.
|
|
18725
18954
|
*/
|
|
18726
|
-
const
|
|
18955
|
+
const DAMP_INPUT = new Uint16Array([
|
|
18727
18956
|
105,
|
|
18728
18957
|
105,
|
|
18729
18958
|
105,
|
|
@@ -18982,9 +19211,9 @@ const DAMP_FEEDBACK = new Uint16Array([
|
|
|
18982
19211
|
2
|
|
18983
19212
|
]);
|
|
18984
19213
|
/**
|
|
18985
|
-
* Damping line
|
|
19214
|
+
* Damping line feedback coefficient words, used for both taps of the two-tap feedback filter.
|
|
18986
19215
|
*/
|
|
18987
|
-
const
|
|
19216
|
+
const DAMP_FEEDBACK = new Uint16Array([
|
|
18988
19217
|
1,
|
|
18989
19218
|
1,
|
|
18990
19219
|
1,
|
|
@@ -19756,7 +19985,7 @@ const LIST_LPF = [
|
|
|
19756
19985
|
50
|
|
19757
19986
|
];
|
|
19758
19987
|
/**
|
|
19759
|
-
*
|
|
19988
|
+
* Damped delay line addresses, 4 per line: write, feedback read, left output tap, right output tap.
|
|
19760
19989
|
*/
|
|
19761
19990
|
const LIST_DIFFUSION = [
|
|
19762
19991
|
31,
|
|
@@ -19777,7 +20006,7 @@ const LIST_DIFFUSION = [
|
|
|
19777
20006
|
7
|
|
19778
20007
|
];
|
|
19779
20008
|
/**
|
|
19780
|
-
* Damping line coefficients, 3 per line:
|
|
20009
|
+
* Damping line coefficients, 3 per line: input, read and previous read gains.
|
|
19781
20010
|
*/
|
|
19782
20011
|
const LIST_TIME = [
|
|
19783
20012
|
91,
|
|
@@ -20059,13 +20288,14 @@ var XGReverb = class extends XGDSPProgram {
|
|
|
20059
20288
|
* @param offset The first list index.
|
|
20060
20289
|
*/
|
|
20061
20290
|
writeDamping(index, list, offset) {
|
|
20062
|
-
this.coefficientWords[list[offset]] =
|
|
20063
|
-
const
|
|
20064
|
-
this.coefficientWords[list[offset + 1]] =
|
|
20065
|
-
this.coefficientWords[list[offset + 2]] =
|
|
20291
|
+
this.coefficientWords[list[offset]] = DAMP_INPUT[index];
|
|
20292
|
+
const feedback = index > 202 ? 255 : DAMP_FEEDBACK[index];
|
|
20293
|
+
this.coefficientWords[list[offset + 1]] = feedback;
|
|
20294
|
+
this.coefficientWords[list[offset + 2]] = feedback;
|
|
20066
20295
|
}
|
|
20067
20296
|
/**
|
|
20068
|
-
* Places the
|
|
20297
|
+
* Places the delay line read and output taps from the diffusion parameter.
|
|
20298
|
+
* The diffusion spreads the left and right output taps of each line apart.
|
|
20069
20299
|
*/
|
|
20070
20300
|
diffusion() {
|
|
20071
20301
|
const scale = DIFFUSION_SCALE[this.params[1]];
|
|
@@ -20221,7 +20451,7 @@ var XGRoomReverb = class extends XGReverb {
|
|
|
20221
20451
|
this.writeDamping(extra + base & 255, LIST_TIME, offset);
|
|
20222
20452
|
}
|
|
20223
20453
|
/**
|
|
20224
|
-
* Places a
|
|
20454
|
+
* Places a delay line's length and output taps from a room dimension.
|
|
20225
20455
|
* @param step The room diffusion step index.
|
|
20226
20456
|
* @param size The dimension size.
|
|
20227
20457
|
* @param offset The first diffusion list index.
|
|
@@ -20242,7 +20472,7 @@ var XGRoomReverb = class extends XGReverb {
|
|
|
20242
20472
|
}
|
|
20243
20473
|
}
|
|
20244
20474
|
/**
|
|
20245
|
-
* Updates one dimension's
|
|
20475
|
+
* Updates one dimension's delay line (damping, length and taps), then pushes it.
|
|
20246
20476
|
* @param dimension 0 width, 1 height, 2 depth.
|
|
20247
20477
|
* @param step The room diffusion step index.
|
|
20248
20478
|
* @param size The dimension size.
|
|
@@ -22066,6 +22296,8 @@ const REVERB_MAP = /* @__PURE__ */ new Map([
|
|
|
22066
22296
|
/**
|
|
22067
22297
|
* The default implementation for {@link XGReverbBlock}.
|
|
22068
22298
|
*
|
|
22299
|
+
* The default type is HALL 1. Unknown types use the type with LSB 0, or NO EFFECT (silence) if that is missing too.
|
|
22300
|
+
*
|
|
22069
22301
|
* @group Synthesizer.XG Effects
|
|
22070
22302
|
* @sealed
|
|
22071
22303
|
*/
|
|
@@ -22074,7 +22306,7 @@ var DefaultXGReverb = class extends DefaultXGSystemEffect {
|
|
|
22074
22306
|
* Constructs a new default XG reverb processor.
|
|
22075
22307
|
* @param sampleRate The sample rate, in Hertz.
|
|
22076
22308
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
22077
|
-
*
|
|
22309
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
22078
22310
|
*/
|
|
22079
22311
|
constructor(sampleRate, maxBufferSize) {
|
|
22080
22312
|
super(REVERB_MAP, XGNoEffect, 256, sampleRate, maxBufferSize, false);
|
|
@@ -22166,7 +22398,7 @@ const LIST_RIGHT_TAPS = [
|
|
|
22166
22398
|
25,
|
|
22167
22399
|
26
|
|
22168
22400
|
];
|
|
22169
|
-
const LIST_DRY_WET = [
|
|
22401
|
+
const LIST_DRY_WET$4 = [
|
|
22170
22402
|
95,
|
|
22171
22403
|
96,
|
|
22172
22404
|
97,
|
|
@@ -22407,7 +22639,7 @@ var XGChorusBase = class extends XGDSPProgram {
|
|
|
22407
22639
|
const w = this.coefficientWords;
|
|
22408
22640
|
w[95] = w[104] = dry & 1023;
|
|
22409
22641
|
w[96] = w[105] = w[97] = w[106] = wet & 1023;
|
|
22410
|
-
this.pushCoefficients(LIST_DRY_WET);
|
|
22642
|
+
this.pushCoefficients(LIST_DRY_WET$4);
|
|
22411
22643
|
break;
|
|
22412
22644
|
}
|
|
22413
22645
|
}
|
|
@@ -23679,6 +23911,8 @@ const CHORUS_MAP = /* @__PURE__ */ new Map([
|
|
|
23679
23911
|
/**
|
|
23680
23912
|
* The default implementation for {@link XGChorusBlock}.
|
|
23681
23913
|
*
|
|
23914
|
+
* The default type is CHORUS 1. Unknown types use the type with LSB 0, or NO EFFECT (silence) if that is missing too.
|
|
23915
|
+
*
|
|
23682
23916
|
* @group Synthesizer.XG Effects
|
|
23683
23917
|
* @sealed
|
|
23684
23918
|
*/
|
|
@@ -23688,11 +23922,14 @@ var DefaultXGChorus = class extends DefaultXGSystemEffect {
|
|
|
23688
23922
|
* Constructs a new default XG chorus processor.
|
|
23689
23923
|
* @param sampleRate The sample rate, in Hertz.
|
|
23690
23924
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
23691
|
-
*
|
|
23925
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
23692
23926
|
*/
|
|
23693
23927
|
constructor(sampleRate, maxBufferSize) {
|
|
23694
23928
|
super(CHORUS_MAP, XGNoEffect, 16640, sampleRate, maxBufferSize, false);
|
|
23695
23929
|
}
|
|
23930
|
+
/**
|
|
23931
|
+
* Resets this block to default values, including the processor type.
|
|
23932
|
+
*/
|
|
23696
23933
|
reset() {
|
|
23697
23934
|
super.reset();
|
|
23698
23935
|
this.sendToReverb = 0;
|
|
@@ -23707,12 +23944,19 @@ var DefaultXGChorus = class extends DefaultXGSystemEffect {
|
|
|
23707
23944
|
reverbRight[i] += this.outputRight[i] * gain;
|
|
23708
23945
|
}
|
|
23709
23946
|
}
|
|
23947
|
+
/**
|
|
23948
|
+
* Gets a snapshot of this XG effect block.
|
|
23949
|
+
*/
|
|
23710
23950
|
getSnapshot() {
|
|
23711
23951
|
return {
|
|
23712
23952
|
...super.getSnapshot(),
|
|
23713
23953
|
sendToReverb: this.sendToReverb
|
|
23714
23954
|
};
|
|
23715
23955
|
}
|
|
23956
|
+
/**
|
|
23957
|
+
* Restores this XG effect block from a snapshot.
|
|
23958
|
+
* @param snapshot The snapshot to restore.
|
|
23959
|
+
*/
|
|
23716
23960
|
applySnapshot(snapshot) {
|
|
23717
23961
|
super.applySnapshot(snapshot);
|
|
23718
23962
|
this.sendToReverb = snapshot.sendToReverb;
|
|
@@ -24141,6 +24385,16 @@ const PARAMETER_RANGES$15 = {
|
|
|
24141
24385
|
13: [4, 124],
|
|
24142
24386
|
14: [0, 1]
|
|
24143
24387
|
};
|
|
24388
|
+
const LIST_INPUT_COEFFICIENT = [
|
|
24389
|
+
170,
|
|
24390
|
+
180,
|
|
24391
|
+
185
|
|
24392
|
+
];
|
|
24393
|
+
const LIST_INPUT_ADDRESS = [
|
|
24394
|
+
59,
|
|
24395
|
+
45,
|
|
24396
|
+
46
|
|
24397
|
+
];
|
|
24144
24398
|
/**
|
|
24145
24399
|
* TREMOLO (`46 00`): amplitude and pitch modulation.
|
|
24146
24400
|
*/
|
|
@@ -24328,16 +24582,8 @@ var XGTremolo = class extends XGTremoloBase {
|
|
|
24328
24582
|
a[45] = 1025;
|
|
24329
24583
|
a[46] = 1026;
|
|
24330
24584
|
}
|
|
24331
|
-
this.pushCoefficients(
|
|
24332
|
-
|
|
24333
|
-
180,
|
|
24334
|
-
185
|
|
24335
|
-
]);
|
|
24336
|
-
this.pushAddresses([
|
|
24337
|
-
59,
|
|
24338
|
-
45,
|
|
24339
|
-
46
|
|
24340
|
-
]);
|
|
24585
|
+
this.pushCoefficients(LIST_INPUT_COEFFICIENT);
|
|
24586
|
+
this.pushAddresses(LIST_INPUT_ADDRESS);
|
|
24341
24587
|
this.convert(5);
|
|
24342
24588
|
this.convert(7);
|
|
24343
24589
|
break;
|
|
@@ -24353,6 +24599,7 @@ const PARAMETER_RANGES$14 = {
|
|
|
24353
24599
|
7: [28, 58],
|
|
24354
24600
|
8: [52, 76]
|
|
24355
24601
|
};
|
|
24602
|
+
const LIST_OUTPUT$3 = [180, 185];
|
|
24356
24603
|
/**
|
|
24357
24604
|
* LFO phases (left, right) per pan direction: L<->R, L->R, L<-R, L turn, R turn.
|
|
24358
24605
|
*/
|
|
@@ -24555,7 +24802,7 @@ var XGAutoPan = class extends XGTremoloBase {
|
|
|
24555
24802
|
break;
|
|
24556
24803
|
case 9:
|
|
24557
24804
|
w[185] = w[180] = this.outputWord(this.insertion ? 362 : 724);
|
|
24558
|
-
this.pushCoefficients(
|
|
24805
|
+
this.pushCoefficients(LIST_OUTPUT$3);
|
|
24559
24806
|
}
|
|
24560
24807
|
}
|
|
24561
24808
|
};
|
|
@@ -24779,6 +25026,11 @@ const PARAMETER_RANGES$13 = {
|
|
|
24779
25026
|
8: [52, 76],
|
|
24780
25027
|
10: [6, 10]
|
|
24781
25028
|
};
|
|
25029
|
+
const LIST_DRY_LEFT = [162];
|
|
25030
|
+
const LIST_MIX_RIGHT$1 = [166, 167];
|
|
25031
|
+
const LIST_WET_SECOND = [164];
|
|
25032
|
+
const LIST_WET_FOURTH = [165];
|
|
25033
|
+
const LIST_INPUT_MIX = [132, 133];
|
|
24782
25034
|
/**
|
|
24783
25035
|
* The converter index of the input mix level, which depends on the connection.
|
|
24784
25036
|
*/
|
|
@@ -25001,14 +25253,14 @@ var XGPhaser1 = class extends XGPhaserBase {
|
|
|
25001
25253
|
case 9:
|
|
25002
25254
|
w[166] = w[162] = PAN_LEFT[mix];
|
|
25003
25255
|
w[167] = PAN_RIGHT[mix];
|
|
25004
|
-
this.pushCoefficients(
|
|
25005
|
-
this.pushCoefficients(
|
|
25256
|
+
this.pushCoefficients(LIST_DRY_LEFT);
|
|
25257
|
+
this.pushCoefficients(LIST_MIX_RIGHT$1);
|
|
25006
25258
|
if (p[10] < 9) {
|
|
25007
25259
|
w[164] = PAN_RIGHT[mix];
|
|
25008
|
-
this.pushCoefficients(
|
|
25260
|
+
this.pushCoefficients(LIST_WET_SECOND);
|
|
25009
25261
|
} else {
|
|
25010
25262
|
w[165] = PAN_RIGHT[mix];
|
|
25011
|
-
this.pushCoefficients(
|
|
25263
|
+
this.pushCoefficients(LIST_WET_FOURTH);
|
|
25012
25264
|
}
|
|
25013
25265
|
break;
|
|
25014
25266
|
case 10: {
|
|
@@ -25032,7 +25284,7 @@ var XGPhaser1 = class extends XGPhaserBase {
|
|
|
25032
25284
|
}
|
|
25033
25285
|
case INPUT_MIX:
|
|
25034
25286
|
w[132] = w[133] = this.insertion ? 256 : 512;
|
|
25035
|
-
this.pushCoefficients(
|
|
25287
|
+
this.pushCoefficients(LIST_INPUT_MIX);
|
|
25036
25288
|
}
|
|
25037
25289
|
}
|
|
25038
25290
|
};
|
|
@@ -25046,6 +25298,8 @@ const PARAMETER_RANGES$12 = {
|
|
|
25046
25298
|
10: [3, 5],
|
|
25047
25299
|
12: [4, 124]
|
|
25048
25300
|
};
|
|
25301
|
+
const LIST_MIX_LEFT = [162, 163];
|
|
25302
|
+
const LIST_MIX_RIGHT = [166, 167];
|
|
25049
25303
|
/**
|
|
25050
25304
|
* PHASER 2 (`48 08`): a stereo phaser with 3 to 5 stages and an LFO phase difference between the sides.
|
|
25051
25305
|
*/
|
|
@@ -25218,8 +25472,8 @@ var XGPhaser2 = class extends XGPhaserBase {
|
|
|
25218
25472
|
const mix = this.mixIndex();
|
|
25219
25473
|
w[166] = w[162] = PAN_LEFT[mix];
|
|
25220
25474
|
w[167] = w[163] = PAN_RIGHT[mix];
|
|
25221
|
-
this.pushCoefficients(
|
|
25222
|
-
this.pushCoefficients(
|
|
25475
|
+
this.pushCoefficients(LIST_MIX_LEFT);
|
|
25476
|
+
this.pushCoefficients(LIST_MIX_RIGHT);
|
|
25223
25477
|
break;
|
|
25224
25478
|
}
|
|
25225
25479
|
case 10: {
|
|
@@ -25616,6 +25870,8 @@ const LIST_HIGH_LEFT = [
|
|
|
25616
25870
|
131,
|
|
25617
25871
|
133
|
|
25618
25872
|
];
|
|
25873
|
+
const LIST_OUTPUT$2 = [185, 186];
|
|
25874
|
+
const LIST_DRY_WET$3 = [171, 173];
|
|
25619
25875
|
/**
|
|
25620
25876
|
* AUTO WAH (`4E 00`): a resonant filter per side, swept by an exponential LFO.
|
|
25621
25877
|
*/
|
|
@@ -25869,8 +26125,8 @@ var XGAutoWah = class extends XGFilterVariationProgram {
|
|
|
25869
26125
|
const mix = this.mixIndex();
|
|
25870
26126
|
w[171] = w[185] = PAN_LEFT[mix];
|
|
25871
26127
|
w[173] = w[186] = PAN_RIGHT[mix];
|
|
25872
|
-
this.pushCoefficients(
|
|
25873
|
-
this.pushCoefficients(
|
|
26128
|
+
this.pushCoefficients(LIST_OUTPUT$2);
|
|
26129
|
+
this.pushCoefficients(LIST_DRY_WET$3);
|
|
25874
26130
|
break;
|
|
25875
26131
|
}
|
|
25876
26132
|
}
|
|
@@ -25897,16 +26153,17 @@ const UNWRITTEN_CATEGORY_2 = [
|
|
|
25897
26153
|
128,
|
|
25898
26154
|
174
|
|
25899
26155
|
];
|
|
26156
|
+
const LIST_MONO_INPUT$1 = [109, 110];
|
|
25900
26157
|
/**
|
|
25901
26158
|
* The base for the S-YXG50 category 2 variation programs (3-band EQ, distortion, early reflections).
|
|
25902
|
-
* They process a mono sum of the input through
|
|
26159
|
+
* They process a mono sum of the input through two first-order input sections, keeping scaled copies
|
|
25903
26160
|
* of the dry input that the routines mix back in.
|
|
25904
26161
|
* Any of 0x76, 0x7a, 0x7d and 0x80 sets 0x76 to their product.
|
|
25905
26162
|
*/
|
|
25906
26163
|
var XGMonoVariationProgram = class extends XGVariationProgram {
|
|
25907
26164
|
unwrittenCoefficients = UNWRITTEN_CATEGORY_2;
|
|
25908
26165
|
/**
|
|
25909
|
-
* The mono input after the
|
|
26166
|
+
* The mono input after the input sections.
|
|
25910
26167
|
*/
|
|
25911
26168
|
mono;
|
|
25912
26169
|
/**
|
|
@@ -26027,7 +26284,7 @@ var XGMonoVariationProgram = class extends XGVariationProgram {
|
|
|
26027
26284
|
monoInputLevel() {
|
|
26028
26285
|
const w = this.coefficientWords;
|
|
26029
26286
|
w[109] = w[110] = this.insertion ? 256 : 512;
|
|
26030
|
-
this.pushCoefficients(
|
|
26287
|
+
this.pushCoefficients(LIST_MONO_INPUT$1);
|
|
26031
26288
|
}
|
|
26032
26289
|
};
|
|
26033
26290
|
// src/synthesizer/audio_engine/effects/xg/implementation/dsp/equalizer/three_band_eq.ts
|
|
@@ -26387,6 +26644,12 @@ const LIST_MID = [
|
|
|
26387
26644
|
166,
|
|
26388
26645
|
165
|
|
26389
26646
|
];
|
|
26647
|
+
const LIST_DRY_WET$2 = [
|
|
26648
|
+
173,
|
|
26649
|
+
176,
|
|
26650
|
+
177,
|
|
26651
|
+
179
|
|
26652
|
+
];
|
|
26390
26653
|
/**
|
|
26391
26654
|
* The clipping range (the original works on a 16-bit scale, which is 4 times the full scale).
|
|
26392
26655
|
*/
|
|
@@ -26480,12 +26743,7 @@ var XGDistortionBase = class extends XGMonoVariationProgram {
|
|
|
26480
26743
|
if (this.insertion) wet >>= 1;
|
|
26481
26744
|
w[173] = w[177] = PAN_LEFT[mix];
|
|
26482
26745
|
w[176] = w[179] = VARIATION_GAIN * wet >> 6;
|
|
26483
|
-
this.pushCoefficients(
|
|
26484
|
-
173,
|
|
26485
|
-
176,
|
|
26486
|
-
177,
|
|
26487
|
-
179
|
|
26488
|
-
]);
|
|
26746
|
+
this.pushCoefficients(LIST_DRY_WET$2);
|
|
26489
26747
|
}
|
|
26490
26748
|
};
|
|
26491
26749
|
// src/synthesizer/audio_engine/effects/xg/implementation/dsp/distortion/distortion.ts
|
|
@@ -28425,6 +28683,12 @@ const LIST_HIGH_DAMP = [
|
|
|
28425
28683
|
119,
|
|
28426
28684
|
121
|
|
28427
28685
|
];
|
|
28686
|
+
const LIST_DRY_WET$1 = [
|
|
28687
|
+
173,
|
|
28688
|
+
175,
|
|
28689
|
+
177,
|
|
28690
|
+
178
|
|
28691
|
+
];
|
|
28428
28692
|
/**
|
|
28429
28693
|
* The converter index of the mono input level, which depends on the connection.
|
|
28430
28694
|
*/
|
|
@@ -28544,12 +28808,7 @@ var XGEarlyReflectionsBase = class extends XGMonoVariationProgram {
|
|
|
28544
28808
|
const mix = this.mixIndex();
|
|
28545
28809
|
w[173] = w[177] = MIX_DRY[mix] & 1023;
|
|
28546
28810
|
w[175] = w[178] = MIX_WET[mix] & 1023;
|
|
28547
|
-
this.pushCoefficients(
|
|
28548
|
-
173,
|
|
28549
|
-
175,
|
|
28550
|
-
177,
|
|
28551
|
-
178
|
|
28552
|
-
]);
|
|
28811
|
+
this.pushCoefficients(LIST_DRY_WET$1);
|
|
28553
28812
|
break;
|
|
28554
28813
|
}
|
|
28555
28814
|
case 10:
|
|
@@ -29308,7 +29567,7 @@ const DELAY_LEVEL = new Float32Array([
|
|
|
29308
29567
|
]);
|
|
29309
29568
|
// src/synthesizer/audio_engine/effects/xg/implementation/dsp/delay/xg_delay.ts
|
|
29310
29569
|
/**
|
|
29311
|
-
* The right line input position used by the two-line delays (
|
|
29570
|
+
* The right line input position used by the two-line delays (past the longest left line).
|
|
29312
29571
|
*/
|
|
29313
29572
|
const RIGHT_LINE_START = 16384;
|
|
29314
29573
|
const LIST_DAMP_A = [159, 158];
|
|
@@ -29373,7 +29632,7 @@ var XGDelayBase = class extends XGStereoVariationProgram {
|
|
|
29373
29632
|
this.pushAddresses([tap]);
|
|
29374
29633
|
}
|
|
29375
29634
|
/**
|
|
29376
|
-
* Designs a feedback damping filter (a
|
|
29635
|
+
* Designs a feedback damping filter (a two-tap low-pass) from the high damp parameter.
|
|
29377
29636
|
* @param damp The high damp value (1-10).
|
|
29378
29637
|
* @param list The coefficient indices for the input and feedback coefficients.
|
|
29379
29638
|
*/
|
|
@@ -29402,6 +29661,14 @@ const PARAMETER_RANGES$4 = {
|
|
|
29402
29661
|
14: [28, 58],
|
|
29403
29662
|
15: [52, 76]
|
|
29404
29663
|
};
|
|
29664
|
+
const LIST_MIX$3 = [
|
|
29665
|
+
178,
|
|
29666
|
+
181,
|
|
29667
|
+
184,
|
|
29668
|
+
185,
|
|
29669
|
+
187,
|
|
29670
|
+
182
|
|
29671
|
+
];
|
|
29405
29672
|
/**
|
|
29406
29673
|
* DELAY L,C,R (`05 00`): left, right and center taps with a separate feedback tap.
|
|
29407
29674
|
*/
|
|
@@ -29575,14 +29842,7 @@ var XGDelayLCR = class extends XGDelayBase {
|
|
|
29575
29842
|
if (this.insertion) wet >>= 1;
|
|
29576
29843
|
w[185] = w[181] = wet & 1023;
|
|
29577
29844
|
w[187] = w[182] = Math.trunc(wet * DELAY_LEVEL[p[5]]) & 1023;
|
|
29578
|
-
this.pushCoefficients(
|
|
29579
|
-
178,
|
|
29580
|
-
181,
|
|
29581
|
-
184,
|
|
29582
|
-
185,
|
|
29583
|
-
187,
|
|
29584
|
-
182
|
|
29585
|
-
]);
|
|
29845
|
+
this.pushCoefficients(LIST_MIX$3);
|
|
29586
29846
|
break;
|
|
29587
29847
|
}
|
|
29588
29848
|
case 6:
|
|
@@ -29611,6 +29871,12 @@ const PARAMETER_RANGES$3 = {
|
|
|
29611
29871
|
14: [28, 58],
|
|
29612
29872
|
15: [52, 76]
|
|
29613
29873
|
};
|
|
29874
|
+
const LIST_MIX$2 = [
|
|
29875
|
+
178,
|
|
29876
|
+
181,
|
|
29877
|
+
184,
|
|
29878
|
+
187
|
|
29879
|
+
];
|
|
29614
29880
|
/**
|
|
29615
29881
|
* DELAY L,R (`06 00`): left and right taps with two feedback taps.
|
|
29616
29882
|
*/
|
|
@@ -29786,12 +30052,7 @@ var XGDelayLR = class extends XGDelayBase {
|
|
|
29786
30052
|
let wet = MIX_WET[mix];
|
|
29787
30053
|
if (this.insertion) wet >>= 1;
|
|
29788
30054
|
w[187] = w[181] = wet & 1023;
|
|
29789
|
-
this.pushCoefficients(
|
|
29790
|
-
178,
|
|
29791
|
-
181,
|
|
29792
|
-
184,
|
|
29793
|
-
187
|
|
29794
|
-
]);
|
|
30055
|
+
this.pushCoefficients(LIST_MIX$2);
|
|
29795
30056
|
break;
|
|
29796
30057
|
}
|
|
29797
30058
|
case 12:
|
|
@@ -29818,6 +30079,14 @@ const PARAMETER_RANGES$2 = {
|
|
|
29818
30079
|
14: [28, 58],
|
|
29819
30080
|
15: [52, 76]
|
|
29820
30081
|
};
|
|
30082
|
+
const LIST_MIX$1 = [
|
|
30083
|
+
178,
|
|
30084
|
+
180,
|
|
30085
|
+
181,
|
|
30086
|
+
184,
|
|
30087
|
+
185,
|
|
30088
|
+
187
|
|
30089
|
+
];
|
|
29821
30090
|
/**
|
|
29822
30091
|
* ECHO (`07 00`): two independent delay lines with feedback and a second tap each.
|
|
29823
30092
|
*/
|
|
@@ -29999,14 +30268,7 @@ var XGEcho = class extends XGDelayBase {
|
|
|
29999
30268
|
const wet = MIX_WET[mix];
|
|
30000
30269
|
w[185] = w[180] = wet & 1023;
|
|
30001
30270
|
w[187] = w[181] = Math.trunc(wet * DELAY_LEVEL[p[7]]) & 1023;
|
|
30002
|
-
this.pushCoefficients(
|
|
30003
|
-
178,
|
|
30004
|
-
180,
|
|
30005
|
-
181,
|
|
30006
|
-
184,
|
|
30007
|
-
185,
|
|
30008
|
-
187
|
|
30009
|
-
]);
|
|
30271
|
+
this.pushCoefficients(LIST_MIX$1);
|
|
30010
30272
|
break;
|
|
30011
30273
|
}
|
|
30012
30274
|
case 12:
|
|
@@ -30031,6 +30293,12 @@ const PARAMETER_RANGES$1 = {
|
|
|
30031
30293
|
14: [28, 58],
|
|
30032
30294
|
15: [52, 76]
|
|
30033
30295
|
};
|
|
30296
|
+
const LIST_MIX = [
|
|
30297
|
+
178,
|
|
30298
|
+
180,
|
|
30299
|
+
184,
|
|
30300
|
+
185
|
|
30301
|
+
];
|
|
30034
30302
|
/**
|
|
30035
30303
|
* CROSS DELAY (`08 00`): two delay lines feeding back into each other.
|
|
30036
30304
|
*/
|
|
@@ -30221,12 +30489,7 @@ var XGCrossDelay = class extends XGDelayBase {
|
|
|
30221
30489
|
let wet = MIX_WET[mix];
|
|
30222
30490
|
if (p[3] < 2) wet >>= 1;
|
|
30223
30491
|
w[185] = w[180] = wet & 1023;
|
|
30224
|
-
this.pushCoefficients(
|
|
30225
|
-
178,
|
|
30226
|
-
180,
|
|
30227
|
-
184,
|
|
30228
|
-
185
|
|
30229
|
-
]);
|
|
30492
|
+
this.pushCoefficients(LIST_MIX);
|
|
30230
30493
|
break;
|
|
30231
30494
|
}
|
|
30232
30495
|
case 12:
|
|
@@ -30421,6 +30684,10 @@ const LIST_DENSITY_ADDRESS = [
|
|
|
30421
30684
|
54,
|
|
30422
30685
|
51
|
|
30423
30686
|
];
|
|
30687
|
+
const LIST_DRY = [185];
|
|
30688
|
+
const LIST_WET = [187];
|
|
30689
|
+
const LIST_DRY_WET = [174, 172];
|
|
30690
|
+
const LIST_MONO_INPUT = [109, 110];
|
|
30424
30691
|
/**
|
|
30425
30692
|
* The converter index of the mono input level, which depends on the connection.
|
|
30426
30693
|
*/
|
|
@@ -30543,9 +30810,9 @@ var XGKaraokeBase = class extends XGFilterVariationProgram {
|
|
|
30543
30810
|
const mix = this.mixIndex();
|
|
30544
30811
|
w[172] = w[185] = MIX_DRY[mix] & 1023;
|
|
30545
30812
|
w[174] = w[187] = MIX_WET[mix] & 1023;
|
|
30546
|
-
this.pushCoefficients(
|
|
30547
|
-
this.pushCoefficients(
|
|
30548
|
-
this.pushCoefficients(
|
|
30813
|
+
this.pushCoefficients(LIST_DRY);
|
|
30814
|
+
this.pushCoefficients(LIST_WET);
|
|
30815
|
+
this.pushCoefficients(LIST_DRY_WET);
|
|
30549
30816
|
break;
|
|
30550
30817
|
}
|
|
30551
30818
|
case 10:
|
|
@@ -30554,7 +30821,7 @@ var XGKaraokeBase = class extends XGFilterVariationProgram {
|
|
|
30554
30821
|
break;
|
|
30555
30822
|
case MONO_INPUT$1:
|
|
30556
30823
|
w[109] = w[110] = this.insertion ? 181 : 362;
|
|
30557
|
-
this.pushCoefficients(
|
|
30824
|
+
this.pushCoefficients(LIST_MONO_INPUT);
|
|
30558
30825
|
}
|
|
30559
30826
|
}
|
|
30560
30827
|
};
|
|
@@ -31013,6 +31280,9 @@ const VARIATION_MAP = /* @__PURE__ */ new Map([
|
|
|
31013
31280
|
/**
|
|
31014
31281
|
* The default implementation for {@link XGVariationBlock}.
|
|
31015
31282
|
*
|
|
31283
|
+
* The default type is DELAY L,C,R in the insertion connection.
|
|
31284
|
+
* Unknown types use the type with LSB 0, or THRU if that is missing too.
|
|
31285
|
+
*
|
|
31016
31286
|
* @group Synthesizer.XG Effects
|
|
31017
31287
|
* @sealed
|
|
31018
31288
|
*/
|
|
@@ -31025,11 +31295,14 @@ var DefaultXGVariation = class extends DefaultXGSystemEffect {
|
|
|
31025
31295
|
* Constructs a new default XG variation processor.
|
|
31026
31296
|
* @param sampleRate The sample rate, in Hertz.
|
|
31027
31297
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
31028
|
-
*
|
|
31298
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
31029
31299
|
*/
|
|
31030
31300
|
constructor(sampleRate, maxBufferSize) {
|
|
31031
31301
|
super(VARIATION_MAP, XGThru, 1280, sampleRate, maxBufferSize, true);
|
|
31032
31302
|
}
|
|
31303
|
+
/**
|
|
31304
|
+
* Resets this block to default values, including the processor type.
|
|
31305
|
+
*/
|
|
31033
31306
|
reset() {
|
|
31034
31307
|
super.reset();
|
|
31035
31308
|
this.insertionMode = true;
|
|
@@ -31063,6 +31336,9 @@ var DefaultXGVariation = class extends DefaultXGSystemEffect {
|
|
|
31063
31336
|
inputRight[i] = outputRight[i];
|
|
31064
31337
|
}
|
|
31065
31338
|
}
|
|
31339
|
+
/**
|
|
31340
|
+
* Gets a snapshot of this XG effect block.
|
|
31341
|
+
*/
|
|
31066
31342
|
getSnapshot() {
|
|
31067
31343
|
return {
|
|
31068
31344
|
...super.getSnapshot(),
|
|
@@ -31072,6 +31348,10 @@ var DefaultXGVariation = class extends DefaultXGSystemEffect {
|
|
|
31072
31348
|
sendToChorus: this.sendToChorus
|
|
31073
31349
|
};
|
|
31074
31350
|
}
|
|
31351
|
+
/**
|
|
31352
|
+
* Restores this XG effect block from a snapshot.
|
|
31353
|
+
* @param snapshot The snapshot to restore.
|
|
31354
|
+
*/
|
|
31075
31355
|
applySnapshot(snapshot) {
|
|
31076
31356
|
super.applySnapshot(snapshot);
|
|
31077
31357
|
this.insertionMode = snapshot.insertionMode;
|
|
@@ -31124,6 +31404,10 @@ const INSERTION_MAP = /* @__PURE__ */ new Map([
|
|
|
31124
31404
|
/**
|
|
31125
31405
|
* The default implementation for {@link XGInsertionBlock}.
|
|
31126
31406
|
*
|
|
31407
|
+
* The default type is DISTORTION. The EFFECT 2 types run on the same processors as the variation block types.
|
|
31408
|
+
* The early reflection types and PHASER 2 are variation only, so PHASER 2 falls back to PHASER 1 here.
|
|
31409
|
+
* Unknown types use the type with LSB 0, or THRU if that is missing too.
|
|
31410
|
+
*
|
|
31127
31411
|
* @group Synthesizer.XG Effects
|
|
31128
31412
|
* @sealed
|
|
31129
31413
|
*/
|
|
@@ -31133,11 +31417,14 @@ var DefaultXGInsertion = class extends DefaultXGEffect {
|
|
|
31133
31417
|
* Constructs a new default XG insertion processor.
|
|
31134
31418
|
* @param sampleRate The sample rate, in Hertz.
|
|
31135
31419
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
31136
|
-
*
|
|
31420
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
31137
31421
|
*/
|
|
31138
31422
|
constructor(sampleRate, maxBufferSize) {
|
|
31139
31423
|
super(INSERTION_MAP, XGThru, 18688, sampleRate, maxBufferSize, true);
|
|
31140
31424
|
}
|
|
31425
|
+
/**
|
|
31426
|
+
* Resets this block to default values, including the processor type.
|
|
31427
|
+
*/
|
|
31141
31428
|
reset() {
|
|
31142
31429
|
super.reset();
|
|
31143
31430
|
this.partNumber = 127;
|
|
@@ -31150,12 +31437,19 @@ var DefaultXGInsertion = class extends DefaultXGEffect {
|
|
|
31150
31437
|
inputRight[i] = outputRight[i];
|
|
31151
31438
|
}
|
|
31152
31439
|
}
|
|
31440
|
+
/**
|
|
31441
|
+
* Gets a snapshot of this XG effect block.
|
|
31442
|
+
*/
|
|
31153
31443
|
getSnapshot() {
|
|
31154
31444
|
return {
|
|
31155
31445
|
...super.getSnapshot(),
|
|
31156
31446
|
partNumber: this.partNumber
|
|
31157
31447
|
};
|
|
31158
31448
|
}
|
|
31449
|
+
/**
|
|
31450
|
+
* Restores this XG effect block from a snapshot.
|
|
31451
|
+
* @param snapshot The snapshot to restore.
|
|
31452
|
+
*/
|
|
31159
31453
|
applySnapshot(snapshot) {
|
|
31160
31454
|
super.applySnapshot(snapshot);
|
|
31161
31455
|
this.partNumber = snapshot.partNumber;
|
|
@@ -31181,9 +31475,28 @@ const InterpolationTypes = {
|
|
|
31181
31475
|
*/
|
|
31182
31476
|
nearestNeighbor: 1,
|
|
31183
31477
|
/**
|
|
31184
|
-
*
|
|
31478
|
+
* Cubic Hermite interpolation. Good quality with little artifacting. (Default)
|
|
31479
|
+
*
|
|
31480
|
+
* Recommended for most use-cases.
|
|
31481
|
+
*/
|
|
31482
|
+
hermite: 2,
|
|
31483
|
+
/**
|
|
31484
|
+
* 32-point windowed sinc interpolation.
|
|
31485
|
+
*
|
|
31486
|
+
* Very high quality with almost no aliasing or harmonic distortion,
|
|
31487
|
+
* usable in real time, but at a significantly higher CPU cost.
|
|
31488
|
+
*/
|
|
31489
|
+
sinc32: 3,
|
|
31490
|
+
/**
|
|
31491
|
+
* 128-point windowed sinc interpolation with antialiasing.
|
|
31492
|
+
*
|
|
31493
|
+
* The best quality, but extremely high cpu cost.
|
|
31494
|
+
*
|
|
31495
|
+
* > **Warning**
|
|
31496
|
+
* >
|
|
31497
|
+
* > This is very slow (especially for pitched up samples) and is meant for offline rendering, not real time playback.
|
|
31185
31498
|
*/
|
|
31186
|
-
|
|
31499
|
+
sinc128: 4
|
|
31187
31500
|
};
|
|
31188
31501
|
/**
|
|
31189
31502
|
* The special EFX control sources of a {@link GSInsertionBlock} (EFX CONTROL SOURCE 1/2).
|
|
@@ -31612,7 +31925,7 @@ var DefaultGSReverb = class {
|
|
|
31612
31925
|
* Constructs a new default GS reverb processor.
|
|
31613
31926
|
* @param sampleRate The sample rate, in Hertz.
|
|
31614
31927
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
31615
|
-
*
|
|
31928
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
31616
31929
|
*/
|
|
31617
31930
|
constructor(sampleRate, maxBufferSize) {
|
|
31618
31931
|
this.sampleRate = sampleRate;
|
|
@@ -31735,12 +32048,12 @@ var DefaultGSReverb = class {
|
|
|
31735
32048
|
this.updateLowpass();
|
|
31736
32049
|
}
|
|
31737
32050
|
/**
|
|
31738
|
-
*
|
|
31739
|
-
* @param input 0
|
|
31740
|
-
* @param outputLeft
|
|
31741
|
-
* @param outputRight
|
|
31742
|
-
* @param startIndex
|
|
31743
|
-
* @param sampleCount
|
|
32051
|
+
* Process the effect and **adds** it to the output.
|
|
32052
|
+
* @param input The input buffer to process. It always starts at index 0.
|
|
32053
|
+
* @param outputLeft The left output buffer.
|
|
32054
|
+
* @param outputRight The right output buffer.
|
|
32055
|
+
* @param startIndex The index to start mixing at into the output buffers.
|
|
32056
|
+
* @param sampleCount The amount of samples to mix.
|
|
31744
32057
|
*/
|
|
31745
32058
|
process(input, outputLeft, outputRight, startIndex, sampleCount) {
|
|
31746
32059
|
switch (this._character) {
|
|
@@ -31879,7 +32192,7 @@ var DefaultGSChorus = class {
|
|
|
31879
32192
|
* Constructs a new default GS chorus processor.
|
|
31880
32193
|
* @param sampleRate The sample rate, in Hertz.
|
|
31881
32194
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
31882
|
-
*
|
|
32195
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
31883
32196
|
*/
|
|
31884
32197
|
constructor(sampleRate, maxBufferSize) {
|
|
31885
32198
|
this.sampleRate = sampleRate;
|
|
@@ -32138,7 +32451,7 @@ var DefaultGSDelay = class {
|
|
|
32138
32451
|
* Constructs a new default GS delay processor.
|
|
32139
32452
|
* @param sampleRate The sample rate, in Hertz.
|
|
32140
32453
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
32141
|
-
*
|
|
32454
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
32142
32455
|
*/
|
|
32143
32456
|
constructor(sampleRate, maxBufferSize) {
|
|
32144
32457
|
this.sampleRate = sampleRate;
|
|
@@ -32330,6 +32643,148 @@ var DefaultGSDelay = class {
|
|
|
32330
32643
|
this.gainRight = this._levelRight / 127;
|
|
32331
32644
|
}
|
|
32332
32645
|
};
|
|
32646
|
+
// src/synthesizer/audio_engine/effects/gs/implementation/equalizer.ts
|
|
32647
|
+
const LOW_FREQUENCIES = [225, 425];
|
|
32648
|
+
const HIGH_FREQUENCIES = [3e3, 6e3];
|
|
32649
|
+
/**
|
|
32650
|
+
* The default GS Equalizer implementation for {@link SpessaSynthProcessor}.
|
|
32651
|
+
*
|
|
32652
|
+
* @group Synthesizer.GS Effects
|
|
32653
|
+
*/
|
|
32654
|
+
var DefaultGSEqualizer = class {
|
|
32655
|
+
sampleRate;
|
|
32656
|
+
lowB0 = 1;
|
|
32657
|
+
lowB1 = 0;
|
|
32658
|
+
lowA1 = 0;
|
|
32659
|
+
highB0 = 1;
|
|
32660
|
+
highB1 = 0;
|
|
32661
|
+
highA1 = 0;
|
|
32662
|
+
lowZL = 0;
|
|
32663
|
+
lowZR = 0;
|
|
32664
|
+
highZL = 0;
|
|
32665
|
+
highZR = 0;
|
|
32666
|
+
/**
|
|
32667
|
+
* Constructs a new default GS equalizer processor.
|
|
32668
|
+
* @param sampleRate The sample rate, in Hertz.
|
|
32669
|
+
*/
|
|
32670
|
+
constructor(sampleRate) {
|
|
32671
|
+
this.sampleRate = sampleRate;
|
|
32672
|
+
this.updateLow();
|
|
32673
|
+
this.updateHigh();
|
|
32674
|
+
}
|
|
32675
|
+
_lowFrequency = 0;
|
|
32676
|
+
get lowFrequency() {
|
|
32677
|
+
return this._lowFrequency;
|
|
32678
|
+
}
|
|
32679
|
+
set lowFrequency(value) {
|
|
32680
|
+
this._lowFrequency = value;
|
|
32681
|
+
this.updateLow();
|
|
32682
|
+
}
|
|
32683
|
+
_lowGain = 64;
|
|
32684
|
+
get lowGain() {
|
|
32685
|
+
return this._lowGain;
|
|
32686
|
+
}
|
|
32687
|
+
set lowGain(value) {
|
|
32688
|
+
this._lowGain = value;
|
|
32689
|
+
this.updateLow();
|
|
32690
|
+
}
|
|
32691
|
+
_highFrequency = 0;
|
|
32692
|
+
get highFrequency() {
|
|
32693
|
+
return this._highFrequency;
|
|
32694
|
+
}
|
|
32695
|
+
set highFrequency(value) {
|
|
32696
|
+
this._highFrequency = value;
|
|
32697
|
+
this.updateHigh();
|
|
32698
|
+
}
|
|
32699
|
+
_highGain = 64;
|
|
32700
|
+
get highGain() {
|
|
32701
|
+
return this._highGain;
|
|
32702
|
+
}
|
|
32703
|
+
set highGain(value) {
|
|
32704
|
+
this._highGain = value;
|
|
32705
|
+
this.updateHigh();
|
|
32706
|
+
}
|
|
32707
|
+
process(inputLeft, inputRight, outputLeft, outputRight, startIndex, sampleCount) {
|
|
32708
|
+
const { lowB0, lowB1, lowA1, highB0, highB1, highA1 } = this;
|
|
32709
|
+
let { lowZL, lowZR, highZL, highZR } = this;
|
|
32710
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
32711
|
+
const o = startIndex + i;
|
|
32712
|
+
let x = inputLeft[i];
|
|
32713
|
+
let y = lowB0 * x + lowZL;
|
|
32714
|
+
lowZL = lowB1 * x - lowA1 * y;
|
|
32715
|
+
x = y;
|
|
32716
|
+
y = highB0 * x + highZL;
|
|
32717
|
+
highZL = highB1 * x - highA1 * y;
|
|
32718
|
+
outputLeft[o] += y;
|
|
32719
|
+
x = inputRight[i];
|
|
32720
|
+
y = lowB0 * x + lowZR;
|
|
32721
|
+
lowZR = lowB1 * x - lowA1 * y;
|
|
32722
|
+
x = y;
|
|
32723
|
+
y = highB0 * x + highZR;
|
|
32724
|
+
highZR = highB1 * x - highA1 * y;
|
|
32725
|
+
outputRight[o] += y;
|
|
32726
|
+
}
|
|
32727
|
+
this.lowZL = lowZL;
|
|
32728
|
+
this.lowZR = lowZR;
|
|
32729
|
+
this.highZL = highZL;
|
|
32730
|
+
this.highZR = highZR;
|
|
32731
|
+
}
|
|
32732
|
+
getSnapshot() {
|
|
32733
|
+
return {
|
|
32734
|
+
lowFrequency: this._lowFrequency,
|
|
32735
|
+
lowGain: this._lowGain,
|
|
32736
|
+
highFrequency: this._highFrequency,
|
|
32737
|
+
highGain: this._highGain
|
|
32738
|
+
};
|
|
32739
|
+
}
|
|
32740
|
+
applySnapshot(snapshot) {
|
|
32741
|
+
this.lowFrequency = snapshot.lowFrequency;
|
|
32742
|
+
this.lowGain = snapshot.lowGain;
|
|
32743
|
+
this.highFrequency = snapshot.highFrequency;
|
|
32744
|
+
this.highGain = snapshot.highGain;
|
|
32745
|
+
}
|
|
32746
|
+
getBand(frequency, gain) {
|
|
32747
|
+
const fc = Math.min(frequency, this.sampleRate * .45);
|
|
32748
|
+
const dB = Math.max(-12, Math.min(12, gain - 64));
|
|
32749
|
+
return {
|
|
32750
|
+
k: Math.tan(Math.PI * fc / this.sampleRate),
|
|
32751
|
+
g: 10 ** (Math.abs(dB) / 20),
|
|
32752
|
+
cut: dB < 0
|
|
32753
|
+
};
|
|
32754
|
+
}
|
|
32755
|
+
updateLow() {
|
|
32756
|
+
const { k, g, cut } = this.getBand(LOW_FREQUENCIES[this._lowFrequency & 1], this._lowGain);
|
|
32757
|
+
let b0 = 1 + k;
|
|
32758
|
+
let b1 = k - 1;
|
|
32759
|
+
let a0 = 1 + k / g;
|
|
32760
|
+
let a1 = k / g - 1;
|
|
32761
|
+
if (cut) [b0, b1, a0, a1] = [
|
|
32762
|
+
a0,
|
|
32763
|
+
a1,
|
|
32764
|
+
b0,
|
|
32765
|
+
b1
|
|
32766
|
+
];
|
|
32767
|
+
this.lowB0 = b0 / a0;
|
|
32768
|
+
this.lowB1 = b1 / a0;
|
|
32769
|
+
this.lowA1 = a1 / a0;
|
|
32770
|
+
}
|
|
32771
|
+
updateHigh() {
|
|
32772
|
+
const { k, g, cut } = this.getBand(HIGH_FREQUENCIES[this._highFrequency & 1], this._highGain);
|
|
32773
|
+
let b0 = g * (1 + k);
|
|
32774
|
+
let b1 = g * (k - 1);
|
|
32775
|
+
let a0 = 1 + g * k;
|
|
32776
|
+
let a1 = g * k - 1;
|
|
32777
|
+
if (cut) [b0, b1, a0, a1] = [
|
|
32778
|
+
a0,
|
|
32779
|
+
a1,
|
|
32780
|
+
b0,
|
|
32781
|
+
b1
|
|
32782
|
+
];
|
|
32783
|
+
this.highB0 = b0 / a0;
|
|
32784
|
+
this.highB1 = b1 / a0;
|
|
32785
|
+
this.highA1 = a1 / a0;
|
|
32786
|
+
}
|
|
32787
|
+
};
|
|
32333
32788
|
// src/synthesizer/audio_engine/effects/gs/implementation/dsp/gs_effect.ts
|
|
32334
32789
|
/**
|
|
32335
32790
|
* The number of EFX parameters (1-20 in the manuals, 0-19 here).
|
|
@@ -32838,6 +33293,16 @@ function prewarp(frequency, sampleRate) {
|
|
|
32838
33293
|
return Math.tan(Math.PI * Math.min(frequency, sampleRate * .49) / sampleRate);
|
|
32839
33294
|
}
|
|
32840
33295
|
/**
|
|
33296
|
+
* The corner of the hardware's one-pole low pass `(1 - B) x + B y[n-1]` at 32 kHz (-3 dB, the inverse of
|
|
33297
|
+
* `FirstOrderFilter.setDamp`).
|
|
33298
|
+
* @param pole B.
|
|
33299
|
+
* @internal
|
|
33300
|
+
*/
|
|
33301
|
+
function dampCorner(pole) {
|
|
33302
|
+
const k = (1 + pole * pole) / (2 * pole);
|
|
33303
|
+
return Math.acos(2 - k) * 32e3 / (2 * Math.PI);
|
|
33304
|
+
}
|
|
33305
|
+
/**
|
|
32841
33306
|
* A first order section, `y = b0 x + b1 x[n-1] + a1 y[n-1]`, designed with the bilinear transform.
|
|
32842
33307
|
* The tone filters, the shelves and the DC blockers of the Sound Canvas are all this section.
|
|
32843
33308
|
* @internal
|
|
@@ -39632,7 +40097,7 @@ var GSEqualizer = class extends GSEffect {
|
|
|
39632
40097
|
* @param x The argument.
|
|
39633
40098
|
* @internal
|
|
39634
40099
|
*/
|
|
39635
|
-
function besselI0(x) {
|
|
40100
|
+
function besselI0$1(x) {
|
|
39636
40101
|
let sum = 1;
|
|
39637
40102
|
let term = 1;
|
|
39638
40103
|
for (let k = 1; k < 32; k++) {
|
|
@@ -39648,7 +40113,7 @@ function besselI0(x) {
|
|
|
39648
40113
|
* @internal
|
|
39649
40114
|
*/
|
|
39650
40115
|
function kaiser(t, beta) {
|
|
39651
|
-
return besselI0(beta * Math.sqrt(1 - t * t)) / besselI0(beta);
|
|
40116
|
+
return besselI0$1(beta * Math.sqrt(1 - t * t)) / besselI0$1(beta);
|
|
39652
40117
|
}
|
|
39653
40118
|
// src/synthesizer/audio_engine/effects/gs/implementation/dsp/blocks/delay_line.ts
|
|
39654
40119
|
/**
|
|
@@ -42963,7 +43428,7 @@ function cutoff(value) {
|
|
|
42963
43428
|
*/
|
|
42964
43429
|
const STEP_MS = 1 / 32;
|
|
42965
43430
|
/**
|
|
42966
|
-
*
|
|
43431
|
+
* A bound on the swing of `modulationDepth` (12.4 ms at depth 127), in ms, for sizing the lines.
|
|
42967
43432
|
* @internal
|
|
42968
43433
|
*/
|
|
42969
43434
|
const SWING_MS = 12.5;
|
|
@@ -43526,7 +43991,8 @@ var Dynamics = class {
|
|
|
43526
43991
|
};
|
|
43527
43992
|
/**
|
|
43528
43993
|
* The compressor: 1 ms lookahead, the gain recovers linearly, the output has a makeup gain (the "Attack" parameter,
|
|
43529
|
-
* which sets how far below the fixed output ceiling the knee sits
|
|
43994
|
+
* which sets how far below the fixed output ceiling the knee sits, the manuals call it Sustain, see `GSCompressor`)
|
|
43995
|
+
* and a two-sample average.
|
|
43530
43996
|
* @internal
|
|
43531
43997
|
*/
|
|
43532
43998
|
var Compressor = class extends Dynamics {
|
|
@@ -43670,6 +44136,10 @@ var GSDynamicsEffect = class extends GSEffect {
|
|
|
43670
44136
|
* Compressor (`01 30`).
|
|
43671
44137
|
* Parameters: 1 sustain (how fast the gain comes back), 2 attack (the makeup gain: the output ceiling stays, the
|
|
43672
44138
|
* knee moves), 3 post gain, then the EQ, pan and level.
|
|
44139
|
+
*
|
|
44140
|
+
* The SC-VA and SC-8850 manuals list parameter 1 as Attack and 2 as Sustain, the other way round from the names
|
|
44141
|
+
* here. Only the names differ: parameter 1 still sets how fast the gain recovers (0.09 to 2.25 per second) and
|
|
44142
|
+
* parameter 2 the makeup gain, like in Sound Canvas VA. The multis' compressors and Lo-Fi 1 use the same names.
|
|
43673
44143
|
* @internal
|
|
43674
44144
|
*/
|
|
43675
44145
|
var GSCompressor = class extends GSDynamicsEffect {
|
|
@@ -46868,7 +47338,7 @@ const LINE_LENGTHS = [
|
|
|
46868
47338
|
];
|
|
46869
47339
|
/**
|
|
46870
47340
|
* How many host samples later the whole forward path runs, and how much earlier the outputs read, so that even the
|
|
46871
|
-
* shortest pre delay is read band-limited (the interpolator needs 7 newer samples). The outputs read
|
|
47341
|
+
* shortest pre delay is read band-limited (the interpolator needs 7 newer samples). The outputs read 140 32 kHz
|
|
46872
47342
|
* samples or more, so they have room.
|
|
46873
47343
|
*/
|
|
46874
47344
|
const LOOKAHEAD$1 = 8;
|
|
@@ -46881,15 +47351,6 @@ const ALLPASS$1 = .5;
|
|
|
46881
47351
|
*/
|
|
46882
47352
|
const PRE_GAIN$1 = (127 / 128) ** 2;
|
|
46883
47353
|
/**
|
|
46884
|
-
* The corner of the hardware's one-pole low pass `(1 - B) x + B y[n-1]` at 32 kHz (-3 dB, the inverse of
|
|
46885
|
-
* `FirstOrderFilter.setDamp`).
|
|
46886
|
-
* @param pole B.
|
|
46887
|
-
*/
|
|
46888
|
-
function dampCorner(pole) {
|
|
46889
|
-
const k = (1 + pole * pole) / (2 * pole);
|
|
46890
|
-
return Math.acos(2 - k) * 32e3 / (2 * Math.PI);
|
|
46891
|
-
}
|
|
46892
|
-
/**
|
|
46893
47354
|
* The Reverb: a mono input through a DC blocker and an input low pass into a pre delay, a diffuser of three
|
|
46894
47355
|
* Schroeder allpasses and a delay, then two independent tanks (allpass, delay, allpass, delay, a damping low pass
|
|
46895
47356
|
* and the time gain back to the start). The stereo image comes from the output taps alone, four per side spread
|
|
@@ -47597,7 +48058,7 @@ var LoFiQuantizer = class {
|
|
|
47597
48058
|
};
|
|
47598
48059
|
// src/synthesizer/audio_engine/effects/gs/implementation/dsp/effects/lo_fi_1.ts
|
|
47599
48060
|
/**
|
|
47600
|
-
* The gain after the compressor per pre
|
|
48061
|
+
* The gain after the compressor per pre-ilter (1 bypasses the compressor).
|
|
47601
48062
|
*/
|
|
47602
48063
|
const PRE_GAINS = [
|
|
47603
48064
|
1,
|
|
@@ -47652,9 +48113,9 @@ const POST_FILTERS = [
|
|
|
47652
48113
|
}
|
|
47653
48114
|
];
|
|
47654
48115
|
/**
|
|
47655
|
-
* Lo-Fi 1 (`01 72`): `L+R -> compressor (pre
|
|
48116
|
+
* Lo-Fi 1 (`01 72`): `L+R -> compressor (pre-filter) -> low pass -> quantizer -> high pass and dip (post filter)`,
|
|
47656
48117
|
* mixed with the mono input by the balance, then the EQ and the pan.
|
|
47657
|
-
* Parameters: 1 pre
|
|
48118
|
+
* Parameters: 1 pre-filter (1 dry, 2-6 the compressor with a gain of 2 to 7.94, 3 and 5 with a 4 kHz low pass),
|
|
47658
48119
|
* 2 lo-fi type (15 or 11 bits at 16000, 10667 or 8000 Hz), 3 post filter (high pass at 633 or 1595 Hz and a dip at
|
|
47659
48120
|
* 1 kHz of Q 2, 1 or 0.5), 16 balance (the pan law), 17 low gain, 18 high gain, 19 pan, 20 level. EFX control source
|
|
47660
48121
|
* 1 modulates the balance, source 2 the pan.
|
|
@@ -47692,7 +48153,7 @@ var GSLoFi1 = class extends GSEffect {
|
|
|
47692
48153
|
mix;
|
|
47693
48154
|
compressor;
|
|
47694
48155
|
/**
|
|
47695
|
-
* The compressor's own DC blocker, for the dry path and the pre
|
|
48156
|
+
* The compressor's own DC blocker, for the dry path and the pre-filter that bypasses it.
|
|
47696
48157
|
*/
|
|
47697
48158
|
dcBlocker;
|
|
47698
48159
|
lowPass;
|
|
@@ -47891,7 +48352,7 @@ function clamp32(x) {
|
|
|
47891
48352
|
*
|
|
47892
48353
|
* - White (x 1/32) or "pink": the white noise times a 500 Hz saw (on the SC-88Pro too).
|
|
47893
48354
|
* - Disc: one click per turn of a record at 33 1/3, 45 or 78 rpm, 0.125 + 0.25 abs(saw) while the turn's phase is
|
|
47894
|
-
* within 0.
|
|
48355
|
+
* within 0.014 % of its wrap. RND adds a second train whose phase is offset by twice the hum's (pseudo-random
|
|
47895
48356
|
* times).
|
|
47896
48357
|
* - Hum: a unipolar 50 or 60 Hz square, 0 or 1/16 (it carries DC, on the hardware too).
|
|
47897
48358
|
*
|
|
@@ -48239,7 +48700,7 @@ var GSLoFi2 = class extends GSEffect {
|
|
|
48239
48700
|
this.chainR = new LoFiShaperChain(sampleRate);
|
|
48240
48701
|
this.noise = new NoiseGenerator(sampleRate);
|
|
48241
48702
|
this.radioNoise = new FirstOrderFilter(sampleRate);
|
|
48242
|
-
this.radioNoise.setDamp(
|
|
48703
|
+
this.radioNoise.setDamp(dampCorner(.5));
|
|
48243
48704
|
this.shelvesL = new ShelfEQ(sampleRate);
|
|
48244
48705
|
this.shelvesR = new ShelfEQ(sampleRate);
|
|
48245
48706
|
this.pan = new MonoPan(sampleRate);
|
|
@@ -50683,8 +51144,9 @@ const MULTI_WAH_GAIN = 3.937744140625;
|
|
|
50683
51144
|
const DRIVE_INPUT = 128 / 127;
|
|
50684
51145
|
const DRIVE_OUTPUT = 127 / 128;
|
|
50685
51146
|
/**
|
|
50686
|
-
* The compressor of the multis (parameters 1-4: sustain, attack, level, switch
|
|
50687
|
-
* its post gain at +6 dB and a level after it. The
|
|
51147
|
+
* The compressor of the multis (parameters 1-4: sustain, attack, level, switch, the manuals swap the first two
|
|
51148
|
+
* names, see `GSCompressor`): the Compressor's dynamics with its post gain at +6 dB and a level after it. The
|
|
51149
|
+
* compressor runs when it is switched off too.
|
|
50688
51150
|
* @internal
|
|
50689
51151
|
*/
|
|
50690
51152
|
var MultiCompressor = class {
|
|
@@ -51466,10 +51928,10 @@ var GSDriveDelayMulti = class extends GSMulti {
|
|
|
51466
51928
|
/**
|
|
51467
51929
|
* GTR Multi 1 (`04 00`): `L+R -> compressor -> overdrive/distortion -> 2-band EQ -> amp simulator -> chorus/flanger
|
|
51468
51930
|
* -> delay`.
|
|
51469
|
-
* Parameters: 1-4 compressor sustain, attack
|
|
51470
|
-
* switch,
|
|
51471
|
-
*
|
|
51472
|
-
* EFX control source 1 moves the drive, source 2 the delay level.
|
|
51931
|
+
* Parameters: 1-4 compressor sustain, attack (the manuals swap these two names, see `GSCompressor`), level and
|
|
51932
|
+
* switch, 5 drive type (OD, DS), 6 drive, 7 amp type, 8 amp switch, 9 low gain, 10 high gain, 11 drive switch,
|
|
51933
|
+
* 12 chorus or flanger, 13 rate (Rate2 column), 14 depth, 15 flanger feedback, 16 chorus level, 17 delay time (Dly4
|
|
51934
|
+
* column), 18 feedback, 19 delay level, 20 level. EFX control source 1 moves the drive, source 2 the delay level.
|
|
51473
51935
|
* @internal
|
|
51474
51936
|
*/
|
|
51475
51937
|
var GSGuitarMulti1 = class extends GSDriveDelayMulti {
|
|
@@ -51644,10 +52106,10 @@ var GSDriveEQMulti = class extends GSMulti {
|
|
|
51644
52106
|
};
|
|
51645
52107
|
/**
|
|
51646
52108
|
* GTR Multi 2 (`04 01`): `L+R -> compressor -> overdrive/distortion -> amp simulator -> 3-band EQ -> chorus/flanger`.
|
|
51647
|
-
* Parameters: 1-4 compressor sustain, attack
|
|
51648
|
-
* switch,
|
|
51649
|
-
*
|
|
51650
|
-
* source 2 the chorus level.
|
|
52109
|
+
* Parameters: 1-4 compressor sustain, attack (the manuals swap these two names, see `GSCompressor`), level and
|
|
52110
|
+
* switch, 5 drive type (OD, DS), 6 drive, 7 amp type, 8 amp switch, 9 drive switch, 10 low gain, 11-13 mid-frequency,
|
|
52111
|
+
* Q and gain, 14 high gain, 15 chorus or flanger, 16 rate (Rate2 column), 17 depth, 18 flanger feedback,
|
|
52112
|
+
* 19 chorus level, 20 level. EFX control source 1 moves the drive, source 2 the chorus level.
|
|
51651
52113
|
* @internal
|
|
51652
52114
|
*/
|
|
51653
52115
|
var GSGuitarMulti2 = class extends GSDriveEQMulti {
|
|
@@ -51755,10 +52217,10 @@ var BassAmpSimulator = class {
|
|
|
51755
52217
|
/**
|
|
51756
52218
|
* Bass Multi (`04 05`): `L+R -> compressor -> bass overdrive/distortion -> bass amp simulator -> 3-band EQ ->
|
|
51757
52219
|
* chorus/flanger`.
|
|
51758
|
-
* Parameters: 1-4 compressor sustain, attack
|
|
51759
|
-
*
|
|
51760
|
-
* 16 rate (Rate2 column), 17 depth, 18 flanger feedback,
|
|
51761
|
-
* drive, source 2 the chorus level.
|
|
52220
|
+
* Parameters: 1-4 compressor sustain, attack (the manuals swap these two names, see `GSCompressor`), level and
|
|
52221
|
+
* switch, 5 drive type (OD, DS), 6 drive, 7 amp type (0-2), 8 amp switch, 9 drive switch, 10 low gain, 11-13 mid- * frequency,
|
|
52222
|
+
* Q and gain, 14 high gain, 15 chorus or flanger, 16 rate (Rate2 column), 17 depth, 18 flanger feedback,
|
|
52223
|
+
* 19 chorus level, 20 level. EFX control source 1 moves the drive, source 2 the chorus level.
|
|
51762
52224
|
* @internal
|
|
51763
52225
|
*/
|
|
51764
52226
|
var GSBassMulti = class extends GSDriveEQMulti {
|
|
@@ -51812,10 +52274,10 @@ var GSBassMulti = class extends GSDriveEQMulti {
|
|
|
51812
52274
|
const LINEAR_INPUT = 127 / 128;
|
|
51813
52275
|
/**
|
|
51814
52276
|
* Clean GTR Multi 1 (`04 03`): `L+R -> compressor -> 3-band EQ -> chorus/flanger -> delay`.
|
|
51815
|
-
* Parameters: 1-4 compressor sustain, attack
|
|
51816
|
-
*
|
|
51817
|
-
*
|
|
51818
|
-
* source 2 the delay level.
|
|
52277
|
+
* Parameters: 1-4 compressor sustain, attack (the manuals swap these two names, see `GSCompressor`), level and
|
|
52278
|
+
* switch, 5 low gain, 6-8 mid-frequency, Q and gain, 9 high gain, 10 chorus or flanger, 11 rate (Rate2 column),
|
|
52279
|
+
* 12 depth, 13 flanger feedback, 14 chorus level, 15 delay time (Dly4 column), 16 feedback, 17 HF damp, 18 delay
|
|
52280
|
+
* level, 20 level. EFX control source 1 moves the chorus level, source 2 the delay level.
|
|
51819
52281
|
* @internal
|
|
51820
52282
|
*/
|
|
51821
52283
|
var GSCleanGuitarMulti1 = class extends GSMulti {
|
|
@@ -51889,7 +52351,7 @@ var GSCleanGuitarMulti1 = class extends GSMulti {
|
|
|
51889
52351
|
* Clean GTR Multi 2 (`04 04`): `L+R -> wah -> 3-band EQ -> chorus/flanger -> delay`. The wah is the Auto Wah's without
|
|
51890
52352
|
* the envelope follower, its LFO raises the cutoff (polarity up).
|
|
51891
52353
|
* Parameters: 1 wah filter type (LPF, BPF), 2 manual, 3 peak, 4 rate (Rate1 column), 5 depth, 6 wah switch (SC-VA's
|
|
51892
|
-
* code calls it the EQ switch, but it bypasses the wah), 7 low gain, 8-10 mid
|
|
52354
|
+
* code calls it the EQ switch, but it bypasses the wah), 7 low gain, 8-10 mid-frequency, Q and gain, 11 high gain,
|
|
51893
52355
|
* 12 chorus or flanger, 13 rate (Rate2 column), 14 depth, 15 flanger feedback, 16 chorus level, 17 delay time (Dly4
|
|
51894
52356
|
* column), 18 feedback, 19 delay level, 20 level. EFX control source 1 moves the wah manual, source 2 the delay
|
|
51895
52357
|
* level.
|
|
@@ -52107,9 +52569,11 @@ const DEFAULT_CONTROL_DEPTH = 64;
|
|
|
52107
52569
|
/**
|
|
52108
52570
|
* The offset an EFX control source adds to its parameter, like Sound Canvas VA computes it:
|
|
52109
52571
|
* `(depth - 64) * value / 63`, truncated and stored in a signed byte. Depth 0 counts as 1 (both are -100%).
|
|
52572
|
+
* The byte wraps without a clamp, like in SCCore.dll: pitch bend fully down (-128) at depth 0 or 1 gives +128,
|
|
52573
|
+
* which is stored as -128, so the offset flips sign there.
|
|
52110
52574
|
* @param depth The control depth (0-127).
|
|
52111
52575
|
* @param value The signed controller value.
|
|
52112
|
-
* @returns The signed offset.
|
|
52576
|
+
* @returns The signed offset (-128 to 127).
|
|
52113
52577
|
*/
|
|
52114
52578
|
function controlOffset(depth, value) {
|
|
52115
52579
|
const amount = (depth === 0 ? 1 : depth) - 64;
|
|
@@ -52125,6 +52589,7 @@ var DefaultGSInsertion = class {
|
|
|
52125
52589
|
sendLevelToReverb = DEFAULT_SEND_LEVEL_TO_REVERB;
|
|
52126
52590
|
sendLevelToChorus = 0;
|
|
52127
52591
|
sendLevelToDelay = 0;
|
|
52592
|
+
sendEQSwitch = true;
|
|
52128
52593
|
controlSource1 = GSInsertionControlSources.off;
|
|
52129
52594
|
controlDepth1 = DEFAULT_CONTROL_DEPTH;
|
|
52130
52595
|
controlSource2 = GSInsertionControlSources.off;
|
|
@@ -52167,7 +52632,7 @@ var DefaultGSInsertion = class {
|
|
|
52167
52632
|
* Constructs a new default GS insertion block.
|
|
52168
52633
|
* @param sampleRate The sample rate, in Hertz.
|
|
52169
52634
|
* @param maxBufferSize The maximum buffer size the synthesizer can render at once.
|
|
52170
|
-
*
|
|
52635
|
+
* Processing more samples than this is not supported and produces undefined output.
|
|
52171
52636
|
*/
|
|
52172
52637
|
constructor(sampleRate, maxBufferSize) {
|
|
52173
52638
|
const memory = new DelayMemory();
|
|
@@ -52194,6 +52659,7 @@ var DefaultGSInsertion = class {
|
|
|
52194
52659
|
this.sendLevelToReverb = DEFAULT_SEND_LEVEL_TO_REVERB;
|
|
52195
52660
|
this.sendLevelToChorus = 0;
|
|
52196
52661
|
this.sendLevelToDelay = 0;
|
|
52662
|
+
this.sendEQSwitch = true;
|
|
52197
52663
|
}
|
|
52198
52664
|
getType() {
|
|
52199
52665
|
return this.type;
|
|
@@ -52250,6 +52716,7 @@ var DefaultGSInsertion = class {
|
|
|
52250
52716
|
sendLevelToReverb: this.sendLevelToReverb,
|
|
52251
52717
|
sendLevelToChorus: this.sendLevelToChorus,
|
|
52252
52718
|
sendLevelToDelay: this.sendLevelToDelay,
|
|
52719
|
+
sendEQSwitch: this.sendEQSwitch,
|
|
52253
52720
|
controlSource1: this.controlSource1,
|
|
52254
52721
|
controlDepth1: this.controlDepth1,
|
|
52255
52722
|
controlSource2: this.controlSource2,
|
|
@@ -52270,6 +52737,7 @@ var DefaultGSInsertion = class {
|
|
|
52270
52737
|
this.sendLevelToReverb = snapshot.sendLevelToReverb;
|
|
52271
52738
|
this.sendLevelToChorus = snapshot.sendLevelToChorus;
|
|
52272
52739
|
this.sendLevelToDelay = snapshot.sendLevelToDelay;
|
|
52740
|
+
this.sendEQSwitch = snapshot.sendEQSwitch ?? true;
|
|
52273
52741
|
}
|
|
52274
52742
|
/**
|
|
52275
52743
|
* Sends the offsets of both control sources to the processor.
|
|
@@ -52532,8 +53000,9 @@ var SoundBankManager = class {
|
|
|
52532
53000
|
const bankOffset = s.bankOffset;
|
|
52533
53001
|
for (const p of bank.presets) {
|
|
52534
53002
|
const selectablePreset = new SoundBankManagerPreset(p, bankOffset);
|
|
52535
|
-
|
|
52536
|
-
|
|
53003
|
+
const key = selectablePreset.toMIDIString();
|
|
53004
|
+
if (!addedPresets.has(key)) {
|
|
53005
|
+
addedPresets.add(key);
|
|
52537
53006
|
presetList.push(selectablePreset);
|
|
52538
53007
|
}
|
|
52539
53008
|
}
|
|
@@ -52624,12 +53093,13 @@ for (let pan = MIN_PAN; pan <= MAX_PAN; pan++) {
|
|
|
52624
53093
|
}
|
|
52625
53094
|
/**
|
|
52626
53095
|
* Computes the LFO value for the given phase.
|
|
52627
|
-
*
|
|
53096
|
+
* All waveforms are at zero and rising at the default start phase of 0.25.
|
|
52628
53097
|
* @param phase the LFO phase, from 0 to 1.
|
|
52629
|
-
* @param waveform 0 for triangle, 1 for sawtooth.
|
|
53098
|
+
* @param waveform 0 for triangle, 1 for sawtooth, 2 for sine.
|
|
52630
53099
|
* @returns the LFO value, from -1 to 1.
|
|
52631
53100
|
*/
|
|
52632
53101
|
function getLfoValue(phase, waveform) {
|
|
53102
|
+
if (waveform === 2) return Math.sin(TWO_PI * (phase - .25));
|
|
52633
53103
|
if (waveform === 1) {
|
|
52634
53104
|
const p = phase + .25;
|
|
52635
53105
|
return 2 * (p >= 1 ? p - 1 : p) - 1;
|
|
@@ -52637,10 +53107,11 @@ function getLfoValue(phase, waveform) {
|
|
|
52637
53107
|
return 1 - 4 * Math.abs(phase - .5);
|
|
52638
53108
|
}
|
|
52639
53109
|
/**
|
|
52640
|
-
* Renders a voice
|
|
53110
|
+
* Renders a voice into one of the channel's voice send buffers,
|
|
53111
|
+
* which are later mixed into the stereo output and the effect sends.
|
|
52641
53112
|
* @param voice the voice to render
|
|
52642
53113
|
* @param timeNow current time in seconds
|
|
52643
|
-
* @param sampleCount the
|
|
53114
|
+
* @param sampleCount the number of samples to render (the buffers are 0-based, so no offset is needed)
|
|
52644
53115
|
*/
|
|
52645
53116
|
function renderVoice(voice, timeNow, sampleCount) {
|
|
52646
53117
|
if (timeNow < voice.startTime) {
|
|
@@ -52902,9 +53373,9 @@ var VoiceSendBuffer = class {
|
|
|
52902
53373
|
};
|
|
52903
53374
|
/**
|
|
52904
53375
|
* Mixes a voice send buffer into the channel output and the effect sends, then clears it.
|
|
52905
|
-
* @param channel
|
|
52906
|
-
* @param buffer
|
|
52907
|
-
* @param sampleCount
|
|
53376
|
+
* @param channel The channel that owns the buffer.
|
|
53377
|
+
* @param buffer The voice send buffer to mix.
|
|
53378
|
+
* @param sampleCount The number of samples to mix.
|
|
52908
53379
|
*/
|
|
52909
53380
|
function mixVoiceSendBuffer(channel, buffer, sampleCount) {
|
|
52910
53381
|
const core = channel.synthCore;
|
|
@@ -53203,7 +53674,7 @@ function handleAWE32NRPN(paramLSB, dataValue) {
|
|
|
53203
53674
|
}
|
|
53204
53675
|
// src/synthesizer/audio_engine/channel/data_entry.ts
|
|
53205
53676
|
/**
|
|
53206
|
-
* Executes a data entry
|
|
53677
|
+
* Executes a data entry change for the current channel.
|
|
53207
53678
|
*/
|
|
53208
53679
|
function dataEntry() {
|
|
53209
53680
|
const dataValue = this._midiControllers[MIDIControllers.dataEntryMSB];
|
|
@@ -53342,7 +53813,7 @@ function dataEntry() {
|
|
|
53342
53813
|
break;
|
|
53343
53814
|
case NonRegisteredParameterTypesMSB.drumVariation:
|
|
53344
53815
|
this.drumParams[parameterFine].variationSend = dataCoarse;
|
|
53345
|
-
SpessaLog.coolInfo(`Drum ${parameterFine} Variation Send for ${this.channel}`,
|
|
53816
|
+
SpessaLog.coolInfo(`Drum ${parameterFine} Variation Send for ${this.channel}`, dataCoarse, "");
|
|
53346
53817
|
break;
|
|
53347
53818
|
case NonRegisteredParameterTypesMSB.awe32:
|
|
53348
53819
|
handleAWE32NRPN.call(this, parameterFine, dataValue);
|
|
@@ -53675,7 +54146,7 @@ function noteOn(midiNote, velocity, emit = true) {
|
|
|
53675
54146
|
// src/synthesizer/audio_engine/channel/note_off.ts
|
|
53676
54147
|
/**
|
|
53677
54148
|
* Releases a note by its MIDI note number.
|
|
53678
|
-
* If the
|
|
54149
|
+
* If the synthesizer is in high performance mode and the channel is not a drum channel,
|
|
53679
54150
|
* or the drum channel has an rxNoteOff enabled,
|
|
53680
54151
|
* it kills the note instead of releasing it.
|
|
53681
54152
|
* @param midiNote The MIDI note number to release (0-127).
|
|
@@ -53852,7 +54323,7 @@ var DynamicModulatorManager = class {
|
|
|
53852
54323
|
case 5:
|
|
53853
54324
|
this.setModulator(source, isCC, GeneratorTypes.vibLfoToVolume, normalizedNotCentered * 200, bipolar, false, true);
|
|
53854
54325
|
this.setModulator(source, isCC, GeneratorTypes.initialAttenuation, normalizedNotCentered * 200, bipolar, false, true);
|
|
53855
|
-
SpessaLog.xgInfo(`Channel ${this.channel} ${sourceName} LFO amplitude depth control`, normalizedNotCentered * 200, "
|
|
54326
|
+
SpessaLog.xgInfo(`Channel ${this.channel} ${sourceName} LFO amplitude depth control`, normalizedNotCentered * 200, "cB");
|
|
53856
54327
|
}
|
|
53857
54328
|
}
|
|
53858
54329
|
setupReceiverGS(addr3, data, source, isCC, sourceName, bipolar = false) {
|
|
@@ -54099,6 +54570,7 @@ const DEFAULT_CHANNEL_MIDI_PARAMETERS = {
|
|
|
54099
54570
|
randomPan: false,
|
|
54100
54571
|
assignMode: 2,
|
|
54101
54572
|
efxAssign: false,
|
|
54573
|
+
eqEnabled: true,
|
|
54102
54574
|
cc1: 16,
|
|
54103
54575
|
cc2: 17,
|
|
54104
54576
|
drumMap: 0,
|
|
@@ -54259,6 +54731,7 @@ var MIDIChannel = class {
|
|
|
54259
54731
|
/**
|
|
54260
54732
|
* If true, this skips sends as they all go through insertion.
|
|
54261
54733
|
* Set by {@link SpessaSynthProcessor}.
|
|
54734
|
+
* @internal
|
|
54262
54735
|
*/
|
|
54263
54736
|
xgInsertionAssigned = false;
|
|
54264
54737
|
/**
|
|
@@ -54333,8 +54806,8 @@ var MIDIChannel = class {
|
|
|
54333
54806
|
*/
|
|
54334
54807
|
resetRP15 = resetRP15.bind(this);
|
|
54335
54808
|
/**
|
|
54336
|
-
* Executes a data entry
|
|
54337
|
-
*
|
|
54809
|
+
* Executes a data entry (MSB or LSB) change for the current channel.
|
|
54810
|
+
* The value is read from the controller table and applied to the selected RPN or NRPN.
|
|
54338
54811
|
* @internal
|
|
54339
54812
|
*/
|
|
54340
54813
|
dataEntry = dataEntry.bind(this);
|
|
@@ -54589,7 +55062,7 @@ var MIDIChannel = class {
|
|
|
54589
55062
|
noteOn(midiNote, velocity, emit = true) {}
|
|
54590
55063
|
/**
|
|
54591
55064
|
* Releases a note by its MIDI note number.
|
|
54592
|
-
* If the
|
|
55065
|
+
* If the synthesizer is in high performance mode and the channel is not a drum channel,
|
|
54593
55066
|
* or the drum channel has an rxNoteOff enabled,
|
|
54594
55067
|
* it kills the note instead of releasing it.
|
|
54595
55068
|
* @param midiNote The MIDI note number to release (0-127).
|
|
@@ -54618,14 +55091,16 @@ var MIDIChannel = class {
|
|
|
54618
55091
|
/**
|
|
54619
55092
|
* Reset this channel to its default state.
|
|
54620
55093
|
* Except for the locked controllers.
|
|
55094
|
+
* @param sendEvent If controller change events should be emitted for the reset controllers.
|
|
54621
55095
|
* @internal
|
|
54622
55096
|
*/
|
|
54623
55097
|
reset(sendEvent = true) {}
|
|
54624
55098
|
/**
|
|
54625
|
-
* Renders a voice
|
|
55099
|
+
* Renders a voice into one of the channel's voice send buffers,
|
|
55100
|
+
* which are later mixed into the stereo output and the effect sends.
|
|
54626
55101
|
* @param voice the voice to render
|
|
54627
55102
|
* @param timeNow current time in seconds
|
|
54628
|
-
* @param sampleCount the
|
|
55103
|
+
* @param sampleCount the number of samples to render (the buffers are 0-based, so no offset is needed)
|
|
54629
55104
|
* @internal
|
|
54630
55105
|
*/
|
|
54631
55106
|
renderVoice(voice, timeNow, sampleCount) {}
|
|
@@ -54970,6 +55445,7 @@ const DEFAULT_GLOBAL_SYSTEM_PARAMETERS = {
|
|
|
54970
55445
|
xgChorusLock: false,
|
|
54971
55446
|
gsDelayGain: 1,
|
|
54972
55447
|
gsDelayLock: false,
|
|
55448
|
+
gsEqualizerLock: false,
|
|
54973
55449
|
xgVariationGain: 1,
|
|
54974
55450
|
xgVariationLock: false,
|
|
54975
55451
|
gsInsertionLock: false,
|
|
@@ -55207,7 +55683,7 @@ var LowpassFilter = class LowpassFilter {
|
|
|
55207
55683
|
const qCb = this.resonanceCb;
|
|
55208
55684
|
let cutoffHz = absCentsToHz(cutoffCents);
|
|
55209
55685
|
cutoffHz = Math.min(cutoffHz, this.maxCutoff);
|
|
55210
|
-
const resonanceGain = cbAttenuationToGain(-(qCb -
|
|
55686
|
+
const resonanceGain = cbAttenuationToGain(-(qCb - 30.1));
|
|
55211
55687
|
const qGain = 1 / Math.sqrt(cbAttenuationToGain(-qCb));
|
|
55212
55688
|
const w = 2 * Math.PI * cutoffHz / this.sampleRate;
|
|
55213
55689
|
const cosw = Math.cos(w);
|
|
@@ -55254,7 +55730,7 @@ var VolumeEnvelope = class {
|
|
|
55254
55730
|
/**
|
|
55255
55731
|
* The sample count between updates of the volume envelope.
|
|
55256
55732
|
* Since the volume envelope calculation runs once per rendering quantum,
|
|
55257
|
-
* this effectively the buffer size.
|
|
55733
|
+
* this is effectively the buffer size.
|
|
55258
55734
|
*/
|
|
55259
55735
|
updateInterval;
|
|
55260
55736
|
/**
|
|
@@ -55262,11 +55738,11 @@ var VolumeEnvelope = class {
|
|
|
55262
55738
|
*/
|
|
55263
55739
|
sampleTime = 0;
|
|
55264
55740
|
/**
|
|
55265
|
-
* The
|
|
55741
|
+
* The cB attenuation of the envelope when it entered the release stage.
|
|
55266
55742
|
*/
|
|
55267
55743
|
releaseStartCb = CB_SILENCE;
|
|
55268
55744
|
/**
|
|
55269
|
-
* The time in samples relative to the start of the envelope.
|
|
55745
|
+
* The time in samples when the release stage started, relative to the start of the envelope.
|
|
55270
55746
|
*/
|
|
55271
55747
|
releaseStartTimeSamples = 0;
|
|
55272
55748
|
/**
|
|
@@ -55405,10 +55881,10 @@ var VolumeEnvelope = class {
|
|
|
55405
55881
|
}
|
|
55406
55882
|
/**
|
|
55407
55883
|
* Calculates the gain value for the last sample in the block and writes it to `outputGain`.
|
|
55408
|
-
* Essentially we use approach of
|
|
55409
|
-
* @param sampleCount the amount of samples to
|
|
55884
|
+
* Essentially we use approach of 96 dB (CB_SILENCE) is silence, 0dB is peak.
|
|
55885
|
+
* @param sampleCount the amount of samples in the block to advance the envelope by.
|
|
55410
55886
|
* @param gainTarget the gain to apply.
|
|
55411
|
-
* @returns if the voice has finished.
|
|
55887
|
+
* @returns if the voice is still active (false once the envelope has finished).
|
|
55412
55888
|
*/
|
|
55413
55889
|
process(sampleCount, gainTarget) {
|
|
55414
55890
|
const { releaseStartTimeSamples, releaseStartCb, releaseDuration, delayEnd, attackEnd, attackDuration, holdEnd, decayEnd, decayDuration, sustainCb, decay2End, decay2Duration, sustain2Cb } = this;
|
|
@@ -55566,7 +56042,7 @@ var ModulationEnvelope = class {
|
|
|
55566
56042
|
* Calculates the current modulation envelope value for the given time and voice.
|
|
55567
56043
|
* @param voice the voice we are working on.
|
|
55568
56044
|
* @param currentTime in seconds.
|
|
55569
|
-
* @returns
|
|
56045
|
+
* @returns mod env value, from 0 to 1.
|
|
55570
56046
|
*/
|
|
55571
56047
|
process(voice, currentTime) {
|
|
55572
56048
|
if (this.enteredRelease) {
|
|
@@ -55839,6 +56315,217 @@ var HermiteOscillator = class extends WavetableOscillator {
|
|
|
55839
56315
|
return true;
|
|
55840
56316
|
}
|
|
55841
56317
|
};
|
|
56318
|
+
// src/synthesizer/audio_engine/voice/wavetable_oscillator/sinc.ts
|
|
56319
|
+
/**
|
|
56320
|
+
* Kernel resolution, the amount of table points per sample of distance.
|
|
56321
|
+
* The table is linearly interpolated, which keeps the error around -120 dB.
|
|
56322
|
+
*/
|
|
56323
|
+
const OVERSAMPLING = 256;
|
|
56324
|
+
/**
|
|
56325
|
+
* The highest playback step the anti-aliased kernel gets stretched to.
|
|
56326
|
+
* The tap count grows with the step, so this keeps extreme pitch ups from becoming too slow.
|
|
56327
|
+
* Above it, the content between the new Nyquist frequency and 1/16 of the old one folds back.
|
|
56328
|
+
*/
|
|
56329
|
+
const MAX_STRETCH = 16;
|
|
56330
|
+
/**
|
|
56331
|
+
* Zeroth order modified Bessel function of the first kind, for the Kaiser window.
|
|
56332
|
+
* @param x
|
|
56333
|
+
*/
|
|
56334
|
+
function besselI0(x) {
|
|
56335
|
+
const q = x * x / 4;
|
|
56336
|
+
let sum = 1;
|
|
56337
|
+
let term = 1;
|
|
56338
|
+
for (let k = 1; k < 100; k++) {
|
|
56339
|
+
term *= q / (k * k);
|
|
56340
|
+
sum += term;
|
|
56341
|
+
if (term < sum * 1e-17) break;
|
|
56342
|
+
}
|
|
56343
|
+
return sum;
|
|
56344
|
+
}
|
|
56345
|
+
/**
|
|
56346
|
+
* Builds a Kaiser windowed sinc kernel.
|
|
56347
|
+
* @param halfWidth Half of the kernel width, in samples. The kernel uses `2 * halfWidth` taps.
|
|
56348
|
+
* @param bandwidth The cutoff (-6 dB point) of the kernel, relative to the Nyquist frequency.
|
|
56349
|
+
* @param beta The Kaiser window beta, controls the stopband attenuation.
|
|
56350
|
+
*/
|
|
56351
|
+
function buildKernel(halfWidth, bandwidth, beta) {
|
|
56352
|
+
const windowNorm = besselI0(beta);
|
|
56353
|
+
const kernel = (x) => {
|
|
56354
|
+
const w = x / halfWidth;
|
|
56355
|
+
if (w <= -1 || w >= 1) return 0;
|
|
56356
|
+
const window = besselI0(beta * Math.sqrt(1 - w * w)) / windowNorm;
|
|
56357
|
+
const u = Math.PI * bandwidth * x;
|
|
56358
|
+
return bandwidth * (u === 0 ? 1 : Math.sin(u) / u) * window;
|
|
56359
|
+
};
|
|
56360
|
+
const taps = halfWidth * 2;
|
|
56361
|
+
const rows = new Float64Array(taps * 257);
|
|
56362
|
+
for (let q = 0; q <= OVERSAMPLING; q++) {
|
|
56363
|
+
const row = q * taps;
|
|
56364
|
+
let sum = 0;
|
|
56365
|
+
for (let k = 0; k < taps; k++) {
|
|
56366
|
+
const v = kernel(k + 1 - halfWidth - q / OVERSAMPLING);
|
|
56367
|
+
rows[row + k] = v;
|
|
56368
|
+
sum += v;
|
|
56369
|
+
}
|
|
56370
|
+
for (let k = 0; k < taps; k++) rows[row + k] /= sum;
|
|
56371
|
+
}
|
|
56372
|
+
const phases = new Float32Array(taps * OVERSAMPLING);
|
|
56373
|
+
const phaseDeltas = new Float32Array(taps * OVERSAMPLING);
|
|
56374
|
+
for (let i = 0; i < phases.length; i++) {
|
|
56375
|
+
phases[i] = rows[i];
|
|
56376
|
+
phaseDeltas[i] = rows[i + taps] - rows[i];
|
|
56377
|
+
}
|
|
56378
|
+
const tableLength = halfWidth * 2 * OVERSAMPLING + 2;
|
|
56379
|
+
const table = new Float32Array(tableLength);
|
|
56380
|
+
const tableDeltas = new Float32Array(tableLength);
|
|
56381
|
+
for (let i = 0; i < tableLength; i++) table[i] = kernel(i / OVERSAMPLING - halfWidth);
|
|
56382
|
+
for (let i = 0; i < tableLength - 1; i++) tableDeltas[i] = table[i + 1] - table[i];
|
|
56383
|
+
return {
|
|
56384
|
+
halfWidth,
|
|
56385
|
+
phases,
|
|
56386
|
+
phaseDeltas,
|
|
56387
|
+
table,
|
|
56388
|
+
tableDeltas
|
|
56389
|
+
};
|
|
56390
|
+
}
|
|
56391
|
+
/**
|
|
56392
|
+
* 32 points, meant for real time playback.
|
|
56393
|
+
*/
|
|
56394
|
+
const KERNEL_FAST = buildKernel(16, .92, 9);
|
|
56395
|
+
/**
|
|
56396
|
+
* 128 points, for offline rendering.
|
|
56397
|
+
*/
|
|
56398
|
+
const KERNEL_BEST = buildKernel(64, .975, 12);
|
|
56399
|
+
var SincOscillator = class extends WavetableOscillator {
|
|
56400
|
+
kernel;
|
|
56401
|
+
/**
|
|
56402
|
+
* If the kernel gets stretched when the playback step is above 1.
|
|
56403
|
+
* This lowers the cutoff to the new Nyquist frequency so the sample content above it does not alias.
|
|
56404
|
+
*/
|
|
56405
|
+
antiAlias;
|
|
56406
|
+
/**
|
|
56407
|
+
* The points around the cursor, gathered here when they need wrapping around the loop
|
|
56408
|
+
* or are outside the sample.
|
|
56409
|
+
*/
|
|
56410
|
+
scratch;
|
|
56411
|
+
/**
|
|
56412
|
+
* Creates a new windowed sinc oscillator.
|
|
56413
|
+
* @param best If true, uses the larger, anti-aliased kernel. It is too slow for real time playback.
|
|
56414
|
+
*/
|
|
56415
|
+
constructor(best) {
|
|
56416
|
+
super();
|
|
56417
|
+
this.kernel = best ? KERNEL_BEST : KERNEL_FAST;
|
|
56418
|
+
this.antiAlias = best;
|
|
56419
|
+
this.scratch = new Float32Array(best ? Math.ceil(this.kernel.halfWidth * MAX_STRETCH) * 2 + 2 : this.kernel.halfWidth * 2);
|
|
56420
|
+
}
|
|
56421
|
+
process(sampleCount, tuningRatio, outputBuffer) {
|
|
56422
|
+
const step = tuningRatio * this.playbackStep;
|
|
56423
|
+
const sampleData = this.sampleData;
|
|
56424
|
+
const scratch = this.scratch;
|
|
56425
|
+
const { loopStart, loopEnd, loopLength, end, isLooping } = this;
|
|
56426
|
+
const { halfWidth } = this.kernel;
|
|
56427
|
+
let cursor = this.cursor;
|
|
56428
|
+
let hasLooped = this.hasLooped;
|
|
56429
|
+
const upperLimit = isLooping ? loopEnd : end;
|
|
56430
|
+
const stretch = this.antiAlias && step > 1 ? Math.min(step, MAX_STRETCH) : 1;
|
|
56431
|
+
if (stretch === 1) {
|
|
56432
|
+
const { phases, phaseDeltas } = this.kernel;
|
|
56433
|
+
const taps = halfWidth * 2;
|
|
56434
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
56435
|
+
if (isLooping) {
|
|
56436
|
+
if (cursor >= loopEnd) {
|
|
56437
|
+
hasLooped = true;
|
|
56438
|
+
while (cursor >= loopEnd) cursor -= loopLength;
|
|
56439
|
+
}
|
|
56440
|
+
} else if (cursor + 1 >= end) {
|
|
56441
|
+
outputBuffer.fill(0, i, sampleCount);
|
|
56442
|
+
this.cursor = cursor;
|
|
56443
|
+
return false;
|
|
56444
|
+
}
|
|
56445
|
+
const y0 = cursor | 0;
|
|
56446
|
+
let first = y0 + 1 - halfWidth;
|
|
56447
|
+
let source = sampleData;
|
|
56448
|
+
if (first < (isLooping && hasLooped ? loopStart : 0) || first + taps > upperLimit) {
|
|
56449
|
+
this.gather(first, taps, hasLooped);
|
|
56450
|
+
source = scratch;
|
|
56451
|
+
first = 0;
|
|
56452
|
+
}
|
|
56453
|
+
const phase = (cursor - y0) * OVERSAMPLING;
|
|
56454
|
+
const q = phase | 0;
|
|
56455
|
+
let row = q * taps;
|
|
56456
|
+
const fraction = phase - q;
|
|
56457
|
+
const rowEnd = row + taps;
|
|
56458
|
+
let out = 0;
|
|
56459
|
+
for (; row < rowEnd; row++) out += source[first++] * (phases[row] + phaseDeltas[row] * fraction);
|
|
56460
|
+
outputBuffer[i] = out;
|
|
56461
|
+
cursor += step;
|
|
56462
|
+
}
|
|
56463
|
+
} else {
|
|
56464
|
+
const { table, tableDeltas } = this.kernel;
|
|
56465
|
+
const reach = halfWidth * stretch;
|
|
56466
|
+
const tableStep = OVERSAMPLING / stretch;
|
|
56467
|
+
const center = halfWidth * OVERSAMPLING;
|
|
56468
|
+
const gain = 1 / stretch;
|
|
56469
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
56470
|
+
if (isLooping) {
|
|
56471
|
+
if (cursor >= loopEnd) {
|
|
56472
|
+
hasLooped = true;
|
|
56473
|
+
while (cursor >= loopEnd) cursor -= loopLength;
|
|
56474
|
+
}
|
|
56475
|
+
} else if (cursor + 1 >= end) {
|
|
56476
|
+
outputBuffer.fill(0, i, sampleCount);
|
|
56477
|
+
this.cursor = cursor;
|
|
56478
|
+
return false;
|
|
56479
|
+
}
|
|
56480
|
+
let first = Math.floor(cursor - reach) + 1;
|
|
56481
|
+
const taps = Math.floor(cursor + reach) - first + 1;
|
|
56482
|
+
let position = (first - cursor) * tableStep + center;
|
|
56483
|
+
let source = sampleData;
|
|
56484
|
+
if (first < (isLooping && hasLooped ? loopStart : 0) || first + taps > upperLimit) {
|
|
56485
|
+
this.gather(first, taps, hasLooped);
|
|
56486
|
+
source = scratch;
|
|
56487
|
+
first = 0;
|
|
56488
|
+
}
|
|
56489
|
+
let out = 0;
|
|
56490
|
+
const last = first + taps;
|
|
56491
|
+
for (; first < last; first++) {
|
|
56492
|
+
const index = position | 0;
|
|
56493
|
+
out += source[first] * (table[index] + tableDeltas[index] * (position - index));
|
|
56494
|
+
position += tableStep;
|
|
56495
|
+
}
|
|
56496
|
+
outputBuffer[i] = out * gain;
|
|
56497
|
+
cursor += step;
|
|
56498
|
+
}
|
|
56499
|
+
}
|
|
56500
|
+
this.cursor = cursor;
|
|
56501
|
+
this.hasLooped = hasLooped;
|
|
56502
|
+
return true;
|
|
56503
|
+
}
|
|
56504
|
+
/**
|
|
56505
|
+
* Copies the points into the scratch buffer, wrapping them around the loop.
|
|
56506
|
+
* Points outside the sample are silent.
|
|
56507
|
+
* @param first The index of the first point.
|
|
56508
|
+
* @param count The amount of points.
|
|
56509
|
+
* @param hasLooped If the cursor has already looped.
|
|
56510
|
+
*/
|
|
56511
|
+
gather(first, count, hasLooped) {
|
|
56512
|
+
const sampleData = this.sampleData;
|
|
56513
|
+
const scratch = this.scratch;
|
|
56514
|
+
const { loopStart, loopEnd, loopLength, end } = this;
|
|
56515
|
+
if (this.isLooping) {
|
|
56516
|
+
let index = first;
|
|
56517
|
+
if (index >= loopEnd) index = loopStart + (index - loopStart) % loopLength;
|
|
56518
|
+
else if (index < loopStart && hasLooped) index = loopEnd - 1 - (loopStart - 1 - index) % loopLength;
|
|
56519
|
+
for (let k = 0; k < count; k++) {
|
|
56520
|
+
scratch[k] = index < 0 ? 0 : sampleData[index];
|
|
56521
|
+
if (++index >= loopEnd) index = loopStart;
|
|
56522
|
+
}
|
|
56523
|
+
} else for (let k = 0; k < count; k++) {
|
|
56524
|
+
const index = first + k;
|
|
56525
|
+
scratch[k] = index >= 0 && index < end ? sampleData[index] : 0;
|
|
56526
|
+
}
|
|
56527
|
+
}
|
|
56528
|
+
};
|
|
55842
56529
|
// src/synthesizer/audio_engine/voice/voice.ts
|
|
55843
56530
|
/**
|
|
55844
56531
|
* Voice.ts
|
|
@@ -55863,7 +56550,9 @@ var Voice = class {
|
|
|
55863
56550
|
oscillators = [
|
|
55864
56551
|
new LinearOscillator(),
|
|
55865
56552
|
new NearestOscillator(),
|
|
55866
|
-
new HermiteOscillator()
|
|
56553
|
+
new HermiteOscillator(),
|
|
56554
|
+
new SincOscillator(false),
|
|
56555
|
+
new SincOscillator(true)
|
|
55867
56556
|
];
|
|
55868
56557
|
/**
|
|
55869
56558
|
* The oscillator currently used by this voice.
|
|
@@ -55950,7 +56639,7 @@ var Voice = class {
|
|
|
55950
56639
|
* This is the effective MIDI note number,
|
|
55951
56640
|
* used to calculate scale tuning and envelope times,
|
|
55952
56641
|
* and can be overridden by generators.
|
|
55953
|
-
* It is also used
|
|
56642
|
+
* It is also used for the octave tuning and as the portamento target.
|
|
55954
56643
|
*/
|
|
55955
56644
|
targetKey = 0;
|
|
55956
56645
|
/**
|
|
@@ -56043,7 +56732,9 @@ var Voice = class {
|
|
|
56043
56732
|
this.filter = new LowpassFilter(sampleRate);
|
|
56044
56733
|
}
|
|
56045
56734
|
/**
|
|
56046
|
-
* Releases the voice as exclusiveClass.
|
|
56735
|
+
* Releases the voice as exclusiveClass, with a nearly instant release time.
|
|
56736
|
+
* @param currentTime the current time in seconds.
|
|
56737
|
+
* @param minExclusiveLength minimum note length in seconds.
|
|
56047
56738
|
*/
|
|
56048
56739
|
exclusiveRelease(currentTime, minExclusiveLength = MIN_EXCLUSIVE_LENGTH) {
|
|
56049
56740
|
this.overrideReleaseVolEnv = EXCLUSIVE_CUTOFF_TIME;
|
|
@@ -56051,8 +56742,9 @@ var Voice = class {
|
|
|
56051
56742
|
this.releaseVoice(currentTime, minExclusiveLength);
|
|
56052
56743
|
}
|
|
56053
56744
|
/**
|
|
56054
|
-
*
|
|
56055
|
-
*
|
|
56745
|
+
* Releases the voice. The release phase starts on the first render at or after the release time,
|
|
56746
|
+
* which is pushed back if the note would be shorter than the minimum length.
|
|
56747
|
+
* @param currentTime the current time in seconds.
|
|
56056
56748
|
* @param minNoteLength minimum note length in seconds
|
|
56057
56749
|
*/
|
|
56058
56750
|
releaseVoice(currentTime, minNoteLength = MIN_NOTE_LENGTH) {
|
|
@@ -56190,7 +56882,7 @@ function lockMIDIParameterInternal(parameter, isLocked) {
|
|
|
56190
56882
|
* @param byte1 The first byte (midi note).
|
|
56191
56883
|
* @param byte2 The second byte (most significant bits).
|
|
56192
56884
|
* @param byte3 The third byte (the least significant bits).
|
|
56193
|
-
* @
|
|
56885
|
+
* @returns The MIDI note with the fraction added (in semitones), or -1 for no change.
|
|
56194
56886
|
*/
|
|
56195
56887
|
function getTuning(byte1, byte2, byte3) {
|
|
56196
56888
|
const midiNote = byte1;
|
|
@@ -56354,8 +57046,8 @@ function universalSystemExclusive(syx, channelOffset = 0) {
|
|
|
56354
57046
|
const program = syx[currentMessageIndex++];
|
|
56355
57047
|
const tuningName = readBinaryString(syx, 16, currentMessageIndex);
|
|
56356
57048
|
currentMessageIndex += 16;
|
|
56357
|
-
if (syx.length <
|
|
56358
|
-
SpessaLog.warn(`The Bulk Tuning Dump is too short! (${syx.length} bytes, at least
|
|
57049
|
+
if (syx.length < 405) {
|
|
57050
|
+
SpessaLog.warn(`The Bulk Tuning Dump is too short! (${syx.length} bytes, at least 405 are expected)`);
|
|
56359
57051
|
return;
|
|
56360
57052
|
}
|
|
56361
57053
|
for (let midiNote = 0; midiNote < 128; midiNote++) this.tunings[program * 128 + midiNote] = getTuning(syx[currentMessageIndex++], syx[currentMessageIndex++], syx[currentMessageIndex++]);
|
|
@@ -56766,6 +57458,27 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
56766
57458
|
}
|
|
56767
57459
|
break;
|
|
56768
57460
|
}
|
|
57461
|
+
if (a2 === 2) {
|
|
57462
|
+
if (this.systemParameters.gsEqualizerLock) return;
|
|
57463
|
+
const parameter = [
|
|
57464
|
+
"lowFrequency",
|
|
57465
|
+
"lowGain",
|
|
57466
|
+
"highFrequency",
|
|
57467
|
+
"highGain"
|
|
57468
|
+
][a3];
|
|
57469
|
+
if (parameter === void 0) {
|
|
57470
|
+
SpessaLog.gsFail("Equalizer", [a3]);
|
|
57471
|
+
return;
|
|
57472
|
+
}
|
|
57473
|
+
this.gsEqualizerProcessor[parameter] = data;
|
|
57474
|
+
SpessaLog.gsInfo(`EQ ${parameter}`, data);
|
|
57475
|
+
this.callEvent("effectChange", {
|
|
57476
|
+
effect: "gsEqualizer",
|
|
57477
|
+
parameter,
|
|
57478
|
+
value: data
|
|
57479
|
+
});
|
|
57480
|
+
return;
|
|
57481
|
+
}
|
|
56769
57482
|
if (a2 === 3) {
|
|
56770
57483
|
if (this.systemParameters.gsInsertionLock) return;
|
|
56771
57484
|
const insertion = this.gsInsertionBlock;
|
|
@@ -56842,6 +57555,17 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
56842
57555
|
});
|
|
56843
57556
|
return;
|
|
56844
57557
|
}
|
|
57558
|
+
case 31: {
|
|
57559
|
+
const on = data !== 0;
|
|
57560
|
+
insertion.sendEQSwitch = on;
|
|
57561
|
+
SpessaLog.gsInfo("EFX Send EQ Switch", on ? "ON" : "OFF");
|
|
57562
|
+
this.callEvent("effectChange", {
|
|
57563
|
+
effect: "gsInsertion",
|
|
57564
|
+
parameter: "sendEQSwitch",
|
|
57565
|
+
value: on
|
|
57566
|
+
});
|
|
57567
|
+
return;
|
|
57568
|
+
}
|
|
56845
57569
|
}
|
|
56846
57570
|
}
|
|
56847
57571
|
if (a2 >> 4 === 1) {
|
|
@@ -57028,6 +57752,12 @@ function rolandSystemExclusive(syx, channelOffset = 0) {
|
|
|
57028
57752
|
case 1:
|
|
57029
57753
|
ch.controllerChange(MIDIControllers.bankSelectLSB, data);
|
|
57030
57754
|
break;
|
|
57755
|
+
case 32: {
|
|
57756
|
+
const eq = data === 1;
|
|
57757
|
+
ch.setMIDIParameter("eqEnabled", eq);
|
|
57758
|
+
SpessaLog.gsInfo(`EQ for ${channel}`, eq ? "ON" : "OFF");
|
|
57759
|
+
break;
|
|
57760
|
+
}
|
|
57031
57761
|
case 34: {
|
|
57032
57762
|
const efx = data === 1;
|
|
57033
57763
|
ch.setMIDIParameter("efxAssign", efx);
|
|
@@ -57799,8 +58529,8 @@ function yamahaSystemExclusive(syx, channelOffset = 0) {
|
|
|
57799
58529
|
for (const ch of this.midiChannels) {
|
|
57800
58530
|
if (ch.midiParameters.drumMap !== setupNumber) continue;
|
|
57801
58531
|
ch.drumParams[drumKey].pitchFine = pitch;
|
|
57802
|
-
SpessaLog.xgInfo(`Drum Pitch Fine for key ${drumKey}`, pitch, "semitones");
|
|
57803
58532
|
}
|
|
58533
|
+
SpessaLog.xgInfo(`Drum Pitch Fine for key ${drumKey}`, pitch, "cents");
|
|
57804
58534
|
break;
|
|
57805
58535
|
}
|
|
57806
58536
|
case 2:
|
|
@@ -57933,7 +58663,7 @@ function applyPresetZones(indexes, presetGens, presetMods, instruments, presets)
|
|
|
57933
58663
|
}
|
|
57934
58664
|
// src/soundbank/soundfont/read/presets.ts
|
|
57935
58665
|
/**
|
|
57936
|
-
* Parses soundfont presets
|
|
58666
|
+
* Parses soundfont presets
|
|
57937
58667
|
*/
|
|
57938
58668
|
var SoundFontPreset = class extends BasicPreset {
|
|
57939
58669
|
zoneStartIndex;
|
|
@@ -58030,7 +58760,7 @@ function readInstruments(instrumentChunk, keepTerminal = false) {
|
|
|
58030
58760
|
}
|
|
58031
58761
|
// src/soundbank/soundfont/read/modulators.ts
|
|
58032
58762
|
/**
|
|
58033
|
-
* Reads the
|
|
58763
|
+
* Reads the modulators
|
|
58034
58764
|
* @param modulatorChunk pmod, imod or DMOD
|
|
58035
58765
|
* @param allowSFEGenerators if destinations above endOper are allowed (SFE banks)
|
|
58036
58766
|
*/
|
|
@@ -58375,11 +59105,16 @@ function applySnapshot(snapshot) {
|
|
|
58375
59105
|
this.gsReverbProcessor.applySnapshot(snapshot.gsReverbProcessor);
|
|
58376
59106
|
this.gsChorusProcessor.applySnapshot(snapshot.gsChorusProcessor);
|
|
58377
59107
|
this.gsDelayProcessor.applySnapshot(snapshot.gsDelayProcessor);
|
|
59108
|
+
if (snapshot.gsEqualizerProcessor) this.gsEqualizerProcessor.applySnapshot(snapshot.gsEqualizerProcessor);
|
|
58378
59109
|
this.gsInsertionBlock.applySnapshot(snapshot.gsInsertionBlock);
|
|
58379
59110
|
this.xgReverbBlock.applySnapshot(snapshot.xgReverbBlock);
|
|
58380
59111
|
this.xgChorusBlock.applySnapshot(snapshot.xgChorusBlock);
|
|
58381
59112
|
this.xgVariationBlock.applySnapshot(snapshot.xgVariationBlock);
|
|
58382
|
-
for (let i = 0; i < this.xgInsertionBlocks.length; i++)
|
|
59113
|
+
for (let i = 0; i < this.xgInsertionBlocks.length; i++) {
|
|
59114
|
+
const insertion = this.xgInsertionBlocks[i];
|
|
59115
|
+
if (i < snapshot.xgInsertionBlocks.length) insertion.applySnapshot(snapshot.xgInsertionBlocks[i]);
|
|
59116
|
+
else insertion.reset();
|
|
59117
|
+
}
|
|
58383
59118
|
for (let drumSet = 0; drumSet < snapshot.userDrumSets.length; drumSet++) {
|
|
58384
59119
|
const userDrumSet = snapshot.userDrumSets[drumSet];
|
|
58385
59120
|
for (let midiNote = 0; midiNote < userDrumSet.length; midiNote++) DrumParameterUtils.copyIntoUser(userDrumSet[midiNote], this.soundBankManager.userDrumSets[drumSet].keyParams[midiNote]);
|
|
@@ -58399,6 +59134,7 @@ function getSynthesizerSnapshot() {
|
|
|
58399
59134
|
gsReverbProcessor: this.gsReverbProcessor.getSnapshot(),
|
|
58400
59135
|
gsChorusProcessor: this.gsChorusProcessor.getSnapshot(),
|
|
58401
59136
|
gsDelayProcessor: this.gsDelayProcessor.getSnapshot(),
|
|
59137
|
+
gsEqualizerProcessor: this.gsEqualizerProcessor.getSnapshot(),
|
|
58402
59138
|
gsInsertionBlock: this.gsInsertionBlock.getSnapshot(),
|
|
58403
59139
|
xgReverbBlock: this.xgReverbBlock.getSnapshot(),
|
|
58404
59140
|
xgChorusBlock: this.xgChorusBlock.getSnapshot(),
|
|
@@ -58411,7 +59147,8 @@ function getSynthesizerSnapshot() {
|
|
|
58411
59147
|
/**
|
|
58412
59148
|
* Keeps the decoded sample data under a size budget by releasing the least recently used samples.
|
|
58413
59149
|
* A released sample decodes again the next time it plays.
|
|
58414
|
-
* Samples that cannot decode again (replaced float data) are never released
|
|
59150
|
+
* Samples that cannot decode again (replaced float data) are never released
|
|
59151
|
+
* and are not tracked, so they do not count towards the budget.
|
|
58415
59152
|
* @internal
|
|
58416
59153
|
*/
|
|
58417
59154
|
var DecodedSampleCache = class {
|
|
@@ -58424,6 +59161,7 @@ var DecodedSampleCache = class {
|
|
|
58424
59161
|
*/
|
|
58425
59162
|
totalBytes = 0;
|
|
58426
59163
|
entries = /* @__PURE__ */ new Map();
|
|
59164
|
+
unreleasable = /* @__PURE__ */ new WeakSet();
|
|
58427
59165
|
voiceCache;
|
|
58428
59166
|
/**
|
|
58429
59167
|
* @param budget The budget in bytes.
|
|
@@ -58444,7 +59182,11 @@ var DecodedSampleCache = class {
|
|
|
58444
59182
|
touch(sample, cacheKey) {
|
|
58445
59183
|
let entry = this.entries.get(sample);
|
|
58446
59184
|
if (entry) this.entries.delete(sample);
|
|
58447
|
-
|
|
59185
|
+
if (this.unreleasable.has(sample) || sample.storedForm === "float32" && !(sample instanceof DLSSample)) {
|
|
59186
|
+
if (entry) this.totalBytes -= entry.bytes;
|
|
59187
|
+
return;
|
|
59188
|
+
}
|
|
59189
|
+
if (!entry) {
|
|
58448
59190
|
entry = {
|
|
58449
59191
|
bytes: sample.getAudioData().byteLength,
|
|
58450
59192
|
cacheKeys: /* @__PURE__ */ new Set()
|
|
@@ -58461,7 +59203,10 @@ var DecodedSampleCache = class {
|
|
|
58461
59203
|
if (sample === keep) continue;
|
|
58462
59204
|
this.entries.delete(sample);
|
|
58463
59205
|
this.totalBytes -= entry.bytes;
|
|
58464
|
-
sample.releaseDecoded()
|
|
59206
|
+
if (!sample.releaseDecoded()) {
|
|
59207
|
+
this.unreleasable.add(sample);
|
|
59208
|
+
continue;
|
|
59209
|
+
}
|
|
58465
59210
|
for (const key of entry.cacheKeys) this.voiceCache.delete(key);
|
|
58466
59211
|
}
|
|
58467
59212
|
}
|
|
@@ -58504,6 +59249,16 @@ const GS_CHORUS_MACRO_PARAMETERS = [
|
|
|
58504
59249
|
"sendLevelToDelay"
|
|
58505
59250
|
];
|
|
58506
59251
|
/**
|
|
59252
|
+
* The default GS equalizer parameters (SC-8850 manual p.236).
|
|
59253
|
+
*/
|
|
59254
|
+
const DEFAULT_GS_EQUALIZER = {
|
|
59255
|
+
lowFrequency: 0,
|
|
59256
|
+
lowGain: 64,
|
|
59257
|
+
highFrequency: 0,
|
|
59258
|
+
highGain: 64
|
|
59259
|
+
};
|
|
59260
|
+
const GS_EQUALIZER_PARAMETERS = Object.keys(DEFAULT_GS_EQUALIZER);
|
|
59261
|
+
/**
|
|
58507
59262
|
* The GS delay parameters set by the delay macro.
|
|
58508
59263
|
*/
|
|
58509
59264
|
const GS_DELAY_MACRO_PARAMETERS = [
|
|
@@ -58596,7 +59351,7 @@ var SpessaSynthProcessor = class {
|
|
|
58596
59351
|
*/
|
|
58597
59352
|
sharedVoiceSendBuffer;
|
|
58598
59353
|
/**
|
|
58599
|
-
* The insertion processor's left input buffer.
|
|
59354
|
+
* The GS insertion processor's left input buffer.
|
|
58600
59355
|
* @internal
|
|
58601
59356
|
*/
|
|
58602
59357
|
insertionInputL;
|
|
@@ -58606,6 +59361,16 @@ var SpessaSynthProcessor = class {
|
|
|
58606
59361
|
*/
|
|
58607
59362
|
insertionInputR;
|
|
58608
59363
|
/**
|
|
59364
|
+
* The GS equalizer's left input buffer.
|
|
59365
|
+
* @internal
|
|
59366
|
+
*/
|
|
59367
|
+
equalizerInputL;
|
|
59368
|
+
/**
|
|
59369
|
+
* The GS equalizer's right input buffer.
|
|
59370
|
+
* @internal
|
|
59371
|
+
*/
|
|
59372
|
+
equalizerInputR;
|
|
59373
|
+
/**
|
|
58609
59374
|
* The GS reverb processor's input buffer.
|
|
58610
59375
|
* @internal
|
|
58611
59376
|
*/
|
|
@@ -58801,11 +59566,6 @@ var SpessaSynthProcessor = class {
|
|
|
58801
59566
|
*/
|
|
58802
59567
|
cachedVoices = /* @__PURE__ */ new Map();
|
|
58803
59568
|
/**
|
|
58804
|
-
* Keeps the decoded sample data under the budget from the options.
|
|
58805
|
-
* @internal
|
|
58806
|
-
*/
|
|
58807
|
-
decodedSamples;
|
|
58808
|
-
/**
|
|
58809
59569
|
* Insertion is not used outside SC-88Pro+ MIDIs, this is an optimization.
|
|
58810
59570
|
* @internal
|
|
58811
59571
|
*/
|
|
@@ -58828,6 +59588,11 @@ var SpessaSynthProcessor = class {
|
|
|
58828
59588
|
*/
|
|
58829
59589
|
gsInsertionBlock;
|
|
58830
59590
|
/**
|
|
59591
|
+
* Keeps the decoded sample data under the budget from the options.
|
|
59592
|
+
* @internal
|
|
59593
|
+
*/
|
|
59594
|
+
decodedSamples;
|
|
59595
|
+
/**
|
|
58831
59596
|
* The synthesizer's GS reverb processor.
|
|
58832
59597
|
*
|
|
58833
59598
|
* Used when {@link GlobalMIDIParameter.system} is `gm` `gm2` or `gs`.
|
|
@@ -58849,6 +59614,13 @@ var SpessaSynthProcessor = class {
|
|
|
58849
59614
|
*/
|
|
58850
59615
|
gsDelayProcessor;
|
|
58851
59616
|
/**
|
|
59617
|
+
* The synthesizer's GS equalizer processor.
|
|
59618
|
+
*
|
|
59619
|
+
* Used when {@link GlobalMIDIParameter.system} is `gm` `gm2` or `gs`.
|
|
59620
|
+
* @internal
|
|
59621
|
+
*/
|
|
59622
|
+
gsEqualizerProcessor;
|
|
59623
|
+
/**
|
|
58852
59624
|
* The synthesizer's XG reverb block.
|
|
58853
59625
|
*
|
|
58854
59626
|
* Used when {@link GlobalMIDIParameter.system} is `xg`.
|
|
@@ -58856,7 +59628,7 @@ var SpessaSynthProcessor = class {
|
|
|
58856
59628
|
*/
|
|
58857
59629
|
xgReverbBlock;
|
|
58858
59630
|
/**
|
|
58859
|
-
* The synthesizer's XG
|
|
59631
|
+
* The synthesizer's XG chorus block.
|
|
58860
59632
|
*
|
|
58861
59633
|
* Used when {@link GlobalMIDIParameter.system} is `xg`.
|
|
58862
59634
|
* @internal
|
|
@@ -58932,6 +59704,7 @@ var SpessaSynthProcessor = class {
|
|
|
58932
59704
|
this.gsReverbProcessor = options.gsReverbProcessor ?? new DefaultGSReverb(sampleRate, bufSize);
|
|
58933
59705
|
this.gsChorusProcessor = options.gsChorusProcessor ?? new DefaultGSChorus(sampleRate, bufSize);
|
|
58934
59706
|
this.gsDelayProcessor = options.gsDelayProcessor ?? new DefaultGSDelay(sampleRate, bufSize);
|
|
59707
|
+
this.gsEqualizerProcessor = options.gsEqualizerProcessor ?? new DefaultGSEqualizer(sampleRate);
|
|
58935
59708
|
this.gsInsertionBlock = options.gsInsertionBlock ?? new DefaultGSInsertion(sampleRate, bufSize);
|
|
58936
59709
|
this.xgReverbBlock = options.xgReverbBlock ?? new DefaultXGReverb(sampleRate, bufSize);
|
|
58937
59710
|
this.xgChorusBlock = options.xgChorusBlock ?? new DefaultXGChorus(sampleRate, bufSize);
|
|
@@ -58941,6 +59714,8 @@ var SpessaSynthProcessor = class {
|
|
|
58941
59714
|
this.sharedVoiceSendBuffer = new VoiceSendBuffer(bufSize);
|
|
58942
59715
|
this.insertionInputL = new Float32Array(bufSize);
|
|
58943
59716
|
this.insertionInputR = new Float32Array(bufSize);
|
|
59717
|
+
this.equalizerInputL = new Float32Array(bufSize);
|
|
59718
|
+
this.equalizerInputR = new Float32Array(bufSize);
|
|
58944
59719
|
this.gsReverbInput = new Float32Array(bufSize);
|
|
58945
59720
|
this.gsChorusInput = new Float32Array(bufSize);
|
|
58946
59721
|
this.gsDelayInput = new Float32Array(bufSize);
|
|
@@ -58978,6 +59753,13 @@ var SpessaSynthProcessor = class {
|
|
|
58978
59753
|
return this._voiceCount;
|
|
58979
59754
|
}
|
|
58980
59755
|
/**
|
|
59756
|
+
* The decoded sample data kept under {@link SynthProcessorOptions.decodedSampleBudget}, in bytes.
|
|
59757
|
+
* It is only counted when a budget is set.
|
|
59758
|
+
*/
|
|
59759
|
+
get decodedSampleBytes() {
|
|
59760
|
+
return this.decodedSamples.totalBytes;
|
|
59761
|
+
}
|
|
59762
|
+
/**
|
|
58981
59763
|
* Locks or unlocks a given {@link GlobalMIDIParameter}.
|
|
58982
59764
|
* This prevents any changes to it until it's unlocked.
|
|
58983
59765
|
* @param parameter The Global MIDI Parameter to lock.
|
|
@@ -59010,7 +59792,7 @@ var SpessaSynthProcessor = class {
|
|
|
59010
59792
|
* > Refer to the [MIDI Implementation](../../docs/extra/midi-implementation.md)
|
|
59011
59793
|
* > for the list of supported System Exclusives.
|
|
59012
59794
|
*
|
|
59013
|
-
* @param syx The system exclusive message as an array of bytes.
|
|
59795
|
+
* @param syx The system exclusive message as an array of bytes, without the starting `F0` byte.
|
|
59014
59796
|
* @param channelOffset The channel offset for the message as they usually can only address the first 16 channels.
|
|
59015
59797
|
* For example, to send a system exclusive on channel 16,
|
|
59016
59798
|
* send a system exclusive for channel 0 and set an offset of 16.
|
|
@@ -59232,11 +60014,11 @@ var SpessaSynthProcessor = class {
|
|
|
59232
60014
|
for (const message of messages) this.processMessage(message, channelOffset, options);
|
|
59233
60015
|
}
|
|
59234
60016
|
/**
|
|
59235
|
-
*
|
|
60017
|
+
* Destroy the synthesizer processor, clearing all channels and voices.
|
|
59236
60018
|
*
|
|
59237
|
-
*
|
|
59238
|
-
*
|
|
59239
|
-
*
|
|
60019
|
+
* > **Warning**
|
|
60020
|
+
* >
|
|
60021
|
+
* > This is irreversible, so use with caution.
|
|
59240
60022
|
*/
|
|
59241
60023
|
destroy() {
|
|
59242
60024
|
this.voices.length = 0;
|
|
@@ -59264,6 +60046,9 @@ var SpessaSynthProcessor = class {
|
|
|
59264
60046
|
* >
|
|
59265
60047
|
* > This method overrides the existing System Parameters with the ones from the snapshot.
|
|
59266
60048
|
*
|
|
60049
|
+
* If this processor has more XG insertion blocks than the snapshot,
|
|
60050
|
+
* the blocks without snapshot data are reset to their defaults.
|
|
60051
|
+
*
|
|
59267
60052
|
* @param snapshot The snapshot to apply.
|
|
59268
60053
|
*/
|
|
59269
60054
|
applySnapshot(snapshot) {
|
|
@@ -59325,6 +60110,17 @@ var SpessaSynthProcessor = class {
|
|
|
59325
60110
|
this.setReverbMacro(4);
|
|
59326
60111
|
this.setChorusMacro(2);
|
|
59327
60112
|
this.setDelayMacro(0);
|
|
60113
|
+
if (!this.systemParameters.gsEqualizerLock) {
|
|
60114
|
+
const eq = this.gsEqualizerProcessor;
|
|
60115
|
+
for (const parameter of GS_EQUALIZER_PARAMETERS) {
|
|
60116
|
+
eq[parameter] = DEFAULT_GS_EQUALIZER[parameter];
|
|
60117
|
+
this.callEvent("effectChange", {
|
|
60118
|
+
effect: "gsEqualizer",
|
|
60119
|
+
parameter,
|
|
60120
|
+
value: eq[parameter]
|
|
60121
|
+
});
|
|
60122
|
+
}
|
|
60123
|
+
}
|
|
59328
60124
|
if (!this.systemParameters.gsInsertionLock) {
|
|
59329
60125
|
this.gsInsertionBlock.reset();
|
|
59330
60126
|
this.callEvent("effectChange", {
|
|
@@ -59481,10 +60277,10 @@ var SpessaSynthProcessor = class {
|
|
|
59481
60277
|
* │ │ │ Chorus │ │
|
|
59482
60278
|
* │ │ │ Processor ├──────────┐ │
|
|
59483
60279
|
* │ │ └─┬──────────┬──┘ │ │
|
|
59484
|
-
*
|
|
59485
|
-
*
|
|
59486
|
-
*
|
|
59487
|
-
*
|
|
60280
|
+
* ┌────┴────┐ │ │ │ │ │
|
|
60281
|
+
* │Equalizer│ │ │ │ │ │
|
|
60282
|
+
* │or Bypass│ │ │ │ │ │
|
|
60283
|
+
* └────┬────┘ │ │ │ │ │
|
|
59488
60284
|
* │ │ │
|
|
59489
60285
|
* │ │ │ ┌────────┴───────┐ ┌─┴─────┴────────┐
|
|
59490
60286
|
* │ └───┼>┤ Delay ├─>>┤ Reverb │
|
|
@@ -59513,6 +60309,7 @@ var SpessaSynthProcessor = class {
|
|
|
59513
60309
|
const isXG = this.midiParameters.system === "xg";
|
|
59514
60310
|
const variationInsertion = this.xgVariationBlock.insertionMode;
|
|
59515
60311
|
const fx = this.systemParameters.effectsEnabled;
|
|
60312
|
+
const eq = fx && !isXG;
|
|
59516
60313
|
if (isXG && fx) {
|
|
59517
60314
|
for (const channel of this.midiChannels) channel.xgInsertionAssigned = false;
|
|
59518
60315
|
for (const insertion of this.xgInsertionBlocks) {
|
|
@@ -59528,6 +60325,10 @@ var SpessaSynthProcessor = class {
|
|
|
59528
60325
|
this.insertionInputL.fill(0);
|
|
59529
60326
|
this.insertionInputR.fill(0);
|
|
59530
60327
|
}
|
|
60328
|
+
if (eq) {
|
|
60329
|
+
this.equalizerInputL.fill(0);
|
|
60330
|
+
this.equalizerInputR.fill(0);
|
|
60331
|
+
}
|
|
59531
60332
|
for (const c of this.midiChannels) {
|
|
59532
60333
|
c.clearVoiceCount();
|
|
59533
60334
|
if (c.outputDirty) {
|
|
@@ -59573,10 +60374,11 @@ var SpessaSynthProcessor = class {
|
|
|
59573
60374
|
const channel = this.midiChannels[this.xgVariationBlock.partNumber];
|
|
59574
60375
|
if (channel) this.processXGInsertion(this.xgVariationBlock, channel, sampleCount);
|
|
59575
60376
|
}
|
|
60377
|
+
const { reverbGain, chorusGain } = this.systemParameters;
|
|
59576
60378
|
for (const ch of this.midiChannels) {
|
|
59577
60379
|
if (!ch.xgInsertionAssigned) continue;
|
|
59578
|
-
const revSend = (ch.midiControllers[MIDIControllers.reverbDepth] >> 7) / 127;
|
|
59579
|
-
const choSend = (ch.midiControllers[MIDIControllers.chorusDepth] >> 7) / 127;
|
|
60380
|
+
const revSend = reverbGain * (ch.midiControllers[MIDIControllers.reverbDepth] >> 7) / 127;
|
|
60381
|
+
const choSend = chorusGain * (ch.midiControllers[MIDIControllers.chorusDepth] >> 7) / 127;
|
|
59580
60382
|
if (revSend <= 0 && choSend <= 0) continue;
|
|
59581
60383
|
const { outputLeft, outputRight } = ch;
|
|
59582
60384
|
for (let i = 0; i < sampleCount; i++) {
|
|
@@ -59612,6 +60414,15 @@ var SpessaSynthProcessor = class {
|
|
|
59612
60414
|
}
|
|
59613
60415
|
continue;
|
|
59614
60416
|
}
|
|
60417
|
+
if (eq && midiParameters.eqEnabled) {
|
|
60418
|
+
const equalizerL = this.equalizerInputL;
|
|
60419
|
+
const equalizerR = this.equalizerInputR;
|
|
60420
|
+
for (let i = 0; i < sampleCount; i++) {
|
|
60421
|
+
equalizerL[i] += outputLeft[i];
|
|
60422
|
+
equalizerR[i] += outputRight[i];
|
|
60423
|
+
}
|
|
60424
|
+
continue;
|
|
60425
|
+
}
|
|
59615
60426
|
if (isXG && !variationInsertion) {
|
|
59616
60427
|
const gain = midiParameters.dryLevel / 127;
|
|
59617
60428
|
for (let i = 0; i < sampleCount; i++) {
|
|
@@ -59632,7 +60443,12 @@ var SpessaSynthProcessor = class {
|
|
|
59632
60443
|
this.xgChorusBlock.process(this.xgChorusInputL, this.xgChorusInputR, left, right, this.xgReverbInputL, this.xgReverbInputR, startIndex, sampleCount);
|
|
59633
60444
|
this.xgReverbBlock.process(this.xgReverbInputL, this.xgReverbInputR, left, right, startIndex, sampleCount);
|
|
59634
60445
|
} else {
|
|
59635
|
-
if (this.gsInsertionActive)
|
|
60446
|
+
if (this.gsInsertionActive) {
|
|
60447
|
+
const insertion = this.gsInsertionBlock;
|
|
60448
|
+
const toEqualizer = eq && insertion.sendEQSwitch;
|
|
60449
|
+
insertion.process(insertionInputL, insertionInputR, toEqualizer ? this.equalizerInputL : left, toEqualizer ? this.equalizerInputR : right, gsReverbInput, gsChorusInput, gsDelayInput, toEqualizer ? 0 : startIndex, sampleCount);
|
|
60450
|
+
}
|
|
60451
|
+
if (eq) this.gsEqualizerProcessor.process(this.equalizerInputL, this.equalizerInputR, left, right, startIndex, sampleCount);
|
|
59636
60452
|
this.gsChorusProcessor.process(gsChorusInput, left, right, gsReverbInput, gsDelayInput, startIndex, sampleCount);
|
|
59637
60453
|
if (this.gsDelayActive) this.gsDelayProcessor.process(gsDelayInput, left, right, gsReverbInput, startIndex, sampleCount);
|
|
59638
60454
|
this.gsReverbProcessor.process(gsReverbInput, left, right, startIndex, sampleCount);
|
|
@@ -59692,13 +60508,6 @@ var SpessaSynthProcessor = class {
|
|
|
59692
60508
|
this.soundBankManager.refresh();
|
|
59693
60509
|
}
|
|
59694
60510
|
/**
|
|
59695
|
-
* The decoded sample data kept under {@link SynthProcessorOptions.decodedSampleBudget}, in bytes.
|
|
59696
|
-
* It is only counted when a budget is set.
|
|
59697
|
-
*/
|
|
59698
|
-
get decodedSampleBytes() {
|
|
59699
|
-
return this.decodedSamples.totalBytes;
|
|
59700
|
-
}
|
|
59701
|
-
/**
|
|
59702
60511
|
* Clears the synthesizer's voice cache.
|
|
59703
60512
|
* This can be used to hear the changes after editing a {@link BasicSoundBank}
|
|
59704
60513
|
*/
|
|
@@ -59730,7 +60539,7 @@ var SpessaSynthProcessor = class {
|
|
|
59730
60539
|
*/
|
|
59731
60540
|
purgeCachedPatch(patch) {
|
|
59732
60541
|
const patchKey = this.getCachedVoiceIndex(patch, 0, 0);
|
|
59733
|
-
for (const key of this.cachedVoices.keys()) if (key %
|
|
60542
|
+
for (const key of this.cachedVoices.keys()) if (key % 4194304 === patchKey) this.cachedVoices.delete(key);
|
|
59734
60543
|
}
|
|
59735
60544
|
/**
|
|
59736
60545
|
* Sets a global MIDI parameter of the synthesizer.
|
|
@@ -60210,16 +61019,10 @@ var SpessaSynthProcessor = class {
|
|
|
60210
61019
|
}, "gs");
|
|
60211
61020
|
}
|
|
60212
61021
|
getCachedVoiceIndex(patch, midiNote, velocity) {
|
|
60213
|
-
|
|
60214
|
-
|
|
60215
|
-
const { isGMGSDrum, program } = patch;
|
|
60216
|
-
if (isGMGSDrum) {
|
|
60217
|
-
bankMSB = 128;
|
|
60218
|
-
bankLSB = 0;
|
|
60219
|
-
}
|
|
60220
|
-
return bankMSB + bankLSB * 129 + program * 16512 + 2113536 * midiNote + 270532608 * velocity;
|
|
61022
|
+
const { isGMGSDrum, program, bankLSB } = patch;
|
|
61023
|
+
return (isGMGSDrum ? 128 + patch.bankMSB : patch.bankMSB) + bankLSB * 256 + program * 32768 + 4194304 * midiNote + 536870912 * velocity;
|
|
60221
61024
|
}
|
|
60222
61025
|
};
|
|
60223
|
-
export { BasicInstrument, BasicInstrumentZone, BasicLinkedZone, BasicMIDI, BasicPreset, BasicPresetZone, BasicSample, BasicSoundBank, BasicZone, DEFAULT_CHANNEL_MIDI_PARAMETERS, DEFAULT_CHANNEL_SYSTEM_PARAMETERS, DEFAULT_DRUM_REVERB, DEFAULT_GLOBAL_MIDI_PARAMETERS, DEFAULT_GLOBAL_SYSTEM_PARAMETERS, DEFAULT_MIDI_CONTROLLERS, DEFAULT_SOUND_BANK_CLONE_OPTIONS, DEFAULT_SOUND_BANK_LOAD_OPTIONS, DEFAULT_SYNTH_MODE, DefaultGSChorus, DefaultGSDelay, DefaultGSInsertion, DefaultGSReverb, DefaultXGChorus, DefaultXGEffect, DefaultXGInsertion, DefaultXGReverb, DefaultXGSystemEffect, DefaultXGVariation, DrumParameterUtils, EmptySample, GENERATORS_AMOUNT, GSInsertionControlSources, Generator, GeneratorLimits, GeneratorTypes, INSTRUMENT_ONLY_GENERATORS, IndexedByteArray, InterpolationTypes, MAX_GENERATOR, MIDIBuilder, MIDIChannel, MIDIControllers, MIDIMessage, MIDIMessageTypes, MIDIPatchTools, MIDITrack, MIDIUtils, MIDI_DRUM_CHANNEL, Modulator, ModulatorControllerSources, ModulatorCurveTypes, ModulatorSource, ModulatorTransformTypes, NonRegisteredParameterTypesLSB, NonRegisteredParameterTypesMSB, RegisteredParameterTypes, SPESSASYNTH_GAIN_FACTOR, SPESSA_BUFSIZE, SampleTypes, SoundBankCloner, SoundBankLoader, SoundBankManager, SpessaLog, SpessaSynthCoreUtils, SpessaSynthProcessor, SpessaSynthSequencer, VOICE_CAP, audioToWav };
|
|
61026
|
+
export { BasicInstrument, BasicInstrumentZone, BasicLinkedZone, BasicMIDI, BasicPreset, BasicPresetZone, BasicSample, BasicSoundBank, BasicZone, DEFAULT_CHANNEL_MIDI_PARAMETERS, DEFAULT_CHANNEL_SYSTEM_PARAMETERS, DEFAULT_DRUM_REVERB, DEFAULT_GLOBAL_MIDI_PARAMETERS, DEFAULT_GLOBAL_SYSTEM_PARAMETERS, DEFAULT_MIDI_CONTROLLERS, DEFAULT_SOUND_BANK_CLONE_OPTIONS, DEFAULT_SOUND_BANK_LOAD_OPTIONS, DEFAULT_SYNTH_MODE, DefaultGSChorus, DefaultGSDelay, DefaultGSEqualizer, DefaultGSInsertion, DefaultGSReverb, DefaultXGChorus, DefaultXGEffect, DefaultXGInsertion, DefaultXGReverb, DefaultXGSystemEffect, DefaultXGVariation, DrumParameterUtils, EmptySample, GENERATORS_AMOUNT, GSInsertionControlSources, Generator, GeneratorLimits, GeneratorTypes, INSTRUMENT_ONLY_GENERATORS, IndexedByteArray, InterpolationTypes, MAX_GENERATOR, MIDIBuilder, MIDIChannel, MIDIControllers, MIDIMessage, MIDIMessageTypes, MIDIPatchTools, MIDITrack, MIDIUtils, MIDI_DRUM_CHANNEL, Modulator, ModulatorControllerSources, ModulatorCurveTypes, ModulatorSource, ModulatorTransformTypes, NonRegisteredParameterTypesLSB, NonRegisteredParameterTypesMSB, RegisteredParameterTypes, SPESSASYNTH_GAIN_FACTOR, SPESSA_BUFSIZE, SampleTypes, SoundBankCloner, SoundBankLoader, SoundBankManager, SpessaLog, SpessaSynthCoreUtils, SpessaSynthProcessor, SpessaSynthSequencer, VOICE_CAP, audioToWav };
|
|
60224
61027
|
|
|
60225
61028
|
//# sourceMappingURL=index.js.map
|